用户注册与登录功能(待完善)
Sync All Branches to GitHub / sync (push) Successful in 3s

This commit is contained in:
2026-01-18 21:29:02 +08:00
parent ffecb1f6d8
commit 31f5a31b9b
31 changed files with 894 additions and 175 deletions
+6
View File
@@ -145,6 +145,12 @@
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>${sa-token.version}</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
+4
View File
@@ -64,6 +64,10 @@
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
</dependency>
<!-- Redis -->
<dependency>
@@ -19,6 +19,12 @@ 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
*
@@ -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;
}
}
@@ -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;
}
@@ -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<User> testController() {
return Response.success(User.builder().id(1L).name("ayi").age(18).createTime(LocalDateTime.now()).build());
}
@PostMapping("/test2")
@ApiOperationLog(description = "测试接口2")
public Response<User> 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();
}
}
@@ -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<String> loginOrRegister(@RequestBody @Validated UserLoginReqVO userLoginReqVO) {
return userService.loginOrRegister(userLoginReqVO);
}
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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<PermissionEntity> {
}
@@ -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<RoleEntity> {
}
@@ -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<RolePermissionRelEntity> {
}
@@ -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> {
UserEntity selectByPhone(@Param("phone") String phone);
}
@@ -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<UserRoleRelEntity> {
}
@@ -0,0 +1,20 @@
<?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 = "top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity" id = "PermissionMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "parentId" column = "parent_id" jdbcType = "INTEGER"/>
<result property = "name" column = "name" jdbcType = "VARCHAR"/>
<result property = "type" column = "type" jdbcType = "INTEGER"/>
<result property = "menuUrl" column = "menu_url" jdbcType = "VARCHAR"/>
<result property = "menuIcon" column = "menu_icon" jdbcType = "VARCHAR"/>
<result property = "sort" column = "sort" jdbcType = "INTEGER"/>
<result property = "permissionKey" column = "permission_key" jdbcType = "VARCHAR"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
</mapper>
@@ -0,0 +1,17 @@
<?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 = "top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity" id = "RoleMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "roleName" column = "role_name" jdbcType = "VARCHAR"/>
<result property = "roleKey" column = "role_key" jdbcType = "VARCHAR"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "sort" column = "sort" jdbcType = "INTEGER"/>
<result property = "remark" column = "remark" jdbcType = "VARCHAR"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
</mapper>
@@ -0,0 +1,14 @@
<?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 = "top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity" id = "RolePermissionRelMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "roleId" column = "role_id" jdbcType = "INTEGER"/>
<result property = "permissionId" column = "permission_id" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
</mapper>
@@ -4,13 +4,24 @@
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.UserEntity" id = "UserMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property = "xiaoyishuId" column = "xiaoyishu_id" jdbcType = "VARCHAR"/>
<result property = "password" column = "password" jdbcType = "VARCHAR"/>
<result property = "nickname" column = "nickname" jdbcType = "VARCHAR"/>
<result property = "avatar" column = "avatar" jdbcType = "VARCHAR"/>
<result property = "birthday" column = "birthday" jdbcType = "TIMESTAMP"/>
<result property = "backgroundImg" column = "background_img" jdbcType = "VARCHAR"/>
<result property = "phone" column = "phone" jdbcType = "VARCHAR"/>
<result property = "sex" column = "sex" jdbcType = "INTEGER"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "introduction" column = "introduction" jdbcType = "VARCHAR"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<!-- 所有列的SQL片段 -->
<sql id="Base_Column_List">
id,username,create_time,update_time
</sql>
<select id = "selectByPhone" resultType = "top.crushtj.xiaoyishu.auth.domain.entity.UserEntity">
SELECT id, password
FROM t_user
WHERE phone = #{phone}
</select>
</mapper>
@@ -0,0 +1,14 @@
<?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 = "top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity" id = "UserRoleRelMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "userId" column = "user_id" jdbcType = "INTEGER"/>
<result property = "roleId" column = "role_id" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
</mapper>
@@ -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;
}
}
@@ -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", "验证码错误"),
;
/**
* 错误码
@@ -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;
}
@@ -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;
}
@@ -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<UserEntity> {
/**
* 登录或注册
*
* @param userLoginReqVO 登录或注册参数
* @return 登录结果
*/
Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO);
}
@@ -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<UserMapper, UserEntity> implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
public Response<String> 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("");
}
}
@@ -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');
@@ -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<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserEntity::getUsername, "刑加一");
queryWrapper.orderByDesc(UserEntity::getCreateTime);
queryWrapper.last("limit 1");
UserEntity user = userMapper.selectOne(queryWrapper);
log.info("查询结果:{}", user);
}
}
@@ -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;
}
@@ -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;
}