This commit is contained in:
2026-03-19 22:07:16 +08:00
parent 50aa5e2d5d
commit f967c08b7b
11 changed files with 137 additions and 9 deletions

View File

@@ -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, "当前字典类型不存在");

View File

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

View File

@@ -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), // 图片验证码不正确

View File

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

View File

@@ -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")

View File

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

View File

@@ -23,6 +23,8 @@ public interface PostMapper extends BaseMapperX<PostDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<PostDO>()
.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));
}

View File

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

View File

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

View File

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

View File

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