# 开发风险明细涉疑交易明细后端 Implementation Plan > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. > > **Repo note:** 本仓库 `AGENTS.md` 明确禁止开启 subagent,执行本计划时请在当前会话使用 `superpowers:executing-plans`。 **Goal:** 为结果总览页新增“涉疑交易明细”后端能力,支持按“全部可疑人员类型 / 名单库命中 / 模型规则命中”查询和导出,同时保证同一流水只展示一条。 **Architecture:** 以后端结果总览专用接口承载本功能,不污染现有通用流水明细查询接口。查询以 `ccdi_bank_statement` 为基表,分别聚合“规则名称包含可疑”的命中结果和“交易对手方命中中介库”的命中结果,再按 `bank_statement_id` 去重输出展示所需业务字段。由于数据库当前缺少交易对手方证件号与统一社会信用代码字段,先补齐表结构、LSFX 响应映射和本地入库链路,再落查询聚合。 **Tech Stack:** Java 21, Spring Boot 3, MyBatis XML, MyBatis Plus `Page`, Maven, JUnit 5, Mockito, MySQL 8, ExcelUtil --- ### Task 1: 补齐交易对手方身份字段入库链路 **Files:** - Create: `sql/migration/2026-03-27-ccdi-bank-statement-counterparty-identity-columns.sql` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java` - Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml` - Modify: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java` - Modify: `lsfx-mock-server/models/response.py` - Modify: `lsfx-mock-server/services/statement_service.py` - Modify: `lsfx-mock-server/services/statement_rule_samples.py` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapperXmlTest.java` - Test: `lsfx-mock-server/tests/test_statement_service.py` - [ ] **Step 1: 先写失败测试,锁定新增字段名和映射行为** ```java @Test void testFromResponse_ShouldMapCounterpartyIdentityFields() { BankStatementItem item = new BankStatementItem(); item.setCustomerCertNo("330101199001011234"); item.setCustomerSocialCreditCode("91330100123456789X"); CcdiBankStatement entity = CcdiBankStatement.fromResponse(item); assertEquals("330101199001011234", entity.getCustomerCertNo()); assertEquals("91330100123456789X", entity.getCustomerSocialCreditCode()); } ``` - [ ] **Step 2: 运行测试,确认当前实现确实缺字段** Run: ```bash mvn -pl ccdi-project -Dtest=CcdiBankStatementTest,CcdiBankStatementMapperXmlTest test ``` Expected: - `CcdiBankStatementTest` 编译失败或断言失败,提示缺少 `customerCertNo` / `customerSocialCreditCode` - `CcdiBankStatementMapperXmlTest` 尚未覆盖新列 - [ ] **Step 3: 实现最小字段补齐** ```sql ALTER TABLE `ccdi_bank_statement` ADD COLUMN `customer_cert_no` varchar(50) NULL COMMENT '交易对手方证件号' AFTER `customer_reference`, ADD COLUMN `customer_social_credit_code` varchar(50) NULL COMMENT '交易对手方统一社会信用代码' AFTER `customer_cert_no`; ``` ```java private String customerCertNo; private String customerSocialCreditCode; entity.setCustomerCertNo(item.getCustomerCertNo()); entity.setCustomerSocialCreditCode(item.getCustomerSocialCreditCode()); ``` ```xml ... customer_bank, customer_reference, customer_cert_no, customer_social_credit_code, ... #{item.customerBank}, #{item.customerReference}, #{item.customerCertNo}, #{item.customerSocialCreditCode}, ``` - [ ] **Step 4: 同步本地 LSFX mock 契约,保证联调可生成这两个字段** Run: ```bash pytest lsfx-mock-server/tests/test_statement_service.py -v ``` Expected: - mock 返回结构包含 `customerCertNo` - mock 返回结构包含 `customerSocialCreditCode` - [ ] **Step 5: 提交本任务** ```bash git add sql/migration/2026-03-27-ccdi-bank-statement-counterparty-identity-columns.sql \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java \ ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml \ ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java \ lsfx-mock-server/models/response.py \ lsfx-mock-server/services/statement_service.py \ lsfx-mock-server/services/statement_rule_samples.py \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapperXmlTest.java \ lsfx-mock-server/tests/test_statement_service.py git commit -m "补齐涉疑交易明细对手方身份字段" ``` ### Task 2: 建立结果总览涉疑交易接口契约 **Files:** - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectSuspiciousTransactionQueryDTO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectSuspiciousTransactionItemVO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectSuspiciousTransactionPageVO.java` - Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectSuspiciousTransactionExcel.java` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java` - [ ] **Step 1: 先加结构测试,锁定新 DTO、接口方法和控制器路由** ```java assertNotNull(clazz.getMethod( "getSuspiciousTransactions", Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO") )); ``` ```java assertEquals("/suspicious-transactions", getMapping.value()[0]); assertEquals(List.of("projectId", "suspiciousType", "pageNum", "pageSize"), fieldNames); ``` - [ ] **Step 2: 运行控制器与结构测试,确认当前缺少接口** Run: ```bash mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test ``` Expected: - 结构测试报 `NoSuchMethodException` - 控制器契约测试提示缺少 `/suspicious-transactions` - [ ] **Step 3: 写最小 DTO / VO / Controller / Service 签名** ```java public class CcdiProjectSuspiciousTransactionQueryDTO { private Long projectId; private String suspiciousType; private Integer pageNum; private Integer pageSize; } ``` ```java @GetMapping("/suspicious-transactions") public AjaxResult getSuspiciousTransactions(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) { return AjaxResult.success(overviewService.getSuspiciousTransactions(queryDTO)); } ``` - [ ] **Step 4: 增加导出接口契约** ```java @PostMapping("/suspicious-transactions/export") public void exportSuspiciousTransactions(HttpServletResponse response, CcdiProjectSuspiciousTransactionQueryDTO queryDTO) { ... } ``` Run: ```bash mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test ``` Expected: PASS - [ ] **Step 5: 提交本任务** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectSuspiciousTransactionQueryDTO.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectSuspiciousTransactionItemVO.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectSuspiciousTransactionPageVO.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectSuspiciousTransactionExcel.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java git commit -m "新增结果总览涉疑交易接口契约" ``` ### Task 3: 落地涉疑交易聚合 SQL 与服务逻辑 **Files:** - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java` - Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java` - Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceSuspiciousTransactionTest.java` - [ ] **Step 1: 先写 SQL 结构测试和服务测试,锁定去重与筛选口径** ```java assertTrue(suspiciousSql.contains("rule_name like '%可疑%'")); assertTrue(suspiciousSql.contains("ccdi_biz_intermediary")); assertTrue(suspiciousSql.contains("ccdi_enterprise_base_info")); assertTrue(suspiciousSql.contains("group by merged.bankStatementId")); ``` ```java assertEquals(1, result.getRows().size()); assertTrue(result.getRows().getFirst().getHasModelRuleHit()); assertTrue(result.getRows().getFirst().getHasNameListHit()); ``` - [ ] **Step 2: 运行测试,确认当前尚无聚合查询实现** Run: ```bash mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceSuspiciousTransactionTest test ``` Expected: - SQL 测试找不到 `selectSuspiciousTransactionPage` - 服务测试编译失败或返回空实现 - [ ] **Step 3: 在 Mapper XML 中实现“三层聚合 + 按流水去重”** ```xml ``` - [ ] **Step 4: 在 Service 中补查询类型标准化、项目存在性校验和分页返回** ```java if (queryDTO.getSuspiciousType() == null || queryDTO.getSuspiciousType().isBlank()) { queryDTO.setSuspiciousType("ALL"); } queryDTO.setSuspiciousType(queryDTO.getSuspiciousType().trim().toUpperCase()); ``` Run: ```bash mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceSuspiciousTransactionTest,CcdiProjectOverviewServiceImplTest test ``` Expected: PASS - [ ] **Step 5: 提交本任务** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java \ ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceSuspiciousTransactionTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java git commit -m "实现结果总览涉疑交易聚合查询" ``` ### Task 4: 接通导出与回归验证 **Files:** - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java` - Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java` - Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java` - [ ] **Step 1: 先补导出控制器测试** ```java Method method = CcdiProjectOverviewController.class.getMethod( "exportSuspiciousTransactions", HttpServletResponse.class, CcdiProjectSuspiciousTransactionQueryDTO.class ); assertEquals("/suspicious-transactions/export", postMapping.value()[0]); ``` - [ ] **Step 2: 运行导出相关测试,确认导出接口尚未闭环** Run: ```bash mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest test ``` Expected: FAIL,提示导出接口缺失或未调用服务 - [ ] **Step 3: 实现导出列表查询与 Excel 输出** ```java List rows = overviewService.exportSuspiciousTransactions(queryDTO); ExcelUtil util = new ExcelUtil<>(CcdiProjectSuspiciousTransactionExcel.class); util.exportExcel(response, rows, "涉疑交易明细"); ``` - [ ] **Step 4: 运行后端回归测试** Run: ```bash mvn -pl ccdi-project -Dtest=CcdiBankStatementTest,CcdiBankStatementMapperXmlTest,CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewServiceSuspiciousTransactionTest test ``` Expected: PASS - [ ] **Step 5: 提交本任务** ```bash git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java \ ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java \ ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java git commit -m "接通结果总览涉疑交易导出能力" ```