feat(admin): 站点外观 - 支持 Tab 分类与主题渐变按钮配置

This commit is contained in:
ddCat
2026-09-18 23:28:57 +08:00
parent f77bd2bfa0
commit 3cb1423f7e
27 changed files with 869 additions and 375 deletions

View File

@@ -9,6 +9,33 @@ description: 信封、错误码与 OpenAPI
机器可读规范:运行中的 `GET /api/openapi.yaml`,源文件 `backend/internal/handler/openapi.yaml`。前端 TypeScript 仍走 `web/src/services/api` 的 `http`,不从 OpenAPI 生成客户端。
## 站点皮肤主按钮
`GET /public/appearance` 返回当前 `activeSkin`;管理员通过 `GET/PUT /admin/settings/appearance` 读取、保存完整主题库。外观合同版本 8 新增 `tokens.buttons.light/dark`,字段为 `mode`(`solid|gradient`)、整数 `angle`(0–360)、HEX 色值 `start/end`、`hoverStart/hoverEnd`、`activeStart/activeEnd`、`foreground`。纯色模式使用既有主操作颜色,渐变仅用于主按钮。非法模式、角度、色值或修改锁定经典主题均拒绝保存;整块缺失的旧配置按主题身份补齐。此变更不增加数据表迁移。
## 模型规格报价
`GET /model-catalog` 在 `source=system` 时返回 `channels[].models[]`。每项的 `modelKey` 是产品模型标识,`displayName` 是产品展示名,`icon` 是模型图标键,`channelLabel` 是可选的渠道展示名(默认空字符串)。后台渠道模型创建与更新请求使用相同的 `channelLabel` 字段;服务端去除首尾空白并限制为 80 字,空字符串表示恢复使用渠道公开名称。
渠道模型创建、更新、管理列表、公开目录和会话模型价格信息均使用 `description` 表达模型描述。服务端去除首尾空白后限制为 500 字,允许空字符串清除描述;无管理员权限或超长内容拒绝保存。创作端二级选项在悬浮、聚焦或选中时展示该字段,长内容可悬浮查看完整文本;未填写的系统模型不按名称生成宣传文案。描述区域保留固定行高,悬浮不改变菜单项尺寸。
创作端按 `capability + modelKey` 精确分组,二级菜单保留各渠道模型的独立选择;选中值继续使用 `channelId::modelKey`,报价和生成请求保留该渠道身份。展示名、渠道展示名和 Logo 不参与路由、权限及计费。`source=frontend` 的逻辑模型目录继续按原逻辑模型身份工作,不将逻辑模型的供应线路公开成可选渠道。
同一产品在不同渠道中的 `modelKey` 应保持一致,名称与 Logo 也应统一;不同产品版本使用不同标识。已有模型标识不会自动改写。一级顺序按后台渠道和模型排序首次出现的位置确定,二级保持后台渠道顺序;若历史配置的展示名或 Logo 不一致,以稳定渠道身份排序选取元数据,不随当前选择和渠道排序跳变。
以下接口需要登录 Cookie,返回 `data.quote`,仅校验和报价,不创建任务、账单或冻结余额,不调用供应商生成接口。
| 接口 | 请求 |
| --- | --- |
| `POST /model-catalog/quote` | `{ channelId, modelKey, intent }`,仅允许启用的系统渠道与可用模型,复用任务准入、默认参数和价格档选择 |
| `POST /models/:id/quote` | 直接提交 `intent`,按前台逻辑模型当前可用线路和价格策略报价 |
`intent` 包含 `capability`(`text/image/video/audio`)、`operation`、`inputs`(素材类型到数量的映射)和 `options`(请求规格)。素材数量必须为 0–128 的整数,再按模型能力限制校验。视频规格包括 `vquality`、`size`、`videoSeconds`、布尔值 `videoGenerateAudio`,显式 `false` 必须保留。
报价包含 `logicalModelId`(系统渠道直连为空)、`billingMode`、`quantity`、`amountMicrocredits`、`estimated`。视频 Token 报价另有 `videoTokenEstimate`:`formulaTokens`、`reservedTokens`、`outputWidth`、`outputHeight`、`framesPerSecond`、`outputSeconds`、`referenceSeconds`、`referenceDurationEstimated`、`dimensionsEstimated`、`reservationMarginPercent`。
Token 报价的 `estimated` 为 `true`,`quantity` 是预留 Token 数。参考素材数量不包含时长信息,带参考视频时统一按 15 秒估算并返回 `referenceDurationEstimated: true`。所有视频协议均支持 Token 定价;10% 预留是平台策略,不是费用上限。成功后优先有效上游用量,无有效用量时按提交的公式快照结算,余量不计入公式费用。账单新增 `videoFormulaTokens` 及 `usageSource`(`provider` / `video_formula`),公式结算的 `usageAvailable` 为 `false`,`outputTokens` 表示公式数量。算法、配置与异常用量处理见[视频 Token 计费](/docs/backend/video-token-billing)。
## 画布保存、历史与恢复
画布内容使用数据库递增的 `revision` 做乐观并发校验;`updatedAt` 仅用于展示,不作为版本依据。新建 `PUT /canvas-projects/:id` 的 `project.revision` 为 0,已有画布必须带上读取到的版本。数据库在同一次条件更新中匹配 ID、用户与版本,成功后返回 `data.project` 摘要及递增后的版本;缺失版本返回 428,陈旧版本返回 409,非法版本返回 400。客户端不能获取新版号后直接重发旧内容。