后端:adminpanel tasks(全局 AITask + status/type/team/anomaly 筛分页)+ detail(payload)+ retry(失败重投, 仅图像类 best-effort 否则 400);is_cost_anomaly(实际>预估×1.5)+ F() 异常筛;IsPlatformAdmin + 审计。 前端:adminApi tasks 系列;Admin 任务监控页(状态 tab + 仅成本异常 chip + 表格成本预估→实际+异常标 + 详情抽屉 payload + 失败重投)。 修真 bug:抽屉缺 .show 类停屏外,补上正常滑入。 测试:adminpanel 36 单测过(筛/异常/详情/重投 mock celery + 非失败/不支持/权限拒); 无头 e2e _admin-p6.mjs 6 断言过 + 0 console error(不点真重投);tsc+build 绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
14 KiB
平台 Admin 后台 + 注册邀请码 · 落地进度
计划:
C:/Users/Air-work/.claude/plans/gleaming-sleeping-ullman.md(Phase 0–9 全做)。 每个 Phase 走完「★测试底座」(后端单测 + 无头浏览器 e2e + tsc/build + 回归)才 commit。 全程本地、不 push;测试服 DB 可动。
测试基线(回归红线)
apps.accounts / apps.billing / apps.products:全绿(Phase 0 后 15/15 OK)。apps.projects:进 Phase 0 前已有 3 failures + 1 error(均为 AI providerimage_editmock 相关,与本工程无关)——非本次引入,Phase 0 改动仅在 accounts + 前端,未触碰 projects/ai。后续 Phase 不得新增 fail。
✅ Phase 0 · 基础(角色 + 壳 + 审计)— 完成 2026-06-19
后端
User.is_platform_admin(bool)+ migration0004_user_is_platform_admin_adminauditlog(已 apply 到测试 MySQL)。apps/accounts/permissions.py·IsPlatformAdmin(超管放行 / 普通拒 / 停用拒)。apps/accounts/management/commands/create_platform_admin.py· 建 admin/admin123(幂等;--reset-password才重置;也置is_staff)。已对测试 MySQL 执行,真账号可登。AdminAuditLog模型(operator/operator_name/action/target_*/before/after/ip)+apps/accounts/audit.pylog_admin_action()(失败不阻断主流程)。me/login对无团队的平台超管优雅返回team=null(新增current_team_or_none);UserSerializer暴露is_platform_admin。
前端
- 新
routes/admin/admin-app.tsx:超管后台外壳(折叠分组侧栏 6 组 11 项 + 顶栏 + 概览 + 占位页),全程复用 restraint 共享类。 - 路由:
route-config.ts解析/admin、/admin/<section>;App.tsx分流渲染AdminApp+ gating(超管无团队登录直落 /admin、非超管访问 /admin 纠回工作台)。 app-shell.tsx侧栏「平台」入口(仅超管可见)。admin-page.css(仅 token,无裸 hex)+IconKitSvg补 8 个 Lucide-line 图标。
测试底座(全过)
- 后端单测:
apps.accounts11/11 OK(新增PlatformAdminTests:建超管命令幂等/权限类放行拒绝/登录+me 返回标志且 team 为空/普通注册非超管)。 - 无头 e2e:
core/qa/visual-parity/_admin-p0.mjs—— 全部断言通过 + 0 console error:- admin/admin123 真登录 → 落 /admin;
- 超管壳:6 分组 / 11 导航 /
[ PLATFORM ADMIN ]徽标 / 概览标题 / 点「邀请码」→ /admin/invites 占位「模块即将上线」; - 普通用户访问 /admin → 纠回 /dashboard、admin 壳不渲染。
- 截图:
output/admin/p0-*.png。
- 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:概览截图核对 —— 冷灰底 + 单橙 accent + 8px 圆角 + 准星,符合 design.md 克制调性。
测试账号:平台超管 admin/admin123(测试 MySQL 已建);普通 demo airshelf/Restraint2026。
e2e 端口提示:5173 被同机另一项目(AirDrama)抢占且不稳;AirShelf e2e 改用专用端口 5188(
npx vite --port 5188 --strictPort)。e2e 脚本默认 BASE 已指向 5188。
✅ Phase 1 · 注册邀请码逻辑 — 完成 2026-06-19
后端
Invitation.kind(join_team / create_team)+team可空 + migration0005_invitation_kind_alter_invitation_team(已 apply 测试 MySQL)。RegisterSerializer:invite_code 必填;按 kind 分流 —— create_team 开新团队当 owner + 发试用额度(回填 invite.team),join_team 幂等加入该团队、不重复发额度;删除「空码=开团队」。validate-invite端点(免登录,POST/api/auth/validate-invite/):验码 →{valid, kind, role, team_name?},无效始终 200+valid:false。- 团管发码恒为
kind=join_team;InvitationSerializer增kind/team/team_name。
前端
- 注册页重做(码优先):先填邀请码 → 点「验证」(或 Enter)→ 验过才展开;create_team 显示「团队名」输入,join_team 显示只读团队名(不暴露团队列表);CTA 文案随 kind 切换(创建团队 / 加入团队)。
- 邀请链接
?invite=CODE进页自动回填 + 验码;改码即重置验证态。 api.validateInvite;field-note.ok/err+invite-verify-row+ 只读团队样式(仅 token)。
测试底座(全过)
- 后端单测:
apps.accounts22/22 OK(新增RegisterInviteCodeTests6 项 +ValidateInviteTests5 项:无码拒/空码拒/create_team 开团队+试用额度/join_team 入伙不开新团队/已用·过期拒/验码返回正确 kind+team_name)。 - 无头 e2e:
_admin-p1.mjs(端口 5188)—— 全断言过 + 0 console error:create_team 验码→展开团队名→注册落工作台;join_team 验码→只读团队名「演示团队」→入伙;错码不展开+报错;URL 自动验码。截图output/admin/p1-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:两态注册截图核对 —— 黑块等高、验证按钮对齐、绿色 ok 提示、只读团队名灰底,符合 restraint。
✅ Phase 2 · 邀请码发放管理(Admin)— 完成 2026-06-19
后端(新建 apps/adminpanel/)
- 新 Django app
apps.adminpanel(跨团队后台端点容器),注册进 INSTALLED_APPS + 主 urlspath("api/admin/", ...)。 GET/POST /api/admin/invitations/:列所有邀请码(跨团队,按 kind/status/search 过滤 + 分页);POST 平台超管发 create_team「开团队码」(写审计invite.issue_create_team)。POST /api/admin/invitations/<id>/revoke/:撤销待用码(写审计invite.revoke)。- 全程
IsPlatformAdmin,非超管 403。
前端
adminApi(api.ts 分层):invitations / createInvite / revokeInvite。- Admin「邀请码」页(
routes/admin/admin-invites.tsx):类型筛选段控(全部/开团队码/加入码)+ 表格(码/类型/团队/状态徽章/创建时间/使用者)+「发开团队码」弹窗 + 复制邀请链接(剪贴板 + 反馈)+ 撤销;接入 AdminApp(本地 toast notify)。 Invitation类型补 kind/team/team_name;admin-page.css 补段控/表格/弹窗样式(仅 token)。
测试底座(全过)
- 后端单测:
apps.adminpanel9 项 + apps.accounts 22 = 31 OK(列权限 403/发码+审计/跨团队列表/kind 过滤/撤销+审计/端到端凭发出的码注册开团队/撤销后码失效)。 - 无头 e2e:
_admin-p2.mjs(5188)—— 6 断言全过 + 0 console error:页面加载、发码弹窗、新码入列、复制链接反馈「已复制」、端到端用 UI 发的码 API 注册开团队 201。截图output/admin/p2-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:列表截图核对 —— 类型/状态橙/中性 pill、段控黑激活、成功 toast,符合 restraint。
- 回归:accounts 22 仍全绿;live 后端自动 reload 后
/api/admin/invitations/200(admin)/401(匿名)。
✅ Phase 3 · 平台团队 + 用户管理(Admin)— 完成 2026-06-19
后端(apps/adminpanel 扩展)
- 团队:
GET teams/(列跨团队 + status/search 过滤分页,带 owner/成员数/余额)、GET teams/<id>/(详情含成员)、POST teams/<id>/toggle/(启停 + 审计)。 - 用户:
GET users/(列跨团队 + 过滤分页,带所属团队)、POST users/<id>/toggle/(启停,停用清 token;不允许停用平台超管)、POST users/<id>/reset-password/(强制改密,清 token)。 - 团队停用拦登录:
login校验当前团队status,停用团队的成员一律拒登(平台超管无团队不受影响)。 - 新
apps/adminpanel/serializers.py(AdminTeam/AdminTeamMember/AdminUser);全程 IsPlatformAdmin + 写审计。
前端
adminApi:teams/teamDetail/toggleTeam/users/toggleUser/resetUserPassword;类型 AdminTeam/AdminTeamDetail/AdminUser。- Admin「团队」页(状态筛选 + 搜索 + 表格 + 详情弹窗含成员 + 启停)、「用户」页(筛选 + 搜索 + 表格 + 启停 + 改密弹窗;超管行带「超管」标记且禁操作)。
- admin-page.css 补 search/宽弹窗/详情元信息样式(仅 token)。
测试底座(全过)
- 后端单测:
apps.adminpanel19 项 + accounts 22 = 41 OK(团队列字段/详情/启停拦登录再恢复/用户列含团队/启停拦登录/禁停超管/强制改密生效旧密失效/权限 403)。 - 无头 e2e:
_admin-p3.mjs(5188,用一次性 P3 团队不动 demo)—— 7 断言全过 + 0 console error:团队页+搜索+详情弹窗;UI 停用团队→该成员 API 登录 400→启用→200;用户页超管标记;UI 改密→新密码登录 200。截图output/admin/p3-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:团队/用户表格、绿/红状态 pill、详情弹窗,符合 restraint。
✅ Phase 4 · 质量词 Admin(平台单层)— 完成 2026-06-19
后端
- 新模型
apps/ai/models.QualityWord(stage + slot + text + sort + enabled,平台单层无 team)+ migration0009_qualityword(已 apply 测试 MySQL)。 - 生成侧
services.py新quality_words()/quality_suffix(),4 个 builder 接入(build_person_frontal_prompt/build_model_tryon_prompt_refs/build_storyboard_frame_prompt/build_video_segment_prompt)——无配置回落原写死值,零回归。 - adminpanel CRUD:
GET/POST quality-words/+PATCH/DELETE quality-words/<id>/,IsPlatformAdmin + 审计(create/update/delete);QualityWordSerializer。
前端
adminApiqualityWords/create/update/delete;AdminQualityWord类型。- Admin「质量词」页(
admin-quality.tsx):按 stage(人物立绘/模特上身图/故事板/视频)分组卡片 + 编辑模式开关防误删 + 行内增(回车)/改(失焦保存)/删(× 仅编辑态显示);橙色 chip + 虚线添加框(仅 token)。
测试底座(全过)
- 后端单测:
apps.adminpanel23 项 + accounts 22 = 45 OK(质量词 CRUD/权限 403/非法 stage 拒/生成侧读配置 + 无配置回落 + 描述保留/停用词不取用)。 - ⚠
apps.ai有 3 个StandaloneImageReferenceTests(image_edit 路由)失败 —— 已 git stash 比对确认:HEAD(未改 services.py)同样失败,为进场前既有,非本 Phase 引入(本 Phase 只改提示词字符串内容,不动 provider 路由)。 - 无头 e2e:
_admin-p4.mjs(5188,跑 live 库故用唯一标记词且结束兜底清理)—— 4 断言全过 + 0 console error:页面 4 卡、编辑模式加词→chip 显示+API 持久化、删词→API 移除;live 库零残留。截图output/admin/p4-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:分组卡 + 橙 chip + 编辑态,符合 restraint。
✅ Phase 5 · 火山人像审核队列(Admin)— 完成 2026-06-19
后端(apps/adminpanel 扩展,复用 assets/review.py)
GET asset-reviews/:跨团队真人(person)资产队列(?review_status=none|processing|active|failed筛 + 分页);AdminReviewAssetSerializer(team_name + 首图 preview_url)。POST asset-reviews/submit/(批量送审 / 失败重试,逐个submit_asset_for_review)+POST asset-reviews/poll/(轮询 processing,poll_asset_review);IsPlatformAdmin + 审计。- 性能修复:序列化器
get_preview_url用 prefetch 缓存list(obj.files.all())[0]取代.first(),消除列表页 N+1(100 条从 ~5s 降到 ~1s)。
前端
adminApiassetReviews/submitReviews/pollReviews;AdminReviewAsset类型。- Admin「资产审核」页:状态筛选(全部/未送审/审核中/通过/失败)+ 表格(多选 + 缩略图 + 团队 + 状态盾绿/红/灰 + 失败/未送审行「重试/送审」)+ 吸底 bulk-bar「批量送审」+「刷新状态」(只读 poll);page_size 60。
- admin-page.css 补 checkbox 列/缩略图/红标记/bulk-bar(仅 token;
.bulk-bardesign 未实现,本次补齐)。
测试底座(全过)
- 后端单测:
apps.adminpanel28 项 OK(队列只列 person + 跨团队/状态筛 4 态/批量送审计数+审计(mock 火山)/送审权限 403/poll 只轮询 processing(mock))。 - 无头 e2e:
_admin-p5.mjs(5188;wrapper 预建 1 条 person 资产保证有行,跑完删除)—— 5 断言全过 + 0 console error:页面+5筛选 tab+刷新按钮、60 行、多选出 bulk-bar+批量送审可点(不点真送审,避免 Volcano 真消耗)、刷新状态(只读 poll)。截图output/admin/p5-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:队列表 + 绿/红/灰状态盾,符合 restraint。
✅ Phase 6 · AI 任务监控 + 成本异常(Admin)— 完成 2026-06-19
后端
GET tasks/(全局 AITask,?status/task_type/team/anomaly 筛 + 分页)+GET tasks/<id>/(详情含 request/response payload + error)+POST tasks/<id>/retry/(失败重投)。- 成本异常:
is_cost_anomaly(实际 > 预估 × 1.5 且预估>0)+?anomaly=1F() 表达式筛;AdminTaskSerializer 暴露cost_anomaly。 - 重投 best-effort:仅 FAILED + 图像类(基础资产→generate_base_asset_task / 独立生图→generate_standalone_image_task),其余类型 400(不冒险误投);IsPlatformAdmin + 审计。
前端
adminApitasks/taskDetail/retryTask;AdminTask/AdminTaskDetail类型。- Admin「任务监控」页:状态筛 tab(全部/失败/成功)+「仅成本异常」chip + 表格(类型/团队/模型/状态/成本 预估→实际+异常标/时间)+ 详情抽屉(payload JSON)+ 失败行「重投」。
- admin-page.css 补 JSON 块 / 抽屉宽度 / 异常 chip(仅 token)。修真 bug:抽屉缺
.show类导致面板停在屏外,补上后正常滑入。
测试底座(全过)
- 后端单测:
apps.adminpanel36 项 OK(列权限/状态·类型·异常筛/成本异常标志/详情 payload/重投 dispatch(mock celery)+ 非失败拒 + 不支持类型拒 + 权限 403)。 - 无头 e2e:
_admin-p6.mjs(5188;wrapper 建 1 条 failed+异常任务,跑完删)—— 6 断言全过 + 0 console error:页面+3tab+异常chip、60 行、详情抽屉含 JSON、失败 tab(43 条真实失败)首行有重投、异常筛精确 1 条;不点真重投。截图output/admin/p6-*.png。 - 类型/构建:
tsc --noEmit+npm run build全绿。 - 视觉:任务表 + 详情抽屉(成本⚠ / 错误红框 / payload),符合 restraint。