补充兰溪流水Mock随机命中规则设计
This commit is contained in:
391
docs/design/2026-03-20-lsfx-mock-random-hit-rule-design.md
Normal file
391
docs/design/2026-03-20-lsfx-mock-random-hit-rule-design.md
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
# 兰溪流水 Mock 随机命中规则设计文档
|
||||||
|
|
||||||
|
**模块**: `lsfx-mock-server`
|
||||||
|
**日期**: 2026-03-20
|
||||||
|
|
||||||
|
## 一、背景
|
||||||
|
|
||||||
|
当前 `lsfx-mock-server` 已接通以下主链路:
|
||||||
|
|
||||||
|
1. `getJZFileOrZjrcuFile` 创建 `logId` 并保存主绑定、员工及亲属身份范围
|
||||||
|
2. `getBSByLogId` 按 `logId` 生成流水并分页返回
|
||||||
|
3. 同一 `logId` 首次生成的流水结果会缓存,后续分页查询保持稳定
|
||||||
|
|
||||||
|
现有问题有两类:
|
||||||
|
|
||||||
|
1. 大额交易命中样本当前为固定全量注入,不符合“随机命中一部分”的联调需求
|
||||||
|
2. 第一期新增真实规则虽然已在主项目后端落地,但 `lsfx-mock-server` 还没有对应的随机命中样本,导致“获取流水列表并存储到兰溪本地”这条链路无法稳定覆盖新增规则
|
||||||
|
|
||||||
|
本次需求要求:
|
||||||
|
|
||||||
|
1. 大额交易和新增第一期规则都改为随机命中一部分
|
||||||
|
2. 随机结果对同一个 `logId` 必须稳定,不能每次查询重新变化
|
||||||
|
3. `getJZFileOrZjrcuFile -> getBSByLogId` 生成出的流水可以被兰溪本地接口正确存储
|
||||||
|
4. 必要时允许直接修改关联数据库最小量数据,使新增模型可以正确打标
|
||||||
|
|
||||||
|
## 二、目标
|
||||||
|
|
||||||
|
本次设计目标如下:
|
||||||
|
|
||||||
|
1. 将 `lsfx-mock-server` 的固定全量命中样本改造成“按 `logId` 稳定随机命中规则子集”
|
||||||
|
2. 保持现有 `FileService -> StatementService -> cache` 主链路不变
|
||||||
|
3. 将样本职责拆到“规则命中计划”和“规则样本拼装”两个层次,避免随机逻辑散落
|
||||||
|
4. 让大额交易模型与第一期新增规则都具备“随机命中一部分”的流水样本
|
||||||
|
5. 对无法通过银行流水接口伪造的规则,采用最小数据库基线补齐,不增加兼容性补丁链路
|
||||||
|
|
||||||
|
## 三、范围
|
||||||
|
|
||||||
|
### 3.1 本次范围
|
||||||
|
|
||||||
|
- 修改 `lsfx-mock-server/services/file_service.py`
|
||||||
|
- 修改 `lsfx-mock-server/services/statement_service.py`
|
||||||
|
- 修改 `lsfx-mock-server/services/statement_rule_samples.py`
|
||||||
|
- 修改 `lsfx-mock-server` 相关单元测试与集成测试
|
||||||
|
- 按最小范围调整关联数据库中的基线业务数据,使非流水表规则可被正确打标
|
||||||
|
- 补充设计文档与后续实施计划文档
|
||||||
|
|
||||||
|
### 3.2 不在本次范围
|
||||||
|
|
||||||
|
- 不新增新的 Mock 服务模块
|
||||||
|
- 不把 Mock 服务改造成实时从数据库拼装整套流水
|
||||||
|
- 不改造主项目银行流水打标架构
|
||||||
|
- 不引入兼容性双轨逻辑
|
||||||
|
- 不为前端新增页面或交互
|
||||||
|
|
||||||
|
## 四、现状分析
|
||||||
|
|
||||||
|
### 4.1 Mock 服务现状
|
||||||
|
|
||||||
|
当前 `lsfx-mock-server` 中:
|
||||||
|
|
||||||
|
- `FileService.fetch_inner_flow()` 负责生成 `logId`、主绑定、日期范围和员工亲属身份
|
||||||
|
- `StatementService._generate_statements()` 会混入固定大额交易命中样本,再补随机噪声流水
|
||||||
|
- `StatementService.get_bank_statement()` 首次生成后缓存 200 条流水,同一 `logId` 分页结果稳定
|
||||||
|
|
||||||
|
这套结构已经满足“同一 `logId` 查询稳定”的基础要求,但样本生成仍是“默认全量命中”,不满足当前需求。
|
||||||
|
|
||||||
|
### 4.2 主项目规则现状
|
||||||
|
|
||||||
|
根据 [docs/plans/backend/2026-03-20-bank-tag-real-rule-phase1-backend-implementation.md](/Users/wkc/Desktop/ccdi/ccdi/docs/plans/backend/2026-03-20-bank-tag-real-rule-phase1-backend-implementation.md) 与 [docs/design/2026-03-20-bank-tag-real-rule-two-phase-design.md](/Users/wkc/Desktop/ccdi/ccdi/docs/design/2026-03-20-bank-tag-real-rule-two-phase-design.md),第一期真实规则已覆盖:
|
||||||
|
|
||||||
|
- `GAMBLING_SENSITIVE_KEYWORD`
|
||||||
|
- `SPECIAL_AMOUNT_TRANSACTION`
|
||||||
|
- `SUSPICIOUS_INCOME_KEYWORD`
|
||||||
|
- `FOREX_BUY_AMT`
|
||||||
|
- `FOREX_SELL_AMT`
|
||||||
|
- `LARGE_PURCHASE_TRANSACTION`
|
||||||
|
- `STOCK_TFR_LARGE`
|
||||||
|
- `LARGE_STOCK_TRADING`
|
||||||
|
- `WITHDRAW_CNT`
|
||||||
|
|
||||||
|
其中:
|
||||||
|
|
||||||
|
- `GAMBLING_SENSITIVE_KEYWORD`
|
||||||
|
- `SPECIAL_AMOUNT_TRANSACTION`
|
||||||
|
- `SUSPICIOUS_INCOME_KEYWORD`
|
||||||
|
- `FOREX_BUY_AMT`
|
||||||
|
- `FOREX_SELL_AMT`
|
||||||
|
- `STOCK_TFR_LARGE`
|
||||||
|
- `LARGE_STOCK_TRADING`
|
||||||
|
- `WITHDRAW_CNT`
|
||||||
|
|
||||||
|
都可以通过 Mock 银行流水直接构造命中条件。
|
||||||
|
|
||||||
|
但 `LARGE_PURCHASE_TRANSACTION` 的真实 SQL 查询的是 `ccdi_purchase_transaction`,不是 `ccdi_bank_statement`,因此不适合伪造成银行流水命中。
|
||||||
|
|
||||||
|
### 4.3 本次关键约束
|
||||||
|
|
||||||
|
用户已明确确认以下约束:
|
||||||
|
|
||||||
|
1. 大额交易和新增第一期规则都改为随机命中一部分,而不是固定全量命中
|
||||||
|
2. 随机结果采用“同一个 `logId` 首次生成时随机决定,后续保持不变”
|
||||||
|
|
||||||
|
因此本次设计必须同时满足:
|
||||||
|
|
||||||
|
- 有随机性
|
||||||
|
- 可复现
|
||||||
|
- 可测试
|
||||||
|
- 不因重复查询导致结果漂移
|
||||||
|
|
||||||
|
## 五、方案对比
|
||||||
|
|
||||||
|
### 5.1 方案 A:按规则包稳定随机命中,推荐
|
||||||
|
|
||||||
|
做法:
|
||||||
|
|
||||||
|
- 把“大额交易”和“第一期新增规则”拆成多个独立规则包
|
||||||
|
- `fetch_inner_flow()` 生成 `logId` 时,基于 `logId` 计算一份稳定的规则命中计划
|
||||||
|
- `getBSByLogId` 首次查该 `logId` 时,根据命中计划拼装命中样本并补噪声流水
|
||||||
|
|
||||||
|
优点:
|
||||||
|
|
||||||
|
- 同一 `logId` 结果稳定
|
||||||
|
- 不同 `logId` 命中子集不同,随机性自然
|
||||||
|
- 可以按规则粒度精确控制命中范围
|
||||||
|
- 测试可以直接断言“规则命中计划稳定”
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
|
||||||
|
- 需要把现有固定全量样本生成器改造成可按规则装配的结构
|
||||||
|
|
||||||
|
### 5.2 方案 B:按场景模板随机命中
|
||||||
|
|
||||||
|
做法:
|
||||||
|
|
||||||
|
- 预置若干整套场景模板,如“偏大额交易”“偏新增规则”“混合命中”
|
||||||
|
- 每个 `logId` 只随机选择一套模板
|
||||||
|
|
||||||
|
优点:
|
||||||
|
|
||||||
|
- 实现快
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
|
||||||
|
- 随机粒度粗
|
||||||
|
- 容易出现某些规则长期捆绑命中,不够贴近真实联调需求
|
||||||
|
|
||||||
|
### 5.3 方案 C:重度依赖数据库动态拼装流水
|
||||||
|
|
||||||
|
做法:
|
||||||
|
|
||||||
|
- 先写大量数据库测试数据
|
||||||
|
- Mock 服务每次从数据库读取对象关系与业务记录,再动态拼装流水
|
||||||
|
|
||||||
|
优点:
|
||||||
|
|
||||||
|
- 表面上更接近生产链路
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
|
||||||
|
- 耦合高,调试成本大
|
||||||
|
- 超出本次最短路径要求
|
||||||
|
- 会把 Mock 服务从“可控样本生成器”变成“数据库依赖型服务”
|
||||||
|
|
||||||
|
### 5.4 结论
|
||||||
|
|
||||||
|
采用方案 A。
|
||||||
|
|
||||||
|
## 六、总体设计
|
||||||
|
|
||||||
|
### 6.1 设计原则
|
||||||
|
|
||||||
|
1. 随机决策只在 `FileService` 做一次
|
||||||
|
2. 流水生成只在 `StatementService` 做一次并缓存
|
||||||
|
3. 样本模板只在 `statement_rule_samples.py` 维护
|
||||||
|
4. 不新增新的服务层或中间模块
|
||||||
|
5. 对不能通过银行流水构造的规则,使用最小数据库基线补齐
|
||||||
|
|
||||||
|
### 6.2 数据流
|
||||||
|
|
||||||
|
改造后的数据流如下:
|
||||||
|
|
||||||
|
1. `getJZFileOrZjrcuFile` 调用 `FileService.fetch_inner_flow()`
|
||||||
|
2. `FileService` 创建 `FileRecord` 时,基于 `logId` 生成并保存规则命中计划
|
||||||
|
3. `getBSByLogId` 调用 `StatementService.get_bank_statement()`
|
||||||
|
4. `StatementService` 首次查询该 `logId` 时,从 `FileRecord` 读取命中计划
|
||||||
|
5. `statement_rule_samples.py` 根据命中计划生成对应规则样本
|
||||||
|
6. `StatementService` 再补足噪声流水到固定 200 条
|
||||||
|
7. 首次生成的结果缓存到内存,后续分页继续复用同一份流水
|
||||||
|
|
||||||
|
### 6.3 规则命中计划设计
|
||||||
|
|
||||||
|
规则命中计划按两大类保存:
|
||||||
|
|
||||||
|
- `largeTransactionHitRules`
|
||||||
|
- `phase1HitRules`
|
||||||
|
|
||||||
|
规则命中计划保存的是规则代码集合,不保存具体流水明细。
|
||||||
|
|
||||||
|
同一个 `logId` 的命中计划由固定种子生成,例如:
|
||||||
|
|
||||||
|
- `Random("rule-plan:{logId}")`
|
||||||
|
|
||||||
|
由此保证:
|
||||||
|
|
||||||
|
- 同一个 `logId` 命中子集稳定
|
||||||
|
- 不同 `logId` 命中子集大概率不同
|
||||||
|
|
||||||
|
### 6.4 职责划分
|
||||||
|
|
||||||
|
#### `FileService`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 创建 `logId`
|
||||||
|
- 绑定主体、账号、员工及亲属身份
|
||||||
|
- 生成并保存规则命中计划
|
||||||
|
|
||||||
|
不负责:
|
||||||
|
|
||||||
|
- 生成具体流水明细
|
||||||
|
- 执行规则样本拼装
|
||||||
|
|
||||||
|
#### `StatementService`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 首次读取 `logId` 时,根据规则命中计划生成流水
|
||||||
|
- 补足噪声流水
|
||||||
|
- 分页返回并缓存结果
|
||||||
|
|
||||||
|
不负责:
|
||||||
|
|
||||||
|
- 决定本次命中哪些规则
|
||||||
|
|
||||||
|
#### `statement_rule_samples.py`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 按规则代码生成对应命中样本
|
||||||
|
- 提供统一入口,按命中计划拼装样本集合
|
||||||
|
|
||||||
|
不负责:
|
||||||
|
|
||||||
|
- 随机选择规则
|
||||||
|
- 缓存或分页
|
||||||
|
|
||||||
|
## 七、规则包设计
|
||||||
|
|
||||||
|
### 7.1 大额交易规则包
|
||||||
|
|
||||||
|
保留现有已实现的大额交易命中样本,但改为按规则粒度单独开关,不再默认全量注入。
|
||||||
|
|
||||||
|
每条规则对应一个独立样本 builder,由命中计划决定是否加入。
|
||||||
|
|
||||||
|
### 7.2 第一期新增规则包
|
||||||
|
|
||||||
|
本次通过银行流水构造的第一期规则包为:
|
||||||
|
|
||||||
|
- `GAMBLING_SENSITIVE_KEYWORD`
|
||||||
|
- `SPECIAL_AMOUNT_TRANSACTION`
|
||||||
|
- `SUSPICIOUS_INCOME_KEYWORD`
|
||||||
|
- `FOREX_BUY_AMT`
|
||||||
|
- `FOREX_SELL_AMT`
|
||||||
|
- `STOCK_TFR_LARGE`
|
||||||
|
- `LARGE_STOCK_TRADING`
|
||||||
|
- `WITHDRAW_CNT`
|
||||||
|
|
||||||
|
每个规则包只生成命中该规则所需的最小流水集合,不附带额外未请求逻辑。
|
||||||
|
|
||||||
|
### 7.3 `LARGE_PURCHASE_TRANSACTION` 边界
|
||||||
|
|
||||||
|
`LARGE_PURCHASE_TRANSACTION` 不放入 Mock 银行流水规则包中,原因如下:
|
||||||
|
|
||||||
|
1. 主项目真实 SQL 查询来源是 `ccdi_purchase_transaction`
|
||||||
|
2. `getBSByLogId` 返回的是银行流水,无法自然映射采购业务表
|
||||||
|
3. 若将其伪造成银行流水命中,会偏离真实打标逻辑
|
||||||
|
|
||||||
|
因此该规则采用“数据库最小基线补齐”的方式处理,不强行混入 Mock 流水。
|
||||||
|
|
||||||
|
## 八、随机策略设计
|
||||||
|
|
||||||
|
### 8.1 随机约束
|
||||||
|
|
||||||
|
为避免“完全不命中”或“一次性全命中”的极端情况,本次采用有边界的随机策略:
|
||||||
|
|
||||||
|
- 大额交易规则包:随机命中 2 到 4 条
|
||||||
|
- 第一期新增流水规则包:随机命中 2 到 4 条
|
||||||
|
- 两大类都至少命中 1 条
|
||||||
|
- `WITHDRAW_CNT` 允许独立命中,不要求与明细型规则绑定
|
||||||
|
|
||||||
|
未命中的规则不生成伪样本,只保留普通噪声流水。
|
||||||
|
|
||||||
|
### 8.2 稳定性要求
|
||||||
|
|
||||||
|
同一个 `logId`:
|
||||||
|
|
||||||
|
- 首次生成命中计划后固定不变
|
||||||
|
- 首次生成流水后缓存结果固定不变
|
||||||
|
- 后续分页查询不得重新随机
|
||||||
|
|
||||||
|
## 九、数据库基线设计
|
||||||
|
|
||||||
|
### 9.1 可直接复用的数据
|
||||||
|
|
||||||
|
继续复用当前 `StaffIdentityRepository` 从真实数据库读取的:
|
||||||
|
|
||||||
|
- 员工身份证
|
||||||
|
- 有效亲属身份证
|
||||||
|
|
||||||
|
这部分已经满足 `SPECIAL_AMOUNT_TRANSACTION`、`WITHDRAW_CNT` 等规则的对象关系基础。
|
||||||
|
|
||||||
|
### 9.2 需要最小补齐的数据
|
||||||
|
|
||||||
|
只补齐真实打标必需、但无法通过 Mock 流水自然构造的最小数据:
|
||||||
|
|
||||||
|
1. 若被选中的员工缺少足够的亲属关系区分数据,则补齐最小亲属关系记录,确保能区分“配偶/子女”和“非配偶子女”
|
||||||
|
2. 为 `LARGE_PURCHASE_TRANSACTION` 补最小量 `ccdi_purchase_transaction` 业务数据,使真实 SQL 可命中
|
||||||
|
|
||||||
|
### 9.3 明确不做的事
|
||||||
|
|
||||||
|
- 不大规模预置 `ccdi_bank_statement`
|
||||||
|
- 不让 Mock 服务实时依赖数据库生成整套流水
|
||||||
|
- 不为数据库不存在的业务链路追加补丁式兼容方案
|
||||||
|
|
||||||
|
## 十、错误处理
|
||||||
|
|
||||||
|
1. 若数据库中无法读取员工及亲属身份,保持现有失败语义,直接暴露错误
|
||||||
|
2. 若某次规则命中计划生成异常,不写入 `FileRecord`
|
||||||
|
3. 若 `getBSByLogId` 首次生成流水失败,不写入缓存,避免脏缓存
|
||||||
|
4. 若某类规则本次未被随机命中,不视为错误,只是本次子集未覆盖
|
||||||
|
5. `LARGE_PURCHASE_TRANSACTION` 不因未出现在 Mock 流水中而报错,其命中依赖数据库基线数据
|
||||||
|
|
||||||
|
## 十一、测试设计
|
||||||
|
|
||||||
|
### 11.1 `tests/test_file_service.py`
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
- `fetch_inner_flow()` 会生成并保存规则命中计划
|
||||||
|
- 同一 `logId` 的命中计划稳定
|
||||||
|
- 命中计划中同时包含大额交易与第一期新增规则两类结果
|
||||||
|
|
||||||
|
### 11.2 `tests/test_statement_service.py`
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
- 按命中计划只生成对应规则子集样本,而不是固定全量命中
|
||||||
|
- 同一 `logId` 重复查询时结果不漂移
|
||||||
|
- 不同 `logId` 大概率得到不同命中子集
|
||||||
|
- 命中规则对应的样本字段能被主项目真实 SQL 识别
|
||||||
|
|
||||||
|
### 11.3 `tests/integration/test_full_workflow.py`
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
- `getJZFileOrZjrcuFile -> getBSByLogId` 主链路可正常返回
|
||||||
|
- 返回结果既包含随机命中样本,也包含普通噪声流水
|
||||||
|
- 上传状态与流水接口仍共享同一主绑定信息
|
||||||
|
|
||||||
|
### 11.4 数据库基线验证
|
||||||
|
|
||||||
|
对 `LARGE_PURCHASE_TRANSACTION` 单独验证:
|
||||||
|
|
||||||
|
- 基线业务数据补齐后,主项目真实规则 SQL 可命中
|
||||||
|
- 不将该规则混入 Mock 流水命中断言
|
||||||
|
|
||||||
|
## 十二、实施与文档要求
|
||||||
|
|
||||||
|
后续进入实施计划阶段时,按仓库规范产出两份计划文档:
|
||||||
|
|
||||||
|
1. 后端实施计划:`docs/plans/backend/`
|
||||||
|
2. 前端实施计划:`docs/plans/frontend/`
|
||||||
|
|
||||||
|
其中:
|
||||||
|
|
||||||
|
- 后端实施计划覆盖 `lsfx-mock-server` 改造、测试与数据库基线补齐
|
||||||
|
- 前端实施计划明确记录“无需前端代码改动,仅需联调验收”
|
||||||
|
|
||||||
|
同时补充实施记录文档,记录本次具体改动内容与验证结果。
|
||||||
|
|
||||||
|
## 十三、结论
|
||||||
|
|
||||||
|
本次采用“按 `logId` 稳定随机命中规则包”的方式改造 `lsfx-mock-server`:
|
||||||
|
|
||||||
|
- 保留现有主链路
|
||||||
|
- 不新增模块
|
||||||
|
- 不引入补丁式兼容方案
|
||||||
|
- 让大额交易和第一期新增规则都能随机命中一部分
|
||||||
|
- 对同一个 `logId` 保持稳定、可复现、可联调
|
||||||
|
|
||||||
|
对无法通过银行流水自然构造的 `LARGE_PURCHASE_TRANSACTION`,采用最小数据库基线补齐,不伪造银行流水,以确保整个方案逻辑正确并与主项目真实打标链路一致。
|
||||||
Reference in New Issue
Block a user