@stalfh233/omc-cli 0.4.35 → 0.4.37

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.
@@ -0,0 +1,42 @@
1
+ import { environment, commonRecovery } from "./shared.js";
2
+ const dictionaryKeyValue = Object.freeze({
3
+ id: "dictionary-key-value",
4
+ title: "建一个键值字典并用其 id 作字段下拉来源",
5
+ status: "offline-valid",
6
+ tool: "dictionary.save",
7
+ args: {
8
+ environment,
9
+ code: "visitType",
10
+ name: "回访方式",
11
+ dictionaryType: 0,
12
+ records: [
13
+ { code: "phone", name: "电话" },
14
+ { code: "visit", name: "上门" },
15
+ { code: "online", name: "线上" },
16
+ ],
17
+ },
18
+ preconditions: ["字典 code 尚未存在(存在须改传 id 更新);dictionaryType 0=KEY_VALUE / 1=TREE"],
19
+ conditionalRequirements: [
20
+ "**字段下拉绑定字典时 `checkedDictionary` 必须填字典 id(不是 name)**——前端 a-select 的 option value 就是 id;填 name 运行时会返回『字典已被禁用,无数据』、下拉空白(真机 ticket 02/34)。拿到 id:`dictionary.get`/`dictionary.names` 读回。",
21
+ "TREE 字典的 record 可带 `parentId`;给既有字典加记录也走 `dictionary.save`(带 id + 完整 records)。",
22
+ "字段侧用 `field.create` 的 `options.optionsSet = {optionsType:'dictionary',dictionary:{checkedDictionary:'<id>',checkedDictionaryCode:'<code>',useDictionariesData:[{code,name,id}]}}`;`choices` 是「手写选项」的另一条路,不要与字典混用。",
23
+ ],
24
+ priorToolReferences: [
25
+ { tool: "dictionary.names", args: { environment }, purpose: "查 code/name 冲突并读回既有 id(字典多时结果会溢成 artifact;只需 code/name,用 `artifact get <id> --keys` 或 --jq,别整份取)" },
26
+ { tool: "dictionary.get", args: { environment, code: "visitType" }, purpose: "按 code 精确查存在性(小结果,优于 dictionary.names 全量)" },
27
+ ],
28
+ dryRun: { supported: true, command: `omc call dictionary.save --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 预检 code 冲突/形状,不写" },
29
+ expectedReadback: { performed: { errcode: 0 }, readback: { present: true, dictionary: { id: "<dictionary.id>", code: "visitType", records: ["phone", "visit", "online"] } }, next: ["field.create --example dictionary-backed-single-select(用回读的 id)"] },
30
+ refusalRecovery: [
31
+ { code: "dictionary-create-duplicate", action: "code 已存在:从 dictionary.names 取 id 后改传 id 做更新" },
32
+ { code: "wire-business", action: "平台 `errcode -1 系统中已经存在相同名称的字典` = **字典名称全局唯一**(code 唯一还不够)——换一个 name(真机 2026-09-25:新 code + 旧 name 仍被拒)" },
33
+ { code: "capability-out-of-coverage", action: "确认 dictionaryType 0/1 与 records[{code,name}] 形状" },
34
+ ...commonRecovery,
35
+ ],
36
+ requiresEnvironmentDerivedIds: true,
37
+ prerequisiteData: { source: "dictionary.get / dictionary.names", fields: ["dictionary.id"] },
38
+ });
39
+ export const DICTIONARY_METADATA = Object.freeze({
40
+ "dictionary.save": Object.freeze({ recipes: [dictionaryKeyValue], relatedTools: ["dictionary.names", "dictionary.records", "field.create"] }),
41
+ });
42
+ //# sourceMappingURL=dictionary.js.map
@@ -0,0 +1,230 @@
1
+ import { environment, model, commonRecovery } from "./shared.js";
2
+ const fieldSerialNumber = Object.freeze({
3
+ id: "field-serial-number",
4
+ title: "单据号字段:表单 Serial Number 控件(seqNoSetting 分段构建器,按天/月/年重置原生)——不要落 SequenceService SPI 二开",
5
+ status: "offline-valid",
6
+ tool: "field.update",
7
+ args: {
8
+ environment,
9
+ schemaCode: "crm_contract",
10
+ code: "<字段 code>",
11
+ name: "合同编号",
12
+ options: {
13
+ formPropertyType: 100,
14
+ seqNoSetting: [
15
+ { type: "CONSTANT", value: "CT-" },
16
+ { type: "DATE", value: "yyyyMMdd" },
17
+ { type: "CONSTANT", value: "-" },
18
+ { type: "SEQNO", value: { startValue: 1, maxLength: 4, leftPad: true, resetDate: "DAY" } },
19
+ ],
20
+ },
21
+ },
22
+ preconditions: [
23
+ "需求是**编号自动生成**(前缀/日期/流水/按期重置),不是人工填写的编号字段——人工填写用普通短文本,别用本配方。",
24
+ "**单据号=表单 Serial Number 控件(字段类型 100 SequenceNo)**,不是 SHORT_TEXT 字段 + SequenceService SPI 二开(r5-crm 实证:SPI 路线在 8.6.26 无触发配置可写、属重造轮子;表单控件开箱即用)。",
25
+ ],
26
+ conditionalRequirements: [
27
+ "seqNoSetting 是**段数组**(分段构建器,顺序即拼接序):CONSTANT(固定前缀/分隔)/ DATE(value=Java 格式串 yyyyMMdd 等)/ SEQNO(流水)/ DATAITEM(取行内字段值)/ UID。设计师入口=控件属性「Document Number Configuration」。",
28
+ "SEQNO 段:startValue(起始)、maxLength(位数)、leftPad(补零)、**resetDate(重置策略 NONE/DAY/MONTH/YEAR——按天重置是原生能力)**。",
29
+ "写完必须 `field.publish` + `configure_form`(rebuild:true)重建表单并发布,控件才落到表单 attributes 的 options.seqNoSetting。",
30
+ "**编号生成时机=submit(表单提交管线)**:`data.save`(含 submit:true)不生成编号、行落 DRAFT;直接 `data.submit`(不带 bizObjectId=新建并提交)生成编号且行 COMPLETED(r5-crm 真机:CT-20261009-0001→0002 双跑)。",
31
+ "验证:连续两次 data.submit,编号应 +1 且格式符合段拼接;跨天重置由 resetDate 保证(勿自建 Redis 计数)。",
32
+ ],
33
+ priorToolReferences: [{ tool: "field.get", args: { environment, schemaCode: "<模型>", code: "<字段 code>" }, purpose: "确认字段存在且未配置过 seqNoSetting" }],
34
+ dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 options 形状,零 wire" },
35
+ expectedReadback: { performed: { errcode: 0 }, readback: { fieldPresent: true, optionsContain: "seqNoSetting+formPropertyType:100" }, next: ["data.submit 双跑 → 编号 0001/0002、行 COMPLETED"] },
36
+ refusalRecovery: [
37
+ { code: "field-options-freeform", action: "options 是自由对象——形状错误不会被出门校验拦,务必对照本配方逐键核对(type 全大写、resetDate 大写枚举)" },
38
+ ...commonRecovery,
39
+ ],
40
+ requiresEnvironmentDerivedIds: false,
41
+ });
42
+ const formConditionalRequired = Object.freeze({
43
+ id: "form-conditional-required",
44
+ title: "表单层条件必填:金额大于阈值时说明必填(字段 options)",
45
+ status: "requires-prior-readback",
46
+ tool: "field.update",
47
+ args: {
48
+ environment,
49
+ schemaCode: model,
50
+ code: "leaveReason",
51
+ name: "请假事由",
52
+ options: { requiredFormula: '{leave_amount} 大于 {"valueType":"custom","value":5000}' },
53
+ },
54
+ preconditions: ["目标字段与作为条件的字段都已发布", "这是门户表单交互层(真实门户打开表单、改条件字段值来验证控件是否变必填);**不要用 datarule.create**——实测 type4 不会让门户控件变必填,且符号式经 historyOpt 会让表单渲染崩溃"],
55
+ conditionalRequirements: ["条件串:`{字段} 操作符 [值]`(操作符两侧留空格)。操作符用**中文**(真实设计器产出):等于/不等于/大于/小于/大于等于/小于等于/包含/不包含/包含全部/包含任意一个/等于任意一个/为空/不为空;也接受符号 == != > >= <= Contains IsEmpty。为空/不为空**不带值**(`{reason} 不为空`),其余带 JSON 值 {\"valueType\":...,\"value\":...}。多条件用 ` && `、多组用 ` || `。**不是** eq/gt/isnull——会让门户渲染 SyntaxError"],
56
+ priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认条件字段与目标字段都已发布" }],
57
+ dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验字段 options 形状,不写入" },
58
+ expectedReadback: { readback: { field: { code: "leaveReason", options: { requiredFormula: "<protocol>" } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
59
+ refusalRecovery: [
60
+ { code: "capability-out-of-coverage", action: "条件串用中文或符号操作符(等于/大于/不为空 或 == />/IsEmpty);恒必填用字面量 \"true\"" },
61
+ ...commonRecovery,
62
+ ],
63
+ requiresEnvironmentDerivedIds: false,
64
+ });
65
+ const formAlwaysReadonly = Object.freeze({
66
+ id: "form-always-readonly",
67
+ title: "表单层恒只读字段(字段 options.readonlyFormula:true)",
68
+ status: "offline-valid",
69
+ tool: "field.update",
70
+ args: { environment, schemaCode: model, code: "approvalLevel", name: "审批级别", options: { readonlyFormula: true } },
71
+ preconditions: ["目标字段已发布"],
72
+ conditionalRequirements: ["恒只读用布尔 readonlyFormula:true;条件只读用 readonlyCondition 条件串"],
73
+ priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" }],
74
+ dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 readonlyFormula 是布尔", },
75
+ expectedReadback: { readback: { field: { code: "approvalLevel", options: { readonlyFormula: true } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
76
+ refusalRecovery: [{ code: "capability-out-of-coverage", action: "readonlyFormula 必须是布尔 true;条件只读才用 readonlyCondition" }, ...commonRecovery],
77
+ requiresEnvironmentDerivedIds: false,
78
+ });
79
+ const formConditionalReadonly = Object.freeze({
80
+ id: "form-conditional-readonly",
81
+ title: "表单层条件只读(字段 options.readonlyCondition)",
82
+ status: "offline-valid",
83
+ tool: "field.update",
84
+ args: {
85
+ environment,
86
+ schemaCode: model,
87
+ code: "approvalLevel",
88
+ name: "审批级别",
89
+ options: { readonlyCondition: '{status} 等于 {"valueType":"specifyOption","value":"已通过"}' },
90
+ },
91
+ preconditions: ["目标字段与条件字段已发布"],
92
+ conditionalRequirements: ["条件串协议同必填;恒只读改用 readonlyFormula:true"],
93
+ priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" }],
94
+ dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验条件串协议" },
95
+ expectedReadback: { readback: { field: { code: "approvalLevel", options: { readonlyCondition: "<protocol>" } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
96
+ refusalRecovery: [{ code: "capability-out-of-coverage", action: "使用中文或符号操作符与三段条件串" }, ...commonRecovery],
97
+ requiresEnvironmentDerivedIds: false,
98
+ });
99
+ const numericTwoDecimals = Object.freeze({
100
+ id: "numeric-two-decimals",
101
+ title: "数值字段(金额,两位小数 / 整数)— scale 必填",
102
+ status: "offline-valid",
103
+ tool: "field.create",
104
+ args: { environment, schemaCode: model, code: "applyAmount", name: "申请金额", propertyType: "NUMERICAL", options: { scale: 3 } },
105
+ preconditions: ["模型已存在"],
106
+ conditionalRequirements: ["NUMERICAL 必须显式 options.scale:scale = 小数位数(0=整数, 1=1位, 2=2位 … 8=8位);缺 scale 门户显示裸 decimal 且工具会拒绝。"],
107
+ priorToolReferences: [{ tool: "discover_environment", args: { environment }, purpose: "确认目标模型已存在" }],
108
+ dryRun: { supported: true, command: `omc call field.create --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 propertyType/scale/治理,不创建" },
109
+ expectedReadback: { readback: { draftFieldPresent: true }, next: ["field.publish"] },
110
+ refusalRecovery: [{ code: "capability-out-of-coverage", action: "NUMERICAL 必须带 options.scale;金额两位小数用 scale:2,整数用 scale:0" }, ...commonRecovery],
111
+ requiresEnvironmentDerivedIds: false,
112
+ });
113
+ const dictionarySelection = Object.freeze({
114
+ id: "dictionary-backed-single-select",
115
+ title: "先读字典,再创建字典-backed 单选字段",
116
+ status: "requires-prior-readback",
117
+ tool: "field.create",
118
+ args: {
119
+ environment,
120
+ schemaCode: model,
121
+ code: "status",
122
+ name: "状态",
123
+ propertyType: "DROPDOWN_BOX",
124
+ options: {
125
+ optionsSet: {
126
+ optionsType: "dictionary",
127
+ dictionary: {
128
+ checkedDictionary: "<dictionary.id>",
129
+ checkedDictionaryCode: "<dictionary.code>",
130
+ useDictionariesData: [{ code: "<record.code>", name: "<record.name>", id: "<record.id>" }],
131
+ },
132
+ },
133
+ },
134
+ },
135
+ preconditions: ["目标模型已存在;字段 code 尚未存在;字典及其至少一个启用记录可由只读发现取得"],
136
+ conditionalRequirements: ["若字典树有 parentId,dictionary.records 必须带 parentId;否则用 dictionary.records 的 id 或 code"],
137
+ priorToolReferences: [
138
+ { tool: "dictionary.names", args: { environment }, purpose: "选择环境中的 dictionary id/name/code;checkedDictionary 必须填 **id**(前端 a-select 的 option value 就是 a.id;填 name 运行时 getEnableRecordsByDictionaryId 会返回『字典已被禁用,无数据』,下拉空白)" },
139
+ { tool: "dictionary.records", args: { environment, code: "<dictionary.code>", enabledOnly: true }, purpose: "取得 useDictionariesData;替换 recipe 中的环境 ID" },
140
+ ],
141
+ dryRun: { supported: true, command: `omc call field.create --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 只做 schema/语义/治理预检,不创建字段" },
142
+ expectedReadback: { performed: { dryRun: true }, readback: { draftFieldPresent: false, next: "field.publish" } },
143
+ refusalRecovery: [
144
+ { code: "field-family-options-invalid", action: "确保 options.optionsSet.dictionary 的 checkedDictionary、checkedDictionaryCode 和 useDictionariesData 来自同一字典" },
145
+ { code: "duplicate-field", action: "先 field.list 读取草稿/发布面;已有字段改用 field.update" },
146
+ ...commonRecovery,
147
+ ],
148
+ requiresEnvironmentDerivedIds: true,
149
+ prerequisiteData: { source: "dictionary.names → dictionary.records(enabledOnly:true)", fields: ["dictionary.code", "record.code", "record.id"] },
150
+ });
151
+ const childTable = Object.freeze({
152
+ id: "minimal-child-table",
153
+ title: "创建带两个标量列的子表字段",
154
+ status: "offline-valid",
155
+ tool: "field.create",
156
+ args: {
157
+ environment,
158
+ schemaCode: model,
159
+ code: "<model>_items",
160
+ name: "明细",
161
+ propertyType: "CHILD_TABLE",
162
+ subFields: [
163
+ { code: "itemName", name: "名称", propertyType: "SHORT_TEXT" },
164
+ { code: "itemQty", name: "数量", propertyType: "NUMERICAL", options: { scale: 1 } },
165
+ ],
166
+ },
167
+ preconditions: ["目标模型已存在;父字段 code 和子列 codes 尚未存在;父模型字段发布后再配置表单"],
168
+ conditionalRequirements: [
169
+ "**子表 `code` 会成为全局子 schema code——必须模型作用域唯一(如 `<model>_items`),不要用通用名如 `items`/`purchase_items`,否则撞名时平台只回 errcode 40000「调用数据库发生异常」(实测)**;",
170
+ "子表列不得是 CHILD_TABLE、OCR、ELECTRONIC_SIGN、RELEVANCE_DATA 或 FORMULA;关系列必须另带 relativeCode 与 options;子列 NUMERICAL 同样必须带 options.scale",
171
+ ],
172
+ priorToolReferences: [],
173
+ dryRun: { supported: true, command: `omc call field.create --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 验证 subFields 并跳过环境写入" },
174
+ expectedReadback: { readback: { field: { code: "items", propertyType: "CHILD_TABLE", subSchema: { properties: ["itemName", "itemQty"] } } }, next: ["field.publish", "form.draft", "form.publish"] },
175
+ refusalRecovery: [
176
+ { code: "capability-out-of-coverage", action: "补齐 subFields,并移除不支持的嵌套/高级子列族" },
177
+ { code: "child-table-update-unsupported", action: "子列用 schemaCode=items 的 field.create/update;不要向父字段 update 传 options/choices/subFields" },
178
+ ...commonRecovery,
179
+ ],
180
+ requiresEnvironmentDerivedIds: false,
181
+ });
182
+ const businessModelDropdown = Object.freeze({
183
+ id: "business-model-dropdown",
184
+ title: "用另一个模型的数据作下拉来源(businessModel)",
185
+ status: "requires-prior-readback",
186
+ tool: "field.create",
187
+ args: {
188
+ environment,
189
+ schemaCode: model,
190
+ code: "category",
191
+ name: "类别",
192
+ propertyType: "DROPDOWN_BOX",
193
+ options: {
194
+ optionsSet: {
195
+ optionsType: "businessModel",
196
+ businessModel: {
197
+ schemaCode: "<source.model>",
198
+ sheetDataItem: "<source.businessField>",
199
+ appCode: "<app.code>",
200
+ },
201
+ },
202
+ },
203
+ },
204
+ preconditions: [
205
+ "目标模型与来源模型都已存在",
206
+ "来源模型的显示字段已 field.publish 并已 form.publish(未发布字段不可作为下拉来源)",
207
+ ],
208
+ conditionalRequirements: [
209
+ "sheetDataItem 指向来源模型上真正承载业务值的字段;**不要用系统字段 name** —— 它是平台自动生成的数据标题(人名+模型名+时间),下拉项会显示这串标题而不是业务值",
210
+ "orderByFields/orderType 可选,用于下拉项排序;conditions 可选,用于过滤来源行",
211
+ ],
212
+ priorToolReferences: [
213
+ { tool: "discover_environment", args: { environment, full: true }, purpose: "取得来源模型 schemaCode 与 app.code" },
214
+ { tool: "field.list", args: { environment, schemaCode: "<source.model>", published: true, fields: ["<source.businessField>"] }, purpose: "确认显示字段已发布并取其 code" },
215
+ ],
216
+ dryRun: { supported: true, command: `omc call field.create --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 optionsSet.businessModel 的形状,不创建字段" },
217
+ expectedReadback: { readback: { field: { code: "category", propertyType: "DROPDOWN_BOX", options: { optionsSet: { optionsType: "businessModel" } } } }, next: ["field.publish", "form.draft", "form.publish"] },
218
+ refusalRecovery: [
219
+ { code: "field-family-options-invalid", action: "确保 optionsSet.businessModel 带 schemaCode 与 sheetDataItem,且 schemaCode 是已存在的模型" },
220
+ { code: "duplicate-field", action: "先 field.list 读取草稿/发布面;已有字段改用 field.update" },
221
+ ...commonRecovery,
222
+ ],
223
+ requiresEnvironmentDerivedIds: true,
224
+ prerequisiteData: { source: "discover_environment → field.list(published:true)", fields: ["source.model", "source.businessField", "app.code"] },
225
+ });
226
+ export const FIELD_METADATA = Object.freeze({
227
+ "field.update": Object.freeze({ recipes: [fieldSerialNumber, formConditionalRequired, formAlwaysReadonly, formConditionalReadonly], relatedTools: ["field.list", "field.publish", "form.draft", "form.publish"] }),
228
+ "field.create": Object.freeze({ recipes: [numericTwoDecimals, dictionarySelection, childTable, businessModelDropdown], relatedTools: ["dictionary.names", "dictionary.records", "discover_environment", "field.list", "field.publish", "form.draft", "configure_form", "form.publish"] }),
229
+ });
230
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1,19 @@
1
+ import { environment, model, commonRecovery } from "./shared.js";
2
+ const configureFormRecipe = Object.freeze({
3
+ id: "publish-default-form",
4
+ title: "把已发布字段绑进默认表单并发布(高层编排)",
5
+ status: "offline-valid",
6
+ tool: "configure_form",
7
+ args: { environment, schemaCode: model, publish: true },
8
+ preconditions: ["字段已 field.publish"],
9
+ conditionalRequirements: ["表单层条件必填/只读请用 field.update 的 options(form-conditional-required / form-always-readonly recipe),不是本编排器"],
10
+ priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" }],
11
+ dryRun: { supported: true, command: `omc call configure_form --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 预检,不写" },
12
+ expectedReadback: { readback: { formPublished: true }, next: ["listview.configure(默认视图已存在,勿 listview.create)", "field.update(表单层条件)"] },
13
+ refusalRecovery: [{ code: "capability-out-of-coverage", action: "先发布字段再发表单" }, ...commonRecovery],
14
+ requiresEnvironmentDerivedIds: false,
15
+ });
16
+ export const FORM_METADATA = Object.freeze({
17
+ "configure_form": Object.freeze({ recipes: [configureFormRecipe], relatedTools: ["field.list", "field.update", "listview.create", "listview.get"] }),
18
+ });
19
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1,31 @@
1
+ import { environment, model, commonRecovery } from "./shared.js";
2
+ const listViewConfigure = Object.freeze({
3
+ id: "configure-default-list",
4
+ title: "配置模型的默认列表视图(列/排序;默认视图已自动存在)",
5
+ status: "requires-prior-readback",
6
+ tool: "listview.configure",
7
+ args: { environment, schemaCode: model, fieldCodes: ["applyNo", "applyAmount", "applyNote"] },
8
+ preconditions: ["字段已 field.publish(视图与表单脚手架都依赖已发布字段)", "默认视图在 model.create 时由平台自动生成,code = schemaCode"],
9
+ conditionalRequirements: [
10
+ "**省略 `code` 即配置默认视图(code = schemaCode);不要先 listview.create——那会多出一个自定义视图。** 只有确实需要第二个视图时才 listview.create。",
11
+ "`fieldCodes` 只列业务字段,可给 propertyCode 或字段显示名;系统列由平台决定。未知引用被拒(listview-columns-unknown-field)。",
12
+ "数值列 displayFormat 由字段 0 基 `options.scale` 推导(0=整数,1=1位,2=2位…);本工具会原地修正既有列的 displayFormat。",
13
+ "`query/update` 自动 publish=TRUE——视图无草稿/发布两态,无需独立发布步骤。",
14
+ ],
15
+ priorToolReferences: [
16
+ { tool: "listview.list", args: { environment, schemaCode: model }, purpose: "确认默认视图 code 与是否已有多视图" },
17
+ { tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" },
18
+ ],
19
+ dryRun: { supported: true, command: `omc call listview.configure --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 预检 fieldCodes/形状,不写" },
20
+ expectedReadback: { readback: { code: "<schemaCode>", added: ["applyNo", "applyAmount"], columns: "…" }, next: ["data.list 验证读面"] },
21
+ refusalRecovery: [
22
+ { code: "listview-columns-unknown-field", action: "字段未发布或名字写错;先 field.create + field.publish 或改引用" },
23
+ { code: "listview-not-found", action: "默认视图应已存在;用 listview.list 确认 code,必要时 listview.create" },
24
+ ...commonRecovery,
25
+ ],
26
+ requiresEnvironmentDerivedIds: false,
27
+ });
28
+ export const LISTVIEW_METADATA = Object.freeze({
29
+ "listview.configure": Object.freeze({ recipes: [listViewConfigure], relatedTools: ["listview.list", "listview.get", "field.list", "field.publish"] }),
30
+ });
31
+ //# sourceMappingURL=list-view.js.map
@@ -0,0 +1,34 @@
1
+ import { environment, commonRecovery } from "./shared.js";
2
+ const configureModelRecipe = Object.freeze({
3
+ id: "model-with-fields-form",
4
+ title: "一次建模型 + 多字段 + 发布默认表单(高层编排)",
5
+ status: "offline-valid",
6
+ tool: "configure_model",
7
+ args: {
8
+ environment,
9
+ name: "采购申请",
10
+ code: "purchaseApply",
11
+ applicationCode: "<applicationCode>",
12
+ fields: [
13
+ { code: "applyNo", name: "申请编号", propertyType: "SHORT_TEXT" },
14
+ { code: "applyAmount", name: "申请金额", propertyType: "NUMERICAL", options: { scale: 2 } },
15
+ { code: "applyNote", name: "申请说明", propertyType: "LONG_TEXT" },
16
+ ],
17
+ publishForm: true,
18
+ },
19
+ preconditions: ["应用 code 已确认(discover_environment)"],
20
+ conditionalRequirements: [
21
+ "fields[].propertyType 用族名;NUMERICAL 必须 options.scale(scale = 小数位数:金额两位小数用 scale:2,整数用 scale:0)",
22
+ "子表/单选/人员等复杂族建议改用低层 field.create(configure_model 的 fields[] 只覆盖标量族最稳)",
23
+ ],
24
+ priorToolReferences: [{ tool: "discover_environment", args: { environment }, purpose: "取 applicationCode 与已有模型" }],
25
+ dryRun: { supported: true, command: `omc call configure_model --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 走 schema/语义/治理预检,不创建" },
26
+ expectedReadback: { readback: { modelPresent: true, publishedFields: ["applyNo", "applyAmount", "applyNote"] }, next: ["field.update(表单层条件)", "listview.configure(默认视图已自动存在,直接配置,勿 create)"] },
27
+ refusalRecovery: [{ code: "capability-out-of-coverage", action: "标量族之外改用低层 field.create;NUMERICAL 补 options.scale" }, ...commonRecovery],
28
+ requiresEnvironmentDerivedIds: true,
29
+ prerequisiteData: { source: "discover_environment", fields: ["applicationCode"] },
30
+ });
31
+ export const MODEL_METADATA = Object.freeze({
32
+ "configure_model": Object.freeze({ recipes: [configureModelRecipe], relatedTools: ["discover_environment", "field.update", "listview.create", "listview.get"] }),
33
+ });
34
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1,32 @@
1
+ import { environment, model } from "./shared.js";
2
+ const onlinejsHighlightScript = Object.freeze({
3
+ id: "field-value-driven-style",
4
+ title: "按字段值驱动另一控件样式(生命周期外壳 + valueChange 订阅)",
5
+ status: "offline-valid",
6
+ tool: "onlinejs.compose",
7
+ args: {
8
+ environment,
9
+ schemaCode: model,
10
+ sheetCode: "<默认表单 code == 模型 code>",
11
+ mode: "replace",
12
+ source: "(function(form){ var amt = form.getControl('amountCode'); var party = form.getControl('targetCode'); function refresh(v){ party.options.style = Number(v) > 100000 ? 'color: red;' : ''; } form.on('onRendered', function(){ refresh(amt.getValue()); amt.valueChange.subscribe(function(change){ refresh(change.value); }); }); })",
13
+ },
14
+ preconditions: [
15
+ "控件编码先 model.inspect/field.list 确认(示例 amountCode/targetCode 换成真实字段 code)",
16
+ "完整 `(function(form){…})` 生命周期外壳必须 mode:'replace'(merge 模式收外壳会被拒并指路 replace——r38 实证)",
17
+ ],
18
+ conditionalRequirements: [
19
+ "事件模型:`form.on('onRendered', …)` 初始化 + `<控件>.valueChange.subscribe(function(change){ change.value })` 监听;改样式用 `control.options.style`",
20
+ "**脚本含引号/较长时不要塞 --args 内联**——写 JSON 参数文件走 `--args-file <path>`(shell 引号拆参 r38 实证)",
21
+ "深入教学面:`.claude/skills/form-online-js/references/`(lifecycle-api/control-api)",
22
+ ],
23
+ priorToolReferences: [{ tool: "form.get", args: { environment, schemaCode: model }, purpose: "确认表单已发布与控件编码" }],
24
+ dryRun: { supported: true, command: `omc call onlinejs.compose --env ${environment} --dry-run --args-file compose.json`, notes: "dry-run 校验外壳/合并模式,不写平台" },
25
+ expectedReadback: { readback: { deliveryState: "DELIVERED", published: true, scriptElementPresent: true, htmlRenderCompat: { draft: { ok: true }, published: { ok: true } } } },
26
+ refusalRecovery: [{ code: "merge-mode-requires-shell", action: "完整外壳改 mode:'replace'(六元素模板保留,仅整体替换 customScript)" }],
27
+ requiresEnvironmentDerivedIds: false,
28
+ });
29
+ export const ONLINEJS_METADATA = Object.freeze({
30
+ "onlinejs.compose": Object.freeze({ recipes: [onlinejsHighlightScript], relatedTools: ["form.get", "form.draft", "form.publish"] }),
31
+ });
32
+ //# sourceMappingURL=online-js.js.map
@@ -0,0 +1,113 @@
1
+ import { environment, model, commonRecovery } from "./shared.js";
2
+ const workflowSave = Object.freeze({
3
+ id: "workflow-backed-data-save",
4
+ title: "用 data.save 创建 DRAFT workflow row",
5
+ status: "requires-prior-readback",
6
+ tool: "data.save",
7
+ args: { environment, schemaCode: model, workflowCode: "<workflow.code>", formData: { name: "recipe draft" } },
8
+ preconditions: ["模型默认表单/字段和 workflow 已发布;应用可用;formData 不带已存在 id"],
9
+ conditionalRequirements: ["workflow draft-save 总是 INSERT;不要把已存在记录 id 放进 formData"],
10
+ priorToolReferences: [
11
+ { tool: "workflow.get_published", args: { environment, workflowCode: "<workflow.code>" }, purpose: "确认 published-face marker" },
12
+ ],
13
+ dryRun: { supported: true, command: `omc call data.save --env ${environment} --dry-run --args '<recipe.args>'`, notes: "save dry-run 只校验输入与治理;真实两段式身份只能由执行后的 readback 提供" },
14
+ expectedReadback: { performed: { formType: "save-draft-workflow", captures: ["bizObjectId", "workflowInstanceId", "workItemId"] }, readback: { sequenceStatus: "DRAFT", next: "data.submit" } },
15
+ refusalRecovery: [
16
+ { code: "save-workflow-draft-with-id-unsupported", action: "移除已存在 id;workflow draft-save 只支持 INSERT" },
17
+ { code: "app-not-published", action: "只读确认应用发布状态后修复发布边界;不要盲目重试 runtime 写入" },
18
+ ...commonRecovery,
19
+ ],
20
+ requiresEnvironmentDerivedIds: true,
21
+ prerequisiteData: { source: "workflow.get_published; data.save authoritative readback", fields: ["workflow.code", "bizObjectId", "workflowInstanceId", "workItemId"] },
22
+ });
23
+ const plainSubmit = Object.freeze({
24
+ id: "plain-data-submit",
25
+ title: "无流程模型:新建并提交一调完成(COMPLETED,触发 Create 规则)——正式数据的标准路径",
26
+ status: "offline-valid",
27
+ tool: "data.submit",
28
+ args: { environment, schemaCode: model, formData: { name: "正式记录" } },
29
+ preconditions: ["模型无已发布流程(有流程走 workflow-backed-data-submit)", "模型+表单已发布"],
30
+ conditionalRequirements: [
31
+ "**不带 bizObjectId 即新建并提交**(工具内部建稿+直提,一次调用 COMPLETED);已有草稿要直提时才带 data.save 读回的 bizObjectId;不要手造 id",
32
+ "**目标模型已发布流程时行为不同(r36 实证)**:无 workItemId 的 data.submit = 一调新建并**启动流程**(PROCESSING,读回返回首审批待办 workItemId)——合法路径非错误;要『先草稿后提交/中途改稿』用两段式 data.save(草稿读回三元身份)→ data.submit(workItemId)",
33
+ ],
34
+ priorToolReferences: [],
35
+ dryRun: { supported: true, command: `omc call data.submit --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 验证参数关系,不推进状态" },
36
+ expectedReadback: { performed: { errcode: 0 }, readback: { sequenceStatus: "COMPLETED" } },
37
+ refusalRecovery: [
38
+ { code: "submit-biz-object-absent", action: "bizObjectId 在平台无行(304001)——回 data.save 重建草稿后再直提" },
39
+ { code: "invalid-input", action: "带了 workItemId/workflowInstanceId 时必须同时带 bizObjectId(fail-closed)" },
40
+ ],
41
+ notes: "r23 复盘修正:本配方曾教先 save 再 submit 两步——正确路径是无 id 一调(业务约定:正式数据一律 data.submit)。Create/Update 规则只在 COMPLETED 分发(DomainServiceImpl.java:240-248);规则效果以提交读回/data.load 为准。data.save 定位=草稿/暂存",
42
+ requiresEnvironmentDerivedIds: true,
43
+ });
44
+ const workflowSubmit = Object.freeze({
45
+ id: "workflow-backed-data-submit",
46
+ title: "消费 data.save 读回身份并提交 workflow work item",
47
+ status: "requires-prior-readback",
48
+ tool: "data.submit",
49
+ args: { environment, schemaCode: model, workflowCode: "<workflow.code>", bizObjectId: "<data.save.performed.bizObjectId>", workflowInstanceId: "<data.save.performed.workflowInstanceId>", workItemId: "<data.save.performed.workItemId>", formData: { name: "recipe draft" } },
50
+ preconditions: ["同一 recipe 的 data.save 已成功并读回 DRAFT;workflowInstanceId/workItemId/bizObjectId 均非空"],
51
+ conditionalRequirements: ["workItemId 或 workflowInstanceId 出现时必须传 bizObjectId;身份只能复制 data.save 的 authoritative readback"],
52
+ priorToolReferences: [
53
+ { tool: "data.save", args: { environment, schemaCode: model, workflowCode: "<workflow.code>", formData: { name: "recipe draft" } }, purpose: "提供三个环境生成的提交身份" },
54
+ { tool: "workitem.list", args: { environment }, purpose: "当 data.save 未直接返回 workItemId 时按 workflowInstanceId 解析待办" },
55
+ ],
56
+ dryRun: { supported: true, command: `omc call data.submit --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 验证两段式参数关系,不推进 workflow" },
57
+ expectedReadback: { performed: { formType: "1" }, readback: { sequenceStatus: "COMPLETED", workflowInstanceId: "<same-as-data.save>", workItemId: "<same-as-data.save>" } },
58
+ refusalRecovery: [
59
+ { code: "submit-biz-object-id-required", action: "把 data.save 的 bizObjectId 原样传给 data.submit" },
60
+ { code: "submit-existing-workflow-unsupported", action: "不要用旧行 + workflowCode 启动;先 data.save 新建 DRAFT,再用返回 workItemId 提交" },
61
+ { code: "submit-target-absent", action: "用 data.load 确认 bizObjectId 属于 schemaCode 后停止盲重试" },
62
+ ...commonRecovery,
63
+ ],
64
+ requiresEnvironmentDerivedIds: true,
65
+ prerequisiteData: { source: "data.save authoritative readback; workitem.list fallback", fields: ["bizObjectId", "workflowInstanceId", "workItemId"] },
66
+ });
67
+ const dataListFilter = Object.freeze({
68
+ id: "list-filter-equality",
69
+ title: "按等值条件读一批业务数据(filter DSL 的可运行最小形状)",
70
+ status: "offline-valid",
71
+ tool: "data.list",
72
+ args: { environment, schemaCode: model, filter: { op: "eq", field: "status", value: "OPEN" }, size: 20 },
73
+ preconditions: ["schemaCode 的字段已 field.publish(filter 叶子的 propertyType 从发布面读)"],
74
+ conditionalRequirements: [
75
+ "filter 是**一个**子句 `{op, field, value?}`;op ∈ eq/neq/gt/lt/gte/lte/like/notlike/in/notin/between/notbetween/isnull/isnotnull(camelCase 别名 equals/notEquals/contains/isNull/isNotNull 等价——r36 体检发现别名合法但只列规范名会被误判『静默接受坏 op』)。",
76
+ "多条件 `filters:[…]`(AND);分组 `filterGroup:{op:'and'|'or',clauses:[…]}`;系统/外部字段用 rawCondition。",
77
+ ],
78
+ priorToolReferences: [
79
+ { tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认要过滤的字段 code 已发布" },
80
+ ],
81
+ dryRun: { supported: false, command: `omc call data.list --env ${environment} --args '<recipe.args>'`, notes: "只读,无 dry-run;直接调用即读回" },
82
+ expectedReadback: { readback: { total: 1, rows: "…" } },
83
+ refusalRecovery: [{ code: "invalid-filter-DSL", action: "按错误里的 minimalValidFilter/acceptedOps 修正 op/field/value" }],
84
+ requiresEnvironmentDerivedIds: false,
85
+ });
86
+ const dataDeleteSweep = Object.freeze({
87
+ id: "sweep-all-readback",
88
+ title: "清空模型数据并权威验证(list 取 ids → delete → 复验 total:0)",
89
+ status: "offline-valid",
90
+ tool: "data.delete",
91
+ args: { environment, schemaCode: model, bizObjectIds: ["<data.list 逐条 id>"] },
92
+ preconditions: [
93
+ "先 data.list 取全量 ids(page 0 起、size 够大或翻页);**submit 后平台会同时保留 DRAFT 与 COMPLETED 两类行(r30 实证 5 次提交 = 10 行),清理对象按 data.list 全量**",
94
+ ],
95
+ conditionalRequirements: [
96
+ "参数名是 `bizObjectIds`(数组)——不是 ids/bizObjectId(单条删除另有其参)",
97
+ "id 已被删时幂等 no-op,重复清理安全",
98
+ ],
99
+ priorToolReferences: [
100
+ { tool: "data.list", args: { environment, schemaCode: model }, purpose: "取全量 bizObjectIds(含 DRAFT 行)" },
101
+ ],
102
+ dryRun: { supported: true, command: `omc call data.delete --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 出删除计划不落库" },
103
+ expectedReadback: { readback: { deleted: "N", modelResidue: { total: 0 } }, next: ["data.list 复验 total:0 / items:[](权威清空判据)"] },
104
+ refusalRecovery: [{ code: "wire-business", action: "模型须已创建且有已发布字段;运行面 50000 时先 model.inspect modelPresent 判存在" }],
105
+ requiresEnvironmentDerivedIds: false,
106
+ });
107
+ export const RUNTIME_DATA_METADATA = Object.freeze({
108
+ "data.save": Object.freeze({ recipes: [workflowSave], relatedTools: ["workflow.get_published", "data.submit", "workitem.list"] }),
109
+ "data.submit": Object.freeze({ recipes: [plainSubmit, workflowSubmit], relatedTools: ["data.save", "workitem.list", "data.load"] }),
110
+ "data.list": Object.freeze({ recipes: [dataListFilter], relatedTools: ["data.load", "data.delete", "listview.configure", "field.list"] }),
111
+ "data.delete": Object.freeze({ recipes: [dataDeleteSweep], relatedTools: ["data.list", "data.load", "data.submit"] }),
112
+ });
113
+ //# sourceMappingURL=runtime-data.js.map
@@ -0,0 +1,8 @@
1
+ // 配方共享占位符与公共恢复项(唯一事实源——各 lane 配方从这里 import,勿复制防漂移)。
2
+ export const commonRecovery = Object.freeze([
3
+ Object.freeze({ code: "tool-input-invalid", action: "修正 recipe.args 后重新 dry-run;inputSchema 是参数权威" }),
4
+ Object.freeze({ code: "write-lock-held", action: "等待当前写锁释放后重试;不要并行重放写入" }),
5
+ ]);
6
+ export const environment = "<environment>";
7
+ export const model = "omc_recipe_model";
8
+ //# sourceMappingURL=shared.js.map