mirror of https://github.com/certd/certd
parent
62e3945d30
commit
fbde7cbd93
|
@ -1,7 +1,32 @@
|
||||||
# IntelliJ project files
|
# IntelliJ project files
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
yarn.lock
|
||||||
|
package-lock.json
|
||||||
|
/.idea/
|
||||||
|
*/**/dist
|
||||||
|
*/**/pnpm-lock.yaml
|
||||||
|
*/**/stats.html
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
out
|
out
|
||||||
gen
|
gen
|
||||||
node_modules/
|
/test/*.private.*
|
||||||
packages/*/test/*-private.js
|
|
||||||
|
/*.log
|
||||||
|
|
||||||
|
/packages/ui/*/.idea
|
||||||
|
|
||||||
|
/packages/ui/*/node_modules
|
||||||
|
|
||||||
|
/packages/*/node_modules
|
||||||
|
/packages/ui/certd-server/tmp/
|
||||||
|
/packages/ui/certd-ui/dist/
|
||||||
|
/other
|
||||||
|
/dev-sidecar-test
|
||||||
|
/packages/core/certd/yarn.lock
|
||||||
|
/packages/test
|
||||||
|
/test/own
|
||||||
|
/pnpm-lock.yaml
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
logs/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
coverage/
|
||||||
|
!dist/
|
||||||
|
.idea/
|
||||||
|
run/
|
||||||
|
.DS_Store
|
||||||
|
*.sw*
|
||||||
|
*.un~
|
||||||
|
.tsbuildinfo
|
||||||
|
.tsbuildinfo.*
|
||||||
|
/data/db.sqlite
|
|
@ -0,0 +1,11 @@
|
||||||
|
# 🎨 editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/mwts/",
|
||||||
|
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
||||||
|
"env": {
|
||||||
|
"jest": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
logs/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
coverage/
|
||||||
|
dist/
|
||||||
|
.idea/
|
||||||
|
run/
|
||||||
|
.DS_Store
|
||||||
|
*.sw*
|
||||||
|
*.un~
|
||||||
|
.tsbuildinfo
|
||||||
|
.tsbuildinfo.*
|
||||||
|
/data/db.sqlite
|
||||||
|
/pnpm-lock.yaml
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
...require('mwts/.prettierrc.json')
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
FROM registry.cn-shenzhen.aliyuncs.com/greper/node:15.8.0-alpine
|
||||||
|
|
||||||
|
WORKDIR /home
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
# 如果各公司有自己的私有源,可以替换registry地址
|
||||||
|
#RUN npm install --registry=https://registry.npmmirror.com
|
||||||
|
RUN npm install -g cnpm
|
||||||
|
RUN cnpm install
|
||||||
|
RUN npm run build:preview
|
||||||
|
|
||||||
|
# 如果端口更换,这边可以更新一下
|
||||||
|
EXPOSE 7001
|
||||||
|
|
||||||
|
CMD ["npm", "run", "online:preview"]
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 fast-crud
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,41 @@
|
||||||
|
# fast-server-js
|
||||||
|
|
||||||
|
base on midway
|
||||||
|
|
||||||
|
## QuickStart
|
||||||
|
|
||||||
|
> nodejs需要16以上
|
||||||
|
|
||||||
|
<!-- add docs here for user -->
|
||||||
|
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm i
|
||||||
|
# 如果遇到sqlite安装失败时
|
||||||
|
# 建议使用cnpm
|
||||||
|
# npm install -g cnpm
|
||||||
|
# cnpm install
|
||||||
|
$ npm run dev
|
||||||
|
$ open http://localhost:7001/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### npm scripts
|
||||||
|
|
||||||
|
- Use `npm run lint` to check code style.
|
||||||
|
- Use `npm test` to run unit test.
|
||||||
|
|
||||||
|
|
||||||
|
[midway]: https://midwayjs.org
|
||||||
|
|
||||||
|
see [midway docs][ https://midwayjs.org] for more detail.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# fast-server-js
|
||||||
|
|
||||||
|
## 快速入门
|
||||||
|
|
||||||
|
<!-- 在此次添加使用文档 -->
|
||||||
|
|
||||||
|
如需进一步了解,参见 [midway 文档][midway]。
|
||||||
|
|
||||||
|
### 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm i
|
||||||
|
$ npm run dev
|
||||||
|
$ open http://localhost:7001/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### 内置指令
|
||||||
|
|
||||||
|
- 使用 `npm run lint` 来做代码风格检查。
|
||||||
|
- 使用 `npm test` 来执行单元测试。
|
||||||
|
|
||||||
|
|
||||||
|
[midway]: https://midwayjs.org
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
const WebFramework = require('@midwayjs/koa').Framework;
|
||||||
|
const web = new WebFramework().configure({
|
||||||
|
port: 7001,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { Bootstrap } = require('@midwayjs/bootstrap');
|
||||||
|
Bootstrap.load(web).run();
|
|
@ -0,0 +1,77 @@
|
||||||
|
-- 表:sys_permission
|
||||||
|
CREATE TABLE "sys_permission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(100) NOT NULL, "permission" varchar(100), "parent_id" integer NOT NULL DEFAULT (-1), "sort" integer NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (1, '系统管理', 'sys', -1, 1, 1, 1624085863636);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (2, '权限管理', 'sys:auth', 1, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (3, '用户管理', 'sys:auth:user', 2, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (4, '查看', 'sys:auth:user:view', 3, 100, 1, 1624189112333);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (5, '权限管理', 'sys:auth:per', 2, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (6, '查看', 'sys:auth:per:view', 5, 100, 1, 1624189161317);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (7, '角色管理', 'sys:auth:role', 2, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (8, '查看', 'sys:auth:role:view', 7, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (9, '修改', 'sys:auth:user:edit', 3, 300, 1, 1624189127688);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (10, '删除', 'sys:auth:user:remove', 3, 400, 1, 1624189133184);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (11, '添加', 'sys:auth:user:add', 3, 200, 1, 1624189142576);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (12, '修改', 'sys:auth:role:edit', 7, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (13, '删除', 'sys:auth:role:remove', 7, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (14, '添加', 'sys:auth:role:add', 7, 1, 1, 1);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (15, '修改', 'sys:auth:per:edit', 5, 300, 1, 1624189308837);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (16, '删除', 'sys:auth:per:remove', 5, 400, 1, 1624189256926);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (17, '添加', 'sys:auth:per:add', 5, 200, 1, 1624189283766);
|
||||||
|
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (18,'授权','sys:auth:role:authz',7,100,1,1624335712144);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- 表:sys_role
|
||||||
|
CREATE TABLE "sys_role" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(100) NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||||
|
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (1, '管理员', 1, 1623749138537);
|
||||||
|
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (2, '只读角色', 1, 1623749138537);
|
||||||
|
|
||||||
|
-- 表:sys_role_permission
|
||||||
|
CREATE TABLE "sys_role_permission" ("role_id" integer NOT NULL, "permission_id" integer NOT NULL, PRIMARY KEY ("role_id", "permission_id"));
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 1);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 2);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 3);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 4);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 5);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 6);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 7);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 8);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 9);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 10);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 11);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 12);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 13);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 14);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 15);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 16);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 17);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 18);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, -1);
|
||||||
|
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 4);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 6);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 8);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 1);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 2);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 3);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 5);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 7);
|
||||||
|
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, -1);
|
||||||
|
|
||||||
|
-- 表:sys_user
|
||||||
|
CREATE TABLE "sys_user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar(100) NOT NULL, "password" varchar(50) NOT NULL, "nick_name" varchar(50), "avatar" varchar(255), "phone_code" varchar(20), "mobile" varchar(20), "email" varchar(100),"remark" varchar(100), "status" integer NOT NULL DEFAULT (1), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
|
||||||
|
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,NULL);
|
||||||
|
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (2, 'readonly', 'e10adc3949ba59abbe56e057f20f883e', '只读用户', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,'密码:123456');
|
||||||
|
|
||||||
|
-- 表:sys_user_role
|
||||||
|
CREATE TABLE "sys_user_role" ("role_id" integer NOT NULL, "user_id" integer NOT NULL, PRIMARY KEY ("role_id", "user_id"));
|
||||||
|
INSERT INTO sys_user_role (role_id, user_id) VALUES (1, 1);
|
||||||
|
INSERT INTO sys_user_role (role_id, user_id) VALUES (2, 2);
|
||||||
|
|
||||||
|
-- 索引:IDX_223de54d6badbe43a5490450c3
|
||||||
|
CREATE UNIQUE INDEX "IDX_223de54d6badbe43a5490450c3" ON "sys_role" ("name");
|
||||||
|
|
||||||
|
-- 索引:IDX_9e7164b2f1ea1348bc0eb0a7da
|
||||||
|
CREATE UNIQUE INDEX "IDX_9e7164b2f1ea1348bc0eb0a7da" ON "sys_user" ("username");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
-- for preview 限制演示环境的数据修改
|
||||||
|
update sqlite_sequence set seq = 1000 where name = 'sys_user' ;
|
||||||
|
update sqlite_sequence set seq = 1000 where name = 'sys_permission' ;
|
||||||
|
update sqlite_sequence set seq = 1000 where name = 'sys_role' ;
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
||||||
|
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
||||||
|
};
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"name": "@fast-crud/fs-server-js",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"description": "fast-server base midway",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "NODE_ENV=production node ./bootstrap.js",
|
||||||
|
"online": "NODE_ENV=production node ./bootstrap.js",
|
||||||
|
"online:preview": "NODE_ENV=preview node ./bootstrap.js",
|
||||||
|
"dev": "cross-env NODE_ENV=local midway-bin dev --ts",
|
||||||
|
"dev:preview": "cross-env NODE_ENV=preview midway-bin dev --ts",
|
||||||
|
"test": "midway-bin test --ts",
|
||||||
|
"cov": "midway-bin cov --ts",
|
||||||
|
"lint": "mwts check",
|
||||||
|
"lint:fix": "mwts fix",
|
||||||
|
"ci": "npm run cov",
|
||||||
|
"build": "midway-bin build -c",
|
||||||
|
"build:preview": "cross-env NODE_ENV=preview midway-bin build -c",
|
||||||
|
"check": "luckyeye",
|
||||||
|
"mig": "typeorm migration:create -n name"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"midway-flyway-js": "^3.0.0",
|
||||||
|
"@koa/cors": "^3.1.0",
|
||||||
|
"@midwayjs/bootstrap": "^3.0.0",
|
||||||
|
"@midwayjs/cache": "^3.0.0",
|
||||||
|
"@midwayjs/cli": "^1.2.38",
|
||||||
|
"@midwayjs/core": "^3.0.0",
|
||||||
|
"@midwayjs/decorator": "^3.0.0",
|
||||||
|
"@midwayjs/koa": "^3.0.0",
|
||||||
|
"@midwayjs/logger": "^2.17.0",
|
||||||
|
"@midwayjs/typeorm": "^3.5.3",
|
||||||
|
"@midwayjs/validate": "^3.0.0",
|
||||||
|
"@types/cache-manager": "^3.4.0",
|
||||||
|
"typeorm": "^0.3.10",
|
||||||
|
"cache-manager": "^3.4.3",
|
||||||
|
"dayjs": "^1.10.5",
|
||||||
|
"glob": "^7.1.7",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"koa-bodyparser": "^4.3.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"log4js": "^6.3.0",
|
||||||
|
"md5": "^2.3.0",
|
||||||
|
"sqlite3": "^5.1.2",
|
||||||
|
"svg-captcha": "^1.4.0",
|
||||||
|
"@alicloud/pop-core": "^1.7.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@midwayjs/cli": "^1.0.0",
|
||||||
|
"@midwayjs/luckyeye": "^1.0.0",
|
||||||
|
"@midwayjs/mock": "^3.0.0",
|
||||||
|
"@midwayjs/orm": "^3.0.0",
|
||||||
|
"@types/jest": "^26.0.10",
|
||||||
|
"@types/koa-bodyparser": "^4.3.0",
|
||||||
|
"@types/node": "14",
|
||||||
|
"cross-env": "^6.0.0",
|
||||||
|
"jest": "^26.4.0",
|
||||||
|
"mwts": "^1.0.5",
|
||||||
|
"ts-jest": "^26.2.0",
|
||||||
|
"typescript": "^4.0.0",
|
||||||
|
"ts-node": "^10.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
|
||||||
|
"midway-bin-clean": [
|
||||||
|
".vscode/.tsbuildinfo",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"midway-luckyeye": {
|
||||||
|
"packages": [
|
||||||
|
"midway_v2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/fast-crud/fast-server-js"
|
||||||
|
},
|
||||||
|
"author": "Greper",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Inject } from '@midwayjs/decorator';
|
||||||
|
import { Context } from '@midwayjs/koa';
|
||||||
|
import { Constants } from './constants';
|
||||||
|
|
||||||
|
export abstract class BaseController {
|
||||||
|
@Inject()
|
||||||
|
ctx: Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功返回
|
||||||
|
* @param data 返回数据
|
||||||
|
*/
|
||||||
|
ok(data) {
|
||||||
|
const res = {
|
||||||
|
...Constants.res.success,
|
||||||
|
data: undefined,
|
||||||
|
};
|
||||||
|
if (data) {
|
||||||
|
res.data = data;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 失败返回
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
fail(msg, code) {
|
||||||
|
return {
|
||||||
|
code: code ? code : Constants.res.error.code,
|
||||||
|
msg: msg ? msg : Constants.res.error.code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
import { Inject } from '@midwayjs/decorator';
|
||||||
|
import { ValidateException } from './exception/validation-exception';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import { Context } from '@midwayjs/koa';
|
||||||
|
/**
|
||||||
|
* 服务基类
|
||||||
|
*/
|
||||||
|
export abstract class BaseService {
|
||||||
|
@Inject()
|
||||||
|
ctx: Context;
|
||||||
|
|
||||||
|
abstract getRepository();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得单个ID
|
||||||
|
* @param id ID
|
||||||
|
* @param infoIgnoreProperty 忽略返回属性
|
||||||
|
*/
|
||||||
|
async info(id, infoIgnoreProperty?) {
|
||||||
|
if (!id) {
|
||||||
|
throw new ValidateException('id不能为空');
|
||||||
|
}
|
||||||
|
const info = await this.getRepository().findOne({
|
||||||
|
where:{ id }
|
||||||
|
});
|
||||||
|
if (info && infoIgnoreProperty) {
|
||||||
|
for (const property of infoIgnoreProperty) {
|
||||||
|
delete info[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非分页查询
|
||||||
|
* @param option 查询配置
|
||||||
|
*/
|
||||||
|
async find(options) {
|
||||||
|
return await this.getRepository().find(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||||
|
*/
|
||||||
|
async delete(ids) {
|
||||||
|
if (ids instanceof Array) {
|
||||||
|
await this.getRepository().delete(ids);
|
||||||
|
} else if (typeof ids === 'string') {
|
||||||
|
await this.getRepository().delete(ids.split(','));
|
||||||
|
} else {
|
||||||
|
//ids是一个condition
|
||||||
|
await this.getRepository().delete(ids);
|
||||||
|
}
|
||||||
|
await this.modifyAfter(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增|修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async addOrUpdate(param) {
|
||||||
|
await this.getRepository().save(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async add(param) {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
param.createTime = now;
|
||||||
|
param.updateTime = now;
|
||||||
|
await this.addOrUpdate(param);
|
||||||
|
await this.modifyAfter(param);
|
||||||
|
return {
|
||||||
|
id: param.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async update(param) {
|
||||||
|
if (!param.id) throw new ValidateException('no id');
|
||||||
|
param.updateTime = new Date().getTime();
|
||||||
|
await this.addOrUpdate(param);
|
||||||
|
await this.modifyAfter(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增|修改|删除 之后的操作
|
||||||
|
* @param data 对应数据
|
||||||
|
*/
|
||||||
|
async modifyAfter(data) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param query 查询条件 bean
|
||||||
|
* @param page
|
||||||
|
* @param order
|
||||||
|
* @param buildQuery
|
||||||
|
*/
|
||||||
|
async page(query, page = { offset: 0, limit: 20 }, order, buildQuery) {
|
||||||
|
if (page.offset == null) {
|
||||||
|
page.offset = 0;
|
||||||
|
}
|
||||||
|
if (page.limit == null) {
|
||||||
|
page.limit = 20;
|
||||||
|
}
|
||||||
|
const qb = this.getRepository().createQueryBuilder('main');
|
||||||
|
if (order && order.prop) {
|
||||||
|
qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC');
|
||||||
|
} else {
|
||||||
|
qb.orderBy('id', 'DESC');
|
||||||
|
}
|
||||||
|
qb.offset(page.offset).limit(page.limit);
|
||||||
|
//根据bean query
|
||||||
|
if (query) {
|
||||||
|
let whereSql = '';
|
||||||
|
let index = 0;
|
||||||
|
_.forEach(query, (value, key) => {
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index !== 0) {
|
||||||
|
whereSql += ' and ';
|
||||||
|
}
|
||||||
|
whereSql += ` main.${key} = :${key} `;
|
||||||
|
index++;
|
||||||
|
});
|
||||||
|
if (index > 0) {
|
||||||
|
qb.where(whereSql, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//自定义query
|
||||||
|
if (buildQuery) {
|
||||||
|
buildQuery(qb);
|
||||||
|
}
|
||||||
|
const list = await qb.getMany();
|
||||||
|
const total = await qb.getCount();
|
||||||
|
return {
|
||||||
|
records: list,
|
||||||
|
total,
|
||||||
|
offset: page.offset,
|
||||||
|
limit: page.limit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
export const Constants = {
|
||||||
|
res: {
|
||||||
|
error: {
|
||||||
|
code: 1,
|
||||||
|
message: 'error',
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
code: 0,
|
||||||
|
message: 'success',
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
code: 10,
|
||||||
|
message: '参数错误',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
code: 401,
|
||||||
|
message: '您还未登录或token已过期',
|
||||||
|
},
|
||||||
|
permission: {
|
||||||
|
code: 402,
|
||||||
|
message: '您没有权限',
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
code: 10001,
|
||||||
|
message: '对不起,预览环境不允许修改此数据',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { ALL, Body, Post, Query } from '@midwayjs/decorator';
|
||||||
|
import { BaseService } from './base-service';
|
||||||
|
import { BaseController } from './base-controller';
|
||||||
|
|
||||||
|
export abstract class CrudController<
|
||||||
|
T extends BaseService
|
||||||
|
> extends BaseController {
|
||||||
|
abstract getService();
|
||||||
|
|
||||||
|
@Post('/page')
|
||||||
|
async page(
|
||||||
|
@Body(ALL)
|
||||||
|
body
|
||||||
|
) {
|
||||||
|
const pageRet = await this.getService().page(
|
||||||
|
body?.query,
|
||||||
|
body?.page,
|
||||||
|
body?.sort,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
return this.ok(pageRet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add')
|
||||||
|
async add(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
const id = await this.getService().add(bean);
|
||||||
|
return this.ok(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/update')
|
||||||
|
async update(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
await this.getService().update(bean);
|
||||||
|
return this.ok(null);
|
||||||
|
}
|
||||||
|
@Post('/delete')
|
||||||
|
async delete(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
await this.getService().delete([id]);
|
||||||
|
return this.ok(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export class EnumItem {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
color: string;
|
||||||
|
|
||||||
|
constructor(value, label, color) {
|
||||||
|
this.value = value;
|
||||||
|
this.label = label;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Constants } from '../constants';
|
||||||
|
import { BaseException } from './base-exception';
|
||||||
|
/**
|
||||||
|
* 授权异常
|
||||||
|
*/
|
||||||
|
export class AuthException extends BaseException {
|
||||||
|
constructor(message) {
|
||||||
|
super(
|
||||||
|
'AuthException',
|
||||||
|
Constants.res.auth.code,
|
||||||
|
message ? message : Constants.res.auth.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* 异常基类
|
||||||
|
*/
|
||||||
|
export class BaseException extends Error {
|
||||||
|
status: number;
|
||||||
|
constructor(name, code, message) {
|
||||||
|
super(message);
|
||||||
|
this.name = name;
|
||||||
|
this.status = code;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Constants } from '../constants';
|
||||||
|
import { BaseException } from './base-exception';
|
||||||
|
/**
|
||||||
|
* 通用异常
|
||||||
|
*/
|
||||||
|
export class CommonException extends BaseException {
|
||||||
|
constructor(message) {
|
||||||
|
super(
|
||||||
|
'CommonException',
|
||||||
|
Constants.res.error.code,
|
||||||
|
message ? message : Constants.res.error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Constants } from '../constants';
|
||||||
|
import { BaseException } from './base-exception';
|
||||||
|
/**
|
||||||
|
* 授权异常
|
||||||
|
*/
|
||||||
|
export class PermissionException extends BaseException {
|
||||||
|
constructor(message) {
|
||||||
|
super(
|
||||||
|
'PermissionException',
|
||||||
|
Constants.res.permission.code,
|
||||||
|
message ? message : Constants.res.permission.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Constants } from '../constants';
|
||||||
|
import { BaseException } from './base-exception';
|
||||||
|
/**
|
||||||
|
* 预览模式
|
||||||
|
*/
|
||||||
|
export class PreviewException extends BaseException {
|
||||||
|
constructor(message) {
|
||||||
|
super(
|
||||||
|
'PreviewException',
|
||||||
|
Constants.res.preview.code,
|
||||||
|
message ? message : Constants.res.preview.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Constants } from '../constants';
|
||||||
|
import { BaseException } from './base-exception';
|
||||||
|
/**
|
||||||
|
* 校验异常
|
||||||
|
*/
|
||||||
|
export class ValidateException extends BaseException {
|
||||||
|
constructor(message) {
|
||||||
|
super(
|
||||||
|
'ValidateException',
|
||||||
|
Constants.res.validation.code,
|
||||||
|
message ? message : Constants.res.validation.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
export class Result<T> {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
data: T;
|
||||||
|
constructor(code, msg, data?) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error(code = 1, msg) {
|
||||||
|
return new Result(code, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static success(msg, data?) {
|
||||||
|
return new Result(0, msg, data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { join } from 'path';
|
||||||
|
import {FlywayHistory} from "midway-flyway-js/dist/entity";
|
||||||
|
|
||||||
|
|
||||||
|
import { MidwayConfig } from '@midwayjs/core';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// use for cookie sign key, should change to your own and keep security
|
||||||
|
keys: 'greper-is-666',
|
||||||
|
koa: {
|
||||||
|
port: 7001,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 演示环境
|
||||||
|
*/
|
||||||
|
preview :{
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库
|
||||||
|
*/
|
||||||
|
typeorm : {
|
||||||
|
dataSource: {
|
||||||
|
default: {
|
||||||
|
/**
|
||||||
|
* 单数据库实例
|
||||||
|
*/
|
||||||
|
type: 'sqlite',
|
||||||
|
database: join(__dirname, '../../data/db.sqlite'),
|
||||||
|
synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true
|
||||||
|
logging: true,
|
||||||
|
|
||||||
|
// 配置实体模型 或者 entities: '/entity',
|
||||||
|
entities: ['/modules/authority/entity/*',FlywayHistory],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 自动升级数据库脚本
|
||||||
|
*/
|
||||||
|
flyway : {
|
||||||
|
scriptDir:join(__dirname, '../../db/migration'),
|
||||||
|
},
|
||||||
|
|
||||||
|
biz : {
|
||||||
|
jwt: {
|
||||||
|
secret: 'greper-is-666',
|
||||||
|
expire: 7 * 24 * 60, //单位秒
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
ignoreUrls: ['/', '/api/login', '/api/register'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
} as MidwayConfig;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { MidwayConfig } from '@midwayjs/core';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 演示环境
|
||||||
|
*/
|
||||||
|
preview: {
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
} as MidwayConfig;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { MidwayConfig } from '@midwayjs/core';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 演示环境
|
||||||
|
*/
|
||||||
|
preview: {
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
} as MidwayConfig;
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as validateComp from '@midwayjs/validate';
|
||||||
|
import * as productionConfig from './config/config.production';
|
||||||
|
import * as previewConfig from './config/config.preview';
|
||||||
|
import * as defaultConfig from './config/config.default';
|
||||||
|
import { Configuration, App } from '@midwayjs/decorator';
|
||||||
|
import * as koa from '@midwayjs/koa';
|
||||||
|
import * as bodyParser from 'koa-bodyparser';
|
||||||
|
import * as orm from '@midwayjs/typeorm';
|
||||||
|
import * as cache from '@midwayjs/cache';
|
||||||
|
import * as cors from '@koa/cors';
|
||||||
|
import { join } from 'path';
|
||||||
|
import * as flyway from 'midway-flyway-js';
|
||||||
|
import {ReportMiddleware} from "./middleware/report";
|
||||||
|
import {GlobalExceptionMiddleware} from "./middleware/global-exception";
|
||||||
|
import {PreviewMiddleware} from "./middleware/preview";
|
||||||
|
import {AuthorityMiddleware} from "./middleware/authority";
|
||||||
|
@Configuration({
|
||||||
|
imports: [koa, orm, cache, flyway,validateComp],
|
||||||
|
importConfigs: [
|
||||||
|
{
|
||||||
|
default: defaultConfig,
|
||||||
|
preview: previewConfig,
|
||||||
|
production: productionConfig,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ContainerConfiguration {}
|
||||||
|
@Configuration({
|
||||||
|
conflictCheck: true,
|
||||||
|
importConfigs: [join(__dirname, './config')],
|
||||||
|
})
|
||||||
|
export class ContainerLifeCycle {
|
||||||
|
@App()
|
||||||
|
app: koa.Application;
|
||||||
|
|
||||||
|
async onReady() {
|
||||||
|
//跨域
|
||||||
|
this.app.use(
|
||||||
|
cors({
|
||||||
|
origin: '*',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// bodyparser options see https://github.com/koajs/bodyparser
|
||||||
|
this.app.use(bodyParser());
|
||||||
|
//请求日志打印
|
||||||
|
|
||||||
|
this.app.useMiddleware([
|
||||||
|
|
||||||
|
ReportMiddleware,
|
||||||
|
//统一异常处理
|
||||||
|
GlobalExceptionMiddleware,
|
||||||
|
//预览模式限制修改id<1000的数据
|
||||||
|
PreviewMiddleware,
|
||||||
|
//授权处理
|
||||||
|
AuthorityMiddleware
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Controller, Get, Provide } from '@midwayjs/decorator';
|
||||||
|
|
||||||
|
@Provide()
|
||||||
|
@Controller('/')
|
||||||
|
export class HomeController {
|
||||||
|
@Get('/')
|
||||||
|
async home(): Promise<string> {
|
||||||
|
return 'Hello Midwayjs!';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Config, Provide } from '@midwayjs/decorator';
|
||||||
|
import {
|
||||||
|
IWebMiddleware,
|
||||||
|
IMidwayKoaContext,
|
||||||
|
NextFunction
|
||||||
|
} from '@midwayjs/koa';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as jwt from 'jsonwebtoken';
|
||||||
|
import { Constants } from '../basic/constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class AuthorityMiddleware implements IWebMiddleware {
|
||||||
|
@Config('biz.jwt.secret')
|
||||||
|
private secret: string;
|
||||||
|
@Config('biz.auth.ignoreUrls')
|
||||||
|
private ignoreUrls: string[];
|
||||||
|
|
||||||
|
resolve() {
|
||||||
|
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||||
|
const { url } = ctx;
|
||||||
|
const token = ctx.get('Authorization');
|
||||||
|
// 路由地址为 admin前缀的 需要权限校验
|
||||||
|
// console.log('ctx', ctx);
|
||||||
|
const queryIndex = url.indexOf('?');
|
||||||
|
let uri = url;
|
||||||
|
if (queryIndex >= 0) {
|
||||||
|
uri = url.substring(0, queryIndex);
|
||||||
|
}
|
||||||
|
const yes = this.ignoreUrls.includes(uri);
|
||||||
|
if (yes) {
|
||||||
|
await next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.user = jwt.verify(token, this.secret);
|
||||||
|
} catch (err) {
|
||||||
|
ctx.status = 401;
|
||||||
|
ctx.body = Constants.res.auth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { Provide } from '@midwayjs/decorator';
|
||||||
|
import {
|
||||||
|
IWebMiddleware,
|
||||||
|
IMidwayKoaContext,
|
||||||
|
NextFunction,
|
||||||
|
} from '@midwayjs/koa';
|
||||||
|
import { logger } from '../utils/logger';
|
||||||
|
import { Result } from '../basic/result';
|
||||||
|
|
||||||
|
@Provide()
|
||||||
|
export class GlobalExceptionMiddleware implements IWebMiddleware {
|
||||||
|
resolve() {
|
||||||
|
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||||
|
const { url } = ctx;
|
||||||
|
const startTime = Date.now();
|
||||||
|
logger.info('请求开始:', url);
|
||||||
|
try {
|
||||||
|
await next();
|
||||||
|
logger.info('请求完成', url, Date.now() - startTime + 'ms');
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
|
||||||
|
ctx.status = 200;
|
||||||
|
ctx.body = Result.error(err.code != null ? err.code : 1, err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Config, Provide } from '@midwayjs/decorator';
|
||||||
|
import {
|
||||||
|
IMidwayKoaContext,
|
||||||
|
NextFunction,
|
||||||
|
IWebMiddleware,
|
||||||
|
} from '@midwayjs/koa';
|
||||||
|
import { PreviewException } from '../basic/exception/preview-exception';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览模式
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class PreviewMiddleware implements IWebMiddleware {
|
||||||
|
@Config('preview.enabled')
|
||||||
|
private preview: boolean;
|
||||||
|
|
||||||
|
resolve() {
|
||||||
|
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||||
|
if (!this.preview) {
|
||||||
|
await next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let { url, request } = ctx;
|
||||||
|
const body: any = request.body;
|
||||||
|
let id = body.id || request.query.id;
|
||||||
|
const roleId = body.roleId;
|
||||||
|
if (id == null && roleId != null) {
|
||||||
|
id = roleId;
|
||||||
|
}
|
||||||
|
if (id != null && typeof id === 'string') {
|
||||||
|
id = parseInt(id);
|
||||||
|
}
|
||||||
|
if (url.indexOf('?') !== -1) {
|
||||||
|
url = url.substring(0, url.indexOf('?'));
|
||||||
|
}
|
||||||
|
const isModify =
|
||||||
|
url.endsWith('update') ||
|
||||||
|
url.endsWith('delete') ||
|
||||||
|
url.endsWith('authz');
|
||||||
|
const isPreviewId = id < 1000;
|
||||||
|
if (this.preview && isModify && isPreviewId) {
|
||||||
|
throw new PreviewException(
|
||||||
|
'对不起,预览环境不允许修改此数据,如需体验请添加新数据'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await next();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Provide } from '@midwayjs/decorator';
|
||||||
|
import {
|
||||||
|
IWebMiddleware,
|
||||||
|
IMidwayKoaContext,
|
||||||
|
NextFunction,
|
||||||
|
} from '@midwayjs/koa';
|
||||||
|
import { logger } from '../utils/logger';
|
||||||
|
|
||||||
|
@Provide()
|
||||||
|
export class ReportMiddleware implements IWebMiddleware {
|
||||||
|
resolve() {
|
||||||
|
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||||
|
const { url } = ctx;
|
||||||
|
logger.info('请求开始:', url);
|
||||||
|
const startTime = Date.now();
|
||||||
|
await next();
|
||||||
|
if (ctx.status !== 200) {
|
||||||
|
logger.error(
|
||||||
|
'请求失败:',
|
||||||
|
url,
|
||||||
|
ctx.status,
|
||||||
|
Date.now() - startTime + 'ms'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logger.info('请求完成:', url, ctx.status, Date.now() - startTime + 'ms');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import {
|
||||||
|
ALL,
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Inject,
|
||||||
|
Post,
|
||||||
|
Provide,
|
||||||
|
Query,
|
||||||
|
} from '@midwayjs/decorator';
|
||||||
|
import { CrudController } from '../../../basic/crud-controller';
|
||||||
|
import { PermissionService } from '../service/permission-service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限资源
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/sys/authority/permission')
|
||||||
|
export class PermissionController extends CrudController<PermissionService> {
|
||||||
|
@Inject()
|
||||||
|
service: PermissionService;
|
||||||
|
|
||||||
|
getService() {
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/page')
|
||||||
|
async page(
|
||||||
|
@Body(ALL)
|
||||||
|
body
|
||||||
|
) {
|
||||||
|
return await super.page(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add')
|
||||||
|
async add(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.add(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/update')
|
||||||
|
async update(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.update(bean);
|
||||||
|
}
|
||||||
|
@Post('/delete')
|
||||||
|
async delete(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
return await super.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/tree')
|
||||||
|
async tree() {
|
||||||
|
const tree = await this.service.tree({});
|
||||||
|
return this.ok(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {
|
||||||
|
ALL,
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Inject,
|
||||||
|
Post,
|
||||||
|
Provide,
|
||||||
|
Query,
|
||||||
|
} from '@midwayjs/decorator';
|
||||||
|
import { CrudController } from '../../../basic/crud-controller';
|
||||||
|
import { RoleService } from '../service/role-service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/sys/authority/role')
|
||||||
|
export class RoleController extends CrudController<RoleService> {
|
||||||
|
@Inject()
|
||||||
|
service: RoleService;
|
||||||
|
|
||||||
|
getService() {
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/page')
|
||||||
|
async page(
|
||||||
|
@Body(ALL)
|
||||||
|
body
|
||||||
|
) {
|
||||||
|
return await super.page(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/list')
|
||||||
|
async list() {
|
||||||
|
const ret = await this.service.find({});
|
||||||
|
return this.ok(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add')
|
||||||
|
async add(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.add(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/update')
|
||||||
|
async update(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.update(bean);
|
||||||
|
}
|
||||||
|
@Post('/delete')
|
||||||
|
async delete(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
return await super.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/getPermissionTree')
|
||||||
|
async getPermissionTree(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
const ret = await this.service.getPermissionTreeByRoleId(id);
|
||||||
|
return this.ok(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/getPermissionIds')
|
||||||
|
async getPermissionIds(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
const ret = await this.service.getPermissionIdsByRoleId(id);
|
||||||
|
return this.ok(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给角色授予权限
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
@Post('/authz')
|
||||||
|
async authz(
|
||||||
|
@Body('roleId')
|
||||||
|
roleId,
|
||||||
|
@Body('permissionIds')
|
||||||
|
permissionIds
|
||||||
|
) {
|
||||||
|
await this.service.authz(roleId, permissionIds);
|
||||||
|
return this.ok(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
import {
|
||||||
|
Provide,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
Inject,
|
||||||
|
Body,
|
||||||
|
Query,
|
||||||
|
ALL,
|
||||||
|
} from '@midwayjs/decorator';
|
||||||
|
import { UserService } from '../service/user-service';
|
||||||
|
import { CrudController } from '../../../basic/crud-controller';
|
||||||
|
import { RoleService } from '../service/role-service';
|
||||||
|
import { PermissionService } from '../service/permission-service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/sys/authority/user')
|
||||||
|
export class UserController extends CrudController<UserService> {
|
||||||
|
@Inject()
|
||||||
|
service: UserService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
roleService: RoleService;
|
||||||
|
@Inject()
|
||||||
|
permissionService: PermissionService;
|
||||||
|
|
||||||
|
getService() {
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/page')
|
||||||
|
async page(
|
||||||
|
@Body(ALL)
|
||||||
|
body
|
||||||
|
) {
|
||||||
|
const ret = await super.page(body);
|
||||||
|
|
||||||
|
const users = ret.data.records;
|
||||||
|
|
||||||
|
//获取roles
|
||||||
|
const userIds = users.map((item) => item.id);
|
||||||
|
const userRoles = await this.roleService.getByUserIds(userIds);
|
||||||
|
const userRolesMap = new Map();
|
||||||
|
for (const ur of userRoles) {
|
||||||
|
let roles = userRolesMap.get(ur.userId);
|
||||||
|
if (roles == null) {
|
||||||
|
roles = [];
|
||||||
|
userRolesMap.set(ur.userId, roles);
|
||||||
|
}
|
||||||
|
roles.push(ur.roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const record of users) {
|
||||||
|
//withRoles
|
||||||
|
record.roles = userRolesMap.get(record.id);
|
||||||
|
//删除密码字段
|
||||||
|
delete record.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add')
|
||||||
|
async add(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.add(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/update')
|
||||||
|
async update(
|
||||||
|
@Body(ALL)
|
||||||
|
bean
|
||||||
|
) {
|
||||||
|
return await super.update(bean);
|
||||||
|
}
|
||||||
|
@Post('/delete')
|
||||||
|
async delete(
|
||||||
|
@Query('id')
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
return await super.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前登录用户的个人信息
|
||||||
|
*/
|
||||||
|
@Post('/mine')
|
||||||
|
public async mine() {
|
||||||
|
const id = this.ctx.user.id;
|
||||||
|
const info = await this.service.info(id, ['password']);
|
||||||
|
return this.ok(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前登录用户的权限列表
|
||||||
|
*/
|
||||||
|
@Post('/permissions')
|
||||||
|
public async permissions() {
|
||||||
|
const id = this.ctx.user.id;
|
||||||
|
const permissions = await this.service.getUserPermissions(id);
|
||||||
|
return this.ok(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前登录用户的权限树形列表
|
||||||
|
*/
|
||||||
|
@Post('/permissionTree')
|
||||||
|
public async permissionTree() {
|
||||||
|
const id = this.ctx.user.id;
|
||||||
|
const permissions = await this.service.getUserPermissions(id);
|
||||||
|
const tree = this.permissionService.buildTree(permissions);
|
||||||
|
return this.ok(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限
|
||||||
|
*/
|
||||||
|
@Entity('sys_permission')
|
||||||
|
export class PermissionEntity {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
@Column({ comment: '标题', length: 100 })
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* 权限代码
|
||||||
|
* 示例:sys:user:read
|
||||||
|
*/
|
||||||
|
@Column({ comment: '权限代码', length: 100, nullable: true })
|
||||||
|
permission: string;
|
||||||
|
|
||||||
|
@Column({ name: 'parent_id', comment: '父节点ID', default: -1 })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@Column({ comment: '排序号' })
|
||||||
|
sort: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'create_time',
|
||||||
|
comment: '创建时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
createTime: Date;
|
||||||
|
@Column({
|
||||||
|
name: 'update_time',
|
||||||
|
comment: '修改时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
updateTime: Date;
|
||||||
|
|
||||||
|
// @ManyToMany(type => RoleEntity, res => res.permissions)
|
||||||
|
// roles: RoleEntity[];
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Entity, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限多对多
|
||||||
|
*/
|
||||||
|
@Entity('sys_role_permission')
|
||||||
|
export class RolePermissionEntity {
|
||||||
|
@PrimaryColumn({ name: 'role_id' })
|
||||||
|
roleId: number;
|
||||||
|
@PrimaryColumn({ name: 'permission_id' })
|
||||||
|
permissionId: number;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
*/
|
||||||
|
@Entity('sys_role')
|
||||||
|
export class RoleEntity {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
@Index({ unique: true })
|
||||||
|
@Column({ comment: '角色名称', length: 100 })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'create_time',
|
||||||
|
comment: '创建时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
createTime: Date;
|
||||||
|
@Column({
|
||||||
|
name: 'update_time',
|
||||||
|
comment: '修改时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
updateTime: Date;
|
||||||
|
|
||||||
|
// @ManyToMany(type => PermissionEntity, res => res.roles)
|
||||||
|
// @JoinTable({
|
||||||
|
// name: 'sys_role_resources',
|
||||||
|
// joinColumn: {
|
||||||
|
// name: 'roleId',
|
||||||
|
// referencedColumnName: 'id',
|
||||||
|
// },
|
||||||
|
// inverseJoinColumn: {
|
||||||
|
// name: 'resourceId',
|
||||||
|
// referencedColumnName: 'id',
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// resources: PermissionEntity[];
|
||||||
|
|
||||||
|
// @ManyToMany(type => UserEntity, res => res.roles)
|
||||||
|
// users: UserEntity[];
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Entity, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色多对多
|
||||||
|
*/
|
||||||
|
@Entity('sys_user_role')
|
||||||
|
export class UserRoleEntity {
|
||||||
|
@PrimaryColumn({ name: 'role_id' })
|
||||||
|
roleId: number;
|
||||||
|
@PrimaryColumn({ name: 'user_id' })
|
||||||
|
userId: number;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Entity('sys_user')
|
||||||
|
export class UserEntity {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
@Index({ unique: true })
|
||||||
|
@Column({ comment: '用户名', length: 100 })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Column({ comment: '密码', length: 100 })
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
@Column({ name: 'nick_name', comment: '昵称', length: 100, nullable: true })
|
||||||
|
nickName: string;
|
||||||
|
|
||||||
|
@Column({ comment: '头像', length: 255, nullable: true })
|
||||||
|
avatar: string;
|
||||||
|
|
||||||
|
@Column({ name: 'phone_code', comment: '区号', length: 20, nullable: true })
|
||||||
|
phoneCode: string;
|
||||||
|
|
||||||
|
@Column({ comment: '手机', length: 20, nullable: true })
|
||||||
|
mobile: string;
|
||||||
|
|
||||||
|
@Column({ comment: '邮箱', length: 50, nullable: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Column({ comment: '备注', length: 100, nullable: true })
|
||||||
|
remark: string;
|
||||||
|
|
||||||
|
@Column({ comment: '状态 0:禁用 1:启用', default: 1, type: 'int' })
|
||||||
|
status: number;
|
||||||
|
@Column({
|
||||||
|
name: 'create_time',
|
||||||
|
comment: '创建时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
createTime: Date;
|
||||||
|
@Column({
|
||||||
|
name: 'update_time',
|
||||||
|
comment: '修改时间',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
updateTime: Date;
|
||||||
|
|
||||||
|
// @ManyToMany(type => RoleEntity, res => res.users)
|
||||||
|
// @JoinTable({
|
||||||
|
// name: 'sys_user_roles',
|
||||||
|
// joinColumn: {
|
||||||
|
// name: 'userId',
|
||||||
|
// referencedColumnName: 'id',
|
||||||
|
// },
|
||||||
|
// inverseJoinColumn: {
|
||||||
|
// name: 'roleId',
|
||||||
|
// referencedColumnName: 'id',
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// roles: RoleEntity[];
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { EnumItem } from '../../../basic/enum-item';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
class ResourceTypes {
|
||||||
|
MENU = new EnumItem('menu', '菜单', 'blue');
|
||||||
|
BTN = new EnumItem('btn', '按钮', 'green');
|
||||||
|
ROUTE = new EnumItem('route', '路由', 'red');
|
||||||
|
|
||||||
|
names() {
|
||||||
|
const list = [];
|
||||||
|
_.forEach(this, (item, key) => {
|
||||||
|
list.push(item);
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ResourceTypeEnum = new ResourceTypes();
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Provide } from '@midwayjs/decorator';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { BaseService } from '../../../basic/base-service';
|
||||||
|
import { PermissionEntity } from '../entity/permission';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限资源
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class PermissionService extends BaseService {
|
||||||
|
@InjectEntityModel(PermissionEntity)
|
||||||
|
repository: Repository<PermissionEntity>;
|
||||||
|
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
async tree(options: any = {}) {
|
||||||
|
if (options.order == null) {
|
||||||
|
options.order = {
|
||||||
|
sort: 'ASC',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const list = await this.find(options);
|
||||||
|
return this.buildTree(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTree(list: any) {
|
||||||
|
const idMap = {};
|
||||||
|
const root = [];
|
||||||
|
for (const item of list) {
|
||||||
|
idMap[item.id] = item;
|
||||||
|
if (item.parentId == null || item.parentId <= 0) {
|
||||||
|
root.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.parentId > 0) {
|
||||||
|
const parent = idMap[item.parentId];
|
||||||
|
if (parent) {
|
||||||
|
if (parent.children == null) {
|
||||||
|
parent.children = [];
|
||||||
|
}
|
||||||
|
parent.children.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Provide } from '@midwayjs/decorator';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { BaseService } from '../../../basic/base-service';
|
||||||
|
import { RolePermissionEntity } from '../entity/role-permission';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色->权限
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class RolePermissionService extends BaseService {
|
||||||
|
@InjectEntityModel(RolePermissionEntity)
|
||||||
|
repository: Repository<RolePermissionEntity>;
|
||||||
|
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { Inject, Provide } from '@midwayjs/decorator';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import { In, Repository } from 'typeorm';
|
||||||
|
import { BaseService } from '../../../basic/base-service';
|
||||||
|
import { RoleEntity } from '../entity/role';
|
||||||
|
import { UserRoleService } from './user-role-service';
|
||||||
|
import { RolePermissionEntity } from '../entity/role-permission';
|
||||||
|
import { PermissionService } from './permission-service';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import { RolePermissionService } from './role-permission-service';
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class RoleService extends BaseService {
|
||||||
|
@InjectEntityModel(RoleEntity)
|
||||||
|
repository: Repository<RoleEntity>;
|
||||||
|
@Inject()
|
||||||
|
userRoleService: UserRoleService;
|
||||||
|
@Inject()
|
||||||
|
permissionService: PermissionService;
|
||||||
|
@Inject()
|
||||||
|
rolePermissionService: RolePermissionService;
|
||||||
|
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRoleIdsByUserId(id: any) {
|
||||||
|
const userRoles = await this.userRoleService.find({
|
||||||
|
where: { userId: id },
|
||||||
|
});
|
||||||
|
return userRoles.map(item => item.roleId);
|
||||||
|
}
|
||||||
|
async getByUserIds(ids: any) {
|
||||||
|
return await this.userRoleService.find({
|
||||||
|
where: {
|
||||||
|
userId: In(ids),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPermissionByRoleIds(roleIds: any) {
|
||||||
|
return await this.permissionService.repository
|
||||||
|
.createQueryBuilder('permission')
|
||||||
|
.innerJoinAndSelect(
|
||||||
|
RolePermissionEntity,
|
||||||
|
'rp',
|
||||||
|
'rp.permissionId = permission.id and rp.roleId in (:...roleIds)',
|
||||||
|
{ roleIds }
|
||||||
|
)
|
||||||
|
.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async addRoles(userId: number, roles) {
|
||||||
|
if (roles == null || roles.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const roleId of roles) {
|
||||||
|
await this.userRoleService.add({
|
||||||
|
userId,
|
||||||
|
roleId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateRoles(userId, roles) {
|
||||||
|
if (roles == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oldRoleIds = await this.getRoleIdsByUserId(userId);
|
||||||
|
if (_.xor(roles, oldRoleIds).length === 0) {
|
||||||
|
//如果两个数组相等,则不修改
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//先删除所有
|
||||||
|
await this.userRoleService.delete({ userId });
|
||||||
|
//再添加
|
||||||
|
await this.addRoles(userId, roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPermissionTreeByRoleId(id: any) {
|
||||||
|
const list = await this.getPermissionByRoleIds([id]);
|
||||||
|
return this.permissionService.buildTree(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPermissionIdsByRoleId(id: any) {
|
||||||
|
const list = await this.getPermissionByRoleIds([id]);
|
||||||
|
return list.map(item => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async authz(roleId: any, permissionIds: any) {
|
||||||
|
await this.rolePermissionService.delete({ roleId });
|
||||||
|
for (const permissionId of permissionIds) {
|
||||||
|
await this.rolePermissionService.add({
|
||||||
|
roleId,
|
||||||
|
permissionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Provide } from '@midwayjs/decorator';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { BaseService } from '../../../basic/base-service';
|
||||||
|
import { UserRoleEntity } from '../entity/user-role';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户->角色
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class UserRoleService extends BaseService {
|
||||||
|
@InjectEntityModel(UserRoleEntity)
|
||||||
|
repository: Repository<UserRoleEntity>;
|
||||||
|
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { Inject, Provide } from '@midwayjs/decorator';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { UserEntity } from '../entity/user';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as md5 from 'md5';
|
||||||
|
import { CommonException } from '../../../basic/exception/common-exception';
|
||||||
|
import { BaseService } from '../../../basic/base-service';
|
||||||
|
import { logger } from '../../../utils/logger';
|
||||||
|
import { RoleService } from './role-service';
|
||||||
|
import { PermissionService } from './permission-service';
|
||||||
|
import { UserRoleService } from './user-role-service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class UserService extends BaseService {
|
||||||
|
@InjectEntityModel(UserEntity)
|
||||||
|
repository: Repository<UserEntity>;
|
||||||
|
@Inject()
|
||||||
|
roleService: RoleService;
|
||||||
|
@Inject()
|
||||||
|
permissionService: PermissionService;
|
||||||
|
@Inject()
|
||||||
|
userRoleService: UserRoleService;
|
||||||
|
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得个人信息
|
||||||
|
*/
|
||||||
|
async mine() {
|
||||||
|
const info = await this.repository.findOne({
|
||||||
|
where: {
|
||||||
|
id: this.ctx.user.id,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
delete info.password;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
* @param param
|
||||||
|
*/
|
||||||
|
async add(param) {
|
||||||
|
const exists = await this.repository.findOne({
|
||||||
|
where:{
|
||||||
|
username: param.username,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!_.isEmpty(exists)) {
|
||||||
|
throw new CommonException('用户名已经存在');
|
||||||
|
}
|
||||||
|
const password = param.password ?? '123456';
|
||||||
|
param.password = md5(password); // 默认密码 建议未改密码不能登陆
|
||||||
|
await super.add(param);
|
||||||
|
//添加角色
|
||||||
|
if (param.roles && param.roles.length > 0) {
|
||||||
|
await this.roleService.addRoles(param.id, param.roles);
|
||||||
|
}
|
||||||
|
return param.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async update(param) {
|
||||||
|
if (param.id == null) {
|
||||||
|
throw new CommonException('id不能为空');
|
||||||
|
}
|
||||||
|
const userInfo = await this.repository.findOne({
|
||||||
|
where:{ id: param.id }
|
||||||
|
});
|
||||||
|
if (!userInfo) {
|
||||||
|
throw new CommonException('用户不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
delete param.username;
|
||||||
|
if (!_.isEmpty(param.password)) {
|
||||||
|
param.password = md5(param.password);
|
||||||
|
} else {
|
||||||
|
delete param.password;
|
||||||
|
}
|
||||||
|
await super.update(param);
|
||||||
|
await this.roleService.updateRoles(param.id, param.roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(param) {
|
||||||
|
return this.repository.findOne({
|
||||||
|
where:param
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPassword(rawPassword: any, md5Password: any) {
|
||||||
|
logger.info('md5', md5('123456'));
|
||||||
|
return md5(rawPassword) === md5Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的菜单资源列表
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
async getUserPermissions(id: any) {
|
||||||
|
const roleIds = await this.roleService.getRoleIdsByUserId(id);
|
||||||
|
|
||||||
|
return await this.roleService.getPermissionByRoleIds(roleIds);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { Rule,RuleType } from '@midwayjs/validate';
|
||||||
|
import { ALL, Inject } from '@midwayjs/decorator';
|
||||||
|
import { Body } from '@midwayjs/decorator';
|
||||||
|
import { Controller, Post, Provide } from '@midwayjs/decorator';
|
||||||
|
import { BaseController } from '../../../basic/base-controller';
|
||||||
|
import { CodeService } from '../service/code-service';
|
||||||
|
export class SmsCodeReq {
|
||||||
|
@Rule(RuleType.number().required())
|
||||||
|
phoneCode: number;
|
||||||
|
|
||||||
|
@Rule(RuleType.string().required())
|
||||||
|
mobile: string;
|
||||||
|
|
||||||
|
@Rule(RuleType.string().required().max(10))
|
||||||
|
randomStr: string;
|
||||||
|
|
||||||
|
@Rule(RuleType.number().required().max(4))
|
||||||
|
imgCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const enumsMap = {};
|
||||||
|
// glob('src/modules/**/enums/*.ts', {}, (err, matches) => {
|
||||||
|
// console.log('matched', matches);
|
||||||
|
// for (const filePath of matches) {
|
||||||
|
// const module = require('/' + filePath);
|
||||||
|
// console.log('modules', module);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/basic')
|
||||||
|
export class BasicController extends BaseController {
|
||||||
|
@Inject()
|
||||||
|
codeService: CodeService;
|
||||||
|
@Post('/sendSmsCode')
|
||||||
|
public sendSmsCode(
|
||||||
|
@Body(ALL)
|
||||||
|
body: SmsCodeReq
|
||||||
|
) {
|
||||||
|
// 设置缓存内容
|
||||||
|
return this.ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/captcha')
|
||||||
|
public async getCaptcha(
|
||||||
|
@Body()
|
||||||
|
randomStr
|
||||||
|
) {
|
||||||
|
console.assert(randomStr < 10, 'randomStr 过长');
|
||||||
|
const captcha = await this.codeService.generateCaptcha(randomStr);
|
||||||
|
return this.ok(captcha.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { Inject, Provide } from '@midwayjs/decorator';
|
||||||
|
import { CacheManager } from '@midwayjs/cache';
|
||||||
|
const svgCaptcha = require('svg-captcha');
|
||||||
|
|
||||||
|
// {data: '<svg.../svg>', text: 'abcd'}
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class CodeService {
|
||||||
|
@Inject()
|
||||||
|
cache: CacheManager; // 依赖注入CacheManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async generateCaptcha(randomStr) {
|
||||||
|
console.assert(randomStr < 10, 'randomStr 过长');
|
||||||
|
const c = svgCaptcha.create();
|
||||||
|
//{data: '<svg.../svg>', text: 'abcd'}
|
||||||
|
const imgCode = c.text; // = RandomUtil.randomStr(4, true);
|
||||||
|
await this.cache.set('imgCode:' + randomStr, imgCode, {
|
||||||
|
ttl: 2 * 60 * 1000, //过期时间 2分钟
|
||||||
|
});
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCaptchaText(randomStr) {
|
||||||
|
return await this.cache.get('imgCode:' + randomStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeCaptcha(randomStr) {
|
||||||
|
await this.cache.del('imgCode:' + randomStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkCaptcha(randomStr, userCaptcha) {
|
||||||
|
const code = await this.getCaptchaText(randomStr);
|
||||||
|
if (code == null) {
|
||||||
|
throw new Error('验证码已过期');
|
||||||
|
}
|
||||||
|
if (code !== userCaptcha) {
|
||||||
|
throw new Error('验证码不正确');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async sendSms(phoneCode, mobile, smsCode) {
|
||||||
|
console.assert(phoneCode != null && mobile != null, '手机号不能为空');
|
||||||
|
console.assert(smsCode != null, '验证码不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loginBySmsCode
|
||||||
|
*/
|
||||||
|
async loginBySmsCode(user, smsCode) {
|
||||||
|
console.assert(user.mobile != null, '手机号不能为空');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Inject,
|
||||||
|
Post,
|
||||||
|
Provide,
|
||||||
|
ALL,
|
||||||
|
} from '@midwayjs/decorator';
|
||||||
|
import { LoginService } from '../service/login-service';
|
||||||
|
import { BaseController } from '../../../basic/base-controller';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/')
|
||||||
|
export class LoginController extends BaseController {
|
||||||
|
@Inject()
|
||||||
|
loginService: LoginService;
|
||||||
|
@Post('/login')
|
||||||
|
public async login(
|
||||||
|
@Body(ALL)
|
||||||
|
user
|
||||||
|
) {
|
||||||
|
const token = await this.loginService.login(user);
|
||||||
|
return this.ok(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/logout')
|
||||||
|
public logout() {}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Config, Inject, Provide } from '@midwayjs/decorator';
|
||||||
|
import { UserService } from '../../authority/service/user-service';
|
||||||
|
import * as jwt from 'jsonwebtoken';
|
||||||
|
import { CommonException } from '../../../basic/exception/common-exception';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class LoginService {
|
||||||
|
@Inject()
|
||||||
|
userService: UserService;
|
||||||
|
@Config('biz.jwt')
|
||||||
|
private jwt: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* login
|
||||||
|
*/
|
||||||
|
async login(user) {
|
||||||
|
console.assert(user.username != null, '用户名不能为空');
|
||||||
|
const info = await this.userService.findOne({ username: user.username });
|
||||||
|
if (info == null) {
|
||||||
|
throw new CommonException('用户名或密码错误');
|
||||||
|
}
|
||||||
|
const right = this.userService.checkPassword(user.password, info.password);
|
||||||
|
if (!right) {
|
||||||
|
throw new CommonException('用户名或密码错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.generateToken(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成token
|
||||||
|
* @param user 用户对象
|
||||||
|
*/
|
||||||
|
async generateToken(user) {
|
||||||
|
const tokenInfo = {
|
||||||
|
username: user.username,
|
||||||
|
id: user.id,
|
||||||
|
};
|
||||||
|
const expire = this.jwt.expire;
|
||||||
|
const token = jwt.sign(tokenInfo, this.jwt.secret, {
|
||||||
|
expiresIn: expire,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
expire,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
const log4js = require('log4js');
|
||||||
|
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
|
||||||
|
const path = require('path');
|
||||||
|
const filename = path.join('/logs/server.log');
|
||||||
|
log4js.configure({
|
||||||
|
appenders: {
|
||||||
|
std: { type: 'stdout', level: 'debug' },
|
||||||
|
file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename },
|
||||||
|
},
|
||||||
|
categories: { default: { appenders: ['std'], level } },
|
||||||
|
});
|
||||||
|
export const logger = log4js.getLogger('fast');
|
|
@ -0,0 +1,43 @@
|
||||||
|
const numbers = '0123456789';
|
||||||
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||||
|
const specials = '~!@#$%^*()_+-=[]{}|;:,./<>?';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate random string
|
||||||
|
* @param {Number} length
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
function randomStr(length, options) {
|
||||||
|
length || (length = 8);
|
||||||
|
options || (options = {});
|
||||||
|
|
||||||
|
let chars = '';
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if (options === true) {
|
||||||
|
chars = numbers + letters;
|
||||||
|
} else if (typeof options === 'string') {
|
||||||
|
chars = options;
|
||||||
|
} else {
|
||||||
|
if (options.numbers !== false) {
|
||||||
|
chars += typeof options.numbers === 'string' ? options.numbers : numbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.letters !== false) {
|
||||||
|
chars += typeof options.letters === 'string' ? options.letters : letters;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.specials) {
|
||||||
|
chars +=
|
||||||
|
typeof options.specials === 'string' ? options.specials : specials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
length--;
|
||||||
|
result += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RandomUtil = { randomStr };
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
|
||||||
|
import { Framework } from '@midwayjs/koa';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
|
||||||
|
describe('test/controller/home.test.ts', () => {
|
||||||
|
|
||||||
|
it('should POST /api/get_user', async () => {
|
||||||
|
// create app
|
||||||
|
const app = await createApp<Framework>();
|
||||||
|
|
||||||
|
// make request
|
||||||
|
const result = await createHttpRequest(app).post('/api/get_user').query({ uid: 123 });
|
||||||
|
|
||||||
|
// use expect by jest
|
||||||
|
expect(result.status).toBe(200);
|
||||||
|
expect(result.body.message).toBe('OK');
|
||||||
|
|
||||||
|
// or use assert
|
||||||
|
assert.deepStrictEqual(result.status, 200);
|
||||||
|
assert.deepStrictEqual(result.body.data.uid, '123');
|
||||||
|
|
||||||
|
// close app
|
||||||
|
await close(app);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
|
||||||
|
import { Framework } from '@midwayjs/koa';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
|
||||||
|
describe('test/controller/home.test.ts', () => {
|
||||||
|
|
||||||
|
it('should GET /', async () => {
|
||||||
|
// create app
|
||||||
|
const app = await createApp<Framework>();
|
||||||
|
|
||||||
|
// make request
|
||||||
|
const result = await createHttpRequest(app).get('/');
|
||||||
|
|
||||||
|
// use expect by jest
|
||||||
|
expect(result.status).toBe(200);
|
||||||
|
expect(result.text).toBe('Hello Midwayjs!');
|
||||||
|
|
||||||
|
// or use assert
|
||||||
|
assert.deepStrictEqual(result.status, 200);
|
||||||
|
assert.deepStrictEqual(result.text, 'Hello Midwayjs!');
|
||||||
|
|
||||||
|
// close app
|
||||||
|
await close(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2018",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"inlineSourceMap":true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"skipLibCheck": false,
|
||||||
|
"pretty": true,
|
||||||
|
"declaration": true,
|
||||||
|
"typeRoots": [ "./typings", "./node_modules/@types"],
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue