Forráskód Böngészése

fix : 充值回调CP新增重试机制, 手机号绑定新增时间判定

bilingfeng 3 hónapja
szülő
commit
a791c82d50

+ 5 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/User.java

@@ -201,6 +201,11 @@ public class User implements Serializable {
      */
     private LocalDateTime relationCreateTime;
 
+    /**
+     * 手机号绑定时间
+     */
+    private LocalDateTime mobileBindTime;
+
     /**
      * 获取用户显示手机号
      *

+ 6 - 0
game-module/game-module-sdk/pom.xml

@@ -133,6 +133,12 @@
             <artifactId>kafka-clients</artifactId>
             <version>2.2.1</version>
         </dependency>
+        <!--重试机制-->
+        <dependency>
+            <groupId>org.springframework.retry</groupId>
+            <artifactId>spring-retry</artifactId>
+            <version>1.3.3</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 3 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java

@@ -6,6 +6,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.retry.annotation.EnableRetry;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
 /**
@@ -19,11 +20,12 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 @Configuration
 @EnableDubbo
 @EnableScheduling
+@EnableRetry(proxyTargetClass = true)
 public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <腾讯SDK媒体回传米大师订单全量回传> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <充值回调CP新增重试机制, 手机号绑定新增时间判定> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 55 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/api/CpApiService.java

@@ -0,0 +1,55 @@
+package com.zanxiang.game.module.sdk.service.api;
+
+import com.github.sd4324530.jtuple.Tuple2;
+import com.zanxiang.game.module.sdk.pojo.result.PushCpResult;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Objects;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-01-07
+ * @description : CP方充值接口调用
+ */
+@Slf4j
+@Service
+public class CpApiService {
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    /**
+     * 回调CP充值接口
+     *
+     * @param url : 请求链接
+     * @return : 返回充值结果以及CP返回结果
+     */
+    @Retryable(value = {BaseException.class}, maxAttempts = 4, backoff = @Backoff(delay = 5000, multiplier = 2))
+    public Tuple2<Boolean, String> callCpRecharge(String url) {
+        String resultStr;
+        try {
+            resultStr = restTemplate.getForObject(url, String.class);
+        } catch (Exception e) {
+            log.error("回调CP充值接口异常, url : {}, e : {}", url, e.getMessage());
+            throw new BaseException("回调CP充值接口异常");
+        }
+        PushCpResult pushCpResult = JsonUtil.toObj(resultStr, PushCpResult.class);
+        if (pushCpResult == null) {
+            log.error("CP返回充值结果为空, url : {}, result is null", url);
+            throw new BaseException("CP返回充值结果为空");
+        }
+        boolean pushSuccess = Objects.equals(pushCpResult.getResult(), PushCpResult.SUCCESS_CODE)
+                || Objects.equals(pushCpResult.getCode(), PushCpResult.SUCCESS_CODE);
+        if (pushSuccess) {
+            return Tuple2.with(Boolean.TRUE, resultStr);
+        }
+        throw new BaseException("CP返回充值失败");
+    }
+}

+ 54 - 26
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/PerformOrderServiceImpl.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.sdk.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.github.sd4324530.jtuple.Tuple2;
 import com.zanxiang.game.module.base.pojo.enums.CpStatusEnum;
 import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
 import com.zanxiang.game.module.base.util.DateUtils;
@@ -12,6 +13,7 @@ import com.zanxiang.game.module.sdk.pojo.dto.PayMerchantDTO;
 import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
 import com.zanxiang.game.module.sdk.pojo.result.PushCpResult;
 import com.zanxiang.game.module.sdk.service.*;
+import com.zanxiang.game.module.sdk.service.api.CpApiService;
 import com.zanxiang.game.module.sdk.util.SignUtil;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.URIUtil;
@@ -73,6 +75,9 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
     @Autowired
     private IGameService gameService;
 
+    @Autowired
+    private CpApiService cpApiService;
+
     @Override
     @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
     public Boolean pushCp(PlatformOrderDTO orderInfo) {
@@ -90,31 +95,11 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
         Game game = gameService.getById(orderInfo.getGameId());
         //用户信息
         User user = userService.getById(orderInfo.getUserId());
-        //回调CP
-        Map<String, String> map = new HashMap<>(9);
-        //用户存在关联用户id, 且不是微信小程序, 则判定为导量用户, 提交CP原始用户id
-        if (user.getRelationUserId() != null && !Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
-            map.put("userId", String.valueOf(user.getRelationUserId()));
-        } else {
-            map.put("userId", String.valueOf(orderInfo.getUserId()));
-        }
-        //判断是否设置充值打折
-        if (game.getRechargeRebate() != null) {
-            BigDecimal money = orderInfo.getAmount().multiply(game.getRechargeRebate()).setScale(2, RoundingMode.HALF_UP);
-            map.put("money", money.stripTrailingZeros().toPlainString());
-        } else {
-            map.put("money", orderInfo.getAmount().stripTrailingZeros().toPlainString());
-        }
-        map.put("time", String.valueOf(System.currentTimeMillis()));
-        map.put("serverId", orderInfo.getServerId());
-        map.put("roleId", orderInfo.getRoleId());
-        map.put("roleName", orderInfo.getRoleName());
-        map.put("orderId", orderInfo.getCpOrderId());
-        map.put("ext", URIUtil.encodeURIComponent(orderInfo.getExt()));
-        map.put("sign", this.paySign(orderInfo.getGameId(), map));
-        map.put("orderId2", orderInfo.getOrderId());
-        //url
-        String url = URIUtil.fillUrlParams(cpPaybackUrl, map, false);
+        //回调参数
+        Map<String, String> param = this.payParam(game, user, orderInfo);
+        //拼接请求URI
+        String url = URIUtil.fillUrlParams(cpPaybackUrl, param, false);
+        //调CP充值回调
         String resultStr;
         try {
             resultStr = restTemplate.getForObject(url, String.class);
@@ -125,6 +110,21 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
         PushCpResult pushCpResult = JsonUtil.toObj(resultStr, PushCpResult.class);
         boolean pushSuccess = Objects.equals(pushCpResult.getResult(), PushCpResult.SUCCESS_CODE)
                 || Objects.equals(pushCpResult.getCode(), PushCpResult.SUCCESS_CODE);
+
+        //CP返回充值失败, 进入重试
+        if (!pushSuccess) {
+            try {
+                Tuple2<Boolean, String> tuple2 = cpApiService.callCpRecharge(url);
+                //重试成功, 修改结果, 重试失败, 则打印日志, 将日志给CP方
+                if (tuple2.first) {
+                    pushSuccess = true;
+                    resultStr = tuple2.second;
+                }
+            } catch (Exception e) {
+                log.error("CP充值回调重试失败, url : {}, e : {}", url, e.getMessage());
+            }
+        }
+
         log.error("通知CP支付回调结果, url : {}, resultStr : {}, pushSuccess : {}", url, resultStr, pushSuccess);
         //更新订单信息
         orderService.update(new LambdaUpdateWrapper<Order>()
@@ -140,7 +140,7 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
                 .status(orderInfo.getStatus())
                 .cpStatus(pushSuccess ? CpStatusEnum.SUCCESS.getStatus() : CpStatusEnum.FAIL.getStatus())
                 .cpPaybackUrl(cpPaybackUrl)
-                .params(JsonUtil.toString(map))
+                .params(JsonUtil.toString(param))
                 .ext(orderInfo.getExt())
                 .notifyCnt(orderInfo.getCpNotifyCnt() + 1)
                 .createTime(LocalDateTime.now())
@@ -150,6 +150,34 @@ public class PerformOrderServiceImpl implements IPerformOrderService {
         return pushSuccess;
     }
 
+    private Map<String, String> payParam(Game game, User user, PlatformOrderDTO orderInfo) {
+        Map<String, String> map = new HashMap<>(10);
+        //用户存在关联用户id, 且不是微信小程序, 则判定为导量用户, 提交CP原始用户id
+        if (user.getRelationUserId() != null
+                && !Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
+            map.put("userId", String.valueOf(user.getRelationUserId()));
+        } else {
+            map.put("userId", String.valueOf(orderInfo.getUserId()));
+        }
+        //判断是否设置充值打折
+        if (game.getRechargeRebate() != null) {
+            BigDecimal money = orderInfo.getAmount().multiply(game.getRechargeRebate())
+                    .setScale(2, RoundingMode.HALF_UP);
+            map.put("money", money.stripTrailingZeros().toPlainString());
+        } else {
+            map.put("money", orderInfo.getAmount().stripTrailingZeros().toPlainString());
+        }
+        map.put("time", String.valueOf(System.currentTimeMillis()));
+        map.put("serverId", orderInfo.getServerId());
+        map.put("roleId", orderInfo.getRoleId());
+        map.put("roleName", orderInfo.getRoleName());
+        map.put("orderId", orderInfo.getCpOrderId());
+        map.put("ext", URIUtil.encodeURIComponent(orderInfo.getExt()));
+        map.put("sign", this.paySign(orderInfo.getGameId(), map));
+        map.put("orderId2", orderInfo.getOrderId());
+        return map;
+    }
+
     private String paySign(Long gameId, Map<String, String> map) {
         //查询游戏
         GameExt gameExt = gameExtService.getByGameId(gameId);

+ 5 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/SmsServiceImpl.java

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -56,12 +57,14 @@ public class SmsServiceImpl implements ISmsService {
         if (user == null) {
             throw new BaseException("参数错误, 用户信息不存在");
         }
-        if (Strings.isBlank(user.getMobile())) {
+        if (Strings.isBlank(user.getMobile()) || user.getMobileBindTime() == null
+                || ChronoUnit.DAYS.between(user.getMobileBindTime(), LocalDateTime.now()) >= 30) {
             return Collections.singletonMap("isBindPhone", Boolean.FALSE);
         }
         Map<String, Object> resultMap = new HashMap<>(2);
         resultMap.put("isBindPhone", Boolean.TRUE);
         resultMap.put("phone", user.getShowPhoneNum());
+        resultMap.put("mobileBindTime", user.getMobileBindTime());
         return resultMap;
     }
 
@@ -113,6 +116,7 @@ public class SmsServiceImpl implements ISmsService {
         //更新用户手机号
         userService.update(new LambdaUpdateWrapper<User>()
                 .set(User::getMobile, param.getMobile())
+                .set(User::getMobileBindTime, LocalDateTime.now())
                 .set(User::getUpdateTime, LocalDateTime.now())
                 .eq(User::getId, userData.getUserId()));
         //返回结果