@pisell/pisellos 2.2.93 → 2.2.95

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 (53) hide show
  1. package/dist/modules/Order/index.d.ts +1 -1
  2. package/dist/plugins/app-types/app/app.d.ts +1 -0
  3. package/dist/server/index.d.ts +55 -0
  4. package/dist/server/index.js +826 -207
  5. package/dist/server/modules/index.d.ts +6 -0
  6. package/dist/server/modules/index.js +7 -0
  7. package/dist/server/modules/order/index.d.ts +87 -0
  8. package/dist/server/modules/order/index.js +916 -0
  9. package/dist/server/modules/order/types.d.ts +530 -0
  10. package/dist/server/modules/order/types.js +141 -0
  11. package/dist/server/modules/order/utils/filterBookings.d.ts +6 -0
  12. package/dist/server/modules/order/utils/filterBookings.js +350 -0
  13. package/dist/server/modules/order/utils/filterOrders.d.ts +15 -0
  14. package/dist/server/modules/order/utils/filterOrders.js +226 -0
  15. package/dist/server/modules/resource/index.d.ts +88 -0
  16. package/dist/server/modules/resource/index.js +1202 -0
  17. package/dist/server/modules/resource/types.d.ts +121 -0
  18. package/dist/server/modules/resource/types.js +47 -0
  19. package/dist/solution/BookingByStep/index.d.ts +1 -1
  20. package/dist/solution/BookingTicket/index.d.ts +1 -1
  21. package/dist/solution/Sales/index.d.ts +96 -0
  22. package/dist/solution/Sales/index.js +562 -0
  23. package/dist/solution/Sales/types.d.ts +66 -0
  24. package/dist/solution/Sales/types.js +26 -0
  25. package/dist/solution/index.d.ts +1 -0
  26. package/dist/solution/index.js +2 -1
  27. package/lib/modules/Order/index.d.ts +1 -1
  28. package/lib/plugins/app-types/app/app.d.ts +1 -0
  29. package/lib/server/index.d.ts +55 -0
  30. package/lib/server/index.js +343 -1
  31. package/lib/server/modules/index.d.ts +6 -0
  32. package/lib/server/modules/index.js +16 -2
  33. package/lib/server/modules/order/index.d.ts +87 -0
  34. package/lib/server/modules/order/index.js +543 -0
  35. package/lib/server/modules/order/types.d.ts +530 -0
  36. package/lib/server/modules/order/types.js +34 -0
  37. package/lib/server/modules/order/utils/filterBookings.d.ts +6 -0
  38. package/lib/server/modules/order/utils/filterBookings.js +320 -0
  39. package/lib/server/modules/order/utils/filterOrders.d.ts +15 -0
  40. package/lib/server/modules/order/utils/filterOrders.js +197 -0
  41. package/lib/server/modules/resource/index.d.ts +88 -0
  42. package/lib/server/modules/resource/index.js +571 -0
  43. package/lib/server/modules/resource/types.d.ts +121 -0
  44. package/lib/server/modules/resource/types.js +35 -0
  45. package/lib/solution/BookingByStep/index.d.ts +1 -1
  46. package/lib/solution/BookingTicket/index.d.ts +1 -1
  47. package/lib/solution/Sales/index.d.ts +96 -0
  48. package/lib/solution/Sales/index.js +413 -0
  49. package/lib/solution/Sales/types.d.ts +66 -0
  50. package/lib/solution/Sales/types.js +35 -0
  51. package/lib/solution/index.d.ts +1 -0
  52. package/lib/solution/index.js +3 -1
  53. package/package.json +1 -1
@@ -0,0 +1,350 @@
1
+ var _excluded = ["bookings"],
2
+ _excluded2 = ["bookings"],
3
+ _excluded3 = ["bookings"];
4
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
5
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
6
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
7
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
8
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
9
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
10
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
11
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
12
+ /** 预约状态:协议 appointment_status 优先,兼容历史 status */
13
+ function getBookingAppointmentStatus(booking) {
14
+ var _ref, _booking$appointment_;
15
+ return String((_ref = (_booking$appointment_ = booking.appointment_status) !== null && _booking$appointment_ !== void 0 ? _booking$appointment_ : booking.status) !== null && _ref !== void 0 ? _ref : '');
16
+ }
17
+
18
+ // ─────────────────────────────────────────────────────────────────
19
+ // 时间戳 / 范围解析
20
+ // ─────────────────────────────────────────────────────────────────
21
+
22
+ function toTimestamp(value) {
23
+ if (value === undefined || value === null || value === '') return 0;
24
+ if (typeof value === 'number') return value;
25
+ return new Date(value).getTime();
26
+ }
27
+
28
+ /**
29
+ * 将 start_date / start_time(或 end_*)拼成可被 Date 稳定解析的字符串。
30
+ * 时间字段协议为 H:i(如 15:00)时补全为 H:i:00,避免部分环境对「无秒」非 ISO 串(空格分隔)解析不一致。
31
+ */
32
+ function bookingDateTimeString(datePart, timePart) {
33
+ var d = (datePart !== null && datePart !== void 0 ? datePart : '').trim();
34
+ var t = (timePart !== null && timePart !== void 0 ? timePart : '').trim();
35
+ if (t && /^\d{1,2}:\d{2}$/.test(t)) {
36
+ t = "".concat(t, ":00");
37
+ }
38
+ return "".concat(d, " ").concat(t).trim();
39
+ }
40
+ function parseDateRange(range) {
41
+ if (!Array.isArray(range) || range.length < 2) return null;
42
+ var start = toTimestamp(range[0]);
43
+ var end = toTimestamp(range[1]);
44
+ if (!start && !end) return null;
45
+ return [start, end];
46
+ }
47
+ function parseNumberRange(range) {
48
+ if (!Array.isArray(range) || range.length < 2) return null;
49
+ var min = Number(range[0]);
50
+ var max = Number(range[1]);
51
+ if (isNaN(min) || isNaN(max)) return null;
52
+ return [min, max];
53
+ }
54
+
55
+ // ─────────────────────────────────────────────────────────────────
56
+ // 预编译过滤上下文:将 filters 一次性解析为高效的 Set / Range,
57
+ // 供 matchOrder、matchBooking 复用,避免重复计算
58
+ // ─────────────────────────────────────────────────────────────────
59
+
60
+ function prepareFilters(f) {
61
+ var _f$order_payment_stat, _f$order_status, _f$order_sales_channe, _f$payment_method, _f$appointment_status, _f$form_record_ids;
62
+ var hasCustomerId = f.customer_id !== undefined && f.customer_id !== null;
63
+ return {
64
+ orderPaymentStatusSet: (_f$order_payment_stat = f.order_payment_status) !== null && _f$order_payment_stat !== void 0 && _f$order_payment_stat.length ? new Set(f.order_payment_status) : null,
65
+ orderStatusSet: (_f$order_status = f.order_status) !== null && _f$order_status !== void 0 && _f$order_status.length ? new Set(f.order_status) : null,
66
+ orderSalesChannelSet: (_f$order_sales_channe = f.order_sales_channel) !== null && _f$order_sales_channe !== void 0 && _f$order_sales_channe.length ? new Set(f.order_sales_channel) : null,
67
+ paymentMethodSet: (_f$payment_method = f.payment_method) !== null && _f$payment_method !== void 0 && _f$payment_method.length ? new Set(f.payment_method) : null,
68
+ appointmentStatusSet: (_f$appointment_status = f.appointment_status) !== null && _f$appointment_status !== void 0 && _f$appointment_status.length ? new Set(f.appointment_status) : null,
69
+ formRecordIdSet: (_f$form_record_ids = f.form_record_ids) !== null && _f$form_record_ids !== void 0 && _f$form_record_ids.length ? new Set(f.form_record_ids) : null,
70
+ hasCustomerId: hasCustomerId,
71
+ customerIdStr: hasCustomerId ? String(f.customer_id) : '',
72
+ hasBusinessCode: !!f.business_code,
73
+ businessCode: f.business_code,
74
+ bookingTimeRange: parseDateRange(f.sales_time_between),
75
+ createdAtRange: parseDateRange(f.created_at_between),
76
+ updatedAtRange: parseDateRange(f.updated_at_between),
77
+ numberRange: parseNumberRange(f.number_between),
78
+ totalAmountRange: Array.isArray(f.total_amount_between) && f.total_amount_between.length === 2 ? f.total_amount_between : null,
79
+ keywordLower: f.keyword ? f.keyword.toLowerCase() : '',
80
+ afterExecutionTimeMs: function () {
81
+ var raw = f.after_execution_time;
82
+ if (raw === undefined || raw === null || raw === '') return null;
83
+ var ms = toTimestamp(raw);
84
+ return Number.isFinite(ms) ? ms : null;
85
+ }(),
86
+ enableChildBookingFilter: f.child_booking === 1
87
+ };
88
+ }
89
+
90
+ // ─────────────────────────────────────────────────────────────────
91
+ // 订单级匹配:不通过则整个 order 下所有 bookings 跳过
92
+ // ─────────────────────────────────────────────────────────────────
93
+
94
+ function matchOrder(order, ctx) {
95
+ if (ctx.hasBusinessCode && order.business_code !== ctx.businessCode) return false;
96
+ if (ctx.hasCustomerId && String(order.customer_id) !== ctx.customerIdStr) return false;
97
+ if (ctx.orderPaymentStatusSet && !ctx.orderPaymentStatusSet.has(order.payment_status)) return false;
98
+ if (ctx.orderStatusSet && !ctx.orderStatusSet.has(order.status)) return false;
99
+ if (ctx.orderSalesChannelSet && !ctx.orderSalesChannelSet.has(order.order_sales_channel)) return false;
100
+ if (ctx.paymentMethodSet) {
101
+ var pm = order.payments;
102
+ if (!Array.isArray(pm) || pm.length === 0) return false;
103
+ var matched = false;
104
+ for (var k = 0; k < pm.length; k++) {
105
+ if (ctx.paymentMethodSet.has(pm[k].code)) {
106
+ matched = true;
107
+ break;
108
+ }
109
+ }
110
+ if (!matched) return false;
111
+ }
112
+ if (ctx.createdAtRange) {
113
+ var ts = toTimestamp(order.created_at);
114
+ if (ts < ctx.createdAtRange[0] || ts > ctx.createdAtRange[1]) return false;
115
+ }
116
+ if (ctx.updatedAtRange) {
117
+ var _ts = toTimestamp(order.updated_at);
118
+ if (_ts < ctx.updatedAtRange[0] || _ts > ctx.updatedAtRange[1]) return false;
119
+ }
120
+ if (ctx.totalAmountRange) {
121
+ var _order$summary$total_, _order$summary;
122
+ var raw = (_order$summary$total_ = (_order$summary = order.summary) === null || _order$summary === void 0 ? void 0 : _order$summary.total_amount) !== null && _order$summary$total_ !== void 0 ? _order$summary$total_ : order.total_amount;
123
+ var amt = Number(raw);
124
+ if (isNaN(amt)) return false;
125
+ if (ctx.totalAmountRange[0] != null && amt < ctx.totalAmountRange[0]) return false;
126
+ if (ctx.totalAmountRange[1] != null && amt > ctx.totalAmountRange[1]) return false;
127
+ }
128
+ return true;
129
+ }
130
+
131
+ // ─────────────────────────────────────────────────────────────────
132
+ // Booking 级匹配:在订单通过之后逐条检查每个 booking
133
+ // orderInfo 用于 keyword 搜索时的 JSON 序列化
134
+ // ─────────────────────────────────────────────────────────────────
135
+
136
+ function matchBooking(booking, orderInfo, ctx) {
137
+ if (ctx.enableChildBookingFilter) {
138
+ var _parent_id, _item_type;
139
+ var parentId = Number((_parent_id = booking.parent_id) !== null && _parent_id !== void 0 ? _parent_id : 0);
140
+ var itemType = String((_item_type = booking.item_type) !== null && _item_type !== void 0 ? _item_type : '');
141
+ var isMatched = parentId !== 0 && itemType !== 'recurring_booking' || parentId === 0 && itemType === 'recurring_booking';
142
+ if (!isMatched) return false;
143
+ }
144
+ if (ctx.appointmentStatusSet && !ctx.appointmentStatusSet.has(getBookingAppointmentStatus(booking))) return false;
145
+ if (ctx.bookingTimeRange) {
146
+ var ts = toTimestamp(booking.start_date);
147
+ if (ts < ctx.bookingTimeRange[0] || ts > ctx.bookingTimeRange[1]) return false;
148
+ }
149
+ if (ctx.afterExecutionTimeMs != null) {
150
+ var bookingStartMs = toTimestamp(bookingDateTimeString(booking.start_date, booking.start_time));
151
+ if (!Number.isFinite(bookingStartMs) || bookingStartMs < ctx.afterExecutionTimeMs) return false;
152
+ }
153
+ if (ctx.numberRange) {
154
+ var num = Number(booking.number);
155
+ if (isNaN(num) || num < ctx.numberRange[0] || num > ctx.numberRange[1]) return false;
156
+ }
157
+ if (ctx.formRecordIdSet) {
158
+ var formMatched = false;
159
+ var resources = booking.resources;
160
+ if (Array.isArray(resources)) {
161
+ for (var r = 0; r < resources.length; r++) {
162
+ if (resources[r].relation_type === 'form' && ctx.formRecordIdSet.has(resources[r].relation_id)) {
163
+ formMatched = true;
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ if (!formMatched && booking.relation_type === 'form' && booking.relation_id && booking.relation_id > 0) {
169
+ formMatched = ctx.formRecordIdSet.has(booking.relation_id);
170
+ }
171
+ if (!formMatched) return false;
172
+ }
173
+ if (ctx.keywordLower) {
174
+ var target = orderInfo ? _objectSpread(_objectSpread({}, booking), {}, {
175
+ order: orderInfo
176
+ }) : booking;
177
+ var str = JSON.stringify(target).toLowerCase();
178
+ if (!str.includes(ctx.keywordLower)) return false;
179
+ }
180
+ return true;
181
+ }
182
+
183
+ // ─────────────────────────────────────────────────────────────────
184
+ // 转换:OrderData[] → BookingData[](独立导出,供其他场景使用)
185
+ // ─────────────────────────────────────────────────────────────────
186
+
187
+ export function flattenOrdersToBookings(orders) {
188
+ if (!orders || !Array.isArray(orders)) return [];
189
+ var result = [];
190
+ for (var i = 0, len = orders.length; i < len; i++) {
191
+ var order = orders[i];
192
+ var bookings = order.bookings;
193
+ if (!Array.isArray(bookings) || bookings.length === 0) continue;
194
+ var _omit = order.bookings,
195
+ orderInfo = _objectWithoutProperties(order, _excluded);
196
+ for (var j = 0, bLen = bookings.length; j < bLen; j++) {
197
+ result.push(_objectSpread(_objectSpread({}, bookings[j]), {}, {
198
+ order: orderInfo
199
+ }));
200
+ }
201
+ }
202
+ return result;
203
+ }
204
+
205
+ // ─────────────────────────────────────────────────────────────────
206
+ // 核心:单遍扫描 — 展开 + 过滤 + 分页
207
+ //
208
+ // 性能策略:
209
+ // 1. 订单级条件在外层循环 matchOrder 预判,不匹配则整组 bookings 跳过
210
+ // 2. orderInfo 延迟到有命中 booking 时才创建,同一 order 只创建一次
211
+ // 3. 只对最终命中的 booking 做 object spread
212
+ // ─────────────────────────────────────────────────────────────────
213
+
214
+ export function filterBookingsFromOrders(orders, filters) {
215
+ var f = filters || {};
216
+ if (!orders || !Array.isArray(orders)) {
217
+ return {
218
+ list: [],
219
+ count: 0,
220
+ size: 0,
221
+ skip: 0
222
+ };
223
+ }
224
+ var size = f.num || 20;
225
+ var page = f.skip || 1;
226
+ var startIndex = (page - 1) * size;
227
+ var ctx = prepareFilters(f);
228
+ var totalCount = 0;
229
+ var paginatedList = [];
230
+ for (var i = 0, oLen = orders.length; i < oLen; i++) {
231
+ var order = orders[i];
232
+ var bookings = order.bookings;
233
+ if (!Array.isArray(bookings) || bookings.length === 0) continue;
234
+
235
+ // 订单级预过滤
236
+ if (!matchOrder(order, ctx)) continue;
237
+
238
+ // 订单通过,遍历其 bookings;orderInfo 延迟创建
239
+ var orderInfo = null;
240
+ for (var j = 0, bLen = bookings.length; j < bLen; j++) {
241
+ var booking = bookings[j];
242
+
243
+ // keyword 匹配需要 orderInfo,提前创建
244
+ if (ctx.keywordLower && !orderInfo) {
245
+ var _omit = order.bookings,
246
+ rest = _objectWithoutProperties(order, _excluded2);
247
+ orderInfo = rest;
248
+ }
249
+ if (!matchBooking(booking, orderInfo, ctx)) continue;
250
+
251
+ // 命中:收集到分页窗口内
252
+ if (totalCount >= startIndex && paginatedList.length < size) {
253
+ if (!orderInfo) {
254
+ var _omit2 = order.bookings,
255
+ _rest = _objectWithoutProperties(order, _excluded3);
256
+ orderInfo = _rest;
257
+ }
258
+ paginatedList.push(_objectSpread(_objectSpread({}, booking), {}, {
259
+ order: orderInfo
260
+ }));
261
+ }
262
+ totalCount++;
263
+ }
264
+ }
265
+ return {
266
+ list: paginatedList,
267
+ count: totalCount,
268
+ size: size,
269
+ skip: page
270
+ };
271
+ }
272
+
273
+ // ─────────────────────────────────────────────────────────────────
274
+ // 排序预约列表
275
+ // order_by 格式:"field:direction",如 "start_at:desc"
276
+ // 不带方向后缀则不排序
277
+ // ─────────────────────────────────────────────────────────────────
278
+
279
+ export function sortBookings(result, filters) {
280
+ var f = filters || {};
281
+ var orderBy = f.order_by;
282
+ if (!orderBy) return result;
283
+ var separatorIdx = orderBy.lastIndexOf(':');
284
+ if (separatorIdx === -1) return result;
285
+ var field = orderBy.slice(0, separatorIdx);
286
+ var direction = orderBy.slice(separatorIdx + 1);
287
+ if (direction !== 'asc' && direction !== 'desc') return result;
288
+ var multiplier = direction === 'asc' ? 1 : -1;
289
+ var getTimestamp = function getTimestamp(booking) {
290
+ switch (field) {
291
+ case 'start_at':
292
+ return toTimestamp(bookingDateTimeString(booking.start_date, booking.start_time));
293
+ case 'end_at':
294
+ return toTimestamp(bookingDateTimeString(booking.end_date, booking.end_time));
295
+ case 'created_at':
296
+ return toTimestamp(booking.order.created_at);
297
+ case 'updated_at':
298
+ return toTimestamp(booking.order.updated_at);
299
+ default:
300
+ return 0;
301
+ }
302
+ };
303
+ var sorted = result.list.slice().sort(function (a, b) {
304
+ return (getTimestamp(a) - getTimestamp(b)) * multiplier;
305
+ });
306
+ return _objectSpread(_objectSpread({}, result), {}, {
307
+ list: sorted
308
+ });
309
+ }
310
+
311
+ // ─────────────────────────────────────────────────────────────────
312
+ // 兼容独立调用:对已展开的 BookingData[] 做筛选
313
+ // 复用 matchOrder + matchBooking,仅负责迭代与分页
314
+ // ─────────────────────────────────────────────────────────────────
315
+
316
+ export function filterBookings(bookings, filters) {
317
+ var f = filters || {};
318
+ if (!bookings || !Array.isArray(bookings)) {
319
+ return {
320
+ list: [],
321
+ count: 0,
322
+ size: 0,
323
+ skip: 0
324
+ };
325
+ }
326
+ var size = f.num || 20;
327
+ var page = f.skip || 1;
328
+ var startIndex = (page - 1) * size;
329
+ var ctx = prepareFilters(f);
330
+ var totalCount = 0;
331
+ var paginatedList = [];
332
+ for (var i = 0, len = bookings.length; i < len; i++) {
333
+ var booking = bookings[i];
334
+ var order = booking.order;
335
+
336
+ // 订单级 + booking 级统一匹配
337
+ if (!matchOrder(order, ctx)) continue;
338
+ if (!matchBooking(booking, order, ctx)) continue;
339
+ if (totalCount >= startIndex && paginatedList.length < size) {
340
+ paginatedList.push(booking);
341
+ }
342
+ totalCount++;
343
+ }
344
+ return {
345
+ list: paginatedList,
346
+ count: totalCount,
347
+ size: size,
348
+ skip: page
349
+ };
350
+ }
@@ -0,0 +1,15 @@
1
+ import type { OrderData, OrderFilters, OrderFilterResult } from '../types';
2
+ export type { OrderFilters, OrderFilterResult } from '../types';
3
+ /**
4
+ * 订单过滤函数
5
+ * @param orders 原始订单列表
6
+ * @param filters 过滤条件
7
+ * @returns 过滤 + 排序 + 分页后的结果
8
+ *
9
+ * @优化口子 性能优化预留:
10
+ * - 可在此函数入口加缓存(按 filters 签名缓存结果)
11
+ * - 可对枚举字段建倒排索引(status/payment_status/shipping_status/customer_id)
12
+ * - 可对文本字段加前缀树或分词索引
13
+ * - 可做增量过滤而非全量遍历
14
+ */
15
+ export declare function filterOrders(orders: OrderData[], filters: OrderFilters): OrderFilterResult;
@@ -0,0 +1,226 @@
1
+ /**
2
+ * 订单过滤函数
3
+ * @param orders 原始订单列表
4
+ * @param filters 过滤条件
5
+ * @returns 过滤 + 排序 + 分页后的结果
6
+ *
7
+ * @优化口子 性能优化预留:
8
+ * - 可在此函数入口加缓存(按 filters 签名缓存结果)
9
+ * - 可对枚举字段建倒排索引(status/payment_status/shipping_status/customer_id)
10
+ * - 可对文本字段加前缀树或分词索引
11
+ * - 可做增量过滤而非全量遍历
12
+ */
13
+ export function filterOrders(orders, filters) {
14
+ // Bug 1 修复:防护 filters 为 null/undefined
15
+ var safeFilters = filters || {};
16
+ if (!orders || !Array.isArray(orders)) {
17
+ return {
18
+ list: [],
19
+ count: 0,
20
+ size: 0,
21
+ skip: 0
22
+ };
23
+ }
24
+
25
+ // 默认分页参数
26
+ var size = safeFilters.num || 20;
27
+ var page = safeFilters.skip || 1;
28
+
29
+ // 1. 过滤
30
+ var filteredList = orders.filter(function (order) {
31
+ // status - 订单状态
32
+ if (safeFilters.status && safeFilters.status.length > 0) {
33
+ if (!order.status || !safeFilters.status.includes(order.status)) {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ // payment_status - 支付状态
39
+ if (safeFilters.payment_status && safeFilters.payment_status.length > 0) {
40
+ if (!order.payment_status || !safeFilters.payment_status.includes(order.payment_status)) {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ // shipping_status - 处理状态
46
+ if (safeFilters.shipping_status && safeFilters.shipping_status.length > 0) {
47
+ if (!order.shipping_status || !safeFilters.shipping_status.includes(order.shipping_status)) {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ // customer_id - 客户ID(精确匹配)
53
+ if (safeFilters.customer_id !== undefined && safeFilters.customer_id !== null) {
54
+ if (String(order.customer_id) !== String(safeFilters.customer_id)) {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ // order_sales_channel - 销售渠道
60
+ if (safeFilters.order_sales_channel && safeFilters.order_sales_channel.length > 0) {
61
+ if (!order.order_sales_channel || !safeFilters.order_sales_channel.includes(order.order_sales_channel)) {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ // payment_methods - 支付方式
67
+ if (safeFilters.payment_methods && safeFilters.payment_methods.length > 0) {
68
+ if (!order.payment_methods || !Array.isArray(order.payment_methods) || order.payment_methods.length === 0) {
69
+ return false;
70
+ }
71
+ var hasMatch = order.payment_methods.some(function (pm) {
72
+ return safeFilters.payment_methods.includes(pm);
73
+ });
74
+ if (!hasMatch) {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ // min_total_amount / max_total_amount - 订单金额范围
80
+ if (safeFilters.min_total_amount !== undefined || safeFilters.max_total_amount !== undefined) {
81
+ var orderAmount = parseFloat(String(order.total_amount || 0));
82
+ if (safeFilters.min_total_amount !== undefined && orderAmount < safeFilters.min_total_amount) {
83
+ return false;
84
+ }
85
+ if (safeFilters.max_total_amount !== undefined && orderAmount > safeFilters.max_total_amount) {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ // tag_ids - 标签(统一 String 比较,兼容 number/string)
91
+ if (safeFilters.tag_ids && safeFilters.tag_ids.length > 0) {
92
+ if (!order.tag_ids || !Array.isArray(order.tag_ids) || order.tag_ids.length === 0) {
93
+ return false;
94
+ }
95
+ var filterTagSet = new Set(safeFilters.tag_ids.map(String));
96
+ var _hasMatch = order.tag_ids.some(function (tag) {
97
+ return filterTagSet.has(String(tag));
98
+ });
99
+ if (!_hasMatch) {
100
+ return false;
101
+ }
102
+ }
103
+
104
+ // shipping_type - 配送方式
105
+ if (safeFilters.shipping_type && safeFilters.shipping_type.length > 0) {
106
+ if (!order.shipping_type || !safeFilters.shipping_type.includes(order.shipping_type)) {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ // delivery_zone_name - 配送区域(模糊匹配)
112
+ if (safeFilters.delivery_zone_name !== undefined && safeFilters.delivery_zone_name !== null && safeFilters.delivery_zone_name !== '') {
113
+ if (!order.delivery_zone_name || !order.delivery_zone_name.toLowerCase().includes(safeFilters.delivery_zone_name.toLowerCase())) {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ // location_id - 取货地点(精确匹配)
119
+ if (safeFilters.location_id !== undefined && safeFilters.location_id !== null) {
120
+ if (String(order.location_id) !== String(safeFilters.location_id)) {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ // shipping_order_number - 物流单号(模糊匹配)
126
+ if (safeFilters.shipping_order_number !== undefined && safeFilters.shipping_order_number !== null && safeFilters.shipping_order_number !== '') {
127
+ if (!order.shipping_order_number || !order.shipping_order_number.toLowerCase().includes(safeFilters.shipping_order_number.toLowerCase())) {
128
+ return false;
129
+ }
130
+ }
131
+
132
+ // logistics_company_id - 物流公司
133
+ if (safeFilters.logistics_company_id && safeFilters.logistics_company_id.length > 0) {
134
+ if (!order.logistics_company_id) {
135
+ return false;
136
+ }
137
+ var orderLogisticsId = String(order.logistics_company_id);
138
+ var _hasMatch2 = safeFilters.logistics_company_id.some(function (id) {
139
+ return String(id) === orderLogisticsId;
140
+ });
141
+ if (!_hasMatch2) {
142
+ return false;
143
+ }
144
+ }
145
+
146
+ // zip - 邮编(模糊匹配)
147
+ if (safeFilters.zip !== undefined && safeFilters.zip !== null && safeFilters.zip !== '') {
148
+ if (!order.zip || !order.zip.toLowerCase().includes(safeFilters.zip.toLowerCase())) {
149
+ return false;
150
+ }
151
+ }
152
+
153
+ // business_code - 业务类型(精确匹配)
154
+ if (safeFilters.business_code) {
155
+ if (!order.business_code || order.business_code !== safeFilters.business_code) {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ // start_time / end_time - 创建时间范围
161
+ if (safeFilters.start_time || safeFilters.end_time) {
162
+ var orderTime = order.created_at ? typeof order.created_at === 'number' ? order.created_at : new Date(String(order.created_at)).getTime() : 0;
163
+ if (safeFilters.start_time) {
164
+ var startTs = new Date(safeFilters.start_time).getTime();
165
+ if (orderTime < startTs) return false;
166
+ }
167
+ if (safeFilters.end_time) {
168
+ // end_time 为当天结束(23:59:59.999)
169
+ var endTs = new Date(safeFilters.end_time).getTime() + 86400000 - 1;
170
+ if (orderTime > endTs) return false;
171
+ }
172
+ }
173
+ return true;
174
+ });
175
+
176
+ // 2. 排序
177
+ if (safeFilters.order_by) {
178
+ var sortField = safeFilters.order_by;
179
+ var sortOrder = safeFilters.sort || 'desc';
180
+ filteredList.sort(function (a, b) {
181
+ var aVal;
182
+ var bVal;
183
+ switch (sortField) {
184
+ case 'created_at':
185
+ aVal = a.created_at ? typeof a.created_at === 'number' ? a.created_at : new Date(String(a.created_at)).getTime() : 0;
186
+ bVal = b.created_at ? typeof b.created_at === 'number' ? b.created_at : new Date(String(b.created_at)).getTime() : 0;
187
+ break;
188
+ case 'updated_at':
189
+ aVal = a.updated_at ? typeof a.updated_at === 'number' ? a.updated_at : new Date(String(a.updated_at)).getTime() : 0;
190
+ bVal = b.updated_at ? typeof b.updated_at === 'number' ? b.updated_at : new Date(String(b.updated_at)).getTime() : 0;
191
+ break;
192
+ case 'total_amount':
193
+ aVal = parseFloat(String(a.total_amount || 0));
194
+ bVal = parseFloat(String(b.total_amount || 0));
195
+ break;
196
+ case 'customer_name':
197
+ aVal = (a.customer_name || '').toLowerCase();
198
+ bVal = (b.customer_name || '').toLowerCase();
199
+ break;
200
+ case 'amount_gap':
201
+ return 0;
202
+ default:
203
+ return 0;
204
+ }
205
+ if (aVal < bVal) {
206
+ return sortOrder === 'asc' ? -1 : 1;
207
+ }
208
+ if (aVal > bVal) {
209
+ return sortOrder === 'asc' ? 1 : -1;
210
+ }
211
+ return 0;
212
+ });
213
+ }
214
+
215
+ // 3. 分页(skip 为页码,从 1 开始)
216
+ var totalCount = filteredList.length;
217
+ var startIndex = (page - 1) * size;
218
+ var endIndex = startIndex + size;
219
+ var paginatedList = filteredList.slice(startIndex, endIndex);
220
+ return {
221
+ list: paginatedList,
222
+ count: totalCount,
223
+ size: size,
224
+ skip: page
225
+ };
226
+ }
@@ -0,0 +1,88 @@
1
+ import { Module, ModuleOptions, PisellCore } from '../../../types';
2
+ import { BaseModule } from '../../../modules/BaseModule';
3
+ import { type ResourceBooking, type ResourceData, type ResourceId, type QueryOptions } from './types';
4
+ /**
5
+ * Resource 模块
6
+ */
7
+ export declare class ResourceModule extends BaseModule implements Module {
8
+ protected defaultName: string;
9
+ protected defaultVersion: string;
10
+ private store;
11
+ private dbManager;
12
+ private logger;
13
+ private resourceDataSource;
14
+ /** 倒排索引: resourceId -> ResourceBooking[] */
15
+ private resourceIdIndex;
16
+ /** 待处理的同步消息队列 */
17
+ private pendingSyncMessages;
18
+ private syncTimer?;
19
+ constructor(name?: string, version?: string);
20
+ initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
21
+ private logInfo;
22
+ private logError;
23
+ preload(): Promise<void>;
24
+ getRoutes(): never[];
25
+ destroy(): void;
26
+ /**
27
+ * 获取所有资源(支持 includeBookings 附加预订信息)
28
+ */
29
+ getResources(queryOptions?: QueryOptions): ResourceData[];
30
+ /**
31
+ * 根据 ID 获取单个资源
32
+ */
33
+ getResourceById(id: ResourceId, queryOptions?: QueryOptions): ResourceData | undefined;
34
+ /**
35
+ * 创建资源
36
+ */
37
+ createResource(data: Partial<ResourceData>): ResourceData;
38
+ /**
39
+ * 更新资源
40
+ */
41
+ updateResource(id: ResourceId, data: Partial<ResourceData>): ResourceData | undefined;
42
+ /**
43
+ * 删除资源
44
+ */
45
+ deleteResource(id: ResourceId): boolean;
46
+ /**
47
+ * 根据资源 ID 获取预订列表
48
+ */
49
+ getBookingsByResourceId(resourceId: ResourceId): ResourceBooking[];
50
+ /**
51
+ * 创建预订
52
+ */
53
+ createBooking(booking: Partial<ResourceBooking>): ResourceBooking;
54
+ /**
55
+ * 更新预订
56
+ */
57
+ updateBooking(id: ResourceId, data: Partial<ResourceBooking>): ResourceBooking | undefined;
58
+ /**
59
+ * 删除预订
60
+ */
61
+ deleteBooking(id: ResourceId): boolean;
62
+ /**
63
+ * 清空缓存
64
+ */
65
+ clear(): Promise<void>;
66
+ private attachBookingsToResource;
67
+ private normalizeResource;
68
+ private safeEmit;
69
+ private syncResourcesMap;
70
+ private syncBookingsIndex;
71
+ private rebuildBookingsIndex;
72
+ private getIdKey;
73
+ private loadResourcesByServer;
74
+ private loadResourcesFromSQLite;
75
+ private saveResourcesToSQLite;
76
+ private loadBookingsFromSQLite;
77
+ private saveBookingsToSQLite;
78
+ private initResourceDataSource;
79
+ private setupResourceSync;
80
+ private processSyncMessages;
81
+ private fetchResourcesBySSE;
82
+ private removeResourcesByIds;
83
+ private mergeResourcesToStore;
84
+ private uniqueResourceIds;
85
+ }
86
+ export declare const resourceModule: ResourceModule;
87
+ export type { ResourceData, ResourceBooking, ResourceState, ResourceId, ResourcePageQuery, ResourcePageResult, QueryOptions, ScheduleEventResource, } from './types';
88
+ export { ResourceHooks } from './types';