补充Mock随机命中后端实施记录
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# LSFX Mock 随机命中规则后端实施记录
|
||||
|
||||
## 修改范围
|
||||
- `lsfx-mock-server/services/file_service.py`
|
||||
- `lsfx-mock-server/services/statement_rule_samples.py`
|
||||
- `lsfx-mock-server/services/statement_service.py`
|
||||
- `lsfx-mock-server/tests/test_file_service.py`
|
||||
- `lsfx-mock-server/tests/test_statement_service.py`
|
||||
- `lsfx-mock-server/tests/integration/test_full_workflow.py`
|
||||
- `sql/migration/2026-03-20-lsfx-mock-random-hit-rule-purchase-baseline.sql`
|
||||
|
||||
## 规则命中计划生成方式
|
||||
- 在 `FileService` 中新增 `LARGE_TRANSACTION_RULE_CODES` 与 `PHASE1_RULE_CODES` 两组规则池。
|
||||
- 新增 `_build_rule_hit_plan(log_id)`,使用 `random.Random(f"rule-plan:{log_id}")` 生成稳定随机源。
|
||||
- 通过 `_pick_rule_subset()` 从两组规则池内分别稳定选出 `2-4` 条规则,并保留规则池原始顺序。
|
||||
- 在 `upload_file()` 与 `fetch_inner_flow()` 创建 `FileRecord` 时同步写入:
|
||||
- `large_transaction_hit_rules`
|
||||
- `phase1_hit_rules`
|
||||
|
||||
## 样本模块按规则子集装配
|
||||
- 将原有“大额交易全量样本”拆成按规则代码独立调用的 builder。
|
||||
- 新增 `LARGE_TRANSACTION_BUILDERS` 与 `PHASE1_RULE_BUILDERS` 两组映射,覆盖:
|
||||
- 大额交易 8 条规则
|
||||
- 第一期可由银行流水构造的 8 条规则
|
||||
- 提供统一入口 `build_seed_statements_for_rule_plan(...)`,仅按 `rule_plan` 中被选中的规则拼装最小命中样本,不再默认返回全量命中样本。
|
||||
- `build_large_transaction_seed_statements(...)` 保留为兼容测试入口,但内部已改为走新的规则映射。
|
||||
|
||||
## StatementService 接通方式
|
||||
- `StatementService._generate_statements()` 改为优先读取 `FileRecord` 中保存的命中计划。
|
||||
- 若存在真实 `FileRecord`,则复用其主体、账号、员工及亲属身份证范围,并把命中计划传给 `build_seed_statements_for_rule_plan(...)`。
|
||||
- 命中样本与随机噪声流水继续统一走 `_assign_statement_ids()` 分配稳定 ID。
|
||||
- 首次生成后仍缓存固定 `200` 条流水;同一 `logId` 重复分页读取保持结果稳定。
|
||||
|
||||
## LARGE_PURCHASE_TRANSACTION 单独补数据库基线原因
|
||||
- `LARGE_PURCHASE_TRANSACTION` 的真实命中来源是 `ccdi_purchase_transaction`,不依赖 `ccdi_bank_statement`。
|
||||
- 为避免伪造银行流水造成业务链路偏移,本次不把该规则塞进 Mock 流水样本。
|
||||
- 通过新增 `sql/migration/2026-03-20-lsfx-mock-random-hit-rule-purchase-baseline.sql`,只补一条最小采购记录 `LSFXMOCKPUR001`。
|
||||
- 脚本通过 `ccdi_base_staff` 选取一条真实员工主数据作为 `applicant_id/applicant_name` 来源,`actual_amount=186000.00`,满足真实 SQL 的 `>100000` 命中门槛。
|
||||
|
||||
## 实施结果
|
||||
- `FileService -> StatementService -> 缓存分页` 主链路保持不变。
|
||||
- 大额交易规则与第一期新增规则均已支持“按 `logId` 稳定随机命中一部分”。
|
||||
- `LARGE_PURCHASE_TRANSACTION` 已通过独立数据库基线补齐联调数据来源。
|
||||
@@ -0,0 +1,70 @@
|
||||
# LSFX Mock 随机命中规则后端验证记录
|
||||
|
||||
## 执行命令
|
||||
|
||||
```bash
|
||||
cd lsfx-mock-server
|
||||
python3 -m pytest tests/test_file_service.py -k "rule_hit_plan or persist_rule_hit_plan" -v
|
||||
python3 -m pytest tests/test_statement_service.py -k "rule_plan_should_only_include or withdraw_cnt_samples" -v
|
||||
python3 -m pytest tests/test_statement_service.py -k "follow_rule_hit_plan or fixed_total_count_200 or cached_result" -v
|
||||
python3 -m pytest tests/integration/test_full_workflow.py -k "same_rule_subset or share_same_primary_binding" -v
|
||||
python3 -m pytest tests/test_file_service.py tests/test_statement_service.py tests/test_api.py tests/integration/test_full_workflow.py -v
|
||||
|
||||
cd ..
|
||||
bin/mysql_utf8_exec.sh sql/migration/2026-03-20-lsfx-mock-random-hit-rule-purchase-baseline.sql
|
||||
python3 - <<'PY'
|
||||
import pymysql
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
text = Path('ruoyi-admin/src/main/resources/application-dev.yml').read_text(encoding='utf-8')
|
||||
match = re.search(r"url:\s*jdbc:mysql://(?P<host>[^:/?#]+):(?P<port>\d+)/(?P<db>[^?\n]+).*?\n\s*username:\s*(?P<user>[^\n]+)\n\s*password:\s*(?P<pwd>[^\n]+)", text, re.S)
|
||||
conn = pymysql.connect(
|
||||
host=match.group('host'),
|
||||
port=int(match.group('port')),
|
||||
user=match.group('user').strip(),
|
||||
password=match.group('pwd').strip(),
|
||||
database=match.group('db').strip(),
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor,
|
||||
)
|
||||
with conn, conn.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT COUNT(1) AS cnt
|
||||
FROM ccdi_purchase_transaction
|
||||
WHERE purchase_id = 'LSFXMOCKPUR001'
|
||||
AND actual_amount > 100000
|
||||
""")
|
||||
print(cursor.fetchone()['cnt'])
|
||||
PY
|
||||
```
|
||||
|
||||
## 执行时间与结果
|
||||
- 2026-03-20 14:50 CST 完成目标回归:
|
||||
`python3 -m pytest tests/test_file_service.py tests/test_statement_service.py tests/test_api.py tests/integration/test_full_workflow.py -v`
|
||||
- 回归结果:`38 passed, 20 warnings in 4.20s`
|
||||
- warnings 为现有 `pydantic` 与 `httpx` 弃用提示,本次改动未新增失败或 error。
|
||||
|
||||
## SQL 基线脚本执行结果
|
||||
- 执行命令:`bin/mysql_utf8_exec.sh sql/migration/2026-03-20-lsfx-mock-random-hit-rule-purchase-baseline.sql`
|
||||
- 执行结果:脚本执行成功,无报错输出。
|
||||
- 脚本内容采用“先删后插”的幂等方式,避免重复执行造成脏数据。
|
||||
|
||||
## 采购基线查询结果
|
||||
- 执行前查询:
|
||||
`SELECT COUNT(1) FROM ccdi_purchase_transaction WHERE purchase_id = 'LSFXMOCKPUR001'`
|
||||
返回 `0`
|
||||
- 执行后查询:
|
||||
`SELECT purchase_id, actual_amount, supplier_name FROM ccdi_purchase_transaction WHERE purchase_id = 'LSFXMOCKPUR001'`
|
||||
返回:
|
||||
- `purchase_id = LSFXMOCKPUR001`
|
||||
- `actual_amount = 186000.00`
|
||||
- `supplier_name = 兰溪市联调供应链有限公司`
|
||||
- 最终门槛校验:
|
||||
`SELECT COUNT(1) ... WHERE purchase_id = 'LSFXMOCKPUR001' AND actual_amount > 100000`
|
||||
返回 `1`
|
||||
|
||||
## 是否发现回归
|
||||
- 未发现本次改动引入的功能回归。
|
||||
- `FileService` 的规则命中计划生成、`statement_rule_samples.py` 的按规则子集装配、`StatementService` 的缓存稳定性、端到端接口链路均已通过验证。
|
||||
- 本次验证未启动额外前后端常驻进程,因此无需执行进程清理。
|
||||
Reference in New Issue
Block a user