@yjli/wedaopenapi 1.0.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/auth.js ADDED
@@ -0,0 +1,65 @@
1
+ const fetch = require('node-fetch');
2
+
3
+ class Auth {
4
+ constructor(options) {
5
+ this.options = Object.assign(
6
+ {
7
+ // 主机地址
8
+ host: 'https://weda.web.scut.edu.cn',
9
+
10
+ // 访问令牌密钥
11
+ secretId: undefined,
12
+ secretKey: undefined,
13
+
14
+ debug: false,
15
+ },
16
+ options
17
+ );
18
+
19
+ if (!this.options.secretId || !this.options.secretKey) {
20
+ throw new Error('secretId 或 secretKey 未设置');
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 获取访问令牌
26
+ */
27
+ async getAccessToken() {
28
+ // 如果缓存中存在(未过期),则直接返回缓存中的值
29
+ if (this.token && this.tokenExpiredAt > Date.now()) {
30
+ return this.token;
31
+ }
32
+
33
+ // 否则,获取 token,并存入缓存
34
+ const tokenResponse = await fetch(
35
+ `${this.options.host}/auth/v1/token/clientCredential`,
36
+ {
37
+ method: 'POST',
38
+ headers: {
39
+ 'Content-Type': 'application/json',
40
+ Authorization: `Basic ${Buffer.from(
41
+ `${this.options.secretId}:${this.options.secretKey}`
42
+ ).toString('base64')}`,
43
+ },
44
+ body: JSON.stringify({
45
+ grant_type: 'client_credentials',
46
+ }),
47
+ }
48
+ );
49
+
50
+ const json = await tokenResponse.json();
51
+
52
+ if (this.options.debug) {
53
+ console.log('Access Token Response', json);
54
+ }
55
+
56
+ const { access_token, expires_in } = json;
57
+
58
+ // 缓存 token,并设置过期时间,提前 1 分钟过期扩大容错。
59
+ this.token = access_token;
60
+ this.tokenExpiredAt = Date.now() + expires_in * 1000 - 60000;
61
+
62
+ return access_token;
63
+ }
64
+ }
65
+ module.exports = Auth;
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ WedaOpenApi: require("./wedaopenapi"),
3
+ Workflow: require("./workflow")
4
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@yjli/wedaopenapi",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://ncgit.scut.edu.cn/dev/lowcode/wedaopenapi.git"
11
+ },
12
+ "keywords": [
13
+ "weda",
14
+ "openapi"
15
+ ],
16
+ "author": "yjli",
17
+ "license": "ISC",
18
+ "description": "",
19
+ "dependencies": {
20
+ "node-fetch": "^2.7.0"
21
+ },
22
+ "devDependencies": {
23
+ "dotenv": "^17.4.0"
24
+ }
25
+ }
package/test.js ADDED
@@ -0,0 +1,20 @@
1
+ require('dotenv').config({ path: require('path').resolve(__dirname, '.env.test') });
2
+
3
+ const { WedaOpenApi } = require('./index');
4
+
5
+ (async () => {
6
+ try {
7
+ const api = new WedaOpenApi({
8
+ host: process.env.WEDA_HOST,
9
+ envId: process.env.WEDA_ENV_ID,
10
+ secretId: process.env.WEDA_SECRET_ID,
11
+ secretKey: process.env.WEDA_SECRET_KEY,
12
+ });
13
+
14
+ const res = await api.describeUserList(1, 10);
15
+ console.log('describeUserList result:', res);
16
+ } catch (error) {
17
+ console.error('Failed to initialize WedaOpenApi:', error);
18
+ process.exitCode = 1;
19
+ }
20
+ })();
package/wedaopenapi.js ADDED
@@ -0,0 +1,836 @@
1
+ /**
2
+ * 微搭 open api (服务端接口)
3
+ * const WedaOpenApi = require("./wedaopenapi");
4
+ * const wedaapi = new WedaOpenApi({host: "hhh", envId: "iii", secretId: "xxx", secretKey: "xxx"});
5
+ *
6
+ */
7
+ const fetch = require('node-fetch');
8
+ const Auth = require('./auth');
9
+
10
+ class WedaOpenApi {
11
+ constructor(options = {}) {
12
+ this.wedaPath = this.wedaPath.bind(this);
13
+ this.auth = this.auth.bind(this);
14
+
15
+ this.options = Object.assign(
16
+ {
17
+ // 主机地址
18
+ host: 'https://weda.web.scut.edu.cn',
19
+
20
+ // 环境信息
21
+ envId: 'lowcode-7gwjk8v14d46dda1',
22
+ envType: 'prod',
23
+
24
+ // 访问令牌密钥
25
+ secretId: undefined,
26
+ secretKey: undefined,
27
+
28
+ debug: false,
29
+ },
30
+ options,
31
+ );
32
+
33
+ this.oauth = new Auth(this.options);
34
+
35
+ return;
36
+ }
37
+
38
+ /**
39
+ * 请求指定微搭路径
40
+ * @param {String} path 路径
41
+ * @param {String} httpMethod http 方法,用到:GET|PUT|POST|PATCH|DELETE
42
+ * @param {String} queryString 查询字符串
43
+ * @param {Object} bodyObject 请求体
44
+ * @returns
45
+ */
46
+ async wedaPath(path, httpMethod, queryString, body) {
47
+ const timeLabel = 'weda-api-' + Date.now() + Math.random();
48
+
49
+ const token = await this.oauth.getAccessToken();
50
+ let request = {
51
+ method: httpMethod,
52
+ headers: {
53
+ 'Content-Type': 'application/json',
54
+ Authorization: `Bearer ${token}`,
55
+ 'X-Request-Id': timeLabel,
56
+ },
57
+ };
58
+
59
+ let url = this.options.host + path;
60
+
61
+ if (queryString && queryString.trim().length > 0) {
62
+ url = url.concat('?').concat(queryString.trim());
63
+ }
64
+
65
+ if (body) {
66
+ request.body = JSON.stringify(body);
67
+ }
68
+
69
+ if (this.options.debug) {
70
+ console.time(timeLabel);
71
+ console.log(new Date().toLocaleString(), 'Request URL', url);
72
+ console.log(request);
73
+ }
74
+
75
+ const response = await fetch(url, request);
76
+
77
+ if (this.options.debug) {
78
+ console.log('Response Status:', response.status);
79
+ console.timeEnd(timeLabel);
80
+ }
81
+
82
+ if (response.status >= 400) {
83
+ console.error(`Response url: ${response.url}`);
84
+ console.error(`Response status: ${response.status}`);
85
+ console.error(`Response statusText: ${response.statusText}`);
86
+ }
87
+
88
+ return response;
89
+ }
90
+
91
+ //-------------------------------------------------------------------------------//
92
+ // 以下是 auth 接口
93
+ //-------------------------------------------------------------------------------//
94
+
95
+ /**
96
+ * auth 接口
97
+ * @see https://docs.cloudbase.net/lowcode/manage/auth
98
+ * @param {String} methodName 接口方法名
99
+ * @param {String} version 版本
100
+ * @param {String} httpMethod HTTP 请求方法名
101
+ * @param {String} queryString queryString
102
+ * @param {Object} body 请求 body
103
+ * @returns
104
+ */
105
+ async auth(methodName, version, httpMethod, queryString, body) {
106
+ return await (
107
+ await this.wedaPath(
108
+ `/weda/auth/${version}/${this.options.envType}/${methodName}`,
109
+ httpMethod,
110
+ queryString,
111
+ body,
112
+ )
113
+ ).json();
114
+ }
115
+
116
+ /**
117
+ * 增加用户
118
+ * @param {Object} user 用户对象,example: {Name: "john", NickName: "John Smith", Uuid: "john"}
119
+ * @returns {Object}
120
+ */
121
+ async addUser(user) {
122
+ return await this.auth('addUser', 'v1.1', 'POST', undefined, user);
123
+ }
124
+
125
+ /**
126
+ * 删除用户
127
+ * @param {Array} userIdArray 用户id数组,example: ["17023423432497238478", "17034832794329988"]
128
+ * @returns {Object}
129
+ */
130
+ async deleteUser(userIdArray) {
131
+ return await this.auth('deleteUser', 'v1.1', 'POST', undefined, {
132
+ UserIdList: userIdArray,
133
+ });
134
+ }
135
+
136
+ /**
137
+ * 更新用户信息
138
+ * @param {Object} user 用户对象,example: {UserId: "id", Name: "john", NickName: "John Smith", Uuid: "john"}
139
+ * 注意:MainOrg 和 Orgs 参数,不传或传 null 时,都会清空。其他参数不传表示不修改。
140
+ * @returns {Object} {Response: {Error: {Code, Message}, Data: boolean}}
141
+ */
142
+ async updateUserInfoByUserId(user) {
143
+ return await this.auth(
144
+ 'updateUserInfoByUserId',
145
+ 'v1.1',
146
+ 'POST',
147
+ undefined,
148
+ user,
149
+ );
150
+ }
151
+
152
+ /**
153
+ * 查询用户详细信息
154
+ * @param {String} id 用户id
155
+ * @param {Boolean} fetchRoles 是否获取角色
156
+ * @param {Boolean} fetchIdentities 是否获取 IdP Token 信息
157
+ * @returns {Object} {Response: {Data: {Name, NickName, UserId, RelatedRoles: {Id,...}, MainOrg: {OrgId,...}, ...}}}
158
+ */
159
+ async describeWedaUser(id, fetchRoles = false, fetchIdentities = false) {
160
+ const body = { UserId: id };
161
+ if (fetchRoles) {
162
+ body.RelatedType = 'UserRelatedRole';
163
+ }
164
+ if (fetchIdentities) {
165
+ body.WithIdentities = true;
166
+ }
167
+ return await this.auth('describeWedaUser', 'v1.1', 'POST', undefined, body);
168
+ }
169
+
170
+ /**
171
+ * 分页查询所有用户
172
+ * @param {Number} pageNumber 页码,从第1页开始,默认是1
173
+ * @param {Number} pageSize 每页记录数,默认100
174
+ * @returns {Object}
175
+ */
176
+ async describeUserList(pageNumber = 1, pageSize = 100) {
177
+ const body = {
178
+ PageNo: pageNumber < 1 ? 1 : pageNumber,
179
+ PageSize: pageSize < 0 ? 100 : pageSize,
180
+ };
181
+ return await this.auth('describeUserList', 'v1.1', 'POST', undefined, body);
182
+ }
183
+
184
+ /**
185
+ * 批量增加新用户
186
+ * 注意:此接口目前混合云不支持
187
+ * @param {Array} users 用户对象数组,数量不能超过100. eg: {users: [{"name": "", nickName: "", uuid: "", roleIds: ["1", "2"], mainOrg: {"orgId": "id"}}]}
188
+ * @returns {Object}
189
+ */
190
+ async batchAddUsers(users) {
191
+ return await this.auth('batchAddUsers', 'v1', 'POST', undefined, {
192
+ users: users,
193
+ });
194
+ }
195
+
196
+ /**
197
+ * 新建组织机构
198
+ * @param {Object} org 组织机构对象,{*departmentCode,*departmentName, departmentParentCode, info}
199
+ * @returns {Object}
200
+ */
201
+ async addOrg(org) {
202
+ const body = {
203
+ OrgData: JSON.stringify(org),
204
+ };
205
+ return await this.auth('addOrg', 'v1', 'POST', undefined, body);
206
+ }
207
+
208
+ /**
209
+ * 更新组织机构
210
+ * @param {Object} org 组织机构对象,{*_id, departmentName, departmentParentCode, info}
211
+ * @returns {Object} { Response: { RequestId: '03e0a9f91364c', Data: 1 } }
212
+ */
213
+ async updateOrg(org) {
214
+ const body = {
215
+ OrgData: JSON.stringify(org),
216
+ };
217
+ return await this.auth('updateOrg', 'v1', 'POST', undefined, body);
218
+ }
219
+
220
+ /**
221
+ * 删除组织机构
222
+ * @param {String} id
223
+ * @returns {Object} { Response: { RequestId: 'caeddf6c3136c', Data: 1 } }
224
+ */
225
+ async deleteOrg(id) {
226
+ const body = {
227
+ OrgId: id,
228
+ };
229
+ return await this.auth('deleteOrg', 'v1', 'POST', undefined, body);
230
+ }
231
+
232
+ /**
233
+ * 分页查询组织机构
234
+ * @param {Number} pageNumber 页码,从第1页开始,默认是1
235
+ * @param {Number} pageSize 每页记录数,默认1000
236
+ * @returns {Object} {Response: {RequestId, Data: {Total, Schema, DataList[{DataId: _id, DataRecord: jsonString}]}}}
237
+ * jsonString: '{"departmentName":"","departmentParentCode":"!!ROOT","departmentCode":"","source":1.0,"depth":1,"_id":"","##DATA":{"_userInfoList":[]}}'
238
+ */
239
+ async describeOrgs(pageNumber = 1, pageSize = 1000) {
240
+ const body = {
241
+ PageNo: pageNumber < 1 ? 1 : pageNumber,
242
+ PageSize: pageSize < 0 ? 1000 : pageSize,
243
+ };
244
+ return await this.auth('describeOrgs', 'v1', 'POST', undefined, body);
245
+ }
246
+
247
+ /**
248
+ * 设置组织机构负责人
249
+ * @param {String} orgId 组织机构id
250
+ * @param {Array} userIdList 用户id数组
251
+ * @returns {Object}
252
+ */
253
+ async setOrgPrincipal(orgId, userIdList) {
254
+ const body = {
255
+ orgId: orgId,
256
+ userIdList: userIdList,
257
+ };
258
+ return await this.auth('setOrgPrincipal', 'v1', 'POST', undefined, body);
259
+ }
260
+
261
+ /**
262
+ * 新建角色
263
+ * @param {Object} role 角色对象 {*roleName, *roleIdentity, description}
264
+ * @returns {Object} {response: {requestId, error: {code, message}, data: roleId}}
265
+ */
266
+ async createRole(role) {
267
+ return await this.auth('createRole', 'v1', 'POST', undefined, role);
268
+ }
269
+
270
+ /**
271
+ * 修改角色
272
+ * @param {Object} role 角色对象 {*roleId, roleName, description}
273
+ * @returns {Object} {response: {requestId, error: {code, message}, data: boolean}}
274
+ */
275
+ async modifyRole(role) {
276
+ return await this.auth('modifyRole', 'v1', 'PUT', undefined, role);
277
+ }
278
+
279
+ /**
280
+ * 删除角色
281
+ * @param {String} roleId 角色id
282
+ * @returns {Object} {response: {requestId, error: {code, message}, data: boolean}}
283
+ */
284
+ async deleteRole(roleId) {
285
+ return await this.auth(
286
+ 'deleteRole',
287
+ 'v1',
288
+ 'DELETE',
289
+ `roleId=${roleId}`,
290
+ undefined,
291
+ );
292
+ }
293
+
294
+ /**
295
+ * 查询角色详情
296
+ * @param {String} roleName 角色名
297
+ * @returns {Object} {response: {requestId, error: {code, message}, data: {roleName, roleIdentity, roleId, description, isReleased}}}
298
+ */
299
+ async describeRole(roleName) {
300
+ return await this.auth(
301
+ 'describeRole',
302
+ 'v1',
303
+ 'GET',
304
+ `roleName=${roleName}`,
305
+ undefined,
306
+ );
307
+ }
308
+
309
+ /**
310
+ * 分页查询角色
311
+ * @param {Number} pageNumber 页码,从第1页开始,默认是1
312
+ * @param {Number} pageSize 每页记录数,默认1000
313
+ * @returns {Object} {Response: {RequestId, Data: {Total, RoleList: []}}}
314
+ */
315
+ async describeRoleList(pageNumber = 1, pageSize = 1000) {
316
+ const body = {
317
+ PageNo: pageNumber < 1 ? 1 : pageNumber,
318
+ PageSize: pageSize < 0 ? 1000 : pageSize,
319
+ };
320
+ return await this.auth('describeRoleList', 'v1.1', 'POST', undefined, body);
321
+ }
322
+
323
+ /**
324
+ * 查询拥有角色的用户
325
+ * @param {String} roleId 角色 id
326
+ * @param {Number} pageNumber 页码,从第1页开始,默认是1
327
+ * @param {Number} pageSize 每页记录数,默认1000
328
+ * @returns {Object} {Response: {RequestId, Data: {Total, UserList: []}}}
329
+ */
330
+ async describeUsersByRoleId(roleId, pageNumber = 1, pageSize = 1000) {
331
+ const body = {
332
+ RoleId: roleId,
333
+ PageNo: pageNumber < 1 ? 1 : pageNumber,
334
+ PageSize: pageSize < 0 ? 1000 : pageSize,
335
+ };
336
+ return await this.auth(
337
+ 'describeUsersByRoleId',
338
+ 'v1',
339
+ 'POST',
340
+ undefined,
341
+ body,
342
+ );
343
+ }
344
+
345
+ /**
346
+ * 批量设置关联角色,注意此操作会完全替换用户的角色,并非增加关联。
347
+ * @param {Array} authorizations 授权数组 [{UserId, RoleIds: []}, ...]
348
+ * @returns {Object} {Response: {RequestId, Error: {Code, Message}, Data: boolean}}
349
+ */
350
+ async batchAuthorize(authorizations) {
351
+ const body = {
352
+ AuthorizeParam: authorizations,
353
+ };
354
+ return await this.auth('batchAuthorize', 'v1', 'POST', undefined, body);
355
+ }
356
+
357
+ /**
358
+ * 取消指定角色的关联用户
359
+ * @param {String} roleId 角色 id
360
+ * @param {Array} userIds 用户 id 数组
361
+ * @returns {Object} {response: {requestId, error: {code, message}, data: boolean}}
362
+ */
363
+ async unbindRoleUsers(roleId, userIds) {
364
+ const body = {
365
+ roleId: roleId,
366
+ userIds: userIds,
367
+ };
368
+ return await this.auth('unbindRoleUsers', 'v1', 'POST', undefined, body);
369
+ }
370
+
371
+ /**
372
+ * 更新指定角色的关联用户。
373
+ * @param {String} roleId 角色 id
374
+ * @param {Array} userIds 用户 id 数组
375
+ * @returns {Object} {response: {requestId, error: {code, message}, data: boolean}}
376
+ */
377
+ async updateRoleUsers(roleId, userIds) {
378
+ const body = {
379
+ roleId: roleId,
380
+ userIds: userIds,
381
+ };
382
+ return await this.auth('updateRoleUsers', 'v1', 'POST', undefined, body);
383
+ }
384
+
385
+ /**
386
+ * 查询角色的资源权限
387
+ * @param {String} roleId 角色 id
388
+ * @param {String} resourceType 资源类型,app, page, modelApp, modelAppPage, dataSource, flow
389
+ * @param {Array} resourceIds 资源 id 数组
390
+ * @returns {Object} {response: {data: [{roleId, resourceType, resourceId, isAccess, rowPermissionParams: [{rowPermissionScopeType, rowPermissionOperationType}]}]}}
391
+ */
392
+ async checkRoleResource(roleId, resourceType, resourceIds) {
393
+ const body = {
394
+ roleId: roleId,
395
+ resourceType: resourceType,
396
+ resourceIds: resourceIds,
397
+ };
398
+ return await this.auth('checkRoleResource', 'v1', 'POST', undefined, body);
399
+ }
400
+
401
+ /**
402
+ * 查询角色对 APIs 方法的权限
403
+ * @param {String} roleId 角色 id
404
+ * @param {String} dataSourceId 属于源 id
405
+ * @returns {Object} {response: {data: {methodAuthList: [{methodName, isAccess: boolean}]}}}
406
+ */
407
+ async describeMethodAuth(roleId, dataSourceId) {
408
+ return await this.auth(
409
+ 'describeMethodAuth',
410
+ 'v1',
411
+ 'GET',
412
+ `roleId=${roleId}&dataSourceId=${dataSourceId}`,
413
+ undefined,
414
+ );
415
+ }
416
+
417
+ /**
418
+ * 查询角色绑定的数据模型列权限
419
+ * @param {String} roleId 角色 id
420
+ * @param {String} dataSourceId 数据源 id
421
+ * @param {Array} columnList 字段数组
422
+ * @returns {Object} {response: {data: {columnAuthList: [{column, auth: boolean}]}}}
423
+ */
424
+ async describeColumnAuth(roleId, dataSourceId, columnList) {
425
+ return await this.auth(
426
+ 'describeColumnAuth',
427
+ 'v1',
428
+ 'GET',
429
+ `roleId=${roleId}&dataSourceId=${dataSourceId}&columnList=${columnList.join(',')}`,
430
+ undefined,
431
+ );
432
+ }
433
+
434
+ /**
435
+ * 查询用户对资源的权限(资源鉴权)
436
+ * @param {String} userId 用户 id
437
+ * @param {String} resourceType 资源类型,自定义应用:app,模型应用:modelApp,数据源:dataSource,流程:flow
438
+ * @param {Array} resourceList 资源列表,[{ResourceId: appId|datasourceId|flowId, SubResourceId: [pageId...], MethodIdList: [methodId...], ColumnList: []}]
439
+ * @returns {Object} {Response: {Data: [{ResourceId, ResourceType, IsAccess}]}}
440
+ */
441
+ async describeResourcesPermission(userId, resourceType, resourceList) {
442
+ const body = {
443
+ UserInfo: {
444
+ Uid: userId,
445
+ Source: 4,
446
+ },
447
+ ResourceList: resourceList,
448
+ ResourceType: resourceType,
449
+ };
450
+ return await this.auth(
451
+ 'describeResourcesPermission',
452
+ 'v1',
453
+ 'POST',
454
+ undefined,
455
+ body,
456
+ );
457
+ }
458
+
459
+ //-------------------------------------------------------------------------------//
460
+ // 以下是数据源操作接口 //
461
+ //-------------------------------------------------------------------------------//
462
+
463
+ /**
464
+ * 查询数据模型信息
465
+ * @param {Object} params {PageNum = 1, PageSize = 10, IdList, NameList, QueryAll = boolean}
466
+ * @returns {Object} {response: {data: {DataSourceList, Total}}}
467
+ */
468
+ async describeDataSourceList(params) {
469
+ let body = Object.assign(
470
+ {
471
+ PageNum: 1,
472
+ PageSize: 10,
473
+ },
474
+ params,
475
+ );
476
+ return await (
477
+ await this.wedaPath(
478
+ '/weda/model/v1/describeDataSourceList',
479
+ 'POST',
480
+ undefined,
481
+ body,
482
+ )
483
+ ).json();
484
+ }
485
+
486
+ /**
487
+ * 查询选项集信息
488
+ * @param {Array} optionSetNames 选项集标识列表
489
+ * @returns {Object} {response: {data: {total, items: [{name, title, config, data}]}}}
490
+ */
491
+ async describeOptionSet(optionSetNames) {
492
+ return await (
493
+ await this.wedaPath(
494
+ '/weda/model/v1/describeOptionSet',
495
+ 'POST',
496
+ undefined,
497
+ { optionSetNames: optionSetNames },
498
+ )
499
+ ).json();
500
+ }
501
+
502
+ /**
503
+ * 查询数据源 Schema
504
+ * @param {String} dataSourceName 数据源标识,不提供时查询环境下所有 Schema
505
+ * @returns {Response: {Data: {QueryDataSourceSchemaResultList}}}
506
+ */
507
+ async getSchemaList(dataSourceName = '') {
508
+ console.log(dataSourceName);
509
+ return await (
510
+ await this.wedaPath(
511
+ `/weda/model/v1/getSchemaList/${dataSourceName}`,
512
+ 'GET',
513
+ undefined,
514
+ undefined,
515
+ )
516
+ ).json();
517
+ }
518
+
519
+ /**
520
+ * 新建一个数据
521
+ * @param {String} dataSourceName 数据模型标识
522
+ * @param {Object} data 数据对象
523
+ * @returns {Object} 成功返回数据对象json,失败返回{error: {code, message}}
524
+ */
525
+ async odataCreate(dataSourceName, data) {
526
+ return await (
527
+ await this.wedaPath(
528
+ `/weda/odata/v1/${this.options.envType}/${dataSourceName}`,
529
+ 'POST',
530
+ undefined,
531
+ data,
532
+ )
533
+ ).json();
534
+ }
535
+
536
+ /**
537
+ * 更新一个数据
538
+ * @param {String} dataSourceName 数据模型标识
539
+ * @param {String} id 数据 id
540
+ * @param {Object} data 需要修改的数据属性及其取值
541
+ * @returns {Number} status
542
+ */
543
+ async odataUpdate(dataSourceName, id, data) {
544
+ return (
545
+ await this.wedaPath(
546
+ `/weda/odata/v1/${this.options.envType}/${dataSourceName}('${id}')`,
547
+ 'PATCH',
548
+ undefined,
549
+ data,
550
+ )
551
+ ).ok;
552
+ }
553
+
554
+ /**
555
+ * 删除一个数据
556
+ * @param {String} dataSourceName 数据模型标识
557
+ * @param {String} id 数据 id
558
+ * @returns {Number} response status
559
+ */
560
+ async odataDelete(dataSourceName, id) {
561
+ return (
562
+ await this.wedaPath(
563
+ `/weda/odata/v1/${this.options.envType}/${dataSourceName}('${id}')`,
564
+ 'DELETE',
565
+ undefined,
566
+ undefined,
567
+ )
568
+ ).ok;
569
+ }
570
+
571
+ /**
572
+ * 根据 id 获取一个数据
573
+ * @param {String} dataSourceName 数据模型标识
574
+ * @param {String} id 数据 id
575
+ * @returns {Object} 数据对象。如果没有对应数据,则对象里面所有属性为 null,可以检查 _id 是否为 null。
576
+ */
577
+ async odataById(dataSourceName, id) {
578
+ if (id) {
579
+ return await (
580
+ await this.wedaPath(
581
+ `/weda/odata/v1/${this.options.envType}/${dataSourceName}('${id}')`,
582
+ 'GET',
583
+ undefined,
584
+ undefined,
585
+ )
586
+ ).json();
587
+ } else {
588
+ return await (
589
+ await this.wedaPath(
590
+ `/weda/odata/v1/${this.options.envType}/${dataSourceName}`,
591
+ 'GET',
592
+ undefined,
593
+ undefined,
594
+ )
595
+ ).json();
596
+ }
597
+ }
598
+
599
+ /**
600
+ * 查询数据
601
+ * @param {String} dataSourceName 数据模型标识
602
+ * @param {Object} params {$filter, $select, $orderby, $skip=0, $top=10, $count, NotQueryRelateDepartment=true, ResultWithRelation=false}
603
+ * @returns {Object} {"@odata.context", "@odata.count", value=[]}
604
+ * @param {String} version v1,v2
605
+ */
606
+ async odataQuery(dataSourceName, params, version = 'v1') {
607
+ const query = Object.assign(
608
+ {
609
+ $skip: 0,
610
+ $top: 10,
611
+ NotQueryRelateDepartment: true,
612
+ ResultWithRelation: false,
613
+ },
614
+ params,
615
+ );
616
+
617
+ let queryArray = [];
618
+ for (let q in query) {
619
+ queryArray.push(`${q}=${query[q]}`);
620
+ }
621
+ const response = await this.wedaPath(
622
+ `/weda/odata/${version}/${this.options.envType}/${dataSourceName}`,
623
+ 'GET',
624
+ queryArray.join('&'),
625
+ );
626
+ const json = await response.json();
627
+
628
+ if (!response.ok) {
629
+ console.error(json);
630
+ }
631
+
632
+ return json;
633
+ }
634
+
635
+ /**
636
+ * 批量创建数据
637
+ * 注意:混合云不支持此接口
638
+ * @param {String} dataSourceName 数据模型标识
639
+ * @param {Array} objects 需要创建的对象数组
640
+ * @returns {Object} {"@odata.context", value=[]}
641
+ */
642
+ async odataBatchCreate(dataSourceName, objects) {
643
+ return await (
644
+ await this.wedaPath(
645
+ `/weda/odata/v1/batch/${this.options.envType}/${dataSourceName}`,
646
+ 'POST',
647
+ undefined,
648
+ { value: objects },
649
+ )
650
+ ).json();
651
+ }
652
+
653
+ /**
654
+ * 批量更新
655
+ * @param {String} dataSourceName 数据模型标识
656
+ * @param {Array} ids id 数组,注意数组长度,组装后 url 总长度不要超过长度限制(2048)
657
+ * @param {Object} obj 更新的属性和值
658
+ * @returns {Object} {updateCount}
659
+ */
660
+ async odataBatchUpdate(dataSourceName, ids, obj) {
661
+ return await (
662
+ await this.wedaPath(
663
+ `/weda/odata/v1/batch/${this.options.envType}/${dataSourceName}('${ids.join(',')}')`,
664
+ 'PATCH',
665
+ undefined,
666
+ obj,
667
+ )
668
+ ).json();
669
+ }
670
+
671
+ /**
672
+ * 根据 id 批量获取数据
673
+ * @param {String} dataSourceName 数据模型标识
674
+ * @param {Array} ids 数据 id 数组,注意数组长度,组装后 url 总长度不要超过长度限制(2048)
675
+ * @returns {Object} {"odata.context", value=[]}
676
+ */
677
+ async odataByIds(dataSourceName, ids) {
678
+ return await (
679
+ await this.wedaPath(
680
+ `/weda/odata/v1/batch/${this.options.envType}/${dataSourceName}('${ids.join(',')}')`,
681
+ 'GET',
682
+ undefined,
683
+ undefined,
684
+ )
685
+ ).json();
686
+ }
687
+
688
+ /**
689
+ * 根据 id 批量获取数据
690
+ * @param {String} dataSourceName 数据模型标识
691
+ * @param {Array} ids 数据 id 数组,注意数组长度,组装后 url 总长度不要超过长度限制(2048)
692
+ * @returns {Object} {deleteCount}
693
+ */
694
+ async odataBatchDelete(dataSourceName, ids) {
695
+ return await (
696
+ await this.wedaPath(
697
+ `/weda/odata/v1/batch/${this.options.envType}/${dataSourceName}('${ids.join(',')}')`,
698
+ 'DELETE',
699
+ undefined,
700
+ undefined,
701
+ )
702
+ ).json();
703
+ }
704
+
705
+ //-------------------------------------------------------------------------------//
706
+ // 以下是自定义接口 //
707
+ //-------------------------------------------------------------------------------//
708
+
709
+ /**
710
+ * 对某个属性,提供多个值按批次循环进行相等查询,返回所有结果。
711
+ * 适合于对某个属性进行查询,但是值很多的情况,避免请求长度过长的问题。
712
+ * 2025-04-11: 服务器端目前限制查询参数 batch 不能超过10
713
+ * @param {Object} parameters {dataSourceName, prop, values, select, order, batch: 10, version: 'v1'|'v2'}
714
+ * 注意 v2 目前不支持 order
715
+ * @returns
716
+ */
717
+ async odataQueryByPropEQValues(parameters) {
718
+ const params = Object.assign({ batch: 10, version: 'v1' }, parameters);
719
+
720
+ const query = {};
721
+ if (params.select) {
722
+ query.$select = params.select;
723
+ } else {
724
+ query.$select = '*';
725
+ }
726
+
727
+ // 2025-04-11: 服务器端目前限制查询参数 batch 不能超过10
728
+ if (params.batch > 10) {
729
+ params.batch = 10;
730
+ }
731
+
732
+ if (params.order) {
733
+ query.$orderby = params.order;
734
+ }
735
+
736
+ const items = [];
737
+
738
+ for (let i = 0; i < params.values.length; i += params.batch) {
739
+ query.$filter = params.values
740
+ .slice(i, i + params.batch)
741
+ .map((v) => `${params.prop} eq '${v}'`)
742
+ .join(' or ');
743
+ const response = await this.odataQueryCollectAll(
744
+ params.dataSourceName,
745
+ query,
746
+ params.version,
747
+ );
748
+
749
+ for (const item of response) {
750
+ items.push(item);
751
+ }
752
+ }
753
+
754
+ return items;
755
+ }
756
+
757
+ /**
758
+ * 收集所有结果,受限于 api 每次最多1000个记录的限制,需要多次查询。注意结果集对内存的需求。
759
+ * @param {String} dataSourceName
760
+ * @param {Object} params {$filter, $select, $orderby, $skip=0, $top=10, $count, NotQueryRelateDepartment=true, ResultWithRelation=false}
761
+ * $orderby 应该提供,避免排序不稳定导致数据重复或遗漏
762
+ * @param {String} version v1,v2
763
+ * @returns
764
+ */
765
+ async odataQueryCollectAll(dataSourceName, params, version = 'v1') {
766
+ const items = [];
767
+ const batch = version === 'v1' ? 1000 : 200;
768
+ const _params = Object.assign({}, params);
769
+ _params.$top = batch;
770
+ _params.$count = true;
771
+
772
+ for (let i = 0, count = -1; i < count || count == -1; i += batch) {
773
+ _params.$skip = i;
774
+ const response = await this.odataQuery(dataSourceName, _params, version);
775
+
776
+ if (count === -1) {
777
+ count = response['@odata.count'] ? response['@odata.count'] : 0;
778
+ _params.$count = false;
779
+ }
780
+
781
+ if (response.value) {
782
+ for (const item of response.value) {
783
+ items.push(item);
784
+ }
785
+ }
786
+ }
787
+ return items;
788
+ }
789
+
790
+ /**
791
+ * 为指定用户分配角色(增加)
792
+ * @param {String} userId 用户id
793
+ * @param {Array} roleIds 角色id数组
794
+ * @returns {Boolean}
795
+ * @throws
796
+ */
797
+ async addAuthorizes(userId, roleIds) {
798
+ const _roleIds = roleIds.slice();
799
+
800
+ // 合并已有角色授权
801
+ const describeResponse = await this.describeWedaUser(userId, true, false);
802
+ if (describeResponse.Response.Error) {
803
+ throw new Error(
804
+ `describeWedaUser('${userId}', true, false): ${JSON.stringify(describeResponse.Response.Error)}`,
805
+ );
806
+ }
807
+ const wedaUser = describeResponse.Response.Data;
808
+ if (wedaUser && wedaUser.RelatedRoles) {
809
+ const existsRoleIds = wedaUser.RelatedRoles.map((r) => r.Id);
810
+
811
+ // 如果没有新的角色需要授权,直接返回
812
+ if (roleIds.every((id) => existsRoleIds.includes(id))) {
813
+ return false;
814
+ }
815
+
816
+ existsRoleIds.forEach((id) => {
817
+ if (!_roleIds.includes(id)) {
818
+ _roleIds.push(id);
819
+ }
820
+ });
821
+ }
822
+
823
+ const response = await this.batchAuthorize([
824
+ { UserId: userId, RoleIds: _roleIds },
825
+ ]);
826
+ if (response.Response.Error) {
827
+ throw new Error(
828
+ `batchAuthorize('${userId}', ${JSON.stringify(_roleIds)}): ${JSON.stringify(response.Response.Error)}`,
829
+ );
830
+ }
831
+
832
+ return true;
833
+ }
834
+ }
835
+
836
+ module.exports = WedaOpenApi;
package/workflow.js ADDED
@@ -0,0 +1,130 @@
1
+ const fetch = require('node-fetch');
2
+ const Auth = require('./auth');
3
+
4
+ /**
5
+ * 审批流接口
6
+ * 详细文档:https://cloud.tencent.com/document/product/1301/94470
7
+ *
8
+ * 流程:ProcessKey 为流程的唯一标识。流程可以有多个版本。
9
+ * 流程具体版本:FlowCode 为流程具体版本的唯一标识,在流程每次保存、发布后,会生成新的 FlowCode(ProcessKey 不变)
10
+ *
11
+ * 流程实例:触发流程后产生的运行态实例;InstanceId 为流程实例的唯一标识;一个流程可以被触发多次,即一个流程在运行态包含多个流程实例。
12
+ * 审批任务:每个审批节点下每个审批人被分配的任务;FlowTaskId(TaskId)为审批任务的唯一标识;在一个流程实例中,流转到某一个审批节点时,会为该审批节点下每个审批人分配审批任务,即一个流程实例包含多个审批任务。
13
+ */
14
+ class Workflow {
15
+ constructor(options) {
16
+
17
+ this.options = Object.assign(
18
+ {
19
+ // 主机地址
20
+ host: 'https://weda.web.scut.edu.cn',
21
+
22
+ // 环境信息
23
+ envId: 'lowcode-7gwjk8v14d46dda1',
24
+ envType: 'prod',
25
+
26
+ debug: false,
27
+ },
28
+ options
29
+ );
30
+
31
+ console.log(this.options);
32
+
33
+ this.auth = new Auth(this.options);
34
+ this.baseUrl = `${this.options.host}/weda/workflow/v1/${this.options.envType}`;
35
+ }
36
+
37
+ async post(path, body) {
38
+ const token = await this.auth.getAccessToken();
39
+
40
+ if (this.options.debug) {
41
+ console.log(`Request URL: ${this.baseUrl}/${path}`);
42
+ console.log("Request Body:", body);
43
+ }
44
+
45
+ const response = await fetch(`${this.baseUrl}/${path}`, {
46
+ method: 'POST',
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ "Authorization": `Bearer ${token}`,
50
+ },
51
+ body: JSON.stringify(body)
52
+ });
53
+
54
+ if (this.options.debug) {
55
+ console.log(response);
56
+ }
57
+
58
+ const data = await response.json();
59
+ return data;
60
+ }
61
+
62
+ /**
63
+ * 查询已部署的流程列表
64
+ * @param {Object} body {Type: integer}
65
+ * Type: 1 逻辑流,2 审批流
66
+ * @returns
67
+ */
68
+ async listDeployedFlows(body) {
69
+ return this.post('ListDeployedFlows', body);
70
+ }
71
+
72
+ /**
73
+ * 根据类型查询流程实例列表
74
+ * @param {Object} body
75
+ * @returns
76
+ */
77
+ async ListProcessInstances(body) {
78
+ return this.post('ListProcessInstances', body);
79
+ }
80
+
81
+ /**
82
+ * 查询流程任务相关信息
83
+ * @param {Object} body
84
+ * @returns
85
+ */
86
+ async getFlowInstanceTasks(body) {
87
+ return this.post('GetFlowInstanceTasks', body);
88
+ }
89
+
90
+ /**
91
+ * 查询流程实例详情
92
+ * @param {Object} body { FlowTaskId: string, PageType: string, UserInfo, NeedUserExtraInfo: boolean }
93
+ * PageType: 'TODO' | 'DONE' | 'CREATE' | 'CC'
94
+ * UserInfo: { UserId: string, Type: integer }
95
+ * @returns
96
+ */
97
+ async describeProcessInstance(body) {
98
+ return this.post('DescribeProcessInstance', body);
99
+ }
100
+
101
+ /**
102
+ * 终止单个流程实例
103
+ * @param {Object} body { InstanceId: string, Operator: UserInfo, DeleteReason: string }
104
+ * UserInfo: { UserId: string, Type: integer }
105
+ * @returns { Response: { RequestId: string, Data: { Result: boolean } } }
106
+ */
107
+ async stopFlow(body) {
108
+ return this.post('StopFlow', body);
109
+ }
110
+
111
+ /**
112
+ * 管理后台终止操作
113
+ * @param {Object} body { InstanceIdList: [string], UserInfo: UserInfo }
114
+ * @returns { Response: { RequestId: string, Data: { Result: boolean } } }
115
+ */
116
+ async manageStopProcessList(body) {
117
+ return this.post('ManageStopProcessList', body);
118
+ }
119
+
120
+ /**
121
+ * 查询流程操作相关信息
122
+ * @param {Object} body { InstanceId: string, ElementIds: [string], OperatorUser: UserInfo, PageNumber: integer, PageSize: integer, NeedUserExtraInfo: boolean }
123
+ * @returns { Response: { RequestId: string, Data: { Result: [] }, TotalCount: integer } }
124
+ */
125
+ async getFlowInstanceOperations(body) {
126
+ return this.post('GetFlowInstanceOperations', body);
127
+ }
128
+ }
129
+
130
+ module.exports = Workflow;