✨ 机构OA同步支持双顶级与GWWL分段编码规则
固定集团锚点 id=135/GWWL 与独立外部组织顶级并存;公司/部门编码按上级直拼三位数字生成,并修正类型与孤儿清理逻辑。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+23
-18
@@ -4,29 +4,34 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 部门类型枚举
|
||||
* 组织类型枚举,与业务字典 organization_type 对齐
|
||||
*
|
||||
* @author bfhuange
|
||||
* @date 2024/8/26
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum DeptCategory {
|
||||
/**
|
||||
* 公司
|
||||
*/
|
||||
COMPANY(1, "公司"),
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
DEPT(2, "部门"),
|
||||
;
|
||||
/**
|
||||
* 集团
|
||||
*/
|
||||
GROUP(1, "集团"),
|
||||
/**
|
||||
* 公司
|
||||
*/
|
||||
COMPANY(2, "公司"),
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
DEPT(3, "部门"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 部门类型编码
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* 部门类型名称
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 组织类型编码
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* 组织类型名称
|
||||
*/
|
||||
private final String name;
|
||||
}
|
||||
|
||||
+29
-44
@@ -44,94 +44,79 @@ public interface DeptConvert {
|
||||
|
||||
@Mapping(source = "subcompanyname", target = "deptName")
|
||||
@Mapping(source = "subcompanydesc", target = "fullName")
|
||||
// 父公司id转为父部门编码,要拼接前缀
|
||||
@Mapping(source = "supsubcomid", target = "parentCode")
|
||||
@Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
// OA modified 单独落库,用于增量比对,避免覆盖本系统 updateTime
|
||||
@Mapping(source = "modified", target = "oaModified")
|
||||
Dept baseConvert(OACompanyResponse company);
|
||||
|
||||
@Mapping(source = "departmentname", target = "deptName")
|
||||
@Mapping(source = "departmentmark", target = "fullName")
|
||||
// 父部门id转为父部门编码,要拼接前缀
|
||||
@Mapping(source = "supdepid", target = "parentCode")
|
||||
// 所属子公司id转为所属公司编码,要拼接前缀
|
||||
@Mapping(source = "subcompanyid1", target = "belongCompanyCode")
|
||||
@Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
// OA modified 单独落库,用于增量比对,避免覆盖本系统 updateTime
|
||||
@Mapping(source = "modified", target = "oaModified")
|
||||
Dept baseConvert(OADepartmentResponse department);
|
||||
|
||||
/**
|
||||
* oa公司转本系统部门
|
||||
* @param company
|
||||
* @return
|
||||
* oa公司转本系统部门。组织编码由同步服务按 GWWL+三位数字 统一分配。
|
||||
*/
|
||||
default Dept company2dept(OACompanyResponse company) {
|
||||
Dept dept = this.baseConvert(company);
|
||||
// 部门编码添加前缀
|
||||
dept.setDeptCode(OAConvertConstant.COMPANY_OA_PREFIX + company.getId());
|
||||
// 编码稍后按规则生成;此处仅落 OA 主键,供按 oaId 匹配与重建层级
|
||||
dept.setDeptCode(null);
|
||||
String parentCode = dept.getParentCode();
|
||||
if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) {
|
||||
// 父部门编码不是0,拼接前缀
|
||||
dept.setParentCode(OAConvertConstant.COMPANY_OA_PREFIX + parentCode);
|
||||
// 父部门id后续处理
|
||||
// 非顶级:parentCode 暂存上级 OA 主键,分配编码后再替换为正式组织编码
|
||||
dept.setParentCode(parentCode);
|
||||
dept.setParentId(null);
|
||||
} else {
|
||||
// 父部门编码是0,父id也设置为0
|
||||
dept.setParentId(OAConvertConstant.ROOT_PARENT_ID);
|
||||
dept.setParentCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
dept.setParentId(OAConvertConstant.OA_SYNC_ROOT_DEPT_ID);
|
||||
}
|
||||
// 公司所属公司编码就是他自己的部门编码
|
||||
dept.setBelongCompanyCode(dept.getDeptCode());
|
||||
// OA主键与上级OA主键,分批同步结束后用于重建层级
|
||||
dept.setBelongCompanyCode(null);
|
||||
dept.setOaId(company.getId());
|
||||
dept.setOaSupSubComId(company.getSupsubcomid());
|
||||
// 显示顺序处理
|
||||
dept.setSort(OAUtils.parseInt(company.getShoworder()));
|
||||
// 部门类型,公司
|
||||
dept.setDeptCategory(DeptCategory.COMPANY.getCode());
|
||||
dept.setSyncTime(new Date());
|
||||
// 是oa的
|
||||
dept.setIsOa(CommonConstant.YES);
|
||||
return dept;
|
||||
}
|
||||
|
||||
/**
|
||||
* oa部门转本系统部门
|
||||
* @param oaDept
|
||||
* @param companyDeptMap 所有公司的部门编码与id map
|
||||
* @return
|
||||
* oa部门转本系统部门。组织编码由同步服务按「所属公司编码+三位数字」统一分配。
|
||||
*
|
||||
* @param oaDept OA部门
|
||||
* @param companyByOaId 已同步公司(oaId → 本地公司)
|
||||
*/
|
||||
default Dept dept2dept(OADepartmentResponse oaDept, Map<String, Long> companyDeptMap) {
|
||||
default Dept dept2dept(OADepartmentResponse oaDept, Map<String, Dept> companyByOaId) {
|
||||
Dept dept = this.baseConvert(oaDept);
|
||||
// 部门编码添加前缀
|
||||
dept.setDeptCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + oaDept.getId());
|
||||
// OA主键;上级为根部门时记录所属公司OA主键,否则记录上级部门OA主键
|
||||
dept.setDeptCode(null);
|
||||
dept.setOaId(oaDept.getId());
|
||||
if (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid())) {
|
||||
dept.setOaSupSubComId(oaDept.getSubcompanyid1());
|
||||
} else {
|
||||
dept.setOaSupSubComId(oaDept.getSupdepid());
|
||||
}
|
||||
// 部门所属公司编码就是他所属公司编码加前缀
|
||||
dept.setBelongCompanyCode(OAConvertConstant.COMPANY_OA_PREFIX + dept.getBelongCompanyCode());
|
||||
String parentCode = dept.getParentCode();
|
||||
if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) {
|
||||
// 父部门编码不是0,拼接前缀
|
||||
dept.setParentCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + parentCode);
|
||||
// 父部门id后续处理
|
||||
Dept belongCompany = companyByOaId == null ? null : companyByOaId.get(oaDept.getSubcompanyid1());
|
||||
if (belongCompany != null) {
|
||||
dept.setBelongCompanyCode(belongCompany.getDeptCode());
|
||||
dept.setParentId(null);
|
||||
if (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid())) {
|
||||
dept.setParentCode(belongCompany.getDeptCode());
|
||||
dept.setParentId(belongCompany.getId());
|
||||
} else {
|
||||
// 上级为部门:parentCode 暂存上级部门 OA 主键,重建层级时再解析
|
||||
dept.setParentCode(oaDept.getSupdepid());
|
||||
}
|
||||
} else {
|
||||
// 父部门编码是0,设置为公司编码
|
||||
dept.setParentCode(dept.getBelongCompanyCode());
|
||||
// 根据父部门编码获取对应的id
|
||||
dept.setParentId(companyDeptMap.get(dept.getParentCode()));
|
||||
// 所属公司尚未解析时,暂存 OA 公司主键,后续补齐正式编码
|
||||
dept.setBelongCompanyCode(oaDept.getSubcompanyid1());
|
||||
dept.setParentCode(oaDept.getSupdepid());
|
||||
}
|
||||
|
||||
// 显示顺序处理
|
||||
dept.setSort(OAUtils.parseInt(oaDept.getShoworder()));
|
||||
// 部门类型,部门
|
||||
dept.setDeptCategory(DeptCategory.DEPT.getCode());
|
||||
dept.setSyncTime(new Date());
|
||||
// 是oa的
|
||||
dept.setIsOa(CommonConstant.YES);
|
||||
return dept;
|
||||
}
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import org.springblade.core.literule.core.RuleComponent;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.Tenant;
|
||||
import org.springblade.system.pojo.enums.DeptCategory;
|
||||
import org.springblade.system.rule.context.TenantContext;
|
||||
|
||||
import static org.springblade.system.rule.constant.TenantRuleConstant.TENANT_DEPT_RULE;
|
||||
@@ -54,7 +55,7 @@ public class TenantDeptRule extends RuleComponent {
|
||||
dept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
dept.setDeptName(tenant.getTenantName());
|
||||
dept.setFullName(tenant.getTenantName());
|
||||
dept.setDeptCategory(1);
|
||||
dept.setDeptCategory(DeptCategory.GROUP.getCode());
|
||||
dept.setSort(2);
|
||||
dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
|
||||
|
||||
+8
-1
@@ -149,12 +149,19 @@ public interface IDeptService extends IService<Dept> {
|
||||
boolean removeDept(String ids);
|
||||
|
||||
/**
|
||||
* 删除非顶级组织,顶级组织(父级为 0)保留
|
||||
* 清除非保留顶级外的组织;保留 id=135 集团锚点,以及名称为「外部组织」的顶级
|
||||
*
|
||||
* @return 删除数量
|
||||
*/
|
||||
int clearNonTopDepts();
|
||||
|
||||
/**
|
||||
* 确保双顶级:id=135 集团(编码 GWWL)+ 独立「外部组织」顶级
|
||||
*
|
||||
* @return 集团顶级组织(id=135)
|
||||
*/
|
||||
Dept ensureOaSyncRootDept();
|
||||
|
||||
/**
|
||||
* 新增或修改部门
|
||||
* <p>
|
||||
|
||||
+279
-26
@@ -27,6 +27,7 @@ package org.springblade.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@@ -389,16 +390,193 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int clearNonTopDepts() {
|
||||
Long nonTopCount = this.count(Wrappers.<Dept>lambdaQuery()
|
||||
.isNotNull(Dept::getParentId)
|
||||
.ne(Dept::getParentId, BladeConstant.TOP_PARENT_ID));
|
||||
if (nonTopCount == null || nonTopCount == 0L) {
|
||||
// 同步前清理:保留集团锚点 id=135,以及「外部组织」顶级;其余删除
|
||||
Dept groupRoot = this.ensureOaSyncRootDept();
|
||||
Dept externalRoot = this.ensureExternalOrgTopDept();
|
||||
Set<Long> keepIds = new HashSet<>();
|
||||
if (groupRoot != null && groupRoot.getId() != null) {
|
||||
keepIds.add(groupRoot.getId());
|
||||
}
|
||||
if (externalRoot != null && externalRoot.getId() != null) {
|
||||
keepIds.add(externalRoot.getId());
|
||||
}
|
||||
if (keepIds.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
this.remove(Wrappers.<Dept>lambdaQuery()
|
||||
.isNotNull(Dept::getParentId)
|
||||
.ne(Dept::getParentId, BladeConstant.TOP_PARENT_ID));
|
||||
return nonTopCount.intValue();
|
||||
Long clearCount = this.count(Wrappers.<Dept>lambdaQuery().notIn(Dept::getId, keepIds));
|
||||
if (clearCount == null || clearCount == 0L) {
|
||||
return 0;
|
||||
}
|
||||
this.remove(Wrappers.<Dept>lambdaQuery().notIn(Dept::getId, keepIds));
|
||||
return clearCount.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Dept ensureOaSyncRootDept() {
|
||||
this.migrateLegacyOaDeptCategory();
|
||||
this.removeLegacyOaSyncRoots();
|
||||
Dept rootDept = this.getById(OAConvertConstant.OA_SYNC_ROOT_DEPT_ID);
|
||||
String tenantId = AuthUtil.getTenantId();
|
||||
if (StringUtil.isBlank(tenantId)) {
|
||||
tenantId = BladeConstant.ADMIN_TENANT_ID;
|
||||
}
|
||||
if (rootDept == null) {
|
||||
rootDept = new Dept();
|
||||
rootDept.setId(OAConvertConstant.OA_SYNC_ROOT_DEPT_ID);
|
||||
rootDept.setTenantId(tenantId);
|
||||
rootDept.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
rootDept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootDept.setDeptName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setFullName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setShortName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setDeptCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
rootDept.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootDept.setBelongCompanyCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
rootDept.setDeptCategory(DeptCategory.GROUP.getCode());
|
||||
rootDept.setSort(0);
|
||||
rootDept.setStatus(DataStatusEnum.ENABLE.getCode());
|
||||
rootDept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
rootDept.setIsOa(0);
|
||||
rootDept.setIsPlatformCompany(0);
|
||||
rootDept.setSyncTime(new Date());
|
||||
this.save(rootDept);
|
||||
log.info("已创建 OA 同步集团顶级 id={} name={} code={}",
|
||||
OAConvertConstant.OA_SYNC_ROOT_DEPT_ID,
|
||||
OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME,
|
||||
OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
} else {
|
||||
boolean wronglyNamedAsExternal = OAConvertConstant.EXTERNAL_ORG_NAME.equals(rootDept.getDeptName());
|
||||
boolean needUpdate = wronglyNamedAsExternal
|
||||
|| !OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE.equals(rootDept.getDeptCode())
|
||||
|| !DeptCategory.GROUP.getCode().equals(rootDept.getDeptCategory())
|
||||
|| !Objects.equals(BladeConstant.TOP_PARENT_ID, rootDept.getParentId());
|
||||
if (needUpdate) {
|
||||
Dept updateParam = new Dept();
|
||||
updateParam.setId(rootDept.getId());
|
||||
if (wronglyNamedAsExternal || StringUtil.isBlank(rootDept.getDeptName())) {
|
||||
updateParam.setDeptName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
updateParam.setFullName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
updateParam.setShortName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setDeptName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setFullName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
rootDept.setShortName(OAConvertConstant.OA_SYNC_ROOT_DEPT_NAME);
|
||||
}
|
||||
updateParam.setDeptCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
updateParam.setBelongCompanyCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
updateParam.setDeptCategory(DeptCategory.GROUP.getCode());
|
||||
updateParam.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
updateParam.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
updateParam.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
this.updateById(updateParam);
|
||||
rootDept.setDeptCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
rootDept.setBelongCompanyCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
rootDept.setDeptCategory(DeptCategory.GROUP.getCode());
|
||||
rootDept.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
rootDept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootDept.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
log.info("已校正 OA 同步集团顶级 id={} name={} code={} category={}",
|
||||
rootDept.getId(), rootDept.getDeptName(), rootDept.getDeptCode(), rootDept.getDeptCategory());
|
||||
}
|
||||
}
|
||||
this.ensureExternalOrgTopDept();
|
||||
return rootDept;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保存在独立的「外部组织」顶级(与 id=135 集团并存,仅保留一个同名顶级)
|
||||
*/
|
||||
private Dept ensureExternalOrgTopDept() {
|
||||
List<Dept> externalTops = this.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptName, OAConvertConstant.EXTERNAL_ORG_NAME)
|
||||
.and(wrapper -> wrapper
|
||||
.isNull(Dept::getParentId)
|
||||
.or()
|
||||
.eq(Dept::getParentId, BladeConstant.TOP_PARENT_ID))
|
||||
.ne(Dept::getId, OAConvertConstant.OA_SYNC_ROOT_DEPT_ID)
|
||||
.orderByAsc(Dept::getId));
|
||||
if (CollectionUtil.isNotEmpty(externalTops)) {
|
||||
Dept keep = externalTops.get(0);
|
||||
if (externalTops.size() > 1) {
|
||||
List<Long> duplicateIds = externalTops.stream()
|
||||
.skip(1)
|
||||
.map(Dept::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(duplicateIds)) {
|
||||
this.update(Wrappers.<Dept>lambdaUpdate()
|
||||
.set(Dept::getParentId, keep.getId())
|
||||
.in(Dept::getParentId, duplicateIds));
|
||||
this.removeByIds(duplicateIds);
|
||||
log.info("已合并重复的外部组织顶级,保留 id={},删除{}", keep.getId(), duplicateIds);
|
||||
}
|
||||
}
|
||||
return keep;
|
||||
}
|
||||
String tenantId = AuthUtil.getTenantId();
|
||||
if (StringUtil.isBlank(tenantId)) {
|
||||
tenantId = BladeConstant.ADMIN_TENANT_ID;
|
||||
}
|
||||
Dept externalDept = new Dept();
|
||||
externalDept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
externalDept.setTenantId(tenantId);
|
||||
externalDept.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
externalDept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
externalDept.setDeptName(OAConvertConstant.EXTERNAL_ORG_NAME);
|
||||
externalDept.setFullName(OAConvertConstant.EXTERNAL_ORG_NAME);
|
||||
externalDept.setShortName(OAConvertConstant.EXTERNAL_ORG_NAME);
|
||||
externalDept.setDeptCode(null);
|
||||
externalDept.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
externalDept.setDeptCategory(DeptCategory.GROUP.getCode());
|
||||
externalDept.setSort(0);
|
||||
externalDept.setStatus(DataStatusEnum.ENABLE.getCode());
|
||||
externalDept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
externalDept.setIsOa(0);
|
||||
externalDept.setIsPlatformCompany(0);
|
||||
externalDept.setSyncTime(new Date());
|
||||
this.save(externalDept);
|
||||
log.info("已创建外部组织顶级 id={}", externalDept.getId());
|
||||
return externalDept;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理历史伪顶级(OACROOT);同名「桂物物流集团」仅清理非 id=135 的顶级
|
||||
*/
|
||||
private void removeLegacyOaSyncRoots() {
|
||||
List<Dept> legacyRoots = this.list(Wrappers.<Dept>lambdaQuery()
|
||||
.ne(Dept::getId, OAConvertConstant.OA_SYNC_ROOT_DEPT_ID)
|
||||
.and(wrapper -> wrapper
|
||||
.eq(Dept::getDeptCode, OAConvertConstant.LEGACY_ROOT_DEPT_CODE)
|
||||
.or(nameWrapper -> nameWrapper
|
||||
.eq(Dept::getDeptName, OAConvertConstant.LEGACY_ROOT_DEPT_NAME)
|
||||
.eq(Dept::getDeptCode, OAConvertConstant.LEGACY_ROOT_DEPT_CODE)
|
||||
.and(parentWrapper -> parentWrapper
|
||||
.isNull(Dept::getParentId)
|
||||
.or()
|
||||
.eq(Dept::getParentId, BladeConstant.TOP_PARENT_ID)))));
|
||||
if (CollectionUtil.isEmpty(legacyRoots)) {
|
||||
return;
|
||||
}
|
||||
Set<Long> legacyIds = legacyRoots.stream().map(Dept::getId).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
this.update(Wrappers.<Dept>lambdaUpdate()
|
||||
.set(Dept::getParentId, OAConvertConstant.OA_SYNC_ROOT_DEPT_ID)
|
||||
.set(Dept::getParentCode, OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE)
|
||||
.in(Dept::getParentId, legacyIds));
|
||||
this.removeByIds(legacyIds);
|
||||
log.info("已清理历史伪顶级组织{}条:{}", legacyIds.size(), legacyIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按编码前缀对齐组织类型:OAC→公司、OAD→部门(覆盖历史 1/2 映射)
|
||||
*/
|
||||
private void migrateLegacyOaDeptCategory() {
|
||||
this.update(Wrappers.<Dept>lambdaUpdate()
|
||||
.set(Dept::getDeptCategory, DeptCategory.DEPT.getCode())
|
||||
.likeRight(Dept::getDeptCode, OAConvertConstant.DEPARTMENT_OA_PREFIX));
|
||||
this.update(Wrappers.<Dept>lambdaUpdate()
|
||||
.set(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.likeRight(Dept::getDeptCode, OAConvertConstant.COMPANY_OA_PREFIX)
|
||||
.ne(Dept::getDeptCode, OAConvertConstant.LEGACY_ROOT_DEPT_CODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -650,9 +828,16 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
if (StringUtil.isBlank(parent.getDeptCode())) {
|
||||
throw new ServiceException("请选择已配置组织编码的上级组织");
|
||||
}
|
||||
String pattern = Pattern.quote(parent.getDeptCode()) + "-\\d+";
|
||||
if (!deptCode.matches(pattern)) {
|
||||
throw new ServiceException("组织编码格式应为:上级编码-分段数字");
|
||||
// 公司:GWWL+三位数字;部门:所属公司编码+三位数字;其他:上级编码+三位数字
|
||||
String expectedParentCode = parent.getDeptCode();
|
||||
if (DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())) {
|
||||
expectedParentCode = OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE;
|
||||
} else if (DeptCategory.DEPT.getCode().equals(dept.getDeptCategory())
|
||||
&& StringUtil.isNotBlank(dept.getBelongCompanyCode())) {
|
||||
expectedParentCode = dept.getBelongCompanyCode();
|
||||
}
|
||||
if (!org.springblade.system.util.DeptCodeUtil.matchesChildCode(expectedParentCode, deptCode)) {
|
||||
throw new ServiceException("组织编码格式应为:" + expectedParentCode + "三位数字(不足补零,中间不加-)");
|
||||
}
|
||||
this.ensureDeptCodeUnique(dept, deptCode);
|
||||
dept.setDeptCode(deptCode);
|
||||
@@ -724,8 +909,8 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
log.info("没有记录OA主键的组织,跳过层级重建");
|
||||
return;
|
||||
}
|
||||
Map<String, Dept> companyByOaId = this.indexByOaId(oaDeptList, DeptCategory.COMPANY.getCode());
|
||||
Map<String, Dept> departmentByOaId = this.indexByOaId(oaDeptList, DeptCategory.DEPT.getCode());
|
||||
Map<String, Dept> companyByOaId = this.indexOaCompanies(oaDeptList);
|
||||
Map<String, Dept> departmentByOaId = this.indexOaDepartments(oaDeptList);
|
||||
Map<Long, Long> desiredParentIdMap = new LinkedHashMap<>();
|
||||
Map<Long, String> desiredParentCodeMap = new HashMap<>();
|
||||
for (Dept dept : oaDeptList) {
|
||||
@@ -778,21 +963,20 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
if (CollectionUtil.isEmpty(oaDeptList)) {
|
||||
return 0;
|
||||
}
|
||||
Map<String, Dept> companyByOaId = this.indexByOaId(oaDeptList, DeptCategory.COMPANY.getCode());
|
||||
Map<String, Dept> departmentByOaId = this.indexByOaId(oaDeptList, DeptCategory.DEPT.getCode());
|
||||
Map<String, Dept> companyByOaId = this.indexOaCompanies(oaDeptList);
|
||||
Map<String, Dept> departmentByOaId = this.indexOaDepartments(oaDeptList);
|
||||
Set<Long> deleteIds = new HashSet<>();
|
||||
for (Dept dept : oaDeptList) {
|
||||
String parentOaId = dept.getOaSupSubComId();
|
||||
// 上级为空或为 0:视为本地锚点/根级,不作为孤儿删除
|
||||
// 上级为空或为 0:OA 顶级公司挂本地锚点,不作为孤儿
|
||||
if (StringUtil.isBlank(parentOaId) || OAConstant.ROOT_COMPANY_ID.equals(parentOaId)) {
|
||||
continue;
|
||||
}
|
||||
boolean parentExists;
|
||||
if (DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())) {
|
||||
parentExists = companyByOaId.containsKey(parentOaId);
|
||||
} else {
|
||||
parentExists = departmentByOaId.containsKey(parentOaId) || companyByOaId.containsKey(parentOaId);
|
||||
}
|
||||
boolean isCompany = this.isOaCompany(dept);
|
||||
boolean parentExists = isCompany
|
||||
? companyByOaId.containsKey(parentOaId)
|
||||
: departmentByOaId.containsKey(parentOaId) || companyByOaId.containsKey(parentOaId);
|
||||
// 全量数据整理后仍找不到上级:公司、部门一律删除,不改挂保留
|
||||
if (!parentExists) {
|
||||
deleteIds.add(dept.getId());
|
||||
}
|
||||
@@ -813,7 +997,7 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
if (StringUtil.isBlank(parentOaId) || OAConstant.ROOT_COMPANY_ID.equals(parentOaId)) {
|
||||
continue;
|
||||
}
|
||||
Dept parent = DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())
|
||||
Dept parent = this.isOaCompany(dept)
|
||||
? companyByOaId.get(parentOaId)
|
||||
: Optional.ofNullable(departmentByOaId.get(parentOaId)).orElse(companyByOaId.get(parentOaId));
|
||||
if (parent != null && deleteIds.contains(parent.getId())) {
|
||||
@@ -842,6 +1026,72 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
return deleteIds.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引 OA 公司:同时兼容类型迁移前后的公司记录
|
||||
*/
|
||||
private Map<String, Dept> indexOaCompanies(List<Dept> oaDeptList) {
|
||||
Map<String, Dept> oaIdMap = new HashMap<>();
|
||||
for (Dept dept : oaDeptList) {
|
||||
if (!this.isOaCompany(dept) || StringUtil.isBlank(dept.getOaId())) {
|
||||
continue;
|
||||
}
|
||||
Dept previous = oaIdMap.put(dept.getOaId(), dept);
|
||||
if (previous != null) {
|
||||
log.warn("OA公司主键重复,层级重建使用后一条 oaId={} id={} previousId={}",
|
||||
dept.getOaId(), dept.getId(), previous.getId());
|
||||
}
|
||||
}
|
||||
return oaIdMap;
|
||||
}
|
||||
|
||||
private boolean isOaCompany(Dept dept) {
|
||||
if (dept == null) {
|
||||
return false;
|
||||
}
|
||||
if (DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())) {
|
||||
return true;
|
||||
}
|
||||
if (org.springblade.system.util.DeptCodeUtil.isCompanyCode(dept.getDeptCode())) {
|
||||
return true;
|
||||
}
|
||||
return StringUtil.isNotBlank(dept.getDeptCode())
|
||||
&& dept.getDeptCode().startsWith(OAConvertConstant.COMPANY_OA_PREFIX)
|
||||
&& !OAConvertConstant.LEGACY_ROOT_DEPT_CODE.equals(dept.getDeptCode());
|
||||
}
|
||||
|
||||
private boolean isOaDepartment(Dept dept) {
|
||||
if (dept == null) {
|
||||
return false;
|
||||
}
|
||||
if (DeptCategory.DEPT.getCode().equals(dept.getDeptCategory())) {
|
||||
return true;
|
||||
}
|
||||
if (StringUtil.isNotBlank(dept.getBelongCompanyCode())
|
||||
&& org.springblade.system.util.DeptCodeUtil.matchesChildCode(dept.getBelongCompanyCode(), dept.getDeptCode())) {
|
||||
return true;
|
||||
}
|
||||
return StringUtil.isNotBlank(dept.getDeptCode())
|
||||
&& dept.getDeptCode().startsWith(OAConvertConstant.DEPARTMENT_OA_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引 OA 部门:兼容类型迁移前后
|
||||
*/
|
||||
private Map<String, Dept> indexOaDepartments(List<Dept> oaDeptList) {
|
||||
Map<String, Dept> oaIdMap = new HashMap<>();
|
||||
for (Dept dept : oaDeptList) {
|
||||
if (!this.isOaDepartment(dept) || StringUtil.isBlank(dept.getOaId())) {
|
||||
continue;
|
||||
}
|
||||
Dept previous = oaIdMap.put(dept.getOaId(), dept);
|
||||
if (previous != null) {
|
||||
log.warn("OA部门主键重复,层级重建使用后一条 oaId={} id={} previousId={}",
|
||||
dept.getOaId(), dept.getId(), previous.getId());
|
||||
}
|
||||
}
|
||||
return oaIdMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按机构类型建立 OA 主键索引
|
||||
*/
|
||||
@@ -866,19 +1116,22 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
private OaParentLink resolveOaParentLink(Dept dept, Map<String, Dept> companyByOaId, Map<String, Dept> departmentByOaId) {
|
||||
String parentOaId = dept.getOaSupSubComId();
|
||||
boolean rootParent = StringUtil.isBlank(parentOaId) || OAConstant.ROOT_COMPANY_ID.equals(parentOaId);
|
||||
if (DeptCategory.COMPANY.getCode().equals(dept.getDeptCategory())) {
|
||||
if (this.isOaCompany(dept)) {
|
||||
if (rootParent) {
|
||||
return new OaParentLink(OAConvertConstant.ROOT_PARENT_ID, OAConstant.ROOT_COMPANY_ID);
|
||||
// OA 顶级公司统一挂到本地顶级锚点下
|
||||
return new OaParentLink(OAConvertConstant.OA_SYNC_ROOT_DEPT_ID, OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
}
|
||||
Dept parent = companyByOaId.get(parentOaId);
|
||||
if (parent == null || parent.getId() == null || Objects.equals(parent.getId(), dept.getId())) {
|
||||
// 全量整理后仍无上级:跳过重建,交由孤儿清理直接删除
|
||||
log.warn("未找到上级公司,跳过层级重建 oaId={} oaSupSubComId={} deptId={}",
|
||||
dept.getOaId(), parentOaId, dept.getId());
|
||||
return null;
|
||||
}
|
||||
return new OaParentLink(parent.getId(), this.resolveParentCode(parent, OAConvertConstant.COMPANY_OA_PREFIX, parentOaId));
|
||||
}
|
||||
if (!DeptCategory.DEPT.getCode().equals(dept.getDeptCategory())) {
|
||||
if (!DeptCategory.DEPT.getCode().equals(dept.getDeptCategory())
|
||||
&& !(StringUtil.isNotBlank(dept.getDeptCode()) && dept.getDeptCode().startsWith(OAConvertConstant.DEPARTMENT_OA_PREFIX))) {
|
||||
return null;
|
||||
}
|
||||
if (rootParent) {
|
||||
|
||||
+263
-72
@@ -22,6 +22,7 @@ import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
|
||||
import org.springblade.system.service.*;
|
||||
import org.springblade.system.util.DataSyncRecordUtils;
|
||||
import org.springblade.system.util.DeptCodeUtil;
|
||||
import org.springblade.thirdparty.oa.constant.OAConstant;
|
||||
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
|
||||
import org.springblade.thirdparty.oa.feign.IOAClient;
|
||||
@@ -73,6 +74,8 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
ComposeLogUtil.addLog(log);
|
||||
// 1. 同步公司和部门并记录
|
||||
Date startTime = this.syncAndRecord(DataSyncRecordUtils::createOAOrgFetch, queryStartTime -> {
|
||||
// 0. 确保双顶级:id=135 集团(GWWL) + 独立「外部组织」
|
||||
deptService.ensureOaSyncRootDept();
|
||||
// 1. 同步公司
|
||||
this.syncCompany(queryStartTime);
|
||||
// 公司分批入库后,重建层级并清理无法挂载的公司
|
||||
@@ -338,6 +341,10 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
private OaOrgSyncPageVO syncCompanyFromOaPage(int current, int size, boolean incremental, String modifiedSince) {
|
||||
int pageNo = current < 1 ? 1 : current;
|
||||
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
||||
// 首屏确保双顶级:id=135 集团(GWWL) + 独立「外部组织」
|
||||
if (pageNo == 1) {
|
||||
deptService.ensureOaSyncRootDept();
|
||||
}
|
||||
IncrementalStart incrementalStart = resolveIncrementalStart(incremental, modifiedSince, DeptCategory.COMPANY);
|
||||
OACompanySearch companySearch = buildCompanySearch(incrementalStart.getStartTime());
|
||||
companySearch.setCurPage(pageNo);
|
||||
@@ -437,10 +444,16 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
ComposeLogUtil.getLastLog().warn("解析回传 modifiedSince 失败:{},重新计算", modifiedSince);
|
||||
}
|
||||
}
|
||||
String codePrefix = DeptCategory.COMPANY.equals(deptCategory)
|
||||
? OAConvertConstant.COMPANY_OA_PREFIX
|
||||
: OAConvertConstant.DEPARTMENT_OA_PREFIX;
|
||||
Dept latest = deptService.getOne(Wrappers.<Dept>lambdaQuery()
|
||||
.select(Dept::getOaModified)
|
||||
.eq(Dept::getIsOa, YES)
|
||||
.eq(Dept::getDeptCategory, deptCategory.getCode())
|
||||
.and(wrapper -> wrapper
|
||||
.eq(Dept::getDeptCategory, deptCategory.getCode())
|
||||
.or()
|
||||
.likeRight(Dept::getDeptCode, codePrefix))
|
||||
.isNotNull(Dept::getOaModified)
|
||||
.ne(Dept::getOaModified, "")
|
||||
.orderByDesc(Dept::getOaModified)
|
||||
@@ -608,13 +621,8 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
if (CollectionUtil.isEmpty(oaCompanies)) {
|
||||
return OrgSyncCount.empty();
|
||||
}
|
||||
// 获取需要的公司名称
|
||||
Set<String> companyNames = syncAll ? Collections.emptySet() : getCompanyNames();
|
||||
// 转换数据
|
||||
List<Dept> allParam = oaCompanies.stream()
|
||||
// 强制跳过上级公司为 0 的数据
|
||||
.filter(company -> !OAConstant.ROOT_COMPANY_ID.equals(company.getSupsubcomid()))
|
||||
// 非全量同步时,筛选只要配置的公司
|
||||
.filter(company -> syncAll || companyNames.contains(company.getSubcompanyname()))
|
||||
.map(deptConvert::company2dept)
|
||||
.toList();
|
||||
@@ -623,10 +631,41 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
|
||||
}
|
||||
|
||||
// 查询已存在公司(含 oaModified),按编码索引
|
||||
Map<String, Dept> existingDeptMap = loadExistingDeptMap(DeptCategory.COMPANY, null);
|
||||
Map<String, Long> deptIdByCode = toDeptIdMap(existingDeptMap);
|
||||
return persistSyncedDepts(allParam, existingDeptMap, deptIdByCode, DeptCategory.COMPANY, filteredSkipCount);
|
||||
Map<String, Dept> existingByOaId = loadExistingByOaId(DeptCategory.COMPANY);
|
||||
Map<String, Dept> codeOwnerMap = loadCompanyCodeOwnerMap();
|
||||
Set<String> usedCodes = new HashSet<>(codeOwnerMap.keySet());
|
||||
Map<String, Dept> batchByOaId = new LinkedHashMap<>();
|
||||
for (Dept dept : allParam) {
|
||||
this.assignCompanyCode(dept, existingByOaId, usedCodes);
|
||||
batchByOaId.put(dept.getOaId(), dept);
|
||||
}
|
||||
// 上级公司编码:OA 上级为 0 → GWWL;否则取上级公司已分配编码
|
||||
for (Dept dept : allParam) {
|
||||
if (OAConstant.ROOT_COMPANY_ID.equals(dept.getOaSupSubComId()) || StringUtils.isBlank(dept.getOaSupSubComId())) {
|
||||
dept.setParentCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE);
|
||||
dept.setParentId(OAConvertConstant.OA_SYNC_ROOT_DEPT_ID);
|
||||
continue;
|
||||
}
|
||||
Dept parent = batchByOaId.get(dept.getOaSupSubComId());
|
||||
if (parent == null) {
|
||||
parent = existingByOaId.get(dept.getOaSupSubComId());
|
||||
}
|
||||
if (parent != null && StringUtils.isNotBlank(parent.getDeptCode())) {
|
||||
dept.setParentCode(parent.getDeptCode());
|
||||
dept.setParentId(parent.getId());
|
||||
} else {
|
||||
dept.setParentCode(null);
|
||||
dept.setParentId(null);
|
||||
}
|
||||
}
|
||||
Map<String, Long> deptIdByCode = new HashMap<>();
|
||||
existingByOaId.values().forEach(dept -> {
|
||||
if (StringUtils.isNotBlank(dept.getDeptCode()) && dept.getId() != null) {
|
||||
deptIdByCode.put(dept.getDeptCode(), dept.getId());
|
||||
}
|
||||
});
|
||||
deptIdByCode.put(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE, OAConvertConstant.OA_SYNC_ROOT_DEPT_ID);
|
||||
return persistSyncedDepts(allParam, existingByOaId, deptIdByCode, DeptCategory.COMPANY, filteredSkipCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -639,103 +678,239 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
if (CollectionUtil.isEmpty(oaDepts)) {
|
||||
return OrgSyncCount.empty();
|
||||
}
|
||||
// 查询所有公司的编码和id的map
|
||||
Map<String, Long> companyDeptMap = getAllCompanyDeptMap();
|
||||
// 根公司id
|
||||
Map<String, Dept> companyByOaId = loadCompanyByOaId();
|
||||
String rootCompanyId = getRootCompanyId();
|
||||
if (!syncAll && rootCompanyId == null) {
|
||||
return new OrgSyncCount(0, oaDepts.size(), Collections.emptyList());
|
||||
}
|
||||
// 根公司下要同步的部门名称
|
||||
Set<String> rootCompanyDeptNames = syncAll ? Collections.emptySet() : getRootCompanyDeptNames();
|
||||
// 转换数据
|
||||
List<Dept> allParam = oaDepts.stream()
|
||||
// 全量同步时不过滤;否则:部门的公司不是根公司,或者部门名称在根公司下需要同步的部门列表中,且是根部门
|
||||
.filter(oaDept -> syncAll
|
||||
|| rootCompanyId == null
|
||||
|| !rootCompanyId.equals(oaDept.getSubcompanyid1())
|
||||
|| (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid()) && rootCompanyDeptNames.contains(oaDept.getDepartmentname())))
|
||||
.map(dept -> deptConvert.dept2dept(dept, companyDeptMap))
|
||||
.map(dept -> deptConvert.dept2dept(dept, companyByOaId))
|
||||
.toList();
|
||||
int filteredSkipCount = oaDepts.size() - allParam.size();
|
||||
if (CollectionUtil.isEmpty(allParam)) {
|
||||
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
|
||||
}
|
||||
Set<String> deptCodes = new HashSet<>();
|
||||
allParam.forEach(dept -> {
|
||||
if (StringUtils.isNotEmpty(dept.getDeptCode())) {
|
||||
deptCodes.add(dept.getDeptCode());
|
||||
|
||||
Map<String, Dept> existingByOaId = loadExistingByOaId(DeptCategory.DEPT);
|
||||
Map<String, Integer> nextSerialByCompany = new HashMap<>();
|
||||
Map<String, Set<String>> usedCodesByCompany = new HashMap<>();
|
||||
// 预载各公司下已有部门编码,保证分段连续
|
||||
for (Dept existing : existingByOaId.values()) {
|
||||
if (StringUtils.isBlank(existing.getBelongCompanyCode()) || StringUtils.isBlank(existing.getDeptCode())) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(dept.getParentCode())) {
|
||||
deptCodes.add(dept.getParentCode());
|
||||
usedCodesByCompany
|
||||
.computeIfAbsent(existing.getBelongCompanyCode(), key -> new HashSet<>())
|
||||
.add(existing.getDeptCode());
|
||||
}
|
||||
Map<String, Dept> batchByOaId = new LinkedHashMap<>();
|
||||
for (Dept dept : allParam) {
|
||||
this.assignDepartmentCode(dept, existingByOaId, companyByOaId, usedCodesByCompany, nextSerialByCompany);
|
||||
batchByOaId.put(dept.getOaId(), dept);
|
||||
}
|
||||
// 解析上级:根部门挂公司;子部门挂上级部门(本批或已存在)
|
||||
Map<String, Long> deptIdByCode = new HashMap<>();
|
||||
companyByOaId.values().forEach(company -> {
|
||||
if (StringUtils.isNotBlank(company.getDeptCode()) && company.getId() != null) {
|
||||
deptIdByCode.put(company.getDeptCode(), company.getId());
|
||||
}
|
||||
});
|
||||
// 查询数据库已有部门(含 oaModified)
|
||||
Map<String, Dept> existingDeptMap = loadExistingDeptMap(DeptCategory.DEPT, deptCodes);
|
||||
Map<String, Long> deptIdByCode = toDeptIdMap(existingDeptMap);
|
||||
// 公司编码也放入 map,根部门挂到公司时才能解析父级
|
||||
companyDeptMap.forEach(deptIdByCode::putIfAbsent);
|
||||
return persistSyncedDepts(allParam, existingDeptMap, deptIdByCode, DeptCategory.DEPT, filteredSkipCount);
|
||||
existingByOaId.values().forEach(dept -> {
|
||||
if (StringUtils.isNotBlank(dept.getDeptCode()) && dept.getId() != null) {
|
||||
deptIdByCode.put(dept.getDeptCode(), dept.getId());
|
||||
}
|
||||
});
|
||||
for (Dept dept : allParam) {
|
||||
if (dept.getParentId() != null && StringUtils.isNotBlank(dept.getParentCode())
|
||||
&& DeptCodeUtil.matchesChildCode(dept.getBelongCompanyCode(), dept.getDeptCode())
|
||||
&& dept.getBelongCompanyCode().equals(dept.getParentCode())) {
|
||||
continue;
|
||||
}
|
||||
String parentOaId = dept.getOaSupSubComId();
|
||||
Dept belongCompany = companyByOaId.values().stream()
|
||||
.filter(company -> StringUtils.equals(company.getDeptCode(), dept.getBelongCompanyCode()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (belongCompany != null && (OAConstant.ROOT_COMPANY_ID.equals(parentOaId)
|
||||
|| StringUtils.equals(parentOaId, belongCompany.getOaId()))) {
|
||||
dept.setParentCode(belongCompany.getDeptCode());
|
||||
dept.setParentId(belongCompany.getId());
|
||||
continue;
|
||||
}
|
||||
Dept parentDept = batchByOaId.get(parentOaId);
|
||||
if (parentDept == null) {
|
||||
parentDept = existingByOaId.get(parentOaId);
|
||||
}
|
||||
if (parentDept != null && StringUtils.isNotBlank(parentDept.getDeptCode())) {
|
||||
dept.setParentCode(parentDept.getDeptCode());
|
||||
dept.setParentId(parentDept.getId());
|
||||
} else if (belongCompany != null) {
|
||||
// 上级部门尚未就绪时先挂所属公司,分批结束后按 OA 主键重建
|
||||
dept.setParentCode(belongCompany.getDeptCode());
|
||||
dept.setParentId(belongCompany.getId());
|
||||
} else {
|
||||
dept.setParentCode(null);
|
||||
dept.setParentId(null);
|
||||
}
|
||||
}
|
||||
return persistSyncedDepts(allParam, existingByOaId, deptIdByCode, DeptCategory.DEPT, filteredSkipCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载已存在组织,按部门编码索引
|
||||
*
|
||||
* @param deptCategory 组织类型
|
||||
* @param deptCodes 限定编码;为空则按类型全量加载
|
||||
* 公司编码:GWWL+三位数字;已有合规编码则复用
|
||||
*/
|
||||
private Map<String, Dept> loadExistingDeptMap(DeptCategory deptCategory, Set<String> deptCodes) {
|
||||
if (deptCodes != null && deptCodes.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
private void assignCompanyCode(Dept dept, Map<String, Dept> existingByOaId, Set<String> usedCodes) {
|
||||
Dept existing = existingByOaId.get(dept.getOaId());
|
||||
if (existing != null && DeptCodeUtil.isCompanyCode(existing.getDeptCode())) {
|
||||
dept.setDeptCode(existing.getDeptCode());
|
||||
dept.setBelongCompanyCode(existing.getDeptCode());
|
||||
usedCodes.add(existing.getDeptCode());
|
||||
return;
|
||||
}
|
||||
int serial = DeptCodeUtil.nextSerial(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE, usedCodes);
|
||||
String deptCode = DeptCodeUtil.formatChildCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE, serial);
|
||||
while (usedCodes.contains(deptCode)) {
|
||||
serial++;
|
||||
deptCode = DeptCodeUtil.formatChildCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE, serial);
|
||||
}
|
||||
usedCodes.add(deptCode);
|
||||
dept.setDeptCode(deptCode);
|
||||
dept.setBelongCompanyCode(deptCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门编码:所属公司编码+三位数字;已有合规编码则复用
|
||||
*/
|
||||
private void assignDepartmentCode(Dept dept, Map<String, Dept> existingByOaId, Map<String, Dept> companyByOaId,
|
||||
Map<String, Set<String>> usedCodesByCompany, Map<String, Integer> nextSerialByCompany) {
|
||||
Dept existing = existingByOaId.get(dept.getOaId());
|
||||
Dept belongCompany = null;
|
||||
if (StringUtils.isNotBlank(dept.getBelongCompanyCode())) {
|
||||
belongCompany = companyByOaId.values().stream()
|
||||
.filter(company -> StringUtils.equals(company.getDeptCode(), dept.getBelongCompanyCode())
|
||||
|| StringUtils.equals(company.getOaId(), dept.getBelongCompanyCode()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
if (belongCompany == null && existing != null && StringUtils.isNotBlank(existing.getBelongCompanyCode())) {
|
||||
String belongCode = existing.getBelongCompanyCode();
|
||||
belongCompany = companyByOaId.values().stream()
|
||||
.filter(company -> StringUtils.equals(company.getDeptCode(), belongCode))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
if (belongCompany == null) {
|
||||
return;
|
||||
}
|
||||
final Dept resolvedCompany = belongCompany;
|
||||
final String companyCode = resolvedCompany.getDeptCode();
|
||||
dept.setBelongCompanyCode(companyCode);
|
||||
if (existing != null && DeptCodeUtil.matchesChildCode(companyCode, existing.getDeptCode())) {
|
||||
dept.setDeptCode(existing.getDeptCode());
|
||||
usedCodesByCompany.computeIfAbsent(companyCode, key -> new HashSet<>()).add(existing.getDeptCode());
|
||||
return;
|
||||
}
|
||||
Set<String> usedCodes = usedCodesByCompany.computeIfAbsent(companyCode, key -> new HashSet<>());
|
||||
int serial = nextSerialByCompany.computeIfAbsent(companyCode,
|
||||
key -> DeptCodeUtil.nextSerial(companyCode, usedCodes));
|
||||
String deptCode = DeptCodeUtil.formatChildCode(companyCode, serial);
|
||||
while (usedCodes.contains(deptCode)) {
|
||||
serial++;
|
||||
deptCode = DeptCodeUtil.formatChildCode(companyCode, serial);
|
||||
}
|
||||
nextSerialByCompany.put(companyCode, serial + 1);
|
||||
usedCodes.add(deptCode);
|
||||
dept.setDeptCode(deptCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 oaId 加载已存在组织
|
||||
*/
|
||||
private Map<String, Dept> loadExistingByOaId(DeptCategory deptCategory) {
|
||||
List<Dept> deptList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptCategory, deptCategory.getCode())
|
||||
.in(deptCodes != null, Dept::getDeptCode, deptCodes)
|
||||
.select(Dept::getId, Dept::getDeptCode, Dept::getOaModified, Dept::getParentId));
|
||||
return deptList.stream()
|
||||
.filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode()))
|
||||
.collect(Collectors.toMap(Dept::getDeptCode, dept -> dept, (a, b) -> b));
|
||||
}
|
||||
|
||||
private Map<String, Long> toDeptIdMap(Map<String, Dept> existingDeptMap) {
|
||||
Map<String, Long> deptIdByCode = new HashMap<>();
|
||||
existingDeptMap.forEach((code, dept) -> deptIdByCode.put(code, dept.getId()));
|
||||
return deptIdByCode;
|
||||
.isNotNull(Dept::getOaId)
|
||||
.ne(Dept::getOaId, "")
|
||||
.select(Dept::getId, Dept::getDeptCode, Dept::getOaModified, Dept::getParentId,
|
||||
Dept::getOaId, Dept::getBelongCompanyCode, Dept::getParentCode, Dept::getOaSupSubComId));
|
||||
Map<String, Dept> byOaId = new HashMap<>();
|
||||
for (Dept dept : deptList) {
|
||||
if (StringUtils.isNotBlank(dept.getOaId())) {
|
||||
byOaId.put(dept.getOaId(), dept);
|
||||
}
|
||||
}
|
||||
return byOaId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化同步数据:不存在则新增;存在且 oaModified 不一致才修改
|
||||
*
|
||||
* @param allParam 待处理数据
|
||||
* @param existingDeptMap 已存在组织(同类型)
|
||||
* @param deptIdByCode 编码→id(含上级公司等)
|
||||
* @param deptCategory 组织类型
|
||||
* @param filteredSkipCount 上游过滤跳过数
|
||||
* @return 同步统计
|
||||
* 已占用的公司编码(GWWLxxx)
|
||||
*/
|
||||
private OrgSyncCount persistSyncedDepts(List<Dept> allParam, Map<String, Dept> existingDeptMap,
|
||||
private Map<String, Dept> loadCompanyCodeOwnerMap() {
|
||||
List<Dept> companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.likeRight(Dept::getDeptCode, OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE)
|
||||
.select(Dept::getId, Dept::getDeptCode, Dept::getOaId));
|
||||
Map<String, Dept> codeMap = new HashMap<>();
|
||||
for (Dept dept : companyList) {
|
||||
if (DeptCodeUtil.isCompanyCode(dept.getDeptCode())) {
|
||||
codeMap.put(dept.getDeptCode(), dept);
|
||||
}
|
||||
}
|
||||
return codeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 已同步公司:oaId → 公司
|
||||
*/
|
||||
private Map<String, Dept> loadCompanyByOaId() {
|
||||
List<Dept> companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.isNotNull(Dept::getOaId)
|
||||
.ne(Dept::getOaId, ""));
|
||||
Map<String, Dept> byOaId = new HashMap<>();
|
||||
for (Dept dept : companyList) {
|
||||
if (StringUtils.isNotBlank(dept.getOaId())) {
|
||||
byOaId.put(dept.getOaId(), dept);
|
||||
}
|
||||
}
|
||||
return byOaId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化同步数据:按 oaId 匹配;不存在则新增;存在且 oaModified 不一致才修改
|
||||
*/
|
||||
private OrgSyncCount persistSyncedDepts(List<Dept> allParam, Map<String, Dept> existingByOaId,
|
||||
Map<String, Long> deptIdByCode, DeptCategory deptCategory,
|
||||
int filteredSkipCount) {
|
||||
Set<Long> existsDeptIds = existingDeptMap.values().stream()
|
||||
Set<Long> existsDeptIds = existingByOaId.values().stream()
|
||||
.map(Dept::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
allParam.forEach(dept -> {
|
||||
if (deptIdByCode.containsKey(dept.getDeptCode())) {
|
||||
Dept existing = existingByOaId.get(dept.getOaId());
|
||||
if (existing != null && existing.getId() != null) {
|
||||
dept.setId(existing.getId());
|
||||
} else if (StringUtils.isNotBlank(dept.getDeptCode()) && deptIdByCode.containsKey(dept.getDeptCode())) {
|
||||
dept.setId(deptIdByCode.get(dept.getDeptCode()));
|
||||
} else {
|
||||
dept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
}
|
||||
if (StringUtils.isNotBlank(dept.getDeptCode())) {
|
||||
deptIdByCode.put(dept.getDeptCode(), dept.getId());
|
||||
}
|
||||
});
|
||||
|
||||
// 重新把没有父id的数据设置一下父id;上级尚未入库时,新增记录先挂到根节点,全部分批完成后重建层级并清理无法挂载的数据
|
||||
int unresolvedParentCount = 0;
|
||||
for (Dept dept : allParam) {
|
||||
if (dept.getParentId() != null) {
|
||||
continue;
|
||||
}
|
||||
Long parentId = deptIdByCode.get(dept.getParentCode());
|
||||
Long parentId = StringUtils.isNotBlank(dept.getParentCode()) ? deptIdByCode.get(dept.getParentCode()) : null;
|
||||
if (parentId != null) {
|
||||
dept.setParentId(parentId);
|
||||
continue;
|
||||
@@ -750,8 +925,8 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
deptCategory.getName(), unresolvedParentCount);
|
||||
}
|
||||
|
||||
// 父id不为空才新增;已存在的记录即使父id未解析也允许更新(空父id不会覆盖原值)
|
||||
List<Dept> list = allParam.stream()
|
||||
.filter(dept -> StringUtils.isNotBlank(dept.getDeptCode()))
|
||||
.filter(dept -> dept.getParentId() != null || existsDeptIds.contains(dept.getId()))
|
||||
.toList();
|
||||
List<Dept> addList = list.stream()
|
||||
@@ -761,10 +936,9 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
ComposeLogUtil.getLastLog().info("批量新增{}:{}", deptCategory.getName(), addList.size());
|
||||
deptService.saveBatch(addList);
|
||||
}
|
||||
// 已存在且 oaModified 不一致(或不存在可比对值)才修改
|
||||
List<Dept> updateList = list.stream()
|
||||
.filter(dept -> existsDeptIds.contains(dept.getId()))
|
||||
.filter(dept -> !shouldSkipOaUpdate(existingDeptMap.get(dept.getDeptCode()), dept))
|
||||
.filter(dept -> !shouldSkipOaUpdate(existingByOaId.get(dept.getOaId()), dept))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(updateList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改{}:{}", deptCategory.getName(), updateList.size());
|
||||
@@ -772,10 +946,11 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
}
|
||||
int unchangedCount = (int) list.stream()
|
||||
.filter(dept -> existsDeptIds.contains(dept.getId()))
|
||||
.filter(dept -> shouldSkipOaUpdate(existingDeptMap.get(dept.getDeptCode()), dept))
|
||||
.filter(dept -> shouldSkipOaUpdate(existingByOaId.get(dept.getOaId()), dept))
|
||||
.count();
|
||||
List<Dept> notHandledList = allParam.stream()
|
||||
.filter(dept -> dept.getParentId() == null && !existsDeptIds.contains(dept.getId()))
|
||||
.filter(dept -> StringUtils.isBlank(dept.getDeptCode())
|
||||
|| (dept.getParentId() == null && !existsDeptIds.contains(dept.getId())))
|
||||
.toList();
|
||||
int syncedCount = addList.size() + updateList.size();
|
||||
int skippedCount = filteredSkipCount + unchangedCount + notHandledList.size();
|
||||
@@ -786,12 +961,17 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
}
|
||||
|
||||
/**
|
||||
* oaModified 一致则跳过更新;任一侧为空无法确认一致,执行更新以补齐字段
|
||||
* oaModified 一致则跳过更新;任一侧为空无法确认一致,执行更新以补齐字段。
|
||||
* 组织编码已按新规则重算且与库中不一致时,即使 modified 相同也要更新。
|
||||
*/
|
||||
private boolean shouldSkipOaUpdate(Dept existing, Dept incoming) {
|
||||
if (existing == null) {
|
||||
return false;
|
||||
}
|
||||
if (incoming != null && StringUtils.isNotBlank(incoming.getDeptCode())
|
||||
&& !StringUtils.equals(existing.getDeptCode(), incoming.getDeptCode())) {
|
||||
return false;
|
||||
}
|
||||
String localModified = StringUtils.trimToEmpty(existing.getOaModified());
|
||||
String remoteModified = StringUtils.trimToEmpty(incoming == null ? null : incoming.getOaModified());
|
||||
if (localModified.isEmpty() || remoteModified.isEmpty()) {
|
||||
@@ -842,6 +1022,8 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
if (syncAll) {
|
||||
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.isNotNull(Dept::getOaId)
|
||||
.ne(Dept::getOaId, "")
|
||||
);
|
||||
} else {
|
||||
Set<String> companyNames = this.getCompanyNames();
|
||||
@@ -851,6 +1033,8 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.in(Dept::getDeptName, companyNames)
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.isNotNull(Dept::getOaId)
|
||||
.ne(Dept::getOaId, "")
|
||||
);
|
||||
}
|
||||
return companyList.stream()
|
||||
@@ -951,13 +1135,20 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
// 查询父部门编码是根公司的,部门类型的,根公司下部门名称的部门
|
||||
Dept rootCompany = deptService.getOne(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getOaId, rootCompanyId)
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
.last("limit 1"), false);
|
||||
if (rootCompany == null || StringUtils.isBlank(rootCompany.getDeptCode())) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.in(Dept::getDeptName, rootCompanyDeptNames)
|
||||
.eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode())
|
||||
.eq(Dept::getParentCode, OAConvertConstant.COMPANY_OA_PREFIX + rootCompanyId)
|
||||
.eq(Dept::getParentCode, rootCompany.getDeptCode())
|
||||
).stream()
|
||||
.filter(dept -> StringUtils.isNotBlank(dept.getDeptCode()))
|
||||
.map(dept -> dept.getDeptCode().replace(OAConvertConstant.DEPARTMENT_OA_PREFIX, ""))
|
||||
.map(Dept::getOaId)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@@ -980,11 +1171,11 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
ComposeLogUtil.getLastLog().warn("未查询到根公司 名称:{}", companyName);
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(dept.getDeptCode())) {
|
||||
if (StringUtils.isBlank(dept.getOaId()) && StringUtils.isBlank(dept.getDeptCode())) {
|
||||
ComposeLogUtil.getLastLog().warn("根公司编码为空:{}", companyName);
|
||||
return null;
|
||||
}
|
||||
// 部门编码去掉前缀,就是oa的id
|
||||
return dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, "");
|
||||
return StringUtils.isNotBlank(dept.getOaId()) ? dept.getOaId()
|
||||
: dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, "");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-28
@@ -86,7 +86,7 @@ public class OaUserListSyncHelper {
|
||||
private final IRoleService roleService;
|
||||
|
||||
/**
|
||||
* 从人员数据提取二级公司、三级部门,并挂到「桂物物流集团」下
|
||||
* 从人员数据提取二级公司、三级部门,并挂到集团顶级(id=135/GWWL) 下
|
||||
*
|
||||
* @param oaPersons OA人员
|
||||
* @return 组织索引
|
||||
@@ -383,33 +383,7 @@ public class OaUserListSyncHelper {
|
||||
}
|
||||
|
||||
private Dept getOrCreateRootCompany() {
|
||||
Dept rootCompany = deptService.getOne(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptName, OAConvertConstant.ROOT_COMPANY_NAME)
|
||||
.last("limit 1"), false);
|
||||
if (rootCompany != null) {
|
||||
return rootCompany;
|
||||
}
|
||||
rootCompany = new Dept();
|
||||
rootCompany.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
rootCompany.setTenantId(resolveTenantId());
|
||||
rootCompany.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
rootCompany.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootCompany.setDeptName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setFullName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setShortName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setDeptCode("OACROOT");
|
||||
rootCompany.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootCompany.setBelongCompanyCode("OACROOT");
|
||||
rootCompany.setDeptCategory(DeptCategory.COMPANY.getCode());
|
||||
rootCompany.setSort(0);
|
||||
rootCompany.setStatus(DataStatusEnum.ENABLE.getCode());
|
||||
rootCompany.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
rootCompany.setIsOa(YES);
|
||||
rootCompany.setIsPlatformCompany(0);
|
||||
rootCompany.setSyncTime(new Date());
|
||||
deptService.save(rootCompany);
|
||||
ComposeLogUtil.getLastLog().info("已创建顶级组织:{}", OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
return rootCompany;
|
||||
return deptService.ensureOaSyncRootDept();
|
||||
}
|
||||
|
||||
private Dept upsertOrg(String name, String oaCode, Dept existing, Dept parent, String tenantId,
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 组织编码工具:上级编码直接拼接固定 3 位分段数字(不足补零,中间不加 "-")
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public final class DeptCodeUtil {
|
||||
|
||||
/**
|
||||
* 分段数字位数
|
||||
*/
|
||||
public static final int SEGMENT_WIDTH = 3;
|
||||
|
||||
private DeptCodeUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成子级编码:上级编码 + 三位数字
|
||||
*/
|
||||
public static String formatChildCode(String parentCode, int serial) {
|
||||
return parentCode + String.format("%0" + SEGMENT_WIDTH + "d", serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否符合「上级编码 + 三位数字」格式(恰好多出 3 位数字)
|
||||
*/
|
||||
public static boolean matchesChildCode(String parentCode, String deptCode) {
|
||||
if (StringUtils.isBlank(parentCode) || StringUtils.isBlank(deptCode)) {
|
||||
return false;
|
||||
}
|
||||
return deptCode.matches("^" + Pattern.quote(parentCode) + "\\d{" + SEGMENT_WIDTH + "}$");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为公司编码(GWWL + 三位数字)
|
||||
*/
|
||||
public static boolean isCompanyCode(String deptCode) {
|
||||
return matchesChildCode(OAConvertConstant.OA_SYNC_ROOT_DEPT_CODE, deptCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在已有编码中取下一分段序号(从 1 起)
|
||||
*/
|
||||
public static int nextSerial(String parentCode, Collection<String> existingCodes) {
|
||||
if (StringUtils.isBlank(parentCode)) {
|
||||
return 1;
|
||||
}
|
||||
Pattern pattern = Pattern.compile("^" + Pattern.quote(parentCode) + "(\\d{" + SEGMENT_WIDTH + "})$");
|
||||
int maxSerial = 0;
|
||||
if (existingCodes != null) {
|
||||
for (String code : existingCodes) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(code.trim());
|
||||
if (!matcher.matches()) {
|
||||
continue;
|
||||
}
|
||||
maxSerial = Math.max(maxSerial, Integer.parseInt(matcher.group(1)));
|
||||
}
|
||||
}
|
||||
return maxSerial + 1;
|
||||
}
|
||||
}
|
||||
+30
-2
@@ -31,7 +31,35 @@ public class OAConvertConstant {
|
||||
*/
|
||||
public static final Long ROOT_PARENT_ID = 0L;
|
||||
/**
|
||||
* 同步人员时挂载的顶级组织名称
|
||||
* OA 同步挂载的本地顶级组织主键(集团)
|
||||
*/
|
||||
public static final String ROOT_COMPANY_NAME = "桂物物流集团";
|
||||
public static final Long OA_SYNC_ROOT_DEPT_ID = 135L;
|
||||
/**
|
||||
* OA 同步挂载的本地顶级组织默认名称(集团;已存在时不覆盖原名称,除非误写成外部组织)
|
||||
*/
|
||||
public static final String OA_SYNC_ROOT_DEPT_NAME = "桂物物流集团";
|
||||
/**
|
||||
* OA 同步挂载的本地顶级组织编码(集团)
|
||||
*/
|
||||
public static final String OA_SYNC_ROOT_DEPT_CODE = "GWWL";
|
||||
/**
|
||||
* 承运商等业务使用的外部组织顶级名称(与集团顶级并存)
|
||||
*/
|
||||
public static final String EXTERNAL_ORG_NAME = "外部组织";
|
||||
/**
|
||||
* 组织编码分段数字位数(不足补零)
|
||||
*/
|
||||
public static final int DEPT_CODE_SEGMENT_WIDTH = 3;
|
||||
/**
|
||||
* 历史人员同步创建的伪顶级编码,同步时需清理
|
||||
*/
|
||||
public static final String LEGACY_ROOT_DEPT_CODE = "OACROOT";
|
||||
/**
|
||||
* 历史人员同步创建的伪顶级名称(与集团同名时仅清理非 id=135 的记录)
|
||||
*/
|
||||
public static final String LEGACY_ROOT_DEPT_NAME = "桂物物流集团";
|
||||
/**
|
||||
* 同步人员时挂载的顶级组织名称(兼容旧引用)
|
||||
*/
|
||||
public static final String ROOT_COMPANY_NAME = OA_SYNC_ROOT_DEPT_NAME;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user