@pisell/pisellos 2.3.82 → 2.3.84
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.
- package/dist/modules/BookingContext/index.d.ts +3 -0
- package/dist/modules/BookingContext/index.js +42 -13
- package/dist/modules/BookingContext/types.d.ts +4 -4
- package/dist/modules/BookingContext/types.js +3 -3
- package/dist/modules/Quotation/index.d.ts +16 -0
- package/dist/modules/Quotation/index.js +241 -32
- package/dist/solution/BaseSales/index.js +92 -72
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/VenueBooking/index.d.ts +2 -0
- package/dist/solution/VenueBooking/index.js +542 -494
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/BookingContext/index.d.ts +3 -0
- package/lib/modules/BookingContext/index.js +35 -8
- package/lib/modules/BookingContext/types.d.ts +4 -4
- package/lib/modules/BookingContext/types.js +3 -3
- package/lib/modules/Quotation/index.d.ts +16 -0
- package/lib/modules/Quotation/index.js +117 -11
- package/lib/solution/BaseSales/index.js +25 -9
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/VenueBooking/index.d.ts +2 -0
- package/lib/solution/VenueBooking/index.js +28 -8
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/package.json +1 -1
|
@@ -7,5 +7,6 @@ export declare function buildDateRangeSummary(params: {
|
|
|
7
7
|
rawResources: VenueResourceRawData[];
|
|
8
8
|
resourceProductMap: Map<number | string, ResourceProductMapping[]>;
|
|
9
9
|
quotationModule?: QuotationModule;
|
|
10
|
+
channel?: string;
|
|
10
11
|
resolveConfig?: (date: string) => VenueBookingSlotConfig;
|
|
11
12
|
}): VenueDateSummaryItem[];
|
|
@@ -14,6 +14,7 @@ export function buildDateRangeSummary(params) {
|
|
|
14
14
|
rawResources = params.rawResources,
|
|
15
15
|
resourceProductMap = params.resourceProductMap,
|
|
16
16
|
quotationModule = params.quotationModule,
|
|
17
|
+
channel = params.channel,
|
|
17
18
|
resolveConfig = params.resolveConfig;
|
|
18
19
|
var result = [];
|
|
19
20
|
var productIds = quotationModule ? _toConsumableArray(new Set(_toConsumableArray(resourceProductMap.values()).flat().map(function (m) {
|
|
@@ -32,7 +33,8 @@ export function buildDateRangeSummary(params) {
|
|
|
32
33
|
});
|
|
33
34
|
quotationPriceMap = quotationModule.buildProductPriceMap({
|
|
34
35
|
productIds: productIds,
|
|
35
|
-
timePoints: timePoints
|
|
36
|
+
timePoints: timePoints,
|
|
37
|
+
channel: channel
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
40
|
var grid = buildTimeSlotGrid({
|
|
@@ -5,10 +5,12 @@ export declare function mergeConsecutiveSlots(slots: VenueSlotSelection[]): Merg
|
|
|
5
5
|
export declare function buildPriceBreakdown(params: {
|
|
6
6
|
group: MergedSlotGroup;
|
|
7
7
|
productId: number;
|
|
8
|
+
channel?: string;
|
|
8
9
|
quotation?: {
|
|
9
10
|
getQuotationShelfId: (p: {
|
|
10
11
|
productId: number;
|
|
11
12
|
datetime: string;
|
|
13
|
+
channel?: string;
|
|
12
14
|
}) => number;
|
|
13
15
|
};
|
|
14
16
|
}): PriceBreakdownEntry[];
|
|
@@ -53,6 +53,7 @@ export function mergeConsecutiveSlots(slots) {
|
|
|
53
53
|
export function buildPriceBreakdown(params) {
|
|
54
54
|
var group = params.group,
|
|
55
55
|
productId = params.productId,
|
|
56
|
+
channel = params.channel,
|
|
56
57
|
quotation = params.quotation;
|
|
57
58
|
if (!group.slots.length) return [];
|
|
58
59
|
var entries = [];
|
|
@@ -64,7 +65,8 @@ export function buildPriceBreakdown(params) {
|
|
|
64
65
|
var unitPrice = Number(slot.price || '0');
|
|
65
66
|
var shelfId = quotation ? quotation.getQuotationShelfId({
|
|
66
67
|
productId: productId,
|
|
67
|
-
datetime: slot.startTime
|
|
68
|
+
datetime: slot.startTime,
|
|
69
|
+
channel: channel
|
|
68
70
|
}) : 0;
|
|
69
71
|
var startHm = dayjs(slot.startTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
|
|
70
72
|
var endHm = dayjs(slot.endTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _examples.BUY_X_GET_Y_FREE_STRATEGY;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _examples.ITEM_REWARD_STRATEGY;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "PromotionAdapter", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _adapter.PromotionAdapter;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "PromotionEvaluator", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _evaluator.PromotionEvaluator;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "default", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _adapter.default;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
var _evaluator = require("./evaluator");
|
|
43
|
+
var _adapter = _interopRequireWildcard(require("./adapter"));
|
|
44
|
+
var _examples = require("./examples");
|
|
45
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
46
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -19,12 +19,15 @@ export declare class BookingContextModule extends BaseModule implements Module,
|
|
|
19
19
|
protected defaultVersion: string;
|
|
20
20
|
private request;
|
|
21
21
|
private store;
|
|
22
|
+
private otherParams;
|
|
22
23
|
/** 上次成功 loadBookingConfig 的 query key;命中则不再请求。 */
|
|
23
24
|
private lastConfigQueryKey;
|
|
24
25
|
/** 上次成功 loadResources 的 query key(date|bookingIds);命中则不再请求。 */
|
|
25
26
|
private lastResourcesQueryKey;
|
|
26
27
|
constructor(name?: string, version?: string);
|
|
27
28
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
29
|
+
/** 同步父级解决方案的运行态参数;platform 变化后会自然切换 config endpoint/cache key。 */
|
|
30
|
+
updateOtherParams(params: Record<string, any>): void;
|
|
28
31
|
loadBookingConfig(params?: LoadBookingConfigParams, options?: {
|
|
29
32
|
forceRefresh?: boolean;
|
|
30
33
|
}): Promise<BookingContextConfig | null>;
|
|
@@ -16,6 +16,7 @@ exports.buildBookingResourcesQueryKey = buildBookingResourcesQueryKey;
|
|
|
16
16
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
17
17
|
var _BaseModule = require("../BaseModule");
|
|
18
18
|
var _request = require("../../plugins/request");
|
|
19
|
+
var _platform = require("../../utils/platform");
|
|
19
20
|
var _types = require("./types");
|
|
20
21
|
Object.keys(_types).forEach(function (key) {
|
|
21
22
|
if (key === "default" || key === "__esModule") return;
|
|
@@ -52,6 +53,8 @@ const DEFAULT_BOOKING_CONFIG_PARAMS = {
|
|
|
52
53
|
item_type: 'appointment_booking',
|
|
53
54
|
sub_type: 'ticket'
|
|
54
55
|
};
|
|
56
|
+
const BOOKING_CONFIG_ENDPOINT = '/core/board/management/config';
|
|
57
|
+
const H5_BOOKING_CONFIG_ENDPOINT = '/core/board/config';
|
|
55
58
|
const BOARD_CONFIG_CACHE = {
|
|
56
59
|
useCache: true,
|
|
57
60
|
cache: {
|
|
@@ -167,6 +170,26 @@ function buildBookingConfigQueryKey(params = {}) {
|
|
|
167
170
|
});
|
|
168
171
|
return JSON.stringify(sorted);
|
|
169
172
|
}
|
|
173
|
+
function resolveBookingConfigRequest(platform, params = {}) {
|
|
174
|
+
const isH5 = (0, _platform.normalizeRuntimePlatform)(platform) === 'h5';
|
|
175
|
+
const endpoint = isH5 ? H5_BOOKING_CONFIG_ENDPOINT : BOOKING_CONFIG_ENDPOINT;
|
|
176
|
+
const query = isH5 ? {
|
|
177
|
+
item_type: params.item_type || DEFAULT_BOOKING_CONFIG_PARAMS.item_type
|
|
178
|
+
} : {
|
|
179
|
+
...DEFAULT_BOOKING_CONFIG_PARAMS,
|
|
180
|
+
...params
|
|
181
|
+
};
|
|
182
|
+
const keys = Object.keys(query).sort();
|
|
183
|
+
const sortedQuery = {};
|
|
184
|
+
keys.forEach(key => {
|
|
185
|
+
sortedQuery[key] = query[key];
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
endpoint,
|
|
189
|
+
query,
|
|
190
|
+
key: `${endpoint}:${JSON.stringify(sortedQuery)}`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
170
193
|
|
|
171
194
|
/** 稳定序列化资源列表查询(date + bookingIds),用于跳过重复 loadResources。 */
|
|
172
195
|
function buildBookingResourcesQueryKey(params = {}) {
|
|
@@ -181,6 +204,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
181
204
|
store = {
|
|
182
205
|
...EMPTY_STATE
|
|
183
206
|
};
|
|
207
|
+
otherParams = {};
|
|
184
208
|
/** 上次成功 loadBookingConfig 的 query key;命中则不再请求。 */
|
|
185
209
|
lastConfigQueryKey = null;
|
|
186
210
|
/** 上次成功 loadResources 的 query key(date|bookingIds);命中则不再请求。 */
|
|
@@ -194,6 +218,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
194
218
|
if (!this.request) {
|
|
195
219
|
throw new Error('BookingContextModule 需要 request 插件支持');
|
|
196
220
|
}
|
|
221
|
+
this.otherParams = options.otherParams || {};
|
|
197
222
|
this.store = {
|
|
198
223
|
bookingConfig: null,
|
|
199
224
|
resourcesOrigin: [],
|
|
@@ -212,15 +237,17 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
212
237
|
this.store.date = initialState.date;
|
|
213
238
|
}
|
|
214
239
|
}
|
|
240
|
+
|
|
241
|
+
/** 同步父级解决方案的运行态参数;platform 变化后会自然切换 config endpoint/cache key。 */
|
|
242
|
+
updateOtherParams(params) {
|
|
243
|
+
this.otherParams = params || {};
|
|
244
|
+
}
|
|
215
245
|
async loadBookingConfig(params = {}, options = {}) {
|
|
216
|
-
const
|
|
246
|
+
const requestConfig = resolveBookingConfigRequest(this.otherParams?.platform, params);
|
|
247
|
+
const configKey = requestConfig.key;
|
|
217
248
|
if (!options.forceRefresh && this.store.bookingConfig != null && this.lastConfigQueryKey === configKey) {
|
|
218
249
|
return this.store.bookingConfig;
|
|
219
250
|
}
|
|
220
|
-
const query = {
|
|
221
|
-
...DEFAULT_BOOKING_CONFIG_PARAMS,
|
|
222
|
-
...params
|
|
223
|
-
};
|
|
224
251
|
const sharedKey = buildSharedCacheKey(this.request, configKey);
|
|
225
252
|
if (!options.forceRefresh && sharedBookingConfigCache.has(sharedKey)) {
|
|
226
253
|
const cached = sharedBookingConfigCache.get(sharedKey) ?? null;
|
|
@@ -232,7 +259,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
232
259
|
if (!pending) {
|
|
233
260
|
const requestPromise = (async () => {
|
|
234
261
|
try {
|
|
235
|
-
const res = await this.request.get(
|
|
262
|
+
const res = await this.request.get(requestConfig.endpoint, requestConfig.query, BOARD_CONFIG_CACHE);
|
|
236
263
|
const config = res?.data ?? null;
|
|
237
264
|
sharedBookingConfigCache.set(sharedKey, config);
|
|
238
265
|
return config;
|
|
@@ -293,7 +320,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
293
320
|
* store 内是否已具备与本次 init 入参等价的 config / resources(不再打接口)。
|
|
294
321
|
*/
|
|
295
322
|
isBookingContextReady(params = {}) {
|
|
296
|
-
const configKey =
|
|
323
|
+
const configKey = resolveBookingConfigRequest(this.otherParams?.platform, params.bookingConfigParams).key;
|
|
297
324
|
const dateInput = params.date ?? this.store.date ?? (0, _dayjs.default)();
|
|
298
325
|
const queryDate = toScheduleQueryDate(dateInput);
|
|
299
326
|
const resourcesKey = buildBookingResourcesQueryKey({
|
|
@@ -311,7 +338,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
|
|
|
311
338
|
if (!params.forceRefresh && this.isBookingContextReady(params)) {
|
|
312
339
|
return this.getState();
|
|
313
340
|
}
|
|
314
|
-
const configKey =
|
|
341
|
+
const configKey = resolveBookingConfigRequest(this.otherParams?.platform, params.bookingConfigParams).key;
|
|
315
342
|
const resourcesKey = buildBookingResourcesQueryKey({
|
|
316
343
|
date: queryDate,
|
|
317
344
|
bookingIds: params.bookingIds
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* BookingContext 子模块:承载「预约 UI 上下文」三件套。
|
|
3
3
|
*
|
|
4
|
-
* - bookingConfig:来自 `/core/board/management/config` 的整套预约配置(service_time / resource_tab /
|
|
4
|
+
* - bookingConfig:来自 staff `/core/board/management/config` 或 H5 `/core/board/config` 的整套预约配置(service_time / resource_tab /
|
|
5
5
|
* appointment_subject / subject_require / stock_setting / basic / capacity 等)。
|
|
6
6
|
* - resourcesOrigin / resourcesOriginMap:来自 `/schedule/resource/list` 的资源全集,含 times / event_list /
|
|
7
7
|
* combined_resource 等运行态信息,是 getResourceByIds / getResourceTimeIsUsable 的输入。
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - 默认由 `loadBookingConfig` / `loadResources` 拉取并写入 store;也支持消费方 `set*` 直接灌入(复用 ticketBooking 已拉到的数据)。
|
|
12
12
|
* - 所有读 API 返回浅克隆(数组)或原引用(map / config),避免 UI 写穿。
|
|
13
13
|
*/
|
|
14
|
-
/** `loadBookingConfig`
|
|
14
|
+
/** `loadBookingConfig` 入参;H5 公共接口只消费 `item_type`。 */
|
|
15
15
|
export interface LoadBookingConfigParams {
|
|
16
16
|
item_type?: string;
|
|
17
17
|
sub_type?: string;
|
|
@@ -42,7 +42,7 @@ export interface InitBookingContextParams {
|
|
|
42
42
|
export type BookingContextResource = Record<string, any>;
|
|
43
43
|
export type BookingContextResourceMap = Record<string, BookingContextResource>;
|
|
44
44
|
/**
|
|
45
|
-
* 形参 booking_config(结构来自
|
|
45
|
+
* 形参 booking_config(结构来自 staff management 或 H5 board config 接口的 `data`)。
|
|
46
46
|
* OS 不强制 schema,保留 `Record<string, any>` 兼容现网;常用路径文档化在 SDK docs。
|
|
47
47
|
*/
|
|
48
48
|
export type BookingContextConfig = Record<string, any>;
|
|
@@ -80,7 +80,7 @@ export interface BookingContextModuleAPI {
|
|
|
80
80
|
getState(): BookingContextState;
|
|
81
81
|
/** 一键清空,destroy / 切租户场景用。 */
|
|
82
82
|
clear(): void;
|
|
83
|
-
/**
|
|
83
|
+
/** 按运行平台拉取 board config 并写入 store。 */
|
|
84
84
|
loadBookingConfig(params?: LoadBookingConfigParams): Promise<BookingContextConfig | null>;
|
|
85
85
|
/** 拉取资源全集并写入 store(`/schedule/resource/list`)。 */
|
|
86
86
|
loadResources(params?: LoadBookingResourcesParams): Promise<BookingContextResource[]>;
|
|
@@ -7,7 +7,7 @@ exports.BookingContextEvent = void 0;
|
|
|
7
7
|
/**
|
|
8
8
|
* BookingContext 子模块:承载「预约 UI 上下文」三件套。
|
|
9
9
|
*
|
|
10
|
-
* - bookingConfig:来自 `/core/board/management/config` 的整套预约配置(service_time / resource_tab /
|
|
10
|
+
* - bookingConfig:来自 staff `/core/board/management/config` 或 H5 `/core/board/config` 的整套预约配置(service_time / resource_tab /
|
|
11
11
|
* appointment_subject / subject_require / stock_setting / basic / capacity 等)。
|
|
12
12
|
* - resourcesOrigin / resourcesOriginMap:来自 `/schedule/resource/list` 的资源全集,含 times / event_list /
|
|
13
13
|
* combined_resource 等运行态信息,是 getResourceByIds / getResourceTimeIsUsable 的输入。
|
|
@@ -17,7 +17,7 @@ exports.BookingContextEvent = void 0;
|
|
|
17
17
|
* - 默认由 `loadBookingConfig` / `loadResources` 拉取并写入 store;也支持消费方 `set*` 直接灌入(复用 ticketBooking 已拉到的数据)。
|
|
18
18
|
* - 所有读 API 返回浅克隆(数组)或原引用(map / config),避免 UI 写穿。
|
|
19
19
|
*/
|
|
20
|
-
/** `loadBookingConfig`
|
|
20
|
+
/** `loadBookingConfig` 入参;H5 公共接口只消费 `item_type`。 */
|
|
21
21
|
/** `loadResources` 入参,与 info2 `getResourceList` 一致。 */
|
|
22
22
|
/** `initBookingContext` 入参(BookingTicket 聚合入口)。 */
|
|
23
23
|
/**
|
|
@@ -25,7 +25,7 @@ exports.BookingContextEvent = void 0;
|
|
|
25
25
|
* 这里只声明 Record 形态;具体字段保留 `any`,与 ticketBooking 现网传入的数据形状兼容。
|
|
26
26
|
*/
|
|
27
27
|
/**
|
|
28
|
-
* 形参 booking_config(结构来自
|
|
28
|
+
* 形参 booking_config(结构来自 staff management 或 H5 board config 接口的 `data`)。
|
|
29
29
|
* OS 不强制 schema,保留 `Record<string, any>` 兼容现网;常用路径文档化在 SDK docs。
|
|
30
30
|
*/
|
|
31
31
|
/**
|
|
@@ -11,12 +11,23 @@ export declare class QuotationModule extends BaseModule implements Module {
|
|
|
11
11
|
private scheduleResolver?;
|
|
12
12
|
private app;
|
|
13
13
|
private quotationListUpdatedListener?;
|
|
14
|
+
private quotationCache;
|
|
15
|
+
private quotationLoadPromises;
|
|
16
|
+
private quotationCacheVersions;
|
|
17
|
+
private lifecycleGeneration;
|
|
18
|
+
private activeChannelKey;
|
|
14
19
|
constructor(name?: string, version?: string);
|
|
15
20
|
initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
|
|
16
21
|
loadQuotations(params?: {
|
|
17
22
|
channel?: string;
|
|
18
23
|
customer_id?: number | string;
|
|
19
24
|
}): Promise<void>;
|
|
25
|
+
ensureQuotations(params?: {
|
|
26
|
+
channel?: string;
|
|
27
|
+
customer_id?: number | string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
invalidateQuotationCache(channel?: string): void;
|
|
30
|
+
private fetchQuotations;
|
|
20
31
|
getQuotationList(): QuotationItem[];
|
|
21
32
|
setQuotationListUpdatedListener(listener?: () => void): void;
|
|
22
33
|
getQuotationCustomerScopeInfo(): QuotationCustomerScopeInfo;
|
|
@@ -60,12 +71,17 @@ export declare class QuotationModule extends BaseModule implements Module {
|
|
|
60
71
|
buildProductPriceMap(params: {
|
|
61
72
|
productIds: number[];
|
|
62
73
|
timePoints: string[];
|
|
74
|
+
channel?: string;
|
|
63
75
|
customer_id?: number | string;
|
|
64
76
|
}): Map<string, string | null>;
|
|
65
77
|
setScheduleResolver(resolver: (id: number) => ScheduleItem | undefined): void;
|
|
66
78
|
private applyQuotationResponse;
|
|
67
79
|
private notifyQuotationListUpdated;
|
|
68
80
|
private getQuotationCacheKeyData;
|
|
81
|
+
private normalizeChannel;
|
|
82
|
+
private activateChannel;
|
|
83
|
+
private getQuotationCacheVersion;
|
|
84
|
+
private bumpQuotationCacheVersion;
|
|
69
85
|
private isQuotationVisibleForCustomer;
|
|
70
86
|
private hasValidCustomerId;
|
|
71
87
|
private normalizeCustomerId;
|
|
@@ -16,6 +16,11 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
16
16
|
scheduleResolver;
|
|
17
17
|
app;
|
|
18
18
|
quotationListUpdatedListener;
|
|
19
|
+
quotationCache = new Map();
|
|
20
|
+
quotationLoadPromises = new Map();
|
|
21
|
+
quotationCacheVersions = new Map();
|
|
22
|
+
lifecycleGeneration = 0;
|
|
23
|
+
activeChannelKey = '';
|
|
19
24
|
constructor(name, version) {
|
|
20
25
|
super(name, version);
|
|
21
26
|
}
|
|
@@ -27,17 +32,93 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
27
32
|
this.store = {
|
|
28
33
|
list: []
|
|
29
34
|
};
|
|
35
|
+
this.quotationCache.clear();
|
|
36
|
+
this.quotationLoadPromises.clear();
|
|
37
|
+
this.quotationCacheVersions.clear();
|
|
38
|
+
this.activeChannelKey = '';
|
|
39
|
+
this.lifecycleGeneration += 1;
|
|
30
40
|
}
|
|
31
41
|
async loadQuotations(params) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
const channelKey = this.normalizeChannel(params?.channel);
|
|
43
|
+
const cacheVersion = this.bumpQuotationCacheVersion(channelKey);
|
|
44
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
45
|
+
this.activateChannel(channelKey);
|
|
46
|
+
const loadPromise = this.fetchQuotations({
|
|
47
|
+
channelKey,
|
|
48
|
+
cacheVersion,
|
|
49
|
+
lifecycleGeneration
|
|
50
|
+
});
|
|
51
|
+
this.quotationLoadPromises.set(channelKey, loadPromise);
|
|
52
|
+
try {
|
|
53
|
+
await loadPromise;
|
|
54
|
+
} finally {
|
|
55
|
+
if (this.quotationLoadPromises.get(channelKey) === loadPromise) {
|
|
56
|
+
this.quotationLoadPromises.delete(channelKey);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async ensureQuotations(params) {
|
|
61
|
+
const channelKey = this.normalizeChannel(params?.channel);
|
|
62
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
63
|
+
while (true) {
|
|
64
|
+
if (this.lifecycleGeneration !== lifecycleGeneration) return;
|
|
65
|
+
if (this.quotationCache.has(channelKey)) {
|
|
66
|
+
this.activateChannel(channelKey);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const pending = this.quotationLoadPromises.get(channelKey);
|
|
70
|
+
if (!pending) {
|
|
71
|
+
try {
|
|
72
|
+
await this.loadQuotations(params);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (this.quotationCache.has(channelKey)) {
|
|
75
|
+
this.activateChannel(channelKey);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (this.quotationLoadPromises.has(channelKey)) continue;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const cacheVersion = this.getQuotationCacheVersion(channelKey);
|
|
84
|
+
try {
|
|
85
|
+
await pending;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
const latestPending = this.quotationLoadPromises.get(channelKey);
|
|
88
|
+
if (latestPending && latestPending !== pending) continue;
|
|
89
|
+
if (this.getQuotationCacheVersion(channelKey) !== cacheVersion) continue;
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
if (this.quotationCache.has(channelKey)) {
|
|
93
|
+
this.activateChannel(channelKey);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const latestPending = this.quotationLoadPromises.get(channelKey);
|
|
97
|
+
if (latestPending && latestPending !== pending) continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
invalidateQuotationCache(channel) {
|
|
101
|
+
if (channel !== undefined) {
|
|
102
|
+
const channelKey = this.normalizeChannel(channel);
|
|
103
|
+
this.bumpQuotationCacheVersion(channelKey);
|
|
104
|
+
this.quotationCache.delete(channelKey);
|
|
105
|
+
this.quotationLoadPromises.delete(channelKey);
|
|
106
|
+
if (this.activeChannelKey === channelKey) this.store.list = [];
|
|
107
|
+
return;
|
|
36
108
|
}
|
|
109
|
+
const channelKeys = new Set([...this.quotationCache.keys(), ...this.quotationLoadPromises.keys(), ...this.quotationCacheVersions.keys(), this.activeChannelKey]);
|
|
110
|
+
channelKeys.forEach(channelKey => this.bumpQuotationCacheVersion(channelKey));
|
|
111
|
+
this.quotationCache.clear();
|
|
112
|
+
this.quotationLoadPromises.clear();
|
|
113
|
+
this.store.list = [];
|
|
114
|
+
}
|
|
115
|
+
async fetchQuotations(params) {
|
|
116
|
+
const query = {};
|
|
117
|
+
if (params.channelKey) query.channel = params.channelKey;
|
|
37
118
|
let initialResultApplied = false;
|
|
38
119
|
const applyRemoteUpdate = response => {
|
|
39
|
-
this.applyQuotationResponse(response);
|
|
40
|
-
if (initialResultApplied) {
|
|
120
|
+
const applied = this.applyQuotationResponse(response, params);
|
|
121
|
+
if (applied && initialResultApplied && this.activeChannelKey === params.channelKey) {
|
|
41
122
|
this.notifyQuotationListUpdated();
|
|
42
123
|
}
|
|
43
124
|
};
|
|
@@ -49,7 +130,7 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
49
130
|
},
|
|
50
131
|
cacheUpdateChange: applyRemoteUpdate
|
|
51
132
|
});
|
|
52
|
-
this.applyQuotationResponse(res);
|
|
133
|
+
this.applyQuotationResponse(res, params);
|
|
53
134
|
initialResultApplied = true;
|
|
54
135
|
}
|
|
55
136
|
getQuotationList() {
|
|
@@ -109,7 +190,8 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
109
190
|
datetime,
|
|
110
191
|
customer_id
|
|
111
192
|
} = params;
|
|
112
|
-
|
|
193
|
+
const quotations = params.channel === undefined ? this.store.list : this.quotationCache.get(this.normalizeChannel(params.channel)) || [];
|
|
194
|
+
for (const quotation of quotations) {
|
|
113
195
|
const visible = this.isQuotationVisibleForCustomer(quotation, customer_id);
|
|
114
196
|
if (!visible) continue;
|
|
115
197
|
const active = this.isQuotationActiveAt(quotation, datetime);
|
|
@@ -135,13 +217,15 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
135
217
|
*/
|
|
136
218
|
buildProductPriceMap(params) {
|
|
137
219
|
const map = new Map();
|
|
138
|
-
|
|
220
|
+
const quotations = params.channel === undefined ? this.store.list : this.quotationCache.get(this.normalizeChannel(params.channel)) || [];
|
|
221
|
+
if (!quotations.length) return map;
|
|
139
222
|
for (const productId of params.productIds) {
|
|
140
223
|
for (const timePoint of params.timePoints) {
|
|
141
224
|
const key = `${productId}:${timePoint}`;
|
|
142
225
|
const price = this.getPriceForProduct({
|
|
143
226
|
productId,
|
|
144
227
|
datetime: timePoint,
|
|
228
|
+
channel: params.channel,
|
|
145
229
|
customer_id: params.customer_id
|
|
146
230
|
});
|
|
147
231
|
if (price !== null) {
|
|
@@ -154,9 +238,15 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
154
238
|
setScheduleResolver(resolver) {
|
|
155
239
|
this.scheduleResolver = resolver;
|
|
156
240
|
}
|
|
157
|
-
applyQuotationResponse(response) {
|
|
241
|
+
applyQuotationResponse(response, params) {
|
|
158
242
|
const source = response?.data?.list || response?.list || [];
|
|
159
|
-
|
|
243
|
+
const list = [...source].sort((a, b) => Number(b.sort || 0) - Number(a.sort || 0));
|
|
244
|
+
if (this.lifecycleGeneration !== params.lifecycleGeneration || this.getQuotationCacheVersion(params.channelKey) !== params.cacheVersion) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
this.quotationCache.set(params.channelKey, list);
|
|
248
|
+
if (this.activeChannelKey === params.channelKey) this.store.list = list;
|
|
249
|
+
return true;
|
|
160
250
|
}
|
|
161
251
|
notifyQuotationListUpdated() {
|
|
162
252
|
try {
|
|
@@ -179,6 +269,22 @@ class QuotationModule extends _BaseModule.BaseModule {
|
|
|
179
269
|
shop_id: shopId
|
|
180
270
|
};
|
|
181
271
|
}
|
|
272
|
+
normalizeChannel(channel) {
|
|
273
|
+
const normalized = channel?.trim().toLowerCase() || '';
|
|
274
|
+
return normalized === 'online_store' ? 'online-store' : normalized;
|
|
275
|
+
}
|
|
276
|
+
activateChannel(channelKey) {
|
|
277
|
+
this.activeChannelKey = channelKey;
|
|
278
|
+
this.store.list = this.quotationCache.get(channelKey) || [];
|
|
279
|
+
}
|
|
280
|
+
getQuotationCacheVersion(channelKey) {
|
|
281
|
+
return this.quotationCacheVersions.get(channelKey) || 0;
|
|
282
|
+
}
|
|
283
|
+
bumpQuotationCacheVersion(channelKey) {
|
|
284
|
+
const nextVersion = this.getQuotationCacheVersion(channelKey) + 1;
|
|
285
|
+
this.quotationCacheVersions.set(channelKey, nextVersion);
|
|
286
|
+
return nextVersion;
|
|
287
|
+
}
|
|
182
288
|
isQuotationVisibleForCustomer(quotation, customerId) {
|
|
183
289
|
const customers = quotation.customer || [];
|
|
184
290
|
if (customers.length === 0) return true;
|
|
@@ -639,8 +639,10 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
639
639
|
async loadQuotationForPriceQuery(params) {
|
|
640
640
|
if (!params.quotation) return;
|
|
641
641
|
this.applyQuotationScheduleResolver(params.quotation);
|
|
642
|
-
|
|
643
|
-
|
|
642
|
+
const quotation = params.quotation;
|
|
643
|
+
const load = typeof quotation.ensureQuotations === 'function' ? quotation.ensureQuotations.bind(quotation) : quotation.loadQuotations.bind(quotation);
|
|
644
|
+
await load({
|
|
645
|
+
channel: this.getPriceQueryChannel(params.channel),
|
|
644
646
|
customer_id: params.customer_id
|
|
645
647
|
});
|
|
646
648
|
}
|
|
@@ -3153,22 +3155,24 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3153
3155
|
const quotation = useCustomerUserPricing ? undefined : this.store.quotation;
|
|
3154
3156
|
const customerId = params.customer_id ?? this.getCurrentOrderCustomerId();
|
|
3155
3157
|
await this.prepareProductPriceQueryContext(customerId);
|
|
3158
|
+
const channel = this.getPriceQueryChannel(params.channel);
|
|
3156
3159
|
const [authoritativeProduct] = await this.resolveProductsForPriceQuery({
|
|
3157
3160
|
priceQueries: [params],
|
|
3158
3161
|
schedule: this.getPriceQuerySchedule(params),
|
|
3159
3162
|
customerId,
|
|
3160
|
-
channel
|
|
3163
|
+
channel
|
|
3161
3164
|
});
|
|
3162
3165
|
const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
|
|
3163
3166
|
if (!useCustomerUserPricing) {
|
|
3164
3167
|
await this.loadQuotationForPriceQuery({
|
|
3165
3168
|
quotation,
|
|
3166
3169
|
customer_id: customerId,
|
|
3167
|
-
channel
|
|
3170
|
+
channel
|
|
3168
3171
|
});
|
|
3169
3172
|
}
|
|
3170
3173
|
return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
|
|
3171
3174
|
...params,
|
|
3175
|
+
channel,
|
|
3172
3176
|
product,
|
|
3173
3177
|
fallback_price: authoritativeProduct ? undefined : params.fallback_price,
|
|
3174
3178
|
customer_id: customerId,
|
|
@@ -3242,11 +3246,12 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3242
3246
|
const useCustomerUserPricing = this.isCustomerUserPriceQuery();
|
|
3243
3247
|
const quotation = useCustomerUserPricing ? undefined : this.store.quotation;
|
|
3244
3248
|
const currentOrderCustomerId = this.getCurrentOrderCustomerId();
|
|
3249
|
+
const resolvedChannels = paramsList.map(params => this.getPriceQueryChannel(params.channel));
|
|
3245
3250
|
const authoritativeProducts = paramsList.map(() => null);
|
|
3246
3251
|
const groups = new Map();
|
|
3247
3252
|
paramsList.forEach((params, index) => {
|
|
3248
3253
|
const schedule = this.getPriceQuerySchedule(params);
|
|
3249
|
-
const channel =
|
|
3254
|
+
const channel = resolvedChannels[index];
|
|
3250
3255
|
const customerId = params.customer_id ?? currentOrderCustomerId;
|
|
3251
3256
|
const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || '', this.normalizePriceContextCustomerId(customerId) || ''].join('|');
|
|
3252
3257
|
const group = groups.get(groupKey) || {
|
|
@@ -3272,19 +3277,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3272
3277
|
});
|
|
3273
3278
|
}));
|
|
3274
3279
|
if (!useCustomerUserPricing) {
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
channel
|
|
3280
|
+
const quotationContexts = new Map();
|
|
3281
|
+
paramsList.forEach((params, index) => {
|
|
3282
|
+
const channel = resolvedChannels[index];
|
|
3283
|
+
const key = channel || '';
|
|
3284
|
+
if (quotationContexts.has(key)) return;
|
|
3285
|
+
quotationContexts.set(key, {
|
|
3286
|
+
channel,
|
|
3287
|
+
customer_id: params.customer_id ?? currentOrderCustomerId
|
|
3288
|
+
});
|
|
3279
3289
|
});
|
|
3290
|
+
await Promise.all(Array.from(quotationContexts.values()).map(context => this.loadQuotationForPriceQuery({
|
|
3291
|
+
quotation,
|
|
3292
|
+
...context
|
|
3293
|
+
})));
|
|
3280
3294
|
}
|
|
3281
3295
|
return paramsList.map((params, index) => {
|
|
3282
3296
|
const productInput = params.product || {};
|
|
3283
3297
|
const authoritativeProduct = authoritativeProducts[index];
|
|
3284
3298
|
const product = this.mergeProductForPriceQuery(productInput, authoritativeProduct);
|
|
3285
3299
|
const customerId = params.customer_id ?? currentOrderCustomerId;
|
|
3300
|
+
const channel = resolvedChannels[index];
|
|
3286
3301
|
return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
|
|
3287
3302
|
...params,
|
|
3303
|
+
channel,
|
|
3288
3304
|
product,
|
|
3289
3305
|
fallback_price: authoritativeProduct ? undefined : params.fallback_price,
|
|
3290
3306
|
customer_id: customerId,
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 | 1 | 2 |
|
|
329
|
+
weekNum: 0 | 1 | 2 | 5 | 3 | 4 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
345
345
|
count: number;
|
|
346
346
|
left: number;
|
|
347
347
|
summaryCount: number;
|
|
348
|
-
status: "
|
|
348
|
+
status: "sold_out" | "lots_of_space" | "filling_up_fast";
|
|
349
349
|
}[];
|
|
350
350
|
/**
|
|
351
351
|
* 找到多个资源的公共可用时间段
|
|
@@ -334,7 +334,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
334
334
|
* 获取当前的客户搜索条件
|
|
335
335
|
* @returns 当前搜索条件
|
|
336
336
|
*/
|
|
337
|
-
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "
|
|
337
|
+
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
|
|
338
338
|
/**
|
|
339
339
|
* 获取客户列表状态(包含滚动加载相关状态)
|
|
340
340
|
* @returns 客户状态
|
|
@@ -90,6 +90,8 @@ export declare class VenueBookingImpl extends BaseSalesImpl implements Module {
|
|
|
90
90
|
loadAddonProducts(): Promise<ProductData[]>;
|
|
91
91
|
getVenueProducts(): ProductData[];
|
|
92
92
|
getAddonProductsList(): ProductData[];
|
|
93
|
+
private ensureQuotationsLoaded;
|
|
94
|
+
private preloadQuotations;
|
|
93
95
|
loadQuotations(params?: {
|
|
94
96
|
channel?: string;
|
|
95
97
|
}): Promise<void>;
|