> ⚠️ **历史快照(非当前真相源)**:本文件仅保留当时审查/收口结论。当前状态与执行顺序以 `docs/CURRENT_STATE.md`、`docs/ACTIVE_REMEDIATION_2026-07-05_REVIEW.md`、`docs/ACTIVE_EXECUTION_BOARD_2026-07-05_REVIEW_REMEDIATION.md`、`docs/plans/2026-07-05-review-remediation-systemic-fix-plan.md` 和 `reports/REVIEW_REPORT_2026-07-05_COMPREHENSIVE_PROJECT_REVIEW.md` 为准。 --- # LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20 > 内部预审报告 · 用于 PM / Legal 审阅前发现内部一致性与实现漂移 > 审计对象:4 份法务/隐私/运维草案(合计 522 行) > 仓库 HEAD:`604c8b3 (main)` · 工作目录:`/home/long/project/gaokao-volunteer-system` > 审计日期:2026-06-20 --- ## 0. 审计范围 | # | 文件 | 行数 | 最后更新 | | --- | ---------------------------------------- | ---- | ---------- | | 1 | `docs/PRIVACY_POLICY_DRAFT.md` | 65 | 2026-06-14 | | 2 | `docs/SERVICE_TERMS.md` | 53 | 2026-06-14 | | 3 | `docs/LEGAL_PRIVACY_BASELINE.md` | 162 | 2026-06-14 | | 4 | `docs/DELIVERY_RETENTION_OPS_RUNBOOK.md` | 242 | 2026-06-20 | > 任务说明里 runbook 行数标的是 144,实测为 242(含新增的 §8 T12-D 端到端 acceptance)。 > 以下所有引用基于实际行数。 --- ## 1. 内部一致性(4 份文档互相引用) ### 1.1 ✅ `DATA_RETENTION_AND_DELETION.md` 引用闭环 | 引用方 | 引用路径 | 目标文件状态 | | ----------------------------------- | ------------------------------------- | ---------------------------- | | `PRIVACY_POLICY_DRAFT.md:43` | `docs/DATA_RETENTION_AND_DELETION.md` | ✅ 存在(81 行,2026-06-14) | | `SERVICE_TERMS.md:43` | `docs/DATA_RETENTION_AND_DELETION.md` | ✅ 存在 | | `PRIVACY_POLICY_DRAFT.md:43` (隐含) | "数据保存与删除" §5 | 与 §7 顺序自洽 | **结论**:引用闭环成立,未发现悬空引用。 ### 1.2 ⚠️ `consent_channel` 白名单在 `LEGAL_PRIVACY_BASELINE.md` 内部矛盾 `LEGAL_PRIVACY_BASELINE.md` 同一份文件里出现两套渠道列表: - §4 行 86(后台补录渠道):`闲鱼/微信/学校/线下确认`(中文叙事,**4 个**) - §6 行 123(最小字段表):`consent_channel: web / wechat / xianyu / school / admin`(**5 个**) **实现真相**(见 §2.2):4 个 source 落地为 `xianyu / wechat / web / school`,admin 渠道单走 `consent_channel = payload.source`(即 xianyu/wechat/school/web 之一),并未真正出现字面量 `"admin"`。 **结论**: - §6 的 `admin` 渠道值是 **孤儿**——代码侧没有任何 `consent_channel=admin` 的写入路径。 - §4 的 "线下确认" 渠道是 **未落库**——没有被建模为合法 source。 - 应在送 PM/Legal 之前把 §6 改成 `web / wechat / xianyu / school`,并把 §4 的"线下确认"对应到 `school` 或显式说明走 admin 补录。 ### 1.3 ⚠️ `consent_operator` 内部一致性 `LEGAL_PRIVACY_BASELINE.md:84` 声明白名单 `self / guardian / admin_import`(3 个值),但: - 实现 `admin/routes/orders.py:590` 实际只产生 `"guardian" if source == "web" else "admin_import"`,**`self` 永不产生**。 - `admin/tests/test_routes_orders.py:161, 195` 断言非 web 渠道 = `admin_import`。 - 单元测试中无任何 `consent_operator == "self"` 的断言。 - `data/orders/intake_schema.py:26` 字段叫 `guardian_confirmed`,与 `consent_operator=self` 没有对应转换逻辑。 **结论**:`self` 是 **白名单里的僵尸值**。要么在文档里删掉,要么在实现里补一处(例如旧数据导入场景),否则白名单会误导外部审阅。 ### 1.4 ✅ `retention_days = 180` 口径一致 | 位置 | 数值 | 来源 | | --------------------------------------------------- | --------------------------------------------------------------- | -------------------------------- | | `DELIVERY_RETENTION_OPS_RUNBOOK.md:63` | 默认 `180` | `GAOKAO_RETENTION_DAYS` 环境变量 | | `deploy/systemd/gaokao-retention-cleanup.service:9` | `Environment=GAOKAO_RETENTION_DAYS=180` | ✅ | | `deploy/systemd/gaokao-jobs.env.example:9` | `GAOKAO_RETENTION_DAYS=180` | ✅ | | `deploy/cron/gaokao-jobs.crontab:9` | `GAOKAO_RETENTION_DAYS=180` | ✅ | | `admin/config.py:261` | `retention_days=int(os.getenv("GAOKAO_RETENTION_DAYS", "180"))` | ✅ | | `data/orders/deletion_service.py:38` | `DEFAULT_RETENTION_DAYS = 180` | ✅ | | `docs/DATA_RETENTION_AND_DELETION.md:13-17` | 180 天 | ✅ | | `tests/test_retention_cleanup.py` | 显式 `--retention-days 180` | ✅ | **结论**:180 天口径在文档、环境样例、配置默认值、脚本、测试、运行手册中完全一致。 ### 1.5 ⚠️ Runbook 描述的脚本参数默认值与实现不一致 `DELIVERY_RETENTION_OPS_RUNBOOK.md:36-38` 写: > 手工入口:`scripts/gaokao-retention-cleanup.py --cutoff [--dry-run]` > 定时入口:`scripts/gaokao-retention-cleanup.py --retention-days 180 [--dry-run]` 实现侧 `data/orders/retention_cleanup.py:173-182`: ```python cutoff_group = parser.add_mutually_exclusive_group(required=True) cutoff_group.add_argument("--cutoff", ...) cutoff_group.add_argument("--retention-days", type=int, help="...") # 没有 default=180 ``` - `--retention-days` **没有 default**;裸跑 `python3 scripts/gaokao-retention-cleanup.py` 会 `parser.error: one of the arguments --cutoff --retention-days is required`。 - 真正"默认 180"只来自上层调度的环境变量(systemd unit / env example)。 - runbook §3 的 4 条手工命令示范都显式给了 `--cutoff` 或 `--retention-days 180`,**没有误导操作**;但读者会把 "默认 180" 误以为脚本自带兜底。 **结论**:runbook 措辞需要微调——"定时模式(环境变量默认 180)" 比"默认 180" 更准确。 ### 1.6 ✅ 4 份文档之间互引用闭环 | 源 | 引 | 目标 | | -------------------------------------- | ---------------------------------------------------------------------- | ----------- | | `PRIVACY_POLICY_DRAFT.md:43` | `docs/DATA_RETENTION_AND_DELETION.md` | ✅ 存在 | | `SERVICE_TERMS.md:42-43` | `docs/PRIVACY_POLICY_DRAFT.md` + `docs/DATA_RETENTION_AND_DELETION.md` | ✅ 双向闭环 | | `LEGAL_PRIVACY_BASELINE.md` | 仅引用 docs/ 内文件 | ✅ 自洽 | | `DELIVERY_RETENTION_OPS_RUNBOOK.md:78` | `docs/DATA_RETENTION_AND_DELETION.md` | ✅ 存在 | 唯一需要注意:4 份文件的"最后更新"日期分布: - 3 份:2026-06-14 - 1 份:2026-06-20(runbook) 送审前建议把 baseline / privacy / service terms 同步刷新到 2026-06-20,或在审阅说明里标注 runbook 是最新基线、其他三份为 v0.14 基线。 --- ## 2. 实现 vs 文档漂移(A-2 6/20 落地) ### 2.1 ✅ `consent_method` 5 项白名单 = 完全对齐 | 来源 | 列出 | 数量 | | ----------------------------------------- | --------------------------------------------------------------------------- | ---- | | `LEGAL_PRIVACY_BASELINE.md` | (未列具体白名单) | — | | 实现 `admin/routes/orders.py:248-254` | `verbal_chat / phone_recording / screenshot / written_form / self_declared` | 5 | | 实现注释 `admin/routes/orders.py:242-247` | 同上,附语义说明 | 5 | | 测试 `admin/tests/test_routes_orders.py` | 用例覆盖 `verbal_chat` 等 | ✅ | **结论**:5 项白名单实现落地,文档侧 baseline 没有明文列出具体枚举——建议补一段把 5 项枚举写明,避免 PM/Legal 提问。 ### 2.2 ✅ `consent_operator` 白名单实现侧:3 项含 `self` 但实际不产出 如 §1.3 所述,实现只写 `guardian` 或 `admin_import`,`self` 出现在白名单但无生产路径。 ### 2.3 ✅ Source 4 项 = 与 §6 baseline 一致 | 来源 | 列出 | | ---------------------------------------- | ---------------------------------------------------------------- | | 实现 `admin/routes/orders.py:94` | `OrderSource = Literal["xianyu", "wechat", "web", "school"]` | | 实现 `admin/routes/ui.py:141` | `