@pisell/private-materials 6.12.166 → 6.12.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lowcode/assets-daily.json +11 -11
- package/build/lowcode/assets-dev.json +2 -2
- package/build/lowcode/assets-prod.json +11 -11
- package/build/lowcode/meta.js +1 -1
- package/build/lowcode/render/default/view.js +11 -11
- package/build/lowcode/view.js +11 -11
- package/es/components/HDLKiosk/app/ScreenRenderer.js +7 -7
- package/es/components/HDLKiosk/app/views.js +2 -1
- package/es/components/HDLKiosk/app/workflow/barcode.js +12 -35
- package/es/components/HDLKiosk/app/workflow/{guards.js → predicates.js} +9 -9
- package/es/components/HDLKiosk/app/workflow/quick.js +3 -6
- package/es/components/HDLKiosk/app/workflow/registry.js +1 -1
- package/es/components/HDLKiosk/app/workflow/scale.js +3 -12
- package/es/components/HDLKiosk/components/shell/HDLKioskBottomBarHost/index.js +12 -10
- package/es/components/HDLKiosk/components/shell/HDLKioskShell/index.js +1 -0
- package/es/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +7 -7
- package/es/components/HDLKiosk/docs/01-/346/240/270/345/277/203/350/277/220/350/241/214/346/234/272/345/210/266.md +47 -40
- package/es/components/HDLKiosk/docs/03-/344/270/232/345/212/241/346/265/201/347/250/213/344/270/216/344/272/244/344/272/222/350/247/204/345/210/231.md +3 -3
- package/es/components/HDLKiosk/docs/05-/344/272/244/346/230/223/345/256/211/345/205/250.md +1 -1
- package/es/components/HDLKiosk/docs/07-/345/220/216/347/273/255/345/267/245/344/275/234.md +1 -1
- package/es/components/HDLKiosk/docs/README.md +2 -2
- package/es/components/HDLKiosk/docs//345/256/236/346/226/275/350/277/233/345/272/246.md +3 -3
- package/es/components/HDLKiosk/docs//346/236/266/346/236/204/350/256/276/350/256/241.md +11 -11
- package/es/components/HDLKiosk/index.js +0 -1
- package/es/components/HDLKiosk/kernel/README.md +8 -9
- package/es/components/HDLKiosk/kernel/application/defineApplication.js +18 -19
- package/es/components/HDLKiosk/kernel/application/defineSlice.js +8 -13
- package/es/components/HDLKiosk/kernel/workflow/actor.js +118 -28
- package/es/components/HDLKiosk/kernel/workflow/compiler.js +35 -41
- package/es/components/HDLKiosk/kernel/workflow/selectors.js +9 -0
- package/es/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +10 -9
- package/es/components/HDLKiosk/runtime/controllers/useHDLKioskCustomerController.js +1 -1
- package/es/components/HDLKiosk/runtime/createWorkflowSession.js +3 -3
- package/es/components/HDLKiosk/runtime/domainTransitionRunner.js +9 -9
- package/es/components/HDLKiosk/runtime/workflowEvent.js +9 -4
- package/es/components/HDLKiosk/runtime/workflowLifecycle.js +3 -3
- package/es/components/HDLKiosk/slices/catalog/PrimaryProductPromptDialog/index.js +1 -2
- package/es/components/HDLKiosk/slices/catalog/Screen/index.js +11 -38
- package/es/components/HDLKiosk/slices/catalog/Screen/model.js +1 -5
- package/es/components/HDLKiosk/slices/catalog/slice.js +4 -16
- package/es/components/HDLKiosk/slices/catalog/view.js +4 -6
- package/es/components/HDLKiosk/slices/customization/Screen/index.js +9 -11
- package/es/components/HDLKiosk/slices/customization/operation.js +23 -4
- package/es/components/HDLKiosk/slices/customization/slice.js +5 -9
- package/es/components/HDLKiosk/slices/customization/view.js +3 -2
- package/es/components/HDLKiosk/slices/fulfillment/Screen/index.js +6 -6
- package/es/components/HDLKiosk/slices/fulfillment/slice.js +5 -8
- package/es/components/HDLKiosk/slices/identity/Screen/index.js +18 -12
- package/es/components/HDLKiosk/slices/identity/slice.js +4 -11
- package/es/components/HDLKiosk/slices/index.js +3 -2
- package/es/components/HDLKiosk/slices/payment/Screen/index.js +5 -5
- package/es/components/HDLKiosk/slices/payment/slice.js +7 -8
- package/es/components/HDLKiosk/slices/primaryProductPrompt/slice.js +14 -0
- package/es/components/HDLKiosk/slices/primaryProductPrompt/view.js +23 -0
- package/es/components/HDLKiosk/utils.js +3 -2
- package/es/components/bigSale/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +17 -2
- package/es/pro/Login2.0/Login2.d.ts +2 -0
- package/es/pro/Login2.0/Login2.js +9 -1
- package/es/pro/Login2.0/Register2.d.ts +2 -0
- package/es/pro/Login2.0/Register2.js +9 -1
- package/lib/components/HDLKiosk/app/ScreenRenderer.js +7 -7
- package/lib/components/HDLKiosk/app/views.js +2 -1
- package/lib/components/HDLKiosk/app/workflow/barcode.js +11 -34
- package/lib/components/HDLKiosk/app/workflow/{guards.js → predicates.js} +9 -9
- package/lib/components/HDLKiosk/app/workflow/quick.js +3 -6
- package/lib/components/HDLKiosk/app/workflow/registry.js +1 -1
- package/lib/components/HDLKiosk/app/workflow/scale.js +3 -12
- package/lib/components/HDLKiosk/components/shell/HDLKioskBottomBarHost/index.js +12 -10
- package/lib/components/HDLKiosk/components/shell/HDLKioskShell/index.js +1 -0
- package/lib/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +7 -7
- package/lib/components/HDLKiosk/docs/01-/346/240/270/345/277/203/350/277/220/350/241/214/346/234/272/345/210/266.md +47 -40
- package/lib/components/HDLKiosk/docs/03-/344/270/232/345/212/241/346/265/201/347/250/213/344/270/216/344/272/244/344/272/222/350/247/204/345/210/231.md +3 -3
- package/lib/components/HDLKiosk/docs/05-/344/272/244/346/230/223/345/256/211/345/205/250.md +1 -1
- package/lib/components/HDLKiosk/docs/07-/345/220/216/347/273/255/345/267/245/344/275/234.md +1 -1
- package/lib/components/HDLKiosk/docs/README.md +2 -2
- package/lib/components/HDLKiosk/docs//345/256/236/346/226/275/350/277/233/345/272/246.md +3 -3
- package/lib/components/HDLKiosk/docs//346/236/266/346/236/204/350/256/276/350/256/241.md +11 -11
- package/lib/components/HDLKiosk/index.js +0 -1
- package/lib/components/HDLKiosk/kernel/README.md +8 -9
- package/lib/components/HDLKiosk/kernel/application/defineApplication.js +18 -19
- package/lib/components/HDLKiosk/kernel/application/defineSlice.js +8 -14
- package/lib/components/HDLKiosk/kernel/workflow/actor.js +118 -28
- package/lib/components/HDLKiosk/kernel/workflow/compiler.js +35 -41
- package/lib/components/HDLKiosk/kernel/workflow/selectors.js +10 -0
- package/lib/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +10 -9
- package/lib/components/HDLKiosk/runtime/controllers/useHDLKioskCustomerController.js +1 -1
- package/lib/components/HDLKiosk/runtime/createWorkflowSession.js +3 -3
- package/lib/components/HDLKiosk/runtime/domainTransitionRunner.js +9 -9
- package/lib/components/HDLKiosk/runtime/workflowEvent.js +9 -4
- package/lib/components/HDLKiosk/runtime/workflowLifecycle.js +3 -3
- package/lib/components/HDLKiosk/slices/catalog/PrimaryProductPromptDialog/index.js +1 -2
- package/lib/components/HDLKiosk/slices/catalog/Screen/index.js +13 -40
- package/lib/components/HDLKiosk/slices/catalog/Screen/model.js +0 -5
- package/lib/components/HDLKiosk/slices/catalog/slice.js +2 -15
- package/lib/components/HDLKiosk/slices/catalog/view.js +4 -6
- package/lib/components/HDLKiosk/slices/customization/Screen/index.js +9 -11
- package/lib/components/HDLKiosk/slices/customization/operation.js +23 -3
- package/lib/components/HDLKiosk/slices/customization/slice.js +3 -7
- package/lib/components/HDLKiosk/slices/customization/view.js +3 -2
- package/lib/components/HDLKiosk/slices/fulfillment/Screen/index.js +6 -6
- package/lib/components/HDLKiosk/slices/fulfillment/slice.js +4 -7
- package/lib/components/HDLKiosk/slices/identity/Screen/index.js +17 -11
- package/lib/components/HDLKiosk/slices/identity/slice.js +4 -11
- package/lib/components/HDLKiosk/slices/index.js +2 -1
- package/lib/components/HDLKiosk/slices/payment/Screen/index.js +5 -5
- package/lib/components/HDLKiosk/slices/payment/slice.js +6 -7
- package/lib/components/HDLKiosk/slices/primaryProductPrompt/slice.js +14 -0
- package/lib/components/HDLKiosk/slices/primaryProductPrompt/view.js +25 -0
- package/lib/components/HDLKiosk/utils.js +3 -2
- package/lib/components/bigSale/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +16 -1
- package/lib/pro/Login2.0/Login2.d.ts +2 -0
- package/lib/pro/Login2.0/Login2.js +9 -1
- package/lib/pro/Login2.0/Register2.d.ts +2 -0
- package/lib/pro/Login2.0/Register2.js +9 -1
- package/package.json +4 -4
|
@@ -13,11 +13,11 @@ HDLKiosk Core 不是 BigSale 的另一套页面,也不是本地订单 Store。
|
|
|
13
13
|
↓
|
|
14
14
|
Runtime Controller 决定是否启动会话或消费设备事件
|
|
15
15
|
↓
|
|
16
|
-
Screen 内部动作走 Command
|
|
16
|
+
Screen 内部动作走 Command;离开步骤产生 Activity Intent,返回产生 Navigation
|
|
17
17
|
↓
|
|
18
|
-
Workflow Actor 根据
|
|
18
|
+
Workflow Actor 根据 Intent 选择静态 Transition,并按页面历史处理 Back Navigation
|
|
19
19
|
↓
|
|
20
|
-
HDLKiosk Transition Runner
|
|
20
|
+
HDLKiosk Transition Runner 求值本地 Predicate、执行 Exit Command 并解释交易结果
|
|
21
21
|
↓
|
|
22
22
|
Operation Coordinator 安全执行 Screen Command 或 Exit Command
|
|
23
23
|
↓
|
|
@@ -36,7 +36,7 @@ Screen Renderer 根据当前节点与最新 Projection 渲染
|
|
|
36
36
|
| 流程状态 | entry、nodeId、fulfillment 本地选择、session epoch | Workflow Runtime | 只回答“顾客处于哪一步”,不能证明订单已写成功 |
|
|
37
37
|
| 展示输入 | options 选中项、搜索词、Modal 内选择 | Screen | 可以随页面销毁,不得成为结账依据 |
|
|
38
38
|
|
|
39
|
-
核心规则是“流程、命令与事实分离”:Screen Command/Exit Command 发起业务写入;writer receipt 证明本次写入完成了什么;Projection 提供当前 UI 可观察事实;Workflow Definition 只描述 Activity
|
|
39
|
+
核心规则是“流程、命令与事实分离”:Screen Command/Exit Command 发起业务写入;writer receipt 证明本次写入完成了什么;Projection 提供当前 UI 可观察事实;Workflow Definition 只描述 Activity Intent 到目标节点的关系,Back Navigation 不写入 Definition。
|
|
40
40
|
|
|
41
41
|
## 2. 目录与运行时角色
|
|
42
42
|
|
|
@@ -49,10 +49,10 @@ Screen Renderer 根据当前节点与最新 Projection 渲染
|
|
|
49
49
|
| [`app/workflow/`](../app/workflow) | 三入口业务编排与本地策略 | Activity 数组与特殊分支 | Quick/Barcode/Scale Definition | 流程顺序集中在应用目录,不和 Slice 实现混杂 |
|
|
50
50
|
| [`runtime/createWorkflowSession.ts`](../runtime/createWorkflowSession.ts) | 一次点餐会话的组合根 | entry、Host、locale、可选 Lifecycle 扩展 | Kernel 与 HDLKiosk 业务实现的稳定组合 | 避免页面各自创建状态机或副作用执行器 |
|
|
51
51
|
| [`app/workflow/registry.ts`](../app/workflow/registry.ts) | Activity Registry 类型视图 | Application Manifest | 带 HDLKiosk 字面量类型的 Registry | Runtime 不再维护第二份 Activity 清单 |
|
|
52
|
-
| [`kernel/workflow/compiler.ts`](../kernel/workflow/compiler.ts) | Definition 结构校验 | Workflow Definition + 注入的 Registry | 冻结后的可执行 Definition 或 issues | 防止未知
|
|
52
|
+
| [`kernel/workflow/compiler.ts`](../kernel/workflow/compiler.ts) | Definition 结构校验 | Workflow Definition + 注入的 Registry | 冻结后的可执行 Definition 或 issues | 防止未知 Intent、非法跳转和无界循环进入 Actor |
|
|
53
53
|
| [`app/workflow/policy.ts`](../app/workflow/policy.ts) | HDLKiosk 业务策略校验 | 编译后的 Definition | 支付门禁等业务 issues | 结构规则不与具体交易策略混在一起 |
|
|
54
|
-
| [`kernel/workflow/actor.ts`](../kernel/workflow/actor.ts) | 单会话流程执行器 | Activity
|
|
55
|
-
| [`runtime/domainTransitionRunner.ts`](../runtime/domainTransitionRunner.ts) | Activity 离开策略执行器 | Exit Policy、Host Projection、Coordinator | advance/stay/blocked、支付回执 | Actor 不理解
|
|
54
|
+
| [`kernel/workflow/actor.ts`](../kernel/workflow/actor.ts) | 单会话流程执行器 | Activity Intent、Navigation、Definition、Runner | WorkflowSnapshot、Transition | 解决双击、乱序导航和 Exit Command 完成前跳页 |
|
|
55
|
+
| [`runtime/domainTransitionRunner.ts`](../runtime/domainTransitionRunner.ts) | Activity 离开策略执行器 | Exit Policy、Host Projection、Coordinator | advance/stay/blocked、支付回执 | Actor 不理解 Predicate、购物车和支付 Operation |
|
|
56
56
|
| [`runtime/workflowLifecycle.ts`](../runtime/workflowLifecycle.ts) | 已提交状态的业务生命周期出口 | Actor 提交后的 Snapshot | telemetry | 诊断不进入状态机内核 |
|
|
57
57
|
| [`kernel/operations/coordinator.ts`](../kernel/operations/coordinator.ts) | 通用副作用调度器 | Operation request + 应用 Policy | 规范化 OperationResult | 解决重复 intent、排队、超时和旧 Promise;不认识支付 |
|
|
58
58
|
| [`app/operations.ts`](../app/operations.ts) | 共享 Operation 公共入口 | 跨 Slice Operation 与执行策略 | 稳定的统一导出面 | 避免消费者穿透私有实现目录 |
|
|
@@ -172,34 +172,39 @@ Workflow 用有序 Slice 数组描述静态编排。数组顺序是默认主路
|
|
|
172
172
|
{
|
|
173
173
|
id: 'hdl.barcode',
|
|
174
174
|
steps: [
|
|
175
|
+
catalogSlice,
|
|
175
176
|
{
|
|
176
|
-
slice:
|
|
177
|
-
|
|
177
|
+
slice: primaryProductPromptSlice,
|
|
178
|
+
presentation: { mode: 'modal' },
|
|
179
|
+
visitPolicy: 'once',
|
|
180
|
+
on: { omit: 'fulfillment' },
|
|
178
181
|
},
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
+
customizationSlice,
|
|
183
|
+
fulfillmentSlice,
|
|
184
|
+
paymentMethodSlice,
|
|
182
185
|
{ slice: paymentCompletedSlice, end: 'success' },
|
|
183
186
|
],
|
|
184
187
|
}
|
|
185
188
|
```
|
|
186
189
|
|
|
187
|
-
Workflow 作者不会填写 Screen、
|
|
190
|
+
Workflow 作者不会填写 Screen、Predicate、Operation 或 failureTarget;这些由 Slice 引用带入本地可信 Manifest。Compiler 使用的规范化结构只保留 Activity ID、presentation 与目标,不携带 React 组件、Host 方法或订单对象。Quick、Barcode、Scale 的差异只保留在 [`app/workflow/`](../app/workflow)。
|
|
188
191
|
|
|
189
192
|
四个概念必须分开:
|
|
190
193
|
|
|
191
|
-
- **Slice**:开发者定义的完整业务能力,包含 Screen、
|
|
194
|
+
- **Slice**:开发者定义的完整业务能力,包含 Screen、Intent、Command 和退出 Effect;
|
|
192
195
|
- **Activity**:Slice 暴露给 Actor 的无 UI 运行契约,例如 `catalog`;
|
|
193
196
|
- **Step**:Activity 在一条流程中的一次出现,是编排和运行时定位的稳定位置;
|
|
194
197
|
- **Screen**:Activity 的本地 UI 实现,不属于 Workflow Definition。
|
|
195
198
|
|
|
196
199
|
Slice 在一条流程只出现一次时,Step ID 默认就是 Slice ID;同一 Slice 多次出现时,每次必须显式使用 `slice:用途`,例如 `catalog:initial`、`catalog:final`。这样既保持常规流程易读,也不会把复用能力误认为同一个运行位置。
|
|
197
200
|
|
|
198
|
-
Slice
|
|
201
|
+
Slice Intent 使用 `next()`,或在 `intentPrecondition/effect` 上声明 `navigate: 'next'`,决定相邻 Step 的默认移动。不会自动前进的 Intent 只在 Workflow `on` 中指定目标。Back 是独立 Navigation,由 Actor 的实际页面历史解析。没有下一项的 Step 不会被推测为成功,必须声明 `end: 'success' | 'blocked'`。
|
|
199
202
|
|
|
200
|
-
|
|
203
|
+
Predicate 是本地可信的纯真假判断,例如 `cart.notEmpty`;Intent Precondition 表示某个 Intent 提交前必须满足的 Predicate。作者 API 使用 `intentPrecondition(predicateId)`,底层行为记录 `{ kind: 'precondition', predicate }`,编译后存入 `exitPolicy.precondition`。Predicate 只返回 boolean;它与能返回 `allow | bypass | cancel | redirect | defer | block` 的 Navigation Guard 是两套独立能力。
|
|
201
204
|
|
|
202
|
-
|
|
205
|
+
业务 Workflow 当前不启用 Slice `beforeEnter` 守卫。Actor 仍保留全局 `beforeEach` 管线,用于 `sessionExit` 等会话安全策略;它不承载 Identity、Fulfillment 等步骤的业务进入条件。
|
|
206
|
+
|
|
207
|
+
Actor 分别记录已成功向前离开的 Step、每个 Step 已提交的 `complete | omit` Resolution,以及用户实际访问过的页面历史。`complete` 保留页面的 Back 路径;`omit` 表示将该 Step 从当前流程路径中排除,后续遇到时沿已提交的 `omit` transition 继续,也不写入 Back 历史。Modal 不写入页面历史;历史为空时 Back 进入会话退出导航。Workflow occurrence 只有 `visitPolicy: 'always' | 'once'`:`always` 保留已 `complete` Step 的正常访问,`once` 表示任意 Resolution 提交后不再进入且不写入 Back 历史。用户通过 Back 退出一个已解决页面时,其旧 Resolution 失效。导航在执行 Activity Exit Effect 之前完成解析;被 cancel/defer 的导航不会先写 Host。
|
|
203
208
|
|
|
204
209
|
退出流程也是导航目标 `{ kind: 'sessionExit' }`。全局 `beforeEach` 对它返回 `defer({ kind: 'confirmEndOrder' })`,Actor 保存带唯一 `navigationId` 的 `pendingNavigation`。Shell 只根据该 request 展示确认;旧 ID 不能恢复后来的导航。
|
|
205
210
|
|
|
@@ -210,38 +215,38 @@ Step ID 只标识“一次出现”。Runtime 判断加载哪类数据、是否
|
|
|
210
215
|
[`slices/<name>/slice.ts`](../slices) 是本地可信 Slice 定义,每个 Slice 声明:
|
|
211
216
|
|
|
212
217
|
- 业务 Activity 使用哪个 `screen.id`,以及该 Screen 的 `view`;
|
|
213
|
-
-
|
|
218
|
+
- 可以接受哪些 Intent;
|
|
214
219
|
- 支持 page 还是 modal;
|
|
215
|
-
- 某个
|
|
220
|
+
- 某个 Intent 是否需要纯 Predicate 作为 Precondition;
|
|
216
221
|
- 离开 Slice 前是否需要执行 Effect;
|
|
217
222
|
- Screen 内有哪些不会离开节点的 Command;
|
|
218
223
|
- 使用该 Slice 的流程在支付前需要哪些业务事实。
|
|
219
224
|
|
|
220
|
-
例如 `identity.
|
|
225
|
+
例如 `identity.complete` / `identity.omit` 可在携带输入时执行 `identity.sync`,`customization.complete` 执行主商品同步,`paymentMethod.complete` 检查 Host 已确认支付成功。EFTPOS 派发和店员支付授权是 Payment 页面 Command,不作为流程 Intent。`defineKioskApplication` 从 Slice 自动生成 Activity Contract 与 Operation 注册信息;远端定义只看到 `Slice ID + 标准 Intent → target`,不能替换这些安全规则。
|
|
221
226
|
|
|
222
227
|
Slice 的业务定义与 React View 分离:Slice `id` 是 Workflow 和 Actor 使用的业务 Activity;`screen.id` 会被 Compiler 复制为 Workflow Node 的 `screenId`,供 Shell、Controller 和数据加载策略使用;`app/views.ts` 按 Activity 注册本地可信 View。各 Slice 的 `view.tsx` 仍负责 ViewModel 派生与组件装配,但不会进入 `defineSlice` 或 Workflow Definition。
|
|
223
228
|
|
|
224
229
|
| Slice 定义 | 编译/运行期形态 | 唯一用途 |
|
|
225
230
|
| --- | --- | --- |
|
|
226
|
-
| `id` | Node `activity` | 业务状态、
|
|
231
|
+
| `id` | Node `activity` | 业务状态、Intent 和流程编排 |
|
|
227
232
|
| `screen.id` | Node `screenId` | Shell 变体、数据加载和页面类别判断 |
|
|
228
233
|
| `app/views.ts` 中的 Activity 映射 | 仅应用展示层使用 | React UI 渲染 |
|
|
229
234
|
|
|
230
|
-
因此 `id`、`screen` 和 `view` 不互相替代:`
|
|
235
|
+
因此 `id`、`screen` 和 `view` 不互相替代:`catalog` 是商品浏览页面,`primaryProductPrompt` 是覆盖在 Catalog 上的独立 Modal Slice。店员支付授权不改变当前页面,因此是 `paymentMethod` 的 Screen Command,而不是独立 Activity。Application Manifest 是业务运行 Contract 的唯一索引,`app/views.ts` 是展示实现的唯一索引。View 使用同步本地组件引用,不产生运行期 UI chunk;纯 Compiler/Actor 依赖链也不会加载该注册表。Renderer 若发现 View 缺失,仍使用 `BlockingNotice` fail closed,且不会改变 Workflow。
|
|
231
236
|
|
|
232
237
|
`slices/index.ts` 对整套 Slice 清单执行一次类型约束:`screen.id` 只能取 HDLKiosk 的非 Entry `ScreenId`。`app/views.ts` 使用完整的 `Record<ActivityId, HDLKioskSliceView>`,因此新增 Activity 若没有注册 View 会产生类型错误。Kernel 的 `defineSlice` 同时拒绝空 `screen.id`。
|
|
233
238
|
|
|
234
239
|
Compiler 从实际使用的 Slice 汇总 Checkout Requirement ID,但不把判断函数或 Host 字段写入 Workflow。当前 HDLKiosk 默认注册表为空;未来增加其他业务要求时仍由 [`checkoutRequirements.ts`](../app/operations/checkoutRequirements.ts) 集中判断。
|
|
235
240
|
|
|
236
|
-
`WorkflowEvent` 同时约束 Activity、
|
|
241
|
+
`WorkflowEvent` 同时约束 Activity、Intent 和必要输入:例如 `identity.omit` 必须携带身份输入,`fulfillment.complete` 必须携带 fulfillment context patch。Back 使用不携带业务输入的 `NAVIGATE_BACK`。Barcode 入口的条码输入不是 Workflow Event,而是 Session Controller 在发布会话前交给 `barcode.consume` 的 Command 输入。Screen Command 由 `OperationInputMap` 约束 Command ID 与输入类型。
|
|
237
242
|
|
|
238
243
|
Step 可选声明 Modal 展示关系:
|
|
239
244
|
|
|
240
245
|
```ts
|
|
241
|
-
presentation: { mode: 'modal'
|
|
246
|
+
presentation: { mode: 'modal' }
|
|
242
247
|
```
|
|
243
248
|
|
|
244
|
-
这只控制渲染层级。Compiler
|
|
249
|
+
这只控制渲染层级。Actor Surface 保留进入 Modal 前实际展示的 Page,Workflow 不指定背景 Step。Compiler 会拒绝以 Modal 作为首节点或 Modal 直接进入另一个 Modal。
|
|
245
250
|
|
|
246
251
|
### 6.3 结构 Compiler 与业务 Policy 校验
|
|
247
252
|
|
|
@@ -249,7 +254,7 @@ presentation: { mode: 'modal', base: 'customization' }
|
|
|
249
254
|
|
|
250
255
|
- workflow id、Step 数组及数量/transition 上限;
|
|
251
256
|
- Activity 是否存在;重复 Activity 是否具有稳定且不冲突的 Step ID;
|
|
252
|
-
-
|
|
257
|
+
- Intent 是否由 Activity Contract 暴露;
|
|
253
258
|
- Definition 是否夹带 screen/guard/operation/failureTarget;
|
|
254
259
|
- 默认相邻导航和显式 target 是否有效,所有 Step 是否从首项可达;
|
|
255
260
|
- 无后继 Step 是否声明结束语义,首项是否能到达成功终点;
|
|
@@ -264,16 +269,16 @@ session 创建前完成,但结构错误与业务安全错误保持独立。
|
|
|
264
269
|
|
|
265
270
|
数组天然只有一个首项;不可达 Step 会被拒绝。显式业务回路必须声明 `repeat.max`,Actor 按会话计数,超过限制进入阻塞态。普通 Back 属于用户导航,不计入业务回路限制。校验通过后,Compiler 把数组转成 Actor 查询高效的冻结节点 Map;运行中的 session 不替换 Definition。
|
|
266
271
|
|
|
267
|
-
### 6.4 Actor 如何处理 Activity
|
|
272
|
+
### 6.4 Actor 如何处理 Activity Intent 与 Navigation
|
|
268
273
|
|
|
269
274
|
Actor 使用单 mailbox:
|
|
270
275
|
|
|
271
276
|
1. 忽略 success end、blocked 或 disposed 后的新事件;
|
|
272
277
|
2. 同时校验事件的 `stepId` 和 `activityId`,拒绝旧 Screen 或同类 Activity 旧实例的迟到回调;
|
|
273
|
-
3. 校验
|
|
278
|
+
3. 校验 Intent 是否由当前 Activity 暴露,并查找 Transition;Back 则读取页面历史;
|
|
274
279
|
4. 通过注入的 Context Reducer 合并受限 contextPatch;
|
|
275
280
|
5. 把 Exit Policy 交给 HDLKiosk Transition Runner;
|
|
276
|
-
6. Runner 用最新 Sales Projection
|
|
281
|
+
6. Runner 用最新 Sales Projection 求值 Precondition Predicate,并通过 Coordinator 执行 Exit Command;
|
|
277
282
|
7. Runner 将结果归一为 `advance / stay / blocked`,支付成功回执也由 Runner 解释;
|
|
278
283
|
8. Actor 只负责提交 target、当前 Step 或 blocked,并清理 active operation;
|
|
279
284
|
9. 已提交 Snapshot 交给 Workflow Lifecycle 记录 telemetry;外层可组合额外观察器。
|
|
@@ -301,7 +306,7 @@ Coordinator 现在有两个明确调用来源:
|
|
|
301
306
|
- Screen Command:Catalog 加购、Cart Overlay 数量修改、称重草稿同步等;由 `session.executeCommand` 执行,成功后 Actor 节点不变;
|
|
302
307
|
- Activity Exit Command:Guest 同步、Checkout Prepare/Open 等;由 Transition Runner 从本地 Exit Policy 发起,只有 succeeded 才允许 Actor 转移。
|
|
303
308
|
|
|
304
|
-
partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed
|
|
309
|
+
partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed;它不伪造 Activity Intent。
|
|
305
310
|
|
|
306
311
|
当前 Operation 组:
|
|
307
312
|
|
|
@@ -313,6 +318,7 @@ partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入
|
|
|
313
318
|
| `cart.addProduct` | cart | — | 调宿主商品 Flow,等待规格或其它业务选择后验证写入 |
|
|
314
319
|
| `cart.update/remove` | cart | 15s | 数量修改/删除、summary 重算和目标行验证 |
|
|
315
320
|
| `product.syncPrimaryWeightedProduct` | cart | — | 首次写入可能等待 OS 额外业务弹窗;更新或写入后验证重量/options 和写后证据 |
|
|
321
|
+
| `product.removePrimaryProduct` | cart | 15s | 将主商品置为不存在;本来不存在时直接成功,存在时验证删除结果 |
|
|
316
322
|
| `checkout.prepare*` | checkout | 20s | fulfillment、cart、summary 稳定性与 token |
|
|
317
323
|
| `checkout.open` | checkout | 120s | 消费 token、冻结危险写并打开 Host 支付 |
|
|
318
324
|
|
|
@@ -389,22 +395,22 @@ Host command
|
|
|
389
395
|
- 尚无活跃或启动中的 session;
|
|
390
396
|
- 支付状态未冻结。
|
|
391
397
|
|
|
392
|
-
通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor
|
|
398
|
+
通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor 发送流程 Intent;Barcode 把条码作为 start request 的一部分,Scale 只发起 Scale session。
|
|
393
399
|
|
|
394
400
|
### 8.3 Customer Controller
|
|
395
401
|
|
|
396
|
-
Identity 节点内的登录是 Activity
|
|
402
|
+
Identity 节点内的登录是 Activity Intent,不是 Screen 直接写订单:
|
|
397
403
|
|
|
398
404
|
```text
|
|
399
405
|
BigSale Login2/Register2
|
|
400
406
|
→ 暂存已认证 customer
|
|
401
|
-
→ identity.
|
|
407
|
+
→ identity.complete(customerId)
|
|
402
408
|
→ identity.sync Exit Effect
|
|
403
409
|
→ Adapter customer.select + consistency + repricing
|
|
404
410
|
→ 成功后进入下一节点
|
|
405
411
|
```
|
|
406
412
|
|
|
407
|
-
认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice
|
|
413
|
+
认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice 的 `complete` 在携带输入时执行 `identity.sync`;已登录客户的自动略过使用 `omit` 且不携带输入,因此不会重复写客户。
|
|
408
414
|
|
|
409
415
|
Shell 登录不是 Workflow 跳转,而是 session-level 插入 Operation:
|
|
410
416
|
|
|
@@ -421,7 +427,7 @@ Shell Login
|
|
|
421
427
|
|
|
422
428
|
### 8.4 Screen Data Controller
|
|
423
429
|
|
|
424
|
-
只在相关节点加载 Catalog、Weighted Product 或 Customization DTO;使用 AbortController 防止离开页面后的结果写回。Modal
|
|
430
|
+
只在相关节点加载 Catalog、Weighted Product 或 Customization DTO;使用 AbortController 防止离开页面后的结果写回。Modal 激活时,Screen Data Controller 继续使用 Actor Surface 中保留的 `pageStepId`,底层 Page 保持挂载和原数据,不按 Modal Activity 清空或重新加载。
|
|
425
431
|
|
|
426
432
|
### 8.5 返回 Entry 能力接口
|
|
427
433
|
|
|
@@ -475,8 +481,9 @@ HDLKiosk root
|
|
|
475
481
|
→ components/shell + components/overlays + components/common
|
|
476
482
|
→ Activity Action callback
|
|
477
483
|
→ HDLKioskWorkflowView
|
|
478
|
-
├─
|
|
479
|
-
|
|
484
|
+
├─ Intent → Workflow Actor
|
|
485
|
+
├─ Back → Workflow Navigation
|
|
486
|
+
│ → HDLKiosk Transition Runner(Predicate / Exit Command)
|
|
480
487
|
├─ Command → session.executeCommand → Coordinator
|
|
481
488
|
└─ Session Action → reset controller
|
|
482
489
|
```
|
|
@@ -499,7 +506,7 @@ Screen 只做三件事:
|
|
|
499
506
|
|
|
500
507
|
1. 渲染 View DTO;
|
|
501
508
|
2. 维护局部输入;
|
|
502
|
-
3. 内部业务写入发送 Screen Command,离开步骤发送 Activity
|
|
509
|
+
3. 内部业务写入发送 Screen Command,离开步骤发送 Activity Intent,返回发送 Navigation。
|
|
503
510
|
|
|
504
511
|
Screen 不得:
|
|
505
512
|
|
|
@@ -515,13 +522,13 @@ CartDrawer 也遵循“Screen 声明、Shell 挂载”:Screen 只请求打开
|
|
|
515
522
|
|
|
516
523
|
Customization 的规格 UI 通过 `components/common/HDLKioskSkuOptionsSelection` 接入共享 `skuOptionsSelection`。查询所得 ProductData 本身直接作为 `dataSource`,兼容层不再复制或重命名 option group/item,只在选择器 `SkuValue` 与业务命令所需 `HDLProductOptionSelection[]` 之间转换“用户选中结果”。临时独立运行缺少 Engine 上下文时,退化为直接读取同一 `option_group/option_item` 的单选控件。
|
|
517
524
|
|
|
518
|
-
称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.
|
|
525
|
+
称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.complete` 的 Exit Effect 交给 `product.syncPrimaryWeightedProduct`。OS 完成其内部业务判断并返回加购成功,且 writer receipt/Projection 验证通过后,Actor 才进入下一节点。失败或取消留在当前页面,`partial/unknown` 阻断。底栏和 Drawer 仍只消费 Host `summary.due/cart.lines`,不在本地构造未确认的购物车。
|
|
519
526
|
|
|
520
527
|
Workflow View 负责控制器组合、自动推进和会话级 Dialog;Renderer 只负责选择要展示的组件。普通 `cart.add/update/remove` 和防抖的 `product.syncPrimaryWeightedProduct` 是 Screen Command,保持当前 Activity 挂载,只禁用冲突操作,不产生 transition;会员重报价和 Checkout 等 Activity Exit Command 才阻断整个 surface。
|
|
521
528
|
|
|
522
529
|
OS 加购过程中的内部弹窗和业务判断不属于 HDLKiosk Activity、Host Contract 或 Checkout Requirement。HDLKiosk 不读写相关业务事实,只根据主商品加购操作的最终结果决定是否跳转。
|
|
523
530
|
|
|
524
|
-
Barcode
|
|
531
|
+
Barcode 的主商品询问是独立 `primaryProductPrompt` Modal Slice:Yes 发出 `complete`,No 发出 `omit`。Prompt 声明 `visitPolicy: 'once'`,因此任一选择成功提交后不再询问;关闭 Modal 只执行 Back,没有 Resolution,下次仍会展示。Customization 使用缺省 `always`:`complete` 后保留 Back 编辑能力;`omit` 会先通过本地 Exit Effect 删除已有主商品,再将该 Step 从后续路径和 Back 历史中排除。
|
|
525
532
|
|
|
526
533
|
## 11. 四条端到端调用链
|
|
527
534
|
|
|
@@ -26,7 +26,7 @@ Entry → Identity → Customization → Catalog → Fulfillment
|
|
|
26
26
|
→ Payment Method / Payment Completed → Entry
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
-
|
|
29
|
+
- Quick 的 Identity occurrence 显式声明 `visitPolicy: 'once'`。登录或 Omit 成功后,该 Step 在本会话不再进入,也不写入 Back 页面历史。当没有更早的已访问页面时,Back 的导航目标为 `sessionExit`;全局会话策略返回 `defer(confirmEndOrder)` 并打开结束订单确认。确认后返回 Entry,取消则留在当前页。
|
|
30
30
|
- Fulfillment 统一位于 Payment 之前确认堂食/外带。
|
|
31
31
|
- Customization 只在稳定正净重且必选 options 完整时启用 Next。
|
|
32
32
|
- “I don't need Malatang” 直接跳过主商品并进入 Catalog;如果正在编辑已确认主商品,则先移除该行。
|
|
@@ -67,7 +67,7 @@ Entry 消费第一个有效重量边沿后启动以 Fulfillment 为首节点的
|
|
|
67
67
|
|
|
68
68
|
## 6. 会员中途插入
|
|
69
69
|
|
|
70
|
-
Identity 的
|
|
70
|
+
Identity 的 Omit 只表示当前以 Guest 继续。Shell 可在本地策略允许的页面执行 `identity.sync`。登录、Customer 写入、重报价和 Projection 验证成功后仍留在原节点;`partial`/`unknown` 不得带着未确认价格继续。
|
|
71
71
|
|
|
72
72
|
## 7. UI 和修改规则
|
|
73
73
|
|
|
@@ -76,6 +76,6 @@ Identity 的 Skip 只表示当前以 Guest 继续。Shell 可在本地策略允
|
|
|
76
76
|
- 尺寸校准以上线环境的实际渲染值反推:当 72px 在目标倍率下实测为 100px,而原型目标为 108px 时,当前自有视觉值统一乘以 `108 / 100 = 1.08`;外部生产组件不参与这一次修正。
|
|
77
77
|
- Screen 不得直接 import BigSale/SalesSDK,也不本地计算最终金额。
|
|
78
78
|
- Screen 内部写操作使用 Screen Command;离开节点的写操作使用 Activity Exit Effect。
|
|
79
|
-
- `product.syncPrimaryWeightedProduct` 属于 Customization `
|
|
79
|
+
- `product.syncPrimaryWeightedProduct` 属于 Customization `complete` 的 Exit Effect,不是输入变化时的 Screen Command。
|
|
80
80
|
- Shell 的购物车、金额和 Drawer 只消费 Host 已确认 Projection。
|
|
81
81
|
- 调整节点顺序时同步更新 Workflow Definition、compiler/definition/actor/Screen 测试和实施进度。
|
|
@@ -31,7 +31,7 @@ Host 无法证明写后事实时,Operation 不能返回 succeeded。Cart 和 C
|
|
|
31
31
|
|
|
32
32
|
## 3. Checkout Prepared Freshness
|
|
33
33
|
|
|
34
|
-
Prepare 不是某个页面之前或某个 Workflow
|
|
34
|
+
Prepare 不是某个页面之前或某个 Workflow Intent 的固定副作用。Checkout Coordinator 把它作为所有支付 dispatch 的前置条件,对当前事实版本执行 `ensurePrepared(currentVersion)`。
|
|
35
35
|
|
|
36
36
|
事实版本至少包括 session epoch、cart fingerprint、customer/voucher/promotion revision、Summary 应付金额/币种、Requirement 集合和支付能力上下文。状态为:
|
|
37
37
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
### P1-1 商品和 Catalog
|
|
36
36
|
|
|
37
37
|
- 将模板内临时固定商品 ID 改为租户/门店正式称重商品配置;商品结构已经切换为 OS `queryProducts`,不得恢复本地 fallback;
|
|
38
|
-
- 确认 Quick
|
|
38
|
+
- 确认 Quick 是否允许排除称重主商品;若不允许,从 `customization` Activity Contract/Workflow 中移除 `omit` Intent;
|
|
39
39
|
- 大目录分页/虚拟滚动与分类切换性能;分类、子分类和本地搜索首版已完成;
|
|
40
40
|
- 缺货、下架、菜单不可用、价格变化;
|
|
41
41
|
- Barcode 细分 added/requiresSelection/notFound/cancelled;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
1. [架构设计.md](./架构设计.md):理解独立物料边界、依赖方向和全局不变量;
|
|
11
11
|
2. [00-文件导航.md](./00-文件导航.md):快速查找每个目录和关键文件的角色、调用方与修改落点;
|
|
12
|
-
3. [01-核心运行机制.md](./01-核心运行机制.md):沿 Activity Contract、
|
|
12
|
+
3. [01-核心运行机制.md](./01-核心运行机制.md):沿 Activity Contract、Intent/Navigation Actor、Command Coordinator、Controller 和 Screen 的真实调用链理解 Core;
|
|
13
13
|
4. [02-Host 适配与业务数据.md](./02-Host适配与业务数据.md):理解 BigSale 兼容层每个文件解决的问题、字段映射和当前缺口;
|
|
14
14
|
5. [实施进度.md](./实施进度.md):确认当前已完成能力、临时策略和已知风险;
|
|
15
15
|
6. [03-业务流程与交互规则.md](./03-业务流程与交互规则.md):理解三入口和当前权威流程;
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
| ------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------- |
|
|
32
32
|
| [00-文件导航.md](./00-文件导航.md) | 目录职责、关键文件角色、调用关系和常见修改落点 | 详细业务顺序和字段定义 |
|
|
33
33
|
| [架构设计.md](./架构设计.md) | 边界、总图、状态所有权、不变量 | 实施流水账 |
|
|
34
|
-
| [01-核心运行机制.md](./01-核心运行机制.md) | Core 代码地图、Activity/
|
|
34
|
+
| [01-核心运行机制.md](./01-核心运行机制.md) | Core 代码地图、Activity/Intent/Navigation/Command/Actor/Controller 调用链、修改落点 | BigSale 原始字段 |
|
|
35
35
|
| [02-Host 适配与业务数据.md](./02-Host适配与业务数据.md) | BigSale 兼容层代码地图、痛点与形式、Projection/Command/商品/设备映射、真实缺口 | Screen 导航 |
|
|
36
36
|
| [03-业务流程与交互规则.md](./03-业务流程与交互规则.md) | 三入口、会员、主商品提交、页面行为、UI 基线 | OS 内部实现 |
|
|
37
37
|
| [04-设备与称重.md](./04-设备与称重.md) | Scanner、任意页面扫码方案、Scale、事件仲裁和称重商品 | 支付实现细节 |
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
| Scale | 待实机 | 顶层 Runtime、自动入口;有效重量先进入 Fulfillment,再进入与 Quick 共用的 Customization,本地草稿在 Next 时提交 | 真秤全矩阵、正式商品 |
|
|
37
37
|
| Catalog | 待实机 | HDLKiosk 模板驱动现有 retail Product Provider 正式 load 后读取快照;分类/搜索/商品卡、连续加购、订单悬浮栏及购物车 Drawer 已完成 | 大目录虚拟化、复杂规格接管、实机视觉 |
|
|
38
38
|
| Cart Overlay | 待业务/实机 | 已替代独立 Review/Voucher Slice;复用全局 BottomBar,展示 Host 商品、数量、卡券入口、小计和备注能力 | 称重/定制重新编辑、价格变化反馈、促销/税/节省明细 |
|
|
39
|
-
| Identity 登录 | 待实机 | BigSale 内注册 H5 Login2/Register2;认证结果经
|
|
39
|
+
| Identity 登录 | 待实机 | BigSale 内注册 H5 Login2/Register2;认证结果经 complete 进入 identity.sync,客户写入、订单一致性和重报价统一由 Adapter/Operation 完成 | 登录/注册实机交互、错误文案与客户搜索结果验收 |
|
|
40
40
|
| 会员中途登录 | 待 Host | Activity 级本地入口策略、customer/cart 互斥、统一 Screen Command、重新计价验证;partial/unknown 阻塞 Workflow | 将同一 H5 认证能力接入 Shell/Cart Overlay 入口 |
|
|
41
41
|
| Fulfillment | 待 Host | Workflow Context 已接;支付 dispatch 前的 `ensurePrepared` 统一写入并验证履约方式 | 当前仍允许临时模拟 |
|
|
42
42
|
| Voucher | 已并入 Overlay | 不再是 Workflow Slice;由 Cart Overlay 展示并操作当前用户卡券 | 真实券列表、应用、撤销和 quote |
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
- Customization 将称重/options 保持为页面本地草稿,输入变化不写 OS;Next 才通过 `addProductWithFlow` 提交,加购成功后前进;底栏和 Drawer 统一读取已确认的 `summary.due/cart.lines`;
|
|
74
74
|
- Customization 通过本物料内的 `HDLKioskSkuOptionsSelection` 将原 ProductData 直接作为共享 `skuOptionsSelection` 的 dataSource,只转换选中结果;独立运行缺少 Engine 上下文时直接读取同一 option 结构兜底;
|
|
75
75
|
- HDLKiosk 已删除主商品额外业务弹窗相关的 Activity、Host Contract、Projection 和 Checkout Requirement;是否展示额外弹窗由 OS 加购流内部决定;
|
|
76
|
-
-
|
|
76
|
+
- Barcode 主商品询问使用独立 Modal Slice;拒绝或 Customization 跳过后直接进入 Fulfillment,Back 按真实页面历史返回 Catalog,再次前进沿用已提交的标准结果;
|
|
77
77
|
- Cart Mutation 以 Host consistency barrier 为权威,不再被晚一帧提交的 `consistencyPending` 展示态误判为 unknown;
|
|
78
78
|
- 普通 Catalog/Cart Overlay 写操作不显示全屏遮罩;Catalog 批量锁定卡片时保持原视觉,减少短操作闪烁;
|
|
79
79
|
- Scale Hook 在顶层单一运行,Entry 和 Customization 详情消费同一快照;Scale workflow 从 `fulfillment` 开始,再复用 `customization` 页面分支;
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
- Result 取餐号只读取当前订单或匹配 `salesDetail.header` 的 `shop_full_order_number`;支付成功后 Projection 仍缺号时按已确认 `orderId` 回查订单详情,始终不从支付结果猜测字段,也不以其它订单编号降级;
|
|
112
112
|
- Barcode 当前使用“激活 Session → 消费首码 → Catalog → 缺主商品时询问 → Yes 时 Fulfillment → 称重详情 → Next/OS 加购成功 → Catalog”的路径;No 保持在 Catalog 且不要求 Fulfillment,Cart Overlay 完成订单复核与卡券操作;
|
|
113
113
|
- 移除遗留 `hdl-shell__footer` 与 `resolveScreenBottomBar`:Fulfillment、Customization、Catalog 改为声明实时 BottomBar,由 Shell 内唯一 `HDLKioskBottomBarHost` 渲染。BottomBar 当前区分 `visible / masked / hidden`:退出确认及 Catalog 主商品询问等普通 Modal 保留底栏并由遮罩阻止交互,同时自动收起 CartDrawer;只有等待员工、支付未决和 blocked 等不可导航状态才隐藏。购物车图标已接入基于 `CompensatedPisellContainer` 的底部 Drawer,展示 Host 已确认的商品、定制摘要、数量和金额,并在三个 Catalog workflow 中复用既有改数量/删除 Operation;
|
|
114
|
-
- Workflow 使用线性优先的 Slice 数组:数组表达主路径,Slice
|
|
114
|
+
- Workflow 使用线性优先的 Slice 数组:数组表达主路径,Slice Intent 通过 `next()` 或 `navigate: 'next'` 补齐默认前进,`on` 只描述分支、合流和跳跃;Back 是独立 Navigation,并按实际页面历史解析。同一 Slice 可用 `slice:用途` 多次出现,回路必须声明有限 repeat,Actor 超限即阻塞;Intent 同时携带 `stepId/activityId`,避免同类 Activity 的迟到实例误触发当前 Step。支付不确定是 Actor blocked 状态,不作为业务节点;
|
|
115
115
|
- `slices/<name>/slice.ts` 是各业务能力唯一导出的 `defineSlice` 结果:Slice `id` 标识业务 Activity,`screen.id` 标识页面类别;`view.tsx` 就地完成该 Slice 的 ViewModel 派生和组件装配,`app/views.ts` 单独维护完整的 Activity → View 本地注册表,`ScreenRenderer` 进行同步解析。Kernel、Slice 和 Workflow 不依赖 React View;公共类型与跨 Slice 纯方法分别集中在根 `types.ts`、`utils.ts`;
|
|
116
116
|
- Kernel 已不依赖 HDLKiosk contracts:结构 Compiler、Actor 和通用 Coordinator 位于 `kernel/`;`app/operations.ts` 是共享业务副作用的公共入口,私有实现位于 `app/operations/`,其中 Coordinator 注入 HDLKioskHost、冲突矩阵、支付冻结、错误分类和遥测。Slice 专属 Operation 与对应 Slice 同置;
|
|
117
117
|
- HDLKiosk 阻断式 Dialog 已统一到 `HDLKioskModal → PisellModal`,默认层级高于 CartDrawer;BottomBar 的三态解析、组件依赖边界、Checkout 安全及现有 Dialog/Screen 定向用例通过,HDLKiosk 汇总 Less 编译、变更组件 esbuild 语法转换和 `git diff --check` 通过;
|
|
@@ -71,7 +71,7 @@ flowchart TB
|
|
|
71
71
|
HOST --> OPS
|
|
72
72
|
PROJ --> CONTROLLER
|
|
73
73
|
PROJ --> VIEW --> UI
|
|
74
|
-
UI -->|"Activity
|
|
74
|
+
UI -->|"Activity Intent / Navigation"| VIEW
|
|
75
75
|
VIEW --> ACTOR
|
|
76
76
|
UI -->|"Screen Command"| VIEW
|
|
77
77
|
VIEW --> COORD
|
|
@@ -106,20 +106,20 @@ Kernel ✕ HDLKiosk contracts / BigSale / SalesSDK / merchant workflows / Screen
|
|
|
106
106
|
| ----------------------- | -------------------------------------------------------------------- | --------------------------------------------- |
|
|
107
107
|
| Contracts/Ports | HDLKiosk 自有 DTO、能力和结果语义 | BigSale 字段兼容 |
|
|
108
108
|
| Projection | 标准化 Host 可观察事实 | 发业务命令 |
|
|
109
|
-
| Slice | 将 Screen、合法
|
|
109
|
+
| Slice | 将 Screen、合法 Intent、局部 Command、退出 Effect 和 Requirement 收敛为业务能力 | 前后节点和 Host 实现 |
|
|
110
110
|
| Application Manifest | 汇总 Slice、Workflow 和 Operation 引用并生成可信注册表 | React 生命周期和订单事实 |
|
|
111
111
|
| Structural Compiler | Step ID、相邻导航、可达性、终点、Modal 和有界循环 | 支付和购物车策略 |
|
|
112
112
|
| HDLKiosk Policy | 编译后检查支付来源和 Checkout Prepare 等交易约束 | 修改节点结构 |
|
|
113
|
-
| Workflow Actor | mailbox、step/activity 防迟到、running/blocked、节点提交和循环计数 | Host、
|
|
114
|
-
| Transition Runner |
|
|
113
|
+
| Workflow Actor | mailbox、step/activity 防迟到、running/blocked、节点提交和循环计数 | Host、Predicate、Operation、支付解释和持久化 |
|
|
114
|
+
| Transition Runner | 求值 Predicate/执行 Exit Command,将结果归一为 advance/stay/blocked | React、节点数组位置 |
|
|
115
115
|
| Operations | 封装业务 Command 和写后验证 | Screen 导航 |
|
|
116
116
|
| Requirement Registry | 判断支付前业务要求对当前订单是否适用,并校验 Host Projection | Workflow 节点编排和 UI |
|
|
117
117
|
| Kernel Coordinator | 用应用注入的 Context/Scope/Policy 处理冲突、去重、timeout 和 epoch | 支付含义、Host、业务错误和节点目标 |
|
|
118
118
|
| HDLKiosk Operation Policy | 注入 Host Context、冲突矩阵、支付 freeze、错误分类和遥测 | Screen 导航 |
|
|
119
119
|
| Workflow | 用有序 Slice 数组表达主路径,以 `on` 描述分支、合流和受限回路 | Screen 内部动作和订单事实 |
|
|
120
120
|
| Controllers | session/device/customer 生命周期;提供验证式 `returnToEntry` | OS 字段转换、决定自动返回时机 |
|
|
121
|
-
| View assembly | 把 Activity 节点和 Projection 转换为组件输入,并路由
|
|
122
|
-
| Screens/Dialogs/Layouts | 展示、局部输入、Screen Command 和
|
|
121
|
+
| View assembly | 把 Activity 节点和 Projection 转换为组件输入,并路由 Intent/Navigation/Command | 决定目标节点 |
|
|
122
|
+
| Screens/Dialogs/Layouts | 展示、局部输入、Screen Command、Activity Intent 和 Back Navigation | 直接调用 Host、Actor 或 Controller |
|
|
123
123
|
| BigSale Adapter | DTO 转换、callback、插件、capability | 决定流程节点 |
|
|
124
124
|
|
|
125
125
|
## 5. Host 契约
|
|
@@ -157,9 +157,9 @@ Screen:输入和展示状态
|
|
|
157
157
|
|
|
158
158
|
1. Core 不 import BigSale、SalesSDK、Engine plugin 或 raw tempOrder。
|
|
159
159
|
2. Adapter 不决定 Workflow 目标节点。
|
|
160
|
-
3. Screen 不直接调用 Host;内部写操作发送 Command,离开步骤发送 Activity
|
|
161
|
-
4. Slice 是 Screen、
|
|
162
|
-
5. Workflow 作者只排列已注册 Slice;规范化后的运行结构不得包含 Screen、
|
|
160
|
+
3. Screen 不直接调用 Host;内部写操作发送 Command,离开步骤发送 Activity Intent,返回发送 Navigation。
|
|
161
|
+
4. Slice 是 Screen、Intent、Command 和退出策略的唯一业务定义;Activity Registry 和 Operation Registry 从 Application Manifest 派生,不维护平行清单。
|
|
162
|
+
5. Workflow 作者只排列已注册 Slice;规范化后的运行结构不得包含 Screen、Predicate、Operation 或 failureTarget。重复 Slice 必须使用 `slice:用途` 标识每次出现。
|
|
163
163
|
6. 只有可被业务编排者独立排序或分支的里程碑才成为节点;Dialog 内部询问、选择和等待阶段留在 Activity 内。
|
|
164
164
|
7. Operation 不渲染 UI,不直接切 Screen。
|
|
165
165
|
8. Cart/Customer/Summary/Payment 以 Host Projection 为权威。
|
|
@@ -186,7 +186,7 @@ Scale ───────┘
|
|
|
186
186
|
|
|
187
187
|
入口定义不同顺序,复用 Identity、Fulfillment、Customization、Catalog、Cart、Checkout 等 Screen/Operation。会员中途登录是共享 session 行为,不复制到每个 Workflow 节点。
|
|
188
188
|
|
|
189
|
-
Barcode/Scale 是外部 ingress,不放入 Step 数组。Barcode 首码先归属并激活 Session,在 Session `readyForCommands && cartWritable` 后通过 `barcode.consume` 完成;首屏类型由 Workflow Definition 决定,Scanner 不依赖 Catalog。Scale 当前从 Fulfillment
|
|
189
|
+
Barcode/Scale 是外部 ingress,不放入 Step 数组。Barcode 首码先归属并激活 Session,在 Session `readyForCommands && cartWritable` 后通过 `barcode.consume` 完成;首屏类型由 Workflow Definition 决定,Scanner 不依赖 Catalog。Scale 当前从 Fulfillment 开始。业务 Step 当前不启用本地 `beforeEnter`;Identity 通过 Workflow 中可见的 `visitPolicy: 'once'` 表达会话内只进入一次。Actor 的全局 `beforeEach` 管线仅承载会话安全策略。退出确认由其返回 `defer`,Shell 只消费 `pendingNavigation.request`,不在 View 或 Kernel 中判断具体 Activity 名称。
|
|
190
190
|
|
|
191
191
|
权威节点顺序见 [03-业务流程与交互规则.md](./03-业务流程与交互规则.md)。
|
|
192
192
|
|
|
@@ -222,7 +222,7 @@ HDLKiosk 无法独立保证:
|
|
|
222
222
|
```text
|
|
223
223
|
components/HDLKiosk/
|
|
224
224
|
├── app/ # 应用清单、三入口流程、共享 Operation 入口和节点渲染装配
|
|
225
|
-
│ ├── workflow/ # Quick / Barcode / Scale + registry / guards / policy
|
|
225
|
+
│ ├── workflow/ # Quick / Barcode / Scale + registry / predicates / navigation guards / policy
|
|
226
226
|
│ ├── operations.ts # 跨 Slice Operation 的唯一公共导入面
|
|
227
227
|
│ ├── operations/ # 公共入口背后的私有实现与测试
|
|
228
228
|
│ └── entry/ # Workflow 外三入口页面
|
|
@@ -10,10 +10,9 @@
|
|
|
10
10
|
const identity = defineSlice({
|
|
11
11
|
id: 'identity',
|
|
12
12
|
screen: { id: 'identity' },
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
back: back(),
|
|
13
|
+
intents: {
|
|
14
|
+
complete: effect(identifyCustomer, { navigate: 'next' }),
|
|
15
|
+
omit: next(),
|
|
17
16
|
},
|
|
18
17
|
commands: {
|
|
19
18
|
requestSmsCode,
|
|
@@ -40,11 +39,11 @@ const views = {
|
|
|
40
39
|
```
|
|
41
40
|
|
|
42
41
|
- `defineSlice`:把业务 Activity、Screen 描述、语义出口、Screen 内 Command 和离开时 Effect 放在一起;
|
|
43
|
-
- `next()
|
|
42
|
+
- `next()`:声明默认前进;Back 由独立 Navigation 和页面历史处理;
|
|
44
43
|
- `defineWorkflow`:数组只表达 Slice 顺序,`on` 只处理跳跃、分支、合流和有界回路;
|
|
45
44
|
- `defineKioskApplication`:汇总 Slice、Workflow 与应用级 Operation,并自动生成 Activity/Operation Manifest。
|
|
46
45
|
|
|
47
|
-
Slice 的 `
|
|
46
|
+
Slice 的 `intents` 是“业务活动请求流程执行的意图”,不是按钮或 DOM 事件。Back 使用独立 Navigation;不会离开 Screen 的动作放进 `commands`。支付门禁、设备监听、购物车写入验证等商家规则不进入 Kernel。
|
|
48
47
|
|
|
49
48
|
三个标识的职责固定如下:Slice `id` 是 Workflow 引用的业务 Activity;`screen.id` 是 Shell、数据加载和展示策略使用的页面类别;React View 由应用层按 Activity 单独注册。不导航的业务操作声明为当前 Slice 的 Command,例如 `paymentMethod` 内的店员支付授权。
|
|
50
49
|
|
|
@@ -54,8 +53,8 @@ Compiler 只把 `screen.id` 复制为节点的 `screenId`。Renderer 再按节
|
|
|
54
53
|
|
|
55
54
|
| 目录 | 负责 | 由应用注入 |
|
|
56
55
|
| --- | --- | --- |
|
|
57
|
-
| `application/` | Slice/Workflow/Application 作者 API、Manifest 生成 | Screen 描述、
|
|
58
|
-
| `workflow/` | 结构编译、Actor mailbox、节点提交、有界循环 | Activity Registry、Guard Registry、Transition Runner、Context Reducer |
|
|
56
|
+
| `application/` | Slice/Workflow/Application 作者 API、Manifest 生成 | Screen 描述、Intent、Operation 引用 |
|
|
57
|
+
| `workflow/` | 结构编译、Actor mailbox、节点提交、有界循环 | Activity Registry、Predicate Registry、Navigation Guard Registry、Transition Runner、Context Reducer |
|
|
59
58
|
| `operations/` | 排队、冲突仲裁、intent 去重、timeout、Abort、session epoch | Context factory、错误分类、冲突规则、业务 Policy、观察器 |
|
|
60
59
|
| `lifecycle.ts` | 组合 Actor 提交后的只读观察器 | telemetry、审计或持久化观察器 |
|
|
61
60
|
|
|
@@ -71,7 +70,7 @@ Workflow ┘ │
|
|
|
71
70
|
Structural Compiler ─ Actor
|
|
72
71
|
│
|
|
73
72
|
Screen Command ───────────────────── Operation Coordinator
|
|
74
|
-
Activity
|
|
73
|
+
Activity Intent ─ Transition Runner ────────────┘
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
当前会话只在内存中运行,不包含 checkpoint 或恢复策略。需要恢复时,由应用层读取 OS 事实和持久化证据,形成 Actor 初始化输入;Kernel 只保留 Lifecycle 和初始化参数这些扩展点。
|
|
@@ -27,10 +27,10 @@ function defineKioskApplication(definition) {
|
|
|
27
27
|
var _slice$commands;
|
|
28
28
|
if (slices.has(slice.id)) throw new Error(`Duplicate Kiosk Slice: ${slice.id}`);
|
|
29
29
|
const operationIds = /* @__PURE__ */ new Set();
|
|
30
|
-
Object.values(slice.
|
|
31
|
-
if (
|
|
32
|
-
registerOperation(
|
|
33
|
-
operationIds.add(
|
|
30
|
+
Object.values(slice.intents).forEach((intent) => {
|
|
31
|
+
if (intent.kind !== "effect") return;
|
|
32
|
+
registerOperation(intent.operation);
|
|
33
|
+
operationIds.add(intent.operation.id);
|
|
34
34
|
});
|
|
35
35
|
Object.values((_slice$commands = slice.commands) !== null && _slice$commands !== void 0 ? _slice$commands : {}).forEach((operation) => {
|
|
36
36
|
registerOperation(operation);
|
|
@@ -77,31 +77,30 @@ function createApplicationActivityRegistry(application) {
|
|
|
77
77
|
const contracts = /* @__PURE__ */ new Map();
|
|
78
78
|
application.slices.forEach(({ definition }) => {
|
|
79
79
|
var _definition$presentat, _definition$additiona;
|
|
80
|
-
const exitPolicies =
|
|
80
|
+
const exitPolicies = {};
|
|
81
|
+
Object.entries(definition.intents).forEach(([intent, behavior]) => {
|
|
81
82
|
let policy;
|
|
82
|
-
if (behavior.kind === "
|
|
83
|
+
if (behavior.kind === "precondition") policy = { precondition: behavior.predicate };
|
|
83
84
|
else if (behavior.kind === "effect") policy = {
|
|
84
|
-
|
|
85
|
-
command: {
|
|
86
|
-
id: behavior.operation.id,
|
|
87
|
-
when: behavior.when
|
|
88
|
-
}
|
|
85
|
+
precondition: behavior.precondition,
|
|
86
|
+
command: { id: behavior.operation.id }
|
|
89
87
|
};
|
|
90
|
-
|
|
91
|
-
})
|
|
88
|
+
if (policy) exitPolicies[intent] = Object.freeze(policy);
|
|
89
|
+
});
|
|
92
90
|
const primaryPresentation = (_definition$presentat = definition.presentation) !== null && _definition$presentat !== void 0 ? _definition$presentat : "page";
|
|
93
91
|
contracts.set(definition.id, Object.freeze({
|
|
94
92
|
id: definition.id,
|
|
95
93
|
screenId: definition.screen.id,
|
|
96
|
-
|
|
94
|
+
intents: Object.freeze(Object.keys(definition.intents)),
|
|
97
95
|
exitPolicies: Object.freeze(exitPolicies),
|
|
98
|
-
navigationGuards: definition.guards ? Object.freeze(Object.
|
|
96
|
+
navigationGuards: definition.guards ? Object.freeze(Object.keys(definition.guards).reduce((guards, rawPhase) => {
|
|
97
|
+
const phase = rawPhase;
|
|
98
|
+
guards[phase] = getKioskSliceNavigationGuardId(definition.id, phase);
|
|
99
|
+
return guards;
|
|
100
|
+
}, {})) : void 0,
|
|
99
101
|
allowedPresentations: Object.freeze([...new Set([primaryPresentation, ...(_definition$additiona = definition.additionalPresentations) !== null && _definition$additiona !== void 0 ? _definition$additiona : []])]),
|
|
100
102
|
requirements: definition.requirements ? Object.freeze([...definition.requirements]) : void 0,
|
|
101
|
-
navigation: Object.freeze({
|
|
102
|
-
forward: Object.freeze(Object.entries(definition.outcomes).filter(([, behavior]) => behavior.navigate === "next").map(([outcome]) => outcome)),
|
|
103
|
-
backward: Object.freeze(Object.entries(definition.outcomes).filter(([, behavior]) => behavior.navigate === "previous").map(([outcome]) => outcome))
|
|
104
|
-
})
|
|
103
|
+
navigation: Object.freeze({ forward: Object.freeze(Object.entries(definition.intents).filter(([, behavior]) => behavior.navigate === "next").map(([intent]) => intent)) })
|
|
105
104
|
}));
|
|
106
105
|
});
|
|
107
106
|
return Object.freeze({
|