Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/package' into package

zhangxianyu 1 mēnesi atpakaļ
vecāks
revīzija
6e393704aa
21 mainītis faili ar 725 papildinājumiem un 154 dzēšanām
  1. 69 0
      game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/enums/ChatChannelEnum.java
  2. 1 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java
  3. 49 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/GameUserChatController.java
  4. 45 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/GameUserChatListParam.java
  5. 59 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/GameUserChatVO.java
  6. 24 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameUserChatService.java
  7. 23 7
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/CpServerApiService.java
  8. 167 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameUserChatServiceImpl.java
  9. 40 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/task/GameUserChatClearTask.java
  10. 122 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/GameUserChat.java
  11. 5 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/UserLoginLog.java
  12. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/GameUserChatMapper.java
  13. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  14. 5 0
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CallBackControlParam.java
  15. 9 8
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserLoginLogService.java
  16. 48 35
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameBackLogMediaSdkServiceImpl.java
  17. 0 59
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServiceImpl.java
  18. 3 6
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java
  19. 11 16
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  20. 13 8
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserLoginLogServiceImpl.java
  21. 19 13
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

+ 69 - 0
game-module/game-module-base/src/main/java/com/zanxiang/game/module/base/pojo/enums/ChatChannelEnum.java

@@ -0,0 +1,69 @@
+package com.zanxiang.game.module.base.pojo.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-12
+ * @description : 聊天频道枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum ChatChannelEnum {
+
+    /**
+     * 世界
+     */
+    CHAT_CHANNEL_WORLD("CHAT_CHANNEL_WORLD", "世界"),
+
+    /**
+     * 阵营
+     */
+    CHAT_CHANNEL_FACTION("CHAT_CHANNEL_FACTION", "阵营"),
+
+    /**
+     * 帮派
+     */
+    CHAT_CHANNEL_GUILD("CHAT_CHANNEL_GUILD", "帮派"),
+
+    /**
+     * 队伍
+     */
+    CHAT_CHANNEL_TEAM("CHAT_CHANNEL_TEAM", "队伍"),
+
+    /**
+     * 当前
+     */
+    CHAT_CHANNEL_CURRENT("CHAT_CHANNEL_CURRENT", "当前"),
+
+    /**
+     * 寻师
+     */
+    CHAT_CHANNEL_FIND_TEACHER("CHAT_CHANNEL_FIND_TEACHER", "寻师"),
+
+    /**
+     * 国家
+     */
+    CHAT_CHANNEL_NATION("CHAT_CHANNEL_NATION", "国家"),
+
+    /**
+     * 跨服
+     */
+    CHAT_CHANNEL_CROSS_SERVER("CHAT_CHANNEL_CROSS_SERVER", "跨服"),
+
+    /**
+     * 私聊
+     */
+    CHAT_CHANNEL_PRIVATE("CHAT_CHANNEL_PRIVATE", "私聊");
+
+    /**
+     * 频道
+     */
+    private final String channel;
+
+    /**
+     * 频道名称
+     */
+    private final String name;
+}

+ 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服务启动成功 < (查询渠道接口新增字段修复问题01 ・・)ノ(._.`) \n" +
+        System.out.println("赞象Manage服务启动成功 < (游戏聊天记录功能上线-04 ・・)ノ(._.`) \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +

+ 49 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/controller/GameUserChatController.java

@@ -0,0 +1,49 @@
+package com.zanxiang.game.module.manage.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.zanxiang.erp.security.annotation.PreAuthorize;
+import com.zanxiang.game.module.manage.pojo.params.GameUserChatListParam;
+import com.zanxiang.game.module.manage.pojo.vo.GameUserChatVO;
+import com.zanxiang.game.module.manage.service.IGameUserChatService;
+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.*;
+
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-13
+ * @description :
+ */
+@Api(tags = {"游戏聊天记录相关接口"})
+@RestController
+@RequestMapping("/game/chat")
+@Slf4j
+public class GameUserChatController {
+
+    @Autowired
+    private IGameUserChatService gameUserChatService;
+
+    @ApiOperation(value = "查询有聊天记录的游戏")
+    @GetMapping(value = "/game/list")
+    @PreAuthorize(permissionKey = "manage:gameChat:gameList")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Map.class)})
+    public ResultVO<Map<Long, String>> listOfPage() {
+        return ResultVO.ok(gameUserChatService.chatGameMap());
+    }
+
+    @ApiOperation(value = "查询游戏聊天记录")
+    @PostMapping(value = "/list")
+    @PreAuthorize(permissionKey = "manage:gameChat:chatList")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = GameUserChatVO.class)})
+    public ResultVO<IPage<GameUserChatVO>> listOfPage(@Validated @RequestBody GameUserChatListParam param) {
+        return ResultVO.ok(gameUserChatService.listOfPage(param));
+    }
+}

+ 45 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/params/GameUserChatListParam.java

@@ -0,0 +1,45 @@
+package com.zanxiang.game.module.manage.pojo.params;
+
+import com.zanxiang.game.module.mybatis.entity.GameUserChat;
+import com.zanxiang.module.web.pojo.BaseListDTO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDate;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-13
+ * @description : 游戏角色聊天记录
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GameUserChatListParam extends BaseListDTO<GameUserChat> {
+
+    /**
+     * 超父游戏id
+     */
+    @NotNull(message = "超父游戏id不可为空")
+    private Long supperGameId;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 聊天开始日期
+     */
+    private LocalDate chatStart;
+
+    /**
+     * 聊天结束日期
+     */
+    private LocalDate chatEnd;
+}

+ 59 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/pojo/vo/GameUserChatVO.java

@@ -0,0 +1,59 @@
+package com.zanxiang.game.module.manage.pojo.vo;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-12
+ * @description : 聊天记录
+ */
+@Data
+public class GameUserChatVO {
+
+    /**
+     * 主键id
+     */
+    private Long id;
+
+    /**
+     * 游戏名称
+     */
+    private String gameName;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 区服名称
+     */
+    private String serverName;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 公会名称
+     */
+    private String guildName;
+
+    /**
+     * 聊天内容
+     */
+    private String content;
+
+    /**
+     * 聊天时间
+     */
+    private LocalDateTime chatTime;
+}

+ 24 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameUserChatService.java

@@ -0,0 +1,24 @@
+package com.zanxiang.game.module.manage.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.manage.pojo.params.ChatSubmitParam;
+import com.zanxiang.game.module.manage.pojo.params.GameUserChatListParam;
+import com.zanxiang.game.module.manage.pojo.vo.GameUserChatVO;
+import com.zanxiang.game.module.mybatis.entity.GameUserChat;
+
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-12
+ * @description :
+ */
+public interface IGameUserChatService extends IService<GameUserChat> {
+
+    Map<Long, String> chatGameMap();
+
+    IPage<GameUserChatVO> listOfPage(GameUserChatListParam param);
+
+    void addGameUserChat(ChatSubmitParam param);
+}

+ 23 - 7
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/CpServerApiService.java

@@ -6,6 +6,7 @@ import com.zanxiang.game.module.manage.pojo.params.ChatSubmitParam;
 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.IGameUserChatService;
 import com.zanxiang.game.module.manage.service.IListenCallService;
 import com.zanxiang.game.module.manage.utils.SignUtil;
 import com.zanxiang.game.module.mybatis.entity.GameServer;
@@ -41,6 +42,9 @@ public class CpServerApiService {
     @Autowired
     private IListenCallService listenCallService;
 
+    @Autowired
+    private IGameUserChatService gameUserChatService;
+
     @Autowired
     private RestTemplate restTemplate;
 
@@ -50,8 +54,25 @@ public class CpServerApiService {
         if (gameSupper == null) {
             throw new BaseException("参数错误");
         }
+        //签名验证
         this.signCheck(gameSupper.getCpServerKey(), param.getGameId(), param.getServerId(),
                 param.getSignTime(), param.getSign());
+        //调武哥接口, 监测聊天通知
+        try {
+            this.chatContentCheck(gameSupper, param);
+        } catch (Exception e) {
+            log.error("调武哥接口通知消息异常, chatContentMap : {}, e : {}", JsonUtil.toString(param), e.getMessage(), e);
+        }
+        //保存聊天数据到数据库
+        try {
+            gameUserChatService.addGameUserChat(param);
+        } catch (Exception e) {
+            log.error("保存聊天数据到数据库异常, param : {}, e : {}", JsonUtil.toString(param), e.getMessage(), e);
+        }
+        return Boolean.TRUE;
+    }
+
+    private void chatContentCheck(GameSupper gameSupper, ChatSubmitParam param) {
         //查询区服信息
         GameServer gameServer = gameServerService.getOne(new LambdaQueryWrapper<GameServer>()
                 .eq(GameServer::getGameId, param.getGameId())
@@ -66,12 +87,7 @@ public class CpServerApiService {
         Map<String, Object> paramMap = new HashMap<>(1);
         paramMap.put("data", chatContentMap);
         //调武哥接口通知消息
-        try {
-            restTemplate.postForObject("http://47.99.157.216:9000/game/roleChat", paramMap, Object.class);
-        } catch (Exception e) {
-            log.error("调武哥接口通知消息异常, chatContentMap : {}, e : {}", JsonUtil.toString(paramMap), e.getMessage());
-        }
-        return Boolean.TRUE;
+        restTemplate.postForObject("http://47.99.157.216:9000/game/roleChat", paramMap, Object.class);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -101,7 +117,7 @@ public class CpServerApiService {
         //区服添加或者更新
         gameServerService.saveOrUpdate(gameServer);
         //钉钉通知
-        String content = LocalDateTime.now().toString() + "游戏开服通知\n游戏名称 : " + gameSupper.getName()
+        String content = LocalDateTime.now() + "游戏开服通知\n游戏名称 : " + gameSupper.getName()
                 + "\n区服名称:" + param.getServerName() + "\n开服时间:" + param.getStartTime().toString();
         listenCallService.sendDingTalkMsg(param.getGameId(), Boolean.TRUE, content);
         return Boolean.TRUE;

+ 167 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameUserChatServiceImpl.java

@@ -0,0 +1,167 @@
+package com.zanxiang.game.module.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.manage.pojo.params.ChatSubmitParam;
+import com.zanxiang.game.module.manage.pojo.params.GameUserChatListParam;
+import com.zanxiang.game.module.manage.pojo.vo.GameUserChatVO;
+import com.zanxiang.game.module.manage.service.IGameService;
+import com.zanxiang.game.module.manage.service.IGameSupperService;
+import com.zanxiang.game.module.manage.service.IGameUserChatService;
+import com.zanxiang.game.module.manage.service.IGameUserRoleService;
+import com.zanxiang.game.module.mybatis.entity.Game;
+import com.zanxiang.game.module.mybatis.entity.GameSupper;
+import com.zanxiang.game.module.mybatis.entity.GameUserChat;
+import com.zanxiang.game.module.mybatis.entity.GameUserRole;
+import com.zanxiang.game.module.mybatis.mapper.GameUserChatMapper;
+import com.zanxiang.module.util.DateUtil;
+import com.zanxiang.module.util.JsonUtil;
+import com.zanxiang.module.util.bean.BeanUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-12
+ * @description : 角色聊天记录
+ */
+@Slf4j
+@Service
+public class GameUserChatServiceImpl extends ServiceImpl<GameUserChatMapper, GameUserChat> implements IGameUserChatService {
+
+    @Autowired
+    private IGameService gameService;
+
+    @Autowired
+    private IGameSupperService gameSupperService;
+
+    @Autowired
+    private IGameUserRoleService gameUserRoleService;
+
+    @Override
+    public Map<Long, String> chatGameMap() {
+        return Collections.singletonMap(12L, "仙剑");
+    }
+
+    @Override
+    public IPage<GameUserChatVO> listOfPage(GameUserChatListParam param) {
+        GameSupper gameSupper = gameSupperService.getById(param.getSupperGameId());
+        return super.page(param.toPage(), new LambdaQueryWrapper<GameUserChat>()
+                .eq(GameUserChat::getSupperGameId, param.getSupperGameId())
+                .eq(Strings.isNotBlank(param.getRoleId()), GameUserChat::getRoleId, param.getRoleId())
+                .like(Strings.isNotBlank(param.getRoleName()), GameUserChat::getRoleName, param.getRoleName())
+                .ge(param.getChatStart() != null, GameUserChat::getChatTime,
+                        param.getChatStart() == null ? null : LocalDateTime.of(param.getChatStart(), LocalTime.MIN))
+                .le(param.getChatEnd() != null, GameUserChat::getChatTime,
+                        param.getChatEnd() == null ? null : LocalDateTime.of(param.getChatEnd(), LocalTime.MAX))
+                .orderByDesc(GameUserChat::getCreateTime)
+        ).convert(c -> this.toVO(gameSupper, c));
+    }
+
+    private GameUserChatVO toVO(GameSupper gameSupper, GameUserChat gameUserChat) {
+        if (gameUserChat == null) {
+            return null;
+        }
+        GameUserChatVO vo = BeanUtil.copy(gameUserChat, GameUserChatVO.class);
+        if (gameSupper != null) {
+            vo.setGameName(gameSupper.getName());
+        }
+        return vo;
+    }
+
+    @Override
+    public void addGameUserChat(ChatSubmitParam param) {
+        List<Long> gameIdList = getGameIdBySuperGameId(param.getGameId());
+        if (CollectionUtils.isEmpty(gameIdList)) {
+            return;
+        }
+        //构造聊天记录对象
+        GameUserChat chat = this.transform(param);
+        //补充玩家公会信息, 区服信息
+        this.processRoleInfo(gameIdList, chat.getRoleId(), chat::setGuildId, chat::setGuildName,
+                role -> {
+                    chat.setServerName(role.getServerName());
+                    if (Strings.isBlank(chat.getRoleName())) {
+                        chat.setRoleName(role.getRoleName());
+                    }
+                });
+        //补充被私聊玩家公会信息, 区服信息
+        this.processRoleInfo(gameIdList, chat.getChatRoleId(), chat::setChatRoleGuildId, chat::setChatRoleGuildName,
+                role -> {
+                    chat.setChatRoleName(role.getRoleName());
+                    chat.setChatRoleServerName(role.getServerName());
+                    if (Strings.isBlank(chat.getServerId())) {
+                        chat.setChatRoleServerId(role.getServerId());
+                    }
+                });
+        //保存聊天记录
+        super.save(chat);
+    }
+
+    private void processRoleInfo(List<Long> gameIds, String roleId, Consumer<String> guildIdSetter,
+                                 Consumer<String> guildNameSetter, Consumer<GameUserRole> serverInfoSetter) {
+        if (Strings.isBlank(roleId)) {
+            return;
+        }
+        Optional.ofNullable(this.getGameUserRole(gameIds, roleId))
+                .ifPresent(gameUserRole -> {
+                    if (Strings.isNotBlank(gameUserRole.getExtra())) {
+                        Optional.ofNullable(JsonUtil.toMap(gameUserRole.getExtra(), Map.class, String.class))
+                                .ifPresent(extraMap -> {
+                                    Optional.ofNullable(extraMap.get("countryId"))
+                                            .filter(Strings::isNotBlank)
+                                            .ifPresent(guildIdSetter);
+                                    Optional.ofNullable(extraMap.get("country"))
+                                            .filter(Strings::isNotBlank)
+                                            .ifPresent(guildNameSetter);
+                                });
+                    }
+                    serverInfoSetter.accept(gameUserRole);
+                });
+    }
+
+    private List<Long> getGameIdBySuperGameId(Long superGameId) {
+        return gameService.list(new LambdaQueryWrapper<Game>()
+                .select(Game::getId)
+                .eq(Game::getSuperGameId, superGameId)
+        ).stream().map(Game::getId).collect(Collectors.toList());
+    }
+
+    private GameUserRole getGameUserRole(List<Long> gameIdList, String roleId) {
+        return gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
+                .eq(GameUserRole::getRoleId, roleId)
+                .in(GameUserRole::getGameId, gameIdList)
+                .orderByDesc(GameUserRole::getUpdateTime)
+                .last("LIMIT 1"));
+    }
+
+    private GameUserChat transform(ChatSubmitParam param) {
+        Map<String, Object> chatContentMap = param.getChatContentMap();
+        return GameUserChat.builder()
+                .supperGameId(param.getGameId())
+                .serverId(param.getServerId())
+                .roleId(chatContentMap.containsKey("role_id") ? chatContentMap.get("role_id").toString() : null)
+                .roleName(chatContentMap.containsKey("role_name") ? chatContentMap.get("role_name").toString() : null)
+                .chatTime(chatContentMap.containsKey("chatTime") ? DateUtil.parseLocalDateTime(chatContentMap.get("chatTime").toString()) : null)
+                .content(chatContentMap.containsKey("content") ? chatContentMap.get("content").toString() : null)
+                .channel(chatContentMap.containsKey("channel") ? chatContentMap.get("channel").toString() : null)
+                .chatRoleId(chatContentMap.containsKey("chat_role_id") ? chatContentMap.get("chat_role_id").toString() : null)
+                .chatRoleServerId(chatContentMap.containsKey("chat_role_server_id") ? chatContentMap.get("chat_role_server_id").toString() : null)
+                .sourceData(JsonUtil.toString(param))
+                .createTime(LocalDateTime.now())
+                .build();
+    }
+}

+ 40 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/task/GameUserChatClearTask.java

@@ -0,0 +1,40 @@
+package com.zanxiang.game.module.manage.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zanxiang.game.module.manage.service.IGameUserChatService;
+import com.zanxiang.game.module.mybatis.entity.GameUserChat;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2025-03-13
+ * @description : 聊天记录定时删除任务
+ */
+@Slf4j
+@Component
+@RefreshScope
+public class GameUserChatClearTask {
+
+    @Autowired
+    private IGameUserChatService gameUserChatService;
+
+    /**
+     * 每天凌晨2点清理一次7天前的聊天记录
+     */
+    @Scheduled(cron = "0 0 2 * * ?")
+    public void execute() {
+        try {
+            gameUserChatService.remove(new LambdaQueryWrapper<GameUserChat>()
+                    .lt(GameUserChat::getChatTime, LocalDateTime.now().minusDays(7))
+            );
+        } catch (Exception e) {
+            log.error("每天凌晨2点清理一次7天前的聊天记录异常, e : {}", e.getMessage(), e);
+        }
+    }
+}

+ 122 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/GameUserChat.java

@@ -0,0 +1,122 @@
+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 : 2025-03-12
+ * @description : 游戏玩家聊天记录
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_game_user_chat")
+public class GameUserChat implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 超父游戏id
+     */
+    private Long supperGameId;
+
+    /**
+     * 区服id
+     */
+    private String serverId;
+
+    /**
+     * 区服名称
+     */
+    private String serverName;
+
+    /**
+     * 角色id
+     */
+    private String roleId;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 公会id
+     */
+    private String guildId;
+
+    /**
+     * 公会名称
+     */
+    private String guildName;
+
+    /**
+     * 聊天时间
+     */
+    private LocalDateTime chatTime;
+
+    /**
+     * 聊天内容
+     */
+    private String content;
+
+    /**
+     * 聊天频道
+     */
+    private String channel;
+
+    /**
+     * 私聊角色id
+     */
+    private String chatRoleId;
+
+    /**
+     * 私聊角色名称
+     */
+    private String chatRoleName;
+
+    /**
+     * 私聊角色区服id
+     */
+    private String chatRoleServerId;
+
+    /**
+     * 私聊角色区服名称
+     */
+    private String chatRoleServerName;
+
+    /**
+     * 私聊角色公会id
+     */
+    private String chatRoleGuildId;
+
+    /**
+     * 私聊角色公会名称
+     */
+    private String chatRoleGuildName;
+
+    /**
+     * 源数据
+     */
+    private String sourceData;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+}

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

@@ -39,6 +39,11 @@ public class UserLoginLog implements Serializable {
      */
     private Long gameId;
 
+    /**
+     * 区服id
+     */
+    private String serverId;
+
     /**
      * 角色id
      */

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

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

@@ -25,7 +25,7 @@ public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <媒体SDK玩家沉默唤起回传排除角色限制> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <登录日志优化> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 5 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/CallBackControlParam.java

@@ -33,4 +33,9 @@ public class CallBackControlParam {
      * 角色等级
      */
     private Long level;
+
+    /**
+     * 是否米大师成功回调
+     */
+    private Boolean mipeiSuces;
 }

+ 9 - 8
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserLoginLogService.java

@@ -2,6 +2,8 @@ package com.zanxiang.game.module.sdk.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zanxiang.game.module.mybatis.entity.UserLoginLog;
+import com.zanxiang.game.module.sdk.enums.LoginTypeEnum;
+import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 
 /**
@@ -14,20 +16,19 @@ public interface IUserLoginLogService extends IService<UserLoginLog> {
     /**
      * 创建角色登录日志
      *
-     * @param userData 用户
-     * @param type     类型
-     * @param roleId   角色id
-     * @param roleName 角色名字
+     * @param userData      用户
+     * @param param         上报参数
+     * @param loginTypeEnum 登录类型
      * @return {@link Boolean}
      */
-    Boolean createRoleLoginLog(UserData userData, String roleId, String roleName, Integer type);
+    Boolean createRoleLoginLog(UserData userData, GameUserRoleUpdateParam param, LoginTypeEnum loginTypeEnum);
 
     /**
      * 创建用户登录日志
      *
-     * @param type     类型
-     * @param userData 用户数据
+     * @param userData      用户数据
+     * @param loginTypeEnum 登录类型
      * @return {@link Boolean}
      */
-    Boolean createUserLoginLog(UserData userData, Integer type);
+    Boolean createUserLoginLog(UserData userData, LoginTypeEnum loginTypeEnum);
 }

+ 48 - 35
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameBackLogMediaSdkServiceImpl.java

@@ -8,6 +8,7 @@ import com.github.sd4324530.jtuple.Tuple3;
 import com.zanxiang.game.back.base.ServerInfo;
 import com.zanxiang.game.back.base.pojo.dto.TencentMiniGameOrderBackQueryRpcDTO;
 import com.zanxiang.game.back.base.pojo.dto.TtAppOrderBackQueryRpcDTO;
+import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
 import com.zanxiang.game.back.base.pojo.vo.GameBackPolicyRpcVO;
 import com.zanxiang.game.back.base.pojo.vo.OrderBackQueryRpcVO;
 import com.zanxiang.game.back.base.rpc.IGameBackPolicyRpc;
@@ -240,17 +241,19 @@ public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMedia
         //判断是否订单付费回传, 添加回传金额
         if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER) && Strings.isNotBlank(param.getOrderId())) {
             PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(param.getOrderId());
-            if (platformOrderDTO == null || platformOrderDTO.getAmount() == null) {
-                return false;
-            }
             //头条APP媒体回传, 返回的单位是元
             if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())) {
                 resultMap.put("amount", Collections.singletonList(platformOrderDTO.getAmount().intValue()));
             }
             //腾讯小游戏媒体SDK回传, 返回的单位是分, 乘以100
             if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
-                int amount = platformOrderDTO.getAmount().multiply(new BigDecimal(100)).intValue();
-                resultMap.put("amount", Collections.singletonList(amount));
+                if (this.orderPayAndBackCheck(platformOrderDTO, param.getMipeiSuces())) {
+                    resultMap.put("callBack", Boolean.FALSE);
+                    resultMap.put("backMsg", "测试账号, 订单未支付或者已回传");
+                } else {
+                    int amount = platformOrderDTO.getAmount().multiply(new BigDecimal(100)).intValue();
+                    resultMap.put("amount", Collections.singletonList(amount));
+                }
             }
         }
         //返回结果
@@ -266,28 +269,48 @@ public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMedia
         }
         //查询游戏
         Game game = gameService.getById(user.getGameId());
-        //非买量, 安卓APP, 不回传
+        //自然量安卓APP, 不回传
         if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())) {
-            resultMap.put("backMsg", "玩家属于自然量或者被分享用户, 不回传");
+            resultMap.put("backMsg", "自然量安卓APP, 不执行回传");
             return Tuple2.with(Boolean.FALSE, null);
         }
-        //非买量, 新手引导无法回传
+        //自然量微信小游戏, 新手引导无法回传, 无法定义新手引导回传等级
         if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH)) {
-            resultMap.put("backMsg", "非买量, 新手引导回传无法判定, 不回传");
+            resultMap.put("backMsg", "自然量微信小游戏, 新手引导回传无法判定, 不回传");
             return Tuple2.with(Boolean.FALSE, null);
         }
-        //非买量, 订单回传, 不继续判断, 直接回传
+        //自然量微信小游戏, 全量回传
         if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
-            resultMap.put("callBack", Boolean.TRUE);
-            resultMap.put("backMsg", "微信小游戏自然量订单全量回传");
             PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(param.getOrderId());
-            resultMap.put("amount", Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100));
+            if (this.orderPayAndBackCheck(platformOrderDTO, param.getMipeiSuces())) {
+                resultMap.put("callBack", Boolean.FALSE);
+                resultMap.put("backMsg", "微信小游戏自然量订单未支付或者已回传");
+            } else {
+                resultMap.put("callBack", Boolean.TRUE);
+                resultMap.put("backMsg", "微信小游戏自然量订单全量回传");
+                resultMap.put("amount", Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100));
+            }
             return Tuple2.with(Boolean.FALSE, null);
         }
         //其他回传, 还是走判断
         return Tuple2.with(Boolean.TRUE, null);
     }
 
+    private boolean orderPayAndBackCheck(PlatformOrderDTO platformOrderDTO, Boolean miPaySuccess) {
+        //未上线新版本, 直接不检测, 或者是米大师的成功回调, 直接信任
+        if (miPaySuccess == null || miPaySuccess) {
+            return false;
+        }
+        //订单是否需要回传, 不需要回传直接返回true
+        return !Objects.equals(platformOrderDTO.getStatus(), OrderStatusEnum.SUCCESS_PAY.getValue())
+                || super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
+                .eq(GameBackLogMediaSdk::getGameId, platformOrderDTO.getGameId())
+                .eq(GameBackLogMediaSdk::getUserId, platformOrderDTO.getUserId())
+                .eq(GameBackLogMediaSdk::getOrderId, platformOrderDTO.getOrderId())
+                .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER.getValue())
+        ) > 0;
+    }
+
     private void checkCallBack(User user, Agent agent, Map<String, Object> resultMap, CallBackControlParam param) {
         //玩家id
         Long userId = user.getId();
@@ -330,7 +353,8 @@ public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMedia
                 resultMap.put("backMsg", tuple2.second);
                 break;
             case CALL_BACK_PAY_ORDER:
-                Tuple3<Boolean, List<Long>, String> tuple3 = this.callBackOrderCheck(param.getOrderId(), user, agent);
+                Tuple3<Boolean, List<Long>, String> tuple3 = this.callBackOrderCheck(param.getOrderId(),
+                        param.getMipeiSuces(), user, agent);
                 resultMap.put("callBack", tuple3.first);
                 resultMap.put("amount", tuple3.second);
                 resultMap.put("backMsg", tuple3.third);
@@ -483,7 +507,7 @@ public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMedia
         return Tuple2.with(Boolean.TRUE, "执行新手引导回传");
     }
 
-    private Tuple3<Boolean, List<Long>, String> callBackOrderCheck(String orderId, User user, Agent agent) {
+    private Tuple3<Boolean, List<Long>, String> callBackOrderCheck(String orderId, Boolean miPaySuccess, User user, Agent agent) {
         Boolean doBack = Boolean.FALSE;
         List<Long> amount = null;
         String backMsg = null;
@@ -498,30 +522,19 @@ public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMedia
             doBack = orderBackQueryRpcVO.getDoBack();
             backMsg = orderBackQueryRpcVO.getBackMsg();
         }
-        //腾讯小游戏投腾讯广告
-        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())) {
-            TencentMiniGameOrderBackQueryRpcDTO orderQuery = this.transform(user.getOpenId(), orderId, agent);
-            OrderBackQueryRpcVO orderBackQueryRpcVO = tencentMiniGameBackRpc.orderBackQuery(orderQuery).getData();
-            if (Objects.equals(orderBackQueryRpcVO.getDoBack(), Boolean.TRUE)) {
-                amount = orderBackQueryRpcVO.getBackMoney();
-            }
-            doBack = orderBackQueryRpcVO.getDoBack();
-            backMsg = orderBackQueryRpcVO.getBackMsg();
-            //SDK接入后支付会被监听(包含米大师支付和客服支付), 漏单修正, 直接更改结果为回传
-            if (Objects.equals(orderBackQueryRpcVO.getDoBack(), Boolean.FALSE)) {
-                PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(orderId);
+        //微信小游戏投放腾讯广告或巨量广告 (坑逼腾讯要其他渠道的订单数据)
+        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())
+                || Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
+            PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(orderId);
+            if (this.orderPayAndBackCheck(platformOrderDTO, miPaySuccess)) {
+                doBack = Boolean.FALSE;
+                backMsg = "微信小游戏投放腾讯广告或巨量广告, 订单未支付或已回传";
+            } else {
                 doBack = Boolean.TRUE;
                 amount = Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100);
-                backMsg = "回传漏单, 直接修正为原订单金额回传";
+                backMsg = "微信小游戏投放腾讯广告或巨量广告, 订单全量满金额回传";
             }
         }
-        //微信小游戏投巨量广告 (坑逼腾讯要其他渠道的订单数据)
-        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
-            PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(orderId);
-            doBack = Boolean.TRUE;
-            amount = Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100);
-            backMsg = "微信小游戏投巨量广告, 订单回传给腾讯媒体SDK";
-        }
         return Tuple3.with(doBack, amount, Strings.isBlank(backMsg) ? "未知的渠道投放类型" : backMsg);
     }
 

+ 0 - 59
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameServiceImpl.java

@@ -1,20 +1,13 @@
 package com.zanxiang.game.module.sdk.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.github.sd4324530.jtuple.Tuple2;
-import com.zanxiang.game.module.mybatis.entity.Agent;
 import com.zanxiang.game.module.mybatis.entity.Game;
-import com.zanxiang.game.module.mybatis.entity.User;
 import com.zanxiang.game.module.mybatis.mapper.GameMapper;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
-import com.zanxiang.game.module.sdk.service.IAgentService;
 import com.zanxiang.game.module.sdk.service.IGameService;
-import com.zanxiang.game.module.sdk.service.IUserEventService;
-import com.zanxiang.game.module.sdk.service.IUserService;
 import com.zanxiang.module.util.JsonUtil;
 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 java.util.Collections;
@@ -29,15 +22,6 @@ import java.util.Map;
 @Service
 public class GameServiceImpl extends ServiceImpl<GameMapper, Game> implements IGameService {
 
-    @Autowired
-    private IUserService userService;
-
-    @Autowired
-    private IAgentService agentService;
-
-    @Autowired
-    private IUserEventService userEventService;
-
     @Override
     public Map<String, Object> getAdSdkConfig(UserData userData) {
         Game game = super.getById(userData.getGameId());
@@ -45,48 +29,5 @@ public class GameServiceImpl extends ServiceImpl<GameMapper, Game> implements IG
             return Collections.singletonMap("adSdk", 0);
         }
         return JsonUtil.toMap(game.getAdSdkConfig(), Map.class, Object.class);
-//        //判断是否为APP, APP直接返回配置
-//        if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())) {
-//            return JsonUtil.toMap(game.getAdSdkConfig(), Map.class, Object.class);
-//        }
-//        //查询用户信息以及投放渠道
-//        Tuple2<User, Agent> userAndAgent = this.getUserAndAgent(userData.getUserId());
-//        User user = userAndAgent.first;
-//        Agent agent = userAndAgent.second;
-//        //渠道判断, 目前只有投腾讯才返回
-//        if (agent != null) {
-//            //腾讯广告
-//            if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())) {
-//                return JsonUtil.toMap(game.getAdSdkConfig(), Map.class, Object.class);
-//            }
-//            //头条广告
-//            if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
-//                return Collections.singletonMap("adSdk", 0);
-//            }
-//        }
-//        //用户判断, 是否调试阶段和测试用户
-//        if (user != null) {
-//            if (Objects.equals(game.getStatus(), 2) || userEventService.count(new LambdaQueryWrapper<UserEvent>()
-//                    .eq(UserEvent::getGameId, user.getGameId())
-//                    .and(qw -> qw.eq(UserEvent::getUsername, user.getUsername())
-//                            .or().eq(UserEvent::getUsername, user.getOpenId())
-//                            .or().eq(UserEvent::getMobile, user.getMobile()))
-//            ) > 0) {
-//                return JsonUtil.toMap(game.getAdSdkConfig(), Map.class, Object.class);
-//            }
-//        }
-//        //默认返回不初始化
-//        return Collections.singletonMap("adSdk", 0);
-    }
-
-    private Tuple2<User, Agent> getUserAndAgent(Long userId) {
-        if (userId == null) {
-            return Tuple2.with(null, null);
-        }
-        User user = userService.getById(userId);
-        if (user == null) {
-            return Tuple2.with(null, null);
-        }
-        return Tuple2.with(user, agentService.getById(user.getAgentId()));
     }
 }

+ 3 - 6
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java

@@ -136,8 +136,7 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
             //新建游戏角色
             this.gameRoleCreate(param, userData);
             //插入用户登录记录
-            return userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
-                    LoginTypeEnum.LOGIN_IN.getLoginType());
+            return userLoginLogService.createRoleLoginLog(userData, param, LoginTypeEnum.LOGIN_IN);
         }
         //判断角色是否存在
         GameUserRole gameUserRole = super.getOne(new LambdaQueryWrapper<GameUserRole>()
@@ -154,13 +153,11 @@ public class GameUserRoleServiceImpl extends ServiceImpl<GameUserRoleMapper, Gam
         }
         //进入游戏, 插入用户登录记录
         if (Objects.equals(dataType, DataTypeEnum.TYPE_ENTER_GAME.getDateType())) {
-            userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
-                    LoginTypeEnum.LOGIN_IN.getLoginType());
+            userLoginLogService.createRoleLoginLog(userData, param, LoginTypeEnum.LOGIN_IN);
         }
         //退出游戏, 插入用户退出记录
         if (Objects.equals(dataType, DataTypeEnum.TYPE_EXIT_GAME.getDateType())) {
-            userLoginLogService.createRoleLoginLog(userData, param.getRoleId(), param.getRoleName(),
-                    LoginTypeEnum.LOGIN_OUT.getLoginType());
+            userLoginLogService.createRoleLoginLog(userData, param, LoginTypeEnum.LOGIN_OUT);
         }
         return Boolean.TRUE;
     }

+ 11 - 16
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java

@@ -147,8 +147,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
             //渠道更新和回传判断
             agentService.userAgentUpdate(user, userData);
             //返回登录信息
-            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
-            userLoginVO.setRegUser(Boolean.FALSE);
+            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.FALSE);
             userLoginVO.setAppletShellSwitch(gameAppletShellService.getUserShellSwitch(user, Boolean.FALSE, request));
             userLoginVO.setAccessToken(wxApiService.getAccessToken(gameAppletDTO.getAppId(), gameAppletDTO.getAppSecret()));
             log.error("返回用户登录信息, userLoginVO : {}", JsonUtil.toString(userLoginVO));
@@ -157,8 +156,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //用户注册
         user = userCreateSave(userData, openId, null, null, openId, sessionKey);
         //返回登录信息
-        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
-        userLoginVO.setRegUser(Boolean.TRUE);
+        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.TRUE);
         userLoginVO.setAppletShellSwitch(gameAppletShellService.getUserShellSwitch(user, Boolean.TRUE, request));
         userLoginVO.setAccessToken(wxApiService.getAccessToken(gameAppletDTO.getAppId(), gameAppletDTO.getAppSecret()));
         log.error("返回用户登录信息, userLoginVO : {}", JsonUtil.toString(userLoginVO));
@@ -205,13 +203,12 @@ public class LoginServiceImpl implements IRegisterLoginService {
             //渠道更新和回传判断
             agentService.userAgentUpdate(user, userData);
             //登录信息
-            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
+            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.FALSE);
             Tuple2<Boolean, Long> tuple2 = this.userGuideCheck(user);
             //导量用户返回原有的用户id
             if (tuple2.getT1()) {
                 userLoginVO.setUserId(tuple2.getT2());
             }
-            userLoginVO.setRegUser(Boolean.FALSE);
             //返回登录信息
             return ResultVO.ok(userLoginVO);
         }
@@ -223,8 +220,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //用户注册
         user = userCreateSave(userData, username, RegisterUtil.cmfPassword(password), null, null, null);
         //返回登录信息
-        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
-        userLoginVO.setRegUser(Boolean.TRUE);
+        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.TRUE);
         return ResultVO.ok(userLoginVO);
     }
 
@@ -253,7 +249,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
             }
             //渠道更新和回传判断
             agentService.userAgentUpdate(user, userData);
-            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
+            UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.FALSE);
             //判断是否导量用户
             Tuple2<Boolean, Long> tuple2 = this.userGuideCheck(user);
             //导量用户返回原有的用户id
@@ -261,15 +257,13 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 userLoginVO.setUserId(tuple2.getT2());
                 log.error("导量用户返回用户信息, userLoginVO : {}", JsonUtil.toString(userLoginVO));
             }
-            userLoginVO.setRegUser(Boolean.FALSE);
             //返回登录信息
             return ResultVO.ok(userLoginVO);
         }
         //用户注册
         user = userCreateSave(userData, mobile, null, mobile, null, null);
         //返回登录信息
-        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData);
-        userLoginVO.setRegUser(Boolean.TRUE);
+        UserLoginVO userLoginVO = this.createUserLoginVO(user, userData, Boolean.TRUE);
         return ResultVO.ok(userLoginVO);
     }
 
@@ -332,7 +326,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //注册信息埋点数据发送到卡夫卡
         kafkaService.eventTrack(KafkaEventTrackEnum.KAFKA_EVENT_TRACK_REG, JsonUtil.toString(user));
         //发送到IP解析任务队列
-        redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE, "REG:" + user.getId()+ ":" + user.getIp());
+        redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE, "REG:" + user.getId() + ":" + user.getIp());
         //返回用户信息
         return user;
     }
@@ -360,7 +354,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 .build();
     }
 
-    private UserLoginVO createUserLoginVO(User user, UserData userData) {
+    private UserLoginVO createUserLoginVO(User user, UserData userData, Boolean regUser) {
         //注册, 补全 userData 中的用户id
         if (userData.getUserId() == null) {
             userData.setUserId(user.getId());
@@ -371,7 +365,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
         //获取token
         String userToken = userTokenService.getUserToken(user.getId(), userData.getDeviceType());
         //插入用户登录记录
-        userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_REG.getLoginType());
+        userLoginLogService.createUserLoginLog(userData, regUser ? LoginTypeEnum.LOGIN_REG : LoginTypeEnum.LOGIN_IN);
         //查询用户实名信息
         UserCard userCard = userCardService.getOne(new LambdaQueryWrapper<UserCard>()
                 .eq(UserCard::getUserId, user.getId())
@@ -392,6 +386,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
                 .cardName(userCard == null ? null : userCard.getShowCardName())
                 .cardId(userCard == null ? null : userCard.getShowCardId())
                 .checkSwitch(gameExt.getCheckSwitch())
+                .regUser(regUser)
                 .build();
         log.error("用户登录返回登录信息, userLoginVO : {}", JsonUtil.toString(userLoginVO));
         return userLoginVO;
@@ -401,7 +396,7 @@ public class LoginServiceImpl implements IRegisterLoginService {
     @Transactional(rollbackFor = Exception.class)
     public Boolean loginOut(UserData userData) {
         //新增用户退出记录
-        return userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_OUT.getLoginType());
+        return userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_OUT);
     }
 
     @Override

+ 13 - 8
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserLoginLogServiceImpl.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.game.module.mybatis.entity.UserLoginLog;
 import com.zanxiang.game.module.mybatis.mapper.UserLoginLogMapper;
 import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
+import com.zanxiang.game.module.sdk.enums.LoginTypeEnum;
+import com.zanxiang.game.module.sdk.pojo.param.GameUserRoleUpdateParam;
 import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.service.IUserLoginLogService;
 import com.zanxiang.game.module.sdk.util.RedisUtil;
@@ -26,39 +28,42 @@ public class UserLoginLogServiceImpl extends ServiceImpl<UserLoginLogMapper, Use
     private RedisUtil<String> redisUtil;
 
     @Override
-    public Boolean createRoleLoginLog(UserData userData, String roleId, String roleName, Integer type) {
+    public Boolean createRoleLoginLog(UserData userData, GameUserRoleUpdateParam param, LoginTypeEnum loginTypeEnum) {
         UserLoginLog loginLog = UserLoginLog.builder()
                 .userId(userData.getUserId())
                 .gameId(userData.getGameId())
-                .roleId(roleId)
-                .roleName(roleName)
+                .serverId(param.getServerId())
+                .roleId(param.getRoleId())
+                .roleName(param.getRoleName())
                 .os(userData.getDeviceSystem())
                 .ip(userData.getIp())
                 .deviceType(userData.getDeviceType())
-                .type(type)
+                .type(loginTypeEnum.getLoginType())
                 .createTime(LocalDateTime.now())
                 .build();
         boolean result = super.save(loginLog);
         if (result) {
-            redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE, "LOGIN:" + loginLog.getId() + ":" + loginLog.getIp());
+            redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE,
+                    "LOGIN:" + loginLog.getId() + ":" + loginLog.getIp());
         }
         return result;
     }
 
     @Override
-    public Boolean createUserLoginLog(UserData userData, Integer type) {
+    public Boolean createUserLoginLog(UserData userData, LoginTypeEnum loginTypeEnum) {
         UserLoginLog loginLog = UserLoginLog.builder()
                 .userId(userData.getUserId())
                 .gameId(userData.getGameId())
                 .os(userData.getDeviceSystem())
                 .ip(userData.getIp())
                 .deviceType(userData.getDeviceType())
-                .type(type)
+                .type(loginTypeEnum.getLoginType())
                 .createTime(LocalDateTime.now())
                 .build();
         boolean result = super.save(loginLog);
         if (result) {
-            redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE, "LOGIN:" + loginLog.getId() + ":" + loginLog.getIp());
+            redisUtil.addToSet(RedisKeyConstant.IP_DATA_ASSAY_QUEUE,
+                    "LOGIN:" + loginLog.getId() + ":" + loginLog.getIp());
         }
         return result;
     }

+ 19 - 13
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

@@ -27,21 +27,19 @@ 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;
-import com.zanxiang.module.util.encryption.Md5Util;
+import com.zanxiang.module.util.encryption.AESUtil;
 import com.zanxiang.module.util.exception.BaseException;
 import com.zanxiang.module.util.pojo.ResultVO;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import reactor.util.function.Tuple2;
 import reactor.util.function.Tuples;
 
 import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -54,15 +52,15 @@ import java.util.stream.Collectors;
 @Service
 public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken> implements IUserTokenService {
 
+    @Value("${iapSdk.tokenSecret:unknown}")
+    private String tokenSecret;
+
     @Autowired
     private RedisUtil<UserToken> redisUtil;
 
     @Autowired
     private WxApiService wxApiService;
 
-    @Autowired
-    private IGameAppletService gameAppletService;
-
     @Autowired
     private IGameService gameService;
 
@@ -209,7 +207,9 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
                 .eq(UserToken::getToken, userData.getToken())
                 .eq(UserToken::getDeviceType, userData.getDeviceType()));
         //token已经更新, 返回失效
-        if (userToken == null) {
+        String token = Optional.ofNullable(userToken).map(UserToken::getToken).orElse(null);
+        LocalDateTime localDateTime = DateUtil.parseLocalDateTime("2025-03-11 18:45:00");
+        if (Strings.isBlank(token) || userToken.getUpdateTime().isBefore(localDateTime)) {
             return Boolean.FALSE;
         }
         //判断是否ip封禁
@@ -243,7 +243,7 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
         boolean result = expireTime > endTime;
         //token未过期, 记录登录日志
         if (result) {
-            userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_IN.getLoginType());
+            userLoginLogService.createUserLoginLog(userData, LoginTypeEnum.LOGIN_IN);
         }
         return result;
     }
@@ -341,10 +341,16 @@ public class UserTokenServiceImpl extends ServiceImpl<UserTokenMapper, UserToken
     }
 
     private String createToken(Long userId, Integer deviceType) {
-        //生成随机数
-        String random = userId + deviceType + UUID.randomUUID().toString() + System.currentTimeMillis();
+        String random = String.join("|",
+                userId.toString(),
+                deviceType.toString(),
+                UUID.randomUUID().toString(),
+                Long.toString(System.nanoTime())
+        );
+        //先进行AES加密
+        String encrypted = AESUtil.encrypt(random, this.tokenSecret);
         //创建token
-        return Md5Util.encrypt32(Md5Util.encrypt32(random));
+        return Base64.getUrlEncoder().withoutPadding().encodeToString(encrypted.getBytes());
     }
 
     private String getUserTokenKey(Long userId, Integer deviceType) {