@pisell/pisellos 2.2.124 → 2.2.126

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.
@@ -218,6 +218,40 @@ var OrderModule = class extends import_BaseModule.BaseModule {
218
218
  const ids = this.resourceIdIndex.get(String(resourceId)) || [];
219
219
  return ids.map((id) => this.store.map.get(id)).filter((order) => !!order);
220
220
  }
221
+ /**
222
+ * 仅覆盖本地已存在的订单;不存在则直接跳过,不落库、不 emit。
223
+ * 适用于"按 order_id 主动刷新本地订单详情"的代理场景。
224
+ */
225
+ async overwriteExistingOrder(fresh) {
226
+ const orderId = fresh == null ? void 0 : fresh.order_id;
227
+ if (orderId === void 0 || orderId === null) {
228
+ this.logError("overwriteExistingOrder-入参缺少 order_id");
229
+ return { overwritten: false };
230
+ }
231
+ const key = this.getIdKey(orderId);
232
+ if (!this.store.map.has(key)) {
233
+ this.logInfo("overwriteExistingOrder-本地不存在该订单,跳过", { orderId });
234
+ return { overwritten: false };
235
+ }
236
+ this.logInfo("overwriteExistingOrder-开始覆盖", {
237
+ orderId,
238
+ storeOrderCountBefore: this.store.list.length
239
+ });
240
+ this.store.list = this.store.list.map((order) => {
241
+ const id = order == null ? void 0 : order.order_id;
242
+ if (id === void 0 || id === null)
243
+ return order;
244
+ return this.getIdKey(id) === key ? fresh : order;
245
+ });
246
+ this.syncOrdersMap();
247
+ await this.saveOrdersToSQLite(this.store.list);
248
+ this.logInfo("overwriteExistingOrder-结束", {
249
+ orderId,
250
+ storeOrderCountAfter: this.store.list.length
251
+ });
252
+ await this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
253
+ return { overwritten: true };
254
+ }
221
255
  /**
222
256
  * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
223
257
  */
@@ -86,12 +86,41 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
86
86
  */
87
87
  private pickBookingsForCurrentPoint;
88
88
  /**
89
- * 标准化单条 booking
90
- * - 过滤终态(rejected/cancelled/completed)
91
- * - deviceTime 早于 currentTime 时,过滤 end_time 早于 currentTime 的历史数据
92
- * - 注入 status / isTimeout / reserved_status
89
+ * 计算 booking 在 current 时刻的执行进度百分比(0-100)。
90
+ * - startAt/endAt 非法或 totalMinutes <= 0 返回 0
91
+ * - current 早于 startAt 返回 0,晚于 endAt 返回 100
92
+ */
93
+ private calcProgressPercent;
94
+ /**
95
+ * 标准化单条 booking 的分发入口:
96
+ * - 统一过滤 rejected / cancelled(视为没来过)
97
+ * - current === deviceCurrent 走实时分支(信任 appointment_status)
98
+ * - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
93
99
  */
94
100
  private normalizeMatchedBooking;
101
+ /**
102
+ * 实时模式(current === deviceCurrent):信任 appointment_status
103
+ * - 过滤 new / completed(实时视角下视为非活跃数据)
104
+ * - status 由 getBookingStatus 映射
105
+ * - occupied 且 current > endAt 标记 isTimeout + timeoutTime
106
+ * - reserved 且 current < startAt 标记 not_arrived,否则标记 late
107
+ */
108
+ private buildRealtimeBooking;
109
+ /**
110
+ * 快照模式(current !== deviceCurrent,过去或未来):忽略 appointment_status
111
+ * 的实时态差异,仅按 booking 时间窗判定,为 current 提供时间点快照视图。
112
+ *
113
+ * - new:视作尚未成为真实预约,过滤
114
+ * - locked:保留 status=locked,不计算 timeout/late/progress
115
+ * - 其他非终态:
116
+ * - startAt/endAt 非法 → 过滤
117
+ * - current > endAt → 过滤(已结束,不入池)
118
+ * - current ∈ [startAt, endAt] → occupied,附带 progressPercent
119
+ * - current < startAt → reserved + not_arrived,附带 remainingReserveTime
120
+ *
121
+ * 快照模式下 isTimeout 恒为 false,lateTime / timeoutTime 始终置空。
122
+ */
123
+ private buildSnapshotBooking;
95
124
  getResourceBookingList(currentTime: string, bookingList?: BookingData[], deviceTime?: string): Promise<SalesResourceBookingItem[]>;
96
125
  }
97
126
  export { SalesImpl as Sales };
@@ -315,39 +315,54 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
315
315
  return fallback ? [fallback] : [];
316
316
  }
317
317
  /**
318
- * 标准化单条 booking
319
- * - 过滤终态(rejected/cancelled/completed)
320
- * - deviceTime 早于 currentTime 时,过滤 end_time 早于 currentTime 的历史数据
321
- * - 注入 status / isTimeout / reserved_status
318
+ * 计算 booking 在 current 时刻的执行进度百分比(0-100)。
319
+ * - startAt/endAt 非法或 totalMinutes <= 0 返回 0
320
+ * - current 早于 startAt 返回 0,晚于 endAt 返回 100
321
+ */
322
+ calcProgressPercent(current, startAt, endAt) {
323
+ if (!startAt.isValid() || !endAt.isValid())
324
+ return 0;
325
+ const totalMinutes = endAt.diff(startAt, "minute");
326
+ if (totalMinutes <= 0)
327
+ return 0;
328
+ const elapsedMinutes = current.diff(startAt, "minute");
329
+ if (elapsedMinutes <= 0)
330
+ return 0;
331
+ if (elapsedMinutes >= totalMinutes)
332
+ return 100;
333
+ return Math.floor(elapsedMinutes / totalMinutes * 100);
334
+ }
335
+ /**
336
+ * 标准化单条 booking 的分发入口:
337
+ * - 统一过滤 rejected / cancelled(视为没来过)
338
+ * - current === deviceCurrent 走实时分支(信任 appointment_status)
339
+ * - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
322
340
  */
323
341
  normalizeMatchedBooking(current, deviceCurrent, booking) {
324
342
  const appointmentStatus = String(booking.appointment_status ?? booking.status ?? "");
325
- if (appointmentStatus === "new" || appointmentStatus === "rejected" || appointmentStatus === "cancelled" || appointmentStatus === "completed") {
343
+ if (appointmentStatus === "rejected" || appointmentStatus === "cancelled")
326
344
  return null;
327
- }
345
+ const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
328
346
  const endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
329
- const shouldFilterHistoryByCurrent = deviceCurrent.isBefore(current);
330
- if (shouldFilterHistoryByCurrent && endAt.isValid() && endAt.isBefore(current))
347
+ if (current.isSame(deviceCurrent)) {
348
+ return this.buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt);
349
+ }
350
+ return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
351
+ }
352
+ /**
353
+ * 实时模式(current === deviceCurrent):信任 appointment_status
354
+ * - 过滤 new / completed(实时视角下视为非活跃数据)
355
+ * - status 由 getBookingStatus 映射
356
+ * - occupied 且 current > endAt 标记 isTimeout + timeoutTime
357
+ * - reserved 且 current < startAt 标记 not_arrived,否则标记 late
358
+ */
359
+ buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt) {
360
+ if (appointmentStatus === "new" || appointmentStatus === "completed")
331
361
  return null;
332
- let bookingStatus = this.getBookingStatus(appointmentStatus);
333
- const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
334
- let isTimeout = bookingStatus === "occupied" && endAt.isValid() && current.isAfter(endAt);
335
- let timeoutTime = isTimeout ? current.diff(endAt, "minute") : void 0;
336
- let progressPercent = (() => {
337
- if (bookingStatus !== "occupied")
338
- return 0;
339
- if (!startAt.isValid() || !endAt.isValid())
340
- return 0;
341
- const totalMinutes = endAt.diff(startAt, "minute");
342
- if (totalMinutes <= 0)
343
- return 0;
344
- const elapsedMinutes = current.diff(startAt, "minute");
345
- if (elapsedMinutes <= 0)
346
- return 0;
347
- if (elapsedMinutes >= totalMinutes)
348
- return 100;
349
- return Math.floor(elapsedMinutes / totalMinutes * 100);
350
- })();
362
+ const bookingStatus = this.getBookingStatus(appointmentStatus);
363
+ const isTimeout = bookingStatus === "occupied" && endAt.isValid() && current.isAfter(endAt);
364
+ const timeoutTime = isTimeout ? current.diff(endAt, "minute") : void 0;
365
+ const progressPercent = bookingStatus === "occupied" ? this.calcProgressPercent(current, startAt, endAt) : 0;
351
366
  let reservedStatus;
352
367
  let lateTime;
353
368
  let remainingReserveTime;
@@ -360,27 +375,6 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
360
375
  lateTime = Math.max(current.diff(startAt, "minute"), 0);
361
376
  }
362
377
  }
363
- if (current.isAfter(deviceCurrent) && bookingStatus === "reserved" && reservedStatus === "late" && endAt.isValid() && endAt.isSameOrAfter(current)) {
364
- bookingStatus = "occupied";
365
- reservedStatus = void 0;
366
- lateTime = void 0;
367
- remainingReserveTime = void 0;
368
- isTimeout = false;
369
- timeoutTime = void 0;
370
- progressPercent = (() => {
371
- if (!startAt.isValid() || !endAt.isValid())
372
- return 0;
373
- const totalMinutes = endAt.diff(startAt, "minute");
374
- if (totalMinutes <= 0)
375
- return 0;
376
- const elapsedMinutes = current.diff(startAt, "minute");
377
- if (elapsedMinutes <= 0)
378
- return 0;
379
- if (elapsedMinutes >= totalMinutes)
380
- return 100;
381
- return Math.floor(elapsedMinutes / totalMinutes * 100);
382
- })();
383
- }
384
378
  return {
385
379
  ...booking,
386
380
  status: bookingStatus,
@@ -392,6 +386,62 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
392
386
  remainingReserveTime
393
387
  };
394
388
  }
389
+ /**
390
+ * 快照模式(current !== deviceCurrent,过去或未来):忽略 appointment_status
391
+ * 的实时态差异,仅按 booking 时间窗判定,为 current 提供时间点快照视图。
392
+ *
393
+ * - new:视作尚未成为真实预约,过滤
394
+ * - locked:保留 status=locked,不计算 timeout/late/progress
395
+ * - 其他非终态:
396
+ * - startAt/endAt 非法 → 过滤
397
+ * - current > endAt → 过滤(已结束,不入池)
398
+ * - current ∈ [startAt, endAt] → occupied,附带 progressPercent
399
+ * - current < startAt → reserved + not_arrived,附带 remainingReserveTime
400
+ *
401
+ * 快照模式下 isTimeout 恒为 false,lateTime / timeoutTime 始终置空。
402
+ */
403
+ buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt) {
404
+ if (appointmentStatus === "new")
405
+ return null;
406
+ if (appointmentStatus === "locked") {
407
+ return {
408
+ ...booking,
409
+ status: "locked",
410
+ isTimeout: false,
411
+ timeoutTime: void 0,
412
+ progressPercent: 0,
413
+ lateTime: void 0,
414
+ reserved_status: void 0,
415
+ remainingReserveTime: void 0
416
+ };
417
+ }
418
+ if (!startAt.isValid() || !endAt.isValid())
419
+ return null;
420
+ if (current.isAfter(endAt))
421
+ return null;
422
+ if (this.isSameOrAfter(current, startAt) && this.isSameOrBefore(current, endAt)) {
423
+ return {
424
+ ...booking,
425
+ status: "occupied",
426
+ isTimeout: false,
427
+ timeoutTime: void 0,
428
+ progressPercent: this.calcProgressPercent(current, startAt, endAt),
429
+ lateTime: void 0,
430
+ reserved_status: void 0,
431
+ remainingReserveTime: void 0
432
+ };
433
+ }
434
+ return {
435
+ ...booking,
436
+ status: "reserved",
437
+ isTimeout: false,
438
+ timeoutTime: void 0,
439
+ progressPercent: 0,
440
+ lateTime: void 0,
441
+ reserved_status: "not_arrived",
442
+ remainingReserveTime: Math.max(startAt.diff(current, "minute"), 0)
443
+ };
444
+ }
395
445
  async getResourceBookingList(currentTime, bookingList = [], deviceTime = currentTime) {
396
446
  var _a;
397
447
  const current = (0, import_dayjs.default)(currentTime);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.124",
4
+ "version": "2.2.126",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",