Преглед изворни кода

Merge branch 'dev' of GameCenter/game-center into master

zhimo пре 1 година
родитељ
комит
500ce3ad11
24 измењених фајлова са 643 додато и 49 уклоњено
  1. 1 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java
  2. 38 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/api/AppletServerApi.java
  3. 40 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/KfSessionFromEnum.java
  4. 3 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/handler/GlobalExceptionHandler.java
  5. 5 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfAppletMsgDTO.java
  6. 51 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/UserAppletSubmitParam.java
  7. 28 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameExtService.java
  8. 12 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfAppletReplyService.java
  9. 21 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IUserAppletService.java
  10. 3 20
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/CpServerApiService.java
  11. 3 17
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpSendMsgLogServiceImpl.java
  12. 29 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameExtServiceImpl.java
  13. 88 10
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java
  14. 18 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletReplyServiceImpl.java
  15. 103 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/UserAppletServiceImpl.java
  16. 37 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/utils/SignUtil.java
  17. 66 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfAppletReply.java
  18. 5 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfSessionUser.java
  19. 63 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/UserApplet.java
  20. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfAppletReplyMapper.java
  21. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/UserAppletMapper.java
  22. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  23. 2 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/handler/GlobalExceptionHandler.java
  24. 2 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameAppletServiceImpl.java

+ 1 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java

@@ -23,7 +23,7 @@ public class ManageApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(ManageApplication.class, args);
-        System.out.println("赞象Manage服务启动成功 < (游戏聊天消息对接武哥接口´・・)ノ(._.`) \n" +
+        System.out.println("赞象Manage服务启动成功 < (新链路小程序登自动回复消息´・・)ノ(._.`) \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +

+ 38 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/api/AppletServerApi.java

@@ -0,0 +1,38 @@
+package com.zanxiang.game.module.manage.controller.api;
+
+import com.zanxiang.game.module.manage.pojo.params.UserAppletSubmitParam;
+import com.zanxiang.game.module.manage.service.IUserAppletService;
+import com.zanxiang.module.util.pojo.ResultVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : 小程序接口
+ */
+@Api(tags = {"小程序接口"})
+@RestController
+@RequestMapping("/api/applet")
+@Slf4j
+public class AppletServerApi {
+
+    @Autowired
+    private IUserAppletService userAppletService;
+
+    @ApiOperation(value = "小程序登录信息提交")
+    @PostMapping(value = "/user/submit")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<Boolean> userAppletSubmit(@Validated @RequestBody UserAppletSubmitParam param) {
+        return ResultVO.ok(userAppletService.userAppletSubmit(param));
+    }
+}

+ 40 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/enums/KfSessionFromEnum.java

@@ -0,0 +1,40 @@
+package com.zanxiang.game.module.manage.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-30
+ * @description : 消息来源
+ */
+@Getter
+@AllArgsConstructor
+public enum KfSessionFromEnum {
+
+    /**
+     * 来源广告
+     */
+    KF_SESSION_FROM_AD("KF_SESSION_FROM_AD"),
+
+    /**
+     * 来源平台
+     */
+    KF_SESSION_FROM_PLATFORM("KF_SESSION_FROM_PLATFORM");
+
+    /**
+     * 消息类型
+     */
+    private String value;
+
+    public static KfSessionFromEnum getKfSessionFrom(String sessionFrom) {
+        for (KfSessionFromEnum kfSessionFromEnum : KfSessionFromEnum.values()) {
+            if (Objects.equals(sessionFrom, kfSessionFromEnum.getValue())) {
+                return kfSessionFromEnum;
+            }
+        }
+        return null;
+    }
+}

+ 3 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/handler/GlobalExceptionHandler.java

@@ -30,6 +30,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(BaseException.class)
     public ResultVO<?> baseException(BaseException e) {
+        log.error(e.getMessage(), e);
         return ResultVO.fail(e.getMessage());
     }
 
@@ -50,6 +51,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(BindException.class)
     public ResultVO<?> validatedBindException(BindException e) {
+        log.error(e.getMessage(), e);
         String message = e.getAllErrors().get(0).getDefaultMessage();
         return ResultVO.fail(message);
     }
@@ -59,6 +61,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(MethodArgumentNotValidException.class)
     public ResultVO<?> validExceptionHandler(MethodArgumentNotValidException e) {
+        log.error(e.getMessage(), e);
         String message = e.getBindingResult().getFieldError().getDefaultMessage();
         return ResultVO.fail(message);
     }

+ 5 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/dto/KfAppletMsgDTO.java

@@ -102,4 +102,9 @@ public class KfAppletMsgDTO {
      * 小程序图片媒体id
      */
     private String ThumbMediaId;
+
+    /**
+     * 消息来源
+     */
+    private String SessionFrom;
 }

+ 51 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/UserAppletSubmitParam.java

@@ -0,0 +1,51 @@
+package com.zanxiang.game.module.manage.pojo.params;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : 小程序用户信息提交
+ */
+@Data
+public class UserAppletSubmitParam {
+
+    /**
+     * 加密标识
+     */
+    @NotBlank(message = "加密标识不可为空")
+    @ApiModelProperty(notes = "加密标识")
+    private String sign;
+
+    /**
+     * 请求时间
+     */
+    @NotNull(message = "请求时间不可为空")
+    @ApiModelProperty(notes = "请求时间, 时间戳 : 13位")
+    private Long signTime;
+
+    /**
+     * 应用id
+     */
+    @NotBlank(message = "应用id不可为空")
+    @ApiModelProperty(notes = "应用id")
+    private String appId;
+
+    /**
+     * 授权登录code
+     */
+    @NotBlank(message = "授权登录code不可为空")
+    @ApiModelProperty(notes = "授权登录code")
+    private String code;
+
+    /**
+     * 渠道参数
+     */
+    @NotBlank(message = "渠道参数不可为空")
+    @ApiModelProperty(notes = "渠道参数")
+    private String channel;
+}

+ 28 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameExtService.java

@@ -0,0 +1,28 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.GameExt;
+
+/**
+ * @author : lingfeng
+ * @time : 2022-09-23
+ * @description : 游戏密钥
+ */
+public interface IGameExtService extends IService<GameExt> {
+
+    /**
+     * 通过游戏id
+     *
+     * @param gameId 游戏id
+     * @return {@link GameExt}
+     */
+    GameExt getByGameId(Long gameId);
+
+    /**
+     * 通过游戏应用程序id
+     *
+     * @param appId 应用程序id
+     * @return {@link GameExt}
+     */
+    GameExt getByGameAppId(String appId);
+}

+ 12 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfAppletReplyService.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.KfAppletReply;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-30
+ * @description : 小程序自动回复
+ */
+public interface IKfAppletReplyService extends IService<KfAppletReply> {
+}

+ 21 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IUserAppletService.java

@@ -0,0 +1,21 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.manage.pojo.params.UserAppletSubmitParam;
+import com.zanxiang.game.module.mybatis.entity.UserApplet;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : 投放小程序用户信息
+ */
+public interface IUserAppletService extends IService<UserApplet> {
+
+    /**
+     * 小程序访问信息提交
+     *
+     * @param param : 提交参数
+     * @return : 返回结果
+     */
+    boolean userAppletSubmit(UserAppletSubmitParam param);
+}

+ 3 - 20
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/CpServerApiService.java

@@ -7,6 +7,7 @@ import com.zanxiang.game.module.manage.pojo.params.OpenGameServerParam;
 import com.zanxiang.game.module.manage.service.IGameServerService;
 import com.zanxiang.game.module.manage.service.IGameSupperService;
 import com.zanxiang.game.module.manage.service.IListenCallService;
+import com.zanxiang.game.module.manage.utils.SignUtil;
 import com.zanxiang.game.module.mybatis.entity.GameServer;
 import com.zanxiang.game.module.mybatis.entity.GameSupper;
 import com.zanxiang.module.util.JsonUtil;
@@ -17,8 +18,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.RestTemplate;
 
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
 import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.Map;
@@ -127,26 +126,10 @@ public class CpServerApiService {
     private void signCheck(String cpServerKey, Long gameId, String serverId, Long signTime, String sign) {
         String signStr = "cpServerKey=" + cpServerKey + "gameId=" + gameId
                 + "serverId=" + serverId + "signTime=" + signTime;
-        log.error("请求加密字符串 signStr : {}", signStr);
-        String mySign = this.md5(signStr);
-        if (Objects.equals(mySign, sign)) {
+        String mySign = SignUtil.md5(signStr);
+        if (!Objects.equals(mySign.toUpperCase(), sign.toUpperCase())) {
             log.error("加密验证失败, sign : {}, mySign : {}", sign, mySign);
             throw new BaseException("加密标识错误");
         }
     }
-
-    private String md5(String data) {
-        try {
-            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
-            byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
-            StringBuilder sb = new StringBuilder();
-            for (byte item : array) {
-                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
-            }
-            return sb.toString().toUpperCase();
-        } catch (Exception e) {
-            log.error("MD5加密异常, data : {}", data);
-            throw new BaseException("MD5加密异常");
-        }
-    }
 }

+ 3 - 17
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/CpSendMsgLogServiceImpl.java

@@ -8,6 +8,7 @@ import com.zanxiang.game.module.manage.enums.CpSendRoleResultEnum;
 import com.zanxiang.game.module.manage.pojo.dto.CpSendMsgResultDTO;
 import com.zanxiang.game.module.manage.pojo.dto.GameDTO;
 import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.manage.utils.SignUtil;
 import com.zanxiang.game.module.mybatis.entity.CpSendMsgLog;
 import com.zanxiang.game.module.mybatis.entity.CpSendMsgResult;
 import com.zanxiang.game.module.mybatis.entity.GameSupper;
@@ -25,8 +26,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.support.TransactionTemplate;
 import org.springframework.web.client.RestTemplate;
 
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -40,8 +39,6 @@ import java.util.stream.Collectors;
 @Service
 public class CpSendMsgLogServiceImpl extends ServiceImpl<CpSendMsgLogMapper, CpSendMsgLog> implements ICpSendMsgLogService {
 
-    private static final String SIGN_MD5 = "MD5";
-
     @Autowired
     private TransactionTemplate transactionTemplate;
 
@@ -149,14 +146,13 @@ public class CpSendMsgLogServiceImpl extends ServiceImpl<CpSendMsgLogMapper, CpS
                 .build();
     }
 
-    private CpSendMsgResultDTO cpSendMsgApi(GameSupper gameSupper, String msgId, String serverId, String text,
-                                            List<String> roleIdList) throws Exception {
+    private CpSendMsgResultDTO cpSendMsgApi(GameSupper gameSupper, String msgId, String serverId, String text, List<String> roleIdList) {
         long time = System.currentTimeMillis() / 1000;
         Map<String, Object> param = new HashMap<>(8);
         param.put("msgId", msgId);
         param.put("strRan", msgId);
         param.put("time", time);
-        param.put("sign", this.MD5("key=" + gameSupper.getCpSendMsgKey() + "&msgId=" + msgId + "&strRan=" + msgId + "&time=" + time));
+        param.put("sign", SignUtil.md5("key=" + gameSupper.getCpSendMsgKey() + "&msgId=" + msgId + "&strRan=" + msgId + "&time=" + time));
         param.put("pushType", 1);
         param.put("serverid", serverId);
         param.put("roleIds", roleIdList);
@@ -180,14 +176,4 @@ public class CpSendMsgLogServiceImpl extends ServiceImpl<CpSendMsgLogMapper, CpS
         }
         return JsonUtil.toObj(result, CpSendMsgResultDTO.class);
     }
-
-    private String MD5(String data) throws Exception {
-        java.security.MessageDigest md = MessageDigest.getInstance(SIGN_MD5);
-        byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
-        StringBuilder sb = new StringBuilder();
-        for (byte item : array) {
-            sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
-        }
-        return sb.toString();
-    }
 }

+ 29 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameExtServiceImpl.java

@@ -0,0 +1,29 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.service.IGameExtService;
+import com.zanxiang.game.module.mybatis.entity.GameExt;
+import com.zanxiang.game.module.mybatis.mapper.GameExtMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2022-09-23
+ * @description : 游戏密钥
+ */
+@Slf4j
+@Service
+public class GameExtServiceImpl extends ServiceImpl<GameExtMapper, GameExt> implements IGameExtService {
+
+    @Override
+    public GameExt getByGameId(Long gameId) {
+        return this.getOne(new LambdaQueryWrapper<GameExt>().eq(GameExt::getGameId, gameId));
+    }
+
+    @Override
+    public GameExt getByGameAppId(String appId) {
+        return this.getOne(new LambdaQueryWrapper<GameExt>().eq(GameExt::getAppId, appId));
+    }
+}

+ 88 - 10
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -6,10 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.zanxiang.erp.security.util.SecurityUtil;
 import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
 import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
-import com.zanxiang.game.module.manage.enums.KfRoomMsgOwnerEnum;
-import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
-import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
-import com.zanxiang.game.module.manage.enums.OrderStateEnum;
+import com.zanxiang.game.module.manage.enums.*;
 import com.zanxiang.game.module.manage.pojo.dto.KfAppletMsgDTO;
 import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 import com.zanxiang.game.module.manage.pojo.dto.PayApplicationDTO;
@@ -110,9 +107,18 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
     @Autowired
     private IDistributedLockComponent distributedLockComponent;
 
+    @Autowired
+    private IKfAppletReplyService kfAppletReplyService;
+
+    @Autowired
+    private IUserAppletService userAppletService;
+
     @Override
     public void appletMsg(String postData) {
+        log.error("接收到客服消息, postData : {}", postData);
         KfAppletMsgDTO kfAppletMsgDTO = JsonUtil.toObj(postData, KfAppletMsgDTO.class);
+        log.error("接收到客服消息, kfAppletMsgDTO : {}", JsonUtil.toString(kfAppletMsgDTO));
+        log.error("SessionFrom : {}", kfAppletMsgDTO.getSessionFrom());
         GameApplet gameApplet = gameAppletService.getOne(new LambdaQueryWrapper<GameApplet>()
                 .eq(GameApplet::getGhId, kfAppletMsgDTO.getToUserName()));
         //小游戏信息不存在, 消息不处理
@@ -155,6 +161,8 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
         }
         //客服休息时间, 发送自动回复
         this.systemReplyHandle(gameApplet.getGameId(), kfAppletMsgDTO.getFromUserName(), kfRoom);
+        //小程序自动回复
+        this.appletReplyHandle(gameApplet.getGameId(), kfAppletMsgDTO.getFromUserName(), kfRoom, gameApplet.getAppId());
         //消息报警监测
         this.monitorWordHandle(gameApplet, kfAppletMsgDTO);
         //保存房间消息
@@ -165,6 +173,7 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                     .set(KfSessionUser::getIsWait, Boolean.TRUE)
                     .set(KfSessionUser::getWaitStartTime, LocalDateTime.now())
                     .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
+                    .isNull(KfSessionUser::getSessionFrom)
                     .eq(KfSessionUser::getOpenId, kfAppletMsgDTO.getFromUserName())
                     .eq(KfSessionUser::getGameId, gameApplet.getGameId())
             );
@@ -173,6 +182,72 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
         this.pushMessage(this.transform(kfRoom, gameApplet.getGameId(), kfAppletMsgDTO.getFromUserName(), kfRoomMsg, msgContent));
     }
 
+    private void appletReplyHandle(Long gameId, String openId, KfRoom kfRoom, String appId) {
+        //玩家信息
+        KfSessionUser kfSessionUser = kfSessionUserService.getById(openId, gameId);
+        //来源类型
+        KfSessionFromEnum kfSessionFrom = KfSessionFromEnum.getKfSessionFrom(kfSessionUser.getSessionFrom());
+        if (kfSessionFrom == null) {
+            return;
+        }
+        //获取自动回复配置
+        KfAppletReply kfAppletReply = kfAppletReplyService.getById(gameId);
+        if (kfAppletReply == null) {
+            return;
+        }
+        //判断是否来源与平台
+        if (Objects.equals(kfSessionFrom, KfSessionFromEnum.KF_SESSION_FROM_PLATFORM)) {
+            //回复默认消息
+            if (Strings.isNotBlank(kfAppletReply.getReplyDefault())) {
+                this.sysMsgSend(gameId, openId, kfAppletReply.getReplyDefault(), kfRoom);
+            }
+        }
+        //判断是否来源于广告
+        if (Objects.equals(kfSessionFrom, KfSessionFromEnum.KF_SESSION_FROM_AD)) {
+            //存在文本消息发送
+            if (Strings.isNotBlank(kfAppletReply.getReplyText())) {
+                this.sysMsgSend(gameId, openId, kfAppletReply.getReplyText(), kfRoom);
+            }
+            //存在图片发送
+            if (Strings.isNotBlank(kfAppletReply.getReplyImg())) {
+                this.sysImgMsgSend(gameId, openId, kfAppletReply.getReplyImg(), kfRoom);
+            }
+            //存在链接发送
+            if (Strings.isNotBlank(kfAppletReply.getReplyLink())) {
+                //链接消息参数转换
+                Map<String, String> linkMap = JsonUtil.toMap(kfAppletReply.getReplyLink(), Map.class, String.class);
+                String url = linkMap.get("url");
+                //链接拼接短链id
+                if (Strings.isNotBlank(url)) {
+                    UserApplet userApplet = userAppletService.getOne(new LambdaQueryWrapper<UserApplet>()
+                            .eq(UserApplet::getAppId, appId)
+                            .eq(UserApplet::getOpenId, openId)
+                            .orderByDesc(UserApplet::getCreateTime)
+                            .last("limit 1"));
+                    if (userApplet != null) {
+                        linkMap.put("url", url + "?customer_channel=" + userApplet.getId());
+                    }
+                }
+                this.sysLinkMsgSend(gameId, openId, kfRoom, linkMap);
+            }
+        }
+    }
+
+    private void sysLinkMsgSend(Long gameId, String openId, KfRoom kfRoom, Map<String, String> linkMap) {
+        //发送消息
+        Map<String, Object> msgParamMap = new HashMap<>(3);
+        msgParamMap.put("touser", openId);
+        msgParamMap.put("link", linkMap);
+        msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK.getValue());
+        Tuple2<Long, String> tuple2 = kfWxApiService.sendCustomMessageApi(gameId, msgParamMap);
+        //发送失败
+        if (!Objects.equals(tuple2.getT1(), 0L)) {
+            return;
+        }
+        //保存发送的支付链接消息
+        kfRoomMsgService.save(this.transform(openId, gameId, kfRoom, KfRoomMsgTypeEnum.KF_MSG_TYPE_LINK, JsonUtil.toString(msgParamMap)));
+    }
+
     private void monitorWordHandle(GameApplet gameApplet, KfAppletMsgDTO kfAppletMsgDTO) {
         //非文本消息
         if (!Objects.equals(kfAppletMsgDTO.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue())) {
@@ -399,20 +474,22 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
             return;
         }
         //不存在角色信息, 不做更新
-        if (gameUserRole == null) {
+        if (user == null && Strings.isBlank(kfAppletMsgDTO.getSessionFrom())) {
             return;
         }
         //存在, 更新玩家信息
         kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
-                .set(KfSessionUser::getUserId, user.getId())
-                .set(KfSessionUser::getLastRoleId, gameUserRole.getRoleId())
-                .set(KfSessionUser::getLastRoleName, gameUserRole.getRoleName())
-                .set(KfSessionUser::getServerId, gameUserRole.getServerId())
-                .set(KfSessionUser::getServerName, gameUserRole.getServerName())
+                .set(user != null, KfSessionUser::getUserId, user == null ? null : user.getId())
+                .set(gameUserRole != null, KfSessionUser::getLastRoleId, gameUserRole == null ? null : gameUserRole.getRoleId())
+                .set(gameUserRole != null, KfSessionUser::getLastRoleName, gameUserRole == null ? null : gameUserRole.getRoleName())
+                .set(gameUserRole != null, KfSessionUser::getServerId, gameUserRole == null ? null : gameUserRole.getServerId())
+                .set(gameUserRole != null, KfSessionUser::getServerName, gameUserRole == null ? null : gameUserRole.getServerName())
+                .set(Strings.isNotBlank(kfAppletMsgDTO.getSessionFrom()), KfSessionUser::getSessionFrom, kfAppletMsgDTO.getSessionFrom())
                 .set(KfSessionUser::getUpdateTime, LocalDateTime.now())
                 .eq(KfSessionUser::getOpenId, kfAppletMsgDTO.getFromUserName())
                 .eq(KfSessionUser::getGameId, gameApplet.getGameId())
         );
+        distributedLockComponent.unlock(lockKey);
     }
 
     private KfSessionUser transform(KfAppletMsgDTO kfAppletMsgDTO, GameApplet gameApplet, User user, GameUserRole gameUserRole) {
@@ -425,6 +502,7 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
                 .lastRoleName(gameUserRole == null ? "神秘人[未创角]" : gameUserRole.getRoleName())
                 .serverId(gameUserRole == null ? null : gameUserRole.getServerId())
                 .serverName(gameUserRole == null ? null : gameUserRole.getServerName())
+                .sessionFrom(kfAppletMsgDTO.getSessionFrom())
                 .createTime(LocalDateTime.now())
                 .updateTime(LocalDateTime.now())
                 .build();

+ 18 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletReplyServiceImpl.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.IKfAppletReplyService;
+import com.zanxiang.game.module.mybatis.entity.KfAppletReply;
+import com.zanxiang.game.module.mybatis.mapper.KfAppletReplyMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-30
+ * @description : 小程序自动回复
+ */
+@Slf4j
+@Service
+public class KfAppletReplyServiceImpl extends ServiceImpl<KfAppletReplyMapper, KfAppletReply> implements IKfAppletReplyService {
+}

+ 103 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/UserAppletServiceImpl.java

@@ -0,0 +1,103 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.base.pojo.enums.DeleteEnum;
+import com.zanxiang.game.module.manage.pojo.params.UserAppletSubmitParam;
+import com.zanxiang.game.module.manage.service.IGameAppletService;
+import com.zanxiang.game.module.manage.service.IGameExtService;
+import com.zanxiang.game.module.manage.service.IUserAppletService;
+import com.zanxiang.game.module.manage.utils.SignUtil;
+import com.zanxiang.game.module.mybatis.entity.GameApplet;
+import com.zanxiang.game.module.mybatis.entity.GameExt;
+import com.zanxiang.game.module.mybatis.entity.UserApplet;
+import com.zanxiang.game.module.mybatis.mapper.UserAppletMapper;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.URIUtil;
+import com.zanxiang.module.util.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : 投放小程序用户信息
+ */
+@Slf4j
+@Service
+public class UserAppletServiceImpl extends ServiceImpl<UserAppletMapper, UserApplet> implements IUserAppletService {
+
+    @Autowired
+    private IGameAppletService gameAppletService;
+
+    @Autowired
+    private IGameExtService gameExtService;
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    public boolean userAppletSubmit(UserAppletSubmitParam param) {
+        GameApplet gameApplet = gameAppletService.getOne(new LambdaQueryWrapper<GameApplet>()
+                .eq(GameApplet::getAppId, param.getAppId())
+                .last("limit 1"));
+        if (gameApplet == null) {
+            log.error("非法参数, 小程序信息不存在, param : {}", JsonUtil.toString(param));
+            throw new BaseException("非法参数, 小程序信息不存在");
+        }
+        GameExt gameExt = gameExtService.getByGameId(gameApplet.getGameId());
+        this.signCheck(gameExt.getLoginKey(), param);
+        //获取用户小程序 openId
+        String openId = this.getAppletOpenId(param.getCode(), param.getAppId(), gameApplet.getAppSecret());
+        //数据保存且返回结果
+        return super.save(this.transform(param, openId));
+    }
+
+    private UserApplet transform(UserAppletSubmitParam param, String openId) {
+        return UserApplet.builder()
+                .appId(param.getAppId())
+                .openId(openId)
+                .channel(param.getChannel())
+                .isDelete(DeleteEnum.NO.getCode())
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build();
+    }
+
+    private void signCheck(String loginKey, UserAppletSubmitParam param) {
+        String signStr = "loginKey=" + loginKey + "appId=" + param.getAppId()
+                + "code=" + param.getCode() + "signTime=" + param.getSignTime();
+        String mySign = SignUtil.md5(signStr);
+        if (!Objects.equals(mySign.toUpperCase(), param.getSign().toUpperCase())) {
+            log.error("加密验证失败, sign : {}, mySign : {}", param.getSign(), mySign);
+            throw new BaseException("加密标识错误");
+        }
+    }
+
+    private String getAppletOpenId(String code, String appId, String secret) {
+        // 请求微信服务器,使用code获取openid
+        Map<String, String> paramMap = new HashMap<>(4);
+        paramMap.put("appid", appId);
+        paramMap.put("secret", secret);
+        paramMap.put("js_code", code);
+        paramMap.put("grant_type", "authorization_code");
+        // 发送请求
+        String url = URIUtil.fillUrlParams("https://api.weixin.qq.com/sns/jscode2session", paramMap, Boolean.FALSE);
+        String sr = restTemplate.getForObject(url, String.class);
+        // 解析相应内容(转换成json对象)
+        Map<String, String> resultMap = JsonUtil.toMap(sr, Map.class, String.class);
+        if (resultMap == null || Strings.isBlank(resultMap.get("openid"))) {
+            log.error("获取用户小程序/小游戏openId失败, appId : {}, resultMap : {}", appId, JsonUtil.toString(resultMap));
+            throw new BaseException("获取用户小程序/小游戏openId失败");
+        }
+        return resultMap.get("openid");
+    }
+}

+ 37 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/utils/SignUtil.java

@@ -0,0 +1,37 @@
+package com.zanxiang.game.module.manage.utils;
+
+import com.zanxiang.module.util.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * @author : lingfeng
+ * @time : 2022-09-23
+ * @description : 签名算法工具类
+ */
+@Slf4j
+public class SignUtil {
+
+    /**
+     * MD5加密
+     *
+     * @param data 待处理数据
+     * @return MD5结果
+     */
+    public static String md5(String data) {
+        try {
+            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
+            StringBuilder sb = new StringBuilder();
+            for (byte item : array) {
+                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            log.error("MD5加密异常, data : {}", data);
+            throw new BaseException("MD5加密异常");
+        }
+    }
+}

+ 66 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfAppletReply.java

@@ -0,0 +1,66 @@
+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.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-30
+ * @description : 小程序自动回复
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_kf_applet_reply")
+public class KfAppletReply implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 游戏
+     */
+    @TableId(value = "game_id", type = IdType.INPUT)
+    private Long gameId;
+
+    /**
+     * 应用id
+     */
+    private String appId;
+
+    /**
+     * 默认回复
+     */
+    private String replyDefault;
+
+    /**
+     * 文本回复
+     */
+    private String replyText;
+
+    /**
+     * 图片回复
+     */
+    private String replyImg;
+
+    /**
+     * 图文链接回复
+     */
+    private String replyLink;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

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

@@ -66,6 +66,11 @@ public class KfSessionUser implements Serializable {
      */
     private String serverName;
 
+    /**
+     * 消息来源
+     */
+    private String sessionFrom;
+
     /**
      * 创建时间
      */

+ 63 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/UserApplet.java

@@ -0,0 +1,63 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : 投放小程序用户信息
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_user_applet")
+public class UserApplet implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 应用id
+     */
+    private String appId;
+
+    /**
+     * 用户openId
+     */
+    private String openId;
+
+    /**
+     * 渠道参数
+     */
+    private String channel;
+
+    /**
+     * 1 伪删除  0正常
+     */
+    @TableLogic
+    private Integer isDelete;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfAppletReplyMapper.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.KfAppletReply;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-30
+ * @description : ${description}
+ */
+public interface KfAppletReplyMapper extends BaseMapper<KfAppletReply> {
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/UserAppletMapper.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.UserApplet;
+
+/**
+ * @author : lingfeng
+ * @time : 2024-04-29
+ * @description : ${description}
+ */
+public interface UserAppletMapper extends BaseMapper<UserApplet> {
+}

+ 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服务启动成功 <解决导量玩家重回小程序充值userId不正确的问题> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <删除调试日志> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 2 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/handler/GlobalExceptionHandler.java

@@ -46,6 +46,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(BindException.class)
     public ResultVO<?> validatedBindException(BindException e) {
+        log.error(e.getMessage(), e);
         String message = e.getAllErrors().get(0).getDefaultMessage();
         return ResultVO.fail(message);
     }
@@ -55,6 +56,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(MethodArgumentNotValidException.class)
     public ResultVO<?> validExceptionHandler(MethodArgumentNotValidException e) {
+        log.error(e.getMessage(), e);
         String message = e.getBindingResult().getFieldError().getDefaultMessage();
         return ResultVO.fail(message);
     }

+ 2 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameAppletServiceImpl.java

@@ -60,6 +60,7 @@ public class GameAppletServiceImpl extends ServiceImpl<GameAppletMapper, GameApp
 
     @Override
     public String appletMsgCheck(String appId, String signature, String timestamp, String nonce, String echoStr) throws Exception {
+        log.error("appId : {}, signature : {}, timestamp : {}, nonce : {}, echoStr : {}", appId, signature, timestamp, nonce, echoStr);
         GameAppletDTO gameAppletDTO = this.getByAppId(appId);
         GameAppletDTO.MsgConfigBean msgConfigBean = gameAppletDTO.getMsgConfigBean();
         if (msgConfigBean == null) {
@@ -167,6 +168,7 @@ public class GameAppletServiceImpl extends ServiceImpl<GameAppletMapper, GameApp
         GameApplet gameApplet = super.getOne(new LambdaQueryWrapper<GameApplet>()
                 .eq(GameApplet::getAppId, appId));
         if (gameApplet == null) {
+            log.error("参数错误, 游戏小程序信息不存在, appId : {}", appId);
             throw new BaseException("参数错误, 游戏小程序信息不存在");
         }
         return BeanUtil.copy(gameApplet, GameAppletDTO.class);