openyida 2026.9.7 → 2026.9.8-1
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/README.md +4 -1
- package/lib/app/create-form/args.js +36 -2
- package/lib/app/create-form/batch.js +24 -7
- package/lib/app/create-form/commands.js +2 -0
- package/lib/app/create-form.js +292 -0
- package/lib/core/command-manifest.js +4 -1
- package/lib/core/locales/en.js +17 -4
- package/lib/core/locales/zh.js +17 -4
- package/lib/integration/integration-create.js +151 -4
- package/lib/integration/integration-diagnostics.js +12 -12
- package/lib/integration/integration-form-metadata.js +136 -0
- package/lib/integration/integration-process-builder.js +31 -4
- package/lib/integration/integration-spec-builder.js +570 -73
- package/lib/integration/integration-view-builder.js +240 -15
- package/lib/process/configure-process.js +18 -1
- package/package.json +1 -1
- package/yida-skills/SKILL.md +1 -0
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +3 -3
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +2 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +17 -3
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +18 -4
- package/yida-skills/skills/yida-integration/SKILL.md +44 -5
- package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +21 -0
package/README.md
CHANGED
|
@@ -174,6 +174,8 @@ openyida update-app APP_XXX --theme-file .cache/openyida/crm/app-theme.css --nav
|
|
|
174
174
|
openyida corp-efficiency
|
|
175
175
|
openyida create-form create APP_XXX "Customer" .cache/openyida/forms/customer-fields.json
|
|
176
176
|
openyida create-form update APP_XXX FORM_XXX .cache/openyida/forms/customer-changes.json
|
|
177
|
+
openyida create-form update APP_XXX FORM_XXX --data-file .cache/openyida/forms/customer-changes.json
|
|
178
|
+
openyida create-form resume APP_XXX FORM_XXX .cache/openyida/forms/customer-fields.json --json
|
|
177
179
|
openyida sample openyida-page-template form-fields --output .cache/openyida/forms/customer-fields.json
|
|
178
180
|
openyida sample openyida-page-template canvas-form-drawer --output project/pages/src/customer-entry.canvas.jsx --var APP_TYPE=APP_XXX --var FORM_UUID=FORM_XXX
|
|
179
181
|
openyida get-schema APP_XXX FORM_XXX
|
|
@@ -413,7 +415,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
413
415
|
| `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--icon auto\|<iconName>] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
|
|
414
416
|
| `openyida create-form icons [--json]` | List available form navigation icons |
|
|
415
417
|
| `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
|
|
416
|
-
| `openyida create-form update <appType>
|
|
418
|
+
| `openyida create-form update <appType> <formUuid> (<changesJsonOrFile> \| --data-file <changesJsonOrFile>) [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
|
|
419
|
+
| `openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]` | Update a form page |
|
|
417
420
|
| `openyida create-form patch <appType> <formUuid> <patchJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
418
421
|
| `openyida create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
419
422
|
| `openyida create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open\|--no-open]` | Update a form page |
|
|
@@ -89,6 +89,7 @@ function createParseArgs(dependencies) {
|
|
|
89
89
|
contentLocale: null,
|
|
90
90
|
browserOpenMode: openOption.mode,
|
|
91
91
|
};
|
|
92
|
+
let dataFileInput;
|
|
92
93
|
|
|
93
94
|
const args = [...rawArgs];
|
|
94
95
|
|
|
@@ -111,6 +112,14 @@ function createParseArgs(dependencies) {
|
|
|
111
112
|
i--;
|
|
112
113
|
} else if (args[i] === '--icon') {
|
|
113
114
|
throwCreateFormError('--icon requires a value', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
115
|
+
} else if (args[i] === '--data-file') {
|
|
116
|
+
const next = args[i + 1];
|
|
117
|
+
if (dataFileInput !== undefined || !next || next.startsWith('--')) {
|
|
118
|
+
throwCreateFormError('--data-file requires exactly one value', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
119
|
+
}
|
|
120
|
+
dataFileInput = next;
|
|
121
|
+
args.splice(i, 2);
|
|
122
|
+
i--;
|
|
114
123
|
} else if ((args[i] === '--locale' || args[i] === '--content-locale' || args[i] === '--lang') && i + 1 < args.length) {
|
|
115
124
|
options.contentLocale = args[i + 1];
|
|
116
125
|
if (!normalizeYidaLocale(options.contentLocale)) {
|
|
@@ -145,6 +154,10 @@ function createParseArgs(dependencies) {
|
|
|
145
154
|
|
|
146
155
|
const mode = args[0];
|
|
147
156
|
|
|
157
|
+
if (dataFileInput !== undefined && mode !== 'update') {
|
|
158
|
+
throwCreateFormError('--data-file is only supported by create-form update', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
159
|
+
}
|
|
160
|
+
|
|
148
161
|
if (mode === 'icons' || mode === 'list-icons') {
|
|
149
162
|
return {
|
|
150
163
|
mode: 'icons',
|
|
@@ -182,7 +195,12 @@ function createParseArgs(dependencies) {
|
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
if (mode === 'update') {
|
|
185
|
-
|
|
198
|
+
const positionalInput = args[3];
|
|
199
|
+
if (args.length > 4 || (positionalInput && dataFileInput !== undefined)) {
|
|
200
|
+
throwCreateFormError('Use either <changesJsonOrFile> or --data-file, not both', 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
201
|
+
}
|
|
202
|
+
const changesJsonOrFile = dataFileInput !== undefined ? dataFileInput : positionalInput;
|
|
203
|
+
if (args.length < 3 || !changesJsonOrFile) {
|
|
186
204
|
usage(t('create_form.usage_update'), t('create_form.example_update'));
|
|
187
205
|
throwCreateFormError(t('create_form.usage_update'), 'CREATE_FORM_INVALID_ARGUMENTS');
|
|
188
206
|
}
|
|
@@ -190,7 +208,23 @@ function createParseArgs(dependencies) {
|
|
|
190
208
|
mode: 'update',
|
|
191
209
|
appType: args[1],
|
|
192
210
|
formUuid: args[2],
|
|
193
|
-
changesJsonOrFile
|
|
211
|
+
changesJsonOrFile,
|
|
212
|
+
...options
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (mode === 'resume') {
|
|
217
|
+
if (args.length !== 4) {
|
|
218
|
+
throwCreateFormError(
|
|
219
|
+
'openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> --json',
|
|
220
|
+
'CREATE_FORM_INVALID_ARGUMENTS'
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
mode: 'resume',
|
|
225
|
+
appType: args[1],
|
|
226
|
+
formUuid: args[2],
|
|
227
|
+
fieldsJsonOrFile: args[3],
|
|
194
228
|
...options
|
|
195
229
|
};
|
|
196
230
|
}
|
|
@@ -101,7 +101,10 @@ function execute(args) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
async function schedule(forms, concurrency, results, worker, save) {
|
|
104
|
-
const pending = new Set(forms.filter(form =>
|
|
104
|
+
const pending = new Set(forms.filter(form => {
|
|
105
|
+
const result = results[form.key];
|
|
106
|
+
return !result || result.status === 'blocked' || (result.status === 'failed' && result.formUuid);
|
|
107
|
+
}).map(form => form.key));
|
|
105
108
|
const active = new Map();
|
|
106
109
|
try {
|
|
107
110
|
while (pending.size || active.size) {
|
|
@@ -114,7 +117,7 @@ async function schedule(forms, concurrency, results, worker, save) {
|
|
|
114
117
|
}
|
|
115
118
|
if (active.size >= concurrency || deps.some(status => status !== 'success')) { continue; }
|
|
116
119
|
pending.delete(form.key);
|
|
117
|
-
results[form.key] = { status: 'running' };
|
|
120
|
+
results[form.key] = { ...results[form.key], status: 'running' };
|
|
118
121
|
save(); // Record intent before the request; an interrupted create is never retried automatically.
|
|
119
122
|
const task = Promise.resolve().then(() => worker(form)).then(output => {
|
|
120
123
|
results[form.key] = { ...output, status: 'success' };
|
|
@@ -178,16 +181,30 @@ async function run(args, dependencies = {}) {
|
|
|
178
181
|
return matches[0].fieldId;
|
|
179
182
|
};
|
|
180
183
|
await schedule(forms, options.concurrency, state.results, async form => {
|
|
181
|
-
let formUuid = form.formUuid;
|
|
184
|
+
let formUuid = form.formUuid || state.results[form.key]?.formUuid;
|
|
185
|
+
let shouldResume = Boolean(!form.formUuid && formUuid);
|
|
186
|
+
const resolvedFields = mapReferences(form.fields, resolve);
|
|
182
187
|
if (!formUuid) {
|
|
183
|
-
const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(
|
|
188
|
+
const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(resolvedFields), '--no-open'];
|
|
184
189
|
for (const key of ['icon', 'locale']) { if (form[key]) { argv.push(`--${key}`, form[key]); } }
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
try {
|
|
191
|
+
const created = await call(argv);
|
|
192
|
+
if (typeof created.formUuid !== 'string' || !created.formUuid.startsWith('FORM')) { invalid(`create result: ${form.key}`); }
|
|
193
|
+
formUuid = created.formUuid;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const createdFormUuid = error.output?.formUuid || error.output?.details?.formUuid;
|
|
196
|
+
if (typeof createdFormUuid !== 'string' || !createdFormUuid.startsWith('FORM')) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
formUuid = createdFormUuid;
|
|
200
|
+
shouldResume = true;
|
|
201
|
+
}
|
|
188
202
|
}
|
|
189
203
|
state.results[form.key].formUuid = formUuid;
|
|
190
204
|
save(state);
|
|
205
|
+
if (shouldResume) {
|
|
206
|
+
await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
207
|
+
}
|
|
191
208
|
const schema = await call(['get-schema', options.appType, formUuid, '--field-map-json']);
|
|
192
209
|
if (schema.formUuid !== formUuid || !Array.isArray(schema.fields)) { invalid(`schema: ${form.key}`); }
|
|
193
210
|
return { formUuid, fields: schema.fields };
|
|
@@ -6,6 +6,8 @@ function dispatchCreateFormCommand(parsedArgs, authContext, handlers) {
|
|
|
6
6
|
switch (parsedArgs.mode) {
|
|
7
7
|
case 'update':
|
|
8
8
|
return handlers.update(...args);
|
|
9
|
+
case 'resume':
|
|
10
|
+
return handlers.resume(...args);
|
|
9
11
|
case 'patch':
|
|
10
12
|
return handlers.patch(...args);
|
|
11
13
|
case 'rule':
|
package/lib/app/create-form.js
CHANGED
|
@@ -5911,6 +5911,297 @@ async function mainValidation(parsedArgs, authRef) {
|
|
|
5911
5911
|
|
|
5912
5912
|
// ── update 模式主流程 ─────────────────────────────────
|
|
5913
5913
|
|
|
5914
|
+
function collectResumeFieldEvidence(fields, output) {
|
|
5915
|
+
const evidence = output || [];
|
|
5916
|
+
(fields || []).forEach(function (field) {
|
|
5917
|
+
if (!field || typeof field !== 'object') {
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
const labelText = extractLabelText(field);
|
|
5921
|
+
if (labelText && !isFormPresentationComponent(field.componentName)) {
|
|
5922
|
+
evidence.push({
|
|
5923
|
+
label: labelText,
|
|
5924
|
+
componentName: field.componentName,
|
|
5925
|
+
fieldId: field.props && field.props.fieldId ? field.props.fieldId : null,
|
|
5926
|
+
});
|
|
5927
|
+
}
|
|
5928
|
+
if (Array.isArray(field.children)) {
|
|
5929
|
+
collectResumeFieldEvidence(field.children.flat(), evidence);
|
|
5930
|
+
}
|
|
5931
|
+
});
|
|
5932
|
+
return evidence;
|
|
5933
|
+
}
|
|
5934
|
+
|
|
5935
|
+
function desiredResumeFieldIdentity(field) {
|
|
5936
|
+
const labelValue = field && field.label;
|
|
5937
|
+
const label = typeof labelValue === 'string'
|
|
5938
|
+
? labelValue
|
|
5939
|
+
: labelValue && (labelValue.zh_CN || labelValue.ja_JP || labelValue.en_US || labelValue.pureEn_US);
|
|
5940
|
+
return {
|
|
5941
|
+
label: String(label || '').trim(),
|
|
5942
|
+
componentName: normalizeFormDefinitionType(field),
|
|
5943
|
+
};
|
|
5944
|
+
}
|
|
5945
|
+
|
|
5946
|
+
function collectResumeDesiredFields(fields, options) {
|
|
5947
|
+
const output = [];
|
|
5948
|
+
const verifyNestedFields = Boolean(options && options.verifyNestedFields);
|
|
5949
|
+
|
|
5950
|
+
function visit(value) {
|
|
5951
|
+
if (Array.isArray(value)) {
|
|
5952
|
+
value.forEach(visit);
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
if (!value || typeof value !== 'object') {
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
const componentName = normalizeFormDefinitionType(value);
|
|
5959
|
+
if (isSupportedBusinessFieldType(componentName)) {
|
|
5960
|
+
output.push(value);
|
|
5961
|
+
if (!verifyNestedFields) {
|
|
5962
|
+
return;
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
if (Array.isArray(value.children)) {
|
|
5966
|
+
value.children.forEach(visit);
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
visit(fields);
|
|
5971
|
+
return output;
|
|
5972
|
+
}
|
|
5973
|
+
|
|
5974
|
+
function isRecoverableBlankResumeSchema(schema) {
|
|
5975
|
+
const allowedShellComponents = new Set(['Page', 'RootHeader', 'RootContent', 'RootFooter', 'FooterYida']);
|
|
5976
|
+
const componentsTree = schema && schema.pages && schema.pages[0] && schema.pages[0].componentsTree;
|
|
5977
|
+
if (!Array.isArray(componentsTree) || componentsTree.length === 0) {
|
|
5978
|
+
return true;
|
|
5979
|
+
}
|
|
5980
|
+
let safe = true;
|
|
5981
|
+
function visit(node) {
|
|
5982
|
+
if (!node || typeof node !== 'object' || !safe) {
|
|
5983
|
+
return;
|
|
5984
|
+
}
|
|
5985
|
+
if (node.componentName && !allowedShellComponents.has(node.componentName)) {
|
|
5986
|
+
safe = false;
|
|
5987
|
+
return;
|
|
5988
|
+
}
|
|
5989
|
+
if (Array.isArray(node.children)) {
|
|
5990
|
+
node.children.forEach(visit);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
componentsTree.forEach(visit);
|
|
5994
|
+
return safe;
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
function buildResumeChanges(existingFields, desiredFields) {
|
|
5998
|
+
const existingByLabel = new Map();
|
|
5999
|
+
existingFields.forEach(function (field) {
|
|
6000
|
+
const bucket = existingByLabel.get(field.label) || [];
|
|
6001
|
+
bucket.push(field);
|
|
6002
|
+
existingByLabel.set(field.label, bucket);
|
|
6003
|
+
});
|
|
6004
|
+
|
|
6005
|
+
const desiredLabels = new Set();
|
|
6006
|
+
const missing = [];
|
|
6007
|
+
const existing = [];
|
|
6008
|
+
const conflicts = [];
|
|
6009
|
+
desiredFields.forEach(function (field) {
|
|
6010
|
+
const identity = desiredResumeFieldIdentity(field);
|
|
6011
|
+
if (!identity.label || desiredLabels.has(identity.label)) {
|
|
6012
|
+
conflicts.push({
|
|
6013
|
+
code: identity.label ? 'CREATE_FORM_RESUME_DUPLICATE_DESIRED_FIELD' : 'CREATE_FORM_RESUME_FIELD_LABEL_REQUIRED',
|
|
6014
|
+
label: identity.label || null,
|
|
6015
|
+
});
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
desiredLabels.add(identity.label);
|
|
6019
|
+
const matches = existingByLabel.get(identity.label) || [];
|
|
6020
|
+
if (matches.length === 0) {
|
|
6021
|
+
missing.push(field);
|
|
6022
|
+
return;
|
|
6023
|
+
}
|
|
6024
|
+
if (matches.length !== 1 || matches[0].componentName !== identity.componentName) {
|
|
6025
|
+
conflicts.push({
|
|
6026
|
+
code: 'CREATE_FORM_RESUME_FIELD_CONFLICT',
|
|
6027
|
+
label: identity.label,
|
|
6028
|
+
requestedType: identity.componentName,
|
|
6029
|
+
observedTypes: matches.map(function (item) { return item.componentName; }),
|
|
6030
|
+
});
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6033
|
+
existing.push({
|
|
6034
|
+
label: identity.label,
|
|
6035
|
+
componentName: identity.componentName,
|
|
6036
|
+
fieldId: matches[0].fieldId,
|
|
6037
|
+
});
|
|
6038
|
+
});
|
|
6039
|
+
return { missing, existing, conflicts };
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
async function readResumeSchema(authRef, appType, formUuid) {
|
|
6043
|
+
const schemaResult = await requestWithAutoLogin(function (auth) {
|
|
6044
|
+
return sendGetRequest(
|
|
6045
|
+
auth.baseUrl,
|
|
6046
|
+
buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
|
|
6047
|
+
{ formUuid: formUuid, schemaVersion: 'V5' }
|
|
6048
|
+
);
|
|
6049
|
+
}, authRef);
|
|
6050
|
+
if (!schemaResult || schemaResult.success === false || schemaResult.__needLogin || schemaResult.__csrfExpired) {
|
|
6051
|
+
throwCreateFormError(t('create_form.resume_readback_failed'), 'CREATE_FORM_RESUME_READBACK_FAILED', {
|
|
6052
|
+
appType,
|
|
6053
|
+
formUuid,
|
|
6054
|
+
result: sanitizeFailureResult(schemaResult),
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
if (schemaResult.appType && schemaResult.appType !== appType) {
|
|
6058
|
+
throwCreateFormError(t('create_form.resume_ownership_unverified'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
|
|
6059
|
+
appType,
|
|
6060
|
+
formUuid,
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
if (schemaResult.formUuid && schemaResult.formUuid !== formUuid) {
|
|
6064
|
+
throwCreateFormError(t('create_form.resume_identity_mismatch'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
|
|
6065
|
+
appType,
|
|
6066
|
+
formUuid,
|
|
6067
|
+
});
|
|
6068
|
+
}
|
|
6069
|
+
const schema = schemaResult.content
|
|
6070
|
+
? (typeof schemaResult.content === 'string' ? JSON.parse(schemaResult.content) : schemaResult.content)
|
|
6071
|
+
: schemaResult.pages ? schemaResult : null;
|
|
6072
|
+
if (!schema || !Array.isArray(schema.pages) || schema.pages.length === 0) {
|
|
6073
|
+
throwCreateFormError(t('create_form.resume_schema_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6074
|
+
appType,
|
|
6075
|
+
formUuid,
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
return { schemaResult, schema, version: extractSchemaServerRevision(schemaResult) };
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
async function mainResume(parsedArgs, authRef) {
|
|
6082
|
+
const { appType, formUuid, fieldsJsonOrFile } = parsedArgs;
|
|
6083
|
+
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
6084
|
+
const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
|
|
6085
|
+
assertNoEmojiInFormDefinition(formUuid, fields, [], 'create-form resume input');
|
|
6086
|
+
validateFormFieldDefinitions(fields);
|
|
6087
|
+
|
|
6088
|
+
const firstRead = await readResumeSchema(authRef, appType, formUuid);
|
|
6089
|
+
const root = firstRead.schema.pages[0].componentsTree && firstRead.schema.pages[0].componentsTree[0];
|
|
6090
|
+
const formContainer = root ? findFormContainer(root) : null;
|
|
6091
|
+
const completedStages = ['read_target', 'verify_ownership'];
|
|
6092
|
+
let recoveredBlankShell = false;
|
|
6093
|
+
let plan;
|
|
6094
|
+
|
|
6095
|
+
if (!formContainer) {
|
|
6096
|
+
if (!isRecoverableBlankResumeSchema(firstRead.schema)) {
|
|
6097
|
+
throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6098
|
+
appType,
|
|
6099
|
+
formUuid,
|
|
6100
|
+
retryable: false,
|
|
6101
|
+
remoteWrites: 0,
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
const corpId = resolveCorpId(authRef.authData);
|
|
6105
|
+
const formTitle = firstRead.schemaResult.formTitle || firstRead.schemaResult.title || formUuid;
|
|
6106
|
+
firstRead.schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, 'single', 'default', 'top');
|
|
6107
|
+
const resumeValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
|
|
6108
|
+
if (resumeValidationRules.length > 0) {
|
|
6109
|
+
applySmartValidations(firstRead.schema, resumeValidationRules);
|
|
6110
|
+
}
|
|
6111
|
+
await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
|
|
6112
|
+
completedStages.push('rebuild_blank_schema', 'save_schema');
|
|
6113
|
+
recoveredBlankShell = true;
|
|
6114
|
+
plan = {
|
|
6115
|
+
existing: [],
|
|
6116
|
+
missing: collectResumeDesiredFields(fields, { verifyNestedFields: true }),
|
|
6117
|
+
conflicts: [],
|
|
6118
|
+
};
|
|
6119
|
+
} else if (!Array.isArray(formContainer.children)) {
|
|
6120
|
+
throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
|
|
6121
|
+
appType,
|
|
6122
|
+
formUuid,
|
|
6123
|
+
retryable: false,
|
|
6124
|
+
remoteWrites: 0,
|
|
6125
|
+
});
|
|
6126
|
+
} else {
|
|
6127
|
+
plan = buildResumeChanges(
|
|
6128
|
+
collectResumeFieldEvidence(formContainer.children),
|
|
6129
|
+
collectResumeDesiredFields(fields)
|
|
6130
|
+
);
|
|
6131
|
+
if (plan.conflicts.length > 0) {
|
|
6132
|
+
throwCreateFormError(t('create_form.resume_field_conflict'), 'CREATE_FORM_RESUME_CONFLICT', {
|
|
6133
|
+
appType,
|
|
6134
|
+
formUuid,
|
|
6135
|
+
retryable: false,
|
|
6136
|
+
remoteWrites: 0,
|
|
6137
|
+
diagnostics: plan.conflicts,
|
|
6138
|
+
});
|
|
6139
|
+
}
|
|
6140
|
+
completedStages.push('compare_fields');
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
if (!recoveredBlankShell && plan.missing.length > 0) {
|
|
6144
|
+
const applied = applyChangesToSchema(
|
|
6145
|
+
firstRead.schema,
|
|
6146
|
+
plan.missing.map(function (field) { return { action: 'add', field }; }),
|
|
6147
|
+
{ verbose: true }
|
|
6148
|
+
);
|
|
6149
|
+
if ((applied.diagnostics || []).length > 0) {
|
|
6150
|
+
throwCreateFormError(t('create_form.resume_field_resolution_failed'), 'CREATE_FORM_RESUME_CONFLICT', {
|
|
6151
|
+
appType,
|
|
6152
|
+
formUuid,
|
|
6153
|
+
retryable: false,
|
|
6154
|
+
remoteWrites: 0,
|
|
6155
|
+
diagnostics: applied.diagnostics,
|
|
6156
|
+
});
|
|
6157
|
+
}
|
|
6158
|
+
const corpId = resolveCorpId(authRef.authData);
|
|
6159
|
+
const updatedContainer = findFormContainer(firstRead.schema.pages[0].componentsTree[0]);
|
|
6160
|
+
fillSerialNumberFormulas(updatedContainer.children, corpId, appType, formUuid);
|
|
6161
|
+
await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
|
|
6162
|
+
completedStages.push('add_missing_fields', 'save_schema');
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
const finalRead = await readResumeSchema(authRef, appType, formUuid);
|
|
6166
|
+
const finalContainer = findFormContainer(finalRead.schema.pages[0].componentsTree[0]);
|
|
6167
|
+
const finalPlan = buildResumeChanges(
|
|
6168
|
+
collectResumeFieldEvidence(finalContainer && finalContainer.children),
|
|
6169
|
+
collectResumeDesiredFields(fields, { verifyNestedFields: true })
|
|
6170
|
+
);
|
|
6171
|
+
if (finalPlan.missing.length > 0 || finalPlan.conflicts.length > 0) {
|
|
6172
|
+
throwCreateFormError(t('create_form.resume_readback_mismatch'), 'CREATE_FORM_RESUME_READBACK_MISMATCH', {
|
|
6173
|
+
appType,
|
|
6174
|
+
formUuid,
|
|
6175
|
+
retryable: false,
|
|
6176
|
+
resultUnknown: true,
|
|
6177
|
+
completedStages,
|
|
6178
|
+
missingFieldCount: finalPlan.missing.length,
|
|
6179
|
+
diagnostics: finalPlan.conflicts,
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
completedStages.push('verify_final_schema');
|
|
6183
|
+
const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
|
|
6184
|
+
const output = {
|
|
6185
|
+
success: true,
|
|
6186
|
+
appType,
|
|
6187
|
+
formUuid,
|
|
6188
|
+
completedStages,
|
|
6189
|
+
requestedFieldCount: fields.length,
|
|
6190
|
+
existingFieldCount: plan.existing.length,
|
|
6191
|
+
addedFieldCount: plan.missing.length,
|
|
6192
|
+
finalFieldCount: collectResumeFieldEvidence(finalContainer.children).length,
|
|
6193
|
+
recoveredBlankShell,
|
|
6194
|
+
url: formUrl,
|
|
6195
|
+
};
|
|
6196
|
+
console.log(JSON.stringify(withBrowserHandoff(
|
|
6197
|
+
output,
|
|
6198
|
+
formUrl,
|
|
6199
|
+
{ stage: 'resume_form_success', title: formUuid },
|
|
6200
|
+
parsedArgs.browserOpenMode
|
|
6201
|
+
)));
|
|
6202
|
+
return output;
|
|
6203
|
+
}
|
|
6204
|
+
|
|
5914
6205
|
async function mainUpdate(parsedArgs, authRef) {
|
|
5915
6206
|
const { appType, formUuid, changesJsonOrFile } = parsedArgs;
|
|
5916
6207
|
|
|
@@ -6102,6 +6393,7 @@ async function run(args) {
|
|
|
6102
6393
|
{
|
|
6103
6394
|
create: mainCreate,
|
|
6104
6395
|
update: mainUpdate,
|
|
6396
|
+
resume: mainResume,
|
|
6105
6397
|
patch: mainPatch,
|
|
6106
6398
|
rule: mainRule,
|
|
6107
6399
|
validation: mainValidation,
|
|
@@ -449,6 +449,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
449
449
|
'create-form.bind-datasource',
|
|
450
450
|
'create-form.create',
|
|
451
451
|
'create-form.patch',
|
|
452
|
+
'create-form.resume',
|
|
452
453
|
'create-form.rule',
|
|
453
454
|
'create-form.update',
|
|
454
455
|
'create-form.validation',
|
|
@@ -689,6 +690,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
689
690
|
'create-form.create',
|
|
690
691
|
'create-form.batch',
|
|
691
692
|
'create-form.patch',
|
|
693
|
+
'create-form.resume',
|
|
692
694
|
'create-form.rule',
|
|
693
695
|
'create-form.update',
|
|
694
696
|
'create-form.validation',
|
|
@@ -1223,7 +1225,8 @@ const COMMAND_GROUPS = [
|
|
|
1223
1225
|
command('create-form.validate-fields', ['create-form', 'validate-fields'], 'create-form validate-fields <fieldsJsonOrFile> [--json]', 'help.cmd_validate_form', {
|
|
1224
1226
|
requiresLogin: false,
|
|
1225
1227
|
}),
|
|
1226
|
-
command('create-form.update', ['create-form', 'update'], 'create-form update <appType>
|
|
1228
|
+
command('create-form.update', ['create-form', 'update'], 'create-form update <appType> <formUuid> (<changesJsonOrFile> | --data-file <changesJsonOrFile>) [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_update_form'),
|
|
1229
|
+
command('create-form.resume', ['create-form', 'resume'], 'create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]', 'help.cmd_update_form'),
|
|
1227
1230
|
command('create-form.patch', ['create-form', 'patch'], 'create-form patch <appType> <formUuid> <patchJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
|
1228
1231
|
command('create-form.rule', ['create-form', 'rule'], 'create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
|
1229
1232
|
command('create-form.validation', ['create-form', 'validation'], 'create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
|
package/lib/core/locales/en.js
CHANGED
|
@@ -414,9 +414,9 @@ Examples:
|
|
|
414
414
|
create_opt_spec: ' --spec <file.json> Use a structured flow spec for complex automation nodes (dataUpdate/route, etc.)',
|
|
415
415
|
create_opt_data_form_uuid: ' --data-form-uuid <uuid> Target form UUID for a get-single-data node',
|
|
416
416
|
create_opt_data_condition: ' --data-condition <rule> Get-data condition: targetField:label:triggerField[:component[:opCode[:valueType]]]',
|
|
417
|
-
create_opt_get_self: ' --get-self Insert a get-self node (pid
|
|
417
|
+
create_opt_get_self: ' --get-self Insert a get-self node (process runtime uses pid, designer uses proc_inst_id; ordinary forms use form_inst_id on both sides)',
|
|
418
418
|
create_opt_get_self_field: ' --get-self-field <field> Override the trigger-side system field, default __masterdata_form_inst_id',
|
|
419
|
-
create_opt_get_self_query_field: ' --get-self-query-field <f> Override the query-side system field
|
|
419
|
+
create_opt_get_self_query_field: ' --get-self-query-field <f> Override the query-side system field (process: pid runtime / proc_inst_id designer; ordinary: form_inst_id)',
|
|
420
420
|
create_opt_add_data_form_uuid: ' --add-data-form-uuid <uuid> Target form UUID for an add-data node',
|
|
421
421
|
create_opt_add_data_assignment: ' --add-data-assignment <rule> Add-data assignment: targetField:valueType:value',
|
|
422
422
|
create_opt_initiate_approval_form_uuid: ' --initiate-approval-form-uuid <uuid> Target process-form UUID for an initiate-approval node',
|
|
@@ -432,6 +432,11 @@ Examples:
|
|
|
432
432
|
create_example2: ' openyida integration create APP_XXX FORM-XXX "Get self then notify" --get-self --publish',
|
|
433
433
|
create_missing_args: 'Missing required arguments.',
|
|
434
434
|
create_replace_required: 'Using --process-code fully replaces the existing flow. Pass --replace explicitly. Safe editing is not currently available; integration update only reports capability status.',
|
|
435
|
+
create_source_form_fetch_failed: 'Could not read the data-source form metadata; remote write stopped: {0}',
|
|
436
|
+
create_source_form_not_found: 'Could not find data-source form {0} in navigation; remote write stopped.',
|
|
437
|
+
create_source_form_type_unknown: 'Navigation did not return a verifiable data-source form type; remote write stopped.',
|
|
438
|
+
create_source_form_type_invalid: 'Navigation returned unsupported data-source form type "{0}"; remote write stopped.',
|
|
439
|
+
create_source_form_type_mismatch: 'Explicit formType={0} conflicts with navigation metadata {1}; remote write stopped.',
|
|
435
440
|
create_flow_name_too_long: 'Logic-flow names cannot exceed {0} characters (received {1}).',
|
|
436
441
|
create_invalid_events: 'No valid trigger event was recognized.',
|
|
437
442
|
create_no_receivers: 'No notification receiver or user field specified; no message node will be generated.',
|
|
@@ -449,7 +454,7 @@ Examples:
|
|
|
449
454
|
create_notify_content: 'Notification content: {0}',
|
|
450
455
|
create_data_form: 'Get-data form: {0}',
|
|
451
456
|
create_data_conditions: 'Get-data condition count: {0}',
|
|
452
|
-
create_get_self_summary: 'Get-self guardrail: {0} equals field {1}',
|
|
457
|
+
create_get_self_summary: 'Get-self guardrail: runtime query field {0} equals field {1}; process-form designer maps it to proc_inst_id, while ordinary forms keep the same field',
|
|
453
458
|
create_op_mode_publish: 'Mode: save and publish',
|
|
454
459
|
create_op_mode_draft: 'Mode: save draft only',
|
|
455
460
|
create_step: '[{0}/{1}] {2}',
|
|
@@ -822,7 +827,15 @@ Examples:
|
|
|
822
827
|
example_update: 'Example: openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"Note"}}]\'',
|
|
823
828
|
usage_label: 'Usage:',
|
|
824
829
|
usage_create_short: ' create: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
|
|
825
|
-
usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
|
|
830
|
+
usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile> or --data-file <changesJsonOrFile>',
|
|
831
|
+
resume_readback_failed: 'Unable to read the target form before resume.',
|
|
832
|
+
resume_ownership_unverified: 'The target form does not belong to the requested app; resume stopped.',
|
|
833
|
+
resume_identity_mismatch: 'The target form identity does not match the readback; resume stopped.',
|
|
834
|
+
resume_schema_invalid: 'The target form schema is unavailable.',
|
|
835
|
+
resume_container_invalid: 'The target form container is unavailable.',
|
|
836
|
+
resume_field_conflict: 'Existing fields conflict with the requested definition; resume stopped.',
|
|
837
|
+
resume_field_resolution_failed: 'Resume field resolution failed.',
|
|
838
|
+
resume_readback_mismatch: 'Resume readback did not confirm all requested fields.',
|
|
826
839
|
example_label: '\nExamples:',
|
|
827
840
|
fields_file_not_found: ' ❌ Fields definition file not found: ',
|
|
828
841
|
fields_format_invalid: 'Invalid fields definition format',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -410,9 +410,9 @@ openyida - 宜搭命令行工具
|
|
|
410
410
|
create_opt_spec: ' --spec <file.json> 使用结构化编排文件创建复杂自动化(dataUpdate/route 等)',
|
|
411
411
|
create_opt_data_form_uuid: ' --data-form-uuid <uuid> 插入获取单条数据节点的目标表单 UUID',
|
|
412
412
|
create_opt_data_condition: ' --data-condition <rule> 获取数据过滤条件:目标字段:字段名:触发字段[:组件[:操作符[:值类型]]]',
|
|
413
|
-
create_opt_get_self: ' --get-self
|
|
413
|
+
create_opt_get_self: ' --get-self 自动插入获取自身节点(流程表单运行态使用 pid、设计器使用 proc_inst_id;普通表单两侧使用 form_inst_id)',
|
|
414
414
|
create_opt_get_self_field: ' --get-self-field <field> 覆盖右侧触发事件系统字段,默认 __masterdata_form_inst_id',
|
|
415
|
-
create_opt_get_self_query_field: ' --get-self-query-field <f>
|
|
415
|
+
create_opt_get_self_query_field: ' --get-self-query-field <f> 覆盖左侧查询系统字段(流程表单默认 pid,普通表单默认 form_inst_id)',
|
|
416
416
|
create_opt_add_data_form_uuid: ' --add-data-form-uuid <uuid> 插入新增数据节点的目标表单 UUID',
|
|
417
417
|
create_opt_add_data_assignment: ' --add-data-assignment <rule> 新增数据赋值:目标字段:valueType:value',
|
|
418
418
|
create_opt_initiate_approval_form_uuid: ' --initiate-approval-form-uuid <uuid> 发起审批节点的目标流程表单 UUID',
|
|
@@ -428,6 +428,11 @@ openyida - 宜搭命令行工具
|
|
|
428
428
|
create_example2: ' openyida integration create APP_XXX FORM-XXX "获取自身后通知" --get-self --publish',
|
|
429
429
|
create_missing_args: '缺少必要参数。',
|
|
430
430
|
create_replace_required: '使用 --process-code 会整图替换已有逻辑流;必须显式传入 --replace。安全二次编辑当前不可用,integration update 仅报告 capability 状态。',
|
|
431
|
+
create_source_form_fetch_failed: '获取取数来源表单信息失败,已停止远端写入: {0}',
|
|
432
|
+
create_source_form_not_found: '获取取数来源表单信息失败,导航中未找到表单 {0},已停止远端写入',
|
|
433
|
+
create_source_form_type_unknown: '获取取数来源表单信息失败,导航未返回可确认的表单类型,已停止远端写入',
|
|
434
|
+
create_source_form_type_invalid: '获取取数来源表单信息失败,导航返回的表单类型“{0}”不是可取数表单,已停止远端写入',
|
|
435
|
+
create_source_form_type_mismatch: '获取取数来源表单信息失败,显式 formType={0} 与导航元数据 {1} 不一致,已停止远端写入',
|
|
431
436
|
create_flow_name_too_long: '逻辑流名称不能超过 {0} 个字符(当前 {1} 个)。',
|
|
432
437
|
create_invalid_events: '未识别到有效触发事件。',
|
|
433
438
|
create_no_receivers: '未指定通知接收人或成员字段,将不会生成消息通知节点。',
|
|
@@ -445,7 +450,7 @@ openyida - 宜搭命令行工具
|
|
|
445
450
|
create_notify_content: '通知内容: {0}',
|
|
446
451
|
create_data_form: '获取数据表单: {0}',
|
|
447
452
|
create_data_conditions: '获取数据条件数: {0}',
|
|
448
|
-
create_get_self_summary: '获取自身闭坑: {0}
|
|
453
|
+
create_get_self_summary: '获取自身闭坑: 运行态查询字段 {0} 等于字段 {1};流程表单设计器对应 proc_inst_id,普通表单设计器保持一致',
|
|
449
454
|
create_op_mode_publish: '操作模式: 保存并发布',
|
|
450
455
|
create_op_mode_draft: '操作模式: 仅保存草稿',
|
|
451
456
|
create_step: '[{0}/{1}] {2}',
|
|
@@ -793,7 +798,15 @@ openyida - 宜搭命令行工具
|
|
|
793
798
|
example_update: '示例:openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"备注"}}]\'',
|
|
794
799
|
usage_label: '用法:',
|
|
795
800
|
usage_create_short: ' 创建: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
|
|
796
|
-
usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
|
|
801
|
+
usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile> 或 --data-file <changesJsonOrFile>',
|
|
802
|
+
resume_readback_failed: '恢复前无法回读目标表单。',
|
|
803
|
+
resume_ownership_unverified: '目标表单不属于指定应用,已停止恢复。',
|
|
804
|
+
resume_identity_mismatch: '目标表单身份与回读结果不一致,已停止恢复。',
|
|
805
|
+
resume_schema_invalid: '目标表单 Schema 不可用。',
|
|
806
|
+
resume_container_invalid: '目标表单容器不可用。',
|
|
807
|
+
resume_field_conflict: '已有字段与请求定义冲突,恢复已停止。',
|
|
808
|
+
resume_field_resolution_failed: '恢复字段解析失败。',
|
|
809
|
+
resume_readback_mismatch: '恢复后的回读未确认全部请求字段。',
|
|
797
810
|
example_label: '\n示例:',
|
|
798
811
|
fields_file_not_found: ' ❌ 字段定义文件不存在: ',
|
|
799
812
|
fields_format_invalid: '字段定义格式不正确',
|