diff --git a/docs/design/2026-03-18-bank-statement-hit-tags-design.md b/docs/design/2026-03-18-bank-statement-hit-tags-design.md new file mode 100644 index 00000000..fa21cc1a --- /dev/null +++ b/docs/design/2026-03-18-bank-statement-hit-tags-design.md @@ -0,0 +1,335 @@ +# 流水明细异常标签展示设计 + +## 背景 + +当前项目流水标签能力已经会把规则命中结果写入 `ccdi_bank_statement_tag_result`,结果中保留了: + +- `rule_name` +- `risk_level` +- `reason_detail` +- `result_type` +- `bank_statement_id` + +但现有“流水明细查询”页面和导出能力仍只读取 `ccdi_bank_statement`,列表、详情、导出文件都看不到当前流水命中的异常标签,用户无法直接在查询结果中判断一条流水为何被命中。 + +本次需求要求在“流水明细查询”页面的流水列表、流水详情和导出文件中,展示当前流水直接命中的异常标签信息。 + +## 目标 + +- 在流水列表中展示当前流水命中的异常标签名称。 +- 在流水详情中展示当前流水命中的异常标签名称、风险等级、命中原因摘要。 +- 在导出文件中追加“异常标签”“命中原因摘要”两列。 +- 页面、详情、导出的标签口径保持一致。 + +## 范围 + +### In Scope + +- `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` +- `ruoyi-ui/src/api/ccdiProjectBankStatement.js` +- `ccdi-project` 现有流水查询 Controller / Service / Mapper +- `ccdi_bank_statement_tag_result` 的只读查询与组装 +- 流水明细导出列扩展 + +### Out of Scope + +- 新增按异常标签筛选、排序、页签统计 +- 对象级标签结果展示 +- 标签重算逻辑、规则 SQL、任务调度逻辑调整 +- 新增独立标签页面 + +## 已确认口径 + +- 列表页只展示标签名称。 +- 详情页展示标签名称、风险等级、命中原因摘要。 +- 仅展示当前流水直接命中的流水级标签。 +- 不把对象级标签混入流水列表、流水详情和导出。 +- 导出时需要同时导出异常标签与命中原因摘要。 + +## 方案对比 + +### 方案一:服务层二次组装标签 + +- 列表先按现有分页逻辑查询流水。 +- 再按当前页 `bankStatementId` 批量查询标签结果,并回填到列表 VO。 +- 详情继续走原有详情查询,再单独补充当前流水标签明细。 +- 导出先查导出范围内流水,再批量查询标签结果并拼装导出列。 + +优点: + +- 改动集中在现有查询服务层,最短路径实现。 +- 标签查询与主分页 SQL 解耦,便于维护和测试。 +- 详情可返回结构化标签数据,适合前端展示。 +- 列表、详情、导出口径容易统一。 + +缺点: + +- 列表和导出阶段会增加一次标签批量查询。 + +### 方案二:在 Mapper 主查询里直接聚合标签 + +- 在 `CcdiBankStatementMapper.xml` 主查询中左连接标签表,并聚合标签字段。 + +优点: + +- 接口层看起来较简单。 + +缺点: + +- 分页 SQL、详情 SQL、导出 SQL 都会显著变复杂。 +- 列表适合聚合字符串,不适合详情返回结构化标签。 +- 后续维护成本高。 + +### 方案三:新增独立标签接口,由前端二次拉取 + +- 列表接口仍只查流水。 +- 前端再调一个“按流水 ID 查询标签”的接口做二次组装。 + +优点: + +- 流水主查询改动少。 + +缺点: + +- 前端需要承担拼装逻辑。 +- 导出仍需后端再实现一次同口径查询,容易分叉。 + +## 选型 + +采用方案一:服务层二次组装标签。 + +该方案最符合当前仓库“在既有模块上做局部扩展”的实现方式,不需要引入新的查询页面或复杂聚合 SQL,也能保证详情展示与导出口径一致。 + +## 设计原则 + +- 标签展示仅来自 `ccdi_bank_statement_tag_result` 中的流水级结果。 +- 列表轻量展示,详情完整展示。 +- 导出结果与页面口径一致,不额外创造新的标签解释规则。 +- 不为本次需求扩展筛选、排序和统计能力。 +- 主功能优先,标签展示失败不能拖垮流水列表查询。 + +## 数据口径设计 + +标签查询统一限定以下条件: + +- `result_type = 'STATEMENT'` +- `bank_statement_id` 命中当前流水 + +标签结果读取字段: + +- `rule_name` +- `risk_level` +- `reason_detail` +- `bank_statement_id` +- `rule_code` + +其中: + +- `rule_name` 用于列表、详情、导出展示 +- `risk_level` 仅用于详情展示和前端标签样式映射 +- `reason_detail` 用于详情展示和导出 + +对象级结果即使与当前流水所属身份证、账户或对象相关,也不参与本次页面与导出口径。 + +## 后端设计 + +### 一、VO 与导出模型扩展 + +建议新增一个统一标签明细 VO,例如: + +- `CcdiBankStatementHitTagVO` + - `ruleName` + - `riskLevel` + - `reasonDetail` + +现有 VO 调整: + +- `CcdiBankStatementListVO` + - 新增 `hitTags` +- `CcdiBankStatementDetailVO` + - 新增 `hitTags` +- `CcdiBankStatementExcel` + - 新增 `hitTagNames` + - 新增 `hitTagReasons` + +列表 VO 中虽然只展示标签名称,但仍保留结构化 `hitTags`,这样可以减少后续再次改模型的成本,并让列表、详情、导出共享同一组装逻辑。 + +### 二、Mapper 查询设计 + +保留现有流水查询 Mapper 不做大改,只新增标签结果只读查询能力,建议放在 `CcdiBankTagResultMapper`: + +1. 按流水 ID 集合批量查询标签结果 +- 入参:`List bankStatementIds` +- 条件:`result_type = 'STATEMENT'` +- 返回:标签明细列表 + +2. 按单个流水 ID 查询标签结果 +- 入参:`Long bankStatementId` +- 条件:`result_type = 'STATEMENT'` +- 返回:标签明细列表 + +排序建议统一按以下顺序之一稳定输出: + +- `risk_level` + `rule_code` + +这样同一流水的标签顺序在列表、详情、导出中保持一致。 + +### 三、Service 组装设计 + +#### 1. 列表查询 + +`selectStatementPage()` 调整为两阶段: + +1. 调用现有 Mapper 查询分页流水。 +2. 收集当前页全部 `bankStatementId`。 +3. 批量查询这些流水的标签结果。 +4. 按 `bankStatementId` 分组后回填到每条 `CcdiBankStatementListVO.hitTags`。 + +列表无标签时返回空集合,不返回 `null`,减少前端判空分支。 + +#### 2. 详情查询 + +`getStatementDetail()` 调整为: + +1. 查询原有流水详情。 +2. 按当前 `bankStatementId` 查询标签结果。 +3. 回填到 `CcdiBankStatementDetailVO.hitTags`。 + +#### 3. 导出查询 + +`selectStatementListForExport()` 调整为: + +1. 查询导出范围内全部流水。 +2. 批量查询这些流水的标签结果。 +3. 按流水 ID 分组。 +4. 组装到 `CcdiBankStatementExcel`: + - `异常标签`:按标签名称拼接 + - `命中原因摘要`:按相同顺序拼接 + +拼接分隔符建议统一使用全角分号 `;`,避免在 Excel 中与金额千分位或英文逗号混淆。 + +### 四、Controller 设计 + +不新增接口,继续复用: + +- `GET /ccdi/project/bank-statement/list` +- `GET /ccdi/project/bank-statement/detail/{bankStatementId}` +- `POST /ccdi/project/bank-statement/export` + +这样不会影响现有菜单入口和前端 API 结构。 + +## 前端设计 + +### 一、列表展示 + +在 `DetailQuery.vue` 的表格中新增“异常标签”列,位置放在“摘要 / 交易类型”和“交易金额”之间。 + +展示规则: + +- 有标签:逐个渲染轻量标签组件,仅显示 `ruleName` +- 无标签:显示 `-` +- 单条流水命中多个标签:同列换行或折行展示 + +样式规则: + +- 沿用 Element UI `el-tag` 轻量视觉 +- 可按 `riskLevel` 映射 `type`,但列表不展示风险等级文案 +- 不新增 tooltip、展开收起、二级详情等扩展交互 + +### 二、详情展示 + +在现有详情弹窗的基础字段区下方新增“命中异常标签”模块。 + +展示规则: + +- 无标签:显示“当前流水未命中异常标签” +- 有标签:按条展示 + - 标签名称 + - 风险等级 + - 命中原因摘要 + +详情模块使用纵向结构,优先保证原因摘要可读性,不把多个原因压成单个文本段。 + +### 三、导出展示 + +导出 Excel 在现有列后追加: + +- `异常标签` +- `命中原因摘要` + +导出示例: + +- `异常标签`:`房车消费支出交易;大额转账交易` +- `命中原因摘要`:`摘要命中购买房产首付款;转账金额 200000.00 元超过阈值` + +## 错误处理 + +### 列表与详情 + +- 流水主查询成功、标签补充查询失败时,不让整个列表或详情失败。 +- 列表标签列回退为空展示。 +- 详情标签模块回退为无数据展示。 +- 后端记录错误日志,便于排查标签结果查询异常。 + +### 导出 + +- 导出属于结果交付场景。 +- 若标签结果查询或拼装失败,导出整体失败并返回错误。 +- 不允许静默导出缺少标签列内容的文件,避免形成误导。 + +## 性能考虑 + +- 列表页只对当前页流水做一次批量标签查询,禁止逐条单查。 +- 导出阶段对导出结果范围做一次批量标签查询,再在内存中按流水 ID 分组。 +- 不把标签表聚合逻辑直接并入主分页 SQL,避免影响现有分页查询稳定性。 + +## 测试设计 + +### 后端单测 + +- 列表查询:无标签、单标签、多标签三种场景 +- 详情查询:返回结构化标签明细 +- 导出查询:正确拼装“异常标签”“命中原因摘要” +- 仅返回 `STATEMENT` 标签,不混入对象级结果 + +### Mapper / SQL 测试 + +- 批量标签查询能按 `bank_statement_id` 正确分组 +- 同一流水多个标签返回顺序稳定 + +### 前端单测 + +- 列表标签列正确显示多个标签与空值占位 +- 详情弹窗正确显示命中异常标签模块 +- 不影响现有翻页、排序、详情打开能力 + +### 人工验证 + +1. 进入项目详情页的“流水明细查询” +2. 确认列表中异常标签列展示正常 +3. 打开一条命中流水详情,确认名称、风险等级、命中原因摘要可见 +4. 导出当前筛选结果,确认 Excel 中新增两列且内容与页面口径一致 + +## 风险与边界 + +- 若历史标签结果中 `reason_detail` 缺失,详情和导出只能展示空摘要;本次不补历史数据修复。 +- 若同一流水命中标签较多,列表列宽可能变高;本次仅做轻量折行展示,不增加复杂交互。 +- 本次不新增异常标签筛选,用户仍需通过导出或详情查看具体命中原因。 + +## 实施拆分建议 + +后续进入实施计划时,默认拆为两份文档: + +- 后端实施计划:标签结果查询、VO 扩展、导出扩展、测试 +- 前端实施计划:列表标签列、详情标签模块、单测与联调验证 + +## 结论 + +本次需求采用“现有流水查询 + 服务层批量补标签”的最短路径方案: + +- 列表展示标签名称 +- 详情展示标签名称、风险等级、命中原因摘要 +- 导出追加异常标签与命中原因摘要 +- 仅展示当前流水直接命中的流水级标签 + +该方案不改变现有流水查询入口、筛选项和标签计算逻辑,能在最小改动范围内补齐页面可读性与导出可交付性。 diff --git a/docs/reports/implementation/2026-03-18-bank-statement-hit-tags-design-record.md b/docs/reports/implementation/2026-03-18-bank-statement-hit-tags-design-record.md new file mode 100644 index 00000000..e7b9af50 --- /dev/null +++ b/docs/reports/implementation/2026-03-18-bank-statement-hit-tags-design-record.md @@ -0,0 +1,29 @@ +# 流水明细异常标签展示设计记录 + +## 变更概述 + +- 新增“流水明细异常标签展示”设计文档。 +- 明确列表只展示当前流水直接命中的异常标签名称。 +- 明确详情展示标签名称、风险等级和命中原因摘要。 +- 明确导出文件追加“异常标签”“命中原因摘要”两列。 +- 明确本次只使用流水级标签结果,不混入对象级标签。 + +## 新增文件 + +- `docs/design/2026-03-18-bank-statement-hit-tags-design.md` + +## 设计结论 + +- 后端采用“现有流水查询 + 服务层批量补标签”的方式实现。 +- 列表、详情、导出统一读取 `ccdi_bank_statement_tag_result` 中 `result_type = 'STATEMENT'` 的结果。 +- 页面不新增异常标签筛选、排序和统计能力。 +- 导出失败时不允许静默丢失标签列内容。 + +## 过程修正 + +- 设计文档已从通用技能默认目录纠正到仓库规范目录 `docs/design/`。 +- 后续同类设计文档一律优先以仓库 `AGENTS.md` 与现有目录结构为准,不再使用通用默认 spec 路径。 + +## 后续动作 + +- 待用户审阅 spec 后,进入前后端实施计划阶段。