新增开发风险明细涉疑交易明细实施计划

This commit is contained in:
wkc
2026-03-27 16:33:10 +08:00
parent dc578762b3
commit ed1d07ad05
3 changed files with 680 additions and 0 deletions

View File

@@ -0,0 +1,347 @@
# 开发风险明细涉疑交易明细后端 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
<result property="customerCertNo" column="customer_cert_no" />
<result property="customerSocialCreditCode" column="customer_social_credit_code" />
...
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
<select id="selectSuspiciousTransactionPage" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO">
SELECT merged.bankStatementId,
MAX(merged.trxDate) AS trxDate,
MAX(merged.suspiciousPersonName) AS suspiciousPersonName,
MAX(merged.relatedPersonName) AS relatedPersonName,
MAX(merged.relatedStaffName) AS relatedStaffName,
MAX(merged.relatedStaffCode) AS relatedStaffCode,
MAX(merged.relationType) AS relationType,
MAX(merged.userMemo) AS userMemo,
MAX(merged.cashType) AS cashType,
MAX(merged.displayAmount) AS displayAmount,
MAX(merged.hasModelRuleHit) AS hasModelRuleHit,
MAX(merged.hasNameListHit) AS hasNameListHit
FROM (...)
GROUP BY merged.bankStatementId
</select>
```
- [ ] **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<CcdiProjectSuspiciousTransactionExcel> rows = overviewService.exportSuspiciousTransactions(queryDTO);
ExcelUtil<CcdiProjectSuspiciousTransactionExcel> 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 "接通结果总览涉疑交易导出能力"
```

View File

@@ -0,0 +1,302 @@
# 开发风险明细涉疑交易明细前端 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:** 前端继续以 `PreliminaryCheck.vue` 作为结果总览入口,通过 `projectOverview.js` 新增结果总览涉疑交易接口封装,在父组件加载后将数据注入 `RiskDetailSection.vue``RiskDetailSection.vue` 负责呈现筛选下拉、真实业务列、详情弹窗与导出按钮;详情接口直接复用 `ccdiProjectBankStatement.js` 中已有的 `getBankStatementDetail`
**Tech Stack:** Vue 2, Element UI, RuoYi 全局 `download`, Node `assert` 单测, axios request 封装
---
### Task 1: 新增结果总览涉疑交易前端 API 契约
**Files:**
- Modify: `ruoyi-ui/src/api/ccdi/projectOverview.js`
- Test: `ruoyi-ui/tests/unit/project-overview-api.test.js`
- [ ] **Step 1: 先补 API 契约测试,锁定新接口名和参数透传**
```js
[
"getOverviewSuspiciousTransactions",
"/ccdi/project/overview/suspicious-transactions",
"projectId: params.projectId",
"suspiciousType: params.suspiciousType",
"pageNum: params.pageNum",
"pageSize: params.pageSize",
].forEach((token) => assert(source.includes(token), token));
```
- [ ] **Step 2: 运行测试,确认新接口尚未接入**
Run:
```bash
node ruoyi-ui/tests/unit/project-overview-api.test.js
```
Expected: FAIL提示缺少 `getOverviewSuspiciousTransactions`
- [ ] **Step 3: 写最小 API 封装**
```js
export function getOverviewSuspiciousTransactions(params) {
return request({
url: "/ccdi/project/overview/suspicious-transactions",
method: "get",
params: {
projectId: params.projectId,
suspiciousType: params.suspiciousType,
pageNum: params.pageNum,
pageSize: params.pageSize,
},
});
}
```
- [ ] **Step 4: 回跑 API 契约测试**
Run:
```bash
node ruoyi-ui/tests/unit/project-overview-api.test.js
```
Expected: PASS
- [ ] **Step 5: 提交本任务**
```bash
git add ruoyi-ui/src/api/ccdi/projectOverview.js \
ruoyi-ui/tests/unit/project-overview-api.test.js
git commit -m "新增结果总览涉疑交易前端接口"
```
### Task 2: 把结果总览父组件接到真实涉疑交易数据
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js`
- Test: `ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js`
- Create: `ruoyi-ui/tests/unit/preliminary-check-suspicious-transaction-load.test.js`
- [ ] **Step 1: 先写失败测试,锁定父组件加载新接口和风险明细数据结构**
```js
assert(source.includes("getOverviewSuspiciousTransactions"), "结果总览应加载涉疑交易接口");
assert(source.includes("riskDetails"), "结果总览应继续向 RiskDetailSection 注入 riskDetails");
assert(source.includes("suspiciousTransactionList"), "mock 归一化应保留涉疑交易列表");
```
- [ ] **Step 2: 运行测试,确认当前仍是 mock 空数组**
Run:
```bash
node ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js
node ruoyi-ui/tests/unit/preliminary-check-suspicious-transaction-load.test.js
```
Expected:
- 旧测试仍只认“涉险交易明细”
- 新测试提示未加载涉疑交易接口
- [ ] **Step 3: 在父组件中并行加载涉疑交易接口,并改造 mock 归一化函数**
```js
const [dashboardRes, riskPeopleRes, riskModelCardsRes, suspiciousRes] = await Promise.all([
getOverviewDashboard(this.projectId),
getOverviewRiskPeople(this.projectId),
getOverviewRiskModelCards(this.projectId),
getOverviewSuspiciousTransactions({
projectId: this.projectId,
suspiciousType: "ALL",
pageNum: 1,
pageSize: 10,
}),
]);
```
```js
riskDetails: {
suspiciousTransactionList: normalizeSuspiciousTransactions(suspiciousData && suspiciousData.rows),
suspiciousType: "ALL",
total: suspiciousData && suspiciousData.total ? suspiciousData.total : 0,
abnormalAccountList: [],
}
```
- [ ] **Step 4: 回跑父组件相关测试**
Run:
```bash
node ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js
node ruoyi-ui/tests/unit/preliminary-check-suspicious-transaction-load.test.js
```
Expected: PASS
- [ ] **Step 5: 提交本任务**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue \
ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js \
ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js \
ruoyi-ui/tests/unit/preliminary-check-suspicious-transaction-load.test.js
git commit -m "接通结果总览涉疑交易数据加载"
```
### Task 3: 重写 RiskDetailSection 的涉疑交易表格列和筛选交互
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue`
- Test: `ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js`
- Create: `ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js`
- [ ] **Step 1: 先写失败测试,锁定下拉项、业务列和新标题**
```js
[
"涉疑交易明细",
"全部可疑人员类型",
"名单库命中",
"模型规则命中",
"可疑人员",
"关联人",
"关联员工",
"关系",
"摘要/交易类型",
].forEach((token) => assert(source.includes(token), token));
```
- [ ] **Step 2: 运行测试,确认当前仍是旧占位表格**
Run:
```bash
node ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js
```
Expected: FAIL提示仍存在“涉险交易明细 / 对手方 / 方向 / 账号”
- [ ] **Step 3: 写最小 UI 改造**
```vue
<el-dropdown @command="handleSuspiciousTypeChange">
<span class="el-dropdown-link">{{ currentSuspiciousTypeLabel }}</span>
</el-dropdown>
<el-table-column prop="trxDate" label="交易时间" />
<el-table-column prop="suspiciousPersonName" label="可疑人员" />
<el-table-column prop="relatedPersonName" label="关联人" />
<el-table-column label="关联员工">
<template slot-scope="scope">{{ formatRelatedStaff(scope.row) }}</template>
</el-table-column>
```
- [ ] **Step 4: 回跑布局测试**
Run:
```bash
node ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js
```
Expected: PASS
- [ ] **Step 5: 提交本任务**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue \
ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js \
ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js
git commit -m "改造结果总览涉疑交易表格结构"
```
### Task 4: 复用流水详情弹窗与导出能力,并做前端回归
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue`
- Modify: `ruoyi-ui/src/api/ccdi/projectOverview.js`
- Reuse: `ruoyi-ui/src/api/ccdiProjectBankStatement.js`
- Test: `ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js`
- Create: `ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-detail-dialog.test.js`
- [ ] **Step 1: 先写失败测试,锁定详情复用与导出路径**
```js
[
"getBankStatementDetail",
"detailVisible",
"handleViewDetail",
"this.download(",
"ccdi/project/overview/suspicious-transactions/export",
].forEach((token) => assert(source.includes(token), token));
```
- [ ] **Step 2: 运行测试,确认详情与导出还未接通**
Run:
```bash
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-detail-dialog.test.js
```
Expected: FAIL提示未复用 `getBankStatementDetail` 或未指向新导出路径
- [ ] **Step 3: 在 RiskDetailSection 中复用 DetailQuery 的详情弹窗结构和金额/标签格式化**
```js
import { getBankStatementDetail } from "@/api/ccdiProjectBankStatement";
async handleViewDetail(row) {
const response = await getBankStatementDetail(row.bankStatementId);
this.detailData = normalizeDetailData(response.data);
this.detailVisible = true;
}
handleExport() {
this.download(
"ccdi/project/overview/suspicious-transactions/export",
{ projectId: this.projectId, suspiciousType: this.currentSuspiciousType },
`涉疑交易明细_${new Date().getTime()}.xlsx`
);
}
```
- [ ] **Step 4: 跑前端回归检查**
Run:
```bash
node ruoyi-ui/tests/unit/project-overview-api.test.js
node ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js
node ruoyi-ui/tests/unit/preliminary-check-suspicious-transaction-load.test.js
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js
node ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-detail-dialog.test.js
cd ruoyi-ui && npm run build:prod
```
Expected:
- 所有 Node 断言测试 PASS
- `npm run build:prod` 成功产出构建结果
- [ ] **Step 5: 提交本任务**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue \
ruoyi-ui/src/api/ccdi/projectOverview.js \
ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js \
ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-detail-dialog.test.js
git commit -m "接通结果总览涉疑交易详情与导出"
```

View File

@@ -0,0 +1,31 @@
# 开发风险明细涉疑交易明细实施计划记录
## 本次改动
- 新增后端实施计划:
- `docs/plans/backend/2026-03-27-development-risk-suspicious-transaction-detail-backend-implementation.md`
- 新增前端实施计划:
- `docs/plans/frontend/2026-03-27-development-risk-suspicious-transaction-detail-frontend-implementation.md`
## 计划拆分结果
- 后端计划聚焦三件事:
- 补齐交易对手方证件号与统一社会信用代码的入库链路
- 新增结果总览涉疑交易列表与导出接口
- 实现“规则命中 + 名单库命中”聚合 SQL 与回归测试
- 前端计划聚焦三件事:
- 新增结果总览涉疑交易接口封装
-`PreliminaryCheck.vue` 从 mock 风险明细切换到真实加载
- 改造 `RiskDetailSection.vue`,复用流水详情弹窗并接通导出
## 文档落点检查
- 后端计划已放入:`docs/plans/backend/`
- 前端计划已放入:`docs/plans/frontend/`
- 本次计划记录已放入:`docs/reports/implementation/`
## 说明
- 依据仓库 `AGENTS.md` 约束,本轮未启用 subagent 审核流程
- 下一步可直接按计划在当前会话使用执行型流程落地