cloudcc-cli 2.2.6 → 2.2.8

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.
Files changed (122) hide show
  1. package/.cloudcc-cache.json +24 -20
  2. package/README.md +24 -0
  3. package/bin/cc.js +7 -0
  4. package/cloudcc-cli-dev/BACKEND_CODE.md +114 -0
  5. package/cloudcc-cli-dev/CLI_CHEATSHEET.md +90 -0
  6. package/cloudcc-cli-dev/INSTALL_AND_BOOTSTRAP.md +59 -0
  7. package/cloudcc-cli-dev/OBJECTS_AND_FIELDS.md +120 -0
  8. package/cloudcc-cli-dev/REQUIREMENTS_BREAKDOWN.md +98 -0
  9. package/cloudcc-cli-dev/SKILL.md +39 -0
  10. package/cloudcc-cli-dev/VUE_CUSTOM_COMPONENT.md +50 -0
  11. package/java/com/cloudcc/core/BaseException.java +100 -0
  12. package/java/com/cloudcc/core/BusiException.java +43 -0
  13. package/java/com/cloudcc/core/CCService.java +3 -1
  14. package/java/com/cloudcc/core/StringUtils.java +7 -0
  15. package/java/com/cloudcc/core/TimeUtil.java +33 -0
  16. package/java/com/cloudcc/core/UserInfo.java +9 -0
  17. package/package.json +7 -1
  18. package/pom.xml +1 -1
  19. package/src/api/backend-sdk-java.md +427 -0
  20. package/src/api/ccdk-sdk.md +1039 -0
  21. package/src/classes/doc.js +486 -0
  22. package/src/classes/index.js +1 -0
  23. package/src/mcp/cliRunner.js +61 -0
  24. package/src/mcp/index.js +41 -3
  25. package/src/mcp/tools/Application Creator/handler.js +7 -9
  26. package/src/mcp/tools/Approval/handler.js +34 -151
  27. package/src/mcp/tools/Class Creator/handler.js +18 -15
  28. package/src/mcp/tools/Class Detail Retriever/handler.js +8 -9
  29. package/src/mcp/tools/Class Editor Guide/handler.js +5 -19
  30. package/src/mcp/tools/Class List Retriever/handler.js +8 -3
  31. package/src/mcp/tools/Class Publisher/handler.js +7 -9
  32. package/src/mcp/tools/Class Puller/handler.js +6 -65
  33. package/src/mcp/tools/Client Script Detail Retriever/handler.js +12 -18
  34. package/src/mcp/tools/Client Script Editor Guide/handler.js +9 -605
  35. package/src/mcp/tools/Client Script List Retriever/handler.js +30 -33
  36. package/src/mcp/tools/Client Script Publisher/handler.js +12 -11
  37. package/src/mcp/tools/Client Script Puller/handler.js +23 -30
  38. package/src/mcp/tools/CloudCC Development Overview/handler.js +11 -5
  39. package/src/mcp/tools/Component Creator/handler.js +12 -11
  40. package/src/mcp/tools/Component Detail Retriever/handler.js +12 -9
  41. package/src/mcp/tools/Component Editor Guide/handler.js +5 -22
  42. package/src/mcp/tools/Component List Retriever/handler.js +21 -18
  43. package/src/mcp/tools/Component Publisher/handler.js +25 -3
  44. package/src/mcp/tools/Component Puller/handler.js +13 -16
  45. package/src/mcp/tools/Dev Environment Creator/handler.js +5 -72
  46. package/src/mcp/tools/Dev Environment Validator/handler.js +5 -66
  47. package/src/mcp/tools/Developer Key Setup Guide/handler.js +11 -20
  48. package/src/mcp/tools/JSP Migrator/handler.js +842 -0
  49. package/src/mcp/tools/Menu Creator/handler.js +7 -30
  50. package/src/mcp/tools/Object Creator/handler.js +14 -6
  51. package/src/mcp/tools/Object Fields Creator/handler.js +9 -10
  52. package/src/mcp/tools/Object Fields Retriever/handler.js +6 -3
  53. package/src/mcp/tools/Object List Retriever/handler.js +10 -7
  54. package/src/mcp/tools/Scheduled Class Creator/handler.js +12 -16
  55. package/src/mcp/tools/Scheduled Class Detail Retriever/handler.js +7 -9
  56. package/src/mcp/tools/Scheduled Class List Retriever/handler.js +21 -23
  57. package/src/mcp/tools/Scheduled Class Publisher/handler.js +7 -9
  58. package/src/mcp/tools/Scheduled Class Puller/handler.js +6 -70
  59. package/src/mcp/tools/Trigger Creator/handler.js +12 -20
  60. package/src/mcp/tools/Trigger Detail Retriever/handler.js +7 -9
  61. package/src/mcp/tools/Trigger Editor Guide/handler.js +10 -35
  62. package/src/mcp/tools/Trigger List Retriever/handler.js +12 -4
  63. package/src/mcp/tools/Trigger Publisher/handler.js +8 -11
  64. package/src/mcp/tools/Trigger Puller/handler.js +12 -17
  65. package/src/plugin/doc.js +801 -0
  66. package/src/plugin/get.js +0 -1
  67. package/src/plugin/index.js +1 -0
  68. package/src/plugin/publish1.js +34 -20
  69. package/src/plugin/pull.js +69 -31
  70. package/src/project/doc.js +378 -0
  71. package/src/project/index.js +1 -0
  72. package/src/script/doc.js +259 -0
  73. package/src/script/index.js +1 -0
  74. package/src/timer/index.js +1 -0
  75. package/src/triggers/doc.js +342 -0
  76. package/src/triggers/index.js +5 -0
  77. package/target/ccopenapi-0.0.4-classes.jar +0 -0
  78. package/target/ccopenapi-0.0.4.jar +0 -0
  79. package/target/classes/com/cloudcc/core/BaseException.class +0 -0
  80. package/target/classes/com/cloudcc/core/BusiException.class +0 -0
  81. package/target/classes/com/cloudcc/core/CCService.class +0 -0
  82. package/target/classes/com/cloudcc/core/StringUtils.class +0 -0
  83. package/target/classes/com/cloudcc/core/TimeUtil.class +0 -0
  84. package/target/classes/com/cloudcc/core/UserInfo.class +0 -0
  85. package/target/maven-archiver/pom.properties +1 -1
  86. package/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +3 -1
  87. package/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +3 -3
  88. package/template/lib/ccopenapi-0.0.4.jar +0 -0
  89. package/test/application.cli.test.js +30 -0
  90. package/test/classes.cli.test.js +121 -0
  91. package/test/fields.cli.test.js +69 -0
  92. package/test/mcp.cli.test.js +21 -0
  93. package/test/menu.cli.test.js +41 -0
  94. package/test/object.cli.test.js +64 -0
  95. package/test/plugin.cli.test.js +109 -0
  96. package/test/script.cli.test.js +101 -0
  97. package/test/timer.cli.test.js +107 -0
  98. package/test/trigger.cli.test.js +146 -0
  99. package/.vscode/settings.json +0 -3
  100. package/bin/mcp-svc.js +0 -13
  101. package/src/mcp/MCP/345/234/272/346/231/257/346/250/241/346/213/237.md +0 -8
  102. package/src/mcp/index-sse-svc.js +0 -126
  103. package/src/mcp/index-streamable-svc.js +0 -180
  104. package/src/mcp/tools/Class Detail Retriever/prompt.js +0 -37
  105. package/src/mcp/tools/Class Editor Guide/prompt.js +0 -468
  106. package/src/mcp/tools/Class Publisher/prompt.js +0 -40
  107. package/src/mcp/tools/Class Puller/prompt.js +0 -49
  108. package/src/mcp/tools/Client Script Creator/handler.js +0 -179
  109. package/src/mcp/tools/CloudCC Development Overview/prompt.js +0 -871
  110. package/src/mcp/tools/Component Editor Guide/prompt.js +0 -519
  111. package/src/mcp/tools/Component Publisher/prompt.js +0 -659
  112. package/src/mcp/tools/Dev Environment Creator/prompt.js +0 -273
  113. package/src/mcp/tools/Dev Environment Validator/prompt.js +0 -193
  114. package/src/mcp/tools/Developer Key Setup Guide/prompt.js +0 -71
  115. package/src/mcp/tools/Object Fields Retriever/prompt.js +0 -10
  116. package/src/mcp/tools/Object List Retriever/prompt.js +0 -10
  117. package/src/mcp/tools/ccdk/fetcher.js +0 -18
  118. package/src/mcp/tools/ccdk/handler.js +0 -98
  119. package/src/mcp/tools/ccdk/prompt.js +0 -453
  120. package/target/ccopenapi-0.0.3-classes.jar +0 -0
  121. package/target/ccopenapi-0.0.3.jar +0 -0
  122. package/template/lib/ccopenapi-0.0.3.jar +0 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * script 文档统一入口(全量一次性输出)
3
+ */
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ function generateFullMarkdownDoc() {
8
+ const lines = [
9
+ "# CloudCC 客户端脚本开发指南",
10
+ "",
11
+ "> 本文档参考 CloudCC 官方「客户端脚本」文档整理而成:[客户端脚本](https://help.cloudcc.cn/product03/ke-hu-duan-jiao-ben/)。 ",
12
+ "> 目标:指导开发者在不同页面(列表、新建/编辑、详情、全局)特定时机运行 JavaScript,基于 CCDK 能力实现字段控制、布局调整等前端增强。",
13
+ "",
14
+ "---",
15
+ "",
16
+ "## 1. 概念与入口",
17
+ "",
18
+ "### 1.1 什么是客户端脚本",
19
+ "",
20
+ "- 客户端脚本是在 **浏览器端**、特定页面和时机执行的一段 JavaScript。 ",
21
+ "- 依托 `$CCDK` 提供的桥接能力,可以:",
22
+ " - 显示提示信息",
23
+ " - 控制字段显隐 / 只读",
24
+ " - 调整页面 DOM 结构",
25
+ " - 在保存前/后执行校验或自定义逻辑",
26
+ "",
27
+ "### 1.2 进入客户端脚本设置页面",
28
+ "",
29
+ "1. 登录 CloudCC 系统 ",
30
+ "2. 点击右上角头像,选择「开发者平台」(仅管理员简档可见) ",
31
+ "3. 左侧菜单:`扩展 → 客户端脚本`",
32
+ "",
33
+ "---",
34
+ "",
35
+ "## 2. 创建第一个客户端脚本",
36
+ "",
37
+ "### 2.1 新建脚本",
38
+ "",
39
+ "1. 在「客户端脚本」列表页,点击右上角「新建」 ",
40
+ "2. 填写基本信息(名称、适用对象/页面/事件等),进入代码编辑页面 ",
41
+ "3. 在代码输入区输入示例代码:",
42
+ "",
43
+ "```javascript",
44
+ "$CCDK.CCMessage.showMessage('hello cloudCC');",
45
+ "```",
46
+ "",
47
+ "4. 点击右上角「保存」",
48
+ "",
49
+ "### 2.2 回到前台查看效果",
50
+ "",
51
+ "例如: ",
52
+ "- 配置为在「客户详情页 onLoad」执行 ",
53
+ "- 返回前台 → 打开客户菜单 → 进入任意一条客户详情 ",
54
+ "- 页面加载完成后,会看到 `hello cloudCC` 提示信息",
55
+ "",
56
+ "---",
57
+ "",
58
+ "## 3. 利用 devid 精确控制页面元素",
59
+ "",
60
+ "> 为方便控制页面元素,平台在关键 DOM 节点上添加了 `devid` 自定义属性,并使用特定业务 ID 绑定。可通过 F12 查看并用于脚本选择器。",
61
+ "",
62
+ "### 3.1 示例:禁用编辑页的「分级」字段",
63
+ "",
64
+ "#### 第一步:查看 devid",
65
+ "",
66
+ "1. 按 `F12` 打开浏览器开发者工具 ",
67
+ "2. 切换到「元素」(Elements) 面板 ",
68
+ "3. 使用元素选择器选中「分级」字段对应 DOM ",
69
+ "4. 在右侧/标签属性中查找并复制该元素的 `devid` 值备用",
70
+ "",
71
+ "#### 第二步:创建客户端脚本",
72
+ "",
73
+ "在客户端脚本中,根据 `devid` 操作 DOM:",
74
+ "",
75
+ "```javascript",
76
+ "// 方式一:通过动态插入样式禁用点击",
77
+ "let style = document.createElement('style');",
78
+ "style.type = 'text/css';",
79
+ "style.innerHTML = '[devid=\"ffe201100003855g6Ipz\"]{pointer-events:none}';",
80
+ "document.getElementsByTagName('head').item(0).appendChild(style);",
81
+ "",
82
+ "// 方式二:直接隐藏某个元素",
83
+ "let dd = document.querySelector('div[devid=\"ffe201100003428r65pk\"]');",
84
+ "if (dd) {",
85
+ " dd.style.display = \"none\";",
86
+ "}",
87
+ "```",
88
+ "",
89
+ "保存后回到前台,进入对应页面查看效果。",
90
+ "",
91
+ "### 3.2 示例:隐藏详情页某个字段",
92
+ "",
93
+ "同样通过 `devid` 精确定位字段,再注入样式:",
94
+ "",
95
+ "```javascript",
96
+ "// 隐藏并不占用空间",
97
+ "let style = document.createElement('style');",
98
+ "style.type = 'text/css';",
99
+ "style.innerHTML = '[devid=\"ffe20220523account01\"]{display:none !important}';",
100
+ "document.getElementsByTagName('head').item(0).appendChild(style);",
101
+ "```",
102
+ "",
103
+ "```javascript",
104
+ "// 隐藏但保留空间",
105
+ "let style = document.createElement('style');",
106
+ "style.type = 'text/css';",
107
+ "style.innerHTML = '[devid=\"ffe20220523account01\"]{visibility:hidden !important}';",
108
+ "document.getElementsByTagName('head').item(0).appendChild(style);",
109
+ "```",
110
+ "",
111
+ "### 3.3 示例:移动页面元素到其他位置",
112
+ "",
113
+ "```javascript",
114
+ "// 获取要移动的节点",
115
+ "const moveNode = document.querySelector('[devid=\"aee2024EFBBA5EEfs1Ta\"]');",
116
+ "// 目标节点",
117
+ "const targetNode = document.querySelector('[devid=\"adf2024D33E03DA6xzBj\"]');",
118
+ "// 目标节点的父节点",
119
+ "const targetParentNode = targetNode.parentNode;",
120
+ "",
121
+ "// 将需要移动的节点插入到目标节点前面",
122
+ "if (moveNode && targetNode && targetParentNode) {",
123
+ " targetParentNode.insertBefore(moveNode, targetNode);",
124
+ "}",
125
+ "```",
126
+ "",
127
+ "**注意**:DOM 可能尚未渲染完成,可结合定时器或 `MutationObserver` 等机制等待节点出现。",
128
+ "",
129
+ "---",
130
+ "",
131
+ "## 4. 页面类型与脚本事件映射",
132
+ "",
133
+ "> 系统页面大致分为「全局、列表、新建/编辑、详情」等类型,每种页面支持不同事件,脚本在对应时机执行。",
134
+ "",
135
+ "以下是官方提供的主要事件(简化说明):",
136
+ "",
137
+ "### 4.1 全局类型",
138
+ "",
139
+ "- **onCreate**",
140
+ " - 适用:PC / HS",
141
+ " - 函数签名:`function test($CCDK, obj = {})`",
142
+ " - 说明:项目启动时触发(业务页面代码尚未执行)",
143
+ "",
144
+ "- **onLoad**",
145
+ " - 适用:PC / HS",
146
+ " - 场景:全局 UI 首次展示后",
147
+ "",
148
+ "- **onDestroy**",
149
+ " - 适用:PC / HS",
150
+ " - 场景:关闭 Tab / 更改流程等导致当前应用关闭时",
151
+ "",
152
+ "### 4.2 视图页(列表页面)",
153
+ "",
154
+ "事件函数统一形如:`function test($CCDK, obj = {})`",
155
+ "",
156
+ "- **onCreated**",
157
+ " - 时机:进入视图页,但 DOM 还未创建 ",
158
+ "- **onLoad**",
159
+ " - 时机:首次进入,或点击搜索刷新列表数据后 ",
160
+ "- **onDestroy**",
161
+ " - 时机:离开列表页(如进入详情页等)",
162
+ "",
163
+ "### 4.3 新建/编辑页",
164
+ "",
165
+ "同样使用 `function test($CCDK, obj = {})`:",
166
+ "",
167
+ "- **onCreated**",
168
+ " - 时机:进入新建/编辑页面,但 DOM 尚未创建 ",
169
+ "- **onLoad**",
170
+ " - 时机:详情数据加载完成、页面渲染结束 ",
171
+ "- **onDestroy**",
172
+ " - 时机:离开新建/编辑页面 ",
173
+ "- **beforeSave**",
174
+ " - 参数:`obj.detailData` 为 key-value 形式的详情数据,**可以在这里修改** ",
175
+ " - 时机:点击保存按钮时触发,适合做前端校验与补充字段赋值 ",
176
+ "- **afterSave**",
177
+ " - 时机:保存接口请求完成后触发 ",
178
+ "- **onChange**",
179
+ " - 时机:编辑某个字段、光标失焦,且 value 有变化时触发",
180
+ "",
181
+ "### 4.4 详情页",
182
+ "",
183
+ "- **onLoad**",
184
+ " - 适用:PC / HS ",
185
+ " - 时机:获取详情基础数据并完成 DOM 渲染后触发 ",
186
+ "",
187
+ "---",
188
+ "",
189
+ "## 5. 实战建议与注意事项",
190
+ "",
191
+ "### 5.1 DOM 渲染时机",
192
+ "",
193
+ "- 部分页面在脚本执行时 DOM 可能尚未完全渲染,建议:",
194
+ " - 使用 `setTimeout` / 轮询检查目标节点是否存在 ",
195
+ " - 或配合 `MutationObserver` 监听 DOM 变化后再操作 ",
196
+ "",
197
+ "### 5.2 与 CCDK 的配合",
198
+ "",
199
+ "- `$CCDK` 提供了多种能力,如:",
200
+ " - `CCMessage`:消息提示 ",
201
+ " - `CCLoad`:脚本/样式加载 ",
202
+ " - 其他与对象/类交互的桥接能力 ",
203
+ "- 客户端脚本中应优先调用 `$CCDK` 而非自行拼接接口。",
204
+ "",
205
+ "### 5.3 与自定义组件的边界",
206
+ "",
207
+ "- 客户端脚本更适合:",
208
+ " - 简单的字段控制",
209
+ " - 小范围布局微调",
210
+ " - 轻量交互增强 ",
211
+ "- 更复杂的 UI、交互与业务逻辑推荐用「自定义组件」实现(详见 `custom-component-dev.md`)。",
212
+ "",
213
+ "---",
214
+ "",
215
+ "## 6. 客户端脚本开发 Checklist",
216
+ "",
217
+ "- [ ] 已在「开发者平台 → 客户端脚本」正确选择对象、页面类型与事件(onLoad / beforeSave 等) ",
218
+ "- [ ] 使用 `$CCDK` 能力而非手工拼接接口或直接依赖不稳定的全局变量 ",
219
+ "- [ ] 所有 DOM 操作都考虑了渲染时机,避免因节点不存在而报错 ",
220
+ "- [ ] 通过 `devid` 精确定位关键元素,避免依赖脆弱的层级或 class 选择器 ",
221
+ "- [ ] 对性能敏感操作(如定时器、频繁 DOM 查询)做了节流或销毁处理 ",
222
+ "- [ ] 已区分客户端脚本与自定义组件的职责,将复杂 UI / 交互放到组件中实现 ",
223
+ "- [ ] 在目标环境中完整测试:列表 / 新建 / 编辑 / 详情等场景,确认无脚本错误和布局异常 ",
224
+ "",
225
+ ];
226
+
227
+ const base = lines.join("\n");
228
+ const ccdkPath = path.join(__dirname, "..", "api", "ccdk-sdk.md");
229
+ let ccdkContent = "";
230
+ try {
231
+ ccdkContent = fs.readFileSync(ccdkPath, "utf8");
232
+ } catch (e) {
233
+ ccdkContent = `# CloudCC 前端 SDK(cloudcc-ccdk)使用指南\n\n(未找到文件:${ccdkPath})\n`;
234
+ }
235
+
236
+ return [
237
+ base,
238
+ "",
239
+ "---",
240
+ "",
241
+ "## 附录:CCDK SDK 速查",
242
+ "",
243
+ "> 来源:`src/api/ccdk-sdk.md`",
244
+ "",
245
+ ccdkContent.trim(),
246
+ "",
247
+ ].join("\n");
248
+ }
249
+
250
+ function doc() {
251
+ const content = generateFullMarkdownDoc();
252
+ console.log(content);
253
+ return content;
254
+ }
255
+
256
+ // 兼容旧调用方:仍提供同名入口(但都返回全量文档)
257
+ doc.getEditGuide = doc;
258
+
259
+ module.exports = doc;
@@ -5,6 +5,7 @@ cc.pull = require("./pull")
5
5
  cc.get = require("./get")
6
6
  cc.pullList = require("./pullList")
7
7
  cc.detail = require("./detail")
8
+ cc.doc = require("./doc")
8
9
  function main(action, argvs) {
9
10
  cc[action](argvs)
10
11
  }
@@ -4,6 +4,7 @@ cc.publish = require("./publish")
4
4
  cc.pull = require("./pull")
5
5
  cc.get = require("./get")
6
6
  cc.pullList = require("./pullList")
7
+ cc.detail = require("./detail")
7
8
  function Timer(action, argvs) {
8
9
  cc[action](argvs[2], argvs[3])
9
10
  }
@@ -0,0 +1,342 @@
1
+ /**
2
+ * triggers 文档统一入口(全量一次性输出)
3
+ */
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ function generateFullMarkdownDoc() {
8
+ const lines = [
9
+ "# CloudCC 触发器开发指南",
10
+ "",
11
+ "> 本文档参考 CloudCC 官方「触发器」文档整理而成:[触发器](https://help.cloudcc.cn/product03/hong-fa-qi/)。 ",
12
+ "> 目标:帮助开发者正确编写、调试和使用触发器,理解触发时机、可用变量、与自定义类/Email/时间工具的协同,以及避免循环调用等常见坑。",
13
+ "",
14
+ "---",
15
+ "",
16
+ "## 1. 概念与入口",
17
+ "",
18
+ "### 1.1 什么是触发器",
19
+ "",
20
+ "- 触发器是在特定数据操作事件发生**之前或之后**执行的一段 Java 代码。 ",
21
+ "- 典型触发时间:",
22
+ " - 在对象记录插入数据库之前 / 之后",
23
+ " - 在对象记录更新之前 / 之后",
24
+ " - 在对象记录删除之前 / 之后",
25
+ " - 在审批相关场景(`approval`)中 ",
26
+ "- 主要用于:",
27
+ " - 复杂字段校验(必填、多字段联动、一致性约束)",
28
+ " - 自动带值 / 派生字段写入",
29
+ " - 与其他对象联动更新",
30
+ "",
31
+ "### 1.2 触发器开发入口",
32
+ "",
33
+ "两种方式创建触发器:",
34
+ "",
35
+ "1. **通过开发者平台** ",
36
+ " - 登录系统 → 右上角头像 → 「开发者平台」(管理员简档可见) ",
37
+ " - 左侧菜单:`扩展 → 触发器 → 新建`",
38
+ "",
39
+ "2. **在对象后台设置中创建** ",
40
+ " - 进入目标对象的后台设置(对象管理) ",
41
+ " - 将页面滚动到底部,在「触发器」区域点击「新建`",
42
+ "",
43
+ "---",
44
+ "",
45
+ "## 2. 触发器配置项与触发时机",
46
+ "",
47
+ "### 2.1 基本配置",
48
+ "",
49
+ "- **名称**:汉字说明,用于描述触发器完成的功能 ",
50
+ "- **触发对象**:在哪个对象上执行触发器操作 ",
51
+ "- **触发时间(Trigger Event)**:决定在什么时机触发",
52
+ "",
53
+ "常用触发时间包括:",
54
+ "",
55
+ "- `beforeInsert`:新建/复制记录行满足触发条件前执行 ",
56
+ "- `beforeUpdate`:编辑记录行满足触发条件前执行 ",
57
+ "- `beforeUpsert`:新建和编辑/复制统一入口前执行 ",
58
+ "- `beforeDelete`:删除记录前执行 ",
59
+ "- `afterInsert`:新建/复制记录后执行(可获取记录 ID) ",
60
+ "- `afterUpdate`:修改记录后执行 ",
61
+ "- `afterUpsert`:新建或编辑/复制后执行 ",
62
+ "- `afterDelete`:删除记录后执行 ",
63
+ "- `afterInsertCommit`:插入事务提交后执行,**报错不会回滚主事务** ",
64
+ "- `afterUpdateCommit`:更新提交后执行,报错不会回滚 ",
65
+ "- `afterUpsertCommit`:保存提交后执行,报错不会回滚 ",
66
+ "- `afterDeleteCommit`:删除提交后执行,报错不会回滚 ",
67
+ "- `approval`:审批流程相关触发",
68
+ "",
69
+ "**提示**: ",
70
+ "`before*` 阶段通常用于**校验与字段赋值**;`after*` 阶段适合做**关联对象更新、日志记录、异步操作触发**等。",
71
+ "",
72
+ "---",
73
+ "",
74
+ "## 3. 触发器中可用变量与基础语法",
75
+ "",
76
+ "### 3.1 可直接使用的变量",
77
+ "",
78
+ "- `(UserInfo) userInfo`:当前操作用户信息 ",
79
+ "- `(Map) record_new`:当前记录的**最新值**(新建/修改后) ",
80
+ "- `(Map) record_old`:当前记录的**旧值**(修改前),在 `insert` 场景通常为 `null` ",
81
+ "- `trigger`:触发器上下文对象,可用于添加错误提示等",
82
+ "",
83
+ "### 3.2 常用语法与含义",
84
+ "",
85
+ "| 语法 | 返回值 | 含义 |",
86
+ "|----------------------------------------|----------|------------------------------------|",
87
+ "| `record_new.get(\"字段API\")` | String | 获取记录中字段的最新值 |",
88
+ "| `record_new.put(\"字段API\", \"字段值\")` | void | 为记录的某字段赋值 |",
89
+ "| `record_old.get(\"字段API\")` | String | 获取记录中字段的前一个值 |",
90
+ "| `record_old == null` | boolean | 在 `beforeUpsert/afterUpsert` 中识别是否为新建 |",
91
+ "| `trigger.addErrorMessage(\"提示内容\")` | void | 在保存前用于提示并中止保存 |",
92
+ "",
93
+ "**提示**: ",
94
+ "字段名使用**字段 API 名称**(如 `project_manager`、`start_date`),自定义字段一般以 `__c` 结尾。",
95
+ "",
96
+ "---",
97
+ "",
98
+ "## 4. 常见案例",
99
+ "",
100
+ "### 4.1 案例 1:字段联动带值(查询赋值)",
101
+ "",
102
+ "**需求** ",
103
+ "在项目对象中,将当前项目经理的邮箱自动带入本对象的 `email` 字段。 ",
104
+ "",
105
+ "**触发事件**:`beforeUpsert`",
106
+ "",
107
+ "**代码示例**",
108
+ "",
109
+ "```java",
110
+ "String managerId = record_new.get(\"project_manager\") == null ? \"\" : record_new.get(\"project_manager\") + \"\"; // 新值",
111
+ "String managerId_old = record_old == null || record_old.get(\"project_manager\") == null ? \"\" : record_old.get(\"project_manager\") + \"\"; // 旧值",
112
+ "",
113
+ "// 项目经理字段新值和老值不相同时判定为该字段值发生改变,需重新查询用户表获取邮箱带到本记录中",
114
+ "if (!managerId.equals(managerId_old)) {",
115
+ " List<CCObject> userlist = cquery(\"User\", \"id='\" + managerId + \"'\");",
116
+ " if (userlist.size() > 0) {",
117
+ " String email = userlist.get(0).get(\"email\") == null ? \"\" : userlist.get(0).get(\"email\") + \"\";",
118
+ " record_new.put(\"email\", email);",
119
+ " }",
120
+ "}",
121
+ "```",
122
+ "",
123
+ "要点:",
124
+ "- 使用 `record_new` / `record_old` 比较判断字段是否变更,**避免无谓查询与写入**。",
125
+ "- 查询用户对象时使用 `cquery(\"User\", ...)`。",
126
+ "",
127
+ "### 4.2 案例 2:复杂必填校验",
128
+ "",
129
+ "**需求** ",
130
+ "当项目经理不为空时,项目开始日期与结束日期必填,否则不允许保存。",
131
+ "",
132
+ "**触发时间**:`beforeUpsert`",
133
+ "",
134
+ "**代码示例**",
135
+ "",
136
+ "```java",
137
+ "String managerId = record_new.get(\"project_manager\") == null ? \"\" : record_new.get(\"project_manager\") + \"\"; // 项目经理",
138
+ "String start_date = record_new.get(\"start_date\") == null ? \"\" : record_new.get(\"start_date\") + \"\"; // 项目开始日期",
139
+ "String end_date = record_new.get(\"end_date\") == null ? \"\" : record_new.get(\"end_date\") + \"\"; // 项目结束日期",
140
+ "",
141
+ "if (!\"\".equals(managerId) && (\"\".equals(start_date) || \"\".equals(end_date))) {",
142
+ " trigger.addErrorMessage(\"请填写项目开始日期及项目结束日期\");",
143
+ "}",
144
+ "```",
145
+ "",
146
+ "要点:",
147
+ "- 通过 `trigger.addErrorMessage` 阻断保存并提示用户。",
148
+ "- 适合放在 `beforeInsert` / `beforeUpdate` / `beforeUpsert` 等阶段。",
149
+ "",
150
+ "---",
151
+ "",
152
+ "## 5. 进阶用法与注意事项",
153
+ "",
154
+ "### 5.1 触发器可直接引用的参数",
155
+ "",
156
+ "- `UserInfo userInfo`:当前操作用户 ",
157
+ "- `Map record_new` / `Map record_old`",
158
+ "",
159
+ "取字段值时直接使用字段 API 名称:",
160
+ "",
161
+ "```java",
162
+ "String name = record_new.get(\"name\") == null ? \"\" : record_new.get(\"name\") + \"\";",
163
+ "```",
164
+ "",
165
+ "### 5.2 循环调用限制",
166
+ "",
167
+ "触发器之间可以间接导致**循环调用**,例如:",
168
+ "- 在客户对象的 `update` 触发器中更新业务机会 ",
169
+ "- 在业务机会对象的 `update` 触发器中又更新客户(再次触发客户的 `update` 触发器) ",
170
+ "",
171
+ "如果在触发器中不加条件控制,可能形成无限循环。平台限制:",
172
+ "- **最多允许 3 次循环调用** ",
173
+ "- 超过 3 次系统会报错提示并终止执行",
174
+ "",
175
+ "建议:",
176
+ "- 在跨对象更新时增加「状态字段」或「字段变更判断」作为保护条件 ",
177
+ "- 避免在 A 的触发器中无条件更新 B,同时在 B 的触发器中又无条件更新 A ",
178
+ "- 将复杂联动逻辑尽量下沉到自定义类中集中处理",
179
+ "",
180
+ "### 5.3 触发器中发送邮件通知",
181
+ "",
182
+ "触发器可以使用 `SendEmail` 发送系统邮件:",
183
+ "",
184
+ "```java",
185
+ "SendEmail sendEmail = new SendEmail(userInfo);",
186
+ "",
187
+ "// 直接发送邮件",
188
+ "sendEmail.sendMailFromSystem(",
189
+ " new String[]{\"to@example.com\"}, // 收件人",
190
+ " new String[]{\"cc@example.com\"}, // 抄送",
191
+ " new String[]{\"bcc@example.com\"}, // 密送",
192
+ " \"邮件主题\",",
193
+ " \"邮件内容\",",
194
+ " true // 是否文本邮件",
195
+ ");",
196
+ "",
197
+ "// 使用邮件模板发送",
198
+ "sendEmail.sendEmailNew(",
199
+ " \"emailTemplateId\",",
200
+ " new String[]{\"to@example.com\"},",
201
+ " new String[]{\"cc@example.com\"},",
202
+ " new String[]{\"bcc@example.com\"},",
203
+ " record_new.get(\"id\") + \"\" // 关联记录 ID",
204
+ ");",
205
+ "```",
206
+ "",
207
+ "### 5.4 获取邮件模板内容",
208
+ "",
209
+ "```java",
210
+ "/**",
211
+ " * lang:语言",
212
+ " * emailtemplateid:邮件模板ID",
213
+ " * id:记录id(可选)",
214
+ " */",
215
+ "Map<String, String> m = this.getMessageByTemplateId(lang, emailtemplateid, record_new.get(\"id\") + \"\");",
216
+ "",
217
+ "// 邮件标题",
218
+ "String subject = m.get(\"subject\");",
219
+ "// 邮件内容",
220
+ "String content = m.get(\"context\");",
221
+ "// 邮件是否为文本形式",
222
+ "String isText = m.get(\"istext\");",
223
+ "```",
224
+ "",
225
+ "可以先获取模板内容,再根据业务需要调整后发送。",
226
+ "",
227
+ "### 5.5 TimeUtil:多时区时间支持",
228
+ "",
229
+ "与自定义类中用法相同,触发器也应使用 `TimeUtil` 来处理与时区相关的时间:",
230
+ "",
231
+ "- 获取当前时间(按用户时区):",
232
+ "",
233
+ "```java",
234
+ "TpSysTask task = new TpSysTask();",
235
+ "task.setBeginTime(TimeUtil.getNowDate(userInfo));",
236
+ "```",
237
+ "",
238
+ "- 获取带时区的 `SimpleDateFormat`:",
239
+ "",
240
+ "```java",
241
+ "SimpleDateFormat myDateFormat = TimeUtil.getSimpleDateFormat(\"yyyy-MM-dd\", userInfo);",
242
+ "```",
243
+ "",
244
+ "- 获取带时区的 `Calendar`:",
245
+ "",
246
+ "```java",
247
+ "Calendar cal = TimeUtil.getCalendar(userInfo);",
248
+ "```",
249
+ "",
250
+ "---",
251
+ "",
252
+ "## 6. 与 CCService / 自定义类的协同",
253
+ "",
254
+ "### 6.1 在触发器中使用 CCService",
255
+ "",
256
+ "触发器中同样可以实例化并使用 `CCService` 进行增删改查:",
257
+ "",
258
+ "```java",
259
+ "CCService cs = new CCService((UserInfo) userInfo);",
260
+ "",
261
+ "// 示例:根据当前记录字段查询机会",
262
+ "String accountId = record_new.get(\"khmc__c\") == null ? \"\" : record_new.get(\"khmc__c\") + \"\";",
263
+ "if (!\"\".equals(accountId)) {",
264
+ " List<CCObject> opps = cs.cquery(",
265
+ " \"Opportunity\",",
266
+ " \"khmc__c = '\" + accountId + \"'\",",
267
+ " \"jine__c desc\"",
268
+ " );",
269
+ " // TODO: 根据查询结果执行相应逻辑",
270
+ "}",
271
+ "```",
272
+ "",
273
+ "具体 `CCService` 能力详见 `custom-class-dev.md`。",
274
+ "",
275
+ "### 6.2 将复杂逻辑下沉到自定义类",
276
+ "",
277
+ "推荐模式:",
278
+ "",
279
+ "1. 触发器中只负责:",
280
+ " - 判断是否需要执行逻辑(字段是否变化、状态是否满足、避免循环等)",
281
+ " - 准备必需参数(如 `userInfo`、`record_new` / `record_old` 等)",
282
+ "2. 将主要业务逻辑封装到自定义类方法中:",
283
+ "",
284
+ "```java",
285
+ "// 触发器中",
286
+ "if (/* 条件满足 */) {",
287
+ " new ProjectService(userInfo).handleAfterUpdate(record_old, record_new);",
288
+ "}",
289
+ "```",
290
+ "",
291
+ "好处:",
292
+ "- 逻辑集中、可复用、易测试 ",
293
+ "- 触发器代码保持精简,可读性更高",
294
+ "",
295
+ "---",
296
+ "",
297
+ "## 7. 触发器开发 Checklist",
298
+ "",
299
+ "- [ ] 已选择正确的**触发对象**与**触发时间**(before/after/commit/approval) ",
300
+ "- [ ] 校验逻辑集中在 `before*` 阶段,并通过 `trigger.addErrorMessage` 给出清晰提示 ",
301
+ "- [ ] 使用 `record_new` / `record_old` 判断字段是否变化,避免不必要的数据库操作 ",
302
+ "- [ ] 涉及跨对象更新时,已经考虑并避免循环调用(尤其是双向更新) ",
303
+ "- [ ] 发送邮件时使用 `SendEmail` / 模板,且确认模板与收件人配置正确 ",
304
+ "- [ ] 与时间相关逻辑使用 `TimeUtil`,避免跨时区错误 ",
305
+ "- [ ] 复杂业务逻辑拆分到自定义类中,通过触发器调用,触发器本身保持简洁 ",
306
+ "- [ ] 已在测试环境完整验证:新建、编辑、删除、审批等全场景,确认不会出现循环触发或异常回滚问题 ",
307
+ "",
308
+ ];
309
+
310
+ const base = lines.join("\n");
311
+ const sdkPath = path.join(__dirname, "..", "api", "backend-sdk-java.md");
312
+ let sdkContent = "";
313
+ try {
314
+ sdkContent = fs.readFileSync(sdkPath, "utf8");
315
+ } catch (e) {
316
+ sdkContent = `# CloudCC 后端 SDK(Java)使用指南\n\n(未找到文件:${sdkPath})\n`;
317
+ }
318
+
319
+ return [
320
+ base,
321
+ "",
322
+ "---",
323
+ "",
324
+ "## 附录:后端 SDK(Java)速查",
325
+ "",
326
+ "> 来源:`src/api/backend-sdk-java.md`",
327
+ "",
328
+ sdkContent.trim(),
329
+ "",
330
+ ].join("\n");
331
+ }
332
+
333
+ function doc() {
334
+ const content = generateFullMarkdownDoc();
335
+ console.log(content);
336
+ return content;
337
+ }
338
+
339
+ // 兼容旧调用方:统一返回全量文档
340
+ doc.getEditGuide = doc;
341
+
342
+ module.exports = doc;
@@ -5,7 +5,12 @@ cc.pull = require("./pull")
5
5
  cc.get = require("./get")
6
6
  cc.pullList = require("./pullList")
7
7
  cc.detail = require("./detail")
8
+ cc.doc = require("./doc")
8
9
  function Classes(action, argvs) {
10
+ if (action === "detail") {
11
+ cc[action](argvs[2], argvs[3]);
12
+ return;
13
+ }
9
14
  cc[action](argvs)
10
15
  }
11
16
 
Binary file
@@ -1,3 +1,3 @@
1
1
  artifactId=ccopenapi
2
2
  groupId=com.cloudcc.core
3
- version=0.0.3
3
+ version=0.0.4