@tbox.cn/app-module-member 0.2.0 → 0.8.7

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 (63) hide show
  1. package/AGENTS.md +16 -0
  2. package/README.md +99 -11
  3. package/dist/chunk-EPQEF43P.js +1581 -0
  4. package/dist/chunk-QWGRWD5C.js +766 -0
  5. package/dist/client/index.d.ts +2 -2
  6. package/dist/client/index.js +1 -1
  7. package/dist/index.d.ts +2 -3
  8. package/dist/index.js +12 -2
  9. package/dist/member-372B74DY.css +1740 -0
  10. package/dist/server/index.d.ts +657 -286
  11. package/dist/server/index.js +11 -1
  12. package/package.json +25 -16
  13. package/skills/member/SKILL.md +141 -0
  14. package/skills/member/references/enrollment.md +34 -0
  15. package/skills/member/references/points-and-card.md +24 -0
  16. package/src/client/cards/index.ts +10 -0
  17. package/src/client/cards/member-card-summary/index.tsx +375 -0
  18. package/src/client/cards/member-enrollment-cta/index.tsx +61 -0
  19. package/src/client/cards/member-enrollment-form/index.tsx +181 -0
  20. package/src/client/cards/member-enrollment-status/index.tsx +65 -0
  21. package/src/client/cards/member-info/index.tsx +60 -0
  22. package/src/client/cards/member-login-cta/index.tsx +81 -0
  23. package/src/client/cards/member-point-usage-list/index.tsx +53 -0
  24. package/src/client/cards/view.ts +39 -0
  25. package/src/client/components/ImageWithFallback.tsx +38 -0
  26. package/src/client/index.ts +6 -4
  27. package/src/client/styles/member.css +1740 -0
  28. package/src/client/types/css.d.ts +2 -0
  29. package/src/server/action.ts +348 -0
  30. package/src/server/cards/index.ts +7 -7
  31. package/src/server/cards/member-card-summary/meta.ts +26 -5
  32. package/src/server/cards/member-card-summary/samples.ts +272 -0
  33. package/src/server/cards/member-enrollment-cta/meta.ts +19 -0
  34. package/src/server/cards/member-enrollment-form/meta.ts +10 -6
  35. package/src/server/cards/member-enrollment-status/meta.ts +5 -6
  36. package/src/server/cards/member-info/meta.ts +1 -1
  37. package/src/server/cards/member-login-cta/meta.ts +16 -0
  38. package/src/server/cards/member-point-usage-list/meta.ts +2 -3
  39. package/src/server/handler.ts +159 -119
  40. package/src/server/index.ts +129 -50
  41. package/src/server/ports.ts +66 -0
  42. package/src/server/service.ts +94 -80
  43. package/src/server/status.ts +87 -0
  44. package/src/server/subject-binding.ts +19 -0
  45. package/src/server/tool.ts +469 -99
  46. package/tbox.module.json +124 -0
  47. package/tests/cards-render.test.tsx +526 -0
  48. package/tests/enrollment.test.ts +121 -0
  49. package/tests/ports-resolve.test.ts +95 -0
  50. package/tests/preference-memory.test.ts +86 -0
  51. package/tests/samples.test.ts +61 -0
  52. package/tests/service.test.ts +593 -35
  53. package/tests/skills.test.ts +65 -0
  54. package/tests/snapshot-service.test.ts +75 -0
  55. package/tests/status.test.ts +127 -0
  56. package/tests/view.test.ts +33 -0
  57. package/tsup.config.ts +9 -1
  58. package/dist/chunk-JGZGLFO6.js +0 -111
  59. package/dist/chunk-ZV6B36AO.js +0 -613
  60. package/src/client/cards/index.tsx +0 -135
  61. package/src/server/cards/member-coupon-detail/meta.ts +0 -19
  62. package/src/server/cards/member-coupon-list/meta.ts +0 -18
  63. package/tbox.component.json +0 -28
@@ -1,613 +0,0 @@
1
- // src/server/index.ts
2
- import { definePreTool } from "@tbox.cn/app-sdk/server";
3
-
4
- // src/server/service.ts
5
- var InMemoryMemberService = class {
6
- balances = /* @__PURE__ */ new Map();
7
- levels = /* @__PURE__ */ new Map();
8
- idempotentKeys = /* @__PURE__ */ new Set();
9
- usages = [];
10
- profiles = /* @__PURE__ */ new Map();
11
- audit = [];
12
- constructor(initial = {}) {
13
- for (const [userId, balance] of Object.entries(initial)) {
14
- this.balances.set(userId, balance);
15
- }
16
- }
17
- // ===== 1. MemberAccountPort =====
18
- async getAccount(userId) {
19
- return {
20
- state: this.balances.has(userId) ? "active" : "not_enrolled",
21
- ...this.balances.has(userId) ? { memberRef: `member:${userId}` } : {},
22
- observedAt: (/* @__PURE__ */ new Date()).toISOString()
23
- };
24
- }
25
- // ===== 2. MemberProfilePort =====
26
- async getProfile(userId) {
27
- return this.profiles.get(userId) ?? {
28
- labels: [],
29
- observedAt: (/* @__PURE__ */ new Date()).toISOString()
30
- };
31
- }
32
- // ===== 3. MemberCardPort =====
33
- async getCard(userId) {
34
- if (!this.balances.has(userId)) return null;
35
- return {
36
- cardRef: `card:${userId}`,
37
- cardDisplay: "\u5927\u60A6\u57CE\u4F1A\u5458\u5361",
38
- status: "active",
39
- levelName: this.levelOf(userId),
40
- observedAt: (/* @__PURE__ */ new Date()).toISOString()
41
- };
42
- }
43
- // ===== 4. MemberBenefitsPort =====
44
- async queryPoints(userId) {
45
- return {
46
- userId,
47
- balance: this.balances.get(userId) ?? 0,
48
- level: this.levelOf(userId)
49
- };
50
- }
51
- async getBenefits(userId) {
52
- return {
53
- availablePoints: this.balances.get(userId) ?? 0,
54
- levelName: this.levelOf(userId),
55
- observedAt: (/* @__PURE__ */ new Date()).toISOString()
56
- };
57
- }
58
- async listPointUsage(userId, page, pageSize) {
59
- const all = this.usages.filter((u) => u.usageRecordRef.startsWith(userId));
60
- const start = (page - 1) * pageSize;
61
- return { items: all.slice(start, start + pageSize), total: all.length };
62
- }
63
- async queryDiscount(userId) {
64
- const level = this.levelOf(userId);
65
- const map = {
66
- silver: { level: "silver", discount: 0.95 },
67
- gold: { level: "gold", discount: 0.9 },
68
- platinum: { level: "platinum", discount: 0.85 }
69
- };
70
- return map[level];
71
- }
72
- async deductPoints(userId, amount, idempotencyKey) {
73
- if (this.idempotentKeys.has(idempotencyKey)) return true;
74
- const current = this.balances.get(userId) ?? 0;
75
- if (amount <= 0 || current < amount) return false;
76
- this.balances.set(userId, current - amount);
77
- this.idempotentKeys.add(idempotencyKey);
78
- this.audit.push({ action: "deduct", userId, amount, key: idempotencyKey });
79
- this.usages.push({
80
- usageRecordRef: `${userId}:${Date.now()}`,
81
- title: "\u79EF\u5206\u62B5\u6263",
82
- amount: `-${amount}`,
83
- usedAt: (/* @__PURE__ */ new Date()).toISOString()
84
- });
85
- this.maybeUpgrade(userId);
86
- return true;
87
- }
88
- async refundPoints(userId, amount, idempotencyKey) {
89
- if (this.idempotentKeys.has(`refund:${idempotencyKey}`)) return true;
90
- const current = this.balances.get(userId) ?? 0;
91
- this.balances.set(userId, current + amount);
92
- this.idempotentKeys.add(`refund:${idempotencyKey}`);
93
- this.audit.push({ action: "refund", userId, amount, key: idempotencyKey });
94
- this.usages.push({
95
- usageRecordRef: `${userId}:${Date.now()}`,
96
- title: "\u79EF\u5206\u9000\u56DE",
97
- amount: `+${amount}`,
98
- usedAt: (/* @__PURE__ */ new Date()).toISOString()
99
- });
100
- return true;
101
- }
102
- async awardPoints(userId, amount) {
103
- const current = this.balances.get(userId) ?? 0;
104
- this.balances.set(userId, current + amount);
105
- this.audit.push({ action: "award", userId, amount });
106
- this.usages.push({
107
- usageRecordRef: `${userId}:${Date.now()}`,
108
- title: "\u6D88\u8D39\u5956\u52B1",
109
- amount: `+${amount}`,
110
- usedAt: (/* @__PURE__ */ new Date()).toISOString()
111
- });
112
- this.maybeUpgrade(userId);
113
- return true;
114
- }
115
- // ===== 5. MemberCouponPort =====
116
- async listCoupons(userId, page, pageSize) {
117
- void userId;
118
- void page;
119
- void pageSize;
120
- return { items: [], total: 0 };
121
- }
122
- async getCoupon(_userId, _couponRef) {
123
- return null;
124
- }
125
- // ===== 6. MemberEnrollmentPort =====
126
- async enroll(input) {
127
- if (!this.balances.has(input.userId)) {
128
- this.balances.set(input.userId, 0);
129
- }
130
- this.profiles.set(input.userId, {
131
- ...input.name ? { displayName: input.name } : {},
132
- ...input.phone ? { contactDisplay: input.phone } : {},
133
- labels: [{ labelRef: "member", name: "\u4F1A\u5458" }],
134
- observedAt: (/* @__PURE__ */ new Date()).toISOString()
135
- });
136
- return { success: true, memberRef: `member:${input.userId}` };
137
- }
138
- async completeAuthorization(input) {
139
- void input;
140
- return { success: true };
141
- }
142
- // ===== 内部 =====
143
- levelOf(userId) {
144
- const cached = this.levels.get(userId);
145
- if (cached) return cached;
146
- const balance = this.balances.get(userId) ?? 0;
147
- if (balance >= 1e3) return "platinum";
148
- if (balance >= 300) return "gold";
149
- return "silver";
150
- }
151
- maybeUpgrade(userId) {
152
- const newLevel = this.levelOf(userId);
153
- const prev = this.levels.get(userId);
154
- this.levels.set(userId, newLevel);
155
- if (prev && prev !== newLevel) {
156
- this.levelChangeListeners.forEach((fn) => fn({ userId, newLevel }));
157
- }
158
- }
159
- levelChangeListeners = /* @__PURE__ */ new Set();
160
- onLevelChange(fn) {
161
- this.levelChangeListeners.add(fn);
162
- }
163
- };
164
-
165
- // src/server/cards/member-info/meta.ts
166
- import { memberInfoCardDataSchema } from "@tbox.cn/app-contracts-mall";
167
- var meta = {
168
- cardType: "member-info",
169
- dataSchema: memberInfoCardDataSchema,
170
- allowedTools: ["queryMemberInfo"],
171
- displayName: "\u4F1A\u5458\u4FE1\u606F",
172
- description: "\u4F1A\u5458\u8D26\u6237\u72B6\u6001\u4E0E\u4E2A\u4EBA\u8D44\u6599",
173
- sampleData: {
174
- userId: "u_1001",
175
- state: "active",
176
- displayName: "\u793A\u4F8B\u4F1A\u5458",
177
- labels: [{ labelRef: "member", name: "\u4F1A\u5458" }]
178
- },
179
- schemaVersion: 1
180
- };
181
- var meta_default = meta;
182
-
183
- // src/server/cards/member-card-summary/meta.ts
184
- import { memberCardSummaryCardDataSchema } from "@tbox.cn/app-contracts-mall";
185
- var meta2 = {
186
- cardType: "member-card-summary",
187
- dataSchema: memberCardSummaryCardDataSchema,
188
- allowedTools: ["queryMemberCard"],
189
- displayName: "\u4F1A\u5458\u5361\u9762",
190
- description: "\u4F1A\u5458\u5361\u4FE1\u606F\uFF08\u7B49\u7EA7\u3001\u79EF\u5206\u6458\u8981\uFF09",
191
- sampleData: {
192
- userId: "u_1001",
193
- cardRef: "card:u_1001",
194
- cardDisplay: "\u5927\u60A6\u57CE\u4F1A\u5458\u5361",
195
- status: "active",
196
- levelName: "gold",
197
- availablePoints: 860
198
- },
199
- schemaVersion: 1
200
- };
201
- var meta_default2 = meta2;
202
-
203
- // src/server/cards/member-coupon-list/meta.ts
204
- import { memberCouponListCardDataSchema } from "@tbox.cn/app-contracts-mall";
205
- var meta3 = {
206
- cardType: "member-coupon-list",
207
- dataSchema: memberCouponListCardDataSchema,
208
- allowedTools: ["queryMemberCoupons"],
209
- displayName: "\u4F18\u60E0\u5238\u5217\u8868",
210
- description: "\u4F1A\u5458\u4F18\u60E0\u5238\uFF08\u72B6\u6001/\u9762\u989D/\u95E8\u69DB\uFF09",
211
- sampleData: {
212
- userId: "u_1001",
213
- items: [{ couponRef: "c1", title: "\u6EE1 100 \u51CF 10", type: "amount", status: "effective" }],
214
- total: 1
215
- },
216
- schemaVersion: 1
217
- };
218
- var meta_default3 = meta3;
219
-
220
- // src/server/cards/member-coupon-detail/meta.ts
221
- import { memberCouponDetailCardDataSchema } from "@tbox.cn/app-contracts-mall";
222
- var meta4 = {
223
- cardType: "member-coupon-detail",
224
- dataSchema: memberCouponDetailCardDataSchema,
225
- allowedTools: [],
226
- displayName: "\u4F18\u60E0\u5238\u8BE6\u60C5",
227
- description: "\u5355\u5F20\u4F18\u60E0\u5238\u8BE6\u60C5\uFF08\u70B9\u51FB\u5217\u8868\u9879\u67E5\u770B\uFF09",
228
- sampleData: {
229
- couponRef: "c1",
230
- title: "\u6EE1 100 \u51CF 10",
231
- type: "amount",
232
- status: "effective"
233
- },
234
- schemaVersion: 1
235
- };
236
- var meta_default4 = meta4;
237
-
238
- // src/server/cards/member-point-usage-list/meta.ts
239
- import { memberPointUsageListCardDataSchema } from "@tbox.cn/app-contracts-mall";
240
- var meta5 = {
241
- cardType: "member-point-usage-list",
242
- dataSchema: memberPointUsageListCardDataSchema,
243
- allowedTools: ["queryPoints"],
244
- displayName: "\u79EF\u5206\u4F59\u989D\u4E0E\u6D41\u6C34",
245
- description: "\u4F1A\u5458\u79EF\u5206\u4F59\u989D\u3001\u7B49\u7EA7\u4E0E\u53D8\u52A8\u6D41\u6C34\uFF08\u5438\u6536\u6559\u5B66 points \u5361\uFF09",
246
- sampleData: {
247
- userId: "u_1001",
248
- balance: 860,
249
- level: "gold",
250
- items: [{ usageRecordRef: "r1", title: "\u6D88\u8D39\u5956\u52B1", amount: "+100", usedAt: "2026-08-13T00:00:00Z" }],
251
- total: 1
252
- },
253
- schemaVersion: 1
254
- };
255
- var meta_default5 = meta5;
256
-
257
- // src/server/cards/member-enrollment-form/meta.ts
258
- import { memberEnrollmentFormCardDataSchema } from "@tbox.cn/app-contracts-mall";
259
- var meta6 = {
260
- cardType: "member-enrollment-form",
261
- dataSchema: memberEnrollmentFormCardDataSchema,
262
- allowedTools: ["queryEnrollmentForm"],
263
- displayName: "\u5165\u4F1A\u8868\u5355",
264
- description: "\u4F1A\u5458\u5165\u4F1A\u8868\u5355\uFF08\u5199\u52A8\u4F5C member.enroll\uFF0Cgrant \u95ED\u73AF\uFF09",
265
- sampleData: {
266
- userId: "u_1001",
267
- state: "not_enrolled",
268
- fields: [
269
- { fieldId: "name", label: "\u59D3\u540D", required: true },
270
- { fieldId: "phone", label: "\u624B\u673A\u53F7", required: true }
271
- ],
272
- actionId: "member.enroll"
273
- },
274
- schemaVersion: 1
275
- };
276
- var meta_default6 = meta6;
277
-
278
- // src/server/cards/member-enrollment-status/meta.ts
279
- import { memberEnrollmentStatusCardDataSchema } from "@tbox.cn/app-contracts-mall";
280
- var meta7 = {
281
- cardType: "member-enrollment-status",
282
- dataSchema: memberEnrollmentStatusCardDataSchema,
283
- allowedTools: [],
284
- displayName: "\u5165\u4F1A\u72B6\u6001",
285
- description: "\u5165\u4F1A\u7ED3\u679C\u72B6\u6001\uFF08\u6210\u529F/\u5931\u8D25 + \u4F1A\u5458\u7F16\u53F7\uFF09",
286
- sampleData: {
287
- userId: "u_1001",
288
- success: true,
289
- memberRef: "member:u_1001",
290
- state: "active"
291
- },
292
- schemaVersion: 1
293
- };
294
- var meta_default7 = meta7;
295
-
296
- // src/server/tool.ts
297
- import { createTool, coerceStringifiedFields } from "@tbox.cn/app-sdk/server";
298
- import { z } from "zod";
299
-
300
- // src/server/util.ts
301
- function nowIso() {
302
- return (/* @__PURE__ */ new Date()).toISOString();
303
- }
304
-
305
- // src/server/tool.ts
306
- function coerce(schema) {
307
- return z.preprocess(coerceStringifiedFields, schema);
308
- }
309
- function createQueryPointsTool(getMember) {
310
- return createTool({
311
- id: "queryPoints",
312
- description: "\u67E5\u8BE2\u4F1A\u5458\u5F53\u524D\u79EF\u5206\u4F59\u989D\u3001\u7B49\u7EA7\u4E0E\u79EF\u5206\u6D41\u6C34",
313
- inputSchema: z.object({
314
- userId: z.string().describe("\u4F1A\u5458 userId")
315
- }),
316
- outputSchema: z.object({
317
- userId: z.string(),
318
- balance: z.number(),
319
- level: z.enum(["silver", "gold", "platinum"]),
320
- items: z.array(
321
- z.object({
322
- usageRecordRef: z.string(),
323
- title: z.string(),
324
- amount: z.string(),
325
- usedAt: z.string().optional(),
326
- description: z.string().optional()
327
- })
328
- ),
329
- total: z.number()
330
- }),
331
- execute: async ({ userId }) => {
332
- const member2 = getMember();
333
- const points = member2 ? await member2.queryPoints(userId) : { userId, balance: 0, level: "silver" };
334
- const usage = member2 ? await member2.listPointUsage(userId, 1, 10) : { items: [], total: 0 };
335
- return {
336
- userId,
337
- balance: points.balance,
338
- level: points.level,
339
- items: [...usage.items],
340
- total: usage.total
341
- };
342
- }
343
- });
344
- }
345
- function createQueryMemberInfoTool(getMember) {
346
- return createTool({
347
- id: "queryMemberInfo",
348
- description: "\u67E5\u8BE2\u4F1A\u5458\u8D26\u6237\u72B6\u6001\u4E0E\u4E2A\u4EBA\u8D44\u6599\uFF08\u662F\u5426\u5165\u4F1A\u3001\u6635\u79F0\u3001\u8054\u7CFB\u65B9\u5F0F\u3001\u6807\u7B7E\uFF09",
349
- inputSchema: coerce(z.object({ userId: z.string().describe("\u4F1A\u5458 userId") })),
350
- execute: async ({ userId }) => {
351
- const member2 = getMember();
352
- const account = member2 ? await member2.getAccount(userId) : { state: "not_enrolled", observedAt: nowIso() };
353
- const profile = member2 ? await member2.getProfile(userId) : { labels: [] };
354
- return {
355
- userId,
356
- state: account.state,
357
- ...profile.displayName ? { displayName: profile.displayName } : {},
358
- ...profile.avatarUrl ? { avatarUrl: profile.avatarUrl } : {},
359
- ...profile.contactDisplay ? { contactDisplay: profile.contactDisplay } : {},
360
- ...account.memberRef ? { memberRef: account.memberRef } : {},
361
- ...account.registeredAt ? { registeredAt: account.registeredAt } : {},
362
- labels: profile.labels ?? []
363
- };
364
- }
365
- });
366
- }
367
- function createQueryMemberCardTool(getMember) {
368
- return createTool({
369
- id: "queryMemberCard",
370
- description: "\u67E5\u8BE2\u4F1A\u5458\u5361\u4FE1\u606F\uFF08\u5361\u9762\u3001\u7B49\u7EA7\u3001\u79EF\u5206\u6458\u8981\uFF09",
371
- inputSchema: coerce(z.object({ userId: z.string().describe("\u4F1A\u5458 userId") })),
372
- execute: async ({ userId }) => {
373
- const member2 = getMember();
374
- const card = member2 ? await member2.getCard(userId) : null;
375
- const points = member2 ? await member2.queryPoints(userId) : { balance: 0, level: "silver" };
376
- return {
377
- userId,
378
- cardRef: card?.cardRef ?? `card:${userId}`,
379
- cardDisplay: card?.cardDisplay ?? "\u5927\u60A6\u57CE\u4F1A\u5458\u5361",
380
- status: card?.status ?? "unknown",
381
- ...card?.levelName ? { levelName: card.levelName } : { levelName: points.level },
382
- availablePoints: points.balance,
383
- ...card?.cardImageUrl ? { cardImageUrl: card.cardImageUrl } : {}
384
- };
385
- }
386
- });
387
- }
388
- function createQueryEnrollmentFormTool(getMember) {
389
- return createTool({
390
- id: "queryEnrollmentForm",
391
- description: "\u67E5\u8BE2\u4F1A\u5458\u5165\u4F1A\u72B6\u6001\u5E76\u8FD4\u56DE\u5165\u4F1A\u8868\u5355\uFF08\u672A\u5165\u4F1A\u65F6\u7528\u6237\u53EF\u63D0\u4EA4\u5165\u4F1A\uFF09",
392
- inputSchema: z.object({ userId: z.string().describe("\u4F1A\u5458 userId") }),
393
- execute: async ({ userId }) => {
394
- const member2 = getMember();
395
- const account = member2 ? await member2.getAccount(userId) : { state: "not_enrolled" };
396
- return {
397
- userId,
398
- state: account.state,
399
- fields: account.state === "not_enrolled" ? [
400
- { fieldId: "name", label: "\u59D3\u540D", required: true },
401
- { fieldId: "phone", label: "\u624B\u673A\u53F7", required: true }
402
- ] : [],
403
- actionId: "member.enroll"
404
- };
405
- }
406
- });
407
- }
408
- function createQueryMemberCouponsTool(getMember) {
409
- return createTool({
410
- id: "queryMemberCoupons",
411
- description: "\u67E5\u8BE2\u4F1A\u5458\u4F18\u60E0\u5238\u5217\u8868\uFF08\u72B6\u6001/\u9762\u989D/\u95E8\u69DB\uFF09",
412
- inputSchema: coerce(z.object({ userId: z.string().describe("\u4F1A\u5458 userId") })),
413
- execute: async ({ userId }) => {
414
- const member2 = getMember();
415
- const result = member2 ? await member2.listCoupons(userId, 1, 20) : { items: [], total: 0 };
416
- return {
417
- userId,
418
- items: result.items.map((c) => ({
419
- couponRef: c.couponRef,
420
- title: c.title,
421
- type: c.type,
422
- status: c.status,
423
- ...c.benefitText ? { benefitText: c.benefitText } : {},
424
- ...c.thresholdText ? { thresholdText: c.thresholdText } : {}
425
- })),
426
- total: result.total
427
- };
428
- }
429
- });
430
- }
431
-
432
- // src/server/handler.ts
433
- import { z as z2 } from "zod";
434
- function createMemberHandler(ctx) {
435
- return {
436
- id: "member",
437
- handle: async (action, payload) => {
438
- const member2 = ctx.resolveService("member");
439
- if (!member2) return { cards: [], text: "\u4F1A\u5458\u670D\u52A1\u4E0D\u53EF\u7528" };
440
- const { userId } = payload ?? {};
441
- switch (action) {
442
- case "query-points": {
443
- if (!userId) return { cards: [], text: "\u7F3A\u5C11 userId" };
444
- const points = await member2.queryPoints(userId);
445
- const usage = await member2.listPointUsage(userId, 1, 10);
446
- return {
447
- cards: [
448
- {
449
- cardType: "member-point-usage-list",
450
- data: {
451
- userId,
452
- balance: points.balance,
453
- level: points.level,
454
- items: usage.items,
455
- total: usage.total
456
- }
457
- }
458
- ],
459
- text: `\u5F53\u524D\u79EF\u5206 ${points.balance}\uFF0C\u7B49\u7EA7 ${points.level}`
460
- };
461
- }
462
- default: {
463
- if (!userId) return { cards: [], text: "\u7F3A\u5C11 userId" };
464
- const points = await member2.queryPoints(userId);
465
- return {
466
- cards: [
467
- {
468
- cardType: "member-point-usage-list",
469
- data: {
470
- userId,
471
- balance: points.balance,
472
- level: points.level,
473
- items: [],
474
- total: 0
475
- }
476
- }
477
- ],
478
- text: `\u5F53\u524D\u79EF\u5206 ${points.balance}\uFF0C\u7B49\u7EA7 ${points.level}`
479
- };
480
- }
481
- }
482
- }
483
- };
484
- }
485
- var enrollActionInputSchema = z2.object({
486
- userId: z2.string().optional(),
487
- name: z2.string().optional(),
488
- phone: z2.string().optional()
489
- });
490
- function createEnrollAction(getMember) {
491
- return {
492
- actionId: "member.enroll",
493
- componentId: "member",
494
- cardType: "member-enrollment-form",
495
- label: "\u63D0\u4EA4\u5165\u4F1A",
496
- singleUse: true,
497
- inputSchema: enrollActionInputSchema,
498
- async execute(input, ctx) {
499
- const sessionUserId = ctx?.identity?.userId;
500
- const { userId: inputUserId, name, phone } = input ?? {};
501
- const userId = sessionUserId ?? inputUserId;
502
- const member2 = getMember();
503
- if (!userId) {
504
- return {
505
- card: {
506
- cardType: "member-enrollment-status",
507
- data: { userId: "", success: false, message: "\u7F3A\u5C11 userId", state: "not_enrolled" }
508
- },
509
- text: "\u5165\u4F1A\u5931\u8D25\uFF1A\u7F3A\u5C11\u8EAB\u4EFD\u4FE1\u606F"
510
- };
511
- }
512
- if (!member2) {
513
- return {
514
- card: {
515
- cardType: "member-enrollment-status",
516
- data: { userId, success: false, message: "\u4F1A\u5458\u670D\u52A1\u4E0D\u53EF\u7528", state: "not_enrolled" }
517
- },
518
- text: "\u5165\u4F1A\u5931\u8D25\uFF1A\u4F1A\u5458\u670D\u52A1\u4E0D\u53EF\u7528"
519
- };
520
- }
521
- const result = await member2.enroll({ userId, ...name ? { name } : {}, ...phone ? { phone } : {} });
522
- if (!result.success) {
523
- return {
524
- card: {
525
- cardType: "member-enrollment-status",
526
- data: { userId, success: false, message: result.error, state: "not_enrolled" }
527
- },
528
- text: `\u5165\u4F1A\u5931\u8D25\uFF1A${result.error ?? "\u672A\u77E5\u9519\u8BEF"}`
529
- };
530
- }
531
- const points = await member2.queryPoints(userId);
532
- return {
533
- card: {
534
- cardType: "member-enrollment-status",
535
- data: {
536
- userId,
537
- success: true,
538
- ...result.memberRef ? { memberRef: result.memberRef } : {},
539
- state: "active"
540
- }
541
- },
542
- text: `\u5165\u4F1A\u6210\u529F\uFF01\u5F53\u524D\u79EF\u5206 ${points.balance}\uFF0C\u7B49\u7EA7 ${points.level}`
543
- };
544
- }
545
- };
546
- }
547
-
548
- // src/server/cards/index.ts
549
- var memberCards = {
550
- "member-info": meta_default,
551
- "member-card-summary": meta_default2,
552
- "member-coupon-list": meta_default3,
553
- "member-coupon-detail": meta_default4,
554
- "member-point-usage-list": meta_default5,
555
- "member-enrollment-form": meta_default6,
556
- "member-enrollment-status": meta_default7
557
- };
558
-
559
- // src/server/index.ts
560
- var member = new InMemoryMemberService({ u_1001: 860, u_1002: 120 });
561
- var queryPointsTool = createQueryPointsTool(() => member);
562
- var queryMemberInfoTool = createQueryMemberInfoTool(() => member);
563
- var queryMemberCardTool = createQueryMemberCardTool(() => member);
564
- var queryMemberCouponsTool = createQueryMemberCouponsTool(() => member);
565
- var queryEnrollmentFormTool = createQueryEnrollmentFormTool(() => member);
566
- var cards = {
567
- "member-info": meta_default,
568
- "member-card-summary": meta_default2,
569
- "member-coupon-list": meta_default3,
570
- "member-coupon-detail": meta_default4,
571
- "member-point-usage-list": meta_default5,
572
- "member-enrollment-form": meta_default6,
573
- "member-enrollment-status": meta_default7
574
- };
575
- var enrollAction = createEnrollAction(() => member);
576
- var preTools = {
577
- queryPoints: definePreTool(queryPointsTool, {
578
- routing: { mode: "always" },
579
- decide: ({ identity }) => identity && identity.source !== "anonymous" ? { decision: "run", args: { userId: identity.userId } } : { decision: "skip", reason: "\u533F\u540D\u8EAB\u4EFD\u4E0D\u9884\u6267\u884C" }
580
- })
581
- };
582
- var serverModule = {
583
- declaration: {
584
- componentId: "member",
585
- features: ["member-profile", "member-card", "member-benefits", "member-points", "member-coupons", "member-enrollment"]
586
- },
587
- cards,
588
- preTools,
589
- register(ctx) {
590
- ctx.services.register("member", member);
591
- ctx.tools.register(queryPointsTool);
592
- ctx.tools.register(queryMemberInfoTool);
593
- ctx.tools.register(queryMemberCardTool);
594
- ctx.tools.register(queryMemberCouponsTool);
595
- ctx.tools.register(queryEnrollmentFormTool);
596
- ctx.cards.registerMap(cards);
597
- ctx.handlers.register(createMemberHandler(ctx));
598
- ctx.cardActions.register(enrollAction);
599
- const mallBus = ctx.bus;
600
- mallBus.subscribe("parking.coupon.used", async ({ userId, amount }) => {
601
- await member.awardPoints(userId, Math.floor(amount / 10));
602
- });
603
- member.onLevelChange(({ userId, newLevel }) => {
604
- void mallBus.publish("member.level.changed", { userId, newLevel });
605
- });
606
- }
607
- };
608
-
609
- export {
610
- InMemoryMemberService,
611
- memberCards,
612
- serverModule
613
- };