cloudcc-cli 2.2.8 → 2.2.9
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/.cloudcc-cache.json +32 -1
- package/.cursor/skills/cloudcc-cli-dev/BACKEND_CLASS.md +97 -0
- package/.cursor/skills/cloudcc-cli-dev/BACKEND_SCHEDULE.md +78 -0
- package/.cursor/skills/cloudcc-cli-dev/BACKEND_TRIGGER.md +137 -0
- package/.cursor/skills/cloudcc-cli-dev/CLI_CHEATSHEET.md +215 -0
- package/{cloudcc-cli-dev → .cursor/skills/cloudcc-cli-dev}/SKILL.md +7 -2
- package/.cursor/skills/cloudcc-cli-dev/VUE_CUSTOM_COMPONENT.md +151 -0
- package/README.md +11 -0
- package/bin/index.js +2 -0
- package/build/component-cc-test-001.common.js +831 -0
- package/build/component-cc-test-001.common.js.map +1 -0
- package/build/component-cc-test-001.css +1 -0
- package/build/component-cc-test-001.umd.js +874 -0
- package/build/component-cc-test-001.umd.js.map +1 -0
- package/build/component-cc-test-001.umd.min.js +8 -0
- package/build/component-cc-test-001.umd.min.js.map +1 -0
- package/build/demo.html +1 -0
- package/docs/CloudCC/350/207/252/345/256/232/344/271/211/347/273/204/344/273/266/344/275/277/347/224/250/350/257/264/346/230/216.md +130 -0
- package/docs/cloudcc/345/256/232/346/227/266/344/275/234/344/270/232.md +472 -0
- package/docs/cloudcc/345/256/232/346/227/266/347/261/273.md +302 -0
- package/docs//350/207/252/345/256/232/344/271/211/347/261/273.md +258 -0
- package/docs//350/247/246/345/217/221/345/231/250/347/261/273.md +404 -0
- package/package.json +1 -1
- package/plugins/cc-test-001/cc-test-001.vue +32 -0
- package/plugins/cc-test-001/components/HelloWorld.vue +11 -0
- package/plugins/cc-test-001/config.json +6 -0
- package/src/classes/index.js +1 -6
- package/src/plugin/delete.js +91 -0
- package/src/plugin/doc.js +76 -0
- package/src/plugin/index.js +1 -0
- package/src/triggers/doc.js +258 -222
- package/src/triggers/pullList.js +3 -0
- package/cloudcc-cli-dev/BACKEND_CODE.md +0 -114
- package/cloudcc-cli-dev/CLI_CHEATSHEET.md +0 -90
- package/cloudcc-cli-dev/VUE_CUSTOM_COMPONENT.md +0 -50
- /package/{cloudcc-cli-dev → .cursor/skills/cloudcc-cli-dev}/INSTALL_AND_BOOTSTRAP.md +0 -0
- /package/{cloudcc-cli-dev → .cursor/skills/cloudcc-cli-dev}/OBJECTS_AND_FIELDS.md +0 -0
- /package/{cloudcc-cli-dev → .cursor/skills/cloudcc-cli-dev}/REQUIREMENTS_BREAKDOWN.md +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# CloudCC 触发器使用总结
|
|
2
|
+
|
|
3
|
+
> 基于生产环境 25 个触发器实例分析
|
|
4
|
+
> 文档版本:v1.0
|
|
5
|
+
> 更新时间:2026-03-24
|
|
6
|
+
|
|
7
|
+
## 一、什么是触发器
|
|
8
|
+
|
|
9
|
+
CloudCC 触发器是符合 Java 语法规范的服务端代码,在特定业务事件发生时自动执行,用于实现业务逻辑自动化处理。
|
|
10
|
+
|
|
11
|
+
触发器本质:
|
|
12
|
+
|
|
13
|
+
- 一段 Java 代码,运行在 CloudCC 平台沙箱环境中
|
|
14
|
+
- 使用 `CCService`、`CCObject` 等平台 API 类
|
|
15
|
+
- 在数据变更的特定时刻(前/后)自动触发执行
|
|
16
|
+
|
|
17
|
+
## 二、触发器能干什么
|
|
18
|
+
|
|
19
|
+
### 2.1 核心能力
|
|
20
|
+
|
|
21
|
+
| 能力 | 说明 | 示例 |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| 自动赋值 | 在记录保存前自动计算并填充字段 | 个人业绩/部门业绩自动赋值 |
|
|
24
|
+
| 数据校验 | 阻止不符合业务规则的数据保存 | 预算金额校验、审批金额验证 |
|
|
25
|
+
| 关联更新 | 修改一条记录时自动更新关联记录 | 回款明细汇总到收款计划 |
|
|
26
|
+
| 自动生成 | 根据业务规则自动创建新记录 | 审批通过后自动生成回款记录 |
|
|
27
|
+
| 权限控制 | 自动设置数据共享权限 | 按部门/人员自动共享 |
|
|
28
|
+
| 状态同步 | 审批状态变更后同步更新相关字段 | 预算审批状态回写 |
|
|
29
|
+
| 汇总计算 | 实时汇总明细数据到主记录 | 部门业绩字段汇总 |
|
|
30
|
+
|
|
31
|
+
### 2.2 技术能力(基于 CCService API)
|
|
32
|
+
|
|
33
|
+
```java
|
|
34
|
+
// 1. 新增记录
|
|
35
|
+
CCObject opp = new CCObject("Opportunity");
|
|
36
|
+
opp.put("name", "value");
|
|
37
|
+
cs.insert(opp);
|
|
38
|
+
|
|
39
|
+
// 2. 查询记录
|
|
40
|
+
// 查询指定字段
|
|
41
|
+
List<CCObject> opps = cs.cqueryByFields(
|
|
42
|
+
"Opportunity",
|
|
43
|
+
"khmc__c='" + record_new.get("id") + "'",
|
|
44
|
+
"id,name,createdate"
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 查询全部字段
|
|
48
|
+
List<CCObject> all = cs.cquery("Opportunity", "khmc__c='" + record_new.get("id") + "'");
|
|
49
|
+
|
|
50
|
+
// 复杂 SQL 查询
|
|
51
|
+
List<CCObject> ccobjs = cs.cqlQuery(
|
|
52
|
+
"Opportunity",
|
|
53
|
+
"select sum(amount) as sumAmount from Opportunity where khmc = '0012001238ytwuiw'"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// 3. 修改记录
|
|
57
|
+
cs.update(opp);
|
|
58
|
+
|
|
59
|
+
// 4. 删除记录
|
|
60
|
+
cs.delete(opp);
|
|
61
|
+
|
|
62
|
+
// 5. 发送邮件通知
|
|
63
|
+
SendEmail sendEmail = new SendEmail(userInfo);
|
|
64
|
+
sendEmail.sendMailFromSystem(toAddress, ccAddress, bccAddress, subject, content, isText);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 三、触发器触发时机详解
|
|
68
|
+
|
|
69
|
+
### 3.1 beforeUpsert(创建或更新前)
|
|
70
|
+
|
|
71
|
+
执行时机:记录保存之前,数据尚未写入数据库。
|
|
72
|
+
典型用途:
|
|
73
|
+
|
|
74
|
+
- 自动计算字段值
|
|
75
|
+
- 数据格式校验
|
|
76
|
+
- 阻止非法数据保存(抛出异常)
|
|
77
|
+
- 自动填充默认值
|
|
78
|
+
|
|
79
|
+
生产实例:
|
|
80
|
+
|
|
81
|
+
| 触发器名称 | 对象 | 作用 |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| 个人业绩/部门业绩赋值 | 对外付款 | 自动赋值业绩字段 |
|
|
84
|
+
| 计算产研成本/净业绩 | 预算 | 自动计算成本、净业绩 |
|
|
85
|
+
| 生成部门/个人业绩 | 净业绩拆分 | 自动创建业绩记录 |
|
|
86
|
+
| 编辑更新部门业绩 | 净业绩拆分回款 | 更新前校验和计算 |
|
|
87
|
+
|
|
88
|
+
代码示例:
|
|
89
|
+
|
|
90
|
+
```java
|
|
91
|
+
CCObject record = (CCObject) data.get("record");
|
|
92
|
+
BigDecimal income = record.getBigDecimal("ysr");
|
|
93
|
+
BigDecimal cost = record.getBigDecimal("cycb");
|
|
94
|
+
BigDecimal net = income.subtract(cost);
|
|
95
|
+
record.put("jyj", net);
|
|
96
|
+
if (net.compareTo(BigDecimal.ZERO) < 0) {
|
|
97
|
+
throw new Exception("毛利不能为负数!");
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3.2 afterInsert(创建后)
|
|
102
|
+
|
|
103
|
+
执行时机:记录已成功创建,ID 已生成。
|
|
104
|
+
典型用途:
|
|
105
|
+
|
|
106
|
+
- 基于新记录 ID 创建关联记录
|
|
107
|
+
- 发送创建通知
|
|
108
|
+
- 初始化关联数据
|
|
109
|
+
|
|
110
|
+
生产实例:
|
|
111
|
+
|
|
112
|
+
| 触发器名称 | 对象 | 作用 |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| 根据净业绩拆分生成净业绩拆分回款 | 回款明细 | 新建回款明细后自动生成回款记录 |
|
|
115
|
+
|
|
116
|
+
代码示例:
|
|
117
|
+
|
|
118
|
+
```java
|
|
119
|
+
CCObject newRecord = (CCObject) data.get("record");
|
|
120
|
+
String recordId = newRecord.getString("id");
|
|
121
|
+
|
|
122
|
+
CCObject hkRecord = new CCObject("Huikuan");
|
|
123
|
+
hkRecord.put("mingxi__c", recordId);
|
|
124
|
+
hkRecord.put("je", newRecord.get("je"));
|
|
125
|
+
cs.insert(hkRecord);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3.3 afterUpsert(创建或更新后)
|
|
129
|
+
|
|
130
|
+
执行时机:记录已成功保存(新增或修改)。
|
|
131
|
+
典型用途:
|
|
132
|
+
|
|
133
|
+
- 汇总数据到父记录
|
|
134
|
+
- 触发下游业务流程
|
|
135
|
+
- 更新关联记录状态
|
|
136
|
+
|
|
137
|
+
生产实例:
|
|
138
|
+
|
|
139
|
+
| 触发器名称 | 对象 | 作用 |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| 金额汇总到收款计划 | 回款明细 | 汇总回款金额到收款计划 |
|
|
142
|
+
| 更新部门业绩字段 | 部门业绩 | 汇总各维度业绩数据 |
|
|
143
|
+
| 更新回款明细 | 回款明细 | 重建净业绩拆分回款记录 |
|
|
144
|
+
|
|
145
|
+
代码示例:
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
CCObject detail = (CCObject) data.get("record");
|
|
149
|
+
String planId = detail.getString("skjh__c");
|
|
150
|
+
List<CCObject> plans = cs.cqueryByFields("ShoukuanJihua", "id='" + planId + "'", "id,shyjje,yishouje");
|
|
151
|
+
if (!plans.isEmpty()) {
|
|
152
|
+
CCObject plan = plans.get(0);
|
|
153
|
+
BigDecimal current = plan.getBigDecimal("yishouje");
|
|
154
|
+
BigDecimal detailAmount = detail.getBigDecimal("je");
|
|
155
|
+
plan.put("yishouje", current.add(detailAmount));
|
|
156
|
+
cs.update(plan);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 3.4 afterDelete(删除后)
|
|
161
|
+
|
|
162
|
+
执行时机:记录已成功删除。
|
|
163
|
+
典型用途:
|
|
164
|
+
|
|
165
|
+
- 清理关联数据
|
|
166
|
+
- 反向汇总更新
|
|
167
|
+
|
|
168
|
+
生产实例:
|
|
169
|
+
|
|
170
|
+
| 触发器名称 | 对象 | 作用 |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| 回款明细删除 | 回款明细 | 删除后重新计算回款金额 |
|
|
173
|
+
|
|
174
|
+
### 3.5 approval(审批时)
|
|
175
|
+
|
|
176
|
+
执行时机:记录提交审批或审批通过时。
|
|
177
|
+
典型用途:
|
|
178
|
+
|
|
179
|
+
- 审批前校验业务规则
|
|
180
|
+
- 审批通过后执行业务操作
|
|
181
|
+
|
|
182
|
+
生产实例:
|
|
183
|
+
|
|
184
|
+
| 触发器名称 | 对象 | 作用 |
|
|
185
|
+
| --- | --- | --- |
|
|
186
|
+
| 更新拆分审批状态 | 预算 | 审批通过后回写状态 |
|
|
187
|
+
| 订单审批判断 | 订单 | 审批时校验收款计划 |
|
|
188
|
+
| 审批通过计算剩余欠款 | 费用报销 | 审批后计算欠款 |
|
|
189
|
+
|
|
190
|
+
## 四、为什么要用触发器
|
|
191
|
+
|
|
192
|
+
### 4.1 解决的核心问题
|
|
193
|
+
|
|
194
|
+
| 问题 | 传统方案 | 触发器方案 |
|
|
195
|
+
| --- | --- | --- |
|
|
196
|
+
| 数据一致性 | 依赖用户手动操作,容易遗漏 | 自动执行,保证数据准确 |
|
|
197
|
+
| 业务规则执行 | 前端校验可绕过 | 平台层统一执行,无法绕过 |
|
|
198
|
+
| 跨对象联动 | 需要多次手动操作 | 自动关联更新 |
|
|
199
|
+
| 审批流程复杂逻辑 | 审批流配置无法实现 | Java 代码实现任意逻辑 |
|
|
200
|
+
| 实时性要求 | 定时任务有延迟 | 实时触发,立即执行 |
|
|
201
|
+
|
|
202
|
+
### 4.2 对比其他方案
|
|
203
|
+
|
|
204
|
+
| 方案 | 优点 | 缺点 | 适用场景 |
|
|
205
|
+
| --- | --- | --- | --- |
|
|
206
|
+
| 触发器 | 实时、自动、无法绕过 | 需要 Java 开发能力 | 核心业务逻辑 |
|
|
207
|
+
| 审批流 | 配置简单、可视化 | 逻辑能力有限 | 简单审批流程 |
|
|
208
|
+
| 工作流 | 可视化编排 | 复杂逻辑实现困难 | 跨系统流程编排 |
|
|
209
|
+
| 定时任务 | 适合批量处理 | 有延迟 | 数据同步、报表生成 |
|
|
210
|
+
|
|
211
|
+
## 五、典型业务场景
|
|
212
|
+
|
|
213
|
+
### 5.1 财务业绩管理(核心场景)
|
|
214
|
+
|
|
215
|
+
触发器链路:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
订单 (Order)
|
|
219
|
+
│ approval 触发器:审批判断
|
|
220
|
+
▼
|
|
221
|
+
预算 (Budget)
|
|
222
|
+
│ beforeUpsert: 计算产研成本/净业绩
|
|
223
|
+
│ afterUpsert: 自动拆分/生成回款
|
|
224
|
+
▼
|
|
225
|
+
净业绩拆分 (jyjcf)
|
|
226
|
+
│ beforeUpsert: 生成部门/个人业绩
|
|
227
|
+
│ afterUpsert: 校验金额累计是否超预算
|
|
228
|
+
▼
|
|
229
|
+
部门业绩 (bmyj)
|
|
230
|
+
│ afterUpsert: 更新部门业绩字段
|
|
231
|
+
│ afterUpdate: 汇总部门资金池
|
|
232
|
+
回款明细 (cloudccproceeddetail)
|
|
233
|
+
│ afterInsert: 生成净业绩拆分回款
|
|
234
|
+
│ afterUpsert: 金额汇总到收款计划
|
|
235
|
+
│ afterUpsert: 更新回款明细
|
|
236
|
+
│ afterDelete: 回款明细删除
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
关键触发器说明:
|
|
240
|
+
|
|
241
|
+
1. 计算产研成本/净业绩(预算 - beforeUpsert)
|
|
242
|
+
- 自动计算预算收入、产研成本、净业绩
|
|
243
|
+
- 审批控制:毛利 < 0 阻止提交
|
|
244
|
+
2. 生成部门/个人业绩(净业绩拆分 - beforeUpsert)
|
|
245
|
+
- 根据拆分规则自动创建业绩记录
|
|
246
|
+
- 按 owner 部门与年份关联业绩主数据
|
|
247
|
+
3. 金额汇总到收款计划(回款明细 - afterUpsert)
|
|
248
|
+
- 汇总同一回款单下的明细金额
|
|
249
|
+
- 回填至收款计划(实际收款、余额、是否完成)
|
|
250
|
+
4. 更新部门业绩字段(部门业绩 - afterUpsert)
|
|
251
|
+
- 按“类别 + 业绩计算方式”多维度汇总
|
|
252
|
+
- 计算目标完成率
|
|
253
|
+
|
|
254
|
+
### 5.2 费用报销管理
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
费用报销提交
|
|
258
|
+
│
|
|
259
|
+
▼
|
|
260
|
+
[approval 触发器] 验证审批时校验预算
|
|
261
|
+
│ 校验:报销金额 <= 预算余额
|
|
262
|
+
▼
|
|
263
|
+
审批通过
|
|
264
|
+
│
|
|
265
|
+
▼
|
|
266
|
+
[approval 触发器] 审批通过计算剩余欠款
|
|
267
|
+
│ 计算:剩余欠款 = 借款 - 报销
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 5.3 借款还款管理
|
|
271
|
+
|
|
272
|
+
| 触发器 | 对象 | 时机 | 作用 |
|
|
273
|
+
| --- | --- | --- | --- |
|
|
274
|
+
| 借款单触发器 | 借款单 | beforeUpsert | 借款前校验、自动计算 |
|
|
275
|
+
| 还款记录审批通过更新借款单 | 还款记录 | approval | 审批后更新借款单 |
|
|
276
|
+
|
|
277
|
+
## 六、触发器开发最佳实践
|
|
278
|
+
|
|
279
|
+
### 6.1 代码规范
|
|
280
|
+
|
|
281
|
+
```java
|
|
282
|
+
try {
|
|
283
|
+
CCObject record = (CCObject) data.get("record");
|
|
284
|
+
cs.update(record);
|
|
285
|
+
} catch (Exception e) {
|
|
286
|
+
System.out.println("触发器执行失败:" + e.getMessage());
|
|
287
|
+
throw new Exception("业务校验失败:" + e.getMessage());
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 6.2 性能优化
|
|
292
|
+
|
|
293
|
+
```java
|
|
294
|
+
// 字段过滤,只查询需要的字段
|
|
295
|
+
List<CCObject> records = cs.cqueryByFields(
|
|
296
|
+
"Object__c",
|
|
297
|
+
"status__c='active'",
|
|
298
|
+
"id,name,amount__c"
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
// 批量操作使用 updateLt(避免递归)
|
|
302
|
+
cs.updateLt(records);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 6.3 避免递归触发
|
|
306
|
+
|
|
307
|
+
```java
|
|
308
|
+
// 方法 1:使用 updateLt
|
|
309
|
+
cs.updateLt(records);
|
|
310
|
+
|
|
311
|
+
// 方法 2:添加标志位
|
|
312
|
+
if ("true".equals(System.getProperty("trigger.running"))) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
System.setProperty("trigger.running", "true");
|
|
316
|
+
cs.update(record);
|
|
317
|
+
System.setProperty("trigger.running", "false");
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## 七、触发器调试与监控
|
|
321
|
+
|
|
322
|
+
### 7.1 日志记录
|
|
323
|
+
|
|
324
|
+
```java
|
|
325
|
+
System.out.println("=== 触发器开始执行 ===");
|
|
326
|
+
System.out.println("对象:" + record.getSObjectName());
|
|
327
|
+
System.out.println("操作:" + data.get("action"));
|
|
328
|
+
System.out.println("记录 ID: " + record.get("id"));
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 7.2 常见问题排查
|
|
332
|
+
|
|
333
|
+
| 问题 | 可能原因 | 解决方案 |
|
|
334
|
+
| --- | --- | --- |
|
|
335
|
+
| 触发器不执行 | 未激活、触发时机不对 | 检查状态和配置 |
|
|
336
|
+
| 递归触发 | 更新操作再次触发触发器 | 使用 `updateLt` |
|
|
337
|
+
| 性能问题 | 查询数据量过大 | 添加查询条件、字段过滤 |
|
|
338
|
+
| 数据不一致 | 触发器执行失败 | 添加异常处理 |
|
|
339
|
+
|
|
340
|
+
## 八、总结
|
|
341
|
+
|
|
342
|
+
### 8.1 触发器的价值
|
|
343
|
+
|
|
344
|
+
1. 自动化:减少人工操作,降低出错率
|
|
345
|
+
2. 一致性:保证数据准确性和业务规则执行
|
|
346
|
+
3. 实时性:业务变更立即响应
|
|
347
|
+
4. 灵活性:Java 代码可实现复杂业务逻辑
|
|
348
|
+
|
|
349
|
+
### 8.2 何时使用触发器
|
|
350
|
+
|
|
351
|
+
适合使用:
|
|
352
|
+
|
|
353
|
+
- 需要实时数据联动
|
|
354
|
+
- 核心业务规则校验
|
|
355
|
+
- 跨对象数据同步
|
|
356
|
+
- 审批流程中的复杂逻辑
|
|
357
|
+
- 自动创建/更新关联记录
|
|
358
|
+
|
|
359
|
+
不适合使用:
|
|
360
|
+
|
|
361
|
+
- 简单字段默认值
|
|
362
|
+
- 大批量数据处理
|
|
363
|
+
- 跨系统集成
|
|
364
|
+
- 用户界面交互
|
|
365
|
+
|
|
366
|
+
### 8.3 触发器开发 Checklist
|
|
367
|
+
|
|
368
|
+
- 明确触发时机(before/after/approval)
|
|
369
|
+
- 评估递归触发风险
|
|
370
|
+
- 添加异常处理
|
|
371
|
+
- 记录关键日志
|
|
372
|
+
- 测试边界情况
|
|
373
|
+
- 做大数据量场景性能测试
|
|
374
|
+
|
|
375
|
+
## 附录:25 个生产触发器清单
|
|
376
|
+
|
|
377
|
+
| # | 名称 | 对象 | 时机 | 状态 |
|
|
378
|
+
| --- | --- | --- | --- | --- |
|
|
379
|
+
| 1 | 个人业绩/部门业绩赋值 | 对外付款 | beforeUpsert | ✅ |
|
|
380
|
+
| 2 | 个人业绩/部门业绩赋值 | 费用报销 | beforeUpsert | ❌ |
|
|
381
|
+
| 3 | 本部业绩统计 | 本部业绩统计 | beforeUpsert | ✅ |
|
|
382
|
+
| 4 | 更新拆分审批状态 | 预算 | approval | ✅ |
|
|
383
|
+
| 5 | 订单审批判断 | 订单 | approval | ✅ |
|
|
384
|
+
| 6 | 根据净业绩拆分生成净业绩拆分回款 | 回款明细 | afterInsert | ✅ |
|
|
385
|
+
| 7 | 金额汇总到收款计划 | 回款明细 | afterUpsert | ✅ |
|
|
386
|
+
| 8 | 回款明细删除 | 回款明细 | afterDelete | ❌ |
|
|
387
|
+
| 9 | 计算产研成本/净业绩 | 预算 | beforeUpsert | ✅ |
|
|
388
|
+
| 10 | 保证金回款记录 | 概算 | afterUpdate | ✅ |
|
|
389
|
+
| 11 | 校验金额累计是否超预算 | 净业绩拆分 | afterUpsert | ✅ |
|
|
390
|
+
| 12 | 更新部门业绩字段 | 部门业绩 | afterUpsert | ✅ |
|
|
391
|
+
| 13 | 汇总部门资金池 | 部门业绩 | afterUpdate | ✅ |
|
|
392
|
+
| 14 | 编辑更新部门业绩 | 净业绩拆分回款 | beforeUpsert | ✅ |
|
|
393
|
+
| 15 | 生成部门/个人业绩 | 净业绩拆分 | beforeUpsert | ✅ |
|
|
394
|
+
| 16 | 更新回款明细 | 回款明细 | afterUpsert | ✅ |
|
|
395
|
+
| 17 | (新)新建自动拆分/审批通过自动生成回款 | 预算 | afterUpsert | ✅ |
|
|
396
|
+
| 18 | 提交审批验证金额 | 对外付款 | approval | ❌ |
|
|
397
|
+
| 19 | 还款记录审批通过更新借款单 | 还款记录 | approval | ✅ |
|
|
398
|
+
| 20 | 审批通过计算剩余欠款 | 费用报销 | approval | ✅ |
|
|
399
|
+
| 21 | 借款单触发器 | 借款单 | beforeUpsert | ✅ |
|
|
400
|
+
| 22 | 创建编辑项目任务时判断 | 项目任务 | beforeUpsert | ✅ |
|
|
401
|
+
| 23 | 任务派工工时限制 | 项目任务 | afterUpsert | ✅ |
|
|
402
|
+
| 24 | 测试 AI 代码生成 | 客户 | beforeInsert | ❌ |
|
|
403
|
+
| 25 | 验证审批时校验预算 | 费用报销 | approval | ❌ |
|
|
404
|
+
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cc-container">
|
|
3
|
+
<HelloWorld />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
import HelloWorld from "./components/HelloWorld.vue";
|
|
9
|
+
export default {
|
|
10
|
+
components: {
|
|
11
|
+
HelloWorld,
|
|
12
|
+
},
|
|
13
|
+
data() {
|
|
14
|
+
return {
|
|
15
|
+
componentInfo: {
|
|
16
|
+
|
|
17
|
+
component: "component-cc-test-001",
|
|
18
|
+
|
|
19
|
+
compName: "compName-cc-test-001",
|
|
20
|
+
|
|
21
|
+
compDesc: "Component description information",
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
</script>
|
|
27
|
+
<style lang="scss" scoped>
|
|
28
|
+
.cc-container {
|
|
29
|
+
text-align: center;
|
|
30
|
+
padding: 8px;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
package/src/classes/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
const cc = {}
|
|
2
|
-
|
|
3
|
-
cc.publish = require("./publish")
|
|
4
|
-
cc.pull = require("./pull")
|
|
5
|
-
cc.get = require("./get")
|
|
6
|
-
cc.pullList = require("./pullList")
|
|
7
|
-
cc.detail = require("./detail")
|
|
2
|
+
|
|
8
3
|
cc.doc = require("./doc")
|
|
9
4
|
function Classes(action, argvs) {
|
|
10
5
|
cc[action](argvs[2], argvs[3])
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { getPackageJson } = require("../../utils/config");
|
|
5
|
+
const { post } = require("../../utils/http");
|
|
6
|
+
const BaseUrl = "https://developer.apis.cloudcc.cn";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 删除云端自定义组件
|
|
10
|
+
* 支持通过组件名或 ID 删除:
|
|
11
|
+
* - 传组件名时:从本地 plugins/<name>/config.json 读取 id 后删除
|
|
12
|
+
* - 传 ID 时:直接通过 ID 删除
|
|
13
|
+
*
|
|
14
|
+
* 用法:cc delete plugin <pluginNameOrId> [projectPath]
|
|
15
|
+
*/
|
|
16
|
+
async function deletePlugin(argvs) {
|
|
17
|
+
const input = argvs[2];
|
|
18
|
+
const projectPath = argvs[3] || process.cwd();
|
|
19
|
+
|
|
20
|
+
if (!input) {
|
|
21
|
+
console.error();
|
|
22
|
+
console.error(chalk.red('Error: Plugin name or ID is required'));
|
|
23
|
+
console.error(chalk.yellow('Usage: cc delete plugin <pluginNameOrId> [projectPath]'));
|
|
24
|
+
console.error();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 先尝试将 input 作为组件名处理:检查本地 config.json 是否有 id
|
|
29
|
+
const configPath = path.join(projectPath, `plugins/${input}/config.json`);
|
|
30
|
+
if (fs.existsSync(configPath)) {
|
|
31
|
+
try {
|
|
32
|
+
const configContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
33
|
+
if (configContent.id) {
|
|
34
|
+
await deleteById(configContent.id, projectPath);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(chalk.yellow(`Warning: Failed to read config.json, treating input as ID: ${error.message}`));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 作为 ID 处理
|
|
43
|
+
await deleteById(input, projectPath);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 通过 ID 删除云端组件
|
|
48
|
+
*/
|
|
49
|
+
async function deleteById(pluginId, projectPath) {
|
|
50
|
+
const config = await getPackageJson(projectPath);
|
|
51
|
+
if (!config || !config.accessToken) {
|
|
52
|
+
throw new Error('Configuration not found or accessToken is missing');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const devSvcDispatch = config.devSvcDispatch || '/devconsole';
|
|
56
|
+
const baseUrl = config.baseUrl || BaseUrl;
|
|
57
|
+
|
|
58
|
+
const header = {
|
|
59
|
+
"appType": "lightning-setup",
|
|
60
|
+
"appVersion": "0.0.1",
|
|
61
|
+
"accessToken": config.accessToken,
|
|
62
|
+
"source": "lightning-setup",
|
|
63
|
+
"version": "public"
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const body = {
|
|
67
|
+
"id": pluginId
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
console.error(chalk.green(`Deleting plugin (ID: ${pluginId}), please wait...`));
|
|
71
|
+
|
|
72
|
+
const res = await post(
|
|
73
|
+
`${baseUrl}${devSvcDispatch}/custom/pc/1.0/post/deleteCustomComp`,
|
|
74
|
+
body,
|
|
75
|
+
header
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (res && res.returnCode == 200) {
|
|
79
|
+
console.error(chalk.green(`Success! Plugin (ID: ${pluginId}) deleted from server.`));
|
|
80
|
+
console.error();
|
|
81
|
+
} else {
|
|
82
|
+
const errMsg = res?.returnInfo || 'Unknown error';
|
|
83
|
+
console.error(chalk.red(`Fail: ${errMsg}`));
|
|
84
|
+
console.error();
|
|
85
|
+
throw new Error('Delete Plugin Failed: ' + errMsg);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return res;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = deletePlugin;
|
package/src/plugin/doc.js
CHANGED
|
@@ -13,6 +13,82 @@ function generateFullMarkdownDoc() {
|
|
|
13
13
|
"",
|
|
14
14
|
"---",
|
|
15
15
|
"",
|
|
16
|
+
"## 0. 自定义组件概述(能力与场景)",
|
|
17
|
+
"",
|
|
18
|
+
"本文节整合了仓库内实践用法,帮助你在开始编码前先判断:什么是自定义组件、能解决什么问题、何时该用。",
|
|
19
|
+
"",
|
|
20
|
+
"### 0.1 什么是自定义组件(本项目语境)",
|
|
21
|
+
"",
|
|
22
|
+
"在本项目中,**自定义组件**通常指以 **Vue 2 单文件组件(`.vue`)** 为入口、放在 `plugins/<插件名>/` 下,通过 `cloudccBuild` / `cloudccBuild2` 等 CLI 命令打包后,嵌入 CloudCC 页面或应用的前端能力单元。",
|
|
23
|
+
"",
|
|
24
|
+
"一个插件一般包含:",
|
|
25
|
+
"",
|
|
26
|
+
"| 内容 | 作用 |",
|
|
27
|
+
"|------|------|",
|
|
28
|
+
"| 入口 `.vue`(如 `cc-ai-chat.vue`) | 组件 UI 与业务逻辑 |",
|
|
29
|
+
"| `config.json` | 组件标识、名称、描述、分类、加载模式等元数据 |",
|
|
30
|
+
"| `components/`、`utils/` 等 | 子组件、HTTP 封装、适配器、第三方 SDK 封装 |",
|
|
31
|
+
"",
|
|
32
|
+
"入口组件中常见 `componentInfo` 对象,与 `config.json` 呼应,用于平台展示组件信息与加载策略。",
|
|
33
|
+
"",
|
|
34
|
+
"### 0.2 自定义组件能做什么",
|
|
35
|
+
"",
|
|
36
|
+
"- 扩展标准 CRM 页面:在记录页、列表页、门户中挂载专用界面",
|
|
37
|
+
"- 对接外部系统:接入 OpenAPI、Agent、地图、图可视化等复杂交互",
|
|
38
|
+
"- 使用 CloudCC 运行时能力:通过 `$CCDK` 读取用户、Token、网关、事件总线",
|
|
39
|
+
"- 实现独立数据层(按需):对接组织允许的外部存储或服务",
|
|
40
|
+
"- 搭建实施与诊断工具:同步、调试、日志、排查面板等运营能力",
|
|
41
|
+
"",
|
|
42
|
+
"一句话:自定义组件是在 CloudCC 宿主内运行、可独立版本化的前端扩展能力。",
|
|
43
|
+
"",
|
|
44
|
+
"### 0.3 为什么要用自定义组件",
|
|
45
|
+
"",
|
|
46
|
+
"| 原因 | 说明 |",
|
|
47
|
+
"|------|------|",
|
|
48
|
+
"| 标准功能边界 | 标准布局与字段配置难覆盖甘特图、对话式 AI、复杂工作台等场景 |",
|
|
49
|
+
"| 体验与品牌 | 需要统一交互、定制视觉与复合布局体验 |",
|
|
50
|
+
"| 集成成本 | 第三方 SaaS/自研 API 更适合在前端组件中做适配封装 |",
|
|
51
|
+
"| 迭代效率 | 前端组件可独立发版,与后端逻辑解耦,支持小步快跑 |",
|
|
52
|
+
"| 复用能力 | 同一组件可复用到多个页面,通过属性配置实现差异化 |",
|
|
53
|
+
"",
|
|
54
|
+
"### 0.4 典型可解决问题",
|
|
55
|
+
"",
|
|
56
|
+
"1. 平台可配置但体验较差:通过专用 UI 优化录入、查询、可视化流程",
|
|
57
|
+
"2. 必须与外部系统实时协同:在组件内完成请求与回调闭环",
|
|
58
|
+
"3. 需要强上下文:利用 `$CCDK` 获取当前用户、视图与凭证避免重复登录",
|
|
59
|
+
"4. 需要跨模块联动:借助总线事件让多个区域同步响应",
|
|
60
|
+
"5. 需要工具化交付:建设调试与运维面板降低人工操作风险",
|
|
61
|
+
"",
|
|
62
|
+
"### 0.5 什么时候优先考虑自定义组件",
|
|
63
|
+
"",
|
|
64
|
+
"| 场景类型 | 示例 |",
|
|
65
|
+
"|----------|------|",
|
|
66
|
+
"| 复杂可视化 | 甘特图、关系图、统计大屏 |",
|
|
67
|
+
"| 对话与智能助手 | 嵌入式 AI 聊天、Agent 工具面板 |",
|
|
68
|
+
"| 行业专用工作台 | 客户 360、项目工作台、专题门户 |",
|
|
69
|
+
"| 门户与附件增强 | 文件上传下载、模板导出导入 |",
|
|
70
|
+
"| 表单 + 外部库 | 自定义流程表单 + 第三方服务 |",
|
|
71
|
+
"| 实施与运维工具 | SQL 工具、健康检查、调试面板 |",
|
|
72
|
+
"",
|
|
73
|
+
"不建议强行使用自定义组件的场景:",
|
|
74
|
+
"- 仅靠标准字段、列表、校验即可满足需求时",
|
|
75
|
+
"- 需要强安全与一致性的核心业务规则(应放在服务端)",
|
|
76
|
+
"",
|
|
77
|
+
"### 0.6 开发与构建要点(仓库实践)",
|
|
78
|
+
"",
|
|
79
|
+
"- 依赖按需引入,避免插件无关依赖膨胀",
|
|
80
|
+
"- 使用 `npm run build-plugin`(`cloudccBuild`)产出发布包",
|
|
81
|
+
"- 本地通过 `npm run serve` 快速调试组件交互",
|
|
82
|
+
"- 运行环境通过注入 `$CCDK` 提供统一账号、网关、上传、日志与事件能力",
|
|
83
|
+
"",
|
|
84
|
+
"### 0.7 结论",
|
|
85
|
+
"",
|
|
86
|
+
"- 自定义组件 = CloudCC 宿主内的 Vue 插件包 + 元数据 + 可选 `$CCDK` 集成",
|
|
87
|
+
"- 核心价值:在保持平台一致性的同时,快速交付标准配置覆盖不到的能力",
|
|
88
|
+
"- 选型建议:复杂 UI、多系统集成、强上下文、工具化需求优先;可标准配置解决则优先标准方案",
|
|
89
|
+
"",
|
|
90
|
+
"---",
|
|
91
|
+
"",
|
|
16
92
|
"## 1. 快速上手:发布第一个组件",
|
|
17
93
|
"",
|
|
18
94
|
"### 1.1 启动项目",
|
package/src/plugin/index.js
CHANGED