From 3ff90eb19d471ec2dd13d8efb80ddc963b46db54 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sat, 17 Oct 2026 16:22:37 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8DOA=E5=85=AC=E5=8F=B8?= =?UTF-8?q?=E4=B8=8E=E9=83=A8=E9=97=A8=E4=B8=BB=E9=94=AE=E6=92=9E=E5=8F=B7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E4=B8=8B=E7=BA=A7=E9=83=A8=E9=97=A8=E6=8C=82?= =?UTF-8?q?=E9=94=99=E7=88=B6=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../system/service/impl/DeptServiceImpl.java | 81 ++++++++++++++----- .../service/impl/OASyncServiceImpl.java | 28 ++++++- 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java index d55f05e6..036e161b 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java @@ -1039,7 +1039,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID boolean isCompany = this.isOaCompany(dept); boolean parentExists = isCompany ? companyByOaId.containsKey(parentOaId) - : departmentByOaId.containsKey(parentOaId) || companyByOaId.containsKey(parentOaId); + : this.resolveOaDepartmentParent(dept, parentOaId, companyByOaId, departmentByOaId) != null; // 全量数据整理后仍找不到上级:公司、部门一律删除,不改挂保留 if (!parentExists) { deleteIds.add(dept.getId()); @@ -1063,7 +1063,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID } Dept parent = this.isOaCompany(dept) ? companyByOaId.get(parentOaId) - : Optional.ofNullable(departmentByOaId.get(parentOaId)).orElse(companyByOaId.get(parentOaId)); + : this.resolveOaDepartmentParent(dept, parentOaId, companyByOaId, departmentByOaId); if (parent != null && deleteIds.contains(parent.getId())) { deleteIds.add(dept.getId()); oaCascadeChanged = true; @@ -1175,7 +1175,10 @@ public class DeptServiceImpl extends ServiceImpl implements ID } /** - * 根据 OA 上级主键解析本系统父级 + * 根据 OA 上级主键解析本系统父级。 + *

+ * OA 公司与部门主键同属数字空间(如公司 id=19「运输事业部」与部门 id=19「投资发展部」), + * 解析部门上级时必须按「同属公司」消歧,禁止跨公司误挂。 */ private OaParentLink resolveOaParentLink(Dept dept, Map companyByOaId, Map departmentByOaId) { String parentOaId = dept.getOaSupSubComId(); @@ -1202,21 +1205,7 @@ public class DeptServiceImpl extends ServiceImpl implements ID log.warn("部门缺少上级OA主键,跳过层级重建 oaId={} deptId={}", dept.getOaId(), dept.getId()); return null; } - boolean parentIsDepartment = StringUtil.isNotBlank(dept.getParentCode()) - && dept.getParentCode().startsWith(OAConvertConstant.DEPARTMENT_OA_PREFIX); - boolean parentIsCompany = StringUtil.isNotBlank(dept.getParentCode()) - && dept.getParentCode().startsWith(OAConvertConstant.COMPANY_OA_PREFIX); - Dept parent; - if (parentIsDepartment) { - parent = departmentByOaId.get(parentOaId); - } else if (parentIsCompany) { - parent = companyByOaId.get(parentOaId); - } else { - parent = departmentByOaId.get(parentOaId); - if (parent == null) { - parent = companyByOaId.get(parentOaId); - } - } + Dept parent = this.resolveOaDepartmentParent(dept, parentOaId, companyByOaId, departmentByOaId); if (parent == null || parent.getId() == null || Objects.equals(parent.getId(), dept.getId())) { log.warn("未找到上级组织,跳过层级重建 oaId={} oaSupSubComId={} deptId={}", dept.getOaId(), parentOaId, dept.getId()); @@ -1227,6 +1216,62 @@ public class DeptServiceImpl extends ServiceImpl implements ID return new OaParentLink(parent.getId(), this.resolveParentCode(parent, prefix, parentOaId)); } + /** + * 解析部门的 OA 上级:优先同公司下的上级部门,其次所属/同号公司;避免公司与部门 oaId 撞号误挂。 + */ + private Dept resolveOaDepartmentParent(Dept dept, String parentOaId, + Map companyByOaId, Map departmentByOaId) { + Dept belongCompany = this.findBelongCompany(dept, companyByOaId); + // 根部门:oaSupSubComId 存的是所属公司 OA 主键 + if (belongCompany != null && StringUtil.equals(parentOaId, belongCompany.getOaId())) { + return belongCompany; + } + Dept parentDept = departmentByOaId.get(parentOaId); + if (parentDept != null && this.isSameOaCompanyTree(dept, parentDept, companyByOaId)) { + return parentDept; + } + return companyByOaId.get(parentOaId); + } + + /** + * 按所属公司编码或 OA 主键定位本地公司 + */ + private Dept findBelongCompany(Dept dept, Map companyByOaId) { + if (dept == null || companyByOaId == null || companyByOaId.isEmpty()) { + return null; + } + if (StringUtil.isNotBlank(dept.getBelongCompanyCode())) { + for (Dept company : companyByOaId.values()) { + if (company != null && StringUtil.equals(company.getDeptCode(), dept.getBelongCompanyCode())) { + return company; + } + } + Dept byOaId = companyByOaId.get(dept.getBelongCompanyCode()); + if (byOaId != null) { + return byOaId; + } + } + return null; + } + + /** + * 判断两个组织是否同属一家 OA 公司(用于部门 oaId 与公司 oaId 撞号消歧) + */ + private boolean isSameOaCompanyTree(Dept left, Dept right, Map companyByOaId) { + if (left == null || right == null) { + return false; + } + if (StringUtil.isNotBlank(left.getBelongCompanyCode()) && StringUtil.isNotBlank(right.getBelongCompanyCode())) { + return StringUtil.equals(left.getBelongCompanyCode(), right.getBelongCompanyCode()); + } + Dept leftCompany = this.findBelongCompany(left, companyByOaId); + Dept rightCompany = this.isOaCompany(right) ? right : this.findBelongCompany(right, companyByOaId); + if (leftCompany == null || rightCompany == null || leftCompany.getId() == null || rightCompany.getId() == null) { + return false; + } + return Objects.equals(leftCompany.getId(), rightCompany.getId()); + } + private String resolveParentCode(Dept parent, String prefix, String parentOaId) { if (StringUtil.isNotBlank(parent.getDeptCode())) { return parent.getDeptCode(); diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java index 72930c52..6f3143a4 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java @@ -468,10 +468,11 @@ public class OASyncServiceImpl implements IOASyncService { boolean companyPageFinished = Boolean.TRUE.equals(pageVO.getFinished()); if (companyPageFinished) { if (companyIndex + 1 < companyOaIds.size()) { - // 当前公司部门已拉完,进入下一公司(整体未结束,不重建层级) + // 当前公司部门已拉完:先按 OA 主键校正父子挂载,再进入下一公司 pageVO.setCompanyFinished(true); pageVO.setNextCompanyOaId(companyOaIds.get(companyIndex + 1)); pageVO.setFinished(false); + deptService.rebuildOaHierarchy(); } else { // 全部公司轮询完成:清理白名单外组织、重建层级、清理无法挂载数据并刷新祖级列表 pageVO.setCompanyFinished(true); @@ -805,9 +806,11 @@ public class OASyncServiceImpl implements IOASyncService { } String parentOaId = dept.getOaSupSubComId(); Dept belongCompany = companyByOaId.values().stream() - .filter(company -> StringUtils.equals(company.getDeptCode(), dept.getBelongCompanyCode())) + .filter(company -> StringUtils.equals(company.getDeptCode(), dept.getBelongCompanyCode()) + || StringUtils.equals(company.getOaId(), dept.getBelongCompanyCode())) .findFirst() .orElse(null); + // 根部门:oaSupSubComId 即所属公司 OA 主键,必须挂公司(OA 公司/部门主键会撞号) if (belongCompany != null && (OAConstant.ROOT_COMPANY_ID.equals(parentOaId) || StringUtils.equals(parentOaId, belongCompany.getOaId()))) { dept.setParentCode(belongCompany.getDeptCode()); @@ -818,9 +821,19 @@ public class OASyncServiceImpl implements IOASyncService { if (parentDept == null) { parentDept = existingByOaId.get(parentOaId); } + // 跨公司同号部门不可作为上级(例如总部部门 id=19 与公司 id=19) + if (parentDept != null && StringUtils.isNotBlank(dept.getBelongCompanyCode()) + && StringUtils.isNotBlank(parentDept.getBelongCompanyCode()) + && !StringUtils.equals(dept.getBelongCompanyCode(), parentDept.getBelongCompanyCode())) { + parentDept = null; + } + Dept parentCompany = StringUtils.isNotBlank(parentOaId) ? companyByOaId.get(parentOaId) : null; if (parentDept != null && StringUtils.isNotBlank(parentDept.getDeptCode())) { dept.setParentCode(parentDept.getDeptCode()); dept.setParentId(parentDept.getId()); + } else if (parentCompany != null && StringUtils.isNotBlank(parentCompany.getDeptCode())) { + dept.setParentCode(parentCompany.getDeptCode()); + dept.setParentId(parentCompany.getId()); } else if (belongCompany != null) { // 上级部门尚未就绪时先挂所属公司,分批结束后按 OA 主键重建 dept.setParentCode(belongCompany.getDeptCode()); @@ -1034,7 +1047,7 @@ public class OASyncServiceImpl implements IOASyncService { /** * oaModified 一致则跳过更新;任一侧为空无法确认一致,执行更新以补齐字段。 - * 组织编码已按新规则重算且与库中不一致时,即使 modified 相同也要更新。 + * 组织编码已按新规则重算、或父子挂载与库中不一致时,即使 modified 相同也要更新。 */ private boolean shouldSkipOaUpdate(Dept existing, Dept incoming) { if (existing == null) { @@ -1044,6 +1057,15 @@ public class OASyncServiceImpl implements IOASyncService { && !StringUtils.equals(existing.getDeptCode(), incoming.getDeptCode())) { return false; } + if (incoming != null && incoming.getParentId() != null + && !Objects.equals(existing.getParentId(), incoming.getParentId())) { + return false; + } + if (incoming != null && StringUtils.isNotBlank(incoming.getParentCode()) + && !StringUtils.equals(StringUtils.trimToEmpty(existing.getParentCode()), + StringUtils.trimToEmpty(incoming.getParentCode()))) { + return false; + } String localModified = StringUtils.trimToEmpty(existing.getOaModified()); String remoteModified = StringUtils.trimToEmpty(incoming == null ? null : incoming.getOaModified()); if (localModified.isEmpty() || remoteModified.isEmpty()) {