# lsfx-mock all 模式 SQL 对齐强命中设计

## 1. 背景

当前 `lsfx-mock-server` 的 `--rule-hit-mode all` 已支持为多类规则生成流水样本，但生成策略仍带有早期“语义化样本”特征，未完全对齐主工程真实 SQL 的命中口径。

在最新排查中，以下 5 条已落地真实 SQL 的规则仍无法稳定命中：

- `SPECIAL_AMOUNT_TRANSACTION`
- `MONTHLY_FIXED_INCOME`
- `SUSPICIOUS_INCOME_KEYWORD`
- `FIXED_COUNTERPARTY_TRANSFER`
- `LOW_INCOME_RELATIVE_LARGE_TRANSACTION`

问题根因不是主工程打标链路异常，而是 Mock 样本和关联表数据与后端 XML SQL 条件不一致：

- 特殊金额样本金额错误
- 固定月收入样本月数不足
- 收入关键词样本摘要不在 SQL 词表内
- 固定对手转入样本主体落在家属证件号
- 低收入亲属规则缺少满足阈值的关系表收入基线

本次设计目标是在不修改主工程打标逻辑的前提下，只通过调整 `lsfx-mock-server` 的 `all` 模式生成策略，让以上 5 条真实 SQL 规则稳定命中，并尽量抑制额外噪声命中。

## 2. 目标

- 仅针对 `--rule-hit-mode all` 生效，`subset` 模式保持现状不变。
- 仅覆盖后端 XML 中已落地真实 SQL 的规则，不处理占位 SQL。
- 让上述 5 条规则在真实链路下稳定命中：
  - Mock 返回流水
  - 主工程入库 `ccdi_bank_statement`
  - 触发重打标
  - 在 `ccdi_bank_statement_tag_result` 中看到命中结果
- 允许通过补写最小关联表基线满足 SQL 前置条件。
- 尽量压住因普通噪声流水导致的误命中或聚合口径污染。

## 3. 非目标

- 不修改主工程 `ccdi-project` 中的 XML SQL、Service 或打标逻辑。
- 不处理占位 SQL 规则：
  - `ABNORMAL_CUSTOMER_TRANSACTION`
  - `INTEREST_PAYMENT_BY_OTHERS`
  - 以及其他仍为 `where 1 = 0` 的规则
- 不把 `subset` 模式升级成同样的强命中模型。
- 不引入 DSL、规则解释器或额外配置中心。
- 不做与本次 5 条规则无关的通用重构。

## 4. 范围

### 4.1 目标规则

- `SUSPICIOUS_RELATION / SPECIAL_AMOUNT_TRANSACTION`
- `SUSPICIOUS_PART_TIME / MONTHLY_FIXED_INCOME`
- `SUSPICIOUS_PART_TIME / SUSPICIOUS_INCOME_KEYWORD`
- `SUSPICIOUS_PART_TIME / FIXED_COUNTERPARTY_TRANSFER`
- `ABNORMAL_TRANSACTION / LOW_INCOME_RELATIVE_LARGE_TRANSACTION`

### 4.2 涉及模块

- `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/services/phase2_baseline_service.py`
- `lsfx-mock-server/tests/test_file_service.py`
- `lsfx-mock-server/tests/test_statement_service.py`
- 视需要补充或调整集成测试

## 5. 方案对比

### 5.1 方案 A：SQL 镜像驱动强命中

按后端 XML SQL 条件重写 `all` 模式样本和关联表基线。每条目标规则拥有专属、确定性的输入组，并对噪声数据施加约束。

优点：

- 与真实 SQL 口径一致
- 命中稳定，可重复验证
- 便于定位问题，后续逐条扩展也清晰

缺点：

- 需要逐条核对 SQL 条件并同步改写样本函数

### 5.2 方案 B：最小命中修补

仅把当前明显不命中的样本做最小修修补补，不系统控制噪声和规则串扰。

优点：

- 改动量较小

缺点：

- 容易出现“目标规则命中了，但多出一批误命中”
- 后续 SQL 小改动后容易再次失效

### 5.3 方案 C：配置化规则生成器

抽象规则配置层，由统一生成器按配置组装样本。

优点：

- 长期扩展性好

缺点：

- 明显超出本次最短路径诉求
- 需要引入新的抽象和维护成本

## 6. 推荐方案

采用方案 A。

原因：

- 这是当前最短且逻辑闭环的实现路径。
- 目标是“真实 SQL 下稳定命中”，因此 Mock 端必须直接对齐 XML 条件，而不是继续依靠语义相近的样本。
- 只在 `all` 模式启用该策略，可以在不破坏现有 `subset` 习惯的前提下完成强命中能力。

## 7. 设计原则

- `all` 模式中的目标规则样本必须是 deterministic 的，不依赖随机碰运气。
- 每条目标规则都要有独立的命中样本簇，尽量避免多条规则共享同一组核心流水。
- 规则所需的主体类型必须与 SQL 首层 join 一致：
  - 仅员工本人：样本 `cret_no` 必须落在 `ccdi_base_staff.id_card`
  - 依赖家属：样本 `cret_no` 必须落在 `ccdi_staff_fmy_relation.relation_cert_no`
- 对存在聚合或收口条件的规则，必须同时控制背景噪声，避免误触发额外对手方、额外月份或额外支出。
- 关联表基线只写“命中真实 SQL 所需的最小数据”，并且应可重复写入、可覆盖旧状态。

## 8. 模块设计

### 8.1 `file_service.py`

职责保持为“生成命中计划并记录文件级上下文”，但 `all` 模式逻辑调整为：

- 继续保留现有大额交易、第一期、第二期规则计划字段，避免影响调用方结构。
- 对本次 5 条目标规则，在 `all` 模式下强制加入计划。
- 占位 SQL 规则不进入 `all` 模式强命中计划。
- 在记录中保留足够的身份上下文，供基线服务和样本服务共享使用。

`subset` 模式不改。

### 8.2 `statement_rule_samples.py`

保留“每条规则一个构造函数”的现有结构，不引入新 DSL。

本次只重写以下函数：

- `build_special_amount_transaction_samples`
- `build_monthly_fixed_income_samples`
- `build_suspicious_income_keyword_samples`
- `build_fixed_counterparty_transfer_samples`
- `build_low_income_relative_large_transaction_samples`

同时对 `all` 模式下的噪声样本约束做最小增强，避免干扰：

- 不制造过多重复 `CUSTOMER_ACCOUNT_NAME`
- 不制造额外稳定季度转入
- 不制造会破坏“工资后无支出”或“固定月收入稳定性”的额外流水

### 8.3 `phase2_baseline_service.py`

继续作为关联表最小基线服务使用，但扩展为支持本次规则所需的关系表精确写入。

职责：

- 针对 `LOW_INCOME_RELATIVE_LARGE_TRANSACTION` 写入低收入家属基线
- 针对 `SPECIAL_AMOUNT_TRANSACTION` 保证目标对手不被识别成配偶/子女
- 只覆盖规则命中的必要字段，不额外灌入无关主数据

幂等策略：

- 对既有关系数据，按目标人员主键做“精确覆盖”而不是追加污染
- 保持同一员工域下重复执行结果一致

## 9. 规则级生成策略

### 9.1 `SPECIAL_AMOUNT_TRANSACTION`

对应 SQL 特征：

- 员工本人 `inner join ccdi_base_staff`
- 对手不是配偶/子女
- 金额必须为 `520` 或 `1314`

生成策略：

- 样本主体使用员工本人证件号
- 生成 1 至 2 笔支出或收入流水，金额固定取 `520` 或 `1314`
- 对手方名称使用专属固定名称
- 基线服务不为该对手建立“配偶/子女”关系

约束：

- 不再使用 `88888.88`
- 不复用现有家庭关系中的配偶、子女姓名，避免误入排除条件

### 9.2 `MONTHLY_FIXED_INCOME`

对应 SQL 特征：

- 员工本人 `inner join ccdi_base_staff`
- 近 12 个月至少 6 个月月收入大于阈值
- 排除工资代发主体和工资摘要
- 月度波动率不高于 `0.3`

生成策略：

- 样本主体切到员工本人
- 构造连续 6 个月固定转入，每月 1 笔
- 对手方固定
- 月金额保持完全一致或极小波动
- 对手方名称不是本行工资代发主体
- 摘要不包含工资相关关键词

约束：

- all 模式噪声流水不能再向该员工叠加大额杂散流入，避免抬高波动率

### 9.3 `SUSPICIOUS_INCOME_KEYWORD`

对应 SQL 特征：

- 员工本人 `inner join ccdi_base_staff`
- 收入流水
- 摘要或交易类型命中收入关键词词表

生成策略：

- 样本主体使用员工本人
- 生成 1 笔收入流水
- 摘要直接使用 SQL 关键词集合中的明确命中词，例如“劳务费发放”或“奖金发放”
- 交易类型可同步设置为“劳务费”或“代发收入”一类，形成双重保险

约束：

- 不再使用“咨询返现收入”“合作收入”这类 SQL 不识别的语义词

### 9.4 `FIXED_COUNTERPARTY_TRANSFER`

对应 SQL 特征：

- 员工本人 `inner join ccdi_base_staff`
- 近 12 个月内，固定对手方至少 2 个季度累计转入位于区间 `[3000,15000]`
- 满足条件的固定对手数量 `< 3`

生成策略：

- 样本主体改为员工本人
- 固定 1 个专属对手方
- 至少覆盖 2 个季度，建议 2 到 3 个季度
- 每季度累计转入控制在区间中部，避免靠边界
- 每季度仅保留 1 笔或少量固定笔数，便于验证

噪声控制：

- all 模式噪声流水中，员工本人不得再出现过多重复对手方季度转入
- 避免出现第二、第三个误满足区间条件的稳定对手

### 9.5 `LOW_INCOME_RELATIVE_LARGE_TRANSACTION`

对应 SQL 特征：

- 家属证件号与 `ccdi_staff_fmy_relation.relation_cert_no` 关联
- 家属年收入为空、为 0，或折月小于 `3000`
- 家属累计交易金额大于 `100000`

生成策略：

- 使用目标员工的家属证件号作为流水主体
- 生成 2 笔以上累计超过 `100000` 的交易
- 基线服务把该家属 `annual_income` 精确设置为：
  - `0`
  - `null`
  - 或低于 `36000`

约束：

- 不依赖项目库现存家属收入
- 每次 all 模式生成时都要确保目标家属收入状态被重置到可命中口径

## 10. 噪声控制设计

当前 `StatementService` 中的随机噪声会对聚合类规则造成干扰。本次只做最小必要控制：

- `all` 模式下继续保留背景噪声，但员工本人使用更收敛的对手方集合
- 对目标员工：
  - 避免随机制造跨季度重复转入
  - 避免随机制造大量同月对公收入
  - 避免随机制造会抬高 `MONTHLY_FIXED_INCOME` 波动率的异常大额流入
- 对家属样本：
  - 避免随机覆盖低收入亲属规则的专属家属域

实现上优先采用“all 模式下缩窄噪声候选集”而不是彻底禁掉噪声。

## 11. 数据流

1. `FileService` 在 `all` 模式下构建强命中规则计划。
2. `phase2_baseline_service` 根据计划写入最小关联表基线。
3. `StatementService` 根据规则计划生成专属流水样本，并补受控噪声。
4. 主工程按原流程拉取、入库并执行打标。
5. 目标规则在结果表中稳定出现命中记录。

## 12. 测试与验证

### 12.1 Mock 单元测试

至少补以下断言：

- `SPECIAL_AMOUNT_TRANSACTION` 样本金额为 `520/1314`
- `MONTHLY_FIXED_INCOME` 样本月份数不少于 `6`
- `SUSPICIOUS_INCOME_KEYWORD` 摘要或交易类型命中 SQL 关键词
- `FIXED_COUNTERPARTY_TRANSFER` 样本主体为员工本人而非家属
- `LOW_INCOME_RELATIVE_LARGE_TRANSACTION` 所需家属基线收入满足低收入条件

### 12.2 联调验证

沿用真实链路验证：

1. 启动 Mock：`main.py --rule-hit-mode all`
2. 拉取流水并导入项目
3. 触发重打标
4. 查询 `ccdi_bank_statement_tag_result`

验收通过标准：

- 上述 5 条规则全部存在命中记录
- 命中主体与预期主体一致
- 没有明显失控的额外噪声命中

## 13. 风险与控制

风险：

- 规则间共享主体导致聚合口径互相污染
- 噪声流水再次引入额外固定对手或异常月收入
- 关系表覆盖不当污染其他联调项目

控制：

- 每条目标规则尽量使用独立样本簇
- all 模式下对目标员工域启用受控噪声
- 基线服务只覆盖当前命中域，不做全表灌数

## 14. 实施建议

按以下顺序执行最稳妥：

1. 先改 5 条样本函数
2. 再补低收入家属基线写入
3. 再收紧 all 模式噪声
4. 最后补单元测试和联调验证

这样可以分层确认：

- 样本本身对不对
- 主数据前置条件够不够
- 噪声是否把聚合口径冲坏

## 15. 结论

本次采用“只改 `all` 模式、只改真实 SQL 对应规则、按 XML 口径重写样本与最小基线”的方案，是满足当前需求的最短路径。

设计完成后，后续实施只需围绕 5 条目标规则逐条落地，不需要修改主工程，也不需要引入额外抽象层。
