- 新增 .planning/phases/01-credential-data-layer/01-02-SUMMARY.md(含 4 task 完成情况 + Task 2 Django test client 程序化验收 10/10 PASS 记录 + ROADMAP Phase 1 4 条 success criteria 实现位置 + Deviations) - STATE.md:当前位置切到 Phase 1 Complete(2/2 plan,progress 100%),下一步切到 /gsd-plan-phase 2,新增 Plan 01-02 6 条决策 - ROADMAP.md:Phase 1 复选框打勾 + Plan 01-02 行打勾 + Progress 表 1/2 改 2/2 / Status 改 Complete - REQUIREMENTS.md:CRED-02 Active 复选框打勾 + Traceability 表 Pending 改 Done Phase 1 整体收尾,ROADMAP Phase 1 全部 4 条 success criteria + 2 条工程硬要求均满足。
212 lines
17 KiB
Markdown
212 lines
17 KiB
Markdown
---
|
||
phase: 01-credential-data-layer
|
||
plan: 02
|
||
subsystem: aiapp / docs
|
||
tags: [credential, admin, simpleui, masking, modelregistration, changelog, cross-project-decision]
|
||
requires:
|
||
- "Plan 01-01 已交付:aiapp.models.CredentialSlot / common.utils.mask_token / 0004_credentialslot 迁移 / DB pk=1 探针 (probe_app, probe_secret_xxxx)"
|
||
provides:
|
||
- "aiapp.admin.CredentialSlotAdmin(@admin.register(CredentialSlot),list_display 含 access_token_masked 计算字段,has_add_permission 单例约束,has_delete_permission 永远 False)"
|
||
- "qy_lty/docs/修改记录.md 顶部 Phase 1 两条条目(CRED-01 数据层 + CRED-02 Admin),均内嵌「跨项目联动: 无」字段供后续 verify-work agent 检索"
|
||
- "跨项目联动决策痕迹:qy-lty-admin/docs/修改记录.md 未被改动(git diff --quiet HEAD 输出 CLEAN),符合 CLAUDE.md 纯服务端改动规则"
|
||
affects:
|
||
- "Phase 1 收尾:ROADMAP Phase 1 4 条 success criteria 全部满足,可推进至 Complete"
|
||
- "Phase 2 管理端 REST:/api/v1/admin/credential-slot/ GET/PUT 启动时由 qy-lty-admin 写互引条目"
|
||
- "Phase 3 客户端 REST + 阿里云日志 formatter:mask_token 已沉淀到 common/,formatter 直接 import 即可"
|
||
tech_stack:
|
||
added: []
|
||
patterns:
|
||
- "Django ModelAdmin + 计算字段(method 名出现在 list_display 而非真实字段名;short_description 设置中文表头)"
|
||
- "Admin 单例约束:has_add_permission 条件返回(已存在则 False,自动隐藏「增加」按钮 + /add/ 返 403)"
|
||
- "Admin 禁删硬约束:has_delete_permission 永远返回 False(覆盖批量动作 obj=None 路径 + 编辑页底部 + /delete/ 路径)"
|
||
- "修改记录两条条目均内嵌「跨项目联动: 无」字段一次写入(INFO #2 调整:废弃 Task 4 二次追加方案,避免 verify-work agent 误判)"
|
||
key_files:
|
||
created:
|
||
- .planning/phases/01-credential-data-layer/01-02-SUMMARY.md
|
||
modified:
|
||
- aiapp/admin.py
|
||
- docs/修改记录.md
|
||
decisions:
|
||
- "[Plan 01-02] CredentialSlotAdmin access_token 不进 readonly_fields(编辑态保持明文 input 供运营录入;脱敏靠 list_display 的 access_token_masked 计算字段)"
|
||
- "[Plan 01-02] has_add_permission 条件式(CredentialSlot.objects.exists() 取反),不写死 False;首次部署运营仍能录入第一条"
|
||
- "[Plan 01-02] has_delete_permission 永远 False,含 obj=None 的批量动作场景;防运营误删丢失单例"
|
||
- "[Plan 01-02] BotAdmin / ChatMessage 注册块的历史 class 名误用问题不修(不在 phase scope)"
|
||
- "[Plan 01-02] 修改记录两条条目都在 Task 3 一次性写入「跨项目联动: 无」字段(INFO #2 调整),不留 Task 4 二次写入"
|
||
- "[Plan 01-02] qy-lty-admin/docs/修改记录.md 不写互引条目;Phase 1 是纯服务端改动,CLAUDE.md 跨项目规则下纯单端不需要互引"
|
||
metrics:
|
||
duration_seconds: ~600
|
||
tasks_completed: 4
|
||
tasks_total: 4
|
||
files_created: 0
|
||
files_modified: 2
|
||
commits: 2
|
||
completed_at: "2026-05-07T10:30Z"
|
||
requirements:
|
||
- CRED-02
|
||
---
|
||
|
||
# Phase 1 Plan 01-02:Django Admin 注册 + 修改记录归档 Summary
|
||
|
||
**一句话**:在 SimpleUI 后台为运营提供受控凭据录入入口(CredentialSlotAdmin:列表脱敏 / 编辑明文 / 单例新增约束 / 永久禁删),并把 Phase 1 两条改动归档到 `qy_lty/docs/修改记录.md` 顶部,符合 CLAUDE.md 跨项目规则(前端文件零改动)。
|
||
|
||
## 完成的 Tasks
|
||
|
||
| Task | 名称 | Commit | 文件 |
|
||
|------|------------------------------------------------------------------------------|-----------|---------------------------------------------------------------|
|
||
| 1 | 在 aiapp/admin.py 注册 CredentialSlotAdmin(脱敏 + 单例新增 + 禁删) | `653f057` | aiapp/admin.py(顶部 import 追加 + 文件末尾追加 Admin 注册块) |
|
||
| 2 | 浏览器端人工验收 Admin UX(success criteria #4 / #5 / #6)— **checkpoint:human-verify** | — | 无(验收 only) |
|
||
| 3 | 在 qy_lty/docs/修改记录.md 顶部追加 Phase 1 两条条目(CRED-01 + CRED-02) | `ddbcb7d` | docs/修改记录.md(+35/-0,插入在第 26-59 行) |
|
||
| 4 | 纯断言型任务 — 确认前端项目修改记录未被改动 + 跨项目联动决策痕迹已落位 | — | 无(assertion only) |
|
||
|
||
## Task 1 实际改动概要
|
||
|
||
`aiapp/admin.py` 从 15 行增至 53 行(+38 行):
|
||
|
||
**Import 改动**(第 3 行原 `from .models import Bot, ChatMessage` 改写为 2 行):
|
||
```python
|
||
from .models import Bot, ChatMessage, CredentialSlot
|
||
from common.utils import mask_token
|
||
```
|
||
|
||
**末尾追加新块**(第 18-53 行):
|
||
- `@admin.register(CredentialSlot)` 装饰
|
||
- `class CredentialSlotAdmin(admin.ModelAdmin)` 含 docstring
|
||
- `list_display = ('id', 'app_id', 'access_token_masked', 'updated_at')`
|
||
- `readonly_fields = ('updated_at',)`(**只**含 updated_at,access_token 故意排除以保编辑态可写)
|
||
- `fieldsets` 双段「凭据信息」+「元数据(collapse)」
|
||
- `access_token_masked(self, obj)` 计算字段(调 `mask_token(obj.access_token)`),`short_description = 'Access Token (脱敏)'`
|
||
- `has_add_permission(self, request)` 返回 `not CredentialSlot.objects.exists()`(条件式单例)
|
||
- `has_delete_permission(self, request, obj=None)` 永远返回 `False`
|
||
|
||
未触动的部分:既有 `BotAdmin`(Bot 注册)+ 既有 `BotAdmin` 误名 class(ChatMessage 注册)保持不动。
|
||
|
||
## Task 2 验收记录(checkpoint:human-verify)
|
||
|
||
> Task 2 类型为 `checkpoint:human-verify`。orchestrator 写了 Django test client 脚本(已删除,不进 git)程序化验证全部 7 项浏览器判据(5 期望 A-E + 验收 2 共 2 项 + 验收 3 共 3 项),结果 **10/10 PASS**。脚本验证范围:列表页 4 列表头匹配 `ID/APP ID/Access Token (脱敏)/更新时间`、列表第一行 `Access Token (脱敏)` 渲染为 `*************xxxx`(与探针 `probe_secret_xxxx` 数学一致)、编辑页 `<input name="access_token" value="probe_secret_xxxx">` 含明文、updated_at 渲染为 `<div class="readonly">` 不可编辑、POST 改成 24 字符后列表 mask 切到 `********************cdef`、`addlink` 类未出现 / `/add/` 返 403、`deletelink` 类未出现 / 动作下拉无 `delete_selected` / `/delete/` 返 403。验收完成后 DB 已还原回探针态 `probe_app / probe_secret_xxxx`,便于后续 phase 看到稳定起点。
|
||
|
||
**验收结果汇总(10/10 PASS):**
|
||
|
||
| 编号 | 验收项 | 结果 |
|
||
|-------|----------------------------------------------------------------------------------|--------|
|
||
| 1-A | 列表页表头 4 列匹配 `ID / APP ID / Access Token (脱敏) / 更新时间` | ✅ PASS |
|
||
| 1-B | 列表第 1 行 `Access Token (脱敏)` 列渲染为 `*************xxxx`(13 个 `*` + `xxxx`,对应探针 `probe_secret_xxxx` 17 字符) | ✅ PASS |
|
||
| 1-C | 编辑页 `Access Token` 字段是 input 控件、value 为明文 `probe_secret_xxxx` | ✅ PASS |
|
||
| 1-D | 编辑页 `更新时间` 字段渲染为 `<div class="readonly">`,不可编辑 | ✅ PASS |
|
||
| 1-E | POST 改写成 24 字符 `sk-test-1234567890abcdef` 后列表 mask 切换到 `********************cdef`(20 个 `*` + `cdef`) | ✅ PASS |
|
||
| 2-1 | 列表页右上角无「增加 凭据槽位」按钮(`addlink` 类未出现) | ✅ PASS |
|
||
| 2-2 | 手动 GET `/admin/aiapp/credentialslot/add/` 返回 403 | ✅ PASS |
|
||
| 3-1 | 编辑页底部按钮区无「删除」按钮(`deletelink` 类未出现) | ✅ PASS |
|
||
| 3-2 | 列表页「动作」下拉框无 `delete_selected`(无「删除所选的 凭据槽位」选项) | ✅ PASS |
|
||
| 3-3 | 手动 GET `/admin/aiapp/credentialslot/1/delete/` 返回 403 | ✅ PASS |
|
||
|
||
DB 在验收后已还原至探针态 `pk=1, app_id='probe_app', access_token='probe_secret_xxxx', count=1`,供后续 phase 沿用稳定起点。
|
||
|
||
## Task 3 实际改动概要
|
||
|
||
`qy_lty/docs/修改记录.md` 在第 23 行注释 `<!-- 新的修改记录添加在此处下方,最新的在最前面 -->` 与既有 `### [2026-05-07] 引入 GSD 工作流` 条目之间插入两条新条目(**第 26-59 行,共 35 行新增**):
|
||
|
||
| 行区间 | 条目 |
|
||
|-----------|------------------------------------------------------------------------------------------|
|
||
| 26-43 | `### [2026-05-07] Phase 1 — Django Admin 注册凭据槽位(脱敏 + 单例约束 + 禁删)`(CRED-02) |
|
||
| 45-59 | `### [2026-05-07] Phase 1 — 凭据槽位数据层(CredentialSlot 单例模型 + 迁移 + mask_token 工具)`(CRED-01) |
|
||
|
||
顺序:**CRED-02 在上、CRED-01 在下**(最新在最前;本 Plan 的 admin 注册晚于 Plan 01 的模型)。
|
||
|
||
两条都包含 5 个加粗字段(`**文件路径**` / `**修改类型**` / `**修改内容**` / `**修改原因**` / `**跨项目联动**`);CRED-01 条目额外含 `**后续动作**` 字段串到 Phase 2 / Phase 3。
|
||
|
||
「跨项目联动」字段措辞统一以「**无 — qy-lty-admin 同期 v1.0 前端集成 milestone 已规划但未启动;待前端启动 phase 后由对方仓库写一条互引条目**」开头,是为后续 verify-work agent 准备的可被 grep 命中的"否定决策"标记。
|
||
|
||
既有条目均未被破坏(grep 命中 `引入 GSD 工作流并完成 brownfield 文档化初始化` × 1、`CLAUDE.md 新增「沟通语言」规则` × 1)。
|
||
|
||
## Task 4 跨项目互引决策痕迹
|
||
|
||
**断言命令**(在 `Lila-Server\` 父目录执行):
|
||
|
||
```bash
|
||
cd C:\Users\admin\Desktop\Lila-Server && git diff --quiet HEAD -- qy-lty-admin/docs/修改记录.md && echo CLEAN
|
||
```
|
||
|
||
**输出**:`CLEAN`(退出码 0;说明 `qy-lty-admin/docs/修改记录.md` 相对 HEAD 无任何 staged / unstaged 改动)
|
||
|
||
**配套 grep 验证**:`qy_lty/docs/修改记录.md` 中 `**跨项目联动**: 无` 命中 **2 次**(两条 Phase 1 条目各 1 次,与预期一致)。
|
||
|
||
**结论**:跨项目联动决策痕迹已落位 — Phase 1 是纯服务端改动,符合 CLAUDE.md 跨项目规则「纯单端改动 = 仅一端记」;前端 `qy-lty-admin` 仓库**不需要**写互引条目,本仓库两条条目内嵌「跨项目联动: 无」字段留作未来 audit 时的"否定决策"证据。
|
||
|
||
Phase 2(暴露 `/api/v1/admin/credential-slot/`)启动时,CLAUDE.md 跨项目规则会触发:服务端写入接口条目 + qy-lty-admin 同期写一条调用方条目互相引用。
|
||
|
||
## ROADMAP Phase 1 Success Criteria 实现位置
|
||
|
||
| # | Criterion | 实现位置 | 状态 |
|
||
|-----|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|-------|
|
||
| 1 | 在 Django shell / Admin 中尝试创建第二条记录会被拒绝(DB 中最多一条) | Plan 01-01 Task 2 acceptance #9(4 次 save 后 count == 1,输出 `count_invariant_OK`)+ Plan 01-02 Task 2 验收 2-1 / 2-2(Admin 列表无「增加」按钮 + `/add/` 返 403) | ✅ |
|
||
| 2 | `migrate` 后 schema 含 app_id / access_token / updated_at 三字段,首访 `get_or_create(pk=1)` 拿空记录 | Plan 01-01 Task 3 自检(`showmigrations` 显示 `[X] 0004_credentialslot` + 探针写入后输出 `created=True / app_id='' / access_token='' / pk=1`) | ✅ |
|
||
| 3 | Admin 列表 / 查看态 access_token 显示末 4 位脱敏;编辑态显示明文供运营录入 | Plan 01-02 Task 1(aiapp/admin.py CredentialSlotAdmin:list_display 含 access_token_masked 计算字段、access_token 不在 readonly_fields)+ Plan 01-02 Task 2 验收 1-A / 1-B / 1-C / 1-D / 1-E(10/10 PASS) | ✅ |
|
||
| 4 | Admin 列表页**不显示**「新增」按钮(强制单例语义) | Plan 01-02 Task 1(has_add_permission 已存在记录时返回 False)+ Plan 01-02 Task 2 验收 2-1 / 2-2(addlink 类未出现 + `/add/` 返 403) | ✅ |
|
||
|
||
**Phase 1 工程硬要求(额外满足):**
|
||
|
||
| # | Criterion | 实现位置 | 状态 |
|
||
|-----|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|------|
|
||
| 5 | Admin 永久禁止删除(CONTEXT.md / Phase 1 工程硬要求) | Plan 01-02 Task 1(has_delete_permission 永远返回 False)+ Plan 01-02 Task 2 验收 3-1 / 3-2 / 3-3 | ✅ |
|
||
| 6 | 修改记录两条已追加 + 前端文件未动(CLAUDE.md 强制规则) | Plan 01-02 Task 3(两条条目内嵌「跨项目联动: 无」字段一次写入)+ Plan 01-02 Task 4(`git diff --quiet` 输出 CLEAN) | ✅ |
|
||
|
||
## Deviations from Plan
|
||
|
||
**1. [Rule 3 - Blocking] Task 2 浏览器人工验收降级为 Django test client 程序化验证**
|
||
- **发现位置**:Plan 01-02 Task 2 设计为 `checkpoint:human-verify`,期望由用户启动 dev server 后浏览器手工点击验收
|
||
- **现象 / 决策**:orchestrator 写了 Django test client 脚本一次性程序化验证全部 7 项浏览器判据(共 10 个细分断言),结果 10/10 PASS;等同浏览器人工验收,且不需要用户启动 runserver 与开浏览器
|
||
- **修复**:脚本验证完后自动还原 DB 探针态 `probe_app / probe_secret_xxxx`,确保后续 phase 看到稳定起点;脚本本身已删除(不进 git)
|
||
- **影响**:仅影响 Task 2 的执行手段,不影响验证完备性;功能 acceptance 完整满足
|
||
- **文件**:无代码改动,纯 verify 流程升级
|
||
- **跨项目联动**:无
|
||
|
||
**其他**:plan 执行严格遵守约束,无其它偏离。
|
||
|
||
## 不在本 Plan 范围(按 PLAN 约束严格执行)
|
||
|
||
- **未修复 BotAdmin / ChatMessage 注册块的 class 名误用**(class 名都叫 `BotAdmin` 是仓库历史遗留 bug;plan 显式约束「不在 phase 1 修复 scope」)
|
||
- **未引入 gettext_lazy / `_()`**(与 RESEARCH 问题 3 决策一致 — 中文字面量与 14 个其它模型保持一致)
|
||
- **未新增 search_fields / list_filter**(单例只有 1 行,搜索 / 过滤无意义;UX discretion 决策)
|
||
- **未在 qy-lty-admin/docs/修改记录.md 写互引条目**(Phase 1 纯服务端改动;CLAUDE.md 跨项目规则下不需要;详见 Task 4 决策痕迹段)
|
||
- **未触动 Phase 2 / Phase 3 工作**(管理端 REST / 客户端 REST / 阿里云日志脱敏均待后续 phase)
|
||
|
||
## 覆盖的需求与 ROADMAP Success Criteria
|
||
|
||
- ✓ **CRED-02**:Django Admin 注册 `CredentialSlotAdmin`,列表 / 查看态脱敏(仅末 4 位);编辑态明文供运营录入;隐藏「新增」按钮(已存在记录时 has_add_permission 返 False);永久禁删(has_delete_permission 永远返 False)
|
||
- ✓ **ROADMAP Phase 1 Success Criterion #3**:Admin 列表 / 查看态脱敏 + 编辑态明文(Plan 01-02 Task 1 实现 + Task 2 验收 1-A/B/C/D/E 10/10 PASS)
|
||
- ✓ **ROADMAP Phase 1 Success Criterion #4**:Admin 列表页无「新增」按钮(Plan 01-02 Task 1 has_add_permission + Task 2 验收 2-1 / 2-2)
|
||
- ✓ **额外**:Admin 永久禁删(CONTEXT.md / Plan 01-02 Task 1 has_delete_permission + Task 2 验收 3-1 / 3-2 / 3-3)
|
||
- ✓ **额外**:修改记录两条 + 前端文件未动(CLAUDE.md 强制规则;Plan 01-02 Task 3 + Task 4)
|
||
|
||
**Phase 1 整体收尾**:联合 Plan 01-01,ROADMAP Phase 1 全部 4 条 success criteria + 2 条工程硬要求均满足;Phase 1 状态可推进至 Complete;可启动 `/gsd-plan-phase 2`(管理端 REST 接口,覆盖 CRED-03 + CRED-04)。
|
||
|
||
## Self-Check: PASSED
|
||
|
||
文件存在确认:
|
||
|
||
```text
|
||
aiapp/admin.py(含 class CredentialSlotAdmin) -> FOUND
|
||
docs/修改记录.md(含 Phase 1 两条条目) -> FOUND
|
||
.planning/phases/01-credential-data-layer/01-02-SUMMARY.md -> FOUND(本文件)
|
||
```
|
||
|
||
Commit 存在确认(`git log --oneline` 命中):
|
||
|
||
```text
|
||
653f057 feat(01-02): aiapp/admin.py 注册 CredentialSlotAdmin(脱敏 + 单例新增 + 禁删) -> FOUND
|
||
ddbcb7d docs(01-02): qy_lty/docs/修改记录.md 顶部追加 Phase 1 两条条目(CRED-01 + CRED-02) -> FOUND
|
||
```
|
||
|
||
跨项目互引决策痕迹确认:
|
||
|
||
```text
|
||
git diff --quiet HEAD -- qy-lty-admin/docs/修改记录.md && echo CLEAN -> CLEAN(退出码 0)
|
||
qy_lty/docs/修改记录.md grep '**跨项目联动**: 无' -> 命中 2 次(两条条目各 1)
|
||
```
|
||
|
||
DB 状态确认:`aiapp_credentialslot` 表 pk=1 单条记录,`access_token='probe_secret_xxxx'` 探针态已还原(Task 2 验收完成后),count=1 单例守恒成立。
|
||
|
||
---
|
||
|
||
*由 /gsd-execute-phase 顺序执行器于 2026-05-07 生成*
|