@tbox.cn/app-contracts-mall 0.2.0 → 0.10.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 (84) hide show
  1. package/README.md +16 -2
  2. package/dist/alipay-activity/cards.d.ts +305 -0
  3. package/dist/assembly.d.ts +73 -7
  4. package/dist/chunk-KIZD6FZ2.js +43 -0
  5. package/dist/deployment.d.ts +32 -33
  6. package/dist/domain/activity.d.ts +64 -0
  7. package/dist/domain/coupon.d.ts +2 -0
  8. package/dist/domain/entitlement.d.ts +53 -0
  9. package/dist/domain/offer.d.ts +111 -0
  10. package/dist/domain/operation.d.ts +22 -0
  11. package/dist/domain/parking.d.ts +195 -12
  12. package/dist/domain/points.d.ts +3 -2
  13. package/dist/domain/shopping-guide.d.ts +201 -0
  14. package/dist/index.d.ts +15 -2
  15. package/dist/mall-scope-url.d.ts +13 -0
  16. package/dist/member/cards.d.ts +636 -213
  17. package/dist/parking/cards.d.ts +1271 -0
  18. package/dist/promotion/cards.d.ts +2529 -0
  19. package/dist/query-context.d.ts +47 -0
  20. package/dist/runtime.d.ts +12 -2
  21. package/dist/runtime.js +940 -86
  22. package/dist/scope.d.ts +42 -15
  23. package/dist/server/index.d.ts +11 -0
  24. package/dist/server/index.js +92 -0
  25. package/dist/server/mall-context.d.ts +24 -0
  26. package/dist/server/mall-control-plane.d.ts +10 -0
  27. package/dist/server/request-credentials.d.ts +22 -0
  28. package/dist/services/customer-service.d.ts +11 -0
  29. package/dist/services/mall-info.d.ts +39 -0
  30. package/dist/services/member.d.ts +188 -31
  31. package/dist/services/parking.d.ts +115 -11
  32. package/dist/services/promotion.d.ts +100 -4
  33. package/dist/services/shopping-guide.d.ts +97 -0
  34. package/dist/shared/cards.d.ts +42 -0
  35. package/dist/shopping-guide/cards.d.ts +3399 -561
  36. package/package.json +19 -7
  37. package/src/alipay-activity/cards.ts +66 -0
  38. package/src/assembly.ts +159 -0
  39. package/src/deployment.ts +44 -0
  40. package/src/domain/activity.ts +66 -0
  41. package/src/domain/coupon.ts +11 -0
  42. package/src/domain/discount.ts +6 -0
  43. package/src/domain/entitlement.ts +56 -0
  44. package/src/domain/offer.ts +120 -0
  45. package/src/domain/operation.ts +40 -0
  46. package/src/domain/parking.ts +207 -0
  47. package/src/domain/points.ts +15 -0
  48. package/src/domain/shopping-guide.ts +242 -0
  49. package/src/events/index.ts +10 -0
  50. package/src/guard.ts +48 -0
  51. package/src/index.ts +34 -0
  52. package/src/mall-scope-url.ts +21 -0
  53. package/src/member/cards.ts +209 -0
  54. package/src/parking/cards.ts +233 -0
  55. package/src/promotion/cards.ts +290 -0
  56. package/src/query-context.ts +81 -0
  57. package/src/runtime.ts +43 -0
  58. package/src/scope.ts +92 -0
  59. package/src/server/index.ts +11 -0
  60. package/src/server/mall-context.ts +39 -0
  61. package/src/server/mall-control-plane.ts +92 -0
  62. package/src/server/request-credentials.ts +43 -0
  63. package/src/service-slots.json +26 -0
  64. package/src/services/customer-service.ts +12 -0
  65. package/src/services/mall-info.ts +48 -0
  66. package/src/services/member.ts +386 -0
  67. package/src/services/parking.ts +182 -0
  68. package/src/services/promotion.ts +126 -0
  69. package/src/services/shopping-guide.ts +138 -0
  70. package/src/shared/cards.ts +25 -0
  71. package/src/shopping-guide/cards.ts +438 -0
  72. package/tests/assembly-validate-scope.test.ts +132 -0
  73. package/tests/cards.test.ts +586 -0
  74. package/tests/context-unification.test.ts +73 -0
  75. package/tests/mall-control-plane.test.ts +169 -0
  76. package/tests/mall-scope-url.test.ts +59 -0
  77. package/tests/member-status.test.ts +197 -0
  78. package/tests/query-context.test.ts +111 -0
  79. package/tests/request-credentials.test.ts +38 -0
  80. package/tests/resolve-primitives.test.ts +55 -0
  81. package/tests/service-slots.test.ts +83 -0
  82. package/tsconfig.build.json +15 -0
  83. package/tsconfig.json +5 -0
  84. package/dist/shopping-guide/ports.d.ts +0 -216
package/dist/scope.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 商圈 scope(011 B2:TIER1 领域语义唯一真源):
3
- * MallScope 类型 + zod 解析 + RequestContext 提取器。
3
+ * MallScope 类型 + zod 解析 + ContextSlot 槽位 + RequestContext 提取器。
4
4
  *
5
5
  * 消费方(模块)只依赖本文件解析 attributes,不解析原始形状(011 B4);
6
6
  * TIER0(app-contracts)不出现任何 mall 语义(011 B1)。
@@ -11,8 +11,13 @@ import type { RequestContext } from '@tbox.cn/app-contracts';
11
11
  export declare const MALL_SCOPE_KEY = "mall";
12
12
  /** 商圈 scope(客户端输入形状——HELLO context.attributes.mall;会话边界键:mallId) */
13
13
  export interface MallScope {
14
- /** 小程序 appId(定位到具体小程序实例) */
15
- miniAppId: string;
14
+ /**
15
+ * 小程序 appId(可选项——v4 缺陷修复):
16
+ * 客户端发送侧本就条件附加(miniAppId 不知则省略),服务端此前必填造成
17
+ * mallId-only 会话 N1 死锁(默认注入形态被形状守卫拒绝)。miniAppId 是
18
+ * 尽力回填的展示/定位值,非会话边界(边界恒 mallId)。
19
+ */
20
+ miniAppId?: string;
16
21
  /** 商圈 id(会话边界:跨 mall 强制新会话,011 F3) */
17
22
  mallId: string;
18
23
  /** 可选主体 id(会员体系 subject) */
@@ -23,27 +28,49 @@ export interface MallSessionScope extends MallScope {
23
28
  sessionId: string;
24
29
  }
25
30
  export declare const mallScopeSchema: z.ZodObject<{
26
- miniAppId: z.ZodString;
31
+ miniAppId: z.ZodOptional<z.ZodString>;
27
32
  mallId: z.ZodString;
28
33
  subjectId: z.ZodOptional<z.ZodString>;
29
34
  }, "strip", z.ZodTypeAny, {
30
- miniAppId: string;
31
35
  mallId: string;
36
+ miniAppId?: string | undefined;
32
37
  subjectId?: string | undefined;
33
38
  }, {
34
- miniAppId: string;
35
39
  mallId: string;
40
+ miniAppId?: string | undefined;
36
41
  subjectId?: string | undefined;
37
42
  }>;
38
- /** 解析失败返回 undefined(不 throw),模块自行降级(011 B2 语义) */
43
+ /**
44
+ * mall 槽位(上下文统一化 Stage 2):TIER0 ContextSlot 协议上的 TIER1 定义——
45
+ * attributes.mall 的结构化读取(zod safeParse,fail-soft)。decide / handler / 工具
46
+ * 内统一经 mallSlot.read(attributes) 取 scope,替代散落的 as 断言。
47
+ */
48
+ export declare const mallSlot: import("@tbox.cn/app-contracts").ContextSlot<{
49
+ mallId: string;
50
+ miniAppId?: string | undefined;
51
+ subjectId?: string | undefined;
52
+ }, "mall">;
53
+ /** 解析失败返回 undefined(不 throw),模块自行降级(011 B2 语义;mallSlot 薄包装) */
39
54
  export declare function parseMallScope(attributes: Record<string, unknown> | undefined): MallScope | undefined;
40
55
  /** 从 RequestContext 提取 MallScope(parseMallScope 包装) */
41
56
  export declare function extractMallScope(ctx: RequestContext): MallScope | undefined;
42
- /** mall 领域运行上下文(attributes 含 mall 的 RequestContext 收窄,模块 handler 入参可用) */
43
- export type MallRequestContext = RequestContext & {
44
- attributes: Record<string, unknown> & {
45
- [MALL_SCOPE_KEY]: MallScope;
46
- };
47
- };
48
- /** MallRequestContext 取 MallScope(调用方已确认 scope 存在) */
49
- export declare function requireMallScope(ctx: MallRequestContext): MallScope;
57
+ /**
58
+ * 模块 ports 解析槽位统一 scope 形状(017 槽位原子化:resolveService 入参单一来源)。
59
+ * 行为钉死:返回 `{ mallId } | {}`(flat,不含 miniAppId)——与四模块 ports 既有行为
60
+ * 逐字等价;补 miniAppId 会改变厂商加签身份解析器的凭据 scope 匹配路径。
61
+ */
62
+ export declare function mallScopeOf(reqCtx: {
63
+ attributes?: Record<string, unknown>;
64
+ }): Record<string, string>;
65
+ /** mall 领域运行上下文(attributes 含 mall 的 RequestContext 收窄)
66
+ *
67
+ * @deprecated(上下文统一化 Stage 2):双断言供养的类型收窄已被 mallSlot.read /
68
+ * parseMallScope 显式解析取代——调用点直接传 RequestContext,消费侧 fail-soft 解析。
69
+ * 对外移除已随 0.8.0 落地(0.7.3 additive 波次保留至本波次)。
70
+ */
71
+ /**
72
+ * 登录 context 宽松读取 mallId(F2:换码器归 provider 包后的共享读取器)。
73
+ * 登录 URL 可只带 mallId(不套 parseMallScope 全形状)——登录换码先例,与
74
+ * MALL_SCOPE_KEY 共置单一真源;provider 包 scoped 谓词与换码体共用本入口。
75
+ */
76
+ export declare function readLoginMallId(context?: Readonly<Record<string, unknown>>): string | undefined;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * TIER1 服务端子路径出口(F2 模板中立化后):
3
+ * 请求凭证读取助手(request-credentials)+ mall 身份视图(mall-context)+
4
+ * mall 控制平面投影(mall-control-plane)。
5
+ * 登录换码器已整迁各 provider 包自注册
6
+ * (ctx.authExchanges.register,@tbox.cn/app-provider-<vendor>/server);
7
+ * 本包不再承载任何厂商实现与装配知识。
8
+ */
9
+ export * from './request-credentials';
10
+ export * from './mall-context';
11
+ export * from './mall-control-plane';
@@ -0,0 +1,92 @@
1
+ import {
2
+ extractMallScope
3
+ } from "../chunk-KIZD6FZ2.js";
4
+
5
+ // src/server/request-credentials.ts
6
+ function readString(source, key) {
7
+ if (source === null || typeof source !== "object") return "";
8
+ const value = source[key];
9
+ return typeof value === "string" ? value : "";
10
+ }
11
+ function credentialsToken(ctx) {
12
+ return readString(ctx.credentials, "token");
13
+ }
14
+ function credentialsMemberId(ctx) {
15
+ const attributes = ctx.credentials?.attributes;
16
+ return readString(attributes, "memberId");
17
+ }
18
+ function credentialsVisitortoken(ctx) {
19
+ const attributes = ctx.credentials?.attributes;
20
+ return readString(attributes, "visitortoken");
21
+ }
22
+ function credentialsOpenId(ctx) {
23
+ const attributes = ctx.credentials?.attributes;
24
+ return readString(attributes, "openid");
25
+ }
26
+
27
+ // src/server/mall-context.ts
28
+ function mallIdentityView(reqCtx) {
29
+ const memberId = credentialsMemberId(reqCtx);
30
+ const openId = credentialsOpenId(reqCtx);
31
+ const scope = extractMallScope(reqCtx);
32
+ return {
33
+ identity: reqCtx.identity,
34
+ ...scope ? { scope } : {},
35
+ sessionToken: credentialsToken(reqCtx),
36
+ ...memberId ? { memberId } : {},
37
+ ...openId ? { openId } : {}
38
+ };
39
+ }
40
+
41
+ // src/server/mall-control-plane.ts
42
+ import { z } from "zod";
43
+ import { normalizeInstances } from "@tbox.cn/app-contracts";
44
+ var mallAttributesSchema = z.strictObject({
45
+ /** 小程序 appId(mall 级共享——默认商圈拼装 HELLO context.mall.miniAppId 的承载) */
46
+ miniAppId: z.string().min(1).optional(),
47
+ /** 客服电话(shopping-guide customer-service-faq 卡 link 拨打条目消费,缺省不渲染) */
48
+ contactPhone: z.string().min(1).optional(),
49
+ /** 地理坐标(WGS-84;有值时参与 /api/m/mall-home/nearby-mall 就近推荐,缺省不参与排序) */
50
+ location: z.object({
51
+ lat: z.number(),
52
+ lon: z.number(),
53
+ /** 展示地址(可选;推荐 Banner 展示用) */
54
+ address: z.string().optional()
55
+ }).optional()
56
+ // 门店维度(stores)显式非目标(D13)——新行业拓扑 = 新 TIER1 投影包,不在本文件扩键
57
+ });
58
+ function parseMallAttributes(inst) {
59
+ const result = mallAttributesSchema.safeParse(inst.attributes ?? {});
60
+ if (!result.success) {
61
+ const detail = result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
62
+ throw new Error(
63
+ `integrations instances[${inst.id}].attributes \u8D4B\u4E49\u5931\u8D25\uFF08\u5141\u8BB8\u952E\uFF1AminiAppId/contactPhone/location\uFF09\u2014\u2014${detail}`
64
+ );
65
+ }
66
+ return { mallId: inst.id, name: inst.name, ...result.data };
67
+ }
68
+ function mergeDeclaredInstances(config, declared) {
69
+ if (!declared) return config;
70
+ if (config.instances && config.instances.length > 0) return config;
71
+ if (config.defaultInstanceId !== void 0) return config;
72
+ const rootProvider = config.provider;
73
+ if (!rootProvider) return config;
74
+ const instances = declared[rootProvider];
75
+ if (!instances || instances.length === 0) return config;
76
+ return { ...config, instances: [...instances] };
77
+ }
78
+ function mallControlPlaneOf(config, declared) {
79
+ const { instances, defaultInstanceId } = normalizeInstances(
80
+ mergeDeclaredInstances(config, declared)
81
+ );
82
+ const malls = instances.map(parseMallAttributes);
83
+ return { malls, ...defaultInstanceId !== null ? { defaultMallId: defaultInstanceId } : {} };
84
+ }
85
+ export {
86
+ credentialsMemberId,
87
+ credentialsOpenId,
88
+ credentialsToken,
89
+ credentialsVisitortoken,
90
+ mallControlPlaneOf,
91
+ mallIdentityView
92
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * mall 身份视图(上下文统一化 Stage 2):RequestContext → 一屏业务身份字段。
3
+ *
4
+ * 收敛「怎么带 mallId / userId / token / memberId / openId」的散装读取——
5
+ * identity 原样保留(source 四态可判;登录态 = source !== 'anonymous'),
6
+ * scope 经 mallSlot fail-soft 解析,凭证字段经 request-credentials 单源访问器。
7
+ */
8
+ import type { Identity, RequestContext } from '@tbox.cn/app-contracts';
9
+ import type { MallScope } from '../scope';
10
+ /** mall 域业务身份视图(模块 handler / 工具 / 卡片动作统一消费形态) */
11
+ export interface MallIdentityView {
12
+ /** 原样保留(source: local/external/dev/anonymous 四态可判) */
13
+ identity: Identity;
14
+ /** mall scope(attributes.mall fail-soft 解析;缺失/非法 → undefined,调用方降级) */
15
+ scope?: MallScope;
16
+ /** 登录 token(credentials.token;tokenless / 匿名 → ''——空串语义见 request-credentials) */
17
+ sessionToken: string;
18
+ /** 厂商 memberId(credentials.attributes.memberId) */
19
+ memberId?: string;
20
+ /** 平台 openId(credentials.attributes.openid——绑定场景;契约唯一通道) */
21
+ openId?: string;
22
+ }
23
+ /** 由 RequestContext 组装 mall 身份视图(纯函数,无 IO) */
24
+ export declare function mallIdentityView(reqCtx: RequestContext): MallIdentityView;
@@ -0,0 +1,10 @@
1
+ import type { IntegrationsConfig, ProviderInstanceDeclarations } from '@tbox.cn/app-contracts';
2
+ import type { MallDeploymentControlPlane } from '../deployment';
3
+ /**
4
+ * integrations config → mall 控制平面(装配链唯一入口):
5
+ * - declared = provider 声明快照(缺省关闭——既有调用方零变化;消费方 = mall-core project());
6
+ * - instances 注册表逐条投影(id→mallId、name、attributes 赋义展开——声明实例过同一
7
+ * zod strict 校验,坏键 fail-fast 与显式配置同律);
8
+ * - defaultMallId = normalizeInstances 解析(显式 ?? 单实例唯一解;无 → undefined)。
9
+ */
10
+ export declare function mallControlPlaneOf(config: IntegrationsConfig, declared?: ProviderInstanceDeclarations): MallDeploymentControlPlane;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 请求级凭证访问器(F5 单源):从 RequestContext.credentials 提取用户级凭证,
3
+ * 供 provider adapter factory 构造请求级薄壳 service(E2 不变式:用户凭证只入
4
+ * service 请求层,禁止写入被缓存的 transport/authStrategy)。
5
+ *
6
+ * 空 token 返回空串而非抛错——不需要 token 的工具可正常构造 service;
7
+ * 真正需要 token 的方法调用时厂商会拒绝,错误在正确边界产生。
8
+ */
9
+ import type { RequestContext } from '@tbox.cn/app-contracts';
10
+ /** 登录 token(ctx.credentials.token;厂商 body.Header.Token 注入场景) */
11
+ export declare function credentialsToken(ctx: RequestContext): string;
12
+ /** 厂商 memberId(ctx.credentials.attributes.memberId;openapi 会员域场景) */
13
+ export declare function credentialsMemberId(ctx: RequestContext): string;
14
+ /** 访客令牌(ctx.credentials.attributes.visitortoken;L3 访客层全程头携带) */
15
+ export declare function credentialsVisitortoken(ctx: RequestContext): string;
16
+ /**
17
+ * 平台 openId(ctx.credentials.attributes.openid;外部身份绑定场景)。
18
+ * 契约唯一通道:ExternalCredentials.attributes 三元组随 external claim 流动
19
+ *(identity.ts)——曾误读顶层 credentials.openid(契约无此键,恒 undefined,
20
+ * 致绑定动作 OpenId 恒空),勿改回顶层读取。
21
+ */
22
+ export declare function credentialsOpenId(ctx: RequestContext): string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 客服承接域 Port 契约(2026-08-31 客服模块拆分:自 shopping-guide AssistanceQueryPort
3
+ * 切出 FAQ/知识库两方法;人工导购 listHumanGuides 留导购域 AssistanceQueryPort)。
4
+ * 客服承接 = 本地知识库域(与 provider 远程检索面正交,恒本地装配)。
5
+ */
6
+ import type { ShoppingQueryContext } from './shopping-guide';
7
+ import type { FaqEntry, KnowledgeEntry } from '../domain/shopping-guide';
8
+ export interface CustomerServiceQueryPort {
9
+ searchFaq(ctx: ShoppingQueryContext, query: string, topK?: number): Promise<readonly FaqEntry[]>;
10
+ searchKnowledge(ctx: ShoppingQueryContext, query: string, topK?: number): Promise<readonly KnowledgeEntry[]>;
11
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * 商场基础信息 Port(mall.info 槽服务契约;动态商场信息源)。
3
+ * 厂商 wire 词汇(如厂商侧 plaza 端点词)不进本层——投影后只余平台词汇。
4
+ * 结构类型子集接口——消费方(模板装配点/模块)仅依赖契约包。
5
+ */
6
+ export declare const MALL_INFO_SERVICE = "mall.info";
7
+ /** 商场基础信息(动态商场场景下由 mall.info 槽按 mallId 拉取) */
8
+ export interface MallInfo {
9
+ /** 商场 id(= scope.mallId;投影键) */
10
+ mallId: string;
11
+ /** 商场名(品牌区 mallName 动态真源;与 malls 参考表 name 同语义) */
12
+ name: string;
13
+ address?: string;
14
+ latitude?: number;
15
+ longitude?: number;
16
+ /** 城市 id(nearby-mall 动态化预留,本期不消费) */
17
+ cityId?: string;
18
+ }
19
+ /** mall.info 槽供给端口(provider 包 adapter 实现;模板装配点经 integrations 解析) */
20
+ export interface MallInfoPort {
21
+ /**
22
+ * 查询商场信息;未知/失败 → undefined(fail-soft,调用方负责缓存与降级)。
23
+ * mallId 形状守卫由实现方负责(防 path 注入类字符进上游 URL)。
24
+ */
25
+ getMallInfo(mallId: string): Promise<MallInfo | undefined>;
26
+ }
27
+ /** 跨模块 'mall.info-cache' 词汇(与 service-slots.json 同锁)。 */
28
+ export declare const MALL_INFO_CACHE_SERVICE = "mall.info-cache";
29
+ /**
30
+ * mall.info 缓存 Port(缓存 → single-flight → mall.info 槽的读面)。
31
+ * module-mall-core register 期注册(闭包 integrations);消费方 resolveService 直读。
32
+ * 缓存纪律(TTL/负缓存/LRU/single-flight/形状守卫)由实现方收口。
33
+ */
34
+ export interface MallInfoCachePort {
35
+ /** 缓存名读取(同步回退通道;过期/负缓存 → undefined) */
36
+ cachedMallName(mallId: string): string | undefined;
37
+ /** 缓存 → single-flight → mall.info 槽拉取;失败/未知 → undefined(fail-soft) */
38
+ resolveMallInfo(mallId: string): Promise<MallInfo | undefined>;
39
+ }
@@ -1,12 +1,21 @@
1
1
  import type { Points } from '../domain/points';
2
2
  import type { DiscountRule } from '../domain/discount';
3
+ import type { Identity, RequestContext } from '@tbox.cn/app-contracts';
3
4
  /**
4
5
  * 会员域 Port(011 K2:6 Port 替换旧 MemberQueryPort/MemberSettlementPort,breaking 0.2.0)。
5
6
  * 结构类型子集接口——模块完整实现 extends 本接口,消费方仅依赖契约包。
6
7
  */
7
- /** 会员状态(含未入会/关闭) */
8
- export type MemberState = 'active' | 'not_enrolled' | 'closed';
8
+ export declare const MEMBER_ACCOUNT_SERVICE = "member.account";
9
+ export declare const MEMBER_PROFILE_SERVICE = "member.profile";
10
+ export declare const MEMBER_CARD_SERVICE = "member.card";
11
+ export declare const MEMBER_BENEFITS_SERVICE = "member.benefits";
12
+ export declare const MEMBER_ENROLLMENT_SERVICE = "member.enrollment";
13
+ /** 应用登录状态与会员状态相互独立;登录成功不代表已经入会。 */
14
+ export type MemberAuthState = 'authenticated' | 'unauthenticated';
15
+ /** unknown 表示当前身份无法向会员 Provider 确认状态;其余状态仅描述会员资格。 */
16
+ export type MemberState = 'unknown' | 'active' | 'not_enrolled' | 'closed';
9
17
  export interface MemberAccountSnapshot {
18
+ readonly authState: MemberAuthState;
10
19
  readonly state: MemberState;
11
20
  readonly memberRef?: string;
12
21
  readonly primaryMallCardNo?: string;
@@ -31,12 +40,49 @@ export interface MemberProfileSnapshot {
31
40
  export interface MemberProfilePort {
32
41
  getProfile(userId: string): Promise<MemberProfileSnapshot>;
33
42
  }
43
+ /** 通用卡面视觉变体;具体等级到变体的映射由 provider 负责。 */
44
+ export type MemberCardVisualVariant = 'blue' | 'silver' | 'gold' | 'black';
45
+ export interface MemberCardPresentationSnapshot {
46
+ readonly variant: MemberCardVisualVariant;
47
+ /** 背景资源是否已包含卡名、积分标签等静态文字。 */
48
+ readonly labelsEmbedded?: boolean;
49
+ }
50
+ /** 卡面消费进度;金额统一为分,提示文案由 provider 按厂商规则生成。 */
51
+ export interface MemberCardProgressSnapshot {
52
+ readonly daySpentCents: number;
53
+ readonly yearSpentCents: number;
54
+ readonly percent: number;
55
+ readonly message: string;
56
+ }
57
+ /** 卡片首屏权益摘要;规则正文与跳链不在摘要契约中直接暴露。 */
58
+ export interface MemberBenefitPreview {
59
+ readonly benefitRef: string;
60
+ readonly name: string;
61
+ readonly iconUrl?: string;
62
+ /** 厂商权益摘要标签,例如源码按 parkingFree 拼出的“可免 X 小时”。 */
63
+ readonly description?: string;
64
+ /** 厂商原生权益动作;客户端通过支付宝 startapp 深链交给宿主跳转。 */
65
+ readonly action?: {
66
+ readonly kind: 'miniapp' | 'dialog' | 'toast';
67
+ readonly url?: string;
68
+ readonly title?: string;
69
+ readonly text?: string;
70
+ };
71
+ }
34
72
  export interface MemberCardSnapshot {
35
73
  readonly cardRef: string;
36
74
  readonly cardDisplay: string;
37
75
  readonly status: 'active' | 'inactive' | 'frozen' | 'expired' | 'unknown';
38
76
  readonly levelName?: string;
39
77
  readonly cardImageUrl?: string;
78
+ /** 积分规则长图;客户端以只读图片弹层展示,不作为通用跳链执行。 */
79
+ readonly pointsRuleImageUrl?: string;
80
+ readonly pointsRuleContent?: string;
81
+ /** C 端原生“我的积分”明细页深链。 */
82
+ readonly pointsUrl?: string;
83
+ readonly presentation?: MemberCardPresentationSnapshot;
84
+ readonly progress?: MemberCardProgressSnapshot;
85
+ readonly benefits?: ReadonlyArray<MemberBenefitPreview>;
40
86
  readonly observedAt: string;
41
87
  }
42
88
  export interface MemberCardPort {
@@ -74,38 +120,149 @@ export interface PointUsageRecord {
74
120
  readonly usedAt?: string;
75
121
  readonly description?: string;
76
122
  }
77
- export interface MemberCouponSnapshot {
78
- readonly couponRef: string;
79
- readonly title: string;
80
- readonly type: 'amount' | 'discount' | 'gift' | 'parking' | 'unknown';
81
- readonly status: 'effective' | 'used' | 'expired' | 'invalid' | 'unknown';
82
- readonly description?: string;
83
- readonly benefitText?: string;
84
- readonly thresholdText?: string;
123
+ /**
124
+ * 会员服务解析工厂(上下文统一化 Stage 2):跨模块 'member.service' 注册面。
125
+ * member 模块 register 期注册 `(reqCtx) => resolveMemberBenefitsPort(reqCtx, ctx)`
126
+ * (017 槽位原子化:本面 = benefits 单槽契约,benefits 方法族);
127
+ * 跨模块消费方经 ctx.resolveService<MemberBenefitsFactory>('member.service')
128
+ * 结构解析后 `await factory(reqCtx)`——替代旧 'member' 全局门面(ctx.auth 并发缺陷)。
129
+ * 身份/会员资格判定请走 'member.status' 注册面(outcome + account),勿经本面取 account 域数据。
130
+ */
131
+ export type MemberBenefitsFactory = (reqCtx: RequestContext) => Promise<MemberBenefitsPort>;
132
+ /** 入会身份来源(F2:授权 profile 可信度高于手填) */
133
+ export type MemberEnrollmentIdentity = 'alipay_authorized' | 'enrollment_submission';
134
+ export interface MemberEnrollmentInput {
135
+ readonly userId: string;
136
+ readonly identity: MemberEnrollmentIdentity;
137
+ /** 表单字段(mobile 等;授权模式下 mobile 强制以授权 profile 为准) */
138
+ readonly fieldValues: Readonly<Record<string, string>>;
139
+ /** 已同意的 consent id 集合(模块侧校验必勾后传入) */
140
+ readonly acceptedConsentIds: readonly string[];
141
+ /** 幂等键(模块生成:sha256(userId\0mallId\0formVersion)) */
142
+ readonly idempotencyKey: string;
143
+ readonly formVersion?: string;
85
144
  }
86
- export interface MemberCouponPort {
87
- listCoupons(userId: string, page: number, pageSize: number): Promise<{
88
- items: ReadonlyArray<MemberCouponSnapshot>;
89
- total: number;
90
- }>;
91
- getCoupon(userId: string, couponRef: string): Promise<MemberCouponSnapshot | null>;
145
+ export type MemberEnrollmentResult = {
146
+ readonly status: 'active';
147
+ readonly memberRef: string;
92
148
  }
149
+ /** 厂商异步确认(pending 卡 + 显式刷新收口,R4) */
150
+ | {
151
+ readonly status: 'pending_confirmation';
152
+ readonly attemptRef: string;
153
+ } | {
154
+ readonly status: 'rejected';
155
+ readonly reason?: string;
156
+ };
157
+ export type MemberEnrollmentStatus = 'pending_confirmation' | 'confirmation_timeout' | 'active' | 'failed';
93
158
  export interface MemberEnrollmentPort {
94
- /** 入会(授权回调后调用;幂等:已入会返回成功) */
95
- enroll(input: {
96
- userId: string;
97
- name?: string;
98
- phone?: string;
99
- }): Promise<{
100
- success: boolean;
159
+ /** 入会(幂等:同 idempotencyKey 重入返回首果) */
160
+ enroll(input: MemberEnrollmentInput): Promise<MemberEnrollmentResult>;
161
+ /** 入会状态查询(attemptRef 归属校验由模块完成) */
162
+ getEnrollmentStatus(userId: string, attemptRef: string): Promise<{
163
+ status: MemberEnrollmentStatus;
101
164
  memberRef?: string;
102
- error?: string;
103
- }>;
104
- /** 授权回调补签(alipay-user-profile 授权结果 → 会员资料落库) */
105
- completeAuthorization(input: {
106
- userId: string;
107
- scopes: readonly string[];
108
- }): Promise<{
109
- success: boolean;
165
+ reason?: string;
110
166
  }>;
167
+ /**
168
+ * 入会表单定义(厂商端点拉取,如厂商 enrollmentConfig 端点);无表单端点厂商可缺省——
169
+ * 调用方 typeof 守卫降级(calculateBestDeal? 同款先例)。017 R13 自 module-member
170
+ * fat 接口收编。
171
+ */
172
+ getEnrollmentFormDefinition?(userId: string): Promise<MemberEnrollmentFormDefinition>;
173
+ }
174
+ /**
175
+ * 解析平台 userId → 厂商会员外部引用(externalMemberRef)。
176
+ * member 模块注册实现(ctx.services.register('member.subject-binding', ...))。
177
+ * @deprecated 同步派生引用不承载会员身份语义(对任意 userId 恒返回派生值);
178
+ * 会员状态判定与真实外部会员引用请改用 'member.status' 服务(MemberStatusFactory)
179
+ * 与 externalMemberRefOf。保留仅为版本偏斜期兼容。
180
+ */
181
+ export interface SubjectBindingPort {
182
+ resolveExternalMemberRef(userId: string): string | undefined;
183
+ }
184
+ export interface MemberEnrollmentFormFieldDefinition {
185
+ readonly fieldId: string;
186
+ readonly label: string;
187
+ readonly type: 'text' | 'tel' | 'select' | 'date';
188
+ readonly required: boolean;
189
+ readonly placeholder?: string;
190
+ readonly options?: readonly string[];
191
+ }
192
+ export interface MemberEnrollmentConsentDefinition {
193
+ readonly consentId: string;
194
+ readonly version: string;
195
+ readonly title: string;
196
+ readonly required: boolean;
197
+ }
198
+ export interface MemberEnrollmentFormDefinition {
199
+ readonly formVersion: string;
200
+ readonly fields: readonly MemberEnrollmentFormFieldDefinition[];
201
+ readonly consents: readonly MemberEnrollmentConsentDefinition[];
202
+ readonly authorizationStatus: 'required' | 'unavailable';
203
+ }
204
+ /** 折叠结果:'unauthenticated'(平台匿名短路或凭证轴未认证)或原始会员资格态。 */
205
+ export type MemberOutcome = 'unauthenticated' | MemberState;
206
+ /**
207
+ * 双轴折叠纯函数(无 IO,唯一真源):identity 轴(平台登录态)优先短路;
208
+ * account 轴(凭证态)独立折叠——凭证轴语义由各 provider 自定义,
209
+ * tokenless 的合法登录可为 unauthenticated。
210
+ */
211
+ export declare function classifyMemberOutcome(identity: Pick<Identity, 'source'> | null | undefined, account: MemberAccountSnapshot | null | undefined): MemberOutcome;
212
+ /**
213
+ * 厂商侧会员标识:真实卡号优先,opaque 引用兜底。
214
+ * 兜底引用仅对不校验真实卡号的 provider 安全——需要真实卡号的厂商接口
215
+ * 必须由 provider 提供 primaryMallCardNo。
216
+ */
217
+ export declare function externalMemberRefOf(account: MemberAccountSnapshot): string | undefined;
218
+ /** 跨模块会员状态解析结果(窄读面;实现方注册产物可为超集)。 */
219
+ export interface MemberStatusResolution {
220
+ readonly outcome: MemberOutcome | 'unavailable';
221
+ readonly account?: MemberAccountSnapshot;
222
+ /** 入会引导深链(authenticated 未入会态;未配置缺省) */
223
+ readonly enrollUrl?: string;
224
+ /** 登录引导深链(unauthenticated 态;未配置缺省) */
225
+ readonly loginUrl?: string;
226
+ }
227
+ /** 跨模块 'member.status' 解析工厂(与 MemberBenefitsFactory 并列的注册面契约)。 */
228
+ export type MemberStatusFactory = (reqCtx: RequestContext) => Promise<MemberStatusResolution>;
229
+ /** 门禁卡描述子(形状 = SDK ToolCardSpec { cardType, data, note },出卡管线兼容)。 */
230
+ export interface MemberGateCard {
231
+ readonly cardType: 'member-enrollment-cta' | 'member-login-cta';
232
+ readonly data: Record<string, unknown>;
233
+ readonly note: string;
234
+ }
235
+ /**
236
+ * 纯卡映射:outcome → 门禁卡描述子;硬门禁态恒出卡,深链为**可选透传素材**(未配置
237
+ * 时卡无 URL 字段,客户端禁用按钮/壳能力接管);软放行态 / userId 空 → undefined,
238
+ * 调用方退纯文字引导(notice)。
239
+ * 卡 schema 真源:memberEnrollmentCtaCardDataSchema / memberLoginCtaCardDataSchema(本包 member/cards.ts)。
240
+ */
241
+ export declare function memberGateCardOf(userId: string, status: Pick<MemberStatusResolution, 'outcome' | 'enrollUrl' | 'loginUrl'>): MemberGateCard | undefined;
242
+ /** resolveMemberGate 返回的拦截块(undefined = 放行)。 */
243
+ export interface MemberGateBlock {
244
+ readonly card?: MemberGateCard;
245
+ readonly notice: string;
246
+ }
247
+ /**
248
+ * 门禁编排单源:工厂缺席 / 调用抛错 → fail-open(undefined 放行);
249
+ * active / unknown / unavailable → 软放行(读路径软语义——不误伤瞬时故障的会员);
250
+ * 硬门禁(unauthenticated/not_enrolled/closed)→ { card?(未配深链缺省), notice }。
251
+ * 实现方(member.status 工厂)应自收口异常;本函数对抛错实现兜底为软放行(横切面鲁棒性)。
252
+ */
253
+ export declare function resolveMemberGate(factory: MemberStatusFactory | undefined, reqCtx: RequestContext): Promise<MemberGateBlock | undefined>;
254
+ /** 跨模块 'member.snapshot' 词汇(与 service-slots.json 同锁)。 */
255
+ export declare const MEMBER_SNAPSHOT_SERVICE = "member.snapshot";
256
+ /**
257
+ * 会员快览 Port(linked-panel 消费面:账户/授权三态 + 积分 + 入会引导)。
258
+ * member 模块 register 期注册工厂('member.service' 工厂先例);消费方
259
+ * resolveService 后 await factory(reqCtx) 取 Port——enrollUrl 随请求态解析。
260
+ */
261
+ export interface MemberSnapshotPort {
262
+ getAccount(userId: string): Promise<MemberAccountSnapshot>;
263
+ queryPoints(userId: string): Promise<Points>;
264
+ /** 入会引导深链(未入会态引导;enrollment 槽缺席/失败时缺省——fail-soft 不拖垮主面) */
265
+ readonly enrollUrl?: string;
111
266
  }
267
+ /** 跨模块 'member.snapshot' 解析工厂(register 期注册,运行期调用)。 */
268
+ export type MemberSnapshotFactory = (reqCtx: RequestContext) => Promise<MemberSnapshotPort>;