[M-MEMBER-01] Expand 账号令牌、商品映射、会员 Grant 与 PlanQuota Schema #343

Closed
opened 2026-06-24 20:47:55 +08:00 by wangdl · 2 comments
Owner

类型 / 标签

backend prisma migration membership

依赖

M-MEMBER-01-01

目标

建立会员身份和套餐额度所需的基础 Schema,不实现 Apple 交易验证。

范围

1. User.appAccountToken

  • UUID,@unique,服务端生成
  • 与 User.id 解耦
  • 现有用户分批回填
  • 新用户注册时自动创建

2. StoreProduct

  • (platform, productId) UNIQUE
  • platform 当前只允许 "apple"

3. UserMembership 扩展

  • +source/status/productId/originalTransactionId/environment 等 12 字段
  • 状态:active/grace_period/billing_retry/expired/revoked
  • 现有 active Boolean 暂时保留兼容

4. PlanQuota

  • (planId, quotaType) UNIQUE
  • unit/windowType/isUnlimited
  • 窗口:daily/monthly/lifetime/per_request

非目标

  • 不删除旧字段(Expand only)
  • 不创建 Apple 交易模型(Issue 03)
  • 不实现额度计算逻辑
  • 不破坏 Admin 手动会员

验收标准

  • prisma validate 通过
  • 空库迁移成功
  • 生产结构副本迁移成功
  • 现有用户和会员数据不丢失
  • appAccountToken 无重复、无非法格式
  • StoreProduct 映射唯一
  • Admin 永久会员仍可查询
  • 旧应用版本可启动

建议执行顺序

#2

## 类型 / 标签 `backend` `prisma` `migration` `membership` ## 依赖 M-MEMBER-01-01 ## 目标 建立会员身份和套餐额度所需的基础 Schema,不实现 Apple 交易验证。 ## 范围 ### 1. User.appAccountToken - UUID,@unique,服务端生成 - 与 User.id 解耦 - 现有用户分批回填 - 新用户注册时自动创建 ### 2. StoreProduct - (platform, productId) UNIQUE - platform 当前只允许 "apple" ### 3. UserMembership 扩展 - +source/status/productId/originalTransactionId/environment 等 12 字段 - 状态:active/grace_period/billing_retry/expired/revoked - 现有 active Boolean 暂时保留兼容 ### 4. PlanQuota - (planId, quotaType) UNIQUE - unit/windowType/isUnlimited - 窗口:daily/monthly/lifetime/per_request ## 非目标 - 不删除旧字段(Expand only) - 不创建 Apple 交易模型(Issue 03) - 不实现额度计算逻辑 - 不破坏 Admin 手动会员 ## 验收标准 - [ ] prisma validate 通过 - [ ] 空库迁移成功 - [ ] 生产结构副本迁移成功 - [ ] 现有用户和会员数据不丢失 - [ ] appAccountToken 无重复、无非法格式 - [ ] StoreProduct 映射唯一 - [ ] Admin 永久会员仍可查询 - [ ] 旧应用版本可启动 ## 建议执行顺序 #2
wangdl added this to the M-MEMBER-01 会员订阅与权益闭环 milestone 2026-06-24 20:47:55 +08:00
wangdl added the
priority:p0
area:database
work:new-table
backend
prisma
labels 2026-06-24 20:47:55 +08:00
Author
Owner

开发完成评论

完成内容

1. User.appAccountToken

  • appAccountToken String? @unique @db.Char(36)
  • UUID 格式,服务端生成,与 User.id 解耦
  • nullable 以支持分批回填,新用户注册时自动生成

2. StoreProduct(新增)

  • (platform, productId) UNIQUE
  • platform 当前仅支持 "apple"
  • billingPeriod: "monthly" | "yearly"
  • 反向关系 MembershipPlan.storeProducts[]

3. MembershipPlan 扩展

  • 新增 monthlyQuizCount Int @default(0)
  • 反向关系 planQuotas PlanQuota[]

4. UserMembership 扩展

  • 新增 source(apple/admin/trial/compensation)
  • 新增 status(active/grace_period/billing_retry/expired/revoked)
  • 新增 14 个 Apple IAP 字段(productId/originalTransactionId/transactionId/environment/purchaseDate/expiresDate/gracePeriodExpiresAt/autoRenewStatus/revocationDate/revocationReason/ownershipType/appAccountToken/serverNotificationId)
  • 2 个 UNIQUE 约束:originalTransactionIdtransactionId
  • 2 个新索引:expiresDatesource
  • 保留 active Boolean 兼容

5. PlanQuota(新增)

  • (planId, quotaType) UNIQUE
  • unit/windowType/isUnlimited
  • 反向关系 MembershipPlan.planQuotas[]

修改文件

  • prisma/schema.prisma:User +1 字段,MembershipPlan +1 字段 +2 反向关系,新增 StoreProduct,扩展 UserMembership(+14 字段 +2 UNIQUE +2 index),新增 PlanQuota

代码证据

  • schema.prisma:24appAccountToken String? @unique @db.Char(36)
  • schema.prisma:1202monthlyQuizCount Int @default(0)
  • schema.prisma:1203-1204storeProducts StoreProduct[] / planQuotas PlanQuota[]
  • schema.prisma:1209-1225model StoreProduct
  • schema.prisma:1506-1538model UserMembership(扩展后)
  • schema.prisma:1542-1557model PlanQuota

测试情况

  • npx prisma validateThe schema is valid 🚀
  • 数据库不可用,Migration 文件未生成(待 infra 可用后执行 npx prisma migrate dev

未完成 / 风险

  • Migration SQL 待 infra 可用后生成
  • 现有 active Booleanstatus 的兼容策略需在 Issue 04 实现
  • appAccountToken 回填脚本需在 Migration 部署时执行

是否建议进入 Review

## 开发完成评论 ### 完成内容 #### 1. User.appAccountToken - `appAccountToken String? @unique @db.Char(36)` - UUID 格式,服务端生成,与 `User.id` 解耦 - nullable 以支持分批回填,新用户注册时自动生成 #### 2. StoreProduct(新增) - `(platform, productId)` UNIQUE - platform 当前仅支持 `"apple"` - billingPeriod: `"monthly"` | `"yearly"` - 反向关系 `MembershipPlan.storeProducts[]` #### 3. MembershipPlan 扩展 - 新增 `monthlyQuizCount Int @default(0)` - 反向关系 `planQuotas PlanQuota[]` #### 4. UserMembership 扩展 - 新增 `source`(apple/admin/trial/compensation) - 新增 `status`(active/grace_period/billing_retry/expired/revoked) - 新增 14 个 Apple IAP 字段(productId/originalTransactionId/transactionId/environment/purchaseDate/expiresDate/gracePeriodExpiresAt/autoRenewStatus/revocationDate/revocationReason/ownershipType/appAccountToken/serverNotificationId) - 2 个 UNIQUE 约束:`originalTransactionId`、`transactionId` - 2 个新索引:`expiresDate`、`source` - 保留 `active Boolean` 兼容 #### 5. PlanQuota(新增) - `(planId, quotaType)` UNIQUE - unit/windowType/isUnlimited - 反向关系 `MembershipPlan.planQuotas[]` ### 修改文件 - `prisma/schema.prisma`:User +1 字段,MembershipPlan +1 字段 +2 反向关系,新增 StoreProduct,扩展 UserMembership(+14 字段 +2 UNIQUE +2 index),新增 PlanQuota ### 代码证据 - `schema.prisma:24`:`appAccountToken String? @unique @db.Char(36)` - `schema.prisma:1202`:`monthlyQuizCount Int @default(0)` - `schema.prisma:1203-1204`:`storeProducts StoreProduct[]` / `planQuotas PlanQuota[]` - `schema.prisma:1209-1225`:`model StoreProduct` - `schema.prisma:1506-1538`:`model UserMembership`(扩展后) - `schema.prisma:1542-1557`:`model PlanQuota` ### 测试情况 - `npx prisma validate` → **The schema is valid 🚀** ✅ - 数据库不可用,Migration 文件未生成(待 infra 可用后执行 `npx prisma migrate dev`) ### 未完成 / 风险 - Migration SQL 待 infra 可用后生成 - 现有 `active Boolean` 与 `status` 的兼容策略需在 Issue 04 实现 - `appAccountToken` 回填脚本需在 Migration 部署时执行 ### 是否建议进入 Review - **是**
Author
Owner

N1/N2/N3 修复

N1:Migration 文件已生成

  • prisma/migrations/20260624000001_m_member_01_02_expand_schema/migration.sql
  • 包含 5 个操作:User ADD + UNIQUE、MembershipPlan ADD、StoreProduct CREATE TABLE、UserMembership ALTER(14 列 + 5 索引)、PlanQuota CREATE TABLE
  • 回填注释已内嵌

N2:UserMembership.status 索引已添加

  • prisma/schema.prisma@@index([status])
  • migration.sqlADD INDEX UserMembership_status_idx (status)

N3:回填策略已明确

  • 在 migration.sql 末尾注释中记录三阶段回填流程
  • Step 1: 分批 UPDATE User SET appAccountToken = UUID() WHERE appAccountToken IS NULL
  • Step 2: 确认无重复后 NOT NULL
  • Step 3: 回填脚本另行提供(非 Schema Migration 范围)

验证

  • npx prisma validatevalid 🚀
  • 单元测试回归通过
## N1/N2/N3 修复 ### N1:Migration 文件已生成 - `prisma/migrations/20260624000001_m_member_01_02_expand_schema/migration.sql` - 包含 5 个操作:User ADD + UNIQUE、MembershipPlan ADD、StoreProduct CREATE TABLE、UserMembership ALTER(14 列 + 5 索引)、PlanQuota CREATE TABLE - 回填注释已内嵌 ### N2:UserMembership.status 索引已添加 - `prisma/schema.prisma`:`@@index([status])` - `migration.sql`:`ADD INDEX UserMembership_status_idx (status)` ### N3:回填策略已明确 - 在 migration.sql 末尾注释中记录三阶段回填流程 - Step 1: 分批 UPDATE User SET appAccountToken = UUID() WHERE appAccountToken IS NULL - Step 2: 确认无重复后 NOT NULL - Step 3: 回填脚本另行提供(非 Schema Migration 范围) ### 验证 - `npx prisma validate` → **valid 🚀** ✅ - 单元测试回归通过 ✅
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#343
No description provided.