diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RedisKeyConstants.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RedisKeyConstants.java index 859e5cd..4f1c376 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RedisKeyConstants.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RedisKeyConstants.java @@ -7,12 +7,48 @@ public class RedisKeyConstants { */ private static final String VERIFICATION_CODE_KEY_PREFIX = "verification_code:"; + /** + * 小憨书全局 ID 生成器 KEY + */ + public static final String HAN_NOTE_ID_GENERATOR_KEY = "hannote.id.generator"; + + /** + * 用户角色数据 KEY 前缀 + */ + private static final String USER_ROLES_KEY_PREFIX = "user:roles:"; + + /** + * 角色对应的权限集合 KEY 前缀 + */ + private static final String ROLE_PERMISSIONS_KEY_PREFIX = "role:permissions:"; + /** * 构建验证码 KEY - * @param email 手机号 + * + * @param email 邮箱 * @return 验证码key */ public static String buildVerificationCodeKey(String email) { return VERIFICATION_CODE_KEY_PREFIX + email; } + + /** + * 构建用户-角色 Key + * + * @param email 邮箱 + * @return 用户角色key + */ + public static String buildUserRoleKey(String email) { + return USER_ROLES_KEY_PREFIX + email; + } + + /** + * 构建角色对应的权限集合 KEY + * + * @param roleId 角色ID + * @return 角色权限集合key + */ + public static String buildRolePermissionsKey(Long roleId) { + return ROLE_PERMISSIONS_KEY_PREFIX + roleId; + } } \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RoleConstants.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RoleConstants.java new file mode 100644 index 0000000..e0ed271 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/constant/RoleConstants.java @@ -0,0 +1,11 @@ +package com.hanserwei.hannote.auth.constant; + +public class RoleConstants { + + + /** + * 普通用户的角色 ID + */ + public static final Long COMMON_USER_ROLE_ID = 1L; + +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java new file mode 100644 index 0000000..edec407 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java @@ -0,0 +1,30 @@ +package com.hanserwei.hannote.auth.controller; + +import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO; +import com.hanserwei.hannote.auth.service.UserService; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +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; + +@RestController +@RequestMapping("/user") +@Slf4j +@RequiredArgsConstructor +public class UserController { + + @Resource + private UserService userService; + + @PostMapping("/login") + @ApiOperationLog(description = "用户登录/注册") + public Response loginAndRegister(@Validated @RequestBody UserLoginReqVO userLoginReqVO) { + return userService.loginAndRegister(userLoginReqVO); + } +} diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/PermissionDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/PermissionDO.java new file mode 100644 index 0000000..c3e11c0 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/PermissionDO.java @@ -0,0 +1,91 @@ +package com.hanserwei.hannote.auth.domain.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 权限表 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "t_permission") +public class PermissionDO { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 父ID + */ + @TableField(value = "parent_id") + private Long parentId; + + /** + * 权限名称 + */ + @TableField(value = "`name`") + private String name; + + /** + * 类型(1:目录 2:菜单 3:按钮) + */ + @TableField(value = "`type`") + private Byte type; + + /** + * 菜单路由 + */ + @TableField(value = "menu_url") + private String menuUrl; + + /** + * 菜单图标 + */ + @TableField(value = "menu_icon") + private String menuIcon; + + /** + * 管理系统中的显示顺序 + */ + @TableField(value = "sort") + private Integer sort; + + /** + * 权限标识 + */ + @TableField(value = "permission_key") + private String permissionKey; + + /** + * 状态(0:启用;1:禁用) + */ + @TableField(value = "`status`") + private Byte status; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time") + private Date updateTime; + + /** + * 逻辑删除(0:未删除 1:已删除) + */ + @TableField(value = "is_deleted") + private Boolean isDeleted; +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RoleDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RoleDO.java new file mode 100644 index 0000000..983636a --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RoleDO.java @@ -0,0 +1,73 @@ +package com.hanserwei.hannote.auth.domain.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 角色表 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "t_role") +public class RoleDO { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 角色名 + */ + @TableField(value = "role_name") + private String roleName; + + /** + * 角色唯一标识 + */ + @TableField(value = "role_key") + private String roleKey; + + /** + * 状态(0:启用 1:禁用) + */ + @TableField(value = "`status`") + private Byte status; + + /** + * 管理系统中的显示顺序 + */ + @TableField(value = "sort") + private Integer sort; + + /** + * 备注 + */ + @TableField(value = "remark") + private String remark; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private Date createTime; + + /** + * 最后一次更新时间 + */ + @TableField(value = "update_time") + private Date updateTime; + + /** + * 逻辑删除(0:未删除 1:已删除) + */ + @TableField(value = "is_deleted") + private Boolean isDeleted; +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RolePermissionDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RolePermissionDO.java new file mode 100644 index 0000000..11a54d9 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/RolePermissionDO.java @@ -0,0 +1,55 @@ +package com.hanserwei.hannote.auth.domain.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户权限表 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "t_role_permission_rel") +public class RolePermissionDO { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 角色ID + */ + @TableField(value = "role_id") + private Long roleId; + + /** + * 权限ID + */ + @TableField(value = "permission_id") + private Long permissionId; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time") + private Date updateTime; + + /** + * 逻辑删除(0:未删除 1:已删除) + */ + @TableField(value = "is_deleted") + private Boolean isDeleted; +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java index 6e8cfd0..8f62885 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java @@ -5,15 +5,18 @@ 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.util.Date; +import java.time.LocalDate; +import java.time.LocalDateTime; /** * 用户表 */ @Data +@Builder @AllArgsConstructor @NoArgsConstructor @TableName(value = "t_user") @@ -52,7 +55,7 @@ public class UserDO { * 生日 */ @TableField(value = "birthday") - private Date birthday; + private LocalDate birthday; /** * 背景图 @@ -70,13 +73,13 @@ public class UserDO { * 性别(0:女 1:男) */ @TableField(value = "sex") - private Byte sex; + private Integer sex; /** * 状态(0:启用 1:禁用) */ @TableField(value = "`status`") - private Byte status; + private Integer status; /** * 个人简介 @@ -88,13 +91,13 @@ public class UserDO { * 创建时间 */ @TableField(value = "create_time") - private Date createTime; + private LocalDateTime createTime; /** * 更新时间 */ @TableField(value = "update_time") - private Date updateTime; + private LocalDateTime updateTime; /** * 逻辑删除(0:未删除 1:已删除) diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserRoleDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserRoleDO.java new file mode 100644 index 0000000..2dedc7b --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserRoleDO.java @@ -0,0 +1,58 @@ +package com.hanserwei.hannote.auth.domain.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +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.time.LocalDateTime; + +/** + * 用户角色表 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "t_user_role_rel") +public class UserRoleDO { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID + */ + @TableField(value = "user_id") + private Long userId; + + /** + * 角色ID + */ + @TableField(value = "role_id") + private Long roleId; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time") + private LocalDateTime updateTime; + + /** + * 逻辑删除(0:未删除 1:已删除) + */ + @TableField(value = "is_deleted") + private Boolean isDeleted; +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/PermissionDOMapper.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/PermissionDOMapper.java new file mode 100644 index 0000000..a16a0ba --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/PermissionDOMapper.java @@ -0,0 +1,15 @@ +package com.hanserwei.hannote.auth.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hanserwei.hannote.auth.domain.dataobject.PermissionDO; + +import java.util.List; + +public interface PermissionDOMapper extends BaseMapper { + /** + * 查询 APP 端所有被启用的权限 + * + * @return 权限列表 + */ + List selectAppEnabledList(); +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RoleDOMapper.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RoleDOMapper.java new file mode 100644 index 0000000..a1d27b8 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RoleDOMapper.java @@ -0,0 +1,16 @@ +package com.hanserwei.hannote.auth.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hanserwei.hannote.auth.domain.dataobject.RoleDO; + +import java.util.List; + +public interface RoleDOMapper extends BaseMapper { + + /** + * 查询所有被启用的角色 + * + * @return + */ + List selectEnabledList(); +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RolePermissionDOMapper.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RolePermissionDOMapper.java new file mode 100644 index 0000000..afac2bc --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/RolePermissionDOMapper.java @@ -0,0 +1,17 @@ +package com.hanserwei.hannote.auth.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hanserwei.hannote.auth.domain.dataobject.RolePermissionDO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface RolePermissionDOMapper extends BaseMapper { + /** + * 根据角色 ID 集合批量查询 + * + * @param roleIds 角色 ID 集合 + * @return 角色权限关系 + */ + List selectByRoleIds(@Param("roleIds") List roleIds); +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/UserRoleDOMapper.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/UserRoleDOMapper.java new file mode 100644 index 0000000..3c0cdbf --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/mapper/UserRoleDOMapper.java @@ -0,0 +1,7 @@ +package com.hanserwei.hannote.auth.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hanserwei.hannote.auth.domain.dataobject.UserRoleDO; + +public interface UserRoleDOMapper extends BaseMapper { +} \ No newline at end of file diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/enums/ResponseCodeEnum.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/enums/ResponseCodeEnum.java index 34e0240..4911adf 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/enums/ResponseCodeEnum.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/enums/ResponseCodeEnum.java @@ -14,9 +14,16 @@ public enum ResponseCodeEnum implements BaseExceptionInterface { // ----------- 业务异常状态码 ----------- VERIFICATION_CODE_SEND_FREQUENTLY("AUTH-20000", "请求太频繁,请3分钟后再试"), - MAIL_SEND_ERROR("AUTH-20001", "邮件发送失败,请稍后再试"), - TEMPLATE_RENDER_ERROR("AUTH-20002", "模板渲染错误"), - USER_NOT_EXIST("AUTH-20003", "用户不存在"), + VERIFICATION_CODE_ERROR("AUTH-20001", "验证码错误"), + + + + + + + // ----------- 邮件异常状态码 ----------- + MAIL_SEND_ERROR("EMAIL-20010", "邮件发送失败,请稍后再试"), + TEMPLATE_RENDER_ERROR("EMAIL-20011", "模板渲染错误"), ; // 异常码 diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/exception/GlobalExceptionHandler.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/exception/GlobalExceptionHandler.java index eec386e..96c07d7 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/exception/GlobalExceptionHandler.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/exception/GlobalExceptionHandler.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import java.util.Optional; +@SuppressWarnings("LoggingSimilarMessage") @ControllerAdvice @Slf4j public class GlobalExceptionHandler { @@ -66,11 +67,30 @@ public class GlobalExceptionHandler { return Response.fail(errorCode, errorMessage); } + /** + * 捕获 guava 参数校验异常 + * + * @return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID) + */ + @ExceptionHandler({IllegalArgumentException.class}) + @ResponseBody + public Response handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 错误信息 + String errorMessage = e.getMessage(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + /** * 其他类型异常 * * @param request 请求 - * @param e 异常 + * @param e 异常 * @return Response.fail(ResponseCodeEnum.SYSTEM_ERROR) */ @ExceptionHandler({Exception.class}) diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/model/vo/user/UserLoginReqVO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/model/vo/user/UserLoginReqVO.java index 2312467..80b9202 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/model/vo/user/UserLoginReqVO.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/model/vo/user/UserLoginReqVO.java @@ -19,7 +19,7 @@ public class UserLoginReqVO { */ @NotBlank(message = "邮箱不能为空") @EmailNumber - private String phone; + private String email; /** * 验证码 diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/runner/PushRolePermissions2RedisRunner.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/runner/PushRolePermissions2RedisRunner.java new file mode 100644 index 0000000..5097672 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/runner/PushRolePermissions2RedisRunner.java @@ -0,0 +1,109 @@ +package com.hanserwei.hannote.auth.runner; + +import cn.hutool.core.collection.CollUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.hanserwei.framework.common.utils.JsonUtils; +import com.hanserwei.hannote.auth.constant.RedisKeyConstants; +import com.hanserwei.hannote.auth.domain.dataobject.PermissionDO; +import com.hanserwei.hannote.auth.domain.dataobject.RoleDO; +import com.hanserwei.hannote.auth.domain.dataobject.RolePermissionDO; +import com.hanserwei.hannote.auth.domain.mapper.PermissionDOMapper; +import com.hanserwei.hannote.auth.domain.mapper.RoleDOMapper; +import com.hanserwei.hannote.auth.domain.mapper.RolePermissionDOMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class PushRolePermissions2RedisRunner implements ApplicationRunner { + @Resource + private RedisTemplate redisTemplate; + @Resource + private RoleDOMapper roleDOMapper; + @Resource + private PermissionDOMapper permissionDOMapper; + @Resource + private RolePermissionDOMapper rolePermissionDOMapper; + + // 权限同步标记 Key + private static final String PUSH_PERMISSION_FLAG = "push.permission.flag"; + + @Override + public void run(ApplicationArguments args) { + try { + // 是否能够同步数据: 原子操作,只有在键 PUSH_PERMISSION_FLAG 不存在时,才会设置该键的值为 "1",并设置过期时间为 1 天 + boolean canPushed = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS)); + + // 如果无法同步权限数据 + if (!canPushed) { + log.warn("==> 角色权限数据已经同步至 Redis 中,不再同步..."); + return; + } + // 查询出所有角色 + List roleDOS = roleDOMapper.selectEnabledList(); + + if (CollUtil.isNotEmpty(roleDOS)) { + // 拿到所有角色的 ID + List roleIds = roleDOS.stream().map(RoleDO::getId).toList(); + + // 根据角色 ID, 批量查询出所有角色对应的权限 + List rolePermissionDOS = rolePermissionDOMapper.selectByRoleIds(roleIds); + // 按角色 ID 分组, 每个角色 ID 对应多个权限 ID + Map> roleIdPermissionIdsMap = rolePermissionDOS.stream().collect( + Collectors.groupingBy(RolePermissionDO::getRoleId, + Collectors.mapping(RolePermissionDO::getPermissionId, Collectors.toList())) + ); + + // 查询 APP 端所有被启用的权限 + List permissionDOS = permissionDOMapper.selectAppEnabledList(); + // 权限 ID - 权限 DO + Map permissionIdDOMap = permissionDOS.stream().collect( + Collectors.toMap(PermissionDO::getId, permissionDO -> permissionDO) + ); + + // 组织 角色ID-权限 关系 + Map> roleIdPermissionDOMap = Maps.newHashMap(); + + // 循环所有角色 + roleDOS.forEach(roleDO -> { + // 当前角色 ID + Long roleId = roleDO.getId(); + // 当前角色 ID 对应的权限 ID 集合 + List permissionIds = roleIdPermissionIdsMap.get(roleId); + if (CollUtil.isNotEmpty(permissionIds)) { + List perDOS = Lists.newArrayList(); + permissionIds.forEach(permissionId -> { + // 根据权限 ID 获取具体的权限 DO 对象 + PermissionDO permissionDO = permissionIdDOMap.get(permissionId); + if (Objects.nonNull(permissionDO)) { + perDOS.add(permissionDO); + } + }); + roleIdPermissionDOMap.put(roleId, perDOS); + } + }); + + // 同步至 Redis 中,方便后续网关查询鉴权使用 + roleIdPermissionDOMap.forEach((roleId, permissions) -> { + String key = RedisKeyConstants.buildRolePermissionsKey(roleId); + redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissions)); + }); + } + + log.info("==> 服务启动,成功同步角色权限数据到 Redis 中..."); + } catch (Exception e) { + log.error("==> 同步角色权限数据到 Redis 中失败: ", e); + } + } +} diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/UserService.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/UserService.java new file mode 100644 index 0000000..b06c0f9 --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/UserService.java @@ -0,0 +1,17 @@ +package com.hanserwei.hannote.auth.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.auth.domain.dataobject.UserDO; +import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO; + +public interface UserService extends IService { + + /** + * 登录与注册 + * + * @param reqVO 请求参数 + * @return 响应结果 + */ + Response loginAndRegister(UserLoginReqVO reqVO); +} diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/impl/UserServiceImpl.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..4093c0e --- /dev/null +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/service/impl/UserServiceImpl.java @@ -0,0 +1,139 @@ +package com.hanserwei.hannote.auth.service.impl; + +import cn.dev33.satoken.stp.SaTokenInfo; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.hanserwei.framework.common.enums.DeletedEnum; +import com.hanserwei.framework.common.enums.StatusEnum; +import com.hanserwei.framework.common.exception.ApiException; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.framework.common.utils.JsonUtils; +import com.hanserwei.hannote.auth.constant.RedisKeyConstants; +import com.hanserwei.hannote.auth.constant.RoleConstants; +import com.hanserwei.hannote.auth.domain.dataobject.UserDO; +import com.hanserwei.hannote.auth.domain.dataobject.UserRoleDO; +import com.hanserwei.hannote.auth.domain.mapper.UserDOMapper; +import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper; +import com.hanserwei.hannote.auth.enums.LoginTypeEnum; +import com.hanserwei.hannote.auth.enums.ResponseCodeEnum; +import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO; +import com.hanserwei.hannote.auth.service.UserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.support.TransactionTemplate; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserServiceImpl extends ServiceImpl implements UserService { + + + private final RedisTemplate redisTemplate; + private final UserRoleDOMapper userRoleDOMapper; + private final TransactionTemplate transactionTemplate; + + @Override + public Response loginAndRegister(UserLoginReqVO reqVO) { + Integer loginType = reqVO.getType(); + String email = reqVO.getEmail(); + LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(loginType); + + Long userId = null; + + //noinspection DataFlowIssue + switch (loginTypeEnum) { + case VERIFICATION_CODE: + String verificationCode = reqVO.getCode(); + //校验参数是否为空 + Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空"); + + //构建验证码的RedisKey + String key = RedisKeyConstants.buildVerificationCodeKey(email); + // 查询存储在 Redis 中该用户的登录验证码 + String sentCode = (String) redisTemplate.opsForValue().get(key); + // 判断用户提交的验证码,与 Redis 中的验证码是否一致 + if (!StrUtil.equals(verificationCode, sentCode)) { + throw new ApiException(ResponseCodeEnum.VERIFICATION_CODE_ERROR); + } + //通过邮箱查询用户 + UserDO userDO = this.getOne(new QueryWrapper().eq("email", email)); + log.info("==> 用户是否注册, email: {}, userDO: {}", email, JsonUtils.toJsonString(userDO)); + // 判断是否注册 + if (Objects.isNull(userDO)) { + // 若此用户还没有注册,系统自动注册该用户 + userId = registerUser(email); + } else { + // 已注册,则获取其用户 ID + userId = userDO.getId(); + } + break; + case PASSWORD: + break; + default: + break; + } + // SaToken 登录用户,并返回 token 令牌 + StpUtil.login(userId); + + SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); + + return Response.success(tokenInfo.getTokenValue()); + } + + public Long registerUser(String email) { + return transactionTemplate.execute(status -> { + try { + // 获取全局自增的小憨书ID + Long hanNoteId = redisTemplate.opsForValue().increment(RedisKeyConstants.HAN_NOTE_ID_GENERATOR_KEY); + UserDO userDO = UserDO.builder() + .hanNoteId(String.valueOf(hanNoteId)) // 自动生成小红书号 ID + .nickname("小憨憨" + hanNoteId) // 自动生成昵称, 如:小憨憨10000 + .status(StatusEnum.ENABLE.getValue()) // 状态为启用 + .email(email) + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .isDeleted(DeletedEnum.NO.getValue()) // 逻辑删除 + .build(); + + // 添加入库 + this.save(userDO); + + // 获取入库的用户ID + Long userId = userDO.getId(); + + // 添加默认用户角色 + UserRoleDO userRoleDO = UserRoleDO.builder() + .userId(userId) + .roleId(RoleConstants.COMMON_USER_ROLE_ID) + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .isDeleted(DeletedEnum.NO.getValue()) + .build(); + userRoleDOMapper.insert(userRoleDO); + + // 将该用户的角色 ID 存入 Redis 中 + List roles = Lists.newArrayList(); + roles.add(RoleConstants.COMMON_USER_ROLE_ID); + String userRolesKey = RedisKeyConstants.buildUserRoleKey(email); + redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles)); + + return userId; + } catch (Exception e) { + status.setRollbackOnly(); // 标记事务为回滚 + log.error("==> 系统注册用户异常: ", e); + return null; + } + }); + } +} diff --git a/han-note-auth/src/main/resources/mapperxml/PermissionDOMapper.xml b/han-note-auth/src/main/resources/mapperxml/PermissionDOMapper.xml new file mode 100644 index 0000000..29a0b97 --- /dev/null +++ b/han-note-auth/src/main/resources/mapperxml/PermissionDOMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + id, parent_id, `name`, `type`, menu_url, menu_icon, sort, permission_key, `status`, + create_time, update_time, is_deleted + + + + \ No newline at end of file diff --git a/han-note-auth/src/main/resources/mapperxml/RoleDOMapper.xml b/han-note-auth/src/main/resources/mapperxml/RoleDOMapper.xml new file mode 100644 index 0000000..cdeb998 --- /dev/null +++ b/han-note-auth/src/main/resources/mapperxml/RoleDOMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + id, role_name, role_key, `status`, sort, remark, create_time, update_time, is_deleted + + + + \ No newline at end of file diff --git a/han-note-auth/src/main/resources/mapperxml/RolePermissionDOMapper.xml b/han-note-auth/src/main/resources/mapperxml/RolePermissionDOMapper.xml new file mode 100644 index 0000000..c7a3893 --- /dev/null +++ b/han-note-auth/src/main/resources/mapperxml/RolePermissionDOMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + id, role_id, permission_id, create_time, update_time, is_deleted + + + + \ No newline at end of file diff --git a/han-note-auth/src/main/resources/mapperxml/UserRoleDOMapper.xml b/han-note-auth/src/main/resources/mapperxml/UserRoleDOMapper.xml new file mode 100644 index 0000000..d358bb0 --- /dev/null +++ b/han-note-auth/src/main/resources/mapperxml/UserRoleDOMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + id, user_id, role_id, create_time, update_time, is_deleted + + \ No newline at end of file diff --git a/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/DeletedEnum.java b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/DeletedEnum.java new file mode 100644 index 0000000..3611117 --- /dev/null +++ b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/DeletedEnum.java @@ -0,0 +1,14 @@ +package com.hanserwei.framework.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum DeletedEnum { + + YES(true), + NO(false); + + private final Boolean value; +} \ No newline at end of file diff --git a/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/StatusEnum.java b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/StatusEnum.java new file mode 100644 index 0000000..3d570ba --- /dev/null +++ b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/enums/StatusEnum.java @@ -0,0 +1,15 @@ +package com.hanserwei.framework.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum StatusEnum { + // 启用 + ENABLE(0), + // 禁用 + DISABLED(1); + + private final Integer value; +} \ No newline at end of file