news 2026/9/18 8:31:40

Civitai PaidAccess 门控体系现状全解:PaidAccess 表设计、usage-control 定价与 Creator Studio 批量收费

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Civitai PaidAccess 门控体系现状全解:PaidAccess 表设计、usage-control 定价与 Creator Studio 批量收费

Civitai PaidAccess 门控体系现状全解:PaidAccess 表设计、usage-control 定价与 Creator Studio 批量收费

【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai

本指南以仓库内docs/creator-studio/monetization/paid-access-current-state.md(永久付费访问与 usage-control 定价落地后编写的"现状参考"文档,2026-07-29,commita0535931f5)为骨架,结合 paid-access-schema.md、monetization-rules.md 与两端源码,系统梳理 Civitai 模型版本的付费门控(Paid Access / Early Access)是如何建模、写入、读取、校验与缓存的。读完本文,你将掌握PaidAccess表的语义设计、terms的 bundle 定价规则、主应用与 Creator Studio 两条写入路径的职责边界、月度定价额度(PricingSlot)规则,以及当前已知的遗留问题,可直接作为继续开展付费访问工作的"接手文档"。

文档定位:一个"继续工作"的现状参考

这份文档不是一份新的设计方案,而是 phase-1 cutover 完成之后的现状快照。它解决的问题是:当旧的earlyAccessConfig/earlyAccessEndsAt/earlyAccessPermanent三列(外加earlyAccessTimeFrame)被彻底移除后,付费访问功能"现在长什么样、代码在哪里、有哪些坑"。

文档开头带有一条重要的Superseded 警告:在 monetization revamp(见 monetization-rules.md 的 R3)之后,原先的per-tier 价格上限并发永久门控数量上限已被移除——现在会员体系限制的是"创作者每月可以新增多少个价格"(免费 3 / 青铜 10 / 白银 25 / 黄金 ∞),并且设置了10,000 创作者积分门槛scores.total)才有资格变现。但文档明确说明:门控机制本身(gate mechanics)的表述仍然全部有效。因此阅读本文时需区分两层:机制层(PaidAccess行 +endsAt+terms)长期稳定;配额层(cap / allowance)以 revamp 后的 R3 为准。

核心模型:PaidAccess 表与endsAt唯一门控信号

整个门控体系建立在一张表、一个信号之上。数据库层定义(见 paid-access-schema.md 的 §1,迁移已应用,记录在prisma/migrations/):

enum PaidAccessEntityType { ModelVersion ComicChapter } model PaidAccess { entityType PaidAccessEntityType entityId Int ownerId Int // 写入时反规范化——无需多态 join 即可做 owner 维度计数 endsAt DateTime? // 物化结束时间。endsAt IS NULL ⟺ permanent(无窗口) timeframeDays Int? // 窗口天数;NULL = permanent terms Json // PaidAccessTerms,见下 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@id([entityType, entityId]) // 每个实体最多一个门 @@index([ownerId, entityType, endsAt]) }

行即门:一行存在 ⇒ 收费,无行 ⇒ 免费

这是建模的第一条不变量。"当前是否被门控"不依赖任何布尔列或派生列,只读endsAt一列

endsAt取值含义
NULL永久(Paid Access,无窗口)
未来时间活跃的限时窗口(Early Access)
过去时间已过期(tombstone,行仍保留)

正是因为这个原因,当初的设计决策中刻意没有kind枚举列——endsAt IS NULL本身就是"永久"的判别式。旧实现里earlyAccessPermanent列 +earlyAccessConfig.permanentjsonb 两个事实源靠触发器强行保持一致,正是本次重构要消灭的漂移源。同理也没有termsVersion:zod 写入边界(见后文)才是对抗 jsonb 腐烂的真正防线。

timeframeDays:窗口长度,而非门控信号

timeframeDays发布前窗口的长度(天数),NULL表示永久。文档特别强调了一个反直觉点(在 monetization-rules.md 中有完整表格):

  • endsAtNULL可能表示两种不相关的情况:永久门控(本来就没有结束日期),或尚未开始的限时门控endsAt在发布时才物化,见materializePaidAccessEndsAt)。
  • 从不表示"没有门控"——那对应"没有行"。

所以"是否永久"要判timeframeDays IS NULL(对应共享助手 isPermanentGate),而"当前是否被门控"要判isPaidAccessActiveendsAt == null || endsAt > now)。两者语义不同,不可混用。

termsJSON:bundle 定价语义

terms描述"卖什么、卖多少钱"。对ModelVersion,类型定义在 packages/civitai-buzz/src/paid-access.ts:

export type Grant = { price: number }; // 标准价格,单位 Buzz export type GenerationGrant = | { free: true } // generation 免费/不设防(绕过 access 检查) | { price?: number; trialLimit?: number }; // 付费 generation-only 档位 export type ModelVersionTerms = { download?: Grant; // 全访问(bundle)档位 generation?: GenerationGrant; // 可选的更便宜 generation-only 档位 freeGeneration?: boolean; // 罕见:download 收费但 generation 免费 acceptsBlueBuzz?: boolean; // 接受 Blue Buzz 支付并按 Blue Buzz 结算 };

Bundle 语义是理解一切的钥匙:购买download档位同时授予 generation 访问权(对应EntityAccess.permissions位掩码EarlyAccessDownload=2,购买时得到 3);generation档位缺失并不等于 generation 免费——generation 仍然被 download bundle 门控着。这正是当前线上 453 个活跃门控里那 173 个"只收 download 费"版本(历史数据,见 schema 文档 §6)映射到{ "download": { "price": … } }后行为完全不变的原因。

ComicChapter则是另一个形状{ access: Grant }(单档位解锁章节),由parseTerms(entityType, raw)按实体类型分发——但目前漫画只支持限时模式,尚未迁到PaidAccess表(stage 5 待做)。

示例行

// 全访问 8000 Buzz,可选更便宜的 generation-only 档 5000 Buzz,含 10 次预览生成 { "download": { "price": 8000 }, "generation": { "price": 5000, "trialLimit": 10 } } // 仅 download 档(历史上那 173 个):generation 仍被 bundle 门控 { "download": { "price": 8000 } }

已构建的能力:从限时抢先体验到永久付费

文档的 "What's built" 一节罗列了当前已落地的功能,按此展开:

1. 限时 Early Access + 永久 Paid Access

  • 永久=endsAt IS NULL,对满足资格门槛的所有创作者开放;把它施加到一个尚无任何价格的版本上会消耗一个月度定价额度(free 3 / bronze 10 / silver 25 / gold ∞,见 R3)。
  • 服务端在每一条写入路径上强制校验(assertMonetizationWrite),并带already-priced 豁免(carve-out):重新保存一个已经带价格的版本永远免费,无论积分多少、剩余额度多少。
  • 永久可在发布后设置;而限时窗口不能在发布后开始(R1:initialPublishedAt <= now() OR status = 'Published'即视为已发布,拒绝开启窗口)。

2. Usage-control 感知的定价

"Price for access"(访问价格)只有一个必填项,但落点随 usage control 变化:

usage control定价落点说明
Downloaddownload.price(bundle)+ 可选更便宜的generation.price购买 download 即获得 generation
Generation(仅站内生成)generation.price无 download 档访问价格就是生成价格;generation-only 字段隐藏
InternalGeneration/ExternalGeneration完全不能设门控仅审核/API 使用,Studio 中显示横幅而非选择器

免费预览生成次数 =generation.trialLimit。这一整套映射被收敛到一个共享函数 buildModelVersionTerms(两个应用都调用),保证站内表单与 Creator Studio 对"创作者被告知的内容"不会漂移。

3. Donation goal:限时专属、create-once

  • 仅限时:它会在目标达成时提前结束窗口(写入endsAt = now());永久门控永不结束,因此没有 donation goal。
  • create-once:端点从不更新/删除已有 goal,Studio 对已存在的 goal 只读展示。

4. 批量永久付费(Creator Studio)

Creator Studio 的批量操作按usage-type 过滤器?usage=download|generation)驱动列表与全选,从而保证价格字段无歧义;选择数量不设上限,但当选择超过剩余月度额度、或创作者低于资格门槛时,对话框会给出警告。

代码地图:共享层、主应用、Creator Studio spoke

文档给出了精确的代码位置,结合源码逐项展开:

共享层(两个应用共用,杜绝规则漂移)

  • packages/civitai-buzz/src/paid-access.ts:terms类型、buildModelVersionTerms()(唯一的 config→terms 映射器)、门控/授权助手(grantsGenerationisPaidAccessActiveisTimedGateActiveisPermanentGategenerationPricegenerationTrialLimitmigrateTermsForUsageControl)以及 sale/折扣相关助手(discountedPricebestSaleForsaleDaysUsed等)。
  • packages/civitai-buzz/src/pricing-allowance.ts:月度定价额度(MONTHLY_PRICING_ALLOWANCE_BY_TIER:free 3 / founder 10 / bronze 10 / silver 25 / gold Infinity)、资格门槛(MONETIZATION_MIN_CREATOR_SCORE = 10_000)、pricingEligibilitypricingFloorMessage、共享文案PRICING_SLOT_EXPLAINER/EARLY_ACCESS_NOT_COUNTED。文档注明:旧的 per-tier permanent 上限曾住在这里,已被月度额度取代。

主应用(Next.js,src/

  • 表单:src/components/Resource/Forms/ModelVersionUpsertForm.tsxtoPaidAccessInput/toFormEarlyAccessConfig/toGate、定价.refine()校验、canConfigurePaidAccessisGenOnly/paidAccessUsageOkUI 门控。
  • 服务端:
    • controllers/model-version.controller.ts— permanent membership +assertMonetizationWrite+ carve-out;
    • services/model-version.service.ts— usage-control 守卫(upsert 路径与updateModelVersionPaidAccess双侧)、assertUserEarlyAccessLimits(R1 及窗口长度/并发数限制);
    • services/paid-access.service.tsgetPaidAccessassertMonetizationWriteassertPaidAccessInputwritePaidAccessForModelVersionmaterializePaidAccessEndsAtendPaidAccessNow
    • services/pricing-slot.service.ts— 门槛 + 额度 + 插槽记录/释放;
    • services/generation/paid-access-gating.ts唯一的generation 门控执行点(内部用isPaidAccessActive+grantsGeneration判定);
    • download 门控在services/file.service.ts
  • REST 边界(spoke 调用入口):src/pages/api/v1/model-versions/early-access.ts,请求体{ id, paidAccess, donationGoal, rightsAffirmed }permanent 需要WEBHOOK_TOKEN

Creator Studio spoke(SvelteKit,apps/creator-studio/

  • 页面:src/routes/(app)/models/+page.svelte(抽屉 + 列表),使用src/lib/components/PaidAccessEditor.svelteBulkBar.svelteBulkActionDialog.svelte
  • Actions/schemas:src/routes/(app)/models/+page.server.tssetPaidAccessbulkSetPaidAccess、查询与批量 schema);表单校验在src/lib/server/monetization/paid-access-schema.tspaidAccessFormSchemabulkPaidAccessSchema)。
  • 读取:src/lib/server/models.tspaidAccessToConfigusage过滤器、paidAccessFilter)。
  • 写入:src/lib/server/monetization/paid-access.tssetPaidAccessConfig→ REST 端点)与pricing-slot.ts(本应用直接 SQL 写入的门槛 + 额度)。
  • 共享类型/常量:src/lib/monetization/paid-access.tsPaidAccessConfigMIN_ACCESS_PRICE=100MIN_GENERATION_PRICE=50DEFAULT_GENERATION_TRIAL_LIMIT=10MAX_GENERATION_TRIAL_LIMIT=1000、创作者积分阶梯EARLY_ACCESS_SCORE_UNLOCK/EARLY_ACCESS_QUANTITY_UNLOCK)。

写入路径:三处写门,一条 REST 线

一个核心观察:付费门控只有一条真正承载副作用的写入线——主应用的 REST 端点。Creator Studio 的 gate 写入并不是直写数据库,而是通过callMainApp把调用者的 session cookie 原样转发给主应用:

// apps/creator-studio/src/lib/server/monetization/paid-access.ts const ENDPOINT = '/api/v1/model-versions/early-access'; export async function setPaidAccessConfig(cookie, versionId, config, genOnly = false, rightsAffirmed = false) { const terms = config && config.accessPrice != null ? buildModelVersionTerms({ accessPrice: config.accessPrice, generationPrice: config.generationPrice, freePreviewGenerations: config.freePreviewGenerations ?? 0, genOnly, freeGeneration: config.freeGeneration, acceptsBlueBuzz: config.acceptsBlueBuzz }) : {}; const paidAccess = !config ? null : config.permanent ? { permanent: true, terms } : { permanent: false, timeframeDays: config.timeframe, terms }; // permanent 配置必须带共享 webhook token,直接用户调用无法绕过 const path = config?.permanent ? `${ENDPOINT}?token=${encodeURIComponent(env.WEBHOOK_TOKEN ?? '')}` : ENDPOINT; // ... callMainApp(path, cookie, { method: 'POST', body: { id: versionId, paidAccess, donationGoal, rightsAffirmed } }) }

之所以必须走主应用,是因为这个写有真实副作用:donation-goal 行、buzzTransactionId记账、发布状态守卫、缓存/搜索失效——只有主应用拥有这些。而主应用内部的 tRPCmodelVersion.upsert(站内表单)与 REST 端点(spoke 调用)都必须执行同一套校验,否则"只在其中一处执行的规则等于没执行"(monetization-rules.md 的明确警示)。

REST 端点的校验链(见 early-access.ts):

  1. zod 解析updateModelVersionPaidAccessSchema,失败返回 400;
  2. 所有权校验(非审核员必须为版本所有者);
  3. paidAccessBlockedForPOI(真人肖像)模型与 Private 模型拒绝设门(REST 拒绝而非静默剥离;tRPC upsert 则剥离,因为无关编辑不能被一个创作者已看不到的收费阻塞);
  4. permanent 需要WEBHOOK_TOKEN(否则 403);
  5. assertMonetizationWrite(fee 上限 + 10k 门槛 + 月度额度,按所有者而非操作者判定——审核员代存他人版本不得借用其积分/会员);
  6. assertUserEarlyAccessLimits(限时窗口最大天数 + 并发数量);
  7. 写入后按assertMonetizationWrite返回的spendsSlot/releasesSlot记录或释放PricingSlot

对应的低层写函数writePaidAccessForModelVersion(paid-access.service.ts)定义了四态映射,这与旧的三列编码截然不同:

输入endsAttimeframeDays
permanentNULLNULL
timed + 已发布publishedAt + timeframeDays保留
timed + 未发布NULL(待发布时物化)保留
不设门删除行

读取与缓存:缓存"行"而非"判定"

由于endsAt是物化的,isPaidAccessActive(row, now())可以在读取时实时派生——这带来一个关键的缓存安全性质:缓存永远不会因时间流逝而过期,失效只发生在配置变更时(编辑、阈值触发写endsAt、移除、实体删除)。这就是"缓存行、不缓存判定"的规则(onsite-monetization-parity.md 的 Caching 一节;createCachedObject批量原始组件见src/server/utils/cache-helpers.ts)。

getPaidAccess(entityType, entityIds)entityType分桶缓存(ModelVersion 123ComicChapter 123不会撞键),返回Record<number, PaidAccessRow>,为已持有的 id 集合装饰访问状态;而涉及筛选、排序、计数的查询必须用 SQL 谓词——两者互补,绝不能用getPaidAccess对分页结果做后置过滤(页面会缩水、计数会撒谎)。统一的 SQL 谓词收敛在 paid-access-sql.ts:

pa."entityType" = 'ModelVersion' AND (pa."endsAt" IS NULL OR pa."endsAt" > NOW()) AND mv.status = 'Published'::"ModelStatus"

spoke 的读取侧有同构的paidAccessFilter(KyselyEXISTS子查询,见 models.ts),以及重建 UI 形状的paidAccessToConfig——注意其 gen-only 读取规则:访问价格 =t.download?.price ?? paidGen?.price(见 Gotchas)。

定价规则(revamp 后):会员限制的是"多频繁",不是"多贵"

依据 monetization-rules.md R3,结合 pricing-allowance.ts:

限制来源适用对象
资格门槛(积分 ≥ 10k)Creator score(scores.total,经creatorScoreFromMeta读取)新增一个费用或新设一个门
每月新价格数会员等级新费用或永久门
许可费上限固定 100/次生成 × 媒体类型(视频 500)费用
付费访问价格上限
窗口长度(天)创作者积分限时
并发窗口数创作者积分限时

额度表:free 3 / bronze 10 / silver 25 / gold ∞。关键语义:

  • "价格" = 许可费或永久门;限时 Early Access 窗口两者都不是(窗口关闭即自行"定价出局",且本身受更高的创作者积分门槛约束)。
  • 无价格的版本施加价格:需要积分门槛并消耗一个插槽;编辑、下调、清除已设价格:免费且不需积分;清除最后一个价格还可能退回插槽(R3b:需无任何交易——EntityAccess无他人持有、且自插槽创建以来未发生过许可费收费;ClickHouse 的orchestration.resourceCompensations查询带 3s 超时,失败时以ModelVersionMetric.earnedAmount日镜像兜底,双端 fail-closed——无法回答的问题一律视为"插槽已花")。
  • PricingSlot(entityType, entityId)为键(与PaidAccess同键,天然覆盖 ComicChapter),主键即幂等:已有价格旁的追加、或对已设价格的任何编辑,命中该行则零成本。
  • 会员等级滑坡(incomplete/past_due/unpaid立即把当月额度降回 free 档(主应用读实时订阅状态getCapTier);但未知/缺失等级解析为free 而非 0,失去会员绝不等于失去定价能力。已花的插槽不受影响。
  • 权益声明(rights affirmation,R5):版本第一次收费时必填,按ownerId记录,不随所有权转移而存续MONETIZATION_RIGHTS_AFFIRMATION_VERSION升级会作废全部存量记录。批量场景中只要任一选中版本缺失即要求声明,已声明的服务端跳过。

已验证的端到端行为

文档 "Verified" 一节记录了审计结论(与源码一致):

  • 购买 → 访问权端到端正确:买download授予 download+generation(permissions= 3);买generation仅授予 generation(= 1);
  • 永久买家永不失去访问权EntityAccess无过期时间,expiry cron 排除 permanent 行;
  • orchestrator按用户计量 free-trial 次数并检查EarlyAccessGeneration位(外部行为,勿当作 bug 复报);
  • 主应用pnpm typecheck与 spokepnpm --filter @civitai/creator-studio-app typecheck均 0 错误。

另外从源码可确认:process-ending-early-access这个每分钟 cron(src/server/jobs/process-ending-early-access.ts)现在只做一件事——在限时窗口过期后把版本重新标记为"New"(SET "publishedAt" = NOW()以便重新浮出水面),绝不删除或改写PaidAccess。过期门控是一个 tombstone(timeframeDays有值、endsAt在过去),这正是"行存在 ≠ 被门控"的原因;清除门、删除版本、合并版本是仅有的三个删行路径,且清除门时还会把availability'EarlyAccess'回写为'Public',否则未购买者会被永久锁在门外。

Gotchas:非显而易见的坑

文档 "Gotchas" 一节浓缩了最容易踩的三个点,均有源码佐证:

  1. Gen-only 价格往返:gen-only 版本的访问价格存在generation.price(无 download 档),因此读取必须用download?.price ?? paidGen?.price兜底(spoke 的paidAccessToConfig与主应用表单的toFormEarlyAccessConfig都是如此);而buildModelVersionTermsgenOnly时会忽略generationPrice参数——访问价格就是 generation 价格。
  2. Donation goal create-once:端点从不更新/移除它;active: false只在目标达成时写入一次,没有取消路径。spoke 对已存在 goal 锁定只读。永久门控豁免于目标达成(isTimedGateActive只判限时窗口),因此一个打满的 goal 无法抹掉永久付费墙。另外注意DonationGoal仍有遗留的modelVersionId列双写,查询必须同时检查(entityType, entityId)modelVersionId两路。
  3. 命名漂移:spoke 的写 API 仍用earlyAccess*命名(setEarlyAccessConfig?/setEarlyAccessEarlyAccessConfighasEarlyAccess),尽管它现在同时覆盖永久 Paid Access;Svelte 组件已改名为PaidAccess*,API 重命名是延后的跟进项。

Open / next:待办与遗留发现

待办清单

  • CSV 导出/导入尚未包含付费/抢先体验列:建议先导出列(只读往返),再做独立的导入通道(本质是经端点、带 cap/membership/usage 强制的 CSV 驱动批量变更);donation goal 应为只读列
  • 免费预览"清除"语义两应用不一致:spoke 空值 →0,主应用清除 → 默认10DEFAULT_GENERATION_TRIAL_LIMIT)。两边编辑的是同一批版本,需统一语义。
  • Svelte 组件抽取的运行时冒烟测试:抽屉重开时的状态播种、跨批量栏的共享selected响应性——类型检查抓不到响应性回归。

phase-1 review 遗留发现(需对照当前代码复核)

原 review 文档已删除,以下发现由现状文档代为保存,均标注为"money/access-relevant,需验证是否仍可复现"。

  1. Scheduled(定时发布)抢先体验永不物化endsAt→ 变成永不释放的永久门。以未来publishedAt发布的版本状态为Scheduled,会跳过 EA 分支(要求Published),materializePaidAccessEndsAt因此永不运行;process-scheduled-publishing.ts(~:238)随后在不带publishedAt的情况下重新发布,于是再次被跳过。endsAt永远保持NULL= 永久,而process-ending-early-access(筛选endsAt <= NOW())永远等不到它。文档给出的修复形状是:让一个applyPublishedAt(tx, versionId, publishedAt)同时拥有 anti-bump SQL 与 materialize 逻辑,并被所有发布点使用。从当前源码看,materializePaidAccessEndsAt本身已经具备"跳过 tombstone、幂等、事务参数化"等防护(paid-access.service.ts),但 Scheduled 路径是否已完全接入,仍需对照process-scheduled-publishing.ts验证。
  2. mini/[id].ts(~:148)免费试用限额 SQL 谓词错误:它用terms->'generation'->>'price' IS NOT NULL判定,但常见路径(download bundle 或 gen-only)发出的是generation: { trialLimit: N }没有price,于是默认情况下 SQL 返回NULL试用限额——免费预览次数可能不会上浮给 orchestrator。应改为terms ? 'generation' AND COALESCE(terms->'generation'->>'free','') <> 'true'。该文件在现仓库中路径已不可考(属历史 review 记录),当前@civitai/buzz导出的generationTrialLimit已用grant.trialLimit ?? DEFAULT_GENERATION_TRIAL_LIMIT统一口径。
  3. earlyAccess*命名重命名(延后):见 Gotchas。

历史与迁移:phase-1 cutover 如何收尾

PaidAccess的落地是一次行为保持型的结构迁移,按四个可独立部署的切片推进(详见 paid-access-schema.md 的 Migration 一节,均已应用):

  1. Slice 0(锚点):在ModelVersionComicChapter上加写一次性的initialPublishedAt(仅在NULL时设置),回填COALESCE(earlyAccessConfig->>'originalPublishedAt', publishedAt),并让 expiry job 不再改写publishedAt——单靠这一步就独立修复了漫画"重新发布重启抢先体验时钟"的线上 bug,且不需要任何新表。
  2. Part 1(expand,可逆):建PaidAccess表 + 枚举;为当前被门控的版本回填(availability = 'EarlyAccess' AND (earlyAccessEndsAt > now() OR earlyAccessPermanent = true)——注意绝不能按"存在 config blob"回填,否则会把约 2.6 万个已过期版本映射成endsAt = NULL的"永久"行,永久付费墙掉免费内容);所有写路径(tRPC upsert、REST 端点、发布路径、donation-goal 完成与 expiry job 这两个裸 SQL 写入者)双写;旧的"已结束"编码反向映射(ended →now()绝不写NULL);把约 37 处earlyAccessEndsAt读取连同旁边的availability = 'EarlyAccess'条件一起迁到共享助手。
  3. Part 1.5(flip,可逆):把@civitai/buzz/paid-access.ts重实现为读PaidAccess(JSisPaidAccessActive+ SQLEXISTS子查询),主应用读取源翻转,旧列降级为双写备份。spoke 因自带 Kysely 谓词,单独在 Phase B 转换。
  4. Part 2(contract,破坏性,浸泡后执行):停止双写旧列、停止从付费路径写availability='EarlyAccess';删除earlyAccessEndsAt/earlyAccessConfig/earlyAccessPermanent/earlyAccessTimeFrame四列;删除 DB 触发器(endsAt改为服务端从initialPublishedAt物化);expiry job 重定向为仅做缓存/搜索失效。发布门禁:必须先等 spoke 完成读取转换并部署,否则旧列一删,还在读旧列的 spoke 立即崩溃。Availability.EarlyAccess枚举值的移除是另一个独立、带日期的工单(涉及 Postgres 类型重建、事件引擎与 Meilisearch 文档形状、以及一次 reindex)。

快速接手指引

如果你要在当前代码库继续付费访问工作,最有效的入口依次是:

  1. 本文档的主骨架 paid-access-current-state.md(现状 + 待办 + 遗留发现);
  2. 数据形状与terms语义 paid-access-schema.md(§1 表结构、§6 旧字段去向、§7 类型与 zod 边界);
  3. 规则全集 monetization-rules.md(R1–R6 及"每条规则在哪个文件、是否覆盖所有写路径"的清单);
  4. 设计缘起 onsite-monetization-parity.md(为什么要物化endsAt、为什么拒绝'infinity'哨兵、缓存的装饰 vs 谓词二分法);
  5. 代码侧从共享助手 paid-access.ts 与 pricing-allowance.ts 读起,再进入主应用 paid-access.service.ts 与 spoke monetization/paid-access.ts。

牢记三条主线:一行即一门的PaidAccessendsAt物化带来的缓存安全与四态编码双应用共享同一套纯函数规则、分头执行查询的防漂移架构——这三点串起了整个付费访问体系的设计与实现。

【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 8:30:46

C语言位域深度剖析:内存排布、跨平台陷阱与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 8:29:11

视觉语言模型架构解析与多模态大模型工程落地实践

先说说我自己的经历。去年团队接到一个“看图答题”的内部需求&#xff0c;要求在两个月内上线一个能理解截图、给产品运营提供自动标注的模型服务。当时组里有人提出直接用通用大模型API&#xff0c;有人建议拿开源视觉语言模型&#xff08;Vision Language Model&#xff0c;…

作者头像 李华
网站建设 2026/9/18 8:29:10

电动汽车充电负荷的双层优化调度模型与MATLAB实现

1. 项目背景与核心挑战电动汽车规模化接入电网已成为能源转型的关键课题。根据行业预测&#xff0c;到2030年全球电动汽车保有量将突破3亿辆&#xff0c;其充电负荷将占居民用电总量的15%-20%。这种新型负荷具有时空随机性强、功率波动显著的特点&#xff0c;传统电网调度方法面…

作者头像 李华
网站建设 2026/9/18 8:24:53

ROS 2首个Python节点:环境配置、rclpy代码与运行排查

ROS 2 里那个"第一个节点"&#xff0c;代码抄下来也就十几行&#xff0c;可它背后串着环境变量、构建系统、Python 解释器、执行器、DDS 发现机制一整套东西&#xff0c;新手上手翻车的概率其实相当高。我见过太多人colcon build成功、ros2 run敲下去却什么也不打印&…

作者头像 李华
网站建设 2026/9/18 8:24:36

进口编码器停产替代:三条路线与现场实测复盘

上个月一个老朋友打电话过来&#xff0c;说他们产线上的一台进口编码器彻底买不到了&#xff0c;原厂发了停产通知&#xff0c;备件库里最后两只已经被他锁进柜子当宝贝。这种电话我这两年接过不少。编码器这个位置特别尴尬&#xff0c;它不像轴承、密封件那样有大把通用替代&a…

作者头像 李华
网站建设 2026/9/18 8:24:35

通达信指标公式调试与实战重构指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华