diff --git a/pom.xml b/pom.xml
index 9c9267d..3ba6100 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,6 +145,12 @@
sa-token-spring-boot3-starter
${sa-token.version}
+
+
+ cn.dev33
+ sa-token-redis-jackson
+ ${sa-token.version}
+
org.fusesource.jansi
diff --git a/xiaoyi-auth/pom.xml b/xiaoyi-auth/pom.xml
index aaca05d..d1544ad 100644
--- a/xiaoyi-auth/pom.xml
+++ b/xiaoyi-auth/pom.xml
@@ -64,6 +64,10 @@
cn.dev33
sa-token-spring-boot3-starter
+
+ cn.dev33
+ sa-token-redis-jackson
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RedisKeyConstants.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RedisKeyConstants.java
index 2f60fc2..3be0606 100644
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RedisKeyConstants.java
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RedisKeyConstants.java
@@ -19,9 +19,15 @@ public class RedisKeyConstants {
* 验证码 KEY 过期时间 (分钟)
*/
public static final long VERIFICATION_CODE_EXPIRE_TIME = 3;
+
+ /**
+ * 用户角色数据 KEY 前缀
+ */
+ private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
+
/**
* 构建验证码 KEY
- *
+ *
* @param phone 手机号
* @return 验证码 KEY
*/
@@ -29,4 +35,14 @@ public class RedisKeyConstants {
return VERIFICATION_CODE_KEY_PREFIX + phone;
}
+ /**
+ * 构建用户角色数据 KEY
+ *
+ * @param userId 用户 ID
+ * @return 用户角色数据 KEY
+ */
+ public static String buildUserRolesKey(Long userId) {
+ return USER_ROLES_KEY_PREFIX + userId;
+ }
+
}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RoleConstants.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RoleConstants.java
new file mode 100644
index 0000000..2ba624b
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/constant/RoleConstants.java
@@ -0,0 +1,16 @@
+package top.crushtj.xiaoyishu.auth.constant;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title RoleConstants
+ * @date 2026/01/18 21:24
+ * @description 角色常量
+ */
+
+public class RoleConstants {
+ /**
+ * 普通用户的角色 ID
+ */
+ public static final Long COMMON_USER_ROLE_ID = 1L;
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/TestController.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/TestController.java
deleted file mode 100644
index 1e0a44d..0000000
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/TestController.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package top.crushtj.xiaoyishu.auth.controller;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.validation.annotation.Validated;
-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.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
-import top.crushtj.framework.common.response.Response;
-import top.crushtj.xiaoyishu.auth.model.vo.User;
-
-import cn.dev33.satoken.stp.StpUtil;
-
-import java.time.LocalDateTime;
-
-/**
- *
- * @author ayi
- * @title TestController
- * @description 测试控制器
- * @date 2025/11/21
- */
-
-@Slf4j
-@RestController
-public class TestController {
-
- @GetMapping("/test")
- @ApiOperationLog(description = "测试接口")
- public Response testController() {
- return Response.success(User.builder().id(1L).name("ayi").age(18).createTime(LocalDateTime.now()).build());
- }
-
- @PostMapping("/test2")
- @ApiOperationLog(description = "测试接口2")
- public Response test2(@RequestBody @Validated User user) {
- // int i = 1 / 0;
- return Response.success(user);
- }
-
- @RequestMapping("/user/doLogin")
- public String doLogin(String username, String password) {
- // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
- if ("ayi".equals(username) && "12345678".equals(password)) {
- StpUtil.login(10001);
- return "登录成功";
- }
- return "登录失败";
- }
-
- // 查询登录状态,浏览器访问: http://localhost:8080/user/isLogin
- @RequestMapping("/user/isLogin")
- public String isLogin() {
- return "当前会话是否登录:" + StpUtil.isLogin();
- }
-
-}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/UserController.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/UserController.java
new file mode 100644
index 0000000..ca64f25
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/controller/UserController.java
@@ -0,0 +1,37 @@
+package top.crushtj.xiaoyishu.auth.controller;
+
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
+import top.crushtj.framework.common.response.Response;
+import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
+import top.crushtj.xiaoyishu.auth.service.UserService;
+
+ /**
+ * @author ayi
+ * @version V1.0
+ * @title UserController
+ * @date 2026-01-18 20:40:21
+ * @description 用户表(User)表控制层
+*/
+
+@Slf4j
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+ @Resource
+ private UserService userService;
+
+ @PostMapping("/login")
+ @ApiOperationLog(description = "用户登录或注册")
+ public Response loginOrRegister(@RequestBody @Validated UserLoginReqVO userLoginReqVO) {
+ return userService.loginOrRegister(userLoginReqVO);
+ }
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/PermissionEntity.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/PermissionEntity.java
new file mode 100644
index 0000000..1cb3a29
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/PermissionEntity.java
@@ -0,0 +1,104 @@
+package top.crushtj.xiaoyishu.auth.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title PermissionEntity
+ * @date 2026-01-18 21:20:36
+ * @description 权限表(t_permission)表实体类
+ */
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("t_permission")
+public class PermissionEntity implements Serializable {
+ @Serial
+ private static final long serialVersionUID = -18586083527537804L;
+
+ /**
+ * 主键ID
+ */
+ @TableId("ID")
+ private Long id;
+
+ /**
+ * 父ID
+ */
+ @TableField("PARENT_ID")
+ private Long parentId;
+
+ /**
+ * 权限名称
+ */
+ @TableField("NAME")
+ private String name;
+
+ /**
+ * 类型(1:目录 2:菜单 3:按钮)
+ */
+ @TableField("TYPE")
+ private Integer type;
+
+ /**
+ * 菜单路由
+ */
+ @TableField("MENU_URL")
+ private String menuUrl;
+
+ /**
+ * 菜单图标
+ */
+ @TableField("MENU_ICON")
+ private String menuIcon;
+
+ /**
+ * 管理系统中的显示顺序
+ */
+ @TableField("SORT")
+ private Integer sort;
+
+ /**
+ * 权限标识
+ */
+ @TableField("PERMISSION_KEY")
+ private String permissionKey;
+
+ /**
+ * 状态(1:启用;0:禁用)
+ */
+ @TableField("STATUS")
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ @TableField("CREATE_TIME")
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ @TableField("UPDATE_TIME")
+ private LocalDateTime updateTime;
+
+ /**
+ * 逻辑删除(0:未删除 1:已删除)
+ */
+ @TableField("IS_DELETED")
+ private Integer isDeleted;
+
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RoleEntity.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RoleEntity.java
new file mode 100644
index 0000000..7e29833
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RoleEntity.java
@@ -0,0 +1,86 @@
+package top.crushtj.xiaoyishu.auth.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title RoleEntity
+ * @date 2026-01-18 21:20:51
+ * @description 角色表(t_role)表实体类
+ */
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("t_role")
+public class RoleEntity implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 294965117543247928L;
+
+ /**
+ * 主键ID
+ */
+ @TableId("ID")
+ private Long id;
+
+ /**
+ * 角色名
+ */
+ @TableField("ROLE_NAME")
+ private String roleName;
+
+ /**
+ * 角色唯一标识
+ */
+ @TableField("ROLE_KEY")
+ private String roleKey;
+
+ /**
+ * 状态(1:启用 0:禁用)
+ */
+ @TableField("STATUS")
+ private Integer status;
+
+ /**
+ * 管理系统中的显示顺序
+ */
+ @TableField("SORT")
+ private Integer sort;
+
+ /**
+ * 备注
+ */
+ @TableField("REMARK")
+ private String remark;
+
+ /**
+ * 创建时间
+ */
+ @TableField("CREATE_TIME")
+ private LocalDateTime createTime;
+
+ /**
+ * 最后一次更新时间
+ */
+ @TableField("UPDATE_TIME")
+ private LocalDateTime updateTime;
+
+ /**
+ * 逻辑删除(0:未删除 1:已删除)
+ */
+ @TableField("IS_DELETED")
+ private Integer isDeleted;
+
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RolePermissionRelEntity.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RolePermissionRelEntity.java
new file mode 100644
index 0000000..44e5051
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/RolePermissionRelEntity.java
@@ -0,0 +1,68 @@
+package top.crushtj.xiaoyishu.auth.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title RolePermissionRelEntity
+ * @date 2026-01-18 21:21:12
+ * @description 用户权限表(t_role_permission_rel)表实体类
+ */
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("t_role_permission_rel")
+public class RolePermissionRelEntity implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 308157477991435934L;
+
+ /**
+ * 主键ID
+ */
+ @TableId("ID")
+ private Long id;
+
+ /**
+ * 角色ID
+ */
+ @TableField("ROLE_ID")
+ private Long roleId;
+
+ /**
+ * 权限ID
+ */
+ @TableField("PERMISSION_ID")
+ private Long permissionId;
+
+ /**
+ * 创建时间
+ */
+ @TableField("CREATE_TIME")
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ @TableField("UPDATE_TIME")
+ private LocalDateTime updateTime;
+
+ /**
+ * 逻辑删除(0:未删除 1:已删除)
+ */
+ @TableField("IS_DELETED")
+ private Integer isDeleted;
+
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserEntity.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserEntity.java
index 01eebe8..0104496 100644
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserEntity.java
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserEntity.java
@@ -3,8 +3,6 @@ package top.crushtj.xiaoyishu.auth.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -12,14 +10,15 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
+import java.time.LocalDate;
import java.time.LocalDateTime;
/**
- *
* @author ayi
+ * @version V1.0
* @title UserEntity
- * @date 2025/11/27
- * @description 用户测试表
+ * @date 2026/01/18 19:33:00
+ * @description 用户表
*/
@Data
@@ -28,22 +27,74 @@ import java.time.LocalDateTime;
@NoArgsConstructor
@TableName("t_user")
public class UserEntity implements Serializable {
-
@Serial
- private static final long serialVersionUID = 403394297290102294L;
+ private static final long serialVersionUID = -31680834394879938L;
/**
- * 主键id
+ * 主键ID
*/
- @JsonSerialize(using = ToStringSerializer.class)
@TableId("ID")
private Long id;
/**
- * 用户名
+ * 小壹书号(唯一凭证)
*/
- @TableField("USERNAME")
- private String username;
+ @TableField("XIAOYISHU_ID")
+ private String xiaoyishuId;
+
+ /**
+ * 密码
+ */
+ @TableField("PASSWORD")
+ private String password;
+
+ /**
+ * 昵称
+ */
+ @TableField("NICKNAME")
+ private String nickname;
+
+ /**
+ * 头像
+ */
+ @TableField("AVATAR")
+ private String avatar;
+
+ /**
+ * 生日
+ */
+ @TableField("BIRTHDAY")
+ private LocalDate birthday;
+
+ /**
+ * 背景图
+ */
+ @TableField("BACKGROUND_IMG")
+ private String backgroundImg;
+
+ /**
+ * 手机号
+ */
+ @TableField("PHONE")
+ private String phone;
+
+ /**
+ * 性别(1: 男 2: 女 3: 未知)
+ */
+ @TableField("SEX")
+ private Integer sex;
+
+ /**
+ * 状态(1:启用 0:禁用)
+ */
+ @TableField("STATUS")
+ private Integer status;
+
+ /**
+ * 个人简介
+ */
+ @TableField("INTRODUCTION")
+ private String introduction;
/**
* 创建时间
@@ -57,4 +108,10 @@ public class UserEntity implements Serializable {
@TableField("UPDATE_TIME")
private LocalDateTime updateTime;
+ /**
+ * 逻辑删除(0:未删除 1:已删除)
+ */
+ @TableField("IS_DELETED")
+ private Integer isDeleted;
+
}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserRoleRelEntity.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserRoleRelEntity.java
new file mode 100644
index 0000000..001ecfc
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/entity/UserRoleRelEntity.java
@@ -0,0 +1,68 @@
+package top.crushtj.xiaoyishu.auth.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title UserRoleRelEntity
+ * @date 2026-01-18 21:21:25
+ * @description 用户角色表(t_user_role_rel)表实体类
+ */
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("t_user_role_rel")
+public class UserRoleRelEntity implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 788758889499348222L;
+
+ /**
+ * 主键ID
+ */
+ @TableId("ID")
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ @TableField("USER_ID")
+ private Long userId;
+
+ /**
+ * 角色ID
+ */
+ @TableField("ROLE_ID")
+ private Long roleId;
+
+ /**
+ * 创建时间
+ */
+ @TableField("CREATE_TIME")
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ @TableField("UPDATE_TIME")
+ private LocalDateTime updateTime;
+
+ /**
+ * 逻辑删除(0:未删除 1:已删除)
+ */
+ @TableField("IS_DELETED")
+ private Integer isDeleted;
+
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/PermissionMapper.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/PermissionMapper.java
new file mode 100644
index 0000000..c2266a4
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/PermissionMapper.java
@@ -0,0 +1,16 @@
+package top.crushtj.xiaoyishu.auth.domain.mappers;
+
+import top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title PermissionMapper
+ * @date 2026-01-18 21:20:37
+ * @description 权限表(t_permission)表数据库访问层
+ */
+public interface PermissionMapper extends BaseMapper {
+
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RoleMapper.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RoleMapper.java
new file mode 100644
index 0000000..77b6d4a
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RoleMapper.java
@@ -0,0 +1,16 @@
+package top.crushtj.xiaoyishu.auth.domain.mappers;
+
+import top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title RoleMapper
+ * @date 2026-01-18 21:20:51
+ * @description 角色表(t_role)表数据库访问层
+ */
+public interface RoleMapper extends BaseMapper {
+
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RolePermissionRelMapper.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RolePermissionRelMapper.java
new file mode 100644
index 0000000..c975274
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/RolePermissionRelMapper.java
@@ -0,0 +1,16 @@
+package top.crushtj.xiaoyishu.auth.domain.mappers;
+
+import top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title RolePermissionRelMapper
+ * @date 2026-01-18 21:21:12
+ * @description 用户权限表(t_role_permission_rel)表数据库访问层
+ */
+public interface RolePermissionRelMapper extends BaseMapper {
+
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserMapper.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserMapper.java
index 25058f9..b8292d2 100644
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserMapper.java
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserMapper.java
@@ -1,16 +1,18 @@
package top.crushtj.xiaoyishu.auth.domain.mappers;
+import org.apache.ibatis.annotations.Param;
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
- *
* @author ayi
- * @title 用户测试表
- * @date 2025/11/27
- * @description (t_user)表数据库访问层
+ * @version V1.0
+ * @title User
+ * @date 2026/01/18 19:33:00
+ * @description 用户表(t_user)表数据库访问层
*/
public interface UserMapper extends BaseMapper {
+ UserEntity selectByPhone(@Param("phone") String phone);
}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserRoleRelMapper.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserRoleRelMapper.java
new file mode 100644
index 0000000..57fd697
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappers/UserRoleRelMapper.java
@@ -0,0 +1,16 @@
+package top.crushtj.xiaoyishu.auth.domain.mappers;
+
+import top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title UserRoleRelMapper
+ * @date 2026-01-18 21:21:25
+ * @description 用户角色表(t_user_role_rel)表数据库访问层
+ */
+public interface UserRoleRelMapper extends BaseMapper {
+
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/PermissionMapping.xml b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/PermissionMapping.xml
new file mode 100644
index 0000000..19e8acd
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/PermissionMapping.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RoleMapping.xml b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RoleMapping.xml
new file mode 100644
index 0000000..cbff99d
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RoleMapping.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RolePermissionRelMapping.xml b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RolePermissionRelMapping.xml
new file mode 100644
index 0000000..1f8c670
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/RolePermissionRelMapping.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserMapping.xml b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserMapping.xml
index d4da605..21ab9d6 100644
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserMapping.xml
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserMapping.xml
@@ -1,16 +1,27 @@
-
+
-
-
-
-
-
-
-
-
- id,username,create_time,update_time
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserRoleRelMapping.xml b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserRoleRelMapping.xml
new file mode 100644
index 0000000..048a945
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/domain/mappings/UserRoleRelMapping.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/LoginTypeEnum.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/LoginTypeEnum.java
new file mode 100644
index 0000000..a38dfaa
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/LoginTypeEnum.java
@@ -0,0 +1,34 @@
+package top.crushtj.xiaoyishu.auth.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title LoginTypeEnum
+ * @date 2026/01/18 20:15
+ * @description 登录类型枚举
+ */
+
+@Getter
+@AllArgsConstructor
+public enum LoginTypeEnum {
+ // 验证码
+ VERIFICATION_CODE(1),
+ // 密码
+ PASSWORD(2);
+
+ private final Integer value;
+
+ public static LoginTypeEnum valueOf(Integer code) {
+ for (LoginTypeEnum loginTypeEnum : LoginTypeEnum.values()) {
+ if (Objects.equals(code, loginTypeEnum.getValue())) {
+ return loginTypeEnum;
+ }
+ }
+ return null;
+ }
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/ResponseCodeEnum.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/ResponseCodeEnum.java
index 096307b..99e8661 100644
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/ResponseCodeEnum.java
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/enums/ResponseCodeEnum.java
@@ -16,10 +16,13 @@ import top.crushtj.framework.common.exception.BaseExceptionInterface;
@AllArgsConstructor
public enum ResponseCodeEnum implements BaseExceptionInterface {
// -------- 通用异常状态码 --------
- SYSTEM_ERROR("AUTH-10000", "系统错误"), PARAM_NOT_VALID("AUTH-10001", "参数错误"), // -------- 验证码异常状态码 --------
+ SYSTEM_ERROR("AUTH-10000", "系统错误"),
+ PARAM_NOT_VALID("AUTH-10001", "参数错误"), // -------- 验证码异常状态码 --------
VERIFICATION_CODE_SEND_FREQUENTLY("AUTH-20000", "请求太频繁,请3分钟后再试"),
SMS_SEND_FAILED("AUTH-20001", "短信发送失败,请稍后再试"),
- SMS_SEND_TIMEOUT("AUTH-20002", "短信发送超时,请稍后再试");
+ SMS_SEND_TIMEOUT("AUTH-20002", "短信发送超时,请稍后再试"),
+ VERIFICATION_CODE_ERROR("AUTH-20003", "验证码错误"),
+ ;
/**
* 错误码
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/User.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/User.java
deleted file mode 100644
index 32ffa8b..0000000
--- a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/User.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package top.crushtj.xiaoyishu.auth.model.vo;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.io.Serializable;
-import java.time.LocalDateTime;
-
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-
-/**
- *
- * @author ayi
- * @title User
- * @description 测试用户类
- * @date 2025/11/26
- */
-
-@Data
-@Builder
-@AllArgsConstructor
-@NoArgsConstructor
-public class User implements Serializable {
- @NotNull(message = "用户ID不能为空")
- private Long id;
- @NotBlank(message = "用户名称不能为空")
- private String name;
- private Integer age;
- private LocalDateTime createTime;
-
-}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/user/UserLoginReqVO.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/user/UserLoginReqVO.java
new file mode 100644
index 0000000..5471da7
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/model/vo/user/UserLoginReqVO.java
@@ -0,0 +1,51 @@
+package top.crushtj.xiaoyishu.auth.model.vo.user;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import top.crushtj.framework.common.validator.PhoneNumber;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title UserVo
+ * @date 2026/01/18 20:13
+ * @description 用户登录参数
+ */
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class UserLoginReqVO implements Serializable {
+ @Serial
+ private static final long serialVersionUID = -2349922278492431614L;
+ /**
+ * 手机号
+ */
+ @NotBlank(message = "手机号不能为空")
+ @PhoneNumber
+ private String phone;
+
+ /**
+ * 验证码
+ */
+ private String code;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 登录类型:手机号验证码,或者是账号密码
+ */
+ @NotNull(message = "登录类型不能为空")
+ private Integer type;
+}
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/UserService.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/UserService.java
new file mode 100644
index 0000000..c1041ea
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/UserService.java
@@ -0,0 +1,26 @@
+package top.crushtj.xiaoyishu.auth.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import top.crushtj.framework.common.response.Response;
+import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
+import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title UserService
+ * @date 2026-01-18 20:09:57
+ * @description 用户表(t_user)表服务接口
+ */
+
+public interface UserService extends IService {
+
+ /**
+ * 登录或注册
+ *
+ * @param userLoginReqVO 登录或注册参数
+ * @return 登录结果
+ */
+ Response loginOrRegister(UserLoginReqVO userLoginReqVO);
+}
+
diff --git a/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/impl/UserServiceImpl.java b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..6e90d5e
--- /dev/null
+++ b/xiaoyi-auth/src/main/java/top/crushtj/xiaoyishu/auth/service/impl/UserServiceImpl.java
@@ -0,0 +1,87 @@
+package top.crushtj.xiaoyishu.auth.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import top.crushtj.framework.common.exception.BizException;
+import top.crushtj.framework.common.response.Response;
+import top.crushtj.framework.common.utils.MaskUtils;
+import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
+import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
+import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
+import top.crushtj.xiaoyishu.auth.enums.LoginTypeEnum;
+import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
+import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
+import top.crushtj.xiaoyishu.auth.service.UserService;
+
+import java.util.Objects;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title UserServiceImpl
+ * @date 2026-01-18 20:09:58
+ * @description 用户表(t_user)表服务实现类
+ */
+
+@Slf4j
+@Service("userService")
+public class UserServiceImpl extends ServiceImpl implements UserService {
+ @Resource
+ private UserMapper userMapper;
+ @Resource
+ private RedisTemplate redisTemplate;
+
+ @Override
+ public Response loginOrRegister(UserLoginReqVO userLoginReqVO) {
+ String phone = userLoginReqVO.getPhone();
+ Integer type = userLoginReqVO.getType();
+ Long userId = null;
+ LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);
+ switch (loginTypeEnum) {
+ case PASSWORD -> {
+ String password = userLoginReqVO.getPassword();
+ // 密码登录
+
+ }
+ case VERIFICATION_CODE -> {
+ String verificationCode = userLoginReqVO.getCode();
+ // 验证码登录
+ // 校验入参验证码是否为空
+ if (StringUtils.isBlank(verificationCode)) {
+ return Response.failure(ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(), "验证码不能为空");
+ }
+
+ // 构建验证码 Redis Key
+ String key = RedisKeyConstants.buildVerificationCodeKey(phone);
+ // 查询存储在 Redis 中该用户的登录验证码
+ String sentCode = (String)redisTemplate.opsForValue().get(key);
+
+ // 判断用户提交的验证码,与 Redis 中的验证码是否一致
+ if (!StringUtils.equals(verificationCode, sentCode)) {
+ throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
+ }
+
+ // 通过手机号查询记录
+ UserEntity userEntity = userMapper.selectByPhone(phone);
+
+ log.info("==> 用户是否注册, phone: {}, userId: {}", MaskUtils.maskMobile(phone), userEntity.getId());
+
+ // 判断是否注册
+ if (Objects.isNull(userEntity)) {
+ // 若此用户还没有注册,系统自动注册该用户
+ // todo 自动注册用户逻辑
+
+ } else {
+ // 已注册,则获取其用户 ID
+ userId = userEntity.getId();
+ }
+ }
+ }
+ return Response.success("");
+ }
+}
+
diff --git a/xiaoyi-auth/src/main/resources/scripts/initData.sql b/xiaoyi-auth/src/main/resources/scripts/initData.sql
new file mode 100644
index 0000000..19115a3
--- /dev/null
+++ b/xiaoyi-auth/src/main/resources/scripts/initData.sql
@@ -0,0 +1,17 @@
+-- Description: 初始化权限数据-权限管理未开发,用于测试
+INSERT INTO `t_permission` (`id`, `parent_id`, `name`, `type`, `menu_url`, `menu_icon`, `sort`,
+ `permission_key`, `status`, `create_time`, `update_time`, `is_deleted`)
+VALUES (1, 0, '发布笔记', 3, '', '', 1, 'app:note:publish', 1, NOW(), NOW(), b'0');
+INSERT INTO `t_permission` (`id`, `parent_id`, `name`, `type`, `menu_url`, `menu_icon`, `sort`,
+ `permission_key`, `status`, `create_time`, `update_time`, `is_deleted`)
+VALUES (2, 0, '发布评论', 3, '', '', 2, 'app:comment:publish', 1, NOW(), NOW(), b'0');
+
+INSERT INTO `t_role` (`id`, `role_name`, `role_key`, `status`, `sort`, `remark`, `create_time`, `update_time`,
+ `is_deleted`)
+VALUES (1, '普通用户', 'common_user', 1, 1, '', NOW(), NOW(), b'0');
+
+INSERT INTO `t_role_permission_rel` (`id`, `role_id`, `permission_id`, `create_time`, `update_time`, `is_deleted`)
+VALUES (1, 1, 1, NOW(), NOW(), b'0');
+INSERT INTO `t_role_permission_rel` (`id`, `role_id`, `permission_id`, `create_time`, `update_time`, `is_deleted`)
+VALUES (2, 1, 2, NOW(), NOW(), b'0');
+
diff --git a/xiaoyi-auth/src/test/java/top/crushtj/xiaoyishu/auth/XiaoyiAuthApplicationTests.java b/xiaoyi-auth/src/test/java/top/crushtj/xiaoyishu/auth/XiaoyiAuthApplicationTests.java
deleted file mode 100644
index 6b2885b..0000000
--- a/xiaoyi-auth/src/test/java/top/crushtj/xiaoyishu/auth/XiaoyiAuthApplicationTests.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package top.crushtj.xiaoyishu.auth;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
-import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
-
-import java.time.LocalDateTime;
-
-/**
- *
- * @author ayi
- * @title XiaoyiAuthApplicationTests
- * @description 测试类
- * @date 2025/11/20
- */
-
-@SpringBootTest
-@Slf4j
-class XiaoyiAuthApplicationTests {
-
- @Resource
- UserMapper userMapper;
-
- @Test
- void contextLoads() {
- }
-
- @Test
- void testInsert() {
- UserEntity user =
- UserEntity.builder().username("刑加一").createTime(LocalDateTime.now()).updateTime(LocalDateTime.now())
- .build();
-
- userMapper.insert(user);
- }
-
- @Test
- void queryTest() {
- LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(UserEntity::getUsername, "刑加一");
- queryWrapper.orderByDesc(UserEntity::getCreateTime);
- queryWrapper.last("limit 1");
- UserEntity user = userMapper.selectOne(queryWrapper);
- log.info("查询结果:{}", user);
- }
-
-}
diff --git a/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/DeleteEnum.java b/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/DeleteEnum.java
new file mode 100644
index 0000000..7e63eb4
--- /dev/null
+++ b/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/DeleteEnum.java
@@ -0,0 +1,21 @@
+package top.crushtj.framework.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title DeleteEnum
+ * @date 2026/01/18 21:15
+ * @description 删除标记
+ */
+
+@Getter
+@AllArgsConstructor
+public enum DeleteEnum {
+ YES(true),
+ NO(false);
+
+ private final Boolean value;
+}
diff --git a/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/StatusEnum.java b/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/StatusEnum.java
new file mode 100644
index 0000000..2182667
--- /dev/null
+++ b/xiaoyi-framework/xiaoyi-common/src/main/java/top/crushtj/framework/common/enums/StatusEnum.java
@@ -0,0 +1,21 @@
+package top.crushtj.framework.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author ayi
+ * @version V1.0
+ * @title StatusEnum
+ * @date 2026/01/18
+ * @description 状态
+ */
+
+@Getter
+@AllArgsConstructor
+public enum StatusEnum {
+ ENABLED(1),
+ DISABLED(0);
+
+ private final Integer value;
+}