cloudcc-cli 2.3.8 → 2.4.0
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/.cursor/skills/{cloudcc-cli-dev → dev-guide}/SKILL.md +5 -1
- package/README.md +82 -1
- package/bin/cc.js +2 -1
- package/bin/index.js +1 -0
- package/cloudcc-dev-skill/SKILL.md +31 -8
- package/cloudcc-dev-skill/cloudcc-dev-html.md +42 -0
- package/cloudcc-dev-skill/config.json +2 -2
- package/mcp/index.js +27 -3
- package/mcp/tools/JSP Migrator/handler.js +51 -866
- package/mcp/tools/Object Creator/handler.js +14 -4
- package/mcp/tools/Object Fields Creator/handler.js +149 -3
- package/package.json +1 -1
- package/src/classes/docs/devguide.md +758 -364
- package/src/classes/docs/introduction.md +279 -143
- package/src/fields/buildFieldData.js +692 -0
- package/src/fields/create.js +10 -170
- package/src/fields/detail.js +37 -0
- package/src/fields/docs/devguide.md +168 -44
- package/src/fields/docs/introduction.md +2 -0
- package/src/fields/fields/A.js +3 -2
- package/src/fields/fields/AD.js +4 -2
- package/src/fields/fields/B.js +8 -5
- package/src/fields/fields/C.js +13 -5
- package/src/fields/fields/D.js +4 -4
- package/src/fields/fields/E.js +10 -5
- package/src/fields/fields/ENC.js +27 -8
- package/src/fields/fields/ENCD.js +27 -8
- package/src/fields/fields/F.js +4 -4
- package/src/fields/fields/FL.js +8 -4
- package/src/fields/fields/H.js +4 -4
- package/src/fields/fields/IMG.js +23 -5
- package/src/fields/fields/J.js +21 -6
- package/src/fields/fields/L.js +32 -8
- package/src/fields/fields/LT.js +23 -6
- package/src/fields/fields/M.js +2 -2
- package/src/fields/fields/MR.js +2 -2
- package/src/fields/fields/N.js +31 -8
- package/src/fields/fields/P.js +13 -5
- package/src/fields/fields/Q.js +42 -12
- package/src/fields/fields/S.js +19 -7
- package/src/fields/fields/SCORE.js +9 -4
- package/src/fields/fields/T.js +4 -4
- package/src/fields/fields/U.js +18 -5
- package/src/fields/fields/X.js +20 -6
- package/src/fields/fields/Y.js +17 -4
- package/src/fields/index.js +2 -0
- package/src/fields/update.js +148 -0
- package/src/jsp/analyze.js +17 -0
- package/src/jsp/doc.js +18 -0
- package/src/jsp/docs/devguide.md +111 -0
- package/src/jsp/docs/introduction.md +50 -0
- package/src/jsp/docs.js +21 -0
- package/src/jsp/index.js +14 -0
- package/src/jsp/migration.js +871 -0
- package/src/jsp/split.js +17 -0
- package/src/object/create.js +36 -10
- package/src/object/docs/devguide.md +6 -3
- package/src/project/docs/devguide.md +1 -1
- package/src/timer/docs/devguide.md +849 -400
- package/src/timer/docs/introduction.md +343 -231
- package/src/triggers/docs/devguide.md +929 -352
- package/src/triggers/docs/introduction.md +640 -369
- package/src/version/listModuleCommands.js +6 -0
- package/test/fields.cli.test.js +3 -1
- package/test/jsp.cli.test.js +70 -0
- package/test/object.cli.test.js +9 -1
|
@@ -1,541 +1,935 @@
|
|
|
1
|
-
# CloudCC
|
|
1
|
+
# CloudCC 自定义类 AI 开发规范
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> 官方「自定义类」文档整理而成:[自定义类](https://help.cloudcc.cn/product03/zi-ding-yi-lei/)。\
|
|
5
|
-
> 目标:指导开发者在 CloudCC 平台上编写 Java 自定义类,熟练使用
|
|
6
|
-
> `CCService`、`CCObject`、`SendEmail`、`DevLogger`、`TimeUtil`
|
|
7
|
-
> 等工具类,并理解类在触发器、定时作业、自定义组件中的调用方式。
|
|
3
|
+
## 1. 目的
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
本文档用于指导 AI 在 CloudCC 项目中编写自定义类时,如何做出稳定、可维护、可发布的实现。
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
适用范围:
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
`classes/<类名>/`、不要复制粘贴整套类文件夹、不要私自篡改 `config.json` 里的
|
|
18
|
-
`id` 或破坏版本字段,否则容易导致发布失败、拉取覆盖异常或与线上一致性不一致。
|
|
9
|
+
- 新建自定义类
|
|
10
|
+
- 修改现有自定义类
|
|
11
|
+
- 为按钮、触发器、定时类、自定义页面、组件提供后端能力
|
|
12
|
+
- 编写通用服务类、工具类、集成类、流程编排类
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
标记之间编写与修改业务代码;其余与平台同步相关的操作一律走命令。
|
|
14
|
+
本文档面向 AI,因此重点强调:
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
- 什么时候应该使用自定义类
|
|
17
|
+
- AI 写代码时必须遵守哪些硬规则
|
|
18
|
+
- SDK 该怎么用
|
|
19
|
+
- 代码结构应该怎么组织
|
|
20
|
+
- 哪些做法必须避免
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
## 2. 编写依据
|
|
23
|
+
|
|
24
|
+
本文档基于以下材料整理:
|
|
25
|
+
|
|
26
|
+
- CloudCC 官方后端 SDK 参考:`CCObject`、`UserInfo`、`CCService`、`SendEmail`、`DevLogger`、`TimeUtil`
|
|
27
|
+
|
|
28
|
+
## 3. AI 的总体职责
|
|
29
|
+
|
|
30
|
+
AI 在编写 CloudCC 自定义类时,应把自己视为“服务端业务能力设计者”,而不是“只补一段能跑代码的脚本工具”。
|
|
31
|
+
|
|
32
|
+
AI 需要同时满足以下目标:
|
|
33
|
+
|
|
34
|
+
- 功能正确
|
|
35
|
+
- 结构清晰
|
|
36
|
+
- 可复用
|
|
37
|
+
- 易排错
|
|
38
|
+
- 易发布
|
|
39
|
+
|
|
40
|
+
## 4. 什么时候应该使用自定义类
|
|
41
|
+
|
|
42
|
+
满足以下任一情况时,AI 应优先选择自定义类实现:
|
|
43
|
+
|
|
44
|
+
- 逻辑需要在服务端执行
|
|
45
|
+
- 一个动作会影响多个对象
|
|
46
|
+
- 逻辑会被多个入口复用
|
|
47
|
+
- 需要复杂规则计算
|
|
48
|
+
- 需要流程状态推进
|
|
49
|
+
- 需要调用外部系统
|
|
50
|
+
- 需要处理附件、文件、目录
|
|
51
|
+
- 需要统一日志、异常、时间处理
|
|
52
|
+
- 需要自动通知、提醒、待办协同
|
|
53
|
+
- 需要接入 AI 或分析能力
|
|
54
|
+
|
|
55
|
+
以下场景不要优先上自定义类:
|
|
56
|
+
|
|
57
|
+
- 只是简单展示
|
|
58
|
+
- 只是轻量页面交互
|
|
59
|
+
- 只是简单字段联动
|
|
60
|
+
- 标准配置即可完成
|
|
61
|
+
|
|
62
|
+
## 5. AI 必须遵守的硬规则
|
|
63
|
+
|
|
64
|
+
### 5.1 只能通过 cloudcc-cli 管理类目录
|
|
65
|
+
|
|
66
|
+
AI 不得手工创建或复制 `classes/<类名>/` 目录,不得私自改动 `config.json` 中的 `id` 或版本信息。
|
|
67
|
+
|
|
68
|
+
必须使用以下命令:
|
|
27
69
|
|
|
28
70
|
```bash
|
|
29
71
|
cloudcc create classes <name>
|
|
30
72
|
cloudcc publish classes <name>
|
|
31
73
|
cloudcc pull classes <name>
|
|
32
|
-
cloudcc
|
|
33
|
-
cloudcc detail classes <name>
|
|
34
|
-
cloudcc detail classes "" <id>
|
|
35
|
-
cloudcc pullList classes <id> <projectPath>
|
|
36
|
-
cloudcc delete classes <nameOrId> [projectPath]
|
|
37
|
-
cloudcc doc classes <introduction|devguide>
|
|
74
|
+
cloudcc delete classes <nameOrId>
|
|
38
75
|
```
|
|
39
76
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- `name`:Java 类名,与 `classes/<name>/` 目录名一致。
|
|
43
|
-
- `listQueryJson`:可选;列表查询条件的 JSON 经 `encodeURI(JSON.stringify(...))`
|
|
44
|
-
编码后传入。不传时使用默认分页参数。
|
|
45
|
-
- `projectPath`:项目根路径,可选;不传则使用当前工作目录。
|
|
46
|
-
- `id`:线上类 ID。`detail`
|
|
47
|
-
在仅查服务器时可将类名置空:`cloudcc detail classes "" <id>`。`pullList` 用 ID
|
|
48
|
-
将线上类落到指定项目的 `classes/` 下。
|
|
49
|
-
- `nameOrId`:本地目录名,或类 ID。若本地存在 `config.json` 且含
|
|
50
|
-
`id`,删除时会优先用其中的服务器 ID。
|
|
51
|
-
|
|
52
|
-
### 0.2 命令说明摘要
|
|
53
|
-
|
|
54
|
-
| 命令 | 作用 |
|
|
55
|
-
| ---------- | ----------------------------------------------------------------------------------- |
|
|
56
|
-
| `create` | 在 `classes/<name>/` 生成主类、测试类、`config.json` 模板 |
|
|
57
|
-
| `publish` | 将本地 SOURCE 区域源码提交到服务器;首次成功后会写回 `config.json` 的 `id` |
|
|
58
|
-
| `pull` | 按本地 `config.json` 的 `id` 从服务器拉取源码,覆盖主类中 SOURCE 区域(需已发布过) |
|
|
59
|
-
| `get` | 查询线上类列表(JSON 输出) |
|
|
60
|
-
| `detail` | 按类名优先读本地;或仅按 `id` 读服务器详情 |
|
|
61
|
-
| `pullList` | 按类 `id` 将线上类拉取到指定 `projectPath` 下的 `classes/<类名>/` |
|
|
62
|
-
| `delete` | 调用接口删除服务器上的类;参数可为本地类名或类 ID |
|
|
63
|
-
| `doc` | 输出 `introduction` 或 `devguide`(`devguide` 含附录 SDK 速查) |
|
|
64
|
-
|
|
65
|
-
### 0.3 推荐操作顺序
|
|
77
|
+
### 5.2 只能在 SOURCE 区域内写业务代码
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
# 1) 查看线上已有类(可选,避免重名或确认 ID)
|
|
69
|
-
cloudcc get classes
|
|
79
|
+
AI 修改现有类时,只能编辑主类中的:
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
```java
|
|
82
|
+
// @SOURCE_CONTENT_START
|
|
83
|
+
// @SOURCE_CONTENT_END
|
|
84
|
+
```
|
|
73
85
|
|
|
74
|
-
|
|
75
|
-
cloudcc publish classes MyClass
|
|
86
|
+
之间的内容。
|
|
76
87
|
|
|
77
|
-
|
|
78
|
-
cloudcc pull classes MyClass
|
|
88
|
+
不得破坏:
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
- 包路径
|
|
91
|
+
- 类目录结构
|
|
92
|
+
- `config.json`
|
|
93
|
+
- SOURCE 标记外的框架代码
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
cloudcc delete classes MyClass
|
|
85
|
-
```
|
|
95
|
+
### 5.3 必须保留 UserInfo 上下文
|
|
86
96
|
|
|
87
|
-
|
|
97
|
+
自定义类必须显式接收 `UserInfo`,并通过它构造平台能力对象。
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
标准写法:
|
|
100
|
+
|
|
101
|
+
```java
|
|
102
|
+
private UserInfo userInfo;
|
|
103
|
+
private CCService cs;
|
|
104
|
+
|
|
105
|
+
public XxxClass(UserInfo userInfo) {
|
|
106
|
+
this.userInfo = userInfo;
|
|
107
|
+
this.cs = new CCService(userInfo);
|
|
108
|
+
}
|
|
92
109
|
```
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
### 5.4 触发器、定时类、页面、按钮应做薄入口
|
|
95
112
|
|
|
96
|
-
|
|
113
|
+
AI 不能把大量核心逻辑直接堆在按钮入口、触发器入口、页面接口入口中。
|
|
97
114
|
|
|
98
|
-
|
|
115
|
+
推荐模式:
|
|
99
116
|
|
|
100
|
-
|
|
117
|
+
- 入口层只做参数接收、基础校验、结果返回
|
|
118
|
+
- 复杂逻辑沉到服务方法或独立服务类
|
|
101
119
|
|
|
102
|
-
|
|
103
|
-
- 可被以下模块调用:
|
|
104
|
-
- 自定义按钮
|
|
105
|
-
- 触发器
|
|
106
|
-
- 定时类 / 定时作业
|
|
107
|
-
- 自定义组件(通过 CCDK 请求自定义类)
|
|
108
|
-
- 典型用途:
|
|
109
|
-
- 封装复杂业务逻辑
|
|
110
|
-
- 查询 / 写入对象数据
|
|
111
|
-
- 调用自定义设置、发送邮件、记录开发日志
|
|
120
|
+
### 5.5 必须显式处理异常
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
调用 `CCService` 等平台方法时,AI 不能默认“不会失败”。
|
|
114
123
|
|
|
115
|
-
|
|
124
|
+
必须:
|
|
116
125
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
- 显式 `throws Exception` 或 `try/catch`
|
|
127
|
+
- 在关键路径记录日志
|
|
128
|
+
- 保留失败原因
|
|
129
|
+
- 对外返回明确结果,而不是悄悄吞错
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
### 5.6 涉及时间必须使用 TimeUtil
|
|
122
132
|
|
|
123
|
-
|
|
133
|
+
AI 编写代码时,凡是时间写库、时间比较、格式化、Calendar 处理,都不能默认直接用本地时区对象。
|
|
124
134
|
|
|
125
|
-
|
|
135
|
+
必须优先使用:
|
|
126
136
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* author:*** on 2022-10-12
|
|
134
|
-
* 客户类
|
|
135
|
-
*/
|
|
136
|
-
public class AccountClass {
|
|
137
|
-
private CCService cs;
|
|
138
|
-
private UserInfo userInfo;
|
|
139
|
-
|
|
140
|
-
public AccountClass(UserInfo userInfo) {
|
|
141
|
-
this.userInfo = userInfo;
|
|
142
|
-
cs = new CCService(userInfo);
|
|
143
|
-
}
|
|
137
|
+
- `TimeUtil.getNowDate(userInfo)`
|
|
138
|
+
- `TimeUtil.getUserTimeZone(userInfo)`
|
|
139
|
+
- `TimeUtil.getSimpleDateFormat(format, userInfo)`
|
|
140
|
+
- `TimeUtil.getCalendar(userInfo)`
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
JSONObject rtninfo = new JSONObject(); // 返回结果
|
|
147
|
-
JSONArray dataList = new JSONArray(); // 数据列表
|
|
148
|
-
boolean flag = false;
|
|
149
|
-
try {
|
|
150
|
-
// 查询客户对象数据
|
|
151
|
-
String sql = "select name, leixing, fenji, hangye from Account where is_deleted='0' ";
|
|
152
|
-
List<CCObject> accountList = cs.cqlQuery("Account", sql);
|
|
153
|
-
|
|
154
|
-
for (int i = 0; i < accountList.size(); i++) {
|
|
155
|
-
String name = accountList.get(i).get("name") == null ? "" : accountList.get(i).get("name").toString();
|
|
156
|
-
String type = accountList.get(i).get("leixing") == null ? "" : accountList.get(i).get("leixing").toString();
|
|
157
|
-
String grade = accountList.get(i).get("fenji") == null ? "0" : accountList.get(i).get("fenji").toString();
|
|
158
|
-
String industry = accountList.get(i).get("hangye") == null ? "0" : accountList.get(i).get("hangye").toString();
|
|
159
|
-
|
|
160
|
-
JSONObject data = new JSONObject();
|
|
161
|
-
data.put("name", name);
|
|
162
|
-
data.put("type", type);
|
|
163
|
-
data.put("grade", grade);
|
|
164
|
-
data.put("industry", industry);
|
|
165
|
-
data.put("no", (i + 1) + ""); // 序号
|
|
166
|
-
dataList.add(data);
|
|
167
|
-
}
|
|
168
|
-
flag = true;
|
|
169
|
-
} catch (Exception e) {
|
|
170
|
-
throw e;
|
|
171
|
-
}
|
|
172
|
-
rtninfo.put("status", flag);
|
|
173
|
-
rtninfo.put("data", dataList.toString());
|
|
174
|
-
return rtninfo;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
```
|
|
142
|
+
禁止默认使用:
|
|
178
143
|
|
|
179
|
-
|
|
144
|
+
- `new Date()` 直接作为业务时间来源
|
|
145
|
+
- `Calendar.getInstance()` 不带用户时区
|
|
146
|
+
- `new SimpleDateFormat()` 后不设置时区
|
|
180
147
|
|
|
181
|
-
|
|
182
|
-
- 查询结果使用 `CCObject` 列表封装,再转为 `JSONObject`/`JSONArray` 返回。
|
|
183
|
-
- 方法签名抛出 `Exception`,调用方需要处理。
|
|
148
|
+
## 6. SDK 详细参考与使用规范
|
|
184
149
|
|
|
185
|
-
|
|
150
|
+
本节用于解决一个核心问题:
|
|
186
151
|
|
|
187
|
-
|
|
152
|
+
- AI 不能只知道“该用哪个类”,还必须知道“这个 API 具体怎么调、参数是什么、哪些是官方明确说明的”。
|
|
188
153
|
|
|
189
|
-
|
|
190
|
-
> ORM)的核心服务类,支持增删改查、自定义设置等。
|
|
154
|
+
本节内容基于官方页面:
|
|
191
155
|
|
|
192
|
-
|
|
156
|
+
使用原则:
|
|
193
157
|
|
|
194
|
-
|
|
158
|
+
- 只优先使用官方页面已明确给出签名和参数说明的 API
|
|
159
|
+
- 如果官方页没有说明某个方法的参数或返回结构,AI 不得臆造
|
|
160
|
+
- 遇到未文档化能力时,先参考当前仓库相邻类的现有写法,再决定是否使用
|
|
161
|
+
- 对 `ServiceResult` 这类官方页只给出返回类型、但未完整说明字段结构的对象,AI 不得猜字段名;如果确需读取内部字段,必须先参考本项目已有代码模式
|
|
195
162
|
|
|
196
|
-
|
|
197
|
-
public class DemoClass {
|
|
198
|
-
private CCService cs;
|
|
199
|
-
private UserInfo userInfo;
|
|
163
|
+
### 6.1 CCObject 详细说明
|
|
200
164
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
165
|
+
`CCObject` 是 CloudCC 对象数据载体,继承自 `java.util.HashMap`,用于新增、更新、删除以及承载查询结果。
|
|
166
|
+
|
|
167
|
+
#### 6.1.1 构造方法
|
|
168
|
+
|
|
169
|
+
```java
|
|
170
|
+
public CCObject()
|
|
171
|
+
public CCObject(String ccobj)
|
|
172
|
+
public CCObject(String ccobj, String isShared)
|
|
206
173
|
```
|
|
207
174
|
|
|
208
|
-
|
|
175
|
+
参数说明:
|
|
209
176
|
|
|
210
|
-
|
|
177
|
+
| 方法 | 参数 | 类型 | 必填 | 说明 |
|
|
178
|
+
| --- | --- | --- | --- | --- |
|
|
179
|
+
| `CCObject(String ccobj)` | `ccobj` | `String` | 是 | 对象 APIName |
|
|
180
|
+
| `CCObject(String ccobj, String isShared)` | `ccobj` | `String` | 是 | 对象 APIName |
|
|
181
|
+
| `CCObject(String ccobj, String isShared)` | `isShared` | `String` | 是 | 共享标识,官方建议使用 `CCObject.IS_SHARED` |
|
|
211
182
|
|
|
212
|
-
1.
|
|
213
|
-
2. 调用 `cs.insert(对象)` 持久化到数据库
|
|
183
|
+
#### 6.1.2 常量字段
|
|
214
184
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
CCObject
|
|
185
|
+
| 常量 | 默认值 | 说明 |
|
|
186
|
+
| --- | --- | --- |
|
|
187
|
+
| `CCObject.OBJECT_API` | `CCObjectAPI` | 对象 API |
|
|
188
|
+
| `CCObject.IS_SHARED` | `isShared` | 共享对象标识 |
|
|
218
189
|
|
|
219
|
-
|
|
220
|
-
opp.put("name", "新建机会");
|
|
221
|
-
opp.put("jine__c", 10000); // 示例:金额字段
|
|
190
|
+
#### 6.1.3 常用方法
|
|
222
191
|
|
|
223
|
-
|
|
224
|
-
|
|
192
|
+
```java
|
|
193
|
+
public String getObjectApiName()
|
|
194
|
+
public String put(String apiName, String value)
|
|
225
195
|
```
|
|
226
196
|
|
|
227
|
-
|
|
197
|
+
参数说明:
|
|
198
|
+
|
|
199
|
+
| 方法 | 参数 | 类型 | 必填 | 说明 |
|
|
200
|
+
| --- | --- | --- | --- | --- |
|
|
201
|
+
| `put` | `apiName` | `String` | 是 | 字段 APIName |
|
|
202
|
+
| `put` | `value` | `String` | 是 | 字段值 |
|
|
203
|
+
|
|
204
|
+
AI 使用规则:
|
|
228
205
|
|
|
229
|
-
|
|
206
|
+
- 新增或更新对象时优先使用 `new CCObject("ObjectApiName")`
|
|
207
|
+
- 修改记录时必须写入 `id`
|
|
208
|
+
- 字段赋值一律使用字段 APIName,不用字段显示名
|
|
209
|
+
- 共享对象按官方方式传共享标识,不要自己拼魔法字符串
|
|
210
|
+
- 官方页示例把 `put` 记为 `String` 参数;项目里也广泛传入数字、日期、布尔等对象。AI 如果不确定类型,优先参考同对象的现有项目代码,不要自行猜测
|
|
230
211
|
|
|
231
|
-
|
|
212
|
+
### 6.2 UserInfo 详细说明
|
|
232
213
|
|
|
233
|
-
|
|
214
|
+
`UserInfo` 是当前登录用户上下文对象,是构造 `CCService`、`SendEmail`、`DevLogger`、`TimeUtil` 的基础。
|
|
215
|
+
|
|
216
|
+
#### 6.2.1 构造方法
|
|
234
217
|
|
|
235
218
|
```java
|
|
236
|
-
|
|
219
|
+
public UserInfo()
|
|
237
220
|
```
|
|
238
221
|
|
|
239
|
-
|
|
222
|
+
#### 6.2.2 官方页明确列出的常用方法
|
|
240
223
|
|
|
241
224
|
```java
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
225
|
+
public String getUserId()
|
|
226
|
+
public String getOrgId()
|
|
227
|
+
public String getRoleId()
|
|
246
228
|
```
|
|
247
229
|
|
|
248
|
-
|
|
230
|
+
参数说明:
|
|
231
|
+
|
|
232
|
+
| 方法 | 入参 | 返回 | 说明 |
|
|
233
|
+
| --- | --- | --- | --- |
|
|
234
|
+
| `getUserId` | 无 | `String` | 当前用户 ID,未设置时返回 `null` |
|
|
235
|
+
| `getOrgId` | 无 | `String` | 当前组织 ID,未设置时返回 `null` |
|
|
236
|
+
| `getRoleId` | 无 | `String` | 当前角色 ID,未设置时返回 `null` |
|
|
237
|
+
|
|
238
|
+
AI 使用规则:
|
|
239
|
+
|
|
240
|
+
- 所有服务端类都应把 `UserInfo` 当作基础上下文
|
|
241
|
+
- 不要自己构造“当前用户”概念
|
|
242
|
+
- 不要使用硬编码用户替代 `UserInfo`
|
|
243
|
+
- 如果需要官方页未列出的 `UserInfo` 方法,先去当前仓库搜索已有用法,再决定是否复用
|
|
244
|
+
|
|
245
|
+
### 6.3 CCService 详细说明
|
|
246
|
+
|
|
247
|
+
`CCService` 是 CloudCC 服务端核心工具类,负责增删改查、自定义设置等核心能力。
|
|
248
|
+
|
|
249
|
+
#### 6.3.1 构造函数
|
|
249
250
|
|
|
250
251
|
```java
|
|
251
|
-
|
|
252
|
-
"Opportunity",
|
|
253
|
-
"khmc__c = '" + record_new.get("id") + "'",
|
|
254
|
-
"jine__c desc"
|
|
255
|
-
);
|
|
252
|
+
public CCService(UserInfo userInfo)
|
|
256
253
|
```
|
|
257
254
|
|
|
258
|
-
|
|
255
|
+
参数说明:
|
|
256
|
+
|
|
257
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
258
|
+
| --- | --- | --- | --- |
|
|
259
|
+
| `userInfo` | `UserInfo` | 是 | 当前用户对象 |
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
#### 6.3.2 insert
|
|
261
262
|
|
|
262
263
|
```java
|
|
263
|
-
|
|
264
|
-
List<CCObject> list = cs.cquery("Opportunity", "id = '" + record_id + "'");
|
|
265
|
-
if (!list.isEmpty()) {
|
|
266
|
-
CCObject opp = list.get(0);
|
|
267
|
-
opp.put("name", "修改后的机会名称");
|
|
268
|
-
cs.update(opp);
|
|
269
|
-
}
|
|
264
|
+
public ServiceResult insert(CCObject ccobj) throws BusiException
|
|
270
265
|
```
|
|
271
266
|
|
|
272
|
-
|
|
267
|
+
参数说明:
|
|
268
|
+
|
|
269
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
270
|
+
| --- | --- | --- | --- |
|
|
271
|
+
| `ccobj` | `CCObject` | 是 | 待新增的对象数据 |
|
|
272
|
+
|
|
273
|
+
返回说明:
|
|
274
|
+
|
|
275
|
+
- 返回 `ServiceResult`
|
|
276
|
+
- 官方页未完整展开 `ServiceResult` 字段结构
|
|
277
|
+
|
|
278
|
+
AI 使用规则:
|
|
279
|
+
|
|
280
|
+
- 用于单条新增
|
|
281
|
+
- 新增前补齐必填字段
|
|
282
|
+
- 不要默认插入一定成功
|
|
283
|
+
- 若需要读取 `ServiceResult` 内部字段,必须先参考当前项目现有写法,不得猜字段名
|
|
284
|
+
|
|
285
|
+
#### 6.3.3 cquery
|
|
286
|
+
|
|
287
|
+
官方页给出两个重载。
|
|
273
288
|
|
|
274
289
|
```java
|
|
275
|
-
List<CCObject>
|
|
276
|
-
|
|
277
|
-
CCObject opp = list.get(0);
|
|
278
|
-
cs.delete(opp);
|
|
279
|
-
}
|
|
290
|
+
public List<CCObject> cquery(String apiName, String condtion, String order) throws Exception
|
|
291
|
+
public List<CCObject> cquery(String apiName, String condtion, boolean isDataObject) throws Exception
|
|
280
292
|
```
|
|
281
293
|
|
|
282
|
-
|
|
294
|
+
参数说明:
|
|
295
|
+
|
|
296
|
+
| 方法 | 参数 | 类型 | 必填 | 说明 |
|
|
297
|
+
| --- | --- | --- | --- | --- |
|
|
298
|
+
| `cquery(apiName, condtion, order)` | `apiName` | `String` | 是 | 对象 APIName |
|
|
299
|
+
| `cquery(apiName, condtion, order)` | `condtion` | `String` | 否 | 查询条件;官方说明使用字段 APIName,自定义字段要加 `__c` |
|
|
300
|
+
| `cquery(apiName, condtion, order)` | `order` | `String` | 否 | 排序字段;自定义字段要加 `__c` |
|
|
301
|
+
| `cquery(apiName, condtion, isDataObject)` | `apiName` | `String` | 是 | 对象 APIName |
|
|
302
|
+
| `cquery(apiName, condtion, isDataObject)` | `condtion` | `String` | 否 | 查询条件;自定义字段要加 `__c` |
|
|
303
|
+
| `cquery(apiName, condtion, isDataObject)` | `isDataObject` | `boolean` | 否 | `true` 查询普通数据,`false` 查询共享数据 |
|
|
304
|
+
|
|
305
|
+
返回说明:
|
|
306
|
+
|
|
307
|
+
- 返回 `List<CCObject>`
|
|
308
|
+
|
|
309
|
+
AI 使用规则:
|
|
310
|
+
|
|
311
|
+
- 简单单表查询优先 `cquery`
|
|
312
|
+
- 条件和排序统一使用字段 APIName
|
|
313
|
+
- 自定义字段按官方要求补 `__c`
|
|
314
|
+
- 不要无条件全表扫描
|
|
315
|
+
- 当需求明确查询共享数据时,才使用带 `isDataObject` 的重载
|
|
283
316
|
|
|
284
|
-
####
|
|
317
|
+
#### 6.3.4 cqlQuery
|
|
285
318
|
|
|
286
319
|
```java
|
|
287
|
-
|
|
288
|
-
List<CCObject> shares = cs.cquery(
|
|
289
|
-
"Opportunity",
|
|
290
|
-
"userid = '" + userId + "'",
|
|
291
|
-
false
|
|
292
|
-
);
|
|
320
|
+
public List<CCObject> cqlQuery(String apiName, String cql) throws Exception
|
|
293
321
|
```
|
|
294
322
|
|
|
295
|
-
|
|
323
|
+
参数说明:
|
|
324
|
+
|
|
325
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
326
|
+
| --- | --- | --- | --- |
|
|
327
|
+
| `apiName` | `String` | 是 | 对象 APIName |
|
|
328
|
+
| `cql` | `String` | 否 | SQL/CQL 语句,官方说明支持常用 where 条件 |
|
|
329
|
+
|
|
330
|
+
返回说明:
|
|
331
|
+
|
|
332
|
+
- 返回 `List<CCObject>`
|
|
333
|
+
|
|
334
|
+
AI 使用规则:
|
|
335
|
+
|
|
336
|
+
- 聚合、join、复杂筛选时再用 `cqlQuery`
|
|
337
|
+
- 尽量只查需要的字段,不要默认 `select *`
|
|
338
|
+
- 必须收窄 where 条件
|
|
339
|
+
- 复杂 SQL 要保持可读性和可维护性
|
|
340
|
+
|
|
341
|
+
#### 6.3.5 update
|
|
296
342
|
|
|
297
343
|
```java
|
|
298
|
-
|
|
344
|
+
public ServiceResult update(CCObject ccobj) throws Exception
|
|
299
345
|
```
|
|
300
346
|
|
|
301
|
-
|
|
347
|
+
参数说明:
|
|
302
348
|
|
|
303
|
-
|
|
349
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
350
|
+
| --- | --- | --- | --- |
|
|
351
|
+
| `ccobj` | `CCObject` | 是 | 待更新对象 |
|
|
304
352
|
|
|
305
|
-
|
|
353
|
+
返回说明:
|
|
306
354
|
|
|
307
|
-
-
|
|
355
|
+
- 返回 `ServiceResult`
|
|
356
|
+
- 官方页未完整展开 `ServiceResult` 字段结构
|
|
357
|
+
|
|
358
|
+
AI 使用规则:
|
|
359
|
+
|
|
360
|
+
- 更新前必须带 `id`
|
|
361
|
+
- 只更新必要字段,避免无意义全量覆盖
|
|
362
|
+
- 更新动作应和状态校验绑定
|
|
363
|
+
|
|
364
|
+
#### 6.3.6 delete
|
|
308
365
|
|
|
309
366
|
```java
|
|
310
|
-
|
|
367
|
+
public ServiceResult delete(CCObject ccobj) throws Exception
|
|
311
368
|
```
|
|
312
369
|
|
|
313
|
-
|
|
370
|
+
参数说明:
|
|
371
|
+
|
|
372
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
373
|
+
| --- | --- | --- | --- |
|
|
374
|
+
| `ccobj` | `CCObject` | 是 | 待删除对象,通常至少包含 `id` |
|
|
375
|
+
|
|
376
|
+
返回说明:
|
|
377
|
+
|
|
378
|
+
- 返回 `ServiceResult`
|
|
379
|
+
|
|
380
|
+
AI 使用规则:
|
|
381
|
+
|
|
382
|
+
- 删除前必须确认条件准确
|
|
383
|
+
- 删除前后最好有日志
|
|
384
|
+
- 删除共享关系时优先使用共享专用能力,不要混用普通删除
|
|
385
|
+
|
|
386
|
+
#### 6.3.7 deleteShareObjectBySql
|
|
314
387
|
|
|
315
388
|
```java
|
|
316
|
-
|
|
389
|
+
public void deleteShareObjectBySql(String objectApiName, String expression) throws Exception
|
|
317
390
|
```
|
|
318
391
|
|
|
319
|
-
|
|
392
|
+
参数说明:
|
|
393
|
+
|
|
394
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
395
|
+
| --- | --- | --- | --- |
|
|
396
|
+
| `objectApiName` | `String` | 是 | 对象 APIName |
|
|
397
|
+
| `expression` | `String` | 是 | SQL 表达式;官方说明这里字段不需要加 `__c` |
|
|
398
|
+
|
|
399
|
+
AI 使用规则:
|
|
400
|
+
|
|
401
|
+
- 仅用于共享关系删除
|
|
402
|
+
- 普通对象删除不要误用这个接口
|
|
403
|
+
- 注意这里的字段规则和 `cquery` 条件规则不同
|
|
404
|
+
|
|
405
|
+
#### 6.3.8 自定义设置相关方法
|
|
406
|
+
|
|
407
|
+
官方页列出了 3 类常用能力。
|
|
320
408
|
|
|
321
409
|
```java
|
|
322
|
-
|
|
323
|
-
Map
|
|
410
|
+
public Map getListCustomSetting(String objectApiName)
|
|
411
|
+
public Map getListCustomSetting(String apiName, String name)
|
|
412
|
+
public Map getCustomSetting(String objectApiName, String id)
|
|
324
413
|
```
|
|
325
414
|
|
|
326
|
-
|
|
415
|
+
参数说明:
|
|
416
|
+
|
|
417
|
+
| 方法 | 参数 | 类型 | 必填 | 说明 |
|
|
418
|
+
| --- | --- | --- | --- | --- |
|
|
419
|
+
| `getListCustomSetting(String objectApiName)` | `objectApiName` | `String` | 是 | 列表型自定义设置对象 APIName |
|
|
420
|
+
| `getListCustomSetting(String apiName, String name)` | `apiName` | `String` | 是 | 列表型自定义设置对象 APIName |
|
|
421
|
+
| `getListCustomSetting(String apiName, String name)` | `name` | `String` | 是 | 自定义设置属性名称 |
|
|
422
|
+
| `getCustomSetting(String objectApiName, String id)` | `objectApiName` | `String` | 是 | 层次结构型自定义设置对象 APIName |
|
|
423
|
+
| `getCustomSetting(String objectApiName, String id)` | `id` | `String` | 是 | 简档 ID 或用户 ID |
|
|
424
|
+
|
|
425
|
+
返回说明:
|
|
426
|
+
|
|
427
|
+
- 均返回 `Map`
|
|
428
|
+
|
|
429
|
+
AI 使用规则:
|
|
430
|
+
|
|
431
|
+
- 外部系统地址、业务开关、阈值、角色映射、模板号优先配置化
|
|
432
|
+
- 不要把可配置项直接写死在代码里
|
|
327
433
|
|
|
328
|
-
|
|
434
|
+
### 6.4 SendEmail 详细说明
|
|
329
435
|
|
|
330
|
-
|
|
436
|
+
`SendEmail` 用于发送邮件通知。
|
|
437
|
+
|
|
438
|
+
#### 6.4.1 构造方法
|
|
331
439
|
|
|
332
440
|
```java
|
|
333
|
-
|
|
334
|
-
sendEmail.sendMailFromSystem(
|
|
335
|
-
new String[]{"to@example.com"}, // 收件人
|
|
336
|
-
new String[]{"cc@example.com"}, // 抄送
|
|
337
|
-
new String[]{"bcc@example.com"}, // 密送
|
|
338
|
-
"邮件主题",
|
|
339
|
-
"邮件内容",
|
|
340
|
-
true // 是否纯文本
|
|
341
|
-
);
|
|
441
|
+
public SendEmail(UserInfo userInfo)
|
|
342
442
|
```
|
|
343
443
|
|
|
344
|
-
|
|
444
|
+
参数说明:
|
|
445
|
+
|
|
446
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
447
|
+
| --- | --- | --- | --- |
|
|
448
|
+
| `userInfo` | `UserInfo` | 是 | 当前用户对象 |
|
|
345
449
|
|
|
346
|
-
|
|
450
|
+
#### 6.4.2 sendMailFromSystem
|
|
347
451
|
|
|
348
452
|
```java
|
|
349
|
-
|
|
350
|
-
|
|
453
|
+
public boolean sendMailFromSystem(
|
|
454
|
+
String[] toAddress,
|
|
455
|
+
String[] ccAddress,
|
|
456
|
+
String[] bccAddress,
|
|
457
|
+
String subject,
|
|
458
|
+
String content,
|
|
459
|
+
boolean isText
|
|
460
|
+
)
|
|
461
|
+
```
|
|
351
462
|
|
|
352
|
-
|
|
353
|
-
cclogger.devLogInfo("这是 info 日志");
|
|
463
|
+
参数说明:
|
|
354
464
|
|
|
355
|
-
|
|
356
|
-
|
|
465
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
466
|
+
| --- | --- | --- | --- |
|
|
467
|
+
| `toAddress` | `String[]` | 是 | 收件人数组 |
|
|
468
|
+
| `ccAddress` | `String[]` | 是 | 抄送人数组 |
|
|
469
|
+
| `bccAddress` | `String[]` | 是 | 密送人数组 |
|
|
470
|
+
| `subject` | `String` | 是 | 邮件标题 |
|
|
471
|
+
| `content` | `String` | 是 | 邮件正文 |
|
|
472
|
+
| `isText` | `boolean` | 是 | `false` 表示 HTML 邮件,`true` 表示纯文本 |
|
|
357
473
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
} catch (Exception e) {
|
|
362
|
-
cclogger.devLogError("发生异常", e);
|
|
363
|
-
throw e;
|
|
364
|
-
}
|
|
365
|
-
```
|
|
474
|
+
返回说明:
|
|
475
|
+
|
|
476
|
+
- 返回 `boolean`
|
|
366
477
|
|
|
367
|
-
|
|
478
|
+
AI 使用规则:
|
|
368
479
|
|
|
369
|
-
|
|
480
|
+
- 无抄送/密送时传空数组,不要盲目传 `null`
|
|
481
|
+
- HTML 邮件时 `isText=false`
|
|
482
|
+
- 发信动作应建立在业务动作成功之后
|
|
370
483
|
|
|
371
|
-
|
|
484
|
+
#### 6.4.3 sendEmailNew
|
|
372
485
|
|
|
373
486
|
```java
|
|
374
|
-
|
|
375
|
-
|
|
487
|
+
public ServiceResult sendEmailNew(
|
|
488
|
+
String emailtemplateid,
|
|
489
|
+
String[] toaddress,
|
|
490
|
+
String[] ccaddress,
|
|
491
|
+
String[] bcaddress,
|
|
492
|
+
String id
|
|
493
|
+
) throws Exception
|
|
376
494
|
```
|
|
377
495
|
|
|
378
|
-
|
|
496
|
+
参数说明:
|
|
497
|
+
|
|
498
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
499
|
+
| --- | --- | --- | --- |
|
|
500
|
+
| `emailtemplateid` | `String` | 是 | 邮件模板 ID |
|
|
501
|
+
| `toaddress` | `String[]` | 是 | 收件人数组 |
|
|
502
|
+
| `ccaddress` | `String[]` | 是 | 抄送人数组 |
|
|
503
|
+
| `bcaddress` | `String[]` | 是 | 密送人数组 |
|
|
504
|
+
| `id` | `String` | 是 | 业务记录 ID,通常用于模板数据填充 |
|
|
505
|
+
|
|
506
|
+
返回说明:
|
|
507
|
+
|
|
508
|
+
- 返回 `ServiceResult`
|
|
509
|
+
|
|
510
|
+
AI 使用规则:
|
|
511
|
+
|
|
512
|
+
- 模板邮件优先用这个接口
|
|
513
|
+
- 模板 ID 不要硬编码,优先配置化
|
|
514
|
+
- 如需解析 `ServiceResult`,先参考现有项目代码,不要自行猜字段
|
|
515
|
+
|
|
516
|
+
### 6.5 DevLogger 详细说明
|
|
517
|
+
|
|
518
|
+
`DevLogger` 用于运行日志采集。
|
|
519
|
+
|
|
520
|
+
#### 6.5.1 构造函数
|
|
379
521
|
|
|
380
522
|
```java
|
|
381
|
-
|
|
382
|
-
myDateFormat.setTimeZone(TimeUtil.getUserTimeZone(userInfo));
|
|
523
|
+
public DevLogger(UserInfo userInfo)
|
|
383
524
|
```
|
|
384
525
|
|
|
385
|
-
|
|
526
|
+
参数说明:
|
|
527
|
+
|
|
528
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
529
|
+
| --- | --- | --- | --- |
|
|
530
|
+
| `userInfo` | `UserInfo` | 是 | 当前用户对象 |
|
|
531
|
+
|
|
532
|
+
#### 6.5.2 devLogInfo
|
|
386
533
|
|
|
387
534
|
```java
|
|
388
|
-
|
|
389
|
-
TimeUtil.getSimpleDateFormat("yyyy-MM-dd", userInfo);
|
|
535
|
+
public void devLogInfo(String info)
|
|
390
536
|
```
|
|
391
537
|
|
|
392
|
-
|
|
538
|
+
参数说明:
|
|
393
539
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
540
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
541
|
+
| --- | --- | --- | --- |
|
|
542
|
+
| `info` | `String` | 是 | info 级别日志内容 |
|
|
397
543
|
|
|
398
|
-
|
|
399
|
-
|
|
544
|
+
#### 6.5.3 devLogError
|
|
545
|
+
|
|
546
|
+
```java
|
|
547
|
+
public void devLogError(String error, Throwable throwable)
|
|
400
548
|
```
|
|
401
549
|
|
|
402
|
-
|
|
550
|
+
参数说明:
|
|
403
551
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
552
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
553
|
+
| --- | --- | --- | --- |
|
|
554
|
+
| `error` | `String` | 是 | 错误日志内容 |
|
|
555
|
+
| `throwable` | `Throwable` | 否 | 异常对象 |
|
|
407
556
|
|
|
408
|
-
|
|
409
|
-
CCObject oppshare = new CCObject("Opportunity", CCObject.IS_SHARED);
|
|
557
|
+
AI 使用规则:
|
|
410
558
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
559
|
+
- 方法入口要记录
|
|
560
|
+
- 关键业务 ID 要记录
|
|
561
|
+
- 外部调用前后要记录
|
|
562
|
+
- 异常必须记录
|
|
563
|
+
- 不要只写无意义日志
|
|
564
|
+
|
|
565
|
+
### 6.6 TimeUtil 详细说明
|
|
414
566
|
|
|
415
|
-
|
|
567
|
+
`TimeUtil` 用于解决跨时区数据处理问题。官方页明确说明:涉及时间写库与比较时,不应直接依赖本地时区的 `Date` 与 `Calendar`。
|
|
416
568
|
|
|
417
|
-
|
|
569
|
+
#### 6.6.1 getNowDate
|
|
418
570
|
|
|
419
|
-
|
|
571
|
+
```java
|
|
572
|
+
TimeUtil.getNowDate(userInfo)
|
|
573
|
+
```
|
|
420
574
|
|
|
421
|
-
|
|
422
|
-
- 触发器由 CloudCC 平台动态编译,调用写法与普通 Java 方法调用类似。
|
|
423
|
-
- 典型模式:
|
|
424
|
-
- 在触发器中只做入口和参数准备
|
|
425
|
-
- 将复杂逻辑委托给自定义类方法
|
|
575
|
+
参数说明:
|
|
426
576
|
|
|
427
|
-
|
|
577
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
578
|
+
| --- | --- | --- | --- |
|
|
579
|
+
| `userInfo` | `UserInfo` | 是 | 当前用户对象 |
|
|
428
580
|
|
|
429
|
-
|
|
581
|
+
返回说明:
|
|
430
582
|
|
|
431
|
-
|
|
432
|
-
> `invoker` 方法。
|
|
583
|
+
- 返回按用户时区获取的当前时间
|
|
433
584
|
|
|
434
|
-
|
|
585
|
+
#### 6.6.2 getUserTimeZone
|
|
435
586
|
|
|
436
587
|
```java
|
|
437
|
-
|
|
438
|
-
Object invoker(String className, String method, List<Map> conlist, Map map);
|
|
588
|
+
TimeUtil.getUserTimeZone(userInfo)
|
|
439
589
|
```
|
|
440
590
|
|
|
441
591
|
参数说明:
|
|
442
592
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
- 每个 Map 包含:
|
|
447
|
-
- `argType`:参数类型
|
|
448
|
-
- `argValue`:参数值
|
|
449
|
-
- `arglist` / `map`:方法参数
|
|
450
|
-
- 同样包含 `argType` 和 `argValue`
|
|
593
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
594
|
+
| --- | --- | --- | --- |
|
|
595
|
+
| `userInfo` | `UserInfo` | 是 | 当前用户对象 |
|
|
451
596
|
|
|
452
|
-
|
|
597
|
+
返回说明:
|
|
453
598
|
|
|
454
|
-
|
|
599
|
+
- 返回用户时区对象
|
|
600
|
+
|
|
601
|
+
#### 6.6.3 getSimpleDateFormat
|
|
455
602
|
|
|
456
603
|
```java
|
|
457
|
-
|
|
604
|
+
TimeUtil.getSimpleDateFormat(format, userInfo)
|
|
605
|
+
```
|
|
458
606
|
|
|
459
|
-
|
|
607
|
+
参数说明:
|
|
460
608
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
609
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
610
|
+
| --- | --- | --- | --- | --- |
|
|
611
|
+
| `format` | `String` | 是 | `yyyy-MM-dd` | 格式化模板 |
|
|
612
|
+
| `userInfo` | `UserInfo` | 是 | - | 当前用户对象 |
|
|
464
613
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
```
|
|
614
|
+
返回说明:
|
|
615
|
+
|
|
616
|
+
- 返回带用户时区的 `SimpleDateFormat`
|
|
470
617
|
|
|
471
|
-
|
|
618
|
+
#### 6.6.4 Calendar 获取方式
|
|
619
|
+
|
|
620
|
+
官方页给出两种方式:
|
|
472
621
|
|
|
473
622
|
```java
|
|
474
|
-
|
|
623
|
+
Calendar cal = Calendar.getInstance(TimeUtil.getUserTimeZone(userInfo));
|
|
624
|
+
Calendar cal = TimeUtil.getCalendar(userInfo);
|
|
625
|
+
```
|
|
475
626
|
|
|
476
|
-
|
|
477
|
-
c.put("argType", UserInfo.class);
|
|
478
|
-
c.put("argValue", userInfo);
|
|
479
|
-
conlist.add(c);
|
|
627
|
+
AI 使用规则:
|
|
480
628
|
|
|
481
|
-
|
|
482
|
-
|
|
629
|
+
- 只要涉及时间写库、比较、提醒、截止、统计,一律优先 `TimeUtil`
|
|
630
|
+
- 禁止默认用 `new Date()` 或不带时区的 `Calendar`
|
|
483
631
|
|
|
484
|
-
|
|
485
|
-
|
|
632
|
+
### 6.7 AI 不得猜测的 SDK 内容
|
|
633
|
+
|
|
634
|
+
以下内容如果官方页没有明确说明,AI 不得自己编:
|
|
635
|
+
|
|
636
|
+
- 未在官方页列出的 SDK 方法名
|
|
637
|
+
- 未文档化的参数顺序
|
|
638
|
+
- `ServiceResult` 的内部字段结构
|
|
639
|
+
- 未确认的 `UserInfo` 扩展方法
|
|
640
|
+
- 外部能力类的构造方法和返回值
|
|
641
|
+
|
|
642
|
+
正确做法:
|
|
643
|
+
|
|
644
|
+
1. 先查官方文档
|
|
645
|
+
2. 再查当前仓库已有同类实现
|
|
646
|
+
3. 仍不确定时,不生成“看起来像真的”代码
|
|
647
|
+
|
|
648
|
+
## 7. 代码结构规范
|
|
649
|
+
|
|
650
|
+
### 7.1 分层建议
|
|
651
|
+
|
|
652
|
+
AI 编写自定义类时,优先按职责分层:
|
|
653
|
+
|
|
654
|
+
- `Controller`:入口编排、参数接收、结果返回
|
|
655
|
+
- `Service`:核心业务逻辑
|
|
656
|
+
- `Util/Utils`:通用工具、通用查询、通用转换
|
|
657
|
+
- `Client`:外部系统调用封装
|
|
658
|
+
- `Handler`:触发器或事件逻辑下沉层
|
|
659
|
+
|
|
660
|
+
如果项目已有既定命名习惯,优先跟随相邻模块命名。
|
|
661
|
+
|
|
662
|
+
### 7.2 方法粒度
|
|
663
|
+
|
|
664
|
+
AI 不应默认把所有逻辑塞进一个超长方法。
|
|
665
|
+
|
|
666
|
+
推荐拆分:
|
|
667
|
+
|
|
668
|
+
- `validateXxx`
|
|
669
|
+
- `queryXxx`
|
|
670
|
+
- `calculateXxx`
|
|
671
|
+
- `updateXxx`
|
|
672
|
+
- `sendXxx`
|
|
673
|
+
- `buildXxxResult`
|
|
674
|
+
|
|
675
|
+
原则:
|
|
676
|
+
|
|
677
|
+
- 一个方法只负责一类动作
|
|
678
|
+
- 复杂编排方法负责串联,不负责所有细节
|
|
679
|
+
|
|
680
|
+
### 7.3 返回值规范
|
|
681
|
+
|
|
682
|
+
AI 需要根据调用场景选择返回值类型:
|
|
683
|
+
|
|
684
|
+
- 页面/按钮/组件接口:优先 `JSONObject`、`Map` 或稳定结构对象
|
|
685
|
+
- 内部服务方法:优先返回明确业务结果
|
|
686
|
+
- 仅做写操作的方法:可返回 `void`,但必须通过异常或日志暴露失败
|
|
687
|
+
|
|
688
|
+
规则:
|
|
689
|
+
|
|
690
|
+
- 返回值结构要稳定
|
|
691
|
+
- 不要同一方法一会儿返回字符串、一会儿返回对象
|
|
692
|
+
- 对外接口最好带成功标识和错误信息
|
|
693
|
+
|
|
694
|
+
## 8. 数据与查询规范
|
|
695
|
+
|
|
696
|
+
### 8.1 字段名统一使用 API 名称
|
|
697
|
+
|
|
698
|
+
AI 不得使用字段显示名直接拼查询条件。
|
|
699
|
+
|
|
700
|
+
必须:
|
|
701
|
+
|
|
702
|
+
- 使用对象 APIName
|
|
703
|
+
- 使用字段 APIName
|
|
704
|
+
- 自定义字段按文档规则补 `__c`
|
|
705
|
+
|
|
706
|
+
### 8.2 查询要收敛
|
|
707
|
+
|
|
708
|
+
AI 默认应避免:
|
|
709
|
+
|
|
710
|
+
- 无条件全表查询
|
|
711
|
+
- 无限制 `select *`
|
|
712
|
+
- 在循环里反复查同一批数据
|
|
713
|
+
|
|
714
|
+
应优先:
|
|
715
|
+
|
|
716
|
+
- 一次查够
|
|
717
|
+
- 只查必要字段
|
|
718
|
+
- 提前构造索引或映射
|
|
719
|
+
- 把批量处理合并到同一逻辑块
|
|
720
|
+
|
|
721
|
+
### 8.3 写操作要可回溯
|
|
486
722
|
|
|
487
|
-
|
|
723
|
+
涉及新增、更新、删除时:
|
|
488
724
|
|
|
489
|
-
|
|
725
|
+
- 要明确主键或条件
|
|
726
|
+
- 要记录关键业务键
|
|
727
|
+
- 要尽量保证幂等
|
|
728
|
+
- 要避免重复插入
|
|
729
|
+
|
|
730
|
+
## 9. 外部集成规范
|
|
731
|
+
|
|
732
|
+
AI 编写集成类时,必须把外部系统调用与业务逻辑适度隔离。
|
|
733
|
+
|
|
734
|
+
规则:
|
|
735
|
+
|
|
736
|
+
- 接口地址、密钥、系统标识优先配置化
|
|
737
|
+
- 不要在多个业务方法中重复拼接 URL
|
|
738
|
+
- 外部调用前后必须记录日志
|
|
739
|
+
- 调用失败要保留错误上下文
|
|
740
|
+
- 文件上传、下载、同步逻辑要单独封装
|
|
741
|
+
|
|
742
|
+
推荐模式:
|
|
743
|
+
|
|
744
|
+
- `Client` 负责调用外部接口
|
|
745
|
+
- `Service` 负责业务编排
|
|
746
|
+
- `Controller` 负责入口输出
|
|
747
|
+
|
|
748
|
+
## 10. 通知与协同规范
|
|
749
|
+
|
|
750
|
+
AI 编写通知能力时,应遵循:
|
|
751
|
+
|
|
752
|
+
- 先完成业务动作,再触发通知
|
|
753
|
+
- 通知内容应基于真实业务结果生成
|
|
754
|
+
- 收件人/待办对象来源应可追溯
|
|
755
|
+
- 同一动作不要重复发送
|
|
756
|
+
|
|
757
|
+
如果通知逻辑复杂:
|
|
758
|
+
|
|
759
|
+
- 单独提取通知方法
|
|
760
|
+
- 不要把大段 HTML 拼接散落到多处
|
|
761
|
+
|
|
762
|
+
## 11. 权限与共享规范
|
|
763
|
+
|
|
764
|
+
AI 处理权限相关需求时,应优先使用服务端判断,而不是把关键限制交给前端。
|
|
765
|
+
|
|
766
|
+
规则:
|
|
767
|
+
|
|
768
|
+
- 角色判断放服务端
|
|
769
|
+
- 审批权限放服务端
|
|
770
|
+
- 共享写入放服务端
|
|
771
|
+
- 删除共享关系要使用正确接口
|
|
772
|
+
|
|
773
|
+
当需求和共享、角色、负责人变化相关时,AI 应主动考虑:
|
|
774
|
+
|
|
775
|
+
- 是否要补共享
|
|
776
|
+
- 是否要删旧共享
|
|
777
|
+
- 是否要校验当前用户是否有权执行
|
|
778
|
+
|
|
779
|
+
## 12. 异常与日志规范
|
|
780
|
+
|
|
781
|
+
AI 写代码时必须假设以下事情都可能失败:
|
|
782
|
+
|
|
783
|
+
- 查询为空
|
|
784
|
+
- 字段为空
|
|
785
|
+
- 配置缺失
|
|
786
|
+
- 外部接口超时
|
|
787
|
+
- 文件不存在
|
|
788
|
+
- 写操作失败
|
|
789
|
+
- 状态不满足前置条件
|
|
790
|
+
|
|
791
|
+
因此必须:
|
|
792
|
+
|
|
793
|
+
- 对空值做保护
|
|
794
|
+
- 对异常做显式处理
|
|
795
|
+
- 对关键节点打日志
|
|
796
|
+
- 对外返回明确错误
|
|
797
|
+
|
|
798
|
+
不允许:
|
|
799
|
+
|
|
800
|
+
- 空 `catch`
|
|
801
|
+
- 吞异常后继续假装成功
|
|
802
|
+
- 失败时只返回 `false` 而不给任何上下文
|
|
803
|
+
|
|
804
|
+
## 13. 命名规范
|
|
805
|
+
|
|
806
|
+
优先遵循以下模式:
|
|
807
|
+
|
|
808
|
+
- `XxxController`:入口编排类
|
|
809
|
+
- `XxxService`:核心业务服务类
|
|
810
|
+
- `XxxUtil` / `XxxUtils`:通用工具类
|
|
811
|
+
- `XxxClient`:外部系统客户端
|
|
812
|
+
- `XxxHandler`:触发逻辑处理类
|
|
813
|
+
|
|
814
|
+
方法命名优先使用动词开头:
|
|
815
|
+
|
|
816
|
+
- `queryXxx`
|
|
817
|
+
- `createXxx`
|
|
818
|
+
- `updateXxx`
|
|
819
|
+
- `deleteXxx`
|
|
820
|
+
- `sendXxx`
|
|
821
|
+
- `calculateXxx`
|
|
822
|
+
- `buildXxx`
|
|
823
|
+
|
|
824
|
+
## 14. AI 禁止事项
|
|
825
|
+
|
|
826
|
+
AI 不得:
|
|
827
|
+
|
|
828
|
+
- 手工新建 `classes/<类名>/` 目录
|
|
829
|
+
- 修改 `config.json` 的 `id` 或版本字段
|
|
830
|
+
- 在 SOURCE 区域外写业务代码
|
|
831
|
+
- 默认使用 `new Date()` 作为业务时间
|
|
832
|
+
- 使用字段显示名代替 API 名称
|
|
833
|
+
- 无条件全量查询大对象
|
|
834
|
+
- 将关键逻辑全部堆在入口方法里
|
|
835
|
+
- 在多个地方复制同样的外部接口调用代码
|
|
836
|
+
- 把地址、角色、阈值、模板 ID 等全部硬编码
|
|
837
|
+
- 吞异常不报错
|
|
838
|
+
- 只写“成功/失败”而没有上下文日志
|
|
839
|
+
|
|
840
|
+
## 15. AI 生成代码前的检查清单
|
|
841
|
+
|
|
842
|
+
在开始写代码前,AI 应先确认:
|
|
843
|
+
|
|
844
|
+
1. 这是页面/按钮/触发器/定时类/组件中的哪一种入口
|
|
845
|
+
2. 这段逻辑是否真的应该放到自定义类
|
|
846
|
+
3. 会影响哪些对象
|
|
847
|
+
4. 是否需要外部系统调用
|
|
848
|
+
5. 是否需要附件或文件处理
|
|
849
|
+
6. 是否涉及时区
|
|
850
|
+
7. 是否涉及权限、共享或审批
|
|
851
|
+
8. 哪些参数适合配置化而不是硬编码
|
|
852
|
+
|
|
853
|
+
## 16. AI 交付前的自检清单
|
|
854
|
+
|
|
855
|
+
AI 完成代码后,必须自检:
|
|
856
|
+
|
|
857
|
+
1. 是否只修改了 SOURCE 区域
|
|
858
|
+
2. 是否保留了 `UserInfo` + `CCService`
|
|
859
|
+
3. 是否正确使用对象/字段 API 名称
|
|
860
|
+
4. 是否优先使用了 `cquery` / `cqlQuery` 的正确场景
|
|
861
|
+
5. 是否对写操作做了失败处理
|
|
862
|
+
6. 是否对关键步骤加了日志
|
|
863
|
+
7. 是否避免了直接 `new Date()`
|
|
864
|
+
8. 是否把复杂逻辑拆成了可读的方法
|
|
865
|
+
9. 是否避免了不必要硬编码
|
|
866
|
+
10. 是否让返回结果对调用方足够清晰
|
|
867
|
+
|
|
868
|
+
## 17. 推荐骨架
|
|
869
|
+
|
|
870
|
+
下面是一个适合 AI 生成新类时参考的通用骨架:
|
|
490
871
|
|
|
491
872
|
```java
|
|
492
|
-
|
|
873
|
+
import com.cloudcc.core.*;
|
|
874
|
+
import net.sf.json.JSONObject;
|
|
875
|
+
import java.util.List;
|
|
493
876
|
|
|
494
|
-
|
|
877
|
+
public class XxxService {
|
|
878
|
+
private final UserInfo userInfo;
|
|
879
|
+
private final CCService cs;
|
|
880
|
+
private final DevLogger logger;
|
|
495
881
|
|
|
496
|
-
public
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
ServiceResult sr = cs.insert(co);
|
|
501
|
-
return sr;
|
|
882
|
+
public XxxService(UserInfo userInfo) {
|
|
883
|
+
this.userInfo = userInfo;
|
|
884
|
+
this.cs = new CCService(userInfo);
|
|
885
|
+
this.logger = new DevLogger(userInfo);
|
|
502
886
|
}
|
|
503
|
-
}
|
|
504
|
-
```
|
|
505
887
|
|
|
506
|
-
|
|
888
|
+
public JSONObject execute(String recordId) throws Exception {
|
|
889
|
+
JSONObject result = new JSONObject();
|
|
890
|
+
logger.devLogInfo("XxxService.execute start, recordId=" + recordId);
|
|
891
|
+
try {
|
|
892
|
+
validate(recordId);
|
|
507
893
|
|
|
508
|
-
|
|
509
|
-
|
|
894
|
+
List<CCObject> records = queryData(recordId);
|
|
895
|
+
JSONObject calcResult = calculate(records);
|
|
896
|
+
updateData(recordId, calcResult);
|
|
510
897
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
898
|
+
result.put("success", true);
|
|
899
|
+
result.put("data", calcResult);
|
|
900
|
+
logger.devLogInfo("XxxService.execute success, recordId=" + recordId);
|
|
901
|
+
return result;
|
|
902
|
+
} catch (Exception e) {
|
|
903
|
+
logger.devLogError("XxxService.execute failed, recordId=" + recordId, e);
|
|
904
|
+
throw e;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
515
907
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
908
|
+
private void validate(String recordId) throws Exception {
|
|
909
|
+
if (recordId == null || "".equals(recordId.trim())) {
|
|
910
|
+
throw new Exception("recordId is required");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
520
913
|
|
|
521
|
-
|
|
914
|
+
private List<CCObject> queryData(String recordId) throws Exception {
|
|
915
|
+
return cs.cquery("ObjectApiName", "id = '" + recordId + "'", null);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
private JSONObject calculate(List<CCObject> records) {
|
|
919
|
+
JSONObject result = new JSONObject();
|
|
920
|
+
result.put("count", records == null ? 0 : records.size());
|
|
921
|
+
return result;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
private void updateData(String recordId, JSONObject calcResult) throws Exception {
|
|
925
|
+
CCObject obj = new CCObject("ObjectApiName");
|
|
926
|
+
obj.put("id", recordId);
|
|
927
|
+
obj.put("last_execute_time", TimeUtil.getNowDate(userInfo));
|
|
928
|
+
cs.update(obj);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
522
931
|
```
|
|
523
932
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
- [ ] 类目录与平台同步仅通过 `cloudcc create` / `cloudcc pull` / `cloudcc pullList` /
|
|
529
|
-
`cloudcc publish` / `cloudcc delete` 等命令操作,不手工造目录或改 `config.json` 的
|
|
530
|
-
`id`
|
|
531
|
-
- [ ] 类名、方法名与调用方(按钮/触发器/组件/定时作业)约定一致
|
|
532
|
-
- [ ] 构造函数正确接收 `UserInfo` 并实例化 `CCService` 等依赖
|
|
533
|
-
- [ ] 所有 `CCService` 操作(`insert`/`update`/`delete`/`cquery`)使用正确的对象
|
|
534
|
-
API 名称和字段 API 名称(自定义字段加 `__c`)
|
|
535
|
-
- [ ] 访问共享表时,表达式中的字段未错误地添加 `__c` 后缀
|
|
536
|
-
- [ ] 涉及时间处理的逻辑统一使用 `TimeUtil`,避免时区问题
|
|
537
|
-
- [ ] 异常统一捕获并按需抛出,同时使用 `DevLogger` 记录关键错误信息
|
|
538
|
-
- [ ] 使用 `SendEmail` 时确认收件地址与内容来源安全可靠
|
|
539
|
-
- [ ] 使用 `PageClsInvoker` 跨类调用时,`argType` 与 `argValue` 类型匹配
|
|
540
|
-
- [ ] 对外暴露的方法返回结构(如
|
|
541
|
-
`JSONObject`)与前端/调用方约定一致,字段清晰、语义明确
|
|
933
|
+
## 18. 一句话结论
|
|
934
|
+
|
|
935
|
+
AI 编写 CloudCC 自定义类时,最重要的不是“把需求翻译成几段 Java”,而是遵守 CloudCC 的类管理方式、正确使用 SDK、把复杂逻辑放在服务端可治理的位置,并让代码具备复用性、可追踪性和可发布性。
|