@pisell/pisellos 2.2.122 → 2.2.123

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.
@@ -29,6 +29,10 @@ declare class Server {
29
29
  private productQuerySubscribers;
30
30
  private orderQuerySubscribers;
31
31
  private bookingQuerySubscribers;
32
+ private bookingRemoteQuerySubscribers;
33
+ private bookingRemoteCache;
34
+ private readonly BOOKING_REMOTE_CACHE_TTL_MS;
35
+ private readonly BOOKING_REMOTE_CACHE_MAX_ENTRIES;
32
36
  private floorPlanQuerySubscribers;
33
37
  private moduleRegistry;
34
38
  constructor(core: PisellCore);
@@ -176,11 +180,61 @@ declare class Server {
176
180
  */
177
181
  private handleUnsubscribeOrderQuery;
178
182
  /**
179
- * 判断预约查询的 sales_time_between 起始日期是否为今天
183
+ * 从查询值中提取 YYYY-MM-DD 日期串。
184
+ *
185
+ * @example
186
+ * this.extractQueryDate('2026-04-15 09:58:00') // => '2026-04-15'
180
187
  */
181
- private isBookingQueryForToday;
188
+ private extractQueryDate;
182
189
  /**
183
- * 非今天的预约查询:通过真实 API 获取数据,再做 flattenOrdersToBookings 拆分
190
+ * 判断时间范围是否「开始和结束都不是今天」。
191
+ *
192
+ * @example
193
+ * this.isRangeBothNotToday('2026-04-13', '2026-04-14') // => true
194
+ */
195
+ private isRangeBothNotToday;
196
+ /**
197
+ * 订单查询是否走本地:
198
+ * - 当 start_time 与 end_time 都不是今天时,走远端;
199
+ * - 其余情况走本地。
200
+ */
201
+ private shouldUseLocalOrderQuery;
202
+ /**
203
+ * 预约查询是否走本地:
204
+ * - 当 sales_time_between 的开始和结束都不是今天时,走远端;
205
+ * - 其余情况走本地。
206
+ */
207
+ private shouldUseLocalBookingQuery;
208
+ /**
209
+ * 日历 SSE 精简 with:只拉日历渲染必需的关联字段
210
+ */
211
+ private resolveBookingSalesWith;
212
+ /**
213
+ * 日历 SSE 精简 select:渲染 + filterBookingsFromOrders 所需的主表字段
214
+ * business_code / payment_status 不直接用于渲染,但 matchOrder 过滤依赖
215
+ */
216
+ private resolveBookingSalesSelect;
217
+ private normalizeBookingRemoteQueryPayload;
218
+ /**
219
+ * 远端预约查询内存缓存开关:默认 false,仅显式 true 才启用。
220
+ */
221
+ private isBookingRemoteMemoryCacheEnabled;
222
+ private stableSerialize;
223
+ private buildBookingRemoteCacheKey;
224
+ private parseBookingSalesRangeMeta;
225
+ private readBookingRemoteCache;
226
+ private writeBookingRemoteCache;
227
+ private toOrderIdKey;
228
+ private markBookingRemoteCacheAllStale;
229
+ private buildBookingResponse;
230
+ private notifyBookingRemoteSubscribersByCacheKey;
231
+ private syncBookingRemoteCacheFromOrderChanges;
232
+ /**
233
+ * 非本地窗口的订单查询:走真实 API。
234
+ */
235
+ private fetchOrderListFromAPI;
236
+ /**
237
+ * 非今天的预约查询:通过 SSE 流式拉取订单,再做本地 booking 筛选展开
184
238
  */
185
239
  private fetchBookingListFromAPI;
186
240
  /**
@@ -65,6 +65,10 @@ var Server = class {
65
65
  // ---- 订单 / 预约列表查询订阅者 ----
66
66
  this.orderQuerySubscribers = /* @__PURE__ */ new Map();
67
67
  this.bookingQuerySubscribers = /* @__PURE__ */ new Map();
68
+ this.bookingRemoteQuerySubscribers = /* @__PURE__ */ new Map();
69
+ this.bookingRemoteCache = /* @__PURE__ */ new Map();
70
+ this.BOOKING_REMOTE_CACHE_TTL_MS = 5 * 60 * 1e3;
71
+ this.BOOKING_REMOTE_CACHE_MAX_ENTRIES = 80;
68
72
  this.floorPlanQuerySubscribers = /* @__PURE__ */ new Map();
69
73
  // 模块注册表 - 定义所有可用的模块配置
70
74
  this.moduleRegistry = {
@@ -180,18 +184,32 @@ var Server = class {
180
184
  console.log("[Server] handleOrderList:", url, method, data, config);
181
185
  const queryPayload = data && typeof data === "object" ? { ...data } : {};
182
186
  const { callback, subscriberId } = config || {};
183
- this.logInfo("handleOrderList: 开始处理订单列表请求", { data: queryPayload });
184
- if (subscriberId && typeof callback === "function") {
185
- this.orderQuerySubscribers.set(subscriberId, {
186
- callback,
187
- context: queryPayload
188
- });
189
- this.logInfo("handleOrderList: 已注册订阅者", {
187
+ const useLocal = this.shouldUseLocalOrderQuery(queryPayload);
188
+ this.logInfo("handleOrderList: 开始处理订单列表请求", {
189
+ data: queryPayload,
190
+ useLocal
191
+ });
192
+ if (useLocal) {
193
+ if (subscriberId && typeof callback === "function") {
194
+ this.orderQuerySubscribers.set(subscriberId, {
195
+ callback,
196
+ context: queryPayload
197
+ });
198
+ this.logInfo("handleOrderList: 已注册订阅者", {
199
+ subscriberId,
200
+ totalSubscribers: this.orderQuerySubscribers.size
201
+ });
202
+ }
203
+ return this.computeOrderQueryResult(queryPayload);
204
+ }
205
+ if (subscriberId) {
206
+ this.orderQuerySubscribers.delete(subscriberId);
207
+ this.logInfo("handleOrderList: 已清理订阅者(走远端)", {
190
208
  subscriberId,
191
- totalSubscribers: this.orderQuerySubscribers.size
209
+ remaining: this.orderQuerySubscribers.size
192
210
  });
193
211
  }
194
- return this.computeOrderQueryResult(queryPayload);
212
+ return this.fetchOrderListFromAPI(queryPayload);
195
213
  };
196
214
  /**
197
215
  * 取消订单列表查询订阅(HTTP 路由入口)
@@ -215,12 +233,15 @@ var Server = class {
215
233
  console.log("[Server] handleBookingList:", url, method, data, config);
216
234
  const queryPayload = data && typeof data === "object" ? { ...data } : {};
217
235
  const { callback, subscriberId } = config || {};
218
- const isToday = this.isBookingQueryForToday(queryPayload);
236
+ const useLocal = this.shouldUseLocalBookingQuery(queryPayload);
219
237
  this.logInfo("handleBookingList: 开始处理预约列表请求", {
220
238
  data: queryPayload,
221
- isToday
239
+ useLocal
222
240
  });
223
- if (isToday) {
241
+ if (useLocal) {
242
+ if (subscriberId) {
243
+ this.bookingRemoteQuerySubscribers.delete(subscriberId);
244
+ }
224
245
  if (subscriberId && typeof callback === "function") {
225
246
  this.bookingQuerySubscribers.set(subscriberId, {
226
247
  callback,
@@ -235,6 +256,26 @@ var Server = class {
235
256
  } else {
236
257
  if (subscriberId) {
237
258
  this.bookingQuerySubscribers.delete(subscriberId);
259
+ }
260
+ if (subscriberId && typeof callback === "function") {
261
+ const withFields = this.resolveBookingSalesWith(queryPayload);
262
+ const requestPayload = this.normalizeBookingRemoteQueryPayload(
263
+ queryPayload,
264
+ withFields
265
+ );
266
+ const cacheKey = this.buildBookingRemoteCacheKey(requestPayload);
267
+ this.bookingRemoteQuerySubscribers.set(subscriberId, {
268
+ callback,
269
+ context: queryPayload,
270
+ cacheKey
271
+ });
272
+ this.logInfo("handleBookingList: 已注册订阅者(非今天)", {
273
+ subscriberId,
274
+ cacheKey,
275
+ totalSubscribers: this.bookingRemoteQuerySubscribers.size
276
+ });
277
+ }
278
+ if (subscriberId) {
238
279
  this.logInfo("handleBookingList: 已清理订阅者(非今天)", {
239
280
  subscriberId,
240
281
  remaining: this.bookingQuerySubscribers.size
@@ -301,9 +342,11 @@ var Server = class {
301
342
  const { subscriberId } = data || {};
302
343
  if (subscriberId) {
303
344
  this.bookingQuerySubscribers.delete(subscriberId);
345
+ this.bookingRemoteQuerySubscribers.delete(subscriberId);
304
346
  this.logInfo("handleUnsubscribeBookingQuery: 已移除订阅者", {
305
347
  subscriberId,
306
- remaining: this.bookingQuerySubscribers.size
348
+ remainingLocal: this.bookingQuerySubscribers.size,
349
+ remainingRemote: this.bookingRemoteQuerySubscribers.size
307
350
  });
308
351
  }
309
352
  return { code: 200, message: "ok", status: true };
@@ -634,6 +677,7 @@ var Server = class {
634
677
  this.core.effects.on(import_types3.OrderHooks.onOrdersChanged, () => {
635
678
  this.recomputeAndNotifyOrderQuery();
636
679
  this.recomputeAndNotifyBookingQuery();
680
+ this.syncBookingRemoteCacheFromOrderChanges();
637
681
  });
638
682
  const duration = Date.now() - startTime;
639
683
  this.logInfo("Server 初始化完成", {
@@ -927,23 +971,307 @@ var Server = class {
927
971
  }
928
972
  }
929
973
  /**
930
- * 判断预约查询的 sales_time_between 起始日期是否为今天
974
+ * 从查询值中提取 YYYY-MM-DD 日期串。
975
+ *
976
+ * @example
977
+ * this.extractQueryDate('2026-04-15 09:58:00') // => '2026-04-15'
978
+ */
979
+ extractQueryDate(raw) {
980
+ if (raw == null)
981
+ return null;
982
+ const date = (0, import_dayjs.default)(raw);
983
+ if (date.isValid()) {
984
+ return date.format("YYYY-MM-DD");
985
+ }
986
+ const text = String(raw).trim();
987
+ if (!text)
988
+ return null;
989
+ return text.split("T")[0].split(" ")[0] || null;
990
+ }
991
+ /**
992
+ * 判断时间范围是否「开始和结束都不是今天」。
993
+ *
994
+ * @example
995
+ * this.isRangeBothNotToday('2026-04-13', '2026-04-14') // => true
996
+ */
997
+ isRangeBothNotToday(startRaw, endRaw) {
998
+ const startDate = this.extractQueryDate(startRaw);
999
+ const endDate = this.extractQueryDate(endRaw);
1000
+ if (!startDate || !endDate)
1001
+ return false;
1002
+ const today = (0, import_dayjs.default)().format("YYYY-MM-DD");
1003
+ return startDate !== today && endDate !== today;
1004
+ }
1005
+ /**
1006
+ * 订单查询是否走本地:
1007
+ * - 当 start_time 与 end_time 都不是今天时,走远端;
1008
+ * - 其余情况走本地。
1009
+ */
1010
+ shouldUseLocalOrderQuery(data) {
1011
+ return !this.isRangeBothNotToday(data == null ? void 0 : data.start_time, data == null ? void 0 : data.end_time);
1012
+ }
1013
+ /**
1014
+ * 预约查询是否走本地:
1015
+ * - 当 sales_time_between 的开始和结束都不是今天时,走远端;
1016
+ * - 其余情况走本地。
931
1017
  */
932
- isBookingQueryForToday(data) {
1018
+ shouldUseLocalBookingQuery(data) {
933
1019
  const range = data == null ? void 0 : data.sales_time_between;
934
- if (!Array.isArray(range) || range.length < 1)
1020
+ if (!Array.isArray(range) || range.length < 2)
935
1021
  return true;
936
- const startDateStr = String(range[0]).split("T")[0].split(" ")[0];
937
- const todayStr = (0, import_dayjs.default)().format("YYYY-MM-DD");
938
- return startDateStr === todayStr;
1022
+ return !this.isRangeBothNotToday(range[0], range[1]);
939
1023
  }
940
1024
  /**
941
- * 非今天的预约查询:通过真实 API 获取数据,再做 flattenOrdersToBookings 拆分
1025
+ * 日历 SSE 精简 with:只拉日历渲染必需的关联字段
942
1026
  */
943
- async fetchBookingListFromAPI(data) {
1027
+ resolveBookingSalesWith(_queryPayload) {
1028
+ return [
1029
+ "bookings:booking_id,start_date,start_time,end_date,end_time,holder,metadata,parent_id,item_type",
1030
+ "customer:id,display_name,phone",
1031
+ "products"
1032
+ ];
1033
+ }
1034
+ /**
1035
+ * 日历 SSE 精简 select:渲染 + filterBookingsFromOrders 所需的主表字段
1036
+ * business_code / payment_status 不直接用于渲染,但 matchOrder 过滤依赖
1037
+ */
1038
+ resolveBookingSalesSelect() {
1039
+ return "status,payment_status,business_code,phone,customer_name,schedule_date,created_at,metadata";
1040
+ }
1041
+ normalizeBookingRemoteQueryPayload(data, withFields) {
1042
+ return {
1043
+ ...data,
1044
+ form_record_ids: void 0,
1045
+ enable_remote_memory_cache: void 0,
1046
+ with: withFields,
1047
+ select: this.resolveBookingSalesSelect(),
1048
+ chunk_size: 50
1049
+ };
1050
+ }
1051
+ /**
1052
+ * 远端预约查询内存缓存开关:默认 false,仅显式 true 才启用。
1053
+ */
1054
+ isBookingRemoteMemoryCacheEnabled(queryPayload) {
1055
+ return (queryPayload == null ? void 0 : queryPayload.enable_remote_memory_cache) === true;
1056
+ }
1057
+ stableSerialize(value) {
1058
+ if (value === null || value === void 0)
1059
+ return "null";
1060
+ if (Array.isArray(value)) {
1061
+ return `[${value.map((item) => this.stableSerialize(item)).join(",")}]`;
1062
+ }
1063
+ if (typeof value === "object") {
1064
+ const obj = value;
1065
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
1066
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${this.stableSerialize(obj[k])}`).join(",")}}`;
1067
+ }
1068
+ return JSON.stringify(value);
1069
+ }
1070
+ buildBookingRemoteCacheKey(payload) {
1071
+ return this.stableSerialize(payload);
1072
+ }
1073
+ parseBookingSalesRangeMeta(payload) {
1074
+ const range = payload == null ? void 0 : payload.sales_time_between;
1075
+ if (!Array.isArray(range) || range.length < 2)
1076
+ return null;
1077
+ const startAtMs = (0, import_dayjs.default)(range[0]).valueOf();
1078
+ const endAtMs = (0, import_dayjs.default)(range[1]).valueOf();
1079
+ if (!Number.isFinite(startAtMs) || !Number.isFinite(endAtMs))
1080
+ return null;
1081
+ if (endAtMs <= startAtMs)
1082
+ return null;
1083
+ return { startAtMs, endAtMs };
1084
+ }
1085
+ readBookingRemoteCache(cacheKey) {
1086
+ const entry = this.bookingRemoteCache.get(cacheKey);
1087
+ if (!entry)
1088
+ return null;
1089
+ if (entry.stale) {
1090
+ this.bookingRemoteCache.delete(cacheKey);
1091
+ return null;
1092
+ }
1093
+ const now = Date.now();
1094
+ if (now - entry.updatedAt > this.BOOKING_REMOTE_CACHE_TTL_MS) {
1095
+ this.bookingRemoteCache.delete(cacheKey);
1096
+ return null;
1097
+ }
1098
+ return entry;
1099
+ }
1100
+ writeBookingRemoteCache(entry) {
1101
+ this.bookingRemoteCache.set(entry.key, entry);
1102
+ if (this.bookingRemoteCache.size <= this.BOOKING_REMOTE_CACHE_MAX_ENTRIES) {
1103
+ return;
1104
+ }
1105
+ let oldestKey = null;
1106
+ let oldestTime = Number.POSITIVE_INFINITY;
1107
+ for (const [key, cache] of this.bookingRemoteCache.entries()) {
1108
+ if (cache.updatedAt < oldestTime) {
1109
+ oldestTime = cache.updatedAt;
1110
+ oldestKey = key;
1111
+ }
1112
+ }
1113
+ if (oldestKey)
1114
+ this.bookingRemoteCache.delete(oldestKey);
1115
+ }
1116
+ toOrderIdKey(order) {
1117
+ if (!order || typeof order !== "object")
1118
+ return null;
1119
+ const raw = order.order_id;
1120
+ if (raw === void 0 || raw === null || raw === "")
1121
+ return null;
1122
+ return String(raw);
1123
+ }
1124
+ markBookingRemoteCacheAllStale(reason) {
1125
+ if (this.bookingRemoteCache.size === 0)
1126
+ return;
1127
+ for (const entry of this.bookingRemoteCache.values()) {
1128
+ entry.stale = true;
1129
+ }
1130
+ this.logWarning("bookingRemoteCache: 全量标记失效", {
1131
+ reason,
1132
+ cacheSize: this.bookingRemoteCache.size
1133
+ });
1134
+ }
1135
+ buildBookingResponse(bookingResult, extra) {
1136
+ return {
1137
+ code: 200,
1138
+ data: {
1139
+ ...extra ?? {},
1140
+ list: bookingResult.list || [],
1141
+ count: bookingResult.count || 0,
1142
+ size: bookingResult.size || 0,
1143
+ skip: bookingResult.skip || 0
1144
+ },
1145
+ message: "",
1146
+ status: true
1147
+ };
1148
+ }
1149
+ notifyBookingRemoteSubscribersByCacheKey(cacheKey, entry, reason) {
1150
+ if (this.bookingRemoteQuerySubscribers.size === 0)
1151
+ return;
1152
+ for (const [subscriberId, subscriber] of this.bookingRemoteQuerySubscribers.entries()) {
1153
+ if (subscriber.cacheKey !== cacheKey)
1154
+ continue;
1155
+ try {
1156
+ const callbackStartAt = Date.now();
1157
+ subscriber.callback(
1158
+ this.buildBookingResponse(entry.bookingResult, {
1159
+ cache_hit: true,
1160
+ from_cache_sync: true,
1161
+ sync_reason: reason
1162
+ })
1163
+ );
1164
+ const callbackEndAt = Date.now();
1165
+ this.logInfo("notifyBookingRemoteSubscribersByCacheKey: 已推送", {
1166
+ subscriberId,
1167
+ cacheKey,
1168
+ reason,
1169
+ callbackDurationMs: callbackEndAt - callbackStartAt
1170
+ });
1171
+ } catch (error) {
1172
+ const errMsg = error instanceof Error ? error.message : String(error);
1173
+ this.logError("notifyBookingRemoteSubscribersByCacheKey: 推送失败", {
1174
+ subscriberId,
1175
+ cacheKey,
1176
+ reason,
1177
+ error: errMsg
1178
+ });
1179
+ }
1180
+ }
1181
+ }
1182
+ syncBookingRemoteCacheFromOrderChanges() {
944
1183
  var _a, _b;
1184
+ if (this.bookingRemoteCache.size === 0)
1185
+ return;
1186
+ const syncStartAt = Date.now();
1187
+ const latestOrders = ((_b = (_a = this.order) == null ? void 0 : _a.getOrders) == null ? void 0 : _b.call(_a)) ?? [];
1188
+ if (!Array.isArray(latestOrders) || latestOrders.length === 0) {
1189
+ this.markBookingRemoteCacheAllStale("orderStoreEmptyAfterChange");
1190
+ return;
1191
+ }
1192
+ let syncedEntryCount = 0;
1193
+ for (const [cacheKey, entry] of this.bookingRemoteCache.entries()) {
1194
+ if (entry.stale) {
1195
+ this.bookingRemoteCache.delete(cacheKey);
1196
+ continue;
1197
+ }
1198
+ try {
1199
+ const queryPayload = entry.queryPayload;
1200
+ const orderMap = /* @__PURE__ */ new Map();
1201
+ for (const order of entry.rawOrders) {
1202
+ const key = this.toOrderIdKey(order);
1203
+ if (key)
1204
+ orderMap.set(key, order);
1205
+ }
1206
+ let changed = false;
1207
+ for (const latest of latestOrders) {
1208
+ const key = this.toOrderIdKey(latest);
1209
+ if (!key)
1210
+ continue;
1211
+ if (orderMap.has(key)) {
1212
+ orderMap.set(key, latest);
1213
+ changed = true;
1214
+ continue;
1215
+ }
1216
+ orderMap.set(key, latest);
1217
+ changed = true;
1218
+ }
1219
+ if (!changed)
1220
+ continue;
1221
+ const mergedOrders = [...orderMap.values()];
1222
+ const filterStartAt = Date.now();
1223
+ const bookingResult = (0, import_filterBookings.sortBookings)(
1224
+ (0, import_filterBookings.filterBookingsFromOrders)(mergedOrders, queryPayload),
1225
+ queryPayload
1226
+ );
1227
+ const filterEndAt = Date.now();
1228
+ const nextEntry = {
1229
+ ...entry,
1230
+ rawOrders: mergedOrders,
1231
+ bookingResult,
1232
+ updatedAt: Date.now(),
1233
+ stale: false
1234
+ };
1235
+ this.writeBookingRemoteCache(nextEntry);
1236
+ this.notifyBookingRemoteSubscribersByCacheKey(
1237
+ cacheKey,
1238
+ nextEntry,
1239
+ "orders_changed"
1240
+ );
1241
+ syncedEntryCount += 1;
1242
+ this.logInfo("bookingRemoteCache: 单条缓存同步完成", {
1243
+ cacheKey,
1244
+ mergedOrderCount: mergedOrders.length,
1245
+ filteredBookingCount: bookingResult.count,
1246
+ filterDurationMs: filterEndAt - filterStartAt
1247
+ });
1248
+ } catch (error) {
1249
+ const errMsg = error instanceof Error ? error.message : String(error);
1250
+ this.logWarning("bookingRemoteCache: 同步失败并标记失效", {
1251
+ cacheKey,
1252
+ error: errMsg
1253
+ });
1254
+ const current = this.bookingRemoteCache.get(cacheKey);
1255
+ if (current)
1256
+ current.stale = true;
1257
+ }
1258
+ }
1259
+ const syncEndAt = Date.now();
1260
+ if (syncedEntryCount > 0) {
1261
+ this.logInfo("bookingRemoteCache: 已同步 Ably 变更", {
1262
+ syncedEntryCount,
1263
+ cacheSize: this.bookingRemoteCache.size,
1264
+ totalSyncDurationMs: syncEndAt - syncStartAt
1265
+ });
1266
+ }
1267
+ }
1268
+ /**
1269
+ * 非本地窗口的订单查询:走真实 API。
1270
+ */
1271
+ async fetchOrderListFromAPI(data) {
1272
+ var _a;
945
1273
  if (!((_a = this.app) == null ? void 0 : _a.request)) {
946
- this.logError("fetchBookingListFromAPI: app.request 不可用");
1274
+ this.logError("fetchOrderListFromAPI: app.request 不可用");
947
1275
  return {
948
1276
  code: 500,
949
1277
  message: "app.request 不可用",
@@ -952,24 +1280,118 @@ var Server = class {
952
1280
  };
953
1281
  }
954
1282
  try {
955
- const response = await this.app.request.get("/shop/order/sales", { ...data, form_record_ids: void 0, with: ["all"] }, {
1283
+ const response = await this.app.request.post("/shop/order/v2/list", data, {
956
1284
  isShopApi: true
957
1285
  });
958
- const rawList = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b.list) ?? (response == null ? void 0 : response.list) ?? [];
959
- const list = (0, import_filterBookings.filterBookingsFromOrders)(rawList, data);
960
- this.logInfo("fetchBookingListFromAPI: API 返回并拆分完成", {
961
- rawCount: rawList.length,
962
- flattenedCount: list.count
963
- });
1286
+ const payload = (response == null ? void 0 : response.data) ?? response;
1287
+ const list = Array.isArray(payload == null ? void 0 : payload.list) ? payload.list : [];
1288
+ const count = typeof (payload == null ? void 0 : payload.count) === "number" ? payload.count : list.length;
964
1289
  return {
965
1290
  code: 200,
966
- data: { ...response.data, list: (list == null ? void 0 : list.list) || [] },
1291
+ data: { ...payload, list, count },
967
1292
  message: "",
968
1293
  status: true
969
1294
  };
970
1295
  } catch (error) {
971
1296
  const errorMessage = error instanceof Error ? error.message : String(error);
972
- this.logError("fetchBookingListFromAPI: 请求失败", { error: errorMessage });
1297
+ this.logError("fetchOrderListFromAPI: 请求失败", { error: errorMessage });
1298
+ return {
1299
+ code: 500,
1300
+ message: errorMessage,
1301
+ data: { list: [], count: 0 },
1302
+ status: false
1303
+ };
1304
+ }
1305
+ }
1306
+ /**
1307
+ * 非今天的预约查询:通过 SSE 流式拉取订单,再做本地 booking 筛选展开
1308
+ */
1309
+ async fetchBookingListFromAPI(data) {
1310
+ var _a;
1311
+ if (!this.order) {
1312
+ this.logError("fetchBookingListFromAPI: Order 模块不可用");
1313
+ return {
1314
+ code: 500,
1315
+ message: "Order 模块不可用",
1316
+ data: { list: [], count: 0 },
1317
+ status: false
1318
+ };
1319
+ }
1320
+ try {
1321
+ const memoryCacheEnabled = this.isBookingRemoteMemoryCacheEnabled(data);
1322
+ const withFields = this.resolveBookingSalesWith(data);
1323
+ const requestPayload = this.normalizeBookingRemoteQueryPayload(
1324
+ data,
1325
+ withFields
1326
+ );
1327
+ const cacheKey = this.buildBookingRemoteCacheKey(requestPayload);
1328
+ if (memoryCacheEnabled) {
1329
+ const cached = this.readBookingRemoteCache(cacheKey);
1330
+ if (cached) {
1331
+ this.logInfo("fetchBookingListFromAPI: 命中内存缓存", {
1332
+ cacheKey,
1333
+ listCount: cached.bookingResult.count,
1334
+ withFields: cached.withFields
1335
+ });
1336
+ return this.buildBookingResponse(cached.bookingResult, {
1337
+ cache_hit: true
1338
+ });
1339
+ }
1340
+ }
1341
+ const rawList = await this.order.fetchOrdersBySSE(requestPayload);
1342
+ const bookingResult = (0, import_filterBookings.sortBookings)(
1343
+ (0, import_filterBookings.filterBookingsFromOrders)(rawList, data),
1344
+ data
1345
+ );
1346
+ if (typeof globalThis !== "undefined") {
1347
+ globalThis.__SSE_BOOKING_DEBUG__ = {
1348
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1349
+ sseRequestPayload: requestPayload,
1350
+ calendarFilters: data,
1351
+ rawOrderCount: rawList.length,
1352
+ rawOrderSample: rawList.slice(0, 2).map((o) => {
1353
+ var _a2, _b, _c;
1354
+ return {
1355
+ order_id: o == null ? void 0 : o.order_id,
1356
+ business_code: o == null ? void 0 : o.business_code,
1357
+ status: o == null ? void 0 : o.status,
1358
+ payment_status: o == null ? void 0 : o.payment_status,
1359
+ bookingsCount: (_a2 = o == null ? void 0 : o.bookings) == null ? void 0 : _a2.length,
1360
+ firstBooking: ((_b = o == null ? void 0 : o.bookings) == null ? void 0 : _b[0]) ? {
1361
+ start_date: o.bookings[0].start_date,
1362
+ start_time: o.bookings[0].start_time,
1363
+ parent_id: o.bookings[0].parent_id,
1364
+ item_type: o.bookings[0].item_type,
1365
+ resourcesCount: (_c = o.bookings[0].resources) == null ? void 0 : _c.length
1366
+ } : null
1367
+ };
1368
+ }),
1369
+ filteredBookingCount: bookingResult.count,
1370
+ filteredListLength: (_a = bookingResult.list) == null ? void 0 : _a.length
1371
+ };
1372
+ }
1373
+ if (memoryCacheEnabled) {
1374
+ this.writeBookingRemoteCache({
1375
+ key: cacheKey,
1376
+ queryPayload: data,
1377
+ withFields,
1378
+ rawOrders: Array.isArray(rawList) ? rawList : [],
1379
+ bookingResult,
1380
+ updatedAt: Date.now(),
1381
+ stale: false,
1382
+ rangeMeta: this.parseBookingSalesRangeMeta(requestPayload)
1383
+ });
1384
+ }
1385
+ this.logInfo("fetchBookingListFromAPI: SSE 返回并拆分完成", {
1386
+ rawCount: rawList.length,
1387
+ flattenedCount: bookingResult.count,
1388
+ cacheKey,
1389
+ withFields
1390
+ });
1391
+ return this.buildBookingResponse(bookingResult);
1392
+ } catch (error) {
1393
+ const errorMessage = error instanceof Error ? error.message : String(error);
1394
+ this.logError("fetchBookingListFromAPI: SSE 请求失败", { error: errorMessage });
973
1395
  return {
974
1396
  code: 500,
975
1397
  message: errorMessage,
@@ -1266,14 +1688,25 @@ var Server = class {
1266
1688
  async recomputeAndNotifyOrderQuery() {
1267
1689
  if (this.orderQuerySubscribers.size === 0)
1268
1690
  return;
1691
+ const notifyStartAt = Date.now();
1269
1692
  this.logInfo("recomputeAndNotifyOrderQuery: 开始推送", {
1270
- subscriberCount: this.orderQuerySubscribers.size
1693
+ subscriberCount: this.orderQuerySubscribers.size,
1694
+ notifyStartAt: new Date(notifyStartAt).toISOString()
1271
1695
  });
1272
1696
  for (const [subscriberId, subscriber] of this.orderQuerySubscribers.entries()) {
1273
1697
  try {
1698
+ const computeStartAt = Date.now();
1274
1699
  const result = await this.computeOrderQueryResult(subscriber.context);
1700
+ const computeEndAt = Date.now();
1701
+ const callbackStartAt = Date.now();
1275
1702
  subscriber.callback(result);
1276
- this.logInfo("recomputeAndNotifyOrderQuery: 已推送", { subscriberId });
1703
+ const callbackEndAt = Date.now();
1704
+ this.logInfo("recomputeAndNotifyOrderQuery: 已推送", {
1705
+ subscriberId,
1706
+ computeDurationMs: computeEndAt - computeStartAt,
1707
+ callbackDurationMs: callbackEndAt - callbackStartAt,
1708
+ totalDurationMs: callbackEndAt - computeStartAt
1709
+ });
1277
1710
  } catch (error) {
1278
1711
  const errorMessage = error instanceof Error ? error.message : String(error);
1279
1712
  this.logError("recomputeAndNotifyOrderQuery: 推送失败", {
@@ -1282,6 +1715,11 @@ var Server = class {
1282
1715
  });
1283
1716
  }
1284
1717
  }
1718
+ const notifyEndAt = Date.now();
1719
+ this.logInfo("recomputeAndNotifyOrderQuery: 推送完成", {
1720
+ subscriberCount: this.orderQuerySubscribers.size,
1721
+ totalNotifyDurationMs: notifyEndAt - notifyStartAt
1722
+ });
1285
1723
  }
1286
1724
  /**
1287
1725
  * 预约数据变更后,遍历订阅者重新计算并通过 callback 推送