8.4 KiB
Raw Blame History

Phase 1凭据槽位数据层 - Context

Gathered: 2026-05-07 Status: Ready for planning Source: 用户在 /gsd-plan-phase 1 调用时提供的内联约束(等同 PRD 快速通道)

## Phase 边界

本 phase 仅负责数据库层 + Django Admin 入口

  • 落地 CredentialSlot 单例 Django 模型 + 数据迁移
  • 注册 Django AdminSimpleUI 主题 + 中英 i18n
  • 单例语义DB / 模型 / Admin 三层都要禁止出现第二条记录
  • Access Token 字段在 Admin 列表/查看态脱敏(仅显示末 4 位),编辑态明文供运营录入

不负责(留给后续 phase

  • Phase 2管理端 REST 接口GET/PUT /api/v1/admin/credential-slot/
  • Phase 3客户端 REST 接口 + 阿里云日志脱敏
  • 任何前端工作(在独立项目 ../qy-lty-admin/ 自己的 milestone 里)
## 实现决策(锁定)

模型层CRED-01

  • 单例语义实现方式:pk=1 固定主键 + get_or_create(pk=1) 模式
    • 在模型 save() 钩子中强制 self.pk = 1,配合 get_or_create(pk=1) 让首次访问拿到一条空记录
    • 不使用"单字段唯一约束"备选方案,因为该方案要求始终有非空字段,灵活性不如 pk=1
  • 字段(最小集,沿用 ParadiseUser / 其它现有模型的命名习惯):
    • app_idCharFieldmax_length 合理128 足够覆盖常见服务商 ID 长度),允许空字符串(运营初次访问时尚未填写)
    • access_tokenCharFieldmax_length 长512 留余量,覆盖 JWT 之类的长 token允许空字符串
    • updated_atDateTimeFieldauto_now=True,每次保存自动刷新
  • app 归属:aiappresearcher 已确认:common/ 不是 Django app 无 apps.py、未在 INSTALLED_APPS,无法承载 Modeluserapp/models.py 已 471 行承重过大;aiapp/models.py 当前仅 51 行追加合适;语义"凭据"与 AI 服务商接入强相关)。模型追加到 aiapp/models.py 末尾,不新建子文件
  • 单例模式直接复刻 userapp.models.AffinitySetting247-314 行):仓库已有 pk=1 单例范本含 save() 钩子重定向 + get_solo() 类方法。Planner 必须把这两个文件读进 read_first照抄结构不要重新发明
  • 脱敏工具新建 common/utils.py:mask_token(token, visible_tail=4)grep mask|脱敏|redact 在代码侧 0 命中,须新建。放 common/utils.py 让 Phase 3 阿里云日志 formatter 可直接复用
  • 模型 __str__ 返回类似 f"凭据槽位 (updated {self.updated_at:%Y-%m-%d %H:%M})" 的可读串

数据迁移

  • python manage.py makemigrations <app> 生成迁移文件(不要手写)
  • 迁移生成后须能在 dev 环境跑 python manage.py migrate 通过
  • 迁移文件命名沿用 Django 默认(0001_initial.py 等),不强求中文注释

Django Admin 注册CRED-02

  • 注册位置:与模型同 app 的 admin.py,沿用 SimpleUI + django-rosetta 中英双语注册风格(参考 userapp/admin.py / aiapp/admin.py
  • 列表页 list_display:包含脱敏后的 access_token_maskedapp_idupdated_at
  • 列表/查看态 access_token 脱敏实现:在 ModelAdmin 上加自定义方法 access_token_masked(self, obj) 返回末 4 位掩码(如 ****abcd);通过 list_display / readonly_fields 暴露
  • 编辑表单字段:app_idaccess_token 都明文(让运营录入 / 修改)
  • 隐藏"新增"按钮:重写 has_add_permission(self, request) 返回 False if exists else True(即记录已存在时禁止新增)
  • 禁止删除(避免运营误删后单例语义丢失):重写 has_delete_permission(self, request) 返回 False
  • 中英 i18n沿用仓库实操约定 = 中文字面量researcher 实测:本仓库 4 个 admin.py 全是中文字面量,_() 仅 7 处零散使用,且 LANGUAGES / LOCALE_PATHSsettings.py 已被注释掉。Phase 1 不为 verbose_name / short_description 引入 gettext_lazy,保持与 userapp/admin.py / aiapp/admin.py 一致。i18n 体系化清洗留给独立 milestone。

兼容性 / 不引入新依赖

  • 沿用 Django 4.2.13、Python 3.8(已在 PROJECT.md 「约束」段标注 Python 3.8 EOL但不在本 milestone 内升级)
  • 不引入新第三方包(不使用 django-encrypted-model-fields 等加密库;如未来需要 at-rest 加密,开新 phase 评估)
  • 沿用 StandardResponseMiddleware —— 本 phase 不直接产生 REST 响应,所以无关

Claude's Discretion

下面是 planner / 执行者可以自行决定的细节,不算锁定:

  • 模型放在 aiapp/models.py 还是新建 aiapp/models/credential_slot.py 子文件 —— 取决于 aiapp 现有 models 文件大小
  • 是否把 access_token_masked 工具函数抽到 common/utils.py 复用Phase 3 阿里云日志脱敏可能也用得上) —— 推荐抽,但不是 Phase 1 强约束
  • Admin 列表页的字段顺序、过滤器等 UX 细节
  • verbose_name 中文字面量(如"凭据槽位"还是"通用凭据"

<canonical_refs>

Canonical References

下游 agent 必读researcher / planner / executor 在生成或落地代码前都要读):

项目宪法

  • qy_lty/CLAUDE.md — 沟通语言(中文)+ 修改记录强制规则 + 跨项目联动
  • qy_lty/.planning/PROJECT.md — Milestone v1.0「本期 Milestone」段、关键约束、关键决策
  • qy_lty/.planning/REQUIREMENTS.md — Active 段 CRED-01 + CRED-02 的完整描述
  • qy_lty/.planning/ROADMAP.md — Phase 1 详情段Goal、Success Criteria 4 条)

模型 / Admin 现成模式必读pattern mapper 应该会自动列出来)

  • qy_lty/userapp/models.pyParadiseUser 看自定义模型 + 字段命名 + Meta
  • qy_lty/userapp/admin.py — SimpleUI 主题下的 Admin 注册模式 + list_display / readonly_fields 写法
  • qy_lty/aiapp/models.py — 同 app 内现有模型(决定 CredentialSlot 是否塞进 aiapp
  • qy_lty/aiapp/admin.py — 同 app 内现有 Admin决定共存方式

修改记录

  • qy_lty/docs/修改记录.md — 文件头部「修改格式说明」即本 phase 落地后必须遵循的写入格式

跨项目互引

  • qy-lty-admin/.planning/REQUIREMENTS.md — CRED-FE-01~05本 phase 提交时需在前后端两份 docs/修改记录.md 互相引用条目

</canonical_refs>

## 具体要点Success Criteria 显式化)
# 验证点 检查方式
1 DB / 模型层强制最多一条 Django shell尝试 CredentialSlot.objects.create() 第二次必须抛异常 / 被 save() 钩子改写到 pk=1 覆盖现有
2 迁移落地 + schema 字段齐全 python manage.py migrate 退出码 0python manage.py dbshell\d <表名> 含 app_id / access_token / updated_at 三列
3 get_or_create(pk=1) 首访拿到空记录 shell 中 obj, created = CredentialSlot.objects.get_or_create(pk=1)created==Trueobj.app_id == ''obj.access_token == ''
4 Admin 列表/查看态脱敏,编辑态明文 浏览器登录 admin → 看列表页 access_token 字段显示 ****<末4位> 形态;点进编辑表单看 access_token 字段为完整明文 input
5 Admin 列表页不显示「新增」按钮 浏览器登录 admin → 列表页右上角无 "Add 凭据槽位" / "新增凭据槽位" 按钮
6 Admin 禁止删除(额外保险) 浏览器登录 admin → 编辑页底部无 "Delete" 按钮;批量动作不含 "Delete selected"
## 推迟事项(明确不在 Phase 1 范围)
  • at-rest 加密 access_token 字段:当前明文存 DB依赖 PostgreSQL 访问控制;如未来需要应用层加密,开新 phase 评估 django-encrypted-model-fields 等方案
  • 审计日志(谁在什么时候改了 access_tokenDjango Admin 自带 LogEntry 已经记录基本信息,本 phase 不做专门审计表
  • 管理端 / 客户端 REST 接口:分别在 Phase 2 / Phase 3
  • 阿里云日志脱敏过滤器Phase 3 处理Phase 1 仅保证 DB / Admin 不暴露明文;如果 aiapp 现有日志中已经有"打印 model 实例"的代码路径,最多在本 phase 给 __str__ / __repr__ 加上 access_token 脱敏,但不展开做全链路日志改造

Phase: 01-credential-data-layer Context gathered: 2026-05-07 via inline PRD用户在 /gsd-plan-phase 1 调用时提供完整约束)