diff --git a/.idea/MarsCodeWorkspaceAppSettings.xml b/.idea/MarsCodeWorkspaceAppSettings.xml new file mode 100644 index 00000000..e2a065b7 --- /dev/null +++ b/.idea/MarsCodeWorkspaceAppSettings.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.qoder/rules/rule.md b/.qoder/rules/rule.md new file mode 100644 index 00000000..424ff144 --- /dev/null +++ b/.qoder/rules/rule.md @@ -0,0 +1,74 @@ +--- +trigger: manual + +# Java 开发规范与重构指南 + +作为我的 AI 编程伙伴,请在生成、修改或评审代码时严格遵循以下 10 条核心原则: + +### 1. 单一职责原则 (SRP) + +* **规则**:一个方法只做一件事。 +* **实践**:如果方法逻辑过长,请通过**拆分职责明确的私有方法**(Private Methods)来解耦。 +* **目标**:提升代码的可读性和方法复用率。 + +### 2. 卫语句优先 (Guard Clauses) + +* **规则**:减少 `if-else` 嵌套深度。 +* **实践**:使用**卫语句提前返回**(Early Return)。先处理异常分支和边界条件,核心逻辑放在方法最后。 +* **示例**: +```java +if (user == null) return; +// 执行核心逻辑... + +``` + + + +### 3. 重复逻辑抽取 + +* **规则**:不要重复你自己 (DRY)。 +* **实践**:将频繁出现的**参数校验、类型转换、对象构建**等模式封装为公共工具类或私有通用方法。 + +### 4. 数据封装 (Encapsulation) + +* **规则**:避免多个相关变量散落在方法参数中。 +* **实践**:如果多个变量在逻辑上高度相关(如 `startTime`, `endTime`),应**封装为内部类或 DTO 对象**。 + +### 5. 统一异常处理 + +* **规则**:禁止散乱的 try-catch。 +* **实践**:使用**通用的响应校验方法**或全局异常拦截器。确保错误码和错误信息在整个系统中保持一致。 + +### 6. 参数对象化 + +* **规则**:控制方法入参数量。 +* **实践**:当方法参数**超过 3 个**时,必须封装为 Request 对象或 Context 对象。 + +### 7. 语义化命名 + +* **规则**:变量名应具备自解释性。 +* **实践**:**变量即文档**。使用准确的动词+名词组合,避免使用 `a`, `b`, `list1` 等无意义命名。 + +### 8. 消灭魔术值 (Magic Values) + +* **规则**:禁止在逻辑判断中直接使用硬编码的数字或字符串。 +* **实践**:全面**拥抱枚举 (Enum) 或常量 (Static Final)**。 + +### 9. 声明式编程 + +* **规则**:提升集合处理的可读性。 +* **实践**:优先使用 **Java Stream API** 或函数式编程替代复杂的 `for/while` 循环。 + +### 10. 智能注释 + +* **规则**:避免“无效注释”。 +* **实践**:不要解释代码在做什么(What),而是解释**为什么要这么做(Why)**。如果代码本身足够清晰,则无需注释。 + +--- + +**当你为我生成代码时,请先自检是否违反了上述规则。如果我提交的代码不符合这些原则,请主动提示并给出重构建议。** + +--- + +--- + diff --git a/common/src/main/java/com/sdm/common/config/DataSourcePreWarmer.java b/common/src/main/java/com/sdm/common/config/DataSourcePreWarmer.java new file mode 100644 index 00000000..f45415c1 --- /dev/null +++ b/common/src/main/java/com/sdm/common/config/DataSourcePreWarmer.java @@ -0,0 +1,47 @@ +//package com.sdm.common.config;//package com.sdm.project.config.mybatis; +// +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.beans.factory.annotation.Qualifier; +//import org.springframework.boot.ApplicationRunner; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +// +//import javax.sql.DataSource; +//import java.sql.Connection; +// +//@Slf4j +//@Configuration +//public class DataSourcePreWarmer { +// +// @Bean +// public ApplicationRunner secondDataSourcePreWarmer( +// @Qualifier("secondDataSource") DataSource secondDataSource) { +// return args -> { +// try (Connection conn = secondDataSource.getConnection()) { +// log.info("✅ secondDataSource 预热成功,连接已建立: {}", conn); +// } catch (Exception e) { +// log.error("❌ secondDataSource 预热失败", e); +// throw new RuntimeException(e); +// } +// }; +// } +// +// @Bean +// public ApplicationRunner mainDataSourcePreWarmer( +// @Qualifier("masterDataSource") DataSource master, +// @Qualifier("slaveDataSource") DataSource slave) { +// return args -> { +// try { +// try (Connection c1 = master.getConnection()) { +// log.info("✅ masterDataSource 预热成功: {}", c1); +// } +// try (Connection c2 = slave.getConnection()) { +// log.info("✅ slaveDataSource 预热成功: {}", c2); +// } +// } catch (Exception e) { +// log.error("❌ 主从数据源预热失败", e); +// throw new RuntimeException(e); +// } +// }; +// } +//} \ No newline at end of file diff --git a/project/src/main/java/com/sdm/project/common/RunPerformanceStatusEnum.java b/common/src/main/java/com/sdm/common/entity/enums/PerformanceStatusEnum.java similarity index 74% rename from project/src/main/java/com/sdm/project/common/RunPerformanceStatusEnum.java rename to common/src/main/java/com/sdm/common/entity/enums/PerformanceStatusEnum.java index 08ddace9..f001619b 100644 --- a/project/src/main/java/com/sdm/project/common/RunPerformanceStatusEnum.java +++ b/common/src/main/java/com/sdm/common/entity/enums/PerformanceStatusEnum.java @@ -1,4 +1,4 @@ -package com.sdm.project.common; +package com.sdm.common.entity.enums; import lombok.Getter; @@ -6,7 +6,7 @@ import lombok.Getter; * 指标完成情况状态枚举 */ @Getter -public enum RunPerformanceStatusEnum { +public enum PerformanceStatusEnum { UNCOMPLETED("未完成", "0"), NOT_STARTED("不合格", "1"), @@ -18,7 +18,7 @@ public enum RunPerformanceStatusEnum { private final String code; - RunPerformanceStatusEnum(String name, String code) { + PerformanceStatusEnum(String name, String code) { this.name = name; this.code = code; } diff --git a/common/src/main/java/com/sdm/common/feign/impl/project/SimulationRunFeignClientImpl.java b/common/src/main/java/com/sdm/common/feign/impl/project/SimulationRunFeignClientImpl.java index 06e5f619..79377930 100644 --- a/common/src/main/java/com/sdm/common/feign/impl/project/SimulationRunFeignClientImpl.java +++ b/common/src/main/java/com/sdm/common/feign/impl/project/SimulationRunFeignClientImpl.java @@ -51,4 +51,14 @@ public class SimulationRunFeignClientImpl implements ISimulationRunFeignClient { return SdmResponse.failed("内部调用生成自动化报告失败"); } } + + @Override + public SdmResponse updateStatusByProcessInstanceId(String processInstanceId, Integer statusCode) { + try { + return simulationRunFeignClient.updateStatusByProcessInstanceId(processInstanceId, statusCode); + } catch (Exception e) { + log.error("根据流程实例ID更新算例状态失败: processInstanceId={}, statusCode={}", processInstanceId, statusCode, e); + return SdmResponse.failed("更新算例状态失败"); + } + } } diff --git a/common/src/main/java/com/sdm/common/feign/inter/project/ISimulationRunFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/project/ISimulationRunFeignClient.java index f7d9303f..1803c176 100644 --- a/common/src/main/java/com/sdm/common/feign/inter/project/ISimulationRunFeignClient.java +++ b/common/src/main/java/com/sdm/common/feign/inter/project/ISimulationRunFeignClient.java @@ -26,4 +26,14 @@ public interface ISimulationRunFeignClient { @PostMapping("/run/generateReportInternal") SdmResponse generateReportInternal(@RequestBody SpdmReportReq req); + /** + * 根据流程实例ID更新算例状态 + * + * @param processInstanceId 流程实例ID + * @param statusCode 状态值(RUNNING/SUSPENDED/COMPLETED/CANCELLED/ERROR) + * @return SdmResponse + */ + @PostMapping("/run/updateStatusByProcessInstanceId") + SdmResponse updateStatusByProcessInstanceId(@RequestParam String processInstanceId, @RequestParam Integer statusCode); + } \ No newline at end of file diff --git a/common/src/main/java/com/sdm/common/utils/CommonUtils.java b/common/src/main/java/com/sdm/common/utils/CommonUtils.java new file mode 100644 index 00000000..d1f1f18a --- /dev/null +++ b/common/src/main/java/com/sdm/common/utils/CommonUtils.java @@ -0,0 +1,36 @@ +package com.sdm.common.utils; + +public class CommonUtils { + + /** + * 检查字符串是否为有效的数字格式 + */ + public static boolean isValidNumberFormat(String str) { + if (str == null || str.isEmpty()) { + return false; + } + + // 支持: + // 1. 整数:123, -123, +123 + // 2. 小数:123.45, .45, 123. + // 3. 科学计数法:1.23e10, 1.23E-10 + // 4. 排除:空字符串、纯空格、多个小数点、非法字符 + + // 去除首尾空格 + str = str.trim(); + + // 检查是否为空 + if (str.isEmpty()) { + return false; + } + + // 检查是否只包含数字、小数点、正负号、e/E + if (!str.matches("^[+-]?[\\d.eE]+$")) { + return false; + } + + // 更精确的正则表达式 + return str.matches("^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?$"); + } + +} diff --git a/flowable/src/main/java/com/sdm/flowable/config/FlowableEngineConfig.java b/flowable/src/main/java/com/sdm/flowable/config/FlowableEngineConfig.java new file mode 100644 index 00000000..e5fc9f23 --- /dev/null +++ b/flowable/src/main/java/com/sdm/flowable/config/FlowableEngineConfig.java @@ -0,0 +1,48 @@ +package com.sdm.flowable.config; + +import com.sdm.flowable.listener.GlobalStatusEventListener; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Flowable流程引擎配置类 + * 用于注册全局事件监听器等配置 + * + * @author SDM + * @date 2026-01-23 + */ +@Slf4j +@Configuration +public class FlowableEngineConfig { + + /** + * 注册全局事件监听器 + * 通过EngineConfigurationConfigurer可以在流程引擎初始化时添加监听器 + */ + @Bean + public EngineConfigurationConfigurer globalEventListenerConfigurer( + GlobalStatusEventListener globalStatusEventListener) { + return engineConfiguration -> { + // 获取现有的事件监听器列表 + List eventListeners = engineConfiguration.getEventListeners(); + if (eventListeners == null) { + eventListeners = new ArrayList<>(); + } + + // 添加全局状态同步监听器 + eventListeners.add(globalStatusEventListener); + engineConfiguration.setEventListeners(eventListeners); + + log.info("✅ 已注册Flowable全局事件监听器: GlobalStatusEventListener"); + log.info(" 监听事件类型: PROCESS_COMPLETED, PROCESS_CANCELLED, ENTITY_SUSPENDED, ENTITY_ACTIVATED, JOB_MOVED_TO_DEADLETTER"); + log.info(" 状态映射: COMPLETED, CANCELLED, SUSPENDED, RUNNING, ERROR"); + }; + } +} diff --git a/flowable/src/main/java/com/sdm/flowable/delegate/handler/HpcHandler.java b/flowable/src/main/java/com/sdm/flowable/delegate/handler/HpcHandler.java index 611b9f9c..bdd9d4d7 100644 --- a/flowable/src/main/java/com/sdm/flowable/delegate/handler/HpcHandler.java +++ b/flowable/src/main/java/com/sdm/flowable/delegate/handler/HpcHandler.java @@ -105,7 +105,7 @@ public class HpcHandler implements ExecutionHandler,HPCExecu if(!submitResp.isSuccess()|| StringUtils.isBlank(submitResp.getData())){ // 推送失败消息 sendMsg(ThreadLocalContext.getTenantId(),ThreadLocalContext.getUserId(),submitHpcTaskRemoteReq.getJobName(),"失败"); - log.error("HpcHandler submit failed,jobName:{}",params); + log.error("HpcHandler submit failed:{}",JSONObject.toJSONString(params)); throw new RuntimeException("HpcHandler submit failed,"+submitResp.getMessage()); } diff --git a/flowable/src/main/java/com/sdm/flowable/enums/ProcessInstanceStateEnum.java b/flowable/src/main/java/com/sdm/flowable/enums/ProcessInstanceStateEnum.java index 9512aa34..babd516d 100644 --- a/flowable/src/main/java/com/sdm/flowable/enums/ProcessInstanceStateEnum.java +++ b/flowable/src/main/java/com/sdm/flowable/enums/ProcessInstanceStateEnum.java @@ -7,35 +7,43 @@ public enum ProcessInstanceStateEnum { /** * 运行中 */ - RUNNING("running"), + RUNNING(1,"running"), + + /** + * 已完成 + */ + COMPLETED(2,"completed"), + + /** + * 错误 + */ + ERROR(3,"error"), /** * 挂起 */ - SUSPENDED("suspended"), - - /** - * 错误 - */ - ERROR("error"), - - /** - * 已完成 - */ - COMPLETED("completed"), + SUSPENDED(4,"suspended"), + /** * 已取消 */ - CANCELLED("cancelled"); + CANCELLED(5,"cancelled"); - private final String code; + private final Integer code; - ProcessInstanceStateEnum(String code) { + private final String value; + + ProcessInstanceStateEnum(Integer code, String value) { this.code = code; + this.value = value; } - public String getCode() { + public String getValue() { + return value; + } + + public Integer getCode() { return code; } } \ No newline at end of file diff --git a/flowable/src/main/java/com/sdm/flowable/listener/GlobalStatusEventListener.java b/flowable/src/main/java/com/sdm/flowable/listener/GlobalStatusEventListener.java new file mode 100644 index 00000000..451704d5 --- /dev/null +++ b/flowable/src/main/java/com/sdm/flowable/listener/GlobalStatusEventListener.java @@ -0,0 +1,187 @@ +package com.sdm.flowable.listener; + +import com.sdm.common.feign.inter.project.ISimulationRunFeignClient; +import com.sdm.flowable.enums.ProcessInstanceStateEnum; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.job.api.Job; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * 全局算例状态同步监听器 + * 作用:替代定时任务,实时推送状态 + *

+ * 监听Flowable流程引擎的全生命周期事件,实现算例状态的实时同步: + * - PROCESS_COMPLETED: 流程正常完成 + * - PROCESS_CANCELLED: 流程被取消/终止 + * - ENTITY_SUSPENDED: 实体挂起(需过滤流程实例) + * - ENTITY_ACTIVATED: 实体激活(需过滤流程实例) + * - JOB_MOVED_TO_DEADLETTER: 作业移入死信队列(ERROR状态) + *

+ * + * @author SDM + * @date 2026-01-23 + */ +@Slf4j +@Component +public class GlobalStatusEventListener implements FlowableEventListener { + + @Autowired + private ISimulationRunFeignClient simulationRunFeignClient; + + @Override + public Set getTypes() { + return new HashSet<>(Arrays.asList( + FlowableEngineEventType.PROCESS_COMPLETED, // 流程完成 + FlowableEngineEventType.PROCESS_CANCELLED, // 流程取消(精确匹配,不需要判断deleteReason) + FlowableEngineEventType.ENTITY_SUSPENDED, // 实体挂起(需过滤流程实例) + FlowableEngineEventType.ENTITY_ACTIVATED, // 实体激活(需过滤流程实例) + FlowableEngineEventType.JOB_MOVED_TO_DEADLETTER // 作业进入死信(ERROR状态) + )); + } + + @Override + public void onEvent(FlowableEvent event) { + try { + FlowableEngineEventType eventType = (FlowableEngineEventType) event.getType(); + + // 1. 流程正常完成 + if (eventType == FlowableEngineEventType.PROCESS_COMPLETED) { + handleProcessCompleted((FlowableEngineEntityEvent) event); + } + // 2. 流程被取消(Flowable 7.x有独立的CANCELLED事件) + else if (eventType == FlowableEngineEventType.PROCESS_CANCELLED) { + handleProcessCancelled((FlowableEngineEntityEvent) event); + } + // 3. 实体挂起/激活(需要过滤,只处理流程实例级别) + else if (eventType == FlowableEngineEventType.ENTITY_SUSPENDED || + eventType == FlowableEngineEventType.ENTITY_ACTIVATED) { + handleSuspendOrActivate((FlowableEngineEntityEvent) event, eventType); + } + // 4. 作业进入死信队列(ERROR状态的金标准) + else if (eventType == FlowableEngineEventType.JOB_MOVED_TO_DEADLETTER) { + handleDeadLetter((FlowableEngineEntityEvent) event); + } + } catch (Exception e) { + log.error("处理Flowable事件异常: eventType={}", event.getType(), e); + } + } + + // --- 内部逻辑方法 --- + + /** + * 处理流程正常完成事件 + * PROCESS_COMPLETED只表示流程走到EndEvent,不包括取消场景 + */ + private void handleProcessCompleted(FlowableEngineEntityEvent event) { + String processInstanceId = event.getProcessInstanceId(); + log.info("流程正常完成: processInstanceId={}", processInstanceId); + doUpdate(processInstanceId, ProcessInstanceStateEnum.COMPLETED.getCode()); + } + + /** + * 处理流程取消事件 + * Flowable 7.x提供了独立的PROCESS_CANCELLED事件,更精确 + */ + private void handleProcessCancelled(FlowableEngineEntityEvent event) { + String processInstanceId = event.getProcessInstanceId(); + ExecutionEntity execution = (ExecutionEntity) event.getEntity(); + log.info("流程被取消: processInstanceId={}, deleteReason={}", + processInstanceId, execution.getDeleteReason()); + doUpdate(processInstanceId, ProcessInstanceStateEnum.CANCELLED.getCode()); + } + + /** + * 处理实体挂起/激活事件 + * 关键:必须过滤,只处理流程实例级别的挂起/激活,忽略子流程或其他实体 + */ + private void handleSuspendOrActivate(FlowableEngineEntityEvent event, FlowableEngineEventType eventType) { + Object entity = event.getEntity(); + + // 只处理ExecutionEntity(流程执行实体) + if (entity instanceof ExecutionEntity) { + ExecutionEntity execution = (ExecutionEntity) entity; + + // 关键判断:isProcessInstanceType()确保是流程实例本身,而非子分支 + if (execution.isProcessInstanceType()) { + String processInstanceId = execution.getProcessInstanceId(); + Integer status = (eventType == FlowableEngineEventType.ENTITY_SUSPENDED) ? ProcessInstanceStateEnum.SUSPENDED.getCode() : ProcessInstanceStateEnum.RUNNING.getCode(); + + log.info("流程实例{}状态变更: processInstanceId={}", + status.equals("SUSPENDED") ? "挂起" : "激活", processInstanceId); + doUpdate(processInstanceId, status); + } else { + log.debug("忽略非流程实例级别的挂起/激活事件: executionId={}", execution.getId()); + } + } + } + + /** + * 处理死信事件(ERROR状态的标准方式) + * JOB_MOVED_TO_DEADLETTER是最精确的ERROR信号: + * - 比JOB_EXECUTION_FAILURE准确(FAILURE只是重试中的失败) + * - 表示引擎已放弃重试,必须人工干预 + *

+ * 前提条件:ServiceTask必须配置async="true"和R0/PT0S重试策略 + */ + private void handleDeadLetter(FlowableEngineEntityEvent event) { + Object entity = event.getEntity(); + + if (entity instanceof Job) { + Job job = (Job) entity; + String processInstanceId = job.getProcessInstanceId(); + String exceptionMessage = job.getExceptionMessage(); + + log.error("❌ 作业进入死信队列,流程ERROR: processInstanceId={}, jobId={}, exception={}", + processInstanceId, job.getId(), exceptionMessage); + + doUpdate(processInstanceId, ProcessInstanceStateEnum.ERROR.getCode()); + } + } + + /** + * 真正的更新数据库逻辑 + * 通过Feign调用project服务更新算例状态 + */ + private void doUpdate(String processInstanceId, Integer statusCode) { + if (processInstanceId == null) { + log.warn("流程实例ID为空,跳过状态更新"); + return; + } + + log.info(">>> 更新算例状态 [{}] -> {}", processInstanceId, statusCode); + try { + simulationRunFeignClient.updateStatusByProcessInstanceId(processInstanceId, statusCode); + } catch (Exception e) { + log.error("更新算例状态失败: processInstanceId={}, status={}", processInstanceId, statusCode, e); + } + } + + @Override + public boolean isFailOnException() { + // 返回false:即使监听器抛异常,也不影响流程继续执行 + // 这是容错设计,保证业务流程的稳定性 + return false; + } + + @Override + public boolean isFireOnTransactionLifecycleEvent() { + // 返回false:在事务提交前触发,确保状态及时更新 + // 如果返回true,监听器会在事务提交后触发,可能导致状态更新滞后 + return false; + } + + @Override + public String getOnTransaction() { + return null; + } +} diff --git a/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java b/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java index e4834e87..0d814b3f 100644 --- a/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java +++ b/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java @@ -472,11 +472,11 @@ public class ProcessService implements Iprocess{ if (isRunning) { // --- 运行中 --- if (hasError) { - status = ProcessInstanceStateEnum.ERROR.getCode(); // 有死信作业,视为异常 + status = ProcessInstanceStateEnum.ERROR.getValue(); // 有死信作业,视为异常 } else if (isSuspended) { - status = ProcessInstanceStateEnum.SUSPENDED.getCode(); // 被挂起 + status = ProcessInstanceStateEnum.SUSPENDED.getValue(); // 被挂起 } else { - status = ProcessInstanceStateEnum.RUNNING.getCode(); // 正常运行 + status = ProcessInstanceStateEnum.RUNNING.getValue(); // 正常运行 } } else { // --- 已结束 (运行时查不到,历史表里有) --- @@ -484,11 +484,11 @@ public class ProcessService implements Iprocess{ if (deleteReason == null) { // 1. 正常走完结束节点,deleteReason 为空 - status = ProcessInstanceStateEnum.COMPLETED.getCode(); + status = ProcessInstanceStateEnum.COMPLETED.getValue(); } else { // 2. 有删除原因,说明是被取消或强制终止的 // 你可以根据 reason 的内容做更细的区分,或者统称为 cancelled - status = ProcessInstanceStateEnum.CANCELLED.getCode(); + status = ProcessInstanceStateEnum.CANCELLED.getValue(); } } info.setStatus(status); diff --git a/outbridge/src/main/java/com/sdm/outbridge/config/CommonConfig.java b/outbridge/src/main/java/com/sdm/outbridge/config/CommonConfig.java index fe7d40cc..71968877 100644 --- a/outbridge/src/main/java/com/sdm/outbridge/config/CommonConfig.java +++ b/outbridge/src/main/java/com/sdm/outbridge/config/CommonConfig.java @@ -1,13 +1,19 @@ package com.sdm.outbridge.config; //// common模块:com.xxx.common.config.CommonConfig +import com.sdm.common.utils.AESUtil; +import org.apache.commons.lang3.StringUtils; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MapPropertySource; import org.springframework.core.io.support.DefaultPropertySourceFactory; import org.springframework.core.io.support.EncodedResource; import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 加载common模块的自定义配置文件 @@ -25,7 +31,48 @@ public class CommonConfig { } List> sources = new YamlPropertySourceLoader() .load(resource.getResource().getFilename(), resource.getResource()); - return sources.get(0); + org.springframework.core.env.PropertySource originalSource = sources.get(0); + org.springframework.core.env.PropertySource decryptedSource = + decryptPropertySource(originalSource); + return decryptedSource; } } + + private static org.springframework.core.env.PropertySource decryptPropertySource(org.springframework.core.env.PropertySource source) { + // 只处理可枚举的属性源(yml加载后的源都是EnumerablePropertySource) + if (!(source instanceof EnumerablePropertySource)) { + return source; + } + + EnumerablePropertySource enumerableSource = (EnumerablePropertySource) source; + Map decryptedProperties = new HashMap<>(); + // 遍历所有配置项 + for (String propertyName : enumerableSource.getPropertyNames()) { + Object value = enumerableSource.getProperty(propertyName); + if (value != null && value instanceof String) { + String strValue = (String) value; + if (strValue.startsWith("ENC(") && strValue.endsWith(")")) { + // 解密并替换值 + String spdmEnkey = StringUtils.isBlank(System.getProperty("spdm.enkey"))? + System.getenv("spdm.enkey"):System.getProperty("spdm.enkey"); + String encryptedValue = strValue.substring(4, strValue.length() - 1); + try { + decryptedProperties.put(propertyName, AESUtil.decodeNew(encryptedValue, spdmEnkey)); + } catch (Exception e) { + System.out.println("利元亨现场配置解密异常:"+e.getMessage()); + throw new RuntimeException(e); + } + } else { + // 非加密值,直接保留 + decryptedProperties.put(propertyName, strValue); + } + } else { + // 非字符串类型,直接保留 + decryptedProperties.put(propertyName, value); + } + } + // 生成新的属性源(名称和原始源一致,确保覆盖) + return new MapPropertySource(source.getName(), decryptedProperties); + } + } \ No newline at end of file diff --git a/outbridge/src/main/resources/common.yml b/outbridge/src/main/resources/common.yml index cb9cb324..67e3d02e 100644 --- a/outbridge/src/main/resources/common.yml +++ b/outbridge/src/main/resources/common.yml @@ -2,7 +2,7 @@ spring: datasource: second: username: EP_DM - password: ENC(c04rt9Z6Ygz024EU9eWvig==) + password: EP_DM@123.COM # todo 生产地址 jdbc-url: jdbc:mysql://10.122.48.11:13306/easy_project?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java b/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java index c25d7eba..3a1c6398 100644 --- a/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java +++ b/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java @@ -178,6 +178,7 @@ public class SimulationNodeController implements ISimulationNodeFeignClient { @PostMapping("/getAllUserTaskCompleteStatistics") @Operation(summary = "所有项目的人员任务完成情况统计", description = "所有项目的人员任务完成情况统计") public SdmResponse getAllUserTaskCompleteStatistics(@RequestBody @Validated GetAllUserTaskCompleteStatisticsReq req) { + req.setTenantId(ThreadLocalContext.getTenantId()); return nodeService.getAllUserTaskCompleteStatistics(req); } @@ -188,7 +189,7 @@ public class SimulationNodeController implements ISimulationNodeFeignClient { @GetMapping("/getUserGroupProjectStatistics") @Operation(summary = "用户组项目统计", description = "用户组项目统计") public SdmResponse getUserGroupProjectStatistics(@Parameter(description = "用户组ID")@RequestParam( value = "userGroupId" )@NotNull(message = "用户组ID(userGroupId)为必传参数,请补充后重试") Long userGroupId, @Parameter(description = "用户ID")@RequestParam( value = "userId", required = false) Long userId) { - return nodeService.getUserGroupProjectStatistics(userGroupId,userId); + return nodeService.getUserGroupProjectStatistics(userGroupId,userId,ThreadLocalContext.getTenantId()); } /** @@ -201,6 +202,7 @@ public class SimulationNodeController implements ISimulationNodeFeignClient { @PostMapping("/getUserGroupTaskCompleteStatistics") @Operation(summary = "用户组任务完成情况统计", description = "用户组任务完成情况统计") public SdmResponse getUserGroupTaskCompleteStatistics(@RequestBody @Validated GetUserGroupTaskCompleteStatisticsReq req) { + req.setTenantId(ThreadLocalContext.getTenantId()); return nodeService.getUserGroupTaskCompleteStatistics(req); } diff --git a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java index b3ad8f29..af96589c 100644 --- a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java +++ b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java @@ -240,8 +240,8 @@ public class SimulationRunController implements ISimulationRunFeignClient { */ @PostMapping("/editReport") @Operation(summary = "编辑报告模板生成报告", description = "编辑报告模板生成报告") - public void editReport(@RequestBody EditReportReq req) { - runService.editReport(req); + public SdmResponse editReport(@RequestBody EditReportReq req) { + return runService.editReport(req); } /** @@ -395,4 +395,13 @@ public class SimulationRunController implements ISimulationRunFeignClient { return runService.syncKeyResultToTask(req); } + /** + * 根据流程实例ID更新算例状态 (内部调用) + * 由Flowable全局事件监听器调用 + */ + @PostMapping("/updateStatusByProcessInstanceId") + public SdmResponse updateStatusByProcessInstanceId(@RequestParam String processInstanceId, @RequestParam Integer statusCode) { + return runService.updateStatusByProcessInstanceId(processInstanceId, statusCode); + } + } \ No newline at end of file diff --git a/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java b/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java index 7553893f..b3bea795 100644 --- a/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java +++ b/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java @@ -74,7 +74,7 @@ public interface SimulationNodeMapper extends BaseMapper { List getNodeListByIds(@Param("nodeIdList") List nodeIdList); - List getUserGroupProjectStatistics(@Param("userIds")Set userIds ); + List getUserGroupProjectStatistics(@Param("userIds")Set userIds, @Param("tenantId")Long tenantId); List getUserGroupTaskCompleteStatistics(@Param("req") GetUserGroupTaskCompleteStatisticsReq req); diff --git a/project/src/main/java/com/sdm/project/model/req/GetAllUserTaskCompleteStatisticsReq.java b/project/src/main/java/com/sdm/project/model/req/GetAllUserTaskCompleteStatisticsReq.java index 8e1a58b6..cd6c31fe 100644 --- a/project/src/main/java/com/sdm/project/model/req/GetAllUserTaskCompleteStatisticsReq.java +++ b/project/src/main/java/com/sdm/project/model/req/GetAllUserTaskCompleteStatisticsReq.java @@ -11,6 +11,8 @@ import java.util.List; @Data @Schema(description = "所有项目的人员任务完成情况统计") public class GetAllUserTaskCompleteStatisticsReq { + @Schema(description = "租户ID") + private Long tenantId; @Schema(description = "用户列表") private List userIds; diff --git a/project/src/main/java/com/sdm/project/model/req/GetUserGroupTaskCompleteStatisticsReq.java b/project/src/main/java/com/sdm/project/model/req/GetUserGroupTaskCompleteStatisticsReq.java index d51d5abf..05358692 100644 --- a/project/src/main/java/com/sdm/project/model/req/GetUserGroupTaskCompleteStatisticsReq.java +++ b/project/src/main/java/com/sdm/project/model/req/GetUserGroupTaskCompleteStatisticsReq.java @@ -20,6 +20,9 @@ public class GetUserGroupTaskCompleteStatisticsReq { @Schema(description = "用户ID") private Long userId; + @Schema(description = "租户ID") + private Long tenantId; + @Schema(description = "用户") private Set userIds; diff --git a/project/src/main/java/com/sdm/project/service/INodeService.java b/project/src/main/java/com/sdm/project/service/INodeService.java index afbfa23a..b434f79f 100644 --- a/project/src/main/java/com/sdm/project/service/INodeService.java +++ b/project/src/main/java/com/sdm/project/service/INodeService.java @@ -41,7 +41,7 @@ public interface INodeService extends IService { SdmResponse> getTaskRunList(List uuids); - SdmResponse getUserGroupProjectStatistics(Long userGroupId, Long userId); + SdmResponse getUserGroupProjectStatistics(Long userGroupId, Long userId,Long tenantId); SdmResponse getUserGroupTaskCompleteStatistics(GetUserGroupTaskCompleteStatisticsReq req); diff --git a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java index 3aeaf650..22fc4ec2 100644 --- a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java +++ b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java @@ -71,7 +71,7 @@ public interface ISimulationRunService extends IService { void generateNewReport(SpdmReportReq req, HttpServletResponse response); - void editReport(EditReportReq req); + SdmResponse editReport(EditReportReq req); void editReportAndDownload(EditReportReq req, HttpServletResponse response); @@ -110,4 +110,12 @@ public interface ISimulationRunService extends IService { SdmResponse syncKeyResultToTask(KeyResultReq req); + /** + * 根据流程实例ID更新算例状态 + * @param processInstanceId 流程实例ID + * @param statusCode 状态值(RUNNING/SUSPENDED/COMPLETED/CANCELLED/ERROR) + * @return SdmResponse + */ + SdmResponse updateStatusByProcessInstanceId(String processInstanceId, Integer statusCode); + } \ No newline at end of file diff --git a/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java index a455f3ea..ae768997 100644 --- a/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java @@ -8,7 +8,6 @@ import com.sdm.common.common.ResultCode; import com.sdm.common.common.SdmResponse; import com.sdm.common.common.ThreadLocalContext; import com.sdm.common.entity.ExportExcelFormat; -import com.sdm.common.entity.bo.DataDictionary; import com.sdm.common.entity.constants.TagConstant; import com.sdm.common.entity.enums.DirTypeEnum; import com.sdm.common.entity.enums.FilePermissionEnum; @@ -22,7 +21,6 @@ import com.sdm.common.entity.resp.AllNodeByProjectIdAndTypeResp; import com.sdm.common.entity.resp.PageDataResp; import com.sdm.common.entity.resp.project.SimulationNodeResp; import com.sdm.common.entity.resp.project.SimulationRunResp; -import com.sdm.common.entity.resp.system.CIDStaffResp; import com.sdm.common.entity.resp.system.CIDUserResp; import com.sdm.common.entity.resp.system.SysUserGroupDetailResp; import com.sdm.common.feign.impl.data.DataClientFeignClientImpl; @@ -1440,7 +1438,7 @@ public class NodeServiceImpl extends ServiceImpl userId2Nickname = getUserIdToNicknameMap(userGroupId); if (MapUtils.isEmpty(userId2Nickname)) { return SdmResponse.success(); @@ -1453,7 +1451,7 @@ public class NodeServiceImpl extends ServiceImpl userGroupProjectStatistics = this.baseMapper.getUserGroupProjectStatistics(userIds); + List userGroupProjectStatistics = this.baseMapper.getUserGroupProjectStatistics(userIds,tenantId ); if (CollectionUtils.isEmpty(userGroupProjectStatistics)) { return SdmResponse.success(new ArrayList<>()); } diff --git a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java index 14b081e9..88474c1d 100644 --- a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java @@ -12,12 +12,8 @@ import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sdm.common.common.SdmResponse; import com.sdm.common.common.ThreadLocalContext; -import com.sdm.common.config.FlowableConfig; import com.sdm.common.entity.constants.NumberConstants; -import com.sdm.common.entity.enums.ApproveTypeEnum; -import com.sdm.common.entity.enums.DirTypeEnum; -import com.sdm.common.entity.enums.FileBizTypeEnum; -import com.sdm.common.entity.enums.NodeTypeEnum; +import com.sdm.common.entity.enums.*; import com.sdm.common.entity.flowable.dto.FlowElementDTO; import com.sdm.common.entity.flowable.dto.ProcessDefinitionDTO; import com.sdm.common.entity.req.capability.FlowNodeDto; @@ -60,7 +56,6 @@ import com.sdm.project.model.resp.KeyResultAndTaskInfoResp; import com.sdm.project.model.resp.RunVersionInfoResp; import com.sdm.project.model.vo.SpdmNodeVo; import com.sdm.common.entity.resp.project.SpdmTaskVo; -import com.sdm.project.model.vo.SpdmTaskMemberVo; import com.sdm.project.service.*; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -84,7 +79,6 @@ import java.nio.file.StandardCopyOption; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; import static com.sdm.common.service.BaseService.generateUuid; @@ -629,7 +623,7 @@ public class SimulationRunServiceImpl extends ServiceImpl #{userId} @@ -354,6 +358,8 @@ from simulation_task task left join simulation_task_member task_member on task.uuid = task_member.task_id + task.tenant_Id = #{req.tenantId} + and task_member.user_id in ( @@ -406,7 +412,8 @@ from simulation_task task left join simulation_task_member task_member on task.uuid = task_member.task_id - task_member.user_id in + task.tenant_Id = #{req.tenantId} + and task_member.user_id in ( #{userId} diff --git a/task/src/main/java/com/sdm/task/model/dto/TaskPerformanceDto.java b/task/src/main/java/com/sdm/task/model/dto/TaskPerformanceDto.java index 952db88e..bf7ab008 100644 --- a/task/src/main/java/com/sdm/task/model/dto/TaskPerformanceDto.java +++ b/task/src/main/java/com/sdm/task/model/dto/TaskPerformanceDto.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -66,6 +67,9 @@ public class TaskPerformanceDto { @Schema(description = "标准") private String standard; + @Schema(description = "计算及结果值") + private String resultValue; + @Schema(description = "租户ID") private String tenantId; diff --git a/task/src/main/java/com/sdm/task/model/entity/SimulationPerformance.java b/task/src/main/java/com/sdm/task/model/entity/SimulationPerformance.java index a1cfbe09..6131271c 100644 --- a/task/src/main/java/com/sdm/task/model/entity/SimulationPerformance.java +++ b/task/src/main/java/com/sdm/task/model/entity/SimulationPerformance.java @@ -80,7 +80,7 @@ public class SimulationPerformance implements Serializable { @TableField("method") private String method; - @ApiModelProperty(value = "指标完成情况 未完成 不合格 风险可控 未分析 合格") + @ApiModelProperty(value = "指标完成情况 未完成 不合格 风险可控 未分析 合格 ") @TableField("completeStatus") private String completeStatus; diff --git a/task/src/main/java/com/sdm/task/service/impl/SimulationPerformanceServiceImpl.java b/task/src/main/java/com/sdm/task/service/impl/SimulationPerformanceServiceImpl.java index 6d0be46b..cbc3d080 100644 --- a/task/src/main/java/com/sdm/task/service/impl/SimulationPerformanceServiceImpl.java +++ b/task/src/main/java/com/sdm/task/service/impl/SimulationPerformanceServiceImpl.java @@ -4,12 +4,14 @@ import com.alibaba.fastjson2.JSONArray; import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.ExportExcelFormat; import com.sdm.common.entity.enums.ApprovalFileDataStatusEnum; +import com.sdm.common.entity.enums.PerformanceStatusEnum; import com.sdm.common.entity.req.data.KnowledgeExportExcelParam; import com.sdm.common.entity.req.performance.PerformanceExportExcelFormat; import com.sdm.common.entity.req.performance.PerformanceExportExcelParam; import com.sdm.common.entity.resp.PageDataResp; import com.sdm.common.entity.resp.task.PerformanceResp; import com.sdm.common.service.BaseService; +import com.sdm.common.utils.CommonUtils; import com.sdm.common.utils.RandomUtil; import com.sdm.common.utils.excel.ExcelUtil; import com.sdm.task.model.dto.TaskPerformanceDto; @@ -31,7 +33,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -131,9 +135,48 @@ public class SimulationPerformanceServiceImpl extends ServiceImpl comparison >= 0; + case ">" -> comparison > 0; + case "<" -> comparison < 0; + case "≤" -> comparison <= 0; + default -> throw new IllegalArgumentException("无效运算符: " + method); + }; + simulationPerformance.setCompleteStatus(isPassed ? PerformanceStatusEnum.STARTED.getCode() : PerformanceStatusEnum.NOT_STARTED.getCode()); + } catch (Exception e) { + log.error("数字格式异常:", e); + return SdmResponse.failed("请输入有效的目标值和分析值"); + } + } return SdmResponse.success(this.updateById(simulationPerformance)); } + private boolean validateNumber(String input) { + String trimmed = input.trim(); + // 前置格式检查 - 验证是不是有效数字格式 + if (!CommonUtils.isValidNumberFormat(trimmed)) { + return false; + } + return true; + } + @Override public SdmResponse exportPerformance(PerformanceExportExcelFormat performanceExportExcelFormat, HttpServletResponse httpServletResponse) { SdmResponse response = new SdmResponse();