├── .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 | --------------------------------------------------------------------------------