From 6ac75110191ec0d5eb6e5f9483be8b5a09da9b46 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Tue, 30 Dec 2025 18:38:07 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E5=85=AC=E5=8F=B8=E4=BB=A5=E5=8F=8A=E9=83=A8=E9=97=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9D=83=E9=99=90=E7=9A=84=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/BusinessDeptHandleUtil.java | 4 + .../docs/数据权限忽略与上下文说明.md | 42 +++++++++++ .../ZtDataPermissionAutoConfiguration.java | 12 +++ .../CompanyDataPermissionIgnore.java | 21 ++++++ .../annotation/DeptDataPermissionIgnore.java | 21 ++++++ .../CompanyDataPermissionIgnoreAspect.java | 31 ++++++++ .../aop/DeptDataPermissionIgnoreAspect.java | 31 ++++++++ .../core/db/DataPermissionRuleHandler.java | 11 +++ .../company/CompanyDataPermissionRule.java | 5 ++ .../rule/dept/DeptDataPermissionRule.java | 21 ++++-- .../rule/dept/DeptDataPermissionRuleTest.java | 75 +++++++++++++++++-- 11 files changed, 261 insertions(+), 13 deletions(-) create mode 100644 zt-framework/zt-spring-boot-starter-biz-data-permission/docs/数据权限忽略与上下文说明.md create mode 100644 zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/CompanyDataPermissionIgnore.java create mode 100644 zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/DeptDataPermissionIgnore.java create mode 100644 zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java create mode 100644 zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java diff --git a/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/core/util/BusinessDeptHandleUtil.java b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/core/util/BusinessDeptHandleUtil.java index 03502d9c..0f18c4bb 100644 --- a/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/core/util/BusinessDeptHandleUtil.java +++ b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/core/util/BusinessDeptHandleUtil.java @@ -8,6 +8,7 @@ import com.zt.plat.framework.common.util.json.JsonUtils; import com.zt.plat.framework.common.util.spring.SpringUtils; import com.zt.plat.framework.security.core.LoginUser; import com.zt.plat.framework.tenant.core.context.CompanyContextHolder; +import com.zt.plat.framework.tenant.core.context.DeptContextHolder; import com.zt.plat.framework.web.core.util.WebFrameworkUtils; import com.zt.plat.module.system.api.dept.DeptApi; import com.zt.plat.module.system.api.dept.dto.CompanyDeptInfoRespDTO; @@ -197,6 +198,9 @@ public class BusinessDeptHandleUtil { } CompanyContextHolder.setIgnore(false); CompanyContextHolder.setCompanyId(Long.valueOf(info.getCompanyId())); + DeptContextHolder.setIgnore(false); + DeptContextHolder.setCompanyId(Long.valueOf(info.getCompanyId())); + DeptContextHolder.setDeptId(Long.valueOf(info.getDeptId())); return true; } } diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/docs/数据权限忽略与上下文说明.md b/zt-framework/zt-spring-boot-starter-biz-data-permission/docs/数据权限忽略与上下文说明.md new file mode 100644 index 00000000..669a0729 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/docs/数据权限忽略与上下文说明.md @@ -0,0 +1,42 @@ +# 数据权限忽略与上下文覆盖说明 + +本文说明新增的公司/部门数据权限忽略能力,以及部门上下文对数据权限的覆盖策略。 + +## 新增注解 + +- `@CompanyDataPermissionIgnore(enable = "true")` +- `@DeptDataPermissionIgnore(enable = "true")` + +用法: +- 可标记在类或方法上。 +- `enable` 支持 Spring EL,计算结果为 `true` 时生效,默认开启。 +- 生效后,在方法执行期间临时设置忽略标记,结束后自动恢复。 + +## 忽略生效范围 + +- 公司数据权限:切面在进入方法时将 `CompanyContextHolder.setIgnore(true)`,数据权限规则检测到后直接放行。 +- 部门数据权限:切面在进入方法时将 `DeptContextHolder.setIgnore(true)`,部门数据权限规则检测到后直接放行。 + +## 部门上下文覆盖策略 + +当未忽略部门数据权限且上下文存在有效部门 ID 时: +- 优先使用上下文中的单一部门作为过滤条件,避免因默认的 `ALL` 或“无部门且不可查看自己”导致放行或误判无权。 +- 上下文部门不会修改原有的数据权限 DTO,仅在当前计算中使用。 +- 若上下文公司与缓存公司不一致,会记录告警日志,但仍按上下文部门过滤。 + +## 典型场景 + +1) **任务/全局调用需要暂时关闭数据权限** + - 在方法上标记 `@DeptDataPermissionIgnore` 或 `@CompanyDataPermissionIgnore`。 + +2) **带部门上下文的接口调用** + - 请求预先设置 `DeptContextHolder.setContext(deptId, companyId)`。 + - 即便数据权限声明为 `all=true`,也会按该部门过滤,避免读出全量。 + +3) **无部门权限但指定了上下文部门** + - 即使 `deptIds` 为空且 `self=false`,只要上下文提供部门,也会使用该部门过滤,而非直接判定无权。 + +## 注意事项 + +- 忽略标记只作用于当前线程上下文,切面会在 `finally` 中恢复旧值,嵌套调用安全。 +- 若需要同时忽略公司与部门数据权限,可叠加两个注解或在业务代码中分别设置忽略标记。 diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java index 0a1c5350..1c7d9e91 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java @@ -1,6 +1,8 @@ package com.zt.plat.framework.datapermission.config; +import com.zt.plat.framework.datapermission.core.aop.CompanyDataPermissionIgnoreAspect; import com.zt.plat.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor; +import com.zt.plat.framework.datapermission.core.aop.DeptDataPermissionIgnoreAspect; import com.zt.plat.framework.datapermission.core.db.DataPermissionRuleHandler; import com.zt.plat.framework.datapermission.core.rule.DataPermissionRule; import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactory; @@ -43,4 +45,14 @@ public class ZtDataPermissionAutoConfiguration { return new DataPermissionAnnotationAdvisor(); } + @Bean + public DeptDataPermissionIgnoreAspect deptDataPermissionIgnoreAspect() { + return new DeptDataPermissionIgnoreAspect(); + } + + @Bean + public CompanyDataPermissionIgnoreAspect companyDataPermissionIgnoreAspect() { + return new CompanyDataPermissionIgnoreAspect(); + } + } diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/CompanyDataPermissionIgnore.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/CompanyDataPermissionIgnore.java new file mode 100644 index 00000000..ecc4f1bb --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/CompanyDataPermissionIgnore.java @@ -0,0 +1,21 @@ +package com.zt.plat.framework.datapermission.core.annotation; + +import java.lang.annotation.*; + +/** + * 忽略公司数据权限的注解。 + *

+ * 标记在方法或类上时,匹配的调用会临时忽略公司类型的数据权限规则。 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface CompanyDataPermissionIgnore { + + /** + * 是否开启忽略,默认开启。 + * 支持 Spring EL 表达式,返回 true 时生效。 + */ + String enable() default "true"; +} \ No newline at end of file diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/DeptDataPermissionIgnore.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/DeptDataPermissionIgnore.java new file mode 100644 index 00000000..de80a7d1 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/annotation/DeptDataPermissionIgnore.java @@ -0,0 +1,21 @@ +package com.zt.plat.framework.datapermission.core.annotation; + +import java.lang.annotation.*; + +/** + * 忽略部门数据权限的注解。 + *

+ * 标记在方法或类上时,匹配的调用会临时忽略部门类型的数据权限规则。 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DeptDataPermissionIgnore { + + /** + * 是否开启忽略,默认开启。 + * 支持 Spring EL 表达式,返回 true 时生效。 + */ + String enable() default "true"; +} \ No newline at end of file diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java new file mode 100644 index 00000000..ae051a25 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java @@ -0,0 +1,31 @@ +package com.zt.plat.framework.datapermission.core.aop; + +import com.zt.plat.framework.common.util.spring.SpringExpressionUtils; +import com.zt.plat.framework.datapermission.core.annotation.CompanyDataPermissionIgnore; +import com.zt.plat.framework.tenant.core.context.CompanyContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + +/** + * 公司数据权限忽略切面,基于 {@link CompanyDataPermissionIgnore} 注解。 + */ +@Aspect +@Slf4j +public class CompanyDataPermissionIgnoreAspect { + + @Around("@within(companyDataPermissionIgnore) || @annotation(companyDataPermissionIgnore)") + public Object around(ProceedingJoinPoint joinPoint, CompanyDataPermissionIgnore companyDataPermissionIgnore) throws Throwable { + boolean oldIgnore = CompanyContextHolder.isIgnore(); + try { + Object enable = SpringExpressionUtils.parseExpression(companyDataPermissionIgnore.enable()); + if (Boolean.TRUE.equals(enable)) { + CompanyContextHolder.setIgnore(true); + } + return joinPoint.proceed(); + } finally { + CompanyContextHolder.setIgnore(oldIgnore); + } + } +} \ No newline at end of file diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java new file mode 100644 index 00000000..4ee9054e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java @@ -0,0 +1,31 @@ +package com.zt.plat.framework.datapermission.core.aop; + +import com.zt.plat.framework.common.util.spring.SpringExpressionUtils; +import com.zt.plat.framework.datapermission.core.annotation.DeptDataPermissionIgnore; +import com.zt.plat.framework.tenant.core.context.DeptContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + +/** + * 部门数据权限忽略切面,基于 {@link DeptDataPermissionIgnore} 注解。 + */ +@Aspect +@Slf4j +public class DeptDataPermissionIgnoreAspect { + + @Around("@within(deptDataPermissionIgnore) || @annotation(deptDataPermissionIgnore)") + public Object around(ProceedingJoinPoint joinPoint, DeptDataPermissionIgnore deptDataPermissionIgnore) throws Throwable { + boolean oldIgnore = DeptContextHolder.shouldIgnore(); + try { + Object enable = SpringExpressionUtils.parseExpression(deptDataPermissionIgnore.enable()); + if (Boolean.TRUE.equals(enable)) { + DeptContextHolder.setIgnore(true); + } + return joinPoint.proceed(); + } finally { + DeptContextHolder.setIgnore(oldIgnore); + } + } +} \ No newline at end of file diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/db/DataPermissionRuleHandler.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/db/DataPermissionRuleHandler.java index aa93b5fe..d23e9ac8 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/db/DataPermissionRuleHandler.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/db/DataPermissionRuleHandler.java @@ -10,7 +10,9 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.schema.Table; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck; @@ -41,6 +43,7 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler { } // 生成条件 + final Set processed = new HashSet<>(); Expression allExpression = null; for (DataPermissionRule rule : rules) { // 判断表名是否匹配 @@ -49,6 +52,14 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler { continue; } + // 同一张表 + 同一别名 + 同一规则 在一次 SQL 解析内仅处理一次,避免重复拼接条件 + String aliasName = table.getAlias() == null ? "" : table.getAlias().getName(); + String key = tableName + "|" + aliasName + "|" + rule.getClass().getName(); + if (processed.contains(key)) { + continue; + } + processed.add(key); + // 单条规则的条件 Expression oneExpress = rule.getExpression(tableName, table.getAlias()); if (oneExpress == null) { diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/company/CompanyDataPermissionRule.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/company/CompanyDataPermissionRule.java index 5b46c28f..c02348c6 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/company/CompanyDataPermissionRule.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/company/CompanyDataPermissionRule.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil; import com.zt.plat.framework.common.util.collection.CollectionUtils; import com.zt.plat.framework.datapermission.core.rule.DataPermissionRule; import com.zt.plat.framework.mybatis.core.util.MyBatisUtils; +import com.zt.plat.framework.tenant.core.context.CompanyContextHolder; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.*; @@ -46,6 +47,10 @@ public class CompanyDataPermissionRule implements DataPermissionRule { @Override public Expression getExpression(String tableName, Alias tableAlias) { + // 显式忽略公司数据权限时直接放行 + if (CompanyContextHolder.isIgnore()) { + return null; + } // 业务拼接 Company 的条件 if (getLoginUserCompanyId() == null) { // 如果没有登录用户的公司编号,则不需要拼接条件 diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java index e54a7f54..ea4a8a93 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java @@ -156,21 +156,28 @@ public class DeptDataPermissionRule implements DataPermissionRule { } } - // 情况一,如果是 ALL 可查看全部,则无需拼接条件 - if (deptDataPermission.getAll()) { + // 计算有效的部门与自查标记:当存在上下文部门且未被忽略时,强制仅使用该部门,以避免默认全量或空权限分支 + Set effectiveDeptIds = deptDataPermission.getDeptIds(); + Boolean effectiveSelf = deptDataPermission.getSelf(); + if (!DeptContextHolder.shouldIgnore() && ctxDeptId != null && ctxDeptId > 0L) { + effectiveDeptIds = CollUtil.newHashSet(ctxDeptId); + } + + // 情况一:仅当不存在上下文部门时,且 ALL 可查看全部,才无需拼接条件;若存在上下文部门则仍需基于该部门过滤 + if (ctxDeptId == null && deptDataPermission.getAll()) { return null; } - // 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限 - if (CollUtil.isEmpty(deptDataPermission.getDeptIds()) - && Boolean.FALSE.equals(deptDataPermission.getSelf())) { + // 情况二:仅在有效部门集合为空且不可查看自己时,才认为无权限;若上下文提供部门,则跳过该兜底 + if (CollUtil.isEmpty(effectiveDeptIds) + && Boolean.FALSE.equals(effectiveSelf)) { return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空 } // 情况三,拼接 Dept 和 Company User 的条件,最后组合 - Expression deptExpression = buildDeptExpression(tableName, tableAlias, deptDataPermission.getDeptIds()); + Expression deptExpression = buildDeptExpression(tableName, tableAlias, effectiveDeptIds); // Expression deptExpression = buildDeptExpression(tableName, tableAlias, deptDataPermission.getDeptIds()); - Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId()); + Expression userExpression = buildUserExpression(tableName, tableAlias, effectiveSelf, loginUser.getId()); if (deptExpression == null && userExpression == null) { // TODO ZT:获得不到条件的时候,暂时不抛出异常,而是不返回数据 log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]", diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/test/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/test/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java index 77f194ce..e68941db 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/test/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/test/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java @@ -264,7 +264,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { } } - @Test // 上下文部门存在且公司一致时,清空原集合并覆盖为单一 deptId + @Test // 上下文部门存在且公司一致时,表达式按上下文 deptId 生效,但不修改原数据权限集合 void testGetExpression_deptContextOverride_companyMatch() { try (MockedStatic secMock = mockStatic(SecurityFrameworkUtils.class); MockedStatic deptCtxMock = mockStatic(DeptContextHolder.class); @@ -292,12 +292,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { Expression expression = rule.getExpression(tableName, tableAlias); assertEquals("u.dept_id IN (99)", expression.toString()); - assertEquals(CollUtil.newLinkedHashSet(99L), deptDataPermission.getDeptIds()); + // 原始权限对象不被修改,只是临时使用上下文 deptId 计算 + assertEquals(CollUtil.newLinkedHashSet(10L, 20L), deptDataPermission.getDeptIds()); assertEquals(1L, deptDataPermission.getCompanyId()); } } - @Test // 上下文部门存在但公司不一致时,记录告警并保持原逻辑(不覆盖) + @Test // 上下文部门存在但公司不一致时,仍按上下文 deptId 过滤,原数据权限保持不变 void testGetExpression_deptContextOverride_companyMismatch() { try (MockedStatic secMock = mockStatic(SecurityFrameworkUtils.class); MockedStatic deptCtxMock = mockStatic(DeptContextHolder.class); @@ -324,10 +325,72 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { Expression expression = rule.getExpression(tableName, tableAlias); - assertEquals("u.dept_id IN (10)", expression.toString()); - assertEquals(CollUtil.newLinkedHashSet(10L), deptDataPermission.getDeptIds()); - assertEquals(1L, deptDataPermission.getCompanyId()); + assertEquals("u.dept_id IN (99)", expression.toString()); + // 原始权限对象不被修改 + assertEquals(CollUtil.newLinkedHashSet(10L), deptDataPermission.getDeptIds()); + assertEquals(1L, deptDataPermission.getCompanyId()); } } + @Test // ALL 权限但存在上下文部门时,仍按上下文部门过滤 + void testGetExpression_allPermission_withCtxDept() { + try (MockedStatic secMock = mockStatic(SecurityFrameworkUtils.class); + MockedStatic deptCtxMock = mockStatic(DeptContextHolder.class); + MockedStatic companyCtxMock = mockStatic(CompanyContextHolder.class)) { + + String tableName = "t_user"; + Alias tableAlias = new Alias("u"); + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); + + DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() + .setAll(true) + .setDeptIds(CollUtil.newLinkedHashSet(10L)); + when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); + + deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false); + deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(99L); + deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(1L); + companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L); + companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false); + + rule.addDeptColumn(tableName, "dept_id"); + + Expression expression = rule.getExpression(tableName, tableAlias); + + assertEquals("u.dept_id IN (99)", expression.toString()); + } + } + + @Test // 无部门且不可查看自己,但上下文提供部门时,应使用上下文部门而非判定无权限 + void testGetExpression_noDeptNoSelf_withCtxDept() { + try (MockedStatic secMock = mockStatic(SecurityFrameworkUtils.class); + MockedStatic deptCtxMock = mockStatic(DeptContextHolder.class); + MockedStatic companyCtxMock = mockStatic(CompanyContextHolder.class)) { + + String tableName = "t_user"; + Alias tableAlias = new Alias("u"); + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); + + DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() + .setSelf(false); + when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); + + deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false); + deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(88L); + deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(1L); + companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L); + companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false); + + rule.addDeptColumn(tableName, "dept_id"); + + Expression expression = rule.getExpression(tableName, tableAlias); + + assertEquals("u.dept_id IN (88)", expression.toString()); + } + } + } From 3573217507615d54c3c201f04ad39a5e6f63b1af Mon Sep 17 00:00:00 2001 From: maimaishu <14610861+maimaishu@user.noreply.gitee.com> Date: Wed, 31 Dec 2025 16:55:20 +0800 Subject: [PATCH 2/7] =?UTF-8?q?[+]=E5=A2=9E=E5=8A=A0=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E6=8E=A8=E5=8A=A8=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zt/plat/module/system/api/esp/EspApi.java | 94 +++++++ .../module/system/api/esp/dto/EspDto.java | 27 ++ .../controller/admin/dept/EspController.java | 117 ++++++++ .../dept/vo/depexternalcode/EspPageReqVO.java | 25 ++ .../vo/depexternalcode/EspSaveRespVo.java | 28 ++ .../dal/dataobject/dept/DeptPushMsgDO.java | 58 ++++ .../system/dal/mysql/dept/EspMapper.java | 57 ++++ .../system/service/dept/EspServiceImpl.java | 256 ++++++++++++++++++ .../system/service/dept/IEspService.java | 79 ++++++ 9 files changed, 741 insertions(+) create mode 100644 zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java create mode 100644 zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/dto/EspDto.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspPageReqVO.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java new file mode 100644 index 00000000..a7c338a5 --- /dev/null +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java @@ -0,0 +1,94 @@ +package com.zt.plat.module.system.api.esp; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.system.api.dept.dto.*; +import com.zt.plat.module.system.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 部门") +public interface EspApi { + + String PREFIX = ApiConstants.PREFIX + "/dept"; + + // === 以下为补全的接口方法 === + @PostMapping(PREFIX + "/create") + @Operation(summary = "新增部门") + CommonResult createDept(@RequestBody DeptSaveReqDTO createReqVO); + + @PutMapping(PREFIX + "/update") + @Operation(summary = "修改部门") + CommonResult updateDept(@RequestBody DeptSaveReqDTO updateReqVO); + + @DeleteMapping(PREFIX + "/delete") + @Operation(summary = "删除部门") + CommonResult deleteDept(@RequestParam("id") Long id); + + @PostMapping(PREFIX + "/list-all") + @Operation(summary = "获得部门列表") + CommonResult> getDeptList(@RequestBody DeptListReqDTO reqVO); + + @GetMapping(PREFIX + "/simple-list") + @Operation(summary = "获得部门精简信息列表") + CommonResult> getSimpleDeptList(); + + @GetMapping(PREFIX + "/simple-company-list") + @Operation(summary = "获得公司精简信息列表") + CommonResult> getSimpleCompanyList(); + + @GetMapping(PREFIX + "/all-company-list") + @Operation(summary = "获得所有公司精简信息列表") + CommonResult> getAllCompanyList(); + + @GetMapping(PREFIX + "/get") + @Operation(summary = "获得部门信息") + @Parameter(name = "id", description = "部门编号", example = "1024", required = true) + CommonResult getDept(@RequestParam("id") Long id); + + @GetMapping(PREFIX + "/list") + @Operation(summary = "获得部门信息数组") + @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) + CommonResult> getDeptList(@RequestParam("ids") Collection ids); + + @GetMapping(PREFIX + "/valid") + @Operation(summary = "校验部门是否合法") + @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) + CommonResult validateDeptList(@RequestParam("ids") Collection ids); + + /** + * 获得指定编号的部门 Map + * + * @param ids 部门编号数组 + * @return 部门 Map + */ + default Map getDeptMap(Collection ids) { + List list = getDeptList(ids).getCheckedData(); + return CollectionUtils.convertMap(list, DeptRespDTO::getId); + } + + @GetMapping(PREFIX + "/list-child") + @Operation(summary = "获得指定部门的所有子部门") + @Parameter(name = "id", description = "部门编号", example = "1024", required = true) + CommonResult> getChildDeptList(@RequestParam("id") Long id); + + @GetMapping(PREFIX + "/company-dept-info") + @Operation(summary = "获得指定用户的公司部门信息") + @Parameter(name = "userId", description = "用户编号", example = "1", required = true) + CommonResult> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId); + + // ========== 数据同步专用接口 ========== + + @PostMapping(PREFIX + "/sync") + @Operation(summary = "同步部门") + CommonResult syncDept(@RequestBody DeptSaveReqDTO syncReqDTO); + +} diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/dto/EspDto.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/dto/EspDto.java new file mode 100644 index 00000000..d69e1ab2 --- /dev/null +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/dto/EspDto.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.system.api.esp.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.util.List; + +@Schema(description = "RPC 服务 - 推送外部系统配置信息 Response DTO") +@Data +public class EspDto { + + @Schema(description = "部门名称,模糊匹配", example = "ZT") + private String name; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + + @Schema(description = "是否公司", example = "false") + private Boolean isCompany; + + @Schema(description = "是否集团", example = "false") + private Boolean isGroup; + + @Schema(description = "部门编号集合,支持多部门查询", example = "[\"1001\", \"1002\"]") + private List ids; + + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java new file mode 100644 index 00000000..8ae5afa5 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java @@ -0,0 +1,117 @@ +package com.zt.plat.module.system.controller.admin.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; +import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; +import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; +import com.zt.plat.module.system.service.dept.DeptService; +import com.zt.plat.module.system.service.dept.IEspService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 部门推送消息") +@RestController +@RequestMapping("/system/esp") +@Validated +public class EspController +{ + + @Resource + private IEspService espService; + @Resource + private DeptService deptService; + + @PostMapping("/create") + @Operation(summary = "创建部门推送消息") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:create')") + public CommonResult create(@Valid @RequestBody EspSaveRespVo createReqVO) { + Long id = espService.createDeptPushMsg(createReqVO); + return success(id); + } + + @PutMapping("/update") + @Operation(summary = "修改部门推送消息") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:update')") + public CommonResult update(@Valid @RequestBody EspSaveRespVo updateReqVO) { + espService.updateDeptPushMsg(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除部门推送消息") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:delete')") + public CommonResult delete(@RequestParam("id") Long id) { + espService.deleteDeptPushMsg(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获取部门推送消息详情") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:query')") + public CommonResult get(@RequestParam("id") Long id) { + DeptPushMsgDO entity = espService.getDeptPushMsgDetails(id); + EspSaveRespVo respVO = BeanUtils.toBean(entity, EspSaveRespVo.class); + fillDeptInfo(List.of(respVO)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "分页查询部门推送消息") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:query')") + public CommonResult> page(@Valid EspPageReqVO reqVO) { + PageResult pageResult = espService.getDeptExternalCodePage(reqVO); + PageResult result = BeanUtils.toBean(pageResult, EspSaveRespVo.class); + fillDeptInfo(result.getList()); + return success(result); + } + + @GetMapping("/list-by-dept") + @Operation(summary = "根据部门部门推送消息") + @Parameter(name = "deptId", description = "部门编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:esp-external-code:query')") + public CommonResult> listByDept(@RequestParam("deptId") Long deptId) { + List list = espService.getPushMsgByDeptId(deptId); + List respList = BeanUtils.toBean(list, EspSaveRespVo.class); + fillDeptInfo(respList); + return success(respList); + } + + + + private void fillDeptInfo(List list) { + if (list == null || list.isEmpty()) { + return; + } + Set deptIds = list.stream() + .map(EspSaveRespVo::getDeptId) + .collect(Collectors.toCollection(HashSet::new)); + if (deptIds == null || deptIds.isEmpty()) { + return; + } + Map deptMap = deptService.getDeptList(deptIds).stream() + .collect(Collectors.toMap(DeptDO::getId, dept -> dept, (left, right) -> left)); + list.forEach(item -> { + DeptDO dept = deptMap.get(item.getDeptId()); + if (dept != null) { + item.setDeptName(dept.getName()); + item.setDeptCode(dept.getCode()); + } + }); + } + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspPageReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspPageReqVO.java new file mode 100644 index 00000000..10e0ab8b --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspPageReqVO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 部门外部组织编码映射分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class EspPageReqVO extends PageParam { + + @Schema(description = "部门编号", example = "1024") + private Long deptId; + + @Schema(description = "外部系统标识", example = "ERP") + private String systemCode; + + @Schema(description = "外部组织编码", example = "100200") + private String externalDeptCode; + + @Schema(description = "状态", example = "0") + private Integer status; + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java new file mode 100644 index 00000000..859ba772 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 部门外消息推送创建/修改 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class EspSaveRespVo extends DeptExternalCodeBaseVO { + + @Schema(description = "映射编号", example = "1024") + private Long id; + + @Schema(description = "所属部门名称", example = "技术部") + private String deptName; + + @Schema(description = "所属部门编码", example = "DEPT_001") + private String deptCode; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "最后更新时间") + private LocalDateTime updateTime; + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java new file mode 100644 index 00000000..237c28bc --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java @@ -0,0 +1,58 @@ +package com.zt.plat.module.system.dal.dataobject.dept; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.tenant.core.db.TenantBaseDO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 部门推送消息 DO + */ +@TableName("system_dept_push_msg") +@KeySequence("system_dept_push_msg_seq") +@Data +@EqualsAndHashCode(callSuper = true) +public class DeptPushMsgDO extends TenantBaseDO { + + /** + * 主键编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 本系统部门 ID + */ + private Long deptId; + + /** + * 外部系统标识 + */ + private String systemCode; + + /** + * 外部系统组织编码 + */ + private String externalDeptCode; + + /** + * 外部系统组织名称 + */ + private String externalDeptName; + + /** + * 映射状态 + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java new file mode 100644 index 00000000..7f4cc147 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.system.dal.mysql.dept; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; +import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; +import org.apache.ibatis.annotations.Mapper; +import java.util.List; +/** + * 部门推送消息接口Mapper + */ +@Mapper +public interface EspMapper extends BaseMapperX { + + + /** + * 分页查询 + * @param reqVO 消息推送VO + * @return PageResult + */ + default PageResult selectPage(EspPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DeptPushMsgDO::getDeptId,reqVO.getDeptId()) + .eqIfPresent(DeptPushMsgDO::getSystemCode, reqVO.getSystemCode()) + .likeIfPresent(DeptPushMsgDO::getExternalDeptCode, reqVO.getExternalDeptCode()) + .eqIfPresent(DeptPushMsgDO::getStatus, reqVO.getStatus()) + .orderByDesc(DeptPushMsgDO::getId)); + } + + + default DeptPushMsgDO selectBySystemCodeAndDeptId(String systemCode, Long deptId) { + return selectOne(new LambdaQueryWrapperX() + .eq(DeptPushMsgDO::getSystemCode, systemCode) + .eq(DeptPushMsgDO::getDeptId, deptId)); + } + + default DeptPushMsgDO selectBySystemCodeAndExternalCode(String systemCode, String externalDeptCode) { + return selectOne(new LambdaQueryWrapperX() + .eq(DeptPushMsgDO::getSystemCode, systemCode) + .eq(DeptPushMsgDO::getExternalDeptCode, externalDeptCode)); + } + + default List selectListByDeptId(Long deptId) { + return selectList(DeptPushMsgDO::getDeptId, deptId); + } + + default int deleteByDeptId(Long deptId) { + return delete(DeptPushMsgDO::getDeptId, deptId); + } + + default List selectListBySystemCode(String systemCode) { + return selectList(DeptPushMsgDO::getSystemCode, systemCode); + } + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java new file mode 100644 index 00000000..bd746bfb --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java @@ -0,0 +1,256 @@ +package com.zt.plat.module.system.service.dept; + +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; +import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; +import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; +import com.zt.plat.module.system.dal.mysql.dept.DeptMapper; +import com.zt.plat.module.system.dal.mysql.dept.EspMapper; +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; +import java.util.List; +import java.util.Objects; +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.system.enums.ErrorCodeConstants.*; + +/** + * 部门推送消息接口ServiceImpl实现类 + */ +@Service +@Validated +public class EspServiceImpl implements IEspService { + + @Resource + private EspMapper espMapper; + @Resource + private DeptMapper deptMapper; + @Resource + private CacheManager cacheManager; + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#createReqVO.deptId", beforeInvocation = false) + public Long createDeptPushMsg(EspSaveRespVo createReqVO) { + + //请求校验 + normalizeRequest(createReqVO); + //冲突禁用-映射 + disableActiveMappingIfConflict(createReqVO.getDeptId(), createReqVO.getSystemCode(), createReqVO.getExternalDeptCode()); + validateForCreateOrUpdate(null, createReqVO.getDeptId(), createReqVO.getSystemCode(), + createReqVO.getExternalDeptCode()); + + DeptPushMsgDO entity = BeanUtils.toBean(createReqVO, DeptPushMsgDO.class); + if (entity.getStatus() == null) { + entity.setStatus(CommonStatusEnum.ENABLE.getStatus()); + } + espMapper.insert(entity); + return entity.getId(); + } + + @Override + public void updateDeptPushMsg(EspSaveRespVo updateReqVO) { + normalizeRequest(updateReqVO); + DeptPushMsgDO exists = validateExists(updateReqVO.getId()); + disableActiveMappingIfConflict(updateReqVO.getDeptId(), updateReqVO.getSystemCode(), updateReqVO.getExternalDeptCode()); + validateForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getDeptId(), updateReqVO.getSystemCode(), + updateReqVO.getExternalDeptCode()); + + DeptPushMsgDO updateObj = BeanUtils.toBean(updateReqVO, DeptPushMsgDO.class); + // 保持原有的状态默认值逻辑 + if (updateObj.getStatus() == null) { + updateObj.setStatus(exists.getStatus() == null ? CommonStatusEnum.ENABLE.getStatus() : exists.getStatus()); + } + espMapper.updateById(updateObj); + evictCacheSafely(exists.getDeptId()); + evictCacheSafely(updateObj.getDeptId()); + } + + @Override + public void deleteDeptPushMsg(Long id) { + DeptPushMsgDO exists = validateExists(id); + espMapper.deleteById(id); + evictCacheSafely(exists.getDeptId()); + } + + @Override + public DeptPushMsgDO getDeptPushMsgDetails(Long id) { + return espMapper.selectById(id); + } + + @Override + public PageResult getDeptExternalCodePage(EspPageReqVO reqVO) { + + return espMapper.selectPage(reqVO); + } + + @Override + @Cacheable(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#deptId") + public List getPushMsgByDeptId(Long deptId) { + return espMapper.selectListByDeptId(deptId); + } + +/* @Override + public DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode) { + if (StrUtil.hasEmpty(systemCode, externalDeptCode)) { + return null; + } + return espMapper.selectBySystemCodeAndExternalCode(systemCode.trim(), externalDeptCode.trim()); + }*/ + +/* @Override + public DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId) { + if (StrUtil.isBlank(systemCode) || deptId == null) { + return null; + } + return espMapper.selectBySystemCodeAndDeptId(systemCode.trim(), deptId); + }*/ + +/* @Override + public Long getDeptPushMsgDetails(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); + + disableActiveMappingIfConflict(deptId, normalizedSystemCode, normalizedExternalCode); + + // 如果存在则更新,否则创建 + DeptExternalCodeDO exists = espMapper.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); + + //TODO + //getDeptPushMsgDetails(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 getDeptPushMsgDetails(createReqVO); + }*/ + +/* @Override + public void deleteDeptExternalCodesByDeptId(Long deptId) { + if (deptId == null) { + return; + } + espMapper.deleteByDeptId(deptId); + evictCacheSafely(deptId); + }*/ + + private DeptPushMsgDO validateExists(Long id) { + if (id == null) { + throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS); + } + DeptPushMsgDO entity = espMapper.selectById(id); + if (entity == null) { + throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS); + } + return entity; + } + + private void validateForCreateOrUpdate(Long id, Long deptId, String systemCode, String externalDeptCode) { + + // 校验部门存在 + DeptDO dept = deptMapper.selectById(deptId); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + String normalizedSystemCode = StrUtil.blankToDefault(systemCode, null); + String normalizedExternalCode = StrUtil.blankToDefault(externalDeptCode, null); + + // 校验同一系统下部门唯一 + if (StrUtil.isNotBlank(normalizedSystemCode)) { + DeptPushMsgDO sameDept = espMapper + .selectBySystemCodeAndDeptId(normalizedSystemCode, deptId); + if (sameDept != null && (id == null || !sameDept.getId().equals(id))) { + throw exception(DEPT_EXTERNAL_RELATION_EXISTS, normalizedSystemCode); + } + } + // 校验同一系统下外部编码唯一 + if (StrUtil.isNotBlank(normalizedSystemCode) && StrUtil.isNotBlank(normalizedExternalCode)) { + DeptPushMsgDO sameExternal = espMapper + .selectBySystemCodeAndExternalCode(normalizedSystemCode, normalizedExternalCode); + if (sameExternal != null && (id == null || !sameExternal.getId().equals(id))) { + boolean sameDept = Objects.equals(deptId, sameExternal.getDeptId()); + boolean activeConflict = !sameDept && CommonStatusEnum.isEnable(sameExternal.getStatus()); + if (activeConflict) { + throw exception(DEPT_EXTERNAL_CODE_DUPLICATE, normalizedSystemCode, normalizedExternalCode); + } + } + } + } + + private void normalizeRequest(EspSaveRespVo reqVO) { + if (reqVO == null) { + return; + } + if (StrUtil.isNotBlank(reqVO.getSystemCode())) { + reqVO.setSystemCode(reqVO.getSystemCode().trim()); + } + if (StrUtil.isNotBlank(reqVO.getExternalDeptCode())) { + reqVO.setExternalDeptCode(reqVO.getExternalDeptCode().trim()); + } + if (reqVO.getStatus() == null) { + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + } + } + + private void disableActiveMappingIfConflict(Long targetDeptId, String systemCode, String externalDeptCode) { + String normalizedSystem = StrUtil.trimToNull(systemCode); + String normalizedExternal = StrUtil.trimToNull(externalDeptCode); + if (StrUtil.hasEmpty(normalizedSystem, normalizedExternal) || targetDeptId == null) { + return; + } + DeptPushMsgDO existing = espMapper.selectBySystemCodeAndExternalCode(normalizedSystem, normalizedExternal); + if (existing == null) { + return; + } + if (Objects.equals(existing.getDeptId(), targetDeptId)) { + return; + } + if (CommonStatusEnum.isEnable(existing.getStatus())) { + DeptPushMsgDO update = new DeptPushMsgDO(); + update.setId(existing.getId()); + update.setStatus(CommonStatusEnum.DISABLE.getStatus()); + espMapper.updateById(update); + evictCacheSafely(existing.getDeptId()); + } + } + + 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) { + // 缓存失效失败不影响主流程 + } + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java new file mode 100644 index 00000000..97c83454 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.system.service.dept; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; +import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; +import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; + +import java.util.List; + +/** + * 部门推送消息 Service 接口 + */ +public interface IEspService { + + /** + * 创建映射关系 + * @param createReqVO 创建请求 + * @return 新增记录编号 + */ + Long createDeptPushMsg(EspSaveRespVo createReqVO); + + /** + * 更新映射关系 + * @param updateReqVO 更新请求 + */ + void updateDeptPushMsg(EspSaveRespVo updateReqVO); + + /** + * 删除映射关系 + * + * @param id 记录编号 + */ + void deleteDeptPushMsg(Long id); + + /** + * 获取映射详情 + */ + DeptPushMsgDO getDeptPushMsgDetails(Long id); + + /** + * 分页查询映射 + */ + PageResult getDeptExternalCodePage(EspPageReqVO reqVO); + + /** + * 根据部门推送消息 + */ + List getPushMsgByDeptId(Long deptId); + + /** + * 根据部门与外部系统保存/更新映射(存在则更新,不存在则创建) + * @param deptId 本系统部门 ID + * @param systemCode 外部系统标识 + * @param externalDeptCode 外部系统组织编码 + * @param externalDeptName 外部系统组织名称(可选) + * @param status 状态,默认启用 + * @return 映射记录 ID + */ + /* Long getDeptPushMsgDetails(Long deptId, String systemCode, String externalDeptCode, String externalDeptName, + Integer status);*/ + + /** + * 根据部门删除全部外部编码映射 + * + * @param deptId 部门编号 + */ + //void deleteDeptExternalCodesByDeptId(Long deptId); + + /** + * 根据外部系统与外部组织编码查询映射 + */ + //DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode); + + /** + * 根据外部系统与部门编号查询映射 + */ + //DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId); + +} From 7bd44ffca9b800bcdb093f2e14881d8b2bff4f53 Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Wed, 31 Dec 2025 17:06:51 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=9E=E4=BE=8BID=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=8A=84=E9=80=81=E8=AE=B0=E5=BD=95=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?,=E7=94=A8=E4=BA=8E=E5=BE=85=E5=8A=9E=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E4=B8=AD=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 35 ++++++++ .../vo/instance/BpmProcessInstanceCopyVO.java | 88 +++++++++++++++++++ .../task/BpmProcessInstanceCopyMapper.java | 6 ++ .../task/BpmProcessInstanceCopyService.java | 9 ++ .../BpmProcessInstanceCopyServiceImpl.java | 5 ++ .../src/main/resources/logback-spring.xml | 4 + 6 files changed, 147 insertions(+) create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 53ea6faf..d55f27a0 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -1,5 +1,6 @@ package com.zt.plat.module.bpm.controller.admin.task; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.zt.plat.framework.business.core.util.DeptUtil; @@ -11,8 +12,10 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import com.zt.plat.module.bpm.service.definition.BpmCategoryService; import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; import com.zt.plat.module.bpm.service.task.BpmTaskService; import com.zt.plat.module.system.api.dept.DeptApi; @@ -24,6 +27,8 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.list.SetUniqueList; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; @@ -31,6 +36,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,6 +60,8 @@ public class BpmProcessInstanceController { private BpmProcessDefinitionService processDefinitionService; @Resource private BpmCategoryService categoryService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; @Resource private AdminUserApi adminUserApi; @@ -181,6 +190,32 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/copy-list-by-process-instance-id") + @Operation(summary = "根据流程实例编号获取抄送列表") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult> getCopyListByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) { + List copyDOList = processInstanceCopyService.getByProcessInstanceId(processInstanceId); + if (CollectionUtils.isEmpty(copyDOList)) { + return success(new ArrayList<>(0)); + } + List copyVOList = new ArrayList<>(copyDOList.size()); + SetUniqueList userIdList = SetUniqueList.setUniqueList(new ArrayList<>()); + for (BpmProcessInstanceCopyDO copyDO : copyDOList) { + BpmProcessInstanceCopyVO copyVO = new BpmProcessInstanceCopyVO(); + BeanUtil.copyProperties(copyDO, copyVO); + copyVOList.add(copyVO); + userIdList.add(copyDO.getStartUserId()); + userIdList.add(copyDO.getUserId()); + } + Map userMap = adminUserApi.getUserMap(userIdList); + for (BpmProcessInstanceCopyVO copyVO : copyVOList) { + copyVO.setStartUserName(userMap.get(copyVO.getStartUserId()).getNickname()); + copyVO.setUserName(userMap.get(copyVO.getUserId()).getNickname()); + } + return success(copyVOList); + } + @GetMapping("/get-next-approval-nodes") @Operation(summary = "获取下一个执行的流程节点") @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java new file mode 100644 index 00000000..0012c190 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java @@ -0,0 +1,88 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程抄送 VO + * + * @author kr + * @since 2025-12-31 + */ +@Data +@NoArgsConstructor +public class BpmProcessInstanceCopyVO extends BaseDO { + + /** + * 编号 + */ + private Long id; + + /** + * 发起人 Id + */ + @Schema(description ="发起人 Id") + private Long startUserId; + + /** + * 发起人 姓名 + */ + @Schema(description ="发起人 姓名") + private String startUserName; + /** + * 流程名 + */ + @Schema(description ="流程名") + private String processInstanceName; + /** + * 流程实例的编号 + */ + @Schema(description ="流程实例的编号") + private String processInstanceId; + /** + * 流程实例的流程定义编号 + */ + @Schema(description ="流程实例的流程定义编号") + private String processDefinitionId; + /** + * 流程分类 + */ + @Schema(description ="流程分类") + private String category; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String activityId; + /** + * 流程活动的名字 + */ + @Schema(description ="流程活动的名字") + private String activityName; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String taskId; + + /** + * 用户编号(被抄送的用户编号) + */ + @Schema(description ="用户编号(被抄送的用户编号)") + private Long userId; + + /** + * 用户姓名(被抄送的用户姓名) + */ + @Schema(description ="用户姓名(被抄送的用户姓名)") + private String userName; + + /** + * 抄送意见 + */ + @Schema(description ="抄送意见") + private String reason; + +} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java index 61a73c3c..b35d217d 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java @@ -7,6 +7,8 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstan import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface BpmProcessInstanceCopyMapper extends BaseMapperX { @@ -22,4 +24,8 @@ public interface BpmProcessInstanceCopyMapper extends BaseMapperX getByProcessInstanceId(String processInstanceId) { + return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); + } + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java index e5f06b6f..76350bc8 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotEmpty; import org.flowable.bpmn.model.FlowNode; import java.util.Collection; +import java.util.List; /** * 流程抄送 Service 接口 @@ -57,4 +58,12 @@ public interface BpmProcessInstanceCopyService { */ void deleteProcessInstanceCopy(String processInstanceId); + /** + * 获得流程的抄送列表 + * + * @param processInstanceId 流程实例 ID + * @return 抄送流程列表 + */ + List getByProcessInstanceId(String processInstanceId); + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index dd842b9f..5fec3947 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -93,4 +93,9 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); } + @Override + public List getByProcessInstanceId(String processInstanceId) { + return processInstanceCopyMapper.getByProcessInstanceId(processInstanceId); + } + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml index 0e551414..51a9a489 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -73,4 +73,8 @@ + + + + From 2e761d18678c74ec2ee385962f72d214fec66c26 Mon Sep 17 00:00:00 2001 From: maimaishu <14610861+maimaishu@user.noreply.gitee.com> Date: Fri, 2 Jan 2026 17:27:53 +0800 Subject: [PATCH 4/7] =?UTF-8?q?[+]=E5=A2=9E=E5=8A=A0=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E6=8E=A8=E5=8A=A8=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zt/plat/module/system/api/esp/EspApi.java | 80 ++----------------- .../module/system/api/dept/PostApiImpl.java | 1 - .../module/system/api/esp/EspApiImpl.java | 25 ++++++ .../vo/depexternalcode/EspSaveRespVo.java | 3 + .../dal/dataobject/dept/DeptPushMsgDO.java | 5 ++ .../system/dal/mysql/dept/EspMapper.java | 10 ++- .../system/service/dept/EspServiceImpl.java | 71 +++------------- .../system/service/dept/IEspService.java | 32 ++------ 8 files changed, 62 insertions(+), 165 deletions(-) create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java index a7c338a5..54e560a9 100644 --- a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/esp/EspApi.java @@ -1,18 +1,15 @@ package com.zt.plat.module.system.api.esp; import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.util.collection.CollectionUtils; import com.zt.plat.module.system.api.dept.dto.*; +import com.zt.plat.module.system.api.esp.dto.EspDto; import com.zt.plat.module.system.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; -import java.util.Collection; + import java.util.List; -import java.util.Map; -import java.util.Set; @FeignClient(name = ApiConstants.NAME) @Tag(name = "RPC 服务 - 部门") @@ -20,75 +17,8 @@ public interface EspApi { String PREFIX = ApiConstants.PREFIX + "/dept"; - // === 以下为补全的接口方法 === - @PostMapping(PREFIX + "/create") - @Operation(summary = "新增部门") - CommonResult createDept(@RequestBody DeptSaveReqDTO createReqVO); - - @PutMapping(PREFIX + "/update") - @Operation(summary = "修改部门") - CommonResult updateDept(@RequestBody DeptSaveReqDTO updateReqVO); - - @DeleteMapping(PREFIX + "/delete") - @Operation(summary = "删除部门") - CommonResult deleteDept(@RequestParam("id") Long id); - - @PostMapping(PREFIX + "/list-all") - @Operation(summary = "获得部门列表") - CommonResult> getDeptList(@RequestBody DeptListReqDTO reqVO); - - @GetMapping(PREFIX + "/simple-list") - @Operation(summary = "获得部门精简信息列表") - CommonResult> getSimpleDeptList(); - - @GetMapping(PREFIX + "/simple-company-list") - @Operation(summary = "获得公司精简信息列表") - CommonResult> getSimpleCompanyList(); - - @GetMapping(PREFIX + "/all-company-list") - @Operation(summary = "获得所有公司精简信息列表") - CommonResult> getAllCompanyList(); - - @GetMapping(PREFIX + "/get") - @Operation(summary = "获得部门信息") - @Parameter(name = "id", description = "部门编号", example = "1024", required = true) - CommonResult getDept(@RequestParam("id") Long id); - - @GetMapping(PREFIX + "/list") - @Operation(summary = "获得部门信息数组") - @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) - CommonResult> getDeptList(@RequestParam("ids") Collection ids); - - @GetMapping(PREFIX + "/valid") - @Operation(summary = "校验部门是否合法") - @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) - CommonResult validateDeptList(@RequestParam("ids") Collection ids); - - /** - * 获得指定编号的部门 Map - * - * @param ids 部门编号数组 - * @return 部门 Map - */ - default Map getDeptMap(Collection ids) { - List list = getDeptList(ids).getCheckedData(); - return CollectionUtils.convertMap(list, DeptRespDTO::getId); - } - - @GetMapping(PREFIX + "/list-child") - @Operation(summary = "获得指定部门的所有子部门") - @Parameter(name = "id", description = "部门编号", example = "1024", required = true) - CommonResult> getChildDeptList(@RequestParam("id") Long id); - - @GetMapping(PREFIX + "/company-dept-info") - @Operation(summary = "获得指定用户的公司部门信息") - @Parameter(name = "userId", description = "用户编号", example = "1", required = true) - CommonResult> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId); - - // ========== 数据同步专用接口 ========== - - @PostMapping(PREFIX + "/sync") - @Operation(summary = "同步部门") - CommonResult syncDept(@RequestBody DeptSaveReqDTO syncReqDTO); + @PostMapping(PREFIX + "/pushMsg") + @Operation(summary = "推送消息") + CommonResult> pushMsg(@RequestBody DeptSaveReqDTO syncReqDTO); } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/dept/PostApiImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/dept/PostApiImpl.java index bb760ec4..80eedad0 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/dept/PostApiImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/dept/PostApiImpl.java @@ -13,7 +13,6 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; - import java.util.Collection; import java.util.Collections; import java.util.Comparator; diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java new file mode 100644 index 00000000..5cf33fa5 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.system.api.esp; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO; +import com.zt.plat.module.system.api.esp.dto.EspDto; +import com.zt.plat.module.system.service.dept.IEspService; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; +import java.util.List; + +@RestController +@Validated +public class EspApiImpl implements EspApi { + + + @Resource + private IEspService deptService; + @Override + public CommonResult> pushMsg(DeptSaveReqDTO syncReqDTO) + { + return CommonResult.success(deptService.pushMsg(syncReqDTO)); + } + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java index 859ba772..10ab6aa4 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/vo/depexternalcode/EspSaveRespVo.java @@ -25,4 +25,7 @@ public class EspSaveRespVo extends DeptExternalCodeBaseVO { @Schema(description = "最后更新时间") private LocalDateTime updateTime; + @Schema(description = "是否发送消息") + private Integer isSendMsg; + } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java index 237c28bc..2f209c71 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/dept/DeptPushMsgDO.java @@ -55,4 +55,9 @@ public class DeptPushMsgDO extends TenantBaseDO { */ private String remark; + /** + * 是否发送消息 + */ + private Integer isSendMsg; + } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java index 7f4cc147..804f9362 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java @@ -3,10 +3,12 @@ package com.zt.plat.module.system.dal.mysql.dept; import com.zt.plat.framework.common.pojo.PageResult; import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO; import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; -import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; /** * 部门推送消息接口Mapper @@ -54,4 +56,10 @@ public interface EspMapper extends BaseMapperX { return selectList(DeptPushMsgDO::getSystemCode, systemCode); } + @Select("SELECT ID,DEPT_ID, SYSTEM_CODE,EXTERNAL_DEPT_CODE,EXTERNAL_DEPT_NAME,STATUS,REMARK,TENANT_ID,CREATOR,CREATE_TIME,UPDATER,UPDATE_TIME\n" + + "FROM \n" + + "\t\tSYSTEM_DEPT_PUSH_MSG\n" + + "WHERE\n" + + " IS_SEND_MSG = '0' AND DELETED = '0' ") + List selectpushMsg(@Param("syncReqDTO") DeptSaveReqDTO syncReqDTO); } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java index bd746bfb..17a19662 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/EspServiceImpl.java @@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil; import com.zt.plat.framework.common.enums.CommonStatusEnum; import com.zt.plat.framework.common.pojo.PageResult; import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO; +import com.zt.plat.module.system.api.esp.dto.EspDto; import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; @@ -12,6 +14,8 @@ import com.zt.plat.module.system.dal.mysql.dept.DeptMapper; import com.zt.plat.module.system.dal.mysql.dept.EspMapper; import com.zt.plat.module.system.dal.redis.RedisKeyConstants; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.seata.common.result.Result; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -51,6 +55,7 @@ public class EspServiceImpl implements IEspService { if (entity.getStatus() == null) { entity.setStatus(CommonStatusEnum.ENABLE.getStatus()); } + entity.setIsSendMsg(0); espMapper.insert(entity); return entity.getId(); } @@ -68,6 +73,7 @@ public class EspServiceImpl implements IEspService { if (updateObj.getStatus() == null) { updateObj.setStatus(exists.getStatus() == null ? CommonStatusEnum.ENABLE.getStatus() : exists.getStatus()); } + updateObj.setIsSendMsg(updateReqVO.getIsSendMsg()); espMapper.updateById(updateObj); evictCacheSafely(exists.getDeptId()); evictCacheSafely(updateObj.getDeptId()); @@ -97,68 +103,11 @@ public class EspServiceImpl implements IEspService { return espMapper.selectListByDeptId(deptId); } -/* @Override - public DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode) { - if (StrUtil.hasEmpty(systemCode, externalDeptCode)) { - return null; - } - return espMapper.selectBySystemCodeAndExternalCode(systemCode.trim(), externalDeptCode.trim()); - }*/ + @Override + public List pushMsg(DeptSaveReqDTO syncReqDTO) { -/* @Override - public DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId) { - if (StrUtil.isBlank(systemCode) || deptId == null) { - return null; - } - return espMapper.selectBySystemCodeAndDeptId(systemCode.trim(), deptId); - }*/ - -/* @Override - public Long getDeptPushMsgDetails(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); - - disableActiveMappingIfConflict(deptId, normalizedSystemCode, normalizedExternalCode); - - // 如果存在则更新,否则创建 - DeptExternalCodeDO exists = espMapper.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); - - //TODO - //getDeptPushMsgDetails(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 getDeptPushMsgDetails(createReqVO); - }*/ - -/* @Override - public void deleteDeptExternalCodesByDeptId(Long deptId) { - if (deptId == null) { - return; - } - espMapper.deleteByDeptId(deptId); - evictCacheSafely(deptId); - }*/ + return BeanUtils.toBean(espMapper.selectpushMsg(syncReqDTO), EspDto.class); + } private DeptPushMsgDO validateExists(Long id) { if (id == null) { diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java index 97c83454..7678def2 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/dept/IEspService.java @@ -1,10 +1,11 @@ package com.zt.plat.module.system.service.dept; import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO; +import com.zt.plat.module.system.api.esp.dto.EspDto; import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO; import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo; import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO; - import java.util.List; /** @@ -27,7 +28,6 @@ public interface IEspService { /** * 删除映射关系 - * * @param id 记录编号 */ void deleteDeptPushMsg(Long id); @@ -47,33 +47,11 @@ public interface IEspService { */ List getPushMsgByDeptId(Long deptId); - /** - * 根据部门与外部系统保存/更新映射(存在则更新,不存在则创建) - * @param deptId 本系统部门 ID - * @param systemCode 外部系统标识 - * @param externalDeptCode 外部系统组织编码 - * @param externalDeptName 外部系统组织名称(可选) - * @param status 状态,默认启用 - * @return 映射记录 ID - */ - /* Long getDeptPushMsgDetails(Long deptId, String systemCode, String externalDeptCode, String externalDeptName, - Integer status);*/ /** - * 根据部门删除全部外部编码映射 - * - * @param deptId 部门编号 + * 推送部门数据到外部系统 + * @param syncReqDTO 同步请求 */ - //void deleteDeptExternalCodesByDeptId(Long deptId); - - /** - * 根据外部系统与外部组织编码查询映射 - */ - //DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode); - - /** - * 根据外部系统与部门编号查询映射 - */ - //DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId); + List pushMsg(DeptSaveReqDTO syncReqDTO); } From 88b280a33ff93ab0c982d257ff59180efe69676a Mon Sep 17 00:00:00 2001 From: maimaishu <14610861+maimaishu@user.noreply.gitee.com> Date: Mon, 5 Jan 2026 11:33:11 +0800 Subject: [PATCH 5/7] =?UTF-8?q?[+]=E5=A2=9E=E5=8A=A0IWork=E6=A0=B9?= =?UTF-8?q?=E6=8D=AEID=E5=90=8C=E6=AD=A5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/GlobalErrorCodeConstants.java | 1 + .../module/system/api/esp/EspApiImpl.java | 8 + .../controller/admin/dept/EspController.java | 5 +- .../iwork/IWorkIntegrationController.java | 15 +- .../iwork/vo/IWorkFullSyncReqVO.java | 4 + .../iwork/vo/IWorkSyncByIdReqVO.java | 99 +++++++ .../system/dal/mysql/dept/EspMapper.java | 10 +- .../integration/iwork/IWorkSyncProcessor.java | 8 +- .../integration/iwork/IWorkSyncService.java | 7 + .../iwork/impl/IWorkSyncProcessorImpl.java | 10 +- .../iwork/impl/IWorkSyncServiceImpl.java | 263 +++++++++++++++++- 11 files changed, 405 insertions(+), 25 deletions(-) create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkSyncByIdReqVO.java diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/exception/enums/GlobalErrorCodeConstants.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/exception/enums/GlobalErrorCodeConstants.java index ae712088..a01ff26e 100644 --- a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/exception/enums/GlobalErrorCodeConstants.java +++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/exception/enums/GlobalErrorCodeConstants.java @@ -25,6 +25,7 @@ public interface GlobalErrorCodeConstants { ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许 ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试"); + ErrorCode NOT_NULL_REQUEST_ERROR = new ErrorCode(430, "请求参数不能为空"); // ========== 服务端错误段 ========== diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java index 5cf33fa5..97c8a031 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/esp/EspApiImpl.java @@ -1,6 +1,8 @@ package com.zt.plat.module.system.api.esp; +import com.zt.plat.framework.common.exception.enums.GlobalErrorCodeConstants; import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.object.ObjectUtils; import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO; import com.zt.plat.module.system.api.esp.dto.EspDto; import com.zt.plat.module.system.service.dept.IEspService; @@ -8,6 +10,7 @@ import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Objects; @RestController @Validated @@ -19,6 +22,11 @@ public class EspApiImpl implements EspApi { @Override public CommonResult> pushMsg(DeptSaveReqDTO syncReqDTO) { + if(Objects.isNull(syncReqDTO) || null == syncReqDTO.getId()) + { + return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), + "ID不能为空"); + } return CommonResult.success(deptService.pushMsg(syncReqDTO)); } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java index 8ae5afa5..3a7d88da 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/dept/EspController.java @@ -14,6 +14,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.apache.commons.collections.CollectionUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -94,13 +95,13 @@ public class EspController private void fillDeptInfo(List list) { - if (list == null || list.isEmpty()) { + if (CollectionUtils.isEmpty(list)) { return; } Set deptIds = list.stream() .map(EspSaveRespVo::getDeptId) .collect(Collectors.toCollection(HashSet::new)); - if (deptIds == null || deptIds.isEmpty()) { + if (CollectionUtils.isEmpty(deptIds)) { return; } Map deptMap = deptService.getDeptList(deptIds).stream() diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java index b5a63c35..88adb502 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java @@ -6,6 +6,7 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*; import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService; import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService; import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService; +import lombok.extern.slf4j.Slf4j; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.security.PermitAll; @@ -18,9 +19,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; - import static com.zt.plat.framework.common.pojo.CommonResult.success; - /** * 提供统一 iWork 流程能力的管理端接口。 */ @@ -29,6 +28,7 @@ import static com.zt.plat.framework.common.pojo.CommonResult.success; @RequestMapping("/system/integration/iwork") @RequiredArgsConstructor @Validated +@Slf4j public class IWorkIntegrationController { private final IWorkIntegrationService integrationService; @@ -139,6 +139,17 @@ public class IWorkIntegrationController { return success(syncService.fullSyncUsers(reqVO)); } + // ----------------- 根据ID同步到本地 ----------------- + + @PostMapping("/syncById") + @Operation(summary = "根据ID触发 iWork 同步公司") + public CommonResult syncById(@Valid @RequestBody IWorkSyncByIdReqVO reqVO) { + + log.error("IWork集成后端手动录入syncById{}",reqVO); + return success(syncService.manuallySyncData(reqVO)); + } + + private ResponseEntity buildOaResponse(IWorkOaRawResponse resp) { if (resp == null) { return ResponseEntity.internalServerError().body("OA 响应为空"); diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkFullSyncReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkFullSyncReqVO.java index 09fe2d3e..e3793181 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkFullSyncReqVO.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkFullSyncReqVO.java @@ -56,4 +56,8 @@ public class IWorkFullSyncReqVO { } return resolved; } + + + + } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkSyncByIdReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkSyncByIdReqVO.java new file mode 100644 index 00000000..c91a3f66 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkSyncByIdReqVO.java @@ -0,0 +1,99 @@ +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.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * iWork 手动同步请求 + */ +@Data +public class IWorkSyncByIdReqVO { + + + @Schema(description = "起始页码,从 1 开始", example = "1") + @Min(1) + private Integer startPage = 1; + + @Schema(description = "最大处理页数,null 表示处理至 iWork 返回的末页", example = "10") + @Min(1) + private Integer maxPages; + + @Schema(description = "每次分页从 iWork 拉取的记录数", example = "100") + @Min(1) + @Max(500) + private Integer pageSize = 100; + + @Schema(description = "同步范围列表,默认同步全部。可选:subcompany、department、jobTitle、user") + private List scopes; + + @Schema(description = "是否包含已失效(canceled=1)的记录", example = "false") + private Boolean includeCanceled = Boolean.FALSE; + + @Schema(description = "是否允许更新已存在的本地实体", example = "false") + private Boolean allowUpdate = Boolean.FALSE; + + + @Schema(description = "指定同步记录的 iWork ID。传入后仅同步对应记录", example = "12345") + @NotBlank(message = "ID不能为空") + private String id; + + @Schema(description = "部门编码", example = "ZT001") + private String code; + + @Schema(description = "部门名称", example = "ZT") + private String name; + + @Schema(description = "部门简称", example = "技术") + private String shortName; + + @Schema(description = "父部门 ID", example = "1024") + private String parentId; + + @Schema(description = "负责人的用户编号", example = "2048") + private String leaderUserId; + + @Schema(description = "联系电话", example = "15601691000") + private String phone; + + @Schema(description = "邮箱", example = "zt@iocoder.cn") + private String email; + + @Schema(description = "状态,见 CommonStatusEnum 枚举0 开启 1 关闭", example = "0") + private Integer status; + + private Long tenantId; + + @Schema(description = "是否公司", example = "false") + private boolean isCompany; + + @Schema(description = "是否集团", example = "false") + private boolean isGroup; + + private boolean hasChildren; + + + public Set resolveScopes() { + EnumSet defaults = EnumSet.allOf(IWorkSyncEntityTypeEnum.class); + if (scopes == null || scopes.isEmpty()) { + return defaults; + } + Set resolved = scopes.stream() + .map(IWorkSyncEntityTypeEnum::fromCode) + .filter(java.util.Objects::nonNull) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(IWorkSyncEntityTypeEnum.class))); + if (resolved.isEmpty()) { + return defaults; + } + return resolved; + } + +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java index 804f9362..80466f46 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/dept/EspMapper.java @@ -56,10 +56,10 @@ public interface EspMapper extends BaseMapperX { return selectList(DeptPushMsgDO::getSystemCode, systemCode); } - @Select("SELECT ID,DEPT_ID, SYSTEM_CODE,EXTERNAL_DEPT_CODE,EXTERNAL_DEPT_NAME,STATUS,REMARK,TENANT_ID,CREATOR,CREATE_TIME,UPDATER,UPDATE_TIME\n" + - "FROM \n" + - "\t\tSYSTEM_DEPT_PUSH_MSG\n" + - "WHERE\n" + - " IS_SEND_MSG = '0' AND DELETED = '0' ") + @Select("SELECT ID,DEPT_ID, SYSTEM_CODE,EXTERNAL_DEPT_CODE,EXTERNAL_DEPT_NAME,STATUS,REMARK,TENANT_ID,CREATOR,CREATE_TIME,UPDATER,UPDATE_TIME" + + "FROM" + + "SYSTEM_DEPT_PUSH_MSG" + + "WHERE" + + " ID = #{id} AND IS_SEND_MSG = '0' AND DELETED = '0' ") List selectpushMsg(@Param("syncReqDTO") DeptSaveReqDTO syncReqDTO); } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncProcessor.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncProcessor.java index 17ec9b6a..5fa3a802 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncProcessor.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncProcessor.java @@ -19,19 +19,19 @@ public interface IWorkSyncProcessor { BatchResult syncSubcompanies(List data, SyncOptions options); - + // 同步子公司 BatchResult syncSubcompanies(List data, SyncOptions options, DeptSyncContext context); BatchResult syncDepartments(List data, SyncOptions options); - + // 同步部门 BatchResult syncDepartments(List data, SyncOptions options, DeptSyncContext context); - + // 同步岗位 BatchResult syncJobTitles(List data, SyncOptions options); - + // 同步用户 BatchResult syncUsers(List data, SyncOptions options); /** diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncService.java index 47c70b70..c39dbaf6 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncService.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkSyncService.java @@ -2,6 +2,9 @@ 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.IWorkSyncByIdReqVO; +import jakarta.validation.Valid; +import java.util.List; /** * iWork 组织/人员同步服务 @@ -28,4 +31,8 @@ public interface IWorkSyncService { */ IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO); + /** + * 手动同步 + */ + IWorkFullSyncRespVO manuallySyncData(@Valid IWorkSyncByIdReqVO list); } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java index abf94ac4..0d44b790 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java @@ -52,7 +52,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { public BatchResult syncSubcompanies(List data, SyncOptions options) { return syncSubcompanies(data, options, null); } - + //1、同步子公司 @Override public BatchResult syncSubcompanies(List data, SyncOptions options, @@ -154,7 +154,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { } return result; } - + //2、同步部门 @Override public BatchResult syncDepartments(List data, SyncOptions options) { return syncDepartments(data, options, null); @@ -272,7 +272,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { result.merge(syncDepartmentsInternal(Collections.emptyList(), options, context, true)); return result; } - + //TODO 3、同步岗位 @Override public BatchResult syncJobTitles(List data, SyncOptions options) { List records = CollUtil.emptyIfNull(data); @@ -310,7 +310,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { } return result; } - + //TODO 4、同步用户 @Override public BatchResult syncUsers(List data, SyncOptions options) { List records = CollUtil.emptyIfNull(data); @@ -377,7 +377,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { } return result; } - + //TODO private DeptSyncOutcome upsertDept(Long deptId, DeptSaveReqVO desired, boolean disabled, diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImpl.java index b06959d6..95ac0cdc 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncServiceImpl.java @@ -12,6 +12,7 @@ import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.EnumSet; @@ -34,32 +35,59 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { private final IWorkSyncProcessor syncProcessor; private final DeptService deptService; + /** + * 同步部门 + */ @Override public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) { return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.DEPARTMENT)); } + /** + * 仅同步分部 + */ @Override public IWorkFullSyncRespVO fullSyncSubcompanies(IWorkFullSyncReqVO reqVO) { return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.SUBCOMPANY)); } + /** + * 仅同步岗位 + */ @Override public IWorkFullSyncRespVO fullSyncJobTitles(IWorkFullSyncReqVO reqVO) { return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.JOB_TITLE)); } + /** + * 仅同步人员(会自动包含依赖的分部、部门) + */ @Override public IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO) { return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER)); } + /** + * 手动同步 + */ + @Transactional + @Override + public IWorkFullSyncRespVO manuallySyncData(IWorkSyncByIdReqVO reqVO) { + return manuallySyncData(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER)); + } + + /** + * 全量同步方法 + * @param reqVO 请求参数 + * @param scopes 同步范围 + * @return 响应对象 + */ private IWorkFullSyncRespVO runFullSync(IWorkFullSyncReqVO reqVO, Set scopes) { - IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO(); + IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO();//1 初始化响应对象:创建响应VO并设置分页大小和批次统计列表 respVO.setPageSize(reqVO.getPageSize()); List batchStats = new ArrayList<>(); respVO.setBatches(batchStats); - + //2 解析同步范围:根据传入的scopes确定需要同步的实体类型(用户、部门、分部、岗位) boolean syncUsers = scopes.contains(IWorkSyncEntityTypeEnum.USER); boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT); boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY); @@ -69,18 +97,20 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { IWorkSyncProcessor.DeptSyncContext deptSyncContext = (syncDepartments || syncSubcompanies) ? new IWorkSyncProcessor.DeptSyncContext() : null; - if (syncSubcompanies) { + //3 按类型执行同步:依次执行分部、部门、岗位、用户的分页同步操作 + if (syncSubcompanies) { //公司 processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats, deptSyncContext); } - if (syncDepartments) { + if (syncDepartments) { //部门 processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats, deptSyncContext); } - if (syncJobTitle) { + if (syncJobTitle) { // 岗位 processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats); } - if (syncUsers) { + if (syncUsers) { //人员 processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats); } + //4、处理部门上下文:对部门和分部同步进行特殊处理,包括刷新待处理数据和补全部门编码 if (deptSyncContext != null) { IWorkSyncProcessor.BatchResult flushResult = syncProcessor.flushDeptPending(deptSyncContext, options); updateStat(respVO.getDepartmentStat(), flushResult, 0); @@ -92,6 +122,9 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { return respVO; } + /** + * 全量执行子公司全量同步 + */ private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO, IWorkSyncProcessor.SyncOptions options, IWorkSyncEntityStatVO stat, @@ -103,7 +136,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { query.setPagesize(pageSize); applyQueryConditions(query, reqVO); IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query); - ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage()); + ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());// IWork执行成功 List dataList = CollUtil.emptyIfNull(pageResp.getDataList()); IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options, context); updateStat(stat, result, dataList.size()); @@ -111,6 +144,9 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { }); } + /** + * 执行部门全量同步 + */ private int executeDepartmentFullSync(IWorkFullSyncReqVO reqVO, IWorkSyncProcessor.SyncOptions options, IWorkSyncEntityStatVO stat, @@ -130,6 +166,9 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { }); } + /** + * 执行岗位全量同步 + */ private int executeJobTitleFullSync(IWorkFullSyncReqVO reqVO, IWorkSyncProcessor.SyncOptions options, IWorkSyncEntityStatVO stat, @@ -148,6 +187,9 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { }); } + /** + * 执行用户全量同步 + */ private int executeUserFullSync(IWorkFullSyncReqVO reqVO, IWorkSyncProcessor.SyncOptions options, IWorkSyncEntityStatVO stat, @@ -193,6 +235,12 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { return processedPages; } + /** + * 更新统计 + * @param stat 统计 + * @param result 结果 + * @param pulled 拉取数量 + */ private void updateStat(IWorkSyncEntityStatVO stat, IWorkSyncProcessor.BatchResult result, int pulled) { stat.incrementPulled(pulled); stat.incrementCreated(result.getCreated()); @@ -201,6 +249,11 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { stat.incrementFailed(result.getFailed()); } + /** + * 查询条件 + * @param query 查询条件 + * @param reqVO 请求 + */ private void applyQueryConditions(IWorkOrgBaseQueryReqVO query, IWorkFullSyncReqVO reqVO) { if (query == null || reqVO == null) { return; @@ -216,6 +269,9 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { params.put("id", reqVO.getId()); } + /** + * 全量同步 + */ private IWorkSyncProcessor.SyncOptions buildFullSyncOptions(IWorkFullSyncReqVO reqVO) { boolean includeCanceled = Boolean.TRUE.equals(reqVO.getIncludeCanceled()); boolean allowUpdate = Boolean.TRUE.equals(reqVO.getAllowUpdate()); @@ -237,4 +293,197 @@ public class IWorkSyncServiceImpl implements IWorkSyncService { private record BatchExecution(IWorkSyncProcessor.BatchResult result, int totalPulled) { } + + + /** + * 根据ID同步 + */ + private IWorkFullSyncRespVO manuallySyncData(IWorkSyncByIdReqVO reqVO, Set scopes) { + + //1 初始化响应对象:创建响应VO并设置分页大小和批次统计列表 + IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO(); + List batchStats = new ArrayList<>(); + respVO.setBatches(batchStats); + //2 解析同步范围:根据传入的scopes确定需要同步的实体类型(用户、部门、分部、岗位) + boolean syncUsers = scopes.contains(IWorkSyncEntityTypeEnum.USER); + boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT); + boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY); + boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE); + int processedPages = 0; + IWorkSyncProcessor.SyncOptions options = manualSync(reqVO); + IWorkSyncProcessor.DeptSyncContext deptSyncContext = (syncDepartments || syncSubcompanies) + ? new IWorkSyncProcessor.DeptSyncContext() + : null; + //3 按类型执行同步:依次执行分部、部门、岗位、用户的分页同步操作 + if (syncSubcompanies) { + processedPages += runManualFullSyncForSubsidiaries(reqVO, options, respVO.getSubcompanyStat(), batchStats, deptSyncContext);//分部统计信息 + } + if (syncDepartments) { + processedPages += departments(reqVO, options, respVO.getDepartmentStat(), batchStats, deptSyncContext);//部门统计信息 + } + if (syncJobTitle) { + processedPages += syncPositionsManually(reqVO, options, respVO.getJobTitleStat(), batchStats);//岗位统计信息 + } + if (syncUsers) { + processedPages += syncUsers(reqVO, options, respVO.getUserStat(), batchStats);//用户统计信息 + } + //4、处理部门上下文:对部门和分部同步进行特殊处理,包括刷新待处理数据和补全部门编码 + if (deptSyncContext != null) { + IWorkSyncProcessor.BatchResult flushResult = syncProcessor.flushDeptPending(deptSyncContext, options); + updateStat(respVO.getDepartmentStat(), flushResult, 0);//部门统计信息 + if (CollUtil.isNotEmpty(deptSyncContext.getPlaceholderDeptIds())) { + deptService.backfillMissingCodesWithoutEvent(deptSyncContext.getPlaceholderDeptIds()); + } + } + respVO.setProcessedPages(processedPages); + return respVO; + } + + + /** + * 手动同步 + */ + private IWorkSyncProcessor.SyncOptions manualSync(IWorkSyncByIdReqVO reqVO) { + boolean includeCanceled = Boolean.TRUE.equals(reqVO.getIncludeCanceled()); + boolean allowUpdate = Boolean.TRUE.equals(reqVO.getAllowUpdate()); + return IWorkSyncProcessor.SyncOptions.custom(includeCanceled, allowUpdate, true); + } + + + /** + * 手动执行子公司全量同步f + */ + private int runManualFullSyncForSubsidiaries(IWorkSyncByIdReqVO reqVO, + IWorkSyncProcessor.SyncOptions options, + IWorkSyncEntityStatVO stat, + List batches, + IWorkSyncProcessor.DeptSyncContext context) { + return paged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> { + IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO(); + query.setCurpage(page); + query.setPagesize(pageSize); + applyQuery(query, reqVO);//查询条件 + IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query); + ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());// IWork执行成功 + List dataList = CollUtil.emptyIfNull(pageResp.getDataList()); + IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options, context); + updateStat(stat, result, dataList.size()); + return new BatchExecution(result, dataList.size()); + }); + } + + + /** + * 查询条件 + * @param query 查询条件 + * @param reqVO 请求 + */ + private void applyQuery(IWorkOrgBaseQueryReqVO query, IWorkSyncByIdReqVO reqVO) { + if (query == null || reqVO == null) { + return; + } + if (StrUtil.isBlank(reqVO.getId())) { + return; + } + Map params = query.getParams(); + if (params == null) { + params = new HashMap<>(); + query.setParams(params); + } + params.put("id", reqVO.getId()); + } + + + private int paged(IWorkSyncByIdReqVO reqVO, + IWorkSyncEntityTypeEnum type, + List batches, + PageExecutor executor) { + int startPage = reqVO.getStartPage() == null ? 1 : reqVO.getStartPage(); + int pageSize = reqVO.getPageSize() == null ? 100 : reqVO.getPageSize(); + int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages(); + int processedPages = 0; + for (int page = startPage; processedPages < pagesLimit; page++) { + BatchExecution execution = executor.execute(page, pageSize); + if (execution == null || execution.totalPulled == 0) { + break; + } + processedPages++; + IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO(); + batchStat.setEntityType(type); + batchStat.setPageNumber(page); + batchStat.setPulled(execution.totalPulled); + batchStat.setCreated(execution.result.getCreated()); + batchStat.setSkippedExisting(execution.result.getSkipped()); + batchStat.setDisabled(execution.result.getDisabled()); + batchStat.setFailed(execution.result.getFailed()); + batches.add(batchStat); + } + return processedPages; + } + + /** + * 手动执行部门同步 + */ + private int departments(IWorkSyncByIdReqVO reqVO, + IWorkSyncProcessor.SyncOptions options, + IWorkSyncEntityStatVO stat, + List batches, + IWorkSyncProcessor.DeptSyncContext context) { + return paged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> { + IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO(); + query.setCurpage(page); + query.setPagesize(pageSize); + applyQuery(query, reqVO); + IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query); + ensureIWorkSuccess("拉取部门", pageResp.isSuccess(), pageResp.getMessage()); + List dataList = CollUtil.emptyIfNull(pageResp.getDataList()); + IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options, context); + updateStat(stat, result, dataList.size()); + return new BatchExecution(result, dataList.size()); + }); + } + + + /** + * 手动执行岗位同步 + */ + private int syncPositionsManually(IWorkSyncByIdReqVO reqVO, + IWorkSyncProcessor.SyncOptions options, + IWorkSyncEntityStatVO stat, + List batches) { + return paged(reqVO, IWorkSyncEntityTypeEnum.JOB_TITLE, batches, (page, pageSize) -> { + IWorkJobTitleQueryReqVO query = new IWorkJobTitleQueryReqVO(); + query.setCurpage(page); + query.setPagesize(pageSize); + applyQuery(query, reqVO); + IWorkHrJobTitlePageRespVO pageResp = orgRestService.listJobTitles(query); + ensureIWorkSuccess("拉取岗位", pageResp.isSuccess(), pageResp.getMessage()); + List dataList = CollUtil.emptyIfNull(pageResp.getDataList()); + IWorkSyncProcessor.BatchResult result = syncProcessor.syncJobTitles(dataList, options); + updateStat(stat, result, dataList.size()); + return new BatchExecution(result, dataList.size()); + }); + } + + /** + * 手动执行用户同步 + */ + private int syncUsers(IWorkSyncByIdReqVO reqVO, + IWorkSyncProcessor.SyncOptions options, + IWorkSyncEntityStatVO stat, + List batches) { + return paged(reqVO, IWorkSyncEntityTypeEnum.USER, batches, (page, pageSize) -> { + IWorkUserQueryReqVO query = new IWorkUserQueryReqVO(); + query.setCurpage(page); + query.setPagesize(pageSize); + applyQuery(query, reqVO); + IWorkHrUserPageRespVO pageResp = orgRestService.listUsers(query); + ensureIWorkSuccess("拉取人员", pageResp.isSuccess(), pageResp.getMessage()); + List dataList = CollUtil.emptyIfNull(pageResp.getDataList()); + IWorkSyncProcessor.BatchResult result = syncProcessor.syncUsers(dataList, options); + updateStat(stat, result, dataList.size()); + return new BatchExecution(result, dataList.size()); + }); + } + } From 797cd2abd9bc6eb13369415de520c45ed08d1798 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Tue, 6 Jan 2026 09:18:47 +0800 Subject: [PATCH 6/7] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=20deptignore=20?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E4=BC=9A=E5=AF=BC=E8=87=B4=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E6=8C=87=E9=92=88=E9=97=AE=E9=A2=98=202.=20=E5=BB=B6=E9=95=BF?= =?UTF-8?q?=20databus=20=E9=BB=98=E8=AE=A4=E7=9A=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4=203.=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E8=84=9A=E6=9C=AC=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E7=9A=84=E9=95=BF=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/dm/ruoyi-vue-pro-dm8.sql | 2 +- .../core/aop/DeptDataPermissionIgnoreAspect.java | 4 ++++ .../gateway/vo/definition/ApiDefinitionStepSaveReqVO.java | 2 +- .../integration/gateway/step/impl/HttpStepHandler.java | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sql/dm/ruoyi-vue-pro-dm8.sql b/sql/dm/ruoyi-vue-pro-dm8.sql index 53667219..ba46b297 100644 --- a/sql/dm/ruoyi-vue-pro-dm8.sql +++ b/sql/dm/ruoyi-vue-pro-dm8.sql @@ -25,7 +25,7 @@ CREATE TABLE infra_api_access_log ( user_ip varchar(50) NOT NULL, user_agent varchar(512) NOT NULL, operate_module varchar(50) DEFAULT NULL NULL, - operate_name varchar(50) DEFAULT NULL NULL, + operate_name varchar(256) DEFAULT NULL NULL, operate_type smallint DEFAULT 0 NULL, begin_time datetime NOT NULL, end_time datetime NOT NULL, diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java index 4ee9054e..f00f08e1 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/DeptDataPermissionIgnoreAspect.java @@ -19,6 +19,10 @@ public class DeptDataPermissionIgnoreAspect { public Object around(ProceedingJoinPoint joinPoint, DeptDataPermissionIgnore deptDataPermissionIgnore) throws Throwable { boolean oldIgnore = DeptContextHolder.shouldIgnore(); try { + if (deptDataPermissionIgnore == null) { + Class targetClass = joinPoint.getTarget().getClass(); + deptDataPermissionIgnore = targetClass.getAnnotation(DeptDataPermissionIgnore.class); + } Object enable = SpringExpressionUtils.parseExpression(deptDataPermissionIgnore.enable()); if (Boolean.TRUE.equals(enable)) { DeptContextHolder.setIgnore(true); diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java index 2f66ed5e..bb158491 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java @@ -36,7 +36,7 @@ public class ApiDefinitionStepSaveReqVO { @Schema(description = "响应映射表达式(JSON)") private String responseMappingExpr; - @Schema(description = "超时时间(毫秒)", example = "5000") + @Schema(description = "超时时间(毫秒),缺省 20000(20s)", example = "20000") private Long timeout; @Schema(description = "降级策略(JSON)") diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/step/impl/HttpStepHandler.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/step/impl/HttpStepHandler.java index 58a7c7fd..7f00b6ce 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/step/impl/HttpStepHandler.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/step/impl/HttpStepHandler.java @@ -48,6 +48,7 @@ public class HttpStepHandler implements ApiStepHandler { private static final Duration RETRY_DELAY = Duration.ofMillis(200); private static final int RETRY_ATTEMPTS = 3; + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(20); private static final Set DEFAULT_FORWARDED_HEADERS = Set.of( "authorization", @@ -229,7 +230,7 @@ public class HttpStepHandler implements ApiStepHandler { private Duration resolveTimeout(ApiStepDefinition stepDefinition) { Long timeout = stepDefinition.getStep().getTimeout(); if (timeout == null || timeout <= 0) { - return Duration.ofSeconds(5); + return DEFAULT_TIMEOUT; } return Duration.ofMillis(timeout); } From 09c9276deb4916be10c43f41ad983b6d81f9b18f Mon Sep 17 00:00:00 2001 From: chenbowen Date: Tue, 6 Jan 2026 11:17:20 +0800 Subject: [PATCH 7/7] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=20CompanyDataPermis?= =?UTF-8?q?sionIgnoreAspect=20=E7=A9=BA=E6=8C=87=E9=92=88=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/aop/CompanyDataPermissionIgnoreAspect.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java index ae051a25..8a5b6279 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/aop/CompanyDataPermissionIgnoreAspect.java @@ -19,6 +19,10 @@ public class CompanyDataPermissionIgnoreAspect { public Object around(ProceedingJoinPoint joinPoint, CompanyDataPermissionIgnore companyDataPermissionIgnore) throws Throwable { boolean oldIgnore = CompanyContextHolder.isIgnore(); try { + if (companyDataPermissionIgnore == null) { + Class targetClass = joinPoint.getTarget().getClass(); + companyDataPermissionIgnore = targetClass.getAnnotation(CompanyDataPermissionIgnore.class); + } Object enable = SpringExpressionUtils.parseExpression(companyDataPermissionIgnore.enable()); if (Boolean.TRUE.equals(enable)) { CompanyContextHolder.setIgnore(true);