10 KiB
10 KiB
Phase 2:管理端读写接口 - Context
Gathered: 2026-05-07
Status: Ready for planning
Source: 用户在 /gsd-plan-phase 2 调用时提供的内联约束(PRD 快速通道,等同 Phase 1 同模式)
本 phase 仅负责 DRF 序列化器 + view + URL 路由 + 鉴权 + 修改记录:
- 在
/api/v1/admin/credential-slot/暴露 GET(脱敏返回)+ PUT(全字段覆写)两个方法 - 复用现有
RedisTokenAuthentication(admin token 体系,keyadmin_token:{token}) - 响应必须经过
StandardResponseMiddleware壳层 - Access Token 在 GET 响应中通过
mask_token脱敏(仅末 4 位) - PUT 响应也走脱敏(写入成功后用脱敏值返回,避免运营在 admin 工具里看到自己刚提交的明文回显)
- 接口自动暴露到
/swagger/+/redoc/(drf-yasg),schema 与实际行为一致 - 修改记录在 qy_lty + qy-lty-admin 两端互引条目(Phase 2 是首次跨项目接口契约落地,需要前端记一条互引;qy-lty-admin 那侧 Phase 1 plan 已定义 CRED-FE-01 API client 会消费这个接口契约)
不负责(留给后续 phase):
- Phase 3:客户端读取接口(GET
/api/credential-slot/,user token 鉴权,明文返回)+ 阿里云日志脱敏 - 任何前端代码(在 qy-lty-admin 仓库的 CRED-FE-01 phase 里)
- DB 字段加密、审计日志、token 轮换等增强项
URL 与路由
- 路径:
/api/v1/admin/credential-slot/(trailing slash 沿用 Django 默认风格) - HTTP 方法:GET 和 PUT 在同一 URL(不分两个 endpoint,符合 RESTful 单例资源约定)
- 路由注册位置:决策由 planner 基于 read_first 后选择
- 候选 A:
aiapp/urls.py(凭据槽位语义偏向 AI) - 候选 B:仓库现有的 admin namespace
/api/v1/admin/在哪个 urls 文件汇总?planner 必须 read_first 全仓 grep/api/v1/admin/找到现有汇总点(推测在userapp/urls.py或qy_lty/urls.py),照抄注册风格
- 候选 A:
- 不新建
credentialapp(凭据槽位不值得一个独立 app;它就是 aiapp 的一个子能力)
View 实现
- 不用
ModelViewSet(带不必要的 list / create / delete,单例资源用不上) - 用
RetrieveUpdateAPIView(DRF 提供,开箱支持 GET + PUT/PATCH)或自定义APIView+ 手写get/put方法 - 推荐 自定义
APIView—— 单例语义不走lookup_field/pk,调get_or_create(pk=1)取那条唯一记录;用RetrieveUpdateAPIView反而需要重写get_object(),绕一圈不值得 - View 类命名:
CredentialSlotAdminView,放aiapp/views.py(沿用现有 view 文件)
Serializer
- DRF ModelSerializer:
CredentialSlotSerializer,放aiapp/serializers.py(如不存在则新建;planner 检查aiapp/是否已有 serializers.py) - 字段:
app_id、access_token、updated_at updated_at在 GET 响应里 read_only(auto_now 自动维护)- Access Token 脱敏在 view 层处理,不在 serializer 层:serializer 直接把明文交给 view,view 在返回响应前用
mask_token替换;理由:PUT 写入时需要明文走 serializer.is_valid + save(),脱敏放 view 层避免序列化器既要明文又要脱敏的双重责任 - 写入校验:
app_id和access_token都允许空字符串(与模型blank=True一致),但不允许 None(serializer 字段配allow_null=False)
鉴权
- 直接复用
RedisTokenAuthentication—— Phase 2 researcher 必须 read_first 找到该类的具体位置(推测userapp/authentication.py),并 read 现有/api/v1/admin/namespace 下任一接口的鉴权配置(如 Bot 管理接口)作为照抄对象 - View 类上配
authentication_classes = [RedisTokenAuthentication]+permission_classes = [IsAdminTokenAuthenticated](如果项目已有这种 admin-only permission;否则用IsAuthenticated+ 在 view 里加request.user.is_staff检查) - 关键:admin token 的语义是"该 token 来自
admin_token:{token}Redis key",普通 user token 来自token:{token};planner 应在 read_first 阶段确认现有 admin 接口怎么区分这两类 token(很可能RedisTokenAuthentication自身根据 key 前缀做了区分,且配套有一个 admin-only permission class) - 拒绝时返回 401(无 token)或 403(user token 但非 admin),错误响应必须经过
StandardResponseMiddleware壳层
Swagger / ReDoc
- 接口必须出现在
/swagger/+/redoc/,drf-yasg 会自动扫描 DRF 视图 - View 类上加
@swagger_auto_schema装饰器(method-level,给 GET / PUT 各写一份),声明 request body schema 和 response schema - response schema 显式标注
access_token字段语义为「末 4 位脱敏掩码」,避免前端误解为明文
跨项目联动(修改记录互引)
- 本 phase 同期写两端
docs/修改记录.md:qy_lty/docs/修改记录.md顶部写一条:"新增 Phase 2 管理端 REST 接口",跨项目联动字段引用qy-lty-admin/docs/修改记录.md的对应条目qy-lty-admin/docs/修改记录.md顶部写一条:"锁定 Phase 2 后端 API 契约(消费方文档化)",跨项目联动字段引用 qy_lty 的对应条目
- 这是 Phase 2 与 Phase 1 的关键差异:Phase 1 是纯服务端模型层、不涉及 API 契约,所以前端不需要互引;Phase 2 暴露 REST 接口给前端消费,必须互引
兼容性 / 不引入新依赖
- 沿用 Django 4.2.13、DRF 3.x(现版)、Python 3.8
- drf-yasg 已在依赖里,复用即可
- 不引入新依赖
Claude's Discretion
- 序列化器是否拆
CredentialSlotReadSerializer(脱敏返回) +CredentialSlotWriteSerializer(明文写入)两个类,还是用同一个 + view 层脱敏 —— planner 决定 - view 是放
aiapp/views.py末尾,还是新建aiapp/views/credential_slot.py子文件 —— 取决于aiapp/views.py现有规模 - 错误响应的具体 message 文案(中文),如
"凭据槽位需要管理员权限"/"未提供有效的管理员 token" - View 里如何确保
get_or_create(pk=1)在并发请求下不出竞态(最简:用select_for_update或单例语义本身已经被pk=1 + save 钩子兜底,可以不加锁)
<canonical_refs>
Canonical References
下游 agent 必读:
项目宪法
qy_lty/CLAUDE.md— 沟通语言(中文)+ 修改记录强制规则 + 跨项目互引强制qy_lty/.planning/PROJECT.md— Milestone v1.0「本期 Milestone」段、关键约束(响应壳层 / token 命名)qy_lty/.planning/REQUIREMENTS.md— Active 段 CRED-03 + CRED-04 完整描述qy_lty/.planning/ROADMAP.md— Phase 2 详情段(Goal、Success Criteria 4 条)qy_lty/.planning/phases/01-credential-data-layer/01-CONTEXT.md— 上一 phase 决策(pk=1 单例 + mask_token 工具的来源)qy_lty/.planning/phases/01-credential-data-layer/01-VERIFICATION.md— 上一 phase 验证证据(mask_token 行为、CredentialSlot.get_solo() 用法)
DRF / 鉴权 / 路由现成模式(必读)
qy_lty/aiapp/views.py— 同 app 内现有 view 写法qy_lty/aiapp/models.py—CredentialSlot+get_solo()+mask_token复用入口qy_lty/userapp/authentication.py—RedisTokenAuthentication实现(推测路径,researcher 确认)qy_lty/userapp/views.py—— 现有 admin 接口写法(推测含/api/v1/admin/命名空间下的 view 模板)qy_lty/qy_lty/urls.py或qy_lty/userapp/urls.py—/api/v1/admin/路由汇总点(researcher 找出来)qy_lty/common/middleware.py—StandardResponseMiddleware(确认它如何处理 DRF Response 对象)qy_lty/common/utils.py—mask_token工具(Phase 1 落地)
Swagger
qy_lty/qy_lty/urls.py或独立的 swagger 注册文件 — 看 drf-yasg 如何配 schema_view,照搬@swagger_auto_schema风格
修改记录
qy_lty/docs/修改记录.md— 头部「修改格式说明」+ Phase 1 已落地的两条条目可作模板qy-lty-admin/docs/修改记录.md— 头部「修改格式说明」(如有;如不存在 planner 应在 task 中提示用同款骨架格式新建)
</canonical_refs>
## 具体要点(Success Criteria 显式化)| # | 验证点 | 检查方式 |
|---|---|---|
| 1 | GET 携 admin token 返回脱敏壳层 | curl -H "Authorization: Bearer <admin_token>" /api/v1/admin/credential-slot/ 返回 200 + JSON 含 success: true / data.access_token: <末 4 位掩码> |
| 2 | PUT 携 admin token 全字段覆写 | curl -X PUT -H "Authorization: Bearer <admin_token>" -d '{"app_id": "x", "access_token": "y"}' .../credential-slot/ 返回 200 + DB 更新 + updated_at 刷新 |
| 3 | PUT 在空记录场景自动 get_or_create | DB 删除 pk=1 后调 PUT,依旧成功创建 |
| 4 | 无 token 返回 401 + 标准壳层 | curl /api/v1/admin/credential-slot/ 返回 401 + JSON 含 success: false / code != 0 / message != "" |
| 5 | 仅 user token(非 admin)返回 403 + 标准壳层 | 携 token:{token}(非 admin_token)调用返回 403 + 标准壳层 |
| 6 | Swagger 暴露 | /swagger/ HTML 页面含 /api/v1/admin/credential-slot/ 路径条目,含 GET + PUT 两个方法及对应 schema |
| 7 | drf-yasg request/response schema 与实际一致 | /swagger.json 含 app_id / access_token / updated_at 字段定义 + access_token schema description 标注「末 4 位脱敏掩码」 |
| 8 | 修改记录两端互引 | qy_lty/docs/修改记录.md 与 qy-lty-admin/docs/修改记录.md 顶部各有一条 Phase 2 条目,跨项目联动字段相互引用 |
- 客户端 GET
/api/credential-slot/— Phase 3 - 阿里云日志脱敏过滤器 — Phase 3
- PUT 写入时旧 access_token 的审计日志 — 不在 v1.0 milestone 范畴
- token 鉴权失败时尝试用其它 token 类型重试 / fallback — 当前架构禁止此类降级,无 token 就 401
- API 限流(防暴力 PUT) — 现有架构未上限流,不在本 phase 范畴
- DB at-rest 加密 — 未来评估,不在 v1.0 范畴
Phase: 02-admin-rest Context gathered: 2026-05-07 via inline PRD(用户在 /gsd-plan-phase 2 调用时提供完整约束)