|
|
@@ -0,0 +1,370 @@
|
|
|
+package com.zjrs.zwnw.auth.controller;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.zjrs.zwnw.auth.entity.FwOperator;
|
|
|
+import com.zjrs.zwnw.auth.entity.FwOperatorMapper;
|
|
|
+import com.zjrs.zwnw.auth.service.AuthRedisService;
|
|
|
+import com.zjrs.zwnw.portal.entity.SSgMenuMapper;
|
|
|
+import com.zjrs.zwnw.portal.entity.VRsSyYydhInfoMapper;
|
|
|
+import com.zjrs.zwnw.portal.po.SSgMenuPO;
|
|
|
+import com.zjrs.zwnw.portal.po.VRsSyYydhInfoPO;
|
|
|
+import com.zjrs.zwnw.sxgl.entity.FwRight2ResourceMapper;
|
|
|
+import com.zjrs.zwnw.sxgl.entity.FwrightMapper;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 用户信息控制器
|
|
|
+ * <p>
|
|
|
+ * 对应原 leaf6-uni-cloud-uc-service 中的用户信息接口。
|
|
|
+ * 前端在登录获取 Token 后调用此接口获取用户基本信息(loginUserDTO)。
|
|
|
+ * 路径前缀为 /leaf6-uni-cloud-uc-service(与前端 vue.config.js 中的代理路径一致)。
|
|
|
+ */
|
|
|
+@RestController
|
|
|
+public class UserInfoController {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(UserInfoController.class);
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AuthRedisService authRedisService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private VRsSyYydhInfoMapper vRsSyYydhInfoMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SSgMenuMapper sSgMenuMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FwrightMapper fwrightMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FwRight2ResourceMapper fwRight2ResourceMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FwOperatorMapper fwOperatorMapper;
|
|
|
+
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/users/{id}")
|
|
|
+ public Map<String, Object> getUserById(@PathVariable("id") String id) {
|
|
|
+ if (StringUtils.isEmpty(id)) {
|
|
|
+ throw new org.springframework.web.server.ResponseStatusException(
|
|
|
+ org.springframework.http.HttpStatus.BAD_REQUEST, "ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ FwOperator operator = fwOperatorMapper.selectByOperId(id);
|
|
|
+ if (operator == null) {
|
|
|
+ // 如果不是操作员ID,尝试作为sguserid查询?或者直接返回404
|
|
|
+ // 鉴于用户请求示例是UUID,可能是operid
|
|
|
+ throw new org.springframework.web.server.ResponseStatusException(
|
|
|
+ org.springframework.http.HttpStatus.NOT_FOUND, "用户不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("id", operator.getOperid());
|
|
|
+ result.put("loginId", operator.getLoginid());
|
|
|
+ result.put("userName", operator.getOpername());
|
|
|
+ result.put("operunitid", operator.getOperunitid());
|
|
|
+ result.put("bae001", operator.getBae001());
|
|
|
+ result.put("opertype", operator.getOpertype());
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前登录用户信息
|
|
|
+ * <p>
|
|
|
+ * 请求方式:GET /leaf6-uni-cloud-uc-service/api/security/users/current/loginResponse
|
|
|
+ * Header:Access-Token: eyJ...
|
|
|
+ * <p>
|
|
|
+ * 成功响应:{ "loginUserDTO": { "id": "...", "loginId": "...", "userName": "..." } }
|
|
|
+ * 失败响应(Token 无效或已过期):HTTP 401
|
|
|
+ */
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/security/users/current/loginResponse")
|
|
|
+ public Map<String, Object> getCurrentUser(HttpServletRequest request) {
|
|
|
+ String token = request.getHeader("Access-Token");
|
|
|
+
|
|
|
+ if (token == null || token.trim().isEmpty()) {
|
|
|
+ log.warn("[UserInfo] 请求未携带 Access-Token");
|
|
|
+ throw new org.springframework.web.server.ResponseStatusException(
|
|
|
+ org.springframework.http.HttpStatus.UNAUTHORIZED, "未提供 Token");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从 Redis 获取用户信息
|
|
|
+ Map<String, Object> userInfo = authRedisService.getUserInfo(token.trim());
|
|
|
+ if (userInfo == null) {
|
|
|
+ log.warn("[UserInfo] Token 对应的用户信息不存在或已过期");
|
|
|
+ throw new org.springframework.web.server.ResponseStatusException(
|
|
|
+ org.springframework.http.HttpStatus.UNAUTHORIZED, "Token 已过期,请重新登录");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构造前端期望的 loginUserDTO 格式
|
|
|
+ Map<String, Object> loginUserDTO = new HashMap<>();
|
|
|
+ loginUserDTO.put("id", userInfo.get("id"));
|
|
|
+ loginUserDTO.put("loginId", userInfo.get("loginId"));
|
|
|
+ loginUserDTO.put("userName", userInfo.get("userName"));
|
|
|
+ loginUserDTO.put("operunitid", userInfo.get("operunitid"));
|
|
|
+ loginUserDTO.put("bae001", userInfo.get("bae001"));
|
|
|
+ loginUserDTO.put("opertype", userInfo.get("opertype"));
|
|
|
+ loginUserDTO.put("belongsOrgName", userInfo.get("belongsOrgName"));
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("loginUserDTO", loginUserDTO);
|
|
|
+
|
|
|
+ log.debug("[UserInfo] 返回用户信息,loginId={}", userInfo.get("loginId"));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/version")
|
|
|
+ public Map<String, Object> getVersion() {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("version", "0.15.0");
|
|
|
+ result.put("isShowVersion", true);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/users/{id}/navMenus")
|
|
|
+ public List<Map<String, Object>> getNavMenus(@PathVariable("id") String id, HttpServletRequest request) {
|
|
|
+ String token = request.getHeader("Access-Token");
|
|
|
+ Map<String, Object> userInfo = token == null ? null : authRedisService.getUserInfo(token.trim());
|
|
|
+ String userId = null;
|
|
|
+ if (userInfo != null && userInfo.get("id") != null) {
|
|
|
+ userId = String.valueOf(userInfo.get("id"));
|
|
|
+ } else {
|
|
|
+ userId = id;
|
|
|
+ }
|
|
|
+ if (userId == null || userId.trim().isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 查询用户常用/授权的扁平菜单列表 (基于视图 v_rs_sy_yydh_info)
|
|
|
+ // 这个列表代表“用户拥有的应用权限”
|
|
|
+ List<VRsSyYydhInfoPO> userApps = vRsSyYydhInfoMapper.selectList(
|
|
|
+ new QueryWrapper<VRsSyYydhInfoPO>()
|
|
|
+ .eq("userid", userId)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 提取用户拥有的应用 ID 集合
|
|
|
+ Set<String> userAppIds = new HashSet<>();
|
|
|
+ for (VRsSyYydhInfoPO app : userApps) {
|
|
|
+ if (StringUtils.hasText(app.getCdid())) {
|
|
|
+ userAppIds.add(app.getCdid().trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果用户没有任何应用权限,返回空列表
|
|
|
+ if (userAppIds.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 从 S_SG_MENU 表加载完整菜单树结构 (替换之前的硬编码)
|
|
|
+ List<SSgMenuPO> allMenus = sSgMenuMapper.selectList(
|
|
|
+ new QueryWrapper<SSgMenuPO>().orderByAsc("ORDER_NUMBER")
|
|
|
+ );
|
|
|
+
|
|
|
+ if (allMenus == null || allMenus.isEmpty()) {
|
|
|
+ log.warn("S_SG_MENU table is empty, returning empty menu tree");
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建 ID -> Node 映射
|
|
|
+ Map<String, MenuNode> menuMap = new HashMap<>();
|
|
|
+ for (SSgMenuPO po : allMenus) {
|
|
|
+ MenuNode node = new MenuNode(
|
|
|
+ po.getId(),
|
|
|
+ po.getMenuName(),
|
|
|
+ po.getIcon(),
|
|
|
+ po.getUrl(),
|
|
|
+ po.getParentId(),
|
|
|
+ po.getOrderNumber() != null ? po.getOrderNumber() : 999
|
|
|
+ );
|
|
|
+ menuMap.put(node.id, node);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 根据用户拥有的应用权限,筛选并补全树结构
|
|
|
+ // 逻辑修正:如果用户拥有某个节点权限,则:
|
|
|
+ // (1) 该节点的所有祖先可见(向上递归)
|
|
|
+ // (2) 该节点的所有子孙可见(向下递归,级联显示子菜单)
|
|
|
+ Set<String> visibleMenuIds = new HashSet<>();
|
|
|
+
|
|
|
+ // 第一轮:标记直接拥有的权限节点
|
|
|
+ for (String appId : userAppIds) {
|
|
|
+ if (menuMap.containsKey(appId)) {
|
|
|
+ visibleMenuIds.add(appId);
|
|
|
+ // 级联添加所有子孙节点
|
|
|
+ addDescendants(appId, menuMap, visibleMenuIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二轮:向上递归补全父节点
|
|
|
+ boolean added;
|
|
|
+ do {
|
|
|
+ added = false;
|
|
|
+ // 创建副本进行遍历,避免并发修改异常
|
|
|
+ Set<String> currentIds = new HashSet<>(visibleMenuIds);
|
|
|
+ for (String menuId : currentIds) {
|
|
|
+ MenuNode node = menuMap.get(menuId);
|
|
|
+ if (node != null && StringUtils.hasText(node.parentId)) {
|
|
|
+ if (!visibleMenuIds.contains(node.parentId)) {
|
|
|
+ visibleMenuIds.add(node.parentId);
|
|
|
+ added = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } while (added);
|
|
|
+
|
|
|
+ // 4. 组装树形结构
|
|
|
+ List<MenuNode> rootNodes = new ArrayList<>();
|
|
|
+ for (MenuNode node : menuMap.values()) {
|
|
|
+ // 只处理可见节点
|
|
|
+ if (!visibleMenuIds.contains(node.id)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果父节点为空,或者父节点不可见,则视为当前上下文的根节点
|
|
|
+ // 注意:如果父节点存在但不可见(理论上第二轮已补全,除非数据异常),也暂且视为根节点显示,防止断链
|
|
|
+ if (!StringUtils.hasText(node.parentId) || !menuMap.containsKey(node.parentId) || !visibleMenuIds.contains(node.parentId)) {
|
|
|
+ rootNodes.add(node);
|
|
|
+ } else {
|
|
|
+ MenuNode parent = menuMap.get(node.parentId);
|
|
|
+ parent.addChild(node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根节点排序
|
|
|
+ rootNodes.sort(Comparator.comparingInt(o -> o.sortNo));
|
|
|
+
|
|
|
+ // 转换为 Map 结构返回
|
|
|
+ return convertToMapList(rootNodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递归添加所有子孙节点
|
|
|
+ private void addDescendants(String parentId, Map<String, MenuNode> menuMap, Set<String> visibleMenuIds) {
|
|
|
+ for (MenuNode node : menuMap.values()) {
|
|
|
+ if (parentId.equals(node.parentId)) {
|
|
|
+ if (!visibleMenuIds.contains(node.id)) {
|
|
|
+ visibleMenuIds.add(node.id);
|
|
|
+ // 继续递归添加该子节点的子孙
|
|
|
+ addDescendants(node.id, menuMap, visibleMenuIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Map<String, Object>> convertToMapList(List<MenuNode> nodes) {
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
+ for (MenuNode node : nodes) {
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
+ map.put("id", node.id);
|
|
|
+ map.put("name", node.name);
|
|
|
+ map.put("icon", node.icon);
|
|
|
+ map.put("path", node.path);
|
|
|
+
|
|
|
+ // OpenType 推断
|
|
|
+ String openType = "1";
|
|
|
+ if (node.path != null && (node.path.startsWith("http://") || node.path.startsWith("https://"))) {
|
|
|
+ openType = "2";
|
|
|
+ }
|
|
|
+ map.put("openType", openType);
|
|
|
+
|
|
|
+ if (!node.children.isEmpty()) {
|
|
|
+ map.put("children", convertToMapList(node.children));
|
|
|
+ }
|
|
|
+ result.add(map);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 内部类用于构建树
|
|
|
+ static class MenuNode {
|
|
|
+ String id;
|
|
|
+ String name;
|
|
|
+ String icon;
|
|
|
+ String path;
|
|
|
+ String parentId;
|
|
|
+ int sortNo;
|
|
|
+ List<MenuNode> children = new ArrayList<>();
|
|
|
+
|
|
|
+ public MenuNode(String id, String name, String icon, String path, String parentId, int sortNo) {
|
|
|
+ this.id = id;
|
|
|
+ this.name = name;
|
|
|
+ this.icon = icon;
|
|
|
+ this.path = path;
|
|
|
+ this.parentId = parentId;
|
|
|
+ this.sortNo = sortNo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void addChild(MenuNode child) {
|
|
|
+ children.add(child);
|
|
|
+ children.sort(Comparator.comparingInt(o -> o.sortNo));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- 保留原有辅助方法 (UserConfig, Avatar 等) ---
|
|
|
+
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/userConfig/find/{type}")
|
|
|
+ public Map<String, Object> getUserConfig(@PathVariable("type") String type) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("type", type);
|
|
|
+ if ("global-setting".equals(type)) {
|
|
|
+ Map<String, Object> attribute = new HashMap<>();
|
|
|
+ attribute.put("layout", "sider-layout");
|
|
|
+ attribute.put("enableMultiTags", false);
|
|
|
+ attribute.put("showBreadcrumb", false);
|
|
|
+ result.put("attribute", attribute);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/leaf6-uni-cloud-uc-service/api/userConfig/save")
|
|
|
+ public Map<String, Object> saveUserConfig(@RequestBody Map<String, Object> request) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("type", request.get("type"));
|
|
|
+ result.put("attribute", request.get("attribute"));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/leaf6-uni-cloud-uc-service/api/users/{id}/avatar")
|
|
|
+ public ResponseEntity<byte[]> getAvatar(@PathVariable("id") String id) {
|
|
|
+ String base64 = authRedisService.getUserAvatar(id);
|
|
|
+ if (!StringUtils.hasText(base64)) {
|
|
|
+ return ResponseEntity.noContent().build();
|
|
|
+ }
|
|
|
+ byte[] avatar;
|
|
|
+ try {
|
|
|
+ avatar = Base64.getDecoder().decode(base64);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ return ResponseEntity.noContent().build();
|
|
|
+ }
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+ headers.setContentType(MediaType.IMAGE_PNG);
|
|
|
+ return ResponseEntity.ok().headers(headers).body(avatar);
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/leaf6-uni-cloud-uc-service/api/users/{id}/avatar")
|
|
|
+ public ResponseEntity<String> uploadAvatar(@PathVariable("id") String id, @RequestParam("file") MultipartFile file) {
|
|
|
+ if (file.isEmpty()) {
|
|
|
+ return ResponseEntity.badRequest().body("文件不能为空");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ if (file.getSize() > 2 * 1024 * 1024) {
|
|
|
+ return ResponseEntity.badRequest().body("文件大小不能超过2MB");
|
|
|
+ }
|
|
|
+ byte[] bytes = file.getBytes();
|
|
|
+ String base64 = Base64.getEncoder().encodeToString(bytes);
|
|
|
+ authRedisService.saveUserAvatar(id, base64);
|
|
|
+ return ResponseEntity.ok("头像上传成功");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("头像上传失败", e);
|
|
|
+ return ResponseEntity.status(500).body("头像上传失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|