@gingkoo/base-server 0.0.1-alpha.0 → 0.0.1-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +4 -0
  2. package/app.js +41 -40
  3. package/backend/common/entity.js +55 -0
  4. package/backend/common/enum.js +247 -0
  5. package/backend/common/fss/index.js +18 -0
  6. package/backend/common/fss/routers/attachment.js +45 -0
  7. package/backend/common/fss/routers/download.js +43 -0
  8. package/backend/common/fss/routers/import_export.js +113 -0
  9. package/backend/common/fss/routers/resources.js +149 -0
  10. package/backend/common/fss/routers/upload.js +81 -0
  11. package/backend/common/fss/services/import_export.js +159 -0
  12. package/backend/common/fss/services/index.js +92 -0
  13. package/backend/common/fss/utils.js +39 -0
  14. package/backend/common/ginfo/config.js +5 -0
  15. package/backend/common/ginfo/index.js +62 -0
  16. package/backend/common/index_template.html +28 -0
  17. package/backend/common/logger/index.js +96 -0
  18. package/backend/common/mapping.js +128 -0
  19. package/backend/common/middleware/auth.js +109 -0
  20. package/backend/common/middleware/cors.js +13 -0
  21. package/backend/common/middleware/response.js +25 -0
  22. package/backend/common/page_engine.js +487 -0
  23. package/backend/common/schedule.js +45 -0
  24. package/backend/common/services/dataCombine.js +67 -0
  25. package/backend/common/services/dept.js +37 -0
  26. package/backend/common/services/dict.js +488 -0
  27. package/backend/common/services/email.js +49 -0
  28. package/backend/common/services/generalConfig.js +137 -0
  29. package/backend/common/services/login.js +18 -0
  30. package/backend/common/services/notice.js +260 -0
  31. package/backend/common/services/permission.js +500 -0
  32. package/backend/common/services/roles.js +57 -0
  33. package/backend/common/services/send-notice.js +86 -0
  34. package/backend/common/services/task.js +259 -0
  35. package/backend/common/services/user.js +673 -0
  36. package/backend/common/socket.js +18 -0
  37. package/backend/common/sse/index.js +81 -0
  38. package/backend/common/sse/router.js +30 -0
  39. package/backend/common/task.js +75 -0
  40. package/backend/common/wechat/index.js +9 -0
  41. package/backend/common/wechat/routers/auth.js +238 -0
  42. package/{modules/user/frontend → backend/common/wechat/routers}/index.html +14 -7
  43. package/backend/common/wechat/services/auth.js +209 -0
  44. package/backend/common/wechat/services/notice.js +171 -0
  45. package/backend/config/index.js +61 -0
  46. package/backend/config/path.js +3 -0
  47. package/backend/router.js +100 -0
  48. package/backend/routers/app.js +222 -0
  49. package/backend/routers/automate.js +276 -0
  50. package/backend/routers/dict.js +370 -0
  51. package/backend/routers/email.js +85 -0
  52. package/backend/routers/generalConfig.js +276 -0
  53. package/backend/routers/idm.js +245 -0
  54. package/backend/routers/module.js +357 -0
  55. package/backend/routers/notice.js +138 -0
  56. package/backend/routers/pages.js +46 -0
  57. package/backend/routers/permission.js +985 -0
  58. package/backend/routers/setting.js +184 -0
  59. package/backend/routers/team/index.js +22 -0
  60. package/backend/routers/team/routers/mapping.js +29 -0
  61. package/backend/routers/team/routers/member.js +72 -0
  62. package/backend/routers/team/routers/membermanage.js +289 -0
  63. package/backend/routers/team/routers/pages.js +47 -0
  64. package/backend/routers/team/routers/roles.js +92 -0
  65. package/backend/routers/team/routers/teaminfo.js +27 -0
  66. package/backend/routers/team/routers/usergroup.js +213 -0
  67. package/backend/routers/team/services/mapping.js +101 -0
  68. package/backend/routers/team/services/member.js +206 -0
  69. package/backend/routers/team/services/roles.js +71 -0
  70. package/backend/routers/team/services/teaminfo.js +20 -0
  71. package/backend/routers/team/services/usergroup.js +128 -0
  72. package/backend/routers/user.js +436 -0
  73. package/backend/services/automate.js +60 -0
  74. package/backend/services/config.js +14 -0
  75. package/backend/services/module.js +298 -0
  76. package/backend/services/permission.js +192 -0
  77. package/backend/services/services.js +115 -0
  78. package/backend/services/setting.js +190 -0
  79. package/backend/services/token.js +42 -0
  80. package/backend/space.js +52 -0
  81. package/backend/space_mapping.js +22 -0
  82. package/backend/utils/avatar.js +48 -0
  83. package/backend/utils/color_gen_helper.js +20 -0
  84. package/backend/utils/date.js +66 -0
  85. package/backend/utils/excel.js +446 -0
  86. package/backend/utils/fs/doc.md +64 -0
  87. package/backend/utils/fs/index.js +127 -0
  88. package/backend/utils/jwt.js +54 -0
  89. package/backend/utils/modules/sequence.js +93 -0
  90. package/backend/utils/object.js +31 -0
  91. package/backend/utils/page-query-helper/index.js +61 -0
  92. package/backend/utils/path.js +123 -0
  93. package/backend/utils/run.js +25 -0
  94. package/backend/utils/tokenize.js +82 -0
  95. package/backend/utils/typeof.js +5 -0
  96. package/backend/utils/util.js +153 -0
  97. package/backend/views/api/index.js +32 -0
  98. package/backend/views/api/index.xml +49 -0
  99. package/backend/views/dict/index.js +80 -0
  100. package/backend/views/dict/index.xml +52 -0
  101. package/backend/views/index.js +32 -0
  102. package/backend/views/members_manage/index.js +68 -0
  103. package/backend/views/members_manage/index.xml +68 -0
  104. package/backend/views/roles/index.js +17 -0
  105. package/backend/views/roles/index.xml +47 -0
  106. package/backend/views/usergroup/index.js +68 -0
  107. package/backend/views/usergroup/index.xml +65 -0
  108. package/dist/base-assets/css/index-ffdb55a5.css +3 -0
  109. package/dist/base-assets/css/index-ffdb55a5.css.gz +0 -0
  110. package/dist/base-assets/js/index-b3998a47.js +762 -0
  111. package/dist/base-assets/js/index-b3998a47.js.gz +0 -0
  112. package/dist/base-assets/js/react-cropper.es-d5f06996.js +10 -0
  113. package/dist/base-assets/js/react-cropper.es-d5f06996.js.gz +0 -0
  114. package/dist/base-assets/png/u9-2348c304.png +0 -0
  115. package/dist/base-assets/woff2/materialicons-83be7b2f.woff2 +0 -0
  116. package/dist/index.html +162 -0
  117. package/package.json +62 -5
  118. package/common/router.js +0 -42
  119. package/modules/user/backend/index.js +0 -19
  120. package/modules/user/backend/routers/user.js +0 -11
  121. package/modules/user/frontend/index.js +0 -1
  122. package/serve.js +0 -9
@@ -0,0 +1,500 @@
1
+ 'use strict';
2
+ const { sqlExecutor, checkTableFilterFields, queryParamsBuilder } = require('@gingkoo/node-tools');
3
+ const { logger } = require('../logger/index');
4
+ const entity = require('../entity');
5
+ const { permission: permissionConstant } = require('../enum');
6
+ const rolesService = require('../services/roles');
7
+ // const requirementService = require('@requirement/services/requirement');
8
+
9
+ module.exports = {
10
+ // 根据操作对象查询权限
11
+ checkOperPermission: async function (operType, operId, perm, filter) {
12
+ if (Array.isArray(operId) && operId.length === 0) return [];
13
+ let sql = `select * from ${entity.IDM_PERMISSIONS} where ORG_ID=? and STATUS='00'`;
14
+ let params = [global.orgid];
15
+ if (operType === permissionConstant.OPER_TYPE.USER) {
16
+ sql += ` and (OPER_TYPE in (?, ?, ?) and (OPER_ID in (select GROUP_ID from ${entity.IDM_GROUP_MEMBERS} where USER_ID=?) or OPER_ID in (select DEPT_NO from ${entity.IDM_USERS} where USER_ID=?) or OPER_ID=?))`;
17
+ params = [
18
+ global.orgid,
19
+ permissionConstant.OPER_TYPE.DEPT,
20
+ permissionConstant.OPER_TYPE.GROUP,
21
+ permissionConstant.OPER_TYPE.USER,
22
+ operId,
23
+ operId,
24
+ operId,
25
+ ];
26
+ } else if (operType === permissionConstant.OPER_TYPE.GROUP) {
27
+ sql += ` and (OPER_TYPE in (?, ?) and OPER_ID in ((select DEPT_ID from ${entity.IDM_GROUPS} where GROUP_ID=?), ?))`;
28
+ params = [global.orgid, operType, permissionConstant.OPER_TYPE.DEPT, operId, operId];
29
+ } else if (operType === permissionConstant.OPER_TYPE.DEPT) {
30
+ sql += ` and OPER_TYPE=? and OPER_ID=?`;
31
+ params = [global.orgid, operType, operId];
32
+ } else if (operType === permissionConstant.OPER_TYPE.ROLE) {
33
+ if (typeof operId === 'string') {
34
+ sql += ` and OPER_TYPE=? and OPER_ID=?`;
35
+ params = [global.orgid, operType, operId];
36
+ }
37
+ if (Array.isArray(operId) && operId.length) {
38
+ params.push(operType);
39
+ let operSql = operId
40
+ .reduce((mo, id) => {
41
+ mo.push('?');
42
+ params.push(id);
43
+ return mo;
44
+ }, [])
45
+ .join(',');
46
+ sql += ` and OPER_TYPE=? and OPER_ID in (${operSql})`;
47
+ }
48
+ }
49
+ if (perm && typeof perm === 'string' && perm.trim().length) {
50
+ let permSql = perm
51
+ .split(',')
52
+ .map((item) => {
53
+ return `(RES_RANGE like '%,${item},%' or RES_RANGE like '${item}' or RES_RANGE like '%,${item}' or RES_RANGE like '${item},%')`;
54
+ })
55
+ .join(' or ');
56
+ sql += ` and (${permSql})`;
57
+ }
58
+ if (Array.isArray(perm) && perm.length) {
59
+ let permSql = perm
60
+ .map((item) => {
61
+ return `(RES_RANGE like '%,${item},%' or RES_RANGE like '${item}' or RES_RANGE like '%,${item}' or RES_RANGE like '${item},%')`;
62
+ })
63
+ .join(' or ');
64
+ sql += ` and (${permSql})`;
65
+ }
66
+ let newFilter = await checkTableFilterFields(filter, entity.IDM_PERMISSIONS);
67
+ if (newFilter && Object.keys(newFilter).length) {
68
+ let queryParam = queryParamsBuilder(newFilter, null, null);
69
+ sql = sql + ' and ' + queryParam.sql;
70
+ params = params.concat(queryParam.params);
71
+ }
72
+ let { error, results } = await sqlExecutor(sql, params);
73
+ if (error) {
74
+ logger.error(error);
75
+ return null;
76
+ }
77
+ return results;
78
+ },
79
+
80
+ /**
81
+ *
82
+ * @param {*} userId 用户ID
83
+ * @param {*} navId 导航ID
84
+ * @param {*} resType 权限应用类型
85
+ * @returns 获取用户角色在此导航的功能权限
86
+ */
87
+ checkRoleApiPerm: async function (userId, navId, resType, proId) {
88
+ let userRoles = await rolesService.getRoleMembers(null, {
89
+ USER_ID: userId,
90
+ ROLE_TYPE: permissionConstant.ROLE_TYPE.MANAGER,
91
+ });
92
+ if (
93
+ proId &&
94
+ [
95
+ permissionConstant.RES_TYPE.PROJECT_NAV,
96
+ permissionConstant.RES_TYPE.REQUIREMENT_NAV,
97
+ ].includes(resType)
98
+ ) {
99
+ userRoles = await this.getUserProjectRole({ USER_ID: userId, PROJECT_ID: proId });
100
+ }
101
+ if (!Array.isArray(userRoles)) return null;
102
+ userRoles = userRoles.map((role) => role.ROLE_ID || role.MEM_ROLE);
103
+
104
+ let navPerms = await this.checkOperPermission('role', userRoles, null, {
105
+ RES_KIND: permissionConstant.RES_KIND.API,
106
+ RES_TYPE: resType,
107
+ RES_RANGE: navId,
108
+ });
109
+ if (!Array.isArray(navPerms)) return null;
110
+ let permItem = navPerms.reduce((mo, item) => {
111
+ let items = [];
112
+ try {
113
+ items = JSON.parse(item['PROPS']);
114
+ } catch (error) {}
115
+ mo = mo
116
+ .concat(items)
117
+ .map((v) => v.feature)
118
+ .filter((v, i, arr) => arr.indexOf(v) === i)
119
+ .map((v) => {
120
+ return {
121
+ feature: v,
122
+ };
123
+ });
124
+ return mo;
125
+ }, []);
126
+ return permItem;
127
+ },
128
+
129
+ /**
130
+ *
131
+ * @param {*} userId 用户ID
132
+ * @param {*} navId 导航ID
133
+ * @param {*} resType 权限应用类型
134
+ * @returns 获取用户在此导航的功能权限
135
+ */
136
+ checkUserApiPerm: async function (userId, navId, resType) {
137
+ let navPerms = await this.checkOperPermission('user', userId, null, {
138
+ RES_KIND: permissionConstant.RES_KIND.API,
139
+ RES_TYPE: resType,
140
+ RES_RANGE: navId,
141
+ });
142
+ if (!Array.isArray(navPerms)) return null;
143
+ let permItem = navPerms.reduce((mo, item) => {
144
+ let items = [];
145
+ try {
146
+ items = JSON.parse(item['PROPS']);
147
+ } catch (error) {}
148
+ mo = mo
149
+ .concat(items)
150
+ .map((v) => v.feature)
151
+ .filter((v, i, arr) => arr.indexOf(v) === i)
152
+ .map((v) => {
153
+ return {
154
+ feature: v,
155
+ };
156
+ });
157
+ return mo;
158
+ }, []);
159
+
160
+ return permItem;
161
+ },
162
+ formptBtns: function (btns, permFeatures) {
163
+ if (!Array.isArray(btns) || !Array.isArray(permFeatures)) return null;
164
+ let resultBtns = btns.filter((btn) => {
165
+ return !btn.feature || permFeatures.includes(btn.feature);
166
+ });
167
+ return resultBtns;
168
+ },
169
+
170
+ // 对某些特殊接口过滤 如 columnApi , 特殊接口的功能目前只是编辑edit
171
+ formptApis: function (feature, perms) {
172
+ if (!Array.isArray(perms)) return null;
173
+ let permFeatures = perms.map((per) => per['feature']);
174
+ return permFeatures.includes(feature);
175
+ },
176
+
177
+ /**
178
+ *
179
+ * @param {*} userId 用户ID
180
+ * @param {*} navId 导航ID
181
+ * @param {*} schema JSON数据
182
+ * @returns 查询用户角色在此导航的功能权限,删除没有权限的button
183
+ * schema中的 filterOptions.leftAction.action 和 filterOptions.leftAction.actionMore 和 oprBtns, 目前只对这三个
184
+ * json中对于 "columnApi": "********" 类的api,也做权限校验
185
+ */
186
+ formatPermHtml: async function (userId, navId, schema, resType, proId) {
187
+ // userId为undefined会报错,所以此处直接return
188
+ if (!navId || !userId) return schema;
189
+ let newSchema = [];
190
+ try {
191
+ newSchema = JSON.parse(JSON.stringify(schema));
192
+ } catch (error) {}
193
+ let newBody = newSchema?.body[0] || null;
194
+ if (!newBody) return schema;
195
+ let navPerm = await this.checkRoleApiPerm(userId, navId, resType, proId);
196
+ let specialApis = ['columnApi', 'saveOrderApi'];
197
+ if (!navPerm || !Array.isArray(navPerm)) return schema;
198
+
199
+ let permFeatures = navPerm.map((per) => per['feature']);
200
+
201
+ if (Array.isArray(newBody.oprBtns) && newBody.oprBtns.length) {
202
+ let newBtns = this.formptBtns(newBody.oprBtns, permFeatures);
203
+ newBody.oprBtns = newBtns;
204
+ }
205
+ if (
206
+ Array.isArray(newBody?.filterOptions?.leftAction?.action) &&
207
+ newBody.filterOptions.leftAction.action.length
208
+ ) {
209
+ let newBtns = this.formptBtns(newBody.filterOptions.leftAction.action, permFeatures);
210
+ newBody.filterOptions.leftAction.action = newBtns;
211
+ }
212
+ if (
213
+ Array.isArray(newBody?.filterOptions?.leftAction?.actionMore) &&
214
+ newBody.filterOptions.leftAction.actionMore.length
215
+ ) {
216
+ let newBtns = this.formptBtns(newBody.filterOptions.leftAction.actionMore, permFeatures);
217
+ newBody.filterOptions.leftAction.actionMore = newBtns;
218
+ }
219
+ if (newBody?.rowInfo && newBody?.rowInfo?.feature === 'edit' && Array.isArray(permFeatures)) {
220
+ let quickStatic = permFeatures.includes(newBody?.rowInfo?.feature) ? false : true;
221
+ newBody.rowInfo = { ...newBody.rowInfo, quickStatic };
222
+ Reflect.deleteProperty(newBody.rowInfo, 'feature');
223
+ }
224
+ if (
225
+ Array.isArray(newBody?.rowInfo?.moreBtn) &&
226
+ newBody?.rowInfo?.moreBtn?.length &&
227
+ Array.isArray(permFeatures)
228
+ ) {
229
+ let newMoreBtn = newBody?.rowInfo?.moreBtn
230
+ .filter(
231
+ (btn) => !btn.feature || permFeatures.includes(btn.feature) || btn.type === 'button',
232
+ )
233
+ .map((btn) => {
234
+ if (!btn.feature) return btn;
235
+ if (permFeatures.includes(btn.feature)) return btn;
236
+ if (!permFeatures.includes(btn.feature))
237
+ return {
238
+ ...btn,
239
+ disabled: true,
240
+ };
241
+ });
242
+ newBody.rowInfo = { ...newBody.rowInfo, moreBtn: newMoreBtn };
243
+ }
244
+ specialApis.forEach((apiKey) => {
245
+ if (newBody[apiKey]) {
246
+ let flag = this.formptApis(permissionConstant.PERMISSION_FEATURE_KIND.EDIT, navPerm);
247
+ !flag && Reflect.deleteProperty(newBody, apiKey);
248
+ apiKey === 'saveOrderApi' &&
249
+ !flag &&
250
+ Reflect.deleteProperty(newBody['filterOptions'], 'draggable');
251
+ }
252
+ });
253
+ newSchema['body'][0] = newBody;
254
+ return newSchema;
255
+ },
256
+
257
+ //
258
+ formatLayout: async function (userid, schema, proId, layout) {
259
+ if (!userid) return schema;
260
+ let newSchema = [];
261
+ let newData = [];
262
+ try {
263
+ newSchema = JSON.parse(JSON.stringify(schema));
264
+ newData = JSON.parse(JSON.stringify(layout));
265
+ } catch (error) {}
266
+ // layout中的ASSIGNEE和FORWARD_TO_PROJECT两个字段
267
+ // 前提如果是研发人员,整个layout不可编辑,只有这两个字段可以编辑,需要给这两个字段设置quickStatic: false
268
+ let newLayout = newSchema?.body?.[0]?.rowInfo?.layoutSet || newData || null;
269
+ // let prdRoles = await requirementService.getRequirementRoles();
270
+
271
+ // if (!Array.isArray(prdRoles) || !newLayout) return newSchema;
272
+
273
+ // let prdRoleInfo = prdRoles.reduce((mo, role) => {
274
+ // mo[role.ROLE_NAME] = role.ROLE_ID;
275
+ // return mo;
276
+ // }, {});
277
+
278
+ // let userRole = await requirementService.getMemberUser(null, null, {
279
+ // PROJECT_ID: proId,
280
+ // USER_ID: userid,
281
+ // });
282
+
283
+ // 需求模块 需求页面 研发人员没有编辑权限,但可以编辑产品所属项目和产品负责人这两个字段
284
+ // 需求模块 需求页面 研发人员没有编辑权限,但可以新建关联工作项和关联工作项
285
+ // if (userRole?.[0]?.MEM_ROLE === prdRoleInfo['研发人员'] && Array.isArray(newLayout)) {
286
+ // newLayout = newLayout.map((item) => {
287
+ // if (item.type === 'tab' && Array.isArray(item.children)) {
288
+ // let newChildren = item.children.map((child) => {
289
+ // if (child.title === '详情' && Array.isArray(child.children)) {
290
+ // let aChildren = child.children.map((aChild) => {
291
+ // if (aChild.label === '基础信息' && Array.isArray(aChild.children)) {
292
+ // let bChildren = aChild.children.map((ren) => {
293
+ // if (
294
+ // ['ASSIGNEE', 'FORWARD_TO_PROJECT'].includes(ren) ||
295
+ // ['ASSIGNEE', 'FORWARD_TO_PROJECT'].includes(ren.name)
296
+ // ) {
297
+ // if (isPlainObject(ren)) return { ...ren, quickStatic: false };
298
+ // return {
299
+ // name: ren.name || ren,
300
+ // quickStatic: false,
301
+ // };
302
+ // }
303
+ // return ren;
304
+ // });
305
+ // return {
306
+ // ...aChild,
307
+ // children: bChildren,
308
+ // };
309
+ // }
310
+ // return aChild;
311
+ // });
312
+ // return {
313
+ // ...child,
314
+ // children: aChildren,
315
+ // };
316
+ // }
317
+ // if (child.title === '关联内容' && Array.isArray(child.children)) {
318
+ // let aChildren = child.children.map((aChild) => {
319
+ // if (aChild.name === 'linkIssues' && aChild.feature === 'edit') {
320
+ // return {
321
+ // ...aChild,
322
+ // btns: ['add', 'associated'],
323
+ // quickStatic: false,
324
+ // };
325
+ // }
326
+ // return aChild;
327
+ // });
328
+ // return {
329
+ // ...child,
330
+ // children: aChildren,
331
+ // };
332
+ // }
333
+ // return child;
334
+ // });
335
+ // return {
336
+ // ...item,
337
+ // children: newChildren,
338
+ // };
339
+ // }
340
+ // return item;
341
+ // });
342
+ // }
343
+
344
+ if (!schema) return newLayout;
345
+
346
+ newSchema.body[0].rowInfo.layoutSet = newLayout;
347
+ return newSchema;
348
+ },
349
+
350
+ formatBugHtml: async function (userId, schema, proId) {
351
+ // userId为undefined会报错,所以此处直接return
352
+ if (!userId) return schema;
353
+
354
+ let btnKey = ['refuse', 'pass', 'confirm'];
355
+ let newSchema = [];
356
+ try {
357
+ newSchema = JSON.parse(JSON.stringify(schema));
358
+ } catch (error) {}
359
+
360
+ let newBody = newSchema?.body[0] || null;
361
+ if (!newBody) return schema;
362
+
363
+ // let prdRoles = await requirementService.getRequirementRoles();
364
+ // if (!Array.isArray(prdRoles)) return newSchema;
365
+
366
+ // let prdRoleInfo = prdRoles.reduce((mo, role) => {
367
+ // mo[role.ROLE_NAME] = role.ROLE_ID;
368
+ // return mo;
369
+ // }, {});
370
+ let userRole = await requirementService.getMemberUser(null, null, {
371
+ PROJECT_ID: proId,
372
+ USER_ID: userId,
373
+ });
374
+
375
+ // 需求模块 缺陷页面 研发实施都有编辑权限,但实施不能点击通过缺陷和拒绝缺陷这两个按钮
376
+ // if (userRole?.[0]?.MEM_ROLE === prdRoleInfo['实施人员']) {
377
+ // if (Array.isArray(newBody?.rowInfo?.moreBtn)) {
378
+ // let newMoreBtn = newBody.rowInfo.moreBtn.map((btn) => {
379
+ // if (btnKey.includes(btn.key)) {
380
+ // return {
381
+ // ...btn,
382
+ // disabled: true,
383
+ // };
384
+ // }
385
+ // return btn;
386
+ // });
387
+ // newBody.rowInfo.moreBtn = newMoreBtn;
388
+ // }
389
+ // if (Array.isArray(newBody?.oprBtns)) {
390
+ // let newOprBtns = newBody.oprBtns.map((action) => {
391
+ // if (btnKey.includes(action.key)) {
392
+ // return {
393
+ // ...action,
394
+ // disabled: true,
395
+ // };
396
+ // }
397
+ // return action;
398
+ // });
399
+ // newBody.oprBtns = newOprBtns;
400
+ // }
401
+ // }
402
+
403
+ return newSchema;
404
+ },
405
+
406
+ // 获取项目navs
407
+ getModuleNavs: async function (module) {
408
+ let sql = `select NAV_ID, NAV_NAME, URL from ${entity.BASE_MODULE_NAV} where ORG_ID=? and MODULE_ID=? order by ORDER_NO`;
409
+ let params = [global.orgid, module];
410
+ let { error, results } = await sqlExecutor(sql, params);
411
+ if (error) {
412
+ logger.error(error);
413
+ return null;
414
+ }
415
+
416
+ return results;
417
+ },
418
+
419
+ // 获取用户在指定项目中的角色
420
+ getUserProjectRole: async function (filter) {
421
+ let sql = `select MEM_ROLE from ${entity.PROJECT_MEMBERS} where ORG_ID=?`;
422
+ let params = [global.orgid];
423
+ let newFilter = await checkTableFilterFields(filter, entity.PROJECT_MEMBERS);
424
+ if (newFilter && Object.keys(newFilter)) {
425
+ let queryParam = queryParamsBuilder(newFilter, null, null);
426
+ sql = sql + ' and ' + queryParam.sql;
427
+ params = params.concat(queryParam.params);
428
+ }
429
+
430
+ let { error, results } = await sqlExecutor(sql, params);
431
+ if (error) {
432
+ logger.error(error);
433
+ return null;
434
+ }
435
+
436
+ return results;
437
+ },
438
+
439
+ /**
440
+ * 根据系统页面活模块查询有权限的人员名单
441
+ * @param {*} navId 页面ID
442
+ * @param {*} feature 权限 add|del|edit 如果feature为undefined 则认为搜索查看页面的权限
443
+ */
444
+ getUserListByPagePermiss: async function (navId, feature) {
445
+ if (!navId) return false;
446
+ let sql = `select OPER_ID from ${entity.IDM_PERMISSIONS} where ORG_ID=? and OPER_TYPE='role' and RES_RANGE like '%${navId}%'`;
447
+ let params = [global.orgid];
448
+
449
+ if (['del', 'edit', 'add'].includes(feature)) {
450
+ sql += ` and RES_KIND='api' and PROPS like '%"${feature}"%'`;
451
+ } else {
452
+ sql += ` and RES_KIND='page' and ACTIONS='select'`;
453
+ }
454
+
455
+ let { error, results } = await sqlExecutor(sql, params);
456
+ if (error || !Array.isArray(results)) {
457
+ logger.error(error);
458
+ return null;
459
+ }
460
+ let roleList = results.map((item) => item.OPER_ID);
461
+
462
+ if (!roleList.length) {
463
+ return [];
464
+ }
465
+
466
+ let users = await rolesService.getRoleMembers(null, {
467
+ ROLE_ID: roleList,
468
+ });
469
+
470
+ if (!users || !Array.isArray(users)) return null;
471
+
472
+ users = users.map((item) => item.USER_ID).filter((id, index, arr) => arr.indexOf(id) === index);
473
+ return users;
474
+ },
475
+
476
+ // 获取系统页面数据
477
+ getSystemNavs: async function (filter, customSql) {
478
+ let sql = `select * from ${entity.BASE_SETUP_NAV} where ORG_ID=?`;
479
+ let params = [global.orgid];
480
+
481
+ let where = await checkTableFilterFields(filter, entity.BASE_SETUP_NAV);
482
+ if (where && Object.keys(where)) {
483
+ let queryParam = queryParamsBuilder(where, null, null, { NAV_NAME: 'like' });
484
+ sql = sql + ' and ' + queryParam.sql;
485
+ params = params.concat(queryParam.params);
486
+ }
487
+
488
+ if (customSql) {
489
+ sql += customSql;
490
+ }
491
+
492
+ let { error, results } = await sqlExecutor(sql, params);
493
+ if (error) {
494
+ logger.error(error);
495
+ return null;
496
+ }
497
+
498
+ return results;
499
+ },
500
+ };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+ const { sqlExecutor, checkTableFilterFields, queryParamsBuilder } = require('@gingkoo/node-tools');
3
+ const { logger } = require('../logger/index');
4
+ const entity = require('../entity');
5
+
6
+ const dictService = {
7
+ /**
8
+ * 根据类型获取角色 列表
9
+ * @param {MANAGER|PROJECT} type 指定 role_type 查询
10
+ */
11
+ async getRoles(type) {
12
+ let { error, results } = await sqlExecutor(
13
+ `select ROLE_ID, ROLE_NAME FROM ${entity.IDM_ROLES} where ORG_ID=? and ROLE_TYPE=?`,
14
+ [global.orgid, type],
15
+ );
16
+
17
+ if (error) {
18
+ logger.error(error);
19
+ return null;
20
+ }
21
+
22
+ return results;
23
+ },
24
+
25
+ async getRoleMembers(role, filter) {
26
+ let sql = `select a.ROLE_ID, a.ROLE_TYPE, a.USER_ID, a.REMARKS, b.USER_NAME, b.DEPT_NO from ${entity.IDM_ROLE_MEMBERS} a left join ${entity.IDM_USERS} b on a.USER_ID=b.USER_ID where a.ORG_ID=?`;
27
+ let params = [global.orgid];
28
+ if (typeof role === 'string' && role.trim().length) {
29
+ sql += ' and ROLE_ID=?';
30
+ params = params.concat(role);
31
+ }
32
+ let aFilter = await checkTableFilterFields(filter, entity.IDM_ROLE_MEMBERS);
33
+ if (aFilter && Object.keys(aFilter)) {
34
+ let sqlResult = queryParamsBuilder(aFilter, 'a');
35
+ sql += ' and ' + sqlResult.sql;
36
+ params = params.concat(sqlResult.params);
37
+ }
38
+ let bFilter = await checkTableFilterFields(filter, entity.IDM_USERS);
39
+ if (bFilter && Object.keys(bFilter)) {
40
+ let sqlResult = queryParamsBuilder(bFilter, 'b', null, {
41
+ USER_ID: 'like',
42
+ USER_NAME: 'like',
43
+ });
44
+ sql += ' and ' + sqlResult.sql;
45
+ params = params.concat(sqlResult.params);
46
+ }
47
+ let { error, results } = await sqlExecutor(sql, params);
48
+ if (error) {
49
+ logger.error(error);
50
+ return null;
51
+ }
52
+
53
+ return results;
54
+ },
55
+ };
56
+
57
+ module.exports = dictService;
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+ const { v4: uuidv4 } = require('uuid');
3
+ const { insertSqlBuilder, sqlExecutor } = require('@gingkoo/node-tools');
4
+ const entity = require('./entity');
5
+ const logger = require('./logger');
6
+ const wxNotice = require('@wechat/services/notice');
7
+
8
+ let NOTIF_TOPICS = {
9
+ ISSUE_ACTIVITI_LOG: {
10
+ title: '工作项变更通知',
11
+ },
12
+ ISSUE_DUE_ALERT: {
13
+ title: '工作项到期通知',
14
+ },
15
+ CHAT_MESSAGE_LOG: {
16
+ title: '聊天消息提醒通知',
17
+ },
18
+ };
19
+
20
+ module.exports = {
21
+ /**
22
+ * 发送通知
23
+ * @param {string[]} props.toUsers 通知的用户ids
24
+ * @param {string} props.content 通知的文案
25
+ * @param {string} props.link 通知跳转链接
26
+ * @param {ISSUE_ACTIVITI_LOG | ISSUE_DUE_ALERT} props.topic 消息主题
27
+ * @param {boolean} props.direct 直达消息
28
+ * @param {ISSUE} props.module 来源模块
29
+ *
30
+ */
31
+ async notify(props) {
32
+ let params = {
33
+ title: NOTIF_TOPICS[props['topic']]['title'],
34
+ content: props['content'],
35
+ link: props['link'],
36
+ };
37
+
38
+ let direct = 'N';
39
+ if (props['direct']) {
40
+ direct = 'Y';
41
+ }
42
+
43
+ let channel = 'qywx';
44
+ let toUsers = props['toUsers'] || [];
45
+ let sqlParams = {
46
+ ORG_ID: global.orgid,
47
+ MSG_ID: uuidv4().replaceAll('-', ''),
48
+ MSG_TYPE: props['msgtype'] || 'text',
49
+ TOPIC: props['topic'],
50
+ DIRECT: direct,
51
+ CHANNEL: channel,
52
+ TO_USERS: toUsers.join(','),
53
+ CONTENT: params['content'],
54
+ LINK: params['link'] || '',
55
+ STATUS: '00',
56
+ MODULE_ID: props['module'],
57
+ };
58
+
59
+ let sqlResult = insertSqlBuilder(entity.BASE_NOTIFI_MESSAGE, sqlParams);
60
+ let dbResult = await sqlExecutor(sqlResult.sql, sqlResult.params);
61
+ if (dbResult.error) {
62
+ logger.error(dbResult.error);
63
+ return;
64
+ }
65
+
66
+ // TODO. 这个channel 应该是查表 当前用户 开通的 通知方式
67
+ if (channel == 'qywx') {
68
+ await wxNotice.notice(toUsers, params);
69
+ }
70
+ },
71
+
72
+ /**
73
+ * @param {string[]} props.toUsers 通知的用户ids
74
+ * @param {string} props.content 通知的文案
75
+ * @param {string} props.link 通知跳转链接
76
+ */
77
+ async notmail(props) {
78
+ let { toUsers } = props;
79
+ let params = {
80
+ title: NOTIF_TOPICS[props['topic']]['title'],
81
+ content: props['content'],
82
+ link: props['link'],
83
+ };
84
+ await wxNotice.email(toUsers, params);
85
+ },
86
+ };