@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
|
@@ -6,8 +6,8 @@ import { locales } from "@pisell/utils";
|
|
|
6
6
|
//#region src/components/HDLKiosk/app/ScreenRenderer.tsx
|
|
7
7
|
/**
|
|
8
8
|
* 文件角色:把当前 Workflow Node 交给其 Slice 自有 View 渲染。
|
|
9
|
-
* 运行机制:从应用 View 注册表按 Activity
|
|
10
|
-
*
|
|
9
|
+
* 运行机制:从应用 View 注册表按 Activity 读取同步组件;先渲染 Actor
|
|
10
|
+
* Surface 保留的 Page,再按顺序叠加 Modal Slice View。
|
|
11
11
|
* 边界:只组装 View Context,不调用 Host、不创建 ViewModel、不决定下一节点。
|
|
12
12
|
*/
|
|
13
13
|
function ScreenRenderer({ definition, snapshot, sales, send, commandState = { busy: false }, resetting, capabilities, businessMode = "prototype", scaleMinimumWeightKg = .01, renderIdentityAuth, memberAction = {
|
|
@@ -22,12 +22,12 @@ function ScreenRenderer({ definition, snapshot, sales, send, commandState = { bu
|
|
|
22
22
|
methods: [],
|
|
23
23
|
loading: false
|
|
24
24
|
} }) {
|
|
25
|
-
var
|
|
25
|
+
var _definition$nodes$sna, _commandState$error;
|
|
26
26
|
const activeNode = definition.nodes[snapshot.nodeId];
|
|
27
|
-
const
|
|
28
|
-
const
|
|
27
|
+
const pageNode = (_definition$nodes$sna = definition.nodes[snapshot.surface.pageStepId]) !== null && _definition$nodes$sna !== void 0 ? _definition$nodes$sna : activeNode;
|
|
28
|
+
const overlayNodes = snapshot.surface.overlays.map((stepId) => definition.nodes[stepId]).filter((node) => Boolean(node));
|
|
29
29
|
const bindActivity = (activityId) => (action) => {
|
|
30
|
-
send(action.kind === "
|
|
30
|
+
send(action.kind === "intent" || action.kind === "navigation" ? _objectSpread2(_objectSpread2({}, action), {}, { activityId }) : action);
|
|
31
31
|
};
|
|
32
32
|
const viewSnapshot = commandState.busy || commandState.error ? _objectSpread2(_objectSpread2({}, snapshot), {}, {
|
|
33
33
|
status: commandState.busy ? "running" : snapshot.status,
|
|
@@ -64,7 +64,7 @@ function ScreenRenderer({ definition, snapshot, sales, send, commandState = { bu
|
|
|
64
64
|
};
|
|
65
65
|
return /* @__PURE__ */ React.createElement(SliceView, context);
|
|
66
66
|
};
|
|
67
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, renderNode(
|
|
67
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, renderNode(pageNode), overlayNodes.map((node) => /* @__PURE__ */ React.createElement(React.Fragment, { key: node.id }, renderNode(node))));
|
|
68
68
|
}
|
|
69
69
|
//#endregion
|
|
70
70
|
export { ScreenRenderer };
|
|
@@ -3,13 +3,14 @@ import { renderCustomization } from "../slices/customization/view.js";
|
|
|
3
3
|
import { renderFulfillment } from "../slices/fulfillment/view.js";
|
|
4
4
|
import { renderIdentity } from "../slices/identity/view.js";
|
|
5
5
|
import { renderPayment } from "../slices/payment/view.js";
|
|
6
|
+
import { renderPrimaryProductPrompt } from "../slices/primaryProductPrompt/view.js";
|
|
6
7
|
//#region src/components/HDLKiosk/app/views.ts
|
|
7
8
|
const hdlKioskViews = Object.freeze({
|
|
8
9
|
identity: renderIdentity,
|
|
9
10
|
fulfillment: renderFulfillment,
|
|
10
11
|
customization: renderCustomization,
|
|
11
12
|
catalog: renderCatalog,
|
|
12
|
-
|
|
13
|
+
primaryProductPrompt: renderPrimaryProductPrompt,
|
|
13
14
|
paymentMethod: renderPayment,
|
|
14
15
|
paymentCompleted: renderPayment
|
|
15
16
|
});
|
|
@@ -1,54 +1,31 @@
|
|
|
1
1
|
import { defineWorkflow } from "../../kernel/application/defineApplication.js";
|
|
2
|
-
import {
|
|
2
|
+
import { catalogSlice } from "../../slices/catalog/slice.js";
|
|
3
3
|
import { customizationSlice } from "../../slices/customization/slice.js";
|
|
4
4
|
import { fulfillmentSlice } from "../../slices/fulfillment/slice.js";
|
|
5
5
|
import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/slice.js";
|
|
6
|
+
import { primaryProductPromptSlice } from "../../slices/primaryProductPrompt/slice.js";
|
|
6
7
|
//#region src/components/HDLKiosk/app/workflow/barcode.ts
|
|
7
8
|
/**
|
|
8
9
|
* 文件角色:声明 Barcode 入口的有序业务步骤。
|
|
9
10
|
* 运行机制:Workflow 从 Catalog 开始;首码在 Session readyForCommands 后由全局 Scanner
|
|
10
|
-
* Controller 消费。Catalog
|
|
11
|
-
*
|
|
11
|
+
* Controller 消费。Catalog 完成补充购物后进入独立的主商品询问 Modal,需要时进入
|
|
12
|
+
* Customization;无论选择或拒绝,最终都直接进入 Fulfillment。
|
|
12
13
|
* 边界:设备扫码属于 Session 输入,不是可前进/后退的 Activity;支付 prepare 由支付
|
|
13
14
|
* Operation 在 dispatch 前统一保证,不绑定 Catalog transition。
|
|
14
15
|
*/
|
|
15
16
|
const barcodeWorkflow = defineWorkflow({
|
|
16
17
|
id: "hdl.barcode",
|
|
17
18
|
steps: [
|
|
19
|
+
catalogSlice,
|
|
18
20
|
{
|
|
19
|
-
slice:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
slice: customizationSlice,
|
|
27
|
-
on: {
|
|
28
|
-
completed: {
|
|
29
|
-
target: "catalogWithPrimaryProductPrompt",
|
|
30
|
-
repeat: {
|
|
31
|
-
max: 1,
|
|
32
|
-
exceeded: "blocked"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
skipped: {
|
|
36
|
-
target: "catalogWithPrimaryProductPrompt",
|
|
37
|
-
repeat: {
|
|
38
|
-
max: 1,
|
|
39
|
-
exceeded: "blocked"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
slice: fulfillmentSlice,
|
|
46
|
-
on: { back: "catalogWithPrimaryProductPrompt" }
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
slice: paymentMethodSlice,
|
|
50
|
-
on: { eftposSelected: "paymentCompleted" }
|
|
21
|
+
slice: primaryProductPromptSlice,
|
|
22
|
+
presentation: { mode: "modal" },
|
|
23
|
+
visitPolicy: "once",
|
|
24
|
+
on: { omit: "fulfillment" }
|
|
51
25
|
},
|
|
26
|
+
customizationSlice,
|
|
27
|
+
fulfillmentSlice,
|
|
28
|
+
paymentMethodSlice,
|
|
52
29
|
{
|
|
53
30
|
slice: paymentCompletedSlice,
|
|
54
31
|
end: "success"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _objectSpread2 } from "../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
|
|
2
|
-
//#region src/components/HDLKiosk/app/workflow/
|
|
3
|
-
const
|
|
2
|
+
//#region src/components/HDLKiosk/app/workflow/predicates.ts
|
|
3
|
+
const DEFAULT_PREDICATES = Object.freeze({
|
|
4
4
|
"cart.notEmpty": ({ sales }) => sales.cart.lineCount > 0,
|
|
5
5
|
"cart.noPendingMutation": ({ sales }) => !sales.cart.consistencyPending,
|
|
6
6
|
"identity.satisfied": ({ sales }) => sales.customer !== void 0,
|
|
@@ -9,15 +9,15 @@ const DEFAULT_GUARDS = Object.freeze({
|
|
|
9
9
|
"payment.notPending": ({ sales }) => sales.payment.status !== "pending" && sales.payment.status !== "unknown",
|
|
10
10
|
"payment.succeeded": ({ sales }) => sales.payment.status === "succeeded"
|
|
11
11
|
});
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
const ids = Object.freeze(Object.keys(
|
|
12
|
+
function createPredicateRegistry(overrides = {}) {
|
|
13
|
+
const predicates = Object.freeze(_objectSpread2(_objectSpread2({}, DEFAULT_PREDICATES), overrides));
|
|
14
|
+
const ids = Object.freeze(Object.keys(predicates));
|
|
15
15
|
return Object.freeze({
|
|
16
16
|
ids,
|
|
17
|
-
has: (id) => Object.prototype.hasOwnProperty.call(
|
|
18
|
-
evaluate: (id, context) =>
|
|
17
|
+
has: (id) => Object.prototype.hasOwnProperty.call(predicates, id),
|
|
18
|
+
evaluate: (id, context) => predicates[id](context)
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
const
|
|
21
|
+
const defaultPredicateRegistry = createPredicateRegistry();
|
|
22
22
|
//#endregion
|
|
23
|
-
export {
|
|
23
|
+
export { defaultPredicateRegistry };
|
|
@@ -8,22 +8,19 @@ import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/
|
|
|
8
8
|
/**
|
|
9
9
|
* 文件角色:声明 Quick 入口的有序业务步骤。
|
|
10
10
|
* 运行机制:数组顺序就是主路径;这里只保留偏离相邻前进/返回的特殊分支。
|
|
11
|
-
* 边界:
|
|
11
|
+
* 边界:Predicate、Command、Screen 与失败策略均由本地 Activity Contract 提供。
|
|
12
12
|
*/
|
|
13
13
|
const quickOrderWorkflow = defineWorkflow({
|
|
14
14
|
id: "hdl.quick",
|
|
15
15
|
steps: [
|
|
16
16
|
{
|
|
17
17
|
slice: identitySlice,
|
|
18
|
-
|
|
18
|
+
visitPolicy: "once"
|
|
19
19
|
},
|
|
20
20
|
customizationSlice,
|
|
21
21
|
catalogSlice,
|
|
22
22
|
fulfillmentSlice,
|
|
23
|
-
|
|
24
|
-
slice: paymentMethodSlice,
|
|
25
|
-
on: { eftposSelected: "paymentCompleted" }
|
|
26
|
-
},
|
|
23
|
+
paymentMethodSlice,
|
|
27
24
|
{
|
|
28
25
|
slice: paymentCompletedSlice,
|
|
29
26
|
end: "success"
|
|
@@ -7,7 +7,7 @@ const defaultActivityRegistry = Object.freeze({
|
|
|
7
7
|
has: (id) => manifestRegistry.has(id),
|
|
8
8
|
get: (id) => manifestRegistry.has(id) ? manifestRegistry.require(id) : void 0,
|
|
9
9
|
require: (id) => manifestRegistry.require(id),
|
|
10
|
-
accepts: (id,
|
|
10
|
+
accepts: (id, intent) => manifestRegistry.require(id).intents.some((availableIntent) => availableIntent === intent)
|
|
11
11
|
});
|
|
12
12
|
const BUILT_IN_WORKFLOW_DEFINITIONS = Object.freeze({
|
|
13
13
|
quick: normalizeKioskWorkflow(hdlKioskApplication.workflows.quick),
|
|
@@ -14,19 +14,10 @@ import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/
|
|
|
14
14
|
const scaleWorkflow = defineWorkflow({
|
|
15
15
|
id: "hdl.scale",
|
|
16
16
|
steps: [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
on: { skipped: "$next" }
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
slice: catalogSlice,
|
|
23
|
-
on: { back: "customization" }
|
|
24
|
-
},
|
|
17
|
+
customizationSlice,
|
|
18
|
+
catalogSlice,
|
|
25
19
|
fulfillmentSlice,
|
|
26
|
-
|
|
27
|
-
slice: paymentMethodSlice,
|
|
28
|
-
on: { eftposSelected: "paymentCompleted" }
|
|
29
|
-
},
|
|
20
|
+
paymentMethodSlice,
|
|
30
21
|
{
|
|
31
22
|
slice: paymentCompletedSlice,
|
|
32
23
|
end: "success"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { _objectSpread2 } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
|
|
2
|
-
import { _objectWithoutProperties } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectWithoutProperties.js";
|
|
3
2
|
import { HDLKioskFloatingBar } from "../HDLKioskFloatingBar/index.js";
|
|
4
3
|
import { HDLKioskCartSecondaryActions } from "../HDLKioskCartBarActions/index.js";
|
|
5
4
|
import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
@@ -10,11 +9,6 @@ import React, { createContext, useCallback, useContext, useEffect, useLayoutEffe
|
|
|
10
9
|
* 并区分 Modal 下可见的 masked 状态与不可导航时的 hidden 状态。
|
|
11
10
|
* 边界:不计算购物车/金额;声明数据必须来自 ViewModel/Projection。
|
|
12
11
|
*/
|
|
13
|
-
const _excluded = [
|
|
14
|
-
"overlayActive",
|
|
15
|
-
"cartEnabled",
|
|
16
|
-
"cartOpenBar"
|
|
17
|
-
];
|
|
18
12
|
const HDLKioskBottomBarContext = createContext(null);
|
|
19
13
|
const HDLKioskCartDrawerContext = createContext(null);
|
|
20
14
|
const noop = () => void 0;
|
|
@@ -40,6 +34,17 @@ function useHDLKioskBottomBar(declaration) {
|
|
|
40
34
|
}, [context, declaration]);
|
|
41
35
|
useLayoutEffect(() => () => context === null || context === void 0 ? void 0 : context.unregister(owner.current), [context]);
|
|
42
36
|
}
|
|
37
|
+
function toFloatingBarProps(declaration) {
|
|
38
|
+
const { ariaLabel, className, leading, summary, trailing, variant } = declaration;
|
|
39
|
+
return {
|
|
40
|
+
ariaLabel,
|
|
41
|
+
className,
|
|
42
|
+
leading,
|
|
43
|
+
summary,
|
|
44
|
+
trailing,
|
|
45
|
+
variant
|
|
46
|
+
};
|
|
47
|
+
}
|
|
43
48
|
function resolveHDLKioskBottomBarState(mode, screenOverlayActive = false) {
|
|
44
49
|
const effectiveMode = mode === "hidden" ? "hidden" : mode === "masked" || screenOverlayActive ? "masked" : "visible";
|
|
45
50
|
return {
|
|
@@ -111,10 +116,7 @@ function HDLKioskBottomBarHost({ children, mode = "visible", fallback, cartOverl
|
|
|
111
116
|
}),
|
|
112
117
|
className: `hdl-floating-bar--cart-open${cartBarDeclaration.className ? ` ${cartBarDeclaration.className}` : ""}`
|
|
113
118
|
}) : renderedDeclaration;
|
|
114
|
-
const floatingBarDeclaration = activeBarDeclaration ? (
|
|
115
|
-
const { overlayActive: _overlayActive, cartEnabled: _cartEnabled, cartOpenBar: _cartOpenBar } = activeBarDeclaration;
|
|
116
|
-
return _objectWithoutProperties(activeBarDeclaration, _excluded);
|
|
117
|
-
})() : void 0;
|
|
119
|
+
const floatingBarDeclaration = activeBarDeclaration ? toFloatingBarProps(activeBarDeclaration) : void 0;
|
|
118
120
|
useEffect(() => {
|
|
119
121
|
if (presentation.drawerBlocked || !drawerAvailable) setCartOpen(false);
|
|
120
122
|
}, [drawerAvailable, presentation.drawerBlocked]);
|
|
@@ -46,7 +46,7 @@ HDLKiosk/
|
|
|
46
46
|
| --- | --- |
|
|
47
47
|
| `manifest.ts` | 唯一应用清单;收集 Slice 提供的 Activity/Operation,并绑定三条 Workflow |
|
|
48
48
|
| `constants.ts` | `HDL_WORKFLOW_IDS` 等应用常量 |
|
|
49
|
-
| `events.ts` | HDLKiosk Activity
|
|
49
|
+
| `events.ts` | HDLKiosk Activity Intent 与 Back Navigation 的强类型事件联合 |
|
|
50
50
|
| `operations.ts` | 共享 Operation 的唯一公共导入入口 |
|
|
51
51
|
| `operations/` | `operations.ts` 背后的私有实现、Coordinator 策略和测试;外部代码不要逐文件跨层引用 |
|
|
52
52
|
| `views.ts` | 应用层 Activity → React View 的完整同步注册表;不进入 Slice/Workflow/Kernel |
|
|
@@ -54,7 +54,7 @@ HDLKiosk/
|
|
|
54
54
|
| `workflow/barcode.ts` | Barcode 的有序 Activity 列表与特殊转移 |
|
|
55
55
|
| `workflow/scale.ts` | Scale 的有序 Activity 列表与特殊转移 |
|
|
56
56
|
| `workflow/registry.ts` | 从 Manifest 派生 Activity Registry,并提供内置流程查询 |
|
|
57
|
-
| `workflow/
|
|
57
|
+
| `workflow/predicates.ts` | HDLKiosk 本地 Predicate Registry |
|
|
58
58
|
| `workflow/policy.ts` | 支付门禁等商家应用策略校验;结构编译仍属于 Kernel |
|
|
59
59
|
| `ScreenRenderer.tsx` | 按当前 Activity 从 `views.ts` 取 View;处理 modal base 叠加和缺失 View 的阻断呈现 |
|
|
60
60
|
| `entry/model.ts` | 派生 Workflow 之外三个入口的可用性和展示模型 |
|
|
@@ -75,7 +75,7 @@ slices/<name>/
|
|
|
75
75
|
└── model.ts # 仅本 Slice 使用的纯模型;按需存在
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
当前 Slice:`identity`、`fulfillment`、`customization`、`catalog`、`paymentMethod`、`paymentCompleted`。`paymentCompleted`
|
|
78
|
+
当前 Slice:`identity`、`fulfillment`、`customization`、`catalog`、`primaryProductPrompt`、`paymentMethod`、`paymentCompleted`。`primaryProductPrompt` 是以 Catalog 为 base 的 Modal;`paymentCompleted` 是支付成功终态,与 `paymentMethod` 共用 Checkout Screen/View。
|
|
79
79
|
|
|
80
80
|
- `slices/index.ts` 只汇总各目录导出的 `defineSlice` 结果,并统一约束 `screen.id` 必须是 HDLKiosk `ScreenId`;
|
|
81
81
|
- 每个 `slice.ts` 只通过 `screen: { id }` 声明页面类别;实际 View 在 `app/views.ts` 中按 Activity 单独、完整地注册,使用同步本地组件引用;
|
|
@@ -84,7 +84,7 @@ slices/<name>/
|
|
|
84
84
|
|
|
85
85
|
`Slice` 是作者侧唯一业务能力概念:`defineSlice` 定义能力,`application.slices` 收集能力,Workflow 的 `slice` 字段引用能力;运行期再编译为 Activity Contract。
|
|
86
86
|
|
|
87
|
-
命名关系必须保持单向:Slice `id` 是业务 Activity;`screen.id` 是页面类别;`app/views.ts` 按 Activity 注册实际 UI。`catalog`
|
|
87
|
+
命名关系必须保持单向:Slice `id` 是业务 Activity;`screen.id` 是页面类别;`app/views.ts` 按 Activity 注册实际 UI。`catalog` 负责商品浏览,`primaryProductPrompt` 负责主商品询问;Prompt 以 Modal 覆盖 Actor Surface 中当前实际 Page,不引用固定背景 Step。支付派发和店员支付授权是 `paymentMethod` 页面内 Command,不创建流程 Intent。
|
|
88
88
|
|
|
89
89
|
## 5. `components/`:共享展示能力
|
|
90
90
|
|
|
@@ -103,7 +103,7 @@ slices/<name>/
|
|
|
103
103
|
| `createWorkflowSession.ts` | 读取内置 Definition,直接编译、校验并创建 Actor/Coordinator |
|
|
104
104
|
| `HDLKioskWorkflowView.tsx` | 活跃会话的 React 组合层;连接 Shell、Renderer 和 Controllers |
|
|
105
105
|
| `domainTransitionRunner.ts` | 将 Actor 的 exit effect 交给 Operation Coordinator |
|
|
106
|
-
| `workflowContext.ts` | 只归并经过 Activity
|
|
106
|
+
| `workflowContext.ts` | 只归并经过 Activity Intent 成功提交的流程上下文 |
|
|
107
107
|
| `workflowLifecycle.ts` | Actor blocked/finished 的 Host 遥测与生命周期钩子 |
|
|
108
108
|
| `useHostSnapshot.ts` | 订阅 Host Projection |
|
|
109
109
|
| `useWorkflowSnapshot.ts` | 订阅 Actor Snapshot |
|
|
@@ -126,7 +126,7 @@ BigSale 专用 H5 登录实现位于 `bigSale/templates/HDLKiosk/auth/`。独立
|
|
|
126
126
|
| 位置 | 角色 |
|
|
127
127
|
| --- | --- |
|
|
128
128
|
| `application/` | `defineSlice`、`defineKioskApplication`、Slice 收集和 authoring normalization |
|
|
129
|
-
| `workflow/compiler.ts` | 编译有序 Step 数组,校验 ID、
|
|
129
|
+
| `workflow/compiler.ts` | 编译有序 Step 数组,校验 ID、Intent、可达性、modal base 和有界循环 |
|
|
130
130
|
| `workflow/actor.ts` | 单会话串行状态机,拒绝迟到 Activity/Step 事件 |
|
|
131
131
|
| `workflow/transition.ts` | advance/stay/blocked 等转移结果原语 |
|
|
132
132
|
| `operations/coordinator.ts` | 通用排队、去重、timeout、dispose 和 epoch 协调 |
|
|
@@ -164,7 +164,7 @@ import {
|
|
|
164
164
|
- `host.ts`:Host 总入口和 capabilities;
|
|
165
165
|
- `sales.ts`:标准 ProductData、OS 加购流、购物车、客户、履约、支付和 Projection;
|
|
166
166
|
- `devices.ts`:scanner/scale port;
|
|
167
|
-
- `workflow*.ts`:Activity、Definition 和 Guard 协议;
|
|
167
|
+
- `workflow*.ts`:Activity、Definition、Predicate 和 Navigation Guard 协议;
|
|
168
168
|
- `operations.ts`:允许请求的 Operation ID 与输入映射;
|
|
169
169
|
- `results.ts` / `errors.ts` / `telemetry.ts`:结果、错误与观测。
|
|
170
170
|
|
|
@@ -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
|
|