@stalfh233/omc-fake-platform 0.4.74 → 0.4.76
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/dist/field.js +6 -0
- package/dist/form.js +64 -0
- package/dist/rule-lifecycle.js +63 -0
- package/dist/runtime-data.js +54 -0
- package/dist/state.d.ts +26 -0
- package/dist/state.js +2 -0
- package/package.json +2 -2
package/dist/field.js
CHANGED
|
@@ -6,6 +6,12 @@ export const fieldRoute = (ctx, input) => {
|
|
|
6
6
|
const body = (input.body ?? {});
|
|
7
7
|
switch (input.path) {
|
|
8
8
|
case "/api/api/app/bizproperty/list": {
|
|
9
|
+
// Live 2026-10-23 (issue §1.4): on some versions a CHILD-table schema code
|
|
10
|
+
// answers 601003 业务模型不存在 on this face while bizdatarule/list answers
|
|
11
|
+
// rows for it. That is a version-specific asymmetry, so the fake exposes it
|
|
12
|
+
// as an explicit lever (state.fieldListUnavailable) instead of guessing it.
|
|
13
|
+
if (ctx.state.fieldListUnavailable?.has(String(q.schemaCode ?? "")))
|
|
14
|
+
return businessRefusal(601003, "业务模型不存在");
|
|
9
15
|
const rows = [...ctx.state.fields.entries()]
|
|
10
16
|
.filter(([key]) => key.startsWith(`${q.schemaCode}#`))
|
|
11
17
|
.map(([, field]) => ({
|
package/dist/form.js
CHANGED
|
@@ -30,6 +30,7 @@ export const formRoute = (ctx, input) => {
|
|
|
30
30
|
publishedAttributesJson: sheet.publishedAttributesJson ?? null,
|
|
31
31
|
publishedActionsJson: sheet.publishedActionsJson ?? null,
|
|
32
32
|
publishedHtmlJson: sheet.publishedHtmlJson ?? null,
|
|
33
|
+
options: sheet.options ?? null,
|
|
33
34
|
binding: { fieldCodes: sheet.fieldCodes },
|
|
34
35
|
};
|
|
35
36
|
}
|
|
@@ -78,6 +79,7 @@ export const formRoute = (ctx, input) => {
|
|
|
78
79
|
// customScript element when present). Default to "[]" on first
|
|
79
80
|
// draft so onlinejs.compose can parse it as an array.
|
|
80
81
|
draftHtmlJson: body.draftHtmlJson ?? existing.draftHtmlJson ?? "[]",
|
|
82
|
+
options: typeof body.options === "string" ? body.options : existing.options ?? null,
|
|
81
83
|
});
|
|
82
84
|
return null;
|
|
83
85
|
}
|
|
@@ -91,6 +93,68 @@ export const formRoute = (ctx, input) => {
|
|
|
91
93
|
sheet.publishedAttributesJson = sheet.draftAttributesJson ?? null;
|
|
92
94
|
sheet.publishedActionsJson = body.publishedActionsJson ?? "[]";
|
|
93
95
|
sheet.publishedHtmlJson = sheet.draftHtmlJson ?? "[]";
|
|
96
|
+
// The platform persists the whole BizFormModel on publish, options
|
|
97
|
+
// included; keep the authoring column so the readback can show it.
|
|
98
|
+
if (typeof body.options === "string")
|
|
99
|
+
sheet.options = body.options;
|
|
100
|
+
// ModelManagementFacadeImpl.publishBizForm:1022 — publish reads
|
|
101
|
+
// options.customButtonJson and re-adds the FORM-trigger button rows.
|
|
102
|
+
// A malformed value answers 50000 (fastjson "syntax error, position at
|
|
103
|
+
// 0, name customButtonJson") exactly like the platform.
|
|
104
|
+
const rawOptions = typeof body.options === "string" ? body.options : sheet.options;
|
|
105
|
+
if (typeof rawOptions === "string" && rawOptions !== "") {
|
|
106
|
+
let optionsRecord;
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(rawOptions);
|
|
109
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
110
|
+
optionsRecord = parsed;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return businessRefusal(50000, "options 不是合法 JSON");
|
|
114
|
+
}
|
|
115
|
+
const rawButtons = optionsRecord?.customButtonJson;
|
|
116
|
+
if (rawButtons !== undefined && rawButtons !== null && rawButtons !== "") {
|
|
117
|
+
if (typeof rawButtons !== "string")
|
|
118
|
+
return businessRefusal(50000, "syntax error, position at 0, name customButtonJson");
|
|
119
|
+
let list;
|
|
120
|
+
try {
|
|
121
|
+
list = JSON.parse(rawButtons);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return businessRefusal(50000, "syntax error, position at 0, name customButtonJson");
|
|
125
|
+
}
|
|
126
|
+
if (!Array.isArray(list))
|
|
127
|
+
return businessRefusal(50000, "syntax error, position at 0, name customButtonJson");
|
|
128
|
+
for (const [key, button] of [...ctx.state.buttons]) {
|
|
129
|
+
if (button.schemaCode === sheet.sheetCode && button.triggerType === "FORM" && button.triggerCode === sheet.sheetCode)
|
|
130
|
+
ctx.state.buttons.delete(key);
|
|
131
|
+
}
|
|
132
|
+
list.forEach((entry, index) => {
|
|
133
|
+
const item = (entry ?? {});
|
|
134
|
+
const code = String(item.code ?? "");
|
|
135
|
+
if (code === "")
|
|
136
|
+
return;
|
|
137
|
+
ctx.state.buttons.set(`${sheet.sheetCode}#FORM#${sheet.sheetCode}#${code}`, {
|
|
138
|
+
id: `btn-${sheet.sheetCode}-${code}`,
|
|
139
|
+
schemaCode: sheet.sheetCode,
|
|
140
|
+
code,
|
|
141
|
+
name: String(item.name ?? code),
|
|
142
|
+
name_i18n: typeof item.name_i18n === "string" ? item.name_i18n : null,
|
|
143
|
+
triggerType: "FORM",
|
|
144
|
+
triggerCode: sheet.sheetCode,
|
|
145
|
+
useLocation: typeof item.useLocation === "string" ? item.useLocation : "FORM",
|
|
146
|
+
bindAction: typeof item.bindAction === "string" ? item.bindAction : undefined,
|
|
147
|
+
operateType: typeof item.operateType === "string" ? item.operateType : undefined,
|
|
148
|
+
targetCode: typeof item.targetCode === "string" ? item.targetCode : null,
|
|
149
|
+
targetObjCode: typeof item.targetObjCode === "string" ? item.targetObjCode : null,
|
|
150
|
+
showPermCode: typeof item.showPermCode === "string" ? item.showPermCode : null,
|
|
151
|
+
showPermType: typeof item.showPermType === "string" ? item.showPermType : null,
|
|
152
|
+
actionConfig: typeof item.actionConfig === "string" ? item.actionConfig : JSON.stringify(item.actionConfig ?? {}),
|
|
153
|
+
sortKey: typeof item.sortKey === "number" ? item.sortKey : index,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
94
158
|
}
|
|
95
159
|
return null;
|
|
96
160
|
}
|
package/dist/rule-lifecycle.js
CHANGED
|
@@ -67,9 +67,72 @@ function selfCheckProblems(state, model) {
|
|
|
67
67
|
}
|
|
68
68
|
return problems;
|
|
69
69
|
}
|
|
70
|
+
const RUNTIME_EXECUTE_PREFIX = "/api/api/launcher/domain_service_runtime/";
|
|
71
|
+
/**
|
|
72
|
+
* Manual custom-trigger rule execution — the portal button's face (live
|
|
73
|
+
* 2026-10-23): POST /api/api/launcher/domain_service_runtime/<schema>/<rule>
|
|
74
|
+
* {schemaCode, ids, ruleCode, consolidation}. The response's `data` is the
|
|
75
|
+
* PRE-execution snapshot (the frontend sends reacquireDataFromDb:false), so the
|
|
76
|
+
* fake applies the rule's UPDATE nodes but returns the rows as they were.
|
|
77
|
+
*/
|
|
78
|
+
function runtimeExecuteRule(ctx, schemaCode, ruleCode, ids) {
|
|
79
|
+
const rule = [...ctx.state.rules.values()].find((entry) => entry.schemaCode === schemaCode && entry.code === ruleCode);
|
|
80
|
+
if (!rule)
|
|
81
|
+
return businessRefusal(302024, "业务规则不存在");
|
|
82
|
+
let nodes = [];
|
|
83
|
+
try {
|
|
84
|
+
const parsed = typeof rule.node === "string" ? JSON.parse(rule.node) : rule.node;
|
|
85
|
+
if (Array.isArray(parsed))
|
|
86
|
+
nodes = parsed;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
nodes = [];
|
|
90
|
+
}
|
|
91
|
+
const rows = ids
|
|
92
|
+
.map((id) => ctx.state.dataRows.get(id))
|
|
93
|
+
.filter((row) => row !== undefined);
|
|
94
|
+
const before = rows.map((row) => structuredClone(row));
|
|
95
|
+
for (const node of nodes) {
|
|
96
|
+
if (String(node.nodeType ?? "").toUpperCase() !== "UPDATE")
|
|
97
|
+
continue;
|
|
98
|
+
const actions = Array.isArray(node.dataActions) ? node.dataActions : [];
|
|
99
|
+
const groups = (node.filterCondition?.mainConditions ?? []);
|
|
100
|
+
const matches = (row) => {
|
|
101
|
+
if (groups.length === 0)
|
|
102
|
+
return true;
|
|
103
|
+
return groups.some((group) => (Array.isArray(group) ? group : []).every((raw) => {
|
|
104
|
+
const condition = (raw ?? {});
|
|
105
|
+
const field = String(condition.targetSchemaDataItem ?? "");
|
|
106
|
+
if (condition.ruleValueType !== "FIXED")
|
|
107
|
+
return true;
|
|
108
|
+
return String(row[field] ?? "") === String(condition.value ?? "");
|
|
109
|
+
}));
|
|
110
|
+
};
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
if (!matches(row))
|
|
113
|
+
continue;
|
|
114
|
+
for (const action of actions) {
|
|
115
|
+
if (action.ruleValueType !== "FIXED")
|
|
116
|
+
continue;
|
|
117
|
+
row[String(action.targetDataItem ?? "")] = action.value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// The fake's route return value IS the envelope's `data` (errcode is added by
|
|
122
|
+
// the transport), so hand back the pre-execution rows exactly like the live
|
|
123
|
+
// `{"errcode":0,"errmsg":"Execution Completed","data":[…]}` unwraps to.
|
|
124
|
+
return before;
|
|
125
|
+
}
|
|
70
126
|
export const ruleLifecycleRoute = (ctx, input) => {
|
|
71
127
|
const q = input.query ?? {};
|
|
72
128
|
const body = (input.body ?? {});
|
|
129
|
+
if (input.path.startsWith(RUNTIME_EXECUTE_PREFIX)) {
|
|
130
|
+
const [schemaCode, ruleCode] = input.path.slice(RUNTIME_EXECUTE_PREFIX.length).split("/").map((part) => decodeURIComponent(part));
|
|
131
|
+
const ids = Array.isArray(body.ids) ? body.ids.map(String) : [];
|
|
132
|
+
if (!schemaCode || !ruleCode)
|
|
133
|
+
return businessRefusal(10011, "业务模型不存在");
|
|
134
|
+
return runtimeExecuteRule(ctx, String(body.schemaCode ?? schemaCode), String(body.ruleCode ?? ruleCode), ids);
|
|
135
|
+
}
|
|
73
136
|
switch (input.path) {
|
|
74
137
|
case "/api/api/app/businessrule/node_run_map":
|
|
75
138
|
return selfCheckProblems(ctx.state, body);
|
package/dist/runtime-data.js
CHANGED
|
@@ -170,6 +170,60 @@ export const runtimeDataRoute = (ctx, input) => {
|
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
|
+
case "/api/api/system/getCustomButtons": {
|
|
174
|
+
// SystemAdminController.getCustomButtons: SchemaCustomButtonModel with one
|
|
175
|
+
// TriggerInfo per default form; only triggerType=FORM buttons ride here.
|
|
176
|
+
const schemaCode = String(q.schemaCode ?? "");
|
|
177
|
+
const forms = [...ctx.state.sheets.values()].filter((sheet) => sheet.sheetCode === schemaCode);
|
|
178
|
+
return {
|
|
179
|
+
formCustomButtons: forms.map((sheet) => ({
|
|
180
|
+
triggerCode: sheet.sheetCode,
|
|
181
|
+
triggerName: sheet.sheetCode,
|
|
182
|
+
hasChildTable: false,
|
|
183
|
+
buttonModels: [...ctx.state.buttons.values()].filter((button) => button.triggerType === "FORM" && button.triggerCode === sheet.sheetCode && button.schemaCode === schemaCode),
|
|
184
|
+
})),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
case "/api/api/runtime/form/executeCustomButton": {
|
|
188
|
+
// FormRuntimeController.executeCustomButton (live 2026-10-23): the portal
|
|
189
|
+
// posts the whole bizObject for a FORM button (not ids), and the payload
|
|
190
|
+
// carries the button row itself. Apply the bound rule's UPDATE nodes.
|
|
191
|
+
const model = (body.bizButtonModel ?? {});
|
|
192
|
+
const list = Array.isArray(body.bizObjectList) ? body.bizObjectList : [];
|
|
193
|
+
const schemaCode = String(model.schemaCode ?? "");
|
|
194
|
+
const ruleCode = String(model.targetCode ?? "");
|
|
195
|
+
const rule = [...ctx.state.rules.values()].find((entry) => entry.schemaCode === schemaCode && entry.code === ruleCode);
|
|
196
|
+
if (!rule)
|
|
197
|
+
return businessRefusal(302024, "业务规则不存在");
|
|
198
|
+
let nodes = [];
|
|
199
|
+
try {
|
|
200
|
+
const parsed = typeof rule.node === "string" ? JSON.parse(rule.node) : rule.node;
|
|
201
|
+
if (Array.isArray(parsed))
|
|
202
|
+
nodes = parsed;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
nodes = [];
|
|
206
|
+
}
|
|
207
|
+
for (const entry of list) {
|
|
208
|
+
const data = (entry?.data ?? {});
|
|
209
|
+
const id = String(data.id ?? entry?.id ?? "");
|
|
210
|
+
const row = id !== "" ? ctx.state.dataRows.get(id) : undefined;
|
|
211
|
+
for (const node of nodes) {
|
|
212
|
+
if (String(node.nodeType ?? "").toUpperCase() !== "UPDATE")
|
|
213
|
+
continue;
|
|
214
|
+
const actions = Array.isArray(node.dataActions) ? node.dataActions : [];
|
|
215
|
+
for (const action of actions) {
|
|
216
|
+
if (action.ruleValueType !== "FIXED")
|
|
217
|
+
continue;
|
|
218
|
+
if (row)
|
|
219
|
+
row[String(action.targetDataItem ?? "")] = action.value;
|
|
220
|
+
else
|
|
221
|
+
data[String(action.targetDataItem ?? "")] = action.value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { errcode: 0, errmsg: "Execution Completed" };
|
|
226
|
+
}
|
|
173
227
|
case "/api/api/runtime/form/delete": {
|
|
174
228
|
// Ticket 22 source evidence (FormRuntimeController:1233): @RequestParam
|
|
175
229
|
// schemaCode + objectId; validateNotEmpty(schemaCode) -> PortalException
|
package/dist/state.d.ts
CHANGED
|
@@ -70,8 +70,31 @@ export interface FakeSheet {
|
|
|
70
70
|
publishedAttributesJson?: string | null;
|
|
71
71
|
publishedActionsJson?: string | null;
|
|
72
72
|
publishedHtmlJson?: string | null;
|
|
73
|
+
/** `options` (BizForm.options): form-level JSON carrying customButtonJson,
|
|
74
|
+
* presetButtonManage, sheetPresetButtonManage. Live column, string value. */
|
|
75
|
+
options?: string | null;
|
|
73
76
|
fieldCodes: string[];
|
|
74
77
|
}
|
|
78
|
+
/** h_biz_button row (BizButtonModel). triggerType=QUERY for list buttons,
|
|
79
|
+
* FORM for form toolbar buttons (published from bizsheet options). */
|
|
80
|
+
export interface FakeButton {
|
|
81
|
+
id: string;
|
|
82
|
+
schemaCode: string;
|
|
83
|
+
code: string;
|
|
84
|
+
name: string;
|
|
85
|
+
name_i18n?: string | null;
|
|
86
|
+
triggerType: "QUERY" | "FORM";
|
|
87
|
+
triggerCode: string;
|
|
88
|
+
useLocation?: string;
|
|
89
|
+
bindAction?: string;
|
|
90
|
+
operateType?: string;
|
|
91
|
+
targetCode?: string | null;
|
|
92
|
+
targetObjCode?: string | null;
|
|
93
|
+
showPermCode?: string | null;
|
|
94
|
+
showPermType?: string | null;
|
|
95
|
+
actionConfig?: string;
|
|
96
|
+
sortKey?: number;
|
|
97
|
+
}
|
|
75
98
|
export interface FakeRule {
|
|
76
99
|
id: string;
|
|
77
100
|
schemaCode: string;
|
|
@@ -265,6 +288,9 @@ export interface FakeState {
|
|
|
265
288
|
}>;
|
|
266
289
|
sheets: Map<string, FakeSheet>;
|
|
267
290
|
rules: Map<string, FakeRule>;
|
|
291
|
+
buttons: Map<string, FakeButton>;
|
|
292
|
+
/** Schema codes for which bizproperty/list answers 601003 (see field.ts). */
|
|
293
|
+
fieldListUnavailable: Set<string>;
|
|
268
294
|
/**
|
|
269
295
|
* Ticket 109 platform fidelity: every businessrule create/update mints a NEW
|
|
270
296
|
* record id — superseded rows stay readable via businessrule/get?id=
|
package/dist/state.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-fake-platform",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.76",
|
|
4
4
|
"description": "OMC 的内存假 CloudPivot 平台(wire 接缝的第二个适配器)+ HTTP 假服务器——@stalfh233/omc-cli 的测试与 bench 基准基座",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"build": "tsc --project tsconfig.build.json"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"omc-core": "0.4.
|
|
21
|
+
"omc-core": "0.4.76"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=22.13"
|