VOZEB Retail Next:Key、模型、用量三域最小任务包
Task ID:
VOZEB-RETAIL-NEXT-KEY-MODEL-USAGE-01状态:LOCAL_IMPLEMENTATION_CANDIDATE / CANDIDATE_EVIDENCE / NOT_FORMAL_TESTruntime authorization:LOCAL_CANDIDATE_IMPLEMENTATION_GRANTED(用户已明确要求后续仅在oceanway-vozeb的 Next.js/React 项目继续实现;本文件仍不代表正式 Testing、发布或生产授权)
1. 基线与事实源
| 项目 | 固定值 |
|---|---|
| 唯一 UI/运行时基座 | VOZEB-PRO Next.js App Router + React 19 |
| clean base SHA | db32a44b25b55d3d04ea6f8e7e5ac00596b87fef |
| base tree | 19b66e73c923dac90f960986a70f453bcb5aebc4 |
| 上游来源 | VOZEB-PRO v0.0.6 / 4de8360f46b3b8e9a76649ec68b6ae3d028345ac |
| Retail 参考 | /Users/wangzihao/Projects/retail-platform(只读;不 cherry-pick、不改写) |
| Retail 契约入口 | contracts/portal.openapi.yaml 的 Keys、Catalog、Usage 章节 |
| 页面/产品参考 | Retail OWPDOC-04-003_Page_List.md(只读产品事实) |
所有页面、组件和 Next 服务器代码均从上述 VOZEB clean base 产生。retail-platform
只提供产品规则、公开 DTO、错误和端点事实;其 Vue 运行时、Pinia、Vue Router、旧页面
壳和旧视觉均不迁入 VOZEB。Retail 仍是用户、公开模型、Key、用量和账务事实源;浏览器
不得直连 Retail 或任何上游,统一经过 VOZEB 的 server-only BFF/typed adapter。
2. 目标用户结果
在 VOZEB 现有首页、Canvas、媒体和其它能力保持不变的前提下,登录用户可以:
- 在
/keys查看脱敏的通用 API Key、创建 Key、按一次明确的本人操作取回单个完整 Secret,并立即删除/失效 Key。 - 在
/models查看已发布且可用的公开模型,按能力筛选并阅读公开参数/价格摘要; 页面不显示 Channel、Group、供应商账号或内部绑定。 - 在
/usage查看带筛选、排序和分页的零售用量,在/usage/:requestId查看一条 自己拥有的请求的生命周期、计量、资金分摊和对账状态。
这三个域是只读或用户自有凭据操作;不在本任务内实现新的生成、扣费、商品、订单、 钱包、Canvas、媒体或管理后台行为。
事实映射(仅作为候选追踪入口):K-01 对应 KEY-P0-001、KEY-P0-002、
KEY-P0-003、KEY-P0-004;M-01 对应 MOD-P0-001;U-01 对应
BILL-P0-003。这些映射不提升 Retail 事实域的 Design/Development/Testing/Operations
状态,正式追踪仍以各自登记册为准。
3. 前置决策(未确认前停在候选)
- 会话:推荐 Retail Portal Session 为唯一业务身份,由 VOZEB server-only BFF
代持 HttpOnly Cookie;VOZEB 本地 session 只能作为 UI hydration,不得形成双登录或
按邮箱猜测映射。当前候选默认拒绝未完成的 Identity Map/Session Bridge;仅当
OCEANWAY_RETAIL_SESSION_BRIDGE_MODE=local-cookie-fixture且 Gateway 为 loopback 合成服务时,才允许注入测试 Cookie;生产进程强制禁用该模式。正式接入前必须先固定 服务端映射、失效同步和审计规则,并更新本任务包。 - 公开契约:
GET /portal/v1/models没有独立 detail endpoint;第一版只做目录列表 和列表内快照。GET/PATCH /portal/v1/keys/{key_id}在当前契约中标为 reserved, 不擅自实现;Key 第一版只使用明确列出的 list/create/reveal/delete。 - 金额/用量:Decimal 以字符串穿过 BFF、React state 和 UI;禁止用浮点计算。用户
界面只展示图片次数、积分及其它公开计量单位,不展示人民币、法币或内部 USD;
retail_cost/final_consumption仅作为服务端契约字段保留,不能直接渲染。
4. 三个最小垂直切片及顺序
K-01:通用 Key(首个运行时切片)
端点:
GET /portal/v1/keys:只返回脱敏元数据;POST /portal/v1/keys:创建并在响应中短暂返回 Secret;POST /portal/v1/keys/{key_id}/secret:仅当前用户明确请求的单 Key Secret;DELETE /portal/v1/keys/{key_id}:立即失效并保留历史用量事实。
页面结果:/keys 列表、空态、创建表单、创建成功一次性提示、显式复制/再次取回、
删除确认、active/disabled/expired、未登录/无权限/服务不可用状态。
安全不变量:Secret 不进入 URL、LocalStorage、SessionStorage、普通日志、分析事件、
列表/详情 DTO 或批量响应;Secret 读取响应必须 Cache-Control: no-store,服务端只按
当前会话和 Key 所有权取回。
M-01:公开模型目录
端点:GET /portal/v1/models?capability=&limit=&offset=。
页面结果:/models 列表、能力筛选、分页、搜索/空态、不可用/服务异常状态;只展示
PublicModel 的公开字段。模型选择器可复用同一份已校验目录,但不能在浏览器解析 Auto、
选择渠道或推导价格。
明确缺口:没有冻结的模型 detail endpoint,因此 /models/:id 暂留为后继任务;不
通过读取全量目录伪造 detail API。
U-01:用量报告与请求详情
端点:
GET /portal/v1/usage:日期、model、key、source、status、q、sort、limit、offset;GET /portal/v1/usage/{request_id}:用户范围内的生命周期、requested/actual usage、 meter items、funding 和 reconciliation。
页面结果:/usage 列表/聚合/筛选/分页,/usage/:requestId 详情;展示
created/reserved/dispatched/settled/released/reconciliation_required 等真实状态,
不提供重试、退款、再次执行或下游诊断。
顺序固定为 K-01 → M-01 → U-01:Key 先建立凭据与会话安全边界;模型目录再为 统一 Agent 的公开能力选择提供事实;用量最后依赖稳定的 request、model、key 标识和 服务端计量事实。三切片仍可分别审查和独立验收;当前候选为便于在 VOZEB 中复核而收口 为同一组边界文件,不扩展为其它跨域功能。
5. 允许修改路径(仅限本任务)
以下是候选实现的精确白名单;未列出的文件不得因“顺手接线”修改。
边界与服务端 BFF
web/src/integrations/retail/contracts.ts
web/src/integrations/retail/endpoints.ts
web/src/integrations/retail/key-client.ts
web/src/integrations/retail/catalog-client.ts
web/src/integrations/retail/usage-client.ts
web/src/integrations/retail/index.ts
web/src/integrations/retail/*.test.ts
web/src/lib/server/retail-runtime-transport.ts
web/src/lib/server/retail-adapter-factory.ts
web/src/lib/server/retail-route-response.ts
web/src/lib/oceanway/retail-gateway.ts
web/src/lib/oceanway/retail-gateway.test.ts
web/src/lib/server/retail-runtime-transport.test.ts
web/src/lib/server/retail-adapter-factory.test.ts
web/src/lib/server/retail-route-response.test.ts
web/src/app/api/retail/portal/keys/route.ts
web/src/app/api/retail/portal/keys/[keyId]/route.ts
web/src/app/api/retail/portal/keys/[keyId]/secret/route.ts
web/src/app/api/retail/portal/models/route.ts
web/src/app/api/retail/portal/usage/route.ts
web/src/app/api/retail/portal/usage/[requestId]/route.ts
web/src/app/api/retail/portal/route.test.tskey-client.ts 只承载 Key 的字段白名单、所有权/Secret 响应约束和命令映射;
catalog-client.ts、usage-client.ts 只承载公开 DTO 解析与查询归一化。Transport 必须
集中执行 Retail origin(生产仅 https://api.ocean-way.top,本地仅 loopback fixture)/path、
HTTP method、Cookie 范围、Request ID、响应大小和错误分类校验;Route Handler 不得复制
业务规则或直接访问数据库。Usage client 与 Portal 复用正的时间窗、366 天上限及未来五分钟
边界,避免把明显无效查询发送给上游。
页面、同源客户端与左侧导航
web/src/services/api/retail/keys.ts
web/src/services/api/retail/models.ts
web/src/services/api/retail/usage.ts
web/src/services/api/retail/http.ts
web/src/services/api/retail/index.ts
web/src/app/(user)/keys/page.tsx
web/src/app/(user)/models/page.tsx
web/src/app/(user)/usage/page.tsx
web/src/app/(user)/usage/[requestId]/page.tsx
web/src/components/layout/app-sidebar.tsx
web/src/constant/navigation-tools.ts
web/src/constant/navigation-tools.test.ts页面只调用同源 /api/retail/...,不读取 Cookie 或内部 Token。导航只在既有左侧栏中
增加“模型广场 / API 与密钥 / 用量”;不新增顶部菜单,不修改 Foundation 材质、背景、
Canvas、Create、Media 或 Drama 视觉文件。/models/:id、Key metadata GET/PATCH 和
任何 /api 页面别名不在本次白名单内。
候选文档与证据
docs/oceanway/VOZEB-RETAIL-NEXT-KEY-MODEL-USAGE-01.md实现候选若产生测试/发布记录,必须在独立授权任务中另行更新 progress、Testing 和
Operations 事实源;本文件不能替代正式 OWTEST-* 或 OWOPS-* 结论。
6. 明确禁止修改与非目标
apps/user-web/**、apps/admin-web/**、任何.vue、Pinia、Vue Router 或 Vue bridge;- Retail 参考仓库的
cmd/**、internal/**、migrations/**、contracts/**和真实服务; - VOZEB Foundation/Canvas/Create/Assets/Drama/Media/Commerce/Billing/Admin 业务文件;
web/src/lib/server/billing*、web/src/services/api/points.ts、VOZEB 本地 points/账本、 provider/channel/new-api/sub2api 直连或 AI 执行路由;- 数据库表、迁移、真实用户/旧库/生产网络、真实模型调用、支付、部署和远程推送;
- 为模型目录增加内部绑定、渠道探测、Auto 决策或手写价格;
- 为 Usage 增加重试、退款、重跑、后台调账或把下游 Request ID 作为公开字段。
本任务不把“VOZEB 其它能力直接继承”解释为可以继承其重复的身份、points、账务或 provider 事实;只继承现有页面壳、组件和创作能力,三域数据仍走 Retail 公开边界。
7. 验收矩阵(候选阶段)
自动检查
cd web
pnpm exec vitest run src/integrations/retail src/lib/server/retail-* src/app/api/retail src/app/'(user)'/keys src/app/'(user)'/models src/app/'(user)'/usage --reporter=dot
pnpm exec tsc --noEmit --pretty false
pnpm exec eslint src/integrations/retail src/lib/server/retail-runtime-transport.ts src/lib/server/retail-adapter-factory.ts src/app/api/retail src/app/'(user)'/keys src/app/'(user)'/models src/app/'(user)'/usage --quiet
pnpm exec next build
git diff --check必须证明的用户结果
| 域 | 关键断言 |
|---|---|
| Key | 列表永不含完整 Secret;创建/显式取回只针对当前用户单 Key;no-store;删除后立即失效;窄桌面无溢出 |
| 模型 | 只显示 available 公开模型;能力筛选和分页沿用服务端;不出现 Channel/Group/内部模型;空/异常/无权限可恢复 |
| 用量 | Decimal 不经浮点;用户界面只展示次数/积分/公开单位而不展示法币;筛选、排序、分页和详情 ID 绑定当前用户;生命周期/资金/对账状态完整;404 不泄露所有权 |
| 共通 | 同源 BFF、Cookie 不可由 JS 读取、Request ID 透传、键盘/焦点可用、390/430 与桌面布局、reduced-motion/无网络状态 |
浏览器回归必须使用本地 TCP fixture 或合成 BFF,不得调用真实 Retail、旧库、支付或
上游。实现者自测不能将状态提升为 IMPLEMENTED/VERIFIED/OPERATIONALIZED;需由独立
Testing/Operations 任务基于固定候选构建复核。
8. 当前候选证据
代码候选提交为 9c4c0f76f0df3fdb34cb8a6c0db78f6977169bdc,基于
db32a44b25b55d3d04ea6f8e7e5ac00596b87fef,工作树在验证后保持 clean。当前证据仅证明
本地候选可构建和可回归,不提升正式事实域状态:
- 定向 Retail 边界测试:11 个文件、50 个断言通过。
- 完整 VOZEB Vitest:522 个文件通过、4 个文件跳过;2363 个断言通过、9 个跳过。
- TypeScript、ESLint、Prettier、依赖安全审计通过;Next 隔离生产构建 66/66 页面通过, Standalone 产物 151 个静态文件、15 个公开资源完整。
- Chromium 本地 Foundation/Home 回归:9/9 通过;仅使用本地合成安装流程,未访问真实 Retail、旧库、支付或上游模型。
retail-platform与 VOZEB 主工作区均未写入;本候选只存在于oceanway-vozeb-next-migration工作树。
仍未覆盖或未授权的事项:正式 Identity Map/Session Bridge、真实 Retail 会话、完整 Testing/Operations 验收、移动端正式性能基准、Usage 全量筛选/导出、模型详情、Key 轮换和 生产部署。当前页面在没有 loopback 合成 fixture 时会安全地显示“尚未完成 Retail 账户绑定”, 不会猜测 VOZEB 用户与 Retail 用户的对应关系。
9. 停止条件、交付和回滚
遇到会话方案未定、OpenAPI 字段冲突、Key Secret 可能泄露、Decimal 精度无法保持、
需要改 Go/数据库/Provider、需要真实网络或需要修改 Foundation 时,立即停在
CANDIDATE_EVIDENCE,回报具体冲突;fixture 只可用于 loopback 合成验证,不能静默替代
正式 Identity Bridge 或真实服务。
候选交付必须记录:每个切片的 base_sha、变更文件、契约版本、测试原始结果、未覆盖
状态和回滚 commit。回滚仅撤销该候选 commit,不得 reset 或清理共享工作树。正式运行时
接入前,必须由用户/事实域 Owner 重新确认本任务包并另发具名开发授权。