Files
group_fqcd_jr/docs/28-场外与推广域数据表登记.md
T
qyqy f7a666fb5a fix(env)+docs: 按架构师第二轮纠正 mypy 根因(环境版本);补场外/推广域表登记
架构师第二轮答复里有一条技术纠正是对的,我原归因错了:
- 我原判断"184 个 mypy 错是因为缺 SQLAlchemy 2.0 类型信息"——**方向对了一半、结论反了**。
  SQLAlchemy 2.0 自带 py.typed,根本不需要 sqlalchemy2-stubs(那是 1.4 用的)。
- 实测复现矩阵(决定性证据):
    SQLAlchemy 2.0.34 + mypy 1.14.1 → 173 错(我原来的环境)
    SQLAlchemy 2.0.34 + mypy 1.20.2 → 173 错
    SQLAlchemy 2.0.52 + mypy 1.14.1 →   6 错
    SQLAlchemy 2.0.52 + mypy 1.20.2 →   3 错
  ⇒ 主因是 SQLAlchemy 的**补丁版本**(173→6),mypy 版本是次因(6→3)。
- 已把环境升到 SQLAlchemy 2.0.52 + mypy 1.20.2(均在 pyproject 约束内),
  mypy 从 184 降到 **3 个错**(剩下的 3 个全在同事的 offsite 文件里,非本线代码)。
- 没有把 sqlalchemy2-stubs 写进依赖(采纳架构师明确要求)。
- 交接文档已重写该条,并留下"两个反面教训":不要装 1.4 的存根包;补丁版差异会造成量级差异,
  若要门禁稳定需把 SQLAlchemy 钉到具体补丁版(属公共约定,未擅自改)。

按架构师裁决补文档(他裁定:不动 docs/00,另立登记):
- 新增 docs/28-场外与推广域数据表登记.md:17 张表逐表登记(表名/来源迁移/归属域/当前行数),
  并做规则 8 的**两向边界核对**——场内代码零引用这 17 张表(config.py 里的 offsite_ 只是配置项名)、
  场外代码零写场内交易表
- docs/08 审计口径更新为「场内 51 + 场外/推广 17 = 68」,并新增"第四种坏状态"
  (alembic_version 已指向新 revision 但表没建)的处置说明
- 澄清一个易误读点:audit_schema.py 的期望集合是**动态推导**的(读 baseline_generated.sql
  + 扫描 alembic/versions/*.py),表数 51→68 是自动结果,**没有人手工改期望值**;
  代价是只增不减(DROP 表会报 missing 假失败)

测试:3 failed(1 既有 + 2 环境相关)/ 1218 passed;文档守卫 35 份无重号;
mypy 3 错(180 文件);结构审计 68 张表通过。
2026-09-11 20:00:56 +08:00

147 lines
6.8 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.
# 场外与推广域数据表登记(`docs/00` 基线的域外补充)
> **用途**:`docs/00-新数据库基线设计.md` 冻结的是**场内基金模拟交易域**的基线(不可变,规则 1)。
> 同事那条线(袁聪)合并进来的 **17 张表属场外基金运营域与推广域**,**不进 `docs/00`**,
> 由本文单独登记,避免"基线"这个概念因为塞入独立域而失效。
>
> **维护约定**:新增/删除这两个域的表时**必须同步本文**;本文是「68 张表从哪来」的唯一答案。
> **表数口径**:场内 **51** + 场外/推广 **17** = **68**(共 69 张含 `alembic_version`)。
---
## 1. 为什么另立文档(架构师裁决,2026-09-11)
依据 `AGENTS.md` 规则 8:
> 当前系统业务功能只针对场内基金模拟交易;**场外基金运营流程独立,不得写入场内交易表**。
⇒ 场外与推广是**独立域**。把它们塞进 `docs/00` 会让"基线"从"场内交易域的冻结定义"
变成"当前全部表",下次有人拿 `docs/00` 当依据时会错。因此:
| 文档 | 职责 |
|---|---|
| `docs/00-新数据库基线设计.md` | **场内交易域**基线(不可变,51 张) |
| **本文** | 场外/推广域(17 张)的登记与来源 |
| `docs/08-数据库结构审计基线.md` | 审计口径:场内 51 + 场外/推广 17 = 68 |
| `tools/audit_schema.py` | 期望值同为 68(已生效,见 §4) |
---
## 2. 17 张表逐表登记
### 2.1 场外基金运营域(`offsite_*`,10 张)
| 表名 | 来源迁移 | 建立它的提交意图 |
|---|---|---|
| `offsite_fund_mail` | `20260910_offsite_fund` | 场外申购邮件记录(运营入口) |
| `offsite_fund_attachment` | `20260910_offsite_fund` | 邮件附件(**该域唯一有数据的表,见 §3**) |
| `offsite_fund_document` | `20260910_offsite_fund` | 识别后的业务单据 |
| `offsite_execution_plan_task` | `20260910_offsite_fund` | 执行计划任务 |
| `offsite_rule_result` | `20260910_offsite_fund` | 规则校验结果 |
| `offsite_query_record` | `20260910_offsite_fund` | NL2SQL 查询记录 |
| `offsite_notification` | `20260910_offsite_fund` | 对外通知 |
| `offsite_mail_cursor` | `20260910_offsite_worker` | 邮件 Worker 的游标状态 |
| `offsite_recognition_attempt` | `20260910_offsite_recognition_attempt` | 识别尝试追踪(OCR/LLM 各一次尝试) |
| `offsite_field_correction` | `20260911_offsite_field_correction` | 人工字段纠正记录 |
### 2.2 推广域(`promotion_*`,7 张)
| 表名 | 来源迁移 | 用途 |
|---|---|---|
| `promotion_material_task` | `20260910_promotion_material` | 推广素材任务(主表) |
| `promotion_material_version` | `20260910_promotion_material` | 素材版本 |
| `promotion_input_snapshot` | `20260910_promotion_material` | 生成输入快照(可复现) |
| `promotion_attachment` | `20260910_promotion_material` | 素材附件(海报/PPT) |
| `promotion_compliance_check` | `20260910_promotion_material` | 合规校验结果 |
| `promotion_review_record` | `20260910_promotion_material` | 审核记录 |
| `promotion_delivery_record` | `20260910_promotion_material` | 投递记录 |
### 2.3 迁移文件清单(11 个,含 6 个 merge/辅助)
```
建表相关:20260910_offsite_fund / _offsite_worker / _offsite_recognition_attempt /
_promotion_material / _promotion_poster / 20260911_offsite_field_correction
合并链: 20260910_merge_heads / 20260911_merge_review_separation /
20260911_merge_risk_heads(当前 head)
其他: 20260910_offsite_worker_identity / 20260911_drop_review_separation
```
> ⚠️ **两个环境都踩过的坑**:本库曾处于"`alembic_version` 已指向同事的 revision,
> 但 `offsite_*`/`promotion_*` 表一张都没有"的状态(**版本号跑了、表没建**);
> 架构师那边则是"表没建、版本号也没跑"。**两种坏状态都靠 `alembic upgrade heads` 收敛。**
---
## 3. 当前数据量(2026-09-11 实测)
| 表 | 行数 |
|---|---|
`offsite_fund_attachment` | **60** |
其余 16 张 | **0** |
> 说明:只有附件表有数据(同事调试/演示时落盘的附件)。**这不是"该域已在生产运行"的证据**,
> 也不代表其它表可以删——**禁止删表**(规则 3)。
---
## 4. 跨域边界核对(规则 8 的两向检查)
### 4.1 场内代码是否引用这 17 张表 → **否** ✅
`app/` 下搜 `offsite_|promotion_` 只有 12 处命中,**全部是 `app/core/config.py` 里的环境变量名**
(`offsite_mailbox`、`offsite_allowed_senders` 等),**没有任何 SQL/ORM 级引用**。
场内模型(`app/model/fund.py` 等)与只读契约(`FUND_TABLES`)都只覆盖 `fin_*`。
### 4.2 场外代码是否写场内交易表 → **否** ✅
在 `app/service/offsite*.py` 与 `app/model/offsite*.py` 里搜
`fin_sim_order|fin_capital_flow|fin_cash_ledger|fin_transaction|fin_holding` —— **零命中**。
场外域只读写自己的 `offsite_*` 表。
⇒ **两个方向都不越界**,符合规则 8。这一点在合并评审时是要点,故在此留档。
---
## 5. 对审计工具的影响(**不是"有人偷偷建表"**)
```
$ .\.venv\Scripts\python.exe tools\audit_schema.py
schema audit passed: 68 business tables, no missing or unexpected tables
```
**表数从 51 跳到 68 是同事那 11 个迁移的**预期结果**,不是有人绕过基线偷偷建表。**
机制要说清楚(否则会被误读成"期望值被手工改过"):`tools/audit_schema.py` 的期望表集合
是**动态推导**的,不是一份手写清单:
```python
def expected_tables() -> set[str]:
tables = <从 alembic/baseline_generated.sql 正则提取 CREATE TABLE>
for path in (alembic/versions/*.py):
tables |= <从每份迁移里正则提取 CREATE TABLE> # ← 17 张新表由此自动进入
return tables
```
⇒ **期望值随迁移自动增长**,没有人手工改过它。优点是不会漏;**代价**是它只增不减 ——
若将来有迁移 **DROP** 掉某张表,期望集合仍会包含它,审计会报 `missing` 的假失败。
真要删表时(记得规则 3:**禁止删除已有表**)需同步调整该工具。
> ⚠️ 若你那边 `audit_schema.py` 报 `unexpected`,先确认是"库里多表"还是"迁移没进来"——
> 前者是绕过基线的违规,后者只是迁移没合。两者处置完全不同。
---
## 6. 与 `docs/00` 的关系(一句话)
**`docs/00` 一个字段、一张表都没动**(规则 1)。本文是它的**域外补充登记**,
两者合起来才是"当前库里的全部表":
```
docs/00(场内 51) + 本文(场外/推广 17) = 68 张业务表
```
---
*登记时点:2026-09-11 登记依据:现库 `information_schema` 实测 + `alembic/versions` 逐文件核对 +
`tools/audit_schema.py` 的期望值推导机制核对。*