Files
ccdi/docs/design/2026-03-31-lsfx-mock-server-abnormal-account-design.md

286 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LSFX Mock Server 异常账户命中流水设计文档
**模块**: `lsfx-mock-server`
**日期**: 2026-03-31
## 一、背景
当前仓库中的异常账户模型已经在主系统后端完成规则接入,包含以下两条对象型规则:
- `SUDDEN_ACCOUNT_CLOSURE`
- `DORMANT_ACCOUNT_LARGE_ACTIVATION`
根据已落地的后端实施计划与实现结果,这两条规则的命中依赖两类事实:
1. 账户事实:来自 `ccdi_account_info` 的账户状态、开户日、销户日、账户归属人
2. 流水事实:来自 `ccdi_bank_statement` 的账号维度交易时间与交易金额
当前 [lsfx-mock-server](/Users/wkc/Desktop/ccdi/ccdi/lsfx-mock-server) 已具备以下能力:
- `FileService` 为每个 `logId` 生成稳定的规则命中计划
- `StatementService` 根据命中计划拼接规则样本流水
- `/watson/api/project/getBSByLogId` 返回分页流水列表
但 Mock 服务现阶段尚未支持异常账户模型对应的“账户事实 + 命中流水”闭环,因此后端即使接入了真实规则 SQL也无法通过现有 Mock 数据稳定命中异常账户规则。
本次目标是在不改动现有接口协议的前提下,为 `lsfx-mock-server` 补齐异常账户规则的最小闭环造数能力,让同一个 `logId` 下既有可命中后端 SQL 的账户事实,也有与之匹配的流水样本。
## 二、目标
- 在现有 Mock 规则计划体系中新增异常账户命中计划。
- 为每个命中异常账户规则的 `logId` 生成稳定的异常账户事实。
- 按后端 SQL 口径生成两条规则对应的流水样本。
- 保持现有 `/watson/api/project/getBSByLogId` 返回结构不变。
- 保持现有 `FileService -> StatementService` 主链路不变,不引入平行造数机制。
- 补充测试,确保命中计划、账户事实和流水样本三者一致。
## 三、非目标
- 不新增异常账户独立接口。
- 不修改现有上传、拉取行内流水、查询流水接口的请求参数与响应结构。
- 不对外直接返回异常账户事实列表。
- 不模拟 `ccdi_account_info` 全字段,只保留两条规则所需最小字段。
- 不扩展异常账户详情页、分页查询或导出链路。
- 不引入动态配置平台、DSL 或补丁式兼容逻辑。
## 四、方案对比
### 4.1 方案一:并入现有 `rule_hit_plan` 体系
做法:
-`FileRecord` 中新增异常账户命中计划与账户事实
-`build_seed_statements_for_rule_plan(...)` 中接入异常账户样本生成
- `StatementService` 继续统一补噪声、编号、分页
优点:
- 完全复用当前 Mock 主链路
- 同一个 `logId` 下规则计划、账户事实、流水样本天然一致
- 实现路径最短,后续联调稳定
缺点:
- 需要在 `FileRecord` 中增加一层最小账户事实建模
### 4.2 方案二:仅在 `StatementService` 中硬编码异常账户流水样本
优点:
- 改动最少,实现最快
缺点:
- 命中流水和账户事实分离
- 后续若需要调试命中原因或扩展账户事实,维护成本较高
### 4.3 方案三:新增独立异常账户服务模块
优点:
- 抽象边界更清晰
缺点:
- 对当前 Mock 项目来说偏重
- 超出最短路径实现要求
## 五、结论
采用方案一。
原因如下:
- 与当前 Mock 服务的规则计划机制完全一致
- 不新增接口、不增加平行数据流
- 能以最小改动实现“账户事实 + 命中流水”的稳定闭环
- 后续若主系统还需要继续扩展 Mock 规则样本,可以沿用同一套结构
## 六、总体设计
### 6.1 新增命中计划维度
在现有规则计划结构上新增:
- `abnormal_account_hit_rules`
规则池固定为:
- `SUDDEN_ACCOUNT_CLOSURE`
- `DORMANT_ACCOUNT_LARGE_ACTIVATION`
`FileService` 在为 `logId` 生成规则计划时,继续沿用现有“稳定随机且可复现”的逻辑,把异常账户规则作为平级维度一起生成并回填到 `FileRecord`
### 6.2 新增最小账户事实
`FileRecord` 新增以下字段:
- `abnormal_account_hit_rules: List[str]`
- `abnormal_accounts: List[AbnormalAccountFact]`
其中 `AbnormalAccountFact` 仅保留两条规则需要的最小字段:
- `account_no`
- `owner_id_card`
- `account_name`
- `status`
- `effective_date`
- `invalid_date`
说明:
- 本次不复制主系统 `ccdi_account_info` 全量结构
- 只保留命中 SQL 真正会用到的事实,避免过度设计
### 6.3 样本流水接入位置
现有 `StatementService` 已通过 `build_seed_statements_for_rule_plan(...)` 生成规则样本流水。
本次改造保持该入口不变,仅在其内部追加异常账户规则样本构造:
- `build_sudden_account_closure_samples(...)`
- `build_dormant_account_large_activation_samples(...)`
生成出的异常账户样本与其他规则样本一起组成 `seeded_statements`,之后继续复用现有逻辑:
1. 补足噪声流水
2. 统一分配流水编号
3. 打乱顺序
4. 分页返回
## 七、规则口径设计
### 7.1 `SUDDEN_ACCOUNT_CLOSURE`
Mock 口径必须与后端 SQL 对齐:
- 账户状态为 `2`
- `invalid_date` 非空
- 流水账号与账户事实中的 `account_no` 一致
- 所有命中样本流水时间都落在 `[invalid_date - 30天, invalid_date)` 区间内
样本策略:
- 为单个命中账户生成 2 到 3 笔流水
- 同时覆盖收入和支出,便于后端聚合 `windowTotalAmount`
- 保证存在明确的最后交易日,便于推导 `lastTxDate`
- 保证存在稳定的单笔最大金额,便于推导 `windowMaxSingleAmount`
### 7.2 `DORMANT_ACCOUNT_LARGE_ACTIVATION`
Mock 口径必须与后端 SQL 对齐:
- 账户状态为 `1`
- `effective_date` 非空
- 首笔流水日期 `>= effective_date + 6个月`
- 启用后流水满足:
- `windowTotalAmount >= 500000`
-`windowMaxSingleAmount >= 100000`
样本策略:
- 首笔流水明确落在开户满 6 个月以后
- 为避免边界漂移,直接让累计金额和单笔最大金额同时满足阈值
- 启用后生成 2 笔以上流水,保证累计口径稳定
### 7.3 未命中规则时的处理
当某个 `logId``abnormal_account_hit_rules` 中不包含某条规则时:
- 不生成该规则的账户事实
- 不生成该规则的流水样本
- 不制造“接近命中但未命中”的灰度样本
这样可以避免误命中,保证 Mock 语义清晰。
## 八、数据流设计
本次改造后的数据流如下:
1. `FileService.fetch_inner_flow(...)` 或上传链路创建 `FileRecord`
2. `FileService` 生成四类命中计划:
- `large_transaction_hit_rules`
- `phase1_hit_rules`
- `phase2_statement_hit_rules`
- `abnormal_account_hit_rules`
3. `FileService` 根据异常账户命中计划生成 `abnormal_accounts`
4. `StatementService._generate_statements(...)` 读取 `FileRecord`
5. `build_seed_statements_for_rule_plan(...)` 依据异常账户命中计划拼接对应样本流水
6. 服务层继续补噪声并返回现有结构的流水列表
结论:
- 内部多了一层异常账户事实
- 对外接口保持不变
## 九、模块与职责
### 9.1 `lsfx-mock-server/services/file_service.py`
职责调整:
- 扩展 `FileRecord`
- 生成并保存 `abnormal_account_hit_rules`
- 按命中规则生成对应 `abnormal_accounts`
- 保证同一 `logId` 下账户事实稳定可复现
### 9.2 `lsfx-mock-server/services/statement_rule_samples.py`
职责调整:
- 新增异常账户事实结构
- 新增两类异常账户样本构造器
- 在统一种子流水构造入口中接入异常账户样本
### 9.3 `lsfx-mock-server/services/statement_service.py`
职责保持不变,仅消费新增计划与样本:
- 读取 `FileRecord` 中的异常账户计划
- 调用种子样本生成器
- 继续完成补噪声、编号、缓存、分页返回
## 十、测试设计
测试分为三层:
### 10.1 计划层测试
验证点:
- `FileRecord` 能保存 `abnormal_account_hit_rules`
- 生成的异常账户计划稳定、可复现
- 命中计划与账户事实数量、规则类型一致
### 10.2 样本层测试
验证点:
- `SUDDEN_ACCOUNT_CLOSURE` 样本流水日期全部处于销户前 30 天窗口内
- `DORMANT_ACCOUNT_LARGE_ACTIVATION` 首笔流水日期晚于开户满 6 个月
- 休眠账户样本的累计金额和单笔最大金额达到后端 SQL 阈值
### 10.3 服务层测试
验证点:
- `StatementService._generate_statements(...)` 能把异常账户样本混入返回流水
- 未命中的异常账户规则不会污染其他 `logId`
- 同一个 `logId` 重复查询时缓存结果保持稳定
## 十一、风险与约束
- 本次不改协议,因此异常账户事实仅在 Mock 服务内部使用
- 由于不新增独立接口,联调时仍需通过现有流水接口间接触发后端规则命中
- 样本日期和金额必须严格贴后端 SQL 口径避免出现“Mock 看起来合理但后端不命中”的问题
## 十二、后续计划
本设计确认后,下一步仅进入实施计划编写阶段,不直接扩展其他功能。
按仓库约定,需要继续补充:
- 后端实施计划
- 前端实施计划
- 本次改动的实施记录