فهرست منبع

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

zhangxianyu 1 سال پیش
والد
کامیت
cbec85ee80
14فایلهای تغییر یافته به همراه206 افزوده شده و 40 حذف شده
  1. 7 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameAuthRoleService.java
  2. 12 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IKfSystemReplyService.java
  3. 8 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameAuthRoleServiceImpl.java
  4. 30 1
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java
  5. 18 0
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfSystemReplyServiceImpl.java
  6. 17 10
      game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java
  7. 58 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/KfSystemReply.java
  8. 12 0
      game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/KfSystemReplyMapper.java
  9. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java
  10. 7 2
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/constant/RedisKeyConstant.java
  11. 9 11
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameUserRoleServiceImpl.java
  12. 3 5
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/LoginServiceImpl.java
  13. 1 1
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/OrderPayServiceImpl.java
  14. 23 9
      game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserTokenServiceImpl.java

+ 7 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IGameAuthRoleService.java

@@ -17,6 +17,13 @@ import java.util.Map;
  */
 public interface IGameAuthRoleService extends IService<GameAuthRole> {
 
+    /**
+     * 是否是客服
+     *
+     * @return boolean
+     */
+    boolean isCustomer();
+
     /**
      * 获取用户身份验证类型
      *

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

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

+ 8 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/GameAuthRoleServiceImpl.java

@@ -47,6 +47,14 @@ public class GameAuthRoleServiceImpl extends ServiceImpl<GameAuthRoleMapper, Gam
     @Autowired
     private IGameAuthService gameAuthService;
 
+    @Override
+    public boolean isCustomer() {
+        return super.count(new LambdaQueryWrapper<GameAuthRole>()
+                .eq(GameAuthRole::getAuthType, GameAuthEnum.CUSTOMER.getValue())
+                .eq(GameAuthRole::getUserId, SecurityUtil.getUserId())
+        ) > 0;
+    }
+
     @Override
     public Map<Long, String> getUserByAuthType(String authType) {
         List<GameAuthRole> authRoleList = super.list(new LambdaQueryWrapper<GameAuthRole>()

+ 30 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -87,12 +87,14 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
     @Autowired
     private IKfRoomMsgService kfRoomMsgService;
 
+    @Autowired
+    private IKfSystemReplyService kfSystemReplyService;
+
     @Autowired
     private RedisUtil<String> redisUtil;
 
     @Override
     public void appletMsg(String postData) {
-        log.error("接收到SDK转发的小程序消息, postData : {}", postData);
         KfAppletMsgDTO kfAppletMsgDTO = JsonUtil.toObj(postData, KfAppletMsgDTO.class);
         GameApplet gameApplet = gameAppletService.getOne(new LambdaQueryWrapper<GameApplet>()
                 .eq(GameApplet::getGhId, kfAppletMsgDTO.getToUserName()));
@@ -123,6 +125,8 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
         if (Strings.isNotBlank(orderId) && this.orderHandle(orderId, gameApplet.getGameId(), kfAppletMsgDTO, kfRoom, kfRoomMsg)) {
             return;
         }
+        //客服休息时间, 发送自动回复
+        this.systemReplyHandle(gameApplet.getGameId(), kfAppletMsgDTO.getFromUserName(), kfRoom);
         //保存房间消息
         kfRoomMsgService.save(kfRoomMsg);
         //玩家状态更新为待接入状态
@@ -137,6 +141,31 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
         this.pushMessage(this.transform(kfRoom, gameApplet.getGameId(), kfRoomMsg, msgContent));
     }
 
+    private void systemReplyHandle(Long gameId, String openId, KfRoom kfRoom) {
+        //获取自动回复配置
+        KfSystemReply kfSystemReply = kfSystemReplyService.getById(gameId);
+        if (kfSystemReply == null) {
+            return;
+        }
+        //判断当前时间是否在时间内
+        LocalTime nowTime = LocalTime.now();
+        LocalTime startTime = LocalTime.parse(kfSystemReply.getStartTime());
+        LocalTime endTime = LocalTime.parse(kfSystemReply.getEndTime());
+        if (nowTime.isAfter(startTime) && nowTime.isBefore(endTime)) {
+            return;
+        }
+        //休息时间, 发送指定消息
+        Map<String, Object> textMap = new HashMap<>(1);
+        textMap.put("content", kfSystemReply.getSysReply());
+        Map<String, Object> msgParamMap = new HashMap<>(3);
+        msgParamMap.put("touser", openId);
+        msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue());
+        msgParamMap.put("text", textMap);
+        kfWxApiService.sendCustomMessageApi(gameId, msgParamMap);
+        //保存消息
+        kfRoomMsgService.save(this.transform(openId, gameId, kfRoom, JsonUtil.toString(msgParamMap)));
+    }
+
     private boolean orderHandle(String orderId, Long gameId, KfAppletMsgDTO kfAppletMsgDTO, KfRoom kfRoom, KfRoomMsg kfRoomMsg) {
         //判断收到的消息是否为充值消息或者小程序消息
         if (!KfAppletMsgDTO.MSG_CONTENT_PAY.contains(kfAppletMsgDTO.getContent())

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

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

+ 17 - 10
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java

@@ -10,10 +10,7 @@ import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
 import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 import com.zanxiang.game.module.manage.pojo.params.KfWebSocketMsgParam;
-import com.zanxiang.game.module.manage.service.IKfQuickReplyService;
-import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
-import com.zanxiang.game.module.manage.service.IKfRoomService;
-import com.zanxiang.game.module.manage.service.IKfSessionUserService;
+import com.zanxiang.game.module.manage.service.*;
 import com.zanxiang.game.module.manage.service.api.KfWxApiService;
 import com.zanxiang.game.module.manage.utils.FileUtil;
 import com.zanxiang.game.module.mybatis.entity.KfRoom;
@@ -66,6 +63,9 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     @Autowired
     private KfWxApiService wxApiService;
 
+    @Autowired
+    private IGameAuthRoleService gameAuthRoleService;
+
     @Autowired
     private IKfQuickReplyService kfQuickReplyService;
 
@@ -327,6 +327,8 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
                 .gameId(gameId)
                 .roomList(onlineRoomList)
                 .build());
+        //释放锁
+        distributedLockComponent.unlock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + kfRoom.getOpenId());
     }
 
     private void finishRoomList(WebSocketSession session, KfWebSocketMsgParam param) {
@@ -368,15 +370,16 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
     }
 
     private void userConnectJoin(WebSocketSession session, KfWebSocketMsgParam param) {
+        //参数判断
         if (Strings.isBlank(param.getOpenId())) {
             this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
                     "接入玩家参数错误, openId不可为空, param : " + JsonUtil.toString(param)));
             return;
         }
-        //触发玩家接入线程锁
-        if (!distributedLockComponent.doLock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId(),
-                0L, 5L, TimeUnit.MINUTES)) {
-            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家已被其他客服接入"));
+        //判断是否为客服
+        if (!gameAuthRoleService.isCustomer()) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
+                    "非客服人员, 不可接待玩家, param : " + JsonUtil.toString(param)));
             return;
         }
         //玩家信息, 判断玩家是否已经被接入
@@ -385,6 +388,12 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
             this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家已被其他客服接入"));
             return;
         }
+        //触发玩家接入线程锁
+        if (!distributedLockComponent.doLock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId(),
+                0L, 5L, TimeUnit.MINUTES)) {
+            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), "玩家已被其他客服接入"));
+            return;
+        }
         //玩家更新为已接入
         kfSessionUserService.update(new LambdaUpdateWrapper<KfSessionUser>()
                 .set(KfSessionUser::getIsWait, Boolean.FALSE)
@@ -417,8 +426,6 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
                 .gameId(param.getGameId())
                 .waitUserList(waitUserList)
                 .build());
-        //释放锁
-        distributedLockComponent.unlock(RedisKeyConstant.KF_MSG_USER_CONNECT_JOIN + param.getOpenId());
     }
 
     private void kfCreateConnect(WebSocketSession session, KfWebSocketMsgEnum msgTypeEnum, Long gameId) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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