@pisell/private-materials 6.12.228 → 6.12.230

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 (109) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/meta.js +1 -1
  5. package/build/lowcode/render/default/view.css +1 -1
  6. package/build/lowcode/render/default/view.js +2 -2
  7. package/build/lowcode/view.css +1 -1
  8. package/build/lowcode/view.js +2 -2
  9. package/es/components/HDLKiosk/app/manifest.js +16 -1
  10. package/es/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +2 -2
  11. package/es/components/HDLKiosk/docs/04-/350/256/276/345/244/207/344/270/216/347/247/260/351/207/215.md +15 -31
  12. package/es/components/HDLKiosk/docs/08-/346/227/240/345/244/264Application/350/277/220/350/241/214/346/227/266/351/207/215/346/236/204/350/256/276/350/256/241.md +1 -1
  13. package/es/components/HDLKiosk/kernel/application/defineApplication.js +24 -10
  14. package/es/components/HDLKiosk/kernel/application/defineSlice.js +3 -1
  15. package/es/components/HDLKiosk/kernel/application/runtime.js +16 -12
  16. package/es/components/HDLKiosk/kernel/workflow/actor.js +11 -2
  17. package/es/components/HDLKiosk/locales.js +30 -0
  18. package/es/components/HDLKiosk/runtime/admissionPolicy.js +2 -2
  19. package/es/components/HDLKiosk/runtime/application.js +2 -1
  20. package/es/components/HDLKiosk/runtime/devices/scannerSessionPort.js +2 -1
  21. package/es/components/HDLKiosk/runtime/devices/scannerSessionRouter.js +36 -8
  22. package/es/components/HDLKiosk/runtime/operations/writes.js +4 -1
  23. package/es/components/HDLKiosk/runtime/session/createSession.js +24 -3
  24. package/es/components/HDLKiosk/slices/catalog/Screen/index.js +4 -8
  25. package/es/components/HDLKiosk/slices/catalog/slice.js +2 -2
  26. package/es/components/HDLKiosk/slices/customization/Screen/index.js +24 -20
  27. package/es/components/HDLKiosk/slices/customization/Screen/index.less +0 -18
  28. package/es/components/HDLKiosk/slices/identity/Screen/index.js +16 -2
  29. package/es/components/HDLKiosk/slices/identity/slice.js +2 -0
  30. package/es/components/HDLKiosk/slices/identity/view.js +9 -6
  31. package/es/components/HDLKiosk/slices/primaryProductPrompt/slice.js +3 -3
  32. package/es/components/HDLKiosk/ui/HDLKioskWorkflowView.js +18 -0
  33. package/es/components/HDLKiosk/ui/controllers/useHDLKioskCustomerController.js +2 -7
  34. package/es/components/HDLKiosk/utils.js +6 -11
  35. package/es/components/bigSale/components/BookingFloatingPanel/useBookingPanelController.js +1 -0
  36. package/es/components/bigSale/components/TimeBar/index.js +1 -0
  37. package/es/components/bigSale/dialog.js +1 -0
  38. package/es/components/bigSale/hooks/useBigSaleContext.js +1 -0
  39. package/es/components/bigSale/hooks/useSelectFloorMapResourceForBigSale.js +1 -0
  40. package/es/components/bigSale/index.js +1 -0
  41. package/es/components/bigSale/templates/HDLKiosk/adapter/createHost.js +2 -1
  42. package/es/components/bigSale/templates/HDLKiosk/adapter/sales/cart.js +6 -2
  43. package/es/components/bigSale/templates/HDLKiosk/adapter/sales/index.js +4 -14
  44. package/es/components/bigSale/templates/HDLKiosk/hooks/useBigSaleHDLKioskAdapter.js +28 -4
  45. package/es/components/bigSale/templates/HDLKiosk/index.js +1 -1
  46. package/es/components/bigSale/templates/HDLKiosk/utils/transactionState.js +12 -12
  47. package/es/components/bigSale/templates/Kiosk/index.js +1 -0
  48. package/es/components/sharedSecondaryBigSale/SharedSecondaryBigSaleReceiver.js +1 -0
  49. package/es/index.js +2 -0
  50. package/es/plus/multiCurrencySettlement/index.js +1 -1
  51. package/es/plus/pisellReservation/data/bookingToReservationTables.js +1 -1
  52. package/es/plus/pisellReservation/floorMap/floorPlanApi.js +1 -1
  53. package/es/plus/pisellReservation/floorMap/reservationMock.js +1 -1
  54. package/es/plus/pisellReservation/index.js +17 -0
  55. package/es/plus/saasDevice/devicePasscodes/model.js +4 -4
  56. package/es/plus/saleDetail/Base.js +1 -0
  57. package/es/plus/saleDetail/components/Client/hooks/useClientSelectorProps.js +1 -0
  58. package/es/plus/saleDetail/components/DetailActions/PrintAction.js +1 -0
  59. package/es/plus/saleDetail/components/DetailActions/index.js +1 -0
  60. package/es/plus/saleDetail/components/SummaryFinancialSections/index.js +1 -0
  61. package/es/plus/saleDetail/index.js +1 -0
  62. package/es/plus/saleDetail/tmpl/B/components/Dish/index.js +1 -0
  63. package/es/plus/saleDetail/tmpl/B/index.js +1 -0
  64. package/es/plus/salesSdk/context/SalesSdkProvider.js +20 -1
  65. package/es/plus/salesSdk/hooks/useSalesSdkTempOrderSync.js +117 -27
  66. package/es/plus/salesSdk/index.js +53 -0
  67. package/es/plus/salesSdk/utils/addProductWithFlowDebounced.js +1 -1
  68. package/es/plus/salesSdk/utils/bookingDate.js +1 -1
  69. package/es/plus/salesSdk/utils/enrichCartLineHolderDisplay.js +1 -1
  70. package/es/plus/walletPassGallery/components/timelineSection/index.js +4 -4
  71. package/es/pro/Login2.0/AuthModal.d.ts +1 -1
  72. package/es/pro/Login2.0/index.d.ts +2 -2
  73. package/es/pro/Login2.0/shared/types.d.ts +1 -1
  74. package/lib/components/HDLKiosk/app/manifest.js +16 -1
  75. package/lib/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +2 -2
  76. package/lib/components/HDLKiosk/docs/04-/350/256/276/345/244/207/344/270/216/347/247/260/351/207/215.md +15 -31
  77. package/lib/components/HDLKiosk/docs/08-/346/227/240/345/244/264Application/350/277/220/350/241/214/346/227/266/351/207/215/346/236/204/350/256/276/350/256/241.md +1 -1
  78. package/lib/components/HDLKiosk/kernel/application/defineApplication.js +24 -10
  79. package/lib/components/HDLKiosk/kernel/application/defineSlice.js +3 -0
  80. package/lib/components/HDLKiosk/kernel/application/runtime.js +16 -12
  81. package/lib/components/HDLKiosk/kernel/workflow/actor.js +11 -2
  82. package/lib/components/HDLKiosk/locales.js +30 -0
  83. package/lib/components/HDLKiosk/runtime/admissionPolicy.js +2 -2
  84. package/lib/components/HDLKiosk/runtime/application.js +2 -1
  85. package/lib/components/HDLKiosk/runtime/devices/scannerSessionPort.js +2 -1
  86. package/lib/components/HDLKiosk/runtime/devices/scannerSessionRouter.js +36 -8
  87. package/lib/components/HDLKiosk/runtime/operations/writes.js +4 -1
  88. package/lib/components/HDLKiosk/runtime/session/createSession.js +24 -3
  89. package/lib/components/HDLKiosk/slices/catalog/Screen/index.js +4 -8
  90. package/lib/components/HDLKiosk/slices/catalog/slice.js +1 -1
  91. package/lib/components/HDLKiosk/slices/customization/Screen/index.js +24 -20
  92. package/lib/components/HDLKiosk/slices/customization/Screen/index.less +0 -18
  93. package/lib/components/HDLKiosk/slices/identity/Screen/index.js +16 -2
  94. package/lib/components/HDLKiosk/slices/identity/slice.js +2 -0
  95. package/lib/components/HDLKiosk/slices/identity/view.js +9 -6
  96. package/lib/components/HDLKiosk/slices/primaryProductPrompt/slice.js +2 -2
  97. package/lib/components/HDLKiosk/ui/HDLKioskWorkflowView.js +20 -2
  98. package/lib/components/HDLKiosk/ui/controllers/useHDLKioskCustomerController.js +2 -7
  99. package/lib/components/HDLKiosk/utils.js +8 -13
  100. package/lib/components/bigSale/templates/HDLKiosk/adapter/createHost.js +2 -1
  101. package/lib/components/bigSale/templates/HDLKiosk/adapter/sales/cart.js +6 -2
  102. package/lib/components/bigSale/templates/HDLKiosk/adapter/sales/index.js +4 -14
  103. package/lib/components/bigSale/templates/HDLKiosk/hooks/useBigSaleHDLKioskAdapter.js +28 -4
  104. package/lib/components/bigSale/templates/HDLKiosk/index.js +1 -1
  105. package/lib/components/bigSale/templates/HDLKiosk/utils/transactionState.js +12 -12
  106. package/lib/plus/salesSdk/context/SalesSdkProvider.js +20 -1
  107. package/lib/plus/salesSdk/hooks/useSalesSdkTempOrderSync.js +117 -27
  108. package/package.json +2 -2
  109. package/es/private-materials/lowcode/authmodal/meta.d.ts +0 -1
@@ -0,0 +1,53 @@
1
+ import "./hosts/createInstanceHostBridge.js";
2
+ import "./holderConfirm/createHolderConfirmBridge.js";
3
+ import "./giftSelect/createGiftSelectBridge.js";
4
+ import "./context/SalesSdkHostBridgesContext.js";
5
+ import "./utils/formatHolderOptions.js";
6
+ import "./utils/enrichCartLineHolderDisplay.js";
7
+ import "./context/SalesSdkHolderContext.js";
8
+ import "../multiCurrencySettlement/index.js";
9
+ import "./utils/appendPromotionTags.js";
10
+ import "./context/SalesSdkUIHostsContext.js";
11
+ import "./utils/runAddProductFlow.js";
12
+ import "./components/showAddToCartToast.js";
13
+ import "./utils/resolveAddProductDebounceKey.js";
14
+ import "./utils/productSkuOptions.js";
15
+ import "./utils/identity.js";
16
+ import "./utils/resolveInCartQuantityForAddToast.js";
17
+ import "./utils/addProductWithFlowDebounced.js";
18
+ import "./utils/customerAuthGate.js";
19
+ import "./utils/bookingDate.js";
20
+ import "./utils/holderSubmitValidation.js";
21
+ import "./utils/removeOrderProducts.js";
22
+ import "./selectors.js";
23
+ import "./utils/enrich.js";
24
+ import "./giftSelect/createOpenGiftSelectHost.js";
25
+ import "./utils/ensureHoldersBeforeSubmit.js";
26
+ import "./hooks/useSalesSdk.js";
27
+ import "./bookingEditService/BookingEditServiceDrawer.js";
28
+ import "./hosts/BookingEditHostRenderer.js";
29
+ import "./hosts/CustomerAuthHostRenderer.js";
30
+ import "./hooks/useSalesSdkCart.js";
31
+ import "./hooks/useSalesSdkCustomer.js";
32
+ import "./normalProductDetail/NormalProductDetailHostRenderer.js";
33
+ import "./hosts/createOpenBookingEditHost.js";
34
+ import "./hosts/createOpenMultiDaySelectHost.js";
35
+ import "./hosts/createOpenHolderSelectHost.js";
36
+ import "./hosts/createOpenCustomerAuthHost.js";
37
+ import "./demo/hosts/legacyOpenProductDetail.js";
38
+ import "./hosts/createCompositeOpenProductDetailHost.js";
39
+ import "./normalProductDetail/createOpenNormalProductDetailHost.js";
40
+ import "./normalProductDetail/createOpenBookingSelectionFlowHost.js";
41
+ import "./giftSelect/GiftSelectModal.js";
42
+ import "./giftSelect/GiftSelectHostRenderer.js";
43
+ import "./allergy/AllergySelectionModal.js";
44
+ import "./context/SalesSdkProvider.js";
45
+ import "./context/SalesSdkDishContext.js";
46
+ import "./context/SalesSdkBookingContext.js";
47
+ import "./hooks/useSalesSdkSummary.js";
48
+ import "./hooks/useSalesSdkProducts.js";
49
+ import "./demo/index.js";
50
+ import "./hooks/useSalesSdkDish.js";
51
+ import "./hooks/useCurrentSalesSdkDish.js";
52
+ import "./hooks/useSyncOsModule.js";
53
+ export {};
@@ -360,4 +360,4 @@ function createAddProductWithFlowDebounced(deps) {
360
360
  };
361
361
  }
362
362
  //#endregion
363
- export { createAddProductWithFlowDebounced };
363
+ export { createAddProductWithFlowDebounced, shouldBypassAddProductDebounce };
@@ -31,4 +31,4 @@ function subscribeBookingDateChange(bookingTicket, callback) {
31
31
  return typeof off === "function" ? off : () => {};
32
32
  }
33
33
  //#endregion
34
- export { formatBookingDateTime, subscribeBookingDateChange };
34
+ export { BOOKING_CONTEXT_DATE_EVENT, formatBookingDateTime, subscribeBookingDateChange };
@@ -71,4 +71,4 @@ function buildHolderListRequestKey(params) {
71
71
  });
72
72
  }
73
73
  //#endregion
74
- export { buildHolderListRequestKey, buildHolderListRequestParams, enrichCartBookingHolderDisplay, resolveHolderDisplayConfig };
74
+ export { buildHolderListRequestKey, buildHolderListRequestParams, enrichCartBookingHolderDisplay, pickHolderIdFromBooking, resolveHolderDisplayConfig };
@@ -6,12 +6,12 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react";
6
6
  import { locales } from "@pisell/utils";
7
7
  import { Button, PisellEmpty, PisellModal, PisellStatisticList, Skeleton, Timeline, usePisellConfig } from "@pisell/materials";
8
8
  import { useRequest } from "ahooks";
9
- import timezonePlugin from "dayjs/plugin/timezone";
10
- import utcPlugin from "dayjs/plugin/utc";
9
+ import timezone from "dayjs/plugin/timezone";
10
+ import utc from "dayjs/plugin/utc";
11
11
  import "./index.less";
12
12
  //#region src/plus/walletPassGallery/components/timelineSection/index.tsx
13
- dayjs.extend(timezonePlugin);
14
- dayjs.extend(utcPlugin);
13
+ dayjs.extend(timezone);
14
+ dayjs.extend(utc);
15
15
  const stateTimeUnix = 1723255863;
16
16
  const POLL_INTERVAL = 2e3;
17
17
  const POLL_MAX_TIMES = 5;
@@ -14,4 +14,4 @@ interface AuthModalProps {
14
14
  */
15
15
  declare const AuthModal: React.FC<AuthModalProps>;
16
16
  //#endregion
17
- export { AuthModal, AuthModalProps };
17
+ export { AuthModal };
@@ -1,4 +1,4 @@
1
- import { AuthMethodType, VerificationMethod } from "./shared/types.js";
1
+ import { AuthMethodType, EmailAuthConfig, EmailRegistrationVerification, LoginFormData, OAuthConfig, PhoneAuthConfig, RegisterFormData, UIConfig, VerificationMethod } from "./shared/types.js";
2
2
  import { Login2, Login2Config, Login2Events, Login2Props } from "./Login2.js";
3
3
  import { Register2, Register2Config, Register2Events, Register2Props } from "./Register2.js";
4
- import { AuthModal, AuthModalProps } from "./AuthModal.js";
4
+ import { AuthModal } from "./AuthModal.js";
@@ -226,4 +226,4 @@ interface RegisterFormData {
226
226
  verification_token?: string;
227
227
  }
228
228
  //#endregion
229
- export { AuthMethodType, EmailAuthConfig, LegalTermsConfig, LoginFormData, LoginMethodItem, OAuthConfig, PhoneAuthConfig, RegisterFormData, RegistrationMethodItem, UIConfig, VerificationMethod };
229
+ export { AuthMethodType, EmailAuthConfig, EmailRegistrationVerification, LegalTermsConfig, LoginFormData, LoginMethodItem, OAuthConfig, PhoneAuthConfig, RegisterFormData, RegistrationMethodItem, UIConfig, VerificationMethod };
@@ -19,7 +19,7 @@ const hdlKioskPredicates = [
19
19
  }),
20
20
  require_defineApplication.definePredicate({
21
21
  id: "identity.satisfied",
22
- evaluate: ({ sales }) => sales.customer !== void 0
22
+ evaluate: ({ sales }) => sales.customer != null
23
23
  }),
24
24
  require_defineApplication.definePredicate({
25
25
  id: "fulfillment.selected",
@@ -57,6 +57,13 @@ const hdlKioskDefinition = require_defineApplication.defineApplication({
57
57
  workflows: {
58
58
  quick: require_defineApplication.defineWorkflow({
59
59
  id: "hdl.quick",
60
+ start: [{
61
+ trigger: "manual",
62
+ channel: "hdl.quick"
63
+ }, {
64
+ trigger: "onScan",
65
+ channel: "identity"
66
+ }],
60
67
  exitPolicy: "session.activeOrder",
61
68
  steps: [
62
69
  {
@@ -86,6 +93,10 @@ const hdlKioskDefinition = require_defineApplication.defineApplication({
86
93
  },
87
94
  steps: [
88
95
  require_index.hdlKioskSlices.catalog,
96
+ {
97
+ slice: require_index.hdlKioskSlices.identity,
98
+ resolutionRouting: { complete: "bypass" }
99
+ },
89
100
  {
90
101
  slice: require_index.hdlKioskSlices.primaryProductPrompt,
91
102
  presentation: { mode: "modal" },
@@ -114,6 +125,10 @@ const hdlKioskDefinition = require_defineApplication.defineApplication({
114
125
  slice: require_index.hdlKioskSlices.customization,
115
126
  resolutionRouting: { omit: "reenter" }
116
127
  },
128
+ {
129
+ slice: require_index.hdlKioskSlices.identity,
130
+ resolutionRouting: { complete: "bypass" }
131
+ },
117
132
  require_index.hdlKioskSlices.catalog,
118
133
  require_index.hdlKioskSlices.fulfillment,
119
134
  require_index.hdlKioskSlices.cart,
@@ -111,9 +111,9 @@ Host device event
111
111
  → React 或纯 JS 消费者
112
112
  ```
113
113
 
114
- - Quick 未声明 `start`,因此默认 manual。
114
+ - Quick 同时声明 manual 和 `onScan/identity` 入口,供手动点餐与会员/券码启动。
115
115
  - Barcode/Scale 只能由 automatic Trigger 启动,手动调用 Workflow Handle 会被拒绝。
116
- - 当前一个 automatic Trigger 只能绑定一个 Workflow,重复绑定 fail-closed。
116
+ - 同一 automatic `trigger + channel` 只能绑定一个 Workflow;Workflow 可声明多个纯数据入口,重复通道绑定 fail-closed。
117
117
  - Channel 由 Runtime Adapter 赋予,不要求物理秤事件自带 Channel。
118
118
  - 流程内扫码是 Session Command,不再进入 Workflow 启动 API。
119
119
  - 当前不实现多 Workflow 广播和嵌套调用;`parentSessionId` 只保留因果关联字段。
@@ -109,29 +109,17 @@ ScanPolicyDecision
109
109
  - 无焦点栈时,必须能显式暂停 SalesSDK 的全局扫码 listener;如果 Host 不提供该能力,生产环境应把 Scanner capability 标记为不可用,不能依赖两个 listener 的短窗去重。
110
110
  - 复用 SalesSDK 的 global scan/payment activity context,形成双向门禁:队列 running/waitingSelection 时禁止支付;支付 processing 时禁止新扫码任务。
111
111
 
112
- ## 5. Entry 首码:先激活 Session,再消费
112
+ ## 5. Entry 首码:只读识别、页面就绪、统一消费
113
113
 
114
- Entry 收到合法商品扫码事件时:
114
+ 1. ScannerIngress 完成 capability、去重和背压校验,Router 建立 startId 并保留首码;
115
+ 2. Host 只读识别首码:商品选择 `onScan/product`(Barcode),会员/券选择 `onScan/identity`(Quick);
116
+ 3. Runtime 创建 Session、完成 `session.ensure` 并发布 Session;
117
+ 4. WorkflowView 挂载后确认 `markPresented`,Router 才开始消费;这保证 SKU 选择发生在具体流程页面内;
118
+ 5. 所有码均由同一 FIFO 串行执行 `barcode.consume`。首码也使用原有消费函数,再次查找通常命中 OS 的本地商品或识别缓存;
119
+ 6. Identity 处理首码时展示 Toast,隐藏认证表单。完成后由 identity slice 根据最新原始 customer 决定是否 redirect;券码本身不代表会员已登录。
120
+ 7. 停留在 Identity 期间,主屏等外部来源更新为有效会员时,视图重评当前 Slice 的 redirect;不重复写入 customer,也不提前打断尚未完成的首码消费。
115
121
 
116
- 1. ScannerIngress 完成 capability、去重和背压校验;
117
- 2. Entry Arbiter claim `barcode`;
118
- 3. `session.ensure` 创建或恢复安全订单;
119
- 4. 创建 Barcode Session,并把首码任务绑定到该 `sessionEpoch`;
120
- 5. `setSession`,由 Session Controller 完成 Actor、Operation Coordinator、Host binding 和 Projection subscription 的安装;
121
- 6. Session Runtime 进入 `active` 并发布与 Screen 无关的 `sessionReadyForCommands`,同时由交易策略给出独立的 `sessionCartWritable`;
122
- 7. ScannerSessionRouter 把首码交给该 Session,串行执行 `barcode.consume`;
123
- 8. 成功后刷新 Projection/Summary;失败则留在该 Session 的当前页面展示可恢复错误,不回退到 Entry;
124
- 9. 只有无法建立安全 Session 时才释放 claim 并返回 Entry。
125
-
126
- `sessionReadyForCommands` 是 Session Runtime 的生命周期状态,不由 Catalog 或任意 Screen 的 mount/commit 事件驱动。未来某类 Workflow 即使以 Identity、Fulfillment 或其他页面作为首屏,也不需要改 ScannerSessionRouter。
127
-
128
- 这里应区分三个概念:
129
-
130
- - `sessionCreated`:已有 Session identity,但运行时依赖可能尚未安装;
131
- - `sessionReadyForCommands`:Session 已激活,能够安全接受串行 Operation;
132
- - `sessionCartWritable`:当前交易阶段允许修改购物车。
133
-
134
- 首码等待前两者完成且第三项为真。UI 只是订阅并投影 Session 状态,不承担解除扫码任务的职责。
122
+ 页面挂载确认只表示 UI 已就绪。交易可写性、支付冻结、队列取消和去重仍由 Runtime 负责,Screen 不持有扫码任务。
135
123
 
136
124
  若 Quick/Scale Session 正在 `ensure`,此时收到的扫码任务应绑定到该 pending session,等 Session 发布和目标页面可写后消费;不得另起 Barcode Session,也不得静默丢弃。
137
125
 
@@ -143,9 +131,9 @@ Entry 收到合法商品扫码事件时:
143
131
 
144
132
  | 当前状态/页面 | 策略 | 扫码成功后的行为 |
145
133
  | --- | --- | --- |
146
- | Entry、无 Session | 接受 | 创建并激活 Barcode Session,由 Session 消费首码 |
134
+ | Entry、无 Session | 接受 | 识别后选择 Barcode/Quick,页面就绪后消费首码 |
147
135
  | Entry、Quick/Scale 正在启动 | 接受并暂存 | 绑定 pending session,Session 可写后消费 |
148
- | Identity | 接受 | 留在 Identity,刷新全局购物车 |
136
+ | Identity | 接受 | 消费后由 identity slice 按客户身份重评当前节点 |
149
137
  | Fulfillment | 接受 | 留在 Fulfillment,刷新全局购物车 |
150
138
  | Customization | 接受 | 留在 Customization;主称重商品码须走防重复规则 |
151
139
  | Catalog | 接受 | 留在 Catalog,刷新商品与金额 |
@@ -158,7 +146,7 @@ Entry 收到合法商品扫码事件时:
158
146
  | payment pending/unknown/succeeded | 拒绝 | 进入既有支付恢复/结果流程 |
159
147
  | Payment Completed、blocked、resetting | 拒绝 | 不修改订单,等待新 Session |
160
148
 
161
- 扫码不触发 Workflow `Next`,不改变当前节点。Barcode Flow 的首码也只是启动方式,不意味着后续扫码只能发生在 Barcode Flow。
149
+ 扫码不直接触发 Workflow `Next`;Identity 在处理结束后重评自身 redirect。Barcode Flow 的首码也只是启动方式,不意味着后续扫码只能发生在 Barcode Flow。
162
150
 
163
151
  Customization 需额外识别当前主称重商品:扫描同一主商品不能按普通商品重复加购,应提示用户通过称重详情修改;其他普通商品仍可加入。
164
152
 
@@ -176,15 +164,11 @@ ScannerSessionRouter 维护真实的有限 FIFO 队列:
176
164
  - `requiresSelection` 拒绝在选择态建立前已经排队的旧任务并结束本轮 drain;选择弹层
177
165
  通过 Scanner focus stack 临时接管物理扫码,关闭后新扫码按正常准入重新进入队列。
178
166
 
179
- ## 8. 只消费商品码
180
-
181
- 业务要求是“扫码添加商品”,所以必须在副作用发生前确定条码类型。优先级如下:
167
+ ## 8. 识别与消费边界
182
168
 
183
- 1. Host 提供 `classifyBarcode` + `consumeProductBarcode`;
184
- 2. 或提供原子的 `consumeProductBarcode`,非商品码无副作用返回 `unsupported`;
185
- 3. 临时阶段才复用通用扫码 API,但必须保留完整 `kind`、`promotionResult`、商品 receipt 和选择态结果。
169
+ OS 仅新增 `getGlobalScanCodeType`,内部复用旧版识别和缓存。原有 `handleGlobalScanCode` 保持不变,所有码仍从原入口消费;不新增消费接口,不跨层传递识别数据。再次查找遵循旧版的本地商品、识别缓存、远程查询顺序。缓存被清除或淘汰时可能重新请求,入口查询不锁定后续识别结果。
186
170
 
187
- 通用 `handleGlobalScanCode` 已经执行完再返回 `kind`,因此“执行后发现不是商品再拒绝”不能作为生产方案:会员码、订单码或促销码可能已经产生不可逆业务副作用。
171
+ HDLKiosk 首码通过 `resolveBarcode` 查询码类型,只决定入口,不提前写订单。商品、会员、券均在页面就绪后由原队列消费;订单码等未支持类型不启动流程。商品结果仍须满足 receipt/重报价校验,客户和优惠券成功不能被误判为缺少商品写入凭据。
188
172
 
189
173
  建议扩展 HDLKiosk Host 结果:
190
174
 
@@ -367,7 +367,7 @@ Kernel 禁止导入 HDLKiosk、Host、设备、订单、支付和 React 类型
367
367
  1. Application Definition 不产生外部副作用;
368
368
  2. Application Implementation 不持有实例资源;
369
369
  3. `bind()` 创建实例资源,`activate()` 安装 automatic Trigger;
370
- 4. automatic Trigger 与 Workflow binding 唯一;
370
+ 4. automatic `trigger + channel` 与 Workflow binding 唯一;
371
371
  5. Session 完成 prepare 和 attach 后才可见;
372
372
  6. Attachment 在 Session 发布后激活;
373
373
  7. 同一 occurrence 最多拥有一个 Session;
@@ -20,7 +20,7 @@ function isWorkflowStep(value) {
20
20
  function defineWorkflow(workflow) {
21
21
  if (!workflow.id.trim()) throw new Error("Workflow id cannot be empty");
22
22
  if (workflow.steps.length === 0) throw new Error(`Workflow ${workflow.id} must contain steps`);
23
- return Object.freeze(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, workflow), workflow.start ? { start: Object.freeze(require_objectSpread2._objectSpread2({}, workflow.start)) } : {}), {}, { steps: Object.freeze([...workflow.steps]) }));
23
+ return Object.freeze(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, workflow), workflow.start ? { start: Object.freeze(Array.isArray(workflow.start) ? workflow.start.map((start) => Object.freeze(require_objectSpread2._objectSpread2({}, start))) : require_objectSpread2._objectSpread2({}, workflow.start)) } : {}), {}, { steps: Object.freeze([...workflow.steps]) }));
24
24
  }
25
25
  const manualTrigger = Object.freeze({
26
26
  id: "manual",
@@ -31,8 +31,8 @@ function defineTrigger(trigger) {
31
31
  return Object.freeze(require_objectSpread2._objectSpread2({}, trigger));
32
32
  }
33
33
  function resolveWorkflowStartBinding(workflow) {
34
- var _workflow$start;
35
- const start = (_workflow$start = workflow.start) !== null && _workflow$start !== void 0 ? _workflow$start : {
34
+ var _ref, _workflow$start$find;
35
+ const start = (_ref = Array.isArray(workflow.start) ? (_workflow$start$find = workflow.start.find((binding) => binding.trigger === manualTrigger.id)) !== null && _workflow$start$find !== void 0 ? _workflow$start$find : workflow.start[0] : workflow.start) !== null && _ref !== void 0 ? _ref : {
36
36
  trigger: manualTrigger.id,
37
37
  channel: workflow.id
38
38
  };
@@ -101,12 +101,19 @@ function defineApplication(definition) {
101
101
  if (workflowStarts.has(workflow.id)) throw new Error(`Duplicate Workflow: ${workflow.id}`);
102
102
  if (!exitPolicies.has(workflow.exitPolicy)) throw new Error(`Workflow ${workflow.id} references unknown Exit Policy ${workflow.exitPolicy}`);
103
103
  const start = resolveWorkflowStartBinding(workflow);
104
- const trigger = triggers.get(start.trigger);
105
- if (!trigger) throw new Error(`Workflow ${workflow.id} references unknown Trigger ${start.trigger}`);
106
- if (trigger.mode === "automatic") {
107
- const owner = automaticTriggerOwners.get(trigger.id);
108
- if (owner) throw new Error(`Automatic Trigger ${trigger.id} is already bound to Workflow ${owner}`);
109
- automaticTriggerOwners.set(trigger.id, workflow.id);
104
+ const starts = Array.isArray(workflow.start) ? workflow.start : [start];
105
+ if (starts.length === 0) throw new Error(`Workflow ${workflow.id} needs a start binding`);
106
+ for (const binding of starts) {
107
+ var _binding$trigger, _binding$channel;
108
+ if (!((_binding$trigger = binding.trigger) === null || _binding$trigger === void 0 ? void 0 : _binding$trigger.trim()) || !((_binding$channel = binding.channel) === null || _binding$channel === void 0 ? void 0 : _binding$channel.trim())) throw new Error(`Workflow ${workflow.id} has an invalid start binding`);
109
+ const trigger = triggers.get(binding.trigger);
110
+ if (!trigger) throw new Error(`Workflow ${workflow.id} references unknown Trigger ${binding.trigger}`);
111
+ if (trigger.mode === "automatic") {
112
+ const key = `${binding.trigger}\0${binding.channel}`;
113
+ const owner = automaticTriggerOwners.get(key);
114
+ if (owner) throw new Error(`Automatic Trigger ${binding.trigger}/${binding.channel} is already bound to Workflow ${owner}`);
115
+ automaticTriggerOwners.set(key, workflow.id);
116
+ }
110
117
  }
111
118
  workflowStarts.set(workflow.id, start);
112
119
  workflow.steps.forEach((rawStep) => {
@@ -136,11 +143,18 @@ function defineApplication(definition) {
136
143
  }
137
144
  });
138
145
  const beforeEach = (request) => {
139
- var _activities$require$n;
146
+ var _slices$get2, _activities$require$n;
140
147
  for (const guard of globalNavigationGuards.values()) {
141
148
  const decision = guard.evaluate(request);
142
149
  if (decision.type !== "allow") return decision;
143
150
  }
151
+ const redirect = (_slices$get2 = slices.get(request.to.activityId)) === null || _slices$get2 === void 0 ? void 0 : _slices$get2.definition.redirect;
152
+ const target = redirect === null || redirect === void 0 ? void 0 : redirect(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, request), {}, { workflow: request.context }));
153
+ if (target === "$next") return { type: "bypass" };
154
+ if (target) return {
155
+ type: "redirect",
156
+ to: target
157
+ };
144
158
  if (request.to.kind !== "step" || !request.to.guardPhases.includes("beforeEnter")) return { type: "allow" };
145
159
  const guardId = (_activities$require$n = activities.require(request.to.activityId).navigationGuards) === null || _activities$require$n === void 0 ? void 0 : _activities$require$n.beforeEnter;
146
160
  if (!guardId) throw new Error(`Activity ${request.to.activityId} has no beforeEnter guard`);
@@ -1,5 +1,6 @@
1
1
  const require_objectSpread2 = require("../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js");
2
2
  //#region src/components/HDLKiosk/kernel/application/defineSlice.ts
3
+ const accept = () => Object.freeze({ kind: "accept" });
3
4
  const require$1 = (predicate) => Object.freeze({
4
5
  kind: "require",
5
6
  predicate
@@ -20,6 +21,7 @@ function defineSlice(slice) {
20
21
  if (phase !== "beforeEnter") throw new Error(`Slice ${slice.id} has an unknown navigation guard phase: ${phase}`);
21
22
  if (typeof navigationGuard !== "function") throw new Error(`Slice ${slice.id} guard ${phase} must be a function`);
22
23
  });
24
+ if (slice.redirect !== void 0 && typeof slice.redirect !== "function") throw new Error(`Slice ${slice.id} redirect must be a function`);
23
25
  return Object.freeze(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, slice), {}, {
24
26
  surface: slice.surface ? Object.freeze(require_objectSpread2._objectSpread2({}, slice.surface)) : void 0,
25
27
  resolutions: Object.freeze(require_objectSpread2._objectSpread2({}, slice.resolutions)),
@@ -29,6 +31,7 @@ function defineSlice(slice) {
29
31
  }));
30
32
  }
31
33
  //#endregion
34
+ exports.accept = accept;
32
35
  exports.defineSlice = defineSlice;
33
36
  exports.perform = perform;
34
37
  exports.require = require$1;
@@ -18,14 +18,18 @@ function createApplicationRuntime({ definition, environment, implementation, tri
18
18
  Object.entries(definition.workflows).forEach(([workflowKey, workflow]) => {
19
19
  const start = definition.workflowStarts.get(workflow.id);
20
20
  if (!start) throw new Error(`Unknown Workflow start: ${workflow.id}`);
21
- if (start.trigger === "manual") return;
22
- if (workflowsByTrigger.has(start.trigger)) throw new Error(`Automatic Trigger ${start.trigger} has multiple Workflow bindings`);
23
- workflowsByTrigger.set(start.trigger, {
24
- workflowKey,
25
- workflowId: workflow.id,
26
- workflow,
27
- start
28
- });
21
+ const starts = Array.isArray(workflow.start) ? workflow.start : [start];
22
+ for (const binding of starts) {
23
+ if (binding.trigger === "manual") continue;
24
+ const key = `${binding.trigger}\0${binding.channel}`;
25
+ if (workflowsByTrigger.has(key)) throw new Error(`Automatic Trigger ${binding.trigger}/${binding.channel} has multiple Workflow bindings`);
26
+ workflowsByTrigger.set(key, {
27
+ workflowKey,
28
+ workflowId: workflow.id,
29
+ workflow,
30
+ start: binding
31
+ });
32
+ }
29
33
  });
30
34
  const adapters = /* @__PURE__ */ new Map();
31
35
  triggerAdapters.forEach((adapter) => {
@@ -34,7 +38,7 @@ function createApplicationRuntime({ definition, environment, implementation, tri
34
38
  if (!contract || contract.mode !== "automatic") throw new Error(`Unknown automatic Trigger Adapter: ${adapter.trigger}`);
35
39
  adapters.set(adapter.trigger, adapter);
36
40
  });
37
- workflowsByTrigger.forEach((_binding, trigger) => {
41
+ workflowsByTrigger.forEach(({ start: { trigger } }) => {
38
42
  if (!adapters.has(trigger)) throw new Error(`Missing Trigger Adapter: ${trigger}`);
39
43
  });
40
44
  const sessions = /* @__PURE__ */ new Map();
@@ -84,7 +88,7 @@ function createApplicationRuntime({ definition, environment, implementation, tri
84
88
  publishSnapshot();
85
89
  return Promise.resolve(result);
86
90
  }
87
- const occurrenceKey = `${input.cause.trigger}\u0000${input.cause.occurrenceId}`;
91
+ const occurrenceKey = `${input.cause.trigger}\u0000${input.cause.channel}\u0000${input.cause.occurrenceId}`;
88
92
  const existingId = occurrenceSessions.get(occurrenceKey);
89
93
  const existing = existingId ? sessions.get(existingId) : void 0;
90
94
  if (existing) return Promise.resolve({
@@ -183,7 +187,7 @@ function createApplicationRuntime({ definition, environment, implementation, tri
183
187
  };
184
188
  const emitAutomatic = (trigger, occurrence) => {
185
189
  var _occurrence$correlati;
186
- const binding = workflowsByTrigger.get(trigger);
190
+ const binding = workflowsByTrigger.get(`${trigger}\0${occurrence.channel}`);
187
191
  const cause = Object.freeze({
188
192
  trigger,
189
193
  channel: occurrence.channel,
@@ -216,7 +220,7 @@ function createApplicationRuntime({ definition, environment, implementation, tri
216
220
  const session = sessions.get(sessionId);
217
221
  if (!session || !ownedSessionIds.delete(sessionId)) return false;
218
222
  sessions.delete(sessionId);
219
- const occurrenceKey = `${session.cause.trigger}\u0000${session.cause.occurrenceId}`;
223
+ const occurrenceKey = `${session.cause.trigger}\u0000${session.cause.channel}\u0000${session.cause.occurrenceId}`;
220
224
  if (occurrenceSessions.get(occurrenceKey) === sessionId) occurrenceSessions.delete(occurrenceKey);
221
225
  const detach = sessionDetachments.get(sessionId);
222
226
  sessionDetachments.delete(sessionId);
@@ -87,14 +87,23 @@ function createWorkflowActor({ definition, context, sessionEpoch, transitionRunn
87
87
  const direction = !fromNodeId ? "initial" : delta < 0 ? "backward" : "forward";
88
88
  for (let candidateIndex = targetIndex; candidateIndex >= 0 && candidateIndex < definition.stepOrder.length; candidateIndex += delta) {
89
89
  const candidateStepId = definition.stepOrder[candidateIndex];
90
- if (definition.nodes[candidateStepId].presentation.mode === "modal") {
90
+ const candidateNode = definition.nodes[candidateStepId];
91
+ const candidateResolution = stepResolutions.get(candidateStepId);
92
+ if (shouldBypassResolvedStep(candidateStepId, candidateResolution)) {
93
+ const transition = candidateResolution ? candidateNode.on[candidateResolution] : void 0;
94
+ if (delta > 0 && transition) return resolveTarget({
95
+ kind: "step",
96
+ stepId: transition.target
97
+ }, redirectCount + 1, direction);
98
+ continue;
99
+ }
100
+ if (candidateNode.presentation.mode === "modal") {
91
101
  const fromNode = fromNodeId ? definition.nodes[fromNodeId] : void 0;
92
102
  if (!fromNode || fromNode.presentation.mode === "modal") return {
93
103
  type: "block",
94
104
  error: createWorkflowError(fromNode ? "workflowNestedModalUnsupported" : "workflowModalRequiresPage", "staff")
95
105
  };
96
106
  }
97
- if (shouldBypassResolvedStep(candidateStepId, stepResolutions.get(candidateStepId))) continue;
98
107
  const decision = beforeEach({
99
108
  navigationId,
100
109
  intentId,
@@ -64,6 +64,12 @@ const localeTexts = {
64
64
  "pisell2.hdl-kiosk.login-via-sms": "Login via SMS",
65
65
  "pisell2.hdl-kiosk.skip": "Skip",
66
66
  "pisell2.hdl-kiosk.member-login": "Member login",
67
+ "pisell2.text.add-client-success": "Add Client Success",
68
+ "pisell2.hdl-kiosk.scan-service-unavailable": "Scanning is temporarily unavailable. Please ask a staff member for help.",
69
+ "pisell2.hdl-kiosk.scan-request-failed": "Unable to check this code. Please try again.",
70
+ "pisell2.hdl-kiosk.signing-in": "Signing in…",
71
+ "pisell2.hdl-kiosk.processing": "Processing…",
72
+ "pisell2.hdl-kiosk.code-applied": "Code applied",
67
73
  "pisell2.hdl-kiosk.member-logged-in": "Member signed in",
68
74
  "pisell2.hdl-kiosk.member": "Member",
69
75
  "pisell2.hdl-kiosk.member-logout": "Sign out",
@@ -231,6 +237,12 @@ const localeTexts = {
231
237
  "pisell2.hdl-kiosk.login-via-sms": "短信登录",
232
238
  "pisell2.hdl-kiosk.skip": "跳过",
233
239
  "pisell2.hdl-kiosk.member-login": "会员登录",
240
+ "pisell2.text.add-client-success": "添加客户成功",
241
+ "pisell2.hdl-kiosk.scan-service-unavailable": "扫码服务暂不可用,请联系店员。",
242
+ "pisell2.hdl-kiosk.scan-request-failed": "暂时无法查询此码,请重试。",
243
+ "pisell2.hdl-kiosk.signing-in": "登录中…",
244
+ "pisell2.hdl-kiosk.processing": "处理中…",
245
+ "pisell2.hdl-kiosk.code-applied": "使用成功",
234
246
  "pisell2.hdl-kiosk.member-logged-in": "会员已登录",
235
247
  "pisell2.hdl-kiosk.member": "会员",
236
248
  "pisell2.hdl-kiosk.member-logout": "退出登录",
@@ -398,6 +410,12 @@ const localeTexts = {
398
410
  "pisell2.hdl-kiosk.login-via-sms": "短信登入",
399
411
  "pisell2.hdl-kiosk.skip": "跳過",
400
412
  "pisell2.hdl-kiosk.member-login": "會員登入",
413
+ "pisell2.text.add-client-success": "添加客戶成功",
414
+ "pisell2.hdl-kiosk.scan-service-unavailable": "掃碼服務暫不可用,請聯絡店員。",
415
+ "pisell2.hdl-kiosk.scan-request-failed": "暫時無法查詢此碼,請重試。",
416
+ "pisell2.hdl-kiosk.signing-in": "登入中…",
417
+ "pisell2.hdl-kiosk.processing": "處理中…",
418
+ "pisell2.hdl-kiosk.code-applied": "使用成功",
401
419
  "pisell2.hdl-kiosk.member-logged-in": "會員已登入",
402
420
  "pisell2.hdl-kiosk.member": "會員",
403
421
  "pisell2.hdl-kiosk.member-logout": "登出",
@@ -565,6 +583,12 @@ const localeTexts = {
565
583
  "pisell2.hdl-kiosk.login-via-sms": "SMSでログイン",
566
584
  "pisell2.hdl-kiosk.skip": "スキップ",
567
585
  "pisell2.hdl-kiosk.member-login": "会員ログイン",
586
+ "pisell2.text.add-client-success": "クライアントの追加に成功しました",
587
+ "pisell2.hdl-kiosk.scan-service-unavailable": "スキャンは現在利用できません。スタッフにお声がけください。",
588
+ "pisell2.hdl-kiosk.scan-request-failed": "このコードを確認できませんでした。もう一度お試しください。",
589
+ "pisell2.hdl-kiosk.signing-in": "ログイン中…",
590
+ "pisell2.hdl-kiosk.processing": "処理中…",
591
+ "pisell2.hdl-kiosk.code-applied": "コードを適用しました",
568
592
  "pisell2.hdl-kiosk.member-logged-in": "会員ログイン済み",
569
593
  "pisell2.hdl-kiosk.member": "会員",
570
594
  "pisell2.hdl-kiosk.member-logout": "ログアウト",
@@ -732,6 +756,12 @@ const localeTexts = {
732
756
  "pisell2.hdl-kiosk.login-via-sms": "Iniciar sessão por SMS",
733
757
  "pisell2.hdl-kiosk.skip": "Ignorar",
734
758
  "pisell2.hdl-kiosk.member-login": "Início de sessão de membro",
759
+ "pisell2.text.add-client-success": "Cliente Adicionado com Sucesso",
760
+ "pisell2.hdl-kiosk.scan-service-unavailable": "A leitura de códigos está temporariamente indisponível. Peça ajuda a um funcionário.",
761
+ "pisell2.hdl-kiosk.scan-request-failed": "Não foi possível verificar este código. Tente novamente.",
762
+ "pisell2.hdl-kiosk.signing-in": "A iniciar sessão…",
763
+ "pisell2.hdl-kiosk.processing": "A processar…",
764
+ "pisell2.hdl-kiosk.code-applied": "Código aplicado",
735
765
  "pisell2.hdl-kiosk.member-logged-in": "Sessão de membro iniciada",
736
766
  "pisell2.hdl-kiosk.member": "Membro",
737
767
  "pisell2.hdl-kiosk.member-logout": "Terminar sessão",
@@ -7,8 +7,8 @@ function resolveHDLKioskEntryCapabilityBlockReason({ entry, capabilities, primar
7
7
  if (require_workflow.builtInWorkflowHasActivity(entry, "customization") && (!capabilities.customization.weightedProduct || !capabilities.scale.available)) return "customizationUnavailable";
8
8
  if (entry === "scale" && (!capabilities.scale.available || !capabilities.scale.weightChangeTrigger)) return "scaleTriggerUnavailable";
9
9
  }
10
- function evaluateHDLKioskAdmission({ entry, config, capabilities }) {
11
- if (!config.enabledEntries.includes(entry)) return {
10
+ function evaluateHDLKioskAdmission({ entry, enabledEntry = entry, config, capabilities }) {
11
+ if (!config.enabledEntries.includes(enabledEntry)) return {
12
12
  status: "rejected",
13
13
  reason: "entryDisabled"
14
14
  };
@@ -23,8 +23,9 @@ const hdlKioskApplication = require_manifest.hdlKioskDefinition.implement({
23
23
  }),
24
24
  disposeEnvironment: ({ scanner }) => scanner.dispose(),
25
25
  triggerAdapters: ({ environment }) => require_triggers.createHDLKioskEntryTriggerAdapters(environment.host, environment.config, environment.scanner.triggerAdapter),
26
- admission: ({ workflowKey, environment }) => require_admissionPolicy.evaluateHDLKioskAdmission({
26
+ admission: ({ workflowKey, cause, environment }) => require_admissionPolicy.evaluateHDLKioskAdmission({
27
27
  entry: workflowKey,
28
+ enabledEntry: cause.trigger === "onScan" ? "barcode" : workflowKey,
28
29
  config: environment.config,
29
30
  capabilities: environment.host.capabilities
30
31
  }),
@@ -23,6 +23,7 @@ function createHDLKioskScannerSessionPort(host, session, onSessionActivity = ()
23
23
  return Object.freeze({
24
24
  id: session.id,
25
25
  sessionEpoch: () => session.actor.getSnapshot().sessionEpoch,
26
+ whenReady: session.whenPresented,
26
27
  decideConsume: () => {
27
28
  if (isPaymentFrozen(host)) return {
28
29
  status: "rejected",
@@ -39,7 +40,7 @@ function createHDLKioskScannerSessionPort(host, session, onSessionActivity = ()
39
40
  consume: function() {
40
41
  var _ref = require_asyncToGenerator._asyncToGenerator(function* ({ code, eventId }) {
41
42
  const sessionEpoch = session.actor.getSnapshot().sessionEpoch;
42
- const result = yield session.executeCommand("barcode.consume", { code }, `scan:${sessionEpoch}:${eventId}`);
43
+ const result = yield session.executeCommand("barcode.consume", { code }, `scan:${sessionEpoch}:${eventId}`).finally(() => session.settleScan());
43
44
  if (result.status === "succeeded") return {
44
45
  operationStatus: result.status,
45
46
  queueDirective: result.data.code === "requiresSelection" ? "pauseForSelection" : "continue"