# Project Bank Statement Tagging Backend Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 为项目流水新增自动打标与手动重算后端能力,支持批量上传和拉取本行信息两条链路自动触发,支持项目级互斥重算与规则级并行执行。 **Architecture:** 在 `ccdi-project` 中新增标签规则表、结果表、任务表及对应 Mapper;引入项目级重算协调器和规则级线程池;在 `CcdiFileUploadServiceImpl` 的批量上传与拉取本行信息收尾阶段统一申请项目级标签重算;通过独立的标签服务读取规则元数据、参数配置,调度 Mapper XML 中的规则 SQL 并写入结果表。 **Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus, MyBatis XML, JUnit 5, Mockito, Maven --- ### Task 1: 定义手动重算接口契约 **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankTagRebuildDTO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankTagService.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java` **Step 1: Write the failing test** 新增控制器测试,验证接口会把 `projectId + modelCode` 透传到 Service: ```java @Test void rebuild_shouldDelegateProjectAndModelCode() { CcdiBankTagRebuildDTO dto = new CcdiBankTagRebuildDTO(); dto.setProjectId(40L); dto.setModelCode("LARGE_TRANSACTION"); when(bankTagService.submitRebuild(dto, "admin")).thenReturn("标签重算任务已提交"); try (MockedStatic mocked = mockStatic(SecurityUtils.class)) { mocked.when(SecurityUtils::getUsername).thenReturn("admin"); AjaxResult result = controller.rebuild(dto); assertEquals(200, result.get("code")); verify(bankTagService).submitRebuild(dto, "admin"); } } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagControllerTest#rebuild_shouldDelegateProjectAndModelCode ``` Expected: - `FAIL` - 原因是 DTO、Controller 或 Service 契约尚不存在 **Step 3: Write minimal implementation** 补最小接口: ```java public interface ICcdiBankTagService { String submitRebuild(CcdiBankTagRebuildDTO dto, String operator); } ``` ```java @PostMapping("/rebuild") public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) { String operator = SecurityUtils.getUsername(); return AjaxResult.success(bankTagService.submitRebuild(dto, operator)); } ``` **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagControllerTest#rebuild_shouldDelegateProjectAndModelCode ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankTagRebuildDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankTagService.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java git commit -m "test: 补充流水标签重算接口契约" ``` ### Task 2: 新增标签核心表结构与实体映射 **Files:** - Create: `sql/2026-03-16-bank-tagging.sql` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagRule.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagResult.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagTask.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagEntityMappingTest.java` **Step 1: Write the failing test** 新增一个轻量测试,校验结果实体的关键字段存在: ```java @Test void bankTagResult_shouldExposeStatementAndObjectFields() { CcdiBankTagResult result = new CcdiBankTagResult(); result.setProjectId(40L); result.setRuleCode("RULE_1"); result.setBankStatementId(10L); result.setGroupId(40); result.setLogId(40001); result.setObjectType("STAFF_ID_CARD"); result.setObjectKey("330101198801010011"); assertEquals(40L, result.getProjectId()); assertEquals(40001, result.getLogId()); assertEquals("STAFF_ID_CARD", result.getObjectType()); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagEntityMappingTest#bankTagResult_shouldExposeStatementAndObjectFields ``` Expected: - `FAIL` - 原因是新实体尚不存在 **Step 3: Write minimal implementation** - 新建三张表的 SQL 脚本 - 新建三个实体类,字段只覆盖第一版设计所需字段 - 规则表初始化“大额交易” 8 条规则元数据 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagEntityMappingTest#bankTagResult_shouldExposeStatementAndObjectFields ``` Expected: - `PASS` **Step 5: Commit** ```bash git add sql/2026-03-16-bank-tagging.sql ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagRule.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagResult.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagTask.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankTagEntityMappingTest.java git commit -m "feat: 新增流水标签核心表结构与实体映射" ``` ### Task 3: 建立规则元数据与结果表 Mapper **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagRuleMapper.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagResultMapper.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagTaskMapper.java` - Create: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagRuleMapper.xml` - Create: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagResultMapper.xml` - Create: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagTaskMapper.xml` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagResultMapperXmlTest.java` **Step 1: Write the failing test** 新增 XML 渲染测试,校验结果表支持按项目或项目+模型删除: ```java @Test void deleteByProjectAndOptionalModel_shouldRenderScopedDelete() throws Exception { String xml = readXml("mapper/ccdi/project/CcdiBankTagResultMapper.xml"); assertTrue(xml.contains("delete from ccdi_bank_statement_tag_result")); assertTrue(xml.contains("project_id = #{projectId}")); assertTrue(xml.contains("model_code = #{modelCode}")); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagResultMapperXmlTest#deleteByProjectAndOptionalModel_shouldRenderScopedDelete ``` Expected: - `FAIL` - 原因是 Mapper XML 尚不存在 **Step 3: Write minimal implementation** - 规则表 Mapper 提供启用规则查询 - 结果表 Mapper 提供批量插入和按范围删除 - 任务表 Mapper 提供创建任务、更新任务、查询运行中任务 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagResultMapperXmlTest#deleteByProjectAndOptionalModel_shouldRenderScopedDelete ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagRuleMapper.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagResultMapper.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagTaskMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagRuleMapper.xml ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagResultMapper.xml ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagTaskMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagResultMapperXmlTest.java git commit -m "feat: 新增流水标签规则结果任务Mapper" ``` ### Task 4: 为规则 SQL 定义统一返回 VO **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java` - Create: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java` **Step 1: Write the failing test** 新增 XML 测试,先校验流水级命中 SQL 会返回 `group_id` 和 `log_id`: ```java @Test void statementRuleSql_shouldSelectGroupIdAndLogId() throws Exception { String xml = readXml("mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml"); assertTrue(xml.contains("AS groupId")); assertTrue(xml.contains("AS logId")); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest#statementRuleSql_shouldSelectGroupIdAndLogId ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 先定义统一 VO 和一个最小的 XML 框架,确保: - 流水级规则 SQL 返回 `bankStatementId`、`groupId`、`logId`、`reasonDetail` - 对象级规则 SQL 返回 `objectType`、`objectKey`、`reasonDetail` **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest#statementRuleSql_shouldSelectGroupIdAndLogId ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagStatementHitVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagObjectHitVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java git commit -m "feat: 定义流水标签规则命中返回结构" ``` ### Task 5: 先实现一条流水级规则的失败测试与最小通过 **Files:** - Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java` **Step 1: Write the failing test** 以“房车消费支出交易”为第一条规则,测试 XML 中存在 `selectHouseOrCarExpenseStatements`: ```java @Test void houseOrCarExpenseRule_shouldJoinBankStatementAndReturnStatementHitFields() throws Exception { String xml = readXml("mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml"); assertTrue(xml.contains("selectHouseOrCarExpenseStatements")); assertTrue(xml.contains("bs.bank_statement_id AS bankStatementId")); assertTrue(xml.contains("bs.group_id AS groupId")); assertTrue(xml.contains("bs.batch_id AS logId")); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest#houseOrCarExpenseRule_shouldJoinBankStatementAndReturnStatementHitFields ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 在 XML 中补第一条规则 SQL,并返回固定原因摘要。 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest#houseOrCarExpenseRule_shouldJoinBankStatementAndReturnStatementHitFields ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java git commit -m "test: 打通首条流水标签规则SQL" ``` ### Task 6: 完成剩余 7 条规则 SQL **Files:** - Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java` **Step 1: Write the failing tests** 为剩余规则补 XML 存在性断言,分别覆盖: - `selectTaxExpenseStatements` - `selectSingleLargeIncomeStatements` - `selectCumulativeIncomeObjects` - `selectAnnualTurnoverObjects` - `selectLargeCashDepositStatements` - `selectFrequentCashDepositObjects` - `selectLargeTransferStatements` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 补齐 7 条规则 SQL,要求: - 阈值类规则使用入参,不在 XML 中写死参数值 - 流水级规则返回 `bankStatementId/groupId/logId/reasonDetail` - 对象级规则返回 `objectType/objectKey/reasonDetail` **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java git commit -m "feat: 补齐大额交易全部标签规则SQL" ``` ### Task 7: 实现项目参数读取与规则注册表 **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagRuleExecutionConfig.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java` **Step 1: Write the failing test** 新增测试,验证 resolver 会根据项目读取有效参数,并为规则产出执行配置: ```java @Test void resolve_shouldReadEffectiveProjectParamsForThresholdRules() { BankTagRuleExecutionConfig config = resolver.resolve(40L, ruleMeta); assertEquals("1111", config.getThresholdValue("SINGLE_TRANSACTION_AMOUNT")); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=BankTagRuleConfigResolverTest#resolve_shouldReadEffectiveProjectParamsForThresholdRules ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 实现 resolver: - 读取项目 `configType` - 选择有效 `projectId` - 读取模型参数 - 按 `ruleCode` 组装执行配置 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=BankTagRuleConfigResolverTest#resolve_shouldReadEffectiveProjectParamsForThresholdRules ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/BankTagRuleExecutionConfig.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java git commit -m "feat: 新增流水标签规则执行参数解析器" ``` ### Task 8: 实现规则级并行执行服务 **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/config/BankTagThreadPoolConfig.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java` **Step 1: Write the failing test** 新增服务测试,验证项目级任务会先删旧结果,再并行执行规则并汇总命中数: ```java @Test void rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks() { service.rebuildProject(40L, null, "admin", TriggerType.MANUAL); verify(resultMapper).deleteByProjectAndModel(40L, null); verify(resultMapper).insertBatch(anyList()); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagServiceImplTest#rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 实现 `CcdiBankTagServiceImpl`: - 查询启用规则 - 删旧结果 - 使用 `tagRuleExecutor` 并行提交每条规则 - 汇总命中结果 - 更新任务状态 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagServiceImplTest#rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/config/BankTagThreadPoolConfig.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java git commit -m "feat: 实现规则级并行流水标签重算服务" ``` ### Task 9: 实现同项目互斥与自动补跑协调器 **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java` **Step 1: Write the failing test** 新增测试,验证同一项目运行中时: - 手动触发会被拒绝 - 自动触发会标记 `need_rerun` ```java @Test void submitManualRebuild_shouldRejectWhenProjectAlreadyRunning() { assertThrows(ServiceException.class, () -> coordinator.submitManual(40L, null, "admin")); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=ProjectBankTagRebuildCoordinatorTest ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 实现协调器: - 使用 `projectId` 级别互斥 - 自动触发遇到运行中任务时打 `need_rerun` - 当前任务结束后根据 `need_rerun` 触发补跑 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=ProjectBankTagRebuildCoordinatorTest ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java git commit -m "feat: 新增项目级流水标签重算互斥与补跑协调器" ``` ### Task 10: 接入批量上传收尾自动触发 **Files:** - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java` **Step 1: Write the failing test** 新增测试,验证批量上传所有文件完成后会申请项目级重算: ```java @Test void batchUploadCompletion_shouldSubmitProjectTagRebuildWhenAnyFileSucceeded() { verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_BATCH_UPLOAD); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiFileUploadServiceImplTest#batchUploadCompletion_shouldSubmitProjectTagRebuildWhenAnyFileSucceeded ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 重构 `submitTasksAsync`: - 收集每个文件任务的 `CompletableFuture` - `allOf(...).whenComplete(...)` 收尾 - 至少一个文件成功落库时申请自动重算 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiFileUploadServiceImplTest#batchUploadCompletion_shouldSubmitProjectTagRebuildWhenAnyFileSucceeded ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java git commit -m "feat: 接入批量上传完成后的自动流水打标" ``` ### Task 11: 接入拉取本行信息收尾自动触发 **Files:** - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java` **Step 1: Write the failing test** 新增测试,验证拉取本行信息全部任务完成后也会申请项目级重算: ```java @Test void pullBankInfoCompletion_shouldSubmitProjectTagRebuildWhenAnyTaskSucceeded() { verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_PULL_BANK_INFO); } ``` **Step 2: Run test to verify it fails** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiFileUploadServiceImplTest#pullBankInfoCompletion_shouldSubmitProjectTagRebuildWhenAnyTaskSucceeded ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 仿照批量上传链路改造 `submitPullBankInfoTasks`,在全部任务结束后申请一次自动重算。 **Step 4: Run test to verify it passes** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiFileUploadServiceImplTest#pullBankInfoCompletion_shouldSubmitProjectTagRebuildWhenAnyTaskSucceeded ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java git commit -m "feat: 接入拉取本行信息完成后的自动流水打标" ``` ### Task 12: 完成全量验证 **Files:** - Modify: `docs/plans/2026-03-16-project-bank-statement-tagging-backend-implementation.md` **Step 1: Run focused backend tests** Run: ```bash mvn test -pl ccdi-project -am -Dtest=CcdiBankTagControllerTest,CcdiBankTagEntityMappingTest,CcdiBankTagResultMapperXmlTest,CcdiBankTagAnalysisMapperXmlTest,BankTagRuleConfigResolverTest,CcdiBankTagServiceImplTest,ProjectBankTagRebuildCoordinatorTest,CcdiFileUploadServiceImplTest ``` Expected: - 所有新增测试通过 **Step 2: Run module compile** Run: ```bash mvn clean compile -pl ccdi-project -am ``` Expected: - `BUILD SUCCESS` **Step 3: Update verification notes** 在本实施计划末尾补充实际执行结果摘要和任何遗留风险。 **Step 4: Commit** ```bash git add ccdi-project sql docs/plans/2026-03-16-project-bank-statement-tagging-backend-implementation.md git commit -m "feat: 完成项目流水标签后端实现" ``` --- ## 实际执行结果 ### 已完成范围 - 已新增手动重算接口、标签规则/结果/任务三张核心表及实体映射 - 已新增规则、结果、任务、分析四类 Mapper 与 XML - 已完成“大额交易” 8 条规则 SQL 的首版落地 - 已完成规则参数解析器、规则级线程池、规则级并行重算服务 - 已完成项目级互斥协调器与自动触发补跑标记逻辑 - 已接入批量上传与拉取本行信息两条链路的批次收尾自动触发 ### 实际验证命令 在 `ccdi-project` 模块目录执行: ```bash mvn test "-Dtest=CcdiBankTagControllerTest,CcdiBankTagEntityMappingTest,CcdiBankTagResultMapperXmlTest,CcdiBankTagAnalysisMapperXmlTest,BankTagRuleConfigResolverTest,CcdiBankTagServiceImplTest,ProjectBankTagRebuildCoordinatorTest,CcdiFileUploadServiceImplTest" mvn clean compile ``` ### 实际验证结果 - 聚焦测试集:`BUILD SUCCESS` - `ccdi-project` 模块编译:`BUILD SUCCESS` - 聚焦测试共执行 30 个测试,0 失败,0 错误 ### 遗留风险 - 当前规则 SQL 已按设计稿和现有表结构落地,但尚未补集成测试验证真实数据库数据命中情况 - 自动补跑已支持 `need_rerun` 标记与串行补跑,后续建议增加更完整的并发场景回归测试 - 当前实施仅完成后端能力,结果查询接口与前端展示仍未接入