1. 新增生成自有组织 CODE,同步其他系统额外生成编码映射关系

This commit is contained in:
chenbowen
2025-12-15 19:25:19 +08:00
parent 8d25f4224b
commit 2323ee5c3b
13 changed files with 442 additions and 54 deletions

View File

@@ -23,6 +23,15 @@ public class DeptSaveReqVO {
@Size(max = 50, message = "部门编码长度不能超过 50 个字符")
private String code;
@Schema(description = "外部系统标识,用于建立编码映射", example = "ERP")
private String externalSystemCode;
@Schema(description = "外部系统组织编码,用于建立映射", example = "ERP-001")
private String externalDeptCode;
@Schema(description = "外部系统组织名称", example = "ERP总部")
private String externalDeptName;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT")
@NotBlank(message = "部门名称不能为空")
@Size(max = 30, message = "部门名称长度不能超过 30 个字符")

View File

@@ -37,6 +37,10 @@ public interface DeptExternalCodeMapper extends BaseMapperX<DeptExternalCodeDO>
return selectList(DeptExternalCodeDO::getDeptId, deptId);
}
default int deleteByDeptId(Long deptId) {
return delete(DeptExternalCodeDO::getDeptId, deptId);
}
default List<DeptExternalCodeDO> selectListBySystemCode(String systemCode) {
return selectList(DeptExternalCodeDO::getSystemCode, systemCode);
}

View File

@@ -17,6 +17,14 @@ public interface RedisKeyConstants {
*/
String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
/**
* 指定部门的外部组织编码映射列表缓存
* <p>
* KEY 格式dept_external_code_list:{deptId}
* VALUE 数据类型String 映射列表
*/
String DEPT_EXTERNAL_CODE_LIST = "dept_external_code_list";
/**
* 角色的缓存
* <p>

View File

@@ -49,6 +49,26 @@ public interface DeptExternalCodeService {
*/
List<DeptExternalCodeDO> getDeptExternalCodeListByDeptId(Long deptId);
/**
* 根据部门与外部系统保存/更新映射(存在则更新,不存在则创建)
*
* @param deptId 本系统部门 ID
* @param systemCode 外部系统标识
* @param externalDeptCode 外部系统组织编码
* @param externalDeptName 外部系统组织名称(可选)
* @param status 状态,默认启用
* @return 映射记录 ID
*/
Long saveOrUpdateDeptExternalCode(Long deptId, String systemCode, String externalDeptCode, String externalDeptName,
Integer status);
/**
* 根据部门删除全部外部编码映射
*
* @param deptId 部门编号
*/
void deleteDeptExternalCodesByDeptId(Long deptId);
/**
* 根据外部系统与外部组织编码查询映射
*/

View File

@@ -9,7 +9,12 @@ import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.DeptEx
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptExternalCodeDO;
import com.zt.plat.module.system.dal.mysql.dept.DeptExternalCodeMapper;
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
import com.zt.plat.module.system.dal.redis.RedisKeyConstants;
import jakarta.annotation.Resource;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -28,9 +33,12 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
@Resource
private DeptExternalCodeMapper deptExternalCodeMapper;
@Resource
private DeptService deptService;
private DeptMapper deptMapper;
@Resource
private CacheManager cacheManager;
@Override
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#createReqVO.deptId", beforeInvocation = false)
public Long createDeptExternalCode(DeptExternalCodeSaveReqVO createReqVO) {
normalizeRequest(createReqVO);
validateForCreateOrUpdate(null, createReqVO.getDeptId(), createReqVO.getSystemCode(),
@@ -57,12 +65,15 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
updateObj.setStatus(exists.getStatus() == null ? CommonStatusEnum.ENABLE.getStatus() : exists.getStatus());
}
deptExternalCodeMapper.updateById(updateObj);
evictCacheSafely(exists.getDeptId());
evictCacheSafely(updateObj.getDeptId());
}
@Override
public void deleteDeptExternalCode(Long id) {
validateExists(id);
DeptExternalCodeDO exists = validateExists(id);
deptExternalCodeMapper.deleteById(id);
evictCacheSafely(exists.getDeptId());
}
@Override
@@ -76,6 +87,7 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#deptId")
public List<DeptExternalCodeDO> getDeptExternalCodeListByDeptId(Long deptId) {
return deptExternalCodeMapper.selectListByDeptId(deptId);
}
@@ -96,6 +108,48 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
return deptExternalCodeMapper.selectBySystemCodeAndDeptId(systemCode.trim(), deptId);
}
@Override
public Long saveOrUpdateDeptExternalCode(Long deptId, String systemCode, String externalDeptCode,
String externalDeptName, Integer status) {
if (StrUtil.hasEmpty(systemCode, externalDeptCode) || deptId == null) {
return null;
}
String normalizedSystemCode = systemCode.trim();
String normalizedExternalCode = externalDeptCode.trim();
String normalizedExternalName = StrUtil.blankToDefault(StrUtil.trimToNull(externalDeptName), null);
// 如果存在则更新,否则创建
DeptExternalCodeDO exists = deptExternalCodeMapper.selectBySystemCodeAndDeptId(normalizedSystemCode, deptId);
if (exists != null) {
DeptExternalCodeSaveReqVO updateReqVO = new DeptExternalCodeSaveReqVO();
updateReqVO.setId(exists.getId());
updateReqVO.setDeptId(deptId);
updateReqVO.setSystemCode(normalizedSystemCode);
updateReqVO.setExternalDeptCode(normalizedExternalCode);
updateReqVO.setExternalDeptName(normalizedExternalName);
updateReqVO.setStatus(status == null ? exists.getStatus() : status);
updateDeptExternalCode(updateReqVO);
return exists.getId();
}
DeptExternalCodeSaveReqVO createReqVO = new DeptExternalCodeSaveReqVO();
createReqVO.setDeptId(deptId);
createReqVO.setSystemCode(normalizedSystemCode);
createReqVO.setExternalDeptCode(normalizedExternalCode);
createReqVO.setExternalDeptName(normalizedExternalName);
createReqVO.setStatus(status == null ? CommonStatusEnum.ENABLE.getStatus() : status);
return createDeptExternalCode(createReqVO);
}
@Override
public void deleteDeptExternalCodesByDeptId(Long deptId) {
if (deptId == null) {
return;
}
deptExternalCodeMapper.deleteByDeptId(deptId);
evictCacheSafely(deptId);
}
private DeptExternalCodeDO validateExists(Long id) {
if (id == null) {
throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS);
@@ -109,7 +163,7 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
private void validateForCreateOrUpdate(Long id, Long deptId, String systemCode, String externalDeptCode) {
// 校验部门存在
DeptDO dept = deptService.getDept(deptId);
DeptDO dept = deptMapper.selectById(deptId);
if (dept == null) {
throw exception(DEPT_NOT_FOUND);
}
@@ -148,4 +202,17 @@ public class DeptExternalCodeServiceImpl implements DeptExternalCodeService {
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
}
}
private void evictCacheSafely(Long deptId) {
if (deptId == null || cacheManager == null) {
return;
}
try {
if (cacheManager.getCache(RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST) != null) {
cacheManager.getCache(RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST).evict(deptId);
}
} catch (Exception ignore) {
// 缓存失效失败不影响主流程
}
}
}

View File

@@ -11,12 +11,18 @@ import com.zt.plat.framework.datapermission.core.annotation.DataPermission;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.zt.plat.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
import com.zt.plat.module.system.dal.dataobject.dict.DictTypeDO;
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper;
import com.zt.plat.module.system.dal.redis.RedisKeyConstants;
import com.zt.plat.module.system.enums.dept.DeptSourceEnum;
import com.zt.plat.module.system.enums.DictTypeConstants;
import com.zt.plat.module.system.service.dict.DictDataService;
import com.zt.plat.module.system.service.dict.DictTypeService;
import org.apache.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -52,6 +58,12 @@ public class DeptServiceImpl implements DeptService {
private UserDeptMapper userDeptMapper;
@Resource
private com.zt.plat.module.system.mq.producer.databus.DatabusChangeProducer databusChangeProducer;
@Resource
private DeptExternalCodeService deptExternalCodeService;
@Resource
private DictTypeService dictTypeService;
@Resource
private DictDataService dictDataService;
private static final String ROOT_CODE_PREFIX = "ZT";
private static final int CODE_SEGMENT_LENGTH = 3;
@@ -75,23 +87,10 @@ public class DeptServiceImpl implements DeptService {
// 校验部门名的唯一性
validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
// 生成并校验部门编码
boolean isIWorkSource = Objects.equals(createReqVO.getDeptSource(), DeptSourceEnum.IWORK.getSource());
if (isIWorkSource) {
// iWork 来源直接使用提供的编码,不再生成
String providedCode = StrUtil.blankToDefault(createReqVO.getCode(), null);
createReqVO.setCode(providedCode);
} else {
Long effectiveParentId = normalizeParentId(createReqVO.getParentId());
boolean isTopLevel = Objects.equals(effectiveParentId, DeptDO.PARENT_ID_ROOT);
String resolvedCode;
if (isTopLevel) {
resolvedCode = resolveTopLevelCode(null, createReqVO.getCode());
} else {
resolvedCode = generateDeptCode(effectiveParentId);
validateDeptCodeUnique(null, resolvedCode);
}
createReqVO.setCode(resolvedCode);
}
Long effectiveParentId = normalizeParentId(createReqVO.getParentId());
String resolvedCode = generateDeptCode(effectiveParentId);
validateDeptCodeUnique(null, resolvedCode);
createReqVO.setCode(resolvedCode);
// 插入部门
DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
@@ -101,6 +100,9 @@ public class DeptServiceImpl implements DeptService {
}
deptMapper.insert(dept);
// 维护外部系统编码映射(若有传入)
upsertExternalCodeMapping(createReqVO, dept.getId());
// 发布部门创建事件
databusChangeProducer.sendDeptCreatedMessage(dept);
@@ -121,37 +123,15 @@ public class DeptServiceImpl implements DeptService {
// 校验部门名的唯一性
validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
// 如果上级发生变化,需要重新生成编码并同步子级
boolean isIWorkSource = Objects.equals(originalDept.getDeptSource(), DeptSourceEnum.IWORK.getSource());
Long newParentId = normalizeParentId(updateReqVO.getParentId());
Long oldParentId = normalizeParentId(originalDept.getParentId());
boolean parentChanged = !Objects.equals(newParentId, oldParentId);
if (isIWorkSource) {
// iWork 来源直接使用提供的编码,不再生成
String providedCode = StrUtil.blankToDefault(updateReqVO.getCode(), null);
updateReqVO.setCode(providedCode);
} else {
if (parentChanged) {
String newCode;
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
newCode = resolveTopLevelCode(updateReqVO.getId(), updateReqVO.getCode());
} else {
newCode = generateDeptCode(updateReqVO.getParentId());
validateDeptCodeUnique(updateReqVO.getId(), newCode);
}
updateReqVO.setCode(newCode);
} else {
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
String requestedCode = updateReqVO.getCode();
if (StrUtil.isNotBlank(requestedCode) && !StrUtil.equals(requestedCode.trim(), originalDept.getCode())) {
updateReqVO.setCode(resolveTopLevelCode(updateReqVO.getId(), requestedCode));
} else {
updateReqVO.setCode(originalDept.getCode());
}
} else {
updateReqVO.setCode(originalDept.getCode());
}
}
String resolvedCode = originalDept.getCode();
if (parentChanged || StrUtil.isBlank(resolvedCode)) {
resolvedCode = generateDeptCode(newParentId);
validateDeptCodeUnique(updateReqVO.getId(), resolvedCode);
}
updateReqVO.setCode(resolvedCode);
// 更新部门
DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
@@ -166,6 +146,9 @@ public class DeptServiceImpl implements DeptService {
if (parentChanged) {
refreshChildCodesRecursively(updateObj.getId(), updateReqVO.getCode());
}
// 维护外部系统编码映射(若有传入)
upsertExternalCodeMapping(updateReqVO, updateReqVO.getId());
}
@Override
@@ -183,6 +166,9 @@ public class DeptServiceImpl implements DeptService {
DeptDO dept = deptMapper.selectById(id);
Long tenantId = (dept != null) ? dept.getTenantId() : null;
// 级联删除外部编码映射并清理缓存
deptExternalCodeService.deleteDeptExternalCodesByDeptId(id);
// 删除部门
deptMapper.deleteById(id);
@@ -754,4 +740,65 @@ public class DeptServiceImpl implements DeptService {
return dept;
}
private void upsertExternalCodeMapping(DeptSaveReqVO reqVO, Long deptId) {
if (reqVO == null || deptId == null) {
return;
}
String systemCode = StrUtil.trimToNull(reqVO.getExternalSystemCode());
String externalCode = StrUtil.trimToNull(reqVO.getExternalDeptCode());
if (StrUtil.isBlank(systemCode) || StrUtil.isBlank(externalCode)) {
return;
}
// 缺失的外部系统字典类型或数据会自动补齐
ensureExternalSystemDict(systemCode);
deptExternalCodeService.saveOrUpdateDeptExternalCode(
deptId,
systemCode,
externalCode,
reqVO.getExternalDeptName(),
CommonStatusEnum.ENABLE.getStatus());
}
/**
* 确保外部系统字典存在(含字典类型与对应值),若缺失则自动创建
*/
private void ensureExternalSystemDict(String systemCode) {
String normalizedCode = StrUtil.trimToNull(systemCode);
if (normalizedCode == null) {
return;
}
try {
DictTypeDO dictType = dictTypeService.getDictType(DictTypeConstants.DEPT_EXTERNAL_SYSTEM);
if (dictType == null) {
DictTypeSaveReqVO typeReq = new DictTypeSaveReqVO();
typeReq.setName("部门外部系统标识");
typeReq.setType(DictTypeConstants.DEPT_EXTERNAL_SYSTEM);
typeReq.setStatus(CommonStatusEnum.ENABLE.getStatus());
typeReq.setRemark("外部组织同步自动创建");
dictTypeService.createDictType(typeReq);
} else if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) {
DictTypeSaveReqVO updateReq = new DictTypeSaveReqVO();
updateReq.setId(dictType.getId());
updateReq.setName(dictType.getName());
updateReq.setType(dictType.getType());
updateReq.setStatus(CommonStatusEnum.ENABLE.getStatus());
updateReq.setRemark(dictType.getRemark());
dictTypeService.updateDictType(updateReq);
}
if (dictDataService.getDictData(DictTypeConstants.DEPT_EXTERNAL_SYSTEM, normalizedCode) == null) {
DictDataSaveReqVO dataReq = new DictDataSaveReqVO();
dataReq.setDictType(DictTypeConstants.DEPT_EXTERNAL_SYSTEM);
dataReq.setLabel(normalizedCode);
dataReq.setValue(normalizedCode);
dataReq.setSort(0);
dataReq.setStatus(CommonStatusEnum.ENABLE.getStatus());
dataReq.setRemark("外部组织同步自动创建");
dictDataService.createDictData(dataReq);
}
} catch (Exception ex) {
log.warn("[Dept] Ensure external system dict failed, systemCode={}", normalizedCode, ex);
}
}
}