Files
gaokao-volunteer-system/CHANGELOG.md
Hermes Agent a8b49276e8
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
feat(share): complete poster generation and unify share CLI surface
2026-06-30 17:48:58 +08:00

670 lines
50 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.
# 变更日志
记录高考志愿填报智能系统的所有变更。
---
## v2.1.8 (2026-06-24) — 测试弃用 warning 清零
### 🧪 测试依赖
- `requirements-dev.txt` 新增 `httpx2>=2.0.0`
- `starlette.testclient``httpx2` 的弃用 warning 已清除
### ✅ 验证
- `./.venv/bin/python -m pytest -q admin/tests/test_order_status_page.py admin/tests/test_web_public.py admin/tests/test_web_public_content_pages.py admin/tests/test_web_public_review_flow.py admin/tests/test_web_public_portal_info.py admin/tests/test_order_deletion.py admin/tests/test_routes_cases.py admin/tests/test_routes_stats_dashboard.py admin/tests/test_admin_ui_pages.py admin/tests/test_order_info_form.py data/orders/tests/test_schema.py data/orders/tests/test_public_flow.py tests/test_backup_restore_service_level.py``149 passed`
## v2.1.7 (2026-06-24) — 严格审查汇总版与最新入口切换
### 📄 审查与文档
- 新增 `reports/STRICT_SYSTEM_REVIEW_2026-06-24.md`
- 以当前仓库状态重写严格审查汇总版,不再继续在 6/23 过程稿上叠加历史结论
- 明确当前结论:前台主链路、后台安全边界、恢复演练口径、后台录单契约、第五轮补充问题均已修复
- `docs/CURRENT_STATE.md` 已切换为以 `STRICT_SYSTEM_REVIEW_2026-06-24.md` 作为最新严格审查结论
- `docs/NAVIGATION.md` 已新增 6/24 汇总版入口,同时保留 6/23 过程稿作为历史轨迹参考
### ✅ 验证
- `./.venv/bin/python -m pytest -q admin/tests/test_order_status_page.py admin/tests/test_web_public.py admin/tests/test_web_public_content_pages.py admin/tests/test_web_public_review_flow.py admin/tests/test_web_public_portal_info.py admin/tests/test_order_deletion.py admin/tests/test_routes_cases.py admin/tests/test_routes_stats_dashboard.py admin/tests/test_admin_ui_pages.py admin/tests/test_order_info_form.py data/orders/tests/test_schema.py data/orders/tests/test_public_flow.py tests/test_backup_restore_service_level.py``149 passed, 1 warning`
## v2.1.6 (2026-06-23) — 主链路真实 TestClient 覆盖收口
### 🧪 测试可信度收口
- **用户端主链路不再依赖 `RouteClient` 自证**
- 公开页、内容页、资料页、状态页、报告页、CWB、full-plan、payment-success 的关键页面访问已迁到真实 `TestClient`
- `POST /api/public/orders``POST /review/action`(真实 form + 303`POST /pay/mock/{payment_id}/complete` 的主链路已迁到真实 `client`
- **前台关键测试文件已清零 `RouteClient`**
- `admin/tests/test_web_public.py`
- `admin/tests/test_web_public_content_pages.py`
- `admin/tests/test_web_public_review_flow.py`
- `admin/tests/test_web_public_portal_info.py`
- `admin/tests/test_order_status_page.py`
- **剩余 `RouteClient` 已降级为非前台主证据**
- 仅作为仓库中的轻量辅助夹具保留,不再用于这批关键前台测试文件
### 📄 文档
- 新增 `reports/TEST_CLIENT_COVERAGE_2026-06-23.md`
- `reports/STRICT_SYSTEM_REVIEW_2026-06-23.md` 与覆盖报告已互链
- `docs/CURRENT_STATE.md` 已将两份 6/23 报告纳入真相源优先级前部
### ✅ 验证
- `./.venv/bin/python -m pytest -q admin/tests/test_web_public.py admin/tests/test_web_public_review_flow.py admin/tests/test_web_public_portal_info.py``54 passed, 1 warning`
- `./.venv/bin/python -m pytest -q admin/tests/test_order_status_page.py admin/tests/test_web_public.py admin/tests/test_web_public_content_pages.py admin/tests/test_web_public_review_flow.py admin/tests/test_web_public_portal_info.py data/orders/tests/test_schema.py``89 passed, 1 warning`
## v2.1.5 (2026-06-23) — P1/P2 页面升级与轻量版本管理
### ✨ 新增
- **Step 2-4 / P2 偏好收口**
- Portal Step 2 新增 `target_cities` 输入控件
- `submit_order_info` 现在保留 `profile_versions[]``latest_profile_version_id`,相同快照不重复记版本
- **报告页 / 冲稳保页辅助能力升级**
- 报告页新增“基于最新档案生成 / 基于历史档案版本生成,建议刷新”判断
- 报告页与冲稳保页新增政策中心、同分段参考入口与辅助判断因子区块
- 完整规划入口页新增版本历史与辅助判断因子区块
- **统一信任条**
- 政策中心与同分段参考页改为共用统一信任条:来源、更新时间、适用范围、适用省份、置信等级、边界文案
- 同分段页继续保留“非高置信数据不得作为强推荐依据”约束
- **轻量报告版本元数据**
- `order_intakes.payload_json` 新增 `report_versions[]` / `latest_report_version_id`
- 报告页外显与 `profile_versions[]` / `review_results{}` 形成最小版本关系
### 🧪 测试
- `admin/tests/test_web_public_portal_info.py`
- `test_portal_info_page_renders_target_cities_field`
- `test_submit_order_info_creates_profile_version_history_without_duplicate_snapshots`
- `admin/tests/test_web_public_review_flow.py`
- `test_cwb_page_links_policy_same_score_and_auxiliary_factors`
- `test_report_page_shows_latest_profile_state_and_helper_links`
- `test_report_page_warns_when_based_on_historical_profile_version`
- `test_full_plan_page_shows_profile_versions_and_assessment_context`
- `admin/tests/test_web_public_content_pages.py`
- `test_policy_and_same_score_pages_expose_helper_navigation`
- 回归:`./.venv/bin/python -m pytest -q admin/tests/test_web_public_content_pages.py admin/tests/test_web_public_portal_info.py admin/tests/test_web_public_review_flow.py data/orders/tests/test_schema.py``60 passed`
## v2.1.4 (2026-06-23) — 审核优先主线第三轮质量收敛
### ✨ 新增
- **审核页最小输入 / 约束 / 输出 / 分流四区块收口**
- `admin/routes/web_public.py``ReviewResultContract` 新增 `review_input_summary` / `review_input_attachments` / `review_constraints`
- `/review/start` 现在支持承接首页 `province / score / goal / consult` 查询参数,并把最小约束与现有方案说明写入轻量 review result
- 审核结果页不再只展示 JSON占位页已补齐“审核输入 / 最小约束 / 审核输出摘要 / 下一步分流”四区块
- **首页咨询卡回到审核主线**
- 首页咨询表单由 `/pricing` 改为 `/review/start`
- 增加隐藏字段 `source=home`,保持 `review_entry_source` 可追踪
- **工作台主动作按资料完整度收口**
- 当 Step 1 已完成且还没有最近一次复核结果时,首页工作台默认动作改为“开始方案复核”
- 仍保留“继续补充 Step 1”与“继续查看最近一次复核”两种路径
### 🐛 修复
- **第三轮质量收敛残留**:首页咨询卡此前仍把用户导向 `/pricing`,与 `P0`“先审核再规划”主线不一致
- **第三轮质量收敛残留**:审核页此前只有轻量 JSON 占位,未满足“输入区 / 最小约束区 / 结果区 / 分流区”结构验收
- **第三轮质量收敛残留**Step 1 已完整但无复核结果时,首页工作台仍默认指向补资料,造成主动作偏移
### 🧪 测试
- `admin/tests/test_web_public_review_flow.py`
- `test_landing_page_review_consult_form_targets_review_start`
- `test_landing_page_uses_review_as_workspace_primary_action_when_step1_complete`
- `test_review_start_page_renders_input_constraints_result_and_diversion`
- 回归:`./.venv/bin/python -m pytest -q admin/tests/test_web_public.py admin/tests/test_web_public_content_pages.py admin/tests/test_web_public_portal_info.py admin/tests/test_web_public_review_flow.py data/orders/tests/test_schema.py``70 passed`
## v2.1.3 (2026-06-20) — 生产加固 + L-A 送审前修复 + crowd_db 质量契约
### ✨ 新增
- **admin `/health` 端点增强** (6/20 加固)
- 主键契约: `status: "ok"` (PRODUCTION_DEPLOYMENT_CHECKLIST §4 liveness probe)
- 新增 `checks` 子对象: `db_writable` / `disk_writable` / `settings_valid`
- DB 可写: connect + CREATE TEMP TABLE + INSERT + SELECT + DROP
- 磁盘可写: 在 ops_alert_log_path 目录创建临时文件 + 删除
- settings_valid: 复用 `is_jwt_secret_secure` 判断配置就绪
- 仍不暴露环境/路径/版本细节
- **`_enforce_jwt_secret_policy`** (P2-4 同级, 6/20 加固)
- `GAOKAO_ENV=prod` + dev 默认 JWT secret → RuntimeError fail-closed
- `GAOKAO_ENV=prod` + JWT secret 长度 < 32 → RuntimeError fail-closed
- **`_enforce_default_admin_password_policy`** (6/20 加固)
- `GAOKAO_ENV=prod` + `GAOKAO_ADMIN_PASS=admin123` → RuntimeError fail-closed
- `GAOKAO_ENV=prod` + 密码 < 10 字符 / 字符类 < 3 → RuntimeError fail-closed
- **`tests/test_crowd_db_data_quality.py`** (Q-A 闭环, CROWD_DB_DATA_QUALITY §7 承诺的锁死文件)
- 锁住 27 省总数 + 仅湖南 high + 其它 26 省 ≤ usable + 高考生源大省不在 high 集合
- 锁住 data_year=2025 (6/25 后需显式更新)
- 防止"27 省高置信"合规假象回归
- **admin 后台 footer 隐私政策链接** (L-A R7)
- `admin/static/dashboard.html` (592 行) 加 `<footer>` 块, 链接 /privacy + /deletion-policy + /service-terms
- `admin/routes/ui.py` 内联 admin/orders/new 模板 (line 182 闭包) 同步加 footer
- 与 portal 前台 `_render_footer_links()` 同口径
### 🐛 修复
- **L-A R1**: `docs/LEGAL_PRIVACY_BASELINE.md` §6 `consent_channel` 列表移除孤儿 `admin` 渠道值 (代码侧从未实际产生)
- **L-A R4**: `docs/LEGAL_PRIVACY_BASELINE.md` §7 "已具备/尚缺" 同步 6/20 A-2 + T12-D 进展 (后台/外部渠道补录同意审计 + 数据删除 SOP 脚本化)
### 📝 文档
- `docs/LEGAL_PRIVACY_BASELINE.md` §6 增注释说明 admin 渠道值移除原因
- `docs/LEGAL_PRIVACY_BASELINE.md` §7 重写"已具备/尚缺"分区, 显式归到 6/20 增量
- `docs/CURRENT_STATE.md` 状态词段升级 (见下)
- `docs/ACTIVE_REMEDIATION_2026-06-20.md` 加固项标记 ✅ completed
- `reports/PRODUCTION_HARDENING_AUDIT_2026-06-20.md` (子代理失信, 主代理自跑)
- `reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md` (子代理 363 行交付)
- `reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md` (子代理 45 行交付)
### 🧪 测试
- `admin/tests/test_health.py`:
- `test_health_endpoint_returns_minimal_readiness_only` (升级: 断言 checks 子对象)
- `test_load_settings_prod_rejects_dev_jwt_secret` (新)
- `test_load_settings_prod_rejects_short_jwt_secret` (新)
- `test_load_settings_prod_rejects_default_admin_password` (新)
- `data/crowd_db/tests/test_crowd_db_data_quality.py` (新, 8 个测试):
- `test_total_provinces_is_27`
- `test_hunan_is_the_only_high_quality_province`
- `test_hunan_confidence_meets_high_threshold`
- `test_non_hunan_provinces_not_high`
- `test_high_population_provinces_acknowledged_as_skeleton_or_lower`
- `test_quality_levels_are_valid_enum`
- `test_confidence_values_in_valid_range`
- `test_data_year_is_2025_until_2026_published`
---
## v2.1.2 (2026-06-20) — A-2 后台/外部渠道补录同意审计统一化
### ✨ 新增
- **admin `POST /api/orders` 强制要求 `consent` 块**A-2 落地)
- `CreateOrderRequest` 新增必填字段 `consent: ConsentInfo`
- `ConsentInfo.consent_method` 白名单: `verbal_chat / phone_recording / screenshot / written_form / self_declared`
- `ConsentInfo.consent_note` 可选, 表示"已确认但无备注"
- 缺失或非法 → HTTP 422
- 4 个 source (xianyu / wechat / web / school) 一致处理
- **`Order` 冗余字段: `consent_method` + `consent_given_at`**
- 落 orders 表, 避免每次列表 join `order_intakes`
- schema 增量升级: `ALTER TABLE orders ADD COLUMN consent_method TEXT` (幂等)
- schema 增量升级: `ALTER TABLE orders ADD COLUMN consent_given_at TEXT` (幂等)
- DAO `_WRITABLE_COLUMNS` 加新字段
- **创建订单后同步写 `order_intakes` 记录**
- 与 portal 路径同口径: `consent_version` / `consent_scope` / `consent_channel` / `consent_operator` / `consent_method` / `consent_given_at` / `consent_note`
- `consent_channel` = 渠道 source (xianyu / wechat / school / web)
- `consent_operator` 严格按 `LEGAL_PRIVACY_BASELINE §4` 白名单 `self / guardian / admin_import`:
- `web` 渠道: `guardian` (用户本人或监护人自助 portal, 与 `intake_store.save` 默认值一致)
- 其他渠道 (xianyu / wechat / school): `admin_import` (后台代录, 同意来源是渠道商)
### 🐛 修复
- **后台代录合规盲区**: 6/20 之前 admin `create_order` 路径完全不入任何 consent 字段
(consent 字段在 `admin/routes/orders.py` grep 0 命中), 直接违反
`LEGAL_PRIVACY_BASELINE §6`。现已统一审计口径。
### 📝 文档
- `docs/LEGAL_PRIVACY_BASELINE.md` §6 已隐含 admin 补录需 consent 字段,
本次落地后真正闭环
- `docs/ACTIVE_REMEDIATION_2026-06-20.md` A-2 状态从 `pending` 升级为 `completed`
- `CHANGELOG.md` 顶部加 v2.1.2 段
### 🧪 测试
- `test_create_order_rejects_missing_consent_block[xianyu|wechat|school|web]` (4 个参数化)
- `test_create_order_writes_intake_record_with_consent_audit`
- `test_create_order_external_channel_marks_consent_operator_as_admin`
- `test_create_order_rejects_invalid_consent_method`
- `test_order_detail_returns_consent_method_and_given_at`
- 更新: `test_create_order_returns_masked_payload_with_history` (加 consent 块)
---
## v2.1.1 (2026-06-20) — T12-D retention cleanup 生产化部署验收
### 🐛 修复
- **retention cleanup 多订单 anonymize 崩溃**P0 — T12-D 端到端 acceptance 必现)
- 现象: `retention_cleanup.run_cleanup(apply=True)` 在一次命中 ≥ 2 笔终端态订单时,
第二笔开始全部 `sqlite3.ProgrammingError: Cannot operate on a closed database`
- 根因: `OrdersDAO.__exit__` 不区分连接所有权,对外部 service 传入的
`self._conn``close()``deletion_service.anonymize_order` 把 service 持有的
连接包成 `OrdersDAO(self._conn)` 走 with-block第一笔执行完就把连接关掉
- 触发条件: 生产 `retention_days=180` + 周日 03:30 timer 触发时极易命中
- 修复: `OrdersDAO.__init__` 新增 `owns_conn: bool = False` 参数;
`__exit__` 仅在 `owns_conn=True` 时 close`connect()` classmethod
创建的连接自动设 `owns_conn=True`(保持原行为)
- 回归测试: `tests/test_retention_cleanup.py::test_retention_cleanup_apply_anonymizes_multiple_old_orders_in_sequence` 锁住多订单连续 anonymize 契约
### 📝 文档
- `docs/DELIVERY_RETENTION_OPS_RUNBOOK.md` §8 新增 T12-D 本地端到端 acceptance 步骤
- 验收通过判定表6 项全过)+ 部署前 checklist
- 历史 bug 背景已写入 runbook §8.4,避免后续误判为"运行环境问题"
---
## v2.1 (2026-06-13)
### 🚧 进行中
#### 新增T5.3 已完成)
- 📝 **T5 集成测试与文档收口**
- **T5.1 端到端主链已固化到 `tests/test_t5_e2e_workflows.py`**:覆盖“咨询→方案生成 / 审核→报告 / 订单→交付 / 升级流程 / 数据溯源展示”5 条主链
- **T5.2 性能与并发门禁已固化到 `tests/test_t5_performance.py`**
- `gaokao-quick-3min.py` 的真实 `parse_quick_response + generate_quick_summary + generate_quick_recommendation` 链路 100 次执行断言 `< 5s`
- `locustfile.py``admin.app` 做 10 并发、15 秒 headless 压测,断言聚合成功率 `>95%`
- **T5.3 文档同步**:重写 `docs/API.md``docs/ARCHITECTURE.md` 以匹配当前已落地真相,并补充 T5 验证入口与当前系统边界说明
- **定向验证**`python3 -m pytest tests/test_t5_e2e_workflows.py tests/test_t5_performance.py -q` 通过;其中性能基准覆盖 100 次方案生成与 10 并发后台访问两条验收路径
- **当前定位修正**:项目应描述为“管理后台 + 订单/分享/渠道同步 + AI 审核链路”的可运行系统;用户端 Web 自助闭环仍未落地
#### 新增T6.4 已完成)
-**管理后台订单管理 `admin/routes/orders.py`**
- **端点落地**`GET /api/orders`(真实列表)/ `GET /api/orders/{id}`(详情 + 状态历史)/ `GET /api/orders/export`CSV 导出)/ `POST /api/orders`(手工录单)/ `PATCH /api/orders/{id}`(业务字段更新 + 状态流转 + 退款)
- **状态机守护**:写路径复用 `OrdersDAO.update()``transition_status()`,非法流转统一映射到 `E02301`,不允许绕过 6 态状态机直接写库
- **默认脱敏**:列表、详情和 CSV 导出均走 `Order.to_dict(decrypt_sensitive="mask")`,避免管理后台直接导出完整手机号/身份证号
- **手工兜底**:支持 `external_id` 留空的人工补录路径,与 `docs/CHANNEL_INTEGRATION.md` 约定一致;退款仅更新本地订单状态,不主动触发第三方退款 API
- **测试 6 个全部通过**`admin/tests/test_routes_orders.py`):创建、列表/详情、PATCH 更新+付款、退款、非法状态冲突、CSV 导出
- **定向验证**`python3 -m pytest admin/tests/test_routes_orders.py admin/tests/test_routes.py -q` → 20 passed`ruff check admin/routes/orders.py admin/tests/test_routes_orders.py admin/tests/test_routes.py` 通过
- **文档同步**`README.md` 新增 T6.4 章节;`docs/plans/T6-admin-mvp.md` 标记 T6.4 已落地并更新路由清单
#### 新增T6.2 已完成)
-**管理后台仪表盘 — 真实 SQL 聚合层 `admin/stats.py`**434 行)
- **端点落地**`GET /api/stats/dashboard`(一站式仪表盘)+ `GET /api/stats/orders`(沿用 T6.1 stub 字段名,去掉 `_stub` 标记)
- **纯函数聚合层**`build_dashboard_payload` / `compute_summary` / `compute_by_status` / `compute_by_source` / `compute_by_service_version` / `compute_trends` / `generate_day_series`
- **响应契约**`summary`(订单/用户/收入 + 今日/7d/30d 切片 9 字段) + `by_status` / `by_source` / `by_service_version` (完整 0 填充) + `trends` (today=1, 7d=7, 30d=30 个点, 日粒度, 0 填充) + `generated_at`
- **关键口径**
- 收入 = `paid / serving / delivered / completed` 四态订单的 `amount_cents` 累计值;`pending`(未付款)与 `refunded`(已退款)排除
- 趋势桶粒度 = 日UTC`YYYY-MM-DD`),用 `substr(created_at, 1, 10)` 切片
- 0 填充 = 窗口内的"无订单日"也返回 0 点,前端拿到稠密序列
- 数据源隔离 = `orders``GAOKAO_ORDERS_DB_PATH`(默认 `data/orders.db``admin_users``GAOKAO_DB_PATH`(默认 `data/orders/admin.db`
- 不读 PII统计路径只触碰 `amount_cents` / `status` / `source` / `service_version` / `created_at`
- **配置新增**`Settings.orders_db_path``GAOKAO_ORDERS_DB_PATH` 环境变量,默认 `data/orders.db`),与 `data.channel_sync.webhook_server` 已有的同名变量对齐
- **测试 12 个全部通过**`admin/tests/test_routes_stats_dashboard.py`
- 鉴权(无 token 401 / 有 token 200
- 空库形状契约summary / by\_\* / trends 三层结构稳定
- 趋势序列1 / 7 / 30 个点,按日期严格升序
- 0 填充点:包含完整三字段(`date` / `orders` / `revenue_cents`
- 真实数据窗口边界45 天前的订单被 30d 窗口排除,但计入 total
- 收入口径pending / refunded 不计入 revenue
- 0 填充:范围内无订单的日也返回 0 点
- 兼容层:`/api/stats/orders` 字段名不变,`_stub` 标记已移除
- **T9.3 已知缺口已闭环**`admin/tests/conftest.py` 新增 `orders_db` autouse fixtureT6.2 起,所有测试默认带空 orders DB`pytest -q admin/tests` 87/87 通过
- **测试套件**`pytest -q admin/tests` 87/87 passed`pytest -q` 仓库全量 392/392 passed`ruff check admin/stats.py admin/routes/stats.py admin/tests/test_routes_stats_dashboard.py admin/tests/conftest.py admin/config.py admin/tests/test_routes.py` All checks passed
- **文档同步**`docs/plans/T6-admin-mvp.md` 新增 §10T6.2 设计与 DoD§12 后续任务衔接标 [x]`README.md` 新增 T6.2 章节;`CHANGELOG.md` 本条
#### 新增T9.3 已完成)
-**结构化 JSON 日志 `admin/logging_utils.py`**
- 新增 `JsonLogFormatter`:单行 JSON 输出 `ts / level / logger / msg / ctx / exc`
- 新增 `log_event()` / `log_event_exc()`:业务侧一行写结构化事件,避免散落字符串拼接
- 新增 `contextvars` 请求上下文:自动注入 `request_id / path / method`,并在请求结束后清理,避免串请求污染
- `admin/app.py` 新增 `request_context_middleware``--log-format json|plain` 启动参数CLI 默认输出 JSON保留 plain 便于本地调试
- `admin/errors/exceptions.py``BusinessError` / `HTTPException` / `RequestValidationError` / 兜底异常日志全部升级为结构化事件
- 新增 `admin/tests/test_logging.py` 8 个测试,覆盖 formatter、上下文绑定、异常 traceback、FastAPI 端到端 JSON 日志
- 定向验证通过:`pytest -q admin/tests/test_logging.py admin/tests/test_errors.py` = 34/34 passed`ruff check admin/app.py admin/errors/exceptions.py admin/logging_utils.py admin/tests/test_logging.py admin/tests/test_errors.py` 通过
- 已知缺口T6.2 已闭环):`pytest -q admin/tests` 之前有 1 个非 T9.3 失败(`test_stats_orders_real_shape``sqlite3.OperationalError: no such table: orders`)— 根因是 stats 端点读 orders 表,但 T6.1 阶段 conftest 没建 orders DBT6.2 在 conftest 加 `orders_db` autouse fixture 闭环,当前 `pytest -q admin/tests` 87/87 通过
#### 新增T4.3 已完成)
-**订单管理 CLI `scripts/gaokao-order-manager` + `data/orders/cli.py`**
- 子命令落地:`create / list / show / update / pay / deliver / stats`
- 默认 JSON 输出,订单详情默认走 `Order.to_dict()` 遮罩模式,避免 CLI 直接泄露完整手机号/身份证
- `create` 直接复用 `OrdersDAO.create()``show` 返回订单详情 + `order_status_history`
- `update` 只允许业务字段,空更新返回 exit code 2继续强制 `status` 变更走 DAO 状态机
- `pay` 推进 `pending -> paid``deliver``paid` 时自动串联 `serving -> delivered`,兼容已在 `serving` 的最后一步交付
- `stats` 输出 `total_orders / by_status / by_source / by_service_version`
- 新增 `data/orders/tests/test_cli.py` 5 个 pytest 用例:
- 脚本级端到端主链路create/list/show/update/pay/deliver/stats
- 缺失订单错误码
- 空更新防御
- 模块内 direct-main 覆盖,`data.orders.cli` 覆盖率 **90%**
- 定向验证:`pytest -q data/orders/tests/test_cli.py` 5/5 passed`pytest -q data/orders/tests` 166/166 passed`ruff check data/orders/cli.py data/orders/tests/test_cli.py scripts/gaokao-order-manager` 通过;`py_compile` 通过
- 📝 **`data/orders/README.md`** 新增 T4.3 CLI 使用示例与子命令语义
#### 新增T4.2 已完成)
-**订单 DAO 数据访问层 `data/orders/dao.py`**530 行,`OrdersDAO` 类)
- **CRUD 完整**`create()` / `get()` / `get_by_external_id()` / `find_by_phone()` / `list()` / `count()` / `stats_by_status()` / `update()` / `delete()`
- **事务守护**`dao.transaction()` 上下文管理器;嵌套语义(外层在事务中时内层不重复 commit异常统一回滚外层
- **加密透明化**API 入口接收 `Order` dataclass明文 PIIDAO 内部用 `to_db_row()` 加密落盘 / `from_db_row()` 解密读回;调用方无需接触 `*_enc` 字段
- **状态机守护**`transition_status()``assert_valid_transition()` 单事务内 `UPDATE orders` + `INSERT order_status_history`;非法抛 `InvalidStateTransition` 并回滚
- **时间戳联动**`paid → paid_at` / `serving → started_at` / `delivered → delivered_at` / `completed → completed_at` 由状态自动置位(`COALESCE` 保留首次值)
- **幂等 upsert**`upsert_by_external_id()` 接管 T8.1 闲鱼 Webhook 路径4 种 `action``data/channel_sync/dao_extension.py` 完全对齐(`inserted` / `updated` / `unchanged` / `illegal_transition`
- **防御设计**`_row_factory_ctx()` 临时切 `sqlite3.Row`、退出后恢复外部 row_factory`update()` 显式拒绝 `status` 字段(强制走 `transition_status()`
- **51 个 pytest 用例全绿**(覆盖加密透明化、状态机合法/非法路径、事务回滚、upsert 4 种 action、状态历史时间线、row_factory 不污染、删除 cascade
- **data/orders 模块总测试 163/163 通过**(含 T4.1 既有 112 + T4.2 新增 51`data/` 全模块 386/386 通过ruff 0 warning
- 🔧 **`data/orders/__init__.py` 导出 DAO 公共 API**`OrdersDAO` / `UpsertResult` / `StatusChange` / `OrderNotFound` / `DuplicateOrder` 一行 import
- 📝 **`data/orders/README.md`** 更新 T4.2 DAO 一览表、加密透明化边界表、DoD 勾选、API 下游衔接
#### 新增T2.4 已完成)
-**扎堆报告生成器 `risk_report.py`**`data/crowd_db/risk_report.py`183 行)
- `build_crowd_risks(plan, user_score, province, loader=None)` — 把 `crowd_detector.detect_crowd_risk``RiskFinding` 列表转换为 `templates/audit_report.html` 模板所需的 `crowd_risks` 字典格式
- 三色 emoji 标识high → 🔴、medium → 🟡、low → 🟢(随 `RISK_LEVEL_META` 自动匹配)
- 模板字段完整:`school / major / frequency / predicted_increase / risk_level / risk_level_label / risk_emoji / platforms / alternatives`
- alternatives 字段重映射:`name``school``score` 强制 `int`(防御非数字字符串)
- 辅助能力:
- `group_by_risk()` — 按风险等级分组(永远返回 high/medium/low 三个 key
- `format_risk_summary()` — 单行汇总(如 "🔴 1 个高风险、🟡 2 个中风险"),给报告顶部用
- `render_risk_table()` — CLI / 微信消息场景的纯文本表格(含替代院校行)
- 类型设计:用 `Protocol` 定义 `_LoaderProtocol`,避免对 `CrowdDBLoader` 强依赖(支持 mock 注入与未来的不同实现)
- 22 个 pytest 单元测试全部通过(三色 emoji / 字段完整性 / 替代项重映射 / 防御性 fallback / 空方案 / 不存在省份 / 自定义 loader 注入)
-**端到端集成验证脚本 `scripts/verify_t2_4_e2e.py`**95 行)
- 用真实湖南 575 分数据 + `audit_report.html` 模板渲染一遍
- 断言:模板必需字段全部存在 / alternatives 重映射正确 / 三色 emoji 在渲染输出可见
- 跑法:`python3 scripts/verify_t2_4_e2e.py`
- 6/6 集成断言通过
-**质量门禁**
- `data/crowd_db/tests/` 105/105 通过(含本次新增 22 + 历史 83
- 仓库全量 pytest392 passed
- `ruff check data/crowd_db/ scripts/verify_t2_4_e2e.py` — All checks passed
- `validate_template.py` 仍 PASS模板字段契约未破坏
#### 新增T2.5 已完成)
-**扎堆检测单元测试 T2.5**`data/crowd_db/tests/test_crowd_detector.py` 由 35 增至 55 个测试(新增 20 个)
- **用例 1 高风险识别**`test_high_risk_use_case_frequency_4_full_payload`freq=4 全平台 + predicted_increase + alternatives/ `test_high_risk_boundary_frequency_exactly_4`>=4 闭合区间边界)/ `test_high_risk_distinct_from_medium_and_low`high/medium 互不混淆)
- **用例 2 替代方案**`test_alternatives_use_case_contains_required_keys`(每个 alt 必须含 name/major/score, 0-100 区间)/ `test_alternatives_use_case_sortable_by_score`detector 透传 alternatives 字段不丢)/ `test_alternatives_use_case_empty_list_is_valid`(空列表合法)
- **用例 3 跨省份**`test_cross_province_hunan_hit_guangdong_miss`(湖南 575 命中 vs 广东 575 无数据隔离)/ `test_cross_province_beijing_at_690`(同校不同省命中不同记录 — 清华 工科试验班)/ `test_cross_province_loader_called_with_correct_province`province 透传给 loader, 不做全局查询)
- **用例 4 异常处理**`test_exception_use_case_loader_returns_none_field`name=None 不崩)/ `test_exception_use_case_loader_returns_non_dict`(混入 None/str/int 时优雅过滤)/ `test_exception_use_case_loader_raises_propagates`loader 抛错向上传播)/ `test_exception_use_case_unrecognized_entry_type`int/float/object 走 str 兜底)/ `test_exception_use_case_entry_with_int_school_dict`school=0 falsy 跳过)/ `test_exception_use_case_province_is_none`province=None 不抛)/ `test_exception_use_case_tuple_with_non_string_school`tuple 元组非 str school 走 str() 兜底)/ `test_exception_use_case_dict_with_truthy_non_string_school`0.5 这种 truthy 非 str school 走 str() 兜底)/ `test_exception_use_case_dict_with_name_truthy_non_string`name 字段 truthy 非 str 同样兜底)/ `test_exception_use_case_single_element_tuple_non_string`(单元素 tuple 非 str 兜底)/ `test_exception_use_case_malformed_frequency_in_record`frequency="not a number"/None 时 try/except 按 0 处理)
- 🛡️ **`crowd_detector.py` 根因加固** — 与测试同步detector 主循环加入三层防御:
- `if not isinstance(rec, dict): continue` — 防止 loader 混入非 dict 元素时 `rec["name"]` 抛 TypeError
- `if not isinstance(rec_name, str) or not rec_name.strip(): continue` — 防止 name 缺失/非 str 时 `.strip()` 抛 AttributeError
- `try/except (TypeError, ValueError): freq = 0` — 防止 frequency 字段异常时 `int(...)` 崩溃
- `_normalize_entry` 增加 truthy 非 str school 的 str() 兜底dict / tuple / list 三分支)
- 取值统一用 `or` 兜底空值:`rec.get("platforms") or []` / `rec.get("predicted_increase") or 0` / `rec.get("alternatives") or []`
-**质量门禁**
- `crowd_detector.py` 单模块覆盖率:**93%**101 statements / 7 missed 仅 `__main__` CLI demo 块)
- 远高于 T2.5 PRD 阈值 **≥80%**
- `data/crowd_db/tests/test_crowd_detector.py` 55/55 passed
- `data/crowd_db/tests/` 全量 125/125 passed不动 T2.1/T2.2/T2.4 既有测试)
- `pytest -q --ignore=data/orders` 仓库全量 249 passed
- `ruff check data/crowd_db/` — All checks passed
#### 新增T1.3 已完成)
-**方案解析器 `plan_parser.py`**`skills/gaokao-audit/scripts/plan_parser.py`295 行)
- 解析大厂AI志愿方案文本纯文本/PDF提取/截图OCR`ParsedPlan` dataclass
- 字段提取省份27 省 + 别名)、分数、位次、选科、来源(千问/元宝/百度/豆包)、志愿列表
- 志愿行识别支持 4 种行首编号格式:`1. xxx` / `1、xxx` / `1) xxx` / `1 xxx`PDF 提取常见)
- 学校/专业分隔支持 4 种风格:`学校 - 专业` / `学校:专业` / `学校(专业组)` / `学校 专业`(按大学/学院关键字兜底)
- 健壮边界:空文本/无志愿/编号异常均返回 `ParsedPlan` 默认值,不抛异常
- CLI`python3 -m skills.gaokao-audit.scripts.plan_parser <file>` 输出 JSON
- **10 个 pytest 单元测试全部通过**(基础解析/志愿列表/省份别名/选科/PDF 格式/空文本/无志愿/编号多样性/序列化/真实样本回归)
- 真实样本回归 fixture`tests/fixtures/sample_xianyu.txt`百度AI方案
- 🔒 **T1.2 review 闭环修复**`skills/gaokao-audit/scripts/validate_template.py`
- 模板渲染 `Environment(autoescape=False)``autoescape=True`——修复用户派生字段policy_errors/crowd_risks/data_issuesXSS 注入风险
- 标签平衡检查:原 "(HTML5 void elements ignored)" 注释与实际行为不符regex 未排除 void 元素),改为显式 `VOID_ELEMENTS` 集合减去空元素计数103=1037 void 元素正确排除)
- T1.2 验证脚本仍 PASSmock payload 全部 7 个 must_contain 字符串命中)
- 📝 **`docs/plans/T1-2-audit-skill-and-parser.md` 实施记录**10/10 测试 ✓ · 真实样本解析正确 ✓ · 省份别名 27 省 ✓ · 编号格式 4 种 ✓
#### 新增T1.2 已完成)
-**AI方案审核服务 Skill**`skills/gaokao-audit/`
- `SKILL.md`143 行)— 角色边界 + 49 元服务定价 + 四维审核框架 + 合规红线 + DoD
- `templates/audit_report.html`262 行Jinja2— 报告模板,含政策合规/扎堆风险/数据存疑/改进建议 4 区块 + 升级到 99 元完整方案卡片
- `examples/sample_audit.md`144 行)— 输入样例大厂AI方案文本/PDF/截图)+ 输出报告样例 + 渲染数据契约表
- `scripts/validate_template.py`137 行)— 模板渲染校验器16 个占位符 + 7 个渲染输出字符串断言
- `tests/test_validate_template.py`33 行)— pytest 入口1 用例通过
- 关键设计T1.1 review 暴露的 4 个反模式(吞错、模糊 substring、vacuous 测试、缺失档位)已显式列入"对齐 T1.1 review 教训"表,由 T1.3 实施时强制规避
- T1.2 测试套件:**1/1 通过**(含项目原有 6 个 spec_checker 测试 → 7/7 通过)
#### 新增T7.1 已完成)
-**短链接生成**`data/share/short_link.py` + `scripts/gaokao-shortlink`
- base62 短码生成(默认 6 位4-16 位可配,加密随机 + 冲突重试)
- SQLite 映射表 `share_links`WAL 模式code 主键 + report/owner/expires 三索引)
- `ShortLinkService` 完整 API`create / get / resolve / revoke / list_by_report / list_by_owner / get_stats / purge_expired`
- 访问控制:`permission`read/comment/edit/admin+ 密码PBKDF2-HMAC-SHA256兼容历史 sha256 读时迁移)+ `expires_at` + `revoked`
- `route_short_link(code, password, base_url)` 路由辅助,可挂载任意 Web 框架的 `/s/<code>`
- CLI`create / resolve / revoke / list / stats / purge`,全部子命令 JSON 输出
- 25 个 pytest 用例全部通过base62 编解码、碰撞重试、TTL、密码、撤销、列表、统计、清理、路由
#### 新增T7.3 已完成)
-**分享权限策略**`data/share/permission.py`
- 新增 `PermissionPolicy.for_permission(permission)`:把 `read/comment/edit/admin` 归一化成分享页能力策略
- 能力判断:`can_view / can_comment / can_edit / allows_field / can("...")`
- 安全兜底:未知 permission 一律回退到最严格的 `read` 策略,防止越权
-**姓名脱敏策略复用**
- 复用 `data/orders/masking.py::mask_name`,避免在分享链路重复实现脱敏算法
- `read/comment` 默认回传脱敏姓名公开分享场景额外收紧为3 字及以上中文名统一 `姓+**`,非中文名统一 `**`
- `edit`(以及历史兼容 `admin`)下姓名原样展示,但 `password_hash / internal_note / note / debug_info / raw_payload` 等内部字段仍强制隐藏
-**权限感知路由辅助**`data.share.short_link.route_short_link_with_report`
-`route_short_link()` 之上叠加报告 payload 渲染,支持 `report=` 直接注入或 `report_loader(report_id)` 懒加载
- resolve 失败not_found / revoked / expired / password_required / password_wrong时不下发 `rendered` payload避免泄露报告元数据
- `render_report_payload(permission, report, share_url=...)` 输出统一结构:`policy + visible_fields + payload + masked_fields`
-**测试验证**
- 新增 `data/share/tests/test_permission.py`34 个 pytest 用例覆盖三档权限、admin alias、未知 permission fallback、字段裁剪、姓名脱敏、公开分享收敛规则、route + report_loader 端到端
- `python3 -m pytest data/share/tests/test_permission.py -q`**34 passed**
- `python3 -m pytest data/share/tests/ -q`**61 passed**
- `python3 -m pytest data/share/ data/orders/ -q`**231 passed**
- `python3 -m ruff check data/share/permission.py data/share/tests/test_permission.py data/share/short_link.py`**All checks passed**
#### 新增T7.2 / T7.5 已完成)
-**分享海报生成**`data/share/poster.py` + `scripts/gaokao-poster`
- 新增 1080×1920 海报生成链路:标题卡 / 考生摘要 / 推荐院校 TOP3 / 二维码区 / 品牌区
- `build_poster_payload(...)` 统一提取报告标题、分数、省份、年份、推荐院校与分享链接
- `save_poster(...)` 按扩展名保存 PNG / JPG默认姓名脱敏CLI 可 `--unmask-name` 关闭
- `requirements-admin.txt` 新增 `qrcode>=8.2,<9.0`
-**公开分享页 WebUI**`admin/routes/ui.py::share_page` + `admin/share_page.py`
- `GET /s/{code}` / `GET /s/{code}?pwd=...` 正式落地
- 统一复用 `route_short_link_with_report(...)`,接入短链、权限、密码、撤销与统计体系
- `ok/not_found/revoked/expired/password_required/password_wrong` 六类状态页已具备独立渲染
- 分享页支持移动端 viewport、考生摘要、推荐院校列表、分享说明与复制链接按钮
-**测试验证**
- 新增 `data/share/tests/test_poster.py`payload + PNG/JPG 输出)
- 新增 `tests/test_poster_cli.py`CLI help + 实际生成 PNG
- 分享相关总回归:`data/share/tests/test_poster.py tests/test_poster_cli.py data/share/tests/test_short_link.py data/share/tests/test_permission.py admin/tests/test_share_ui.py admin/tests/test_share_link_api.py admin/tests/test_share_link_frontend.py admin/tests/test_share_status_panel.py`**93 passed**
#### 新增T10.1 已完成)
-**GitHub Actions CI 流水线**
- 自动化测试push / pull_request 触发)
- 多 Python 版本矩阵3.10 / 3.11 / 3.12
- pytest + pytest-cov 覆盖率报告XML 产物供 T10.2 codecov 集成)
- pip 依赖缓存(加速运行)
-`requirements-dev.txt` — 测试依赖pytest / pytest-cov / pytest-timeout / pytest-xdist
-`pytest.ini` — 统一本地与 CI 测试发现配置
- ✨ README 顶部 CI 状态徽章
#### 新增T10.2 已完成)
-**Codecov 覆盖率报告与徽章**
- `.github/workflows/ci.yml` 集成 `codecov/codecov-action@v4`3.11 单点上传 coverage.xml
- `codecov.yml` 定义 project 目标 80% / 核心 skills 目标 100% / patch 目标 70%,与 T5.5 硬门槛对齐
- `fail_ci_if_error: false`codecov 不可达时不影响 CI 通过(适配多镜像仓库场景)
- README 顶部新增 codecov 覆盖率徽章
- ✨ PR 浮动容忍 1%project/ 2%core避免噪声红灯
#### 新增T11.2 已完成)
-**敏感字段展示脱敏工具 `data/orders/masking.py`**127 行)
- `mask_phone` / `mask_id_card` / `mask_name` / `mask_sensitive_dict` 四个纯函数
- 手机号11 位 → `138****1234`;支持 `+86` / `86` 国家码剥离、常见分隔符(空格 / 横杠)
- 身份证18 位 → `430102********1234`(保留前 6 行政区划 + 末 4 位15 位老版兼容;非 18/15 位且 ≥13 位的非标准输入也走"前 6 后 4"遮罩
- 姓名1 字原样 / 2 字 `张*` / 3 字 `张*丰` / 4+ 字 `张**`;非中日韩字符全遮 `*****`
- 默认安全None / 空串 / 非字符串均不抛错,统一返回 None / ""
- `mask_sensitive_dict` 一键遮罩订单字典中所有已知 PII 字段(不动 \_enc / \_hash 等索引/密文字段)
-**`Order.to_dict` 扩展 `decrypt_sensitive` 三态策略**
- `True` : 完整明文(权限内接口使用,如后台人工核对)
- `False`: 完全移除明文(对外公开统计 / 审计日志)
- `"mask"` (默认): 部分遮罩(列表 / 详情默认 — 推荐且默认安全)
- 未知字符串策略值回退为 mask防误传"plaintext"导致明文泄露)
-`data/orders/tests/test_masking.py` — 32 个 pytest 用例覆盖:手机号 11/12/带 +86 / 短串 / 非法输入、身份证 18/15/X 校验位 / 短串 / 13 位非标准、姓名 1/2/3/4+ 字 / 英文 / 混合、`mask_sensitive_dict` 不变性、Order.to_dict 三态与默认 mask
-**测试验证**data/orders/tests 全套 112 用例通过(含原有 80 + 新增 32全仓 344/344 通过;`ruff check` 0 warning`ruff format --check` 3 文件已规范
- 🔒 **与 T4.1 落盘加密的关系**crypto.py 负责"落盘形态"(密文 + hashmasking.py 负责"展示形态"(部分遮罩),两者正交 — 落盘加密是底层保险,脱敏展示是上层最小特权
#### 新增T8.3 已完成)
-**企业微信最小客户端**`data/channel_sync/wecom_adapter.py`
- `WeComBotClient.from_env()`:从 `GAOKAO_WECOM_BOT_KEY` / `GAOKAO_WECOM_API_BASE` / `GAOKAO_WECOM_TIMEOUT_SECONDS` 读取机器人 webhook 配置,缺失时 fail-fast
- `WeComAppClient.from_env()`:从 `GAOKAO_WECOM_CORP_ID` / `GAOKAO_WECOM_CORP_SECRET` / `GAOKAO_WECOM_AGENT_ID` / `GAOKAO_WECOM_API_BASE` / `GAOKAO_WECOM_TIMEOUT_SECONDS` 读取应用消息配置
- 机器人消息:`send_text(...)` 覆盖文本通知,支持 `mentioned_list` / `mentioned_mobile_list`
- 应用消息:`send_text(...)` 覆盖 `touser` / `toparty` / `totag` 文本推送,支持 `safe` 与重复消息检查参数
- access_token 自动缓存 + 提前刷新窗口:避免每次应用推送都重新拉 token
- 错误模型统一HTTP 非 2xx、上游 `errcode != 0`、transport 异常、非 JSON 响应统一包装为 `WeComAPIError`
-**企业微信集成测试**`data/channel_sync/tests/test_wecom_adapter.py`
- 覆盖机器人 webhook payload、应用 token 缓存/消息 payload、上游 errcode、transport 异常、环境变量缺失 fail-fast
- 同时补上 `WeChatClient` transport 异常包装回归,避免标准库 transport 网络错误直接泄漏原始异常
#### 新增T8.2 已完成)
-**微信 SDK 最小客户端**`data/channel_sync/wechat_adapter.py`
- `WeChatClient.from_env()`:从 `GAOKAO_WECHAT_APP_ID` / `GAOKAO_WECHAT_APP_SECRET` / `GAOKAO_WECHAT_API_BASE` / `GAOKAO_WECHAT_TIMEOUT_SECONDS` 读取配置,缺失时 fail-fast
- access_token 自动缓存 + 提前刷新窗口:避免每次发送都重新拉 token也避免临近过期 token 被继续复用
- 订阅消息:`send_subscribe_message(...)` 自动规范化 `data``{key: {value: ...}}` 形状,并透传 `page / miniprogram_state / lang`
- 客服消息:`send_custom_message(...)` / `send_custom_text(...)` 覆盖文本客服场景,支持可选 `kf_account`
- 纯标准库 HTTP`UrllibTransport` + 可注入 `Transport` 协议,便于测试 fake transport 与后续 T8.3 复用
- 错误模型统一HTTP 非 2xx、上游 `errcode != 0`、非 JSON 响应全部包装为 `WeChatAPIError`
-**微信集成测试**`data/channel_sync/tests/test_wechat_adapter.py`
- 覆盖 token 缓存/刷新、订阅消息 payload、客服文本消息 payload、上游 errcode、HTTP 500、环境变量缺失 fail-fast
- `pytest --cov=data.channel_sync.wechat_adapter` 覆盖率 **90%**8/8 通过)
- `pytest -q data/channel_sync/tests`**75 passed**`ruff check data/channel_sync/wechat_adapter.py data/channel_sync/tests/test_wechat_adapter.py data/channel_sync/__init__.py`**All checks passed**
#### 新增T8.1 已完成)
-**闲鱼 Webhook + poller 兜底同步**`data/channel_sync/`
- `webhook_server.py``POST /webhook/xianyu` + `/healthz`,覆盖签名校验、时间戳过期、重复 event、非法状态迁移、审计落库、单 IP 限流
- `poller.py`:共享 `upsert_by_external_id` 幂等链路,支持 cursor 推进与 `poller_state` / `poller_run` 运行记录
- `audit.py` + `dao_extension.py` + `xianyu_adapter.py` + `signature.py`:补齐渠道同步所需审计、适配、验签与订单写库能力
- `data/channel_sync/tests/test_xianyu_channel.py`129 个 `data/orders + data/channel_sync` 测试通过;`data.channel_sync` 覆盖率 90%`ruff check data/channel_sync` 0 warning
- 修复关键缺陷Webhook server 预热 SQLite 连接原先在主线程创建server 线程复用时触发 `sqlite3.ProgrammingError`,导致 accepted/duplicate/illegal-transition 路径误报 500 且拒绝审计落库失败;现改为 schema bootstrap 后以 `check_same_thread=False` 重新打开连接
#### 新增T2.3 已完成)
-**扎堆检测算法**`data/crowd_db/crowd_detector.py`
- `detect_crowd_risk(plan, user_score, province) → list[RiskFinding]`:遍历方案每条志愿,匹配该分数段内的 crowd_db 记录,返回风险列表 + 替代方案
- 风险等级映射frequency ≥4 high / 2-3 medium / 1 low / 0 跳过
- 院校模糊匹配(互相包含),专业可选(计划未指定专业时按院校命中)
#### 新增T3.1 已完成)
-**27 省院校数据模型扩展溯源字段**`data/crowd_db/{province}.json` + `loader.py`
- 27 省 JSON 顶层新增 6 字段:`source` / `source_url` / `source_type` / `data_year` / `confidence` / `last_updated`
- 现有 26 省骨架数据confidence=0.45source_type=`manual_summary`data_year=2025
- 湖南数据confidence=0.85,含 68 条推荐专科批→省外顶尖10 段连续 440-690 覆盖)
- `CrowdDBLoader` 扩展 `warn_low_confidence` / `load_metadata(province) → 8 字段` / `list_supported_provinces()` / `list_provinces()` / `load_province` 对 confidence<0.5 发 `UserWarning`
- 新增 `data/crowd_db/SCHEMA.md`:顶层/分数段/推荐条目三表 schema + `source_type` 枚举(`manual_summary` / `official_release` / `platform_scrape` / `derived`+ 骨架文件约定
- 测试:`data/crowd_db/tests/test_provenance.py` 9 用例27 存在性、顶层字段、score_range schema、confidence 区间、loader 27 报告、metadata 8 字段、低 confidence 警告、端到端匹配T2.1/T2.2/T2.3 46 用例无回归
- 与 T2.3 协同:`crowd_detector._risk_level_from_frequency``CrowdRecommendation.risk_level` 在 frequency 0-5 边界完全一致
#### 新增T3.2 已完成)
-**溯源字段查询 + 验证 API**`data/crowd_db/loader.py` + `tests/test_provenance_query.py`
- 新增 dataclass `ProvenanceValidation`:含 `ok` / `errors` / `warnings` / `is_usable` / `summary` 5 字段 + `to_dict()`
- 类常量:`USABLE_CONFIDENCE_THRESHOLD = 0.5` / `VALID_SOURCE_TYPES`4 枚举)/ `ISO_DATE_PATTERN` / `REQUIRED_PROVENANCE_FIELDS`7 字段)
- `validate_provenance(data, province) → ProvenanceValidation`@classmethod,不需 loader 实例):校验字段类型/范围/枚举/日期格式,区分硬错误与软警告
- `validate_province(province) → ProvenanceValidation`:加载 + 校验一体化
- `validate_all() → dict[province, ProvenanceValidation]`27 省一次性扫描
- `filter_provinces(*, source_type, min_confidence, max_confidence, data_year, updated_since, updated_before, only_usable) → list[province]`:多维溯源过滤,按 `PROVINCE_FILE_MAP` 顺序返回
- `get_provenance_report(*, only_usable, source_type) → dict`:汇总 `total` / `usable_count` / `failed_count` / `by_source_type` / `items`
- 27 省现状:全部 schema 校验通过(`failed=0`),仅湖南 1 省 `is_usable=True`confidence=0.8526 省 low_confidence warning
- 测试:`data/crowd_db/tests/test_provenance_query.py` **33 用例全通过**,覆盖 validate_all/27、validate_province 单省、validate_provenance 边界None/空 dict/confidence 越界/source_type 非法/data_year 类型错/date 格式错/empty source warning、filter_provinces 9 维 + 组合、get_provenance_report 5 统计项、未知省份 load_failed、ProvenanceValidation.to_dict、load_metadata 兼容性、validate_provenance 不可变性
- 不破坏既有 API`load_province` / `find_recommendations` / `find_recommendation_by_school` / `load_metadata` 行为与契约完全不变T2.x 46 用例 + T3.1 9 用例 + T3.2 33 用例 = **88/88 通过**
- 多种 plan 形态dict / CrowdRecommendation / tuple / list
- 支持注入 `loader` 便于测试,频率降序排序
- 20 个单元测试覆盖:高/中风险识别、替代方案、分数段边界、模糊匹配、专业匹配、空/异常输入、部分命中、排序
-`data/crowd_db/tests/test_crowd_detector.py` — 20 个测试覆盖算法各分支
### 📅 计划中
- T5.5 覆盖率硬门槛(全局 ≥80% / 核心 =100% / E2E模拟通过
---
## v2.0 (2026-06-11)
### 🎉 重大变化
#### 新增
-**规范检查Skill V2**支持27个省份自动适配
- 院校专业组模式14省
- 专业+学校模式8省
- 传统模式5省
- ✨ 省份规则库(`rules/provinces/`
- ✨ 错误模式库(`rules/errors/`
- ✨ 真实案例库(`docs/case-studies/`
- ✨ 优化日志(`docs/optimization-log/`
- ✨ 完整项目目录结构
#### 修复
- 🐛 修正"院校专业组"概念(原错误为"45个学校"
- 🐛 修正调剂规则(原错误为"全校调剂"
- 🐛 修正投档后规则(原错误为"退到下个志愿"
- 🐛 移除主观概率(原错误为"35%、80%"
- 🐛 修正选科一刀切(原错误为"所有会计都要求物+化+生"
#### 改进
- ⚡ 院校数据从2025年位次精确化
- ⚡ 风险提示前置
- ⚡ 数据来源明确标注
- ⚡ 强调"以官方为准"
### 📝 起源事故
- 真实用户咨询湖南578分/物化生
- 原方案出现致命错误(院校专业组概念错误)
- 用户质疑"是否符合规范"触发自查
- 创建本检查Skill防止错误复发
---
## v1.0 (2026-06-10 → 2026-06-11)
### 初始版本
- gaokao-college-advisor Skill
- 可视化报告脚本
- 1分钟/3分钟/7步三套问卷
- 邮件发送功能
### 已知问题
- ❌ 政策理解停留在2024年水平
- ❌ 数据主观化严重
- ❌ 缺乏方案检查机制
- ❌ 部分细节不符合本省规范
### 用户反馈
- "符合今年对应区域志愿填报规范吗?" ← 触发v2.0创建
---
## 📊 版本对比
| 维度 | v1.0 | v2.0 |
| ---------- | ---- | ------------- |
| 省份支持 | 湖南 | 27个 |
| 检查机制 | 无 | 规范检查Skill |
| 错误模式库 | 无 | 8+种错误 |
| 数据精度 | 主观 | 基于2025位次 |
| 风险提示 | 后置 | 前置 |
| 项目结构 | 散落 | 集中 |
| 文档体系 | 简单 | 完善 |
---
## 🎯 下一步
- 添加更多省份覆盖完整30+省)
- 完善数据自动化对接
- 增强算法精度
- 完善UI/UX
详见 `docs/optimization-log/future-plan.md`