Merge remote-tracking branch 'origin/master' into fix/agc-composer-layout
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
合并 master 76166b08c(模型目录不可用项 / 管理端等):保留双方条目
- apps/ai-game-creator-shell/src/services/clientApi.ts:取 master 的 typed 目录读取(`ClientLlmModelCatalog` 已含 unavailableModels,不再手写 agentMode 映射),但保留本分支的文案「模型服务不可用」(本 PR 的口径,master 那侧还是旧的「本地…」)
- ConversationModelSelect.tsx:import 取并集(`Lock` + `ReactNode`),渲染结构保留本分支的「强度区外壳」(`strengthSection ? <shell>{strengthSection}{modelMenu}</shell> : modelMenu`)——master 把菜单内联展开、并加了不可用项列表,那段已自动合并进 `modelMenu` 变量里,两边语义都在
- tests/conversationModelSelect.test.tsx:取 master 的新文案「所选模型已下线,已切换为默认模型」,保留本分支的异步查询(`await screen.findByText`,文案走 portal toast)
- docs/project-memory/shared-memory/pitfalls.md:两侧条目都保留(本分支在前,master 新增在后)
- 校验:typecheck pass 3 / fail 0;`appSurface + chatDialogFrameLayout + conversationModelSelect` 257 passed / 9 skipped
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
2. 拉不到就报错、不写替代目录,并在下一次启动继续重试,直到目录里有数据。
|
||||
3. **保持既有格式与契约不变**:目录字段(`id`/`alias`/`modelId`/`defaultModelId`)、后台页面与 DTO、`GET /api/llm/models` 形状、客户端模型标识校验都不变,不引入不兼容变更。
|
||||
|
||||
> 2026-10-03 修订:本条「`GET /api/llm/models` 形状不变」只约束本里程碑自身;后续「模型权限」变更已把该接口改为可用 / 不可用分桶(新增 `unavailableModels` 与 `reason` 枚举),见 `docs/adr/【ADR】模型权限与并发上限的强制边界-2026-10-03.md` 的修订节。
|
||||
|
||||
## 不在本里程碑内
|
||||
|
||||
- 不改目录字段语义与后台维护方式,不删别名/稳定标识概念。
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-10-03 每日免费发放额为 0 时前端隐藏该池
|
||||
|
||||
- 背景:运营需要一个可逆的「不提供每日免费泥点」状态。不给它新增 `retired` 状态位或新字段,直接把后台配置 `daily_free_points_per_day` 配成 0,让「每日免费发放额」这个普通数值自己表达;前端据此隐藏每日免费相关入口。
|
||||
- 决策(信号取 `dailyFreeResetPoints` 而非剩余):`ProfileMudPointBalance.dailyFreeResetPoints <= 0` 表示不提供该池。`dailyFreePoints`(当天剩余)为 0 是每天用完后的正常状态,不能当信号。
|
||||
- 决策(显示规则 `remaining > 0 || reset > 0`):当天仍有存量(配置中途改成 0,但当天已发未用完)时必须继续展示——扣点顺序不变,这部分存量会被优先扣减,隐藏后用户会「有余额看不见、还被先扣」。判定收口到 `packages/shared/src/utils/mudPoints.ts` 的 `shouldShowProfileDailyFreePool`,三端共享组件共用,消费方 controller 不改。
|
||||
- 决策(后端放开校验、不新增契约字段):`server-rs/crates/module-runtime/src/commands.rs` 的 `daily_free_points_per_day` 由「必须 > 0」放宽为 `0..=i64::MAX`,错误变体改名为 `DailyFreePointsPerDayOverflow`;`0` 本就是 `u64` 合法值,`shared-contracts` / ts-rs 生成物 / OpenAPI 不改。发放与账本逻辑不变(`amount_delta == 0` 时后端本就跳过写账)。
|
||||
- 影响范围:`packages/shared/src/utils/mudPoints.ts`、`PlatformMudPointWalletEntry`、`PlatformProfileRechargeModal`(池概览 3 列变 2 列、扣点顺序提示与泥点确认页文案随可见性切换)、`apps/admin-web/src/pages/AdminProfileWalletConfigPage.tsx`(每日免费允许填 0)、`server-rs/crates/module-runtime/{commands,errors,lib}.rs`;`daily_free_grant` / `daily_free_reset` 账本文案保留,历史流水不改写。
|
||||
- 验证:`cargo check -p module-runtime`、`cargo test -p module-runtime profile_wallet_config_allows_zero_daily_free_points`、`cargo fmt --check`、根 `npm run typecheck`、`npm run admin-web:typecheck`、共享层与 admin 定向 vitest 22/22、`npm run check:encoding`、`git diff --check` 通过。
|
||||
|
||||
|
||||
## 2026-10-05 创作者主页与关注粉丝的产品边界
|
||||
|
||||
- 产品已确认:桌面第四项“创作者主页”默认进入当前账号主页,“我的”移到第五项;他人的关注/粉丝列表公开可查看,自己或他人的两类列表均可点击用户进入其创作者主页。
|
||||
@@ -194,6 +204,25 @@
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/styles.css`、`src/view/home/index.tsx`、`src/view/template-library/index.tsx`。
|
||||
- 验证:Chromium 真机量测(1440x800 / 1440x560 / 390x844 / 390x560,带与不带横幅)——首页 / 模板库 / 项目页改动前后高度一致,帮助页从「被裁 112~150px 且无可滚动祖先」变为正好等于 `.window-chrome__content` 高度并可滚动到底;`npm run typecheck`、`npm run check:encoding`、`git diff --check`、`eslint` 通过。
|
||||
|
||||
## 2026-10-02 泥点双余额与会员补差升级方案(拷问定稿)
|
||||
|
||||
- 决策(对外投影):余额接口并列暴露每日免费泥点 / 月度泥点 / 永久泥点三池,永久泥点不再表现为「总额的剩余」;`totalPoints` 的池语义退役,`limitedPoints` 更名 `monthlyPoints`。存储层不动——`wallet_balance` 仍是唯一权威总额,永久泥点 = 总额 − 每日免费 − 月度,并留 TODO 待后续改为独立存储(触发条件见 ADR)。
|
||||
- 决策(扣减与展示):扣减顺序维持「每日免费 → 月度 → 永久」,可用总额 = 月度 + 永久,每日免费保留并单独计量与展示。
|
||||
- 决策(会员侧重做):会员侧未上线,旧档位 `Month/Season/Year` 与 `Basic/Ultimate` 及后台按档位权益字段直接删除;账期改为按开通日自然月(当月最后一天夹取),年付 12 期用 `cycle_index` / `cycle_count` 表达,仍只有一行活跃会员记录。
|
||||
- 决策(充点侧):6 档充点、取消首充赠送;复用旧 4 档 `points_*` id 并把赠送置 0,新增两档(`points_1280` / `points_3280`),不删除任何行(历史订单与首充资格按 `user_id + product_id` 引用)。
|
||||
- 决策(升级):后端只读报价接口 + 下单重算并落订单快照;会员订单复用 `profile_recharge_order`(新增 kind 与末位变更快照字段);新增账本来源 `MembershipUpgradeGrant`,幂等锚 `membership-upgrade-grant:{user}:{order_id}`;金额向上取到分、补点向下取整,年付按「后续完整月数 + 本期实际月长占比」;会员退款维持 `manual_review`,只靠订单快照支撑人工复核。
|
||||
- 边界:每日免费池、永久余数口径、账本与充点订单**在线上**;只有会员侧未上线,因此会员侧零迁移、钱包侧不允许口径漂移。
|
||||
- 决策(会员档位枚举化):会员档位身份改为 Rust 枚举 `RuntimeProfileMembershipPlan { Normal, Starter, Plus, Pro, Max }`,绝不字符串;新增 `profile_membership_plan` 目录表并**以该枚举为主键**(后台按档位改价 / 改权益);`profile_membership.tier` 与 `profile_recharge_product_config.tier` 两列删除、旧 `RuntimeProfileMembershipTier` 整体退役。会员侧无存量行,属经用户授权的破坏性变更;账本来源枚举仍只允许末尾追加 `MembershipUpgradeGrant`。
|
||||
- 决策(目录权益):目录一行一档,含月价、**独立可配置年价** `year_price_cents`、每期额度、模型权限 `Basic` / `Full`(本期只落字段、不执行拦截)、并发上限 `u32`(**哨兵 128 = 不设上限**,不用 `Option`);`Normal` = 0 点 / `Basic` / 并发 1 / rank 0。
|
||||
- 决策(目录数值):`Starter` ¥39 / 400 / ¥390 · `Plus` ¥99 / 1150 / ¥990 · `Pro` ¥299 / 3650 / ¥2990 · `Max` ¥699 / 8650 / ¥6990。
|
||||
- 权威入口:[实施计划](../../technical/【实施计划】双余额泥点与会员补差升级-2026-10-02.md);设计见 [`【技术设计】泥点三池与会员计费后端设计`](../../technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md);决策见 [`【ADR】泥点三池以单一总额为权威`](../../adr/【ADR】泥点三池以单一总额为权威-2026-10-02.md)、[`【ADR】会员账期按开通日自然月`](../../adr/【ADR】会员账期按开通日自然月-2026-10-02.md)、[`【ADR】会员订单复用充值订单与补差升级幂等锚点`](../../adr/【ADR】会员订单复用充值订单与补差升级幂等锚点-2026-10-02.md)、[`【ADR】会员档位以枚举为权威`](../../adr/【ADR】会员档位以枚举为权威-2026-10-02.md)。
|
||||
- 状态:**已落地**。后端(领域模块、`profile_membership_plan` 表与播种、两处 `tier` 列删除、订单变更快照、自然月账期、6 档充点、三池余额投影、升级报价接口、后台档位接口)、契约生成(ts-rs → `packages/shared/src/contracts/generated/`)、共享组件与平台入口三池展示、AGC 壳、后台「会员档位」页与充值商品页收敛均已实现并通过门禁。明确不做:模型权限与并发上限只落字段、不提供 `availableTotalPoints`、会员购买 / 升级的 C 端 UI。
|
||||
- 实现期补充决断(未改变上述范围):
|
||||
- 余额 DTO 由 Rust 单一真源经 ts-rs 导出,前端 re-export 而非手写第二份字段列表;`u64` 标 `ts(type = "number")` 避免 `bigint` 分叉,`check:generated-bindings` 以「重新生成逐字节比对」守漂移。
|
||||
- 「合计」由前端派生(`packages/shared/src/utils/mudPoints.ts` 的 `resolveProfileMudPointBalanceView`),月度刷新时刻固定按北京时间格式化,不把第二份口径放回后端。
|
||||
- 会员订单 `product_id` 编码「档位 + 周期」(`membership-{plan}-{cycleKind}`),会员商品不进 `profile_recharge_product_config`;支付确认不做出售校验,避免付款后档位被下架导致权益丢失。
|
||||
- 后台 `rank` 不可改(升级比较基准由代码内目录决定),`normal` 档位的价格与额度由后端强制归零。
|
||||
|
||||
## 2026-10-01 游戏广场评分展示边界
|
||||
|
||||
- 用户确认广场卡片增加一位小数的 10 分制平均分与评分人数,无有效评价显示“暂无评分”;保留现有排序、筛选、卡片打开详情及返回上下文。
|
||||
@@ -9624,6 +9653,136 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 边界(未验证):macOS 真机的 sidecar 加载与 `check-macos-bundle.mjs` 包内容门禁未在本机验证;Linux 门禁按新配置不再要求 sidecar 资源,需 CI 实跑确认转绿。
|
||||
- 关联:issue #519、master `fb130d184`、`docs/technical/【技术方案】AGC随包资源staging归位-2026-09-26.md`、CI run 3083。
|
||||
|
||||
## 2026-10-03 后台补全会员计费运维面:订单快照、用户会员状态、三池命名与表标签
|
||||
|
||||
- 背景:`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §5.6 声称「订单列表 / 退款人工复核读取 §2.4 快照」,但管理端 `AdminRechargeOrderEntryPayload` 没有任何会员字段;用户详情不展示会员档位与账期,钱包月度池仍叫「会员限时」,通用表浏览器缺 `profile_membership_plan` 标签。
|
||||
- 决策(订单快照透出):新增 `AdminMembershipOrderChangePayload`(变更类型 / 前后档位 / 前后账期 / 前后每期泥点 / 补点增量 / 期数 / 前后到期与重置 / 价格拆分 JSON)挂到 `AdminRechargeOrderEntryPayload.membershipChange`,由 `map_membership_order_change` 从 module-runtime 既有的 `RuntimeProfileMembershipOrderChangeSnapshot` 直接映射;非会员订单为 `null`。只读,不改下单 / 退款语义。
|
||||
- 决策(用户会员状态):`AdminUserDetailResponse` 新增 `membership: AdminProfileMembershipPayload`,api-server 复用 `get_profile_recharge_center` 的 `membership` 投影,不再新开一条 admin 专用读路径。
|
||||
- 决策(三池命名只在契约 / UI 层):`AdminProfileWalletPayload.membership_limited_points` 改名为 `monthly_points`(TS `monthlyPoints`),后台钱包区块「会员限时」改为「月度泥点」。**SpacetimeDB 列名 `membership_limited_points` 与生成绑定保持不动**——持久化改名属破坏性变更,须先确认迁移计划。
|
||||
- 决策(展示复用):新建 `apps/admin-web/src/config/membershipDisplay.ts` 收敛档位 / 账期展示名与 `format*`,`AdminMembershipPlanPage`、`AdminRechargeOrderPage`、`AdminUserDetailDialog` 共用,不再各留一份 `planLabels`。
|
||||
- 影响范围:`server-rs/crates/shared-contracts/src/admin.rs`、`server-rs/crates/api-server/src/admin_recharge.rs`、`apps/admin-web/src/{api/adminApiTypes.ts,config/membershipDisplay.ts,pages/AdminMembershipPlanPage.tsx,pages/AdminRechargeOrderPage.tsx,components/AdminUserDetailDialog.tsx}`、`scripts/admin-web-fake-api.mjs`、设计文档 §5.6/§10.2、本文件。
|
||||
- 验证:`cargo check -p api-server`;`npm run admin-web:typecheck`;`npx vitest run apps/admin-web` → 26 files / 248 tests passed(新增「会员订单在发放泥点列展示会员变更快照」及用户详情会员断言)。
|
||||
- 边界:`/admin/api/*` 不在 `/api/external/v1` OpenAPI 门禁内,本次未补接口级契约测试,会员快照只在管理端页面用例层面取证。
|
||||
|
||||
|
||||
## 2026-10-03 模型权限与并发上限的强制边界(拷问定案)
|
||||
|
||||
- 背景:`model_access` 与 `concurrent_job_limit` 此前只落字段、只用于展示,服务端零拦截;用户点出 raw gpt-image-2 这类同步生成,确认「只统计 job 的 `running`」覆盖不到它,因此先把「并发单位」定义清楚再强制。
|
||||
- 决策(并发上限只约束队列 job):强制点放在唯一的 `pending → running` 事务点 `claim_external_generation_jobs_tx`,认领时按账号统计 `running` 数、达上限则跳过(任务留在 `pending`,天然排队,不新建队列)。只算 `running`(含过期待回收的 `expired_running`);失败退回 `pending` 重试释放名额;自身过期 `running` 回收豁免;上限读 `profile_membership_plan.concurrent_job_limit`,缺行失败关闭到 `Normal`(=1),`128` 哨兵为不设上限;计数用新增 `(owner_user_id, status)` 组合索引现算,不建计数表;`/api/external/v1` 任务同样计入;不设排队深度上限。
|
||||
- 决策(模型权限只约束 AGC LLM 模型):`AgcModel` 末位加权限档、缺省 `Basic`(失败开放),由人工在后台「AGC 模型」页手动标 `Full`;`GET /api/llm/models` 按档过滤并把 `defaultModelId` 取可用集合首个,`/api/llm/responses` 经 `resolve_requested` 后校验、越权返 `MODEL_NOT_AVAILABLE_FOR_PLAN`;本地残留旧选择明确 4xx,不静默回退;不设灰度开关。
|
||||
- 边界(本轮不做):直连同步生成(raw gpt-image-2、`editor_project` 直连图片 / 图标 / 背景、角色资源、音频)没有 job 行,`running` 计数覆盖不到;统一「账号在飞生成」口径(在飞租约表 / 或统一改走 job)留作后续 TODO。
|
||||
- 影响范围:`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md`(新增 §11,修订 §9/§10)、`CONTEXT.md`(并发上限 / 新增「独立生成任务」)、`docs/adr/【ADR】模型权限与并发上限的强制边界-2026-10-03.md`、`docs/README.md`。未改代码。
|
||||
- 验证:`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。
|
||||
|
||||
## 2026-10-03 模型权限与并发上限落地实现
|
||||
|
||||
- 背景:承接同日「模型权限与并发上限的强制边界(拷问定案)」,把定案按 §11.4 编码约定落地;本文记实现拆分与验证,不再重复决策理由。
|
||||
- 实现(`module-runtime`,`b78003143`):新增 `agc_model_access.rs`(`AgcModelAccess::Basic/Full`、`MODEL_NOT_AVAILABLE_FOR_PLAN`、`AgcModelResolveError::{Unavailable, NotAvailableForPlan}`、`RuntimeProfileMembershipModelAccess → AgcModelAccess`);`AgcModel` 末位追加 `#[serde(default)] access`(存量 JSON 缺字段即 `Basic`,失败开放);`AgcModelCatalog` 增加 `available_models_for / default_model_id_for / resolve_requested_for`,默认模型优先取登记项、不在档内则回退目录顺序首个可用项、无可用项返回 `Unavailable`。
|
||||
- 实现(`spacetime-module`,`db8d467b8`):`external_generation_job` 追加 btree 组合索引 `(owner_user_id, status)`;`claim_external_generation_jobs_tx` 在 `pending → running` 前按 owner 现算 `running` 行数并过滤,达上限留在 `pending`;只算 `running`(含过期待回收),自身过期 `running` 回收豁免,`pending` 不占名额;`effective_profile_concurrent_job_limit` 走 `profile_membership.plan → concurrent_job_limit`,缺行失败关闭到 `Normal`(=1),`128` 为不限;抽取纯函数 `external_generation_claim_within_concurrency_limit` 并补单测。
|
||||
- 实现(后台,`919b4b01d`):`AdminAgcModel` 新增 `access`(缺省 `basic`),api-server 保存时只接受 `basic/full`,`GET` 回读 / `PUT` 写入;后台「AGC 模型」页新增「权限档」列与选择器,fake API 与页面用例覆盖。
|
||||
- 实现(`api-server`,`9b1a538d2`):新增 `llm/model_access.rs`,用现有 `get_profile_recharge_center` 解析账号档位到 `AgcModelAccess`,缺会员行 / 缺目录行 / 读失败统一失败关闭到 `Basic`;`GET /api/llm/models` 按档过滤且默认项取该档可用集合;`/api/llm/responses`、`/api/llm/chat/completions`、`/api/llm/anthropic/*` 全改按档解析;越权映射 403 + `MODEL_NOT_AVAILABLE_FOR_PLAN`,目录外 / 停用仍为 422。
|
||||
- 术语口径:`Basic` / `Full` 是模型对会员档的要求,`Normal` 等是会员档位;缺省语义统一「失败开放到基础档、失败关闭到 1 并发」——模型档缺省 `Basic` 是失败开放(不拦),并发上限缺行失败关闭到 `Normal`(=1)。
|
||||
- 验证:`cargo check -p api-server`、`cargo check -p api-server --tests`;`cargo test -p api-server llm` 49 passed、`model_catalog_tests` 3 passed(含 Basic 档过滤 Full 模型、403 错误码映射);`npm run check:spacetime-schema` 的失败项全部是分支既有 `profile_membership` / `profile_recharge_product_config` 字段变更(对照 `origin/master` merge-base `25beb3ad5`),与本次仅新增 btree 索引无关,guard 不比较索引;`npm run check:encoding`、`git diff --check`。
|
||||
- 边界 / TODO:账号档位仍复用带周期刷新写入的 `get_profile_recharge_center`,后续替换为轻量专用读;AGC 客户端仍靠「目录按档过滤 + 本地选择对账回退默认项」,未加专门的 403 重选提示;统一「账号在飞生成」口径(含无 job 行的同步生成)仍为后续 TODO。
|
||||
|
||||
## 2026-10-03 模型列表改为可用 / 不可用分桶(修订)
|
||||
|
||||
- 背景:`GET /api/llm/models` 原按档过滤,降级用户在客户端只看到「所选模型已停用」的笼统提示,且无法在列表里露出可升级的高档模型。
|
||||
- 决策(契约):返回 `models`(本档可用)与 `unavailableModels`(目录里其余全部,带 `reason` 枚举)两个平行数组,**不新增** `available` 布尔;`reason` 取 `plan_required` / `disabled` / `unknown`,同时停用且档位不够取 `disabled`;`unavailableModels` 保持目录顺序、恒定下发(无内容为空数组);上游真实模型名两桶都不下发;`defaultModelId` 必须落在 `models` 内;某档一个可用模型都没有时保留 503 `MODEL_UNAVAILABLE`。
|
||||
- 决策(兼容):`unavailableModels` 是加法字段,旧客户端忽略后行为不变,无需能力协商;`reason` 在 wire 侧带 `unknown` 兜底,后端将来新增取值不会让旧客户端解析失败;服务端按档强制不变。
|
||||
- 决策(客户端):AGC 桌面选择器把 `unavailableModels` 渲染为不可点项,仅 hover / 键盘 focus 出 tooltip(`plan_required`→「订阅计划不支持」、`disabled`→「该模型已下线」、`unknown`→「暂不可用」);降级仍自动回退默认,提示按 `reason` 取;后台直接删除(两桶都没有)用泛化文案「所选模型已不可用,已切回默认模型」;不再新增「当前已选不可用」的 API 字段,前端自行从 `unavailableModels` 推断。
|
||||
- 决策(ts-rs 单一真源):整个目录 DTO 与枚举导出到 `packages/shared/src/contracts/generated/`;新增 barrel `packages/shared/src/llm/modelCatalog.ts` 并由 `packages/shared/src/index.ts` 转发;`AgcAgentMode` / `AgcModelProtocol` 从 `module-runtime` 迁入 `shared-contracts`(`module-runtime` re-export);`revision: u64` 标 `#[ts(as = "f64")]`;AGC 前端删除手写 `ClientLlmModel`,改从 `@genarrative/shared` 根导入。
|
||||
- 影响范围:`docs/adr/【ADR】模型权限与并发上限的强制边界-2026-10-03.md`(修订节)、技术设计 §11.2/§11.4、`docs/technical/【技术方案】AGC后台模型别名与对话选择-2026-09-05.md`、`docs/project-memory/plans/【里程碑】AGC模型目录上游同步-2026-09-24.md`、本文件。
|
||||
- 验证:`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。
|
||||
|
||||
## 2026-10-03 模型权限改走只读 procedure(性能收口)
|
||||
|
||||
- 背景:`api-server` 的 AGC 模型权限解析原复用 `get_profile_recharge_center`,它带幂等的账期 / 免费点刷新与钱包 / 商品等无关快照字段,位于每个 `/api/llm/*` 请求热路径;同日「落地实现」条目已把它列为 TODO。
|
||||
- 决策:新增只读 procedure `get_profile_agc_model_access_and_return`(输入沿用 `RuntimeProfileMembershipGetInput`,结果 `RuntimeProfileAgcModelAccessSnapshot { user_id, plan, model_access }`,仅 editor generation runtime service identity 可调):只读会员账期投影 + `profile_membership_plan.model_access`,缺会员行 / 已过期按 `Normal`,缺档位目录行失败关闭到 `Basic`,不刷新、不写库;`api-server` 改调它,失败关闭语义不变。
|
||||
- 影响范围:`module-runtime`(领域类型 `9e397fe9c`)、`spacetime-module`(procedure,同提交)、生成绑定、`spacetime-client`(`get_profile_agc_model_access`)、`api-server/src/llm/model_access.rs`(`b0dc94b46`)、技术设计 §11.4。
|
||||
- 验证:`cargo check -p spacetime-module`、`cargo check -p spacetime-client`、`cargo check -p api-server --tests`;`cargo test -p api-server llm` 51 passed;`npm run check:encoding`、`git diff --check`;`npm run check:spacetime-schema` 失败项仍为分支既有 `profile_membership` / `profile_recharge_product_config` 表字段变更,与本次仅新增 procedure、未改表无关。
|
||||
|
||||
## 2026-10-03 会员链路错误分类改为机器可读错误码(typed error)
|
||||
|
||||
- 背景:`api-server/src/runtime_profile.rs` 的 `is_runtime_profile_membership_domain_error` 用中文 `error_message` 前缀 / 精确匹配决定返回 400 还是 502;文案一改(或新增拒绝点)就静默退化,且分类逻辑与 module 的错误文案跨层重复。
|
||||
- 决策(错误码随 procedure 结果透传):`module-runtime` 新增 `RuntimeProfileMembershipErrorCode`(`UpgradeRejected` / `NotMember` / `AlreadyActive` / `CycleKindLocked` / `MembershipExpired` / `StateChanged` / `PlanNotPurchasable` / `PlanCatalogMissing` / `ProductIdUnparseable` / `MembershipProductRetired` / `InvalidPlanConfig`)与内部 `RuntimeProfileMembershipDomainError { code, message }`;三个会员 procedure 结果(升级报价、充值中心 / 下单、后台档位 upsert)末尾追加 `error_code: Option<...>`,拒绝点显式赋码,不再做字符串分类。
|
||||
- 决策(客户端错误类型):`spacetime-client` 新增 `SpacetimeClientError::ProcedureRejected { code, message }`;`api-server` 据此返回 400(provider `runtime-profile`),基础设施错误保持 502。字段校验类错误不产码,仍落 502。
|
||||
- 决策(行为变化):`会员目录缺少档位 ...` 与「已迁移的会员商品」原先落 502,现在明确 400;兑换码链路本轮未改造,仍保留一个文案匹配函数。procedure result 新增字段属 wire 契约变更,要求 module 与 api-server 同版本部署。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active.rs,active/mapper/runtime_profile.rs}`、生成绑定、`server-rs/crates/api-server/src/{runtime_profile.rs,asset_billing.rs,editor_project.rs}`、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §5.8、本文件。
|
||||
- 验证:`npm run spacetime:generate`;`cargo check --workspace`;`api-server runtime_profile::tests` 41 passed;`module-runtime --lib membership::` 39 passed;`npm run check:encoding`、`npm run check:spacetime-schema`、`cargo fmt -- --check`、`git diff --check` 通过。
|
||||
|
||||
## 2026-10-03 会员目录播种策略定为「保留懒播种 + 明示副作用」
|
||||
|
||||
- 背景:评审第 20 项指出「只读」procedure(报价 / 充值中心 / 后台查询)首次读取时会经 `ensure_default_profile_membership_plan` 写库,与代码注释「不写库」矛盾。三条候选:①保留懒播种并承认副作用;②只靠 init + 发布期 migration 播种;③只读投影不落表、内存回退代码内目录。
|
||||
- 决策:选 ①。`init_profile_membership_plan_catalog` 只在数据库首次创建时执行,增量发布不重跑;「表已存在但为空」的存量库必须靠读取 helper 懒播种兜底,否则目录为空会让下单 / 报价失败。保留懒播种,并把「只读入口在目录为空的首次调用会写库、表非空后幂等只读」明确写进代码注释与设计文档 §7,消除注释与实现的矛盾。
|
||||
- 未选 ②/③ 的原因:②依赖发布流程保证存量库被种上,当前没有这条保证;③改动读路径回退逻辑,风险与收益不匹配。将来若做发布期 migration,再删除懒播种。
|
||||
- 影响范围:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`(`ensure_default_profile_membership_plan` / `init_profile_membership_plan_catalog` / `membership_plan_row` / `membership_plan_records` / 报价与建单函数注释)、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §7/§10.2、本文件。
|
||||
|
||||
## 2026-10-03 兑换码链路补机器可读错误码(typed error 收口)
|
||||
|
||||
- 背景:评审第 12 项指出 `SpacetimeClientError::ProcedureRejected` 的 `Display` 丢掉 `code`,而 `api-server/src/runtime_profile.rs` 的 `is_runtime_profile_redeem_code_domain_error` 仍在精确匹配 7 条中文 `error_message` 来判断兑换码拒绝该返回 400 还是 502,文案一改就静默退化,与会员链路已落地的 typed error 不一致。
|
||||
- 决策(module 侧赋码):`module-runtime` 新增 `RuntimeProfileRewardCodeRedeemErrorCode`(`NotFound` / `Disabled` / `NotStarted` / `Expired` / `UsesExhausted` / `NotAllowedForUser` / `InvalidReward`);`RuntimeProfileRewardCodeRedeemProcedureResult` 末尾追加 `error_code: Option<...>`;`spacetime-module` 用 `ProfileRewardCodeRedeemProcedureError::{Classified, Plain}` 在拒绝点显式赋码,入参与钱包类错误保持 `error_code = null`。
|
||||
- 决策(客户端 / BFF):`SpacetimeClientError::ProcedureRejected` 的 `code` 改为 `RuntimeProfileProcedureRejectionCode`(聚合 `Membership` 与 `RewardCodeRedeem` 两类),`Display` 输出 `[code] message` 保留机器原因;`api-server` 的 `map_runtime_profile_client_error` 只读变体的 `code` / `message`,`AppError.code` 取 `code.as_str()`,删除 `is_runtime_profile_domain_error` 与 `is_runtime_profile_redeem_code_domain_error` 两个文案匹配函数。
|
||||
- 决策(行为变化):兑换码的 `兑换码不存在` / `已停用` / `未生效` / `已过期` / `次数已用完` / `不适用于当前账号` / `奖励无效` 由原来的 502 变为 400 且带机器码;procedure result 新增字段属 wire 契约变更,要求 module 与 api-server 同版本部署。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active.rs,active/mapper/runtime_profile.rs}`、生成绑定、`server-rs/crates/api-server/src/runtime_profile.rs`、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §5.8、本文件。
|
||||
- 验证:`npm run spacetime:generate`;`cargo check -p api-server`;`cargo test -p api-server runtime_profile::tests` 41 passed;`cargo test -p module-runtime`、`cargo test -p spacetime-client`、`cargo test -p spacetime-module` 全通过;`npm run check:spacetime-schema`、`npm run check:encoding`、`git diff --check` 通过。
|
||||
|
||||
## 2026-10-03 会员计费评审第 13–20 项收口:契约枚举化、生成类型、计费与结算防御
|
||||
|
||||
- 背景:评审剩下一批 medium / low 的契约与防御性缺陷,用户逐项确认后一次性修复;每项单独提交,便于回溯。
|
||||
- 决策(契约枚举化,13/14/18):`shared-contracts` 新增 `ProfileMembershipStatusToken`(`normal` / `active`),`AdminProfileMembershipPayload` 的 `plan` / `status` / `cycle_kind` 改用 token 枚举;5 个会员 token 枚举补 `ts_rs::TS` + `ts(export, ...)`,前端 `packages/shared/src/contracts/runtime.ts` 与 `apps/admin-web/src/api/adminApiTypes.ts` 改为生成类型别名 / re-export,禁止手写联合类型;`AdminAgcModel.access` 由裸 `String` 改为 `ProfileMembershipModelAccessToken`,未知值反序列化即拒。
|
||||
- 决策(计费防御,15/16):`quote_runtime_profile_membership_upgrade` 显式拒绝价格 / 每期泥点 `<=`,堵住 0 元升级;`advance_beijing_months_clamped` 去掉 `expect`,超出 i64 微秒可表示范围(约 ±29 万年)时按方向饱和到 `i64::MAX` / `i64::MIN`,不改签名以免牵动 `membership_cycle_window` / `membership_expires_at`。
|
||||
- 决策(错误链 typed,17):`resolve_llm_router_client` 返回 `Result<_, AppError>`,`load_owner_llm_catalog` 的 `503 + MODEL_ACCESS_UNAVAILABLE` 与 `resolve_requested_for` 的 `MODEL_UNAVAILABLE` 不再被拍平成中文,Chat Completions 与 `/api/llm/models`、代理端点错误码对齐。
|
||||
- 决策(去掉退役入参,19):后台 upsert 请求 DTO 与 SpacetimeDB procedure 入参移除 `bonus_points` / `duration_days`,`profile_recharge_product_config` 退役列保留、写死 0;删除 `RuntimeProfileFieldError::InvalidRechargeProductFields`;后台表单去掉「首充赠送」,假数据同步。
|
||||
- 决策(结算归一,20):`frozen_membership_cycle_matches` 比较双方都取 `max(1)`,修复 `cycle_index == 0` 迁移行被永久判成「会员状态已变化」的问题;不新增写库副作用。
|
||||
- 影响范围:`shared-contracts/{runtime,admin}.rs`、`module-runtime/{domain,commands,errors}.rs` 与 `module-runtime/src/{membership/upgrade,membership/cycle,agc_model_access}.rs`、`spacetime-client/{active,active/mapper/runtime_profile,module_bindings}`、`spacetime-module/src/runtime/active/profile.rs`、`api-server/src/{runtime_profile,llm/mod,agc_models}.rs`、`packages/shared/src/contracts/runtime.ts`、`apps/admin-web/src/api/adminApiTypes.ts`、`apps/admin-web/src/pages/AdminRechargeProductPage.tsx`、`scripts/admin-web-fake-api.mjs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、本文件。
|
||||
- 验证:`cargo test -p api-server` 1206 passed;`-p module-runtime` / `-p spacetime-client` / `-p spacetime-module` 全通过;`npm run spacetime:generate`、`npm run check:spacetime-schema`(92 表)、`npm run check:generated-bindings`(117 文件)、`npm run admin-web:typecheck`、`npm run typecheck`、`npm run check:encoding`、`git diff --check` 通过。
|
||||
|
||||
## 2026-10-03 后台兑换码管理补机器可读错误码(typed error 收口)
|
||||
|
||||
- 背景:`bd06e4269` 删除中文文案匹配后,`admin_disable_profile_redeem_code` / `admin_upsert_profile_redeem_code` / `admin_list_profile_redeem_codes` 仍只回传裸 `error_message`。这些 procedure 的 `ok = false` 是明确的业务拒绝(停用不存在的码、入参非法、私有码缺可兑换用户),却被 `spacetime-client` 拍平成 `Procedure(String)`,在 `api-server` 落 502,与会员 / 核销两条链路已落地的 typed error 不一致。
|
||||
- 决策(module 侧赋码):`module-runtime` 新增 `RuntimeProfileRedeemCodeAdminErrorCode`(`NotFound` / `InvalidField` / `AllowedUsersRequired`);`RuntimeProfileRedeemCodeAdminProcedureResult` 与 `RuntimeProfileRedeemCodeAdminListProcedureResult` 末尾追加 `error_code: Option<...>`;`spacetime-module` 用 `ProfileRedeemCodeAdminProcedureError::{Classified, Plain}` 在拒绝点显式赋码,`RuntimeProfileFieldError` 统一归 `invalid_field`,私有码缺可兑换用户归 `allowed_users_required`,停用不存在的码归 `not_found`。
|
||||
- 决策(客户端 / BFF):`RuntimeProfileProcedureRejectionCode` 增加 `RedeemCodeAdmin` 聚合变体,`spacetime-client` 两个后台 mapper 有 `error_code` 时走 `ProcedureRejected`、缺失时保持 `Procedure`;`api-server` 复用既有 `map_runtime_profile_client_error`,无需再引入字符串分类。
|
||||
- 决策(行为变化):后台兑换码的 `兑换码不存在`、入参校验失败、私有码缺可兑换用户由 502 变为 400 且带机器码;procedure result 新增字段属 wire 契约变更,要求 module 与 api-server 同版本部署。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active.rs,active/mapper/runtime_profile.rs,module_bindings,module_bindings.rs}`、`server-rs/crates/api-server/src/runtime_profile.rs`、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §5.8、本文件。
|
||||
- 验证:`npm run spacetime:generate`;`cargo check`(module-runtime / spacetime-module / spacetime-client / api-server);`cargo test -p spacetime-client` 30 passed;`cargo test -p api-server runtime_profile::tests` 42 passed;`cargo test -p module-runtime --lib` 113 passed;`npm run check:spacetime-schema`、`npm run check:encoding`、`git diff --check` 通过。
|
||||
|
||||
## 2026-10-03 后台邀请码管理补机器可读错误码
|
||||
|
||||
- 背景:后台邀请码的 `admin_upsert_profile_invite_code` / `admin_list_profile_invite_codes` 与上一轮的后台兑换码同病——`map_runtime_profile_invite_code_admin_procedure_result` 把 `ok = false` 一律拍成 `Procedure(String)`,`邀请码已被其他用户占用` 这类明确业务拒绝在 `api-server` 落 502,且 `map_runtime_profile_client_error` 的注释夸大成 «profile procedure 的业务拒绝都已 typed»。
|
||||
- 决策:`module-runtime` 新增 `RuntimeProfileInviteCodeAdminErrorCode`(`InvalidField` / `InUse`);两个后台邀请码 procedure 结果末尾追加 `error_code: Option<...>`;`spacetime-module` 用 `ProfileInviteCodeAdminProcedureError::{Classified, Plain}` 赋码,字段校验归 `invalid_field`、被他人占用归 `in_use`;`RuntimeProfileProcedureRejectionCode` 增加 `InviteCodeAdmin` 聚合变体,`spacetime-client` mapper 有码走 `ProcedureRejected`、缺失时保持 `Procedure`;`api-server` 复用既有映射,并把注释收窄为 «带 typed error_code 的四条链路»。
|
||||
- 行为变化:后台邀请码的入参校验失败与 `邀请码已被其他用户占用` 由 502 变为 400 且带机器码;procedure result 新增字段仍要求 module 与 api-server 同版本部署。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active.rs,active/mapper/runtime_profile.rs,module_bindings,module_bindings.rs}`、`server-rs/crates/api-server/src/runtime_profile.rs`、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §5.8、本文件。
|
||||
- 验证:`npm run spacetime:generate`;`cargo check`(module-runtime / spacetime-module / spacetime-client / api-server);`cargo test -p spacetime-client` 32 passed;`cargo test -p api-server runtime_profile::tests` 43 passed;`cargo test -p module-runtime --lib` 114 passed;`cargo test -p spacetime-module` 271 passed。
|
||||
|
||||
## 2026-10-03 档位无可用模型改用独立错误码 MODEL_UNAVAILABLE_FOR_TIER
|
||||
|
||||
- 背景:`AgcModelResolveError::Unavailable`(422)与 `NoModelForTier`(503)此前共用 `MODEL_UNAVAILABLE`(见本文件 2026-10-03 的列表契约条目),只按 `error.code` 分派的客户端无法分辨「单个模型不可用」与「整个档位无可用模型(目录 / 档位配置问题)」,两者修法不同。
|
||||
- 决策:`module-runtime` 新增常量 `MODEL_UNAVAILABLE` 与 `MODEL_UNAVAILABLE_FOR_TIER`,`NoModelForTier.code()` 返回后者,`Unavailable` 仍返回 `MODEL_UNAVAILABLE`;HTTP 状态(422 / 503)与端点行为不变,仅把错误码拆细,`resolve_error_codes_are_stable` 同步断言三码两两不同。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/agc_model_access.rs`、`server-rs/crates/api-server/src/llm/{mod,model_access}.rs`、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §11.4、本文件。
|
||||
- 验证:`cargo test -p module-runtime --lib agc_model_access`;`cargo test -p api-server llm::tests`。
|
||||
|
||||
## 2026-10-03 会员响应 token 反序列化前向兼容
|
||||
|
||||
- 背景:`shared-contracts` 的 5 个会员 token 枚举同时被入参 / 响应 DTO 复用,此前响应侧也严格反序列化;后端一旦新增 plan / status / cycle 取值,AGC Tauri shell 的 `read_profile_recharge_center` / `read_profile_membership_upgrade_quote`(`account_api.rs` 用 `serde_json::from_value` 解析整份响应)就会得到 `响应格式无效`,整个钱包页不可用。
|
||||
- 决策:只给**响应** DTO 的 token 字段挂 `#[serde(deserialize_with = "...")]` 做前向兼容,未知取值 fail-open 兜底到已知最高档(plan→`max`、status→`active`、cycle→`yearly`、model_access→`full`;真实权益 / 下单金额以后端重算为准,避免把付费会员显示成非会员后引导重复购买)。**入参** DTO 不改,非法 token 仍在反序列化即拒;不改枚举本身的 `Deserialize`,避免 `Unknown` 变体污染入参与穷尽 match。
|
||||
- 影响范围:`server-rs/crates/shared-contracts/src/runtime.rs`(4 个 helper + `ProfileMembershipPlanResponse` / `ProfileMembershipResponse` / `ProfileMembershipUpgradeQuoteResponse` 的 8 个字段)、`docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md` §2.5。后台 DTO(`AdminProfileMembershipPayload` / `AdminMembershipOrderChangePayload`)只有 TS 消费方,不做 Rust 侧处理。
|
||||
- 验证:`cargo test -p shared-contracts --lib` 106 passed(含 `membership_response_tokens_tolerate_unknown_values_from_newer_backend`、`membership_request_tokens_stay_strict_for_unknown_values`)。
|
||||
|
||||
## 2026-10-03 会员错误码 `ProductIdUnparsable` 拼写修正(未上线,直接破坏性改名)
|
||||
|
||||
- 背景:`RuntimeProfileMembershipErrorCode::ProductIdUnparsable` 拼写有误,生成绑定与对外 `error.code = "product_id_unparsable"` 同错。
|
||||
- 决策:该 PR 尚未上线,不留兼容,直接改名为 `ProductIdUnparseable`,wire 串同步改为 `"product_id_unparseable"`;重新生成 SpacetimeDB 绑定。
|
||||
- 影响范围:`server-rs/crates/module-runtime/src/domain.rs`、`server-rs/crates/spacetime-module/src/runtime/active/profile.rs`、`server-rs/crates/spacetime-client/src/{active/mapper/runtime_profile.rs,module_bindings/runtime_profile_membership_error_code_type.rs}`、`server-rs/crates/api-server/src/runtime_profile.rs`、设计文档 §5.8、本文件(含上文旧条目中的同名标识符同步更正)。
|
||||
- 验证:`cargo test -p module-runtime --lib` 114 passed;`cargo test -p spacetime-client` 32 passed;`cargo test -p api-server runtime_profile::tests` 43 passed;`cargo test -p spacetime-module` 271 passed。
|
||||
|
||||
## 2026-10-03 会员结构体也走 ts-rs 生成类型(去前端手写漂移)
|
||||
|
||||
- 背景:会员 token 枚举早已 `ts(export)`,但承载它们的结构体(`ProfileMembershipPlanResponse`、`ProfileMembershipResponse`、`ProfileRechargeCenterResponse`、`ProfileRechargeProductResponse`、`ProfileRechargeOrderResponse`、`ProfileMembershipUpgradeQuoteResponse`、`ProfileMembershipPlanAdminListResponse`、`AdminUpsertProfileMembershipPlanRequest`、`ProfileDailyFreePointsResponse`、`ProfileMembershipUpgradeQuoteRequest`)仍是前端手写,字段与后端可能漂移。
|
||||
- 决策:这 10 个结构体补齐 `ts_rs::TS` + `ts(export)`;64 位整数字段统一补 `ts(type = "number")`(ts-rs 默认 `bigint`,JSON 实际是 number)。`packages/shared/src/contracts/runtime.ts` 与 `apps/admin-web/src/api/adminApiTypes.ts` 改为 re-export 生成类型,仅前端需要收窄的 `kind` / `status` 联合用 `Omit` 组合保留;`ProfileRechargeCenterResponse` 的 `pointProducts` / `latestOrder` 同样收窄。
|
||||
- 影响范围:`server-rs/crates/shared-contracts/src/runtime.rs`、`packages/shared/src/contracts/generated/*`(新增 10 个文件)、`packages/shared/src/contracts/runtime.ts`、`apps/admin-web/src/api/adminApiTypes.ts`、设计文档 §2.5。
|
||||
- 验证:`npm run check:generated-bindings`(shared-contracts 23 个文件);根 `npm run typecheck`、`apps/admin-web` 与 AGC shell 主 `tsc` 通过;`cargo test -p shared-contracts --lib` 106 passed;`PlatformProfileRechargeModal` / `usePlatformProfileCenterController` vitest 14 passed。
|
||||
|
||||
## 2026-10-04 AGC 窗口标题栏项目标签改认「当前项目」,运行中项目降级为圆点与徽标
|
||||
|
||||
- 背景:issue #618。窗口标题栏那枚标签原来被「正在运行的项目」整块接管——`WindowChrome` 只要存在活动回合或运行快照读取失败,就把当前工作区标题换成 `ActiveProjectRunsPanel`(titlebar 形态),主文案取开始时间最晚的在跑项目。于是「当前项目没在跑、后台别的项目在跑」时,标签显示的是别的项目(现场:当前项目 `gameagent-ff7f3240`,标签 `gameagent-c3af9c7e`),用户以为自己开错了项目;读取失败时标签还会变成「正在运行的项目读取失败」,同样顶掉当前项目名。
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
- 对已明确退役且无现役调用方、公开契约、持久化数据、活跃实例或迁移要求的对象,直接清理实现、专属测试和说明,将权威文档更新为当前状态;历史由 Git 保存。公开契约、持久化数据和正式迁移按实际需求保留最小兼容及对应测试。
|
||||
- 修改 `/api/external/v1` 时,同批更新 `docs/openapi/genarrative-external-v1.openapi.json` 与契约测试。
|
||||
- 修改 SpacetimeDB schema 时遵守字段追加/default 约束,同步 migration、表目录、生成绑定,并运行 schema 检查;删除、改名、重排或改类型前先确认迁移计划。
|
||||
- `shared-contracts` 响应 DTO 只增不删:新增字段一律可选(`Option`,或带安全 `#[serde(default ...)]`),身份 / 状态字段保持必填;删除、改名、改类型或改必填属性前先确认版本化计划,不保留仅服务旧客户端的 legacy shim。详见后端架构文档「shared-contracts DTO 变更规则」。
|
||||
- 日志不递归输出完整配置、应用状态或 provider client;新增字段默认不进入安全摘要。所有 provider 配置类型(`LlmConfig`、`OssConfig`、`WechatConfig`、`WechatPayConfig`、`MattingConfig`、`VolcengineSpeechConfig`、`Hyper3dSettings`、`VectorEngineImageSettings`、`VectorEngineAudioSettings`)都必须手写脱敏 `Debug`:只输出枚举、数值、布尔、有界标识与 `<redacted>` 占位,凭据、私钥、路径与 URL 一律不递归格式化,并各带一条哨兵值回归用例(`cargo test -p platform-* debug`)。新增同类配置必须照此办理,不允许直接 `derive(Debug)`。
|
||||
- HTTP 横切能力集中在 Axum/Tower 中间件:正常与降级路由复用追踪层;指标与 trace 使用 `MatchedPath` 模板及固定兜底,不把请求 ID、实际资源 ID 或 query 放入指标标签。在途请求通过 RAII guard 覆盖 Future 取消与 panic unwind;请求执行和响应体存活分别计量,不能把 handler 耗时当作 SSE 全生命周期。
|
||||
- 业务依赖在组合根显式装配,Axum `FromRef` 只抽取可浅拷贝的窄能力。项目元数据与 External API 鉴权不持有完整 `AppState`,测试经相同接口注入替代依赖。集中鉴权仍保留方法级 fallback、公开入口、MCP 和 body limit 顺序;Provider span 跳过完整参数,不隐藏计费、重试、幂等或事务规则。
|
||||
|
||||
Reference in New Issue
Block a user