Ver código fonte

新增 UserUtil 同步更新用户信息 (redis)(sse)

Mure 2 semanas atrás
pai
commit
f534d8ecf2

+ 12 - 0
src/main/java/com/backendsys/modules/TestController.java

@@ -8,6 +8,7 @@ import cn.hutool.json.JSONUtil;
 import com.backendsys.aspect.RateLimiting;
 import com.backendsys.aspect.QueuingPoll;
 import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
+import com.backendsys.modules.common.config.security.utils.JwtUtil;
 import com.backendsys.modules.common.utils.Result;
 import com.backendsys.modules.sdk.baidu.yunapp.entity.ExecuteScriptParams;
 import com.backendsys.modules.sdk.baidu.yunapp.service.YunappService;
@@ -30,6 +31,7 @@ import com.tencentcloudapi.common.Credential;
 //import io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionResponse;
 import com.volcengine.service.visual.IVisualService;
 import com.volcengine.service.visual.impl.VisualServiceImpl;
+import io.jsonwebtoken.Claims;
 import jakarta.annotation.PostConstruct;
 import jakarta.servlet.ServletContext;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
@@ -67,6 +69,16 @@ import com.tencentcloudapi.common.exception.TencentCloudSDKException;
 @SuppressWarnings({"rawtypes", "unchecked"})
 public class TestController {
 
+    @Autowired
+    private JwtUtil jwtUtil;
+
+    @GetMapping("/testExtractToken")
+    public Claims extractToken(String token) {
+        Claims claims = jwtUtil.extractAllClaims(token);
+        System.out.println("claims = " + claims);
+        return claims;
+    }
+
 
     @Value("${tencent.facefusion.secret-id}")
     private String SECRET_ID;

+ 64 - 9
src/main/java/com/backendsys/modules/material/service/impl/MaterialUserServiceImpl.java

@@ -3,14 +3,20 @@ package com.backendsys.modules.material.service.impl;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.common.config.redis.utils.RedisUtil;
+import com.backendsys.modules.common.config.security.entity.SecurityUserInfo;
 import com.backendsys.modules.common.config.security.utils.CaptchaUtil;
+import com.backendsys.modules.common.config.security.utils.JwtUtil;
 import com.backendsys.modules.common.config.security.utils.LockStatusUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.material.entity.MaterialAudit;
 import com.backendsys.modules.material.entity.MaterialUser;
 import com.backendsys.modules.material.service.MaterialUserService;
+import com.backendsys.modules.sse.entity.SseResponse;
+import com.backendsys.modules.sse.entity.SseResponseEnum;
+import com.backendsys.modules.sse.utils.SseUtil;
 import com.backendsys.modules.system.dao.SysUserDao;
 import com.backendsys.modules.system.dao.SysUserInfoDao;
 import com.backendsys.modules.system.dao.SysUserRoleDao;
@@ -20,6 +26,7 @@ import com.backendsys.modules.system.service.SysAuthService;
 import com.backendsys.modules.system.service.SysCommonService;
 import com.backendsys.modules.system.service.SysUserIntegralService;
 import com.backendsys.modules.system.service.SysUserService;
+import com.backendsys.modules.system.utils.UserUtil;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
@@ -40,6 +47,11 @@ import java.util.Map;
 @Service
 public class MaterialUserServiceImpl implements MaterialUserService {
 
+    @Autowired
+    private UserUtil userUtil;
+    @Autowired
+    private SseUtil sseUtil;
+
     @Value("${spring.application.name}")
     private String APPLICATION_NAME;
     @Value("${tencent.sms.debug}")
@@ -108,11 +120,11 @@ public class MaterialUserServiceImpl implements MaterialUserService {
     public Map<String, Object> auditMaterialUser(MaterialAudit materialAudit) {
 
         // 判断是否为素材用户 / 用户是否存在
-        LambdaQueryWrapper<SysUserInfo> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SysUserInfo::getUser_id, materialAudit.getUser_id());
-        wrapper.eq(SysUserInfo::getInvite_code, "Material");
-        SysUserInfo user_info_detail = sysUserInfoDao.selectOne(wrapper);
-        if (user_info_detail == null) throw new CustException("用户不存在");
+        SysUserInfo entity = new SysUserInfo();
+        entity.setUser_id(materialAudit.getUser_id());
+        entity.setInvite_code("Material");
+        SysUserInfo userInfo = sysUserService.selectUserInfo(entity);
+        if (userInfo == null) throw new CustException("用户不存在");
 
         // 判断用户是否待审核状态 (-1审核拒绝, 1待审核, 2审核通过)
         if (materialAudit.getUser_id() == SecurityUtil.getUserId()) throw new CustException("不能审核自己");
@@ -123,10 +135,14 @@ public class MaterialUserServiceImpl implements MaterialUserService {
         if (!role_ids.contains(materialAudit.getRole_id())) throw new CustException("必须指定素材用户角色");
 
         // [DB] 更新用户信息-审核状态/用户状态
-        SysUserInfo entity = new SysUserInfo();
-        entity.setAudit_status(materialAudit.getAudit_status());
-        entity.setStatus(materialAudit.getStatus());
-        sysUserInfoDao.update(entity, wrapper);
+        LambdaQueryWrapper<SysUserInfo> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysUserInfo::getUser_id, materialAudit.getUser_id());
+        wrapper.eq(SysUserInfo::getInvite_code, "Material");
+        //
+        userInfo.setAudit_status(materialAudit.getAudit_status());
+        userInfo.setAudit_note("素材管理审核通过");
+        userInfo.setStatus(materialAudit.getStatus());
+        sysUserInfoDao.update(userInfo, wrapper);
 
         // 如果是禁用状态,则清除该用户的登录信息
         if (materialAudit.getStatus() == -1) {
@@ -139,6 +155,45 @@ public class MaterialUserServiceImpl implements MaterialUserService {
         sysUserRoleRelationDao.delete(wrapperRoleRelation);
         sysUserRoleRelationDao.insertBatch(materialAudit.getUser_id(), Arrays.asList(materialAudit.getRole_id()));
 
+        /*
+            Token 解析结果:
+                Claims claims = jwtUtil.extractAllClaims(token);
+                {
+                    "sub": "8fc878c1-0a67-4cd0-a0de-eced643e5546",
+                    "userInfo": {
+                        "id": 1,
+                        "user_id": 1,
+                        "username": "admin",
+                        "last_login_uuid": "8fc878c1-0a67-4cd0-a0de-eced643e5546",
+                        "is_super": -1,
+                        "token_expiration": "2025-08-24 14:55:44",
+                        "target": null,
+                        "roles": [
+                            {
+                                "role_id": 4,
+                                "role_sign": "MATERIAL_ADMIN",
+                                "role_name": "素材库-管理员",
+                                "login_default_page": "/material/materialList"
+                            }
+                        ]
+                    },
+                    "target": "System",
+                    "exp": 1756018544
+                }
+
+
+            Redis Key:backendsys:local:login:token:8fc878c1-0a67-4cd0-a0de-eced643e5546
+            Redis Value:
+            "\"{\\\"token\\\":\\\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4ZmM4NzhjMS0wYTY3LTRjZDAtYTBkZS1lY2VkNjQzZTU1NDYiLCJ1c2VySW5mbyI6eyJpZCI6MSwidXNlcl9pZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsImxhc3RfbG9naW5fdXVpZCI6IjhmYzg3OGMxLTBhNjctNGNkMC1hMGRlLWVjZWQ2NDNlNTU0NiIsImlzX3N1cGVyIjotMSwidG9rZW5fZXhwaXJhdGlvbiI6IjIwMjUtMDgtMjQgMTQ6NTU6NDQiLCJ0YXJnZXQiOm51bGwsInJvbGVzIjpbeyJyb2xlX2lkIjo0LCJyb2xlX3NpZ24iOiJNQVRFUklBTF9BRE1JTiIsInJvbGVfbmFtZSI6Iue0oOadkOW6ky3nrqHnkIblkZgiLCJsb2dpbl9kZWZhdWx0X3BhZ2UiOiIvbWF0ZXJpYWwvbWF0ZXJpYWxMaXN0In1dfSwidGFyZ2V0IjoiU3lzdGVtIiwiZXhwIjoxNzU2MDE4NTQ0fQ.Mz9juWF2sqioaFMAdTJtcoTQONZFfCGDaRuibbVn2Mw\\\",\\\"permission_ids\\\":[\\\"1.1.2\\\",\\\"1.1.3\\\",\\\"1.1.6\\\",\\\"3.2.3.3\\\",\\\"3.5.1\\\",\\\"20\\\",\\\"20.1\\\",\\\"20.1.1\\\",\\\"20.1.2\\\",\\\"20.1.3\\\",\\\"20.1.4\\\",\\\"20.1.5\\\",\\\"20.2\\\",\\\"20.3\\\",\\\"20.4\\\",\\\"20.4.1\\\"]}\""
+
+
+            // SecurityUserInfo(id=1, user_id=1, username=admin, last_login_uuid=8fc878c1-0a67-4cd0-a0de-eced643e5546, is_super=-1, token_expiration=2025-08-24 14:55:44, target=null, roles=[{role_id=4, role_sign=MATERIAL_ADMIN, role_name=素材库-管理员, login_default_page=/material/materialList}])
+
+         */
+        // 实时更新:用户信息/角色/权限 (发送SSE)
+        userUtil.syncUpdateUserCatch(userInfo);
+        sseUtil.send(userInfo.getUser_id(), new SseResponse(SseResponseEnum.SYSTEM_USER_UPDATE, userInfo).toJsonStr());
+
         return Map.of("user_id", materialAudit.getUser_id());
     }
 

+ 2 - 0
src/main/java/com/backendsys/modules/sse/entity/SseResponseEnum.java

@@ -2,6 +2,8 @@ package com.backendsys.modules.sse.entity;
 
 public enum SseResponseEnum {
 
+    SYSTEM_USER_UPDATE("system_user_update", "更新用户信息"),
+
     CONNECT("connect", "建立连接"),
     DISCONNECT("disconnect", "断开连接"),
     LOGOUT("logout", "退出登录"),

+ 1 - 1
src/main/java/com/backendsys/modules/system/service/SysUserService.java

@@ -14,7 +14,7 @@ public interface SysUserService extends IService<SysUser> {
     PageEntity selectUserOnlineList(SysUserDTO sysUserDTO);
 
     // 获取系统用户详情
-    SysUserInfo selectUserInfo(Long user_id);
+    SysUserInfo selectUserInfo(SysUserInfo sysUserInfo);
     // 获取系统用户详情(简约)
     SysUserInfoSimple selectUserInfoSimple(Long user_id);
 

+ 23 - 15
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -19,6 +19,7 @@ import com.backendsys.modules.system.service.SysAuthService;
 import com.backendsys.modules.system.service.SysCommonService;
 import com.backendsys.modules.system.service.SysUserIntegralService;
 import com.backendsys.modules.system.service.SysUserService;
+import com.backendsys.modules.system.utils.UserUtil;
 import com.backendsys.utils.response.ResultEnum;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.code.kaptcha.Producer;
@@ -47,6 +48,8 @@ public class SysAuthServiceImpl implements SysAuthService {
     @Autowired
     private Environment env;
     @Autowired
+    private UserUtil userUtil;
+    @Autowired
     private JwtUtil jwtUtil;
     @Autowired
     private RedisUtil redisUtil;
@@ -152,7 +155,9 @@ public class SysAuthServiceImpl implements SysAuthService {
     public SysUserInfo loginSuccess(Long user_id, Integer is_remember) {
 
         // [查询] 登录的用户信息
-        SysUserInfo sysUserInfo = sysUserService.selectUserInfo(user_id);
+        SysUserInfo entity = new SysUserInfo();
+        entity.setUser_id(user_id);
+        SysUserInfo sysUserInfo = sysUserService.selectUserInfo(entity);
 
         // 删除图形验证码缓存
         redisUtil.delete(httpRequestUtil.getKaptchaKey());
@@ -187,25 +192,28 @@ public class SysAuthServiceImpl implements SysAuthService {
         Long DEFAULT_MILLISECONDS = SYSTEM_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
         // 7天 (转毫秒)
         Long SEVEN_DAY_MILLISECONDS = 7L * 24 * 60 * 60 * 1000;
-
+        // 是否钩选 "7天免登录",否则按系统配置-默认过期时间
         Long token_duration_milliseconds = (is_remember != null && is_remember.equals(1)) ? SEVEN_DAY_MILLISECONDS : DEFAULT_MILLISECONDS;
-        Integer token_duration_hours = Convert.toInt(token_duration_milliseconds / 3600000L);
+        Integer tokenDurationHours = Convert.toInt(token_duration_milliseconds / 3600000L);
 
         Date token_expiration = new Date((new Date()).getTime() + token_duration_milliseconds);
         sysUserInfo.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
 
-        // 生成 Token
-        SecurityUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(sysUserInfo), SecurityUserInfo.class);
-        String token = jwtUtil.createSystemJwtToken(securityUserInfo);
-        String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + uuid;
-        sysUserInfo.setToken(token);
-
-        // 生成 PerMissionIds
-        List<String> permission_ids_list = sysUserInfo.getPermission_ids();
-
-        // [Redis] 将 Token 与 Permission 存入缓存
-        TokenCatch tokenCatch = new TokenCatch(token, permission_ids_list);
-        redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
+        // 实时更新缓存: 用户信息
+        sysUserInfo = userUtil.syncUpdateUserCatch(sysUserInfo, tokenDurationHours);
+
+        //// 生成 Token
+        //SecurityUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(sysUserInfo), SecurityUserInfo.class);
+        //String token = jwtUtil.createSystemJwtToken(securityUserInfo);
+        //sysUserInfo.setToken(token);
+        //
+        //// 生成 PerMissionIds
+        //List<String> permission_ids_list = sysUserInfo.getPermission_ids();
+        //
+        //// [Redis] 将 Token 与 Permission 存入缓存
+        //String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + uuid;
+        //TokenCatch tokenCatch = new TokenCatch(token, permission_ids_list);
+        //redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
 
         return sysUserInfo;
     }

+ 12 - 2
src/main/java/com/backendsys/modules/system/service/impl/SysUserServiceImpl.java

@@ -1,6 +1,7 @@
 package com.backendsys.modules.system.service.impl;
 
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.StrUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.common.config.redis.utils.RedisUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
@@ -100,14 +101,23 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> impleme
      * 获取系统用户详情
      */
     @Override
-    public SysUserInfo selectUserInfo(Long user_id) {
+    public SysUserInfo selectUserInfo(SysUserInfo entity) {
+
+        Long user_id = entity.getUser_id();
 
         // 获得 用户账号信息
         SysUser sysUser = sysUserDao.selectOne(new QueryWrapper<SysUser>().eq("id", user_id));
         if (sysUser == null) throw new CustException("用户不存在");
 
         // 获得 用户基本信息
-        SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(new QueryWrapper<SysUserInfo>().eq("user_id", user_id));
+        LambdaQueryWrapper<SysUserInfo> wrapperUserInfo = new LambdaQueryWrapper<>();
+        // 条件:用户ID (必选)
+        wrapperUserInfo.eq(SysUserInfo::getUser_id, user_id);
+        // 条件:邀请码
+        if (StrUtil.isNotEmpty(entity.getInvite_code())) {
+            wrapperUserInfo.eq(SysUserInfo::getInvite_code, entity.getInvite_code());
+        }
+        SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(wrapperUserInfo);
         if (sysUserInfo == null) throw new CustException("用户不存在");
 
         // 用户账号信息 赋值到 用户基本信息

+ 61 - 0
src/main/java/com/backendsys/modules/system/utils/UserUtil.java

@@ -0,0 +1,61 @@
+package com.backendsys.modules.system.utils;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.backendsys.modules.common.config.redis.utils.RedisUtil;
+import com.backendsys.modules.common.config.security.entity.SecurityUserInfo;
+import com.backendsys.modules.common.config.security.utils.JwtUtil;
+import com.backendsys.modules.system.entity.SysUserInfo;
+import com.backendsys.modules.system.entity.TokenCatch;
+import com.backendsys.modules.system.service.SysCommonService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class UserUtil {
+
+    @Autowired
+    private JwtUtil jwtUtil;
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
+    private String REDIS_LOGIN_TOKEN_PREFIX;
+
+    /**
+     * 实时更新缓存: 用户信息
+     */
+    public SysUserInfo syncUpdateUserCatch(SysUserInfo userInfo) {
+        return syncUpdateUserCatch(userInfo, null);
+    }
+
+    public SysUserInfo syncUpdateUserCatch(SysUserInfo userInfo, Integer tokenDurationHours) {
+        // 生成 Token
+        String lastLoginUuid = userInfo.getLast_login_uuid();
+        SecurityUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(userInfo), SecurityUserInfo.class);
+        String token = jwtUtil.createSystemJwtToken(securityUserInfo);
+        userInfo.setToken(token);
+
+        // [Redis] 将 Token 与 Permission 存入缓存
+        String tokenRedisKey = REDIS_LOGIN_TOKEN_PREFIX + lastLoginUuid;
+        TokenCatch tokenCatch = new TokenCatch(token, userInfo.getPermission_ids());
+
+        // -- 重置过期时间,直接给7天 -----------------------------------
+        Integer durationHours = tokenDurationHours != null ? tokenDurationHours : Convert.toInt(7L * 24 * 60 * 60 * 1000 / 3600000L);
+        redisUtil.setCacheObject(tokenRedisKey, JSONUtil.toJsonStr(tokenCatch), durationHours, TimeUnit.HOURS);
+
+        System.out.println("-- 实时更新用户信息 (syncUpdateUserInfo) --");
+        System.out.println(userInfo);
+
+        return userInfo;
+    }
+
+}

+ 3 - 1
src/main/java/com/backendsys/service/Ai/Aiivh/AiivhQuotaServiceImpl.java

@@ -270,7 +270,9 @@ public class AiivhQuotaServiceImpl implements AiivhQuotaService{
 //            Map<String, Object> userInfo = sysUserService.queryUserById(user_id);
 //            Float origin_point_balance = (Float) userInfo.get("point_balance");
 
-            SysUserInfo userInfo = sysUserService.selectUserInfo(user_id);
+            SysUserInfo entity = new SysUserInfo();
+            entity.setUser_id(user_id);
+            SysUserInfo userInfo = sysUserService.selectUserInfo(entity);
             Float origin_point_balance = Convert.toFloat(userInfo.getPoint_balance());
 
             Float new_point_balance = origin_point_balance - aiivhQuotaOrderDTO.getTotal_point();