diff --git a/sql/dm/外部系统推送配置初始化_DM8_20260120.sql b/sql/dm/外部系统推送配置初始化_DM8_20260120.sql new file mode 100644 index 00000000..1b16eef8 --- /dev/null +++ b/sql/dm/外部系统推送配置初始化_DM8_20260120.sql @@ -0,0 +1,132 @@ +-- DM8 外部系统推送配置初始化脚本 +-- 用于配置不同公司/部门/业务类型下的外部系统推送开关 +-- 创建日期:2026-01-20 + +-- 重复执行时请先备份数据 +-- DROP TABLE IF EXISTS system_external_push_config; + +-- 创建表 +CREATE TABLE system_external_push_config ( + id BIGINT NOT NULL PRIMARY KEY, + company_id BIGINT DEFAULT NULL NULL, + dept_id BIGINT DEFAULT NULL NULL, + business_type VARCHAR(32) DEFAULT NULL NULL, + external_system VARCHAR(64) DEFAULT NULL NULL, + enable_push BIT DEFAULT '1' NOT NULL, + remark VARCHAR(512) DEFAULT NULL NULL, + tenant_id BIGINT DEFAULT 0 NOT NULL, + creator VARCHAR(64) DEFAULT '' NULL, + create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + updater VARCHAR(64) DEFAULT '' NULL, + update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted TINYINT DEFAULT 0 NOT NULL +); + +-- 表和字段注释 +COMMENT ON TABLE system_external_push_config IS '外部系统推送配置'; +COMMENT ON COLUMN system_external_push_config.id IS '主键编号'; +COMMENT ON COLUMN system_external_push_config.company_id IS '公司编号(关联 system_dept 表的 is_company=1 的记录,为空表示不限制公司)'; +COMMENT ON COLUMN system_external_push_config.dept_id IS '部门编号(关联 system_dept 表的 is_company=0 的记录,为空表示公司级配置)'; +COMMENT ON COLUMN system_external_push_config.business_type IS '业务类型(PURCHASE/SALE/PRODUCTION,为空表示所有业务类型)'; +COMMENT ON COLUMN system_external_push_config.external_system IS '外部系统标识(ERP/IWORK/等,为空表示所有外部系统)'; +COMMENT ON COLUMN system_external_push_config.enable_push IS '是否启用推送(1启用 0停用)'; +COMMENT ON COLUMN system_external_push_config.remark IS '备注'; +COMMENT ON COLUMN system_external_push_config.tenant_id IS '租户编号'; +COMMENT ON COLUMN system_external_push_config.creator IS '创建者'; +COMMENT ON COLUMN system_external_push_config.create_time IS '创建时间'; +COMMENT ON COLUMN system_external_push_config.updater IS '更新者'; +COMMENT ON COLUMN system_external_push_config.update_time IS '更新时间'; +COMMENT ON COLUMN system_external_push_config.deleted IS '删除标记(0未删除 1已删除)'; + +/*-- 唯一索引:租户+公司+部门+业务类型+外部系统的组合唯一 +-- 注意:因为 dept_id 和 external_system 可以为 NULL,使用 COALESCE 处理 +CREATE UNIQUE INDEX uk_external_push_config_unique + ON system_external_push_config (tenant_id, company_id, COALESCE(dept_id, 0), business_type, COALESCE(external_system, '')); + +-- 辅助索引:按公司查询 +CREATE INDEX idx_external_push_config_company + ON system_external_push_config (tenant_id, company_id); + +-- 辅助索引:按业务类型查询 +CREATE INDEX idx_external_push_config_biz_type + ON system_external_push_config (tenant_id, business_type); + + + +-- 初始化菜单权限数据 +-- 主菜单 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, component_name, status, + visible, keep_alive, always_show, creator, create_time, + updater, update_time, deleted +) +SELECT + 20060, '外部系统推送配置', '', 2, 60, 1, + 'external-push-config', 'setting', 'system/push/config/index', 'SystemExternalPushConfig', 0, + '1', '1', '1', 'admin', SYSDATE, + 'admin', SYSDATE, 0 +FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM system_menu WHERE id = 20060 +); + +-- 查询权限 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, visible, keep_alive, + creator, create_time, updater, update_time, deleted +) +SELECT + 2006001, '推送配置查询', 'system:external-push-config:query', 3, 1, 20060, + '', '', '', 0, '1', '1', + 'admin', SYSDATE, 'admin', SYSDATE, 0 +FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM system_menu WHERE id = 2006001 +); + +-- 创建权限 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, visible, keep_alive, + creator, create_time, updater, update_time, deleted +) +SELECT + 2006002, '推送配置创建', 'system:external-push-config:create', 3, 2, 20060, + '', '', '', 0, '1', '1', + 'admin', SYSDATE, 'admin', SYSDATE, 0 +FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM system_menu WHERE id = 2006002 +); + +-- 修改权限 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, visible, keep_alive, + creator, create_time, updater, update_time, deleted +) +SELECT + 2006003, '推送配置修改', 'system:external-push-config:update', 3, 3, 20060, + '', '', '', 0, '1', '1', + 'admin', SYSDATE, 'admin', SYSDATE, 0 +FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM system_menu WHERE id = 2006003 +); + +-- 删除权限 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, visible, keep_alive, + creator, create_time, updater, update_time, deleted +) +SELECT + 2006004, '推送配置删除', 'system:external-push-config:delete', 3, 4, 20060, + '', '', '', 0, '1', '1', + 'admin', SYSDATE, 'admin', SYSDATE, 0 +FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM system_menu WHERE id = 2006004 +);*/ diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApi.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApi.java new file mode 100644 index 00000000..5020a83f --- /dev/null +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApi.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.system.api.push; + +import com.zt.plat.framework.common.pojo.CommonResult; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 外部系统推送配置 Feign API + * + * @author ZT Cloud + */ +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 外部系统推送配置") +public interface ExternalPushConfigApi { + + String PREFIX = ApiConstants.PREFIX + "/external-push-config"; + + /** + * 判断是否允许推送到外部系统 + * + * @param companyId 公司编号(可选,为 null 时表示不限制公司) + * @param deptId 部门编号(可选,为 null 时只按公司配置判断) + * @param businessType 业务类型(可选:PURCHASE/SALE/PRODUCTION,为 null 时表示所有业务类型) + * @param externalSystem 外部系统标识(可选:ERP/IWORK/等,为 null 时表示所有外部系统) + * @return 是否允许推送(true=允许,false=禁止,默认 true) + */ + @GetMapping(PREFIX + "/is-push-enabled") + @Operation(summary = "判断是否允许推送到外部系统") + CommonResult isPushEnabled( + @RequestParam(value = "companyId", required = false) @Parameter(description = "公司编号") Long companyId, + @RequestParam(value = "deptId", required = false) @Parameter(description = "部门编号") Long deptId, + @RequestParam(value = "businessType", required = false) @Parameter(description = "业务类型") String businessType, + @RequestParam(value = "externalSystem", required = false) @Parameter(description = "外部系统标识") String externalSystem); +} diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/ErrorCodeConstants.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/ErrorCodeConstants.java index 41f4e40e..b9231f52 100644 --- a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/ErrorCodeConstants.java +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/ErrorCodeConstants.java @@ -230,4 +230,11 @@ public interface ErrorCodeConstants { // ========== 门户网站 1-002-033-000 ========== ErrorCode PORTAL_NOT_EXISTS = new ErrorCode(1_002_033_000, "门户不存在"); + // ========== 外部系统推送配置 1_002_034_000 ========== + ErrorCode EXTERNAL_PUSH_CONFIG_NOT_EXISTS = new ErrorCode(1_002_034_001, "外部系统推送配置不存在"); + ErrorCode EXTERNAL_PUSH_CONFIG_EXISTS = new ErrorCode(1_002_034_002, "该配置已存在"); + ErrorCode EXTERNAL_PUSH_CONFIG_COMPANY_INVALID = new ErrorCode(1_002_034_003, "公司编号必须是公司节点(is_company=1)"); + ErrorCode EXTERNAL_PUSH_CONFIG_DEPT_INVALID = new ErrorCode(1_002_034_004, "部门编号必须是部门节点(is_company=0)"); + ErrorCode EXTERNAL_PUSH_CONFIG_BUSINESS_TYPE_INVALID = new ErrorCode(1_002_034_005, "业务类型无效,仅支持 PURCHASE/SALE/PRODUCTION"); + } diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/push/BusinessTypeEnum.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/push/BusinessTypeEnum.java new file mode 100644 index 00000000..441a239a --- /dev/null +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/enums/push/BusinessTypeEnum.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.system.enums.push; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 业务类型枚举 + * + * @author ZT Cloud + */ +@AllArgsConstructor +@Getter +public enum BusinessTypeEnum { + + PURCHASE(1, "PURCHASE", "采购"), + SALE(2, "SALE", "销售"), + PRODUCTION(3, "PRODUCTION", "生产"); + + /** + * 类型 + */ + private final Integer type; + + /** + * 编码 + */ + private final String code; + + /** + * 名称 + */ + private final String name; + + /** + * 根据编码获取枚举 + */ + public static BusinessTypeEnum valueOfCode(String code) { + if (code == null) { + return null; + } + for (BusinessTypeEnum value : BusinessTypeEnum.values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } + + /** + * 根据类型获取枚举 + */ + public static BusinessTypeEnum valueOfType(Integer type) { + if (type == null) { + return null; + } + for (BusinessTypeEnum value : BusinessTypeEnum.values()) { + if (value.getType().equals(type)) { + return value; + } + } + return null; + } + + /** + * 验证编码是否有效 + */ + public static boolean isValidCode(String code) { + return valueOfCode(code) != null; + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApiImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApiImpl.java new file mode 100644 index 00000000..7fac6614 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/push/ExternalPushConfigApiImpl.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.system.api.push; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.system.service.push.ExternalPushConfigService; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * 外部系统推送配置 Feign API 实现类 + * + * @author ZT Cloud + */ +@RestController +@Validated +public class ExternalPushConfigApiImpl implements ExternalPushConfigApi { + + @Resource + private ExternalPushConfigService externalPushConfigService; + + @Override + public CommonResult isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem) { + Boolean result = externalPushConfigService.isPushEnabled(companyId, deptId, businessType, externalSystem); + return success(result); + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/ExternalPushConfigController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/ExternalPushConfigController.java new file mode 100644 index 00000000..1e3b160b --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/ExternalPushConfigController.java @@ -0,0 +1,160 @@ +package com.zt.plat.module.system.controller.admin.push; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.system.controller.admin.push.vo.BusinessTypeRespVO; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigRespVO; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO; +import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; +import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO; +import com.zt.plat.module.system.enums.push.BusinessTypeEnum; +import com.zt.plat.module.system.service.dept.DeptService; +import com.zt.plat.module.system.service.push.ExternalPushConfigService; +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.Arrays; +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; + +/** + * 外部系统推送配置 Controller + * + * @author ZT Cloud + */ +@Tag(name = "管理后台 - 外部系统推送配置") +@RestController +@RequestMapping("/system/external-push-config") +@Validated +public class ExternalPushConfigController { + + @Resource + private ExternalPushConfigService externalPushConfigService; + @Resource + private DeptService deptService; + + @PostMapping("/create") + @Operation(summary = "创建推送配置") + @PreAuthorize("@ss.hasPermission('system:external-push-config:create')") + public CommonResult create(@Valid @RequestBody ExternalPushConfigSaveReqVO createReqVO) { + Long id = externalPushConfigService.createExternalPushConfig(createReqVO); + return success(id); + } + + @PutMapping("/update") + @Operation(summary = "修改推送配置") + @PreAuthorize("@ss.hasPermission('system:external-push-config:update')") + public CommonResult update(@Valid @RequestBody ExternalPushConfigSaveReqVO updateReqVO) { + externalPushConfigService.updateExternalPushConfig(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除推送配置") + @PreAuthorize("@ss.hasPermission('system:external-push-config:delete')") + public CommonResult delete(@RequestParam("id") Long id) { + externalPushConfigService.deleteExternalPushConfig(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获取推送配置详情") + @PreAuthorize("@ss.hasPermission('system:external-push-config:query')") + public CommonResult get(@RequestParam("id") Long id) { + ExternalPushConfigDO entity = externalPushConfigService.getExternalPushConfig(id); + ExternalPushConfigRespVO respVO = BeanUtils.toBean(entity, ExternalPushConfigRespVO.class); + fillDeptInfo(List.of(respVO)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "分页查询推送配置") + @PreAuthorize("@ss.hasPermission('system:external-push-config:query')") + public CommonResult> page(@Valid ExternalPushConfigPageReqVO reqVO) { + PageResult pageResult = externalPushConfigService.getExternalPushConfigPage(reqVO); + PageResult result = BeanUtils.toBean(pageResult, ExternalPushConfigRespVO.class); + fillDeptInfo(result.getList()); + return success(result); + } + + @GetMapping("/is-push-enabled") + @Operation(summary = "判断是否允许推送") + @Parameter(name = "companyId", description = "公司编号(为空表示不限制公司)") + @Parameter(name = "deptId", description = "部门编号") + @Parameter(name = "businessType", description = "业务类型(为空表示所有业务类型)") + @Parameter(name = "externalSystem", description = "外部系统标识(为空表示所有外部系统)") + @PreAuthorize("@ss.hasPermission('system:external-push-config:query')") + public CommonResult isPushEnabled( + @RequestParam(value = "companyId", required = false) Long companyId, + @RequestParam(value = "deptId", required = false) Long deptId, + @RequestParam(value = "businessType", required = false) String businessType, + @RequestParam(value = "externalSystem", required = false) String externalSystem) { + Boolean result = externalPushConfigService.isPushEnabled(companyId, deptId, businessType, externalSystem); + return success(result); + } + + @GetMapping("/business-types") + @Operation(summary = "获取业务类型列表") + public CommonResult> getBusinessTypes() { + List result = Arrays.stream(BusinessTypeEnum.values()) + .map(e -> new BusinessTypeRespVO(e.getType(), e.getCode(), e.getName())) + .collect(Collectors.toList()); + return success(result); + } + + /** + * 填充公司和部门名称 + */ + private void fillDeptInfo(List list) { + if (list == null || list.isEmpty()) { + return; + } + + // 收集所有公司ID和部门ID + Set deptIds = list.stream() + .flatMap(item -> { + Set ids = new java.util.HashSet<>(); + ids.add(item.getCompanyId()); + if (item.getDeptId() != null) { + ids.add(item.getDeptId()); + } + return ids.stream(); + }) + .collect(Collectors.toSet()); + + if (deptIds.isEmpty()) { + return; + } + + // 批量查询部门信息 + Map deptMap = CollectionUtils.convertMap( + deptService.getDeptList(deptIds), DeptDO::getId); + + // 填充名称 + list.forEach(item -> { + DeptDO company = deptMap.get(item.getCompanyId()); + if (company != null) { + item.setCompanyName(company.getName()); + } + if (item.getDeptId() != null) { + DeptDO dept = deptMap.get(item.getDeptId()); + if (dept != null) { + item.setDeptName(dept.getName()); + } + } + }); + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/BusinessTypeRespVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/BusinessTypeRespVO.java new file mode 100644 index 00000000..5dc83f66 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/BusinessTypeRespVO.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.system.controller.admin.push.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 业务类型 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BusinessTypeRespVO { + + @Schema(description = "类型", example = "1") + private Integer type; + + @Schema(description = "编码", example = "PURCHASE") + private String code; + + @Schema(description = "名称", example = "采购") + private String name; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigBaseVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigBaseVO.java new file mode 100644 index 00000000..fd385e70 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigBaseVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.system.controller.admin.push.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - 外部系统推送配置基础信息") +@Data +public class ExternalPushConfigBaseVO { + + @Schema(description = "公司编号(为空表示不限制公司)", example = "1024") + private Long companyId; + + @Schema(description = "部门编号(为空表示公司级配置)", example = "2048") + private Long deptId; + + @Schema(description = "业务类型(为空表示所有业务类型)", example = "PURCHASE") + @Size(max = 32, message = "业务类型长度不能超过 32 个字符") + private String businessType; + + @Schema(description = "外部系统标识(为空表示所有外部系统)", example = "ERP") + @Size(max = 64, message = "外部系统标识长度不能超过 64 个字符") + private String externalSystem; + + @Schema(description = "是否启用推送", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "推送开关不能为空") + private Boolean enablePush; + + @Schema(description = "备注", example = "ERP 采购单推送配置") + @Size(max = 512, message = "备注长度不能超过 512 个字符") + private String remark; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigPageReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigPageReqVO.java new file mode 100644 index 00000000..2ac1eb4e --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigPageReqVO.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.system.controller.admin.push.vo; + +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 ExternalPushConfigPageReqVO extends PageParam { + + @Schema(description = "公司编号", example = "1024") + private Long companyId; + + @Schema(description = "部门编号", example = "2048") + private Long deptId; + + @Schema(description = "业务类型", example = "PURCHASE") + private String businessType; + + @Schema(description = "外部系统标识", example = "ERP") + private String externalSystem; + + @Schema(description = "是否启用推送", example = "true") + private Boolean enablePush; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigRespVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigRespVO.java new file mode 100644 index 00000000..7e2e5763 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigRespVO.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.system.controller.admin.push.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 外部系统推送配置 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class ExternalPushConfigRespVO extends ExternalPushConfigBaseVO { + + @Schema(description = "配置编号", example = "1024") + private Long id; + + @Schema(description = "公司名称", example = "浙江中天建设集团") + private String companyName; + + @Schema(description = "部门名称", example = "采购部") + private String deptName; + + @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/controller/admin/push/vo/ExternalPushConfigSaveReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigSaveReqVO.java new file mode 100644 index 00000000..3a51a5f8 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/push/vo/ExternalPushConfigSaveReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.system.controller.admin.push.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 外部系统推送配置创建/修改 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class ExternalPushConfigSaveReqVO extends ExternalPushConfigBaseVO { + + @Schema(description = "配置编号", example = "1024") + private Long id; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/push/ExternalPushConfigDO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/push/ExternalPushConfigDO.java new file mode 100644 index 00000000..b42ae1b9 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/push/ExternalPushConfigDO.java @@ -0,0 +1,76 @@ +package com.zt.plat.module.system.dal.dataobject.push; + +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.tenant.core.db.TenantBaseDO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 外部系统推送配置 DO + * + * 用于配置不同公司/部门/业务类型下的外部系统推送开关 + * + * @author ZT Cloud + */ +@TableName("system_external_push_config") +@KeySequence("system_external_push_config_seq") +@Data +@EqualsAndHashCode(callSuper = true) +public class ExternalPushConfigDO extends TenantBaseDO { + + /** + * 主键编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 公司编号(可为空) + * + * 关联 system_dept 表,is_company = 1 + * 为空表示不限制公司 + */ + private Long companyId; + + /** + * 部门编号(可为空) + * + * 关联 system_dept 表,is_company = 0 + * 为空表示公司级配置 + */ + private Long deptId; + + /** + * 业务类型(可为空) + * + * 枚举值:PURCHASE, SALE, PRODUCTION + * 为空表示所有业务类型 + * 枚举 {@link com.zt.plat.module.system.enums.push.BusinessTypeEnum} + */ + private String businessType; + + /** + * 外部系统标识(可为空) + * + * 如:ERP, IWORK + * 为空表示所有外部系统 + * 枚举 {@link com.zt.plat.module.system.enums.dept.ExternalPlatformEnum} + */ + private String externalSystem; + + /** + * 是否启用推送 + * + * true:启用推送 + * false:停用推送 + */ + private Boolean enablePush; + + /** + * 备注 + */ + private String remark; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/push/ExternalPushConfigMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/push/ExternalPushConfigMapper.java new file mode 100644 index 00000000..f05cdaf6 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/push/ExternalPushConfigMapper.java @@ -0,0 +1,92 @@ +package com.zt.plat.module.system.dal.mysql.push; + +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.push.vo.ExternalPushConfigPageReqVO; +import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 外部系统推送配置 Mapper + * + * @author ZT Cloud + */ +@Mapper +public interface ExternalPushConfigMapper extends BaseMapperX { + + default PageResult selectPage(ExternalPushConfigPageReqVO reqVO) { + LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX() + .eqIfPresent(ExternalPushConfigDO::getCompanyId, reqVO.getCompanyId()) + .eqIfPresent(ExternalPushConfigDO::getBusinessType, reqVO.getBusinessType()) + .eqIfPresent(ExternalPushConfigDO::getExternalSystem, reqVO.getExternalSystem()) + .eqIfPresent(ExternalPushConfigDO::getEnablePush, reqVO.getEnablePush()); + + // 如果传了 companyId 但没传 deptId,则只查公司级配置(dept_id IS NULL) + if (reqVO.getCompanyId() != null && reqVO.getDeptId() == null) { + wrapper.isNull(ExternalPushConfigDO::getDeptId); + } else if (reqVO.getDeptId() != null) { + // 如果传了 deptId,则查指定部门的配置 + wrapper.eq(ExternalPushConfigDO::getDeptId, reqVO.getDeptId()); + } + // 如果都没传,则查所有配置 + + wrapper.orderByDesc(ExternalPushConfigDO::getId); + return selectPage(reqVO, wrapper); + } + + /** + * 通用查询配置方法 + * + * @param companyId 公司ID(null 表示查询 company_id IS NULL 的记录) + * @param deptId 部门ID(null 表示查询 dept_id IS NULL 的记录) + * @param businessType 业务类型(null 表示查询 business_type IS NULL 的记录) + * @param externalSystem 外部系统(null 表示查询 external_system IS NULL 的记录) + * @return 配置对象 + */ + default ExternalPushConfigDO selectByConfig(Long companyId, Long deptId, String businessType, String externalSystem) { + LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX<>(); + + if (companyId == null) { + wrapper.isNull(ExternalPushConfigDO::getCompanyId); + } else { + wrapper.eq(ExternalPushConfigDO::getCompanyId, companyId); + } + + if (deptId == null) { + wrapper.isNull(ExternalPushConfigDO::getDeptId); + } else { + wrapper.eq(ExternalPushConfigDO::getDeptId, deptId); + } + + if (businessType == null) { + wrapper.isNull(ExternalPushConfigDO::getBusinessType); + } else { + wrapper.eq(ExternalPushConfigDO::getBusinessType, businessType); + } + + if (externalSystem == null) { + wrapper.isNull(ExternalPushConfigDO::getExternalSystem); + } else { + wrapper.eq(ExternalPushConfigDO::getExternalSystem, externalSystem); + } + + return selectOne(wrapper); + } + + /** + * 查询公司下所有配置 + */ + default List selectListByCompanyId(Long companyId) { + return selectList(ExternalPushConfigDO::getCompanyId, companyId); + } + + /** + * 查询部门下所有配置 + */ + default List selectListByDeptId(Long deptId) { + return selectList(ExternalPushConfigDO::getDeptId, deptId); + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigService.java new file mode 100644 index 00000000..904a474e --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigService.java @@ -0,0 +1,53 @@ +package com.zt.plat.module.system.service.push; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO; +import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO; +import jakarta.validation.Valid; + +/** + * 外部系统推送配置 Service 接口 + * + * @author ZT Cloud + */ +public interface ExternalPushConfigService { + + /** + * 创建推送配置 + */ + Long createExternalPushConfig(@Valid ExternalPushConfigSaveReqVO createReqVO); + + /** + * 修改推送配置 + */ + void updateExternalPushConfig(@Valid ExternalPushConfigSaveReqVO updateReqVO); + + /** + * 删除推送配置 + */ + void deleteExternalPushConfig(Long id); + + /** + * 获取推送配置详情 + */ + ExternalPushConfigDO getExternalPushConfig(Long id); + + /** + * 分页查询推送配置 + */ + PageResult getExternalPushConfigPage(ExternalPushConfigPageReqVO reqVO); + + /** + * 判断是否允许推送(核心业务逻辑) + * + * 优先级:部门配置 > 公司配置 > 默认允许 + * + * @param companyId 公司编号(必填) + * @param deptId 部门编号(可选) + * @param businessType 业务类型(必填) + * @param externalSystem 外部系统标识(必填) + * @return 是否允许推送(true=允许,false=禁止,默认 true) + */ + Boolean isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem); +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigServiceImpl.java new file mode 100644 index 00000000..b36aa03c --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/push/ExternalPushConfigServiceImpl.java @@ -0,0 +1,280 @@ +package com.zt.plat.module.system.service.push; + +import cn.hutool.core.util.StrUtil; +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.push.vo.ExternalPushConfigPageReqVO; +import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO; +import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; +import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO; +import com.zt.plat.module.system.dal.mysql.dept.DeptMapper; +import com.zt.plat.module.system.dal.mysql.push.ExternalPushConfigMapper; +import com.zt.plat.module.system.enums.push.BusinessTypeEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.system.enums.ErrorCodeConstants.*; + +/** + * 外部系统推送配置 Service 实现类 + * + * @author ZT Cloud + */ +@Service +@Validated +public class ExternalPushConfigServiceImpl implements ExternalPushConfigService { + + @Resource + private ExternalPushConfigMapper externalPushConfigMapper; + @Resource + private DeptMapper deptMapper; + + @Override + public Long createExternalPushConfig(ExternalPushConfigSaveReqVO createReqVO) { + // 参数规范化 + normalizeRequest(createReqVO); + + // 业务校验 + validateForCreateOrUpdate(null, createReqVO); + + // 创建配置 + ExternalPushConfigDO entity = BeanUtils.toBean(createReqVO, ExternalPushConfigDO.class); + if (entity.getEnablePush() == null) { + entity.setEnablePush(true); + } + externalPushConfigMapper.insert(entity); + return entity.getId(); + } + + @Override + public void updateExternalPushConfig(ExternalPushConfigSaveReqVO updateReqVO) { + // 参数规范化 + normalizeRequest(updateReqVO); + + // 校验存在 + validateExists(updateReqVO.getId()); + // 业务校验 + validateForCreateOrUpdate(updateReqVO.getId(), updateReqVO); + + // 更新配置 + ExternalPushConfigDO updateObj = BeanUtils.toBean(updateReqVO, ExternalPushConfigDO.class); + externalPushConfigMapper.updateById(updateObj); + } + + @Override + public void deleteExternalPushConfig(Long id) { + validateExists(id); + externalPushConfigMapper.deleteById(id); + } + + @Override + public ExternalPushConfigDO getExternalPushConfig(Long id) { + return externalPushConfigMapper.selectById(id); + } + + @Override + public PageResult getExternalPushConfigPage(ExternalPushConfigPageReqVO reqVO) { + return externalPushConfigMapper.selectPage(reqVO); + } + + @Override + public Boolean isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem) { + // 规范化参数 + String normalizedBusinessType = StrUtil.isNotBlank(businessType) ? businessType.trim().toUpperCase() : null; + String normalizedExternalSystem = StrUtil.isNotBlank(externalSystem) ? externalSystem.trim().toUpperCase() : null; + + // 优先级 1:部门级配置(如果 deptId 不为空),递归查找父部门 + if (deptId != null) { + ExternalPushConfigDO deptConfig = findDeptConfigRecursive( + companyId, deptId, normalizedBusinessType, normalizedExternalSystem); + if (deptConfig != null) { + return deptConfig.getEnablePush() != null ? deptConfig.getEnablePush() : true; + } + } + + // 优先级 2:公司级配置(dept_id 为 null) + if (companyId != null) { + ExternalPushConfigDO companyConfig = externalPushConfigMapper.selectByConfig( + companyId, null, normalizedBusinessType, normalizedExternalSystem); + if (companyConfig != null) { + return companyConfig.getEnablePush() != null ? companyConfig.getEnablePush() : true; + } + } + + // 优先级 3:全局配置(company_id 和 dept_id 都为空) + ExternalPushConfigDO globalConfig = externalPushConfigMapper.selectByConfig( + null, null, normalizedBusinessType, normalizedExternalSystem); + if (globalConfig != null) { + return globalConfig.getEnablePush() != null ? globalConfig.getEnablePush() : true; + } + + // 优先级 4:没有配置,默认允许推送 + return true; + } + + /** + * 递归查找部门配置(包括父部门) + * + * @param companyId 公司ID + * @param deptId 部门ID + * @param businessType 业务类型 + * @param externalSystem 外部系统 + * @return 配置对象,如果找不到返回 null + */ + private ExternalPushConfigDO findDeptConfigRecursive(Long companyId, Long deptId, + String businessType, String externalSystem) { + if (deptId == null) { + return null; + } + + // 查询当前部门的配置 + ExternalPushConfigDO config = externalPushConfigMapper.selectByConfig( + companyId, deptId, businessType, externalSystem); + if (config != null) { + return config; + } + + // 查询当前部门信息,获取父部门ID + DeptDO dept = deptMapper.selectById(deptId); + if (dept == null || dept.getParentId() == null || dept.getParentId() == 0L) { + // 没有父部门了,返回 null + return null; + } + + // 检查父部门是否是公司节点 + DeptDO parentDept = deptMapper.selectById(dept.getParentId()); + if (parentDept != null && Boolean.TRUE.equals(parentDept.getIsCompany())) { + // 父部门是公司,不再向上查找(公司级配置在外层处理) + return null; + } + + // 递归查找父部门的配置 + return findDeptConfigRecursive(companyId, dept.getParentId(), businessType, externalSystem); + } + + //==================== 私有方法 ==================== + + /** + * 校验配置是否存在 + */ + private ExternalPushConfigDO validateExists(Long id) { + if (id == null) { + throw exception(EXTERNAL_PUSH_CONFIG_NOT_EXISTS); + } + ExternalPushConfigDO entity = externalPushConfigMapper.selectById(id); + if (entity == null) { + throw exception(EXTERNAL_PUSH_CONFIG_NOT_EXISTS); + } + return entity; + } + + /** + * 业务校验 + */ + private void validateForCreateOrUpdate(Long id, ExternalPushConfigSaveReqVO reqVO) { + // 1. 如果指定公司,校验公司存在且是公司节点 + if (reqVO.getCompanyId() != null) { + DeptDO company = deptMapper.selectById(reqVO.getCompanyId()); + if (company == null) { + throw exception(DEPT_NOT_FOUND); + } + if (!Boolean.TRUE.equals(company.getIsCompany())) { + throw exception(EXTERNAL_PUSH_CONFIG_COMPANY_INVALID); + } + } + + // 2. 如果指定部门,校验部门存在且不是公司节点 + if (reqVO.getDeptId() != null) { + DeptDO dept = deptMapper.selectById(reqVO.getDeptId()); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + if (Boolean.TRUE.equals(dept.getIsCompany())) { + throw exception(EXTERNAL_PUSH_CONFIG_DEPT_INVALID); + } + } + + // 3. 如果指定业务类型,校验业务类型有效性 + if (StrUtil.isNotBlank(reqVO.getBusinessType())) { + if (!BusinessTypeEnum.isValidCode(reqVO.getBusinessType())) { + throw exception(EXTERNAL_PUSH_CONFIG_BUSINESS_TYPE_INVALID); + } + } + + // 4. 校验唯一性:同一租户+公司+部门+业务类型+外部系统的配置唯一 + ExternalPushConfigDO existing = externalPushConfigMapper.selectByConfig( + reqVO.getCompanyId(), reqVO.getDeptId(), + reqVO.getBusinessType(), reqVO.getExternalSystem()); + + if (existing != null && (id == null || !existing.getId().equals(id))) { + throw exception(EXTERNAL_PUSH_CONFIG_EXISTS); + } + } + + /** + * 参数规范化 + */ + private void normalizeRequest(ExternalPushConfigSaveReqVO reqVO) { + if (reqVO == null) { + return; + } + + // 如果 companyId 为空但 deptId 不为空,自动查找并填充顶级公司ID + if (reqVO.getCompanyId() == null && reqVO.getDeptId() != null) { + Long topCompanyId = findTopCompanyId(reqVO.getDeptId()); + if (topCompanyId != null) { + reqVO.setCompanyId(topCompanyId); + } + } + + if (StrUtil.isNotBlank(reqVO.getBusinessType())) { + reqVO.setBusinessType(reqVO.getBusinessType().trim().toUpperCase()); + } + if (StrUtil.isNotBlank(reqVO.getExternalSystem())) { + reqVO.setExternalSystem(reqVO.getExternalSystem().trim().toUpperCase()); + } + } + + /** + * 查找部门的顶级公司ID + * + * @param deptId 部门ID + * @return 顶级公司ID,如果不存在返回 null + */ + private Long findTopCompanyId(Long deptId) { + if (deptId == null) { + return null; + } + + DeptDO dept = deptMapper.selectById(deptId); + if (dept == null) { + return null; + } + + // 递归向上查找,直到找到公司节点或者parentId为空/0 + Long currentDeptId = deptId; + while (currentDeptId != null) { + DeptDO currentDept = deptMapper.selectById(currentDeptId); + if (currentDept == null) { + break; + } + + // 如果当前节点是公司,返回其ID + if (Boolean.TRUE.equals(currentDept.getIsCompany())) { + return currentDept.getId(); + } + + // 如果没有父节点或父节点为0,结束查找 + if (currentDept.getParentId() == null || currentDept.getParentId() == 0L) { + break; + } + + // 继续向上查找 + currentDeptId = currentDept.getParentId(); + } + + return null; + } +}