diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java index 6417e70..6aab50a 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java @@ -16,6 +16,7 @@ public interface ErrorCodeConstants { ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}"); + ErrorCode AUTH_LOGIN_USER_RESIGNED = new ErrorCode(1_002_000_009, "登录失败,账号已离职"); // ========== 菜单模块 1-002-001-000 ========== ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单"); @@ -45,6 +46,7 @@ public interface ErrorCodeConstants { ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); + ErrorCode USER_IS_RESIGNED = new ErrorCode(1_002_003_012, "名字为【{}】的用户已离职"); // ========== 部门模块 1-002-004-000 ========== ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); @@ -62,6 +64,7 @@ public interface ErrorCodeConstants { ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择"); ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位"); ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位"); + ErrorCode POST_TYPE_INVALID = new ErrorCode(1_002_005_004, "岗位类型({})不合法"); // ========== 字典类型 1-002-006-000 ========== ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在"); diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/dept/PostTypeEnum.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/dept/PostTypeEnum.java new file mode 100644 index 0000000..4a46f09 --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/dept/PostTypeEnum.java @@ -0,0 +1,28 @@ +package com.njcn.rdms.module.system.enums.dept; + +import java.util.Arrays; + +/** + * 岗位类型枚举 + */ +public enum PostTypeEnum { + + MANAGEMENT("management"), + TECHNICAL("technical"), + BUSINESS("business"); + + private final String type; + + PostTypeEnum(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public static boolean isValid(String type) { + return Arrays.stream(values()).anyMatch(item -> item.type.equals(type)); + } + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/logger/LoginResultEnum.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/logger/LoginResultEnum.java index 1e719d8..5b3af11 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/logger/LoginResultEnum.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/logger/LoginResultEnum.java @@ -13,6 +13,7 @@ public enum LoginResultEnum { SUCCESS(0), // 成功 BAD_CREDENTIALS(10), // 账号或密码不正确 USER_DISABLED(20), // 用户被禁用 + USER_RESIGNED(21), // 用户已离职 CAPTCHA_NOT_FOUND(30), // 图片验证码不存在 CAPTCHA_CODE_ERROR(31), // 图片验证码不正确 diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostPageReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostPageReqVO.java index 9dc178d..5f04a86 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostPageReqVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostPageReqVO.java @@ -16,6 +16,12 @@ public class PostPageReqVO extends PageParam { @Schema(description = "岗位名称,模糊匹配", example = "灿能") private String name; + @Schema(description = "岗位类型", example = "technical") + private String postType; + + @Schema(description = "岗位级别", example = "8") + private Integer levelRank; + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") private Integer status; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java index 3362473..a1715cd 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java @@ -3,6 +3,7 @@ package com.njcn.rdms.module.system.controller.admin.dept.vo.post; import com.njcn.rdms.framework.common.enums.CommonStatusEnum; import com.njcn.rdms.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -30,6 +31,7 @@ public class PostSaveReqVO { private String postType; @Schema(description = "岗位级别", example = "8") + @Min(value = 0, message = "岗位级别不能小于 0") private Integer levelRank; @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java index 06c9aa1..b565d94 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java @@ -16,4 +16,16 @@ public class PostSimpleRespVO { @ExcelProperty("岗位名称") private String name; + @Schema(description = "岗位编码", example = "backend") + private String code; + + @Schema(description = "岗位类型", example = "technical") + private String postType; + + @Schema(description = "岗位级别", example = "8") + private Integer levelRank; + + @Schema(description = "岗位排序", example = "1") + private Integer sort; + } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/PostMapper.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/PostMapper.java index 929db58..bd3836c 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/PostMapper.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/dept/PostMapper.java @@ -23,6 +23,8 @@ public interface PostMapper extends BaseMapperX { return selectPage(reqVO, new LambdaQueryWrapperX() .likeIfPresent(PostDO::getCode, reqVO.getCode()) .likeIfPresent(PostDO::getName, reqVO.getName()) + .eqIfPresent(PostDO::getPostType, reqVO.getPostType()) + .eqIfPresent(PostDO::getLevelRank, reqVO.getLevelRank()) .eqIfPresent(PostDO::getStatus, reqVO.getStatus()) .orderByDesc(PostDO::getId)); } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/auth/AdminAuthServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/auth/AdminAuthServiceImpl.java index 555dce7..fc101ed 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/auth/AdminAuthServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/auth/AdminAuthServiceImpl.java @@ -38,6 +38,7 @@ import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_DISABLED; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_RESIGNED; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.AUTH_REGISTER_CAPTCHA_CODE_ERROR; /** @@ -83,6 +84,10 @@ public class AdminAuthServiceImpl implements AdminAuthService { createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); throw exception(AUTH_LOGIN_USER_DISABLED); } + if (userService.isUserResigned(user)) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_RESIGNED); + throw exception(AUTH_LOGIN_USER_RESIGNED); + } return user; } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/PostServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/PostServiceImpl.java index 5e25577..ac38aed 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/PostServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/dept/PostServiceImpl.java @@ -1,6 +1,7 @@ package com.njcn.rdms.module.system.service.dept; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import com.njcn.rdms.framework.common.enums.CommonStatusEnum; import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.util.object.BeanUtils; @@ -8,6 +9,8 @@ import com.njcn.rdms.module.system.controller.admin.dept.vo.post.PostPageReqVO; import com.njcn.rdms.module.system.controller.admin.dept.vo.post.PostSaveReqVO; import com.njcn.rdms.module.system.dal.dataobject.dept.PostDO; import com.njcn.rdms.module.system.dal.mysql.dept.PostMapper; +import com.njcn.rdms.module.system.enums.dept.PostTypeEnum; +import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -36,7 +39,7 @@ public class PostServiceImpl implements PostService { @Override public Long createPost(PostSaveReqVO createReqVO) { // 校验正确性 - validatePostForCreateOrUpdate(null, createReqVO.getName(), createReqVO.getCode()); + validatePostForCreateOrUpdate(null, createReqVO.getName(), createReqVO.getCode(), createReqVO.getPostType()); // 插入岗位 PostDO post = BeanUtils.toBean(createReqVO, PostDO.class); @@ -47,7 +50,7 @@ public class PostServiceImpl implements PostService { @Override public void updatePost(PostSaveReqVO updateReqVO) { // 校验正确性 - validatePostForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getCode()); + validatePostForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getPostType()); // 更新岗位 PostDO updateObj = BeanUtils.toBean(updateReqVO, PostDO.class); @@ -67,16 +70,19 @@ public class PostServiceImpl implements PostService { postMapper.deleteByIds(ids); } - private void validatePostForCreateOrUpdate(Long id, String name, String code) { + private void validatePostForCreateOrUpdate(Long id, String name, String code, String postType) { // 校验自己存在 validatePostExists(id); // 校验岗位名的唯一性 validatePostNameUnique(id, name); // 校验岗位编码的唯一性 validatePostCodeUnique(id, code); + // 校验岗位类型 + validatePostType(postType); } - private void validatePostNameUnique(Long id, String name) { + @VisibleForTesting + void validatePostNameUnique(Long id, String name) { PostDO post = postMapper.selectByName(name); if (post == null) { return; @@ -90,7 +96,8 @@ public class PostServiceImpl implements PostService { } } - private void validatePostCodeUnique(Long id, String code) { + @VisibleForTesting + void validatePostCodeUnique(Long id, String code) { PostDO post = postMapper.selectByCode(code); if (post == null) { return; @@ -104,6 +111,16 @@ public class PostServiceImpl implements PostService { } } + @VisibleForTesting + void validatePostType(String postType) { + if (StrUtil.isBlank(postType)) { + return; + } + if (!PostTypeEnum.isValid(postType)) { + throw exception(POST_TYPE_INVALID, postType); + } + } + private void validatePostExists(Long id) { if (id == null) { return; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserService.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserService.java index d113c14..effaeaf 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserService.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserService.java @@ -162,6 +162,7 @@ public interface AdminUserService { * 校验用户们是否有效。如下情况,视为无效: * 1. 用户编号不存在 * 2. 用户被禁用 + * 3. 用户已离职生效 * * @param ids 用户编号数组 */ @@ -214,4 +215,24 @@ public interface AdminUserService { */ boolean isPasswordMatch(String rawPassword, String encodedPassword); + /** + * 判断用户是否已离职生效 + * + * @param user 用户 + * @return 是否已离职 + */ + boolean isUserResigned(AdminUserDO user); + + /** + * 判断用户当前是否可用 + * + * 口径: + * 1. `status` 必须为启用 + * 2. `resignedAt` 为空,或者晚于当前时间 + * + * @param user 用户 + * @return 是否可用 + */ + boolean isUserAvailable(AdminUserDO user); + } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java index 2ca4df5..99905b1 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java @@ -54,6 +54,7 @@ import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_EMAIL_EX import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_IMPORT_INIT_PASSWORD; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_IMPORT_LIST_IS_EMPTY; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_IS_DISABLE; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_IS_RESIGNED; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_MOBILE_EXISTS; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_PASSWORD_FAILED; @@ -147,6 +148,10 @@ public class AdminUserServiceImpl implements AdminUserService { // 3. 记录操作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class)); LogRecordContext.putVariable("user", oldUser); + // 4. 如果更新后用户已不可用,主动移除其 token + if (!isUserAvailable(buildAvailabilityUser(oldUser, updateReqVO.getResignedAt(), oldUser.getStatus()))) { + oauth2TokenService.removeAccessToken(oldUser.getId(), UserTypeEnum.ADMIN.getValue()); + } } @Override @@ -193,14 +198,14 @@ public class AdminUserServiceImpl implements AdminUserService { @Override public void updateUserStatus(Long id, Integer status) { // 校验用户存在 - validateUserExists(id); + AdminUserDO user = validateUserExists(id); // 更新状态 AdminUserDO updateObj = new AdminUserDO(); updateObj.setId(id); updateObj.setStatus(status); userMapper.updateById(updateObj); - // 如果是禁用用户,则删除其 Token 信息 - if (CommonStatusEnum.isDisable(status)) { + // 如果更新后用户不可用,则删除其 Token 信息 + if (!isUserAvailable(buildAvailabilityUser(user, user.getResignedAt(), status))) { oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue()); } } @@ -295,9 +300,12 @@ public class AdminUserServiceImpl implements AdminUserService { if (user == null) { throw exception(USER_NOT_EXISTS); } - if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) { + if (CommonStatusEnum.isDisable(user.getStatus())) { throw exception(USER_IS_DISABLE, user.getNickname()); } + if (isUserResigned(user)) { + throw exception(USER_IS_RESIGNED, user.getNickname()); + } }); } @@ -477,6 +485,21 @@ public class AdminUserServiceImpl implements AdminUserService { return passwordEncoder.matches(rawPassword, encodedPassword); } + @Override + public boolean isUserResigned(AdminUserDO user) { + if (user == null || user.getResignedAt() == null) { + return false; + } + return !user.getResignedAt().isAfter(LocalDateTime.now()); + } + + @Override + public boolean isUserAvailable(AdminUserDO user) { + return user != null + && CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()) + && !isUserResigned(user); + } + /** * 对密码进行加密 * @@ -498,4 +521,12 @@ public class AdminUserServiceImpl implements AdminUserService { return config != null ? config.getValue() : null; } + private AdminUserDO buildAvailabilityUser(AdminUserDO user, LocalDateTime resignedAt, Integer status) { + return new AdminUserDO() + .setId(user.getId()) + .setNickname(user.getNickname()) + .setStatus(status) + .setResignedAt(resignedAt); + } + }