跳到内容

权益 / 套餐 / 配额

受众:开发者 / 运维 摘要:Entitlement 模块提供中性的套餐、权益项、配额策略、用户权益、余额和流水底座,供 Open Platform 与后续业务消费方复用。

spring-open-core-entitlement 是平台化权益底座。它不绑定 Open Platform、AI、会员、商城或阅读等单一业务语义,而是用 consumerCodeitemCode、业务对象和幂等键隔离不同消费方的套餐与配额。

本模块已按 P1 平台化底座推进,不再把额外消费方作为启动前置。实现仍保留中性命名和消费方隔离字段,后续 Open Platform 旧套餐 / 配额能力迁移时只作为其中一个消费方接入。

当前范围

当前版本已冻结数据模型、表结构、基础 contract、用户权益生命周期状态机和配额消耗 contract:

能力说明
套餐目录entitlement_package 保存套餐编码、名称、状态、版本和描述
权益项entitlement_item 保存可消费权益,按 consumer_code + item_code 唯一隔离
套餐权益绑定entitlement_package_item 关联套餐与权益项,并承载额度、周期和重置策略
配额策略entitlement_quota_policy 保存权益项的默认额度、周期、重置和透支策略
用户权益entitlement_user_entitlement 保存用户、消费方、来源套餐、状态和有效期
配额余额entitlement_quota_balance 保存用户在某个权益项上的总额、已用、冻结和剩余额度
配额流水entitlement_quota_ledger 保存检查、消耗、释放、退款等幂等流水的中性载体
内容访问策略ContentAccessPolicyService 统一解释免费、登录可见、限免、作者直读、直接购买解锁和权益凭证匹配
内容解锁凭证ContentUnlockCredentialServiceconsumerCode + ownerType + ownerId + itemCode 查询 active 用户权益,支持按 packageCode 或套餐内启用 itemCode 解锁付费 / 会员内容

生命周期状态机

用户权益状态由后端统一流转,前端只展示返回的 statusstatusLabelavailableeffectiveAtexpiresAtfrozenAt

状态展示值可用性说明
active生效中effectiveAt 已到且 expiresAt 未到时可用购买成功或过期后续费恢复
frozen已冻结不可用运维冻结,保留有效期和来源信息
expired已到期不可用读取或操作时发现 expiresAt <= now 会自动落库为到期
cancelled已取消不可用运维取消后的终态,不允许续费恢复

购买支持 durationDays 或显式 expiresAtdurationDays 会从 effectiveAt 开始计算。续费支持 durationDays 或显式 expiresAt,按当前有效期叠加,不允许缩短未到期权益。带 sourceType + sourceId 的购买会按来源幂等返回已有权益,带相同来源的续费重复请求不会再次延长。

配额消耗 Contract

配额操作统一按 consumerCode + userEntitlementId + itemCode + quotaScope + quotaPeriod 定位余额。服务会校验用户权益仍为可用状态、权益所属消费方与请求消费方一致、权益项已绑定到套餐、配额策略已启用,并在缺少余额记录时按策略懒创建余额。

操作语义结果
check检查余额是否足够,不扣减返回 allowedfailureReason 和当前余额;带幂等键时写入检查流水
consume扣减可用余额余额不足抛出业务错误;成功后增加已用额度、减少剩余额度并写入消耗流水
release释放预占 / 已用额度从已用额度回补剩余额度,不允许回补超过已用额度
refund退款或冲正回补额度与 release 一样回补已用额度,并用独立流水方向保留审计语义

idempotencyKey 用于同一方向操作的幂等保护。重复请求会返回已有流水和余额;同一个幂等键跨消费方或跨操作方向复用会被拒绝。

内容访问与解锁凭证

ContentAccessPolicyService 是面向 Blog、Forum、Reading、Video 等内容模块的中性内容访问策略入口。调用方传入消费方、用户、内容作者、访问类型、权益项编码、直接购买解锁标记、限免时间以及可选会员折扣权益项,服务统一返回是否可读、命中原因、可选权益凭证和可选会员折扣资格。

当前统一访问类型包括:

  • free:公开可读。
  • login:登录后可读。
  • limited-free:限免期内可读。
  • paid / points / vip / member:可通过直接购买 / 积分扣减 / 模块自有订单先行解锁,也可通过 Entitlement 内容解锁凭证兜底解锁。

ContentUnlockCredentialService 负责底层凭证查询。调用方传入消费方、归属用户和内容需要的权益项编码,服务只返回当前仍处于 active、已生效且未过期的用户权益凭证。

paidpoints 内容的完整解锁凭证未命中时,调用方可以额外传入 memberDiscountItemCodememberDiscountRate。如果当前用户命中该会员折扣权益项,决策会返回 readable=falsereason=needs-payment-with-member-discountdiscountEligible=truediscountRate 和折扣凭证摘要,表示内容仍需购买 / 积分解锁,但购买链路可以使用会员折扣。vip / member 访问类型仍要求完整会员权益,不会因为折扣资格被误判为可读。

匹配规则:

  • itemCode 可直接匹配用户权益的 packageCode,用于“整包会员”解锁。
  • itemCode 也可匹配该套餐内启用的 entitlement_item.code,用于“套餐内某个内容权益项”解锁。
  • 冻结、过期、取消、未生效或套餐项禁用的权益不会返回凭证。

Blog、Forum、Reading 和 Video 的内容读侧已通过 ContentAccessPolicyService 复用该服务:Blog 使用 consumerCode=blogownerType=user,文章 vipItemCode 可命中 Entitlement 套餐编码或套餐项编码,member / vip 都走同一会员权益凭证;Blog 付费文章还会把保存请求里的 memberDiscountItemCode / memberDiscountRate 写入文章扩展元数据,详情 VO 返回折扣资格字段并由 PC / App 锁定态展示“可享会员价”提示;Forum 使用 consumerCode=forumownerType=user,默认 forum-paid-topic / forum-vip-topic 解锁付费 / 会员主题;Reading 使用 reading-paid-chapter / reading-vip-chapter,其中 points 章节先走 Money 积分扣减直接解锁;Video 使用 video-paid / video-vipmember 内容流同样复用会员凭证。业务模块仍保留自己的购买订单、积分扣减、支付回调和退款失效状态机,只把作者直读、直接解锁、限免、权益凭证匹配和折扣资格决策统一交给 ContentAccessPolicyService,后续新增内容付费消费方不再各自复制免费 / 登录 / 作者 / 限免 / 权益匹配逻辑。

Admin 侧提供内容访问审计入口,用于复现某个用户对某个内容的当前可读性决策。运维可传入 consumerCodeownerTypeuserIdcontentOwnerIdaccessTypeitemCodememberDiscountItemCodememberDiscountRatedirectUnlocklimitedFreeUntilbusinessTypebusinessId,接口返回 readablereasoncredentialMatched、命中的权益凭证信息、discountEligible、折扣率和折扣凭证摘要。退款撤销或订单失效后,业务模块应把 directUnlock=false 传入该审计入口,确认是否仍能通过 Entitlement 凭证兜底解锁;需要核对会员价时,再传入折扣权益项和折扣率复现用户是否具备折扣资格。

Open Platform 侧提供只读内容访问检查 API,用于第三方服务端在同一 HMAC、scope、限流、调用日志、用量和账单治理边界内复用该策略。接口不创建解锁凭证、不写购买订单、不处理退款撤销,只返回和 Admin 审计一致的当前访问决策;业务侧购买、积分扣减、支付回调和退款状态仍由 Blog / Forum / Reading / Video 等 owning module 处理。

管理端 Contract

当前 Controller 位于 /api/v1/admin/entitlement,用于 Admin 运维入口的第一阶段接入。

方法路径权限说明
POST/api/v1/admin/entitlement/packagesentitlement:package:create创建或更新套餐基础资料
GET/api/v1/admin/entitlement/packages/{id}entitlement:package:view查询套餐详情与已绑定权益项
POST/api/v1/admin/entitlement/itemsentitlement:item:create创建或更新权益项
POST/api/v1/admin/entitlement/packages/{id}/itemsentitlement:package:update将权益项绑定到套餐
POST/api/v1/admin/entitlement/packages/{id}/items/{itemId}/quota-policiesentitlement:quota-policy:update保存套餐项配额策略
GET/api/v1/admin/entitlement/user-entitlementsentitlement:user:view按用户与消费方查询用户权益
POST/api/v1/admin/entitlement/user-entitlements/purchasesentitlement:user:purchase给指定 owner 购买并生成用户权益
GET/api/v1/admin/entitlement/purchases/auditentitlement:user:view按权益 ID、消费方、归属、套餐、状态或 source 查询购买 / 续费排障信息
POST/api/v1/admin/entitlement/user-entitlements/{id}/renewalsentitlement:user:renew按天数或到期时间续费用户权益
POST/api/v1/admin/entitlement/user-entitlements/{id}/freezeentitlement:user:freeze冻结用户权益
POST/api/v1/admin/entitlement/user-entitlements/{id}/unfreezeentitlement:user:freeze解冻用户权益
POST/api/v1/admin/entitlement/user-entitlements/{id}/cancelentitlement:user:cancel取消用户权益
POST/api/v1/admin/entitlement/user-entitlements/{id}/expireentitlement:user:expire手动标记用户权益到期
POST/api/v1/admin/entitlement/quotas/checkentitlement:quota:check检查权益配额余额
POST/api/v1/admin/entitlement/quotas/consumeentitlement:quota:consume扣减权益配额
POST/api/v1/admin/entitlement/quotas/releaseentitlement:quota:release释放权益配额
POST/api/v1/admin/entitlement/quotas/refundentitlement:quota:refund退款或冲正回补权益配额
GET/api/v1/admin/entitlement/quotas/ledgersentitlement:user:view分页查询权益配额流水,支持消费方、用户权益、权益项、余额、方向、业务信息、幂等键和时间范围筛选
GET/api/v1/admin/entitlement/content-access/auditentitlement:user:view复现 Blog / Forum / Reading / Video 等内容的访问策略决策,返回可读性、命中原因和权益凭证

Admin 页面入口为 /entitlement/entitlements。页面已接入“套餐配置”页签,可按“创建套餐 -> 创建权益项 -> 绑定套餐项 -> 保存配额策略”的顺序完成中性目录配置;创建套餐和权益项成功后会把返回 ID 自动回填到绑定与配额策略表单,也可直接按套餐 ID 读取详情和已绑定权益项确认配置结果。“用户权益”页签支持按消费方、归属和状态查询,并可对非终态权益执行冻结、解冻、取消和手动过期;“购买排障”页签支持直接给指定归属对象购买套餐,购买成功后会带入返回权益和 source 自动执行审计,也可按权益 ID、消费方、归属、套餐、状态或 sourceType/sourceId 查询购买 / 续费来源、命中权益、最近配额流水和 release / refund 类回补流水数量,用于排查套餐购买、续费幂等、退款撤销后权益凭证是否仍命中;“配额操作”页签支持检查、消耗、释放和退回权益配额,写操作要求填写幂等键,执行后展示余额和流水信息;“配额流水”页签支持按消费方、用户权益、权益项、余额、方向、业务类型 / ID、幂等键和发生时间分页检索历史流水;“内容访问审计”页签支持复现内容访问决策,便于排查退款撤销后仍可读或权益凭证未命中的问题。

Open Platform 内容访问 API

方法路径Scope说明
POST/api/v1/open/entitlement/content-access/checkopen:entitlement:content-access:readContentAccessPolicyService 复现内容访问决策,返回可读性、命中原因、权益凭证和会员折扣资格

请求体字段与 Admin 内容访问审计一致,常用字段包括 consumerCodeownerTypeuserIdcontentOwnerIdaccessTypeitemCodememberDiscountItemCodememberDiscountRatedirectUnlocklimitedFreeUntilbusinessTypebusinessId。调用方必须先在 Open Platform 后台为应用授权 open:entitlement:content-access:read,并按开放平台 HMAC 规则签名。成功调用会进入 Open Platform 调用日志、用量统计和账单治理;接口本身只读,不绕过内容业务自己的购买、支付、退款和积分扣减状态机。

用户侧只读 Contract

当前用户只读入口用于 App / PC 用户中心展示账号维度的权益套餐、配额余额、到期时间和最近消耗流水,不允许用户传入 ownerTypeownerId 或内部状态字段。

方法路径鉴权说明
GET/api/v1/entitlement/me/entitlements当前登录用户查询当前用户 ownerType=userownerId=当前用户 ID 的权益概览

响应字段:

  • entitlementCount / availableCount / quotaBalanceCount:当前账号权益、可用权益和配额余额项数量。
  • totalQuota / usedQuota / remainingQuota:当前返回配额余额的汇总额度。
  • entitlements:用户权益列表,包含 consumerCodepackageCodestatusstatusLabelavailableeffectiveAtexpiresAt
  • quotaBalances:配额余额列表,按用户权益、权益项、scope / period 展示总额、已用、剩余和重置时间。
  • recentLedgers:最近配额流水,支持 ledgerLimit 查询参数,后端限制最大返回数量。

视图入口:

  • PC 用户中心:/account/entitlements,并从 /account 常用服务入口进入。
  • App 会员中心:/member/index 的权益卡片和概览指标展示当前用户权益和剩余额度。

后续收口

当前 P1 首段已完成中性模型、状态机、配额消耗、Open Platform 迁移适配、Admin 套餐配置、直接购买、购买 / 续费来源审计、用户权益生命周期运维、配额操作入口、独立配额流水检索页、App / PC 用户侧只读入口、内容访问策略、会员折扣元数据、Blog 消费端折扣提示、Admin 内容访问审计和 Open Platform 内容访问检查 API。后续积分解锁策略验证、更多消费方三端展示或复杂营销规则时,应按独立功能簇补充 contract、视图、测试和文档。

Released under the Apache License 2.0.