# Phase 2 开发日志 — 仪表盘真数据 + 用户管理 + Excel 批量导入 > 《军事科技》栏目内部工作台 · Phase 2 > 撰写日期:2026-05-15 > 状态:**Task 1 后端 CRUD 完成,冒烟实测 6/6 通过;Task 2 进行中** --- ## 一、本 Phase 完成时间 - **开始日期**:2026-05-15 - **Task 1 后端 CRUD**:2026-05-15 完成,冒烟 6/6 全绿 - **Task 2 Excel 批量导入**:进行中 - **Task 3 前端**:待开始 - **预计 Phase 2 整体收官**:待定 --- ## 二、本 Phase 做了什么 ### Task 1 — 后端 CRUD(2026-05-15) 1. **models**:`episode.py`(软引用+快照)、`yearly_target.py`(year UNIQUE) 2. **schemas**:`episode.py`、`yearly_target.py`、`user_admin.py`、`auth.py` 顺手改 ConfigDict 3. **API**:`users.py`(管理员 CRUD)、`episodes.py`(增删改查)、`yearly_targets.py`(增删改查,新增 DELETE) 4. **路由注册**:`main.py` 注册新路由 --- ## 三、Task 1 冒烟实测记录(2026-05-15 Cline 自跑) 使用 `backend/scripts/smoke_test.py`(幂等版可重复跑),结果: | 步骤 | 接口 | 预期 | 实际 | 结果 | |------|------|------|------|------| | 1 | POST /api/users 新建 zebian | 201 | 201 | ✅ ID=4 | | 2 | POST /api/yearly_targets 新建 2026 | 201 | 201 | ✅ ID=2 | | 3 | POST /api/yearly_targets 重复 2026 | 409 | 409 | ✅ 正确拦截 | | 4 | POST /api/episodes editor_id=null | 201 | 201 | ✅ ID=2 | | 5 | GET /api/episodes 列表 | 200 | 200 | ✅ | | 6 | PATCH /api/yearly_targets 尝试改 year | 200(year 不变) | 200 | ✅ year 仍为 2026 | **6/6 全部通过。** --- ## 四、业务红线验证记录 | 红线 | 代码位置 | 验证结果 | |------|----------|----------| | 收视颜色只对 audience_share | `episode.py` 注释 | ✅ 未实现颜色逻辑,仅存字段 | | 年度目标只增不改 year UNIQUE | `yearly_targets.py` create_target + `yearly_target.py` UNIQUE | ✅ 冲突报 409 | | year 字段禁止 UPDATE 修改 | `yearly_targets.py` update_target pop("year") | ✅ 冒烟 Step 6 验证 | | editor_id 可空+editor_name_snapshot 必填 | `episode.py` Field 设计 | ✅ 冒烟 Step 4 editor_id=null 成功 | | 角色枚举全拼音 | `UserRole` Enum + CHECK 约束 | ✅ 三角色全是拼音 | | ON CONFLICT DO NOTHING(非 UPDATE) | `yearly_targets.py` 注释 | ✅ 只增不改 | | password_hash 明文传输开发配置 | `security.py` + `main.py` https_only=False | ⚠️ Phase 5 部署前必改 | --- ## 五、踩过的坑(Phase 2 内部教训) ### 坑 1:API 文件漏了模型类导入(一类 bug) **现象**:Swagger UI 实测 `POST /api/yearly_targets` 报 500,`NameError: name 'YearlyTarget' is not defined` **原因**:`api/yearly_targets.py` 和 `api/episodes.py` 用了 `Episode` / `YearlyTarget` 类但顶部漏了 import 语句 **排查**:同步检查 `api/users.py`、`api/episodes.py`、`api/yearly_targets.py` 三文件,发现 episodes.py 也漏了 **修复**: - `episodes.py` 加 `from app.models.episode import Episode` - `yearly_targets.py` 加 `from app.models.yearly_target import YearlyTarget` - `users.py` 删掉未使用的 `verify_password` 导入 **教训**:新建 API 文件后,**第一件事检查所有模型类是否已 import**,与 Schema import 同等优先级。Task 1 完工报告前未做最小冒烟测试,导致这类问题漏到实测阶段才发现。以后每个 Task Act 完成后必须自跑冒烟,不依赖制片人发现。 ### 坑 2:冒烟脚本 emoji + 中文导致 Windows GBK 崩溃 **现象**:`UnicodeEncodeError: 'gbk' codec can't encode character '\u274c'` **修复**:将 `smoke_test.py` 全部改英文 + ASCII 字符(`✅`→`[OK]`,`❌`→`[FAIL]`) ### 坑 3:数据不幂等导致重跑失败 **现象**:第二次跑冒烟,Step 1/2/4 全部 409(数据已存在) **修复**:改为幂等版脚本,先清理同名数据再创建 ### 坑 4:pip 默认路由到系统 Python 3.14,绕过项目锁定的 3.12 **现象**:`pip install pandas openpyxl` 后报错,或运行时报 `ModuleNotFoundError: No module named 'pandas'`(已确认装过) **原因**:系统 PATH 中 python 命令指向 Microsoft Store 的 Python 3.14,而项目要求 Python 3.12,导致装到了错误的解释器。`pip show pandas` 发现路径在 AppData/Local/Microsoft/WindowsApps 而非项目 venv。 **修复**: - 确认项目使用 `python -m pip install ...`(明确调项目锁定的 3.12) - 或激活 venv 后 `& .\venv\Scripts\Activate.ps1` 再 pip install - 装完 `pip show pandas` 核对路径包含 `venv` 或项目目录 **教训**:**任何 pip install 必须验证装到了哪个 Python 解释器**。先用 `where python` 确认 PATH 优先级,用 `python -c "import sys; print(sys.executable)"` 确认实际调用的解释器,再执行安装。 --- ### 坑 5:002b SQL 表达式索引使用 EXTRACT(...)::int 语法不被 PostgreSQL 接受 **现象**:`CREATE INDEX idx_episodes_broadcast_date ON episodes (EXTRACT(year FROM broadcast_date)::int);` 在 pgAdmin / psql 中执行报错:`ERROR: syntax error at or near "::"` **原因**:PostgreSQL 的 `EXTRACT()` 返回 `double precision` 类型,`::int` 是标准 cast 语法,但某些 PostgreSQL 版本或客户端对表达式索引内 cast 有限制。SQL 在 commit 前未本机干跑直接进库,翻车后才发现。 **修复**:改为 `date_part('year', broadcast_date)` 语法,等效且兼容性更好: ```sql CREATE INDEX idx_episodes_broadcast_date ON episodes (date_part('year', broadcast_date)); ``` **教训**:**SQL 文件 commit 前必须在本机干跑**。即使是看似简单的 DDL,本机跑一遍能提前发现语法兼容性问题,避免入库后才发现翻车。Task 1 NameError(坑 1)与本坑同源,都是"没自测就 commit"。 --- ### 坑 6:Gitea OAuth refresh token 过期导致 git push 反复 401 **现象**:`git push` 多次返回 401 Unauthorized,Git 凭证管理器反复弹框,输入正确账密后仍然失败。 **原因**:Gitea OAuth access token 有效期到期,refresh token 也已失效,导致 Git 凭证无法续期。多次 401 后需重新完成 OAuth 认证流程刷新 token。 **修复**: - 删掉旧 token:Git Bash 执行 `git credential reject` + 重置 credential helper - 重新触发 GitHub/Gitea OAuth 流程获取新 access token - 验证:`git push` 成功后确认 remote URL 使用新的 token **教训**:**push 不成功不算完工**。本次 Task 2 期间 push 已全部恢复正常,之前的 git push 条目从"认证失败,需制片人手动"已转为✅完成。 --- ## 六、纪律警告(Phase 2 Task 2 期间立) ### 纪律 1:"push 待制片人手动处理"不算完工 **背景**:Task 1 期间 MiniMax 多次以"push 待手动处理"作为完工结尾。本 chat 期间明确规矩。 **教训**:push 是 Git 工作流最后一环,未 push 就不是真正的完工。Git commit 只代表本地变更保存,push 成功才代表远程仓库同步完成。在任务报告中以"push 待手动处理"结尾会误导协作者认为工作已完成。 **落地规矩**:任何任务的完工报告必须包含"git push 成功"状态才算闭环。若 push 遇到认证问题: - 优先排查 token 状态(坑 6 经验) - 不得以"待手动处理"为由跳过 push 环节 - push 失败时用红字标注"⚠️ push 待解决",不写入"完工"状态 --- ### 纪律 2:SQL / 代码 commit 前必须本机干跑 **背景**:Task 1 NameError(坑 1)+ Task 2 002b SQL 语法错(坑 5)同源,都是 commit 前没自测直接进库。 **教训**:任何变更(SQL 文件、后端代码、前端代码)在 commit 前必须经过本机验证。对于 SQL 文件,本机用 `psql -U postgres -d milsci_dev -f backend/sql/00X_xxx.sql` 干跑;对于 Python 代码,用 `python -m pytest` 或冒烟脚本验证。发现错误当场修,不把问题带进 commit。 **落地规矩**: - SQL 文件:commit 前用 psql 在本机 dev 数据库干跑,确认无语法错误 - Python 代码:至少跑一遍冒烟测试或单元测试,确认基本功能正常 - 禁止用"这只是小改/语法简单/理论上没问题"为由跳过自测 - 养成习惯:**commit 前自测,发现问题当场修,比入库后发现 cost 低一个数量级** --- ## 七、遗留事项 ### 7.1 Phase 2 内部待完成 - [x] **Task 1 后端 CRUD**:✅ 冒烟 6/6 全绿 - [x] **git push**:✅ 已恢复正常(坑 6 已解决) - [ ] **Task 2**:Excel 批量导入(收视数据 + 排期模板)进行中 - [ ] **Task 3**:前端仪表盘 + 录入界面 + 用户管理界面 ### 7.2 跨 Phase 遗留 #### Phase 4 或后续 Task 处理 - **PATCH /api/users 接口 password 相关权限**:当前 `UserUpdate` Schema 允许改 `is_active`、`role`、`display_name`、`profile_text`,但: - `password_hash` **不允许**通过 API 修改(当前设计) - 能否改自己密码?不能(当前设计) - 改别人密码是否强制对方下次登录改密?未实现 - 建议 Phase 4+ 新增独立 `/api/users/change-password` 接口,设计时拉 Op #### Phase 5 部署时处理(与 SECRET_KEY 并列) - **传输安全**:当前 `password` 在请求体明文传输,仅 HTTPS 环境下可接受。Phase 5 部署到腾讯云后: - 改 `same_site="strict"` + `https_only=True` - 确认 HTTPS 开启,强制全站 TLS - 考虑 API 请求体密码加密或单独 `/api/auth/change-password` 接口 - **SECRET_KEY 占位符**:`.env` 当前是 `change-me-to-a-random-string-in-production`,**Phase 5 部署前必须改**为真实随机值。生成命令: ```python python -c "import secrets; print(secrets.token_urlsafe(32))" ``` --- ## 八、Git commit 记录 | commit | 内容 | |--------|------| | 143601c | docs: 合入第十三章 Opus task brief 节制原则,版本升至 v5 | | 33bee0c | feat: 后端 CRUD 完成(episodes + yearly_targets + users 管理 API) | | 63c031a | docs: Task 1 完工记录,Swagger UI 验收清单,跨 Phase 遗留事项 | | 4f7c92d | fix: 修复 episodes.py + yearly_targets.py 模型导入缺失,删 users.py 残留 verify_password,增 DELETE 接口(幂等冒烟后确认 6/6 全绿) | | f82a71b | docs: phase2_log 追加 Task 2 进行中坑与纪律警告 | --- --- ## 九、Task 2 完工记录(2026-05-19 commit 442d52f) ### 9.1 冒烟结果(8 步全绿) | Step | 描述 | 预期 | 实际 | 结果 | |------|------|------|------|------| | 1 | GET /api/imports/template | 200 xlsx | 200 xlsx (5595 bytes) | ✅ | | 2 | POST /api/imports/episodes 合法行 | 200 success=1 | 200 success=1 failed=0 | ✅ | | 3 | POST 部分失败(1ok+2bad) | 200 errors=2 | 200 errors=2 row_number=[3,4] | ✅ | | 4 | GET /api/imports/errors/{batch_id} | 200 xlsx | 200 xlsx (5181 bytes) | ✅ | | 5 | POST 2024 无年度目标 | 400 | 400 含"年度目标"+"2024" | ✅ | | 6 | POST 重复期次(库内重复) | 数据库约束触发 | failed_count=1 UniqueViolation | ✅ | | 7 | POST 未知编导"李不存在"软落地 | 200 GET id 确认 null | 200 id=22 editor_id=null | ✅ | | 8 | GET /api/episodes/{真实id} 验收 | 200 editor_id=null | 200 ✅ | ✅ | **smoke 脚本:`backend/scripts/import_smoke.py`(幂等版,可重复跑)** --- ### 9.2 坑 7:pandas 2.x 不接受 raw bytes(BytesIO 包装解决) **发现时间**:2026-05-19 第一轮冒烟 **现象**:`pd.read_excel(file_content)` 传入 bytes,pandas 2.x 报错或读不出数据。 **原因**:pandas 2.x 要求 file-like object,不再直接接受 bytes。 **修复**:`excel_service.py` 中用 `io.BytesIO(file_content)` 包装后传给 `pd.read_excel`。 **commit**:442d52f --- ### 坑 8:openpyxl add_named_style 误用(4ca24dc 原代码遗留) **发现时间**:2026-05-19 冒烟 Step 4 下载失败行 Excel **现象**:`generate_error_excel()` 调用 `wb.add_named_style("error_header")` 触发 `TypeError: Only NamedStyle instances can be added`,导致 GET /api/imports/errors/{batch_id} 500。 **来源**:经 git show 4ca24dc 确认,该 bug **在 4ca24dc 原代码中已存在**,非修坑引入。4ca24dc 自检只发现了 pandas bytes 问题,未发现此第二个 bug。 **修复**:删除 `wb.add_named_style("error_header")` 这一行(该样式未实际使用)。 **教训**:commit 前自测要覆盖**所有**接口路径,不能只测主要流程。Step 1-3 通过不等于 Step 4 也通。 --- ### 9.3 业务红线澄清:Step 6 重复期次拦截的两套机制 **两套拦截路径,各管各的事,不能混为一谈:** 1. **文件内重复 → check_duplicates() 整体 409** - 检测 Excel 内部是否有重复的 (year, episode_number) - 由 `ExcelService.check_duplicates()` 在入库前调用 - 有重复则整体拒绝,整文件不入库,返回 HTTP 409 2. **库内重复 → 数据库 UNIQUE 约束 单行失败,其他行入库** - 检测 Excel 中某行与数据库已有记录重复 - 由 PostgreSQL `idx_episodes_year_number` 唯一约束触发 - 重复行报错进入 `errors[]`,其他行继续入库,返回 HTTP 200 **Step 6 实测的是第二套**(库内重复):Excel 写入 episode_number=99(与 Step 2 已入库的重复),数据库约束抛出 `UniqueViolation`,整行失败,success=0 failed=1。这两套机制分管文件内重复和库内重复,职责清晰。 --- ### 9.4 纪律 3:启动期三件套不能证明接口业务路径通 **背景**:Task 2 开发期间,启动期(imports.py + excel_service.py + schemas/imports.py)写完后只跑了 GET 接口自测,POST/PATCH/DELETE 接口靠理论推导认为没问题就直接 commit。实际冒烟时发现 Step 2/3/4/5/6/7 全是 POST,全部出问题。 **教训**:启动期三件套(models/schemas/API 文件 + 路由注册 + 依赖注入)只能证明**文件层面**没有导入错误、路由能匹配、类型能解析。**不能**证明接口的业务路径(读写数据库、事务提交、错误处理)正常。任何 POST/PUT/PATCH/DELETE 接口 commit 前必须发真请求验证业务路径。 **落地规矩**: - 所有变更(SQL 文件、后端代码、前端代码)在 commit 前必须经过本机验证 - POST/PUT/PATCH/DELETE 接口必须用真请求实测,不能靠代码逻辑推导 - 发现错误当场修,不把问题带进 commit --- ### 9.5 遗留 bug:smoke 脚本 Step 7 写错(已改正) **发现时间**:2026-05-19 冒烟 Step 7 第一版 **现象**:GET /api/episodes/300 返回 404,"在库中不存在"。 **原因**:smoke 脚本**把 episode_number 当 id 传**给 GET /api/episodes/{id}。后端 `GET /api/episodes/{id}` 接收的是数据库自增主键 id,不是业务期号 episode_number。episode_number=300 的记录入库后真实 id 是 14(或列表查询返回的其他值),不是 300。 **改正**:先从 `GET /api/episodes?limit=100` 列表中筛选 episode_number=301 的记录,取其真实 id,再用该真实 id 调用 `GET /api/episodes/{real_id}`。确保 GET /api/episodes/{id} 路径被实际验收。 **教训**:smoke 测试中查到新记录后,应先从查询结果中提取真实 id,再进行后续验证,不能假设 episode_number 等于数据库 id。 --- ### 9.6 Task 2 收尾清单(还未做的四件小尾巴) - [ ] **补录 2024 真值年度目标**:Step 5 冒烟用到 2024 年,当前库中无 2024 目标,补录后 Step 5 才能真正通过 - [ ] **Windows python 默认 PATH 改指向 3.12**:坑 4 未完全解决,`py` 命令仍可能路由到系统 Python 3.14,需在系统 PATH 中显式优先 3.12 - [ ] **backups/ 进 .gitignore**:虽然 .gitignore 有 `*.log` 等模式,但 `backups/` 目录显式加入更稳,避免样本数据误传 - [ ] **MiniMax 自检报告里 schema 类名对照 Plan 自查**:`ImportError`/`ImportResult` vs `UploadTemplateResponse`/`ImportResultResponse` 等命名差异对着 Plan 确认是否一致 --- ## 十、Git commit 记录(2026-05-19 追加) | commit | 内容 | |--------|------| | 442d52f | feat: 完善批量导入逻辑,8步冒烟全绿 | *本日志由 MiniMax M2.7(Cline Act 模式)于 2026-05-15 持续追加,2026-05-19 追加 Task 2 完工坑与红线澄清,2026-05-20 追加 Task 2 收尾记录。* --- ## 十一、Task 2 收尾完工记录(2026-05-20) ### 11.1 Git 安全卫生:token 从 URL 剥离 **背景**:`.git/config` 里 origin 远程 URL 含明文 token(`cline-tps-dashboard`), 存在被 Cline 误贴日志、截图泄露、备份带出等风险。 **改动**: - `git remote set-url origin` 改成干净 URL(去掉 `user:token@`) - token 改由 Windows Git Credential Manager (manager) 加密存储 - 两次 fetch 静默通过,push 走 GCM 透明工作 - 旧 token `cline-tps-dashboard` 继续使用,不吊销 **纪律备注**:以后任何凭证(密码、API key、token)都不许写进 URL、不许写进 .py 文件、 不许写进任何会被 commit 的文件。 ### 11.2 小尾巴 1:补录 2024 真值 **操作**:制片人手动 psql 直接 INSERT(绕过 API,因 API 权限收紧待办未做)。 **当前 yearly_targets 真值**: - 2024: base=0.6448 / stretch=0.8290 (issued_date=2024-01-01, 补录说明在 notes) - 2025: base=0.6448 / stretch=0.8989 - 2026: base=0.6448 / stretch=0.8989 ### 11.3 小尾巴 3:backups/ 进 .gitignore **commit**:20c0a1d(chore: backups/ 进 .gitignore) **push**:已成功 ### 11.4 小尾巴 4:schema 类名自查通过 类名与 Task 2 Plan 一致,无漂移。`ImportError` / `ImportResult` 命名正确。 发现 2 个隐患,**暂不处理,记为待办**: - 隐患 A:`ImportError` 与 Python 内置同名,当前未撞,未来若在导入逻辑里 raise 才会冲突 - 隐患 B:`ExcelService.import_episodes()` 返回 dict 而非 Pydantic 对象, API 层手动构造 `ImportResult`,缺一次 Pydantic 校验层。8 步冒烟已验证业务正确, 仅代码洁癖问题 ### 11.5 小尾巴 2:Python PATH 根治坑 4 **改动**:从系统 PATH 删除 `C:\Python314\Scripts\` 和 `C:\Python314\` 两行。 用户 PATH 中 Python 3.12 自动顶上来。 **验证**: - `where.exe python` 第一行变成 3.12 路径 - `python --version` = Python 3.12.10 - `sys.executable` 指向 3.12 **遗留**:E:\tps-dashboard\backend\ 下没有 venv,Python 依赖装在系统 3.12 全局 site-packages 里。**记入待办**,Phase 4 部署前或 Task 3 启动前评估是否补 venv。 ### 11.6 待办清单(Task 3 启动前或后续 Phase 处理) | 待办 | 触发时机 | 优先级 | |---|---|---| | `yearly_targets` 写权限收紧到 `zhipianren` + `zebian`,`biandao` 只读 | Task 3 启动前 | 高(业务红线) | | backend/ 补 venv,把全局依赖搬进去 | Task 3 启动前评估 | 中 | | 隐患 A:`ImportError` 同名冲突预防 | 真要 raise 时再说 | 低 | | 隐患 B:`ExcelService` 返回 Pydantic 化 | 代码洁癖,有空时做 | 低 | ### 11.7 Task 2 完整 commit 链(2026-05-19 → 2026-05-20) | commit | 内容 | |---|---| | 442d52f | feat: 完善批量导入逻辑,8步冒烟全绿(2026-05-19) | | 6de8111 | docs: Task 2 完工记录,坑7/8/纪律3/Q1-Q4 写入(2026-05-19) | | 20c0a1d | chore: backups/ 进 .gitignore(Task 2 收尾)(2026-05-20) | **Task 2 完整收尾,Phase 2 Task 2 状态:✅ 完工。** --- ## 第十二章 yearly_targets 写权限收紧(2026-05-20) **状态:✅ 完工。冒烟 12/12 全绿。** ### 12.1 任务来源 task brief 明确要求收紧 yearly_targets 表的写权限,是业务红线。 ### 12.2 改动文件 | 文件 | 改动内容 | |------|----------| | `backend/app/api/yearly_targets.py` | 5处 `require_role` 参数修正 | | `backend/app/schemas/yearly_target.py` | `YearlyTargetUpdate` 改为部分字段可选(支持 PATCH 部分更新) | | `backend/scripts/smoke_yearly_targets_auth.py` | 新建,12步幂等冒烟脚本 | | `README.md` | 新增"角色权限表"节 | | `.clinerules` 5.5 | 新增 yearly_targets 写权限收紧说明 | | `logs/phase2_log.md` | 本章记录 | ### 12.3 权限矩阵最终状态 | 接口 | zhipianren | zebian | biandao | |------|-----------|--------|---------| | GET 列表/详情 | ✅ 200 | ✅ 200 | ✅ 200 | | POST 新增 | ✅ 201 | ✅ 201 | ❌ 403 | | PATCH 修改 | ✅ 200 | ✅ 200 | ❌ 403 | | DELETE 删除 | ✅ 204 | ❌ 403 | ❌ 403 | ### 12.4 GET 权限修改说明 GET 权限修改原本不在 task brief 范围,是 Plan 阶段自查发现并扩大的范围。 Opus 审 Plan 时同意纳入,理由:权限矩阵整体校正一次做完更合理。 此条在 Opus 审方案 v2 中明确记录,phase2_log 本章也写入此说明,不得糊涂带过。 ### 12.5 冒烟实测记录 `backend/scripts/smoke_yearly_targets_auth.py`,12步结果: | 步骤 | 场景 | 预期 | 结果 | |------|------|------|------| | 1 | zhipianren POST | 201 | ✅ | | 2 | zebian POST | 201 | ✅ | | 3 | biandao POST | 403 | ✅ | | 4 | 未登录 POST | 401 | ✅ | | 5 | zhipianren PATCH | 200 | ✅ | | 6 | zebian PATCH | 200 | ✅ | | 7 | biandao PATCH | 403 | ✅ | | 8 | zhipianren DELETE | 204 | ✅ | | 9 | zebian DELETE | 403 | ✅ | | 10 | biandao DELETE | 403 | ✅ | | 11 | biandao GET 列表 | 200 | ✅ | | 12 | zhipianren GET 列表 | 200 | ✅ | **12/12 全部通过。** ### 12.6 测试账号保留 冒烟脚本末尾显式打印保留账号(供后续手动清理): - `test_zebian_smoke` (zebian) - `test_biandao_smoke` (biandao) --- ## 第十三章 Task 3 前端三界面收官(2026-05-22) **状态:✅ 完工。六条尾巴全闭环,Phase 2 整体收官。** Task 3 = 仪表盘真数据 + 用户管理 + 责编录入页。本次开局承接 2026-05-21 的"部分完成、未收官"状态(HEAD `1ebb8a6`),清完六条尾巴正式收官。 ### 13.1 今日 commit 链 | commit | 内容 | |--------|------| | `4946ab2` | fix: AppLayout 未挂载——路由改两层 Outlet 嵌套,AuthGuard 不再包裹 AppLayout | | `ebcb530` | fix: episodes GET 放开三角色,写权限仍仅 zhipianren/zebian | | `c7296a5` | style: 柱图三色换降饱和柔和版(柔蓝/柔绿/柔红),图例同步 | | `5839fc6` | chore: .gitignore 新增 cookies*.txt 忽略规则 | (昨日另有 `bfcaddf` CSS 改动,非侧边栏病根但保留无害。) ### 13.2 六条尾巴收官情况 1. **侧边栏死穴(最硬)**:simonkoson 登录后整个侧边栏不渲染。连追五轮才定位真因——**根因不是 CSS(前两轮误判 fixed/flex),是 AuthGuard 被当包裹组件用却内部只渲染 ``、丢弃了 children ``**。改为路由两层 Outlet 嵌套(AuthGuard 独立守卫层 + AppLayout 独立 layout route)。三账号实测侧边栏 + 角色过滤全对(zhipianren 见用户管理、zebian 见责编录入不见用户管理、biandao 两者都不见)。 2. **episodes GET 放开三角色**:biandao 仪表盘从空变有数据;反向验证 biandao 发 POST/PATCH/DELETE 全 403。 3. **柱图配色**:见 13.4 色板固化。 4. **EditorDesk 责编录入**:**实测发现三界面本就完整**(三 Tab + 列表 CRUD + 表单字段齐全,含 audience_share 输入框),昨日"表单不全 / 找不到 audience_share"系侧边栏不通、进不去页面时凭印象误记。四步实测全过,无需改码: - 新增期次能存(期次 8888 / 份额 0.95 / 编导张颖) - 编导姓名快照必填、留空不能存(符合软引用红线:editor_id 可空、editor_name_snapshot 必填) - 年度目标只增不改:2028 能存、2026 重复被拒(year UNIQUE 生效,旧值不被覆盖) - 三色极端数据实测通过 5. **cookies 进 .gitignore**:5 个测试 cookie 文件忽略,工作区干净。 6. **极端数据三色实测**:造 0.95→柔红、0.5→柔绿、0.71/0.72→柔蓝,三色同框,颜色逻辑 + 配色双验。 ### 13.3 侧边栏一役复盘(重点教训) | 轮次 | Cline 判定的"根因" | 实际 | 结果 | |------|-------------------|------|------| | 1 | AuthGuard user===null 返回 null,整页空白 | 与现象矛盾(仪表盘明明渲染了) | 打回 | | 2 | CSS `.app-sider` fixed 塌成 0 宽 | 猜测,未看真实 CSS | 打回 | | 3 | 改 fixed 为 flex | 改了但浏览器仍不显示 | 非病根 | | 4 | 制片人 F12 看真实 DOM | `#root` 下直接是 `.dashboard`,无 AppLayout 层 | 锁定方向 | | 5 | AuthGuard 不渲染 children 却被当包裹组件用 | 真因,两段代码 + DOM 三者咬合 | 修复成功 | 教训沉淀为下方纪律 4。 ### 13.4 柱图三色色板固化(制片人拍板) **降饱和柔和版**:达标 = 柔蓝 `#5b8db8` / 待提升 = 柔绿 `#7aa874` / 优秀 = 柔红 `#c0584f`。 原则:①降饱和度,贴合奶油底 `#fbf9f1` + 深绿主色 `#6b8e6b` 整体柔和基调,不用高饱和纯色;②待提升的绿须与主色深绿拉开(更浅更嫩),且与达标蓝有明显色相区分,杜绝"达标待提升一个色系"。 语义映射仍按 `.clinerules` 5.1:红 = 优秀 / 蓝 = 达标 / 绿 = 待提升。 **教训**:此色板此前仅口头拍板未入档,导致 Cline 重新生成时配错(达标套了主色深绿、待提升用土黄凑数)。**凡视觉拍板必入 log 固化**。 ### 13.5 纪律 4:根因未经代码 / DOM 实证,不许动手修 侧边栏一役 Cline 连续三轮在猜测的根因上改代码(loading 态 → CSS fixed → CSS flex),均非病根。最终靠制片人 F12 看真实 DOM + 逼 Cline 贴真实代码才定位。 **落地规矩**:Cline 报告"根因是 X"时,必须要求它贴出支撑该结论的真实代码 / DOM 证据;只要它在用"可能 / 也许 / 应该",就是在猜,打回。 --- ## 第十四章 Phase 2 整体收官(2026-05-22) - Task 1 后端 CRUD:✅(冒烟 6/6) - Task 2 Excel 批量导入:✅(冒烟 8/8) - Task 2 收尾 + yearly_targets 写权限收紧:✅(冒烟 12/12) - Task 3 前端三界面:✅(六尾巴全闭环) - **Phase 2 整体收官**,打 tag `phase2-dashboard-done` - 收官 commit:`5839fc6`,working tree clean ### 14.1 跨 Phase 遗留(带入后续) - **题图上传 Task(独立小 Task,下一个起手)**:①题图 banner 化;②仪表盘布局重排——题图做全宽 banner、近 9 期收视紧贴其下做全宽横条(不再挤在中间小格)。**走 Claude Design 流程**:Design 出可交互原型 → 制片人定稿 → Opus 翻译成色值 + antd 组件名规格 → 喂 MiniMax,**不直接把图甩给 Cline**(见 design_环节备忘.md)。 - **Phase 5 上线前数据清理**:清掉今日测试数据(期次 8888/8889 等、年度目标 2027/2028 等占位),录入真数据。删除走 UI 逐条删或 Cline 写"清业务表保真数据"SQL 经制片人确认后跑。 - backend/ 补 venv;隐患 A(ImportError 同名)、隐患 B(ExcelService 返回 Pydantic 化)低优;SECRET_KEY + same_site/https_only(Phase 5 部署)。 --- *第十三~十四章由 Claude Opus 4.7 协助、MiniMax M2.7 落地,于 2026-05-22 Task 3 收官后追加。*