作者 唐俊升

update:

1. 刷新用户redis缓存。
2. 修改菜单树,解决子节点未选择仍显示问题。
@@ -100,6 +100,9 @@ eureka: @@ -100,6 +100,9 @@ eureka:
100 health-check-url-path: /ac/health 100 health-check-url-path: /ac/health
101 client: 101 client:
102 #eureka注册中心服务器地址 102 #eureka注册中心服务器地址
  103 +# 关闭向eureka注册,用于测试
  104 +# register-with-eureka: false
  105 +# fetch-registry: false
103 service-url: 106 service-url:
104 # defaultZone: http://127.0.0.1:12345/eureka/ 107 # defaultZone: http://127.0.0.1:12345/eureka/
105 defaultZone: http://192.168.1.53:12345/eureka/ 108 defaultZone: http://192.168.1.53:12345/eureka/
@@ -179,30 +179,42 @@ public class UserController { @@ -179,30 +179,42 @@ public class UserController {
179 * 更新目标用户的权限缓存 179 * 更新目标用户的权限缓存
180 */ 180 */
181 String authHeader = request.getHeader("Authorization"); 181 String authHeader = request.getHeader("Authorization");
182 - if (authHeader != null && authHeader.startsWith("Bearer ")) {  
183 - final String authToken = authHeader.substring("Bearer ".length());  
184 - try {  
185 - String userJson = redisUtils.get(authToken);  
186 - if (userJson != null) {  
187 - KakoUser u = JSON.parseObject(userJson, KakoUser.class);  
188 - String username = u.getUsername();  
189 -  
190 -// String username = JwtTokenUtil.parseToken(authToken);  
191 - if (username != null) {  
192 - UserDetails userDetails = userDetailService.loadUserByUsername(username);  
193 - if (userDetails != null) {  
194 - String json = JSON.toJSONString(userDetails);  
195 - redisUtils.set(authToken, json, 3600 * 24 * 7);  
196 - return new ResultJson("200", "缓存更新成功");  
197 - }  
198 - }  
199 - }  
200 - }catch (Exception e){  
201 - log.error(e.toString());  
202 - return new ResultJson("500","缓存更新失败"); 182 + if (authHeader == null || !authHeader.startsWith("Bearer ")) {
  183 + log.warn("刷新权限缓存失败:请求头缺少 Authorization 或不是 Bearer 格式");
  184 + return new ResultJson("500", "缓存更新失败");
  185 + }
  186 + final String authToken = authHeader.substring("Bearer ".length());
  187 + try {
  188 + //redis 中登录时写入的结构是 JSON(KakoUser),键为 token
  189 + String userJson = redisUtils.get(authToken);
  190 + if (userJson == null) {
  191 + log.warn("刷新权限缓存失败:token 对应的用户缓存不存在,token:{}", authToken);
  192 + return new ResultJson("500", "缓存更新失败");
  193 + }
  194 + KakoUser cachedUser = JSON.parseObject(userJson, KakoUser.class);
  195 + String username = cachedUser == null ? null : cachedUser.getUsername();
  196 + if (username == null) {
  197 + log.warn("刷新权限缓存失败:缓存内容无法解析出用户名,token:{}", authToken);
  198 + return new ResultJson("500", "缓存更新失败");
  199 + }
  200 +
  201 + //重新加载最新的用户信息与权限(含 RBAC 过滤后的权限列表)
  202 + KakoUser kakoUser = userService.loadByUsername(username);
  203 + if (kakoUser == null) {
  204 + log.warn("刷新权限缓存失败:未查询到用户信息,username:{}", username);
  205 + return new ResultJson("500", "缓存更新失败");
203 } 206 }
  207 + /**
  208 + * 必须按登录时的格式写回:value = JSON(KakoUser),不能写成 JSON(UserDetails),
  209 + * 否则 KakoJwtAuthenticationTokenFilter 用 JSON.parseObject(userJson, KakoUser.class) 解析时会丢字段。
  210 + */
  211 + redisUtils.set(authToken, JSON.toJSONString(kakoUser), 3600 * 24 * 7);
  212 + log.info("用户权限缓存刷新成功,username:{},token:{}", username, authToken);
  213 + return new ResultJson("200", "缓存更新成功");
  214 + } catch (Exception e) {
  215 + log.error("刷新用户权限缓存异常,token:" + authToken, e);
  216 + return new ResultJson("500", "缓存更新失败");
204 } 217 }
205 - return new ResultJson("500","缓存更新失败");  
206 } 218 }
207 219
208 } 220 }
@@ -9,6 +9,7 @@ import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable; @@ -9,6 +9,7 @@ import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable;
9 import com.tianbo.warehouse.dao.PERMISSIONMapper; 9 import com.tianbo.warehouse.dao.PERMISSIONMapper;
10 import com.tianbo.warehouse.model.PERMISSION; 10 import com.tianbo.warehouse.model.PERMISSION;
11 import com.tianbo.warehouse.service.PermissionService; 11 import com.tianbo.warehouse.service.PermissionService;
  12 +import lombok.extern.slf4j.Slf4j;
12 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
13 14
14 import javax.annotation.Resource; 15 import javax.annotation.Resource;
@@ -18,6 +19,7 @@ import java.util.List; @@ -18,6 +19,7 @@ import java.util.List;
18 import java.util.Map; 19 import java.util.Map;
19 20
20 @Service("PermissionService") 21 @Service("PermissionService")
  22 +@Slf4j
21 public class PermissionServiceImp implements PermissionService { 23 public class PermissionServiceImp implements PermissionService {
22 24
23 @Resource 25 @Resource
@@ -83,32 +85,13 @@ public class PermissionServiceImp implements PermissionService { @@ -83,32 +85,13 @@ public class PermissionServiceImp implements PermissionService {
83 public Map<String, Object> getUserMenus(Integer userId) { 85 public Map<String, Object> getUserMenus(Integer userId) {
84 Map<String, Object> data = new HashMap<String, Object>(); 86 Map<String, Object> data = new HashMap<String, Object>();
85 try { 87 try {
86 - //查询所有菜单  
87 - List<PERMISSION> allMenu = permissionMapper.findByUserId(userId);  
88 -// //根节点  
89 -// List<PERMISSION> rootMenu = new ArrayList<PERMISSION>();  
90 -// for (PERMISSION nav : allMenu) {  
91 -// if (nav.getParentId() == 0) {//父节点是0的,为根节点。  
92 -// rootMenu.add(nav);  
93 -// }  
94 -// }  
95 -  
96 - /* 根据Menu类的order排序 */  
97 -// Collections.sort(rootMenu, order());  
98 - //为根菜单设置子菜单,getClild是递归调用的  
99 -// for (PERMISSION nav : rootMenu) {  
100 -// /* 获取根节点下的所有子节点 使用getChild方法*/  
101 -// List<PERMISSION> childList = getChild(nav.getPermissionId(), allMenu);  
102 -// nav.setChildren(childList);//给根节点设置子节点  
103 -// }  
104 - /**  
105 - * 输出构建好的菜单数据。  
106 - *  
107 - */ 88 + //一次查出用户有权限的全部层级菜单(带 RBAC 权限过滤),再在 Java 层组树,避免递归查库
  89 + List<PERMISSION> allMenu = permissionMapper.findByUserIdWithLogin(userId);
108 data.put("success", "true"); 90 data.put("success", "true");
109 - data.put("list", allMenu); 91 + data.put("list", getPermissionList(allMenu, 0));
110 return data; 92 return data;
111 } catch (Exception e) { 93 } catch (Exception e) {
  94 + log.error("获取用户菜单树失败,userId:" + userId, e);
112 data.put("success", "false"); 95 data.put("success", "false");
113 data.put("list", new ArrayList()); 96 data.put("list", new ArrayList());
114 return data; 97 return data;
@@ -119,32 +102,13 @@ public class PermissionServiceImp implements PermissionService { @@ -119,32 +102,13 @@ public class PermissionServiceImp implements PermissionService {
119 public Map<String, Object> getUserMenusKako(String userId) { 102 public Map<String, Object> getUserMenusKako(String userId) {
120 Map<String, Object> data = new HashMap<String, Object>(); 103 Map<String, Object> data = new HashMap<String, Object>();
121 try { 104 try {
122 - //查询所有菜单  
123 - List<PERMISSION> allMenu = permissionMapper.findByUserIdKako(userId);  
124 -// //根节点  
125 -// List<PERMISSION> rootMenu = new ArrayList<PERMISSION>();  
126 -// for (PERMISSION nav : allMenu) {  
127 -// if (nav.getParentId() == 0) {//父节点是0的,为根节点。  
128 -// rootMenu.add(nav);  
129 -// }  
130 -// }  
131 -  
132 - /* 根据Menu类的order排序 */  
133 -// Collections.sort(rootMenu, order());  
134 - //为根菜单设置子菜单,getClild是递归调用的  
135 -// for (PERMISSION nav : rootMenu) {  
136 -// /* 获取根节点下的所有子节点 使用getChild方法*/  
137 -// List<PERMISSION> childList = getChild(nav.getPermissionId(), allMenu);  
138 -// nav.setChildren(childList);//给根节点设置子节点  
139 -// }  
140 - /**  
141 - * 输出构建好的菜单数据。  
142 - *  
143 - */ 105 + //一次查出用户有权限的全部层级菜单(带 RBAC 权限过滤),再在 Java 层组树,避免递归查库
  106 + List<PERMISSION> allMenu = permissionMapper.findByUserIdWithLoginKako(userId);
144 data.put("success", "true"); 107 data.put("success", "true");
145 - data.put("list", allMenu); 108 + data.put("list", getPermissionList(allMenu, 0));
146 return data; 109 return data;
147 } catch (Exception e) { 110 } catch (Exception e) {
  111 + log.error("获取卡口用户菜单树失败,userId:" + userId, e);
148 data.put("success", "false"); 112 data.put("success", "false");
149 data.put("list", new ArrayList()); 113 data.put("list", new ArrayList());
150 return data; 114 return data;