@gingkoo/base-server 0.0.1-alpha.0 → 0.0.1-alpha.1
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/README.md +7 -0
- package/app.js +32 -40
- package/backend/common/entity.js +55 -0
- package/backend/common/enum.js +247 -0
- package/backend/common/fss/index.js +18 -0
- package/backend/common/fss/routers/attachment.js +45 -0
- package/backend/common/fss/routers/download.js +43 -0
- package/backend/common/fss/routers/import_export.js +113 -0
- package/backend/common/fss/routers/resources.js +149 -0
- package/backend/common/fss/routers/upload.js +81 -0
- package/backend/common/fss/services/import_export.js +159 -0
- package/backend/common/fss/services/index.js +92 -0
- package/backend/common/fss/utils.js +39 -0
- package/backend/common/ginfo/config.js +5 -0
- package/backend/common/ginfo/index.js +62 -0
- package/backend/common/index_template.html +28 -0
- package/backend/common/logger/index.js +96 -0
- package/backend/common/mapping.js +98 -0
- package/backend/common/middleware/auth.js +97 -0
- package/backend/common/middleware/cors.js +13 -0
- package/backend/common/middleware/response.js +25 -0
- package/backend/common/page_engine.js +487 -0
- package/backend/common/schedule.js +45 -0
- package/backend/common/services/dataCombine.js +67 -0
- package/backend/common/services/dept.js +37 -0
- package/backend/common/services/dict.js +487 -0
- package/backend/common/services/email.js +49 -0
- package/backend/common/services/generalConfig.js +137 -0
- package/backend/common/services/login.js +18 -0
- package/backend/common/services/notice.js +260 -0
- package/backend/common/services/permission.js +500 -0
- package/backend/common/services/roles.js +57 -0
- package/backend/common/services/send-notice.js +86 -0
- package/backend/common/services/task.js +259 -0
- package/backend/common/services/user.js +673 -0
- package/backend/common/socket.js +18 -0
- package/backend/common/sse/index.js +81 -0
- package/backend/common/sse/router.js +30 -0
- package/backend/common/task.js +75 -0
- package/backend/common/wechat/index.js +9 -0
- package/backend/common/wechat/routers/auth.js +238 -0
- package/{modules/user/frontend → backend/common/wechat/routers}/index.html +14 -7
- package/backend/common/wechat/services/auth.js +209 -0
- package/backend/common/wechat/services/notice.js +171 -0
- package/backend/config/index.js +63 -0
- package/backend/config/path.js +3 -0
- package/backend/router.js +96 -0
- package/backend/routers/app.js +222 -0
- package/backend/routers/automate.js +276 -0
- package/backend/routers/dict.js +370 -0
- package/backend/routers/email.js +85 -0
- package/backend/routers/generalConfig.js +276 -0
- package/backend/routers/idm.js +245 -0
- package/backend/routers/module.js +357 -0
- package/backend/routers/notice.js +138 -0
- package/backend/routers/pages.js +46 -0
- package/backend/routers/permission.js +985 -0
- package/backend/routers/setting.js +184 -0
- package/backend/routers/team/index.js +22 -0
- package/backend/routers/team/routers/mapping.js +29 -0
- package/backend/routers/team/routers/member.js +72 -0
- package/backend/routers/team/routers/membermanage.js +289 -0
- package/backend/routers/team/routers/pages.js +47 -0
- package/backend/routers/team/routers/roles.js +92 -0
- package/backend/routers/team/routers/teaminfo.js +27 -0
- package/backend/routers/team/routers/usergroup.js +213 -0
- package/backend/routers/team/services/mapping.js +101 -0
- package/backend/routers/team/services/member.js +206 -0
- package/backend/routers/team/services/roles.js +71 -0
- package/backend/routers/team/services/teaminfo.js +20 -0
- package/backend/routers/team/services/usergroup.js +128 -0
- package/backend/routers/user.js +436 -0
- package/backend/services/automate.js +60 -0
- package/backend/services/config.js +14 -0
- package/backend/services/module.js +298 -0
- package/backend/services/permission.js +192 -0
- package/backend/services/services.js +115 -0
- package/backend/services/setting.js +190 -0
- package/backend/services/token.js +42 -0
- package/backend/space.js +52 -0
- package/backend/space_mapping.js +15 -0
- package/backend/utils/avatar.js +48 -0
- package/backend/utils/color_gen_helper.js +20 -0
- package/backend/utils/date.js +66 -0
- package/backend/utils/excel.js +446 -0
- package/backend/utils/fs/doc.md +64 -0
- package/backend/utils/fs/index.js +127 -0
- package/backend/utils/jwt.js +54 -0
- package/backend/utils/modules/sequence.js +93 -0
- package/backend/utils/object.js +31 -0
- package/backend/utils/page-query-helper/index.js +61 -0
- package/backend/utils/path.js +123 -0
- package/backend/utils/run.js +25 -0
- package/backend/utils/tokenize.js +82 -0
- package/backend/utils/typeof.js +5 -0
- package/backend/utils/util.js +153 -0
- package/backend/views/api/index.js +32 -0
- package/backend/views/api/index.xml +49 -0
- package/backend/views/dict/index.js +80 -0
- package/backend/views/dict/index.xml +52 -0
- package/backend/views/index.js +32 -0
- package/backend/views/members_manage/index.js +68 -0
- package/backend/views/members_manage/index.xml +68 -0
- package/backend/views/roles/index.js +17 -0
- package/backend/views/roles/index.xml +47 -0
- package/backend/views/usergroup/index.js +68 -0
- package/backend/views/usergroup/index.xml +65 -0
- package/dist/assets/css/index-cc834b52.css +3 -0
- package/dist/assets/css/index-cc834b52.css.gz +0 -0
- package/dist/assets/js/index-9eef7474.js +762 -0
- package/dist/assets/js/index-9eef7474.js.gz +0 -0
- package/dist/assets/js/react-cropper.es-d3337769.js +10 -0
- package/dist/assets/js/react-cropper.es-d3337769.js.gz +0 -0
- package/dist/assets/png/u9-2348c304.png +0 -0
- package/dist/assets/woff2/materialicons-83be7b2f.woff2 +0 -0
- package/dist/index.html +162 -0
- package/package.json +60 -5
- package/common/router.js +0 -42
- package/modules/user/backend/index.js +0 -19
- package/modules/user/backend/routers/user.js +0 -11
- package/modules/user/frontend/index.js +0 -1
- 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
|
+
};
|