feat(ops 6/20 v2.1.3): 生产加固 + L-A 送审前修复 + crowd_db 质量契约
## 实现内容 (6 项改动) 1. **admin /health 端点增强** — 主键契约 status:ok 保持 + checks 子对象 - db_writable: connect + CREATE TEMP TABLE + INSERT + SELECT + DROP - disk_writable: 在 ops_alert_log_path 目录创建临时文件 + 删除 - settings_valid: 复用 is_jwt_secret_secure 判断 2. **_enforce_jwt_secret_policy** — prod env 使用 dev 默认 JWT / 长度<32 → fail-closed 3. **_enforce_default_admin_password_policy** — prod env 用 admin123 / 长度<10 / 字符类<3 → fail-closed 4. **L-A R7: admin UI footer** — dashboard.html (592 行) + ui.py 内联 admin/orders/new 模板加 footer 隐私政策 + 数据删除 + 服务说明链接, 与 portal _render_footer_links() 同口径 5. **L-A R1+R4: LEGAL_PRIVACY_BASELINE 文档同步** - §6 移除孤儿 'admin' consent_channel 值 (代码侧从未实际产生) - §7 已具备/尚缺 重写, 显式归到 6/20 增量 6. **Q-A: tests/test_crowd_db_data_quality.py** — 8 个测试锁住 - 27 省总数 + 仅湖南 high + 其它 26 省 ≤ usable - 高考生源大省 (广东/江苏/北京/上海/山东/河南/四川/湖北) 不在 high 集合 - data_year=2025 (6/25 后需显式更新) ## 测试 - 4/4 RED → GREEN (admin/tests/test_health.py: JWT/admin password 拒绝 dev 默认值) - 8/8 GREEN (data/crowd_db/tests/test_crowd_db_data_quality.py: 数据质量契约) - 3 回归修复 (test_app.py + test_routes.py + test_health.py 适配新 /health checks 字段) - 31/31 GREEN (admin/tests/test_app.py + test_routes.py + test_health.py) ## 报告 - reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md (363 行, 9 风险 0 阻塞) - reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md (45 行, CRITICAL 文档失真已规避) ## 文件 M CHANGELOG.md (v2.1.3) M admin/config.py (2 个 _enforce_*_policy + load_settings post-load) M admin/routes/health.py (3 个 _check_* + checks 子对象) M admin/routes/ui.py (admin/orders/new 模板加 footer) M admin/static/dashboard.html (footer 块) M admin/tests/test_app.py (适配 checks 字段 + regex 兼容) M admin/tests/test_health.py (4 个新测试) M admin/tests/test_routes.py (适配 checks 字段) M docs/CURRENT_STATE.md (0.3-0.5 增量段) M docs/LEGAL_PRIVACY_BASELINE.md (§6 清理 + §7 重写) + data/crowd_db/tests/test_crowd_db_data_quality.py (8 tests) + reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md + reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md
This commit is contained in:
363
reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md
Normal file
363
reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md
Normal file
@@ -0,0 +1,363 @@
|
||||
# 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 <ISO8601> [--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` | `<option>` 同样 4 项 |
|
||||
| baseline `LEGAL_PRIVACY_BASELINE.md:86` | `闲鱼/微信/学校/线下确认`(叙事,4 项) |
|
||||
| baseline `LEGAL_PRIVACY_BASELINE.md:123` | `web / wechat / xianyu / school / admin`(5 项,含孤儿 `admin`) |
|
||||
|
||||
**结论**:核心 4 项对齐;`admin` 渠道值是 §6 列表里的多余项(详见 §1.2)。
|
||||
|
||||
### 2.4 ✅ 字段落库:A-2 6/20 已把 4 类字段同步到 `order_intakes` payload
|
||||
|
||||
| 字段 | 落库位置 | 实现证据 |
|
||||
| ------------------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `consent_version` | order_intakes payload | `admin/routes/orders.py:651` 写死 `t12-web-mvp-v1` |
|
||||
| `consent_scope` | order_intakes payload | `web_public.py:1675` 默认 `web-self-service-order-intake`;admin 路径 `${source}-channel-intake` |
|
||||
| `consent_channel` | order_intakes + orders.consent_method | `orders.py:653` 写 `payload.source` |
|
||||
| `consent_operator` | order_intakes | `orders.py:654, 590` |
|
||||
| `consent_method` | order_intakes + orders.consent_method (A-2 冗余) | `orders.py:655, 615`;`schema.py:110-114` 增列 |
|
||||
| `consent_given_at` | order_intakes + orders.consent_given_at (A-2 冗余) | `schema.py:115-116` 增列 |
|
||||
|
||||
**结论**:A-2 落地的 5 个审计字段全部在 admin 路径与 portal 路径双轨写出。orders 表新增的 `consent_method` / `consent_given_at` 列在 `data/orders/schema.py:113-116` 通过 `ALTER TABLE` 兼容迁移——`data/orders/models.py:97-98` 同步加 dataclass 字段。
|
||||
|
||||
### 2.5 ⚠️ 严格审查报告 vs 现状
|
||||
|
||||
`reports/STRICT_COMPREHENSIVE_REVIEW_2026-06-18.md:537`(2026-06-18 写)批评:
|
||||
|
||||
> 落库仍只有 `consent_version`、`consent_scope` 与几个布尔值。
|
||||
|
||||
A-2 (6/20) 已经把这条批评点修复:`consent_given_at / privacy_accepted_at / service_terms_accepted_at / consent_channel / consent_operator` 全部落库。
|
||||
|
||||
**结论**:这条历史批评点已修复,但 strict review 报告本身没有更新;建议在下次 strict review 时回填修复状态。
|
||||
|
||||
---
|
||||
|
||||
## 3. T12-D 端到端 Acceptance 步骤完整性(runbook §8)
|
||||
|
||||
### 3.1 步骤清单与实现交叉验证
|
||||
|
||||
| 步骤 | runbook §8.x | 实现证据 | 状态 |
|
||||
| -------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------- |
|
||||
| §8.1 前置环境(export 4 个变量) | `PY/DB_PATH/SHARE_DB_PATH/LOG_PATH/FERNET_KEY` | 全部为 retention cleanup 入口的环境变量 | ✅ 完整 |
|
||||
| §8.2.1 跑回归测试 | `$PY -m pytest tests/test_retention_cleanup.py -q` | 文件中实测有 **6 个 test function**(行 37/52/73/98/123/167) | ✅ 数量正确 |
|
||||
| §8.2.2 端到端 smoke 脚本 | seed 4 订单 + apply + 验证 | `data/orders/retention_cleanup.py:run_cleanup()`、`data/orders/dao.py`(T12-D 修复后 owns_conn) | ✅ 完整 |
|
||||
| §8.3 验收矩阵 | 7 行 checklist | 对应 `run_cleanup` 返回的 `candidates / anonymized / deletion_logs_pruned / share_events_pruned` | ✅ 完整 |
|
||||
| §8.4 历史 bug 背景 | `OrdersDAO.__exit__` 关外部 conn | `data/orders/dao.py` 增加 `owns_conn: bool = False` 参数 | ✅ 已修复并解释 |
|
||||
| §8.5 部署前 checklist | 5 行 `- [ ]` 项 | 全部对应 systemd/journalctl/测试套件 | ✅ 完整 |
|
||||
|
||||
### 3.2 步骤中可执行性检查
|
||||
|
||||
- §8.2.1 的 smoke 脚本是 inline Python `-c "..."`,**易复制但出错时栈跟踪难看**。建议改成 `scripts/smoke_retention_cleanup.py` 脚本文件,runbook 改成调脚本。
|
||||
- §8.2.2 的 `Order(..., created_at='2024-12-01T00:00:00+00:00')` 是固定时间戳,cutoff `2025-06-30T00:00:00+00:00`,**只要时钟在 2025-06-30 之后都成立**。runbook 没标注这条隐性假设。
|
||||
- §8.5 没有把 `systemctl status` 的"成功"判定值写明——只列了命令。这是已知可接受的最小步骤。
|
||||
|
||||
**结论**:T12-D acceptance 步骤 **完整且可执行**。微调建议是脚本提取 + 时间戳假设显式化。
|
||||
|
||||
---
|
||||
|
||||
## 4. 缺失项识别
|
||||
|
||||
### 4.1 ✅ 隐私政策 §5 引用 `DATA_RETENTION_AND_DELETION.md` 已存在
|
||||
|
||||
- 文件存在:`docs/DATA_RETENTION_AND_DELETION.md`(81 行)
|
||||
- 内容覆盖:保留期表(§2 表格 9 行)、删除请求处理原则(§3)、当前系统能力与缺口(§4)、MVP 最低要求(§5)、最小调度与 runbook 引用(§6)
|
||||
- 关键数字 `180` 出现在 §2 表格(订单/资料/报告/通知/支付)和 §6 命令行
|
||||
- 与 `DELIVERY_RETENTION_OPS_RUNBOOK.md` 互引:runbook 行 78 / retention_doc 行 53
|
||||
|
||||
**结论**:引用文件**存在且内容完整**,未发现悬空或残缺。
|
||||
|
||||
### 4.2 ⚠️ `LEGAL_PRIVACY_BASELINE.md` §7 "尚缺"列表与现实存在部分漂移
|
||||
|
||||
baseline §7 (行 142-146) 写:
|
||||
|
||||
> 尚缺:
|
||||
>
|
||||
> - 后台代录 / 外部渠道补录的同意审计统一化
|
||||
> - 数据删除 SOP 的脚本化/产品化
|
||||
> - 对外正式法务审阅与版本管理
|
||||
|
||||
A-2 (6/20) + T12-D (6/20) 已经把前两条都做了:
|
||||
|
||||
- 后台/外部渠道同意审计已统一化(`consent_method` + `consent_operator` + `consent_channel` 全部白名单化并落库)
|
||||
- 数据删除 SOP 已脚本化(`scripts/gaokao-retention-cleanup.py` 配合 systemd/cron 样例)
|
||||
|
||||
**结论**:baseline §7 "尚缺" 列表需要在下次刷新时把前两条移到"已具备"侧,仅保留"对外正式法务审阅与版本管理"。
|
||||
|
||||
### 4.3 ⚠️ "线下确认"渠道的最终落地
|
||||
|
||||
baseline §4 写 `闲鱼/微信/学校/线下确认`,但实现侧 4 个 source 中没有"线下"——`school` 看上去承接了线下场景,但语义上"线下确认"(如家长到场签纸质单)≠ "school 渠道"。
|
||||
|
||||
**结论**:建议在 baseline §4 显式说明"线下确认 = school 渠道" 或新增 `offline` source(如果业务确实要区分)。
|
||||
|
||||
### 4.4 ⚠️ `consent_version` 没有走基线化的版本号管理
|
||||
|
||||
实现侧 `admin/routes/web_public.py:1674` 与 `admin/routes/orders.py:651` 都硬编码 `t12-web-mvp-v1`:
|
||||
|
||||
- privacy policy 与 service terms 还没有声明"当前生效版本号"
|
||||
- 文档侧用 `最后更新: 2026-06-14` 的自然日期;代码侧用语义化 `v1`
|
||||
- 当 privacy policy / service terms 改版时,没有机制把 `consent_version` 一起升档
|
||||
|
||||
**结论**:送 PM/Legal 之前需要在 baseline §6 加一段"版本升级机制":什么时候 bump `consent_version`,是否需要用户重新同意。
|
||||
|
||||
### 4.5 ⚠️ Portal 提交路径 vs Admin 提交路径的 `consent_scope` 命名空间不一致
|
||||
|
||||
| 路径 | consent_scope 字面量 |
|
||||
| ----------------------------- | ------------------------------------------------------- |
|
||||
| Portal (`web_public.py:1675`) | `web-self-service-order-intake` |
|
||||
| Admin (`orders.py:652`) | `{source}-channel-intake`(如 `xianyu-channel-intake`) |
|
||||
|
||||
两条路径在数据库侧都是字符串,**没有枚举**——下游做合规报表/审计聚合时需要做模糊匹配,违反 "consent_scope 必须来自受控白名单" 的合规最小要求。
|
||||
|
||||
**结论**:建议把 consent_scope 提升为 Literal 与 consent_method 同级。
|
||||
|
||||
---
|
||||
|
||||
## 5. 前台文案入口:admin UI 模板 footer 隐私政策链接
|
||||
|
||||
### 5.1 admin UI 路由清单(`admin/routes/ui.py`)
|
||||
|
||||
| 路径 | 处理函数 | 模板来源 | footer 隐私政策链接 |
|
||||
| -------------------------------- | ------------------------ | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `/dashboard`、`/admin/dashboard` | `dashboard_page()` | `admin/static/dashboard.html` | ❌ **无 footer**(592 行 HTML,无 `<footer>` 标签) |
|
||||
| `/admin/orders/new` | `admin_new_order_page()` | `_render_admin_new_order_page()` 内联 HTML(ui.py:75-182) | ❌ **无 footer**(只在 `</body>` 前一段 `<script>`,无任何 `<footer>`/`<a href="/privacy">`) |
|
||||
| `/s/{code}` | `share_page()` | `admin/share_page.py:158` 有 `.footer-panel` | ⚠️ **是 share 页 footer,不是隐私政策链接** |
|
||||
|
||||
### 5.2 对比:web_public.py 的前台 portal 页
|
||||
|
||||
`admin/routes/web_public.py:749-763` 提供了 `_render_footer_links()`:
|
||||
|
||||
```python
|
||||
f'<footer style="margin-top:24px;color:#5b6b88;font-size:14px;">'
|
||||
f'<a href="{privacy_href}">隐私政策</a> · '
|
||||
```
|
||||
|
||||
测试 `admin/tests/test_web_public.py:445-464` 验证 `landing` / `pricing` / `privacy` 页都有 `href="/privacy"` 链接 + `/privacy` 路由可访问。
|
||||
|
||||
**结论**:
|
||||
|
||||
- **portal 前台页有 footer 隐私政策链接 ✅**
|
||||
- **admin 后台页(dashboard.html + ui.py 内联页)均无 footer 隐私政策链接 ❌**
|
||||
- `dashboard.html` 整个文件 592 行无 `<footer>` 标签,缺明显的合规入口
|
||||
|
||||
### 5.3 严重程度判断
|
||||
|
||||
baseline §5 提到:
|
||||
|
||||
> 建议文案入口:下单页提交前 / 资料填写页提交前 / **页脚"隐私政策 / 数据删除说明"链接**
|
||||
|
||||
baseline §8 提到:
|
||||
|
||||
> 前台存在可见入口(隐私政策 + 数据删除规则)
|
||||
|
||||
**严重度**:中。
|
||||
|
||||
- legal baseline §5 用词是 "前台",可解释为"用户端 web"——portal 已有 footer。
|
||||
- 但严格意义上,**后台 dashboard 缺 footer 隐私政策**会留下"内部用户完全不知道数据策略"的口子;如果后台操作员误以为后台操作可豁免合规,会被外部审计抓到。
|
||||
- 推荐修法:在 `dashboard.html` 末尾加 `<footer>` 块,链接到 `/privacy` 与 `/deletion`(或对应路由);ui.py 的内联 admin 新单页同样加 footer。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险评级汇总
|
||||
|
||||
| ID | 风险 | 等级 | 阻塞送审? |
|
||||
| --- | ------------------------------------------------------- | ---- | ----------------------------------------- |
|
||||
| R1 | baseline §6 `admin` 渠道值孤儿 + §4 "线下确认" 未建模 | 中 | **建议在送审前修** |
|
||||
| R2 | `consent_operator=self` 僵尸白名单 | 低 | 建议在送审前删 |
|
||||
| R3 | runbook `--retention-days 180` 措辞歧义(无 default) | 低 | 文档措辞改一下 |
|
||||
| R4 | baseline §7 "尚缺" 列表未同步 A-2/T12-D 进展 | 中 | **建议在送审前同步** |
|
||||
| R5 | `consent_version` 硬编码无升级机制 | 中 | 建议在 baseline §6 加一段 |
|
||||
| R6 | `consent_scope` 不是枚举(portal/admin 命名空间不统一) | 中 | 建议在 baseline §6 加一段或提升为 Literal |
|
||||
| R7 | admin 后台 dashboard.html 无 footer 隐私政策链接 | 中 | **建议在送审前补** |
|
||||
| R8 | strict review 2026-06-18 报告未回填 A-2 修复状态 | 低 | 顺手做 |
|
||||
| R9 | 3 份文档 "最后更新" 还是 6/14,runbook 已 6/20 | 低 | 措辞补一句 |
|
||||
|
||||
**送审阻塞级**:0 个。
|
||||
**建议在送审前修**:R1、R4、R7(3 个中等风险)。
|
||||
**可以下个迭代修**:R2、R3、R5、R6、R8、R9(6 个低/中风险)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 给 PM / Legal 的提示清单
|
||||
|
||||
1. **A-2 6/20 落地的 5 个同意审计字段已经在 admin/portal 双轨写库**——可以对外说"同意审计口径已统一化",但仍不可说"监护人同意闭环已整体完成"(baseline §4 末段措辞)。
|
||||
2. **source 实际只 4 项**(`xianyu / wechat / web / school`),文档里出现过的 `admin` 渠道值从未被实际写入;送审前请把 baseline §6 的 `admin` 去掉。
|
||||
3. **`consent_operator` 只产生 2 个值**(`guardian` for web, `admin_import` for other 3 source),`self` 永不使用——白名单可删 `self`,或为旧数据导入场景补一条生产路径。
|
||||
4. **admin 后台页面(dashboard.html + admin/orders/new)还没有 footer 隐私政策链接**——portal 前台有,admin 后台没有;如内部操作员被外部审计问到,会缺一个合规入口。
|
||||
5. **retention_days=180 在文档/环境/代码/测试/运行手册五处完全对齐**,可以放心。
|
||||
6. **T12-D acceptance 步骤(runbook §8)完整且可执行**,6 个 pytest 全过是已知可达状态;建议把 inline `-c` 提到 `scripts/smoke_retention_cleanup.py` 改善可维护性。
|
||||
7. **DATA_RETENTION_AND_DELETION.md(81 行)存在且内容完整**——隐私政策 §5 的引用闭环成立。
|
||||
|
||||
---
|
||||
|
||||
## 8. 附录:关键引用行号速查
|
||||
|
||||
| 引用 | 位置 |
|
||||
| --------------------------------------- | ------------------------------------------------------------ |
|
||||
| `consent_method` Literal | `admin/routes/orders.py:248-254` |
|
||||
| `consent_method` 列迁移 | `data/orders/schema.py:113-114` |
|
||||
| `consent_given_at` 列迁移 | `data/orders/schema.py:115-116` |
|
||||
| `OrderSource` Literal | `admin/routes/orders.py:94` |
|
||||
| `consent_operator` 计算 | `admin/routes/orders.py:590` |
|
||||
| `consent_scope` portal | `admin/routes/web_public.py:1675` |
|
||||
| `consent_scope` admin | `admin/routes/orders.py:652` |
|
||||
| 4 source 内联选项 | `admin/routes/ui.py:141` |
|
||||
| dashboard.html 模板 | `admin/static/dashboard.html`(592 行,无 footer) |
|
||||
| `_render_footer_links` | `admin/routes/web_public.py:749-763` |
|
||||
| retention cleanup 入口 | `scripts/gaokao-retention-cleanup.py` |
|
||||
| retention 默认 180 | `admin/config.py:261` / `data/orders/deletion_service.py:38` |
|
||||
| retention `--retention-days` 无 default | `data/orders/retention_cleanup.py:178-182` |
|
||||
| retention 回归测试 6 个 | `tests/test_retention_cleanup.py:37/52/73/98/123/167` |
|
||||
| T12-D 修复点 | `data/orders/dao.py` `owns_conn: bool = False` |
|
||||
| A-2 整改文档 | `docs/ACTIVE_REMEDIATION_2026-06-20.md` |
|
||||
| 历史批评点(已修复) | `reports/STRICT_COMPREHENSIVE_REVIEW_2026-06-18.md:537` |
|
||||
|
||||
---
|
||||
|
||||
## 9. 审计结论
|
||||
|
||||
**送审整体判断**:4 份文档作为 T12 上线前法务/隐私基线草案**结构完整、引用闭环成立、A-2 6/20 落地的事实准确反映在实现层**。
|
||||
|
||||
**送审前**建议处理 3 项中等风险:
|
||||
|
||||
1. baseline §6 删 `admin` 渠道值 / §4 解释 "线下确认" 对应关系(R1)
|
||||
2. baseline §7 "尚缺" 列表同步 A-2/T12-D 进展(R4)
|
||||
3. admin 后台页面补 footer 隐私政策链接(R7)
|
||||
|
||||
**可以下个迭代处理**:6 项低/中风险(R2/R3/R5/R6/R8/R9),不影响本次送审。
|
||||
|
||||
**总体送审建议**:✅ 可送 PM / Legal 审阅,附本报告作为"已知问题与下一迭代计划"附页。
|
||||
45
reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md
Normal file
45
reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Q-A 数据质量审计报告 — 非湖南数据密度
|
||||
|
||||
审计时间: 2026-06-20 | HEAD: 604c8b3 | 范围: `data/crowd_db/*`
|
||||
|
||||
## 1. crowd_db 现状 (省份覆盖, 时间窗口, 关键字段)
|
||||
|
||||
- **省份覆盖**: 27 个 JSON 文件齐全(23 省 + 4 直辖市),不含 5 自治区/港澳台。结构 100% 落地。
|
||||
- **时间窗口**: 所有 27 个文件 `data_year=2025`、`last_updated=2026-06-12`(同一批次生成)。
|
||||
- **关键字段**: 顶层 8 字段(province / last_updated / data_year / source / source_url / source_type / confidence / score_ranges)全部齐备,符合 T3.1 schema。
|
||||
- **不存位次/分数/选科/院校代码原始字段**——本数据库是"大厂 AI 反扎堆推荐"库,不是录取数据库;院校用 name+major 标识,无标准院校代码。
|
||||
|
||||
## 2. 非湖南数据密度评估
|
||||
|
||||
| 维度 | 湖南 | 其它 26 省 | 高考生源大省(北京/上海/广东/江苏/山东/河南/四川) |
|
||||
| -------------------- | ---------- | --------------- | -------------------------------------------------- |
|
||||
| confidence | 0.85 | 0.45 全部 | 0.45 全部 |
|
||||
| quality_level | high (A级) | skeleton (C级) | skeleton (C级) |
|
||||
| score_ranges 段数 | 10 | 1-3 | 2-3 |
|
||||
| recommendations 条数 | 68 | 1-8 | 2-8 |
|
||||
| alternatives 条数 | 136 | 0-3 | 0-3 |
|
||||
| 覆盖分数段 | 440-690 | 多为头部 1-2 段 | 头部 2-3 段 |
|
||||
|
||||
**结论**: 湖南与其它 26 省是"高原+26 骨架"二八结构。高考生源大省无一例外都是 C 级骨架,平均只覆盖头部 2-3 个分数段、2-8 条推荐;典型如广东 2 段/4 条、贵州仅 1 段/1 条,**不足以驱动反扎堆结论**。
|
||||
|
||||
## 3. 已有数据质量工具
|
||||
|
||||
- `data/crowd_db/quality_summary.py` + `python -m data.crowd_db.quality_summary --human`:列出 27 省质量分级(实测 high=1 / skeleton=26)。
|
||||
- `data/crowd_db/cli.py` + `scripts/gaokao-data-trace`:按校名溯源查询(输出 confidence / quality_level / last_updated)。
|
||||
- `data/crowd_db/risk_report.py`:三色风险分级,模板已绑 `audit_report.html`。
|
||||
- 测试: `tests/test_crowd_db_data_quality.py`(**设计文档要求**,实测**未在仓库找到该文件**——缺口)。`data/crowd_db/tests/test_provenance.py` 已锁死湖南 confidence≥0.8、record_count>0,但未锁死"非湖南 ≤ skeleton"。
|
||||
|
||||
## 4. 2026 高考季数据完备性
|
||||
|
||||
- **2024 历史数据**: 仓库无 2024 单独快照(`data_year=2025` 即为当前)。
|
||||
- **2025 实际数据**: 27 省全部为 `data_year=2025`,但实质只有湖南是真实整理。
|
||||
- **2026 模拟数据**: 不存在。`data_completeness=skeleton` 的 26 省 `predicted_increase` 是"预测 2026 上涨分"(仅 1 条/省的元字段),不是 2026 模拟卷。
|
||||
- **官方进度**:`spec_checker_v2.py:780` 注释"2026 年招生计划 6/15-20 公布、位次 6/25 出分后确定"——**当前 6/20 处于真空期**,可解释为何 2026 数据未落库。
|
||||
|
||||
## 5. 推荐改进 Top 5
|
||||
|
||||
1. **补 `tests/test_crowd_db_data_quality.py`**(CROWD_DB_DATA_QUALITY §7 承诺的锁死文件),断言"湖南 high + 其它 26 省 in {medium, skeleton}"——当前测试网有缺口。
|
||||
2. **明确 2026 高考季数据来源策略**:在 `data/crowd_db/README.md` 加 `data_year=2026` 字段约定与"6/25 前仅 2025 基线+湖南优先"口径。
|
||||
3. **为广东/江苏/山东/河南/四川优先扩容**——这些是 top 5 考生大省,当前 C 级骨架对业务无支撑意义;按湖南 10 段模板补到 medium 即可大幅改善报告可用性。
|
||||
4. **CRITICAL:修正 `docs/CURRENT_STATE.md:302`** —— 文档写"27 省 crowd_db 均为高置信强推荐数据",与代码事实(仅 1 省 high)严重不符,存在合规风险。
|
||||
5. **加 `data_completeness` 字段到 JSON 顶层**(CROWD_DB_DATA_QUALITY §2 已约定,但当前 27 个 JSON 均无此字段,仅以 `confidence` 间接推导),与 `quality_level` 一一对应后报告渲染可自动加 `[置信度: 中]` 标签。
|
||||
Reference in New Issue
Block a user