From e7f90b8d9706c95144b63bac3c1f3e9fbc12ff32 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 20 Mar 2026 17:15:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=88=90=E5=8A=9F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xservice/auth/api/dto/resp/DeptDTO.java | 4 + .../xservice/auth/api/dto/resp/MenuVO.java | 103 +++++++ .../xservice/auth/api/dto/resp/MetaVo.java | 92 ++++++ .../xservice/auth/api/dto/resp/RouterVo.java | 64 +++++ .../auth/api/dto/resp/TreeSelect.java | 86 ++++++ .../xservice/auth/api/dto/resp/UserDTO.java | 2 + .../xservice/auth/api/dto/resp/UserResp.java | 2 + .../server/controller/MenuController.java | 29 ++ .../server/controller/TokenController.java | 9 +- .../auth/service/constants/UserConstants.java | 88 ++++++ .../auth/service/convert/XMenuConverter.java | 18 ++ .../xservice/auth/service/entity/XMenuDO.java | 72 +++++ .../xservice/auth/service/entity/XUser.java | 5 + .../repository/mapper/XMenuMapper.java | 18 ++ .../repository/mapper/XUserMapper.java | 7 - .../auth/service/service/XMenuService.java | 14 + .../auth/service/service/XUserService.java | 2 +- .../service/impl/XMenuServiceImpl.java | 261 ++++++++++++++++++ .../service/impl/XUserServiceImpl.java | 23 +- .../resources/mapper/user/XMenuMapper.xml | 60 ++++ .../resources/mapper/user/XUserMapper.xml | 112 +------- 21 files changed, 942 insertions(+), 129 deletions(-) create mode 100644 xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MenuVO.java create mode 100644 xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MetaVo.java create mode 100644 xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/RouterVo.java create mode 100644 xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/TreeSelect.java create mode 100644 xs-server/src/main/java/com/xiang/xservice/auth/server/controller/MenuController.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/constants/UserConstants.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/convert/XMenuConverter.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XMenuDO.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XMenuMapper.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/service/XMenuService.java create mode 100644 xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XMenuServiceImpl.java create mode 100644 xs-service/src/main/resources/mapper/user/XMenuMapper.xml diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/DeptDTO.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/DeptDTO.java index 13ce718..9bd0a97 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/DeptDTO.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/DeptDTO.java @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; + @Data @AllArgsConstructor @NoArgsConstructor @@ -29,4 +31,6 @@ public class DeptDTO { * 排序 */ private Integer sortNo; + + private List children; } diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MenuVO.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MenuVO.java new file mode 100644 index 0000000..dc6e0b6 --- /dev/null +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MenuVO.java @@ -0,0 +1,103 @@ +package com.xiang.xservice.auth.api.dto.resp; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:24 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MenuVO { + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 父菜单名称 + */ + private String parentName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String query; + + /** + * 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) + */ + private String routeName; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 类型(M目录 C菜单 F按钮) + */ + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限字符串 + */ + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 子菜单 + */ + private List children = new ArrayList(); +} diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MetaVo.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MetaVo.java new file mode 100644 index 0000000..749dcb3 --- /dev/null +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/MetaVo.java @@ -0,0 +1,92 @@ +package com.xiang.xservice.auth.api.dto.resp; + + +import com.xiang.xservice.basic.utils.MyStringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo { + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() { + } + + public MetaVo(String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (MyStringUtils.isHttp(link)) { + this.link = link; + } + } + + public boolean isNoCache() { + return noCache; + } + + public void setNoCache(boolean noCache) { + this.noCache = noCache; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } +} diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/RouterVo.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/RouterVo.java new file mode 100644 index 0000000..08d04e0 --- /dev/null +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/RouterVo.java @@ -0,0 +1,64 @@ +package com.xiang.xservice.auth.api.dto.resp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; +} diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/TreeSelect.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/TreeSelect.java new file mode 100644 index 0000000..eaf47c1 --- /dev/null +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/TreeSelect.java @@ -0,0 +1,86 @@ +package com.xiang.xservice.auth.api.dto.resp; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 节点ID + */ + private Long id; + + /** + * 节点名称 + */ + private String label; + + /** + * 节点禁用 + */ + private boolean disabled = false; + + /** + * 子节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() { + + } + + public TreeSelect(DeptDTO dept) { + this.id = dept.getId(); + this.label = dept.getName(); + this.disabled = true; + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(MenuVO menu) { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public boolean isDisabled() { + return disabled; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserDTO.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserDTO.java index 78d8990..01ca1f0 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserDTO.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserDTO.java @@ -5,6 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; +import java.util.Set; @Data @AllArgsConstructor @@ -15,4 +16,5 @@ public class UserDTO { private UserResp user; private DeptDTO dept; private List permissionRoles; + private Set permissions; } diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserResp.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserResp.java index a4b6b17..a2571e8 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserResp.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/resp/UserResp.java @@ -11,6 +11,8 @@ import java.time.LocalDateTime; @NoArgsConstructor public class UserResp { + private Long id; + /** * 用户名(昵称) */ diff --git a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/MenuController.java b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/MenuController.java new file mode 100644 index 0000000..9bc7c1d --- /dev/null +++ b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/MenuController.java @@ -0,0 +1,29 @@ +package com.xiang.xservice.auth.server.controller; + +import com.xiang.xservice.auth.api.dto.resp.RouterVo; +import com.xiang.xservice.auth.service.service.XMenuService; +import com.xiang.xservice.basic.common.resp.Result; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:19 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class MenuController { + + private final XMenuService menuService; + + @GetMapping("/private/menu/getRouter") + public Result> getRouter(@RequestParam("userId") Long userId) { + return Result.data(menuService.getRouter(userId)); + } +} diff --git a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java index 7a02afa..98b512f 100644 --- a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java +++ b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java @@ -4,6 +4,7 @@ import com.xiang.xservice.auth.api.api.TokenApi; import com.xiang.xservice.auth.api.dto.req.LoginRequest; import com.xiang.xservice.auth.api.dto.req.RefreshRequest; import com.xiang.xservice.auth.api.dto.req.RegisterRequest; +import com.xiang.xservice.auth.api.dto.req.user.UserQueryRequest; import com.xiang.xservice.auth.api.dto.resp.LoginResp; import com.xiang.xservice.auth.api.dto.resp.RegisterResp; import com.xiang.xservice.auth.api.dto.resp.UserDTO; @@ -12,7 +13,6 @@ import com.xiang.xservice.basic.common.resp.Result; import com.xiang.xservice.basic.exception.BusinessException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -73,9 +73,8 @@ public class TokenController implements TokenApi { return Result.error("操作失败"); } - @GetMapping("/private/auth/getInfo") - public Result getUserInfo() { - // todo token的工具类,直接获取token中的userId无需传参 - return Result.data(userService.getUserDetail(1L)); + @PostMapping("/private/auth/getInfo") + public Result getUserInfo(@RequestBody @Valid @NotNull(message = "请求参数不能为空") UserQueryRequest request) { + return Result.data(userService.getUserDetail(request.getUsername())); } } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/UserConstants.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/UserConstants.java new file mode 100644 index 0000000..1077e6d --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/UserConstants.java @@ -0,0 +1,88 @@ +package com.xiang.xservice.auth.service.constants; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色正常状态 */ + public static final String ROLE_NORMAL = "0"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + + public static final int PASSWORD_MAX_LENGTH = 20; + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/convert/XMenuConverter.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/convert/XMenuConverter.java new file mode 100644 index 0000000..0237531 --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/convert/XMenuConverter.java @@ -0,0 +1,18 @@ +package com.xiang.xservice.auth.service.convert; + +import com.xiang.xservice.auth.api.dto.resp.MenuVO; +import com.xiang.xservice.auth.service.entity.XMenuDO; +import org.mapstruct.Mapper; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:39 + */ +@Mapper(componentModel = "spring") +public interface XMenuConverter { + + + List toVoList(List xMenuDOS); +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XMenuDO.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XMenuDO.java new file mode 100644 index 0000000..8502c9c --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XMenuDO.java @@ -0,0 +1,72 @@ +package com.xiang.xservice.auth.service.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:21 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("x_menu") +public class XMenuDO { + + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */ + private String routeName; + + /** 是否为外链(0是 1否) */ + private Integer isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private Integer isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + private String createBy; + private LocalDateTime createTime; + private String updateBy; + private LocalDateTime updateTime; + private String remark; + +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java index 6e7d4f8..45dbb0a 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java @@ -104,4 +104,9 @@ public class XUser implements Serializable { * 刷新token */ private String refreshToken; + /** + * 1:后台用户、2:中台用户、3:前台用户 + */ + private Integer userType; + } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XMenuMapper.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XMenuMapper.java new file mode 100644 index 0000000..393e781 --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XMenuMapper.java @@ -0,0 +1,18 @@ +package com.xiang.xservice.auth.service.repository.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.xservice.auth.service.entity.XMenuDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:31 + */ +@Mapper +@Repository +public interface XMenuMapper extends BaseMapper { + List selectMenuTreeByUserId(Long userId); +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserMapper.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserMapper.java index 1b74c55..4754798 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserMapper.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserMapper.java @@ -21,13 +21,6 @@ public interface XUserMapper extends BaseMapper { */ XUser selectByUsername(String username); - /** - * 新增用户 - * @param user 用户 - * @return - */ - int insert(XUser user); - /** * 批量插入 * @param list diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XMenuService.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XMenuService.java new file mode 100644 index 0000000..9f4dd2a --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XMenuService.java @@ -0,0 +1,14 @@ +package com.xiang.xservice.auth.service.service; + +import com.xiang.xservice.auth.api.dto.resp.RouterVo; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:30 + */ +public interface XMenuService { + + List getRouter(Long userId); +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XUserService.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XUserService.java index d41a52e..d254e65 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XUserService.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/XUserService.java @@ -38,7 +38,7 @@ public interface XUserService { Boolean setUserRole(UserRoleUpdateRequest request); - UserDTO getUserDetail(Long userId); + UserDTO getUserDetail(String username); LoginResp refresh(RefreshRequest request); } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XMenuServiceImpl.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XMenuServiceImpl.java new file mode 100644 index 0000000..272c153 --- /dev/null +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XMenuServiceImpl.java @@ -0,0 +1,261 @@ +package com.xiang.xservice.auth.service.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; +import com.xiang.xservice.auth.api.dto.resp.MenuVO; +import com.xiang.xservice.auth.api.dto.resp.MetaVo; +import com.xiang.xservice.auth.api.dto.resp.RouterVo; +import com.xiang.xservice.auth.service.constants.UserConstants; +import com.xiang.xservice.auth.service.convert.XMenuConverter; +import com.xiang.xservice.auth.service.entity.XMenuDO; +import com.xiang.xservice.auth.service.repository.mapper.XMenuMapper; +import com.xiang.xservice.auth.service.service.XMenuService; +import com.xiang.xservice.basic.constants.Constants; +import com.xiang.xservice.basic.utils.MyStringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * @Author: xiang + * @Date: 2026-03-20 15:30 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class XMenuServiceImpl implements XMenuService { + + private final XMenuMapper menuMapper; + private final XMenuConverter menuConverter; + + @Override + public List getRouter(Long userId) { + List xMenuDOS = Lists.newArrayList(); + // 超级管理员 admin + if (Objects.equals(userId, 1L)) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(XMenuDO::getStatus, 0); + lambdaQueryWrapper.in(XMenuDO::getMenuType, 'M', 'C'); + lambdaQueryWrapper.orderByAsc(XMenuDO::getParentId, XMenuDO::getOrderNum); + xMenuDOS = menuMapper.selectList(lambdaQueryWrapper); + } else { + xMenuDOS = menuMapper.selectMenuTreeByUserId(userId); + } + return buildMenus(getChildPerms(menuConverter.toVoList(xMenuDOS), 0)); + } + + public List buildMenus(List menus) { + List routers = new LinkedList(); + for (MenuVO menu : menus) { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (CollectionUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (isMenuFrame(menu)) { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(getRouteName(menu.getRouteName(), menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(getRouteName(menu.getRouteName(), routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + public String getRouteName(MenuVO menu) { + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) { + return StringUtils.EMPTY; + } + return getRouteName(menu.getRouteName(), menu.getPath()); + } + + /** + * 获取路由名称,如没有配置路由名称则取路由地址 + * + * @param name 路由名称 + * @param path 路由地址 + * @return 路由名称(驼峰格式) + */ + public String getRouteName(String name, String path) { + String routerName = StringUtils.isNotEmpty(name) ? name : path; + return StringUtils.capitalize(routerName); + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(MenuVO menu) { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(MenuVO menu) { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) { + component = menu.getComponent(); + } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + component = UserConstants.INNER_LINK; + } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(MenuVO menu) { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(MenuVO menu) { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && MyStringUtils.isHttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(MenuVO menu) { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext(); ) { + MenuVO t = (MenuVO) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List list, MenuVO t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (MenuVO tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, MenuVO t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + MenuVO n = (MenuVO) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, MenuVO t) { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) { + return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"}, + new String[]{"", "", "", "/", "/"}); + } +} diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java index ec6ee80..08d118d 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.xiang.xmc.service.cache.service.IRedisService; import com.xiang.xservice.auth.api.code.Code01UserErrorCode; import com.xiang.xservice.auth.api.code.Code02RoleErrorCode; @@ -44,10 +45,13 @@ import com.xiang.xservice.auth.service.service.XUserService; import com.xiang.xservice.basic.enums.DelStatusEnum; import com.xiang.xservice.basic.exception.BusinessException; import com.xiang.xservice.basic.utils.PrimaryKeyUtils; +import com.xiang.xservice.basic.utils.RandomCodeUtils; +import com.xiang.xservice.basic.utils.SnowflakeIdGenerator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.dao.DuplicateKeyException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -211,8 +215,16 @@ public class XUserServiceImpl implements XUserService { user.setCreateTime(LocalDateTime.now()); user.setUpdateBy("admin"); user.setUpdateTime(LocalDateTime.now()); + user.setUserType(2); + user.setTenantId(8000000000000000L + SnowflakeIdGenerator.of16(RandomCodeUtils.getRandomNumber(1)).nextId()); - if (userMapper.insert(user) > 0) { + int i = 0; + try { + i = userMapper.insert(user); + } catch (DuplicateKeyException e) { + throw new BusinessException(Code01UserErrorCode.USER_EXISTS); + } + if (i > 0) { RegisterResp registerResp = new RegisterResp(); registerResp.setName(user.getName()); registerResp.setUsername(user.getUsername()); @@ -290,21 +302,21 @@ public class XUserServiceImpl implements XUserService { } @Override - public UserDTO getUserDetail(Long userId) { + public UserDTO getUserDetail(String username) { UserDTO dto = new UserDTO(); - XUser user = userMapper.getUserById(userId); + XUser user = userMapper.selectByUsername(username); if (Objects.isNull(user)) { throw new BusinessException(Code01UserErrorCode.USER_NOT_EXISTS); } dto.setUser(userConvert.toResp(user)); - List userRoles = userRoleMapper.getByUserId(userId); + List userRoles = userRoleMapper.getByUserId(user.getId()); List roleIds = userRoles.stream().map(XUserRole::getRoleId).toList(); List roles = roleMapper.getRoleByIds(roleIds); if (CollectionUtils.isEmpty(roles)) { throw new BusinessException(Code02RoleErrorCode.ROLE_NOT_EXISTS); } dto.setRoles(roleConvert.toDTOList(roles)); - XUserDept userDept = userDeptMapper.getByUserId(userId); + XUserDept userDept = userDeptMapper.getByUserId(user.getId()); if (Objects.nonNull(userDept)) { Long deptId = userDept.getDeptId(); XDept dept = deptMapper.getDeptById(deptId); @@ -319,6 +331,7 @@ public class XUserServiceImpl implements XUserService { List permissions = permissionMapper.getPermissionByIds(permissionIds); dto.setPermissionRoles(permissionConvert.toDTOList(permissions)); } + dto.setPermissions(Sets.newHashSet("*:*:*")); return dto; } diff --git a/xs-service/src/main/resources/mapper/user/XMenuMapper.xml b/xs-service/src/main/resources/mapper/user/XMenuMapper.xml new file mode 100644 index 0000000..fc5b6e7 --- /dev/null +++ b/xs-service/src/main/resources/mapper/user/XMenuMapper.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from x_menu + + + + + \ No newline at end of file diff --git a/xs-service/src/main/resources/mapper/user/XUserMapper.xml b/xs-service/src/main/resources/mapper/user/XUserMapper.xml index a64f815..0b0ce2f 100644 --- a/xs-service/src/main/resources/mapper/user/XUserMapper.xml +++ b/xs-service/src/main/resources/mapper/user/XUserMapper.xml @@ -44,116 +44,6 @@ token, refresh_token - - - INSERT INTO x_user - - - name, - - - username, - - - password, - - - email, - - - phone, - - - avatar, - - - login_ip, - - - login_date, - - - del_flag, - - - status, - - - create_by, - - - create_time, - - - update_by, - - - update_time, - - - tenant_id, - - - token, - - - refreshToken - - - - - #{name}, - - - #{username}, - - - #{password}, - - - #{email}, - - - #{phone}, - - - #{avatar}, - - - #{loginIp}, - - - #{loginDate}, - - - #{delFlag}, - - - #{status}, - - - #{createBy}, - - - #{createTime}, - - - #{updateBy}, - - - #{updateTime}, - - - #{tenantId}, - - - #{token}, - - - #{refreshToken} - - - insert into x_user(name, username, password, email, phone, status, tenant_id) VALUES @@ -197,7 +87,7 @@