From f5dcbbf821646f361edf8d03d202a3f1a7d41157 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Thu, 19 Mar 2026 09:06:21 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=90=8E=E8=87=AA=E5=8A=A8=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E9=87=8D=E6=89=93=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/CcdiModelParamServiceImpl.java | 19 +++++-- .../impl/CcdiBankTagServiceImplTest.java | 10 ++++ .../impl/CcdiModelParamServiceImplTest.java | 53 +++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java index df96ce41..c8fcca29 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java @@ -116,6 +116,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService { } String username = SecurityUtils.getUsername(); + int updatedCount = 0; for (ModelParamSaveDTO.ParamValueItem item : saveDTO.getParams()) { int updated = modelParamMapper.updateParamValue( projectId, @@ -126,9 +127,12 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService { ); if (updated == 0) { log.warn("参数不存在或未更新,paramCode={}", item.getParamCode()); + continue; } + updatedCount += updated; } + submitAutoRebuildIfNeeded(projectId, updatedCount); } catch (ServiceException e) { // 业务异常,直接抛出 throw e; @@ -218,11 +222,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService { if (!updateList.isEmpty()) { modelParamMapper.batchUpdateParamValues(updateList); log.info("批量更新参数成功, count={}", updateList.size()); - if (projectId > 0) { - bankTagService.submitAutoRebuild(projectId, TriggerType.AUTO_PARAM_CHANGE); - log.info("项目参数保存成功,已触发流水自动重打标: projectId={}, updatedCount={}", - projectId, updateList.size()); - } + submitAutoRebuildIfNeeded(projectId, updateList.size()); } } catch (ServiceException e) { throw e; @@ -300,4 +300,13 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService { target.setUpdateBy(username); return target; } + + private void submitAutoRebuildIfNeeded(Long projectId, int updatedCount) { + if (projectId == null || projectId <= 0 || updatedCount <= 0) { + return; + } + + bankTagService.submitAutoRebuild(projectId, TriggerType.AUTO_PARAM_CHANGE); + log.info("项目参数保存成功,已触发流水自动重打标: projectId={}, updatedCount={}", projectId, updatedCount); + } } diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java index 71fde2dd..28e7e204 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java @@ -60,6 +60,16 @@ class CcdiBankTagServiceImplTest { @Mock private ICcdiProjectService projectService; + @Mock + private ProjectBankTagRebuildCoordinator coordinator; + + @Test + void submitAutoRebuild_shouldKeepAutoParamChangeTriggerType() { + service.submitAutoRebuild(40L, TriggerType.AUTO_PARAM_CHANGE); + + verify(coordinator).submitAuto(40L, TriggerType.AUTO_PARAM_CHANGE); + } + @Test void rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks() { ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImplTest.java index d6e2aafd..6dc2c8dc 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImplTest.java @@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mockStatic; @@ -154,6 +155,58 @@ class CcdiModelParamServiceImplTest { verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_PARAM_CHANGE); } + @Test + void saveParams_shouldSubmitAutoRebuildAfterProjectParamsUpdated() { + CcdiProject project = new CcdiProject(); + project.setProjectId(40L); + project.setConfigType("custom"); + when(projectMapper.selectById(40L)).thenReturn(project); + when(modelParamMapper.updateParamValue(40L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2000", "admin")) + .thenReturn(1); + + ModelParamSaveDTO saveDTO = new ModelParamSaveDTO(); + saveDTO.setProjectId(40L); + saveDTO.setModelCode("LARGE_TRANSACTION"); + ModelParamSaveDTO.ParamValueItem item = new ModelParamSaveDTO.ParamValueItem(); + item.setParamCode("SINGLE_TRANSACTION_AMOUNT"); + item.setParamValue("2000"); + saveDTO.setParams(List.of(item)); + + try (MockedStatic mocked = mockStatic(SecurityUtils.class)) { + mocked.when(SecurityUtils::getUsername).thenReturn("admin"); + + service.saveParams(saveDTO); + } + + verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_PARAM_CHANGE); + } + + @Test + void saveParams_shouldNotSubmitAutoRebuildWhenNoProjectParamUpdated() { + CcdiProject project = new CcdiProject(); + project.setProjectId(40L); + project.setConfigType("custom"); + when(projectMapper.selectById(40L)).thenReturn(project); + when(modelParamMapper.updateParamValue(40L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2000", "admin")) + .thenReturn(0); + + ModelParamSaveDTO saveDTO = new ModelParamSaveDTO(); + saveDTO.setProjectId(40L); + saveDTO.setModelCode("LARGE_TRANSACTION"); + ModelParamSaveDTO.ParamValueItem item = new ModelParamSaveDTO.ParamValueItem(); + item.setParamCode("SINGLE_TRANSACTION_AMOUNT"); + item.setParamValue("2000"); + saveDTO.setParams(List.of(item)); + + try (MockedStatic mocked = mockStatic(SecurityUtils.class)) { + mocked.when(SecurityUtils::getUsername).thenReturn("admin"); + + service.saveParams(saveDTO); + } + + verify(bankTagService, never()).submitAutoRebuild(anyLong(), any()); + } + @Test void saveAllParams_shouldNotSubmitAutoRebuildForGlobalDefaults() { ModelParamSaveAllDTO saveAllDTO = buildSaveAllDto(); From d03427bde44c4b7afa360ee379da4de4cfdbe601 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Thu, 19 Mar 2026 09:06:26 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=A7=A6=E5=8F=91=E9=87=8D=E6=89=93=E6=A0=87?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=AE=9E=E6=96=BD=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...model-param-save-trigger-rebuild-record.md | 40 ++++++++----------- ...ve-trigger-rebuild-backend-verification.md | 23 ++++++----- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/docs/reports/implementation/2026-03-18-model-param-save-trigger-rebuild-record.md b/docs/reports/implementation/2026-03-18-model-param-save-trigger-rebuild-record.md index bc78749a..a70d4454 100644 --- a/docs/reports/implementation/2026-03-18-model-param-save-trigger-rebuild-record.md +++ b/docs/reports/implementation/2026-03-18-model-param-save-trigger-rebuild-record.md @@ -1,36 +1,28 @@ -# 参数保存触发项目流水重打标实施记录 +# 参数保存触发项目流水重打标后端实施记录 ## 本次改动 -- 后端在项目参数批量保存成功后自动触发项目内流水重新打标 -- 自动重打标由同步执行改为后台异步排队执行 -- 前端在项目参数提交前增加提醒弹窗,确认后再提交保存 +- 补齐项目级单模型参数保存成功后的自动重打标触发 +- 保持批量参数保存与单模型保存使用一致的触发语义 +- 增加触发类型透传测试与后端验证记录 ## 修改内容 -### 后端 - -- 在 `TriggerType` 中新增 `AUTO_PARAM_CHANGE` -- 在 `CcdiModelParamServiceImpl.saveAllParams()` 中,项目级参数保存成功且存在实际更新时触发 `submitAutoRebuild` -- 在 `ProjectBankTagRebuildCoordinator` 中新增 `tagRebuildExecutor` 异步调度,自动重打标改为后台执行 -- 增加异步排队窗口的补跑标记,避免重复触发请求在任务创建前被吞掉 -- 在 `BankTagThreadPoolConfig` 中新增项目级重打标线程池配置 - -### 前端 - -- 在 `ParamConfig.vue` 的 `handleSaveAll` 中增加确认弹窗 -- 保存成功提示改为“已开始项目内流水重新打标” -- 保存成功后刷新参数列表,并向父页面发出 `refresh-project` 事件 +- 在 `CcdiModelParamServiceImpl.saveParams()` 中统计实际更新条数,仅在 `projectId > 0` 且存在实际更新时触发 `submitAutoRebuild` +- 抽取 `submitAutoRebuildIfNeeded` 私有方法,统一 `saveParams` 与 `saveAllParams` 的触发条件和日志 +- 在 `CcdiModelParamServiceImplTest` 中新增: + - 项目级单模型保存成功后触发自动重打标 + - 无实际更新时不触发自动重打标 +- 在 `CcdiBankTagServiceImplTest` 中新增 `AUTO_PARAM_CHANGE` 触发类型透传校验 +- 更新后端验证记录,覆盖单模型保存、批量保存、默认参数与未更新场景 ## 测试与验证 -- 后端: - `mvn -pl ccdi-project -Dtest=CcdiModelParamServiceImplTest,ProjectBankTagRebuildCoordinatorTest test` -- 前端: - `cd ruoyi-ui && npm run build:prod` +```bash +mvn -pl ccdi-project -Dtest=CcdiModelParamServiceImplTest,CcdiBankTagServiceImplTest test +``` ## 结果 -- 后端相关单元测试全部通过 -- 前端生产构建通过 -- 未启动额外前后端运行进程,因此无需额外清理测试进程 +- 后端相关聚焦单元测试全部通过 +- 本次验证未启动额外前后端运行进程,无需清理测试进程 diff --git a/docs/tests/records/2026-03-18-model-param-save-trigger-rebuild-backend-verification.md b/docs/tests/records/2026-03-18-model-param-save-trigger-rebuild-backend-verification.md index dde3a368..acb29ae3 100644 --- a/docs/tests/records/2026-03-18-model-param-save-trigger-rebuild-backend-verification.md +++ b/docs/tests/records/2026-03-18-model-param-save-trigger-rebuild-backend-verification.md @@ -2,25 +2,28 @@ ## 验证范围 -- 项目参数批量保存成功后自动提交异步重打标 -- 自动重打标通过后台执行器异步排队,不阻塞当前请求线程 -- 自动重打标在已存在运行任务时仍可记录补跑信号 +- 项目级单模型参数保存成功后自动异步触发重打标 +- 项目级批量参数保存成功后自动异步触发重打标 +- 全局默认参数保存不触发项目重打标 +- 参数未实际更新或保存失败时不触发重打标 +- 自动触发来源透传为 `AUTO_PARAM_CHANGE` ## 验证命令 ```bash -mvn -pl ccdi-project -Dtest=CcdiModelParamServiceImplTest,ProjectBankTagRebuildCoordinatorTest test +mvn -pl ccdi-project -Dtest=CcdiModelParamServiceImplTest,CcdiBankTagServiceImplTest test ``` ## 验证结果 - 结果:通过 -- `CcdiModelParamServiceImplTest` 通过 6 个用例 -- `ProjectBankTagRebuildCoordinatorTest` 通过 6 个用例 -- 总计 12 个用例全部通过 +- `CcdiModelParamServiceImplTest` 通过 8 个用例 +- `CcdiBankTagServiceImplTest` 通过 8 个用例 +- 总计 16 个用例全部通过 ## 关键结论 -- `saveAllParams` 在项目级参数实际更新成功后会调用 `submitAutoRebuild(projectId, TriggerType.AUTO_PARAM_CHANGE)` -- `submitAuto` 已改为通过 `tagRebuildExecutor` 异步提交后台执行 -- 当前实现不会为 `projectId=0` 的全局默认参数触发项目重打标 +- `saveParams` 与 `saveAllParams` 在项目级参数实际更新成功后,都会调用 `submitAutoRebuild(projectId, TriggerType.AUTO_PARAM_CHANGE)` +- `projectId=0` 的全局默认参数保存不会触发项目级重打标 +- `submitAutoRebuild` 会保持 `AUTO_PARAM_CHANGE` 触发类型透传到协调器 +- 当参数未实际更新时,不会误触发自动重打标