├── .autod.conf.js
├── .deploy.yml
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .prettierignore
├── .sequelizerc
├── .travis.yml
├── LICENSE
├── README.md
├── app-boot-hook-do
└── index.js
├── app.js
├── app
├── contract
│ ├── dto.js
│ ├── request
│ │ ├── configuration.js
│ │ ├── department.js
│ │ ├── invite.js
│ │ ├── menu.js
│ │ ├── message.js
│ │ ├── operation_log.js
│ │ ├── permission.js
│ │ ├── project.js
│ │ ├── project_file.js
│ │ ├── project_template.js
│ │ ├── project_template_task.js
│ │ ├── role.js
│ │ ├── role_menu.js
│ │ ├── role_permission.js
│ │ ├── task.js
│ │ ├── task_list.js
│ │ ├── task_log.js
│ │ ├── task_priority.js
│ │ ├── task_state.js
│ │ ├── task_tag.js
│ │ ├── task_task_tag.js
│ │ ├── task_type.js
│ │ ├── task_working_hour.js
│ │ ├── user.js
│ │ ├── user_project.js
│ │ ├── user_project_collect.js
│ │ ├── user_role.js
│ │ ├── user_task.js
│ │ ├── user_task_like.js
│ │ └── verification_code.js
│ └── response
│ │ └── user.js
├── controller
│ └── v1
│ │ ├── configurations.js
│ │ ├── departments.js
│ │ ├── invites.js
│ │ ├── menus.js
│ │ ├── messages.js
│ │ ├── operation_logs.js
│ │ ├── permissions.js
│ │ ├── project_files.js
│ │ ├── project_template_tasks.js
│ │ ├── project_templates.js
│ │ ├── projects.js
│ │ ├── role_menus.js
│ │ ├── role_permissions.js
│ │ ├── roles.js
│ │ ├── task_lists.js
│ │ ├── task_logs.js
│ │ ├── task_prioritys.js
│ │ ├── task_states.js
│ │ ├── task_tags.js
│ │ ├── task_task_tags.js
│ │ ├── task_types.js
│ │ ├── task_working_hours.js
│ │ ├── tasks.js
│ │ ├── uploads.js
│ │ ├── user_project_collects.js
│ │ ├── user_projects.js
│ │ ├── user_roles.js
│ │ ├── user_task_likes.js
│ │ ├── user_tasks.js
│ │ ├── users.js
│ │ └── verification_codes.js
├── extend
│ ├── RESTfulHTTPStatus.rec
│ └── helper.js
├── io
│ ├── controller
│ │ └── index.js
│ └── middleware
│ │ ├── connection.js
│ │ └── packet.js
├── middleware
│ ├── error_handler.js
│ ├── jurisdiction_handler.js
│ └── log_handler.js
├── model
│ ├── configurations.js
│ ├── departments.js
│ ├── invites.js
│ ├── menus.js
│ ├── messages.js
│ ├── operation_logs.js
│ ├── permissions.js
│ ├── project_files.js
│ ├── project_template_tasks.js
│ ├── project_templates.js
│ ├── projects.js
│ ├── role_menus.js
│ ├── role_permissions.js
│ ├── roles.js
│ ├── task_lists.js
│ ├── task_logs.js
│ ├── task_prioritys.js
│ ├── task_states.js
│ ├── task_tags.js
│ ├── task_task_tags.js
│ ├── task_types.js
│ ├── task_working_hours.js
│ ├── tasks.js
│ ├── user_project_collects.js
│ ├── user_projects.js
│ ├── user_roles.js
│ ├── user_task_likes.js
│ ├── user_tasks.js
│ ├── users.js
│ └── verification_codes.js
├── router.js
├── schedule
│ ├── resend_not_ack_socket.js
│ └── update_cache_redis.js
└── service
│ ├── configurations.js
│ ├── departments.js
│ ├── invites.js
│ ├── menus.js
│ ├── messages.js
│ ├── operation_logs.js
│ ├── permissions.js
│ ├── project_files.js
│ ├── project_template_tasks.js
│ ├── project_templates.js
│ ├── projects.js
│ ├── role_menus.js
│ ├── role_permissions.js
│ ├── roles.js
│ ├── task_lists.js
│ ├── task_logs.js
│ ├── task_prioritys.js
│ ├── task_states.js
│ ├── task_tags.js
│ ├── task_task_tags.js
│ ├── task_types.js
│ ├── task_working_hours.js
│ ├── tasks.js
│ ├── user_project_collects.js
│ ├── user_projects.js
│ ├── user_roles.js
│ ├── user_task_likes.js
│ ├── user_tasks.js
│ ├── users.js
│ └── verification_codes.js
├── appveyor.yml
├── autocannon
├── index.js
├── index2.js
├── index3.js
├── index4.js
└── index5.js
├── config
├── config.default.js
├── config.local.js
├── config.prod.js
├── config.unittest.js
└── plugin.js
├── database
├── config.json
├── egg-beehive-dev.sql
├── egg-beehive-test.sql
└── migrations
│ ├── 20200110080210-create-configuration.js
│ ├── 20200110080210-create-department.js
│ ├── 20200110080210-create-invite.js
│ ├── 20200110080210-create-menu.js
│ ├── 20200110080210-create-message.js
│ ├── 20200110080210-create-operation_log.js
│ ├── 20200110080210-create-permission.js
│ ├── 20200110080210-create-project.js
│ ├── 20200110080210-create-project_file.js
│ ├── 20200110080210-create-role.js
│ ├── 20200110080210-create-task.js
│ ├── 20200110080210-create-task_list.js
│ ├── 20200110080210-create-task_log.js
│ ├── 20200110080210-create-task_priority.js
│ ├── 20200110080210-create-task_state.js
│ ├── 20200110080210-create-task_tag.js
│ ├── 20200110080210-create-task_task_tag.js
│ ├── 20200110080210-create-task_type.js
│ ├── 20200110080210-create-task_working_hour.js
│ ├── 20200110080210-create-user.js
│ ├── 20200110080210-create-user_project.js
│ ├── 20200110080210-create-user_project_collect.js
│ ├── 20200110080210-create-user_role.js
│ ├── 20200110080210-create-user_task.js
│ ├── 20200110080210-create-user_task_like.js
│ ├── 20200110080210-create-verification_code.js
│ ├── 20200110080211-create-project_template.js
│ ├── 20200110080211-create-role_menu.js
│ ├── 20200110080211-create-role_permission.js
│ └── 20200110080212-create-project_template_task.js
├── example
├── 2021-09-12_112646.png
├── 2021-09-12_113236.png
├── 2021-09-12_113312.png
├── 2021-09-12_113341.png
├── 2021-09-12_113612.png
├── 2021-09-12_113839.png
├── 2021-09-12_114307.png
├── 2021-09-12_115100.png
├── 2021-09-12_115127.png
├── 2021-09-12_115228.png
├── 2021-09-13_021734.png
├── Beehive-data-model.jpg
├── Beehive功能设计.jpg
├── Beehive后端设计.jpg
└── home-page.gif
├── generator
├── config-department.js
├── config-invite.js
├── config-menu.js
├── config-message.js
├── config-operation-log.js
├── config-permission.js
├── config-project-file.js
├── config-project-participant.js
├── config-project-template-task.js
├── config-project-template.js
├── config-project.js
├── config-role-menu.js
├── config-role_permission.js
├── config-task-list.js
├── config-task-log.js
├── config-task-priority.js
├── config-task-state.js
├── config-task-tag.js
├── config-task-taskTag.js
├── config-task-type.js
├── config-task-working-hour.js
├── config-task.js
├── config-user-project-collect.js
├── config-user-project.js
├── config-user-role.js
├── config-user-task-like.js
├── config-user-task.js
├── config-user.js
├── config-verification_code.js
├── config.configuration.js
├── config.example.js
├── config.js
├── delete-entity.js
├── index.js
├── lib
│ ├── contract.js
│ ├── controller.js
│ ├── migrations.js
│ ├── model.js
│ ├── router.js
│ ├── service.js
│ └── test.js
└── template
│ ├── contract
│ └── request
│ │ └── template.js
│ ├── controller
│ └── template.js
│ ├── migrations
│ └── 20200110080210-create-template.js
│ ├── model
│ └── template.js
│ ├── service
│ └── template.js
│ └── test
│ └── controller
│ └── template.test.js
├── jsconfig.json
├── package.json
├── prettier.config.js
├── public
└── uploads
│ └── 1606988796827_683491316.jpg
├── remoteConfig.js
├── test
├── .setup.js
├── app
│ └── controller
│ │ ├── configurations.test.js
│ │ ├── departments.test.js
│ │ ├── invites.test.js
│ │ ├── menus.test.js
│ │ ├── messages.test.js
│ │ ├── operation_logs.test.js
│ │ ├── permissions.test.js
│ │ ├── project_files.test.js
│ │ ├── project_template_tasks.test.js
│ │ ├── project_templates.test.js
│ │ ├── projects.test.js
│ │ ├── role_menus.test.js
│ │ ├── role_permissions.test.js
│ │ ├── roles.test.js
│ │ ├── task_lists.test.js
│ │ ├── task_logs.test.js
│ │ ├── task_prioritys.test.js
│ │ ├── task_states.test.js
│ │ ├── task_tags.test.js
│ │ ├── task_task_tags.test.js
│ │ ├── task_types.test.js
│ │ ├── task_working_hours.test.js
│ │ ├── tasks.test.js
│ │ ├── user_project_collects.test.js
│ │ ├── user_projects.test.js
│ │ ├── user_roles.test.js
│ │ ├── user_task_likes.test.js
│ │ ├── user_tasks.test.js
│ │ ├── users.test.js
│ │ └── verification_codes.test.js
└── factories.js
├── tshelper.js
└── unhealthy.js
/.autod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | write: true,
5 | prefix: '^',
6 | plugin: 'autod-egg',
7 | test: ['test', 'benchmark'],
8 | dep: ['egg', 'egg-scripts'],
9 | devdep: ['egg-ci', 'egg-bin', 'egg-mock', 'autod', 'autod-egg', 'eslint', 'eslint-config-egg'],
10 | exclude: ['./test/fixtures', './dist'],
11 | };
12 |
--------------------------------------------------------------------------------
/.deploy.yml:
--------------------------------------------------------------------------------
1 | instances:
2 | - port: 7006
3 | title: 7006 # 自定义标题,避免与同机上其他 eggjs 重名
4 | - port: 7007
5 | title: 7007
6 | startCommand: service nginx start # nginx 启动命令,运行时若 nginx 未运行会尝试执行
7 | reloadCommand: nginx -s reload # nginx reload 命令
8 | nginxConfig: nginx.conf # nginx 配置地址,可以是绝对地址,如果放置于项目下,记得在 nginx 全局配置里 include
9 | waitStopTime: 5000 # 停止前的等待时间
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-egg",
3 | "rules": {
4 | "no-unused-vars": "off",
5 | "template-curly-spacing": "off",
6 | "array-bracket-spacing": "off",
7 | "newline-per-chained-call": [
8 | "error",
9 | {
10 | "ignoreChainWithDepth": 1
11 | }
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/
2 | npm-debug.log
3 | yarn-error.log
4 | node_modules/
5 | package-lock.json
6 | yarn.lock
7 | coverage/
8 | .idea/
9 | run/
10 | .DS_Store
11 | *.sw*
12 | *.un~
13 | typings/
14 | .nyc_output/
15 | app/public/uploads/*
16 | .github
17 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 |
2 | /node_modules/**
3 | /typings
4 | /coverage
5 |
--------------------------------------------------------------------------------
/.sequelizerc:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | module.exports = {
6 | config: path.join(__dirname, 'database/config.json'),
7 | 'migrations-path': path.join(__dirname, 'database/migrations'),
8 | 'seeders-path': path.join(__dirname, 'database/seeders'),
9 | 'models-path': path.join(__dirname, 'app/model'),
10 | };
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '10'
4 | before_install:
5 | - npm i npminstall -g
6 | install:
7 | - npminstall
8 | script:
9 | - npm run ci
10 | after_script:
11 | - npminstall codecov && codecov
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Imfdj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app-boot-hook-do/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | // 资源数据缓存到redis
4 | async permissionsToRedis(app) {
5 | const ctx = await app.createAnonymousContext();
6 | const { models } = app.model;
7 | const { redis } = app;
8 | const permissionsPromise = models.permissions.findAll({ limit: 10000 });
9 | const rolesPromise = models.roles.findAll({
10 | attributes: ['id', 'name'],
11 | include: [{ attributes: ['id', 'url', 'action'], model: models.permissions }],
12 | limit: 10000,
13 | raw: false,
14 | });
15 | const redisKeysPermissions = redis.keys('permissions:url:*');
16 | const [permissions, roles, redisKeys] = await Promise.all([permissionsPromise, rolesPromise, redisKeysPermissions]);
17 | const pipeline = redis.pipeline();
18 | // 删除所有permissions:url:*
19 | redisKeys.forEach(v => pipeline.del(v));
20 | permissions.forEach(v => pipeline.hmset(ctx.helper.redisKeys.permissionsBaseActionUrl(v.action, v.url), v.dataValues));
21 | // 根据角色id存储对应资源
22 | const rolesArr = JSON.parse(JSON.stringify(roles));
23 | rolesArr.forEach(e => {
24 | pipeline.del(ctx.helper.redisKeys.rolePermissionsBaseRoleId(e.id));
25 | if (e.permissions.length) {
26 | const arr = [];
27 | e.permissions.forEach(permission => arr.push(`${permission.action}_${permission.url}`));
28 | pipeline.sadd(ctx.helper.redisKeys.rolePermissionsBaseRoleId(e.id), arr);
29 | }
30 | });
31 | await pipeline.exec();
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/app/contract/dto.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | // user: {
5 | // id: { type: 'number', description: 'id 唯一键' },
6 | // file: { type: 'file', description: '文件' },
7 | // },
8 | };
9 |
--------------------------------------------------------------------------------
/app/contract/request/configuration.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | configurationId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | configurationBodyReq: {
8 | rsa_private_key: {
9 | type: 'string',
10 | required: true,
11 | max: 1000,
12 | trim: true,
13 | example: '',
14 | description: 'rsa私钥',
15 | },
16 | rsa_public_key: {
17 | type: 'string',
18 | required: true,
19 | max: 1000,
20 | trim: true,
21 | example: '',
22 | description: 'rsa公钥',
23 | },
24 | },
25 | };
26 |
27 | module.exports = {
28 | ...body,
29 | configurationPutBodyReq: {
30 | ...body.configurationId,
31 | ...body.configurationBodyReq,
32 | },
33 | configurationDelBodyReq: {
34 | ids: {
35 | type: 'array',
36 | required: true,
37 | itemType: 'number',
38 | description: 'ids',
39 | example: [1, 2],
40 | },
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/app/contract/request/department.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | departmentId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | departmentBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | max: 60,
12 | trim: true,
13 | example: '研发部',
14 | description: '部门名称',
15 | },
16 | owner_id: {
17 | type: 'number',
18 | required: false,
19 | min: 0,
20 | example: 0,
21 | description: '拥有者ID',
22 | },
23 | parent_id: {
24 | type: 'number',
25 | required: true,
26 | min: 0,
27 | example: 0,
28 | description: '父ID',
29 | },
30 | sort: {
31 | type: 'number',
32 | required: false,
33 | max: 999999999,
34 | example: 0,
35 | description: '排序,越大越靠前',
36 | },
37 | },
38 | };
39 |
40 | module.exports = {
41 | ...body,
42 | departmentPutBodyReq: {
43 | ...body.departmentId,
44 | ...body.departmentBodyReq,
45 | },
46 | departmentDelBodyReq: {
47 | ids: {
48 | type: 'array',
49 | required: true,
50 | itemType: 'number',
51 | description: 'ids',
52 | example: [1, 2],
53 | },
54 | },
55 | };
56 |
--------------------------------------------------------------------------------
/app/contract/request/invite.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | inviteId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | inviteBodyReq: {
8 | uuid: {
9 | type: 'string',
10 | required: false,
11 | example: '00000000-0000-0000-0000-000000000000',
12 | description: '随机字符串ID',
13 | },
14 | actor_id: {
15 | type: 'number',
16 | required: false,
17 | min: 1,
18 | example: '1',
19 | description: '发起者ID',
20 | },
21 | receiver_id: {
22 | type: 'number',
23 | required: false,
24 | min: 1,
25 | example: '1',
26 | description: '接受者ID',
27 | },
28 | is_accept: {
29 | type: 'number',
30 | required: false,
31 | min: 0,
32 | max: 1,
33 | example: '0',
34 | description: '是否已接受.1为true,0为false',
35 | },
36 | group: {
37 | type: 'string',
38 | required: true,
39 | min: 1,
40 | max: 20,
41 | trim: true,
42 | example: 'Projects',
43 | description: '邀请加入群体的类型',
44 | },
45 | group_id: {
46 | type: 'number',
47 | required: true,
48 | min: 1,
49 | example: '1',
50 | description: '邀请加入群体的ID',
51 | },
52 | expires: {
53 | type: 'string',
54 | required: false,
55 | example: 'YYYY-MM-DD HH:mm:ss',
56 | description: '到期时间',
57 | },
58 | },
59 | };
60 |
61 | module.exports = {
62 | ...body,
63 | invitePutBodyReq: {
64 | ...body.inviteId,
65 | ...body.inviteBodyReq,
66 | },
67 | inviteDelBodyReq: {
68 | ids: {
69 | type: 'array',
70 | required: true,
71 | itemType: 'number',
72 | description: 'ids',
73 | example: [1, 2],
74 | },
75 | },
76 | inviteUUID: {
77 | uuid: body.inviteBodyReq.uuid,
78 | },
79 | };
80 |
--------------------------------------------------------------------------------
/app/contract/request/message.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | messageId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | messageBodyReq: {
8 | actor_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '发起者ID',
14 | },
15 | receiver_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '接受者ID',
21 | },
22 | content: {
23 | type: 'string',
24 | required: true,
25 | min: 1,
26 | max: 700,
27 | trim: true,
28 | example: '1',
29 | description: '内容',
30 | },
31 | is_read: {
32 | type: 'number',
33 | required: false,
34 | min: 0,
35 | max: 1,
36 | example: '0',
37 | description: '是否为已读.1为true,0为false',
38 | },
39 | type: {
40 | type: 'string',
41 | required: true,
42 | min: 1,
43 | max: 30,
44 | trim: true,
45 | example: 'inform',
46 | description: '类型',
47 | },
48 | url: {
49 | type: 'string',
50 | required: false,
51 | min: 1,
52 | max: 255,
53 | trim: true,
54 | example: '',
55 | description: '跳转路径',
56 | },
57 | },
58 | };
59 |
60 | module.exports = {
61 | ...body,
62 | messagePutBodyReq: {
63 | ...body.messageId,
64 | ...body.messageBodyReq,
65 | },
66 | messageDelBodyReq: {
67 | ids: {
68 | type: 'array',
69 | required: true,
70 | itemType: 'number',
71 | description: 'ids',
72 | example: [1, 2],
73 | },
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/app/contract/request/operation_log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | operation_logId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | operation_logBodyReq: {
8 | operator_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '发起者ID',
14 | },
15 | operator_username: {
16 | type: 'string',
17 | required: true,
18 | min: 2,
19 | max: 60,
20 | trim: true,
21 | example: 'Imfdj',
22 | description: '发起者用户名',
23 | },
24 | status: {
25 | type: 'string',
26 | required: true,
27 | min: 1,
28 | max: 15,
29 | trim: true,
30 | example: '200',
31 | description: '请求返回状态 ',
32 | },
33 | ip: {
34 | type: 'string',
35 | required: true,
36 | min: 1,
37 | max: 100,
38 | trim: true,
39 | example: '127.0.0.1',
40 | description: '请求ip地址',
41 | },
42 | method: {
43 | type: 'string',
44 | required: true,
45 | min: 1,
46 | max: 15,
47 | trim: true,
48 | example: 'GET',
49 | description: '请求方法',
50 | },
51 | url: {
52 | type: 'string',
53 | required: true,
54 | min: 1,
55 | max: 255,
56 | trim: true,
57 | example: '',
58 | description: '请求路径',
59 | },
60 | params: {
61 | type: 'string',
62 | required: true,
63 | min: 1,
64 | max: 3000,
65 | trim: true,
66 | example: '',
67 | description: '请求参数',
68 | },
69 | },
70 | };
71 |
72 | module.exports = {
73 | ...body,
74 | operation_logPutBodyReq: {
75 | ...body.operation_logId,
76 | ...body.operation_logBodyReq,
77 | },
78 | operation_logDelBodyReq: {
79 | ids: {
80 | type: 'array',
81 | required: true,
82 | itemType: 'number',
83 | description: 'ids',
84 | example: [1, 2],
85 | },
86 | },
87 | };
88 |
--------------------------------------------------------------------------------
/app/contract/request/permission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | permissionId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | permissionBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | max: 60,
12 | trim: true,
13 | example: '新增',
14 | description: '资源名',
15 | },
16 | mark: {
17 | type: 'string',
18 | required: true,
19 | max: 60,
20 | trim: true,
21 | example: 'mark',
22 | description: '标识码',
23 | },
24 | mark_name: {
25 | type: 'string',
26 | required: true,
27 | max: 60,
28 | trim: true,
29 | example: 'mark_name',
30 | description: '标识码中文名',
31 | },
32 | url: {
33 | type: 'string',
34 | required: true,
35 | max: 255,
36 | trim: true,
37 | example: '/',
38 | description: '路径',
39 | },
40 | action: {
41 | type: 'string',
42 | required: true,
43 | max: 60,
44 | trim: true,
45 | example: 'post',
46 | description: '动作',
47 | },
48 | description: {
49 | type: 'string',
50 | required: false,
51 | max: 255,
52 | trim: true,
53 | example: '描述',
54 | description: '描述',
55 | },
56 | state: {
57 | type: 'number',
58 | required: false,
59 | min: 0,
60 | max: 1,
61 | example: 1,
62 | description: '状态.1为true,0为false',
63 | },
64 | authentication: {
65 | type: 'number',
66 | required: false,
67 | min: 0,
68 | max: 1,
69 | example: 1,
70 | description: '是否需要认证.1为true,0为false',
71 | },
72 | authorization: {
73 | type: 'number',
74 | required: false,
75 | min: 0,
76 | max: 1,
77 | example: 1,
78 | description: '是否需要授权.1为true,0为false',
79 | },
80 | },
81 | };
82 |
83 | module.exports = {
84 | ...body,
85 | permissionPutBodyReq: {
86 | ...body.permissionId,
87 | ...body.permissionBodyReq,
88 | },
89 | permissionDelBodyReq: {
90 | ids: {
91 | type: 'array',
92 | required: true,
93 | itemType: 'number',
94 | description: 'ids',
95 | example: [1, 2],
96 | },
97 | },
98 | };
99 |
--------------------------------------------------------------------------------
/app/contract/request/project_template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | project_templateId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | project_templateBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 2,
12 | max: 255,
13 | trim: true,
14 | example: '项目模板名称',
15 | description: '项目模板名称',
16 | },
17 | cover: {
18 | type: 'string',
19 | required: false,
20 | max: 255,
21 | trim: true,
22 | example: '',
23 | description: '项目模板封面',
24 | },
25 | is_custom: {
26 | type: 'number',
27 | required: false,
28 | min: 0,
29 | max: 1,
30 | example: '1',
31 | description: '是否为自定义模板.1为true,0为false',
32 | },
33 | intro: {
34 | type: 'string',
35 | required: false,
36 | max: 255,
37 | trim: true,
38 | example: '简介',
39 | description: '简介',
40 | },
41 | },
42 | };
43 |
44 | module.exports = {
45 | ...body,
46 | project_templatePutBodyReq: {
47 | ...body.project_templateId,
48 | ...body.project_templateBodyReq,
49 | },
50 | project_templateDelBodyReq: {
51 | ids: {
52 | type: 'array',
53 | required: true,
54 | itemType: 'number',
55 | description: 'ids',
56 | example: [1, 2],
57 | },
58 | },
59 | };
60 |
--------------------------------------------------------------------------------
/app/contract/request/project_template_task.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | project_template_taskId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | project_template_taskBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 2,
12 | max: 255,
13 | trim: true,
14 | example: '项目模板任务名称',
15 | description: '项目模板任务名称',
16 | },
17 | project_template_id: {
18 | type: 'number',
19 | required: true,
20 | min: 1,
21 | example: '1',
22 | description: '所属项目模板ID',
23 | },
24 | sort: {
25 | type: 'number',
26 | required: false,
27 | max: 999999999,
28 | example: '0',
29 | description: '排序,越大越靠前',
30 | },
31 | },
32 | };
33 |
34 | module.exports = {
35 | ...body,
36 | project_template_taskPutBodyReq: {
37 | ...body.project_template_taskId,
38 | ...body.project_template_taskBodyReq,
39 | },
40 | project_template_taskDelBodyReq: {
41 | ids: {
42 | type: 'array',
43 | required: true,
44 | itemType: 'number',
45 | description: 'ids',
46 | example: [1, 2],
47 | },
48 | },
49 | };
50 |
--------------------------------------------------------------------------------
/app/contract/request/role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | roleId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | roleBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 50,
13 | description: '角色姓名',
14 | },
15 | },
16 | };
17 |
18 | module.exports = {
19 | ...body,
20 | rolePutBodyReq: {
21 | ...body.roleId,
22 | ...body.roleBodyReq,
23 | },
24 | roleDelBodyReq: {
25 | ids: {
26 | type: 'array',
27 | required: true,
28 | itemType: 'number',
29 | description: 'ids',
30 | example: [1, 2],
31 | },
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/app/contract/request/role_menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | role_menuId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | role_menuBodyReq: {
8 | role_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: 1,
13 | description: '角色ID',
14 | },
15 | menu_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: 1,
20 | description: '菜单ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | role_menuPutBodyReq: {
28 | ...body.role_menuId,
29 | ...body.role_menuBodyReq,
30 | },
31 | role_menuDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/role_permission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | role_permissionId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | role_permissionBodyReq: {
8 | role_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: 1,
13 | description: '角色ID',
14 | },
15 | permission_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: 1,
20 | description: '资源ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | role_permissionPutBodyReq: {
28 | ...body.role_permissionId,
29 | ...body.role_permissionBodyReq,
30 | },
31 | role_permissionDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/task_list.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_listId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_listBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 30,
13 | trim: true,
14 | example: '任务列表名称',
15 | description: '任务列表名称',
16 | },
17 | project_id: {
18 | type: 'number',
19 | required: true,
20 | min: 1,
21 | example: '1',
22 | description: '所属项目ID',
23 | },
24 | sort: {
25 | type: 'number',
26 | required: false,
27 | max: 999999999,
28 | example: '0',
29 | description: '排序,越大越靠前',
30 | },
31 | },
32 | };
33 |
34 | module.exports = {
35 | ...body,
36 | task_listPutBodyReq: {
37 | ...body.task_listId,
38 | ...body.task_listBodyReq,
39 | },
40 | task_listDelBodyReq: {
41 | ids: {
42 | type: 'array',
43 | required: true,
44 | itemType: 'number',
45 | description: 'ids',
46 | example: [1, 2],
47 | },
48 | },
49 | };
50 |
--------------------------------------------------------------------------------
/app/contract/request/task_log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_logId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_logBodyReq: {
8 | remark: {
9 | type: 'string',
10 | required: false,
11 | min: 1,
12 | max: 255,
13 | trim: true,
14 | example: '日志说明',
15 | description: '日志说明',
16 | },
17 | task_id: {
18 | type: 'number',
19 | required: true,
20 | min: 1,
21 | example: '1',
22 | description: '任务ID',
23 | },
24 | project_id: {
25 | type: 'number',
26 | required: true,
27 | min: 1,
28 | example: '1',
29 | description: '项目ID',
30 | },
31 | operator_id: {
32 | type: 'number',
33 | required: true,
34 | min: 1,
35 | example: '1',
36 | description: '操作人ID',
37 | },
38 | icon: {
39 | type: 'string',
40 | required: false,
41 | min: 1,
42 | max: 60,
43 | trim: true,
44 | example: '图标',
45 | description: '图标',
46 | },
47 | content: {
48 | type: 'string',
49 | required: false,
50 | trim: true,
51 | example: '',
52 | description: '日志内容',
53 | },
54 | is_comment: {
55 | type: 'number',
56 | required: false,
57 | min: 0,
58 | max: 1,
59 | example: '0',
60 | description: '是否为评论.1为true,0为false',
61 | },
62 | type: {
63 | type: 'string',
64 | required: true,
65 | min: 1,
66 | max: 40,
67 | trim: true,
68 | example: 'status',
69 | description: '类型',
70 | },
71 | },
72 | };
73 |
74 | module.exports = {
75 | ...body,
76 | task_logPutBodyReq: {
77 | ...body.task_logId,
78 | ...body.task_logBodyReq,
79 | },
80 | task_logDelBodyReq: {
81 | ids: {
82 | type: 'array',
83 | required: true,
84 | itemType: 'number',
85 | description: 'ids',
86 | example: [1, 2],
87 | },
88 | },
89 | };
90 |
--------------------------------------------------------------------------------
/app/contract/request/task_priority.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_priorityId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_priorityBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 30,
13 | trim: true,
14 | example: '优先级名称',
15 | description: '优先级名称',
16 | },
17 | color: {
18 | type: 'string',
19 | required: true,
20 | min: 1,
21 | max: 10,
22 | trim: true,
23 | example: '颜色',
24 | description: '颜色',
25 | },
26 | sort: {
27 | type: 'number',
28 | required: false,
29 | max: 999999999,
30 | example: '0',
31 | description: '排序,越大越靠前',
32 | },
33 | },
34 | };
35 |
36 | module.exports = {
37 | ...body,
38 | task_priorityPutBodyReq: {
39 | ...body.task_priorityId,
40 | ...body.task_priorityBodyReq,
41 | },
42 | task_priorityDelBodyReq: {
43 | ids: {
44 | type: 'array',
45 | required: true,
46 | itemType: 'number',
47 | description: 'ids',
48 | example: [1, 2],
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/app/contract/request/task_state.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_stateId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_stateBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 50,
13 | trim: true,
14 | example: '状态名称',
15 | description: '状态名称',
16 | },
17 | color: {
18 | type: 'string',
19 | required: false,
20 | min: 1,
21 | max: 16,
22 | trim: true,
23 | example: '颜色',
24 | description: '颜色',
25 | },
26 | icon: {
27 | type: 'string',
28 | required: false,
29 | min: 1,
30 | max: 50,
31 | trim: true,
32 | example: '图标',
33 | description: '图标',
34 | },
35 | sort: {
36 | type: 'number',
37 | required: false,
38 | max: 999999999,
39 | example: '0',
40 | description: '排序,越大越靠前',
41 | },
42 | },
43 | };
44 |
45 | module.exports = {
46 | ...body,
47 | task_statePutBodyReq: {
48 | ...body.task_stateId,
49 | ...body.task_stateBodyReq,
50 | },
51 | task_stateDelBodyReq: {
52 | ids: {
53 | type: 'array',
54 | required: true,
55 | itemType: 'number',
56 | description: 'ids',
57 | example: [1, 2],
58 | },
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/app/contract/request/task_tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_tagId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_tagBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 100,
13 | trim: true,
14 | example: '标签名称',
15 | description: '标签名称',
16 | },
17 | color: {
18 | type: 'string',
19 | required: true,
20 | min: 1,
21 | max: 30,
22 | trim: true,
23 | example: '颜色',
24 | description: '颜色',
25 | },
26 | project_id: {
27 | type: 'number',
28 | required: true,
29 | min: 1,
30 | example: '1',
31 | description: '所属项目ID',
32 | },
33 | },
34 | };
35 |
36 | module.exports = {
37 | ...body,
38 | task_tagPutBodyReq: {
39 | ...body.task_tagId,
40 | ...body.task_tagBodyReq,
41 | },
42 | task_tagDelBodyReq: {
43 | ids: {
44 | type: 'array',
45 | required: true,
46 | itemType: 'number',
47 | description: 'ids',
48 | example: [1, 2],
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/app/contract/request/task_task_tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_task_tagId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_task_tagBodyReq: {
8 | task_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '任务ID',
14 | },
15 | task_tag_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '任务标签ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | task_task_tagPutBodyReq: {
28 | ...body.task_task_tagId,
29 | ...body.task_task_tagBodyReq,
30 | },
31 | task_task_tagDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/task_type.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_typeId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_typeBodyReq: {
8 | name: {
9 | type: 'string',
10 | required: true,
11 | min: 1,
12 | max: 50,
13 | trim: true,
14 | example: '类型名称',
15 | description: '类型名称',
16 | },
17 | color: {
18 | type: 'string',
19 | required: false,
20 | min: 1,
21 | max: 16,
22 | trim: true,
23 | example: '颜色',
24 | description: '颜色',
25 | },
26 | icon: {
27 | type: 'string',
28 | required: false,
29 | min: 1,
30 | max: 50,
31 | trim: true,
32 | example: '图标',
33 | description: '图标',
34 | },
35 | sort: {
36 | type: 'number',
37 | required: false,
38 | max: 999999999,
39 | example: '0',
40 | description: '排序,越大越靠前',
41 | },
42 | },
43 | };
44 |
45 | module.exports = {
46 | ...body,
47 | task_typePutBodyReq: {
48 | ...body.task_typeId,
49 | ...body.task_typeBodyReq,
50 | },
51 | task_typeDelBodyReq: {
52 | ids: {
53 | type: 'array',
54 | required: true,
55 | itemType: 'number',
56 | description: 'ids',
57 | example: [1, 2],
58 | },
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/app/contract/request/task_working_hour.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | task_working_hourId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | task_working_hourBodyReq: {
8 | description: {
9 | type: 'string',
10 | required: false,
11 | min: 1,
12 | max: 255,
13 | trim: true,
14 | example: '工作进展',
15 | description: '工作进展',
16 | },
17 | work_time: {
18 | type: 'number',
19 | required: true,
20 | min: 0,
21 | example: '1',
22 | description: '实际工时数',
23 | },
24 | task_id: {
25 | type: 'number',
26 | required: true,
27 | min: 1,
28 | example: '1',
29 | description: '任务ID',
30 | },
31 | project_id: {
32 | type: 'number',
33 | required: true,
34 | min: 1,
35 | example: '1',
36 | description: '所属项目ID',
37 | },
38 | executor_id: {
39 | type: 'number',
40 | required: true,
41 | min: 1,
42 | example: '1',
43 | description: '执行者ID',
44 | },
45 | start_date: {
46 | type: 'string',
47 | required: true,
48 | description: '开始时间',
49 | },
50 | end_date: {
51 | type: 'string',
52 | required: false,
53 | description: '结束时间',
54 | },
55 | },
56 | };
57 |
58 | module.exports = {
59 | ...body,
60 | task_working_hourPutBodyReq: {
61 | ...body.task_working_hourId,
62 | ...body.task_working_hourBodyReq,
63 | },
64 | task_working_hourDelBodyReq: {
65 | ids: {
66 | type: 'array',
67 | required: true,
68 | itemType: 'number',
69 | description: 'ids',
70 | example: [1, 2],
71 | },
72 | },
73 | };
74 |
--------------------------------------------------------------------------------
/app/contract/request/user_project.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | user_projectId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | user_projectBodyReq: {
8 | user_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '用户ID',
14 | },
15 | project_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '项目ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | user_projectPutBodyReq: {
28 | ...body.user_projectId,
29 | ...body.user_projectBodyReq,
30 | },
31 | user_projectDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/user_project_collect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | user_project_collectId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | user_project_collectBodyReq: {
8 | user_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '用户ID',
14 | },
15 | project_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '项目ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | user_project_collectPutBodyReq: {
28 | ...body.user_project_collectId,
29 | ...body.user_project_collectBodyReq,
30 | },
31 | user_project_collectDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/user_role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | user_roleId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | user_roleBodyReq: {
8 | user_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: 1,
13 | description: '用户ID',
14 | },
15 | role_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: 1,
20 | description: '角色ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | user_rolePutBodyReq: {
28 | ...body.user_roleId,
29 | ...body.user_roleBodyReq,
30 | },
31 | user_roleDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/user_task.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | user_taskId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | user_taskBodyReq: {
8 | user_id: {
9 | type: 'number',
10 | required: true,
11 | min: 1,
12 | example: '1',
13 | description: '用户ID',
14 | },
15 | task_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '任务ID',
21 | },
22 | },
23 | };
24 |
25 | module.exports = {
26 | ...body,
27 | user_taskPutBodyReq: {
28 | ...body.user_taskId,
29 | ...body.user_taskBodyReq,
30 | },
31 | user_taskDelBodyReq: {
32 | ids: {
33 | type: 'array',
34 | required: true,
35 | itemType: 'number',
36 | description: 'ids',
37 | example: [1, 2],
38 | },
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/app/contract/request/user_task_like.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | user_task_likeId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | user_task_likeBodyReq: {
8 | user_id: {
9 | type: 'number',
10 | required: false,
11 | min: 1,
12 | example: '1',
13 | description: '用户ID',
14 | },
15 | task_id: {
16 | type: 'number',
17 | required: true,
18 | min: 1,
19 | example: '1',
20 | description: '任务ID',
21 | },
22 | project_id: {
23 | type: 'number',
24 | required: true,
25 | min: 1,
26 | example: '1',
27 | description: '项目ID',
28 | },
29 | },
30 | };
31 |
32 | module.exports = {
33 | ...body,
34 | user_task_likePutBodyReq: {
35 | ...body.user_task_likeId,
36 | ...body.user_task_likeBodyReq,
37 | },
38 | user_task_likeDelBodyReq: {
39 | ids: {
40 | type: 'array',
41 | required: true,
42 | itemType: 'number',
43 | description: 'ids',
44 | example: [1, 2],
45 | },
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/app/contract/request/verification_code.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | verification_codeId: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | verification_codeBodyReq: {
8 | code: {
9 | type: 'string',
10 | required: false,
11 | max: 60,
12 | trim: true,
13 | example: '000000',
14 | description: '验证码',
15 | },
16 | target: {
17 | type: 'string',
18 | required: true,
19 | max: 60,
20 | trim: true,
21 | example: '333@qq.com',
22 | description: '验证码接受者',
23 | },
24 | type: {
25 | type: 'number',
26 | required: true,
27 | min: 0,
28 | max: 127,
29 | example: 1,
30 | description: '类型.1为邮箱验证码,2为手机验证码',
31 | },
32 | available: {
33 | type: 'number',
34 | required: false,
35 | min: 0,
36 | max: 1,
37 | example: 1,
38 | description: '是否可用.1为true,0为false',
39 | },
40 | },
41 | };
42 |
43 | module.exports = {
44 | ...body,
45 | verification_codePutBodyReq: {
46 | ...body.verification_codeId,
47 | ...body.verification_codeBodyReq,
48 | },
49 | verification_codeDelBodyReq: {
50 | ids: {
51 | type: 'array',
52 | required: true,
53 | itemType: 'number',
54 | description: 'ids',
55 | example: [1, 2],
56 | },
57 | },
58 | };
59 |
--------------------------------------------------------------------------------
/app/contract/response/user.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | // queryUserResponse: {
5 | // users: { type: 'array', itemType: 'user' },
6 | // pageNo: { type: 'integer' },
7 | // pageSize: { type: 'integer' },
8 | // totalCount: { type: 'integer' },
9 | // hasNextPage: { type: 'boolean' },
10 | // },
11 | };
12 |
--------------------------------------------------------------------------------
/app/controller/v1/configurations.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller;
4 | const NodeRSA = require('node-rsa');
5 |
6 | /**
7 | * @controller 配置 configuration
8 | */
9 |
10 | class RoleController extends Controller {
11 | /**
12 | * @apikey
13 | * @summary 获取公钥 配置
14 | * @description 获取公钥 配置
15 | * @router get /api/v1/configurations/public_key
16 | */
17 | async findRsaPublicKey() {
18 | const { ctx, service } = this;
19 | const res = await service.configurations.findRsaPublicKey(1);
20 | res ? ctx.helper.body.SUCCESS({ ctx, res }) : ctx.helper.body.NOT_FOUND({ ctx });
21 | }
22 |
23 | /**
24 | * @apikey
25 | * @summary 更新 配置
26 | * @description 更新 配置
27 | * @router put /api/v1/configurations
28 | * @request body configurationPutBodyReq
29 | */
30 | async update() {
31 | const { ctx, service } = this;
32 | const key = new NodeRSA({ b: 512 });
33 | key.setOptions({ encryptionScheme: 'pkcs1' });
34 | const rsa_public_key = key.exportKey('public');
35 | const rsa_private_key = key.exportKey('private');
36 | const body = {
37 | ...ctx.request.body,
38 | id: 1,
39 | rsa_private_key,
40 | rsa_public_key,
41 | };
42 | ctx.validate(ctx.rule.configurationPutBodyReq, body);
43 | const res = await service.configurations.update(body);
44 | res && res[0] !== 0 ? ctx.helper.body.CREATED_UPDATE({ ctx }) : ctx.helper.body.NOT_FOUND({ ctx });
45 | }
46 | }
47 |
48 | module.exports = RoleController;
49 |
--------------------------------------------------------------------------------
/app/controller/v1/user_project_collects.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller;
4 |
5 | /**
6 | * @controller 用户-项目-收藏关系表 user_project_collect
7 | */
8 |
9 | class RoleController extends Controller {
10 | /**
11 | * @apikey
12 | * @summary 创建或删除 用户-项目-收藏关系表
13 | * @description 创建或删除 用户-项目-收藏关系表
14 | * @router post /api/v1/user_project_collects/change
15 | * @request body user_project_collectBodyReq
16 | */
17 | async change() {
18 | const { ctx } = this;
19 | ctx.validate(ctx.rule.user_project_collectBodyReq, ctx.request.body);
20 | await ctx.service.userProjectCollects.change(ctx.request.body);
21 | ctx.helper.body.CREATED_UPDATE({ ctx });
22 | }
23 | }
24 |
25 | module.exports = RoleController;
26 |
--------------------------------------------------------------------------------
/app/controller/v1/user_task_likes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller;
4 |
5 | /**
6 | * @controller 用户-任务-点赞关系表 user_task_like
7 | */
8 |
9 | class RoleController extends Controller {
10 | /**
11 | * @apikey
12 | * @summary 创建或删除 用户-任务-点赞关系表
13 | * @description 创建或删除 用户-任务-点赞关系表
14 | * @router post /api/v1/user_task_likes/change
15 | * @request body user_task_likeBodyReq
16 | */
17 | async change() {
18 | const { ctx } = this;
19 | ctx.validate(ctx.rule.user_task_likeBodyReq, ctx.request.body);
20 | await ctx.service.userTaskLikes.change(ctx.request.body);
21 | ctx.helper.body.CREATED_UPDATE({ ctx });
22 | }
23 | }
24 |
25 | module.exports = RoleController;
26 |
--------------------------------------------------------------------------------
/app/extend/RESTfulHTTPStatus.rec:
--------------------------------------------------------------------------------
1 | 200 SUCCESS - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
2 | 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
3 | 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
4 | 204 NO CONTENT - [DELETE]:用户删除数据成功。
5 | 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
6 | 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
7 | 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
8 | 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
9 | 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
10 | 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
11 | 422 Unprocesable entity - [*] 参数发生验证错误。
12 | 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
13 |
14 | /**
15 | * 在接口处理发生错误的时候,如果是客户端请求参数导致的错误,我们会返回 4xx 状态码,
16 | * 如果是服务端自身的处理逻辑错误,我们会返回 5xx 状态码。
17 | * 所有的异常对象都是对这个异常状态的描述,其中 error 字段是错误的描述,detail 字段(可选)是导致错误的详细原因。
18 | */
19 |
20 | eg----:
21 | 533 INTERNAL SERVER ERROR - [*]:keystore 不存在
22 | 534 INTERNAL SERVER ERROR - [*]:keystore 已过期
23 |
--------------------------------------------------------------------------------
/app/io/controller/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller;
4 |
5 | class DefaultController extends Controller {
6 | async ping() {
7 | const { ctx, app } = this;
8 | console.log('ping');
9 | const message = ctx.args[0];
10 | await ctx.socket.emit('res', `Hi! I've got your message: ${ message }`);
11 | }
12 |
13 | async ack() {
14 | const { ctx, app } = this;
15 | console.log('ack');
16 | const message = ctx.args[0];
17 | console.log(message);
18 | await app.redis.del(ctx.helper.redisKeys.socketBaseSocketId(message.id));
19 | }
20 |
21 | }
22 |
23 | module.exports = DefaultController;
24 |
--------------------------------------------------------------------------------
/app/io/middleware/packet.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = app => {
4 | return async (ctx, next) => {
5 | const {
6 | packet,
7 | app: { redis },
8 | } = ctx;
9 | // ctx.socket.emit('packet', 'packet received!');
10 | // if (packet && packet[0] === 'ack') {
11 | // await redis.del('12341234');
12 | // }
13 | await next();
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/app/middleware/error_handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = (option, app) => {
4 | return async function(ctx, next) {
5 | try {
6 | await next();
7 | } catch (err) {
8 | // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
9 | app.emit('error', err, this);
10 | const status = err.status || 500;
11 | // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
12 | const error = status === 500 && app.config.env === 'prod' ? 'Internal Server Error' : err.message;
13 | // 从 error 对象上读出各个属性,设置到响应中
14 | ctx.body = {
15 | // code: status, // 服务端自身的处理逻辑错误(包含框架错误500 及 自定义业务逻辑错误533开始 ) 客户端请求参数导致的错误(4xx开始),设置不同的状态码
16 | error,
17 | };
18 | ctx.status = status;
19 | /**
20 | * 参数错误,mysql返回的错误处理
21 | */
22 | if (err.parent && err.parent.errno) {
23 | const res = {
24 | error,
25 | detail: err.errors,
26 | };
27 | ctx.helper.body.INVALID_REQUEST({ ctx, res, code: err.parent.errno });
28 | }
29 | if (status === 422) {
30 | const res = {
31 | error,
32 | detail: err.errors,
33 | };
34 | ctx.helper.body.VALIDATION_FAILED({ ctx, res });
35 | } else {
36 | app.logger.errorAndSentry(err);
37 | }
38 | }
39 | };
40 | };
41 |
--------------------------------------------------------------------------------
/app/middleware/log_handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = (option, app) => {
4 | return async function(ctx, next) {
5 | try {
6 | await next();
7 | // 如果是开发环境或者是生产环境且非GET 存储操作日志
8 | if (
9 | (app.config.env === 'prod' || app.config.env === 'local') &&
10 | ctx.request.method.toLocaleUpperCase() !== 'GET' &&
11 | ctx.currentRequestData &&
12 | ctx.currentRequestData.userInfo
13 | ) {
14 | const payload = {
15 | operator_id: ctx.currentRequestData.userInfo.id,
16 | operator_username: ctx.currentRequestData.userInfo.username,
17 | method: ctx.request.method,
18 | url: ctx.request.url.split('?')[0],
19 | ip: ctx.request.ip,
20 | status: ctx.response.status,
21 | params: JSON.stringify(ctx.request.body),
22 | };
23 | ctx.service.operationLogs.create(payload);
24 | }
25 | } catch (err) {
26 | app.logger.errorAndSentry(err);
27 | }
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/app/model/configurations.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const configuration = app.model.define(
6 | 'configurations',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | rsa_private_key: Sequelize.STRING(1000),
10 | rsa_public_key: Sequelize.STRING(1000),
11 | },
12 | {}
13 | );
14 | configuration.associate = function(models) {
15 | // associations can be defined here
16 | };
17 | return configuration;
18 | };
19 |
--------------------------------------------------------------------------------
/app/model/departments.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const department = app.model.define(
6 | 'departments',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(60),
10 | owner_id: Sequelize.INTEGER(11),
11 | parent_id: Sequelize.INTEGER(11),
12 | sort: Sequelize.INTEGER(11),
13 | },
14 | {}
15 | );
16 | department.associate = function(models) {
17 | // associations can be defined here
18 | };
19 | return department;
20 | };
21 |
--------------------------------------------------------------------------------
/app/model/invites.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const invite = app.model.define(
6 | 'invites',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | uuid: Sequelize.STRING(36),
10 | actor_id: Sequelize.INTEGER(11),
11 | receiver_id: Sequelize.INTEGER(11),
12 | is_accept: Sequelize.TINYINT(1),
13 | group: Sequelize.STRING(20),
14 | group_id: Sequelize.INTEGER(11),
15 | expires: Sequelize.DATE,
16 | },
17 | {}
18 | );
19 | invite.addHook('afterUpdate', async (invite, options) => {
20 | const ctx = await app.createAnonymousContext();
21 | // 如果此次将is_accept从0修改为1,则视为一次接受修改,且存在接受者id, 则为发起者创建一次站内信,说明邀请已被接受
22 | if (invite.dataValues.is_accept === 1 && invite._previousDataValues.is_accept === 0 && invite.receiver_id) {
23 | if (invite.group === 'Projects') {
24 | const project = await ctx.model.Projects.findOne({ where: { id: invite.group_id } });
25 | ctx.model.Messages.create({
26 | actor_id: invite.receiver_id,
27 | receiver_id: invite.actor_id,
28 | content: `已接受你的邀请,加入了项目 ${project.name}`,
29 | type: 'personal',
30 | url: `/projectManagement/Project/${invite.group_id}`,
31 | });
32 | }
33 | }
34 | });
35 |
36 | invite.associate = function(models) {
37 | // associations can be defined here
38 | invite.hasOne(app.model.Users, { foreignKey: 'id', sourceKey: 'actor_id', as: 'actor' });
39 | };
40 | return invite;
41 | };
42 |
--------------------------------------------------------------------------------
/app/model/menus.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const menu = app.model.define(
6 | 'menus',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(60),
10 | path: Sequelize.STRING(100),
11 | parent_id: Sequelize.INTEGER(11),
12 | icon: Sequelize.STRING,
13 | title: Sequelize.STRING,
14 | hidden: Sequelize.TINYINT(1),
15 | always_show: Sequelize.TINYINT(1),
16 | keep_alive: Sequelize.TINYINT(1),
17 | target: Sequelize.STRING(20),
18 | component: Sequelize.STRING(100),
19 | redirect: Sequelize.STRING,
20 | sort: Sequelize.INTEGER(11),
21 | },
22 | {}
23 | );
24 | menu.associate = function(models) {
25 | // associations can be defined here
26 | };
27 | return menu;
28 | };
29 |
--------------------------------------------------------------------------------
/app/model/messages.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const message = app.model.define(
6 | 'messages',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | actor_id: Sequelize.INTEGER(11),
10 | receiver_id: Sequelize.INTEGER(11),
11 | content: Sequelize.TEXT,
12 | is_read: Sequelize.TINYINT(1),
13 | type: Sequelize.STRING(30),
14 | url: Sequelize.STRING(255),
15 | },
16 | {}
17 | );
18 |
19 | message.addHook('afterCreate', async (message, options) => {
20 | const ctx = await app.createAnonymousContext();
21 | // 发送socket消息
22 | const newMessage = Object.assign(
23 | {
24 | is_read: 0,
25 | url: '',
26 | },
27 | message.dataValues
28 | );
29 | newMessage.actor = await ctx.model.Users.findOne({
30 | where: {
31 | id: message.actor_id,
32 | },
33 | });
34 | const { receiver_id } = message;
35 | ctx.helper.sendMessageToSocket(receiver_id, newMessage, 'create:message');
36 | });
37 |
38 | message.addHook('afterUpdate', async (message, options) => {
39 | const ctx = await app.createAnonymousContext();
40 | const { receiver_id } = message;
41 | ctx.helper.sendMessageToSocket(receiver_id, message, 'update:message');
42 | });
43 |
44 | message.associate = function(models) {
45 | // associations can be defined here
46 | message.hasOne(app.model.Users, { foreignKey: 'id', sourceKey: 'actor_id', as: 'actor' });
47 | };
48 | return message;
49 | };
50 |
--------------------------------------------------------------------------------
/app/model/operation_logs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const operation_log = app.model.define(
6 | 'operation_logs',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | operator_id: Sequelize.INTEGER(11),
10 | operator_username: Sequelize.STRING(60),
11 | status: Sequelize.STRING(15),
12 | ip: Sequelize.STRING(100),
13 | method: Sequelize.STRING(15),
14 | url: Sequelize.STRING(255),
15 | params: Sequelize.TEXT,
16 | },
17 | {}
18 | );
19 | operation_log.associate = function(models) {
20 | // associations can be defined here
21 | };
22 | return operation_log;
23 | };
24 |
--------------------------------------------------------------------------------
/app/model/permissions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const { Sequelize } = app;
4 | const ctx = app.createAnonymousContext();
5 |
6 | const permission = app.model.define(
7 | 'permissions',
8 | {
9 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
10 | name: Sequelize.STRING(60),
11 | mark: Sequelize.STRING(60),
12 | mark_name: Sequelize.STRING(60),
13 | url: Sequelize.STRING(255),
14 | action: Sequelize.STRING(60),
15 | description: Sequelize.STRING(255),
16 | state: Sequelize.TINYINT(1),
17 | authentication: Sequelize.TINYINT(1),
18 | authorization: Sequelize.TINYINT(1),
19 | },
20 | {}
21 | );
22 | permission.associate = function(models) {
23 | // associations can be defined here
24 | };
25 | permission.addHook('afterCreate', (permission, options) => {
26 | const { dataValues } = permission;
27 | app.redis.hmset(ctx.helper.redisKeys.permissionsBaseActionUrl(dataValues.action, dataValues.url), dataValues);
28 | });
29 | permission.afterBulkUpdate(async options => {
30 | const { attributes } = options;
31 | app.redis.hmset(ctx.helper.redisKeys.permissionsBaseActionUrl(attributes.action, attributes.url), attributes);
32 | });
33 | permission.afterBulkDestroy(async options => {
34 | options.delData.forEach(v => {
35 | app.redis.del(ctx.helper.redisKeys.permissionsBaseActionUrl(v.dataValues.action, v.dataValues.url));
36 | // app.redis.smove(`${ v.dataValues.action }_${ v.dataValues.url }`);
37 | });
38 | });
39 | return permission;
40 | };
41 |
--------------------------------------------------------------------------------
/app/model/project_files.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const project_file = app.model.define(
6 | 'project_files',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | title: Sequelize.STRING(100),
10 | project_id: Sequelize.INTEGER(11),
11 | task_id: Sequelize.INTEGER(11),
12 | creator_id: Sequelize.INTEGER(11),
13 | filename: Sequelize.STRING(50),
14 | path: Sequelize.STRING(225),
15 | extension: Sequelize.STRING(30),
16 | file_type: Sequelize.STRING(120),
17 | size: Sequelize.INTEGER(11),
18 | downloads: Sequelize.INTEGER(11),
19 | is_recycle: Sequelize.TINYINT(1),
20 | },
21 | {
22 | paranoid: true,
23 | }
24 | );
25 |
26 | project_file.addHook('afterCreate', async (project_file, options) => {
27 | const ctx = await app.createAnonymousContext();
28 | const newProjectFile = Object.assign(
29 | {
30 | task_id: null,
31 | filename: '',
32 | path: '',
33 | extension: '',
34 | file_type: '',
35 | size: 0,
36 | downloads: 0,
37 | is_recycle: 0,
38 | },
39 | project_file.dataValues
40 | );
41 | ctx.helper.sendSocketToClientOfRoom(newProjectFile, 'create:project_file');
42 | });
43 | project_file.addHook('afterUpdate', async (project_file, options) => {
44 | const ctx = await app.createAnonymousContext();
45 | ctx.helper.sendSocketToClientOfRoom(project_file, 'update:project_file');
46 | });
47 | project_file.addHook('afterDestroy', async (project_file, options) => {
48 | const ctx = await app.createAnonymousContext();
49 | ctx.helper.sendSocketToClientOfRoom(project_file, 'delete:project_file');
50 | });
51 |
52 | project_file.associate = function(models) {
53 | // associations can be defined here
54 | };
55 | return project_file;
56 | };
57 |
--------------------------------------------------------------------------------
/app/model/project_template_tasks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const project_template_task = app.model.define(
6 | 'project_template_tasks',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(255),
10 | project_template_id: Sequelize.INTEGER(11),
11 | sort: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 | project_template_task.associate = function(models) {
16 | // associations can be defined here
17 | };
18 | return project_template_task;
19 | };
20 |
--------------------------------------------------------------------------------
/app/model/project_templates.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const project_template = app.model.define(
6 | 'project_templates',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(255),
10 | cover: Sequelize.STRING(255),
11 | is_custom: Sequelize.TINYINT(1),
12 | intro: Sequelize.STRING(255),
13 | },
14 | {}
15 | );
16 | project_template.associate = function(models) {
17 | // associations can be defined here
18 | app.model.ProjectTemplates.hasMany(app.model.ProjectTemplateTasks, {
19 | foreignKey: 'project_template_id',
20 | targetKey: 'id',
21 | });
22 | };
23 | return project_template;
24 | };
25 |
--------------------------------------------------------------------------------
/app/model/role_menus.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const role_menu = app.model.define(
6 | 'role_menus',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | role_id: Sequelize.INTEGER(11),
10 | menu_id: Sequelize.INTEGER(11),
11 | },
12 | {}
13 | );
14 | role_menu.associate = function(models) {
15 | // associations can be defined here
16 | };
17 | return role_menu;
18 | };
19 |
--------------------------------------------------------------------------------
/app/model/role_permissions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 | const ctx = app.createAnonymousContext();
5 | const { models } = app.model;
6 |
7 | const role_permission = app.model.define(
8 | 'role_permissions',
9 | {
10 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
11 | role_id: Sequelize.INTEGER(11),
12 | permission_id: Sequelize.INTEGER(11),
13 | },
14 | {}
15 | );
16 | role_permission.associate = function(models) {
17 | // associations can be defined here
18 | };
19 | role_permission.afterBulkCreate((instances, options) => {
20 | resetRolePermissionsBaseRoleId(instances[0].dataValues.role_id);
21 | });
22 | role_permission.afterBulkDestroy(options => {
23 | const roleIds = options.delData.map(v => v.dataValues.role_id);
24 | resetRolePermissionsBaseRoleId(app.lodash.uniq(roleIds));
25 | });
26 |
27 | // 根据roleId,重置redis中的RolePermissions
28 | async function resetRolePermissionsBaseRoleId(roleIds) {
29 | const roles = await models.roles.findAll({
30 | attributes: ['id', 'name'],
31 | include: [{ attributes: ['id', 'url', 'action'], model: models.permissions }],
32 | where: { id: roleIds },
33 | limit: 10000,
34 | raw: false,
35 | });
36 | // 根据角色id存储对应资源
37 | const pipeline = app.redis.pipeline();
38 | roles.forEach(e => {
39 | pipeline.del(ctx.helper.redisKeys.rolePermissionsBaseRoleId(e.id));
40 | if (e.permissions.length) {
41 | const arr = [];
42 | e.permissions.forEach(permission => arr.push(`${permission.action}_${permission.url}`));
43 | pipeline.sadd(ctx.helper.redisKeys.rolePermissionsBaseRoleId(e.id), arr);
44 | }
45 | });
46 | pipeline.exec();
47 | }
48 |
49 | return role_permission;
50 | };
51 |
--------------------------------------------------------------------------------
/app/model/roles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const { STRING, INTEGER, TINYINT } = app.Sequelize;
4 |
5 | const role = app.model.define(
6 | 'roles',
7 | {
8 | id: { type: INTEGER, primaryKey: true, autoIncrement: true },
9 | name: STRING(50),
10 | is_default: TINYINT,
11 | },
12 | {}
13 | );
14 | role.associate = function(models) {
15 | // associations can be defined here
16 | app.model.Roles.belongsToMany(app.model.Permissions, {
17 | through: app.model.RolePermissions,
18 | foreignKey: 'role_id',
19 | otherKey: 'permission_id',
20 | });
21 | app.model.Roles.belongsToMany(app.model.Menus, {
22 | through: app.model.RoleMenus,
23 | foreignKey: 'role_id',
24 | otherKey: 'menu_id',
25 | });
26 | };
27 | return role;
28 | };
29 |
--------------------------------------------------------------------------------
/app/model/task_lists.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_list = app.model.define(
6 | 'task_lists',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(30),
10 | project_id: Sequelize.INTEGER(11),
11 | sort: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 |
16 | task_list.addHook('afterCreate', async (task_list, options) => {
17 | const ctx = await app.createAnonymousContext();
18 | const newProjectFile = Object.assign(
19 | {
20 | sort: 0,
21 | },
22 | task_list.dataValues
23 | );
24 | ctx.helper.sendSocketToClientOfRoom(newProjectFile, 'create:task_list');
25 | });
26 | task_list.addHook('afterUpdate', async (task_list, options) => {
27 | const ctx = await app.createAnonymousContext();
28 | ctx.helper.sendSocketToClientOfRoom(task_list, 'update:task_list');
29 | });
30 | task_list.addHook('afterDestroy', async (task_list, options) => {
31 | const ctx = await app.createAnonymousContext();
32 | ctx.helper.sendSocketToClientOfRoom(task_list, 'delete:task_list');
33 | });
34 |
35 | task_list.associate = function(models) {
36 | // associations can be defined here
37 | app.model.TaskLists.hasMany(app.model.Tasks, {
38 | foreignKey: 'task_list_id',
39 | targetKey: 'id',
40 | });
41 | app.model.TaskLists.hasOne(app.model.Projects, { foreignKey: 'id', sourceKey: 'project_id', as: 'project' });
42 | };
43 |
44 | return task_list;
45 | };
46 |
--------------------------------------------------------------------------------
/app/model/task_logs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_log = app.model.define(
6 | 'task_logs',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | remark: Sequelize.STRING(255),
10 | task_id: Sequelize.INTEGER(11),
11 | project_id: Sequelize.INTEGER(11),
12 | operator_id: Sequelize.INTEGER(11),
13 | icon: Sequelize.STRING(60),
14 | content: Sequelize.TEXT('medium'),
15 | is_comment: Sequelize.TINYINT(1),
16 | type: Sequelize.STRING(40),
17 | },
18 | {}
19 | );
20 |
21 | task_log.addHook('afterCreate', async (task_log, options) => {
22 | const ctx = await app.createAnonymousContext();
23 | const newProjectFile = Object.assign(
24 | {
25 | remark: '',
26 | icon: '',
27 | content: '',
28 | is_comment: 0,
29 | },
30 | task_log.dataValues
31 | );
32 | ctx.helper.sendSocketToClientOfRoom(newProjectFile, 'create:task_log');
33 | });
34 | task_log.addHook('afterUpdate', async (task_log, options) => {
35 | const ctx = await app.createAnonymousContext();
36 | ctx.helper.sendSocketToClientOfRoom(task_log, 'update:task_log');
37 | });
38 | task_log.addHook('afterDestroy', async (task_log, options) => {
39 | const ctx = await app.createAnonymousContext();
40 | ctx.helper.sendSocketToClientOfRoom(task_log, 'delete:task_log');
41 | });
42 |
43 | task_log.associate = function(models) {
44 | task_log.hasOne(app.model.Users, { foreignKey: 'id', sourceKey: 'operator_id', as: 'operator' });
45 | task_log.hasOne(app.model.Tasks, { foreignKey: 'id', sourceKey: 'task_id', as: 'task' });
46 | // associations can be defined here
47 | };
48 | return task_log;
49 | };
50 |
--------------------------------------------------------------------------------
/app/model/task_prioritys.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_priority = app.model.define(
6 | 'task_prioritys',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(30),
10 | color: Sequelize.STRING(10),
11 | sort: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 | task_priority.associate = function(models) {
16 | // associations can be defined here
17 | };
18 | return task_priority;
19 | };
20 |
--------------------------------------------------------------------------------
/app/model/task_states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_state = app.model.define(
6 | 'task_states',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(50),
10 | color: Sequelize.STRING(16),
11 | icon: Sequelize.STRING(50),
12 | sort: Sequelize.INTEGER(11),
13 | },
14 | {}
15 | );
16 | task_state.associate = function(models) {
17 | // associations can be defined here
18 | };
19 | return task_state;
20 | };
21 |
--------------------------------------------------------------------------------
/app/model/task_tags.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_tag = app.model.define(
6 | 'task_tags',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(100),
10 | color: Sequelize.STRING(30),
11 | project_id: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 |
16 | task_tag.addHook('afterCreate', async (task_tag, options) => {
17 | const ctx = await app.createAnonymousContext();
18 | ctx.helper.sendSocketToClientOfRoom(task_tag, 'create:task_tag');
19 | });
20 | task_tag.addHook('afterUpdate', async (task_tag, options) => {
21 | const ctx = await app.createAnonymousContext();
22 | ctx.helper.sendSocketToClientOfRoom(task_tag, 'update:task_tag');
23 | });
24 | task_tag.addHook('afterDestroy', async (task_tag, options) => {
25 | const ctx = await app.createAnonymousContext();
26 | ctx.helper.sendSocketToClientOfRoom(task_tag, 'delete:task_tag');
27 | });
28 |
29 | task_tag.associate = function(models) {
30 | task_tag.hasOne(app.model.Users, { foreignKey: 'id', sourceKey: 'operator_id', as: 'operator' });
31 | // associations can be defined here
32 | };
33 |
34 | task_tag.associate = function(models) {
35 | // associations can be defined here
36 | };
37 | return task_tag;
38 | };
39 |
--------------------------------------------------------------------------------
/app/model/task_task_tags.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_task_tag = app.model.define(
6 | 'task_task_tags',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | task_id: Sequelize.INTEGER(11),
10 | task_tag_id: Sequelize.INTEGER(11),
11 | project_id: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 |
16 | task_task_tag.addHook('afterCreate', async (task_task_tag, options) => {
17 | const ctx = await app.createAnonymousContext();
18 | const task_tag = await app.model.TaskTags.findOne({ where: { id: task_task_tag.task_tag_id } });
19 | ctx.helper.sendSocketToClientOfRoom(task_tag, 'create:task_task_tag');
20 | });
21 | task_task_tag.addHook('afterDestroy', async (task_task_tag, options) => {
22 | const ctx = await app.createAnonymousContext();
23 | ctx.helper.sendSocketToClientOfRoom(task_task_tag, 'delete:task_task_tag');
24 | });
25 |
26 | task_task_tag.associate = function(models) {
27 | // associations can be defined here
28 | app.model.Tasks.belongsToMany(app.model.TaskTags, {
29 | through: app.model.TaskTaskTags,
30 | foreignKey: 'task_id',
31 | otherKey: 'task_tag_id',
32 | });
33 | };
34 |
35 | return task_task_tag;
36 | };
37 |
--------------------------------------------------------------------------------
/app/model/task_types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_type = app.model.define(
6 | 'task_types',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | name: Sequelize.STRING(50),
10 | color: Sequelize.STRING(16),
11 | icon: Sequelize.STRING(50),
12 | sort: Sequelize.INTEGER(11),
13 | },
14 | {}
15 | );
16 | task_type.associate = function(models) {
17 | // associations can be defined here
18 | };
19 | return task_type;
20 | };
21 |
--------------------------------------------------------------------------------
/app/model/task_working_hours.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const task_working_hour = app.model.define(
6 | 'task_working_hours',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | description: Sequelize.STRING(255),
10 | work_time: Sequelize.INTEGER(11),
11 | task_id: Sequelize.INTEGER(11),
12 | project_id: Sequelize.INTEGER(11),
13 | executor_id: Sequelize.INTEGER(11),
14 | start_date: Sequelize.DATE,
15 | end_date: Sequelize.DATE,
16 | },
17 | {
18 |
19 | }
20 | );
21 |
22 | task_working_hour.addHook('afterCreate', async (task_working_hour, options) => {
23 | const ctx = await app.createAnonymousContext();
24 | ctx.helper.sendSocketToClientOfRoom(task_working_hour, 'create:task_working_hour');
25 | });
26 | task_working_hour.addHook('afterUpdate', async (task_working_hour, options) => {
27 | const ctx = await app.createAnonymousContext();
28 | ctx.helper.sendSocketToClientOfRoom(task_working_hour, 'update:task_working_hour');
29 | });
30 | task_working_hour.addHook('afterDestroy', async (task_working_hour, options) => {
31 | const ctx = await app.createAnonymousContext();
32 | ctx.helper.sendSocketToClientOfRoom(task_working_hour, 'delete:task_working_hour');
33 | });
34 |
35 | task_working_hour.associate = function(models) {
36 | task_working_hour.hasOne(app.model.Users, { foreignKey: 'id', sourceKey: 'executor_id', as: 'executor' });
37 | // associations can be defined here
38 | };
39 | return task_working_hour;
40 | };
41 |
--------------------------------------------------------------------------------
/app/model/user_project_collects.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const user_project_collect = app.model.define(
6 | 'user_project_collects',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | user_id: Sequelize.INTEGER(11),
10 | project_id: Sequelize.INTEGER(11),
11 | },
12 | {
13 |
14 | }
15 | );
16 | user_project_collect.associate = function(models) {
17 | // associations can be defined here
18 | };
19 | return user_project_collect;
20 | };
21 |
--------------------------------------------------------------------------------
/app/model/user_projects.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const user_project = app.model.define(
6 | 'user_projects',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | user_id: Sequelize.INTEGER(11),
10 | project_id: Sequelize.INTEGER(11),
11 | },
12 | {}
13 | );
14 | user_project.addHook('afterCreate', async (user_project, options) => {
15 | const ctx = await app.createAnonymousContext();
16 | const { user_id, project_id } = user_project;
17 | const roomName = `${app.config.socketProjectRoomNamePrefix}${project_id}`;
18 | const nsp = app.io.of('/');
19 | const rex = new RegExp(`^${user_id}_.*`);
20 | nsp.adapter.clients((err, clients) => {
21 | if (err) {
22 | app.logger.errorAndSentry(err);
23 | return;
24 | }
25 | clients.forEach(clientId => {
26 | // 正则userID_uuid,给同一个用户多个socket分别发送消息
27 | if (rex.test(clientId)) {
28 | try {
29 | const socket = nsp.sockets[clientId];
30 | // const socket = nsp.to(clientId);
31 | // 当此用户在线,则将用户加入此项目room
32 | socket &&
33 | socket.join(roomName, () => {
34 | ctx.helper.sendSocketToClientOfRoom(user_project, 'create:user_project');
35 | });
36 | } catch (e) {
37 | app.logger.errorAndSentry(e);
38 | }
39 | }
40 | });
41 | });
42 | });
43 |
44 | user_project.addHook('afterDestroy', async (user_project, options) => {
45 | const ctx = await app.createAnonymousContext();
46 | ctx.helper.sendSocketToClientOfRoom(user_project, 'delete:user_project');
47 | });
48 |
49 | user_project.associate = function(models) {
50 | // associations can be defined here
51 | };
52 | return user_project;
53 | };
54 |
--------------------------------------------------------------------------------
/app/model/user_roles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 | const ctx = app.createAnonymousContext();
5 | const { models } = app.model;
6 |
7 | const user_role = app.model.define(
8 | 'user_roles',
9 | {
10 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
11 | user_id: Sequelize.INTEGER(11),
12 | role_id: Sequelize.INTEGER(11),
13 | },
14 | {}
15 | );
16 | user_role.associate = function(models) {
17 | // associations can be defined here
18 | app.model.UserRoles.belongsTo(app.model.Roles, {
19 | foreignKey: 'role_id',
20 | targetKey: 'id',
21 | });
22 | };
23 |
24 | user_role.afterBulkCreate((instances, options) => {
25 | resetUserRoleIdsBaseUserId([instances[0].dataValues.user_id]);
26 | });
27 | user_role.afterBulkDestroy(options => {
28 | const userIds = options.delData.map(v => v.dataValues.user_id);
29 | resetUserRoleIdsBaseUserId(app.lodash.uniq(userIds));
30 | });
31 |
32 | // 根据userId,重置redis中的userRoleIds
33 | async function resetUserRoleIdsBaseUserId(userIds) {
34 | const user_roles = await models.user_roles.findAll({
35 | attributes: ['user_id', 'role_id'],
36 | where: { user_id: userIds },
37 | limit: 10000,
38 | raw: true,
39 | });
40 | const userGroup = app.lodash.groupBy(user_roles, 'user_id');
41 | const pipeline = app.redis.pipeline();
42 | userIds.forEach(e => pipeline.del(ctx.helper.redisKeys.userRoleIdsBaseUserId(e)));
43 | Object.values(userGroup)
44 | .forEach(e => {
45 | const arr = [];
46 | e.forEach(item => arr.push(item.role_id));
47 | pipeline.sadd(ctx.helper.redisKeys.userRoleIdsBaseUserId(e[0].user_id), arr);
48 | // 设置3天的过期期限
49 | pipeline.expire(ctx.helper.redisKeys.userRoleIdsBaseUserId(e[0].user_id), 60 * 60 * 24 * 3);
50 | });
51 | pipeline.exec();
52 | }
53 |
54 | return user_role;
55 | };
56 |
--------------------------------------------------------------------------------
/app/model/user_task_likes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const user_task_like = app.model.define(
6 | 'user_task_likes',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | user_id: Sequelize.INTEGER(11),
10 | task_id: Sequelize.INTEGER(11),
11 | project_id: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 |
16 | user_task_like.addHook('afterCreate', async (user_task_like, options) => {
17 | const ctx = await app.createAnonymousContext();
18 | ctx.helper.sendSocketToClientOfRoom(user_task_like, 'create:user_task_like');
19 | });
20 |
21 | user_task_like.addHook('afterDestroy', async (user_task_like, options) => {
22 | const ctx = await app.createAnonymousContext();
23 | ctx.helper.sendSocketToClientOfRoom(user_task_like, 'delete:user_task_like');
24 | });
25 |
26 | user_task_like.associate = function(models) {
27 | // associations can be defined here
28 | };
29 | return user_task_like;
30 | };
31 |
--------------------------------------------------------------------------------
/app/model/user_tasks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const user_task = app.model.define(
6 | 'user_tasks',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | user_id: Sequelize.INTEGER(11),
10 | task_id: Sequelize.INTEGER(11),
11 | project_id: Sequelize.INTEGER(11),
12 | },
13 | {}
14 | );
15 |
16 | user_task.addHook('afterCreate', async (user_task, options) => {
17 | const ctx = await app.createAnonymousContext();
18 | const user = await app.model.Users.findOne({ where: { id: user_task.user_id } });
19 | ctx.helper.sendSocketToClientOfRoom(user, 'create:user_task', user_task.project_id);
20 | });
21 | user_task.addHook('afterDestroy', async (user_task, options) => {
22 | const ctx = await app.createAnonymousContext();
23 | const task = await app.model.Tasks.findOne({ where: { id: user_task.task_id } });
24 | ctx.helper.sendSocketToClientOfRoom(user_task, 'delete:user_task', task.project_id);
25 | });
26 |
27 | user_task.associate = function(models) {
28 | // associations can be defined here
29 | };
30 | return user_task;
31 | };
32 |
--------------------------------------------------------------------------------
/app/model/users.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const user = app.model.define(
6 | 'users',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | user_id_github: Sequelize.INTEGER,
10 | username: Sequelize.STRING(60),
11 | department_id: Sequelize.INTEGER,
12 | nickname: Sequelize.STRING(60),
13 | password: Sequelize.STRING(64),
14 | email: Sequelize.STRING(60),
15 | state: Sequelize.TINYINT,
16 | phone: Sequelize.STRING(15),
17 | avatar: Sequelize.STRING(255),
18 | company: Sequelize.STRING(80),
19 | city: Sequelize.STRING(80),
20 | last_login: Sequelize.DATE,
21 | },
22 | {
23 | paranoid: true,
24 | defaultScope: {
25 | attributes: { exclude: ['password'] },
26 | },
27 | scopes: {
28 | withPassword: {
29 | attributes: {},
30 | },
31 | },
32 | }
33 | );
34 | user.associate = function(models) {
35 | user.hasOne(app.model.Departments, { foreignKey: 'id', sourceKey: 'department_id', as: 'department' });
36 | app.model.Users.belongsToMany(app.model.Roles, {
37 | through: app.model.UserRoles,
38 | foreignKey: 'user_id',
39 | otherKey: 'role_id',
40 | });
41 | app.model.Users.belongsToMany(app.model.Projects, {
42 | through: app.model.UserProjects,
43 | foreignKey: 'user_id',
44 | otherKey: 'project_id',
45 | });
46 | };
47 | return user;
48 | };
49 |
--------------------------------------------------------------------------------
/app/model/verification_codes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const verification_code = app.model.define(
6 | 'verification_codes',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | code: Sequelize.STRING(60),
10 | target: Sequelize.STRING(60),
11 | type: Sequelize.TINYINT(1),
12 | available: Sequelize.TINYINT(1),
13 | expiration_time: Sequelize.DATE,
14 | },
15 | {}
16 | );
17 | verification_code.associate = function(models) {
18 | // associations can be defined here
19 | };
20 | return verification_code;
21 | };
22 |
--------------------------------------------------------------------------------
/app/schedule/resend_not_ack_socket.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const lodash = require('lodash');
3 |
4 | module.exports = {
5 | schedule: {
6 | interval: 1000 * 2, // 2s间隔
7 | type: 'worker', // 指定所有的 worker 都需要执行
8 | disable: false, // 配置该参数为 true 时,这个定时任务不会被启动。
9 | immediate: false, // 配置了该参数为 true 时,这个定时任务会在应用启动并 ready 后立刻执行一次这个定时任务。
10 | },
11 | async task(ctx) {
12 | const { app: { redis, io }, helper } = ctx;
13 | const socketKeys = await redis.keys(helper.redisKeys.socketBaseSocketId('*'));
14 | // console.log('------------------------');
15 | // console.log(socketKeys);
16 | // console.log(ctx.app.socketIdOnRedisKeys);
17 | // 同上一次获取比较,相同的key代表最少2秒,最多4秒没有收到ACK反馈,则重发
18 | // 没有直接获取到就重发,是为了,避免小于2秒存入的重发。
19 | const intersection = lodash.intersection(ctx.app.socketIdOnRedisKeys, socketKeys);
20 | // const data = await redis.mget(...intersection);
21 | const nsp = io.of('/');
22 | // console.log(nsp.adapter.rooms);
23 | // nsp.clients((error, clients) => {
24 | // if (error) throw error;
25 | // console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]
26 | // });
27 | try {
28 | intersection.forEach(id => {
29 | redis.get(id, (err, data) => {
30 | if (!err) {
31 | const emitData = JSON.parse(data);
32 | const socket = nsp.to(emitData[1].clientId);
33 | socket.emit(...emitData);
34 | }
35 | });
36 | });
37 | } catch (e) {
38 | throw e;
39 | }
40 | // 本次覆盖存入
41 | ctx.app.socketIdOnRedisKeys = socketKeys;
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/app/schedule/update_cache_redis.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { permissionsToRedis } = require('../../app-boot-hook-do');
4 |
5 | module.exports = {
6 | schedule: {
7 | interval: 1000 * 60 * 5, // 5 分钟间隔
8 | type: 'worker', // 指定所有的 worker 都需要执行
9 | disable: false, // 配置该参数为 true 时,这个定时任务不会被启动。
10 | immediate: false, // 配置了该参数为 true 时,这个定时任务会在应用启动并 ready 后立刻执行一次这个定时任务。
11 | },
12 | async task(ctx) {
13 | // 资源数据缓存到redis
14 | await permissionsToRedis(ctx.app);
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/app/service/configurations.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async update(payload) {
7 | const { ctx } = this;
8 | return await ctx.model.Configurations.update(payload, {
9 | where: { id: payload.id },
10 | });
11 | }
12 |
13 | async findRsaPublicKey(id) {
14 | const { ctx } = this;
15 | return await ctx.model.Configurations.findOne({
16 | where: { id },
17 | attributes: { exclude: ['rsa_private_key'] },
18 | });
19 | }
20 | }
21 |
22 | module.exports = _objectName_Service;
23 |
--------------------------------------------------------------------------------
/app/service/departments.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.Departments.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.Departments.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.Departments.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.Departments.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.Departments.destroy({ where: { id: payload.ids }, individualHooks: true });
40 | }
41 | }
42 |
43 | module.exports = _objectName_Service;
44 |
--------------------------------------------------------------------------------
/app/service/messages.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, name } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | name ? (where.name = { [Op.like]: `%${name}%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model.Messages.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | include: [
20 | {
21 | model: ctx.model.Users,
22 | attributes: ['username', 'id', 'avatar'],
23 | as: 'actor',
24 | },
25 | ],
26 | });
27 | }
28 |
29 | async findOne(id) {
30 | const { ctx } = this;
31 | return await ctx.model.Messages.findOne({ where: { id } });
32 | }
33 |
34 | async create(payload) {
35 | const { ctx } = this;
36 | return await ctx.model.Messages.create(payload);
37 | }
38 |
39 | async update(payload) {
40 | const { ctx } = this;
41 | return await ctx.model.Messages.update(payload, {
42 | where: { id: payload.id },
43 | individualHooks: true,
44 | });
45 | }
46 |
47 | async destroy(payload) {
48 | const { ctx } = this;
49 | return await ctx.model.Messages.destroy({
50 | where: { id: payload.ids },
51 | individualHooks: true,
52 | });
53 | }
54 | }
55 |
56 | module.exports = _objectName_Service;
57 |
--------------------------------------------------------------------------------
/app/service/operation_logs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, name } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | name ? (where.name = { [Op.like]: `%${name}%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model.OperationLogs.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | });
20 | }
21 |
22 | async findOne(id) {
23 | const { ctx } = this;
24 | return await ctx.model.OperationLogs.findOne({ where: { id } });
25 | }
26 |
27 | async create(payload) {
28 | const { ctx } = this;
29 | return await ctx.model.OperationLogs.create(payload);
30 | }
31 |
32 | async update(payload) {
33 | const { ctx } = this;
34 | return await ctx.model.OperationLogs.update(payload, {
35 | where: { id: payload.id },
36 | });
37 | }
38 |
39 | async destroy(payload) {
40 | const { ctx } = this;
41 | return await ctx.model.OperationLogs.destroy({
42 | where: { id: payload.ids },
43 | });
44 | }
45 | }
46 |
47 | module.exports = _objectName_Service;
48 |
--------------------------------------------------------------------------------
/app/service/permissions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | prop_order && order ? Order.push([prop_order, order]) : null;
13 | return await ctx.model.Permissions.findAndCountAll({
14 | limit,
15 | offset,
16 | where,
17 | order: Order,
18 | });
19 | }
20 |
21 | async findOne(id) {
22 | const { ctx } = this;
23 | return await ctx.model.Permissions.findOne({ where: { id } });
24 | }
25 |
26 | async create(payload) {
27 | const { ctx } = this;
28 | const { url, action } = payload;
29 | const one = await ctx.model.Permissions.findOne({ where: { url, action } });
30 | if (one) {
31 | const err = new Error('已存在');
32 | err.parent = {};
33 | err.parent.errno = 1062;
34 | throw err;
35 | }
36 | return await ctx.model.Permissions.create(payload);
37 | }
38 |
39 | async update(payload) {
40 | const { ctx } = this;
41 | const { id, url, action } = payload;
42 | const one = await ctx.model.Permissions.findOne({
43 | where: { id: { [Op.not]: id }, url, action },
44 | });
45 | if (one) {
46 | const err = new Error('已存在');
47 | err.parent = {};
48 | err.parent.errno = 1062;
49 | throw err;
50 | }
51 | return await ctx.model.Permissions.update(payload, {
52 | ctx,
53 | where: { id: payload.id },
54 | });
55 | }
56 |
57 | async destroy(payload) {
58 | const { ctx } = this;
59 | const delData = await ctx.model.Permissions.findAll({
60 | where: { id: payload.ids },
61 | });
62 | return await ctx.model.Permissions.destroy({
63 | ctx,
64 | where: { id: payload.ids },
65 | delData,
66 | });
67 | }
68 | }
69 |
70 | module.exports = _objectName_Service;
71 |
--------------------------------------------------------------------------------
/app/service/project_files.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, title } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | title ? (where.title = { [Op.like]: `%${title}%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model.ProjectFiles.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | });
20 | }
21 |
22 | async findOne(id) {
23 | const { ctx } = this;
24 | return await ctx.model.ProjectFiles.findOne({ where: { id } });
25 | }
26 |
27 | async create(payload) {
28 | const { ctx } = this;
29 | return await ctx.model.ProjectFiles.create(payload);
30 | }
31 |
32 | async update(payload) {
33 | const { ctx } = this;
34 | return await ctx.model.ProjectFiles.update(payload, {
35 | where: { id: payload.id },
36 | individualHooks: true,
37 | });
38 | }
39 |
40 | async destroy(payload) {
41 | const { ctx } = this;
42 | return await ctx.model.ProjectFiles.destroy({
43 | where: { id: payload.ids },
44 | individualHooks: true,
45 | });
46 | }
47 | }
48 |
49 | module.exports = _objectName_Service;
50 |
--------------------------------------------------------------------------------
/app/service/project_template_tasks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.ProjectTemplateTasks.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.ProjectTemplateTasks.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.ProjectTemplateTasks.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.ProjectTemplateTasks.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.ProjectTemplateTasks.destroy({
40 | where: { id: payload.ids },
41 | });
42 | }
43 | }
44 |
45 | module.exports = _objectName_Service;
46 |
--------------------------------------------------------------------------------
/app/service/project_templates.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [[ctx.model.ProjectTemplateTasks, 'sort', 'desc']];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.ProjectTemplates.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | include: [
18 | {
19 | model: ctx.model.ProjectTemplateTasks,
20 | attributes: {
21 | exclude: ['created_at', 'updated_at'],
22 | },
23 | },
24 | ],
25 | });
26 | }
27 |
28 | async findOne(id) {
29 | const { ctx } = this;
30 | return await ctx.model.ProjectTemplates.findOne({ where: { id } });
31 | }
32 |
33 | async create(payload) {
34 | const { ctx } = this;
35 | return await ctx.model.ProjectTemplates.create(payload);
36 | }
37 |
38 | async update(payload) {
39 | const { ctx } = this;
40 | return await ctx.model.ProjectTemplates.update(payload, {
41 | where: { id: payload.id },
42 | });
43 | }
44 |
45 | async destroy(payload) {
46 | const { ctx } = this;
47 | return await ctx.model.ProjectTemplates.destroy({
48 | where: { id: payload.ids },
49 | });
50 | }
51 | }
52 |
53 | module.exports = _objectName_Service;
54 |
--------------------------------------------------------------------------------
/app/service/role_menus.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.RoleMenus.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.RoleMenus.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.RoleMenus.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.RoleMenus.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.RoleMenus.destroy({ where: { id: payload.ids } });
40 | }
41 |
42 | /**
43 | * 单角色批量添加多菜单
44 | * @param payload
45 | * @return {Promise}
46 | */
47 | async bulkCreateMenu(payload) {
48 | const { ctx } = this;
49 | payload = payload.menu_ids.map(e => {
50 | return { role_id: payload.role_id, menu_id: e };
51 | });
52 | return await ctx.model.RoleMenus.bulkCreate(payload);
53 | }
54 | }
55 |
56 | module.exports = _objectName_Service;
57 |
--------------------------------------------------------------------------------
/app/service/role_permissions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx, app } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.RolePermissions.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.RolePermissions.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.RolePermissions.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.RolePermissions.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | const delData = await ctx.model.RolePermissions.findAll({
40 | where: { id: payload.ids },
41 | });
42 | return await ctx.model.RolePermissions.destroy({
43 | where: { id: payload.ids },
44 | delData,
45 | });
46 | }
47 |
48 | /**
49 | * 单角色批量添加多菜单
50 | * @param payload
51 | * @return {Promise}
52 | */
53 | async bulkCreatePremission(payload) {
54 | const { ctx } = this;
55 | payload = payload.permission_ids.map(e => {
56 | return { role_id: payload.role_id, permission_id: e };
57 | });
58 | return await ctx.model.RolePermissions.bulkCreate(payload);
59 | }
60 | }
61 |
62 | module.exports = _objectName_Service;
63 |
--------------------------------------------------------------------------------
/app/service/roles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class RoleService extends Service {
7 | async index(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | prop_order && order ? Order.push([prop_order, order]) : null;
13 | // 如果请求者不是用户id为1的超级管理员,则不返回id为1的超级管理员角色
14 | if (ctx.currentRequestData.userInfo.id !== 1) {
15 | if (where[Op.and]) {
16 | where[Op.and].push({ id: { [Op.ne]: 1 } });
17 | } else {
18 | where[Op.and] = [{ id: { [Op.ne]: 1 } }];
19 | }
20 | }
21 | return await ctx.model.Roles.findAndCountAll({
22 | limit,
23 | offset,
24 | where,
25 | order: Order,
26 | });
27 | }
28 |
29 | async show(id) {
30 | const { ctx } = this;
31 | return await ctx.model.Roles.findOne({ where: { id } });
32 | }
33 |
34 | async create(payload) {
35 | const { ctx } = this;
36 | return await ctx.model.Roles.create(payload);
37 | }
38 |
39 | async update(payload) {
40 | const { ctx } = this;
41 | return await ctx.model.Roles.update(payload, { where: { id: payload.id } });
42 | }
43 |
44 | async destroy(payload) {
45 | const { ctx } = this;
46 | const allRoles = await ctx.model.Roles.findAll({
47 | where: { id: payload.ids },
48 | });
49 | if (!allRoles.every(e => e.is_default !== 1)) {
50 | return { __code_wrong: 40000 };
51 | }
52 | return await ctx.model.Roles.destroy({ where: { id: payload.ids } });
53 | }
54 |
55 | async updateIsDefault(payload) {
56 | const { ctx } = this;
57 | const transaction = await ctx.model.transaction();
58 | await ctx.model.Roles.update({ is_default: 0 }, { where: { is_default: 1 }, transaction });
59 | const res = await ctx.model.Roles.update({ is_default: 1 }, { where: { id: payload.id }, transaction });
60 | if (res && res[0] === 1) {
61 | await transaction.commit();
62 | return true;
63 | }
64 | await transaction.rollback();
65 | return false;
66 | }
67 | }
68 |
69 | module.exports = RoleService;
70 |
--------------------------------------------------------------------------------
/app/service/task_logs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, remark } = payload;
10 | const where = payload.where;
11 | const Order = [['id', 'asc']];
12 | remark ? (where.remark = { [Op.like]: `%${remark}%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model.TaskLogs.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | include: [
20 | {
21 | model: ctx.model.Users,
22 | as: 'operator',
23 | attributes: {
24 | exclude: ['updated_at'],
25 | },
26 | },
27 | {
28 | model: ctx.model.Tasks,
29 | as: 'task',
30 | attributes: ['id', 'name'],
31 | },
32 | ],
33 | });
34 | }
35 |
36 | async findOne(id) {
37 | const { ctx } = this;
38 | return await ctx.model.TaskLogs.findOne({ where: { id } });
39 | }
40 |
41 | async create(payload) {
42 | const { ctx } = this;
43 | return await ctx.model.TaskLogs.create(payload);
44 | }
45 |
46 | async update(payload) {
47 | const { ctx } = this;
48 | return await ctx.model.TaskLogs.update(payload, {
49 | where: { id: payload.id },
50 | individualHooks: true,
51 | });
52 | }
53 |
54 | async destroy(payload) {
55 | const { ctx } = this;
56 | return await ctx.model.TaskLogs.destroy({
57 | where: { id: payload.ids },
58 | individualHooks: true,
59 | });
60 | }
61 | }
62 |
63 | module.exports = _objectName_Service;
64 |
--------------------------------------------------------------------------------
/app/service/task_prioritys.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.TaskPrioritys.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.TaskPrioritys.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.TaskPrioritys.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.TaskPrioritys.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.TaskPrioritys.destroy({
40 | where: { id: payload.ids },
41 | });
42 | }
43 | }
44 |
45 | module.exports = _objectName_Service;
46 |
--------------------------------------------------------------------------------
/app/service/task_states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.TaskStates.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.TaskStates.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.TaskStates.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.TaskStates.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.TaskStates.destroy({
40 | where: { id: payload.ids },
41 | });
42 | }
43 | }
44 |
45 | module.exports = _objectName_Service;
46 |
--------------------------------------------------------------------------------
/app/service/task_tags.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.TaskTags.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.TaskTags.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.TaskTags.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.TaskTags.update(payload, {
33 | where: { id: payload.id },
34 | individualHooks: true,
35 | });
36 | }
37 |
38 | async destroy(payload) {
39 | const { ctx } = this;
40 | return await ctx.model.TaskTags.destroy({
41 | where: { id: payload.ids },
42 | individualHooks: true,
43 | });
44 | }
45 | }
46 |
47 | module.exports = _objectName_Service;
48 |
--------------------------------------------------------------------------------
/app/service/task_task_tags.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.TaskTaskTags.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.TaskTaskTags.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | const { task_id } = payload;
28 | const task = await ctx.model.Tasks.findOne({ where: { id: task_id } });
29 | if (!task) return false;
30 | payload.project_id = task.project_id;
31 | return await ctx.model.TaskTaskTags.create(payload);
32 | }
33 |
34 | async update(payload) {
35 | const { ctx } = this;
36 | return await ctx.model.TaskTaskTags.update(payload, {
37 | where: { id: payload.id },
38 | individualHooks: true,
39 | });
40 | }
41 |
42 | async destroy(payload) {
43 | const { ctx } = this;
44 | return await ctx.model.TaskTaskTags.destroy({
45 | where: { id: payload.ids },
46 | individualHooks: true,
47 | });
48 | }
49 |
50 | async change(payload) {
51 | const { ctx } = this;
52 | const one = await ctx.model.TaskTaskTags.findOne({ where: payload });
53 | if (one) {
54 | return await ctx.model.TaskTaskTags.destroy({
55 | where: payload,
56 | individualHooks: true,
57 | });
58 | }
59 | const { task_id } = payload;
60 | const task = await ctx.model.Tasks.findOne({ where: { id: task_id } });
61 | if (!task) return false;
62 | payload.project_id = task.project_id;
63 | return await ctx.model.TaskTaskTags.create(payload);
64 | }
65 | }
66 |
67 | module.exports = _objectName_Service;
68 |
--------------------------------------------------------------------------------
/app/service/task_types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async findAll(payload) {
7 | const { ctx } = this;
8 | const { limit, offset, prop_order, order } = payload;
9 | const where = payload.where;
10 | const Order = [];
11 | prop_order && order ? Order.push([prop_order, order]) : null;
12 | return await ctx.model.TaskTypes.findAndCountAll({
13 | limit,
14 | offset,
15 | where,
16 | order: Order,
17 | });
18 | }
19 |
20 | async findOne(id) {
21 | const { ctx } = this;
22 | return await ctx.model.TaskTypes.findOne({ where: { id } });
23 | }
24 |
25 | async create(payload) {
26 | const { ctx } = this;
27 | return await ctx.model.TaskTypes.create(payload);
28 | }
29 |
30 | async update(payload) {
31 | const { ctx } = this;
32 | return await ctx.model.TaskTypes.update(payload, {
33 | where: { id: payload.id },
34 | });
35 | }
36 |
37 | async destroy(payload) {
38 | const { ctx } = this;
39 | return await ctx.model.TaskTypes.destroy({
40 | where: { id: payload.ids },
41 | });
42 | }
43 | }
44 |
45 | module.exports = _objectName_Service;
46 |
--------------------------------------------------------------------------------
/app/service/task_working_hours.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, description } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | description ? (where.description = { [Op.like]: `%${ description }%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model.TaskWorkingHours.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | include: [
20 | {
21 | model: ctx.model.Users,
22 | attributes: ['username', 'id', 'avatar'],
23 | as: 'executor',
24 | },
25 | ],
26 | });
27 | }
28 |
29 | async findOne(id) {
30 | const { ctx } = this;
31 | return await ctx.model.TaskWorkingHours.findOne({ where: { id } });
32 | }
33 |
34 | async create(payload) {
35 | const { ctx } = this;
36 | return await ctx.model.TaskWorkingHours.create(payload);
37 | }
38 |
39 | async update(payload) {
40 | const { ctx } = this;
41 | return await ctx.model.TaskWorkingHours.update(payload, {
42 | where: { id: payload.id },
43 | individualHooks: true,
44 | });
45 | }
46 |
47 | async destroy(payload) {
48 | const { ctx } = this;
49 | return await ctx.model.TaskWorkingHours.destroy({
50 | where: { id: payload.ids },
51 | individualHooks: true,
52 | });
53 | }
54 | }
55 |
56 | module.exports = _objectName_Service;
57 |
--------------------------------------------------------------------------------
/app/service/user_project_collects.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 |
7 | async change(payload) {
8 | const { ctx } = this;
9 | const one = await ctx.model.UserProjectCollects.findOne({ where: payload });
10 | if (one) {
11 | return await ctx.model.UserProjectCollects.destroy({
12 | where: payload,
13 | });
14 | }
15 | return await ctx.model.UserProjectCollects.create(payload);
16 | }
17 | }
18 |
19 | module.exports = _objectName_Service;
20 |
--------------------------------------------------------------------------------
/app/service/user_task_likes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 |
5 | class _objectName_Service extends Service {
6 | async change(payload) {
7 | const { ctx } = this;
8 | payload.user_id = ctx.currentRequestData.userInfo.id;
9 | const one = await ctx.model.UserTaskLikes.findOne({ where: payload });
10 | if (one) {
11 | return await ctx.model.UserTaskLikes.destroy({
12 | where: payload,
13 | individualHooks: true,
14 | });
15 | }
16 | return await ctx.model.UserTaskLikes.create(payload);
17 | }
18 | }
19 |
20 | module.exports = _objectName_Service;
21 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '10'
4 |
5 | install:
6 | - ps: Install-Product node $env:nodejs_version
7 | - npm i npminstall && node_modules\.bin\npminstall
8 |
9 | test_script:
10 | - node --version
11 | - npm --version
12 | - npm run test
13 |
14 | build: off
15 |
--------------------------------------------------------------------------------
/autocannon/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const autocannon = require('autocannon');
3 | autocannon(
4 | {
5 | url: 'http://localhost:7002/api/v1/roles/list?name=&offset=0&prop_order=id&order=desc',
6 | headers: {
7 | Authorization:
8 | 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJJbmZvIjp7ImlkIjoxLCJyb2xlSWRzIjpbMSwyLDI2LDE3LDE4LDE5LDIwLDI0LDIyXX19LCJleHAiOjE2MDAxNzAwODksImlhdCI6MTYwMDA4MzY4OX0.RPPpnfPqI0yqD8Es7tS4DHe-HrOiD6kGtM7RvE0UTTs',
9 | }, // default
10 | connections: 200, // default
11 | pipelining: 1, // default
12 | duration: 5, // default
13 | },
14 | console.log
15 | );
16 |
--------------------------------------------------------------------------------
/autocannon/index2.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const autocannon = require('autocannon');
3 | autocannon(
4 | {
5 | url: 'http://localhost:7002/api/upload',
6 | method: 'POST',
7 | headers: {
8 | Authorization:
9 | 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJJbmZvIjp7ImlkIjoxLCJyb2xlSWRzIjpbMSwyLDI2LDE3LDE4LDE5LDIwLDI0LDIyXX19LCJleHAiOjE2MDAxNzAwODksImlhdCI6MTYwMDA4MzY4OX0.RPPpnfPqI0yqD8Es7tS4DHe-HrOiD6kGtM7RvE0UTTs',
10 | }, // default
11 | connections: 1, // default
12 | pipelining: 1, // default
13 | duration: 350, // default
14 | form: {
15 | field: {
16 | type: 'file',
17 | path: 'C://Users/Administrator/Desktop/2020-09-15_171643.jpg',
18 | options: {
19 | filepath: 'C://Users/Administrator/Desktop/2020-09-15_171643.jpg',
20 | },
21 | },
22 | }, // default
23 | },
24 | console.log
25 | );
26 |
--------------------------------------------------------------------------------
/autocannon/index3.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const autocannon = require('autocannon');
3 | autocannon(
4 | {
5 | url: 'http://localhost:7002/api/v1/menus/user_menus',
6 | headers: {
7 | Authorization:
8 | 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJJbmZvIjp7ImlkIjoxLCJyb2xlSWRzIjpbMSwyLDI2LDE4LDE5LDIwLDI0LDIyXX19LCJleHAiOjE2MDAzMzU4NzEsImlhdCI6MTYwMDI0OTQ3MX0.erlp7z2EvA7Ofvzb897b2hnGtym_m1EPnIKVoZpSwws',
9 | }, // default
10 | connections: 200, // default
11 | pipelining: 1, // default
12 | duration: 5, // default
13 | },
14 | console.log
15 | );
16 |
--------------------------------------------------------------------------------
/autocannon/index4.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const autocannon = require('autocannon');
3 | autocannon(
4 | {
5 | url: 'http://39.105.27.90:7009/all',
6 | headers: {
7 | Authorization:
8 | 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJJbmZvIjp7ImlkIjoxLCJyb2xlSWRzIjpbMSwyLDI2LDE4LDE5LDIwLDI0LDIyXX19LCJleHAiOjE2MDAzMzU4NzEsImlhdCI6MTYwMDI0OTQ3MX0.erlp7z2EvA7Ofvzb897b2hnGtym_m1EPnIKVoZpSwws',
9 | }, // default
10 | connections: 20, // default
11 | pipelining: 1, // default
12 | duration: 5, // default
13 | },
14 | console.log
15 | );
16 |
--------------------------------------------------------------------------------
/autocannon/index5.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const autocannon = require('autocannon');
3 | autocannon(
4 | {
5 | url: 'http://beehive.imfdj.top/api/v1/configurations/public_key',
6 | headers: {
7 | Authorization:
8 | 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJJbmZvIjp7ImlkIjoxLCJyb2xlSWRzIjpbMSwyLDI2LDE4LDE5LDIwLDI0LDIyXX19LCJleHAiOjE2MDAzMzU4NzEsImlhdCI6MTYwMDI0OTQ3MX0.erlp7z2EvA7Ofvzb897b2hnGtym_m1EPnIKVoZpSwws',
9 | }, // default
10 | connections: 20, // default
11 | pipelining: 1, // default
12 | duration: 5, // default
13 | },
14 | console.log
15 | );
16 |
--------------------------------------------------------------------------------
/config/config.local.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { v4: uuidv4 } = require('uuid');
3 |
4 | exports.security = {
5 | csrf: {
6 | enable: false,
7 | },
8 | };
9 |
10 | exports.sequelize = {
11 | dialect: 'mysql',
12 | host: '127.0.0.1',
13 | port: 33066,
14 | password: '123123',
15 | database: 'egg-beehive-dev',
16 | timezone: '+08:00',
17 | define: {
18 | raw: true,
19 | underscored: false,
20 | charset: 'utf8',
21 | timestamp: true,
22 | createdAt: 'created_at',
23 | updatedAt: 'updated_at',
24 | deletedAt: 'deleted_at',
25 | },
26 | dialectOptions: {
27 | dateStrings: true,
28 | typeCast: true,
29 | // collate: 'utf8_general_ci',
30 | },
31 | };
32 |
33 | exports.cors = {
34 | // origin: [ 'http://192.168.6.150:8080' ],
35 | // allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',
36 | };
37 |
38 | exports.redis = {
39 | client: {
40 | port: 6379,
41 | host: '127.0.0.1',
42 | password: '123123',
43 | db: 1,
44 | },
45 | };
46 |
47 | exports.jwt_exp = 60 * 60 * 24 * 90; // 开发环境下,jwt过期时间(秒)
48 |
49 | exports.io = {
50 | init: {
51 | // transports: ['websocket'],
52 | // pingInterval: 5000,
53 | // allowEIO3: true,
54 | }, // passed to engine.io
55 | namespace: {
56 | '/': {
57 | connectionMiddleware: ['connection'],
58 | packetMiddleware: ['packet'],
59 | },
60 | },
61 | redis: {
62 | host: '127.0.0.1',
63 | port: 6379,
64 | password: '123123',
65 | db: 3,
66 | },
67 | generateId: req => {
68 | // 自定义 socket.id 生成函数
69 | // const data = qs.parse(req.url.split('?')[1]);
70 | return `${req._query.userId}_${uuidv4()}`; // custom id must be unique
71 | },
72 | };
73 |
74 | // github授权登录请求参数
75 | exports.github = {
76 | access_token_url: 'https://github.com/login/oauth/access_token',
77 | user_info_url: 'https://api.github.com/user',
78 | client_id: 'The client ID you received from GitHub for your app.',
79 | client_secret: 'The client secret you received from GitHub for your OAuth App.',
80 | };
81 |
82 | exports.oss = {
83 | client: {
84 | accessKeyId: 'xxx',
85 | accessKeySecret: 'xxx',
86 | bucket: 'xxx',
87 | endpoint: 'oss-cn-guangzhou.aliyuncs.com',
88 | timeout: '60s',
89 | },
90 | };
91 |
--------------------------------------------------------------------------------
/config/config.unittest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.security = {
4 | csrf: {
5 | enable: false,
6 | },
7 | };
8 |
9 | exports.sequelize = {
10 | dialect: 'mysql',
11 | host: '127.0.0.1',
12 | port: 33066,
13 | password: '123123',
14 | database: 'egg-beehive-test',
15 | timezone: '+08:00',
16 | define: {
17 | raw: true,
18 | underscored: false,
19 | charset: 'utf8',
20 | timestamp: true,
21 | createdAt: 'created_at',
22 | updatedAt: 'updated_at',
23 | deletedAt: 'deleted_at',
24 | dialectOptions: {
25 | dateStrings: true,
26 | typeCast: true,
27 | // collate: 'utf8_general_ci',
28 | },
29 | },
30 | };
31 |
32 | exports.redis = {
33 | client: {
34 | port: 6379,
35 | host: '127.0.0.1',
36 | password: '123123',
37 | db: 1,
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/config/plugin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /** @type Egg.EggPlugin */
4 | module.exports = {
5 | // had enabled by egg
6 | // static: {
7 | // enable: true,
8 | // }
9 | validate: {
10 | enable: true,
11 | package: 'egg-validate',
12 | },
13 | jwt: {
14 | enable: true,
15 | package: 'egg-jwt',
16 | },
17 | sequelize: {
18 | enable: true,
19 | package: 'egg-sequelize',
20 | },
21 | swaggerdoc: {
22 | enable: true,
23 | package: 'egg-swagger-doc',
24 | },
25 | cors: {
26 | enable: true,
27 | package: 'egg-cors',
28 | },
29 | healthy: {
30 | enable: true,
31 | package: 'egg-healthy',
32 | },
33 | mailer: {
34 | enable: true,
35 | package: 'egg-mailer',
36 | },
37 | redis: {
38 | enable: true,
39 | package: 'egg-redis',
40 | },
41 | alinode: {
42 | enable: false,
43 | package: 'egg-alinode',
44 | env: ['prod'],
45 | },
46 | io: {
47 | enable: true,
48 | package: 'egg-socket.io',
49 | },
50 | oss: {
51 | enable: true,
52 | package: 'egg-oss',
53 | },
54 | };
55 |
--------------------------------------------------------------------------------
/database/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "host": "127.0.0.1",
4 | "port": "33066",
5 | "username": "root",
6 | "password": "123123",
7 | "database": "egg-beehive-dev",
8 | "dialect": "mysql",
9 | "operatorsAliases": false
10 | },
11 | "test": {
12 | "host": "127.0.0.1",
13 | "port": "33066",
14 | "username": "root",
15 | "password": "123123",
16 | "database": "egg-beehive-test",
17 | "dialect": "mysql",
18 | "operatorsAliases": false
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-configuration.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'configurations',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | rsa_private_key: {
14 | type: Sequelize.STRING(1000),
15 | allowNull: false,
16 | defaultValue: '',
17 | comment: 'rsa私钥',
18 | },
19 | rsa_public_key: {
20 | type: Sequelize.STRING(1000),
21 | allowNull: false,
22 | defaultValue: '',
23 | comment: 'rsa公钥',
24 | },
25 | created_at: {
26 | allowNull: false,
27 | type: Sequelize.DATE,
28 | },
29 | updated_at: {
30 | allowNull: false,
31 | type: Sequelize.DATE,
32 | },
33 | },
34 | {}
35 | );
36 | },
37 | down: (queryInterface, Sequelize) => {
38 | return queryInterface.dropTable('configurations');
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-department.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'departments',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(60),
15 | allowNull: false,
16 | unique: true,
17 | defaultValue: '',
18 | comment: '部门名称',
19 | },
20 | owner_id: {
21 | type: Sequelize.INTEGER(11).UNSIGNED,
22 | allowNull: false,
23 | defaultValue: 0,
24 | comment: '拥有者ID',
25 | },
26 | parent_id: {
27 | type: Sequelize.INTEGER(11).UNSIGNED,
28 | allowNull: false,
29 | comment: '父ID',
30 | },
31 | sort: {
32 | type: Sequelize.INTEGER(11),
33 | allowNull: false,
34 | defaultValue: 0,
35 | comment: '排序,越大越靠前',
36 | },
37 | created_at: {
38 | allowNull: false,
39 | type: Sequelize.DATE,
40 | },
41 | updated_at: {
42 | allowNull: false,
43 | type: Sequelize.DATE,
44 | },
45 | },
46 | {}
47 | );
48 | },
49 | down: (queryInterface, Sequelize) => {
50 | return queryInterface.dropTable('departments');
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-invite.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'invites',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | uuid: {
14 | type: Sequelize.STRING(36),
15 | primaryKey: true,
16 | allowNull: false,
17 | unique: true,
18 | comment: '随机字符串ID',
19 | },
20 | actor_id: {
21 | type: Sequelize.INTEGER(11).UNSIGNED,
22 | allowNull: false,
23 | comment: '发起者ID',
24 | references: {
25 | model: 'users',
26 | key: 'id',
27 | },
28 | onUpdate: 'CASCADE',
29 | onDelete: 'CASCADE',
30 | },
31 | receiver_id: {
32 | type: Sequelize.INTEGER(11).UNSIGNED,
33 | allowNull: true,
34 | comment: '接受者ID',
35 | references: {
36 | model: 'users',
37 | key: 'id',
38 | },
39 | onUpdate: 'CASCADE',
40 | onDelete: 'CASCADE',
41 | },
42 | is_accept: {
43 | type: Sequelize.TINYINT(1),
44 | allowNull: false,
45 | defaultValue: '0',
46 | comment: '是否已接受.1为true,0为false',
47 | },
48 | group: {
49 | type: Sequelize.STRING(20),
50 | allowNull: false,
51 | defaultValue: 'Projects',
52 | comment: '邀请加入群体的类型',
53 | },
54 | group_id: {
55 | type: Sequelize.INTEGER(11).UNSIGNED,
56 | allowNull: false,
57 | comment: '邀请加入群体的ID',
58 | },
59 | expires: {
60 | type: Sequelize.DATE,
61 | allowNull: false,
62 | comment: '到期时间',
63 | },
64 | created_at: {
65 | allowNull: false,
66 | type: Sequelize.DATE,
67 | },
68 | updated_at: {
69 | allowNull: false,
70 | type: Sequelize.DATE,
71 | },
72 | },
73 | {}
74 | );
75 | },
76 | down: (queryInterface, Sequelize) => {
77 | return queryInterface.dropTable('invites');
78 | },
79 | };
80 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-message.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'messages',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | actor_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '发起者ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'CASCADE',
22 | onDelete: 'CASCADE',
23 | },
24 | receiver_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '接受者ID',
28 | references: {
29 | model: 'users',
30 | key: 'id',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | content: {
36 | type: Sequelize.TEXT,
37 | allowNull: false,
38 | comment: '内容',
39 | },
40 | is_read: {
41 | type: Sequelize.TINYINT(1),
42 | allowNull: false,
43 | defaultValue: '0',
44 | comment: '是否为已读.1为true,0为false',
45 | },
46 | type: {
47 | type: Sequelize.STRING(30),
48 | allowNull: false,
49 | defaultValue: 'inform',
50 | comment: '类型',
51 | },
52 | url: {
53 | type: Sequelize.STRING(255),
54 | allowNull: true,
55 | defaultValue: '',
56 | comment: '跳转路径',
57 | },
58 | created_at: {
59 | allowNull: false,
60 | type: Sequelize.DATE,
61 | },
62 | updated_at: {
63 | allowNull: false,
64 | type: Sequelize.DATE,
65 | },
66 | },
67 | {}
68 | );
69 | },
70 | down: (queryInterface, Sequelize) => {
71 | return queryInterface.dropTable('messages');
72 | },
73 | };
74 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-operation_log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'operation_logs',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | operator_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '发起者ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'CASCADE',
22 | onDelete: 'CASCADE',
23 | },
24 | operator_username: {
25 | type: Sequelize.STRING(60),
26 | allowNull: false,
27 | comment: '发起者用户名',
28 | references: {
29 | model: 'users',
30 | key: 'username',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | status: {
36 | type: Sequelize.STRING(15),
37 | allowNull: false,
38 | comment: '请求返回状态',
39 | },
40 | ip: {
41 | type: Sequelize.STRING(100),
42 | allowNull: false,
43 | comment: '请求ip地址',
44 | },
45 | method: {
46 | type: Sequelize.STRING(15),
47 | allowNull: false,
48 | comment: '请求方法',
49 | },
50 | url: {
51 | type: Sequelize.STRING(255),
52 | allowNull: false,
53 | comment: '请求路径',
54 | },
55 | params: {
56 | type: Sequelize.TEXT,
57 | allowNull: false,
58 | comment: '请求参数',
59 | },
60 | created_at: {
61 | allowNull: false,
62 | type: Sequelize.DATE,
63 | },
64 | updated_at: {
65 | allowNull: false,
66 | type: Sequelize.DATE,
67 | },
68 | },
69 | {}
70 | );
71 | },
72 | down: (queryInterface, Sequelize) => {
73 | return queryInterface.dropTable('operation_logs');
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-permission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'permissions',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(60),
15 | allowNull: false,
16 | defaultValue: '',
17 | comment: '资源名',
18 | },
19 | mark: {
20 | type: Sequelize.STRING(60),
21 | allowNull: false,
22 | defaultValue: '',
23 | comment: '标识码',
24 | },
25 | mark_name: {
26 | type: Sequelize.STRING(60),
27 | allowNull: false,
28 | defaultValue: '',
29 | comment: '标识码中文名',
30 | },
31 | url: {
32 | type: Sequelize.STRING(255),
33 | allowNull: false,
34 | defaultValue: '',
35 | comment: '路径',
36 | },
37 | action: {
38 | type: Sequelize.STRING(60),
39 | allowNull: false,
40 | defaultValue: '',
41 | comment: '动作',
42 | },
43 | description: {
44 | type: Sequelize.STRING(255),
45 | allowNull: false,
46 | defaultValue: '',
47 | comment: '描述',
48 | },
49 | state: {
50 | type: Sequelize.TINYINT(1),
51 | allowNull: false,
52 | defaultValue: 1,
53 | comment: '状态.1为true,0为false',
54 | },
55 | authentication: {
56 | type: Sequelize.TINYINT(1),
57 | allowNull: false,
58 | defaultValue: 1,
59 | comment: '是否需要认证.1为true,0为false',
60 | },
61 | authorization: {
62 | type: Sequelize.TINYINT(1),
63 | allowNull: false,
64 | defaultValue: 1,
65 | comment: '是否需要授权.1为true,0为false',
66 | },
67 | created_at: {
68 | allowNull: false,
69 | type: Sequelize.DATE,
70 | },
71 | updated_at: {
72 | allowNull: false,
73 | type: Sequelize.DATE,
74 | },
75 | },
76 | {}
77 | );
78 | },
79 | down: (queryInterface, Sequelize) => {
80 | return queryInterface.dropTable('permissions');
81 | },
82 | };
83 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable('roles', {
5 | id: {
6 | allowNull: false,
7 | autoIncrement: true,
8 | primaryKey: true,
9 | type: Sequelize.INTEGER(11).UNSIGNED,
10 | },
11 | name: {
12 | allowNull: false,
13 | unique: true,
14 | type: Sequelize.STRING(50),
15 | },
16 | is_default: {
17 | type: Sequelize.TINYINT(1),
18 | allowNull: false,
19 | defaultValue: 0,
20 | comment: '是否为默认角色:0.非默认、1.默认',
21 | },
22 | created_at: {
23 | allowNull: false,
24 | type: Sequelize.DATE,
25 | },
26 | updated_at: {
27 | allowNull: false,
28 | type: Sequelize.DATE,
29 | },
30 | });
31 | },
32 | down: (queryInterface, Sequelize) => {
33 | return queryInterface.dropTable('roles');
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_list.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_lists',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(30),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: false,
18 | comment: '任务列表名称',
19 | },
20 | project_id: {
21 | type: Sequelize.INTEGER(11).UNSIGNED,
22 | allowNull: false,
23 | comment: '所属项目ID',
24 | references: {
25 | model: 'projects',
26 | key: 'id',
27 | },
28 | onUpdate: 'CASCADE',
29 | onDelete: 'CASCADE',
30 | },
31 | sort: {
32 | type: Sequelize.INTEGER(11),
33 | allowNull: false,
34 | defaultValue: '0',
35 | comment: '排序,越大越靠前',
36 | },
37 | created_at: {
38 | allowNull: false,
39 | type: Sequelize.DATE,
40 | },
41 | updated_at: {
42 | allowNull: false,
43 | type: Sequelize.DATE,
44 | },
45 | },
46 | {}
47 | );
48 | },
49 | down: (queryInterface, Sequelize) => {
50 | return queryInterface.dropTable('task_lists');
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_priority.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_prioritys',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(30),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: true,
18 | comment: '优先级名称',
19 | },
20 | color: {
21 | type: Sequelize.STRING(10),
22 | allowNull: false,
23 | defaultValue: '',
24 | unique: false,
25 | comment: '颜色',
26 | },
27 | sort: {
28 | type: Sequelize.INTEGER(11),
29 | allowNull: false,
30 | defaultValue: '0',
31 | comment: '排序,越大越靠前',
32 | },
33 | created_at: {
34 | allowNull: false,
35 | type: Sequelize.DATE,
36 | },
37 | updated_at: {
38 | allowNull: false,
39 | type: Sequelize.DATE,
40 | },
41 | },
42 | {}
43 | );
44 | },
45 | down: (queryInterface, Sequelize) => {
46 | return queryInterface.dropTable('task_prioritys');
47 | },
48 | };
49 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_state.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_states',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(50),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: true,
18 | comment: '状态名称',
19 | },
20 | color: {
21 | type: Sequelize.STRING(16),
22 | allowNull: false,
23 | defaultValue: '',
24 | unique: false,
25 | comment: '颜色',
26 | },
27 | icon: {
28 | type: Sequelize.STRING(50),
29 | allowNull: false,
30 | defaultValue: '',
31 | comment: '图标',
32 | },
33 | sort: {
34 | type: Sequelize.INTEGER(11),
35 | allowNull: false,
36 | defaultValue: '0',
37 | comment: '排序,越大越靠前',
38 | },
39 | created_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | updated_at: {
44 | allowNull: false,
45 | type: Sequelize.DATE,
46 | },
47 | },
48 | {}
49 | );
50 | },
51 | down: (queryInterface, Sequelize) => {
52 | return queryInterface.dropTable('task_states');
53 | },
54 | };
55 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_tags',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(100),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: true,
18 | comment: '标签名称',
19 | },
20 | color: {
21 | type: Sequelize.STRING(30),
22 | allowNull: false,
23 | defaultValue: '',
24 | unique: false,
25 | comment: '颜色',
26 | },
27 | project_id: {
28 | type: Sequelize.INTEGER(11).UNSIGNED,
29 | allowNull: false,
30 | comment: '所属项目ID',
31 | references: {
32 | model: 'projects',
33 | key: 'id',
34 | },
35 | onUpdate: 'CASCADE',
36 | onDelete: 'CASCADE',
37 | },
38 | created_at: {
39 | allowNull: false,
40 | type: Sequelize.DATE,
41 | },
42 | updated_at: {
43 | allowNull: false,
44 | type: Sequelize.DATE,
45 | },
46 | },
47 | {}
48 | );
49 | },
50 | down: (queryInterface, Sequelize) => {
51 | return queryInterface.dropTable('task_tags');
52 | },
53 | };
54 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_task_tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_task_tags',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | task_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '任务ID',
17 | references: {
18 | model: 'tasks',
19 | key: 'id',
20 | },
21 | onUpdate: 'CASCADE',
22 | onDelete: 'CASCADE',
23 | },
24 | task_tag_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '任务标签ID',
28 | references: {
29 | model: 'task_tags',
30 | key: 'id',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | project_id: {
36 | type: Sequelize.INTEGER(11).UNSIGNED,
37 | allowNull: false,
38 | comment: '所属项目ID',
39 | references: {
40 | model: 'projects',
41 | key: 'id',
42 | },
43 | onUpdate: 'CASCADE',
44 | onDelete: 'CASCADE',
45 | },
46 | created_at: {
47 | allowNull: false,
48 | type: Sequelize.DATE,
49 | },
50 | updated_at: {
51 | allowNull: false,
52 | type: Sequelize.DATE,
53 | },
54 | },
55 | {}
56 | );
57 | },
58 | down: (queryInterface, Sequelize) => {
59 | return queryInterface.dropTable('task_task_tags');
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-task_type.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'task_types',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(50),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: true,
18 | comment: '类型名称',
19 | },
20 | color: {
21 | type: Sequelize.STRING(16),
22 | allowNull: false,
23 | defaultValue: '',
24 | unique: false,
25 | comment: '颜色',
26 | },
27 | icon: {
28 | type: Sequelize.STRING(50),
29 | allowNull: false,
30 | defaultValue: '',
31 | comment: '图标',
32 | },
33 | sort: {
34 | type: Sequelize.INTEGER(11),
35 | allowNull: false,
36 | defaultValue: '0',
37 | comment: '排序,越大越靠前',
38 | },
39 | created_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | updated_at: {
44 | allowNull: false,
45 | type: Sequelize.DATE,
46 | },
47 | },
48 | {}
49 | );
50 | },
51 | down: (queryInterface, Sequelize) => {
52 | return queryInterface.dropTable('task_types');
53 | },
54 | };
55 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-user_project.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'user_projects',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | user_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'NO ACTION',
22 | onDelete: 'NO ACTION',
23 | },
24 | project_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '项目ID',
28 | references: {
29 | model: 'projects',
30 | key: 'id',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | created_at: {
36 | allowNull: false,
37 | type: Sequelize.DATE,
38 | },
39 | updated_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | },
44 | {}
45 | );
46 | },
47 | down: (queryInterface, Sequelize) => {
48 | return queryInterface.dropTable('user_projects');
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-user_project_collect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'user_project_collects',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | user_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'CASCADE',
22 | onDelete: 'CASCADE',
23 | },
24 | project_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '项目ID',
28 | references: {
29 | model: 'projects',
30 | key: 'id',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | created_at: {
36 | allowNull: false,
37 | type: Sequelize.DATE,
38 | },
39 | updated_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | },
44 | {}
45 | );
46 | },
47 | down: (queryInterface, Sequelize) => {
48 | return queryInterface.dropTable('user_project_collects');
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-user_role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'user_roles',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | user_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'NO ACTION',
22 | onDelete: 'NO ACTION',
23 | },
24 | role_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '用户ID',
28 | references: {
29 | model: 'roles',
30 | key: 'id',
31 | },
32 | onUpdate: 'NO ACTION',
33 | onDelete: 'NO ACTION',
34 | },
35 | created_at: {
36 | allowNull: false,
37 | type: Sequelize.DATE,
38 | },
39 | updated_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | },
44 | {}
45 | );
46 | },
47 | down: (queryInterface, Sequelize) => {
48 | return queryInterface.dropTable('user_roles');
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-user_task.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'user_tasks',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | user_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'users',
19 | key: 'id',
20 | },
21 | onUpdate: 'CASCADE',
22 | onDelete: 'CASCADE',
23 | },
24 | task_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '任务ID',
28 | references: {
29 | model: 'tasks',
30 | key: 'id',
31 | },
32 | onUpdate: 'CASCADE',
33 | onDelete: 'CASCADE',
34 | },
35 | project_id: {
36 | type: Sequelize.INTEGER(11).UNSIGNED,
37 | allowNull: false,
38 | comment: '所属项目ID',
39 | references: {
40 | model: 'projects',
41 | key: 'id',
42 | },
43 | onUpdate: 'CASCADE',
44 | onDelete: 'CASCADE',
45 | },
46 | created_at: {
47 | allowNull: false,
48 | type: Sequelize.DATE,
49 | },
50 | updated_at: {
51 | allowNull: false,
52 | type: Sequelize.DATE,
53 | },
54 | },
55 | {
56 | uniqueKeys: {
57 | actions_unique: {
58 | fields: ['user_id', 'task_id'],
59 | },
60 | },
61 | }
62 | );
63 | },
64 | down: (queryInterface, Sequelize) => {
65 | return queryInterface.dropTable('user_tasks');
66 | },
67 | };
68 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-user_task_like.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'user_task_likes',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | user_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | unique: 'user_task_like_unique',
17 | comment: '用户ID',
18 | references: {
19 | model: 'users',
20 | key: 'id',
21 | },
22 | onUpdate: 'CASCADE',
23 | onDelete: 'CASCADE',
24 | },
25 | task_id: {
26 | type: Sequelize.INTEGER(11).UNSIGNED,
27 | allowNull: false,
28 | unique: 'user_task_like_unique',
29 | comment: '任务ID',
30 | references: {
31 | model: 'tasks',
32 | key: 'id',
33 | },
34 | onUpdate: 'CASCADE',
35 | onDelete: 'CASCADE',
36 | },
37 | project_id: {
38 | type: Sequelize.INTEGER(11).UNSIGNED,
39 | allowNull: false,
40 | comment: '项目ID',
41 | references: {
42 | model: 'projects',
43 | key: 'id',
44 | },
45 | onUpdate: 'CASCADE',
46 | onDelete: 'CASCADE',
47 | },
48 | created_at: {
49 | allowNull: false,
50 | type: Sequelize.DATE,
51 | },
52 | updated_at: {
53 | allowNull: false,
54 | type: Sequelize.DATE,
55 | },
56 | },
57 | {
58 | uniqueKeys: {
59 | user_task_like_unique: {
60 | fields: ['user_id', 'task_id'],
61 | },
62 | },
63 | }
64 | );
65 | },
66 | down: (queryInterface, Sequelize) => {
67 | return queryInterface.dropTable('user_task_likes');
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/database/migrations/20200110080210-create-verification_code.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'verification_codes',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | code: {
14 | type: Sequelize.STRING(60),
15 | allowNull: false,
16 | defaultValue: '',
17 | comment: '验证码',
18 | },
19 | target: {
20 | type: Sequelize.STRING(60),
21 | allowNull: false,
22 | defaultValue: '',
23 | comment: '验证码接受者',
24 | },
25 | type: {
26 | type: Sequelize.TINYINT(1),
27 | allowNull: false,
28 | defaultValue: 1,
29 | comment: '类型.1为邮箱验证码,2为手机验证码',
30 | },
31 | available: {
32 | type: Sequelize.TINYINT(1),
33 | allowNull: false,
34 | defaultValue: 1,
35 | comment: '是否可用.1为true,0为false',
36 | },
37 | expiration_time: {
38 | type: Sequelize.DATE,
39 | allowNull: false,
40 | comment: '过期时间',
41 | },
42 | created_at: {
43 | allowNull: false,
44 | type: Sequelize.DATE,
45 | },
46 | updated_at: {
47 | allowNull: false,
48 | type: Sequelize.DATE,
49 | },
50 | },
51 | {}
52 | );
53 | },
54 | down: (queryInterface, Sequelize) => {
55 | return queryInterface.dropTable('verification_codes');
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/database/migrations/20200110080211-create-project_template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'project_templates',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(255),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: true,
18 | comment: '项目模板名称',
19 | },
20 | cover: {
21 | type: Sequelize.STRING(255),
22 | allowNull: false,
23 | defaultValue: '',
24 | comment: '项目模板封面',
25 | },
26 | is_custom: {
27 | type: Sequelize.TINYINT(1),
28 | allowNull: false,
29 | defaultValue: '1',
30 | comment: '是否为自定义模板.1为true,0为false',
31 | },
32 | intro: {
33 | type: Sequelize.STRING(255),
34 | allowNull: false,
35 | defaultValue: '',
36 | comment: '简介',
37 | },
38 | created_at: {
39 | allowNull: false,
40 | type: Sequelize.DATE,
41 | },
42 | updated_at: {
43 | allowNull: false,
44 | type: Sequelize.DATE,
45 | },
46 | },
47 | {}
48 | );
49 | },
50 | down: (queryInterface, Sequelize) => {
51 | return queryInterface.dropTable('project_templates');
52 | },
53 | };
54 |
--------------------------------------------------------------------------------
/database/migrations/20200110080211-create-role_menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'role_menus',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | role_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'roles',
19 | key: 'id',
20 | },
21 | onUpdate: 'NO ACTION',
22 | onDelete: 'NO ACTION',
23 | },
24 | menu_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '菜单ID',
28 | references: {
29 | model: 'menus',
30 | key: 'id',
31 | },
32 | onUpdate: 'NO ACTION',
33 | onDelete: 'NO ACTION',
34 | },
35 | created_at: {
36 | allowNull: false,
37 | type: Sequelize.DATE,
38 | },
39 | updated_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | },
44 | {}
45 | );
46 | },
47 | down: (queryInterface, Sequelize) => {
48 | return queryInterface.dropTable('role_menus');
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/database/migrations/20200110080211-create-role_permission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'role_permissions',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | role_id: {
14 | type: Sequelize.INTEGER(11).UNSIGNED,
15 | allowNull: false,
16 | comment: '用户ID',
17 | references: {
18 | model: 'roles',
19 | key: 'id',
20 | },
21 | onUpdate: 'NO ACTION',
22 | onDelete: 'NO ACTION',
23 | },
24 | permission_id: {
25 | type: Sequelize.INTEGER(11).UNSIGNED,
26 | allowNull: false,
27 | comment: '资源ID',
28 | references: {
29 | model: 'permissions',
30 | key: 'id',
31 | },
32 | onUpdate: 'NO ACTION',
33 | onDelete: 'NO ACTION',
34 | },
35 | created_at: {
36 | allowNull: false,
37 | type: Sequelize.DATE,
38 | },
39 | updated_at: {
40 | allowNull: false,
41 | type: Sequelize.DATE,
42 | },
43 | },
44 | {}
45 | );
46 | },
47 | down: (queryInterface, Sequelize) => {
48 | return queryInterface.dropTable('role_permissions');
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/database/migrations/20200110080212-create-project_template_task.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | 'project_template_tasks',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | name: {
14 | type: Sequelize.STRING(255),
15 | allowNull: false,
16 | defaultValue: '',
17 | unique: false,
18 | comment: '项目模板名称',
19 | },
20 | project_template_id: {
21 | type: Sequelize.INTEGER(11).UNSIGNED,
22 | allowNull: false,
23 | comment: '所属项目模板ID',
24 | references: {
25 | model: 'project_templates',
26 | key: 'id',
27 | },
28 | onUpdate: 'CASCADE',
29 | onDelete: 'CASCADE',
30 | },
31 | sort: {
32 | type: Sequelize.INTEGER(11),
33 | allowNull: false,
34 | defaultValue: '0',
35 | comment: '排序,越大越靠前',
36 | },
37 | created_at: {
38 | allowNull: false,
39 | type: Sequelize.DATE,
40 | },
41 | updated_at: {
42 | allowNull: false,
43 | type: Sequelize.DATE,
44 | },
45 | },
46 | {}
47 | );
48 | },
49 | down: (queryInterface, Sequelize) => {
50 | return queryInterface.dropTable('project_template_tasks');
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/example/2021-09-12_112646.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_112646.png
--------------------------------------------------------------------------------
/example/2021-09-12_113236.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_113236.png
--------------------------------------------------------------------------------
/example/2021-09-12_113312.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_113312.png
--------------------------------------------------------------------------------
/example/2021-09-12_113341.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_113341.png
--------------------------------------------------------------------------------
/example/2021-09-12_113612.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_113612.png
--------------------------------------------------------------------------------
/example/2021-09-12_113839.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_113839.png
--------------------------------------------------------------------------------
/example/2021-09-12_114307.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_114307.png
--------------------------------------------------------------------------------
/example/2021-09-12_115100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_115100.png
--------------------------------------------------------------------------------
/example/2021-09-12_115127.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_115127.png
--------------------------------------------------------------------------------
/example/2021-09-12_115228.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-12_115228.png
--------------------------------------------------------------------------------
/example/2021-09-13_021734.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/2021-09-13_021734.png
--------------------------------------------------------------------------------
/example/Beehive-data-model.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/Beehive-data-model.jpg
--------------------------------------------------------------------------------
/example/Beehive功能设计.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/Beehive功能设计.jpg
--------------------------------------------------------------------------------
/example/Beehive后端设计.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/Beehive后端设计.jpg
--------------------------------------------------------------------------------
/example/home-page.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/example/home-page.gif
--------------------------------------------------------------------------------
/generator/config-department.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'department',
4 | cname: '部门',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'name',
32 | type: 'string',
33 | length: 60,
34 | max: 60,
35 | trim: true,
36 | unique: true,
37 | required: true,
38 | description: '部门名称', // 供swagger使用
39 | example: '研发部', // 供swagger使用
40 | allowNull: false, // 是否允许为空
41 | defaultValue: '', // 数据库表中字段的默认值
42 | comment: '部门名称', // 数据库表中字段的描述
43 | },
44 | {
45 | name: 'owner_id',
46 | type: 'INTEGER',
47 | length: 11,
48 | min: 0,
49 | required: true,
50 | description: '拥有者ID', // 供swagger使用
51 | example: 0, // 供swagger使用
52 | allowNull: false, // 是否允许为空
53 | defaultValue: 0, // 数据库表中字段的默认值
54 | comment: '拥有者ID', // 数据库表中字段的描述
55 | },
56 | {
57 | name: 'parent_id',
58 | type: 'INTEGER',
59 | length: 11,
60 | min: 0,
61 | required: true,
62 | description: '父ID', // 供swagger使用
63 | example: 0, // 供swagger使用
64 | allowNull: false, // 是否允许为空
65 | comment: '父ID', // 数据库表中字段的描述
66 | },
67 | {
68 | name: 'sort',
69 | type: 'INTEGER',
70 | length: 11,
71 | max: 999999999,
72 | required: false,
73 | description: '排序,越大越靠前', // 供swagger使用
74 | example: 0, // 供swagger使用
75 | allowNull: false, // 是否允许为空
76 | defaultValue: 0, // 数据库表中字段的默认值
77 | comment: '排序,越大越靠前', // 数据库表中字段的描述
78 | },
79 | ],
80 | fields_option: {},
81 | };
82 |
--------------------------------------------------------------------------------
/generator/config-project-participant.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'project_participant',
4 | cname: '项目-参与者关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | references: {
20 | // 外键设置
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'project_id',
30 | type: 'INTEGER',
31 | length: 11,
32 | min: 1,
33 | required: true,
34 | description: '项目ID', // 供swagger使用
35 | example: 1, // 供swagger使用
36 | allowNull: false, // 是否允许为空
37 | comment: '项目ID', // 数据库表中字段的描述
38 | references: {
39 | // 外键设置
40 | model: 'projects', // 外键关联表
41 | key: 'id', // 外键字段名
42 | },
43 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
44 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | },
46 | {
47 | name: 'user_id',
48 | type: 'INTEGER',
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '用户ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '用户ID', // 数据库表中字段的描述
56 | // 外键设置
57 | references: {
58 | model: 'users', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | ],
65 | fields_option: {},
66 | };
67 |
--------------------------------------------------------------------------------
/generator/config-project-template-task.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'project_template_task',
4 | cname: '项目模板任务',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'name',
32 | type: 'string',
33 | length: 255,
34 | max: 255,
35 | min: 2,
36 | trim: true,
37 | required: true,
38 | description: '项目模板任务名称', // 供swagger使用
39 | example: '项目模板任务名称', // 供swagger使用
40 | allowNull: false, // 是否允许为空
41 | defaultValue: '', // 数据库表中字段的默认值
42 | comment: '项目模板名称', // 数据库表中字段的描述
43 | unique: false, // 是否唯一
44 | },
45 | {
46 | name: 'project_template_id',
47 | type: 'INTEGER',
48 | length: 11,
49 | min: 1,
50 | required: true,
51 | description: '所属项目模板ID', // 供swagger使用
52 | example: 1, // 供swagger使用
53 | allowNull: false, // 是否允许为空
54 | comment: '所属项目模板ID', // 数据库表中字段的描述
55 | // 外键设置
56 | references: {
57 | model: 'project_templates', // 外键关联表
58 | key: 'id', // 外键字段名
59 | },
60 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
61 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | },
63 | {
64 | name: 'sort',
65 | type: 'INTEGER',
66 | length: 11,
67 | max: 999999999,
68 | required: false,
69 | description: '排序,越大越靠前', // 供swagger使用
70 | example: 0, // 供swagger使用
71 | allowNull: false, // 是否允许为空
72 | defaultValue: 0, // 数据库表中字段的默认值
73 | comment: '排序,越大越靠前', // 数据库表中字段的描述
74 | },
75 | ],
76 | fields_option: {},
77 | };
78 |
--------------------------------------------------------------------------------
/generator/config-role-menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'role_menu',
4 | cname: '角色-菜单关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | references: {
20 | // 外键设置
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'role_id',
30 | type: 'INTEGER',
31 | length: 11,
32 | min: 1,
33 | required: true,
34 | description: '角色ID', // 供swagger使用
35 | example: 1, // 供swagger使用
36 | allowNull: false, // 是否允许为空
37 | comment: '用户ID', // 数据库表中字段的描述
38 | references: {
39 | // 外键设置
40 | model: 'roles', // 外键关联表
41 | key: 'id', // 外键字段名
42 | },
43 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
44 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | },
46 | {
47 | name: 'menu_id',
48 | type: 'INTEGER',
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '菜单ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '菜单ID', // 数据库表中字段的描述
56 | // 外键设置
57 | references: {
58 | model: 'menus', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | ],
65 | fields_option: {},
66 | };
67 |
--------------------------------------------------------------------------------
/generator/config-role_permission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'role_permission',
4 | cname: '角色-资源关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | // 外键设置
20 | references: {
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'role_id',
30 | type: 'INTEGER',
31 | length: 11,
32 | min: 1,
33 | required: true,
34 | description: '角色ID', // 供swagger使用
35 | example: 1, // 供swagger使用
36 | allowNull: false, // 是否允许为空
37 | comment: '用户ID', // 数据库表中字段的描述
38 | references: {
39 | // 外键设置
40 | model: 'roles', // 外键关联表
41 | key: 'id', // 外键字段名
42 | },
43 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
44 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | },
46 | {
47 | name: 'permission_id',
48 | type: 'INTEGER',
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '资源ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '资源ID', // 数据库表中字段的描述
56 | references: {
57 | // 外键设置
58 | model: 'permissions', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | ],
65 | fields_option: {},
66 | };
67 |
--------------------------------------------------------------------------------
/generator/config-task-list.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'task_list',
4 | cname: '任务列表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'name',
32 | type: 'string',
33 | length: 30,
34 | max: 30,
35 | min: 1,
36 | trim: true,
37 | required: true,
38 | description: '任务列表名称', // 供swagger使用
39 | example: '任务列表名称', // 供swagger使用
40 | allowNull: false, // 是否允许为空
41 | defaultValue: '', // 数据库表中字段的默认值
42 | comment: '任务列表名称', // 数据库表中字段的描述
43 | unique: false, // 是否唯一
44 | },
45 | {
46 | name: 'project_id',
47 | type: 'INTEGER',
48 | UNSIGNED: true,
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '所属项目ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '所属项目ID', // 数据库表中字段的描述
56 | // 外键设置
57 | references: {
58 | model: 'projects', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | {
65 | name: 'sort',
66 | type: 'INTEGER',
67 | length: 11,
68 | max: 999999999,
69 | required: false,
70 | description: '排序,越大越靠前', // 供swagger使用
71 | example: 0, // 供swagger使用
72 | allowNull: false, // 是否允许为空
73 | defaultValue: 0, // 数据库表中字段的默认值
74 | comment: '排序,越大越靠前', // 数据库表中字段的描述
75 | },
76 | ],
77 | fields_option: {},
78 | };
79 |
--------------------------------------------------------------------------------
/generator/config-task-priority.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'task_priority',
4 | cname: '任务优先级',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'name',
32 | type: 'string',
33 | length: 30,
34 | max: 30,
35 | min: 1,
36 | trim: true,
37 | required: true,
38 | description: '优先级名称', // 供swagger使用
39 | example: '优先级名称', // 供swagger使用
40 | allowNull: false, // 是否允许为空
41 | defaultValue: '', // 数据库表中字段的默认值
42 | comment: '优先级名称', // 数据库表中字段的描述
43 | unique: true, // 是否唯一
44 | },
45 | {
46 | name: 'color',
47 | type: 'string',
48 | length: 10,
49 | max: 10,
50 | min: 1,
51 | trim: true,
52 | required: true,
53 | description: '颜色', // 供swagger使用
54 | example: '颜色', // 供swagger使用
55 | allowNull: false, // 是否允许为空
56 | defaultValue: '', // 数据库表中字段的默认值
57 | comment: '颜色', // 数据库表中字段的描述
58 | unique: false, // 是否唯一
59 | },
60 | {
61 | name: 'sort',
62 | type: 'INTEGER',
63 | length: 11,
64 | max: 999999999,
65 | required: false,
66 | description: '排序,越大越靠前', // 供swagger使用
67 | example: 0, // 供swagger使用
68 | allowNull: false, // 是否允许为空
69 | defaultValue: 0, // 数据库表中字段的默认值
70 | comment: '排序,越大越靠前', // 数据库表中字段的描述
71 | },
72 | ],
73 | fields_option: {},
74 | };
75 |
--------------------------------------------------------------------------------
/generator/config-task-tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'task_tag',
4 | cname: '任务标签',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'name',
32 | type: 'string',
33 | length: 100,
34 | max: 100,
35 | min: 1,
36 | trim: true,
37 | required: true,
38 | description: '标签名称', // 供swagger使用
39 | example: '标签名称', // 供swagger使用
40 | allowNull: false, // 是否允许为空
41 | defaultValue: '', // 数据库表中字段的默认值
42 | comment: '标签名称', // 数据库表中字段的描述
43 | unique: true, // 是否唯一
44 | },
45 | {
46 | name: 'color',
47 | type: 'string',
48 | length: 30,
49 | max: 30,
50 | min: 1,
51 | trim: true,
52 | required: true,
53 | description: '颜色', // 供swagger使用
54 | example: '颜色', // 供swagger使用
55 | allowNull: false, // 是否允许为空
56 | defaultValue: '', // 数据库表中字段的默认值
57 | comment: '颜色', // 数据库表中字段的描述
58 | unique: false, // 是否唯一
59 | },
60 | {
61 | name: 'project_id',
62 | UNSIGNED: true,
63 | type: 'INTEGER',
64 | length: 11,
65 | min: 1,
66 | required: true,
67 | description: '所属项目ID', // 供swagger使用
68 | example: 1, // 供swagger使用
69 | allowNull: false, // 是否允许为空
70 | comment: '所属项目ID', // 数据库表中字段的描述
71 | // 外键设置
72 | references: {
73 | model: 'projects', // 外键关联表
74 | key: 'id', // 外键字段名
75 | },
76 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
77 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
78 | },
79 | ],
80 | fields_option: {},
81 | };
82 |
--------------------------------------------------------------------------------
/generator/config-user-project-collect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'user_project_collect',
4 | cname: '用户-项目-收藏关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | references: {
20 | // 外键设置
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'user_id',
30 | type: 'INTEGER',
31 | UNSIGNED: true,
32 | length: 11,
33 | min: 1,
34 | required: true,
35 | description: '用户ID', // 供swagger使用
36 | example: 1, // 供swagger使用
37 | allowNull: false, // 是否允许为空
38 | comment: '用户ID', // 数据库表中字段的描述
39 | // 外键设置
40 | references: {
41 | model: 'users', // 外键关联表
42 | key: 'id', // 外键字段名
43 | },
44 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
46 | },
47 | {
48 | name: 'project_id',
49 | type: 'INTEGER',
50 | UNSIGNED: true,
51 | length: 11,
52 | min: 1,
53 | required: true,
54 | description: '项目ID', // 供swagger使用
55 | example: 1, // 供swagger使用
56 | allowNull: false, // 是否允许为空
57 | comment: '项目ID', // 数据库表中字段的描述
58 | references: {
59 | // 外键设置
60 | model: 'projects', // 外键关联表
61 | key: 'id', // 外键字段名
62 | },
63 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
64 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
65 | },
66 | ],
67 | fields_option: {},
68 | };
69 |
--------------------------------------------------------------------------------
/generator/config-user-project.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'user_project',
4 | cname: '用户-项目关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | references: {
20 | // 外键设置
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'user_id',
30 | type: 'INTEGER',
31 | length: 11,
32 | min: 1,
33 | required: true,
34 | description: '用户ID', // 供swagger使用
35 | example: 1, // 供swagger使用
36 | allowNull: false, // 是否允许为空
37 | comment: '用户ID', // 数据库表中字段的描述
38 | // 外键设置
39 | references: {
40 | model: 'users', // 外键关联表
41 | key: 'id', // 外键字段名
42 | },
43 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
44 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | },
46 | {
47 | name: 'project_id',
48 | type: 'INTEGER',
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '项目ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '项目ID', // 数据库表中字段的描述
56 | references: {
57 | // 外键设置
58 | model: 'projects', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | ],
65 | fields_option: {},
66 | };
67 |
--------------------------------------------------------------------------------
/generator/config-user-role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'user_role',
4 | cname: '用户-角色关系表',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | required: true,
12 | description: '这里是描述', // 供swagger使用
13 | primaryKey: false, // 是否为主键
14 | unique: false, // 是否唯一
15 | allowNull: false, // 是否允许为空
16 | autoIncrement: false, // 是否自增
17 | defaultValue: '', // 数据库表中字段的默认值
18 | comment: '外键', // 数据库表中字段的描述
19 | references: {
20 | // 外键设置
21 | model: 'xxxs', // 外键关联表
22 | key: 'id', // 外键字段名
23 | },
24 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
25 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
26 | },
27 | fields: [
28 | {
29 | name: 'user_id',
30 | type: 'INTEGER',
31 | length: 11,
32 | min: 1,
33 | required: true,
34 | description: '用户ID', // 供swagger使用
35 | example: 1, // 供swagger使用
36 | allowNull: false, // 是否允许为空
37 | comment: '用户ID', // 数据库表中字段的描述
38 | // 外键设置
39 | references: {
40 | model: 'users', // 外键关联表
41 | key: 'id', // 外键字段名
42 | },
43 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
44 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
45 | },
46 | {
47 | name: 'role_id',
48 | type: 'INTEGER',
49 | length: 11,
50 | min: 1,
51 | required: true,
52 | description: '角色ID', // 供swagger使用
53 | example: 1, // 供swagger使用
54 | allowNull: false, // 是否允许为空
55 | comment: '用户ID', // 数据库表中字段的描述
56 | references: {
57 | // 外键设置
58 | model: 'roles', // 外键关联表
59 | key: 'id', // 外键字段名
60 | },
61 | onUpdate: 'CASCADE', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
62 | onDelete: 'CASCADE', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
63 | },
64 | ],
65 | fields_option: {},
66 | };
67 |
--------------------------------------------------------------------------------
/generator/config.configuration.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'configuration',
4 | cname: '配置',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | min: 1,
10 | max: 1,
11 | trim: true,
12 | required: true,
13 | description: '这里是描述', // 供swagger使用
14 | example: 0, // 供swagger使用
15 | allowNull: false, // 是否允许为空
16 | defaultValue: '', // 数据库表中字段的默认值
17 | comment: '外键', // 数据库表中字段的描述
18 | unique: false, // 是否唯一
19 | primaryKey: false, // 是否为主键
20 | autoIncrement: false, // 是否自增
21 | // 外键设置
22 | references: {
23 | model: 'xxxs', // 外键关联表
24 | key: 'id', // 外键字段名
25 | },
26 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
27 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
28 | },
29 | fields: [
30 | {
31 | name: 'rsa_private_key',
32 | type: 'string',
33 | length: 1000,
34 | max: 1000,
35 | trim: true,
36 | required: true,
37 | description: 'rsa私钥', // 供swagger使用
38 | example: '', // 供swagger使用
39 | allowNull: false, // 是否允许为空
40 | defaultValue: '', // 数据库表中字段的默认值
41 | comment: 'rsa私钥', // 数据库表中字段的描述
42 | },
43 | {
44 | name: 'rsa_public_key',
45 | type: 'string',
46 | length: 1000,
47 | max: 1000,
48 | trim: true,
49 | required: true,
50 | description: 'rsa公钥', // 供swagger使用
51 | example: '', // 供swagger使用
52 | allowNull: false, // 是否允许为空
53 | defaultValue: '', // 数据库表中字段的默认值
54 | comment: 'rsa公钥', // 数据库表中字段的描述
55 | },
56 | ],
57 | fields_option: {},
58 | };
59 |
--------------------------------------------------------------------------------
/generator/config.example.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | name: 'menu2',
4 | cname: '前端菜单2',
5 | fieldsItemExample: {
6 | name: 'xx_id',
7 | type: 'INTEGER',
8 | length: 11,
9 | required: true,
10 | description: '这里是描述', // 供swagger使用
11 | primaryKey: false, // 是否为主键
12 | unique: false, // 是否唯一
13 | allowNull: false, // 是否允许为空
14 | autoIncrement: false, // 是否自增
15 | defaultValue: '', // 数据库表中字段的默认值
16 | comment: '外键', // 数据库表中字段的描述
17 | // 外键设置
18 | references: {
19 | model: 'xxxs', // 外键关联表
20 | key: 'id', // 外键字段名
21 | },
22 | onUpdate: 'NO ACTION', // 外键更新约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
23 | onDelete: 'NO ACTION', // 外键删除约束 CASCADE RESTRICT SET NULL SET DEFAULT NO ACTION
24 | },
25 | fields: [
26 | {
27 | name: 'name',
28 | type: 'String',
29 | length: 50,
30 | required: true,
31 | allowNull: false,
32 | description: '路由名',
33 | },
34 | {
35 | name: 'parent_id',
36 | type: 'INTEGER',
37 | allowNull: false,
38 | required: true,
39 | description: '父ID',
40 | },
41 | {
42 | name: 'icon',
43 | type: 'string',
44 | length: 255,
45 | required: false,
46 | allowNull: true,
47 | description: '图标',
48 | },
49 | ],
50 | };
51 |
--------------------------------------------------------------------------------
/generator/delete-entity.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('./config');
5 | const escapeStringRegexp = require('escape-string-regexp');
6 |
7 | const files = [
8 | path.join(__dirname, '../', `/app/contract/request/${config.name}.js`),
9 | path.join(__dirname, '../', `/app/controller/v1/${config.name}s.js`),
10 | path.join(__dirname, '../', `/database/migrations/20200110080210-create-${config.name}.js`),
11 | path.join(__dirname, '../', `/app/model/${config.name}s.js`),
12 | path.join(__dirname, '../', `/app/service/${config.name}s.js`),
13 | path.join(__dirname, '../', `/test/app/controller/${config.name}s.test.js`),
14 | ];
15 |
16 | // 删除当前entity文件
17 | files.forEach((e, i) => {
18 | fs.unlinkSync(e);
19 | });
20 |
21 | // 删除router.js 中的路由
22 | let template = fs.readFileSync(path.join(__dirname, '../', './app/router.js'), 'utf8');
23 | const reg = new RegExp(`/api/v1/${config.name}`, 'ig');
24 | if (template.match(reg)) {
25 | let fields = `
26 | router.post('/api/v1/_objectName_s', controller.v1._objectName_s.create);
27 | router.put('/api/v1/_objectName_s', controller.v1._objectName_s.update);
28 | router.get('/api/v1/_objectName_s/list', controller.v1._objectName_s.findAll);
29 | router.get('/api/v1/_objectName_s', controller.v1._objectName_s.findOne);
30 | router.delete('/api/v1/_objectName_s', controller.v1._objectName_s.destroy);\n`;
31 | fields = fields.replace(/_objectName_/gi, config.name);
32 | // 转义特殊字符符合RegExp正则
33 | fields = escapeStringRegexp(fields);
34 | const reg1 = new RegExp(fields, 'ig');
35 | if (template.match(reg1)) {
36 | template = template.replace(reg1, '');
37 | // 写文件
38 | fs.writeFileSync(path.join(__dirname, '../', './app/router.js'), template);
39 | console.log(`deleted ${config.name} entity --- router`);
40 | }
41 | }
42 |
43 | console.log(`deleted ${config.name} entity`);
44 |
--------------------------------------------------------------------------------
/generator/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | require('./lib/migrations');
3 | require('./lib/model');
4 | require('./lib/contract');
5 | require('./lib/controller');
6 | require('./lib/service');
7 | require('./lib/router');
8 | require('./lib/test');
9 |
--------------------------------------------------------------------------------
/generator/lib/contract.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../template/contract/request/template.js'), 'utf8');
6 | // 替换对象名
7 | template = template.replace(/_objectName_/gi, config.name);
8 |
9 | let fields = '';
10 | // 循环添加字段
11 | config.fields.forEach((v, i) => {
12 | let type;
13 | switch (v.type.toUpperCase()) {
14 | case 'INTEGER':
15 | case 'TINYINT':
16 | type = 'number';
17 | break;
18 | case 'STRING':
19 | case 'TEXT':
20 | type = 'string';
21 | break;
22 | case 'DATE':
23 | type = 'date';
24 | break;
25 | default:
26 | break;
27 | }
28 | const item = {
29 | [v.name]: {
30 | type: `'${type.toLowerCase()}'`,
31 | required: v.required ? v.required : false,
32 | min: v.min,
33 | max: v.max,
34 | trim: v.trim,
35 | format: `${v.format}`,
36 | example: `'${v.example}'`,
37 | description: `'${v.description}'`,
38 | },
39 | };
40 | // 清除没有设置的属性
41 | for (const key in item[v.name]) {
42 | if (item[v.name][key] === undefined || item[v.name][key].toString()
43 | .match(/undefined/gi)) {
44 | delete item[v.name][key];
45 | }
46 | }
47 | // console.log(JSON.stringify(item, '', 1));
48 | fields += JSON.stringify(item, '', 1)
49 | .replace(/^\{\s+/, '')
50 | .replace(/\s+\}$/, `,${i === config.fields.length - 1 ? '' : '\n'}`)
51 | .replace(/"/gi, '')
52 | .replace(/\\\\\./gi, '\\.');
53 | });
54 | template = template.replace(/\$:\s'\{\{fields\}\}',/gi, fields);
55 |
56 | // 写文件
57 | fs.writeFileSync(path.join(__dirname, '../../', `/app/contract/request/${config.name}.js`), template);
58 | console.log('contract -- success');
59 |
--------------------------------------------------------------------------------
/generator/lib/controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../template/controller/template.js'), 'utf8');
6 |
7 | // 如果config.name中带有下划线分割,则改为驼峰
8 | const name_hump = config.name.replace(/\_(\w)/g, (all, letter) => letter.toUpperCase());
9 | console.log(config.name);
10 | console.log(name_hump);
11 | // 替换对象名
12 | template = template.replace(/_objectName_/gi, config.name);
13 | template = template.replace(/_objectNameHump_/gi, name_hump);
14 | template = template.replace(/__cname__/gi, config.cname);
15 |
16 | let fields = '';
17 | // 循环添加字段
18 | config.fields.forEach((v, i) => {
19 | const item = {
20 | [v.name]: {
21 | type: `'${v.type.toLowerCase()}'`,
22 | required: v.required ? v.required : false,
23 | },
24 | };
25 | // 清除没有设置的属性
26 | for (const key in item[v.name]) {
27 | if (item[v.name][key] === undefined) {
28 | delete item[v.name][key];
29 | }
30 | }
31 | // console.log(JSON.stringify(item, '', 1));
32 | fields += JSON.stringify(item, '', 1)
33 | .replace(/^\{\s+/, '')
34 | .replace(/\s+\}$/, `,${i === config.fields.length - 1 ? '' : '\n'}`)
35 | .replace(/"/gi, '');
36 | });
37 | template = template.replace(/\$:\s'\{\{fields\}\}',/gi, fields);
38 |
39 | // 写文件
40 | fs.writeFileSync(path.join(__dirname, '../../', `/app/controller/v1/${config.name}s.js`), template);
41 | console.log('controller -- success');
42 |
--------------------------------------------------------------------------------
/generator/lib/model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../template/model/template.js'), 'utf8');
6 | // 替换对象名
7 | template = template.replace(/_objectName_/gi, config.name);
8 |
9 | let fields = '';
10 | let fields_option = '';
11 |
12 | // 循环添加字段
13 | config.fields.forEach((v, i) => {
14 | const _length = typeof v.length === 'string' ? `'${v.length}'` : v.length;
15 | const length = v.length ? `(${_length})` : '';
16 | const item = {
17 | [v.name]: `Sequelize.${v.type.toUpperCase()}${length}`,
18 | };
19 | // 清除没有设置的属性
20 | for (const key in item[v.name]) {
21 | if (item[v.name][key] === undefined) {
22 | delete item[v.name][key];
23 | }
24 | }
25 | // console.log(JSON.stringify(item, '', 1));
26 | fields += JSON.stringify(item, '', 1)
27 | .replace(/^\{\s+/, '')
28 | .replace(/\s+\}$/, `,${i === config.fields.length - 1 ? '' : '\n'}`)
29 | .replace(/"/gi, '');
30 | });
31 | template = template.replace(/\$:\s'\{\{fields\}\}',/gi, fields);
32 |
33 | // 添加option
34 | fields_option += JSON.stringify(config.fields_option, '', 1)
35 | .replace(/^\{\s+/, '')
36 | .replace(/\s+\}$/, '')
37 | .replace(/"/gi, '');
38 | if (Object.keys(config.fields_option).length !== 0) {
39 | template = template.replace(/\$_:\s'\{\{fields\}\}',/gi, fields_option);
40 | } else {
41 | template = template.replace(/\$_:\s'\{\{fields\}\}',/gi, '');
42 | }
43 |
44 | // 写文件
45 | fs.writeFileSync(path.join(__dirname, '../../', `/app/model/${config.name}s.js`), template);
46 | console.log('model -- success');
47 |
--------------------------------------------------------------------------------
/generator/lib/router.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../../', './app/router.js'), 'utf8');
6 | // 如果config.name中带有下划线分割,则改为驼峰
7 | const name_hump = config.name.replace(/\_(\w)/g, (all, letter) => letter.toUpperCase());
8 |
9 | const reg = new RegExp(`'/api/v1/${config.name}'`, 'ig');
10 | if (template.match(reg)) {
11 | console.log(`router.js exist ${config.name}`);
12 | return;
13 | }
14 | let fields = `
15 | router.post('/api/v1/_objectName_s', controller.v1._objectNameHump_s.create);
16 | router.put('/api/v1/_objectName_s', controller.v1._objectNameHump_s.update);
17 | router.get('/api/v1/_objectName_s/list', controller.v1._objectNameHump_s.findAll);
18 | router.get('/api/v1/_objectName_s', controller.v1._objectNameHump_s.findOne);
19 | router.delete('/api/v1/_objectName_s', controller.v1._objectNameHump_s.destroy);\n};\n`;
20 |
21 | fields = fields.replace(/_objectName_/gi, config.name);
22 | fields = fields.replace(/_objectNameHump_/gi, name_hump);
23 | template = template.replace(/\};\s+$/gi, fields);
24 | // 写文件
25 | fs.writeFileSync(path.join(__dirname, '../../', './app/router.js'), template);
26 | console.log('router -- success');
27 |
--------------------------------------------------------------------------------
/generator/lib/service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../template/service/template.js'), 'utf8');
6 |
7 | // 如果config.name中带有下划线分割,则改为驼峰
8 | const name_hump = config.name.replace(/\_(\w)/g, (all, letter) => letter.toUpperCase());
9 |
10 | // 替换对象名
11 | // template = template.replace(/_objectName_/ig, config.name.toLowerCase().replace(/\w/, config.name.charAt(0).toUpperCase()));
12 | template = template.replace(/_objectNameHump_/gi, name_hump.replace(/\w/, name_hump.charAt(0)
13 | .toUpperCase()));
14 |
15 | let fields = '';
16 | // 循环添加字段
17 | config.fields.forEach((v, i) => {
18 | const item = {
19 | [v.name]: {
20 | type: `'${v.type.toLowerCase()}'`,
21 | required: v.required ? v.required : false,
22 | },
23 | };
24 | // 清除没有设置的属性
25 | for (const key in item[v.name]) {
26 | if (item[v.name][key] === undefined) {
27 | delete item[v.name][key];
28 | }
29 | }
30 | // console.log(JSON.stringify(item, '', 1));
31 | fields += JSON.stringify(item, '', 1)
32 | .replace(/^\{\s+/, '')
33 | .replace(/\s+\}$/, `,${i === config.fields.length - 1 ? '' : '\n'}`)
34 | .replace(/"/gi, '');
35 | });
36 | template = template.replace(/\$:\s'\{\{fields\}\}',/gi, fields);
37 |
38 | // 写文件
39 | fs.writeFileSync(path.join(__dirname, '../../', `/app/service/${config.name}s.js`), template);
40 | console.log('service -- success');
41 |
--------------------------------------------------------------------------------
/generator/lib/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const path = require('path');
4 | const config = require('../config');
5 | let template = fs.readFileSync(path.join(__dirname, '../template/test/controller/template.test.js'), 'utf8');
6 |
7 | // 替换对象名
8 | // template = template.replace(/_objectName_/ig, config.name.toLowerCase().replace(/\w/, config.name.charAt(0).toUpperCase()));
9 | template = template.replace(/_objectName_/gi, config.name);
10 | let fields = '';
11 | // 循环添加字段
12 | config.fields.forEach((v, i) => {
13 | const item = {
14 | [v.name]: {
15 | type: `'${v.type.toLowerCase()}'`,
16 | required: v.required ? v.required : false,
17 | },
18 | };
19 | // 清除没有设置的属性
20 | for (const key in item[v.name]) {
21 | if (item[v.name][key] === undefined) {
22 | delete item[v.name][key];
23 | }
24 | }
25 | // console.log(JSON.stringify(item, '', 1));
26 | fields += JSON.stringify(item, '', 1)
27 | .replace(/^\{\s+/, '')
28 | .replace(/\s+\}$/, `,${i === config.fields.length - 1 ? '' : '\n'}`)
29 | .replace(/"/gi, '');
30 | });
31 | template = template.replace(/\$:\s'\{\{fields\}\}',/gi, fields);
32 |
33 | // 写文件
34 | fs.writeFileSync(path.join(__dirname, '../../', `/test/app/controller/${config.name}s.test.js`), template);
35 | console.log('test -- success');
36 |
--------------------------------------------------------------------------------
/generator/template/contract/request/template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const body = {
4 | _objectName_Id: {
5 | id: { type: 'number', required: true, description: 'id' },
6 | },
7 | _objectName_BodyReq: {
8 | $: '{{fields}}',
9 | },
10 | };
11 |
12 | module.exports = {
13 | ...body,
14 | _objectName_PutBodyReq: {
15 | ...body._objectName_Id,
16 | ...body._objectName_BodyReq,
17 | },
18 | _objectName_DelBodyReq: {
19 | ids: {
20 | type: 'array',
21 | required: true,
22 | itemType: 'number',
23 | description: 'ids',
24 | example: [1, 2],
25 | },
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/generator/template/migrations/20200110080210-create-template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: (queryInterface, Sequelize) => {
4 | return queryInterface.createTable(
5 | '_objectName_s',
6 | {
7 | id: {
8 | allowNull: false,
9 | autoIncrement: true,
10 | primaryKey: true,
11 | type: Sequelize.INTEGER(11).UNSIGNED,
12 | },
13 | $: '{{fields}}',
14 | created_at: {
15 | allowNull: false,
16 | type: Sequelize.DATE,
17 | },
18 | updated_at: {
19 | allowNull: false,
20 | type: Sequelize.DATE,
21 | },
22 | },
23 | {
24 | $: '{{createTable_option}}',
25 | }
26 | );
27 | },
28 | down: (queryInterface, Sequelize) => {
29 | return queryInterface.dropTable('_objectName_s');
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/generator/template/model/template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = app => {
3 | const Sequelize = app.Sequelize;
4 |
5 | const _objectName_ = app.model.define(
6 | '_objectName_s',
7 | {
8 | id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
9 | $: '{{fields}}',
10 | },
11 | {
12 | $_: '{{fields}}',
13 | }
14 | );
15 | _objectName_.associate = function(models) {
16 | // associations can be defined here
17 | };
18 | return _objectName_;
19 | };
20 |
--------------------------------------------------------------------------------
/generator/template/service/template.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Service = require('egg').Service;
4 | const { Op } = require('sequelize');
5 |
6 | class _objectName_Service extends Service {
7 | async findAll(payload) {
8 | const { ctx } = this;
9 | const { limit, offset, prop_order, order, name } = payload;
10 | const where = payload.where;
11 | const Order = [];
12 | name ? (where.name = { [Op.like]: `%${name}%` }) : null;
13 | prop_order && order ? Order.push([prop_order, order]) : null;
14 | return await ctx.model._objectNameHump_s.findAndCountAll({
15 | limit,
16 | offset,
17 | where,
18 | order: Order,
19 | });
20 | }
21 |
22 | async findOne(id) {
23 | const { ctx } = this;
24 | return await ctx.model._objectNameHump_s.findOne({ where: { id } });
25 | }
26 |
27 | async create(payload) {
28 | const { ctx } = this;
29 | return await ctx.model._objectNameHump_s.create(payload);
30 | }
31 |
32 | async update(payload) {
33 | const { ctx } = this;
34 | return await ctx.model._objectNameHump_s.update(payload, {
35 | where: { id: payload.id },
36 | });
37 | }
38 |
39 | async destroy(payload) {
40 | const { ctx } = this;
41 | return await ctx.model._objectNameHump_s.destroy({
42 | where: { id: payload.ids },
43 | });
44 | }
45 | }
46 |
47 | module.exports = _objectName_Service;
48 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["**/*"]
3 | }
4 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | printWidth: 160,
5 | tabWidth: 2,
6 | useTabs: false,
7 | semi: true,
8 | singleQuote: true,
9 | quoteProps: 'as-needed',
10 | jsxSingleQuote: false,
11 | trailingComma: 'es5',
12 | bracketSpacing: true,
13 | jsxBracketSameLine: false,
14 | arrowParens: 'avoid',
15 | vueIndentScriptAndStyle: false,
16 | endOfLine: 'lf',
17 | };
18 |
--------------------------------------------------------------------------------
/public/uploads/1606988796827_683491316.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Imfdj/egg-beehive/3ac201d18c92d5d456312a1b0adaf053ba49d6bf/public/uploads/1606988796827_683491316.jpg
--------------------------------------------------------------------------------
/remoteConfig.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Title: $ 获取远端配置,写为本地文件
3 | * @Description: TODO
4 | * @author Imfdj
5 | * @date $ $
6 | */
7 | 'use strict';
8 | const ACMClient = require('acm-client').ACMClient;
9 | const path = require('path');
10 | const fs = require('fs');
11 |
12 | (async function() {
13 | const argv1 = process.argv.splice(2);
14 | const namespace = argv1.filter(e => e.includes('--namespace='))[0].split('--namespace=')[1];
15 | const accessKey = argv1.filter(e => e.includes('--accessKey='))[0].split('--accessKey=')[1];
16 | const secretKey = argv1.filter(e => e.includes('--secretKey='))[0].split('--secretKey=')[1];
17 | console.log(namespace);
18 | console.log(accessKey);
19 | console.log(secretKey);
20 | const acm = new ACMClient({
21 | endpoint: 'addr-bj-internal.edas.aliyun.com',
22 | namespace,
23 | accessKey,
24 | secretKey,
25 | requestTimeout: 12000, // Request timeout, 6s by default
26 | });
27 | // 务必要等待 ready
28 | await acm.ready();
29 | // 主动拉取配置
30 | const content = await acm.getConfig('imfdj', 'imfdj');
31 | // console.log('getConfig = ', content);
32 | fs.writeFileSync(
33 | path.resolve(__dirname, './config/config.prod.js'),
34 | `const { v4: uuidv4 } = require('uuid');
35 | module.exports = ${content}`,
36 | 'utf-8'
37 | );
38 | console.log('got remote config!!!');
39 | process.exit(0);
40 | // 监听数据更新
41 | // acm.subscribe(
42 | // {
43 | // dataId: 'imfdj',
44 | // group: 'imfdj',
45 | // },
46 | // content => {
47 | // console.log(content);
48 | // }
49 | // );
50 | // acm.on('error', function (err) {
51 | // console.log(err);
52 | // // 可以在这里统一进行日志的记录
53 | // // 如果不监听错误事件,所有的异常都将会打印到 stderr
54 | // });
55 | })();
56 |
--------------------------------------------------------------------------------
/test/.setup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { app } = require('egg-mock/bootstrap');
3 | const factories = require('./factories');
4 |
5 | before(async () => {
6 | const res = await app
7 | .httpRequest()
8 | .post('/api/v1/users/login')
9 | .send({
10 | username: 'admin',
11 | password: '123123',
12 | })
13 | .expect(200);
14 | if (app.config.verification_mode === 'jwt') {
15 | app.__authorization = `Bearer ${res.body.data.accessToken}`;
16 | } else {
17 | app.__authorization = '';
18 | app.__cookies = res.headers['set-cookie'][0].split('EGG_SESS=')[1].split(';')[0];
19 | }
20 | return factories(app);
21 | });
22 | afterEach(async () => {
23 | // clear database after each test case
24 | try {
25 | await Promise.all([
26 | // app.model.Users.destroy({ where: {}, force: true }),
27 | // app.model.Roles.destroy({ truncate: true, force: true }),
28 | // app.model.UserRoles.destroy({ truncate: true, force: true }),
29 | ]);
30 | } catch (e) {
31 | console.log(e);
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/test/app/controller/configurations.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { assert, app } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/configurations.test.js', () => {
6 | const createName = 'configurationName' + Math.random();
7 | before(async () => {});
8 |
9 | describe('GET /api/v1/configurations/public_key', () => {
10 | it('should work', async () => {
11 | app.mockCookies({ EGG_SESS: app.__cookies });
12 | const res = await app.httpRequest()
13 | .get('/api/v1/configurations/public_key')
14 | .query({})
15 | .set('authorization', app.__authorization);
16 | assert(res.status === 200);
17 | assert(res.body.data);
18 | assert(res.body.code === 0);
19 | });
20 | });
21 |
22 | describe('PUT /api/v1/configurations', () => {
23 | it('should work', async () => {
24 | app.mockCsrf();
25 | app.mockCookies({ EGG_SESS: app.__cookies });
26 | const res = await app.httpRequest()
27 | .put('/api/v1/configurations')
28 | .set('authorization', app.__authorization)
29 | .send({});
30 | assert(res.status === 201);
31 | assert(res.body.code === 0);
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/test/app/controller/task_task_tags.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { assert, app } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/task_task_tags.test.js', () => {
6 | const createName = 'task_task_tagName' + Math.random();
7 | const createMenuData = {};
8 | before(async () => {
9 | app.mockCsrf();
10 | });
11 |
12 | describe('POST /api/v1/task_task_tags', () => {
13 | it('should work', async () => {
14 | app.mockCookies({ EGG_SESS: app.__cookies });
15 | const res = await app.httpRequest()
16 | .post('/api/v1/task_task_tags')
17 | .set('authorization', app.__authorization)
18 | .send({
19 | task_id: 999999,
20 | task_tag_id: 999999,
21 | });
22 | assert(res.status === 201);
23 | assert(res.body.code === 0);
24 | });
25 | });
26 |
27 | describe('POST /api/v1/task_task_tags/change', () => {
28 | it('should work', async () => {
29 | app.mockCookies({ EGG_SESS: app.__cookies });
30 | const res = await app.httpRequest()
31 | .post('/api/v1/task_task_tags/change')
32 | .set('authorization', app.__authorization)
33 | .send({
34 | task_id: 999999,
35 | task_tag_id: 999999,
36 | });
37 | assert(res.status === 201);
38 | assert(res.body.code === 0);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/app/controller/user_project_collects.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { assert, app } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/user_project_collects.test.js', () => {
6 | before(async () => {
7 | app.mockCsrf();
8 | });
9 |
10 | describe('POST /api/v1/user_project_collects/change', () => {
11 | it('should work', async () => {
12 | app.mockCookies({ EGG_SESS: app.__cookies });
13 | const res = await app.httpRequest()
14 | .post('/api/v1/user_project_collects/change')
15 | .set('authorization', app.__authorization)
16 | .send({
17 | user_id: 1,
18 | project_id: 999999,
19 | });
20 | assert(res.status === 201);
21 | assert(res.body.code === 0);
22 | });
23 | });
24 |
25 | });
26 |
--------------------------------------------------------------------------------
/test/app/controller/user_task_likes.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { assert, app } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/user_task_likes.test.js', () => {
6 | before(async () => {
7 | app.mockCsrf();
8 | });
9 |
10 | describe('POST /api/v1/user_task_likes/change', () => {
11 | it('should work', async () => {
12 | app.mockCookies({ EGG_SESS: app.__cookies });
13 | const res = await app.httpRequest()
14 | .post('/api/v1/user_task_likes/change')
15 | .set('authorization', app.__authorization)
16 | .send({
17 | task_id: 999999,
18 | project_id: 999999,
19 | });
20 | assert(res.status === 201);
21 | assert(res.body.code === 0);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/factories.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { factory } = require('factory-girl');
4 | const { tools } = require('../app/extend/helper');
5 | module.exports = async app => {
6 | // 可以通过 app.factory 访问 factory 实例
7 | app.factory = factory;
8 |
9 | // 定义 user 和默认数据
10 | const saltPassword = await tools.saltPassword('123123');
11 | factory.define('user', app.model.Users, {
12 | username: factory.sequence('Users.username', n => `username_${n}`),
13 | password: saltPassword.password + saltPassword.salt,
14 | // email: factory.sequence('Users.email', n => `email_${n}`),
15 | });
16 |
17 | // // 定义 role 和默认数据
18 | // factory.define('role', app.model.Roles, {
19 | // name: factory.sequence('Roles.name', n => `name_${n}`),
20 | // });
21 | //
22 | // // 定义 user_role 和默认数据
23 | // factory.define('user_role', app.model.UserRoles, {
24 | // user_id: factory.sequence('UserRoles.user_id', n => n),
25 | // role_id: factory.sequence('UserRoles.role_id', n => n),
26 | // });
27 | };
28 |
--------------------------------------------------------------------------------
/tshelper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | watchDirs: {
4 | model: {
5 | directory: 'app/model', // files directory.
6 | // pattern: '**/*.(ts|js)', // glob pattern, default is **/*.(ts|js). it doesn't need to configure normally.
7 | // ignore: '', // ignore glob pattern, default to empty.
8 | generator: 'class', // generator name, eg: class、auto、function、object
9 | interface: 'IModel', // interface name
10 | declareTo: 'Context.model', // declare to this interface
11 | // watch: true, // whether need to watch files
12 | // caseStyle: 'upper', // caseStyle for loader
13 | // interfaceHandle: val => `ReturnType`, // interfaceHandle
14 | // trigger: ['add', 'unlink'], // recreate d.ts when receive these events, all events: ['add', 'unlink', 'change']
15 | },
16 | extend: {
17 | directory: 'app/extend', // files directory.
18 | // pattern: '**/*.(ts|js)', // glob pattern, default is **/*.(ts|js). it doesn't need to configure normally.
19 | // ignore: '', // ignore glob pattern, default to empty.
20 | generator: 'class', // generator name, eg: class、auto、function、object
21 | // interface: 'IModel', // interface name
22 | declareTo: 'Context.helper', // declare to this interface
23 | // watch: true, // whether need to watch files
24 | // caseStyle: 'upper', // caseStyle for loader
25 | // interfaceHandle: val => `ReturnType`, // interfaceHandle
26 | // trigger: ['add', 'unlink'], // recreate d.ts when receive these events, all events: ['add', 'unlink', 'change']
27 | },
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/unhealthy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const runScript = require('runscript');
3 | const isWin = process.platform === 'win32';
4 | const REGEX = isWin ? /^(.*)\s+(\d+)\s*$/ : /^\s*(\d+)\s+(.*)/;
5 |
6 | async function findNodeProcess(filterFn) {
7 | const command = isWin
8 | ? 'wmic Path win32_process Where "Name = \'node.exe\'" Get CommandLine,ProcessId'
9 | : // command, cmd are alias of args, not POSIX standard, so we use args
10 | 'ps -eo "pid,args"';
11 | const stdio = await runScript(command, { stdio: 'pipe' });
12 | const processList = stdio.stdout
13 | .toString()
14 | .split('\n')
15 | .reduce((arr, line) => {
16 | if (!!line && !line.includes('/bin/sh') && line.includes('node')) {
17 | const m = line.match(REGEX);
18 | /* istanbul ignore else */
19 | if (m) {
20 | const item = isWin ? { pid: m[2], cmd: m[1] } : { pid: m[1], cmd: m[2] };
21 | if (!filterFn || filterFn(item)) {
22 | arr.push(item);
23 | }
24 | }
25 | }
26 | return arr;
27 | }, []);
28 | return processList;
29 | }
30 |
31 | (async function() {
32 | const title = process.argv.splice(2)[0].split('--title=')[1];
33 | const processList = await findNodeProcess(item => {
34 | const { cmd } = item;
35 | return cmd.includes('app_worker.js') && cmd.includes(title);
36 | });
37 | console.log(`processList.length: ${processList.length}`);
38 | for (const pro of processList) {
39 | process.kill(pro.pid, 'SIGINT');
40 | }
41 | // 等待nginx移除负载,nginx设置的3秒两次失败移除,额外提供6秒的现有请求处理时间。
42 | await new Promise(resolve => setTimeout(resolve, 1000 * 12));
43 | })();
44 |
--------------------------------------------------------------------------------