Skip to content

feat(account): 账号编辑弹窗支持手动覆盖 OpenAI 订阅档位 plan_type(仅 OAuth)#4055

Open
iMouseWu wants to merge 1 commit into
Wei-Shaw:mainfrom
iMouseWu:feat/account-plan-type-edit
Open

feat(account): 账号编辑弹窗支持手动覆盖 OpenAI 订阅档位 plan_type(仅 OAuth)#4055
iMouseWu wants to merge 1 commit into
Wei-Shaw:mainfrom
iMouseWu:feat/account-plan-type-edit

Conversation

@iMouseWu

Copy link
Copy Markdown

问题场景

账号管理列表里 OpenAI 账号的 Plus / Pro / Free 徽章读的是 credentials.plan_type,而这个字段只由系统自动写入,没有任何手动编辑入口:

  • OAuth 换码 / 令牌刷新时,从 id_token 的 chatgpt_plan_type claim 读取;
  • 命中 429 限流时,用限流响应里的 plan_type 同步;
  • Codex PAT 登录、数据导入时写入。

问题在于这些自动来源有时滞后或不准:

  • 账号从 Plus 升到 Pro 后,要等令牌临期(默认过期前 30 分钟才刷新)才会更新,中间徽章一直显示旧档位;
  • 偶发被写入异常值(如 abnormal 或某些 workspace 内部计费名),徽章显示不对。

此时管理员想纠正一下,只能直接调 PUT /admin/accounts/:id 或重新导入,没有 UI 入口——不方便,也容易误操作把其它凭据字段带没了。

解决思路

在账号编辑弹窗(EditAccountModal)给 OpenAI 账号加一个"订阅档位(手动覆盖)"下拉,让管理员直接改 plan_type。

  • 不改后端:plan_type 是非敏感字段,后端 UpdateAccount 走 MergePreservingSensitiveCreds 合并——前端把完整非敏感凭据带回、只改这一个键即可持久化,token 等敏感字段由后端自动保留。
  • 精准限定范围(经评审收敛):
    • 仅 OAuth 非影子账号显示/提交。setup-token 没有订阅调度语义(IsOpenAIChatGPTSubscription 要求 oauth),给它开口是"看得见改不动"的误导;影子账号凭据由母账号管理。
    • 友好标签 + 保留 canonical 值:ChatGPT Pro 的规范值是 chatgptpro,下拉里显示为 "Pro"(不暴露原始串),未改动时保留 chatgptpro;team 等预设外的值也友好显示并保留,避免编辑时丢值或写脏。
    • 只接受字符串值:挡住脏数据(42/true);选"清空"则删除该键,恢复自动识别。
  • 非粘性,文案已说明:手改值不是永久的——令牌临期刷新会用新 id_token 的真实档位覆盖,命中 429 也会用真实档位纠正。适合"临时纠正",系统随后会自愈。
  • 可测性:把选项构建、写回凭据等逻辑抽成 credentialsBuilder.ts 里的纯函数(buildPlanTypeOptions / applyPlanType / readPlanType / planTypeDisplayLabel),配 12 个单测(别名去重、保留其余凭据键、set/delete 语义),避免挂载 4500 行的弹窗组件也能覆盖核心逻辑。

此前 plan_type(ChatGPT Plus/Pro/Free)只由系统自动写入(OAuth 换码/刷新、
429 同步、导入),UI 无编辑入口。现于 EditAccountModal 为 OpenAI OAuth 非影子
账号新增一个订阅档位下拉,可手动纠正(走既有 PUT /admin/accounts/:id 合并,
plan_type 非敏感字段无需改后端)。

- 仅 OAuth 非影子账号显示/提交:setup-token 无订阅调度语义
  (IsOpenAIChatGPTSubscription 要求 oauth),影子账号凭据由母账号管理
- 下拉:清空 + Plus/Pro/Free;别名(chatgptpro→Pro)与自定义值(team 等)
  友好显示并保留 canonical 值,避免编辑丢失或写脏
- readPlanType 仅接受字符串,挡住脏数据;清空则删除该键(恢复自动识别)
- 抽 buildPlanTypeOptions/applyPlanType/readPlanType/planTypeDisplayLabel 为
  纯函数入 credentialsBuilder.ts,并补 12 个单测(别名去重、保留其余凭据键、
  set/delete 语义)
- 文案(zh/en)注明:令牌临期刷新或 429 会用真实档位自动覆盖手改值

经三方评审会(Claude + Codex + 作者)一轮交叉评审达成共识后落地。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@iMouseWu

Copy link
Copy Markdown
Author

recheck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant