Преглед на файлове

fix : SDK解决线上并发锁的问题

bilingfeng преди 1 година
родител
ревизия
59f2fa0d29

+ 12 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfSystemReplyService.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.KfSystemReply;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-11
+ * @description : 客服自动回复
+ */
+public interface IKfSystemReplyService extends IService<KfSystemReply> {
+}

+ 13 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -87,6 +87,9 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
     @Autowired
     private IKfRoomMsgService kfRoomMsgService;
 
+    @Autowired
+    private IKfSystemReplyService kfSystemReplyService;
+
     @Autowired
     private RedisUtil<String> redisUtil;
 
@@ -137,6 +140,16 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
         this.pushMessage(this.transform(kfRoom, gameApplet.getGameId(), kfRoomMsg, msgContent));
     }
 
+    private void systemReplyHandle(Long gameId){
+        //获取自动回复配置
+        KfSystemReply kfSystemReply = kfSystemReplyService.getById(gameId);
+        if (kfSystemReply == null){
+            return;
+        }
+        //判断当前时间是否在时间内
+//        LocalTime.parse(kfSystemReply.getStartTime());
+    }
+
     private boolean orderHandle(String orderId, Long gameId, KfAppletMsgDTO kfAppletMsgDTO, KfRoom kfRoom, KfRoomMsg kfRoomMsg) {
         //判断收到的消息是否为充值消息或者小程序消息
         if (!KfAppletMsgDTO.MSG_CONTENT_PAY.contains(kfAppletMsgDTO.getContent())

+ 18 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfSystemReplyServiceImpl.java

@@ -0,0 +1,18 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.service.IKfSystemReplyService;
+import com.zanxiang.game.module.mybatis.entity.KfSystemReply;
+import com.zanxiang.game.module.mybatis.mapper.KfSystemReplyMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-11
+ * @description : 客服自动回复
+ */
+@Slf4j
+@Service
+public class KfSystemReplyServiceImpl extends ServiceImpl<KfSystemReplyMapper, KfSystemReply> implements IKfSystemReplyService {
+}

+ 58 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfSystemReply.java

@@ -0,0 +1,58 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.Date;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-11
+ * @description : 客服系统回复
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_kf_system_reply")
+public class KfSystemReply {
+
+    /**
+     * 游戏
+     */
+    @TableId(value = "game_id", type = IdType.INPUT)
+    private Long gameId;
+
+    /**
+     * APPID
+     */
+    private String appId;
+
+    /**
+     * 回复
+     */
+    private String sysReply;
+
+    /**
+     * 开始时间
+     */
+    private Date startTime;
+
+    /**
+     * 结束时间
+     */
+    private Date endTime;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfSystemReplyMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.KfSystemReply;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-03-11
+ * @description : ${description}
+ */
+public interface KfSystemReplyMapper extends BaseMapper<KfSystemReply> {
+}

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

@@ -23,7 +23,7 @@ public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <解决线上BUg01> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <解决SDK线上并发问题> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 7 - 2
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java

@@ -30,7 +30,7 @@ public class RedisKeyConstant {
     /**
      * 角色信息更新
      */
-    public static final String ROLE_UPDATE_KEY = RedisKeyConstant.REDIS_PREFIX + "role_update";
+    public static final String ROLE_CREATE_LOCK = RedisKeyConstant.REDIS_PREFIX + "role_create_lock_";
 
     /**
      * 角色信息更新
@@ -55,7 +55,7 @@ public class RedisKeyConstant {
     /**
      * 用户注册
      */
-    public static final String USER_CREATE = RedisKeyConstant.REDIS_PREFIX + "user_create";
+    public static final String USER_CREATE = RedisKeyConstant.REDIS_PREFIX + "user_create_";
 
     /**
      * 客服支付订单标记
@@ -67,4 +67,9 @@ public class RedisKeyConstant {
      */
     public static final String GET_PAY_PARAM_LOCK = RedisKeyConstant.REDIS_PREFIX + "get_pay_param_lock_";
 
+    /**
+     * 用户token创建锁
+     */
+    public static final String TOKEN_CREATE_LOCK = RedisKeyConstant.REDIS_PREFIX + "create_token_lock_";
+
 }

+ 9 - 11
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -140,23 +140,23 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
     }
 
     private void gameRoleCreate(GameUserRoleUpdateParam param, UserData userData) {
-        //上锁
-        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId(), 0L, 1L, TimeUnit.MINUTES)) {
-            return;
-        }
-        //查询玩家信息
-        GameUser gameUser = gameUserService.getOne(new LambdaQueryWrapper<GameUser>().eq(GameUser::getGameId, userData.getGameId())
-                .eq(GameUser::getUserId, userData.getUserId()));
         //查询玩家角色信息
         GameUserRole userRole = this.getOne(new LambdaQueryWrapper<GameUserRole>()
                 .eq(GameUserRole::getGameId, userData.getGameId())
                 .eq(GameUserRole::getRoleId, param.getRoleId()));
         if (userRole != null) {
-            //释放锁
-            distributedLockComponent.unlock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId());
             return;
         }
+        //上锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.ROLE_CREATE_LOCK + param.getRoleId(), 0L, 3L, TimeUnit.MINUTES)) {
+            return;
+        }
+        //查询玩家信息
         User user = userService.getById(userData.getUserId());
+        GameUser gameUser = gameUserService.getOne(new LambdaQueryWrapper<GameUser>()
+                .eq(GameUser::getGameId, userData.getGameId())
+                .eq(GameUser::getUserId, userData.getUserId()));
+        //创建角色
         userRole = this.transform(param, userData, gameUser, user);
         super.save(userRole);
         //更新玩家创角数
@@ -173,8 +173,6 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         callBackService.roleCallBack(userRole, userData);
         //用户创角埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_ROLE_CREATE, JsonUtil.toString(userRole));
-        //释放锁
-        distributedLockComponent.unlock(RedisKeyConstant.ROLE_UPDATE_KEY + "_" + userData.getUserId());
     }
 
     private GameUserRole transform(GameUserRoleUpdateParam param, UserData userData, GameUser gameUser, User user) {

+ 3 - 5
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java

@@ -277,10 +277,10 @@ public class LoginServiceImpl implements IRegisterLoginService {
 
     private User userCreateSave(UserData userData, String userName, String password, String mobile, String openId, String sessionKey) {
         //锁Key
-        String lockKey = RedisKeyConstant.USER_CREATE + "_" + userData.getGameId() + "_" + userName;
+        String lockKey = RedisKeyConstant.USER_CREATE + userData.getGameId() + "_" + userName;
         //上锁
-        if (!distributedLockComponent.doLock(lockKey, 3L, 10L, TimeUnit.SECONDS)) {
-            throw new BaseException("用户信息正在注册中, 请稍后重试");
+        if (!distributedLockComponent.doLock(lockKey, 0L, 3L, TimeUnit.MINUTES)) {
+            throw new BaseException("用户信息正在注册中, 请勿重复请求!");
         }
         //渠道id, 链接参数, 分享人id
         Tuple3<Long, Map<String, String>, String> tuple3 = agentService.getUserAgentId(userData);
@@ -312,8 +312,6 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 .build());
         //注册信息埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_REG, JsonUtil.toString(user));
-        //释放锁
-        distributedLockComponent.unlock(lockKey);
         return user;
     }
 

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

@@ -94,7 +94,7 @@ public class OrderPayServiceImpl implements IOrderPayService {
                 .eq(OrderPayParam::getOrderId, order.getOrderId()));
         if (orderPayParam == null) {
             log.error("支付参数获取失败, orderId : {}", orderId);
-            throw new BaseException("支付参数获取失败");
+            throw new BaseException("支付参数正在创建中, 请勿重复请求!");
         }
         //返回支付参数
         return BeanUtil.copy(orderPayParam, PayParamVO.class);

+ 23 - 9
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

@@ -19,6 +19,7 @@ import com.zanxiang.game.module.sdk.pojo.vo.CpTokenCheckVO;
 import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.game.module.sdk.util.RedisUtil;
 import com.zanxiang.game.module.sdk.util.SignUtil;
+import com.zanxiang.module.redis.service.IDistributedLockComponent;
 import com.zanxiang.module.util.DateUtil;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
@@ -35,6 +36,7 @@ import reactor.util.function.Tuples;
 import java.time.LocalDateTime;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author : lingfeng
@@ -57,6 +59,9 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
     @Autowired
     private IUserService userService;
 
+    @Autowired
+    private IDistributedLockComponent distributedLockComponent;
+
     @Autowired
     private IUserLoginLogService userLoginLogService;
 
@@ -248,15 +253,7 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
                 .eq(UserToken::getDeviceType, deviceType));
         //不存在token数据, 直接创建
         if (userToken == null) {
-            userToken = UserToken.builder()
-                    .userId(userId)
-                    .token(token)
-                    .deviceType(deviceType)
-                    .createTime(LocalDateTime.now())
-                    .updateTime(LocalDateTime.now())
-                    .expireTime(DateUtils.localDateTimeToSecond(LocalDateTime.now()) + ExpireTimeEnum.ONE_WEEK.getTime())
-                    .build();
-            super.save(userToken);
+            userToken = this.tokenSave(userId, token, deviceType);
         } else {
             //更新数据库
             userToken.setToken(token);
@@ -274,6 +271,23 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
         return token;
     }
 
+    private UserToken tokenSave(Long userId, String token, Integer deviceType) {
+        //上锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.TOKEN_CREATE_LOCK + userId + "_" + deviceType, 0L, 3L, TimeUnit.MINUTES)) {
+            throw new BaseException("token创建中, 请勿重复请求!");
+        }
+        UserToken userToken = UserToken.builder()
+                .userId(userId)
+                .token(token)
+                .deviceType(deviceType)
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .expireTime(DateUtils.localDateTimeToSecond(LocalDateTime.now()) + ExpireTimeEnum.ONE_WEEK.getTime())
+                .build();
+        super.save(userToken);
+        return userToken;
+    }
+
     @Override
     public void deleteUserToken(Long userId, Integer deviceType) {
         UserToken userToken = super.getOne(new LambdaQueryWrapper<UserToken>()