1. 清理 iwork 无用的接口。
2. 整合 iwork 用户的密码管理策略。
This commit is contained in:
@@ -113,12 +113,6 @@ public class IWorkIntegrationController {
|
||||
|
||||
// ----------------- 同步到本地 -----------------
|
||||
|
||||
@PostMapping("/hr/full-sync")
|
||||
@Operation(summary = "手动触发 iWork 组织/人员同步")
|
||||
public CommonResult<IWorkFullSyncRespVO> fullSync(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
|
||||
return success(syncService.fullSync(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/hr/departments/full-sync")
|
||||
@Operation(summary = "手动触发 iWork 部门同步")
|
||||
public CommonResult<IWorkFullSyncRespVO> fullSyncDepartments(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
|
||||
@@ -142,10 +136,4 @@ public class IWorkIntegrationController {
|
||||
public CommonResult<IWorkFullSyncRespVO> fullSyncUsers(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
|
||||
return success(syncService.fullSyncUsers(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/hr/single-sync")
|
||||
@Operation(summary = "按 iWork ID 同步单条组织/人员")
|
||||
public CommonResult<IWorkSingleSyncRespVO> singleSync(@Valid @RequestBody IWorkSingleSyncReqVO reqVO) {
|
||||
return success(syncService.syncSingle(reqVO));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,10 @@ public class IWorkHrUserPageRespVO {
|
||||
@JsonProperty("accounttype")
|
||||
private String accounttype;
|
||||
|
||||
@Schema(description = "用户密码(MD5 密文)")
|
||||
@JsonProperty("password")
|
||||
private String password;
|
||||
|
||||
@JsonIgnore
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* iWork 单条同步请求
|
||||
*/
|
||||
@Data
|
||||
public class IWorkSingleSyncReqVO {
|
||||
|
||||
@Schema(description = "同步的实体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "user")
|
||||
@NotNull(message = "实体类型不能为空")
|
||||
private IWorkSyncEntityTypeEnum entityType;
|
||||
|
||||
@Schema(description = "iWork 提供的实体主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10001")
|
||||
@NotNull(message = "实体 ID 不能为空")
|
||||
@Min(1)
|
||||
private Long entityId;
|
||||
|
||||
@Schema(description = "缺失时是否自动创建", example = "true")
|
||||
private Boolean createIfMissing = Boolean.TRUE;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* iWork 单条同步响应
|
||||
*/
|
||||
@Data
|
||||
public class IWorkSingleSyncRespVO {
|
||||
|
||||
@Schema(description = "同步的实体类型")
|
||||
private IWorkSyncEntityTypeEnum entityType;
|
||||
|
||||
@Schema(description = "实体 ID")
|
||||
private Long entityId;
|
||||
|
||||
@Schema(description = "是否创建了新的记录")
|
||||
private boolean created;
|
||||
|
||||
@Schema(description = "是否对已有记录进行了更新")
|
||||
private boolean updated;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String message;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
if (!userService.isPasswordMatch(password, user.getPassword())) {
|
||||
if (!userService.isPasswordMatch(user, password)) {
|
||||
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
if (length < 4 || length > 16) {
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
if (!userService.isPasswordMatch(password, user.getPassword())) {
|
||||
if (!userService.isPasswordMatch(user, password)) {
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
@@ -436,22 +436,11 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为内部用户
|
||||
* 根据UserSourceEnum判断:同步用户为内部用户,外部用户为外部用户
|
||||
* 判断是否为内部用户,仅通过 E 办同步(SYNC)来源的账号才视为内部用户
|
||||
*/
|
||||
private boolean isInternalUser(AdminUserDO user) {
|
||||
// 根据userSource字段判断用户类型
|
||||
Integer userSource = user.getUserSource();
|
||||
|
||||
// 同步用户(SYNC = 2)为内部用户,需要使用E办登录
|
||||
if (userSource != null &&
|
||||
(userSource.equals(UserSourceEnum.SYNC.getSource()) ||
|
||||
userSource.equals(UserSourceEnum.IWORK.getSource()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 外部用户(EXTERNAL = 1)或其他情况为外部用户,使用账号密码登录
|
||||
return false;
|
||||
return Objects.equals(userSource, UserSourceEnum.SYNC.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,19 +2,12 @@ package com.zt.plat.module.system.service.integration.iwork;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncRespVO;
|
||||
|
||||
/**
|
||||
* iWork 组织/人员同步服务
|
||||
*/
|
||||
public interface IWorkSyncService {
|
||||
|
||||
/**
|
||||
* 发起全量分批同步
|
||||
*/
|
||||
IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 仅同步部门
|
||||
*/
|
||||
@@ -35,8 +28,4 @@ public interface IWorkSyncService {
|
||||
*/
|
||||
IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 根据 iWork ID 进行单条同步
|
||||
*/
|
||||
IWorkSingleSyncRespVO syncSingle(IWorkSingleSyncReqVO reqVO);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
|
||||
private static final String JOB_CODE_PREFIX = "IWORK_JOB_";
|
||||
private static final String DEFAULT_USER_PASSWORD = "Zgty@9527";
|
||||
private static final int DEFAULT_SORT = 999;
|
||||
|
||||
private final DeptService deptService;
|
||||
@@ -234,6 +233,8 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
}
|
||||
Long postId = resolveUserPostId(user);
|
||||
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
|
||||
// 直接沿用 iWork 原始密码,避免重复格式化造成校验偏差
|
||||
String externalPassword = trimToNull(user.getPassword());
|
||||
AdminUserDO existing = adminUserMapper.selectByUsername(username);
|
||||
UserSyncOutcome outcome;
|
||||
if (existing == null) {
|
||||
@@ -242,7 +243,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
result.increaseSkipped();
|
||||
continue;
|
||||
}
|
||||
outcome = createUser(user, username, deptId, postId, status);
|
||||
if (StrUtil.isBlank(externalPassword)) {
|
||||
log.warn("[iWork] 人员缺少密码信息,无法创建:id={} username={}", user.getId(), username);
|
||||
result.increaseFailed();
|
||||
continue;
|
||||
}
|
||||
outcome = createUser(user, username, deptId, postId, status, externalPassword);
|
||||
} else {
|
||||
if (!Objects.equals(existing.getUserSource(), UserSourceEnum.IWORK.getSource())) {
|
||||
logSkip("人员", existing.getId(), "非 iWork 来源用户,保持原状");
|
||||
@@ -254,7 +260,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
result.increaseSkipped();
|
||||
continue;
|
||||
}
|
||||
outcome = updateUser(existing, user, username, deptId, postId, status);
|
||||
outcome = updateUser(existing, user, username, deptId, postId, status, externalPassword);
|
||||
}
|
||||
applyUserOutcome(result, outcome, user.getLastname(), username);
|
||||
} catch (Exception ex) {
|
||||
@@ -357,13 +363,14 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
String username,
|
||||
Long deptId,
|
||||
Long postId,
|
||||
CommonStatusEnum status) {
|
||||
CommonStatusEnum status,
|
||||
String externalPassword) {
|
||||
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||
Long desiredUserId = source.getId() == null ? null : source.getId().longValue();
|
||||
if (desiredUserId != null) {
|
||||
req.setId(desiredUserId);
|
||||
}
|
||||
req.setPassword(DEFAULT_USER_PASSWORD);
|
||||
req.setPassword(externalPassword);
|
||||
req.setUserSource(UserSourceEnum.IWORK.getSource());
|
||||
Long userId = adminUserService.createUser(req);
|
||||
Long effectiveUserId = desiredUserId != null ? desiredUserId : userId;
|
||||
@@ -375,11 +382,13 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
String username,
|
||||
Long deptId,
|
||||
Long postId,
|
||||
CommonStatusEnum status) {
|
||||
CommonStatusEnum status,
|
||||
String externalPassword) {
|
||||
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||
req.setId(existing.getId());
|
||||
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
||||
adminUserService.updateUser(req);
|
||||
syncPassword(existing, externalPassword);
|
||||
return new UserSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||
}
|
||||
|
||||
@@ -491,6 +500,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
return post;
|
||||
}
|
||||
|
||||
// 优先匹配部门ID,若缺失则回退使用分部ID
|
||||
private Long resolveUserDeptId(IWorkHrUserPageRespVO.User user) {
|
||||
Long deptId = toLong(user.getDepartmentid());
|
||||
if (deptId != null) {
|
||||
@@ -503,6 +513,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 通过岗位编码命中缓存,否则才按名称自动建档
|
||||
private Long resolveUserPostId(IWorkHrUserPageRespVO.User user) {
|
||||
if (user.getJobtitleid() == null) {
|
||||
return null;
|
||||
@@ -552,6 +563,22 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
StrUtil.blankToDefault(displayName, username), describeAction(outcome.action())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅在密码发生变化时才写库,避免多余更新
|
||||
*/
|
||||
private void syncPassword(AdminUserDO existing, String externalPassword) {
|
||||
if (existing == null || StrUtil.isBlank(externalPassword)) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.equals(externalPassword, existing.getPassword())) {
|
||||
return;
|
||||
}
|
||||
AdminUserDO updateObj = new AdminUserDO();
|
||||
updateObj.setId(existing.getId());
|
||||
updateObj.setPassword(externalPassword);
|
||||
adminUserMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
private void incrementByAction(BatchResult result, SyncAction action) {
|
||||
if (action == null) {
|
||||
return;
|
||||
@@ -624,23 +651,17 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
return value == null ? null : value.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 工号优先、登录账号兜底,确保账号体系与 iWork 一致
|
||||
*/
|
||||
private String resolveUsername(IWorkHrUserPageRespVO.User user) {
|
||||
String candidate = sanitizeUsername(user.getWorkcode());
|
||||
if (candidate == null) {
|
||||
candidate = sanitizeUsername(user.getLoginid());
|
||||
if (StrUtil.isNotBlank(user.getWorkcode())) {
|
||||
return user.getWorkcode().trim();
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private String sanitizeUsername(String raw) {
|
||||
if (StrUtil.isBlank(raw)) {
|
||||
return null;
|
||||
if (StrUtil.isNotBlank(user.getLoginid())) {
|
||||
return user.getLoginid().trim();
|
||||
}
|
||||
String normalized = raw.replaceAll("[^A-Za-z0-9]", "");
|
||||
if (StrUtil.isBlank(normalized)) {
|
||||
return null;
|
||||
}
|
||||
return normalized.length() > 30 ? normalized.substring(0, 30) : normalized;
|
||||
return null;
|
||||
}
|
||||
|
||||
private Set<Long> singletonSet(Long value) {
|
||||
|
||||
@@ -12,7 +12,10 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_ORG_REMOTE_FAILED;
|
||||
|
||||
@@ -27,11 +30,6 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
private final IWorkOrgRestService orgRestService;
|
||||
private final IWorkSyncProcessor syncProcessor;
|
||||
|
||||
@Override
|
||||
public IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO) {
|
||||
return runFullSync(reqVO, reqVO.resolveScopes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) {
|
||||
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.DEPARTMENT));
|
||||
@@ -80,21 +78,6 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkSingleSyncRespVO syncSingle(IWorkSingleSyncReqVO reqVO) {
|
||||
IWorkSingleSyncRespVO respVO = new IWorkSingleSyncRespVO();
|
||||
respVO.setEntityType(reqVO.getEntityType());
|
||||
respVO.setEntityId(reqVO.getEntityId());
|
||||
switch (reqVO.getEntityType()) {
|
||||
case SUBCOMPANY -> processSingleSubcompany(reqVO, respVO);
|
||||
case DEPARTMENT -> processSingleDepartment(reqVO, respVO);
|
||||
case JOB_TITLE -> processSingleJob(reqVO, respVO);
|
||||
case USER -> processSingleUser(reqVO, respVO);
|
||||
default -> throw new IllegalArgumentException("不支持的实体类型: " + reqVO.getEntityType());
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO,
|
||||
IWorkSyncProcessor.SyncOptions options,
|
||||
IWorkSyncEntityStatVO stat,
|
||||
@@ -163,46 +146,6 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
});
|
||||
}
|
||||
|
||||
private void processSingleSubcompany(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||
IWorkHrSubcompanyPageRespVO.Subcompany data = fetchSingleSubcompany(reqVO.getEntityId());
|
||||
if (data == null) {
|
||||
markNotFound(respVO, "分部");
|
||||
return;
|
||||
}
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompany(data, buildSingleOptions(reqVO));
|
||||
populateSingleResult(respVO, result);
|
||||
}
|
||||
|
||||
private void processSingleDepartment(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||
IWorkHrDepartmentPageRespVO.Department data = fetchSingleDepartment(reqVO.getEntityId());
|
||||
if (data == null) {
|
||||
markNotFound(respVO, "部门");
|
||||
return;
|
||||
}
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartment(data, buildSingleOptions(reqVO));
|
||||
populateSingleResult(respVO, result);
|
||||
}
|
||||
|
||||
private void processSingleJob(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||
IWorkHrJobTitlePageRespVO.JobTitle data = fetchSingleJob(reqVO.getEntityId());
|
||||
if (data == null) {
|
||||
markNotFound(respVO, "岗位");
|
||||
return;
|
||||
}
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncJobTitle(data, buildSingleOptions(reqVO));
|
||||
populateSingleResult(respVO, result);
|
||||
}
|
||||
|
||||
private void processSingleUser(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||
IWorkHrUserPageRespVO.User data = fetchSingleUser(reqVO.getEntityId().toString());
|
||||
if (data == null) {
|
||||
markNotFound(respVO, "人员");
|
||||
return;
|
||||
}
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncUser(data, buildSingleOptions(reqVO));
|
||||
populateSingleResult(respVO, result);
|
||||
}
|
||||
|
||||
private int executePaged(IWorkFullSyncReqVO reqVO,
|
||||
IWorkSyncEntityTypeEnum type,
|
||||
List<IWorkSyncBatchStatVO> batches,
|
||||
@@ -238,66 +181,10 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
stat.incrementFailed(result.getFailed());
|
||||
}
|
||||
|
||||
private void populateSingleResult(IWorkSingleSyncRespVO respVO, IWorkSyncProcessor.BatchResult result) {
|
||||
respVO.setCreated(result.getCreated() > 0);
|
||||
respVO.setUpdated(result.getUpdated() > 0);
|
||||
respVO.setMessage(result.getMessage());
|
||||
}
|
||||
|
||||
private void markNotFound(IWorkSingleSyncRespVO respVO, String entityName) {
|
||||
respVO.setCreated(false);
|
||||
respVO.setUpdated(false);
|
||||
respVO.setMessage(StrUtil.format("未在 iWork 中找到{}(ID={})", entityName, respVO.getEntityId()));
|
||||
}
|
||||
|
||||
private IWorkSyncProcessor.SyncOptions buildFullSyncOptions(IWorkFullSyncReqVO reqVO) {
|
||||
return IWorkSyncProcessor.SyncOptions.full(Boolean.TRUE.equals(reqVO.getIncludeCanceled()));
|
||||
}
|
||||
|
||||
private IWorkSyncProcessor.SyncOptions buildSingleOptions(IWorkSingleSyncReqVO reqVO) {
|
||||
return IWorkSyncProcessor.SyncOptions.single(Boolean.TRUE.equals(reqVO.getCreateIfMissing()));
|
||||
}
|
||||
|
||||
private IWorkHrSubcompanyPageRespVO.Subcompany fetchSingleSubcompany(Long entityId) {
|
||||
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
||||
query.setCurpage(1);
|
||||
query.setPagesize(1);
|
||||
query.setParams(Collections.singletonMap("subcompanyid", entityId));
|
||||
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
||||
ensureIWorkSuccess("获取分部详情", pageResp.isSuccess(), pageResp.getMessage());
|
||||
return CollUtil.getFirst(pageResp.getDataList());
|
||||
}
|
||||
|
||||
private IWorkHrDepartmentPageRespVO.Department fetchSingleDepartment(Long entityId) {
|
||||
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
||||
query.setCurpage(1);
|
||||
query.setPagesize(1);
|
||||
query.setParams(Collections.singletonMap("departmentid", entityId));
|
||||
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
||||
ensureIWorkSuccess("获取部门详情", pageResp.isSuccess(), pageResp.getMessage());
|
||||
return CollUtil.getFirst(pageResp.getDataList());
|
||||
}
|
||||
|
||||
private IWorkHrJobTitlePageRespVO.JobTitle fetchSingleJob(Long entityId) {
|
||||
IWorkJobTitleQueryReqVO query = new IWorkJobTitleQueryReqVO();
|
||||
query.setCurpage(1);
|
||||
query.setPagesize(1);
|
||||
query.setParams(Collections.singletonMap("jobtitleid", entityId));
|
||||
IWorkHrJobTitlePageRespVO pageResp = orgRestService.listJobTitles(query);
|
||||
ensureIWorkSuccess("获取岗位详情", pageResp.isSuccess(), pageResp.getMessage());
|
||||
return CollUtil.getFirst(pageResp.getDataList());
|
||||
}
|
||||
|
||||
private IWorkHrUserPageRespVO.User fetchSingleUser(String entityId) {
|
||||
IWorkUserQueryReqVO query = new IWorkUserQueryReqVO();
|
||||
query.setCurpage(1);
|
||||
query.setPagesize(1);
|
||||
query.setParams(Collections.singletonMap("id", entityId));
|
||||
IWorkHrUserPageRespVO pageResp = orgRestService.listUsers(query);
|
||||
ensureIWorkSuccess("获取人员详情", pageResp.isSuccess(), pageResp.getMessage());
|
||||
return CollUtil.getFirst(pageResp.getDataList());
|
||||
}
|
||||
|
||||
private void ensureIWorkSuccess(String action, boolean success, String remoteMessage) {
|
||||
if (success) {
|
||||
return;
|
||||
|
||||
@@ -190,10 +190,10 @@ public interface AdminUserService {
|
||||
/**
|
||||
* 判断密码是否匹配
|
||||
*
|
||||
* @param user 用户信息(用于决策密码策略)
|
||||
* @param rawPassword 未加密的密码
|
||||
* @param encodedPassword 加密后的密码
|
||||
* @return 是否匹配
|
||||
*/
|
||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||
boolean isPasswordMatch(AdminUserDO user, String rawPassword);
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import com.zt.plat.module.system.enums.user.PasswordStrategyEnum;
|
||||
import com.zt.plat.module.system.enums.user.UserSourceEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.dept.PostService;
|
||||
@@ -40,9 +41,12 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -122,8 +126,9 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
if (user.getUserSource() == null) {
|
||||
user.setUserSource(UserSourceEnum.EXTERNAL.getSource());
|
||||
}
|
||||
PasswordStrategyEnum passwordStrategy = determinePasswordStrategy(user.getUserSource());
|
||||
user.setAvatar(normalizeAvatarValue(createReqVO.getAvatar()));
|
||||
user.setPassword(encodePassword(createReqVO.getPassword()));
|
||||
user.setPassword(encodePassword(createReqVO.getPassword(), passwordStrategy));
|
||||
userMapper.insert(user);
|
||||
// 2.2 插入关联部门
|
||||
if (CollectionUtil.isNotEmpty(user.getDeptIds())) {
|
||||
@@ -161,7 +166,7 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
|
||||
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
|
||||
user.setUserSource(UserSourceEnum.EXTERNAL.getSource()); // 注册用户设为外部用户
|
||||
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码
|
||||
user.setPassword(encodePassword(registerReqVO.getPassword(), PasswordStrategyEnum.LOCAL_BCRYPT)); // 加密密码
|
||||
userMapper.insert(user);
|
||||
return user.getId();
|
||||
}
|
||||
@@ -268,30 +273,23 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
|
||||
@Override
|
||||
public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) {
|
||||
// 校验旧密码密码
|
||||
validateOldPassword(id, reqVO.getOldPassword());
|
||||
// 执行更新
|
||||
AdminUserDO updateObj = new AdminUserDO().setId(id);
|
||||
updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
|
||||
userMapper.updateById(updateObj);
|
||||
AdminUserDO user = validateUserExists(id);
|
||||
ensurePasswordCanBeModified(user);
|
||||
validateOldPassword(user, reqVO.getOldPassword());
|
||||
applyLocalPassword(user, reqVO.getNewPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS)
|
||||
public void updateUserPassword(Long id, String password) {
|
||||
// 1. 校验用户存在
|
||||
AdminUserDO user = validateUserExists(id);
|
||||
|
||||
// 2. 更新密码
|
||||
AdminUserDO updateObj = new AdminUserDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setPassword(encodePassword(password)); // 加密密码
|
||||
userMapper.updateById(updateObj);
|
||||
ensurePasswordCanBeModified(user);
|
||||
String encoded = applyLocalPassword(user, password);
|
||||
|
||||
// 3. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("user", user);
|
||||
LogRecordContext.putVariable("newPassword", updateObj.getPassword());
|
||||
LogRecordContext.putVariable("newPassword", encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -611,15 +609,24 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void validateOldPassword(Long id, String oldPassword) {
|
||||
AdminUserDO user = userMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
if (!isPasswordMatch(oldPassword, user.getPassword())) {
|
||||
AdminUserDO user = validateUserExists(id);
|
||||
validateOldPassword(user, oldPassword);
|
||||
}
|
||||
|
||||
private void validateOldPassword(AdminUserDO user, String oldPassword) {
|
||||
if (!isPasswordMatch(user, oldPassword)) {
|
||||
throw exception(USER_PASSWORD_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensurePasswordCanBeModified(AdminUserDO user) {
|
||||
UserSourceEnum sourceEnum = UserSourceEnum.of(user.getUserSource());
|
||||
if (sourceEnum == null || sourceEnum.isExternal()) {
|
||||
return;
|
||||
}
|
||||
throw exception(USER_PASSWORD_MODIFY_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AdminUserDO> getUserListByStatus(Integer status) {
|
||||
List<AdminUserDO> users = userMapper.selectListByStatus(status);
|
||||
@@ -628,18 +635,58 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
public boolean isPasswordMatch(AdminUserDO user, String rawPassword) {
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
PasswordStrategyEnum strategy = determinePasswordStrategy(user.getUserSource());
|
||||
if (strategy == PasswordStrategyEnum.IWORK_MD5) {
|
||||
String stored = user.getPassword();
|
||||
if (isBcryptFormat(stored)) {
|
||||
return passwordEncoder.matches(rawPassword, stored);
|
||||
}
|
||||
String hashed = md5Upper(rawPassword);
|
||||
return StrUtil.isNotBlank(hashed) && hashed.equals(StrUtil.nullToDefault(stored, ""));
|
||||
}
|
||||
return passwordEncoder.matches(rawPassword, user.getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
private String encodePassword(String password) {
|
||||
private String applyLocalPassword(AdminUserDO user, String password) {
|
||||
AdminUserDO updateObj = new AdminUserDO();
|
||||
updateObj.setId(user.getId());
|
||||
String encoded = encodePassword(password, PasswordStrategyEnum.LOCAL_BCRYPT);
|
||||
updateObj.setPassword(encoded);
|
||||
userMapper.updateById(updateObj);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
private PasswordStrategyEnum determinePasswordStrategy(Integer userSource) {
|
||||
return UserSourceEnum.resolvePasswordStrategy(userSource);
|
||||
}
|
||||
|
||||
private String encodePassword(String password, PasswordStrategyEnum strategy) {
|
||||
if (strategy == PasswordStrategyEnum.IWORK_MD5) {
|
||||
return normalizeMd5(password);
|
||||
}
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
private String normalizeMd5(String password) {
|
||||
if (StrUtil.isBlank(password)) {
|
||||
return null;
|
||||
}
|
||||
return password.trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private String md5Upper(String rawPassword) {
|
||||
if (StrUtil.isBlank(rawPassword)) {
|
||||
return null;
|
||||
}
|
||||
return DigestUtils.md5DigestAsHex(rawPassword.getBytes(StandardCharsets.UTF_8)).toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private boolean isBcryptFormat(String value) {
|
||||
return StrUtil.isNotBlank(value) && value.startsWith("$2");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user