feat:成功登录

This commit is contained in:
Xiang
2026-03-20 17:15:56 +08:00
parent 816dfb2304
commit e7f90b8d97
21 changed files with 942 additions and 129 deletions

View File

@@ -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;
}
}

View File

@@ -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<MenuVO> toVoList(List<XMenuDO> xMenuDOS);
}

View File

@@ -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;
}

View File

@@ -104,4 +104,9 @@ public class XUser implements Serializable {
* 刷新token
*/
private String refreshToken;
/**
* 1后台用户、2中台用户、3前台用户
*/
private Integer userType;
}

View File

@@ -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<XMenuDO> {
List<XMenuDO> selectMenuTreeByUserId(Long userId);
}

View File

@@ -21,13 +21,6 @@ public interface XUserMapper extends BaseMapper<XUser> {
*/
XUser selectByUsername(String username);
/**
* 新增用户
* @param user 用户
* @return
*/
int insert(XUser user);
/**
* 批量插入
* @param list

View File

@@ -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<RouterVo> getRouter(Long userId);
}

View File

@@ -38,7 +38,7 @@ public interface XUserService {
Boolean setUserRole(UserRoleUpdateRequest request);
UserDTO getUserDetail(Long userId);
UserDTO getUserDetail(String username);
LoginResp refresh(RefreshRequest request);
}

View File

@@ -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<RouterVo> getRouter(Long userId) {
List<XMenuDO> xMenuDOS = Lists.newArrayList();
// 超级管理员 admin
if (Objects.equals(userId, 1L)) {
LambdaQueryWrapper<XMenuDO> 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<RouterVo> buildMenus(List<MenuVO> menus) {
List<RouterVo> routers = new LinkedList<RouterVo>();
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<MenuVO> 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<RouterVo> childrenList = new ArrayList<RouterVo>();
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<RouterVo> childrenList = new ArrayList<RouterVo>();
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<MenuVO> getChildPerms(List<MenuVO> list, int parentId) {
List<MenuVO> returnList = new ArrayList<MenuVO>();
for (Iterator<MenuVO> 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<MenuVO> list, MenuVO t) {
// 得到子节点列表
List<MenuVO> childList = getChildList(list, t);
t.setChildren(childList);
for (MenuVO tChild : childList) {
if (hasChild(list, tChild)) {
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<MenuVO> getChildList(List<MenuVO> list, MenuVO t) {
List<MenuVO> tlist = new ArrayList<MenuVO>();
Iterator<MenuVO> 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<MenuVO> 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[]{"", "", "", "/", "/"});
}
}

View File

@@ -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<XUserRole> userRoles = userRoleMapper.getByUserId(userId);
List<XUserRole> userRoles = userRoleMapper.getByUserId(user.getId());
List<Long> roleIds = userRoles.stream().map(XUserRole::getRoleId).toList();
List<XRole> 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<XPermission> permissions = permissionMapper.getPermissionByIds(permissionIds);
dto.setPermissionRoles(permissionConvert.toDTOList(permissions));
}
dto.setPermissions(Sets.newHashSet("*:*:*"));
return dto;
}

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiang.xservice.auth.service.repository.mapper.XMenuMapper">
<resultMap type="com.xiang.xservice.auth.service.entity.XMenuDO" id="SysMenuResult">
<id property="menuId" column="menu_id" />
<result property="menuName" column="menu_name" />
<result property="parentName" column="parent_name" />
<result property="parentId" column="parent_id" />
<result property="orderNum" column="order_num" />
<result property="path" column="path" />
<result property="component" column="component" />
<result property="query" column="query" />
<result property="routeName" column="route_name" />
<result property="isFrame" column="is_frame" />
<result property="isCache" column="is_cache" />
<result property="menuType" column="menu_type" />
<result property="visible" column="visible" />
<result property="status" column="status" />
<result property="perms" column="perms" />
<result property="icon" column="icon" />
</resultMap>
<sql id="selectMenuVo">
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
</sql>
<select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
select distinct m.menu_id,
m.parent_id,
m.menu_name,
m.path,
m.component,
m.`query`,
m.route_name,
m.visible,
m.status,
ifnull(m.perms, '') as perms,
m.is_frame,
m.is_cache,
m.menu_type,
m.icon,
m.order_num,
m.create_time
from x_menu m
left join x_role_menu rm on m.menu_id = rm.menu_id
left join x_user_role ur on rm.role_id = ur.role_id
left join x_role ro on ur.role_id = ro.id
left join x_user u on ur.user_id = u.id
where u.id = #{userId}
and m.menu_type in ('M', 'C')
and m.status = 0
AND ro.status = 0
order by m.parent_id, m.order_num
</select>
</mapper>

View File

@@ -44,116 +44,6 @@
token,
refresh_token
</sql>
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id" parameterType="com.xiang.xservice.auth.service.entity.XUser">
INSERT INTO x_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="null != name and '' != name">
name,
</if>
<if test="null != username and '' != username">
username,
</if>
<if test="null != password and '' != password">
password,
</if>
<if test="null != email and '' != email">
email,
</if>
<if test="null != phone and '' != phone">
phone,
</if>
<if test="null != avatar and '' != avatar">
avatar,
</if>
<if test="null != loginIp and '' != loginIp">
login_ip,
</if>
<if test="null != loginDate ">
login_date,
</if>
<if test="null != delFlag ">
del_flag,
</if>
<if test="null != status ">
status,
</if>
<if test="null != createBy and '' != createBy">
create_by,
</if>
<if test="null != createTime ">
create_time,
</if>
<if test="null != updateBy and '' != updateBy">
update_by,
</if>
<if test="null != updateTime ">
update_time,
</if>
<if test="tenantId != null">
tenant_id,
</if>
<if test="token != null and token != ''">
token,
</if>
<if test="refreshToken != null and refreshToken != ''">
refreshToken
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="null != name and '' != name">
#{name},
</if>
<if test="null != username and '' != username">
#{username},
</if>
<if test="null != password and '' != password">
#{password},
</if>
<if test="null != email and '' != email">
#{email},
</if>
<if test="null != phone and '' != phone">
#{phone},
</if>
<if test="null != avatar and '' != avatar">
#{avatar},
</if>
<if test="null != loginIp and '' != loginIp">
#{loginIp},
</if>
<if test="null != loginDate ">
#{loginDate},
</if>
<if test="null != delFlag ">
#{delFlag},
</if>
<if test="null != status ">
#{status},
</if>
<if test="null != createBy and '' != createBy">
#{createBy},
</if>
<if test="null != createTime ">
#{createTime},
</if>
<if test="null != updateBy and '' != updateBy">
#{updateBy},
</if>
<if test="null != updateTime ">
#{updateTime},
</if>
<if test="tenantId != null">
#{tenantId},
</if>
<if test="token != null and token != ''">
#{token},
</if>
<if test="refreshToken != null and refreshToken != ''">
#{refreshToken}
</if>
</trim>
</insert>
<insert id="insertBatch">
insert into x_user(name, username, password, email, phone, status, tenant_id) VALUES
<foreach collection="list" item="item" separator=",">
@@ -197,7 +87,7 @@
<select id="selectByUsername" resultMap="BaseResultMap">
select <include refid="Base_Column_List"/>
from x_user
where username = #{username} and del_flag = 0 and status = 1
where username = #{username} and del_flag = 0
</select>
<select id="getUserList" resultMap="BaseResultMap">