@windrun-huaiin/backend-core 30.0.0 → 31.0.1

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.
Files changed (61) hide show
  1. package/README.md +95 -0
  2. package/dist/app/api/user/anonymous/init/fingerprint-only-route.d.ts +8 -0
  3. package/dist/app/api/user/anonymous/init/fingerprint-only-route.d.ts.map +1 -0
  4. package/dist/app/api/user/anonymous/init/fingerprint-only-route.js +20 -0
  5. package/dist/app/api/user/anonymous/init/fingerprint-only-route.mjs +18 -0
  6. package/dist/app/api/user/anonymous/init/route-shared.d.ts +10 -0
  7. package/dist/app/api/user/anonymous/init/route-shared.d.ts.map +1 -0
  8. package/dist/app/api/user/anonymous/init/route-shared.js +557 -0
  9. package/dist/app/api/user/anonymous/init/route-shared.mjs +555 -0
  10. package/dist/app/api/user/anonymous/init/route.d.ts +3 -3
  11. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -1
  12. package/dist/app/api/user/anonymous/init/route.js +6 -554
  13. package/dist/app/api/user/anonymous/init/route.mjs +7 -555
  14. package/dist/app/api/webhook/clerk/user/route.js +16 -16
  15. package/dist/app/api/webhook/clerk/user/route.mjs +16 -16
  16. package/dist/auth/auth-utils.d.ts +8 -23
  17. package/dist/auth/auth-utils.d.ts.map +1 -1
  18. package/dist/auth/auth-utils.js +8 -20
  19. package/dist/auth/auth-utils.mjs +8 -20
  20. package/dist/lib/money-price-config.d.ts +28 -28
  21. package/dist/lib/money-price-config.js +31 -31
  22. package/dist/lib/money-price-config.mjs +31 -31
  23. package/dist/lib/stripe-config.js +3 -3
  24. package/dist/lib/stripe-config.mjs +3 -3
  25. package/dist/prisma/prisma-transaction-util.js +1 -1
  26. package/dist/prisma/prisma-transaction-util.mjs +1 -1
  27. package/dist/prisma/prisma.d.ts.map +1 -1
  28. package/dist/prisma/prisma.js +18 -19
  29. package/dist/prisma/prisma.mjs +18 -19
  30. package/dist/services/aggregate/billing.aggregate.service.js +6 -6
  31. package/dist/services/aggregate/billing.aggregate.service.mjs +6 -6
  32. package/dist/services/aggregate/user.aggregate.service.d.ts +9 -9
  33. package/dist/services/aggregate/user.aggregate.service.js +16 -16
  34. package/dist/services/aggregate/user.aggregate.service.mjs +16 -16
  35. package/dist/services/database/constants.js +34 -34
  36. package/dist/services/database/constants.mjs +34 -34
  37. package/dist/services/database/credit.service.js +2 -2
  38. package/dist/services/database/credit.service.mjs +2 -2
  39. package/dist/services/database/transaction.service.js +1 -1
  40. package/dist/services/database/transaction.service.mjs +1 -1
  41. package/dist/services/database/user.service.js +2 -2
  42. package/dist/services/database/user.service.mjs +2 -2
  43. package/dist/services/stripe/webhook-handler.js +5 -5
  44. package/dist/services/stripe/webhook-handler.mjs +5 -5
  45. package/package.json +18 -6
  46. package/src/app/api/user/anonymous/init/fingerprint-only-route.ts +14 -0
  47. package/src/app/api/user/anonymous/init/route-shared.ts +710 -0
  48. package/src/app/api/user/anonymous/init/route.ts +7 -712
  49. package/src/app/api/webhook/clerk/user/route.ts +17 -17
  50. package/src/auth/auth-utils.ts +8 -23
  51. package/src/lib/money-price-config.ts +31 -32
  52. package/src/lib/stripe-config.ts +3 -3
  53. package/src/prisma/prisma-transaction-util.ts +1 -1
  54. package/src/prisma/prisma.ts +18 -19
  55. package/src/services/aggregate/billing.aggregate.service.ts +7 -7
  56. package/src/services/aggregate/user.aggregate.service.ts +16 -16
  57. package/src/services/database/constants.ts +34 -34
  58. package/src/services/database/credit.service.ts +2 -2
  59. package/src/services/database/transaction.service.ts +1 -1
  60. package/src/services/database/user.service.ts +2 -2
  61. package/src/services/stripe/webhook-handler.ts +5 -5
@@ -11,7 +11,7 @@ import { headers } from 'next/headers';
11
11
  import { NextRequest, NextResponse } from 'next/server';
12
12
  import { Webhook } from 'svix';
13
13
 
14
- // 定义Clerk Webhook事件类型
14
+ // Clerk webhook event type definition
15
15
  interface ClerkWebhookEvent {
16
16
  data: {
17
17
  id: string;
@@ -41,12 +41,12 @@ interface ClerkWebhookEvent {
41
41
  }
42
42
  export async function POST(request: NextRequest) {
43
43
  try {
44
- // 获取原始请求体
44
+ // Read the raw request body.
45
45
  const rawBody = await request.text();
46
46
 
47
47
  let event: ClerkWebhookEvent;
48
48
 
49
- // 开发环境跳过签名校验
49
+ // Skip signature verification in development.
50
50
  if (process.env.NODE_ENV === 'development') {
51
51
  console.log('Development mode: skipping webhook signature verification');
52
52
  try {
@@ -59,13 +59,13 @@ export async function POST(request: NextRequest) {
59
59
  );
60
60
  }
61
61
  } else {
62
- // 生产环境进行签名校验
62
+ // Verify the signature in production.
63
63
  const headerPayload = await headers();
64
64
  const svix_id = headerPayload.get('svix-id');
65
65
  const svix_timestamp = headerPayload.get('svix-timestamp');
66
66
  const svix_signature = headerPayload.get('svix-signature');
67
67
 
68
- // 如果缺少必要的header,返回错误
68
+ // Reject requests missing required headers.
69
69
  if (!svix_id || !svix_timestamp || !svix_signature) {
70
70
  return NextResponse.json(
71
71
  { error: 'Missing webhook headers' },
@@ -73,7 +73,7 @@ export async function POST(request: NextRequest) {
73
73
  );
74
74
  }
75
75
 
76
- // 获取webhook signing secret
76
+ // Load the webhook signing secret.
77
77
  const webhookSecret = process.env.CLERK_WEBHOOK_SECRET;
78
78
  if (!webhookSecret) {
79
79
  console.error('CLERK_WEBHOOK_SECRET is not configured');
@@ -83,7 +83,7 @@ export async function POST(request: NextRequest) {
83
83
  );
84
84
  }
85
85
 
86
- // 验证webhook签名
86
+ // Verify the webhook signature.
87
87
  try {
88
88
  const wh = new Webhook(webhookSecret);
89
89
  event = wh.verify(rawBody, {
@@ -110,7 +110,7 @@ export async function POST(request: NextRequest) {
110
110
  let processingResult = { success: true, message: 'Event processed successfully' };
111
111
 
112
112
  try {
113
- // 处理不同的事件类型
113
+ // Dispatch by event type.
114
114
  const { type } = event;
115
115
 
116
116
  switch (type) {
@@ -155,7 +155,7 @@ export async function POST(request: NextRequest) {
155
155
  }
156
156
 
157
157
  /**
158
- * 处理用户创建事件
158
+ * Handle the user.created event.
159
159
  */
160
160
  async function handleUserCreated(event: ClerkWebhookEvent) {
161
161
  const { data } = event;
@@ -174,7 +174,7 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
174
174
  userName
175
175
  });
176
176
 
177
- // 检查必要参数
177
+ // Validate required fields.
178
178
  if (!fingerprintId) {
179
179
  console.error('Missing fingerprintId in webhook data, process flow error');
180
180
  return;
@@ -186,17 +186,17 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
186
186
  }
187
187
 
188
188
  try {
189
- // 按fingerprintId查询该设备的所有未注销过的用户记录,注销过的记录相当于是死数据
189
+ // Find all non-deleted users for this device fingerprint.
190
190
  const existingUsers = await userService.findListByFingerprintId(fingerprintId);
191
191
  if (!existingUsers || existingUsers.length === 0) {
192
192
  console.error('Invalid fingerprintId in webhook data, process flow error');
193
193
  return;
194
194
  }
195
195
 
196
- // 查找email相同的记录
196
+ // Find an existing user with the same email.
197
197
  const sameEmailUser = existingUsers.find(user => user.email === email);
198
198
  if (sameEmailUser) {
199
- // 同一账号,检查是否需要更新clerkUserId
199
+ // Same account; update clerkUserId if needed.
200
200
  if (sameEmailUser.clerkUserId !== clerkUserId) {
201
201
  await userService.updateUser(sameEmailUser.userId, { clerkUserId, userName: userName, status: UserStatus.REGISTERED });
202
202
  console.log(`Updated clerkUserId for user ${sameEmailUser.userId}`);
@@ -206,16 +206,16 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
206
206
  return;
207
207
  }
208
208
 
209
- // 查找匿名用户(email为空且clerkUserId为空)
209
+ // Find an anonymous user with no email or clerkUserId.
210
210
  const anonymousUser = existingUsers.find(user => !user.email && !user.clerkUserId && user.status === UserStatus.ANONYMOUS );
211
211
  if (anonymousUser) {
212
- // 匿名用户升级
212
+ // Upgrade the anonymous user.
213
213
  await userAggregateService.upgradeToRegistered(anonymousUser.userId, email, clerkUserId, userName);
214
214
  console.log(`Successfully upgraded anonymous user ${anonymousUser.userId} to registered user`);
215
215
  return;
216
216
  }
217
217
 
218
- // 同设备新账号,创建新用户
218
+ // New account on the same device; create a new user.
219
219
  await userAggregateService.createNewRegisteredUser(clerkUserId, email, fingerprintId, userName);
220
220
  console.log(`Created new user for device ${fingerprintId} with email ${email}`);
221
221
 
@@ -226,7 +226,7 @@ async function handleUserCreated(event: ClerkWebhookEvent) {
226
226
  }
227
227
 
228
228
  /**
229
- * 处理用户删除事件
229
+ * Handle the user.deleted event.
230
230
  */
231
231
  async function handleUserDeleted(event: ClerkWebhookEvent) {
232
232
  const { data } = event;
@@ -4,9 +4,6 @@ import { userService } from '../services/database/index';
4
4
  import { User } from '../services/database/prisma-model-type';
5
5
  import { AUTH_ERRORS, AUTH_HEADERS, type AuthProvider, type ProviderIdentity } from './auth-shared';
6
6
 
7
- /**
8
- * 认证结果类型
9
- */
10
7
  export interface AuthResult {
11
8
  userId: string;
12
9
  user: User;
@@ -15,7 +12,7 @@ export interface AuthResult {
15
12
  }
16
13
 
17
14
  /**
18
- * 从中间件设置的 Clerk ID 获取完整用户信息
15
+ * Fetch User's info from header field by Middleware
19
16
  */
20
17
  export async function getAuthenticatedUser(req: NextRequest): Promise<AuthResult> {
21
18
  try {
@@ -43,7 +40,7 @@ export async function getAuthenticatedUser(req: NextRequest): Promise<AuthResult
43
40
  }
44
41
 
45
42
  /**
46
- * 要求用户必须已认证,返回用户ID
43
+ * Require Auth, success back user's id
47
44
  */
48
45
  export async function requireAuth(req: NextRequest): Promise<string> {
49
46
  const auth = await getAuthenticatedUser(req);
@@ -51,15 +48,15 @@ export async function requireAuth(req: NextRequest): Promise<string> {
51
48
  }
52
49
 
53
50
  /**
54
- * 要求用户必须已认证,返回完整用户信息
51
+ * Require Auth, success back user's info
55
52
  */
56
53
  export async function requireAuthWithUser(req: NextRequest): Promise<AuthResult> {
57
54
  return await getAuthenticatedUser(req);
58
55
  }
59
56
 
60
57
  /**
61
- * 服务端场景下获取当前已认证身份(如果存在)
62
- * 适用于只依赖登录态、不需要查询业务用户的逻辑
58
+ * Only use in server side
59
+ * Server Component / Server Action, just need user's login status
63
60
  */
64
61
  export async function getOptionalServerAuthIdentity(): Promise<ProviderIdentity | null> {
65
62
  try {
@@ -79,8 +76,8 @@ export async function getOptionalServerAuthIdentity(): Promise<ProviderIdentity
79
76
  }
80
77
 
81
78
  /**
82
- * 服务端场景下获取当前已认证用户(如果存在)
83
- * 适用于 Server Component / Server Action 中基于登录态控制展示的逻辑
79
+ * Only use in server side
80
+ * Server Component / Server Action, need user's login status and user's data, will check db
84
81
  */
85
82
  export async function getOptionalServerAuthUser(): Promise<AuthResult | null> {
86
83
  try {
@@ -107,7 +104,7 @@ export async function getOptionalServerAuthUser(): Promise<AuthResult | null> {
107
104
  }
108
105
 
109
106
  /**
110
- * API Route版本的认证工具函数
107
+ * API Route Auth Util
111
108
  */
112
109
  export class ApiAuthUtils {
113
110
  private req: NextRequest;
@@ -116,23 +113,14 @@ export class ApiAuthUtils {
116
113
  this.req = req;
117
114
  }
118
115
 
119
- /**
120
- * 要求用户必须已认证,返回用户ID
121
- */
122
116
  async requireAuth(): Promise<string> {
123
117
  return await requireAuth(this.req);
124
118
  }
125
119
 
126
- /**
127
- * 要求用户必须已认证,返回完整用户信息
128
- */
129
120
  async requireAuthWithUser(): Promise<AuthResult> {
130
121
  return await requireAuthWithUser(this.req);
131
122
  }
132
123
 
133
- /**
134
- * 获取用户ID(如果已认证)
135
- */
136
124
  async getUserId(): Promise<string | null> {
137
125
  try {
138
126
  const auth = await getAuthenticatedUser(this.req);
@@ -142,9 +130,6 @@ export class ApiAuthUtils {
142
130
  }
143
131
  }
144
132
 
145
- /**
146
- * 获取完整用户信息(如果已认证)
147
- */
148
133
  async getUser(): Promise<AuthResult | null> {
149
134
  try {
150
135
  return await getAuthenticatedUser(this.req);
@@ -12,7 +12,7 @@ export const moneyPriceConfig: MoneyPriceConfig = {
12
12
  stripe: {
13
13
  provider: 'stripe',
14
14
  enabled: true,
15
- // 订阅模式产品
15
+ // Subscription products
16
16
  subscriptionProducts: {
17
17
  F1: {
18
18
  key: 'F1',
@@ -70,7 +70,7 @@ export const moneyPriceConfig: MoneyPriceConfig = {
70
70
  }
71
71
  }
72
72
  },
73
- // 积分包产品
73
+ // Credit pack products
74
74
  creditPackProducts: {
75
75
  F1: {
76
76
  key: 'F1',
@@ -106,56 +106,56 @@ export const moneyPriceConfig: MoneyPriceConfig = {
106
106
  }
107
107
  };
108
108
 
109
- // ============ 应用层wrapper - 隐藏moneyPriceConfig细节 ============
109
+ // ============ Application-level wrappers that hide moneyPriceConfig details ============
110
110
 
111
111
  /**
112
- * 获取当前激活的支付供应商配置
112
+ * Get the currently active payment provider configuration.
113
113
  *
114
- * 🔒 安全设计:
115
- * - wrapper函数隐藏moneyPriceConfig
116
- * - util层负责从config中提取激活的provider配置
117
- * - 外部只能通过这个wrapper访问,看不到config对象
114
+ * Security design:
115
+ * - Wrapper functions keep moneyPriceConfig private.
116
+ * - Utility functions extract the active provider configuration from the config.
117
+ * - External callers can access only this wrapper, not the full config object.
118
118
  *
119
- * @returns 当前激活的支付供应商配置
119
+ * @returns The currently active payment provider configuration.
120
120
  */
121
121
  export function getActiveProviderConfig(): PaymentProviderConfig {
122
122
  return getActiveProviderConfigUtil(moneyPriceConfig);
123
123
  }
124
124
 
125
125
  /**
126
- * 根据 priceId 获取对应的积分数量
126
+ * Get the credit amount for a price ID.
127
127
  *
128
- * 🔒 安全设计:
129
- * - wrapper函数隐藏moneyPriceConfig
130
- * - util层负责解析config并提取结果
131
- * - 外部只能通过这个wrapper访问,看不到config对象
128
+ * Security design:
129
+ * - Wrapper functions keep moneyPriceConfig private.
130
+ * - Utility functions parse the config and extract the result.
131
+ * - External callers can access only this wrapper, not the full config object.
132
132
  *
133
- * @param priceId - 查询的价格ID
134
- * @param _provider - 保留参数(向后兼容),暂未使用
135
- * @returns 对应的积分数量,或null
133
+ * @param priceId - Price ID to query.
134
+ * @param _provider - Reserved for backward compatibility; currently unused.
135
+ * @returns The matching credit amount, or null.
136
136
  */
137
137
  export function getCreditsFromPriceId(priceId?: string, _provider?: string): number | null {
138
138
  return getCreditsFromPriceIdUtil(priceId, moneyPriceConfig);
139
139
  }
140
140
 
141
141
  /**
142
- * 根据查询参数获取价格配置
142
+ * Get price configuration by query parameters.
143
143
  *
144
- * 支持三种查询方式:
145
- * 1. priceId 查询:getPriceConfig(priceId='price_xxx')
146
- * 2. plan billingType 查询:getPriceConfig(undefined, 'P2', 'monthly')
147
- * 3. plan 查询:getPriceConfig(undefined, 'P2')
144
+ * Supported query modes:
145
+ * 1. By priceId: getPriceConfig(priceId='price_xxx')
146
+ * 2. By plan and billingType: getPriceConfig(undefined, 'P2', 'monthly')
147
+ * 3. By plan: getPriceConfig(undefined, 'P2')
148
148
  *
149
- * 🔒 安全设计:
150
- * - wrapper函数隐藏moneyPriceConfig
151
- * - util层负责解析config并提取匹配的结果
152
- * - 外部只能通过这个wrapper访问,看不到config对象
149
+ * Security design:
150
+ * - Wrapper functions keep moneyPriceConfig private.
151
+ * - Utility functions parse the config and extract the matching result.
152
+ * - External callers can access only this wrapper, not the full config object.
153
153
  *
154
- * @param priceId - 查询的价格ID(可选)
155
- * @param plan - 查询的套餐名称如'P2''U3'(可选)
156
- * @param billingType - 查询的计费类型如'monthly''yearly'(可选)
157
- * @param _provider - 保留参数(向后兼容),暂未使用
158
- * @returns 匹配的价格配置,包含计算好的元数据(priceNamedescriptioninterval
154
+ * @param priceId - Optional price ID to query.
155
+ * @param plan - Optional plan name, such as 'P2' or 'U3'.
156
+ * @param billingType - Optional billing type, such as 'monthly' or 'yearly'.
157
+ * @param _provider - Reserved for backward compatibility; currently unused.
158
+ * @returns The matching price config with derived metadata: priceName, description, and interval.
159
159
  */
160
160
  export function getPriceConfig(
161
161
  priceId?: string,
@@ -165,4 +165,3 @@ export function getPriceConfig(
165
165
  ): (EnhancePricePlan & { priceName: string; description: string; interval?: string }) | null {
166
166
  return getPriceConfigUtil(priceId, plan, billingType, moneyPriceConfig);
167
167
  }
168
-
@@ -97,7 +97,7 @@ export const createCheckoutSession = async (
97
97
 
98
98
  // One-time payment specific configuration
99
99
  if (isSubscriptionMode) {
100
- // 在这里注入订单元数据,以保证后续事件处理能根据订单去匹配处理,只能在订阅模式里设置数据,否则Stripe报错
100
+ // Attach order metadata for later event matching. Stripe only allows this in subscription mode.
101
101
  sessionParams.subscription_data = subscriptionData;
102
102
  } else {
103
103
  // One-time payments don't create invoices
@@ -129,7 +129,7 @@ export const createCheckoutSession = async (
129
129
  }
130
130
  };
131
131
 
132
- // 根据发票ID去查支付ID
132
+ // Resolve the payment ID from an invoice ID.
133
133
  export const fetchPaymentId = async (invoiceId: string ): Promise<string> => {
134
134
  const fullInvoice = await getStripe().invoices.retrieve(invoiceId, {
135
135
  expand: ['payments']
@@ -193,7 +193,7 @@ export const createOrGetCustomer = async (params: {
193
193
  }
194
194
  }
195
195
 
196
- // 创建新客户
196
+ // Create a new customer.
197
197
  const customerParams: Stripe.CustomerCreateParams = {
198
198
  metadata: {
199
199
  user_id: userId,
@@ -1,7 +1,7 @@
1
1
  import { getBackendCorePrisma } from './prisma';
2
2
  import type { Prisma } from '@core/db/prisma-model-type';
3
3
 
4
- // 事务工具类,回滚时打印回滚日志
4
+ // Transaction helper that logs rollback details on failure.
5
5
  export async function runInTransaction<T>(
6
6
  fn: (tx: Prisma.TransactionClient) => Promise<T>,
7
7
  operationName?: string
@@ -41,7 +41,7 @@ const globalForPrisma = globalThis as unknown as {
41
41
  __prisma_ssl_warning_logged?: boolean;
42
42
  };
43
43
 
44
- // ==================== 日志配置 ====================
44
+ // ==================== Logging Configuration ====================
45
45
  const getLogConfig = () => {
46
46
  if (process.env.PRISMA_DEBUG === 'true') {
47
47
  return [
@@ -125,42 +125,41 @@ function registerDevelopmentQueryLogger(prismaClient: BackendCoreHostPrismaClien
125
125
  globalForPrisma[ID_KEY] = listenerId;
126
126
  console.log(`Prisma Query Logger Registered | Listener ID: ${listenerId} | Instance ID: ${instanceId}`);
127
127
 
128
- // --- 自定义SQL拼接 ---
128
+ // --- Custom SQL interpolation ---
129
129
  const interpolate = (query: string, params: string) => {
130
- // 1. 【核心修改】:安全检查和参数解析
130
+ // 1. Validate and parse parameters safely.
131
131
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
132
  let parameters: any[] = [];
133
133
  try {
134
- // 尝试解析 params 字符串
135
- // 如果 params 是空字符串 "",或者不是有效的 JSON,这里会捕获错误
134
+ // Parse the params string. Empty strings or invalid JSON are handled by the catch block.
136
135
  parameters = params && params.length > 0 ? JSON.parse(params) : [];
137
136
  // eslint-disable-next-line unused-imports/no-unused-vars
138
137
  } catch (e) {
139
- // 如果无法解析,则直接返回原始查询,跳过替换
138
+ // If parsing fails, return the original query without interpolation.
140
139
  return query;
141
140
  }
142
141
 
143
- // 确保 parameters 是一个数组
142
+ // Ensure parameters is an array.
144
143
  if (!Array.isArray(parameters)) {
145
- console.warn('Prisma params解析结果不是数组,跳过参数替换。Result:', parameters);
144
+ console.warn('Prisma params did not parse to an array; skipping parameter interpolation. Result:', parameters);
146
145
  return query;
147
146
  }
148
147
 
149
- // 如果没有参数,直接返回查询
148
+ // If there are no parameters, return the query as-is.
150
149
  if (parameters.length === 0) {
151
150
  return query;
152
151
  }
153
152
 
154
- // 2. 将参数列表的值进行安全的字符串化处理
153
+ // 2. Safely stringify parameter values.
155
154
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
155
  const safeValues = parameters.map((p: any) => {
157
156
  if (p === null) return 'NULL';
158
- // 对字符串类型的值加上单引号并转义(这是SQL安全的关键)
157
+ // Quote and escape string values for readable SQL logging.
159
158
  if (typeof p === 'string') return `'${p.replace(/'/g, "''")}'`;
160
- return p; // 数字、布尔值等直接返回
159
+ return p; // Numbers, booleans, and similar values can be returned directly.
161
160
  });
162
161
 
163
- // 3. 循环替换 $1, $2, ...
162
+ // 3. Replace $1, $2, ... placeholders.
164
163
  let sql = query;
165
164
  for (let i = 0; i < safeValues.length; i++) {
166
165
  const placeholder = new RegExp('\\$' + (i + 1) + '(?!\\d)', 'g');
@@ -176,23 +175,23 @@ function registerDevelopmentQueryLogger(prismaClient: BackendCoreHostPrismaClien
176
175
  const interpolatedSql = interpolate(event.query, event.params);
177
176
 
178
177
  const clean = interpolatedSql
179
- .replace(/"[^"]+"\./g, '') // "".
180
- .replace(/= '([^']+)'/g, `= '$1'`) // 已经替换成单引号,此处可以优化
181
- .replace(/"/g, ''); // 彻底灭双引号
178
+ .replace(/"[^"]+"\./g, '') // Remove "table". prefixes.
179
+ .replace(/= '([^']+)'/g, `= '$1'`) // Keep normalized quoted values.
180
+ .replace(/"/g, ''); // Remove remaining double quotes.
182
181
 
183
182
  console.log('─'.repeat(60));
184
183
  console.log(`Prisma Instance ID: ${instanceId} | Listener ID: ${listenerId}`);
185
184
  console.log(`${clean};`);
186
- console.log(`⏰ 耗时: ${ms}ms, ${slow}`);
185
+ console.log(`Duration: ${ms}ms, ${slow}`);
187
186
  };
188
- // 注册包装后的 handler
187
+ // Register the wrapped handler.
189
188
  prismaClient.$on?.('query' as never, wrappedHandler);
190
189
 
191
190
  globalForPrisma[REGISTERED_KEY] = true;
192
191
  }
193
192
  }
194
193
 
195
- // ==================== 便捷方法, 入参事务客户端不存在或者不传, 就返回全局非事务客户端 ====================
194
+ // ==================== Client Helper: fall back to the global non-transaction client when no transaction client is provided ====================
196
195
  export function checkAndFallbackWithNonTCClient(tx?: Prisma.TransactionClient): Prisma.TransactionClient | BackendCorePrismaClient {
197
196
  return tx ?? getBackendCorePrisma();
198
197
  }
@@ -35,7 +35,7 @@ type BasicOrderContext = {
35
35
  };
36
36
 
37
37
  type RenewalOrderContext = BasicOrderContext & {
38
- // 续订时以Stripe的发票价格为准
38
+ // Use Stripe invoice price as the source of truth for renewal
39
39
  amountPaidCents: number;
40
40
  currency: string;
41
41
  }
@@ -64,7 +64,7 @@ class BillingAggregateService {
64
64
  ): Promise<void> {
65
65
  await runInTransaction(async (tx) => {
66
66
  const now = new Date();
67
- // 订阅截止时间统一为到期日最后1s
67
+ // Set subscription expiry to the last second of the expiration date
68
68
  const originSubPeriodEnd = context.periodEnd;
69
69
  const specialEnd = originSubPeriodEnd ? new Date(originSubPeriodEnd.setHours(23, 59, 59, 999)) : originSubPeriodEnd;
70
70
  const updatedSubscription = await subscriptionService.updateSubscription(
@@ -237,7 +237,7 @@ class BillingAggregateService {
237
237
  },
238
238
  tx
239
239
  );
240
- // 订阅截止时间统一为到期日最后1s
240
+ // Set subscription expiry to the last second of the expiration date
241
241
  const originSubPeriodEnd = context.periodEnd;
242
242
  const specialEnd = originSubPeriodEnd ? new Date(originSubPeriodEnd.setHours(23, 59, 59, 999)) : originSubPeriodEnd;
243
243
 
@@ -365,7 +365,7 @@ class BillingAggregateService {
365
365
  ): Promise<void> {
366
366
  await runInTransaction(async (tx) => {
367
367
  if (params.isUserCancel) {
368
- // 记录用户取消订阅的时间信息
368
+ // Record the time when the user unsubscribes
369
369
  await transactionService.update(
370
370
  params.orderId,
371
371
  {
@@ -391,7 +391,7 @@ class BillingAggregateService {
391
391
  context: SubscriptionCancelContext
392
392
  ): Promise<void> {
393
393
  await runInTransaction(async (tx) => {
394
- // 更新订单, 记录取消信息
394
+ // Update the order and log cancellation details
395
395
  await transactionService.update(
396
396
  context.orderId,
397
397
  {
@@ -400,10 +400,10 @@ class BillingAggregateService {
400
400
  },
401
401
  tx
402
402
  )
403
- // 更新订阅信息
403
+ // Update subscription information
404
404
  await subscriptionService.updateStatus(context.subIdKey, SubscriptionStatus.CANCELED, tx);
405
405
 
406
- // 清理积分并留痕
406
+ // Clear credits and keep audit trail
407
407
  await creditService.purgePaidCredit(context.userId, 'cancel_subscription_purge', context.orderId, tx);
408
408
  })
409
409
  }
@@ -40,17 +40,17 @@ export class UserAggregateService {
40
40
  }
41
41
 
42
42
  /**
43
- * 创建新的注册用户
43
+ * Create a new registered user
44
44
  *
45
- * 初始化步骤(与 credit 平行):
46
- * 1. 创建 User 记录
47
- * 2. 初始化 Credit 记录(免费积分)
48
- * 3. 初始化 Subscription 记录(占位符,status=INCOMPLETE
49
- * 4. 记录 CreditUsage(审计)
45
+ * Initialization steps (parallel to credit):
46
+ * 1. Create User record
47
+ * 2. Initialize Credit record (free credits)
48
+ * 3. Initialize Subscription record (placeholder, status=INCOMPLETE)
49
+ * 4. Record CreditUsage (audit)
50
50
  *
51
- * 后续当用户通过 Stripe 订阅时:
52
- * - session.completed invoice.paid UPDATE subscription 记录
53
- * - 不需要 CREATE,只需 UPDATE 确保逻辑一致
51
+ * When the user subscribes via Stripe later:
52
+ * - session.completed or invoice.paid will UPDATE the subscription record
53
+ * - No CREATE needed, only UPDATE to ensure logical consistency
54
54
  */
55
55
  async createNewRegisteredUser(
56
56
  clerkUserId: string,
@@ -89,7 +89,7 @@ export class UserAggregateService {
89
89
  });
90
90
  }
91
91
 
92
- // 注意积分审查日志的处理
92
+ // Note: Handle credit review logs
93
93
  async upgradeToRegistered(
94
94
  userId: string,
95
95
  email: string,
@@ -107,9 +107,9 @@ export class UserAggregateService {
107
107
  tx
108
108
  );
109
109
 
110
- // 先清空匿名积分并审计日志留痕
110
+ // Clear anonymous credits first and audit for traceability
111
111
  await creditService.purgeFreeCredit(userId, 'user_registered_purge', userId, tx);
112
- // 再初始化完成注册获得免费积分
112
+ // Then initialize free credits upon successful registration
113
113
  const credit = await creditService.initializeCreditWithFree(
114
114
  {
115
115
  userId: updateUser.userId,
@@ -128,21 +128,21 @@ export class UserAggregateService {
128
128
 
129
129
  async handleUserUnregister(clerkUserId: string): Promise<string | null> {
130
130
  return runInTransaction(async (tx) => {
131
- // 根据clerkUserId查找用户
131
+ // query DB user
132
132
  const user = await userService.findByClerkUserId(clerkUserId, tx);
133
133
  if (!user) {
134
134
  console.log(`User with clerkUserId ${clerkUserId} not found`);
135
135
  return null;
136
136
  }
137
137
  const userId = user.userId;
138
- // 更改用户状态,保留user信息尤其是FingerprintId,防止反复注册薅羊毛
138
+ // Update user status and retain user info (especially FingerprintId) to prevent repeated registration abuse
139
139
  await userService.unregister(user.userId, tx);
140
- // 清空积分
140
+ // Clear credits
141
141
  await creditService.purgeCredit(userId, 'soft_delete_user', userId, tx);
142
142
 
143
143
  const subscription = await subscriptionService.getActiveSubscription(userId, tx);
144
144
  if (subscription) {
145
- // 如果有订阅信息,则要更新
145
+ // Update subscription info if it exists
146
146
  await subscriptionService.cancelSubscription(subscription.id, true, tx);
147
147
  }
148
148