@pisell/pisellos 2.2.97 → 2.2.99
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/model/strategy/adapter/walletPass/type.d.ts +2 -2
- package/dist/model/strategy/adapter/walletPass/utils.js +70 -57
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/server/index.d.ts +30 -0
- package/dist/server/index.js +660 -330
- package/dist/server/modules/floor-plan/index.d.ts +39 -0
- package/dist/server/modules/floor-plan/index.js +595 -0
- package/dist/server/modules/floor-plan/types.d.ts +43 -0
- package/dist/server/modules/floor-plan/types.js +13 -0
- package/dist/server/modules/index.d.ts +3 -0
- package/dist/server/modules/index.js +4 -0
- package/dist/server/modules/order/types.d.ts +13 -1
- package/dist/server/modules/order/types.js +2 -1
- package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/dist/server/modules/order/utils/filterBookings.js +64 -4
- package/dist/server/modules/resource/index.d.ts +0 -5
- package/dist/server/modules/resource/index.js +186 -269
- package/dist/server/types.d.ts +2 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/Sales/index.d.ts +2 -1
- package/dist/solution/Sales/index.js +23 -10
- package/dist/solution/Sales/types.d.ts +1 -1
- package/lib/model/strategy/adapter/walletPass/type.d.ts +2 -2
- package/lib/model/strategy/adapter/walletPass/utils.js +58 -51
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/server/index.d.ts +30 -0
- package/lib/server/index.js +202 -9
- package/lib/server/modules/floor-plan/index.d.ts +39 -0
- package/lib/server/modules/floor-plan/index.js +327 -0
- package/lib/server/modules/floor-plan/types.d.ts +43 -0
- package/lib/server/modules/floor-plan/types.js +34 -0
- package/lib/server/modules/index.d.ts +3 -0
- package/lib/server/modules/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +13 -1
- package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/lib/server/modules/order/utils/filterBookings.js +69 -3
- package/lib/server/modules/resource/index.d.ts +0 -5
- package/lib/server/modules/resource/index.js +60 -73
- package/lib/server/types.d.ts +2 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/Sales/index.d.ts +2 -1
- package/lib/solution/Sales/index.js +11 -4
- package/lib/solution/Sales/types.d.ts +1 -1
- package/package.json +1 -1
package/lib/server/index.js
CHANGED
|
@@ -39,16 +39,20 @@ var import_menu = require("./modules/menu");
|
|
|
39
39
|
var import_quotation = require("./modules/quotation");
|
|
40
40
|
var import_schedule = require("./modules/schedule");
|
|
41
41
|
var import_resource = require("./modules/resource");
|
|
42
|
+
var import_floor_plan = require("./modules/floor-plan");
|
|
43
|
+
var import_types = require("./modules/floor-plan/types");
|
|
42
44
|
var import_schedule2 = require("./utils/schedule");
|
|
43
|
-
var
|
|
45
|
+
var import_types2 = require("./modules/products/types");
|
|
44
46
|
var import_product = require("./utils/product");
|
|
45
47
|
var import_order = require("./modules/order");
|
|
46
|
-
var
|
|
48
|
+
var import_types3 = require("./modules/order/types");
|
|
47
49
|
var import_filterOrders = require("./modules/order/utils/filterOrders");
|
|
48
50
|
var import_filterBookings = require("./modules/order/utils/filterBookings");
|
|
49
51
|
__reExport(server_exports, require("./modules"), module.exports);
|
|
50
52
|
var Server = class {
|
|
51
53
|
constructor(core) {
|
|
54
|
+
/** GET 前缀路由(最长前缀优先匹配) */
|
|
55
|
+
this.prefixRouterGet = [];
|
|
52
56
|
// 路由注册表
|
|
53
57
|
this.router = {
|
|
54
58
|
get: {},
|
|
@@ -61,6 +65,7 @@ var Server = class {
|
|
|
61
65
|
// ---- 订单 / 预约列表查询订阅者 ----
|
|
62
66
|
this.orderQuerySubscribers = /* @__PURE__ */ new Map();
|
|
63
67
|
this.bookingQuerySubscribers = /* @__PURE__ */ new Map();
|
|
68
|
+
this.floorPlanQuerySubscribers = /* @__PURE__ */ new Map();
|
|
64
69
|
// 模块注册表 - 定义所有可用的模块配置
|
|
65
70
|
this.moduleRegistry = {
|
|
66
71
|
products: {
|
|
@@ -121,6 +126,17 @@ var Server = class {
|
|
|
121
126
|
map: /* @__PURE__ */ new Map(),
|
|
122
127
|
bookings: []
|
|
123
128
|
}
|
|
129
|
+
},
|
|
130
|
+
floor_plan: {
|
|
131
|
+
name: "floor_plan",
|
|
132
|
+
moduleClass: import_floor_plan.FloorPlanModule,
|
|
133
|
+
moduleName: "server_floor_plan",
|
|
134
|
+
version: "1.0.0",
|
|
135
|
+
defaultStore: {
|
|
136
|
+
list: [],
|
|
137
|
+
map: /* @__PURE__ */ new Map(),
|
|
138
|
+
codeMap: /* @__PURE__ */ new Map()
|
|
139
|
+
}
|
|
124
140
|
}
|
|
125
141
|
};
|
|
126
142
|
/**
|
|
@@ -245,6 +261,38 @@ var Server = class {
|
|
|
245
261
|
status: true
|
|
246
262
|
};
|
|
247
263
|
};
|
|
264
|
+
/**
|
|
265
|
+
* GET /shop/schedule/floor-plan* 前缀路由:读本地 store;支持 subscriberId + callback 订阅更新
|
|
266
|
+
*/
|
|
267
|
+
this.handleFloorPlanGet = async ({ url, data, config, path: routePath }) => {
|
|
268
|
+
const base = "/shop/schedule/floor-plan";
|
|
269
|
+
const path = this.parseRequestPath(url, routePath);
|
|
270
|
+
if (path === `${base}/unsubscribe`) {
|
|
271
|
+
const subscriberId2 = data == null ? void 0 : data.subscriberId;
|
|
272
|
+
if (subscriberId2) {
|
|
273
|
+
this.floorPlanQuerySubscribers.delete(subscriberId2);
|
|
274
|
+
this.logInfo("handleFloorPlanGet: 已取消订阅", {
|
|
275
|
+
subscriberId: subscriberId2,
|
|
276
|
+
remaining: this.floorPlanQuerySubscribers.size
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return { code: 200, message: "ok", status: true };
|
|
280
|
+
}
|
|
281
|
+
const { callback, subscriberId } = config || {};
|
|
282
|
+
const ctx = this.resolveFloorPlanQueryContext(path, base);
|
|
283
|
+
if (!ctx) {
|
|
284
|
+
return { status: false, code: 404, message: "Not Found", data: null };
|
|
285
|
+
}
|
|
286
|
+
if (subscriberId && typeof callback === "function") {
|
|
287
|
+
this.floorPlanQuerySubscribers.set(subscriberId, { callback, context: ctx });
|
|
288
|
+
this.logInfo("handleFloorPlanGet: 已注册订阅者", {
|
|
289
|
+
subscriberId,
|
|
290
|
+
kind: ctx.kind,
|
|
291
|
+
total: this.floorPlanQuerySubscribers.size
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return this.computeFloorPlanQueryResult(ctx);
|
|
295
|
+
};
|
|
248
296
|
/**
|
|
249
297
|
* 取消预约列表查询订阅(HTTP 路由入口)
|
|
250
298
|
*/
|
|
@@ -387,6 +435,23 @@ var Server = class {
|
|
|
387
435
|
this.logInfo(`注册路由`, { method: method.toUpperCase(), path });
|
|
388
436
|
});
|
|
389
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* 注册前缀路由(仅 GET)。匹配规则:path === prefix 或 path 以 prefix + '/' 开头;最长前缀优先。
|
|
440
|
+
*/
|
|
441
|
+
registerPrefixRoutes(routes) {
|
|
442
|
+
for (const r of routes) {
|
|
443
|
+
if (r.method !== "get") {
|
|
444
|
+
this.logWarning("registerPrefixRoutes: 当前仅支持 GET", {
|
|
445
|
+
method: r.method,
|
|
446
|
+
prefix: r.prefix
|
|
447
|
+
});
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
this.prefixRouterGet.push({ prefix: r.prefix, handler: r.handler });
|
|
451
|
+
}
|
|
452
|
+
this.prefixRouterGet.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
453
|
+
this.logInfo("前缀 GET 路由已更新", { count: this.prefixRouterGet.length });
|
|
454
|
+
}
|
|
390
455
|
/**
|
|
391
456
|
* 注册单个模块并自动注册其路由
|
|
392
457
|
* @param module 模块实例
|
|
@@ -557,12 +622,15 @@ var Server = class {
|
|
|
557
622
|
} else {
|
|
558
623
|
this.logInfo("跳过自动预加载", { autoPreload });
|
|
559
624
|
}
|
|
560
|
-
this.core.effects.on(
|
|
625
|
+
this.core.effects.on(import_types2.ProductsHooks.onProductsSyncCompleted, (payload) => {
|
|
561
626
|
this.recomputeAndNotifyProductQuery({
|
|
562
627
|
changedIds: payload == null ? void 0 : payload.changedIds
|
|
563
628
|
});
|
|
564
629
|
});
|
|
565
|
-
this.core.effects.on(
|
|
630
|
+
this.core.effects.on(import_types.FloorPlanHooks.onFloorPlansChanged, () => {
|
|
631
|
+
void this.recomputeAndNotifyFloorPlanQuery();
|
|
632
|
+
});
|
|
633
|
+
this.core.effects.on(import_types3.OrderHooks.onOrdersChanged, () => {
|
|
566
634
|
this.recomputeAndNotifyOrderQuery();
|
|
567
635
|
this.recomputeAndNotifyBookingQuery();
|
|
568
636
|
});
|
|
@@ -614,6 +682,8 @@ var Server = class {
|
|
|
614
682
|
modules.push("resource");
|
|
615
683
|
if (this.order)
|
|
616
684
|
modules.push("order");
|
|
685
|
+
if (this.floor_plan)
|
|
686
|
+
modules.push("floor_plan");
|
|
617
687
|
return modules;
|
|
618
688
|
}
|
|
619
689
|
/**
|
|
@@ -675,6 +745,10 @@ var Server = class {
|
|
|
675
745
|
clearTasks.push(this.order.clear());
|
|
676
746
|
moduleNames.push("Order");
|
|
677
747
|
}
|
|
748
|
+
if (this.floor_plan) {
|
|
749
|
+
clearTasks.push(this.floor_plan.clear());
|
|
750
|
+
moduleNames.push("FloorPlan");
|
|
751
|
+
}
|
|
678
752
|
if (clearTasks.length === 0) {
|
|
679
753
|
console.warn("[Server] 没有找到已注册的模块,无需清空");
|
|
680
754
|
this.logWarning("没有找到已注册的模块,无需清空 IndexedDB");
|
|
@@ -705,7 +779,18 @@ var Server = class {
|
|
|
705
779
|
*/
|
|
706
780
|
getRouteHandler(method, path) {
|
|
707
781
|
var _a;
|
|
708
|
-
|
|
782
|
+
const exact = (_a = this.router[method]) == null ? void 0 : _a[path];
|
|
783
|
+
if (exact) {
|
|
784
|
+
return exact;
|
|
785
|
+
}
|
|
786
|
+
if (method === "get" && this.prefixRouterGet.length > 0) {
|
|
787
|
+
for (const { prefix, handler } of this.prefixRouterGet) {
|
|
788
|
+
if (path === prefix || path.startsWith(`${prefix}/`)) {
|
|
789
|
+
return handler;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return void 0;
|
|
709
794
|
}
|
|
710
795
|
/**
|
|
711
796
|
* 检查路由是否存在
|
|
@@ -713,9 +798,7 @@ var Server = class {
|
|
|
713
798
|
* @param path 路径
|
|
714
799
|
*/
|
|
715
800
|
hasRoute(method, path) {
|
|
716
|
-
|
|
717
|
-
console.log(method, path, "method, path");
|
|
718
|
-
return !!((_a = this.router[method]) == null ? void 0 : _a[path]);
|
|
801
|
+
return !!this.getRouteHandler(method, path);
|
|
719
802
|
}
|
|
720
803
|
/**
|
|
721
804
|
* 执行路由处理函数
|
|
@@ -737,7 +820,7 @@ var Server = class {
|
|
|
737
820
|
throw new Error(`Route not found: ${method.toUpperCase()} ${path}`);
|
|
738
821
|
}
|
|
739
822
|
try {
|
|
740
|
-
const result = await handler(params);
|
|
823
|
+
const result = await handler({ ...params, path });
|
|
741
824
|
const duration = Date.now() - startTime;
|
|
742
825
|
this.logInfo(`路由请求完成: ${method.toUpperCase()} ${path}`, {
|
|
743
826
|
method: method.toUpperCase(),
|
|
@@ -822,6 +905,13 @@ var Server = class {
|
|
|
822
905
|
handler: this.handleResourceList.bind(this)
|
|
823
906
|
}
|
|
824
907
|
]);
|
|
908
|
+
this.registerPrefixRoutes([
|
|
909
|
+
{
|
|
910
|
+
method: "get",
|
|
911
|
+
prefix: "/shop/schedule/floor-plan",
|
|
912
|
+
handler: this.handleFloorPlanGet.bind(this)
|
|
913
|
+
}
|
|
914
|
+
]);
|
|
825
915
|
}
|
|
826
916
|
/**
|
|
827
917
|
* 根据 subscriberId 移除商品查询订阅者
|
|
@@ -887,6 +977,109 @@ var Server = class {
|
|
|
887
977
|
};
|
|
888
978
|
}
|
|
889
979
|
}
|
|
980
|
+
/**
|
|
981
|
+
* 从 url 或路由 path 解析 pathname(不含 query,去掉末尾 /)
|
|
982
|
+
*/
|
|
983
|
+
parseRequestPath(url, routePath) {
|
|
984
|
+
const raw = routePath || url;
|
|
985
|
+
const noQuery = raw.split("?")[0] || "";
|
|
986
|
+
try {
|
|
987
|
+
const pathname = url.startsWith("http") ? new URL(url).pathname : noQuery;
|
|
988
|
+
return pathname.replace(/\/$/, "") || pathname;
|
|
989
|
+
} catch {
|
|
990
|
+
return noQuery.replace(/\/$/, "");
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* 解析平面图 GET 路径为查询上下文
|
|
995
|
+
*/
|
|
996
|
+
resolveFloorPlanQueryContext(path, base) {
|
|
997
|
+
if (path === base) {
|
|
998
|
+
return { kind: "list" };
|
|
999
|
+
}
|
|
1000
|
+
if (!path.startsWith(`${base}/`)) {
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
const after = path.slice(base.length + 1);
|
|
1004
|
+
if (after === "unsubscribe") {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
if (after.startsWith("code/")) {
|
|
1008
|
+
const code = decodeURIComponent(after.slice("code/".length));
|
|
1009
|
+
if (!code)
|
|
1010
|
+
return null;
|
|
1011
|
+
return { kind: "code", code };
|
|
1012
|
+
}
|
|
1013
|
+
if (/^\d+$/.test(after)) {
|
|
1014
|
+
return { kind: "id", id: after };
|
|
1015
|
+
}
|
|
1016
|
+
return null;
|
|
1017
|
+
}
|
|
1018
|
+
computeFloorPlanQueryResult(context) {
|
|
1019
|
+
if (!this.floor_plan) {
|
|
1020
|
+
return {
|
|
1021
|
+
status: false,
|
|
1022
|
+
code: 500,
|
|
1023
|
+
message: "Floor plan 模块未注册",
|
|
1024
|
+
data: null
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
if (context.kind === "list") {
|
|
1028
|
+
return {
|
|
1029
|
+
status: true,
|
|
1030
|
+
code: 200,
|
|
1031
|
+
message: "",
|
|
1032
|
+
data: this.floor_plan.getSortedList()
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
if (context.kind === "id") {
|
|
1036
|
+
const item2 = this.floor_plan.getById(context.id);
|
|
1037
|
+
if (!item2) {
|
|
1038
|
+
return { status: false, code: 404, message: "Not Found", data: null };
|
|
1039
|
+
}
|
|
1040
|
+
return { status: true, code: 200, message: "", data: item2 };
|
|
1041
|
+
}
|
|
1042
|
+
const item = this.floor_plan.getByCode(context.code);
|
|
1043
|
+
if (!item) {
|
|
1044
|
+
return { status: false, code: 404, message: "Not Found", data: null };
|
|
1045
|
+
}
|
|
1046
|
+
return { status: true, code: 200, message: "", data: item };
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* 平面图数据变更后向所有 GET 订阅者推送最新结果
|
|
1050
|
+
*/
|
|
1051
|
+
async recomputeAndNotifyFloorPlanQuery() {
|
|
1052
|
+
if (this.floorPlanQuerySubscribers.size === 0)
|
|
1053
|
+
return;
|
|
1054
|
+
this.logInfo("recomputeAndNotifyFloorPlanQuery: 开始推送", {
|
|
1055
|
+
subscriberCount: this.floorPlanQuerySubscribers.size
|
|
1056
|
+
});
|
|
1057
|
+
for (const [subscriberId, sub] of this.floorPlanQuerySubscribers.entries()) {
|
|
1058
|
+
try {
|
|
1059
|
+
const result = this.computeFloorPlanQueryResult(sub.context);
|
|
1060
|
+
sub.callback(result);
|
|
1061
|
+
this.logInfo("recomputeAndNotifyFloorPlanQuery: 已推送", { subscriberId });
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1064
|
+
this.logError("recomputeAndNotifyFloorPlanQuery: 推送失败", {
|
|
1065
|
+
subscriberId,
|
|
1066
|
+
error: errorMessage
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* 取消平面图 GET 订阅(也可走 GET .../unsubscribe + subscriberId)
|
|
1073
|
+
*/
|
|
1074
|
+
removeFloorPlanQuerySubscriber(subscriberId) {
|
|
1075
|
+
if (subscriberId) {
|
|
1076
|
+
this.floorPlanQuerySubscribers.delete(subscriberId);
|
|
1077
|
+
this.logInfo("removeFloorPlanQuerySubscriber: 已移除", {
|
|
1078
|
+
subscriberId,
|
|
1079
|
+
remaining: this.floorPlanQuerySubscribers.size
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
890
1083
|
/**
|
|
891
1084
|
* 订单列表本地计算(编排 Order 模块)
|
|
892
1085
|
* filter 逻辑暂为 mock,仅记录参数
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Module, PisellCore } from '../../../types';
|
|
2
|
+
import { BaseModule } from '../../../modules/BaseModule';
|
|
3
|
+
import { type FloorPlanItem } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* 平面图模块:预加载列表、pubsub 同步、供 OS Server 路由读本地 store
|
|
6
|
+
*/
|
|
7
|
+
export declare class FloorPlanModule extends BaseModule implements Module {
|
|
8
|
+
protected defaultName: string;
|
|
9
|
+
protected defaultVersion: string;
|
|
10
|
+
private store;
|
|
11
|
+
private request;
|
|
12
|
+
private logger;
|
|
13
|
+
private floorPlanDataSource;
|
|
14
|
+
private pendingSyncMessages;
|
|
15
|
+
private syncTimer?;
|
|
16
|
+
constructor(name?: string, version?: string);
|
|
17
|
+
initialize(core: PisellCore, options: any): Promise<void>;
|
|
18
|
+
private logInfo;
|
|
19
|
+
private logWarning;
|
|
20
|
+
private logError;
|
|
21
|
+
private idKey;
|
|
22
|
+
private syncMaps;
|
|
23
|
+
/** 与接口约定一致:sort 升序,id 降序 */
|
|
24
|
+
getSortedList(): FloorPlanItem[];
|
|
25
|
+
getById(id: number | string): FloorPlanItem | undefined;
|
|
26
|
+
getByCode(code: string): FloorPlanItem | undefined;
|
|
27
|
+
private replaceStore;
|
|
28
|
+
private mergeItems;
|
|
29
|
+
private removeByIds;
|
|
30
|
+
private loadListFromServer;
|
|
31
|
+
fetchByIdFromServer(id: number | string): Promise<FloorPlanItem | null>;
|
|
32
|
+
fetchByCodeFromServer(code: string): Promise<FloorPlanItem | null>;
|
|
33
|
+
preload(): Promise<void>;
|
|
34
|
+
private initFloorPlanDataSource;
|
|
35
|
+
private setupFloorPlanSync;
|
|
36
|
+
private processFloorPlanSyncMessages;
|
|
37
|
+
clear(): Promise<void>;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/server/modules/floor-plan/index.ts
|
|
20
|
+
var floor_plan_exports = {};
|
|
21
|
+
__export(floor_plan_exports, {
|
|
22
|
+
FloorPlanModule: () => FloorPlanModule
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(floor_plan_exports);
|
|
25
|
+
var import_BaseModule = require("../../../modules/BaseModule");
|
|
26
|
+
var import_types = require("./types");
|
|
27
|
+
var FLOOR_PLAN_SYNC_DEBOUNCE_MS = 3e3;
|
|
28
|
+
var FloorPlanModule = class extends import_BaseModule.BaseModule {
|
|
29
|
+
constructor(name, version) {
|
|
30
|
+
super(name, version);
|
|
31
|
+
this.defaultName = "floor_plan";
|
|
32
|
+
this.defaultVersion = "1.0.0";
|
|
33
|
+
this.pendingSyncMessages = [];
|
|
34
|
+
}
|
|
35
|
+
async initialize(core, options) {
|
|
36
|
+
var _a;
|
|
37
|
+
this.core = core;
|
|
38
|
+
this.store = options.store;
|
|
39
|
+
if (Array.isArray((_a = options == null ? void 0 : options.initialState) == null ? void 0 : _a.list) && options.initialState.list.length > 0) {
|
|
40
|
+
this.store.list = options.initialState.list;
|
|
41
|
+
this.syncMaps();
|
|
42
|
+
this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.store.list);
|
|
43
|
+
} else {
|
|
44
|
+
this.store.list = [];
|
|
45
|
+
this.store.map = /* @__PURE__ */ new Map();
|
|
46
|
+
this.store.codeMap = /* @__PURE__ */ new Map();
|
|
47
|
+
}
|
|
48
|
+
this.request = core.getPlugin("request");
|
|
49
|
+
const appPlugin = core.getPlugin("app");
|
|
50
|
+
if (appPlugin) {
|
|
51
|
+
const app = appPlugin.getApp();
|
|
52
|
+
this.logger = app.logger;
|
|
53
|
+
}
|
|
54
|
+
this.initFloorPlanDataSource();
|
|
55
|
+
this.setupFloorPlanSync();
|
|
56
|
+
}
|
|
57
|
+
logInfo(title, metadata) {
|
|
58
|
+
var _a, _b;
|
|
59
|
+
try {
|
|
60
|
+
(_b = (_a = this.logger) == null ? void 0 : _a.addLog) == null ? void 0 : _b.call(_a, {
|
|
61
|
+
type: "info",
|
|
62
|
+
title: `[FloorPlanModule] ${title}`,
|
|
63
|
+
metadata: metadata || {}
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
logWarning(title, metadata) {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
try {
|
|
71
|
+
(_b = (_a = this.logger) == null ? void 0 : _a.addLog) == null ? void 0 : _b.call(_a, {
|
|
72
|
+
type: "warning",
|
|
73
|
+
title: `[FloorPlanModule] ${title}`,
|
|
74
|
+
metadata: metadata || {}
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
logError(title, metadata) {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
try {
|
|
82
|
+
(_b = (_a = this.logger) == null ? void 0 : _a.addLog) == null ? void 0 : _b.call(_a, {
|
|
83
|
+
type: "error",
|
|
84
|
+
title: `[FloorPlanModule] ${title}`,
|
|
85
|
+
metadata: metadata || {}
|
|
86
|
+
});
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
idKey(id) {
|
|
91
|
+
return String(id);
|
|
92
|
+
}
|
|
93
|
+
syncMaps() {
|
|
94
|
+
this.store.map = /* @__PURE__ */ new Map();
|
|
95
|
+
this.store.codeMap = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const item of this.store.list) {
|
|
97
|
+
if ((item == null ? void 0 : item.id) == null)
|
|
98
|
+
continue;
|
|
99
|
+
const key = this.idKey(item.id);
|
|
100
|
+
this.store.map.set(key, item);
|
|
101
|
+
if (item.code) {
|
|
102
|
+
this.store.codeMap.set(String(item.code), item);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** 与接口约定一致:sort 升序,id 降序 */
|
|
107
|
+
getSortedList() {
|
|
108
|
+
return [...this.store.list].sort((a, b) => {
|
|
109
|
+
const sortDiff = Number(a.sort ?? 0) - Number(b.sort ?? 0);
|
|
110
|
+
if (sortDiff !== 0)
|
|
111
|
+
return sortDiff;
|
|
112
|
+
return Number(b.id) - Number(a.id);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
getById(id) {
|
|
116
|
+
return this.store.map.get(this.idKey(id));
|
|
117
|
+
}
|
|
118
|
+
getByCode(code) {
|
|
119
|
+
return this.store.codeMap.get(String(code));
|
|
120
|
+
}
|
|
121
|
+
replaceStore(list) {
|
|
122
|
+
this.store.list = list.map((i) => ({ ...i }));
|
|
123
|
+
this.syncMaps();
|
|
124
|
+
}
|
|
125
|
+
mergeItems(items) {
|
|
126
|
+
if (items.length === 0)
|
|
127
|
+
return;
|
|
128
|
+
const byId = new Map(this.store.map);
|
|
129
|
+
for (const item of items) {
|
|
130
|
+
if ((item == null ? void 0 : item.id) == null)
|
|
131
|
+
continue;
|
|
132
|
+
byId.set(this.idKey(item.id), { ...item });
|
|
133
|
+
}
|
|
134
|
+
this.store.list = Array.from(byId.values());
|
|
135
|
+
this.syncMaps();
|
|
136
|
+
}
|
|
137
|
+
removeByIds(ids) {
|
|
138
|
+
if (ids.length === 0)
|
|
139
|
+
return;
|
|
140
|
+
const set = new Set(ids.map((id) => this.idKey(id)));
|
|
141
|
+
this.store.list = this.store.list.filter((p) => !set.has(this.idKey(p.id)));
|
|
142
|
+
this.syncMaps();
|
|
143
|
+
}
|
|
144
|
+
async loadListFromServer() {
|
|
145
|
+
try {
|
|
146
|
+
const res = await this.request.get(
|
|
147
|
+
`/schedule/floor-plan`,
|
|
148
|
+
{},
|
|
149
|
+
{ isShopApi: true }
|
|
150
|
+
);
|
|
151
|
+
const body = res;
|
|
152
|
+
const raw = (body == null ? void 0 : body.data) !== void 0 ? body.data : body;
|
|
153
|
+
const list = Array.isArray(raw) ? raw : [];
|
|
154
|
+
return list;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
this.logWarning("loadListFromServer 失败", {
|
|
157
|
+
error: e instanceof Error ? e.message : String(e)
|
|
158
|
+
});
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async fetchByIdFromServer(id) {
|
|
163
|
+
try {
|
|
164
|
+
const res = await this.request.get(
|
|
165
|
+
`/schedule/floor-plan/${id}`,
|
|
166
|
+
{},
|
|
167
|
+
{ isShopApi: true }
|
|
168
|
+
);
|
|
169
|
+
const body = res;
|
|
170
|
+
const data = (body == null ? void 0 : body.data) !== void 0 ? body.data : body;
|
|
171
|
+
return data && typeof data === "object" ? data : null;
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async fetchByCodeFromServer(code) {
|
|
177
|
+
try {
|
|
178
|
+
const enc = encodeURIComponent(code);
|
|
179
|
+
const res = await this.request.get(
|
|
180
|
+
`/schedule/floor-plan/code/${enc}`,
|
|
181
|
+
{},
|
|
182
|
+
{ isShopApi: true }
|
|
183
|
+
);
|
|
184
|
+
const body = res;
|
|
185
|
+
const data = (body == null ? void 0 : body.data) !== void 0 ? body.data : body;
|
|
186
|
+
return data && typeof data === "object" ? data : null;
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async preload() {
|
|
192
|
+
this.logInfo("开始预加载平面图列表");
|
|
193
|
+
const list = await this.loadListFromServer();
|
|
194
|
+
if (list.length > 0) {
|
|
195
|
+
this.replaceStore(list);
|
|
196
|
+
this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
|
|
197
|
+
this.logInfo("预加载完成", { count: list.length });
|
|
198
|
+
} else {
|
|
199
|
+
this.logWarning("预加载完成但列表为空");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
initFloorPlanDataSource() {
|
|
203
|
+
var _a, _b;
|
|
204
|
+
const Ctor = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.FloorPlanDataSource;
|
|
205
|
+
if (!Ctor) {
|
|
206
|
+
this.logWarning("FloorPlanDataSource 不可用,跳过 pubsub 同步");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.floorPlanDataSource = new Ctor();
|
|
210
|
+
this.logInfo("FloorPlanDataSource 实例已创建");
|
|
211
|
+
}
|
|
212
|
+
setupFloorPlanSync() {
|
|
213
|
+
if (!this.floorPlanDataSource)
|
|
214
|
+
return;
|
|
215
|
+
const flush = () => {
|
|
216
|
+
if (this.syncTimer)
|
|
217
|
+
clearTimeout(this.syncTimer);
|
|
218
|
+
this.syncTimer = setTimeout(() => {
|
|
219
|
+
this.processFloorPlanSyncMessages();
|
|
220
|
+
}, FLOOR_PLAN_SYNC_DEBOUNCE_MS);
|
|
221
|
+
};
|
|
222
|
+
this.floorPlanDataSource.run({
|
|
223
|
+
pubsub: {
|
|
224
|
+
callback: (res) => {
|
|
225
|
+
const data = (res == null ? void 0 : res.data) || res;
|
|
226
|
+
if (!data || data.module !== "floor_plan")
|
|
227
|
+
return;
|
|
228
|
+
this.pendingSyncMessages.push({ ...data });
|
|
229
|
+
flush();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}).catch((err) => {
|
|
233
|
+
this.logError("setupFloorPlanSync: DataSource run 出错", {
|
|
234
|
+
error: err instanceof Error ? err.message : String(err)
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
this.logInfo("floor_plan pubsub 订阅已建立");
|
|
238
|
+
}
|
|
239
|
+
async processFloorPlanSyncMessages() {
|
|
240
|
+
var _a, _b;
|
|
241
|
+
const messages = [...this.pendingSyncMessages];
|
|
242
|
+
this.pendingSyncMessages = [];
|
|
243
|
+
if (messages.length === 0)
|
|
244
|
+
return;
|
|
245
|
+
this.logInfo("processFloorPlanSyncMessages 开始", { count: messages.length });
|
|
246
|
+
let changed = false;
|
|
247
|
+
const deleteIds = [];
|
|
248
|
+
const bodyItems = [];
|
|
249
|
+
let needFullList = false;
|
|
250
|
+
const partialIds = /* @__PURE__ */ new Set();
|
|
251
|
+
for (const msg of messages) {
|
|
252
|
+
if (msg.operation === "delete" || msg.action === "delete") {
|
|
253
|
+
if ((_a = msg.ids) == null ? void 0 : _a.length) {
|
|
254
|
+
deleteIds.push(...msg.ids);
|
|
255
|
+
} else if (msg.id != null) {
|
|
256
|
+
deleteIds.push(msg.id);
|
|
257
|
+
}
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const body = msg.body;
|
|
261
|
+
const hasBody = body && typeof body === "object" && Object.keys(body).length > 0 && body.id != null;
|
|
262
|
+
if (hasBody) {
|
|
263
|
+
bodyItems.push(body);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (msg.ids && msg.ids.length > 1) {
|
|
267
|
+
needFullList = true;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (((_b = msg.ids) == null ? void 0 : _b.length) === 1) {
|
|
271
|
+
partialIds.add(msg.ids[0]);
|
|
272
|
+
} else if (msg.id != null) {
|
|
273
|
+
partialIds.add(msg.id);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const uniqueDeletes = [...new Set(deleteIds)];
|
|
277
|
+
if (uniqueDeletes.length > 0) {
|
|
278
|
+
this.removeByIds(uniqueDeletes);
|
|
279
|
+
changed = true;
|
|
280
|
+
}
|
|
281
|
+
if (bodyItems.length > 0) {
|
|
282
|
+
this.mergeItems(bodyItems);
|
|
283
|
+
changed = true;
|
|
284
|
+
}
|
|
285
|
+
if (needFullList) {
|
|
286
|
+
const list = await this.loadListFromServer();
|
|
287
|
+
this.replaceStore(list);
|
|
288
|
+
changed = true;
|
|
289
|
+
} else if (partialIds.size > 0) {
|
|
290
|
+
const fetched = [];
|
|
291
|
+
for (const id of partialIds) {
|
|
292
|
+
const one = await this.fetchByIdFromServer(id);
|
|
293
|
+
if (one)
|
|
294
|
+
fetched.push(one);
|
|
295
|
+
}
|
|
296
|
+
if (fetched.length > 0) {
|
|
297
|
+
this.mergeItems(fetched);
|
|
298
|
+
changed = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (changed) {
|
|
302
|
+
this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
|
|
303
|
+
this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlanSyncCompleted, null);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async clear() {
|
|
307
|
+
this.store.list = [];
|
|
308
|
+
this.store.map.clear();
|
|
309
|
+
this.store.codeMap.clear();
|
|
310
|
+
}
|
|
311
|
+
destroy() {
|
|
312
|
+
var _a;
|
|
313
|
+
if (this.syncTimer) {
|
|
314
|
+
clearTimeout(this.syncTimer);
|
|
315
|
+
this.syncTimer = void 0;
|
|
316
|
+
}
|
|
317
|
+
this.pendingSyncMessages = [];
|
|
318
|
+
if ((_a = this.floorPlanDataSource) == null ? void 0 : _a.destroy) {
|
|
319
|
+
this.floorPlanDataSource.destroy();
|
|
320
|
+
}
|
|
321
|
+
super.destroy();
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
325
|
+
0 && (module.exports = {
|
|
326
|
+
FloorPlanModule
|
|
327
|
+
});
|