@windrun-huaiin/diaomao 2.3.6 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1979 @@
1
+ ---
2
+ title: 订阅与积分系统产品设计文档 v1.0
3
+ description: 付费订阅系统通用设计
4
+ date: 2025-07-31
5
+ ---
6
+
7
+ ## 概述
8
+
9
+ 本文档详细描述了一个与Stripe支付处理集成的订阅与积分系统的全面设计。该系统旨在为用户提供无缝的订阅服务、积分管理和支付体验, 确保可扩展性、安全性和可维护性。设计涵盖了用户操作、数据模型、数据流、用户场景和系统时序, 确保清晰度、专业性和鲁棒性。
10
+
11
+ ### 1.1 系统目标
12
+
13
+ - 使用户能够通过Stripe订阅计划或购买一次性积分包
14
+ - 提供用户友好的界面, 用于管理订阅和查看积分余额
15
+ - 使用Fingerprint确保用户(包括匿名用户)的安全识别
16
+ - 支持灵活的订阅管理(自动续费、取消、升级、附加包)
17
+ - 维护详细的交易和积分使用历史记录
18
+ - 设计可扩展的数据模型和清晰的数据流, 确保可靠性和性能
19
+ - 支持匿名用户到注册用户的完整生命周期管理
20
+
21
+ ---
22
+
23
+ ## 2 用户场景与生命周期
24
+
25
+ ### 2.1. 匿名用户与注册用户场景及数据流程设计
26
+
27
+ #### 2.1.1 用户场景分析
28
+
29
+ 以下场景涵盖了匿名用户和注册用户的完整生命周期, 重点关注从匿名用户到注册用户、注销后再次成为匿名用户, 以及再次注册的流程。
30
+
31
+ ##### 场景 1: 匿名用户初次访问
32
+ - **描述**: 用户首次访问平台, 未注册, 系统通过 Fingerprint 识别其设备。
33
+ - **流程**:
34
+ 1. 用户访问平台, 系统通过 Fingerprint 生成唯一的 `fingerprint_id`。
35
+ 2. 系统在 `Users` 表中创建一条记录, 生成唯一的 `user_id`, `email` 字段为空, `fingerprint_id` 记录设备标识。
36
+ 3. 系统分配 50 个免费积分, 更新 `Credits` 表(`user_id` 关联, `balance_free = 50`, `total_free_limit = 50`)。
37
+ 4. 在 `Credit_Usage` 表中插入记录, `operation_type` 为 `recharge`, `credit_type` 为 `free`。
38
+ 5. 用户使用免费积分访问功能, 系统记录在 `Credit_Usage` 表(`operation_type` 为 `consume`, `credit_type` 为 `free`)。
39
+ 6. 如果积分耗尽, 提示用户注册或购买积分。
40
+ - **结果**: 匿名用户获得 `user_id` 和有限的免费积分, 数据已与 `fingerprint_id` 关联。
41
+
42
+ ##### 场景 2: 匿名用户注册
43
+ - **描述**: 匿名用户决定注册为正式用户。
44
+ - **流程**:
45
+ 1. 用户提交注册信息(电子邮件、密码或其他 SSO 方法)。
46
+ 2. 系统验证 `fingerprint_id`, 找到对应的 `user_id`。
47
+ 3. 更新 `Users` 表, 将 `email` 和其他注册信息(如密码哈希)写入已有记录。
48
+ 4. 用户可选择订阅计划或购买积分, 触发 `Subscriptions` 或 `Transactions` 表更新。
49
+ - **结果**: 匿名用户转换为注册用户, 保留原有 `user_id`, 数据连续性得以保持。
50
+
51
+ ##### 场景 3: 注册用户登录
52
+ - **描述**: 注册用户通过电子邮件和密码或 SSO 登录。
53
+ - **流程**:
54
+ 1. 用户提交登录凭证(电子邮件/密码或 SSO 令牌)。
55
+ 2. 系统验证凭证, 查找 `Users` 表中匹配的 `user_id` 和 `email`。
56
+ 3. 如果设备不同, 系统可能更新 `fingerprint_id`(或记录多设备登录)。
57
+ 4. 系统返回用户数据(如积分余额、订阅状态), 从 `Credits` 和 `Subscriptions` 表查询。
58
+ - **结果**: 用户获得完整功能访问权限, 界面显示其积分和订阅信息。
59
+
60
+ ##### 场景 4: 用户注销(删除账户)
61
+ - **描述**: 注册用户选择删除账户, 恢复匿名状态。
62
+ - **流程**:
63
+ 1. 用户通过界面发起账户删除请求。
64
+ 2. 系统验证用户身份(可能需要密码或 SSO 验证)。
65
+ 3. 系统标记 `Users` 表中的记录为已删除(软删除, 设置 `email = NULL`, 保留 `user_id` 和 `fingerprint_id`), 或完全删除记录(硬删除, 视 GDPR 要求)。
66
+ 4. 关联数据处理:
67
+ - 删除或归档 `Subscriptions`、`Credits`、`Transactions` 和 `Credit_Usage` 表中的记录。
68
+ - 如果保留 `user_id`, 可在 `Credits` 表中分配新的免费积分。
69
+ 5. 系统为用户生成新的 `fingerprint_id`(如适用), 重新作为匿名用户。
70
+ - **结果**: 用户恢复匿名状态, 数据根据策略保留或删除。
71
+
72
+ ##### 场景 5: 匿名用户再次注册
73
+ - **描述**: 注销后的匿名用户再次注册。
74
+ - **流程**:
75
+ 1. 用户访问平台, 系统识别 `fingerprint_id`。
76
+ 2. 如果 `user_id` 保留(软删除), 系统可复用原有 `user_id` 并更新 `email`。
77
+ 3. 如果 `user_id` 已删除(硬删除), 系统生成新的 `user_id` 和 `fingerprint_id`。
78
+ 4. 用户提交注册信息, 流程同场景 2。
79
+ - **结果**: 用户重新成为注册用户, 数据连续性取决于删除策略(软删除或硬删除)。
80
+
81
+ #### 2.1.2 匿名用户场景
82
+
83
+ 匿名用户通过Fingerprint识别, 防止滥用(例如, 过度使用免费积分)。他们对功能访问受限, 但可在注册前体验系统。
84
+
85
+ - **场景1: 使用免费积分探索功能**
86
+ - **描述**: 匿名用户访问平台, 获得免费积分, 使用基本功能。
87
+ - **步骤**:
88
+ 1. 用户访问平台, 系统分配Fingerprint ID。
89
+ 2. 系统分配50个免费积分(通过Fingerprint限制, 防止滥用)。
90
+ 3. 用户尝试使用功能(例如, API调用, 消耗10积分)。
91
+ 4. 系统从免费余额中扣除积分并记录使用情况。
92
+ 5. 如果积分耗尽, 提示用户注册或购买积分。
93
+ - **结果**: 用户体验平台, 但被鼓励注册以获得完整访问权限。
94
+
95
+ - **场景2: 尝试订阅**
96
+ - **描述**: 匿名用户尝试订阅付费计划, 但需先注册。
97
+ - **步骤**:
98
+ 1. 用户选择计划(例如, 专业版计划)。
99
+ 2. 系统提示用户注册或登录。
100
+ 3. 用户完成注册, 将Fingerprint ID关联到`user_id`。
101
+ 4. 用户被重定向到Stripe的结账页面完成支付。
102
+ 5. 支付成功后, 系统分配积分并更新订阅状态。
103
+ - **结果**: 用户成为注册用户, 拥有活跃订阅。
104
+
105
+ #### 2.1.3 登录用户场景
106
+
107
+ 登录用户拥有对所有功能的完整访问权限, 包括订阅管理、积分购买和历史记录跟踪。
108
+
109
+ - **场景1: 订阅计划**
110
+ - **描述**: 登录用户订阅月度计划。
111
+ - **步骤**:
112
+ 1. 用户导航到订阅管理界面。
113
+ 2. 用户选择专业版计划(¥140/月, 250积分)。
114
+ 3. 系统创建Stripe Checkout Session并将用户重定向到Stripe。
115
+ 4. 用户完成支付; Stripe向后端发送Webhook。
116
+ 5. 系统更新订阅状态, 分配250积分并记录交易。
117
+ - **结果**: 用户获得高级功能访问权限和每月积分。
118
+
119
+ - **场景2: 升级订阅**
120
+ - **描述**: 用户从基础版升级到专业版计划。
121
+ - **步骤**:
122
+ 1. 用户在订阅界面选择专业版计划。
123
+ 2. 系统通过Stripe计算按比例分配费用并创建新Checkout Session。
124
+ 3. 用户完成按比例分配的支付。
125
+ 4. Stripe发送Webhook; 系统将订阅更新为专业版并添加额外积分。
126
+ - **结果**: 用户享受增强的功能和增加的积分分配。
127
+
128
+ - **场景3: 购买一次性积分**
129
+ - **描述**: 用户购买附加积分包。
130
+ - **步骤**:
131
+ 1. 用户选择100积分附加包(¥35)。
132
+ 2. 系统创建一次性Stripe Checkout Session。
133
+ 3. 用户完成支付; Stripe发送Webhook。
134
+ 4. 系统将100个付费积分添加到用户余额并记录交易。
135
+ - **结果**: 用户获得额外的即时使用积分。
136
+
137
+ - **场景4: 取消订阅**
138
+ - **描述**: 用户取消订阅。
139
+ - **步骤**:
140
+ 1. 用户在界面点击"取消订阅"。
141
+ 2. 系统向Stripe发送取消请求。
142
+ 3. Stripe确认取消; 系统更新订阅状态为"已取消"。
143
+ 4. 用户在计费周期结束前保留访问权限。
144
+ - **结果**: 用户的订阅终止, 不再产生费用。
145
+
146
+ - **场景5: 查看历史记录**
147
+ - **描述**: 用户查看交易和积分使用历史。
148
+ - **步骤**:
149
+ 1. 用户导航到历史记录选项卡。
150
+ 2. 系统获取交易记录(例如, 支付、退款)和积分使用日志。
151
+ 3. 界面显示包含时间戳、金额和使用功能的数据表。
152
+ - **结果**: 用户获得账户活动的透明度。
153
+
154
+ ### 2.2. 用户操作场景
155
+
156
+ #### 2.2.1 用户注册与识别
157
+ - **匿名用户**: 通过Fingerprint(基于设备的标识符)识别, 防止滥用(例如, 过度使用免费积分)。
158
+ - **注册用户**: 用户可通过电子邮件和密码注册, 或使用SSO(例如Google、Apple)。注册用户分配唯一的`user_id`。
159
+ - **场景**:
160
+ - 新用户访问平台, 系统分配Fingerprint ID。
161
+ - 用户无需注册即可使用有限的免费积分。
162
+ - 要访问付费功能, 用户必须注册或登录, 将Fingerprint ID关联到`user_id`。
163
+
164
+ #### 2.2.2 订阅管理
165
+ - **订阅计划**: 用户可选择多种计划(例如, 基础版、专业版、企业版), 具有不同的积分分配和定价。
166
+ - **操作**:
167
+ - **订阅**: 用户选择计划并被重定向到Stripe的结账页面进行支付。
168
+ - **自动续费**: 订阅默认自动续费, 除非用户取消。
169
+ - **升级/降级**: 用户可切换计划, Stripe处理按比例分配费用。
170
+ - **取消**: 用户可取消订阅, 取消在计费周期结束时生效。
171
+ - **附加包**: 用户可购买额外的积分包, 而无需更改计划。
172
+ - **场景**:
173
+ - 用户选择专业版计划, 通过Stripe完成支付, 获得每月积分。
174
+ - 在计费周期中, 用户购买额外的积分包以增加使用量。
175
+ - 用户随后升级到企业版计划, Stripe处理按比例分配费用。
176
+
177
+ #### 2.2.3 积分使用
178
+ - **积分系统**: 积分用于访问高级功能(例如, API调用、高级工具)。
179
+ - **免费积分与付费积分**:
180
+ - 新用户获得免费积分(通过Fingerprint限制, 防止滥用)。
181
+ - 付费积分根据订阅计划或一次性购买分配。
182
+ - **场景**:
183
+ - 用户为API请求消耗积分。
184
+ - 系统从用户余额中扣除积分, 优先使用付费积分。
185
+ - 如果积分耗尽, 提示用户购买更多积分或升级计划。
186
+
187
+ #### 2.2.4 历史记录与报告
188
+ - **交易历史**: 用户可查看过去的支付记录, 包括发票和收据。
189
+ - **积分使用历史**: 用户可查看积分使用的详细日志(例如, 使用功能、时间戳、消耗积分)。
190
+ - **场景**:
191
+ - 用户导航到历史页面, 查看过去三个月的积分使用和支付记录。
192
+
193
+ #### 2.2.5 退款与争议
194
+ - **退款流程**: 用户可为符合条件的交易(例如, 7天内)请求退款。
195
+ - **场景**:
196
+ - 用户为一次性积分购买请求退款。系统通过Stripe处理退款并更新积分余额。
197
+
198
+ ---
199
+
200
+ ## 3 数据模型设计
201
+
202
+ ### 3.1. 核心数据表
203
+
204
+ #### 用户表 (Users)
205
+ 存储用户信息, 包括通过Fingerprint识别的匿名用户和Clerk注册用户。
206
+
207
+ | 列名 | 类型 | 描述 |
208
+ |---------------------|--------------|----------------------------------------|
209
+ | `id` | BigInt | 主键 |
210
+ | `user_id` | UUID | 用户ID, 唯一用户标识符, 用于关联其他表 |
211
+ | `fingerprint_id` | String | 匿名用户的Fingerprint标识符 |
212
+ | `clerk_user_id` | String | Clerk用户ID, 注册用户必填, 匿名用户为空 |
213
+ | `email` | String | 用户电子邮件(注册用户必填, 匿名用户为空) |
214
+ | `status` | Enum | 状态: anonymous、registered、frozen、deleted等 |
215
+ | `created_at` | Timestamp | 账户创建时间戳 |
216
+ | `updated_at` | Timestamp | 最后更新时间戳 |
217
+
218
+ #### 订阅表 (Subscriptions)
219
+ 跟踪活跃订阅及其详细信息。
220
+
221
+ | 列名 | 类型 | 描述 |
222
+ |---------------------|--------------|----------------------------------------|
223
+ | `id` | BigInt | 主键 |
224
+ | `subscription_id` | UUID | 订阅ID, 唯一订阅ID |
225
+ | `user_id` | UUID | 外键, 引用`Users`表 |
226
+ | `stripe_subscription_id` | String | Stripe订阅ID (sub_xxx) |
227
+ | `price_id` | String | Stripe价格ID (price_xxx) |
228
+ | `price_name` | String | 价格名称(例如, Basic、Pro) |
229
+ | `status` | Enum | 状态: 活跃、已取消、逾期等 |
230
+ | `credits_allocated` | Integer | 每个计费周期分配的积分 |
231
+ | `sub_period_start` | Timestamp | 订阅周期开始时间戳 |
232
+ | `sub_period_end` | Timestamp | 订阅周期结束时间戳 |
233
+ | `created_at` | Timestamp | 记录创建时间戳 |
234
+ | `updated_at` | Timestamp | 最后更新时间戳 |
235
+
236
+ #### 积分表 (Credits)
237
+ 管理用户积分余额。
238
+
239
+ | 列名 | 类型 | 描述 |
240
+ |---------------------|--------------|----------------------------------------|
241
+ | `id` | BigInt | 主键, 唯一积分记录ID |
242
+ | `user_id` | UUID | 外键, 引用`Users`表 |
243
+ | `balance_free` | Integer | 免费积分余额 |
244
+ | `total_free_limit` | Integer | 免费积分总量 |
245
+ | `balance_paid` | Integer | 付费积分余额 |
246
+ | `total_paid_limit` | Integer | 付费积分总量 |
247
+ | `created_at` | Timestamp | 记录创建时间戳 |
248
+ | `updated_at` | Timestamp | 最后更新时间戳 |
249
+
250
+ #### 订单交易表 (Transactions)
251
+ 记录支付交易, 包括订阅和一次性购买。
252
+
253
+ | 列名 | 类型 | 描述 |
254
+ |---------------------|--------------|----------------------------------------|
255
+ | `id` | BigInt | 主键, 唯一交易ID |
256
+ | `user_id` | UUID | 外键, 引用`Users`表 |
257
+ | `order_id` | String | 订单ID, 唯一订单标识符 |
258
+ | `order_status` | Enum | 状态: 已创建created、已支付success、已退款refunded、已取消canceled、失败failed |
259
+ | `order_created_at` | Timestamp | 订单创建时间戳 |
260
+ | `order_expired_at` | Timestamp | 订单过期时间戳 |
261
+ | `order_updated_at` | Timestamp | 订单最后更新时间戳 |
262
+ | `stripe_transaction_id` | String | Stripe交易ID, 唯一交易标识符 |
263
+ | `stripe_subscription_id` | String | Stripe订阅ID (sub_xxx) |
264
+ | `stripe_session_id` | String | Stripe Checkout Session ID (cs_xxx) |
265
+ | `stripe_invoice_id` | String | Stripe发票ID (in_xxx) |
266
+ | `price_id` | String | Stripe价格ID (price_xxx) |
267
+ | `price_name` | String | 价格名称(例如, Basic、Pro) |
268
+ | `sub_interval_count`| Integer | 订阅间隔计数(例如, 1、3、6、12个月) |
269
+ | `sub_cycle_anchor` | Timestamp | 订阅周期锚点(例如, start_of_period) |
270
+ | `amount` | DECIMAL(10,2) | 支付金额 |
271
+ | `currency` | String | 货币代码(例如, USD、CNY) |
272
+ | `type` | Enum | 类型: 订阅、一次性 |
273
+ | `credits_granted` | Integer | 此交易授予的积分 |
274
+ | `sub_period_start` | Timestamp | 订阅周期开始时间戳 |
275
+ | `sub_period_end` | Timestamp | 订阅周期结束时间戳 |
276
+ | `order_detail` | String | 订单详情(例如, 订阅、一次性购买) |
277
+ | `paid_at` | Timestamp | 支付时间戳 |
278
+ | `paid_email` | String | 支付邮箱 |
279
+ | `paid_detail` | String | 支付详情(例如, 订阅、一次性购买) |
280
+ | `stripe_created_at` | Timestamp | Stripe交易创建时间戳 |
281
+ | `stripe_updated_at` | Timestamp | Stripe交易最后更新时间戳 |
282
+
283
+ #### 积分使用表 (Credit Usage)
284
+ 跟踪积分的消耗情况。
285
+
286
+ | 列名 | 类型 | 描述 |
287
+ |---------------------|--------------|----------------------------------------|
288
+ | `id` | BigInt | 主键, 唯一使用记录ID |
289
+ | `user_id` | UUID | 外键, 引用`Users`表 |
290
+ | `feature` | String | 使用的功能(例如, API调用、工具) |
291
+ | `order_id` | String | 订单ID, 订单标识符可为null |
292
+ | `credit_type` | Enum | 积分类型: free、paid |
293
+ | `operation_type` | Enum | 操作类型: consume、recharge、freeze、unfreeze |
294
+ | `credits_used` | Integer | 消耗的积分数量 |
295
+ | `created_at` | Timestamp | 使用时间戳 |
296
+
297
+ #### 用户备份表 (UserBackup)
298
+ 存储用户注销时的备份数据, 用于数据恢复和审计。
299
+
300
+ | 列名 | 类型 | 描述 |
301
+ |---------------------|--------------|----------------------------------------|
302
+ | `id` | BigInt | 主键, 唯一备份记录ID |
303
+ | `original_user_id` | UUID | 原始用户ID |
304
+ | `fingerprint_id` | String | 设备指纹ID |
305
+ | `clerk_user_id` | String | Clerk用户ID |
306
+ | `email` | String | 用户邮箱 |
307
+ | `status` | String | 用户状态 |
308
+ | `backup_data` | JSON | 完整的用户数据备份(包括积分、订阅等) |
309
+ | `deleted_at` | Timestamp | 删除时间戳 |
310
+ | `created_at` | Timestamp | 备份创建时间戳 |
311
+
312
+ ### 3.2. 索引
313
+ - **用户表**: 主键 (`id`), `user_id` 唯一索引, `fingerprint_id` 唯一索引, `clerk_user_id` 唯一索引, `email` 索引。
314
+ - **订阅表**: 主键 (`id`), `subscription_id` 唯一索引, `user_id` 索引。
315
+ - **积分表**: 主键 (`id`), `user_id` 索引。
316
+ - **交易表**: 主键 (`id`), `user_id`、`stripe_session_id`、`stripe_invoice_id` 索引。
317
+ - **积分使用表**: 主键 (`id`), `user_id`索引。
318
+ - **用户备份表**: 主键 (`id`), `original_user_id` 索引, `fingerprint_id` 索引, `clerk_user_id` 索引。
319
+
320
+ ### 3.3. 数据表关联关系
321
+
322
+ #### 3.3.1 数据表及关联关系
323
+
324
+ 1. **用户表 (Users)**
325
+ - **描述**: 存储用户(包括匿名用户和注册用户)的信息。
326
+ - **关联关系**:
327
+ - **与订阅表 (Subscriptions)**: 一对多
328
+ - 一个用户可以有多个订阅(例如, 历史订阅记录或同时订阅多个服务)。
329
+ - **关联字段**: `Users.user_id` (主键) 与 `Subscriptions.user_id` (外键)。
330
+ - **与积分表 (Credits)**: 一对一
331
+ - 每个用户有且仅有一个积分余额记录, 用于跟踪免费和付费积分。
332
+ - **关联字段**: `Users.user_id` (主键) 与 `Credits.user_id` (外键)。
333
+ - **与交易表 (Transactions)**: 一对多
334
+ - 一个用户可以有多个交易记录(例如, 多次订阅或购买积分包)。
335
+ - **关联字段**: `Users.user_id` (主键) 与 `Transactions.user_id` (外键)。
336
+ - **与积分使用表 (Credit Usage)**: 一对多
337
+ - 一个用户可以有多次积分使用记录。
338
+ - **关联字段**: `Users.user_id` (主键) 与 `Credit Usage.user_id` (外键)。
339
+
340
+ 2. **订阅表 (Subscriptions)**
341
+ - **描述**: 跟踪用户的订阅信息。
342
+ - **关联关系**:
343
+ - **与用户表 (Users)**: 多对一
344
+ - 每个订阅属于一个用户。
345
+ - **关联字段**: `Subscriptions.user_id` (外键) 与 `Users.user_id` (主键)。
346
+ - **与交易表 (Transactions)**: 一对多(间接关联)
347
+ - 订阅可能涉及多个交易(例如, 续费、升级等), 但这种关联通常通过 Stripe 的 `stripe_subscription_id` 间接建立, `Transactions` 表记录与订阅相关的支付。
348
+ - **关联字段**: `Subscriptions.stripe_subscription_id` 与 `Transactions.stripe_subscription_id`。
349
+
350
+ 3. **积分表 (Credits)**
351
+ - **描述**: 管理用户的积分余额。
352
+ - **关联关系**:
353
+ - **与用户表 (Users)**: 一对一
354
+ - 每个用户有一个积分余额记录。
355
+ - **关联字段**: `Credits.user_id` (外键) 与 `Users.user_id` (主键)。
356
+ - **与交易表 (Transactions)**: 间接关联
357
+ - 积分余额可能通过交易增加(例如, 订阅或一次性购买), 但不直接通过外键关联, 而是通过业务逻辑(如 `Transactions.credits_granted` 影响 `Credits.balance_paid`)。
358
+ - **关联字段**: 无直接外键, 依赖业务逻辑。
359
+
360
+ 4. **交易表 (Transactions)**
361
+ - **描述**: 记录支付交易(订阅或一次性购买)。
362
+ - **关联关系**:
363
+ - **与用户表 (Users)**: 多对一
364
+ - 每个交易属于一个用户。
365
+ - **关联字段**: `Transactions.user_id` (外键) 与 `Users.user_id` (主键)。
366
+ - **与订阅表 (Subscriptions)**: 多对一(间接)
367
+ - 交易可能与订阅相关, 记录订阅的支付或续费。
368
+ - **关联字段**: `Transactions.stripe_subscription_id` 与 `Subscriptions.stripe_subscription_id`。
369
+
370
+ 5. **积分使用表 (Credit Usage)**
371
+ - **描述**: 跟踪用户如何消耗和充值积分。
372
+ - **关联关系**:
373
+ - **与用户表 (Users)**: 多对一
374
+ - 每次积分操作记录属于一个用户。
375
+ - **关联字段**: `Credit Usage.user_id` (外键) 与 `Users.user_id` (主键)。
376
+ - **与交易表 (Transactions)**: 间接关联
377
+ - 当`operation_type`为`recharge`且`credit_type`为`paid`时, 积分充值来源于交易。
378
+ - **关联逻辑**: 通过业务逻辑关联, 充值积分数量来自`Transactions.credits_granted`。
379
+
380
+ #### 3.3.2 关联关系总结
381
+
382
+ | 表名 | 关联表 | 关联类型 | 关联字段 |
383
+ |---------------------|---------------------|------------|---------------------------------------|
384
+ | Users | Subscriptions | 一对多 | `Users.user_id` -> `Subscriptions.user_id` |
385
+ | Users | Credits | 一对一 | `Users.user_id` -> `Credits.user_id` |
386
+ | Users | Transactions | 一对多 | `Users.user_id` -> `Transactions.user_id` |
387
+ | Users | Credit Usage | 一对多 | `Users.user_id` -> `Credit Usage.user_id` |
388
+ | Subscriptions | Users | 多对一 | `Subscriptions.user_id` -> `Users.user_id` |
389
+ | Subscriptions | Transactions | 一对多(间接) | `Subscriptions.stripe_subscription_id` -> `Transactions.stripe_subscription_id` |
390
+ | Credits | Users | 一对一 | `Credits.user_id` -> `Users.user_id` |
391
+ | Transactions | Users | 多对一 | `Transactions.user_id` -> `Users.user_id` |
392
+ | Credit Usage | Users | 多对一 | `Credit Usage.user_id` -> `Users.user_id` |
393
+ | Credit Usage | Transactions | 多对一(间接) | 通过业务逻辑关联, 充值积分来自`Transactions.credits_granted` |
394
+
395
+ #### 3.3.3 数据表结构图
396
+
397
+ <Mermaid
398
+ title="数据表结构图"
399
+ chart={`
400
+ classDiagram
401
+ class Users {
402
+ number id PK
403
+ string user_id FK
404
+ string fingerprint_id
405
+ string clerk_user_id
406
+ string email
407
+ <<enumeration>> status
408
+ string created_at
409
+ string updated_at
410
+ }
411
+
412
+ class Subscriptions {
413
+ number id PK
414
+ string subscription_id FK
415
+ string user_id
416
+ string stripe_subscription_id
417
+ string price_id
418
+ string price_name
419
+ <<enumeration>> status
420
+ int credits_allocated
421
+ string sub_period_start
422
+ string sub_period_end
423
+ string created_at
424
+ string updated_at
425
+ }
426
+
427
+ class Credits {
428
+ number id PK
429
+ string user_id
430
+ int balance_free
431
+ int total_free_limit
432
+ int balance_paid
433
+ int total_paid_limit
434
+ string created_at
435
+ string updated_at
436
+ }
437
+
438
+ class Transactions {
439
+ number id PK
440
+ string user_id
441
+ string order_id
442
+ <<enumeration>> order_status
443
+ string order_created_at
444
+ string order_expired_at
445
+ string order_updated_at
446
+ string stripe_transaction_id
447
+ string stripe_subscription_id
448
+ string stripe_session_id
449
+ string stripe_invoice_id
450
+ string price_id
451
+ string price_name
452
+ int sub_interval_count
453
+ string sub_cycle_anchor
454
+ decimal amount
455
+ string currency
456
+ <<enumeration>> type
457
+ int credits_granted
458
+ string sub_period_start
459
+ string sub_period_end
460
+ string order_detail
461
+ string paid_at
462
+ string paid_email
463
+ string paid_detail
464
+ string stripe_created_at
465
+ string stripe_updated_at
466
+ }
467
+
468
+ class Credit_Usage {
469
+ number id PK
470
+ string user_id
471
+ string feature
472
+ string order_id
473
+ <<enumeration>> credit_type
474
+ <<enumeration>> operation_type
475
+ int credits_used
476
+ string created_at
477
+ }
478
+
479
+ class UserBackup {
480
+ number id PK
481
+ string original_user_id
482
+ string fingerprint_id
483
+ string clerk_user_id
484
+ string email
485
+ string status
486
+ string backup_data
487
+ string deleted_at
488
+ string created_at
489
+ }
490
+
491
+ Users "1" --o "N" Subscriptions : user_id
492
+ Users "1" --> "1" Credits : user_id
493
+ Users "1" --o "N" Transactions : user_id
494
+ Users "1" --* "N" Credit_Usage : user_id
495
+ Users "1" --> "1" UserBackup : user_id (backup)
496
+ Subscriptions "1" --* "N" Transactions : stripe_subscription_id
497
+ Transactions "1" --* "N" Credit_Usage : credits_granted
498
+ `}/>
499
+
500
+
501
+ ### 3.4. 数据模型表主题与优势分析
502
+
503
+ #### 3.4.1 各表主题含义分析
504
+
505
+ ##### 3.4.1.1 用户表 (Users) - 用户身份管理
506
+ **主题含义**: 用户身份识别与生命周期管理
507
+ - **核心职责**: 统一管理匿名用户和注册用户身份
508
+ - **关键设计**: 通过`fingerprint_id`实现匿名用户识别, 通过`email`实现注册用户管理
509
+ - **业务价值**: 支持用户从匿名到注册的无缝转换, 保持数据连续性
510
+
511
+ ##### 3.4.1.2 订阅表 (Subscriptions) - 订阅服务管理
512
+ **主题含义**: 订阅服务的生命周期管理
513
+ - **核心职责**: 跟踪用户的订阅状态、计费周期和积分分配
514
+ - **关键设计**: 与Stripe深度集成, 通过`price_id`和`price_name`管理订阅计划
515
+ - **业务价值**: 支持灵活的订阅管理(升级、降级、取消、续费)
516
+
517
+ ##### 3.4.1.3 积分表 (Credits) - 积分余额管理
518
+ **主题含义**: 用户积分资产的管理
519
+ - **核心职责**: 维护用户的积分余额和总量限制
520
+ - **关键设计**: 区分免费积分和付费积分, 设置总量限制防止滥用
521
+ - **业务价值**: 为用户提供透明的积分管理, 支持积分消耗和充值
522
+
523
+ ##### 3.4.1.4 订单交易表 (Transactions) - 订单交易记录
524
+ **主题含义**: 完整的订单交易生命周期管理
525
+ - **核心职责**: 记录所有支付交易, 包括订单状态、支付详情、积分授予
526
+ - **关键设计**: 完整的订单状态流转, 详细的支付信息记录
527
+ - **业务价值**: 提供完整的交易审计和财务对账能力
528
+
529
+ ##### 3.4.1.5 积分使用表 (Credit Usage) - 积分操作审计
530
+ **主题含义**: 积分操作的完整审计追踪
531
+ - **核心职责**: 记录所有积分消耗和充值操作
532
+ - **关键设计**: 通过`operation_type`和`credit_type`区分操作类型
533
+ - **业务价值**: 提供积分使用的完整审计和数据分析能力
534
+
535
+ #### 3.4.2 设计优势分析
536
+
537
+ ##### 3.4.2.1 数据完整性
538
+ - **用户身份连续性**: 通过`fingerprint_id`实现匿名到注册的无缝转换
539
+ - **交易完整性**: 完整的订单状态流转和支付信息记录
540
+ - **积分审计完整性**: 所有积分操作都有详细记录
541
+
542
+ ##### 3.4.2.2 业务灵活性
543
+ - **订阅管理灵活**: 支持多种订阅计划和计费周期
544
+ - **积分管理灵活**: 支持免费和付费积分的混合使用
545
+ - **支付方式灵活**: 与Stripe深度集成, 支持多种支付方式
546
+
547
+ ##### 3.4.2.3 系统可扩展性
548
+ - **字段设计合理**: 预留了足够的扩展字段
549
+ - **关联关系清晰**: 表间关联关系设计合理
550
+ - **索引策略完善**: 关键字段都有索引支持
551
+
552
+ ### 3.5. 表数据运转架构图
553
+
554
+ #### 3.5.1 核心业务流程架构
555
+
556
+ <Mermaid
557
+ title="核心业务流程架构"
558
+ chart={`
559
+ flowchart TB
560
+ %% 自上而下布局, 使用入口/出口汇聚点, 减少跨层直连
561
+
562
+ classDef entry fill:#fff,stroke:#bbb,stroke-dasharray:3 3,color:#666;
563
+ classDef core fill:#f7faff,stroke:#7aa7e0,color:#1f2937;
564
+
565
+ %% 用户身份层
566
+ subgraph L1[用户身份层]
567
+ direction TB
568
+ U[Users表<br/>用户身份管理]:::core
569
+ A[匿名用户识别]:::core
570
+ R[注册用户管理]:::core
571
+ U --> |fingerprint_id| A
572
+ U --> |email| R
573
+ L1_OUT((身份→)):::entry
574
+ U --> L1_OUT
575
+ end
576
+
577
+ %% 订阅服务层
578
+ subgraph L2[订阅服务层]
579
+ direction TB
580
+ L2_IN((←身份)):::entry
581
+ S[Subscriptions表<br/>订阅服务管理]:::core
582
+ P[订阅计划管理]:::core
583
+ C[计费周期管理]:::core
584
+ I[积分分配管理]:::core
585
+ L2_IN --> S
586
+ S --> |price_id| P
587
+ S --> |sub_period_*| C
588
+ S --> |credits_allocated| I
589
+ L2_OUT((订阅→)):::entry
590
+ S --> L2_OUT
591
+ end
592
+
593
+ %% 订单交易层
594
+ subgraph L3[订单交易层]
595
+ direction TB
596
+ L3_IN((←订阅)):::entry
597
+ T[Transactions表<br/>订单交易记录]:::core
598
+ OS[订单状态管理]:::core
599
+ SP[Stripe支付集成]:::core
600
+ CG[积分授予管理]:::core
601
+ L3_IN --> T
602
+ T --> |order_status| OS
603
+ T --> |stripe_*| SP
604
+ T --> |credits_granted| CG
605
+ L3_OUT((交易→)):::entry
606
+ T --> L3_OUT
607
+ end
608
+
609
+ %% 积分资产层
610
+ subgraph L4[积分资产层]
611
+ direction TB
612
+ L4_IN((←交易/身份)):::entry
613
+ CR[Credits表<br/>积分余额管理]:::core
614
+ F[免费积分管理]:::core
615
+ PD[付费积分管理]:::core
616
+ L[积分限制管理]:::core
617
+ L4_IN --> CR
618
+ CR --> |balance_free| F
619
+ CR --> |balance_paid| PD
620
+ CR --> |total_*_limit| L
621
+ L4_OUT((积分→)):::entry
622
+ CR --> L4_OUT
623
+ end
624
+
625
+ %% 积分操作层
626
+ subgraph L5[积分操作层]
627
+ direction TB
628
+ L5_IN((←积分)):::entry
629
+ CU[Credit_Usage表<br/>积分操作审计]:::core
630
+ OT[操作类型管理]:::core
631
+ CT[积分类型管理]:::core
632
+ FE[功能使用追踪]:::core
633
+ L5_IN --> CU
634
+ CU --> |operation_type| OT
635
+ CU --> |credit_type| CT
636
+ CU --> |feature| FE
637
+ end
638
+
639
+ %% 层级衔接(仅入口/出口连接, 避免交叉)
640
+ L1_OUT --> L2_IN
641
+ L2_OUT --> L3_IN
642
+ L3_OUT --> L4_IN
643
+ L4_OUT --> L5_IN
644
+
645
+ %% 业务事件通过入口路由到目标层, 减少跨层线段
646
+ A --> |新用户注册| L4_IN
647
+ R --> |用户登录| L4_IN
648
+ P --> |订阅创建| L3_IN
649
+ OS --> |支付成功| CG
650
+ CG --> |积分充值| L4_IN
651
+ PD --> |积分使用| L5_IN
652
+ `}/>
653
+
654
+ #### 3.5.2 数据流转时序架构
655
+
656
+ <Mermaid
657
+ title="数据流转时序架构"
658
+ chart={`
659
+ sequenceDiagram
660
+ participant U as Users表
661
+ participant S as Subscriptions表
662
+ participant T as Transactions表
663
+ participant C as Credits表
664
+ participant CU as Credit_Usage表
665
+
666
+ Note over U,CU: 用户注册流程
667
+ U->>U: 创建用户记录(fingerprint_id)
668
+ U->>C: 初始化积分余额(balance_free=50)
669
+ C->>CU: 记录免费积分授予(recharge, free)
670
+
671
+ Note over U,CU: 订阅购买流程
672
+ U->>S: 创建订阅记录(price_id, status)
673
+ S->>T: 创建订单记录(order_status=created)
674
+ T->>T: 更新订单状态(order_status=success)
675
+ T->>C: 充值付费积分(balance_paid += credits_granted)
676
+ C->>CU: 记录付费积分充值(recharge, paid)
677
+
678
+ Note over U,CU: 积分使用流程
679
+ U->>C: 查询积分余额(balance_free, balance_paid)
680
+ C->>C: 扣除积分(优先free, 后paid)
681
+ C->>CU: 记录积分消耗(consume, free/paid)
682
+
683
+ Note over U,CU: 订阅续费流程
684
+ S->>S: 更新订阅周期(sub_period_*)
685
+ S->>T: 创建续费订单记录
686
+ T->>T: 更新订单状态(order_status=success)
687
+ T->>C: 充值付费积分(balance_paid += credits_granted)
688
+ C->>CU: 记录付费积分充值(recharge, paid)
689
+ `}/>
690
+
691
+ #### 3.5.3 状态流转架构
692
+
693
+ <Mermaid
694
+ title="状态流转架构"
695
+ chart={`
696
+ stateDiagram-v2
697
+ direction TB % 整体纵向布局, 让初始流程和积分操作上下排列
698
+
699
+ % 定义初始流程状态, 内部用LR横向布局
700
+ state "基础操作" as InitialFlow {
701
+ direction LR
702
+ [*] --> 创建Users记录
703
+ 创建Users记录 --> 创建Credits记录
704
+ 创建Credits记录 --> 用户选择订阅计划
705
+ 用户选择订阅计划 --> 创建Transactions记录
706
+ 创建Transactions记录 --> 订单创建初始化: order_status=created
707
+ }
708
+
709
+ % 定义积分操作状态, 内部可根据需求调整布局, 这里也用LR示例
710
+ state "积分操作" as CreditOps {
711
+ direction LR
712
+ [*] --> 积分消耗: 用户注销
713
+ 积分充值 --> 积分消耗: 继续使用功能
714
+ 积分充值 --> 积分消耗: 用户使用功能
715
+ 订阅续费 --> 积分充值: 自动续费成功
716
+ 积分消耗 --> 积分充值: 新订阅/购买
717
+ 积分消耗 --> 积分不足: 余额耗尽
718
+ 积分不足 --> 积分充值: 用户购买积分
719
+ 积分消耗 --> 积分消耗: 继续使用功能
720
+ 积分消耗 --> 订阅续费: 订阅周期结束
721
+ }
722
+
723
+ % 从初始流程指向积分操作, 实现上下并列结构衔接
724
+ InitialFlow --> CreditOps: order_status=success
725
+ `}/>
726
+
727
+
728
+
729
+ ### 3.6. 潜在改进建议与总结
730
+
731
+ #### 3.6.1 潜在改进建议
732
+
733
+ ##### 3.6.1.1 数据一致性
734
+ - 建议在积分操作时使用数据库事务确保数据一致性
735
+ - 考虑添加积分余额的校验机制
736
+ - 建议添加定期对账机制, 确保积分余额与使用记录一致
737
+
738
+ ##### 3.6.1.2 性能优化
739
+ - 考虑对高频查询的积分余额进行Redis缓存
740
+ - 建议对历史数据按时间分区, 提高查询性能
741
+ - 考虑对Credit_Usage表进行归档策略
742
+
743
+ ##### 3.6.1.3 业务逻辑增强
744
+ - 建议添加积分过期机制, 提高积分使用率
745
+ - 考虑添加积分转让功能, 增强用户粘性
746
+ - 建议添加积分使用分析功能, 优化产品策略
747
+
748
+ ##### 3.6.1.4 监控告警
749
+ - 建议添加积分异常使用监控
750
+ - 考虑添加订单状态异常告警
751
+ - 建议添加积分余额不足提醒
752
+
753
+ #### 3.6.2 总结
754
+
755
+ 这个数据模型设计整体上非常完善, 具有以下特点:
756
+
757
+ 1. **业务完整性**: 覆盖了从用户注册到积分使用的完整业务流程
758
+ 2. **数据一致性**: 通过合理的表关联和状态管理确保数据一致性
759
+ 3. **扩展性良好**: 预留了足够的扩展字段和灵活的关联关系
760
+ 4. **审计能力**: 提供了完整的操作审计和数据分析能力
761
+
762
+ 建议在实施过程中重点关注数据一致性、性能优化和业务监控, 确保系统的稳定性和可维护性。
763
+
764
+ ---
765
+
766
+ ## 4 数据流设计
767
+
768
+ ### 4.1. 订阅购买流程
769
+ 1. **用户发起订阅**:
770
+ - 用户在订阅管理界面选择计划。
771
+ - 前端向后端发送请求, 包含`plan_id`和`user_id`(或匿名用户的`fingerprint_id`)。
772
+ 2. **创建Stripe会话**:
773
+ - 后端为选定的计划创建Stripe Checkout Session (`stripe_session_id`)。
774
+ - 用户被重定向到Stripe的结账页面。
775
+ 3. **支付完成**:
776
+ - 支付成功后, Stripe向后端发送Webhook (`checkout.session.completed`)。
777
+ - 后端更新`Subscriptions`表, 创建订阅记录。
778
+ - 在`Transactions`表中创建记录, 包含`stripe_session_id`、`amount`和`credits_granted`。
779
+ 4. **积分充值**:
780
+ - 从`Transactions`表的`credits_granted`字段获取积分数量。
781
+ - 更新`Credits`表的`balance_paid`和`total_paid_limit`。
782
+ - 在`Credit_Usage`表中插入充值记录, `operation_type`为`recharge`, `credit_type`为`paid`。
783
+ 5. **用户通知**:
784
+ - 用户收到确认电子邮件, 界面显示更新后的积分余额。
785
+
786
+ ### 4.2. 积分使用流程
787
+
788
+ #### 4.2.1 积分消耗操作
789
+ 1. **功能访问请求**:
790
+ - 用户尝试访问高级功能(如API调用、工具使用)。
791
+ - 前端向后端发送请求, 包含`user_id`和功能标识。
792
+ 2. **积分余额检查**:
793
+ - 后端查询`Credits`表, 获取`balance_free`和`balance_paid`。
794
+ - 判断用户积分是否足够支付功能费用。
795
+ 3. **积分扣除逻辑**:
796
+ - **优先扣除策略**: 优先从`balance_free`扣除, 不足时从`balance_paid`扣除。
797
+ - **数据更新**: 更新`Credits`表中对应的余额字段。
798
+ - **使用记录**: 在`Credit_Usage`表中插入记录, `operation_type`为`consume`, `credit_type`为实际扣除的积分类型。
799
+ 4. **功能授权**:
800
+ - 积分扣除成功后, 返回功能访问授权。
801
+ - 积分不足时, 提示用户购买积分或升级计划。
802
+
803
+ #### 4.2.2 积分充值操作
804
+ 1. **系统授予免费积分**:
805
+ - **触发场景**: 新用户注册、活动奖励、系统补偿等。
806
+ - **数据变化**: 更新`Credits`表的`balance_free`和`total_free_limit`。
807
+ - **记录追踪**: 在`Credit_Usage`表中插入记录, `operation_type`为`recharge`, `credit_type`为`free`。
808
+
809
+ 2. **用户支付后充值**:
810
+ - **触发场景**: 订阅支付成功、一次性购买完成。
811
+ - **数据来源**: 从`Transactions`表的`credits_granted`字段获取充值数量。
812
+ - **数据变化**: 更新`Credits`表的`balance_paid`和`total_paid_limit`。
813
+ - **记录追踪**: 在`Credit_Usage`表中插入记录, `operation_type`为`recharge`, `credit_type`为`paid`。
814
+
815
+ #### 4.2.3 积分余额管理
816
+ - **总量限制**: `total_free_limit`和`total_paid_limit`记录用户获得的总积分量。
817
+ - **余额计算**: `balance_free`和`balance_paid`为当前可用余额。
818
+ - **历史追踪**: 通过`Credit_Usage`表完整记录所有积分操作历史。
819
+
820
+ ### 4.3. 订阅管理流程
821
+ - **自动续费**:
822
+ - Stripe处理自动续费并向后端发送Webhook (`invoice.paid`)。
823
+ - 后端更新`Subscriptions`表, 创建续费交易记录。
824
+ - 从`Transactions`表的`credits_granted`字段获取积分数量。
825
+ - 更新`Credits`表的`balance_paid`和`total_paid_limit`。
826
+ - 在`Credit_Usage`表中插入充值记录, `operation_type`为`recharge`, `credit_type`为`paid`。
827
+ - **取消**:
828
+ - 用户通过订阅管理界面取消订阅。
829
+ - 后端向Stripe发送取消请求, 更新`Subscriptions`表(`status` = canceled)。
830
+ - **升级/降级**:
831
+ - 用户选择新计划; 后端更新Stripe订阅并按比例分配费用。
832
+ - 更新`Subscriptions`表的`plan_id`和`credits_allocated`。
833
+ - 处理按比例分配的积分充值, 更新`Credits`表和`Credit_Usage`表。
834
+ - **附加包购买**:
835
+ - 与订阅购买类似, 但创建一次性Stripe Checkout Session。
836
+ - Webhook确认后, 创建交易记录并充值积分。
837
+
838
+ ### 4.4. 退款流程
839
+ 1. **用户请求退款**:
840
+ - 用户通过界面发起退款请求。
841
+ - 后端验证退款资格(例如, 7天内, 基于`Transactions`表)。
842
+ 2. **处理退款**:
843
+ - 后端使用`stripe_session_id`向Stripe发送退款请求。
844
+ - Stripe处理退款并发送Webhook (`charge.refunded`)。
845
+ - 后端更新`Transactions`表(`status` = refunded)。
846
+ - 从`Transactions`表的`credits_granted`字段获取需要扣除的积分数量。
847
+ - 更新`Credits`表的`balance_paid`和`total_paid_limit`(减少积分)。
848
+ - 在`Credit_Usage`表中插入扣除记录, `operation_type`为`consume`, `credit_type`为`paid`。
849
+
850
+ ### 4.5. Clerk用户认证流程
851
+
852
+ #### 4.5.1 匿名用户初始化
853
+ 1. **前端处理**:
854
+ - 用户访问平台时, 前端生成Fingerprint ID。
855
+ - 调用Clerk创建匿名会话, 获取`clerk_user_id`。
856
+ - 向后端发送初始化请求, 包含`fingerprint_id`和`clerk_user_id`。
857
+ 2. **后端处理**:
858
+ - 查询Redis缓存, 检查是否已有用户记录。
859
+ - 缓存未命中时, 查询数据库`Users`表。
860
+ - 用户不存在时, 创建新的匿名用户记录。
861
+ - 分配50个免费积分, 创建`Credits`和`Credit_Usage`记录。
862
+ - 将用户信息缓存到Redis, TTL设置为24小时。
863
+ 3. **返回结果**:
864
+ - 返回用户信息和积分余额给前端。
865
+ - 前端显示平台界面和积分余额。
866
+
867
+ #### 4.5.2 用户注册/登录
868
+ 1. **Clerk认证**:
869
+ - 用户通过Clerk界面完成注册或登录。
870
+ - Clerk验证用户信息, 返回`clerk_user_id`和`email`。
871
+ - 前端将认证结果发送给后端。
872
+ 2. **后端处理**:
873
+ - 查询Redis缓存, 检查用户状态。
874
+ - **匿名用户升级**: 更新`Users`表, 设置`email`和`clerk_user_id`, 状态改为`registered`。
875
+ - **新注册用户**: 创建完整的用户记录, 包括积分初始化。
876
+ - 更新Redis缓存, 包含新的用户信息。
877
+ 3. **数据一致性**:
878
+ - 确保`fingerprint_id`和`clerk_user_id`的关联关系正确。
879
+ - 维护用户从匿名到注册的完整数据连续性。
880
+
881
+ #### 4.5.3 用户注销
882
+ 1. **Clerk注销**:
883
+ - 用户通过Clerk完成注销操作。
884
+ - 前端向后端发送注销请求。
885
+ 2. **数据备份**:
886
+ - 将用户数据备份到`UserBackup`表。
887
+ - 硬删除`Users`及关联表记录。
888
+ 3. **缓存清理**:
889
+ - 删除Redis中所有相关的用户缓存。
890
+ - 包括`fingerprint_id`、`email`、`user_id`相关的缓存。
891
+ 4. **状态重置**:
892
+ - 用户重新访问时, 重新开始匿名用户流程。
893
+
894
+ #### 4.5.4 Redis缓存策略
895
+ 1. **缓存键设计**:
896
+ - `fingerprint_id:user_info`: 用户身份信息
897
+ - `email:user_info`: 邮箱关联的用户信息
898
+ - `clerk_user_id:user_info`: Clerk用户关联信息
899
+ - `user_id:credits`: 用户积分余额
900
+ - `user_id:session`: 用户会话状态
901
+ 2. **缓存更新策略**:
902
+ - 用户信息变更时主动更新缓存。
903
+ - 积分操作时更新积分缓存。
904
+ - 用户注销时清理所有相关缓存。
905
+ 3. **缓存一致性**:
906
+ - 采用Cache-Aside模式, 先更新数据库再更新缓存。
907
+ - 缓存失效时从数据库重新加载。
908
+ - 监控缓存命中率和响应时间。
909
+
910
+ ---
911
+
912
+ ## 5 Mermaid流程图
913
+
914
+ ### 5.1. 匿名用户与注册用户数据流程图
915
+
916
+ 以下是匿名用户到注册用户、注销、再次注册的综合数据流程图, 展示数据如何在系统中流动。
917
+
918
+ <Mermaid
919
+ title="匿名用户与注册用户数据流程图"
920
+ chart={`
921
+ flowchart TB
922
+ %% 自上而下布局, 模块内顺序化; 使用入口/出口汇聚节点减少跨模块交叉
923
+ classDef entry fill:#fff,stroke:#bbb,stroke-dasharray:3 3,color:#666;
924
+ classDef core fill:#f7faff,stroke:#7aa7e0,color:#1f2937;
925
+
926
+ %% 模块1: 匿名用户初始化
927
+ subgraph M1[匿名用户初始化]
928
+ direction LR
929
+ A[用户访问平台]:::core --> B{检查 Fingerprint ID}:::core
930
+ B -->|无记录| C[生成 user_id / fingerprint_id]:::core
931
+ C --> D[插入 Users 表]:::core --> E[分配 50 免费积分]:::core --> F[记录到 Credits 表]:::core
932
+ B -->|有记录| M1_OUT((→ 已有指纹ID处理)):::entry
933
+ F --> M1_OUT
934
+ end
935
+
936
+ %% 模块2: 已有指纹ID处理
937
+ subgraph M2[已有指纹ID处理]
938
+ direction LR
939
+ M2_IN((← 匿名初始化)):::entry
940
+ M2_IN --> G{用户状态?}:::core
941
+ G -->|匿名| M2_OUT_ANON((→ 匿名功能)):::entry
942
+ G -->|注册| I[验证登录凭证]:::core --> J[访问 Subscriptions/Credits]:::core --> M2_OUT_REG((→ 注册用户操作)):::entry
943
+ end
944
+
945
+ %% 模块3: 匿名用户功能使用
946
+ subgraph M3[匿名用户功能使用]
947
+ direction LR
948
+ M3_IN((← 匿名功能)):::entry
949
+ M3_IN --> K[匿名用户使用功能]:::core --> L[记录 Credit_Usage 表]:::core --> M{积分是否足够?}:::core
950
+ M -->|是| K
951
+ M -->|否| M3_OUT_NEEDREG((→ 注册引导)):::entry
952
+ end
953
+
954
+ %% 模块4: 注册用户操作
955
+ subgraph M4[注册用户操作]
956
+ direction LR
957
+ M4_IN((← 已有指纹ID/注册完成)):::entry
958
+ M4_IN --> O[注册用户操作: 订阅/购买/使用]:::core --> P{用户注销?}:::core
959
+ P -->|是| Q[备份 UserBackup 表]:::core --> R[硬删除关联表]:::core --> S[恢复匿名用户]:::core --> M3_IN
960
+ P -->|否| O
961
+ O --> M4_OUT((→ 完)):::entry
962
+ end
963
+
964
+ %% 模块5: 注册引导
965
+ subgraph M5[注册引导]
966
+ direction LR
967
+ M5_IN((← 积分不足)):::entry
968
+ M5_IN --> U{用户注册?}:::core
969
+ U -->|是| V[提交注册信息]:::core --> W[更新 Users 表]:::core --> M4_IN
970
+ U -->|否| M3_IN
971
+ end
972
+
973
+ %% 模块衔接(仅入口/出口连接, 减少交叉)
974
+ M1_OUT --> M2_IN
975
+ M2_OUT_ANON --> M3_IN
976
+ M2_OUT_REG --> M4_IN
977
+ M3_OUT_NEEDREG --> M5_IN
978
+ `}/>
979
+
980
+ ### 5.2. 匿名用户积分使用流程图
981
+
982
+ <Mermaid
983
+ title="匿名用户积分使用流程图"
984
+ chart={`
985
+ flowchart LR
986
+ classDef entry fill:#fff,stroke:#bbb,stroke-dasharray:3 3,color:#666;
987
+
988
+ %% 模块1: 初始分配流程(子图横向)
989
+ subgraph 初始分配流程
990
+ direction LR
991
+ A[用户访问平台] --> B{分配 Fingerprint ID}
992
+ B --> C[分配 50 个免费积分]
993
+ C --> D[更新 Credits 表: balance_free=50, total_free_limit=50]
994
+ D --> E[插入 Credit_Usage: recharge, free]
995
+ E --> OUT_INIT((→ 判断余额)):::entry
996
+ end
997
+
998
+ %% 中央判断
999
+ G{检查免费积分余额}
1000
+
1001
+ OUT_INIT --> G
1002
+
1003
+ %% 模块2: 功能使用流程(子图横向)
1004
+ subgraph 功能使用流程
1005
+ direction LR
1006
+ IN_USE((入口)):::entry --> H[扣除 free 积分] --> I[更新 Credits 表: 减少 balance_free] --> J[插入 Credit_Usage: consume, free] --> K[功能访问授权]
1007
+ end
1008
+
1009
+ %% 模块3: 积分不足处理(子图横向)
1010
+ subgraph 积分不足处理
1011
+ direction LR
1012
+ IN_LOW((入口)):::entry --> L[提示注册或购买] --> M{用户注册?}
1013
+ M -->|是| N[将 Fingerprint 关联到用户 ID] --> P[重定向到订阅/购买]
1014
+ M -->|否| O[结束会话]
1015
+ end
1016
+
1017
+ %% 模块间连接(仅通过入口/出口)
1018
+ G -->|足够| IN_USE
1019
+ G -->|不足| IN_LOW
1020
+ `}/>
1021
+
1022
+ ### 5.3. 登录用户订阅流程图
1023
+
1024
+ <Mermaid
1025
+ title="登录用户订阅流程图"
1026
+ chart={`
1027
+ flowchart LR
1028
+ %% ========== 子图1: 核心操作与分支(内部横向布局) ==========
1029
+ subgraph 操作与分支
1030
+ direction LR %% 子图内节点横向排列
1031
+ A[用户登录] --> B[导航到订阅界面]
1032
+ B --> C{选择操作}
1033
+ %% 所有分支都在第一个子图内
1034
+ C -->|订阅| D[选择计划]
1035
+ C -->|升级/降级| E[选择新计划]
1036
+ C -->|取消| F[向Stripe发送取消请求]
1037
+ C -->|附加包| G[选择积分包]
1038
+ %% 汇聚到统一节点, 进入支付流程
1039
+ D & E & G --> H[创建Stripe Checkout Session]
1040
+ F --> I[更新订阅状态: 已取消]
1041
+ H --> J[重定向到Stripe]
1042
+ end
1043
+
1044
+ %% ========== 子图2: 支付与订阅更新(内部横向布局) ==========
1045
+ subgraph 支付与更新
1046
+ direction LR %% 子图内节点横向排列
1047
+ J --> K[用户完成支付]
1048
+ K --> L[Stripe Webhook: 支付成功]
1049
+ L --> M[更新Subscriptions表]
1050
+ M --> N[创建Transactions记录]
1051
+ end
1052
+
1053
+ %% ========== 子图3: 积分与交易收尾(内部横向布局) ==========
1054
+ subgraph 积分与收尾
1055
+ direction LR %% 子图内节点横向排列
1056
+ N --> O[从credits_granted获取积分数量]
1057
+ O --> P[更新Credits表: 增加balance_paid, total_paid_limit]
1058
+ P --> Q[插入Credit_Usage: recharge, paid]
1059
+ Q --> R[记录交易完成]
1060
+ end
1061
+
1062
+
1063
+ `}/>
1064
+
1065
+ ### 5.4. 积分操作流程图
1066
+
1067
+ <Mermaid
1068
+ title="积分操作流程图"
1069
+ chart={`
1070
+ flowchart LR
1071
+ A[积分操作请求] --> B{操作类型}
1072
+
1073
+ B -->|消耗| C[检查积分余额]
1074
+ C --> D{余额是否足够?}
1075
+ D -->|是| E[优先扣除free积分]
1076
+ E --> F{free积分是否足够?}
1077
+ F -->|是| G[扣除free积分]
1078
+ F -->|否| H[扣除剩余free积分]
1079
+ H --> I[扣除paid积分补足]
1080
+ G --> J[更新Credits表: 减少balance_free]
1081
+ I --> K[更新Credits表: 减少balance_paid]
1082
+ J --> L[插入Credit_Usage: consume, free]
1083
+ K --> M[插入Credit_Usage: consume, paid]
1084
+ L --> N[功能访问授权]
1085
+ M --> N
1086
+ D -->|否| O[返回积分不足错误]
1087
+
1088
+ B -->|充值| P{充值类型}
1089
+ P -->|系统授予| Q[更新Credits表: 增加balance_free, total_free_limit]
1090
+ P -->|用户支付| R[从Transactions获取credits_granted]
1091
+ R --> S[更新Credits表: 增加balance_paid, total_paid_limit]
1092
+ Q --> T[插入Credit_Usage: recharge, free]
1093
+ S --> U[插入Credit_Usage: recharge, paid]
1094
+ T --> V[充值完成]
1095
+ U --> V
1096
+ `}/>
1097
+
1098
+ ---
1099
+
1100
+ ## 6 Mermaid时序图
1101
+
1102
+ ### 6.1. 匿名用户订阅时序图
1103
+
1104
+ <Mermaid
1105
+ title="匿名用户订阅时序图"
1106
+ chart={`
1107
+ sequenceDiagram
1108
+ participant U as 匿名用户
1109
+ participant F as 前端
1110
+ participant B as 后端
1111
+ participant S as Stripe
1112
+ participant D as 数据库
1113
+
1114
+ U->>F: 访问平台
1115
+ F->>B: 请求Fingerprint ID
1116
+ B->>D: 存储Fingerprint ID
1117
+ B->>F: 返回Fingerprint ID
1118
+ F->>U: 显示免费积分 (50)
1119
+ U->>F: 选择专业版计划
1120
+ F->>U: 提示注册
1121
+ U->>F: 提交注册 (电子邮件, 密码)
1122
+ F->>B: 创建用户账户
1123
+ B->>D: 将Fingerprint关联到用户ID
1124
+ B->>F: 返回用户ID
1125
+ F->>B: 请求订阅 (专业版计划)
1126
+ B->>S: 创建Checkout Session
1127
+ S->>B: 返回Session ID
1128
+ B->>F: 重定向到Stripe
1129
+ F->>U: 重定向到Stripe结账
1130
+ U->>S: 完成支付
1131
+ S->>B: Webhook: checkout.session.completed
1132
+ B->>D: 更新订阅, 分配积分
1133
+ B->>F: 通知支付成功
1134
+ F->>U: 显示更新后的积分
1135
+ `}/>
1136
+
1137
+ ### 6.2. 登录用户升级订阅时序图
1138
+
1139
+ <Mermaid
1140
+ title="登录用户升级订阅时序图"
1141
+ chart={`
1142
+ sequenceDiagram
1143
+ participant U as 登录用户
1144
+ participant F as 前端
1145
+ participant B as 后端
1146
+ participant S as Stripe
1147
+ participant D as 数据库
1148
+
1149
+ U->>F: 导航到订阅界面
1150
+ F->>B: 获取当前订阅
1151
+ B->>D: 查询订阅详情
1152
+ D->>B: 返回订阅数据
1153
+ B->>F: 显示订阅 (基础版计划)
1154
+ U->>F: 选择升级到专业版计划
1155
+ F->>B: 请求升级
1156
+ B->>S: 更新订阅并按比例分配费用
1157
+ S->>B: 返回更新后的Session ID
1158
+ B->>F: 重定向到Stripe进行支付
1159
+ F->>U: 重定向到Stripe结账
1160
+ U->>S: 完成按比例分配的支付
1161
+ S->>B: Webhook: invoice.paid
1162
+ B->>D: 将订阅更新为专业版, 添加积分
1163
+ B->>F: 通知升级成功
1164
+ F->>U: 显示更新后的计划和积分
1165
+ `}/>
1166
+
1167
+ ### 6.3. 积分消耗操作时序图
1168
+
1169
+ <Mermaid
1170
+ title="积分消耗操作时序图"
1171
+ chart={`
1172
+ sequenceDiagram
1173
+ participant U as 用户
1174
+ participant F as 前端
1175
+ participant B as 后端
1176
+ participant D as 数据库
1177
+
1178
+ U->>F: 请求使用功能 (API调用)
1179
+ F->>B: 检查积分余额
1180
+ B->>D: 查询Credits表 (balance_free, balance_paid)
1181
+ D->>B: 返回积分余额
1182
+ B->>B: 判断积分是否足够
1183
+ alt 积分足够
1184
+ B->>B: 优先扣除free积分, 不足则扣除paid积分
1185
+ B->>D: 更新Credits表 (减少balance_free/balance_paid)
1186
+ B->>D: 插入Credit_Usage记录 (operation_type: consume, credit_type: free/paid)
1187
+ D->>B: 确认更新成功
1188
+ B->>F: 返回功能访问授权
1189
+ F->>U: 提供功能服务
1190
+ else 积分不足
1191
+ B->>F: 返回积分不足错误
1192
+ F->>U: 提示购买积分或升级计划
1193
+ end
1194
+ `}/>
1195
+
1196
+ ### 6.4. 系统授予免费积分时序图
1197
+
1198
+ <Mermaid
1199
+ title="系统授予免费积分时序图"
1200
+ chart={`
1201
+ sequenceDiagram
1202
+ participant S as 系统
1203
+ participant B as 后端
1204
+ participant D as 数据库
1205
+
1206
+ S->>B: 触发免费积分授予 (新用户注册/活动奖励)
1207
+ B->>D: 查询Credits表 (total_free_limit)
1208
+ D->>B: 返回当前免费积分总量
1209
+ B->>B: 计算可授予积分数量
1210
+ B->>D: 更新Credits表 (增加balance_free, total_free_limit)
1211
+ B->>D: 插入Credit_Usage记录 (operation_type: recharge, credit_type: free)
1212
+ D->>B: 确认更新成功
1213
+ B->>S: 返回授予成功
1214
+ `}/>
1215
+
1216
+ ### 6.5. 用户支付后积分充值时序图
1217
+
1218
+ <Mermaid
1219
+ title="用户支付后积分充值时序图"
1220
+ chart={`
1221
+ sequenceDiagram
1222
+ participant U as 用户
1223
+ participant F as 前端
1224
+ participant B as 后端
1225
+ participant S as Stripe
1226
+ participant D as 数据库
1227
+
1228
+ U->>F: 完成支付 (订阅/一次性购买)
1229
+ S->>B: Webhook: checkout.session.completed
1230
+ B->>D: 查询Transactions表 (获取credits_granted)
1231
+ D->>B: 返回交易信息
1232
+ B->>D: 查询Credits表 (total_paid_limit)
1233
+ D->>B: 返回当前付费积分总量
1234
+ B->>B: 计算充值积分数量
1235
+ B->>D: 更新Credits表 (增加balance_paid, total_paid_limit)
1236
+ B->>D: 插入Credit_Usage记录 (operation_type: recharge, credit_type: paid)
1237
+ D->>B: 确认更新成功
1238
+ B->>F: 通知积分充值成功
1239
+ F->>U: 显示更新后的积分余额
1240
+ `}/>
1241
+
1242
+ ### 6.6. 积分余额查询时序图
1243
+
1244
+ <Mermaid
1245
+ title="积分余额查询时序图"
1246
+ chart={`
1247
+ sequenceDiagram
1248
+ participant U as 用户
1249
+ participant F as 前端
1250
+ participant B as 后端
1251
+ participant D as 数据库
1252
+
1253
+ U->>F: 请求查看积分余额
1254
+ F->>B: 获取用户积分信息
1255
+ B->>D: 查询Credits表 (balance_free, total_free_limit, balance_paid, total_paid_limit)
1256
+ D->>B: 返回积分数据
1257
+ B->>D: 查询Credit_Usage表 (最近使用记录)
1258
+ D->>B: 返回使用历史
1259
+ B->>F: 返回完整积分信息
1260
+ F->>U: 显示积分余额和使用历史
1261
+ `}/>
1262
+
1263
+ ### 6.7. Clerk用户登录时序图
1264
+
1265
+ #### 6.7.1 匿名用户首次访问时序图
1266
+
1267
+ <Mermaid
1268
+ title="匿名用户首次访问时序图"
1269
+ chart={`
1270
+ sequenceDiagram
1271
+ participant U as 用户
1272
+ participant F as 前端
1273
+ participant R as Redis
1274
+ participant B as 后端
1275
+ participant C as Clerk
1276
+ participant DB as 数据库
1277
+
1278
+ U->>F: 访问平台
1279
+ F->>F: 生成Fingerprint ID
1280
+ F->>R: 查询缓存: fingerprint_id
1281
+ R-->>F: 缓存未命中
1282
+
1283
+ F->>C: 创建匿名会话
1284
+ C-->>F: 返回clerk_user_id
1285
+
1286
+ F->>B: 匿名用户初始化请求
1287
+ Note over B: 包含fingerprint_id和clerk_user_id
1288
+
1289
+ B->>R: 查询缓存: fingerprint_id
1290
+ R-->>B: 缓存未命中
1291
+
1292
+ B->>DB: 查询Users表: fingerprint_id
1293
+ DB-->>B: 用户不存在
1294
+
1295
+ B->>DB: 创建Users记录
1296
+ Note over B: user_id=UUID, fingerprint_id, status=anonymous
1297
+ DB-->>B: 创建成功
1298
+
1299
+ B->>DB: 创建Credits记录
1300
+ Note over B: balance_free=50, total_free_limit=50
1301
+ DB-->>B: 创建成功
1302
+
1303
+ B->>DB: 插入Credit_Usage记录
1304
+ Note over B: operation_type=recharge, credit_type=free, credits_used=50
1305
+ DB-->>B: 插入成功
1306
+
1307
+ B->>R: 缓存用户信息
1308
+ Note over B: key: fingerprint_id, value: {user_id, status, balance_free}
1309
+ R-->>B: 缓存成功
1310
+
1311
+ B-->>F: 返回用户信息和积分余额
1312
+ F-->>U: 显示平台界面和积分余额
1313
+ `}/>
1314
+
1315
+ #### 6.7.2 匿名用户注册登录时序图
1316
+
1317
+ <Mermaid
1318
+ title="匿名用户注册登录时序图"
1319
+ chart={`
1320
+ sequenceDiagram
1321
+ participant U as 用户
1322
+ participant F as 前端
1323
+ participant C as Clerk
1324
+ participant R as Redis
1325
+ participant B as 后端
1326
+ participant DB as 数据库
1327
+
1328
+ U->>F: 点击注册/登录
1329
+ F->>C: 打开Clerk注册界面
1330
+ U->>C: 填写邮箱/密码
1331
+ C->>C: 验证用户信息
1332
+ C-->>F: 注册/登录成功
1333
+ Note over C: 返回clerk_user_id和email
1334
+
1335
+ F->>B: 用户注册/登录完成通知
1336
+ Note over B: 包含clerk_user_id, email, fingerprint_id
1337
+
1338
+ B->>R: 查询缓存: fingerprint_id
1339
+ R-->>B: 返回用户信息
1340
+
1341
+ alt 匿名用户升级为注册用户
1342
+ B->>DB: 更新Users表
1343
+ Note over B: 设置email, status=registered
1344
+ DB-->>B: 更新成功
1345
+
1346
+ B->>R: 更新缓存
1347
+ Note over B: 更新status和email信息
1348
+ R-->>B: 更新成功
1349
+
1350
+ B-->>F: 返回升级成功信息
1351
+ else 新注册用户
1352
+ B->>DB: 创建Users记录
1353
+ Note over B: user_id=UUID, email, status=registered
1354
+ DB-->>B: 创建成功
1355
+
1356
+ B->>DB: 创建Credits记录
1357
+ Note over B: balance_free=50, total_free_limit=50
1358
+ DB-->>B: 创建成功
1359
+
1360
+ B->>DB: 插入Credit_Usage记录
1361
+ Note over B: operation_type=recharge, credit_type=free
1362
+ DB-->>B: 插入成功
1363
+
1364
+ B->>R: 缓存用户信息
1365
+ R-->>B: 缓存成功
1366
+
1367
+ B-->>F: 返回新用户信息
1368
+ end
1369
+
1370
+ F-->>U: 显示用户仪表板
1371
+ `}/>
1372
+
1373
+ #### 6.7.3 注册用户登录时序图
1374
+
1375
+ <Mermaid
1376
+ title="注册用户登录时序图"
1377
+ chart={`
1378
+ sequenceDiagram
1379
+ participant U as 用户
1380
+ participant F as 前端
1381
+ participant C as Clerk
1382
+ participant R as Redis
1383
+ participant B as 后端
1384
+ participant DB as 数据库
1385
+
1386
+ U->>F: 点击登录
1387
+ F->>C: 打开Clerk登录界面
1388
+ U->>C: 输入邮箱/密码
1389
+ C->>C: 验证用户凭证
1390
+ C-->>F: 登录成功
1391
+ Note over C: 返回clerk_user_id和email
1392
+
1393
+ F->>B: 用户登录请求
1394
+ Note over B: 包含clerk_user_id和email
1395
+
1396
+ B->>R: 查询缓存: email
1397
+ R-->>B: 缓存命中, 返回用户信息
1398
+
1399
+ B->>R: 查询缓存: user_id积分信息
1400
+ R-->>B: 返回积分余额
1401
+
1402
+ B-->>F: 返回用户信息和积分余额
1403
+ F-->>U: 显示用户仪表板
1404
+
1405
+ Note over B: 异步更新缓存
1406
+ B->>DB: 查询最新用户信息
1407
+ DB-->>B: 返回用户数据
1408
+ B->>R: 更新缓存
1409
+ R-->>B: 更新成功
1410
+ `}/>
1411
+
1412
+ #### 6.7.4 用户注销时序图
1413
+
1414
+ <Mermaid
1415
+ title="用户注销时序图"
1416
+ chart={`
1417
+ sequenceDiagram
1418
+ participant U as 用户
1419
+ participant F as 前端
1420
+ participant C as Clerk
1421
+ participant R as Redis
1422
+ participant B as 后端
1423
+ participant DB as 数据库
1424
+
1425
+ U->>F: 点击注销
1426
+ F->>C: 调用Clerk注销
1427
+ C-->>F: 注销成功
1428
+
1429
+ F->>B: 用户注销请求
1430
+ Note over B: 包含user_id和fingerprint_id
1431
+
1432
+ B->>DB: 备份用户数据到UserBackup表
1433
+ DB-->>B: 备份成功
1434
+
1435
+ B->>DB: 硬删除Users及关联表记录
1436
+ DB-->>B: 删除成功
1437
+
1438
+ B->>R: 删除缓存: user_id相关数据
1439
+ R-->>B: 删除成功
1440
+
1441
+ B->>R: 删除缓存: fingerprint_id相关数据
1442
+ R-->>B: 删除成功
1443
+
1444
+ B-->>F: 注销完成
1445
+ F-->>U: 重定向到首页
1446
+ `}/>
1447
+
1448
+ #### 6.7.5 Redis缓存设计
1449
+
1450
+ ##### 6.7.5.1 缓存键设计
1451
+
1452
+ <Mermaid
1453
+ title="Redis缓存键设计"
1454
+ chart={`
1455
+ graph TD
1456
+ A[Redis缓存键设计] --> B[用户身份缓存]
1457
+ A --> C[积分余额缓存]
1458
+ A --> D[会话状态缓存]
1459
+
1460
+ B --> B1[fingerprint_id:user_info]
1461
+ B --> B2[email:user_info]
1462
+ B --> B3[clerk_user_id:user_info]
1463
+
1464
+ C --> C1[user_id:credits]
1465
+ C --> C2[user_id:credit_usage]
1466
+
1467
+ D --> D1[user_id:session]
1468
+ D --> D2[fingerprint_id:session]
1469
+ `}/>
1470
+
1471
+ ##### 6.7.5.2 缓存数据结构
1472
+
1473
+ ```json
1474
+ {
1475
+ "fingerprint_id:user_info": {
1476
+ "user_id": "uuid",
1477
+ "fingerprint_id": "fp_xxx",
1478
+ "email": "user@example.com",
1479
+ "status": "anonymous|registered",
1480
+ "created_at": "timestamp"
1481
+ },
1482
+ "user_id:credits": {
1483
+ "balance_free": 50,
1484
+ "total_free_limit": 50,
1485
+ "balance_paid": 100,
1486
+ "total_paid_limit": 100,
1487
+ "updated_at": "timestamp"
1488
+ },
1489
+ "user_id:session": {
1490
+ "clerk_user_id": "clerk_xxx",
1491
+ "last_active": "timestamp",
1492
+ "fingerprint_id": "fp_xxx"
1493
+ }
1494
+ }
1495
+ ```
1496
+
1497
+ ##### 6.7.5.3 缓存策略
1498
+
1499
+ | 缓存类型 | 键格式 | TTL | 更新策略 | 失效策略 |
1500
+ |---------|--------|-----|----------|----------|
1501
+ | 用户身份 | `fingerprint_id:user_info` | 24小时 | 用户信息变更时 | 用户注销时 |
1502
+ | 用户身份 | `email:user_info` | 24小时 | 用户信息变更时 | 用户注销时 |
1503
+ | 积分余额 | `user_id:credits` | 1小时 | 积分操作时 | 定时刷新 |
1504
+ | 会话状态 | `user_id:session` | 30分钟 | 用户活动时 | 会话超时 |
1505
+ | 积分使用记录 | `user_id:credit_usage` | 30分钟 | 积分操作时 | 定时刷新 |
1506
+
1507
+ ##### 6.7.5.4 缓存一致性保证
1508
+
1509
+ 1. **写入策略**: 先更新数据库, 再更新缓存
1510
+ 2. **读取策略**: 先查缓存, 缓存未命中则查数据库并更新缓存
1511
+ 3. **失效策略**: 数据变更时主动失效相关缓存
1512
+ 4. **降级策略**: 缓存服务不可用时直接访问数据库
1513
+ 5. **监控策略**: 监控缓存命中率和响应时间
1514
+
1515
+ ---
1516
+
1517
+ ## 7 Mermaid状态机图
1518
+
1519
+ ### 7.1. 用户生命周期状态机图
1520
+
1521
+ 以下状态机图描述了用户从匿名状态到注册、登录、注销、再注册的生命周期状态转换, 涵盖 `Users` 表的状态变化。
1522
+
1523
+ <Mermaid
1524
+ title="用户生命周期状态机图"
1525
+ chart={`
1526
+ stateDiagram-v2
1527
+ direction TB
1528
+
1529
+ [*] --> Anonymous
1530
+
1531
+ Anonymous --> Registering : 选择注册
1532
+ Anonymous --> [*] : 离开平台
1533
+
1534
+ Registering --> Registered : 提交信息完成注册
1535
+
1536
+ Registered --> LoggedIn : 登录成功
1537
+
1538
+ LoggedIn --> LoggedIn : 进行操作(订阅/使用)
1539
+ LoggedIn --> Anonymous : 登出
1540
+ LoggedIn --> Deleting : 发起注销请求
1541
+
1542
+ Deleting --> Backup : 执行数据备份
1543
+
1544
+ Backup --> Deleted : 备份完成, 执行硬删除
1545
+
1546
+ Deleted --> Anonymous : 重新访问平台
1547
+ `}/>
1548
+
1549
+ ### 7.2. 订阅状态机
1550
+
1551
+ 以下状态机表示用户订阅的生命周期, 包括用户操作或Stripe Webhook触发的状态转换。
1552
+
1553
+ <Mermaid
1554
+ title="订阅状态机图"
1555
+ chart={`
1556
+ stateDiagram-v2
1557
+ direction TB
1558
+
1559
+ [*] --> 订单创建: 用户发起支付
1560
+ 订单创建 --> 支付成功: Stripe支付完成
1561
+ 订单创建 --> 支付失败: 支付被拒/取消/超时
1562
+ 支付失败 --> 订单创建: 用户重试支付
1563
+ 支付失败 --> 订单失败: 用户放弃支付
1564
+ 支付成功 --> 订单完成: 积分充值成功
1565
+ 支付成功 --> 订单失败: 积分充值失败
1566
+
1567
+ state 订单终态 {
1568
+ direction TB
1569
+ 订单完成 --> 退款: 用户申请退款
1570
+ 订单完成 --> 冻结: 风控介入
1571
+ 退款 --> 订单完成: 人工成功
1572
+ 退款 --> 订单失败: 人工失败
1573
+ 冻结 --> 订单完成: 风控解除
1574
+ 冻结 --> 订单失败: 风控取消
1575
+ }
1576
+
1577
+ %% 状态说明
1578
+ note right of 订单创建
1579
+ 订单状态:
1580
+ - created: 订单已创建
1581
+ - success: 支付成功
1582
+ - failed: 支付失败
1583
+ - completed: 订单完成
1584
+ - refunded: 订单退款
1585
+ - frozen: 订单冻结
1586
+ - failed: 订单失败
1587
+ end note
1588
+
1589
+ note right of 订单终态
1590
+ 终态特性:
1591
+ - 订单完成: 不可逆转, 可退款
1592
+ - 订单失败: 不可逆转, 可人工重试
1593
+ - 订单退款: 不可逆转
1594
+ end note
1595
+ `}/>
1596
+
1597
+ ### 7.3. 积分使用状态机
1598
+
1599
+ 此状态机表示用户积分余额在使用过程中的状态。
1600
+
1601
+ <Mermaid
1602
+ title="积分使用状态机图"
1603
+ chart={`
1604
+ stateDiagram-v2
1605
+ direction TB
1606
+ [*] --> 余额 : 分配免费/付费积分
1607
+
1608
+ 余额 --> 检查余额 : 触发消耗
1609
+ 检查余额 --> 足够 : 余额够
1610
+ 检查余额 --> 不足 : 余额不够
1611
+
1612
+ 足够 --> 扣除 : 扣 free→paid
1613
+ 扣除 --> 已扣 : 扣除完成
1614
+ 已扣 --> 足够 : 有剩余
1615
+
1616
+ 不足 --> 提示 : 提醒购买
1617
+ 提示 --> 无操作 : 拒绝
1618
+ 无操作 --> [*] : 结束
1619
+ 提示 --> 充值 : 购买积分
1620
+ 充值 --> 余额 : 补充积分
1621
+
1622
+ `}/>
1623
+
1624
+ ### 7.4. 订单状态流转架构
1625
+
1626
+ 以下状态机图描述了订单的核心状态流转, 简化了中间状态, 专注于关键业务节点。
1627
+
1628
+ <Mermaid
1629
+ title="订单状态流转架构图"
1630
+ chart={`
1631
+ stateDiagram-v2
1632
+ [*] --> 订单创建: 用户发起支付
1633
+
1634
+ 订单创建 --> 支付成功: Stripe支付完成
1635
+ 订单创建 --> 支付失败: 支付被拒绝/取消/超时
1636
+
1637
+ 支付成功 --> 订单完成: 积分充值成功
1638
+ 支付成功 --> 订单失败: 积分充值失败
1639
+
1640
+ 支付失败 --> 订单创建: 用户重试支付
1641
+ 支付失败 --> 订单失败: 用户放弃支付
1642
+
1643
+ 订单完成 --> 退款refunded: 用户申请退款
1644
+ 订单完成 --> 订单冻结: 风控/合规介入
1645
+
1646
+ 退款refunded --> 订单完成: 人工处理成功
1647
+ 退款refunded --> 订单失败: 人工处理失败
1648
+
1649
+ 订单冻结 --> 订单完成: 风控解除
1650
+ 订单冻结 --> 订单失败: 风控取消
1651
+
1652
+ %% 终态定义
1653
+ state 终态 {
1654
+ 订单完成: 最终成功状态
1655
+ 订单失败: 最终失败状态
1656
+ 退款refunded: 最终退款状态
1657
+ }
1658
+
1659
+ %% 状态说明
1660
+ note right of 订单创建
1661
+ 核心订单状态:
1662
+ - created: 订单已创建
1663
+ - success: 支付成功
1664
+ - failed: 支付失败
1665
+ - completed: 订单完成
1666
+ - refunded: 订单退款
1667
+ - frozen: 订单冻结
1668
+ - failed: 订单失败
1669
+ end note
1670
+
1671
+ note right of 终态
1672
+ 终态特性:
1673
+ - 订单完成: 不可逆转, 可退款
1674
+ - 订单失败: 不可逆转, 可人工重试
1675
+ - 订单退款: 不可逆转
1676
+ end note
1677
+ `}/>
1678
+
1679
+ **订单状态详细说明: **
1680
+
1681
+ #### 7.4.1 订单状态定义
1682
+
1683
+ | 状态 | 状态值 | 描述 | 是否终态 | 可逆转性 |
1684
+ |------|--------|------|----------|----------|
1685
+ | 订单创建 | `created` | 订单已创建, 等待支付 | ❌ | ✅ 可重试 |
1686
+ | 支付成功 | `success` | Stripe支付成功 | ❌ | ✅ 可失败 |
1687
+ | 支付失败 | `failed` | 支付被拒绝/取消/超时 | ❌ | ✅ 可重试 |
1688
+ | 订单完成 | `completed` | 订单成功完成, 积分已充值 | ✅ | ❌ 不可逆转 |
1689
+ | 订单退款 | `refunded` | 订单已退款(部分或全额) | ✅ | ❌ 不可逆转 |
1690
+ | 订单冻结 | `frozen` | 风控或合规介入冻结 | ❌ | ✅ 可解除 |
1691
+ | 订单失败 | `failed` | 最终失败状态 | ✅ | ❌ 不可逆转 |
1692
+
1693
+ #### 7.4.2 状态转换规则
1694
+
1695
+ **自动转换规则: **
1696
+ - `created` → `success`: Stripe支付成功
1697
+ - `created` → `failed`: Stripe支付失败/取消/超时
1698
+ - `success` → `completed`: 积分充值成功
1699
+ - `success` → `failed`: 积分充值失败
1700
+
1701
+ **用户操作转换: **
1702
+ - `failed` → `created`: 用户重试支付
1703
+ - `completed` → `refunded`: 用户申请退款
1704
+
1705
+ **人工介入转换: **
1706
+ - `completed` → `frozen`: 风控冻结订单
1707
+ - `frozen` → `completed`: 风控解除冻结
1708
+ - `frozen` → `failed`: 风控取消订单
1709
+ - `refunded` → `completed`: 人工处理成功
1710
+ - `refunded` → `failed`: 人工处理失败
1711
+
1712
+ #### 7.4.3 终态管理策略
1713
+
1714
+ **终态定义: **
1715
+ 1. **订单完成 (`completed`)**: 最终成功状态, 不可逆转
1716
+ 2. **订单失败 (`failed`)**: 最终失败状态, 不可逆转
1717
+ 3. **退款refunded (`refunded`)**: 最终退款状态, 不可逆转
1718
+
1719
+ **终态特性: **
1720
+ - **不可逆转性**: 终态订单不能通过正常流程改变状态
1721
+ - **人工介入**: 只有通过人工介入才能改变终态
1722
+ - **审计要求**: 所有终态变更都需要记录操作日志
1723
+ - **权限控制**: 终态变更需要高级权限
1724
+
1725
+ #### 7.4.4 异常处理机制
1726
+
1727
+ **系统异常处理: **
1728
+ - **积分充值失败**: 自动重试3次, 失败后转为`failed`状态
1729
+ - **Stripe Webhook失败**: 使用幂等性机制, 避免重复处理
1730
+ - **数据库连接异常**: 使用事务回滚, 保持数据一致性
1731
+
1732
+ **人工介入场景: **
1733
+ - **风控介入**: 可疑交易冻结, 需要风控审核
1734
+ - **合规介入**: 违反政策订单, 需要合规处理
1735
+ - **技术介入**: 系统异常订单, 需要技术处理
1736
+ - **客服介入**: 用户投诉订单, 需要客服处理
1737
+
1738
+ **监控告警: **
1739
+ - **状态异常**: 订单在非终态停留时间过长
1740
+ - **转换异常**: 非法的状态转换尝试
1741
+ - **人工介入**: 终态订单被人工修改
1742
+ - **系统异常**: 订单处理失败率过高
1743
+
1744
+ ---
1745
+
1746
+ ## 8 订阅管理界面
1747
+
1748
+ ### 8.1 布局
1749
+ - **顶部**:
1750
+ - 显示当前免费积分(`balance_free`)和付费积分(`balance_paid`)。
1751
+ - 管理订阅按钮(重定向到Stripe客户门户)。
1752
+ - **主区域**:
1753
+ - 列出可用计划及其详细信息(价格、积分、功能)。
1754
+ - 提供购买一次性积分附加包的选项。
1755
+ - **历史记录区域**:
1756
+ - 交易历史选项卡(支付详情、发票)。
1757
+ - 积分使用历史选项卡(功能、消耗积分、时间戳)。
1758
+ - **底部**:
1759
+ - 支持、退款政策和服务条款链接。
1760
+
1761
+ ### 8.2 示例线框
1762
+ ```
1763
+ ----------------------------------------
1764
+ | 免费积分: 50 | 付费积分: 200 |
1765
+ | [管理订阅] |
1766
+ ----------------------------------------
1767
+ | 计划: |
1768
+ | - 基础版 (¥70/月, 100积分) |
1769
+ | - 专业版 (¥140/月, 250积分) |
1770
+ | - 企业版 (¥350/月, 1000积分) |
1771
+ | [购买附加积分] |
1772
+ ----------------------------------------
1773
+ | 历史记录: |
1774
+ | - 交易 | 积分使用 |
1775
+ | [交易列表] |
1776
+ ----------------------------------------
1777
+ ```
1778
+
1779
+ ---
1780
+
1781
+ ## 9 关键考虑
1782
+
1783
+ ### 9.1 数据连续性
1784
+ - 匿名用户通过 `user_id` 和 `fingerprint_id` 保持数据连续性, 注册后复用 `user_id`, 避免数据断裂。
1785
+ - 软删除策略允许用户注销后再次注册时保留部分历史数据(如积分使用记录)。
1786
+
1787
+ ### 9.2 GDPR 合规
1788
+ - 硬删除需确保所有关联表(`Subscriptions`、`Credits`、`Transactions`、`Credit_Usage`)的数据被移除。
1789
+ - 软删除可保留 `user_id` 和 `fingerprint_id`, 便于用户重新注册。
1790
+ - 用户删除操作需身份验证(密码或 SSO), 防止恶意操作。
1791
+ - 先硬删除用户表记录+备份, 再生成新user_id和fingerprint_id, 避免数据断裂; 其他数据异步清理归档, 满足GDPR合规
1792
+
1793
+ ### 9.3 安全性
1794
+ - **Fingerprint**: 通过限制每设备积分分配, 防止免费积分滥用。
1795
+ - **Stripe Webhook**: 验证签名以确保真实性。
1796
+ - **数据隐私**: 加密敏感数据(例如, `stripe_session_id`)并符合GDPR。
1797
+ - 使用 `fingerprint_id` 防止匿名用户滥用免费积分。
1798
+ - 注销时需身份验证(密码或 SSO), 防止恶意操作。
1799
+
1800
+ ### 9.4 可扩展性
1801
+ - **数据库索引**: 优化`user_id`、`fingerprint_id`和`stripe_session_id`的查询。
1802
+ - **缓存**: 使用Redis缓存积分余额和订阅状态。
1803
+ - **异步处理**: 异步处理Stripe Webhook, 避免用户操作延迟。
1804
+ - **负载均衡**: 使用负载均衡器将流量分配到多个后端服务器。
1805
+ - 为 `Users.user_id` 和 `fingerprint_id` 创建索引, 优化查询。
1806
+ - 使用缓存(如 Redis)存储活跃用户的 `Credits` 和 `Subscriptions` 数据。
1807
+
1808
+ ### 9.5 用户体验
1809
+ - **清晰提示**: 当积分耗尽时, 引导匿名用户注册。
1810
+ - **透明历史记录**: 提供详细的交易和使用日志。
1811
+ - **响应式界面**: 确保订阅界面适配移动设备。
1812
+
1813
+ ### 9.6 性能
1814
+ - 为 `Users.user_id` 和 `fingerprint_id` 创建索引, 优化查询。
1815
+ - 使用缓存(如 Redis)存储活跃用户的 `Credits` 和 `Subscriptions` 数据。
1816
+
1817
+ ### 9.7 补充说明
1818
+
1819
+ - **软删除 vs. 硬删除**:
1820
+ - 软删除更适合保留用户历史数据, 便于分析或用户返回。
1821
+ - 硬删除满足严格的隐私要求, 但可能导致数据丢失, 需谨慎使用。
1822
+ - **Fingerprint 更新**:
1823
+ - 如果用户更换设备, 系统可能需更新 `fingerprint_id`, 并通过登录验证关联到原有 `user_id`。
1824
+ - **错误处理**:
1825
+ - 注册时验证 `email` 唯一性, 防止重复注册。
1826
+ - 注销时确保关联数据(如活跃订阅)已处理(如通过 Stripe 取消)。
1827
+
1828
+ ---
1829
+
1830
+ ## 10 开发阶段规划
1831
+
1832
+ ### 10.1. 开发阶段规划图
1833
+
1834
+ <Mermaid
1835
+ title="开发阶段规划图"
1836
+ chart={`
1837
+ gantt
1838
+ title 订阅与积分系统开发计划
1839
+ dateFormat YYYY-MM-DD
1840
+ section 第一阶段: 基础设施
1841
+ 环境搭建 :done, env, 2025-08-08, 3d
1842
+ 数据库设计 :done, db, after env, 5d
1843
+ 基础架构 :done, arch, after db, 5d
1844
+
1845
+ section 第二阶段: 核心功能
1846
+ 用户管理系统 :active, user, after arch, 7d
1847
+ 积分系统核心 :credits, after user, 7d
1848
+ 缓存优化 :cache, after credits, 5d
1849
+
1850
+ section 第三阶段: 支付系统
1851
+ Stripe集成 :stripe, after cache, 7d
1852
+ 订阅系统 :sub, after stripe, 7d
1853
+ 交易管理 :trans, after sub, 5d
1854
+
1855
+ section 第四阶段: 集成优化
1856
+ 系统集成测试 :test, after trans, 5d
1857
+ 性能优化 :perf, after test, 5d
1858
+ 部署上线 :deploy, after perf, 5d
1859
+ `}/>
1860
+
1861
+ ### 10.2. 代码实施计划图
1862
+
1863
+ <Mermaid
1864
+ title="代码实施计划图"
1865
+ chart={`
1866
+ flowchart TB
1867
+ %% 自上而下布局 + 阶段入口/出口汇聚, 减少跨阶段交叉连线
1868
+
1869
+ %% 第一阶段
1870
+ subgraph P1[第一阶段 · 基础设施]
1871
+ direction TB
1872
+ A1[Prisma Schema 设计\nUsers/Subscriptions/Credits/Transactions/Credit_Usage/UserBackup]
1873
+ A2[数据库迁移与环境配置\n本地/云PG + 连接池]
1874
+ A3[Prisma Client 初始化\n类型生成/仓储封装]
1875
+ A4[基础设施基建\nEnv/日志/错误处理/配置管理]
1876
+ A1 --> A2 --> A3 --> A4 --> P1_OUT((P1 出口))
1877
+ end
1878
+
1879
+ %% 第二阶段
1880
+ subgraph P2[第二阶段 · 核心后端能力]
1881
+ direction TB
1882
+ P2_IN((P2 入口))
1883
+ B4[认证与会话 ·Clerk·\n后端校验/中间件/安全策略]
1884
+ B1[用户服务\n匿名初始化/注册升级/用户CRUD]
1885
+ B2[积分服务\n余额查询/优先扣减/充值/使用记录]
1886
+ B3[缓存层 ·Redis·\n键空间设计/Cache-Aside/一致性]
1887
+ P2_IN --> B4 --> B1 --> B3
1888
+ P2_IN --> B2 --> B3
1889
+ B3 --> P2_OUT((P2 出口))
1890
+ end
1891
+
1892
+ %% 第三阶段
1893
+ subgraph P3[第三阶段 · 支付与订阅]
1894
+ direction TB
1895
+ P3_IN((P3 入口))
1896
+ C1[Stripe SDK 初始化\n密钥/产品与价格映射]
1897
+ C2[Checkout Session 接口\n订阅/一次性购买]
1898
+ C3[Stripe Webhook 处理\ncheckout.session.completed / invoice.paid / charge.refunded]
1899
+ C4[订阅服务\n状态管理/升降级/取消/续费]
1900
+ C5[交易服务\n订单状态流转/审计/对账]
1901
+ P3_IN --> C1 --> C2 --> C3
1902
+ C3 --> C4
1903
+ C3 --> C5
1904
+ %% Webhook 内部负责触发积分充值/使用记录的写入(避免跨阶段连线)
1905
+ C5 --> P3_OUT((P3 出口))
1906
+ end
1907
+
1908
+ %% 第四阶段
1909
+ subgraph P4[第四阶段 · 前端界面]
1910
+ direction TB
1911
+ P4_IN((P4 入口))
1912
+ D1[订阅页面\n计划展示/结账触发/客户门户]
1913
+ D2[积分组件\n余额/使用历史/低余额提示]
1914
+ D3[用户中心\n订阅管理/交易与使用记录]
1915
+ P4_IN --> D1 --> D3
1916
+ P4_IN --> D2
1917
+ D1 --> P4_OUT((P4 出口))
1918
+ D2 --> P4_OUT
1919
+ D3 --> P4_OUT
1920
+ end
1921
+
1922
+ %% 横切能力
1923
+ subgraph P5[横切能力 · 异步与监控]
1924
+ direction TB
1925
+ P5_IN((P5 入口))
1926
+ E1[异步队列\nRedis Bull; 初期可EventEmitter]
1927
+ E2[幂等与重试\nWebhook/任务处理]
1928
+ E3[监控与告警\n日志/指标/失败重试与告警]
1929
+ P5_IN --> E1 --> E2 --> E3 --> P5_OUT((P5 出口))
1930
+ end
1931
+
1932
+ %% 测试与部署
1933
+ subgraph P6[测试与部署]
1934
+ direction TB
1935
+ P6_IN((P6 入口))
1936
+ T1[单元测试\n服务与仓储 >=80% 覆盖]
1937
+ T2[集成测试\n注册→订阅→充值→消费→退款·端到端]
1938
+ T3[CI/CD 流水线\nLint/Build/Test → Vercel 部署]
1939
+ T4[验收与回滚\n运行手册/回滚策略]
1940
+ P6_IN --> T1 --> T2 --> T3 --> T4 --> P6_OUT((P6 出口))
1941
+ end
1942
+
1943
+ %% 阶段衔接(仅相邻连接, 减少跨越)
1944
+ P1_OUT --> P2_IN
1945
+ P2_OUT --> P3_IN
1946
+ P3_OUT --> P4_IN
1947
+ P2_OUT --> P4_IN
1948
+ P2_OUT --> P5_IN
1949
+ P3_OUT --> P5_IN
1950
+ P5_OUT --> P6_IN
1951
+ P4_OUT --> P6_IN
1952
+
1953
+ %% 外部依赖(置于右侧, 虚线连接)
1954
+ subgraph EXT[外部依赖]
1955
+ direction TB
1956
+ X1[Stripe]
1957
+ X2[Clerk]
1958
+ X3[Redis]
1959
+ end
1960
+ C1 -.-> X1
1961
+ B4 -.-> X2
1962
+ B3 -.-> X3
1963
+ `}/>
1964
+
1965
+ ## 11 未来改进
1966
+ - **多货币支持**: 通过Stripe支持多种货币支付。
1967
+ - **促销积分**: 提供限时促销积分用于营销活动。
1968
+ - **通知**: 为低积分余额或订阅变化发送电子邮件/短信提醒。
1969
+ - **分析仪表板**: 为用户提供积分使用模式的洞察。
1970
+ - **API访问**: 提供API供开发者集成订阅系统(参考 https://x.ai/api)。
1971
+
1972
+ ---
1973
+
1974
+ ## 12 参考资料
1975
+ - Stripe文档: https://stripe.com/docs
1976
+ - Stripe Webhook文档: https://stripe.com/docs/webhooks
1977
+ - Fingerprint文档: https://fingerprint.com/docs
1978
+ - Mermaid语法: https://mermaid.js.org/syntax/sequenceDiagram.html
1979
+ - 示例UI灵感: https://pikttochart.com/generative-ai/editor/