# Phase 2 开发日志 — 仪表盘真数据 + 用户管理 + Excel 批量导入

> 《军事科技》栏目内部工作台 · Phase 2
> 撰写日期:2026-05-15
> 状态:**Task 1 后端 CRUD 完成，冒烟实测 6/6 通过**

---

## 一、本 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（数据已存在）

**修复**：改为幂等版脚本，先清理同名数据再创建

---

## 六、遗留事项

### 6.1 Phase 2 内部待完成

- [x] **Task 1 后端 CRUD**：✅ 冒烟 6/6 全绿
- [ ] **git push**：认证失败，需制片人手动 push（见 phase1_log.md 坑4）
- [ ] **Task 2**：Excel 批量导入（收视数据 + 排期模板）
- [ ] **Task 3**：前端仪表盘 + 录入界面 + 用户管理界面

### 6.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 遗留事项 |
| (本地待提交) | fix: 修复 episodes.py + yearly_targets.py 模型导入缺失，删 users.py 残留 verify_password，增 DELETE 接口（幂等冒烟后确认 6/6 全绿） |

---

*本日志由 MiniMax M2.7(Cline Act 模式)于 2026-05-15 持续追加。*