@windrun-huaiin/backend-core 14.1.1 → 14.3.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.
Files changed (35) hide show
  1. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -1
  2. package/dist/app/api/user/anonymous/init/route.js +60 -13
  3. package/dist/app/api/user/anonymous/init/route.mjs +61 -14
  4. package/dist/index.js +4 -1
  5. package/dist/index.mjs +3 -1
  6. package/dist/lib/money-price-helper.js +2 -1
  7. package/dist/lib/money-price-helper.mjs +3 -2
  8. package/dist/services/aggregate/anonymous.aggregate.service.d.ts +21 -0
  9. package/dist/services/aggregate/anonymous.aggregate.service.d.ts.map +1 -0
  10. package/dist/services/aggregate/anonymous.aggregate.service.js +87 -0
  11. package/dist/services/aggregate/anonymous.aggregate.service.mjs +85 -0
  12. package/dist/services/aggregate/index.d.ts +1 -0
  13. package/dist/services/aggregate/index.d.ts.map +1 -1
  14. package/dist/services/aggregate/index.js +2 -0
  15. package/dist/services/aggregate/index.mjs +1 -0
  16. package/dist/services/context/index.d.ts +1 -0
  17. package/dist/services/context/index.d.ts.map +1 -1
  18. package/dist/services/context/index.js +2 -1
  19. package/dist/services/context/index.mjs +2 -1
  20. package/dist/services/context/user-context-finalizer.d.ts +13 -0
  21. package/dist/services/context/user-context-finalizer.d.ts.map +1 -0
  22. package/dist/services/context/user-context-finalizer.js +74 -0
  23. package/dist/services/context/user-context-finalizer.mjs +72 -0
  24. package/dist/services/context/user-context-service.d.ts +0 -6
  25. package/dist/services/context/user-context-service.d.ts.map +1 -1
  26. package/dist/services/context/user-context-service.js +0 -64
  27. package/dist/services/context/user-context-service.mjs +1 -64
  28. package/package.json +7 -7
  29. package/src/app/api/user/anonymous/init/route.ts +72 -12
  30. package/src/lib/money-price-helper.ts +3 -3
  31. package/src/services/aggregate/anonymous.aggregate.service.ts +113 -0
  32. package/src/services/aggregate/index.ts +1 -0
  33. package/src/services/context/index.ts +2 -1
  34. package/src/services/context/user-context-finalizer.ts +84 -0
  35. package/src/services/context/user-context-service.ts +0 -77
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/user/anonymous/init/route.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgoBxD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,kCAE9C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/user/anonymous/init/route.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA4rBxD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,kCAE9C"}
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var tslib_es6 = require('../../../../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
4
- var user_aggregate_service = require('../../../../../services/aggregate/user.aggregate.service.js');
4
+ var anonymous_aggregate_service = require('../../../../../services/aggregate/anonymous.aggregate.service.js');
5
5
  var server = require('@windrun-huaiin/third-ui/fingerprint/server');
6
6
  var server$1 = require('@clerk/nextjs/server');
7
7
  var server$2 = require('next/server');
8
8
  var userContextService = require('../../../../../services/context/user-context-service.js');
9
+ var userContextFinalizer = require('../../../../../services/context/user-context-finalizer.js');
9
10
 
10
11
  /* eslint-disable @typescript-eslint/no-explicit-any */
11
12
  // Fix BigInt serialization issue
@@ -16,7 +17,7 @@ BigInt.prototype.toJSON = function () {
16
17
  /** 创建成功响应对象 */
17
18
  function createSuccessResponse(params) {
18
19
  const response = Object.assign({ success: true, xUser: userContextService.mapUserToXUser(params.entities.user), xCredit: params.entities.credit ? userContextService.mapCreditToXCredit(params.entities.credit) : null, xSubscription: userContextService.mapSubscriptionToXSubscription(params.entities.subscription), isNewUser: params.isNewUser }, params.options);
19
- return userContextService.applyUserMockContext(response);
20
+ return userContextFinalizer.finalizeUserContext(response);
20
21
  }
21
22
  /** 创建错误响应 */
22
23
  function createErrorResponse(message, status = 400) {
@@ -185,6 +186,28 @@ function detectChannelFromPlatform(platform) {
185
186
  return null;
186
187
  }
187
188
  }
189
+ function detectChannelFromUtmMedium(value) {
190
+ const normalized = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
191
+ if (!normalized) {
192
+ return null;
193
+ }
194
+ if (/^(cpc|ppc|paid|paid_search|display|banner|affiliate|email|newsletter|push|sms)$/.test(normalized)) {
195
+ return 'campaign';
196
+ }
197
+ if (/^(social|social_paid|social-organic|social_organic)$/.test(normalized)) {
198
+ return 'social';
199
+ }
200
+ if (/^(organic|seo|search)$/.test(normalized)) {
201
+ return 'search';
202
+ }
203
+ if (/^(referral|partner)$/.test(normalized)) {
204
+ return 'referral';
205
+ }
206
+ if (/^(ai|llm)$/.test(normalized)) {
207
+ return 'ai';
208
+ }
209
+ return 'campaign';
210
+ }
188
211
  function parseUserAgent(request) {
189
212
  var _a, _b, _c, _d, _e;
190
213
  const userAgentHeader = request.headers.get('user-agent');
@@ -311,17 +334,30 @@ function parseFirstTouchHeader(request) {
311
334
  }
312
335
  }
313
336
  function finalizeAttribution(sourceRef) {
314
- var _a, _b, _c, _d;
337
+ var _a, _b, _c, _d, _e;
315
338
  const landingHost = normalizeHost(sourceRef.landingHost);
316
339
  const refererHost = normalizeHost(sourceRef.refererHost);
317
340
  const internal = isInternalReferer(landingHost, refererHost);
341
+ const hasCampaignMarker = Boolean(sourceRef.utmSource
342
+ || sourceRef.utmMedium
343
+ || sourceRef.utmCampaign
344
+ || sourceRef.utmTerm
345
+ || sourceRef.utmContent
346
+ || sourceRef.utmId
347
+ || sourceRef.ref
348
+ || sourceRef.gclid
349
+ || sourceRef.fbclid
350
+ || sourceRef.msclkid
351
+ || sourceRef.ttclid
352
+ || sourceRef.twclid
353
+ || sourceRef.liFatId);
318
354
  if (internal) {
319
355
  sourceRef.isInternalReferer = true;
320
356
  }
321
357
  const utmPlatform = detectPlatform(sourceRef.utmSource) || detectPlatform(sourceRef.ref);
322
358
  if (utmPlatform) {
323
359
  sourceRef.sourcePlatform = utmPlatform;
324
- sourceRef.sourceChannel = (_b = (_a = detectChannelFromPlatform(utmPlatform)) !== null && _a !== void 0 ? _a : sourceRef.sourceChannel) !== null && _b !== void 0 ? _b : 'campaign';
360
+ sourceRef.sourceChannel = (_c = (_b = (_a = detectChannelFromPlatform(utmPlatform)) !== null && _a !== void 0 ? _a : detectChannelFromUtmMedium(sourceRef.utmMedium)) !== null && _b !== void 0 ? _b : sourceRef.sourceChannel) !== null && _c !== void 0 ? _c : 'campaign';
325
361
  sourceRef.sourceType = 'campaign';
326
362
  return;
327
363
  }
@@ -361,10 +397,16 @@ function finalizeAttribution(sourceRef) {
361
397
  sourceRef.sourceType = 'campaign';
362
398
  return;
363
399
  }
400
+ if (hasCampaignMarker) {
401
+ sourceRef.sourcePlatform = 'other';
402
+ sourceRef.sourceChannel = (_d = detectChannelFromUtmMedium(sourceRef.utmMedium)) !== null && _d !== void 0 ? _d : 'campaign';
403
+ sourceRef.sourceType = 'campaign';
404
+ return;
405
+ }
364
406
  if (!internal && refererHost) {
365
407
  const refererPlatform = detectPlatform(refererHost) || detectPlatform(sourceRef.httpRefer);
366
- sourceRef.sourcePlatform = (_c = refererPlatform !== null && refererPlatform !== void 0 ? refererPlatform : getRootDomain(refererHost)) !== null && _c !== void 0 ? _c : refererHost;
367
- sourceRef.sourceChannel = (_d = detectChannelFromPlatform(refererPlatform)) !== null && _d !== void 0 ? _d : 'referral';
408
+ sourceRef.sourcePlatform = refererPlatform !== null && refererPlatform !== void 0 ? refererPlatform : 'other';
409
+ sourceRef.sourceChannel = (_e = detectChannelFromPlatform(refererPlatform)) !== null && _e !== void 0 ? _e : 'referral';
368
410
  sourceRef.sourceType = 'referer';
369
411
  return;
370
412
  }
@@ -486,17 +528,22 @@ function handleFingerprintRequest(request_1) {
486
528
  return createErrorResponse('User not found', 404);
487
529
  }
488
530
  const sourceRef = extractSourceRef(request);
489
- // 创建新的匿名用户
490
- const { newUser, credit } = yield user_aggregate_service.userAggregateService.initAnonymousUser(fingerprintId, { sourceRef: sourceRef !== null && sourceRef !== void 0 ? sourceRef : undefined });
491
- console.log(`Created new anonymous user ${newUser.userId} with fingerprint ${fingerprintId}`);
531
+ const anonymousInitResult = yield anonymous_aggregate_service.anonymousAggregateService.getOrCreateByFingerprintId(fingerprintId, { sourceRef: sourceRef !== null && sourceRef !== void 0 ? sourceRef : undefined });
532
+ if (anonymousInitResult.isNewUser) {
533
+ console.log(`Created new anonymous user ${anonymousInitResult.user.userId} with fingerprint ${fingerprintId}`);
534
+ }
492
535
  // 返回创建结果
493
536
  const response = createSuccessResponse({
494
537
  entities: {
495
- user: newUser,
496
- credit,
497
- subscription: null,
538
+ user: anonymousInitResult.user,
539
+ credit: anonymousInitResult.credit,
540
+ subscription: anonymousInitResult.subscription,
541
+ },
542
+ isNewUser: anonymousInitResult.isNewUser,
543
+ options: {
544
+ totalUsersOnDevice: anonymousInitResult.totalUsersOnDevice,
545
+ hasAnonymousUser: anonymousInitResult.hasAnonymousUser,
498
546
  },
499
- isNewUser: true,
500
547
  });
501
548
  return server$2.NextResponse.json(response);
502
549
  }
@@ -1,9 +1,10 @@
1
1
  import { __awaiter, __rest } from '../../../../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
2
- import { userAggregateService } from '../../../../../services/aggregate/user.aggregate.service.mjs';
2
+ import { anonymousAggregateService } from '../../../../../services/aggregate/anonymous.aggregate.service.mjs';
3
3
  import { extractFingerprintFromNextRequest } from '@windrun-huaiin/third-ui/fingerprint/server';
4
4
  import { auth } from '@clerk/nextjs/server';
5
5
  import { NextResponse } from 'next/server';
6
- import { fetchUserContextByClerkUserId, fetchLatestUserContextByFingerprintId, mapSubscriptionToXSubscription, mapCreditToXCredit, mapUserToXUser, applyUserMockContext } from '../../../../../services/context/user-context-service.mjs';
6
+ import { fetchUserContextByClerkUserId, fetchLatestUserContextByFingerprintId, mapSubscriptionToXSubscription, mapCreditToXCredit, mapUserToXUser } from '../../../../../services/context/user-context-service.mjs';
7
+ import { finalizeUserContext } from '../../../../../services/context/user-context-finalizer.mjs';
7
8
 
8
9
  /* eslint-disable @typescript-eslint/no-explicit-any */
9
10
  // Fix BigInt serialization issue
@@ -14,7 +15,7 @@ BigInt.prototype.toJSON = function () {
14
15
  /** 创建成功响应对象 */
15
16
  function createSuccessResponse(params) {
16
17
  const response = Object.assign({ success: true, xUser: mapUserToXUser(params.entities.user), xCredit: params.entities.credit ? mapCreditToXCredit(params.entities.credit) : null, xSubscription: mapSubscriptionToXSubscription(params.entities.subscription), isNewUser: params.isNewUser }, params.options);
17
- return applyUserMockContext(response);
18
+ return finalizeUserContext(response);
18
19
  }
19
20
  /** 创建错误响应 */
20
21
  function createErrorResponse(message, status = 400) {
@@ -183,6 +184,28 @@ function detectChannelFromPlatform(platform) {
183
184
  return null;
184
185
  }
185
186
  }
187
+ function detectChannelFromUtmMedium(value) {
188
+ const normalized = value === null || value === void 0 ? void 0 : value.trim().toLowerCase();
189
+ if (!normalized) {
190
+ return null;
191
+ }
192
+ if (/^(cpc|ppc|paid|paid_search|display|banner|affiliate|email|newsletter|push|sms)$/.test(normalized)) {
193
+ return 'campaign';
194
+ }
195
+ if (/^(social|social_paid|social-organic|social_organic)$/.test(normalized)) {
196
+ return 'social';
197
+ }
198
+ if (/^(organic|seo|search)$/.test(normalized)) {
199
+ return 'search';
200
+ }
201
+ if (/^(referral|partner)$/.test(normalized)) {
202
+ return 'referral';
203
+ }
204
+ if (/^(ai|llm)$/.test(normalized)) {
205
+ return 'ai';
206
+ }
207
+ return 'campaign';
208
+ }
186
209
  function parseUserAgent(request) {
187
210
  var _a, _b, _c, _d, _e;
188
211
  const userAgentHeader = request.headers.get('user-agent');
@@ -309,17 +332,30 @@ function parseFirstTouchHeader(request) {
309
332
  }
310
333
  }
311
334
  function finalizeAttribution(sourceRef) {
312
- var _a, _b, _c, _d;
335
+ var _a, _b, _c, _d, _e;
313
336
  const landingHost = normalizeHost(sourceRef.landingHost);
314
337
  const refererHost = normalizeHost(sourceRef.refererHost);
315
338
  const internal = isInternalReferer(landingHost, refererHost);
339
+ const hasCampaignMarker = Boolean(sourceRef.utmSource
340
+ || sourceRef.utmMedium
341
+ || sourceRef.utmCampaign
342
+ || sourceRef.utmTerm
343
+ || sourceRef.utmContent
344
+ || sourceRef.utmId
345
+ || sourceRef.ref
346
+ || sourceRef.gclid
347
+ || sourceRef.fbclid
348
+ || sourceRef.msclkid
349
+ || sourceRef.ttclid
350
+ || sourceRef.twclid
351
+ || sourceRef.liFatId);
316
352
  if (internal) {
317
353
  sourceRef.isInternalReferer = true;
318
354
  }
319
355
  const utmPlatform = detectPlatform(sourceRef.utmSource) || detectPlatform(sourceRef.ref);
320
356
  if (utmPlatform) {
321
357
  sourceRef.sourcePlatform = utmPlatform;
322
- sourceRef.sourceChannel = (_b = (_a = detectChannelFromPlatform(utmPlatform)) !== null && _a !== void 0 ? _a : sourceRef.sourceChannel) !== null && _b !== void 0 ? _b : 'campaign';
358
+ sourceRef.sourceChannel = (_c = (_b = (_a = detectChannelFromPlatform(utmPlatform)) !== null && _a !== void 0 ? _a : detectChannelFromUtmMedium(sourceRef.utmMedium)) !== null && _b !== void 0 ? _b : sourceRef.sourceChannel) !== null && _c !== void 0 ? _c : 'campaign';
323
359
  sourceRef.sourceType = 'campaign';
324
360
  return;
325
361
  }
@@ -359,10 +395,16 @@ function finalizeAttribution(sourceRef) {
359
395
  sourceRef.sourceType = 'campaign';
360
396
  return;
361
397
  }
398
+ if (hasCampaignMarker) {
399
+ sourceRef.sourcePlatform = 'other';
400
+ sourceRef.sourceChannel = (_d = detectChannelFromUtmMedium(sourceRef.utmMedium)) !== null && _d !== void 0 ? _d : 'campaign';
401
+ sourceRef.sourceType = 'campaign';
402
+ return;
403
+ }
362
404
  if (!internal && refererHost) {
363
405
  const refererPlatform = detectPlatform(refererHost) || detectPlatform(sourceRef.httpRefer);
364
- sourceRef.sourcePlatform = (_c = refererPlatform !== null && refererPlatform !== void 0 ? refererPlatform : getRootDomain(refererHost)) !== null && _c !== void 0 ? _c : refererHost;
365
- sourceRef.sourceChannel = (_d = detectChannelFromPlatform(refererPlatform)) !== null && _d !== void 0 ? _d : 'referral';
406
+ sourceRef.sourcePlatform = refererPlatform !== null && refererPlatform !== void 0 ? refererPlatform : 'other';
407
+ sourceRef.sourceChannel = (_e = detectChannelFromPlatform(refererPlatform)) !== null && _e !== void 0 ? _e : 'referral';
366
408
  sourceRef.sourceType = 'referer';
367
409
  return;
368
410
  }
@@ -484,17 +526,22 @@ function handleFingerprintRequest(request_1) {
484
526
  return createErrorResponse('User not found', 404);
485
527
  }
486
528
  const sourceRef = extractSourceRef(request);
487
- // 创建新的匿名用户
488
- const { newUser, credit } = yield userAggregateService.initAnonymousUser(fingerprintId, { sourceRef: sourceRef !== null && sourceRef !== void 0 ? sourceRef : undefined });
489
- console.log(`Created new anonymous user ${newUser.userId} with fingerprint ${fingerprintId}`);
529
+ const anonymousInitResult = yield anonymousAggregateService.getOrCreateByFingerprintId(fingerprintId, { sourceRef: sourceRef !== null && sourceRef !== void 0 ? sourceRef : undefined });
530
+ if (anonymousInitResult.isNewUser) {
531
+ console.log(`Created new anonymous user ${anonymousInitResult.user.userId} with fingerprint ${fingerprintId}`);
532
+ }
490
533
  // 返回创建结果
491
534
  const response = createSuccessResponse({
492
535
  entities: {
493
- user: newUser,
494
- credit,
495
- subscription: null,
536
+ user: anonymousInitResult.user,
537
+ credit: anonymousInitResult.credit,
538
+ subscription: anonymousInitResult.subscription,
539
+ },
540
+ isNewUser: anonymousInitResult.isNewUser,
541
+ options: {
542
+ totalUsersOnDevice: anonymousInitResult.totalUsersOnDevice,
543
+ hasAnonymousUser: anonymousInitResult.hasAnonymousUser,
496
544
  },
497
- isNewUser: true,
498
545
  });
499
546
  return NextResponse.json(response);
500
547
  }
package/dist/index.js CHANGED
@@ -12,7 +12,9 @@ var apilog_service = require('./services/database/apilog.service.js');
12
12
  var constants = require('./services/database/constants.js');
13
13
  var user_aggregate_service = require('./services/aggregate/user.aggregate.service.js');
14
14
  var billing_aggregate_service = require('./services/aggregate/billing.aggregate.service.js');
15
+ var anonymous_aggregate_service = require('./services/aggregate/anonymous.aggregate.service.js');
15
16
  var userContextService = require('./services/context/user-context-service.js');
17
+ var userContextFinalizer = require('./services/context/user-context-finalizer.js');
16
18
  var webhookHandler = require('./services/stripe/webhook-handler.js');
17
19
  var moneyPriceConfig = require('./lib/money-price-config.js');
18
20
  var moneyPriceHelper = require('./lib/money-price-helper.js');
@@ -60,13 +62,14 @@ exports.isValidTransactionType = constants.isValidTransactionType;
60
62
  exports.isValidUserStatus = constants.isValidUserStatus;
61
63
  exports.userAggregateService = user_aggregate_service.userAggregateService;
62
64
  exports.billingAggregateService = billing_aggregate_service.billingAggregateService;
63
- exports.applyUserMockContext = userContextService.applyUserMockContext;
65
+ exports.anonymousAggregateService = anonymous_aggregate_service.anonymousAggregateService;
64
66
  exports.buildInitUserContextFromEntities = userContextService.buildInitUserContextFromEntities;
65
67
  exports.fetchLatestUserContextByFingerprintId = userContextService.fetchLatestUserContextByFingerprintId;
66
68
  exports.fetchUserContextByClerkUserId = userContextService.fetchUserContextByClerkUserId;
67
69
  exports.mapCreditToXCredit = userContextService.mapCreditToXCredit;
68
70
  exports.mapSubscriptionToXSubscription = userContextService.mapSubscriptionToXSubscription;
69
71
  exports.mapUserToXUser = userContextService.mapUserToXUser;
72
+ exports.finalizeUserContext = userContextFinalizer.finalizeUserContext;
70
73
  exports.handleStripeEvent = webhookHandler.handleStripeEvent;
71
74
  exports.getActiveProviderConfig = moneyPriceConfig.getActiveProviderConfig;
72
75
  exports.getCreditsFromPriceId = moneyPriceConfig.getCreditsFromPriceId;
package/dist/index.mjs CHANGED
@@ -10,7 +10,9 @@ export { Apilogger, apilogService } from './services/database/apilog.service.mjs
10
10
  export { BillingReason, CreditType, OperationType, OrderStatus, PaySupplier, PaymentStatus, SubscriptionStatus, TransactionType, UserStatus, isValidBillingReason, isValidCreditType, isValidOperationType, isValidOrderStatus, isValidPaymentStatus, isValidSubscriptionStatus, isValidTransactionType, isValidUserStatus } from './services/database/constants.mjs';
11
11
  export { userAggregateService } from './services/aggregate/user.aggregate.service.mjs';
12
12
  export { billingAggregateService } from './services/aggregate/billing.aggregate.service.mjs';
13
- export { applyUserMockContext, buildInitUserContextFromEntities, fetchLatestUserContextByFingerprintId, fetchUserContextByClerkUserId, mapCreditToXCredit, mapSubscriptionToXSubscription, mapUserToXUser } from './services/context/user-context-service.mjs';
13
+ export { anonymousAggregateService } from './services/aggregate/anonymous.aggregate.service.mjs';
14
+ export { buildInitUserContextFromEntities, fetchLatestUserContextByFingerprintId, fetchUserContextByClerkUserId, mapCreditToXCredit, mapSubscriptionToXSubscription, mapUserToXUser } from './services/context/user-context-service.mjs';
15
+ export { finalizeUserContext } from './services/context/user-context-finalizer.mjs';
14
16
  export { handleStripeEvent } from './services/stripe/webhook-handler.mjs';
15
17
  export { getActiveProviderConfig, getCreditsFromPriceId, getPriceConfig, moneyPriceConfig } from './lib/money-price-config.mjs';
16
18
  export { getMoneyPriceInitUserContext } from './lib/money-price-helper.mjs';
@@ -5,6 +5,7 @@ var server = require('@clerk/nextjs/server');
5
5
  var headers = require('next/headers');
6
6
  var server$1 = require('@windrun-huaiin/third-ui/fingerprint/server');
7
7
  var userContextService = require('../services/context/user-context-service.js');
8
+ var userContextFinalizer = require('../services/context/user-context-finalizer.js');
8
9
 
9
10
  function readFingerprintIdFromRequest() {
10
11
  return tslib_es6.__awaiter(this, void 0, void 0, function* () {
@@ -36,7 +37,7 @@ function getMoneyPriceInitUserContext() {
36
37
  subscription: userContext.subscription,
37
38
  isClerkAuthenticated: true,
38
39
  });
39
- return userContextService.applyUserMockContext(initUserContext);
40
+ return userContextFinalizer.finalizeUserContext(initUserContext);
40
41
  }
41
42
  const fingerprintId = yield readFingerprintIdFromRequest();
42
43
  if (!fingerprintId) {
@@ -2,7 +2,8 @@ import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.
2
2
  import { auth } from '@clerk/nextjs/server';
3
3
  import { cookies, headers } from 'next/headers';
4
4
  import { extractFingerprintFromNextStores } from '@windrun-huaiin/third-ui/fingerprint/server';
5
- import { fetchUserContextByClerkUserId, buildInitUserContextFromEntities, applyUserMockContext } from '../services/context/user-context-service.mjs';
5
+ import { fetchUserContextByClerkUserId, buildInitUserContextFromEntities } from '../services/context/user-context-service.mjs';
6
+ import { finalizeUserContext } from '../services/context/user-context-finalizer.mjs';
6
7
 
7
8
  function readFingerprintIdFromRequest() {
8
9
  return __awaiter(this, void 0, void 0, function* () {
@@ -34,7 +35,7 @@ function getMoneyPriceInitUserContext() {
34
35
  subscription: userContext.subscription,
35
36
  isClerkAuthenticated: true,
36
37
  });
37
- return applyUserMockContext(initUserContext);
38
+ return finalizeUserContext(initUserContext);
38
39
  }
39
40
  const fingerprintId = yield readFingerprintIdFromRequest();
40
41
  if (!fingerprintId) {
@@ -0,0 +1,21 @@
1
+ import type { Credit, Subscription, User } from '@/db/prisma-model-type';
2
+ import { Prisma } from '@/db/prisma-model-type';
3
+ type AnonymousInitContext = {
4
+ user: User;
5
+ credit: Credit | null;
6
+ subscription: Subscription | null;
7
+ isNewUser: boolean;
8
+ totalUsersOnDevice: number;
9
+ hasAnonymousUser: boolean;
10
+ };
11
+ declare class AnonymousAggregateService {
12
+ private lockFingerprintInit;
13
+ private findLatestUserContextByFingerprintId;
14
+ private createAnonymousUser;
15
+ getOrCreateByFingerprintId(fingerprintId: string, options?: {
16
+ sourceRef?: Prisma.InputJsonValue;
17
+ }): Promise<AnonymousInitContext>;
18
+ }
19
+ export declare const anonymousAggregateService: AnonymousAggregateService;
20
+ export {};
21
+ //# sourceMappingURL=anonymous.aggregate.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymous.aggregate.service.d.ts","sourceRoot":"","sources":["../../../src/services/aggregate/anonymous.aggregate.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAIzE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAIhD,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,cAAM,yBAAyB;YACf,mBAAmB;YAYnB,oCAAoC;YAyBpC,mBAAmB;IAsC3B,0BAA0B,CAC9B,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;KAAE,GAC/C,OAAO,CAAC,oBAAoB,CAAC;CAYjC;AAED,eAAO,MAAM,yBAAyB,2BAAkC,CAAC"}
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ var tslib_es6 = require('../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
4
+ var user_service = require('../database/user.service.js');
5
+ var subscription_service = require('../database/subscription.service.js');
6
+ var credit_service = require('../database/credit.service.js');
7
+ var client = require('@prisma/client');
8
+ var constants = require('../database/constants.js');
9
+ require('../../prisma/prisma.js');
10
+ var prismaTransactionUtil = require('../../prisma/prisma-transaction-util.js');
11
+ var creditInit = require('../../lib/credit-init.js');
12
+
13
+ const ANONYMOUS_INIT_LOCK_NAMESPACE = 92831;
14
+ class AnonymousAggregateService {
15
+ lockFingerprintInit(tx, fingerprintId) {
16
+ return tslib_es6.__awaiter(this, void 0, void 0, function* () {
17
+ yield tx.$executeRaw `
18
+ SELECT pg_advisory_xact_lock(
19
+ ${client.Prisma.raw(String(ANONYMOUS_INIT_LOCK_NAMESPACE))},
20
+ hashtext(${fingerprintId})
21
+ )
22
+ `;
23
+ });
24
+ }
25
+ findLatestUserContextByFingerprintId(fingerprintId, tx) {
26
+ return tslib_es6.__awaiter(this, void 0, void 0, function* () {
27
+ const existingUsers = yield user_service.userService.findListByFingerprintId(fingerprintId, tx);
28
+ if (existingUsers.length === 0) {
29
+ return null;
30
+ }
31
+ const latestUser = existingUsers[0];
32
+ const [credit, subscription] = yield Promise.all([
33
+ credit_service.creditService.getCredit(latestUser.userId, tx),
34
+ subscription_service.subscriptionService.getActiveSubscription(latestUser.userId, tx),
35
+ ]);
36
+ return {
37
+ user: latestUser,
38
+ credit,
39
+ subscription,
40
+ isNewUser: false,
41
+ totalUsersOnDevice: existingUsers.length,
42
+ hasAnonymousUser: true,
43
+ };
44
+ });
45
+ }
46
+ createAnonymousUser(fingerprintId, tx, options) {
47
+ return tslib_es6.__awaiter(this, void 0, void 0, function* () {
48
+ const newUser = yield user_service.userService.createUser({
49
+ fingerprintId,
50
+ sourceRef: options === null || options === void 0 ? void 0 : options.sourceRef,
51
+ status: constants.UserStatus.ANONYMOUS,
52
+ }, tx);
53
+ const credit = yield credit_service.creditService.initializeCreditWithFree({
54
+ userId: newUser.userId,
55
+ feature: 'anonymous_user_init',
56
+ creditType: constants.CreditType.FREE,
57
+ operationType: constants.OperationType.SYS_GIFT,
58
+ operationReferId: newUser.userId,
59
+ creditsChange: creditInit.freeAmount,
60
+ }, tx);
61
+ yield subscription_service.subscriptionService.initializeSubscription(newUser.userId, tx);
62
+ return {
63
+ user: newUser,
64
+ credit,
65
+ subscription: null,
66
+ isNewUser: true,
67
+ totalUsersOnDevice: 1,
68
+ hasAnonymousUser: true,
69
+ };
70
+ });
71
+ }
72
+ getOrCreateByFingerprintId(fingerprintId, options) {
73
+ return tslib_es6.__awaiter(this, void 0, void 0, function* () {
74
+ return prismaTransactionUtil.runInTransaction((tx) => tslib_es6.__awaiter(this, void 0, void 0, function* () {
75
+ yield this.lockFingerprintInit(tx, fingerprintId);
76
+ const existingContext = yield this.findLatestUserContextByFingerprintId(fingerprintId, tx);
77
+ if (existingContext) {
78
+ return existingContext;
79
+ }
80
+ return this.createAnonymousUser(fingerprintId, tx, options);
81
+ }), 'anonymous_get_or_create_by_fingerprint_id');
82
+ });
83
+ }
84
+ }
85
+ const anonymousAggregateService = new AnonymousAggregateService();
86
+
87
+ exports.anonymousAggregateService = anonymousAggregateService;
@@ -0,0 +1,85 @@
1
+ import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
2
+ import { userService } from '../database/user.service.mjs';
3
+ import { subscriptionService } from '../database/subscription.service.mjs';
4
+ import { creditService } from '../database/credit.service.mjs';
5
+ import { Prisma } from '@prisma/client';
6
+ import { UserStatus, OperationType, CreditType } from '../database/constants.mjs';
7
+ import '../../prisma/prisma.mjs';
8
+ import { runInTransaction } from '../../prisma/prisma-transaction-util.mjs';
9
+ import { freeAmount } from '../../lib/credit-init.mjs';
10
+
11
+ const ANONYMOUS_INIT_LOCK_NAMESPACE = 92831;
12
+ class AnonymousAggregateService {
13
+ lockFingerprintInit(tx, fingerprintId) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ yield tx.$executeRaw `
16
+ SELECT pg_advisory_xact_lock(
17
+ ${Prisma.raw(String(ANONYMOUS_INIT_LOCK_NAMESPACE))},
18
+ hashtext(${fingerprintId})
19
+ )
20
+ `;
21
+ });
22
+ }
23
+ findLatestUserContextByFingerprintId(fingerprintId, tx) {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ const existingUsers = yield userService.findListByFingerprintId(fingerprintId, tx);
26
+ if (existingUsers.length === 0) {
27
+ return null;
28
+ }
29
+ const latestUser = existingUsers[0];
30
+ const [credit, subscription] = yield Promise.all([
31
+ creditService.getCredit(latestUser.userId, tx),
32
+ subscriptionService.getActiveSubscription(latestUser.userId, tx),
33
+ ]);
34
+ return {
35
+ user: latestUser,
36
+ credit,
37
+ subscription,
38
+ isNewUser: false,
39
+ totalUsersOnDevice: existingUsers.length,
40
+ hasAnonymousUser: true,
41
+ };
42
+ });
43
+ }
44
+ createAnonymousUser(fingerprintId, tx, options) {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ const newUser = yield userService.createUser({
47
+ fingerprintId,
48
+ sourceRef: options === null || options === void 0 ? void 0 : options.sourceRef,
49
+ status: UserStatus.ANONYMOUS,
50
+ }, tx);
51
+ const credit = yield creditService.initializeCreditWithFree({
52
+ userId: newUser.userId,
53
+ feature: 'anonymous_user_init',
54
+ creditType: CreditType.FREE,
55
+ operationType: OperationType.SYS_GIFT,
56
+ operationReferId: newUser.userId,
57
+ creditsChange: freeAmount,
58
+ }, tx);
59
+ yield subscriptionService.initializeSubscription(newUser.userId, tx);
60
+ return {
61
+ user: newUser,
62
+ credit,
63
+ subscription: null,
64
+ isNewUser: true,
65
+ totalUsersOnDevice: 1,
66
+ hasAnonymousUser: true,
67
+ };
68
+ });
69
+ }
70
+ getOrCreateByFingerprintId(fingerprintId, options) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ return runInTransaction((tx) => __awaiter(this, void 0, void 0, function* () {
73
+ yield this.lockFingerprintInit(tx, fingerprintId);
74
+ const existingContext = yield this.findLatestUserContextByFingerprintId(fingerprintId, tx);
75
+ if (existingContext) {
76
+ return existingContext;
77
+ }
78
+ return this.createAnonymousUser(fingerprintId, tx, options);
79
+ }), 'anonymous_get_or_create_by_fingerprint_id');
80
+ });
81
+ }
82
+ }
83
+ const anonymousAggregateService = new AnonymousAggregateService();
84
+
85
+ export { anonymousAggregateService };
@@ -1,3 +1,4 @@
1
1
  export { userAggregateService } from './user.aggregate.service';
2
2
  export { billingAggregateService } from './billing.aggregate.service';
3
+ export { anonymousAggregateService } from './anonymous.aggregate.service';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/aggregate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/aggregate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -2,8 +2,10 @@
2
2
 
3
3
  var user_aggregate_service = require('./user.aggregate.service.js');
4
4
  var billing_aggregate_service = require('./billing.aggregate.service.js');
5
+ var anonymous_aggregate_service = require('./anonymous.aggregate.service.js');
5
6
 
6
7
 
7
8
 
8
9
  exports.userAggregateService = user_aggregate_service.userAggregateService;
9
10
  exports.billingAggregateService = billing_aggregate_service.billingAggregateService;
11
+ exports.anonymousAggregateService = anonymous_aggregate_service.anonymousAggregateService;
@@ -1,2 +1,3 @@
1
1
  export { userAggregateService } from './user.aggregate.service.mjs';
2
2
  export { billingAggregateService } from './billing.aggregate.service.mjs';
3
+ export { anonymousAggregateService } from './anonymous.aggregate.service.mjs';
@@ -1,2 +1,3 @@
1
1
  export * from './user-context-service';
2
+ export * from './user-context-finalizer';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/context/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/context/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var userContextService = require('./user-context-service.js');
4
+ var userContextFinalizer = require('./user-context-finalizer.js');
4
5
 
5
6
 
6
7
 
7
- exports.applyUserMockContext = userContextService.applyUserMockContext;
8
8
  exports.buildInitUserContextFromEntities = userContextService.buildInitUserContextFromEntities;
9
9
  exports.fetchLatestUserContextByFingerprintId = userContextService.fetchLatestUserContextByFingerprintId;
10
10
  exports.fetchUserContextByClerkUserId = userContextService.fetchUserContextByClerkUserId;
11
11
  exports.mapCreditToXCredit = userContextService.mapCreditToXCredit;
12
12
  exports.mapSubscriptionToXSubscription = userContextService.mapSubscriptionToXSubscription;
13
13
  exports.mapUserToXUser = userContextService.mapUserToXUser;
14
+ exports.finalizeUserContext = userContextFinalizer.finalizeUserContext;
@@ -1 +1,2 @@
1
- export { applyUserMockContext, buildInitUserContextFromEntities, fetchLatestUserContextByFingerprintId, fetchUserContextByClerkUserId, mapCreditToXCredit, mapSubscriptionToXSubscription, mapUserToXUser } from './user-context-service.mjs';
1
+ export { buildInitUserContextFromEntities, fetchLatestUserContextByFingerprintId, fetchUserContextByClerkUserId, mapCreditToXCredit, mapSubscriptionToXSubscription, mapUserToXUser } from './user-context-service.mjs';
2
+ export { finalizeUserContext } from './user-context-finalizer.mjs';
@@ -0,0 +1,13 @@
1
+ import type { XSubscription, XUser } from '@windrun-huaiin/third-ui/fingerprint';
2
+ type FinalizableUserContext = {
3
+ xUser: XUser | null;
4
+ xSubscription: XSubscription | null;
5
+ };
6
+ /**
7
+ * Output finalizer for user-context payloads.
8
+ * Real data assembly should stay in user-context-service; any optional test-only
9
+ * shaping is isolated here so production services do not depend on mock code.
10
+ */
11
+ export declare function finalizeUserContext<T extends FinalizableUserContext>(context: T): T;
12
+ export {};
13
+ //# sourceMappingURL=user-context-finalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context-finalizer.d.ts","sourceRoot":"","sources":["../../../src/services/context/user-context-finalizer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAEjF,KAAK,sBAAsB,GAAG;IAC5B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,sBAAsB,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAsEnF"}