@kne/fastify-account 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (54) hide show
  1. package/README.md +2506 -803
  2. package/index.js +61 -9
  3. package/libs/controllers/account.js +221 -37
  4. package/libs/controllers/admin.js +92 -17
  5. package/libs/controllers/adminPermission.js +188 -38
  6. package/libs/controllers/adminTenant.js +44 -257
  7. package/libs/controllers/adminTenantCompany.js +53 -0
  8. package/libs/controllers/adminTenantOrg.js +71 -0
  9. package/libs/controllers/{adminRole.js → adminTenantRole.js} +15 -12
  10. package/libs/controllers/adminTenantUser.js +169 -0
  11. package/libs/controllers/requestLog.js +77 -0
  12. package/libs/controllers/tenant.js +4 -14
  13. package/libs/controllers/tenantCompany.js +54 -0
  14. package/libs/controllers/tenantOrg.js +65 -0
  15. package/libs/controllers/tenantRole.js +219 -0
  16. package/libs/controllers/tenantUser.js +169 -0
  17. package/libs/controllers/user.js +4 -3
  18. package/libs/models/admin-role.js +4 -8
  19. package/libs/models/application.js +16 -10
  20. package/libs/models/company-info.js +25 -0
  21. package/libs/models/login-log.js +11 -9
  22. package/libs/models/permission.js +7 -9
  23. package/libs/models/request-log.js +43 -0
  24. package/libs/models/tenant-application.js +8 -10
  25. package/libs/models/tenant-org.js +5 -9
  26. package/libs/models/tenant-permission.js +7 -9
  27. package/libs/models/tenant-role-application.js +13 -13
  28. package/libs/models/tenant-role-permission.js +9 -14
  29. package/libs/models/tenant-role.js +5 -9
  30. package/libs/models/tenant-share-group-permission.js +5 -9
  31. package/libs/models/tenant-share-group.js +5 -9
  32. package/libs/models/tenant-source-user-share-group.js +5 -9
  33. package/libs/models/tenant-token.js +7 -9
  34. package/libs/models/tenant-user-org.js +11 -10
  35. package/libs/models/tenant-user-role.js +11 -10
  36. package/libs/models/tenant-user-share-group.js +6 -10
  37. package/libs/models/tenant-user.js +35 -16
  38. package/libs/models/tenant.js +17 -9
  39. package/libs/models/user-account.js +17 -9
  40. package/libs/models/user.js +27 -17
  41. package/libs/models/verification-code.js +8 -10
  42. package/libs/services/account.js +95 -71
  43. package/libs/services/admin.js +38 -122
  44. package/libs/services/application.js +170 -0
  45. package/libs/services/permission.js +161 -163
  46. package/libs/services/request-log.js +37 -0
  47. package/libs/services/tenant-company.js +54 -0
  48. package/libs/services/tenant-invite.js +62 -0
  49. package/libs/services/tenant-org.js +118 -0
  50. package/libs/services/tenant-role.js +109 -0
  51. package/libs/services/tenant-user.js +582 -0
  52. package/libs/services/tenant.js +69 -670
  53. package/libs/services/user.js +109 -33
  54. package/package.json +3 -3
@@ -0,0 +1,582 @@
1
+ const fp = require('fastify-plugin');
2
+ const transform = require('lodash/transform');
3
+ const groupBy = require('lodash/groupBy');
4
+ const { Unauthorized } = require('http-errors');
5
+ const pick = require('lodash/pick');
6
+ const isNil = require('lodash/isNil');
7
+ module.exports = fp(async (fastify, options) => {
8
+ const { models, services } = fastify.account;
9
+ const { Op } = fastify.sequelize.Sequelize;
10
+
11
+ const getTenantUserPermissionList = async ({ tenantRoleIds }) => {
12
+ const tenantRoleApplication = await models.tenantRoleApplication.findAll({
13
+ attributes: ['applicationId'],
14
+ where: {
15
+ roleId: {
16
+ [Op.in]: tenantRoleIds
17
+ }
18
+ }
19
+ });
20
+
21
+ const applications = await services.application.getApplicationListByIds({ ids: tenantRoleApplication.map(({ applicationId }) => applicationId) });
22
+ const permissions = await services.tenantRole.getPermissionByTenantRoles({ tenantRoleIds });
23
+ const permissionMapping = transform(
24
+ await models.permission.findAll({
25
+ where: {
26
+ id: {
27
+ [Op.in]: permissions.map(({ paths }) => paths).reduce((list, item) => [...list, ...(item || [])], [])
28
+ }
29
+ }
30
+ }),
31
+ (result, value) => {
32
+ result[value.id] = { code: value.code, name: value.name };
33
+ },
34
+ {}
35
+ );
36
+
37
+ const applicationsMapping = transform(
38
+ applications,
39
+ (result, value) => {
40
+ result[value.uuid] = value;
41
+ },
42
+ {}
43
+ );
44
+
45
+ const findEndChildren = permissions => {
46
+ const output = [];
47
+ const core = (list, node) => {
48
+ const { children, other } = groupBy(list, item => (item.pid === node.id ? 'children' : 'other'));
49
+ if (!(children && children.length > 0)) {
50
+ node.id !== 0 && output.push(node);
51
+ return;
52
+ }
53
+ children.forEach(node => {
54
+ core(other || [], node);
55
+ });
56
+ return output;
57
+ };
58
+ core(permissions, { id: 0 });
59
+ return output;
60
+ };
61
+
62
+ const userPermissionList = findEndChildren(permissions).map(({ code, applicationId, paths }) => {
63
+ return `${applicationsMapping[applicationId].code}${
64
+ paths && paths.length > 0
65
+ ? `:${paths
66
+ .map(id => {
67
+ return permissionMapping[id].code;
68
+ })
69
+ .join(':')}`
70
+ : ''
71
+ }:${code}`;
72
+ });
73
+
74
+ return {
75
+ applications: applications,
76
+ permissions: permissions.map(item =>
77
+ Object.assign(
78
+ {},
79
+ {
80
+ code: item.code,
81
+ name: item.name,
82
+ isModule: item.isModule,
83
+ paths: (item.paths || []).map(id => permissionMapping[id])
84
+ }
85
+ )
86
+ ),
87
+ userPermissionList
88
+ };
89
+ };
90
+
91
+ const getUserTenant = async authenticatePayload => {
92
+ const user = await services.user.getUser(authenticatePayload);
93
+ const tenantUserList = await models.tenantUser.findAll({
94
+ where: {
95
+ userId: user.id,
96
+ status: 0
97
+ }
98
+ });
99
+
100
+ const tenantList =
101
+ tenantUserList.length > 0
102
+ ? await models.tenant.findAll({
103
+ where: {
104
+ uuid: {
105
+ [Op.in]: tenantUserList.map(({ tenantId }) => tenantId)
106
+ },
107
+ status: 0
108
+ }
109
+ })
110
+ : [];
111
+
112
+ const currentTenantUser = user.currentTenantId && tenantUserList.find(({ tenantId }) => tenantId === user.currentTenantId);
113
+ const currentTenant = currentTenantUser && tenantList.find(({ uuid }) => uuid === currentTenantUser.tenantId);
114
+
115
+ return {
116
+ currentTenant: currentTenant && Object.assign({}, currentTenant.get({ plain: true }), { id: currentTenant.uuid }),
117
+ currentTenantUser: currentTenantUser && Object.assign({}, currentTenantUser.get({ plain: true }), { id: currentTenantUser.uuid }),
118
+ tenantList: tenantList.map(item => {
119
+ return Object.assign({}, item.get({ plain: true }), { id: item.uuid });
120
+ }),
121
+ tenantUserList: tenantUserList.map(item => {
122
+ return Object.assign({}, item.get({ plain: true }), { id: item.uuid });
123
+ }),
124
+ userInfo: user
125
+ };
126
+ };
127
+
128
+ const getTenantUserInstance = async ({ id }) => {
129
+ const currentTenantUser = await models.tenantUser.findOne({
130
+ where: {
131
+ uuid: id
132
+ }
133
+ });
134
+ if (!currentTenantUser) {
135
+ throw new Error('租户用户不存在');
136
+ }
137
+
138
+ return currentTenantUser;
139
+ };
140
+
141
+ const getTenantUserByUserId = async ({ userInfo: user, appName }) => {
142
+ if (!user?.currentTenantId) {
143
+ throw new Unauthorized('没有找到当前绑定租户');
144
+ }
145
+
146
+ const tenant = await services.tenant.getTenant({ id: user.currentTenantId });
147
+
148
+ if (!tenant) {
149
+ throw new Error('当前租户不能使用');
150
+ }
151
+
152
+ const tenantApplications = await services.application.getApplicationList({ tenantId: tenant.id, appName });
153
+
154
+ if (appName && !tenantApplications.some(item => item.code === appName)) {
155
+ throw Unauthorized('当前租户没有开通该应用权限');
156
+ }
157
+
158
+ const tenantUser = await models.tenantUser.findOne({
159
+ attributes: ['uuid', 'avatar', 'name', 'description', 'phone', 'email'],
160
+ include: [
161
+ {
162
+ attributes: ['id', 'name'],
163
+ model: models.tenantOrg
164
+ },
165
+ {
166
+ attributes: ['id', 'name'],
167
+ model: models.tenantRole
168
+ }
169
+ ],
170
+ where: {
171
+ tenantId: tenant.id,
172
+ userId: user.id,
173
+ status: 0
174
+ }
175
+ });
176
+
177
+ if (!tenantUser) {
178
+ throw Unauthorized('租户用户不存在');
179
+ }
180
+
181
+ // 获取当前租户默认角色
182
+ const defaultTenant = await models.tenantRole.findOne({
183
+ where: {
184
+ type: 1,
185
+ tenantId: tenant.id
186
+ }
187
+ });
188
+
189
+ if (!defaultTenant) {
190
+ throw new Error('租户默认角色未设置,请联系管理员');
191
+ }
192
+
193
+ const tenantRoleIds = tenantUser.tenantRoles.map(({ id }) => id);
194
+ tenantRoleIds.push(defaultTenant.id);
195
+
196
+ const { userPermissionList, applications: roleApplications } = await getTenantUserPermissionList({ tenantRoleIds });
197
+ if (!tenantUser) {
198
+ throw new Error('当前租户用户不存在或者已经被关闭');
199
+ }
200
+
201
+ if (appName && !roleApplications.some(item => item.code === appName)) {
202
+ throw Unauthorized('当前用户没有开通该应用权限');
203
+ }
204
+
205
+ const outputTenantUser = Object.assign({}, tenantUser.get({ plain: true }), { id: tenantUser.uuid });
206
+ outputTenantUser.tenantOrgs = outputTenantUser?.tenantOrgs.map(({ id, name }) => ({ id, name }));
207
+ outputTenantUser.tenantRoles = outputTenantUser?.tenantRoles.map(({ id, name }) => ({ id, name }));
208
+ return {
209
+ tenant: pick(tenant, ['id', 'name', 'description']),
210
+ tenantUser: Object.assign({}, outputTenantUser, {
211
+ permissions: userPermissionList
212
+ }),
213
+ userInfo: user
214
+ };
215
+ };
216
+
217
+ const checkTenantRoleUsed = async ({ tenantRoleId }) => {
218
+ if (
219
+ (await models.tenantUserRole.count({
220
+ where: {
221
+ tenantRoleId
222
+ }
223
+ })) > 0
224
+ ) {
225
+ throw new Error('该角色已经被使用,请在租户用户种处理掉所有使用该角色的租户用户后重试');
226
+ }
227
+ };
228
+
229
+ const checkTenantUserInfoValidate = async ({ tenantId, roleIds, orgIds, userId }) => {
230
+ await services.tenant.getTenant({ id: tenantId });
231
+ if (
232
+ roleIds &&
233
+ roleIds.length > 0 &&
234
+ (await models.tenantRole.count({
235
+ where: {
236
+ id: {
237
+ [Op.in]: roleIds
238
+ }
239
+ }
240
+ })) < roleIds.length
241
+ ) {
242
+ throw new Error('包含租户不存在的角色');
243
+ }
244
+ if (orgIds && orgIds.length === 0) {
245
+ const tenantOrg = await models.tenantOrg.findOne({
246
+ where: {
247
+ pid: 0,
248
+ tenantId
249
+ }
250
+ });
251
+ if (!tenantOrg) {
252
+ throw new Error('租户根节点不存在');
253
+ }
254
+ }
255
+
256
+ if (
257
+ orgIds &&
258
+ orgIds.length > 0 &&
259
+ (await models.tenantOrg.count({
260
+ where: {
261
+ tenantId,
262
+ id: {
263
+ [Op.in]: orgIds
264
+ }
265
+ }
266
+ })) < orgIds.length
267
+ ) {
268
+ throw new Error('包含租户不存在组织');
269
+ }
270
+
271
+ try {
272
+ await services.user.getUserInstance({ id: userId });
273
+ } catch (e) {
274
+ throw new Error('用户不存在,应该先创建用户再创建租户用户');
275
+ }
276
+ };
277
+
278
+ const addTenantUser = async ({ tenantId, roleIds, orgIds, userId, ...tenantUser }) => {
279
+ const tenant = await services.tenant.getTenant({ id: tenantId });
280
+
281
+ const currentAccountNumber = await models.tenantUser.count({
282
+ where: { tenantId }
283
+ });
284
+
285
+ if (currentAccountNumber >= tenant.accountNumber) {
286
+ throw new Error('租户用户数量已达上限');
287
+ }
288
+
289
+ await checkTenantUserInfoValidate({ tenantId, roleIds, orgIds, userId });
290
+
291
+ const t = await fastify.sequelize.instance.transaction();
292
+
293
+ if (
294
+ (await models.tenantUser.count({
295
+ where: {
296
+ userId,
297
+ tenantId
298
+ }
299
+ })) > 0
300
+ ) {
301
+ throw new Error('该用户已经属于该租户,不能重复添加');
302
+ }
303
+
304
+ try {
305
+ const currentTenantUser = await models.tenantUser.create(
306
+ {
307
+ name: tenantUser.name,
308
+ avatar: tenantUser.avatar,
309
+ phone: tenantUser.phone,
310
+ email: tenantUser.email,
311
+ description: tenantUser.description,
312
+ tenantId,
313
+ userId
314
+ },
315
+ { transaction: t }
316
+ );
317
+ roleIds.length > 0 &&
318
+ (await models.tenantUserRole.bulkCreate(
319
+ roleIds.map(roleId => {
320
+ return {
321
+ tenantRoleId: roleId,
322
+ tenantId,
323
+ tenantUserId: currentTenantUser.uuid
324
+ };
325
+ }),
326
+ { transaction: t }
327
+ ));
328
+
329
+ await models.tenantUserOrg.bulkCreate(
330
+ orgIds.map(orgId => {
331
+ return {
332
+ tenantOrgId: orgId,
333
+ tenantId,
334
+ tenantUserId: currentTenantUser.uuid
335
+ };
336
+ }),
337
+ { transaction: t }
338
+ );
339
+
340
+ await t.commit();
341
+ } catch (e) {
342
+ await t.rollback();
343
+ throw e;
344
+ }
345
+ };
346
+
347
+ const saveTenantUser = async ({ id, tenantId, roleIds, orgIds, userId, ...tenantUser }) => {
348
+ const currentTenantUser = await getTenantUserInstance({ id });
349
+ if (tenantId !== currentTenantUser.tenantId) {
350
+ throw new Error('租户Id和当前租户用户的租户Id不一致');
351
+ }
352
+ await checkTenantUserInfoValidate({ tenantId, roleIds, orgIds, userId: currentTenantUser.userId });
353
+
354
+ const tenantRoleIds = (
355
+ await models.tenantUserRole.findAll({
356
+ attributes: ['tenantRoleId'],
357
+ where: {
358
+ tenantId,
359
+ tenantUserId: currentTenantUser.uuid
360
+ }
361
+ })
362
+ ).map(({ tenantRoleId }) => tenantRoleId);
363
+
364
+ const tenantOrgIds = (
365
+ await models.tenantUserOrg.findAll({
366
+ attributes: ['tenantOrgId'],
367
+ where: {
368
+ tenantId,
369
+ tenantUserId: currentTenantUser.uuid
370
+ }
371
+ })
372
+ ).map(({ tenantOrgId }) => tenantOrgId);
373
+
374
+ const t = await fastify.sequelize.instance.transaction();
375
+
376
+ try {
377
+ ['name', 'avatar', 'phone', 'email', 'description'].forEach(name => {
378
+ if (!isNil(tenantUser[name])) {
379
+ currentTenantUser[name] = tenantUser[name];
380
+ }
381
+ });
382
+ await currentTenantUser.save({ transaction: t });
383
+ // 修改角色
384
+ if (roleIds) {
385
+ const needDeleteTenantRole = tenantRoleIds.filter(targetId => roleIds.indexOf(targetId) === -1);
386
+ const needAddTenantRole = roleIds.filter(targetId => tenantRoleIds.indexOf(targetId) === -1);
387
+ await models.tenantUserRole.destroy({
388
+ where: {
389
+ tenantId,
390
+ tenantUserId: currentTenantUser.uuid,
391
+ tenantRoleId: {
392
+ [Op.in]: needDeleteTenantRole
393
+ }
394
+ },
395
+ transaction: t
396
+ });
397
+ await models.tenantUserRole.bulkCreate(
398
+ needAddTenantRole.map(tenantRoleId => {
399
+ return { tenantId, tenantUserId: currentTenantUser.uuid, tenantRoleId };
400
+ }),
401
+ { transaction: t }
402
+ );
403
+ }
404
+ //修改组织
405
+ if (orgIds) {
406
+ const needDeleteTenantOrg = tenantOrgIds.filter(targetId => orgIds.indexOf(targetId) === -1);
407
+ const needAddTenantOrg = orgIds.filter(targetId => tenantOrgIds.indexOf(targetId) === -1);
408
+ await models.tenantUserOrg.destroy({
409
+ where: {
410
+ tenantId,
411
+ tenantUserId: currentTenantUser.uuid,
412
+ tenantOrgId: {
413
+ [Op.in]: needDeleteTenantOrg
414
+ }
415
+ },
416
+ transaction: t
417
+ });
418
+ await models.tenantUserOrg.bulkCreate(
419
+ needAddTenantOrg.map(tenantOrgId => {
420
+ return { tenantId, tenantUserId: currentTenantUser.uuid, tenantOrgId };
421
+ }),
422
+ { transaction: t }
423
+ );
424
+ }
425
+ await t.commit();
426
+ } catch (e) {
427
+ await t.rollback();
428
+ throw e;
429
+ }
430
+ };
431
+
432
+ const deleteTenantUser = async ({ tenantId, tenantUserId }) => {
433
+ await services.tenant.getTenant({ id: tenantId });
434
+ const tenantUser = await getTenantUserInstance({ id: tenantUserId });
435
+
436
+ const t = await fastify.sequelize.instance.transaction();
437
+
438
+ try {
439
+ await models.tenantUserOrg.destroy({
440
+ where: {
441
+ tenantId,
442
+ tenantUserId: tenantUser.uuid
443
+ },
444
+ transaction: t
445
+ });
446
+ await models.tenantUserRole.destroy({
447
+ where: {
448
+ tenantId,
449
+ tenantUserId: tenantUser.uuid
450
+ },
451
+ transaction: t
452
+ });
453
+ await tenantUser.destroy({ transaction: t });
454
+ await t.commit();
455
+ } catch (e) {
456
+ await t.rollback();
457
+ throw e;
458
+ }
459
+ };
460
+
461
+ const closeTenantUser = async ({ tenantId, tenantUserId }) => {
462
+ await services.tenant.getTenant({ id: tenantId });
463
+ const tenantUser = await getTenantUserInstance({ id: tenantUserId });
464
+ tenantUser.status = 12;
465
+ await tenantUser.save();
466
+ };
467
+
468
+ const openTenantUser = async ({ tenantId, tenantUserId }) => {
469
+ await services.tenant.getTenant({ id: tenantId });
470
+ const tenantUser = await getTenantUserInstance({ id: tenantUserId });
471
+ tenantUser.status = 0;
472
+ await tenantUser.save();
473
+ };
474
+
475
+ const getTenantUserList = async ({ tenantId, currentPage, perPage, filter }) => {
476
+ const queryFilter = {};
477
+ await services.tenant.getTenant({ id: tenantId });
478
+
479
+ ['name', 'phone', 'email'].forEach(key => {
480
+ if (filter && filter[key]) {
481
+ queryFilter[key] = {
482
+ [Op.like]: `%${filter[key]}%`
483
+ };
484
+ }
485
+ });
486
+
487
+ const { count, rows } = await models.tenantUser.findAndCountAll({
488
+ include: [models.tenantRole, models.tenantOrg, models.user],
489
+ where: Object.assign({}, queryFilter, { tenantId }),
490
+ offset: perPage * (currentPage - 1),
491
+ limit: perPage
492
+ });
493
+
494
+ return {
495
+ pageData: rows.map(item => {
496
+ return Object.assign({}, item.get({ pain: true }), { id: item.uuid });
497
+ }),
498
+ totalCount: count
499
+ };
500
+ };
501
+
502
+ const includeTenantUserBatch = async ({ tenantId, list }) => {
503
+ await services.tenant.getTenant({ id: tenantId });
504
+ const errors = [],
505
+ successes = [];
506
+ for (let current of list) {
507
+ if (!(current.phone || current.email)) {
508
+ errors.push({ item: current, msg: '电话和邮箱不能同时为空' });
509
+ continue;
510
+ }
511
+ const currentQuery = [];
512
+ if (current.phone) {
513
+ currentQuery.push({ phone: current.phone });
514
+ }
515
+ if (current.email) {
516
+ currentQuery.push({ email: current.email });
517
+ }
518
+
519
+ if (
520
+ (await models.tenantUser.count({
521
+ where: {
522
+ [Op.or]: currentQuery
523
+ }
524
+ })) > 0
525
+ ) {
526
+ errors.push({ item: current, msg: '租户用户已经存在,或手机邮箱和已有租户用户重复' });
527
+ continue;
528
+ }
529
+
530
+ if (await services.user.accountIsExists(current, {})) {
531
+ errors.push({ item: current, msg: '用户已经存在,已发送加入租户邀请等待对方同意' });
532
+ continue;
533
+ }
534
+
535
+ try {
536
+ const user = await services.user.addUser({
537
+ nickname: current.name,
538
+ phone: current.phone,
539
+ email: current.email,
540
+ password: services.account.md5(current.password || options.defaultPassword),
541
+ status: 1
542
+ });
543
+ const rootOrg = await services.tenantOrg.getTenantOrgRoot({ tenantId });
544
+ await services.tenantUser.addTenantUser(
545
+ Object.assign(
546
+ {},
547
+ {
548
+ orgIds: [rootOrg.id],
549
+ roleIds: []
550
+ },
551
+ {
552
+ tenantId,
553
+ userId: user.id,
554
+ ...current
555
+ }
556
+ )
557
+ );
558
+ successes.push({ item: current });
559
+ } catch (e) {
560
+ errors.push({ item: current, msg: e.message });
561
+ throw e;
562
+ }
563
+ }
564
+
565
+ return { errors, successes };
566
+ };
567
+
568
+ services.tenantUser = {
569
+ getUserTenant,
570
+ getTenantUserPermissionList,
571
+ getTenantUserByUserId,
572
+ checkTenantRoleUsed,
573
+ checkTenantUserInfoValidate,
574
+ addTenantUser,
575
+ saveTenantUser,
576
+ deleteTenantUser,
577
+ closeTenantUser,
578
+ openTenantUser,
579
+ getTenantUserList,
580
+ includeTenantUserBatch
581
+ };
582
+ });