@stalfh233/omc-fake-platform 0.4.73 → 0.4.75
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/form.js +64 -0
- package/dist/rule-lifecycle.js +63 -0
- package/dist/runtime-data.js +54 -0
- package/dist/state.d.ts +70 -0
- package/dist/state.js +9 -0
- package/dist/workflow.js +233 -2
- package/package.json +2 -2
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,7 @@ export interface FakeState {
|
|
|
265
288
|
}>;
|
|
266
289
|
sheets: Map<string, FakeSheet>;
|
|
267
290
|
rules: Map<string, FakeRule>;
|
|
291
|
+
buttons: Map<string, FakeButton>;
|
|
268
292
|
/**
|
|
269
293
|
* Ticket 109 platform fidelity: every businessrule create/update mints a NEW
|
|
270
294
|
* record id — superseded rows stay readable via businessrule/get?id=
|
|
@@ -298,6 +322,52 @@ export interface FakeState {
|
|
|
298
322
|
reportPages: Map<string, FakeReportPage>;
|
|
299
323
|
/** R1-report: BI dashboards keyed by objectId (= the report page id). */
|
|
300
324
|
biDashboards: Map<string, FakeBiDashboard>;
|
|
325
|
+
/** Organization writes: role groups / roles / members (recId) / scopes. */
|
|
326
|
+
orgGroups: Map<string, {
|
|
327
|
+
id: string;
|
|
328
|
+
name: string;
|
|
329
|
+
code: string | null;
|
|
330
|
+
parentId: string | null;
|
|
331
|
+
}>;
|
|
332
|
+
orgRoles: Map<string, {
|
|
333
|
+
id: string;
|
|
334
|
+
name: string;
|
|
335
|
+
code: string | null;
|
|
336
|
+
roleGroupId: string;
|
|
337
|
+
roleGroupName: string | null;
|
|
338
|
+
roleType: string;
|
|
339
|
+
corpId: string;
|
|
340
|
+
remarks?: string | null;
|
|
341
|
+
}>;
|
|
342
|
+
orgMembers: Map<string, {
|
|
343
|
+
roleId: string;
|
|
344
|
+
userId: string;
|
|
345
|
+
name: string;
|
|
346
|
+
unitType: string;
|
|
347
|
+
ouScope: Record<string, unknown>[];
|
|
348
|
+
}>;
|
|
349
|
+
/** Organization departments/users (writes added 2026-10-22). */
|
|
350
|
+
orgDepartments: Map<string, {
|
|
351
|
+
id: string;
|
|
352
|
+
name: string;
|
|
353
|
+
parentId: string | null;
|
|
354
|
+
managerId: string | null;
|
|
355
|
+
enabled: boolean;
|
|
356
|
+
sortKey: number | null;
|
|
357
|
+
remarks: string | null;
|
|
358
|
+
}>;
|
|
359
|
+
orgUsers: Map<string, {
|
|
360
|
+
id: string;
|
|
361
|
+
name: string;
|
|
362
|
+
username: string;
|
|
363
|
+
departmentId: string;
|
|
364
|
+
mobile: string | null;
|
|
365
|
+
email: string | null;
|
|
366
|
+
status: string;
|
|
367
|
+
active: boolean;
|
|
368
|
+
roleIds: string[];
|
|
369
|
+
deptIds?: string[];
|
|
370
|
+
}>;
|
|
301
371
|
workflows: Map<string, FakeWorkflow>;
|
|
302
372
|
workItems: Map<string, FakeWorkItem>;
|
|
303
373
|
dataRows: Map<string, Record<string, unknown>>;
|
package/dist/state.js
CHANGED
|
@@ -33,6 +33,7 @@ export function createFakeState(options = {}) {
|
|
|
33
33
|
dictionaryClasses: new Map(),
|
|
34
34
|
sheets: new Map(),
|
|
35
35
|
rules: new Map(),
|
|
36
|
+
buttons: new Map(),
|
|
36
37
|
ruleHistory: [],
|
|
37
38
|
ruleSeq: 0,
|
|
38
39
|
dataRules: new Map(),
|
|
@@ -50,6 +51,14 @@ export function createFakeState(options = {}) {
|
|
|
50
51
|
listViews: new Map(),
|
|
51
52
|
reportPages: new Map(),
|
|
52
53
|
biDashboards: new Map(),
|
|
54
|
+
// Seeded with the static 默认 group the role/list face advertises; a new
|
|
55
|
+
// group must not reuse that id (or the delete guard mistakes the seed role
|
|
56
|
+
// for a member of the caller's group).
|
|
57
|
+
orgGroups: new Map([["grp-1", { id: "grp-1", name: "默认", code: "SYS_1000000", parentId: null }]]),
|
|
58
|
+
orgDepartments: new Map(),
|
|
59
|
+
orgUsers: new Map(),
|
|
60
|
+
orgRoles: new Map(),
|
|
61
|
+
orgMembers: new Map(),
|
|
53
62
|
workflows: new Map(),
|
|
54
63
|
workItems: new Map(),
|
|
55
64
|
dataRows: new Map(),
|
package/dist/workflow.js
CHANGED
|
@@ -349,8 +349,30 @@ export const workflowRoute = (ctx, input) => {
|
|
|
349
349
|
{ id: "grp-1", name: "默认", code: "SYS_1000000", children: [{ id: "role-lead", name: "部门负责人", code: "SYS260630023816197", roleType: "SYS", corpId: "corp-1" }] },
|
|
350
350
|
{ id: "role-finance", name: "财务负责人", code: "SYS260515031207258", roleType: "SYS", corpId: "corp-1" },
|
|
351
351
|
];
|
|
352
|
-
case "/api/api/organization/department/tree":
|
|
353
|
-
|
|
352
|
+
case "/api/api/organization/department/tree": {
|
|
353
|
+
// Platform semantics (1.1.25): no deptIds → roots; deptIds=<id> → that
|
|
354
|
+
// department's WHOLE SUBTREE, not one level.
|
|
355
|
+
const all = [
|
|
356
|
+
{ id: "dept-1", name: "总所", code: "1", corpId: "corp-1", parentId: null },
|
|
357
|
+
{ id: "dept-2", name: "业务部", code: "1_1", corpId: "corp-1", parentId: "dept-1" },
|
|
358
|
+
...[...ctx.state.orgDepartments.values()].map((dept) => ({ id: dept.id, name: dept.name, code: null, corpId: "corp-1", parentId: dept.parentId })),
|
|
359
|
+
];
|
|
360
|
+
const requested = String(q.deptIds ?? "");
|
|
361
|
+
if (requested === "")
|
|
362
|
+
return { departmentList: all.filter((dept) => dept.parentId === null) };
|
|
363
|
+
const subtree = [];
|
|
364
|
+
const queue = [requested];
|
|
365
|
+
while (queue.length > 0) {
|
|
366
|
+
const current = queue.shift();
|
|
367
|
+
for (const dept of all) {
|
|
368
|
+
if (dept.parentId !== current || subtree.some((seen) => seen.id === dept.id))
|
|
369
|
+
continue;
|
|
370
|
+
subtree.push(dept);
|
|
371
|
+
queue.push(dept.id);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return { departmentList: subtree };
|
|
375
|
+
}
|
|
354
376
|
case "/api/api/organization/user/search": {
|
|
355
377
|
// UserController.search: @RequestBody {wd,page,size} → PageVO<UserVO>.
|
|
356
378
|
const wd = String(body.wd ?? "");
|
|
@@ -364,6 +386,215 @@ export const workflowRoute = (ctx, input) => {
|
|
|
364
386
|
const size = Number(body.size ?? 100);
|
|
365
387
|
return { totalElements: users.length, totalPages: size > 0 ? Math.ceil(users.length / size) : 0, content: users.slice(page * size, page * size + size) };
|
|
366
388
|
}
|
|
389
|
+
case "/api/api/organization/department/add": {
|
|
390
|
+
if (!body.name)
|
|
391
|
+
return businessRefusal(10005, "部门名称不能为空");
|
|
392
|
+
if (typeof body.deptType !== "number")
|
|
393
|
+
return businessRefusal(10009, "类型转换异常");
|
|
394
|
+
const id = `dept-${ctx.state.orgDepartments.size + 10}`;
|
|
395
|
+
const dept = { id, name: String(body.name), parentId: body.parentId ? String(body.parentId) : null, managerId: body.managerId ? String(body.managerId) : null, enabled: body.enabled !== false, sortKey: body.sortKey === undefined ? null : Number(body.sortKey), remarks: body.remarks ? String(body.remarks) : null };
|
|
396
|
+
ctx.state.orgDepartments.set(id, dept);
|
|
397
|
+
return dept;
|
|
398
|
+
}
|
|
399
|
+
case "/api/api/organization/department/modefied": {
|
|
400
|
+
const dept = ctx.state.orgDepartments.get(String(body.id ?? ""));
|
|
401
|
+
if (!dept)
|
|
402
|
+
return businessRefusal(10011, "部门不存在");
|
|
403
|
+
Object.assign(dept, {
|
|
404
|
+
...(body.name !== undefined ? { name: String(body.name) } : {}),
|
|
405
|
+
...(body.parentId !== undefined ? { parentId: body.parentId === null ? null : String(body.parentId) } : {}),
|
|
406
|
+
...(body.managerId !== undefined ? { managerId: body.managerId === null ? null : String(body.managerId) } : {}),
|
|
407
|
+
...(body.enabled !== undefined ? { enabled: body.enabled === true } : {}),
|
|
408
|
+
...(body.sortKey !== undefined ? { sortKey: body.sortKey === null ? null : Number(body.sortKey) } : {}),
|
|
409
|
+
...(body.remarks !== undefined ? { remarks: body.remarks === null ? null : String(body.remarks) } : {}),
|
|
410
|
+
});
|
|
411
|
+
return dept;
|
|
412
|
+
}
|
|
413
|
+
case "/api/api/organization/department/get_dept_info": {
|
|
414
|
+
const dept = ctx.state.orgDepartments.get(String(q.deptId ?? ""));
|
|
415
|
+
if (!dept)
|
|
416
|
+
return businessRefusal(10011, "部门不存在");
|
|
417
|
+
return dept;
|
|
418
|
+
}
|
|
419
|
+
case "/api/api/organization/department/strike_out": {
|
|
420
|
+
const deptId = String(q.id ?? "");
|
|
421
|
+
// Platform guards (live 1.1.25): children → 200009, users → 200002.
|
|
422
|
+
// dept-1 carries the static child dept-2 in the tree face.
|
|
423
|
+
if (deptId === "dept-1" || [...ctx.state.orgDepartments.values()].some((dept) => dept.parentId === deptId))
|
|
424
|
+
return businessRefusal(200009, "部门下面存在子部门,不能删除!");
|
|
425
|
+
if ([...ctx.state.orgUsers.values()].some((user) => user.departmentId === deptId))
|
|
426
|
+
return businessRefusal(200002, "部门下面存在用户,不能删除!");
|
|
427
|
+
ctx.state.orgDepartments.delete(deptId);
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
case "/api/api/organization/department/list_user": {
|
|
431
|
+
const deptId = String(q.deptId ?? "");
|
|
432
|
+
const users = [...ctx.state.orgUsers.values()].filter((user) => user.departmentId === deptId);
|
|
433
|
+
return users.map((user) => ({ id: user.id, name: user.name, username: user.username, mobile: user.mobile }));
|
|
434
|
+
}
|
|
435
|
+
case "/api/api/organization/user/add": {
|
|
436
|
+
for (const key of ["userLabelIds", "userManagerConfigList"]) {
|
|
437
|
+
// Platform NPEs on null (UserHelper.dealUserLabel:428) — the tool fills
|
|
438
|
+
// them, and the fake reproduces the failure so the guard is testable.
|
|
439
|
+
if (body[key] === null || body[key] === undefined)
|
|
440
|
+
return businessRefusal(50000, "服务异常");
|
|
441
|
+
}
|
|
442
|
+
const deptId = String(body.departmentId ?? "");
|
|
443
|
+
if (!ctx.state.orgDepartments.has(deptId) && deptId !== "dept-1" && deptId !== "dept-2")
|
|
444
|
+
return businessRefusal(201011, "用户的部门不存在!");
|
|
445
|
+
if (!body.username)
|
|
446
|
+
return businessRefusal(10005, "账号不能为空");
|
|
447
|
+
const id = `user-${ctx.state.orgUsers.size + 10}`;
|
|
448
|
+
const user = { id, name: String(body.name ?? ""), username: String(body.username), departmentId: deptId, mobile: body.mobile ? String(body.mobile) : null, email: body.email ? String(body.email) : null, status: String(body.status ?? "ENABLE"), active: body.active !== false, roleIds: Array.isArray(body.roleIds) ? body.roleIds : [] };
|
|
449
|
+
ctx.state.orgUsers.set(id, user);
|
|
450
|
+
return user;
|
|
451
|
+
}
|
|
452
|
+
case "/api/api/organization/user/info": {
|
|
453
|
+
const user = ctx.state.orgUsers.get(String(q.id ?? ""));
|
|
454
|
+
if (!user)
|
|
455
|
+
return businessRefusal(10011, "用户不存在");
|
|
456
|
+
return { ...user, deptIds: [], userLabelIds: [], userManagerConfigList: [] };
|
|
457
|
+
}
|
|
458
|
+
case "/api/api/organization/user/modify": {
|
|
459
|
+
const user = ctx.state.orgUsers.get(String(body.id ?? ""));
|
|
460
|
+
if (!user)
|
|
461
|
+
return businessRefusal(10011, "用户不存在");
|
|
462
|
+
if (!Array.isArray(body.deptIds))
|
|
463
|
+
return businessRefusal(50000, "服务异常");
|
|
464
|
+
Object.assign(user, {
|
|
465
|
+
...(body.name !== undefined ? { name: String(body.name) } : {}),
|
|
466
|
+
...(body.departmentId !== undefined ? { departmentId: String(body.departmentId) } : {}),
|
|
467
|
+
...(body.mobile !== undefined ? { mobile: body.mobile === null ? null : String(body.mobile) } : {}),
|
|
468
|
+
...(body.email !== undefined ? { email: body.email === null ? null : String(body.email) } : {}),
|
|
469
|
+
...(body.status !== undefined ? { status: String(body.status) } : {}),
|
|
470
|
+
...(body.active !== undefined ? { active: body.active === true } : {}),
|
|
471
|
+
...(Array.isArray(body.roleIds) ? { roleIds: body.roleIds } : {}),
|
|
472
|
+
...(Array.isArray(body.deptIds) ? { deptIds: body.deptIds } : {}),
|
|
473
|
+
});
|
|
474
|
+
return user;
|
|
475
|
+
}
|
|
476
|
+
case "/api/api/organization/user/strike_out": {
|
|
477
|
+
ctx.state.orgUsers.delete(String(q.id ?? ""));
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
case "/api/api/organization/user/departments": {
|
|
481
|
+
const user = ctx.state.orgUsers.get(String(q.id ?? ""));
|
|
482
|
+
if (!user)
|
|
483
|
+
return [];
|
|
484
|
+
const dept = ctx.state.orgDepartments.get(user.departmentId);
|
|
485
|
+
return [{ deptId: user.departmentId, deptName: dept?.name ?? user.departmentId, isMain: true }];
|
|
486
|
+
}
|
|
487
|
+
case "/api/api/organization/role/add_role_group": {
|
|
488
|
+
const id = `grp-${ctx.state.orgGroups.size + 1}`;
|
|
489
|
+
ctx.state.orgGroups.set(id, { id, name: String(body.roleGroupName ?? ""), code: body.code ? String(body.code) : null, parentId: body.parentId ? String(body.parentId) : null });
|
|
490
|
+
return { roleGroupId: id, roleGroupName: body.roleGroupName, code: ctx.state.orgGroups.get(id).code, corpId: body.corpId ?? "other" };
|
|
491
|
+
}
|
|
492
|
+
case "/api/api/organization/role/add_role": {
|
|
493
|
+
const groupId = String(body.roleGroupId ?? "");
|
|
494
|
+
if (!groupId)
|
|
495
|
+
return businessRefusal(10005, "角色组不能为空");
|
|
496
|
+
if (groupId !== "grp-1" && !ctx.state.orgGroups.has(groupId))
|
|
497
|
+
return businessRefusal(10011, "角色组不存在");
|
|
498
|
+
const id = `role-${ctx.state.orgRoles.size + 1}`;
|
|
499
|
+
ctx.state.orgRoles.set(id, { id, name: String(body.roleName ?? ""), code: body.code ? String(body.code) : null, roleGroupId: groupId, roleGroupName: body.roleGroupName ? String(body.roleGroupName) : null, roleType: String(body.roleType ?? "SYS"), corpId: String(body.corpId ?? "other") });
|
|
500
|
+
return { roleId: id, roleName: body.roleName, code: ctx.state.orgRoles.get(id).code, roleGroupId: groupId, roleGroupName: body.roleGroupName ?? null, roleType: body.roleType ?? "SYS", corpId: body.corpId ?? "other" };
|
|
501
|
+
}
|
|
502
|
+
case "/api/api/organization/role/get": {
|
|
503
|
+
const role = ctx.state.orgRoles.get(String(q.roleId ?? ""));
|
|
504
|
+
if (!role)
|
|
505
|
+
return businessRefusal(10011, "角色不存在");
|
|
506
|
+
const remarks = role.remarks ?? null;
|
|
507
|
+
return { roleId: role.id, roleName: role.name, name: role.name, code: role.code, roleGroupId: role.roleGroupId, groupId: role.roleGroupId, roleGroupName: role.roleGroupName, roleType: role.roleType, corpId: role.corpId, remarks };
|
|
508
|
+
}
|
|
509
|
+
case "/api/api/organization/role/modify_role": {
|
|
510
|
+
const role = ctx.state.orgRoles.get(String(body.roleId ?? ""));
|
|
511
|
+
if (!role)
|
|
512
|
+
return businessRefusal(10011, "角色不存在");
|
|
513
|
+
role.name = body.roleName === undefined ? role.name : String(body.roleName);
|
|
514
|
+
role.code = body.code === undefined ? role.code : String(body.code);
|
|
515
|
+
// role/get answers RoleModel (name/groupId/remarks), not RoleVO — keep the
|
|
516
|
+
// stored shape aligned so a readback never invents a field.
|
|
517
|
+
if (body.remarks !== undefined)
|
|
518
|
+
role.remarks = body.remarks === null ? null : String(body.remarks);
|
|
519
|
+
if (body.roleGroupId !== undefined)
|
|
520
|
+
role.roleGroupId = String(body.roleGroupId);
|
|
521
|
+
return { roleId: role.id };
|
|
522
|
+
}
|
|
523
|
+
case "/api/api/organization/role/add_role_user": {
|
|
524
|
+
const roleId = String(body.roleId ?? "");
|
|
525
|
+
if (!roleId)
|
|
526
|
+
return businessRefusal(10005, "角色不能为空");
|
|
527
|
+
const rows = Array.isArray(body.userIds) ? body.userIds : [];
|
|
528
|
+
for (const row of rows) {
|
|
529
|
+
const userId = String(row.id ?? "");
|
|
530
|
+
if (!userId)
|
|
531
|
+
continue;
|
|
532
|
+
ctx.state.orgMembers.set(`${roleId}#${userId}`, { roleId, userId, name: String(row.name ?? userId), unitType: String(row.unitType ?? "3"), ouScope: [] });
|
|
533
|
+
}
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
case "/api/api/organization/role/strike_out_role_user": {
|
|
537
|
+
// Body {roleId,userIds}, NOT query ids (live 1.1.25 answers 10009 when the
|
|
538
|
+
// body is missing).
|
|
539
|
+
const roleId = String(body.roleId ?? "");
|
|
540
|
+
const userIds = Array.isArray(body.userIds) ? body.userIds : [];
|
|
541
|
+
if (userIds.length === 0)
|
|
542
|
+
return businessRefusal(10009, "Required request body is missing");
|
|
543
|
+
for (const id of userIds)
|
|
544
|
+
ctx.state.orgMembers.delete(`${roleId}#${String(id)}`);
|
|
545
|
+
return { undeletableLabelUsers: [], deletedUsers: userIds.map((id) => ({ id })) };
|
|
546
|
+
}
|
|
547
|
+
case "/api/api/organization/role/strike_out_role": {
|
|
548
|
+
const ids = String(q.ids ?? "").split(",").filter((value) => value !== "");
|
|
549
|
+
for (const roleId of ids) {
|
|
550
|
+
if ([...ctx.state.orgMembers.values()].some((member) => member.roleId === roleId))
|
|
551
|
+
return businessRefusal(-1, "角色下已经包含用户数据!");
|
|
552
|
+
ctx.state.orgRoles.delete(roleId);
|
|
553
|
+
}
|
|
554
|
+
return ids.map((roleId) => ({ roleId }));
|
|
555
|
+
}
|
|
556
|
+
case "/api/api/organization/role/strike_out_role_group": {
|
|
557
|
+
const ids = String(q.ids ?? "").split(",").filter((value) => value !== "");
|
|
558
|
+
for (const groupId of ids) {
|
|
559
|
+
if ([...ctx.state.orgRoles.values()].some((role) => role.roleGroupId === groupId))
|
|
560
|
+
return businessRefusal(-1, "角色组下已经包含角色数据!");
|
|
561
|
+
ctx.state.orgGroups.delete(groupId);
|
|
562
|
+
}
|
|
563
|
+
return ids.map((roleGroupId) => ({ roleGroupId }));
|
|
564
|
+
}
|
|
565
|
+
case "/api/api/organization/user/dept_transfer": {
|
|
566
|
+
const sourceDept = String(body.sourceDept ?? "");
|
|
567
|
+
const targetDept = String(body.targetDept ?? "");
|
|
568
|
+
if (!sourceDept || !targetDept)
|
|
569
|
+
return businessRefusal(50000, "服务异常");
|
|
570
|
+
const ids = Array.isArray(body.userIds) ? body.userIds : [];
|
|
571
|
+
for (const id of ids) {
|
|
572
|
+
const user = ctx.state.orgUsers.get(String(id));
|
|
573
|
+
if (!user)
|
|
574
|
+
continue;
|
|
575
|
+
if (user.departmentId === sourceDept)
|
|
576
|
+
user.departmentId = targetDept;
|
|
577
|
+
}
|
|
578
|
+
return JSON.stringify(ids);
|
|
579
|
+
}
|
|
580
|
+
case "/api/api/organization/role/users": {
|
|
581
|
+
const roleId = String(q.roleId ?? "");
|
|
582
|
+
const members = [...ctx.state.orgMembers.values()].filter((member) => member.roleId === roleId);
|
|
583
|
+
const page = Number(q.page ?? 0);
|
|
584
|
+
const size = Number(q.size ?? 500);
|
|
585
|
+
return { totalElements: members.length, totalPages: size > 0 ? Math.ceil(members.length / size) : 0, content: members.slice(page * size, page * size + size).map((member) => ({ id: member.userId, name: member.name, unitType: member.unitType, departmentId: "dept-1" })) };
|
|
586
|
+
}
|
|
587
|
+
case "/api/api/organization/role/get_ouscope": {
|
|
588
|
+
const member = ctx.state.orgMembers.get(`${String(q.roleId ?? "")}#${String(q.userId ?? "")}`);
|
|
589
|
+
return member ? member.ouScope : [];
|
|
590
|
+
}
|
|
591
|
+
case "/api/api/organization/role/update_ouscope": {
|
|
592
|
+
const member = ctx.state.orgMembers.get(`${String(body.roleId ?? "")}#${String(body.userId ?? "")}`);
|
|
593
|
+
if (!member)
|
|
594
|
+
return businessRefusal(10011, "角色成员不存在");
|
|
595
|
+
member.ouScope = Array.isArray(body.ouScopeList) ? body.ouScopeList : [];
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
367
598
|
default:
|
|
368
599
|
return NOT_HANDLED;
|
|
369
600
|
}
|
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.75",
|
|
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.75"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=22.13"
|