zs 1 سال پیش
کامیت
0de1090043
91فایلهای تغییر یافته به همراه29287 افزوده شده و 0 حذف شده
  1. 11 0
      .editorconfig
  2. 11 0
      .eslintrc.json
  3. 13 0
      .gitignore
  4. 4 0
      .prettierrc.js
  5. 15 0
      README.md
  6. 29 0
      README.zh-CN.md
  7. 2 0
      bootstrap.js
  8. 20 0
      ecosystem.config.js
  9. 6 0
      jest.config.js
  10. 18457 0
      package-lock.json
  11. 62 0
      package.json
  12. 7006 0
      pnpm-lock.yaml
  13. 33 0
      src/config/config.default.ts
  14. 60 0
      src/config/config.local.ts
  15. 57 0
      src/config/config.prod.ts
  16. 7 0
      src/config/config.unittest.ts
  17. 53 0
      src/configuration.ts
  18. 68 0
      src/controller/core/course.controller.ts
  19. 68 0
      src/controller/core/follow.controller.ts
  20. 68 0
      src/controller/core/opinion.controller.ts
  21. 68 0
      src/controller/core/order.controller.ts
  22. 73 0
      src/controller/core/student.controller.ts
  23. 73 0
      src/controller/core/teacher.controller.ts
  24. 16 0
      src/controller/home.controller.ts
  25. 21 0
      src/controller/init.controller.ts
  26. 97 0
      src/controller/login.controller.ts
  27. 68 0
      src/controller/system/admin.controller.ts
  28. 68 0
      src/controller/system/config.controller.ts
  29. 68 0
      src/controller/system/dictData.controller.ts
  30. 68 0
      src/controller/system/dictType.controller.ts
  31. 62 0
      src/controller/system/menus.controller.ts
  32. 80 0
      src/controller/system/role.controller.ts
  33. 53 0
      src/controller/token.controller.ts
  34. 9 0
      src/controller/tool.controller.ts
  35. 50 0
      src/decorator/checkPermissionCode.ts
  36. 37 0
      src/decorator/verifyToken.decorator.ts
  37. 19 0
      src/entity/core/course.entity.ts
  38. 15 0
      src/entity/core/follow.entity.ts
  39. 15 0
      src/entity/core/opinion.entity.ts
  40. 21 0
      src/entity/core/order.entity.ts
  41. 23 0
      src/entity/core/student.entity.ts
  42. 39 0
      src/entity/core/teacher.entity.ts
  43. 34 0
      src/entity/system/admin.entity.ts
  44. 21 0
      src/entity/system/config.entity.ts
  45. 19 0
      src/entity/system/dictData.entity.ts
  46. 15 0
      src/entity/system/dictType.entity.ts
  47. 33 0
      src/entity/system/menus.entity.ts
  48. 17 0
      src/entity/system/role.entity.ts
  49. 16 0
      src/error/frame.error.ts
  50. 6 0
      src/interface.ts
  51. 88 0
      src/interface/core/course.interface.ts
  52. 82 0
      src/interface/core/follow.interface.ts
  53. 76 0
      src/interface/core/opinion.interface.ts
  54. 97 0
      src/interface/core/order.interface.ts
  55. 108 0
      src/interface/core/student.interface.ts
  56. 160 0
      src/interface/core/teacher.interface.ts
  57. 43 0
      src/interface/login.interface.ts
  58. 93 0
      src/interface/system/admin.interface.ts
  59. 91 0
      src/interface/system/config.interface.ts
  60. 92 0
      src/interface/system/dictData.interface.ts
  61. 82 0
      src/interface/system/dictType.interface.ts
  62. 127 0
      src/interface/system/menus.interface.ts
  63. 85 0
      src/interface/system/role.interface.ts
  64. 3 0
      src/locales/en_us/defaults.ts
  65. 35 0
      src/locales/en_us/errors.ts
  66. 11 0
      src/locales/zh_cn/controller_method.ts
  67. 3 0
      src/locales/zh_cn/defaults.ts
  68. 37 0
      src/locales/zh_cn/errors.ts
  69. 28 0
      src/middleware/checkOnePointLogin.middleware.ts
  70. 31 0
      src/middleware/newsQuery.middleware.ts
  71. 24 0
      src/middleware/setLocaleToCtx.middleware.ts
  72. 11 0
      src/service/core/course.service.ts
  73. 11 0
      src/service/core/follow.service.ts
  74. 11 0
      src/service/core/opinion.service.ts
  75. 11 0
      src/service/core/order.service.ts
  76. 11 0
      src/service/core/student.service.ts
  77. 11 0
      src/service/core/teacher.service.ts
  78. 23 0
      src/service/i18n.service.ts
  79. 162 0
      src/service/init.service.ts
  80. 136 0
      src/service/login.service.ts
  81. 11 0
      src/service/system/admin.service.ts
  82. 11 0
      src/service/system/config.service.ts
  83. 11 0
      src/service/system/dictData.service.ts
  84. 11 0
      src/service/system/dictType.service.ts
  85. 38 0
      src/service/system/menus.service.ts
  86. 73 0
      src/service/system/role.service.ts
  87. 7 0
      src/service/tool.service.ts
  88. 19 0
      src/service/util.service.ts
  89. 20 0
      test/controller/api.test.ts
  90. 21 0
      test/controller/home.test.ts
  91. 28 0
      tsconfig.json

+ 11 - 0
.editorconfig

@@ -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

+ 11 - 0
.eslintrc.json

@@ -0,0 +1,11 @@
+{
+  "extends": "./node_modules/mwts/",
+  "ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
+  "env": {
+    "jest": true
+  },
+  "rules": {
+    "prettier/prettier": 0,
+    "max-len": ["warn", { "code": 250 }]
+  }
+}

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+logs/
+npm-debug.log
+yarn-error.log
+node_modules/
+coverage/
+dist/
+.idea/
+run/
+.DS_Store
+*.sw*
+*.un~
+.tsbuildinfo
+.tsbuildinfo.*

+ 4 - 0
.prettierrc.js

@@ -0,0 +1,4 @@
+module.exports = {
+  ...require('mwts/.prettierrc.json'),
+  printWidth: 250,
+}

+ 15 - 0
README.md

@@ -0,0 +1,15 @@
+# vue3js-template-service
+## 1.单点登录
+* 1.不需要登录就可以使用的接口: 请求函数的方法注解参数添加 description: 'ignore'即可
+
+## 2.注解
+* checkPermissionCode
+|注解名|类型|说明|
+|:-:|:-:|:-:|
+|roleCode|string|该接口权限所需标识 ${路由名称}.${功能}|
+
+* dataRecord
+|注解名|类型|说明|
+|:-:|:-:|:-:|
+|before|string|获取原数据的函数名;该service下的函数名,纯自定义|
+|after|string|获取新数据的函数名;该service下的函数名,纯自定义|

+ 29 - 0
README.zh-CN.md

@@ -0,0 +1,29 @@
+# my_midway_project
+
+## 快速入门
+
+<!-- 在此次添加使用文档 -->
+
+如需进一步了解,参见 [midway 文档][midway]。
+
+### 本地开发
+
+```bash
+$ npm i
+$ npm run dev
+$ open http://localhost:7001/
+```
+
+### 部署
+
+```bash
+$ npm start
+```
+
+### 内置指令
+
+- 使用 `npm run lint` 来做代码风格检查。
+- 使用 `npm test` 来执行单元测试。
+
+
+[midway]: https://midwayjs.org

+ 2 - 0
bootstrap.js

@@ -0,0 +1,2 @@
+const { Bootstrap } = require('@midwayjs/bootstrap');
+Bootstrap.run();

+ 20 - 0
ecosystem.config.js

@@ -0,0 +1,20 @@
+'use strict';
+// 开发服务设置
+const app = 'service';
+module.exports = {
+  apps: [
+    {
+      name: app, // 应用名称
+      script: './bootstrap.js', // 实际启动脚本
+      out: `./logs/${app}.log`,
+      error: `./logs/${app}.err`,
+      watch: [
+        // 监控变化的目录,一旦变化,自动重启
+        'dist',
+      ],
+      env: {
+        NODE_ENV: 'production', // 环境参数,当前指定为生产环境
+      },
+    },
+  ],
+};

+ 6 - 0
jest.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'node',
+  testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
+  coveragePathIgnorePatterns: ['<rootDir>/test/'],
+};

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 18457 - 0
package-lock.json


+ 62 - 0
package.json

@@ -0,0 +1,62 @@
+{
+  "name": "vue3js-template-service",
+  "version": "1.0.0",
+  "description": "",
+  "private": true,
+  "dependencies": {
+    "@midwayjs/bootstrap": "^3.12.0",
+    "@midwayjs/bull": "3",
+    "@midwayjs/core": "^3.12.0",
+    "@midwayjs/decorator": "^3.12.0",
+    "@midwayjs/i18n": "3",
+    "@midwayjs/info": "^3.12.0",
+    "@midwayjs/jwt": "^3.13.7",
+    "@midwayjs/koa": "^3.12.0",
+    "@midwayjs/logger": "^3.1.0",
+    "@midwayjs/redis": "3",
+    "@midwayjs/swagger": "^3.13.7",
+    "@midwayjs/typegoose": "^3.0.0",
+    "@midwayjs/validate": "^3.12.0",
+    "@midwayjs/ws": "^3.15.8",
+    "@typegoose/typegoose": "^9.0.0",
+    "crypto-js": "^4.2.0",
+    "dayjs": "^1.11.10",
+    "free-midway-component": "^1.0.53",
+    "lodash": "^4.17.21",
+    "mongoose": "^6.0.7"
+  },
+  "devDependencies": {
+    "@midwayjs/cli": "^2.1.1",
+    "@midwayjs/mock": "^3.12.0",
+    "@types/jest": "^29.2.0",
+    "@types/koa": "^2.13.4",
+    "@types/lodash": "^4.14.202",
+    "@types/node": "14",
+    "cross-env": "^6.0.0",
+    "jest": "^29.2.2",
+    "mwts": "^1.3.0",
+    "mwtsc": "^1.4.0",
+    "swagger-ui-dist": "^5.10.5",
+    "ts-jest": "^29.0.3",
+    "typescript": "~4.8.0"
+  },
+  "engines": {
+    "node": ">=12.0.0"
+  },
+  "scripts": {
+    "start": "NODE_ENV=production node ./bootstrap.js",
+    "dev": "cross-env TS_NODE_TYPE_CHECK=false TS_NODE_TRANSPILE_ONLY=true NODE_ENV=local midway-bin dev --ts",
+    "test": "cross-env NODE_ENV=unittest jest",
+    "cov": "jest --coverage",
+    "lint": "mwts check",
+    "lint:fix": "mwts fix",
+    "ci": "npm run cov",
+    "build": "mwtsc --cleanOutDir"
+  },
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "author": "anonymous",
+  "license": "MIT"
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 7006 - 0
pnpm-lock.yaml


+ 33 - 0
src/config/config.default.ts

@@ -0,0 +1,33 @@
+import { MidwayConfig } from '@midwayjs/core';
+
+export default {
+  // use for cookie sign key, should change to your own and keep security
+  keys: '1697684406848_4078',
+  koa: {
+    port: 18000,
+  },
+  // 请求记录在redis留存时间,超过时间.数据变化将不会记录.以秒为单位--5分钟
+  requestTimeLimit: 300,
+  i18n: {
+    defaultLocale: 'zh-cn',
+    fallbacks: {
+      'en*': 'en_us',
+      'zh*': 'zh-cn',
+    },
+    localeTable: {
+      en_us: {
+        default: require('../locales/en_us/defaults'),
+        error: require('../locales/en_us/errors'),
+      },
+      zh_cn: {
+        default: require('../locales/zh_cn/defaults'),
+        error: require('../locales/zh_cn/errors'),
+        methods: require('../locales/zh_cn/controller_method'),
+      },
+    },
+  },
+  webSocket: {
+    serverHeartbeatInterval: 30000,
+    enableServerHeartbeatCheck: true,
+  },
+} as MidwayConfig;

+ 60 - 0
src/config/config.local.ts

@@ -0,0 +1,60 @@
+import { MidwayConfig } from '@midwayjs/core';
+const ip = '127.0.0.1'; //120.48.146.1
+const redisHost = '120.48.146.1';
+const redisPwd = '123456';
+const redisDB = 6;
+const projectDB = 'learn';
+const loginSign = 'learnDev';
+export default {
+  // use for cookie sign key, should change to your own and keep security
+  keys: '1697684406848_4078',
+  loginSign,
+  koa: {
+    port: 18000,
+    globalPrefix: '/learn/api',
+  },
+  swagger: {
+    swaggerPath: '/doc/api',
+  },
+  jwt: {
+    secret: 'Ziyouyanfa!@#',
+    expiresIn: 3600, // 3600
+  },
+  dbName: projectDB,
+  mongoose: {
+    dataSource: {
+      default: {
+        uri: `mongodb://${ip}:27017/${projectDB}`,
+        options: {
+          user: 'admin',
+          pass: 'admin',
+          authSource: 'admin',
+          useNewUrlParser: true,
+        },
+        entities: ['./entity'],
+      }
+    },
+  },
+  redis: {
+    client: {
+      port: 6379, // Redis port
+      host: redisHost, // Redis host
+      password: redisPwd,
+      db: redisDB,
+    },
+  },
+  bull: {
+    // 默认的队列配置
+    defaultQueueOptions: {
+      redis: {
+        port: 6379,
+        host: redisHost,
+        password: redisPwd,
+        db: redisDB,
+      },
+    },
+  },
+  upload: {
+    whitelist: null,
+  },
+} as MidwayConfig;

+ 57 - 0
src/config/config.prod.ts

@@ -0,0 +1,57 @@
+import { MidwayConfig } from '@midwayjs/core';
+const ip = 'host.docker.internal';
+const redisHost = ip;
+const redisPwd = '123456';
+const redisDB = 1;
+const projectDB = 'learn';
+const loginSign = 'learn_demand';
+export default {
+  // use for cookie sign key, should change to your own and keep security
+  keys: '1697684406848_4078',
+  loginSign,
+  koa: {
+    port: 18000,
+    globalPrefix: '/learn/api',
+  },
+  jwt: {
+    secret: 'Ziyouyanfa!@#',
+    expiresIn: 3600, // 3600
+  },
+  dbName: projectDB,
+  mongoose: {
+    dataSource: {
+      default: {
+        uri: `mongodb://${ip}:27017/${projectDB}`,
+        options: {
+          user: 'admin',
+          pass: 'admin',
+          authSource: 'admin',
+          useNewUrlParser: true,
+        },
+        entities: ['./entity'],
+      },
+    },
+  },
+  redis: {
+    client: {
+      port: 6379, // Redis port
+      host: redisHost, // Redis host
+      password: redisPwd,
+      db: redisDB,
+    },
+  },
+  bull: {
+    // 默认的队列配置
+    defaultQueueOptions: {
+      redis: {
+        port: 6379,
+        host: redisHost,
+        password: redisPwd,
+        db: redisDB,
+      },
+    },
+  },
+  upload: {
+    whitelist: null,
+  },
+} as MidwayConfig;

+ 7 - 0
src/config/config.unittest.ts

@@ -0,0 +1,7 @@
+import { MidwayConfig } from '@midwayjs/core';
+
+export default {
+  koa: {
+    port: null,
+  },
+} as MidwayConfig;

+ 53 - 0
src/configuration.ts

@@ -0,0 +1,53 @@
+import { Configuration, App, Inject, MidwayDecoratorService } from '@midwayjs/core';
+import * as koa from '@midwayjs/koa';
+import * as validate from '@midwayjs/validate';
+import * as info from '@midwayjs/info';
+import { join } from 'path';
+import * as FreeFrame from 'free-midway-component';
+import * as jwt from '@midwayjs/jwt';
+import * as ws from '@midwayjs/ws';
+import { VerifyTokenInit } from './decorator/verifyToken.decorator';
+import { CheckPermissionCodeInit } from './decorator/checkPermissionCode';
+import * as swagger from '@midwayjs/swagger';
+import * as redis from '@midwayjs/redis';
+import { CheckOnePointLoginMiddleware } from './middleware/checkOnePointLogin.middleware';
+import { newsQueryMiddleware } from './middleware/newsQuery.middleware';
+import * as i18n from '@midwayjs/i18n';
+import { SetLocaleToCtxMiddleware } from './middleware/setLocaleToCtx.middleware';
+import * as bull from '@midwayjs/bull';
+@Configuration({
+  imports: [
+    koa,
+    validate,
+    FreeFrame,
+    jwt,
+    ws,
+    redis,
+    i18n,
+    bull,
+    {
+      component: info,
+      enabledEnvironment: ['local'],
+    },
+    {
+      component: swagger,
+      enabledEnvironment: ['local'],
+    },
+  ],
+  importConfigs: [join(__dirname, './config')],
+})
+export class MainConfiguration {
+  @App('koa')
+  app: koa.Application;
+  @Inject()
+  decoratorService: MidwayDecoratorService;
+
+  async onReady() {
+    this.app.getMiddleware().insertFirst(SetLocaleToCtxMiddleware)
+    this.app.getMiddleware().insertAfter(CheckOnePointLoginMiddleware, 'checkToken');
+    this.app.getMiddleware().insertAfter(newsQueryMiddleware, 'newsQuery');
+    // 注解
+    VerifyTokenInit(this.decoratorService);
+    CheckPermissionCodeInit(this.decoratorService);
+  }
+}

+ 68 - 0
src/controller/core/course.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { CourseService } from '../../service/core/course.service';
+import { CDTO_course, CVO_course, FVO_course, QDTO_course, QVO_course, UDTO_course, UVAO_course } from '../../interface/core/course.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['课程'])
+@Controller('/course')
+export class CourseController extends BaseController {
+  @Inject()
+  service: CourseService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_course })
+  async create(@Body() data: CDTO_course) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_course(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_course })
+  async query(@Query() filter: QDTO_course, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_course(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_course })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_course(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_course })
+  async update(@Param('id') id: string, @Body() body: UDTO_course) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/core/follow.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { FollowService } from '../../service/core/follow.service';
+import { CDTO_follow, CVO_follow, FVO_follow, QDTO_follow, QVO_follow, UDTO_follow, UVAO_follow } from '../../interface/core/follow.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['关注'])
+@Controller('/follow')
+export class FollowController extends BaseController {
+  @Inject()
+  service: FollowService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_follow })
+  async create(@Body() data: CDTO_follow) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_follow(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_follow })
+  async query(@Query() filter: QDTO_follow, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_follow(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_follow })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_follow(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_follow })
+  async update(@Param('id') id: string, @Body() body: UDTO_follow) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/core/opinion.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { OpinionService } from '../../service/core/opinion.service';
+import { CDTO_opinion, CVO_opinion, FVO_opinion, QDTO_opinion, QVO_opinion, UDTO_opinion, UVAO_opinion } from '../../interface/core/opinion.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['意见反馈'])
+@Controller('/opinion')
+export class OpinionController extends BaseController {
+  @Inject()
+  service: OpinionService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_opinion })
+  async create(@Body() data: CDTO_opinion) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_opinion(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_opinion })
+  async query(@Query() filter: QDTO_opinion, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_opinion(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_opinion })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_opinion(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_opinion })
+  async update(@Param('id') id: string, @Body() body: UDTO_opinion) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/core/order.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { OrderService } from '../../service/core/order.service';
+import { CDTO_order, CVO_order, FVO_order, QDTO_order, QVO_order, UDTO_order, UVAO_order } from '../../interface/core/order.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['订单'])
+@Controller('/order')
+export class OrderController extends BaseController {
+  @Inject()
+  service: OrderService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_order })
+  async create(@Body() data: CDTO_order) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_order(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_order })
+  async query(@Query() filter: QDTO_order, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_order(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_order })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_order(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_order })
+  async update(@Param('id') id: string, @Body() body: UDTO_order) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 73 - 0
src/controller/core/student.controller.ts

@@ -0,0 +1,73 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { StudentService } from '../../service/core/student.service';
+import { CDTO_student, CVO_student, FVO_student, QDTO_student, QVO_student, UDTO_student, UVAO_student } from '../../interface/core/student.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+import { UtilService } from '../../service/util.service';
+@ApiTags(['家长或学生'])
+@Controller('/student')
+export class StudentController extends BaseController {
+  @Inject()
+  service: StudentService;
+
+  @Inject()
+  utilService: UtilService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_student })
+  async create(@Body() data: CDTO_student) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_student(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_student })
+  async query(@Query() filter: QDTO_student, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_student(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_student })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_student(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_student })
+  async update(@Param('id') id: string, @Body() body: UDTO_student) {
+    const result = await this.service.updateOne(id, body);
+    await this.utilService.updateUserAfter(id, body, 'Student');
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 73 - 0
src/controller/core/teacher.controller.ts

@@ -0,0 +1,73 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { TeacherService } from '../../service/core/teacher.service';
+import { CDTO_teacher, CVO_teacher, FVO_teacher, QDTO_teacher, QVO_teacher, UDTO_teacher, UVAO_teacher } from '../../interface/core/teacher.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+import { UtilService } from '../../service/util.service';
+@ApiTags(['老师'])
+@Controller('/teacher')
+export class TeacherController extends BaseController {
+  @Inject()
+  service: TeacherService;
+
+  @Inject()
+  utilService: UtilService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_teacher })
+  async create(@Body() data: CDTO_teacher) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_teacher(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_teacher })
+  async query(@Query() filter: QDTO_teacher, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_teacher(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_teacher })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_teacher(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_teacher })
+  async update(@Param('id') id: string, @Body() body: UDTO_teacher) {
+    const result = await this.service.updateOne(id, body);
+    await this.utilService.updateUserAfter(id, body, 'Teacher');
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 16 - 0
src/controller/home.controller.ts

@@ -0,0 +1,16 @@
+import { Controller, Get, Inject } from '@midwayjs/core';
+import { FrameworkErrorEnum, ServiceError } from 'free-midway-component';
+import { Context } from '@midwayjs/koa';
+import { MidwayI18nService } from '@midwayjs/i18n';
+@Controller('/')
+export class HomeController {
+  @Inject()
+  ctx: Context;
+  @Inject()
+  i18n: MidwayI18nService;
+  @Get('/')
+  async home(): Promise<string> {
+    throw new ServiceError(this.i18n.translate(FrameworkErrorEnum.BAD_ARGS, {  group: 'error' }),FrameworkErrorEnum.BAD_ARGS)
+    // return ;
+  }
+}

+ 21 - 0
src/controller/init.controller.ts

@@ -0,0 +1,21 @@
+import { Controller, Inject, Post } from '@midwayjs/decorator';
+import { Context } from '@midwayjs/koa';
+import { InitService } from '../service/init.service';
+@Controller('/init')
+export class InitController {
+  @Inject()
+  ctx: Context;
+
+  @Inject()
+  service: InitService;
+
+  @Post('/')
+  async index() {
+    await this.service.adminUser();
+    // 未初始化,则执行初始化程序
+    await this.service.initDict();
+    await this.service.initRole();
+    await this.service.initMenus();
+    return 'ok';
+  }
+}

+ 97 - 0
src/controller/login.controller.ts

@@ -0,0 +1,97 @@
+import { Body, Config, Controller, Inject, Param, Post, Get } from '@midwayjs/core';
+import { ApiTags } from '@midwayjs/swagger';
+import { LoginDTO, LoginType, LoginVO, UPwdDTO } from '../interface/login.interface';
+import { LoginService } from '../service/login.service';
+import { JwtService } from '@midwayjs/jwt';
+import { Context } from '@midwayjs/koa';
+const assert = require('assert');
+import get = require('lodash/get');
+import { UtilService } from '../service/util.service';
+// import { FrameworkErrorEnum, ServiceError } from 'free-midway-component';
+
+@ApiTags(['登录服务'])
+@Controller('/login')
+export class LoginController {
+  @Inject()
+  loginService: LoginService;
+  @Inject()
+  jwtService: JwtService;
+  @Inject()
+  utilService: UtilService;
+  @Config('jwt.secret')
+  jwtSecret;
+  @Config('jwt.expiresIn')
+  jwtExpiresIn;
+  @Inject()
+  ctx: Context;
+  /**
+   * 账密登录
+   * @param data 用户名和密码
+   * @param type 用户类型
+   */
+  @Post('/:type')
+  async toLogin(@Body() data: LoginDTO, @Param('type') type: string) {
+    const user = await this.loginService.loginByAccount(data, LoginType[type]);
+    user.role = type;
+    let vo = new LoginVO(user);
+    vo = JSON.parse(JSON.stringify(vo));
+    vo.login_code = await this.loginService.onePointLogin(vo);
+    const token = await this.jwtService.sign(vo, this.jwtSecret);
+    return token;
+  }
+
+  @Post('/refresh/:type')
+  async toRefresh(@Body() data, @Param('type') type: string) {
+    const user = await this.loginService.refresh(data, LoginType[type]);
+    user.role = type;
+    let vo = new LoginVO(user);
+    vo = JSON.parse(JSON.stringify(vo));
+    vo.login_code = await this.loginService.onePointLogin(vo);
+    const token = await this.jwtService.sign(vo, this.jwtSecret);
+    return token;
+  }
+
+  /**
+   * TODO:微信小程序登录
+   * @param openid 微信小程序openid
+   */
+  // @Post('/wxapp/:openid')
+  // async wxAppLogin(@Param('openid') openid: string) {
+  //   if (!openid) throw new ServiceError('缺少微信凭证!', FrameworkErrorEnum.BAD_PARAMS);
+  //   const user = await this.loginService.wxAppLogin(openid);
+  //   let vo = new LoginVO(user);
+  //   vo = JSON.parse(JSON.stringify(vo));
+  //   const token = await this.jwtService.sign(vo, this.jwtSecret, {
+  //     expiresIn: this.jwtExpiresIn,
+  //   });
+  //   return token;
+  // }
+  /**
+   * 修改密码
+   * @param data 修改密码所需数据
+   * @param type 账户类型
+   */
+  @Post('/updatePwd/:type')
+  async updatePwd(@Body() data: UPwdDTO, @Param('type') type: string) {
+    await this.loginService.updatePwd(data, LoginType[type]);
+    return 'ok';
+  }
+
+  @Post('/resetPwd/:type')
+  async resetPwd(@Body('_id') _id: string, @Param('type') type: string) {
+    // 随机密码
+    const data = new UPwdDTO();
+    data._id = _id;
+    data.password = this.utilService.randomStr();
+    await this.loginService.updatePwd(data, LoginType[type]);
+    return data.password;
+  }
+
+  @Get('/analysis')
+  async analysisToken() {
+    const token = get(this.ctx, 'request.header.token');
+    assert(token, '缺少token信息');
+    const result = await this.jwtService.decodeSync(token);
+    return result;
+  }
+}

+ 68 - 0
src/controller/system/admin.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { AdminService } from '../../service/system/admin.service';
+import { CDTO_admin, CVO_admin, FVO_admin, QDTO_admin, QVO_admin, UDTO_admin, UVAO_admin } from '../../interface/system/admin.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['管理员表'])
+@Controller('/admin')
+export class AdminController extends BaseController {
+  @Inject()
+  service: AdminService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_admin })
+  async create(@Body() data: CDTO_admin) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_admin(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_admin })
+  async query(@Query() filter: QDTO_admin, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_admin(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_admin })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_admin(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_admin })
+  async update(@Param('id') id: string, @Body() body: UDTO_admin) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/system/config.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { ConfigService } from '../../service/system/config.service';
+import { CDTO_config, CVO_config, FVO_config, QDTO_config, QVO_config, UDTO_config, UVAO_config } from '../../interface/system/config.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['基本设置'])
+@Controller('/config')
+export class ConfigController extends BaseController {
+  @Inject()
+  service: ConfigService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_config })
+  async create(@Body() data: CDTO_config) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_config(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_config })
+  async query(@Query() filter: QDTO_config, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_config(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_config })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_config(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_config })
+  async update(@Param('id') id: string, @Body() body: UDTO_config) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/system/dictData.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { DictDataService } from '../../service/system/dictData.service';
+import { CDTO_dictData, CVO_dictData, FVO_dictData, QDTO_dictData, QVO_dictData, UDTO_dictData, UVAO_dictData } from '../../interface/system/dictData.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['字典数据表'])
+@Controller('/dictData')
+export class DictDataController extends BaseController {
+  @Inject()
+  service: DictDataService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_dictData })
+  async create(@Body() data: CDTO_dictData) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_dictData(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_dictData })
+  async query(@Query() filter: QDTO_dictData, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit, sort: { sort: 1 } });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_dictData(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_dictData })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_dictData(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_dictData })
+  async update(@Param('id') id: string, @Body() body: UDTO_dictData) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 68 - 0
src/controller/system/dictType.controller.ts

@@ -0,0 +1,68 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { DictTypeService } from '../../service/system/dictType.service';
+import { CDTO_dictType, CVO_dictType, FVO_dictType, QDTO_dictType, QVO_dictType, UDTO_dictType, UVAO_dictType } from '../../interface/system/dictType.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['字典类型表'])
+@Controller('/dictType')
+export class DictTypeController extends BaseController {
+  @Inject()
+  service: DictTypeService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_dictType })
+  async create(@Body() data: CDTO_dictType) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_dictType(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_dictType })
+  async query(@Query() filter: QDTO_dictType, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_dictType(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_dictType })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_dictType(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_dictType })
+  async update(@Param('id') id: string, @Body() body: UDTO_dictType) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 62 - 0
src/controller/system/menus.controller.ts

@@ -0,0 +1,62 @@
+import { Body, Controller, Del, Get, Inject, Param, Post } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { MenusService } from '../../service/system/menus.service';
+import { CDTO_menus, CVO_menus, FVO_menus, QVO_menus, UDTO_menus, UVAO_menus } from '../../interface/system/menus.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['菜单表'])
+@Controller('/menus')
+export class MenusController extends BaseController {
+  @Inject()
+  service: MenusService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_menus })
+  async create(@Body() data: CDTO_menus) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_menus(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_menus })
+  async query() {
+    const list = await this.service.queryMenu();
+    return list;
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_menus })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_menus(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_menus })
+  async update(@Param('id') id: string, @Body() body: UDTO_menus) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 80 - 0
src/controller/system/role.controller.ts

@@ -0,0 +1,80 @@
+import { Body, Controller, Del, Get, Inject, Param, Post, Query } from '@midwayjs/decorator';
+import { BaseController } from 'free-midway-component';
+import { RoleService } from '../../service/system/role.service';
+import { CDTO_role, CVO_role, FVO_role, QDTO_role, QVO_role, UDTO_role, UVAO_role } from '../../interface/system/role.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+import { MenusService } from '../../service/system/menus.service';
+import { verifyToken } from '../../decorator/verifyToken.decorator';
+import { checkPermissionCode } from '../../decorator/checkPermissionCode';
+@ApiTags(['角色表'])
+@Controller('/role')
+export class RoleController extends BaseController {
+  @Inject()
+  service: RoleService;
+  @Inject()
+  menusService: MenusService;
+
+  @Post('/')
+  @verifyToken()
+  @Validate()
+  @ApiResponse({ type: CVO_role })
+  async create(@Body() data: CDTO_role) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_role(dbData);
+    return result;
+  }
+  @Get('/')
+  @verifyToken()
+  @checkPermissionCode({ roleCode: 'system_role' })
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_role })
+  async query(@Query() filter: QDTO_role, @Query('skip') skip: number, @Query('limit') limit: number) {
+    const list = await this.service.query(filter, { skip, limit });
+    const data = [];
+    for (const i of list) {
+      const newData = new QVO_role(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @verifyToken()
+  @ApiResponse({ type: FVO_role })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_role(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @verifyToken()
+  @checkPermissionCode({ roleCode: 'system_role.update' })
+  @Validate()
+  @ApiResponse({ type: UVAO_role })
+  async update(@Param('id') id: string, @Body() body: UDTO_role) {
+    const result = await this.service.updateOne(id, body);
+    return result;
+  }
+
+  @Del('/:id')
+  @verifyToken()
+  @Validate()
+  async delete(@Param('id') id: string) {
+    await this.service.delete(id);
+    return 'ok';
+  }
+  async createMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async updateMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+
+  async deleteMany(...args: any[]) {
+    throw new Error('Method not implemented.');
+  }
+}

+ 53 - 0
src/controller/token.controller.ts

@@ -0,0 +1,53 @@
+import { Controller, Get, Inject } from '@midwayjs/decorator';
+import { Context } from '@midwayjs/koa';
+import { ApiResponse, ApiTags } from '@midwayjs/swagger';
+// import { TokenService } from '../service/token.service';
+const assert = require('assert');
+import { JwtService } from '@midwayjs/jwt';
+import get = require('lodash/get');
+import { RoleService } from '../service/system/role.service';
+@ApiTags(['工具'])
+@Controller('/token')
+export class TokenController {
+  // @Inject()
+  // service: TokenService;
+
+  @Inject()
+  jwtService: JwtService;
+
+  @Inject()
+  roleService: RoleService;
+
+  @Inject()
+  ctx: Context;
+
+  @Get('/tokenView')
+  @ApiResponse({})
+  async tokenView() {
+    const token = get(this.ctx, 'request.header.token');
+    assert(token, '缺少token信息');
+    const result: any = await this.jwtService.decode(token);
+    const userMenusResult = await this.roleService.getUserMenus();
+    const menus = get(userMenusResult,'menus')
+    const role_code = get(userMenusResult, 'role_code');
+    result.menus = menus;
+    result.role_code = role_code;
+    return result;
+  }
+
+  @Get('/tokenJwt')
+  @ApiResponse({})
+  async tokenJwt() {
+    const token = get(this.ctx, 'request.header.token');
+    assert(token, '缺少token信息');
+    const result: any = await this.jwtService.decode(token);
+    return result;
+  }
+
+  // @Get('/app')
+  // async appLogin(fiiter) {
+  //   const { config, js_code } = fiiter.query;
+  //   const result: any = await this.service.appLogin(config, js_code);
+  //   return result;
+  // }
+}

+ 9 - 0
src/controller/tool.controller.ts

@@ -0,0 +1,9 @@
+import { Controller, Get, Inject, Query } from '@midwayjs/decorator';
+import { ApiResponse, ApiTags } from '@midwayjs/swagger';
+import { ToolService } from '../service/tool.service';
+@ApiTags(['工具'])
+@Controller('/tool')
+export class ToolController {
+  @Inject()
+  service: ToolService;
+}

+ 50 - 0
src/decorator/checkPermissionCode.ts

@@ -0,0 +1,50 @@
+import { JoinPoint, MidwayDecoratorService, REQUEST_OBJ_CTX_KEY, createCustomMethodDecorator } from '@midwayjs/core';
+import { FrameworkErrorEnum, ServiceError } from 'free-midway-component';
+import { get } from 'lodash';
+import { RoleService } from '../service/system/role.service';
+import { I18nService } from '../service/i18n.service';
+import { FrameErrorEnum } from '../error/frame.error';
+export const CHECKPERMISSIONCODE_KEY = 'decorator:check_permission_code';
+export const checkPermissionCode = options => {
+  return createCustomMethodDecorator(CHECKPERMISSIONCODE_KEY, options);
+};
+// 第二层及以后的权限,去掉第一层级
+const resetRoleMenus = list => {
+  list = list.map(i => {
+    const arr = i.split('.');
+    if (arr.length <= 1) return i;
+    arr.shift();
+    return arr.join('.');
+  });
+  return list;
+};
+
+export const CheckPermissionCodeInit = (decoratorService: MidwayDecoratorService) => {
+  decoratorService.registerMethodHandler(CHECKPERMISSIONCODE_KEY, options => {
+    return {
+      around: async (joinPoint: JoinPoint) => {
+        const roleCode = get(options, 'metadata.roleCode');
+        if (roleCode) {
+          const instance = joinPoint.target;
+          const ctx = instance[REQUEST_OBJ_CTX_KEY];
+          const roleService = await ctx.requestContext.getAsync(RoleService);
+          const isAdmin = roleService.isSuperAdmin();
+          if (!isAdmin) {
+            let roleMenus = await roleService.getUserMenus(true);
+            roleMenus = resetRoleMenus(roleMenus);
+            if (!roleMenus.includes(roleCode)) {
+              const i18n = await ctx.requestContext.getAsync(I18nService);
+              throw new ServiceError(i18n.translateError(FrameErrorEnum.NO_PERMISSION), FrameErrorEnum.NO_PERMISSION);
+            }
+            const result = await joinPoint.proceed(...joinPoint.args);
+            return result;
+          }
+          const result = await joinPoint.proceed(...joinPoint.args);
+          return result;
+        }
+        const result = await joinPoint.proceed(...joinPoint.args);
+        return result;
+      },
+    };
+  });
+};

+ 37 - 0
src/decorator/verifyToken.decorator.ts

@@ -0,0 +1,37 @@
+import {
+  JoinPoint,
+  MidwayDecoratorService,
+  REQUEST_OBJ_CTX_KEY,
+  createCustomMethodDecorator,
+} from '@midwayjs/core';
+import { FrameworkErrorEnum, ServiceError } from 'free-midway-component';
+export const VERIFYTOKEN_KEY = 'decorator:verify_token';
+/**
+ *
+ * 只检测是否有token,此装饰器不做具体用token处理什么.
+ */
+export function verifyToken() {
+  return createCustomMethodDecorator(VERIFYTOKEN_KEY, {});
+}
+/**
+ * 验证token装饰器实现
+ * @param decoratorService 装饰器服务
+ */
+export function VerifyTokenInit(decoratorService: MidwayDecoratorService) {
+  decoratorService.registerMethodHandler(VERIFYTOKEN_KEY, options => {
+    return {
+      around: async (joinPoint: JoinPoint) => {
+        const instance = joinPoint.target;
+        const ctx = instance[REQUEST_OBJ_CTX_KEY];
+        const user = ctx.user;
+        if (!user)
+          throw new ServiceError(
+            '未检测到登录信息,无法访问接口!',
+            FrameworkErrorEnum.NOT_LOGIN
+          );
+        const result = await joinPoint.proceed(...joinPoint.args);
+        return result;
+      },
+    };
+  });
+}

+ 19 - 0
src/entity/core/course.entity.ts

@@ -0,0 +1,19 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'course' },
+})
+export class Course extends BaseModel {
+  @prop({ required: false, index: true, zh: '老师id' })
+  teacher: string;
+  @prop({ required: false, index: false, zh: '金额' })
+  money: number;
+  @prop({ required: false, index: false, zh: '开始时间' })
+  start_time: string;
+  @prop({ required: false, index: false, zh: '结束时间' })
+  end_time: string;
+  @prop({ required: false, index: false, zh: '简介' })
+  brief: string;
+  @prop({ required: false, index: false, zh: '状态' })
+  status: string;
+}

+ 15 - 0
src/entity/core/follow.entity.ts

@@ -0,0 +1,15 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'follow' },
+})
+export class Follow extends BaseModel {
+  @prop({ required: false, index: true, zh: '用户id' })
+  student: string;
+  @prop({ required: false, index: false, zh: '来源id' })
+  source: string;
+  @prop({ required: false, index: true, zh: '类型' })
+  type: string;
+  @prop({ required: false, index: true, zh: '时间' })
+  time: string;
+}

+ 15 - 0
src/entity/core/opinion.entity.ts

@@ -0,0 +1,15 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'opinion' },
+})
+export class Opinion extends BaseModel {
+  @prop({ required: false, index: false, zh: '用户id' })
+  student: string;
+  @prop({ required: false, index: false, zh: '问题类型' })
+  type: string;
+  @prop({ required: false, index: false, zh: '问题描述' })
+  brief: string;
+  @prop({ required: false, index: false, zh: '图片' })
+  file: Array<any>;
+}

+ 21 - 0
src/entity/core/order.entity.ts

@@ -0,0 +1,21 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'order' },
+})
+export class Order extends BaseModel {
+  @prop({ required: false, index: true, zh: '用户id' })
+  student: string;
+  @prop({ required: false, index: true, zh: '课程id' })
+  course: string;
+  @prop({ required: false, index: false, zh: '总金额' })
+  total: number;
+  @prop({ required: false, index: false, zh: '下单时间' })
+  buy_time: string;
+  @prop({ required: false, index: false, zh: '订单号' })
+  no: string;
+  @prop({ required: false, index: false, zh: '支付数据' })
+  pay: string;
+  @prop({ required: false, index: true, zh: '订单状态' })
+  status: string;
+}

+ 23 - 0
src/entity/core/student.entity.ts

@@ -0,0 +1,23 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'student' },
+})
+export class Student extends BaseModel {
+  @prop({ required: false, index: true, zh: '微信唯一标书' })
+  openid: string;
+  @prop({ required: false, index: false, zh: '头像' })
+  icon: Array<any>;
+  @prop({ required: false, index: true, zh: '昵称' })
+  nick_name: string;
+  @prop({ required: false, index: true, zh: '性别' })
+  gender: string;
+  @prop({ required: false, index: true, zh: '年龄' })
+  age: string;
+  @prop({ required: false, index: true, zh: '年级' })
+  grade: string;
+  @prop({ required: false, index: false, zh: '家庭地址' })
+  address: string;
+  @prop({ required: false, index: true, zh: '状态' })
+  status: string;
+}

+ 39 - 0
src/entity/core/teacher.entity.ts

@@ -0,0 +1,39 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'teacher' },
+})
+export class Teacher extends BaseModel {
+  @prop({ required: false, index: true, zh: '微信唯一标识' })
+  openid: string;
+  @prop({ required: false, index: false, zh: '头像' })
+  icon: Array<any>;
+  @prop({ required: false, index: true, zh: '昵称' })
+  nick_name: string;
+  @prop({ required: false, index: true, zh: '性别' })
+  gender: string;
+  @prop({ required: false, index: true, zh: '年龄' })
+  age: string;
+  @prop({ required: false, index: true, zh: '手机号' })
+  phone: string;
+  @prop({ required: false, index: true, zh: '所教年级' })
+  grade: string;
+  @prop({ required: false, index: false, zh: '身份证类型' })
+  cardType: string;
+  @prop({ required: false, index: true, zh: '身份证号码' })
+  card: string;
+  @prop({ required: false, index: false, zh: '任课年限' })
+  years: string;
+  @prop({ required: false, index: true, zh: '学历' })
+  education: string;
+  @prop({ required: false, index: false, zh: '学科' })
+  subject: string;
+  @prop({ required: false, index: true, zh: '院校' })
+  college: string;
+  @prop({ required: false, index: true, zh: '学业状态' })
+  learnStatus: string;
+  @prop({ required: false, index: true, zh: '简介' })
+  brief: string;
+  @prop({ required: false, index: true, zh: '状态' })
+  status: string;
+}

+ 34 - 0
src/entity/system/admin.entity.ts

@@ -0,0 +1,34 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+import { isString } from 'lodash';
+@modelOptions({
+  schemaOptions: { collection: 'admin' },
+})
+export class Admin extends BaseModel {
+  @prop({ required: true, index: true, zh: '账号' })
+  account: string;
+  @prop({ required: false, index: false, zh: '名称' })
+  nick_name: string;
+  // 手动删除set前的大括号,处理太麻烦了.就手动删除吧
+  @prop({
+    required: false,
+    index: false,
+    zh: '密码',
+    select: false,
+    set: val => {
+      if (isString(val)) {
+        return { secret: val };
+      }
+      return val;
+    },
+  })
+  password: object;
+  @prop({ required: false, index: false, zh: '是否是超级管理员', remark: '0:超级管理员;1普通用户', default: '1' })
+  is_super: string;
+  @prop({ required: false, index: false, zh: '角色' })
+  role: string;
+  @prop({ required: false, index: false, zh: 'openid' })
+  openid: string;
+  @prop({ required: false, index: false, zh: '是否启用', default: '0' })
+  is_use: string;
+}

+ 21 - 0
src/entity/system/config.entity.ts

@@ -0,0 +1,21 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'config' },
+})
+export class Config extends BaseModel {
+  @prop({ required: false, index: false, zh: '标题' })
+  title: string;
+  @prop({ required: false, index: false, zh: 'logo' })
+  logo: Array<any>;
+  @prop({ required: false, index: false, zh: '联系电话' })
+  phone: string;
+  @prop({ required: false, index: false, zh: '首页轮播图' })
+  file: Array<any>;
+  @prop({ required: false, index: false, zh: '底部提示语' })
+  bottom_title: string;
+  @prop({ required: false, index: false, zh: '用户协议' })
+  agreement: string;
+  @prop({ required: false, index: false, zh: '简介' })
+  brief: string;
+}

+ 19 - 0
src/entity/system/dictData.entity.ts

@@ -0,0 +1,19 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'dictData' },
+})
+export class DictData extends BaseModel {
+  @prop({ required: false, index: true, zh: '字典类型编码' })
+  code: string;
+  @prop({ required: false, index: false, zh: '数据显示值' })
+  label: string;
+  @prop({ required: false, index: false, zh: '数据选择值' })
+  value: string;
+  @prop({ required: false, index: false, zh: '排序' })
+  sort: number;
+  @prop({ required: false, index: false, zh: '备注' })
+  remark: string;
+  @prop({ required: false, index: true, zh: '是否使用', default: '0' })
+  is_use: string;
+}

+ 15 - 0
src/entity/system/dictType.entity.ts

@@ -0,0 +1,15 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'dictType' },
+})
+export class DictType extends BaseModel {
+  @prop({ required: false, index: false, zh: '字典类型名称' })
+  title: string;
+  @prop({ required: false, index: true, zh: '编码' })
+  code: string;
+  @prop({ required: false, index: false, zh: '备注' })
+  remark: string;
+  @prop({ required: false, index: true, zh: '是否使用', default: '0' })
+  is_use: string;
+}

+ 33 - 0
src/entity/system/menus.entity.ts

@@ -0,0 +1,33 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'menus' },
+})
+export class Menus extends BaseModel {
+  @prop({ required: false, index: false, zh: '菜单名称' })
+  name: string;
+  @prop({ required: false, index: false, zh: '路由名称', remark: '英文' })
+  route_name: string;
+  @prop({ required: false, index: false, zh: '国际化编码' })
+  i18n_code: string;
+  @prop({ required: false, index: true, zh: '父级菜单' })
+  parent_id: string;
+  @prop({ required: false, index: false, zh: '显示顺序' })
+  order_num: number;
+  @prop({ required: false, index: false, zh: '路由地址' })
+  path: string;
+  @prop({ required: false, index: false, zh: '组件地址' })
+  component: string;
+  @prop({ required: false, index: true, zh: '菜单类型', remark: '0:目录;1:菜单;2:子页面' })
+  type: string;
+  @prop({ required: false, index: false, zh: '图标' })
+  icon: string;
+  @prop({ required: false, index: false, zh: '功能列表', remark: '不在功能列表中的功能不能使用' })
+  config: Array<any>;
+  @prop({ required: false, index: true, zh: '是否为默认菜单', default: '1', remark: '默认:0,非默认:1; 默认不能删除' })
+  is_default: string;
+  @prop({ required: false, index: false, zh: '备注' })
+  remark: string;
+  @prop({ required: false, index: true, zh: '是否启用', default: '0' })
+  is_use: string;
+}

+ 17 - 0
src/entity/system/role.entity.ts

@@ -0,0 +1,17 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'role' },
+})
+export class Role extends BaseModel {
+  @prop({ required: false, index: false, zh: '角色名称' })
+  name: string;
+  @prop({ required: false, index: true, zh: '角色编码' })
+  code: string;
+  @prop({ required: false, index: false, zh: '菜单' })
+  menu: Array<any>;
+  @prop({ required: false, index: false, zh: '简介' })
+  brief: string;
+  @prop({ required: false, index: true, zh: '是否使用', default: '0' })
+  is_use: string;
+}

+ 16 - 0
src/error/frame.error.ts

@@ -0,0 +1,16 @@
+import { registerErrorCode } from '@midwayjs/core';
+
+export enum ErrorCode {
+  SERVICE_ERROR = '400',
+  SERVICE_CANT_USE = '400-1',
+  NO_PERMISSION = '401',
+  USER_NOT_FOUND = '401-1',
+  BAD_PASSWORD = '401-2',
+  NOT_LOGIN = '401-3',
+  ACCOUNT_HAS_EXPIRED = '401-4',
+  ACCOUNT_LOGGED_IN_ELESWHERE = '401-5',
+  USER_IS_DISABLED = '401-6',
+  ROLE_IS_DISABLED = '401-7',
+  SERVICE_REPEAT = '401-8',
+}
+export const FrameErrorEnum = registerErrorCode('FrameError', ErrorCode);

+ 6 - 0
src/interface.ts

@@ -0,0 +1,6 @@
+/**
+ * @description User-Service parameters
+ */
+export interface IUserOptions {
+  uid: number;
+}

+ 88 - 0
src/interface/core/course.interface.ts

@@ -0,0 +1,88 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_course {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '老师id' })
+  'teacher': string = undefined;
+  @ApiProperty({ description: '金额' })
+  'money': number = undefined;
+  @ApiProperty({ description: '开始时间' })
+  'start_time': string = undefined;
+  @ApiProperty({ description: '结束时间' })
+  'end_time': string = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+export class QDTO_course extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['teacher'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '老师id' })
+  'teacher': string = undefined;
+}
+
+export class QVO_course extends FVO_course {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_course {
+  @ApiProperty({ description: '老师id' })
+  @Rule(RuleType['string']().empty(''))
+  'teacher': string = undefined;
+  @ApiProperty({ description: '金额' })
+  @Rule(RuleType['number']().empty(''))
+  'money': number = undefined;
+  @ApiProperty({ description: '开始时间' })
+  @Rule(RuleType['string']().empty(''))
+  'start_time': string = undefined;
+  @ApiProperty({ description: '结束时间' })
+  @Rule(RuleType['string']().empty(''))
+  'end_time': string = undefined;
+  @ApiProperty({ description: '简介' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+  @ApiProperty({ description: '状态' })
+  @Rule(RuleType['string']().empty(''))
+  'status': string = undefined;
+}
+
+export class CVO_course extends FVO_course {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_course extends CDTO_course {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_course extends FVO_course {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 82 - 0
src/interface/core/follow.interface.ts

@@ -0,0 +1,82 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_follow {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '用户id' })
+  'student': string = undefined;
+  @ApiProperty({ description: '来源id' })
+  'source': string = undefined;
+  @ApiProperty({ description: '类型' })
+  'type': string = undefined;
+  @ApiProperty({ description: '时间' })
+  'time': string = undefined;
+}
+
+export class QDTO_follow extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['student', 'type', 'time'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '用户id' })
+  'student': string = undefined;
+  @ApiProperty({ description: '类型' })
+  'type': string = undefined;
+  @ApiProperty({ description: '时间' })
+  'time': string = undefined;
+}
+
+export class QVO_follow extends FVO_follow {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_follow {
+  @ApiProperty({ description: '用户id' })
+  @Rule(RuleType['string']().empty(''))
+  'student': string = undefined;
+  @ApiProperty({ description: '来源id' })
+  @Rule(RuleType['string']().empty(''))
+  'source': string = undefined;
+  @ApiProperty({ description: '类型' })
+  @Rule(RuleType['string']().empty(''))
+  'type': string = undefined;
+  @ApiProperty({ description: '时间' })
+  @Rule(RuleType['string']().empty(''))
+  'time': string = undefined;
+}
+
+export class CVO_follow extends FVO_follow {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_follow extends CDTO_follow {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_follow extends FVO_follow {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 76 - 0
src/interface/core/opinion.interface.ts

@@ -0,0 +1,76 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_opinion {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '用户id' })
+  'student': string = undefined;
+  @ApiProperty({ description: '问题类型' })
+  'type': string = undefined;
+  @ApiProperty({ description: '问题描述' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '图片' })
+  'file': Array<any> = undefined;
+}
+
+export class QDTO_opinion extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = [];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+}
+
+export class QVO_opinion extends FVO_opinion {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_opinion {
+  @ApiProperty({ description: '用户id' })
+  @Rule(RuleType['string']().empty(''))
+  'student': string = undefined;
+  @ApiProperty({ description: '问题类型' })
+  @Rule(RuleType['string']().empty(''))
+  'type': string = undefined;
+  @ApiProperty({ description: '问题描述' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+  @ApiProperty({ description: '图片' })
+  @Rule(RuleType['array']().empty(''))
+  'file': Array<any> = undefined;
+}
+
+export class CVO_opinion extends FVO_opinion {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_opinion extends CDTO_opinion {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_opinion extends FVO_opinion {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 97 - 0
src/interface/core/order.interface.ts

@@ -0,0 +1,97 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_order {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '用户id' })
+  'student': string = undefined;
+  @ApiProperty({ description: '课程id' })
+  'course': string = undefined;
+  @ApiProperty({ description: '总金额' })
+  'total': number = undefined;
+  @ApiProperty({ description: '下单时间' })
+  'buy_time': string = undefined;
+  @ApiProperty({ description: '订单号' })
+  'no': string = undefined;
+  @ApiProperty({ description: '支付数据' })
+  'pay': string = undefined;
+  @ApiProperty({ description: '订单状态' })
+  'status': string = undefined;
+}
+
+export class QDTO_order extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['student', 'course', 'status'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '用户id' })
+  'student': string = undefined;
+  @ApiProperty({ description: '课程id' })
+  'course': string = undefined;
+  @ApiProperty({ description: '订单状态' })
+  'status': string = undefined;
+}
+
+export class QVO_order extends FVO_order {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_order {
+  @ApiProperty({ description: '用户id' })
+  @Rule(RuleType['string']().empty(''))
+  'student': string = undefined;
+  @ApiProperty({ description: '课程id' })
+  @Rule(RuleType['string']().empty(''))
+  'course': string = undefined;
+  @ApiProperty({ description: '总金额' })
+  @Rule(RuleType['number']().empty(''))
+  'total': number = undefined;
+  @ApiProperty({ description: '下单时间' })
+  @Rule(RuleType['string']().empty(''))
+  'buy_time': string = undefined;
+  @ApiProperty({ description: '订单号' })
+  @Rule(RuleType['string']().empty(''))
+  'no': string = undefined;
+  @ApiProperty({ description: '支付数据' })
+  @Rule(RuleType['string']().empty(''))
+  'pay': string = undefined;
+  @ApiProperty({ description: '订单状态' })
+  @Rule(RuleType['string']().empty(''))
+  'status': string = undefined;
+}
+
+export class CVO_order extends FVO_order {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_order extends CDTO_order {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_order extends FVO_order {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 108 - 0
src/interface/core/student.interface.ts

@@ -0,0 +1,108 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_student {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '微信唯一标书' })
+  'openid': string = undefined;
+  @ApiProperty({ description: '头像' })
+  'icon': Array<any> = undefined;
+  @ApiProperty({ description: '昵称' })
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  'age': string = undefined;
+  @ApiProperty({ description: '年级' })
+  'grade': string = undefined;
+  @ApiProperty({ description: '家庭地址' })
+  'address': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+export class QDTO_student extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['openid', 'nick_name', 'gender', 'age', 'grade', 'status'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '微信唯一标书' })
+  'openid': string = undefined;
+  @ApiProperty({ description: '昵称' })
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  'age': string = undefined;
+  @ApiProperty({ description: '年级' })
+  'grade': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+export class QVO_student extends FVO_student {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_student {
+  @ApiProperty({ description: '微信唯一标书' })
+  @Rule(RuleType['string']().empty(''))
+  'openid': string = undefined;
+  @ApiProperty({ description: '头像' })
+  @Rule(RuleType['array']().empty(''))
+  'icon': Array<any> = undefined;
+  @ApiProperty({ description: '昵称' })
+  @Rule(RuleType['string']().empty(''))
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  @Rule(RuleType['string']().empty(''))
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  @Rule(RuleType['string']().empty(''))
+  'age': string = undefined;
+  @ApiProperty({ description: '年级' })
+  @Rule(RuleType['string']().empty(''))
+  'grade': string = undefined;
+  @ApiProperty({ description: '家庭地址' })
+  @Rule(RuleType['string']().empty(''))
+  'address': string = undefined;
+  @ApiProperty({ description: '状态' })
+  @Rule(RuleType['string']().empty(''))
+  'status': string = undefined;
+}
+
+export class CVO_student extends FVO_student {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_student extends CDTO_student {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_student extends FVO_student {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 160 - 0
src/interface/core/teacher.interface.ts

@@ -0,0 +1,160 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_teacher {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '微信唯一标识' })
+  'openid': string = undefined;
+  @ApiProperty({ description: '头像' })
+  'icon': Array<any> = undefined;
+  @ApiProperty({ description: '昵称' })
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  'age': string = undefined;
+  @ApiProperty({ description: '手机号' })
+  'phone': string = undefined;
+  @ApiProperty({ description: '所教年级' })
+  'grade': string = undefined;
+  @ApiProperty({ description: '身份证类型' })
+  'cardType': string = undefined;
+  @ApiProperty({ description: '身份证号码' })
+  'card': string = undefined;
+  @ApiProperty({ description: '任课年限' })
+  'years': string = undefined;
+  @ApiProperty({ description: '学历' })
+  'education': string = undefined;
+  @ApiProperty({ description: '学科' })
+  'subject': string = undefined;
+  @ApiProperty({ description: '院校' })
+  'college': string = undefined;
+  @ApiProperty({ description: '学业状态' })
+  'learnStatus': string = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+export class QDTO_teacher extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['openid', 'nick_name', 'gender', 'age', 'phone', 'grade', 'card', 'education', 'college', 'learnStatus', 'brief', 'status'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '微信唯一标识' })
+  'openid': string = undefined;
+  @ApiProperty({ description: '昵称' })
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  'age': string = undefined;
+  @ApiProperty({ description: '手机号' })
+  'phone': string = undefined;
+  @ApiProperty({ description: '所教年级' })
+  'grade': string = undefined;
+  @ApiProperty({ description: '身份证号码' })
+  'card': string = undefined;
+  @ApiProperty({ description: '学历' })
+  'education': string = undefined;
+  @ApiProperty({ description: '院校' })
+  'college': string = undefined;
+  @ApiProperty({ description: '学业状态' })
+  'learnStatus': string = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+export class QVO_teacher extends FVO_teacher {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_teacher {
+  @ApiProperty({ description: '微信唯一标识' })
+  @Rule(RuleType['string']().empty(''))
+  'openid': string = undefined;
+  @ApiProperty({ description: '头像' })
+  @Rule(RuleType['array']().empty(''))
+  'icon': Array<any> = undefined;
+  @ApiProperty({ description: '昵称' })
+  @Rule(RuleType['string']().empty(''))
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '性别' })
+  @Rule(RuleType['string']().empty(''))
+  'gender': string = undefined;
+  @ApiProperty({ description: '年龄' })
+  @Rule(RuleType['string']().empty(''))
+  'age': string = undefined;
+  @ApiProperty({ description: '手机号' })
+  @Rule(RuleType['string']().empty(''))
+  'phone': string = undefined;
+  @ApiProperty({ description: '所教年级' })
+  @Rule(RuleType['string']().empty(''))
+  'grade': string = undefined;
+  @ApiProperty({ description: '身份证类型' })
+  @Rule(RuleType['string']().empty(''))
+  'cardType': string = undefined;
+  @ApiProperty({ description: '身份证号码' })
+  @Rule(RuleType['string']().empty(''))
+  'card': string = undefined;
+  @ApiProperty({ description: '任课年限' })
+  @Rule(RuleType['string']().empty(''))
+  'years': string = undefined;
+  @ApiProperty({ description: '学历' })
+  @Rule(RuleType['string']().empty(''))
+  'education': string = undefined;
+  @ApiProperty({ description: '学科' })
+  @Rule(RuleType['string']().empty(''))
+  'subject': string = undefined;
+  @ApiProperty({ description: '院校' })
+  @Rule(RuleType['string']().empty(''))
+  'college': string = undefined;
+  @ApiProperty({ description: '学业状态' })
+  @Rule(RuleType['string']().empty(''))
+  'learnStatus': string = undefined;
+  @ApiProperty({ description: '简介' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+  @ApiProperty({ description: '状态' })
+  @Rule(RuleType['string']().empty(''))
+  'status': string = undefined;
+}
+
+export class CVO_teacher extends FVO_teacher {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_teacher extends CDTO_teacher {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_teacher extends FVO_teacher {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 43 - 0
src/interface/login.interface.ts

@@ -0,0 +1,43 @@
+import { ApiProperty } from '@midwayjs/swagger';
+import { Rule, RuleType } from '@midwayjs/validate';
+import { get } from 'lodash';
+export enum LoginType {
+  Admin = 'Admin',
+  Tearcher = 'Tearcher',
+  Student = 'Student',
+}
+
+export class LoginDTO {
+  @ApiProperty({ description: '账号' })
+  @Rule(RuleType['string']().required())
+  account: string = undefined;
+  @ApiProperty({ description: '密码' })
+  @Rule(RuleType['string']().required())
+  password: string = undefined;
+}
+
+export class UPwdDTO {
+  @ApiProperty({ description: '用户数据id' })
+  @Rule(RuleType['string']().required())
+  _id: string = undefined;
+  @ApiProperty({ description: '密码' })
+  @Rule(RuleType['string']().required())
+  password: string = undefined;
+}
+
+export class LoginVO {
+  constructor(data: object) {
+    this._id = get(data, '_id');
+    this.nick_name = get(data, 'nick_name');
+    this.openid = get(data, 'openid');
+    this.role = get(data, 'role');
+    this.is_super = get(data, 'is_super');
+  }
+  _id: string;
+  nick_name: string;
+  openid: string;
+  role: string;
+  is_super: number;
+  @ApiProperty({ description: '登录标识' })
+  'login_code': string = undefined;
+}

+ 93 - 0
src/interface/system/admin.interface.ts

@@ -0,0 +1,93 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_admin {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '账号' })
+  'account': string = undefined;
+  @ApiProperty({ description: '名称' })
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '密码' })
+  'password': string = undefined;
+  @ApiProperty({ description: '是否是超级管理员' })
+  'is_super': string = undefined;
+  @ApiProperty({ description: '角色' })
+  'role': string = undefined;
+  @ApiProperty({ description: 'openid' })
+  'openid': string = undefined;
+  @ApiProperty({ description: '是否启用' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_admin extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['account'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '账号' })
+  'account': string = undefined;
+}
+
+export class QVO_admin extends FVO_admin {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_admin {
+  @ApiProperty({ description: '账号' })
+  @Rule(RuleType['string']().empty(''))
+  'account': string = undefined;
+  @ApiProperty({ description: '名称' })
+  @Rule(RuleType['string']().empty(''))
+  'nick_name': string = undefined;
+  @ApiProperty({ description: '密码' })
+  @Rule(RuleType['string']().empty(''))
+  'password': string = undefined;
+  @ApiProperty({ description: '是否是超级管理员' })
+  @Rule(RuleType['string']().empty(''))
+  'is_super': string = undefined;
+  @ApiProperty({ description: '角色' })
+  @Rule(RuleType['string']().empty(''))
+  'role': string = undefined;
+  @ApiProperty({ description: 'openid' })
+  @Rule(RuleType['string']().empty(''))
+  'openid': string = undefined;
+  @ApiProperty({ description: '是否启用' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+}
+
+export class CVO_admin extends FVO_admin {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_admin extends CDTO_admin {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_admin extends FVO_admin {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 91 - 0
src/interface/system/config.interface.ts

@@ -0,0 +1,91 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_config {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '标题' })
+  'title': string = undefined;
+  @ApiProperty({ description: 'logo' })
+  'logo': Array<any> = undefined;
+  @ApiProperty({ description: '联系电话' })
+  'phone': string = undefined;
+  @ApiProperty({ description: '首页轮播图' })
+  'file': Array<any> = undefined;
+  @ApiProperty({ description: '底部提示语' })
+  'bottom_title': string = undefined;
+  @ApiProperty({ description: '用户协议' })
+  'agreement': string = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+}
+
+export class QDTO_config extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = [];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+}
+
+export class QVO_config extends FVO_config {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_config {
+  @ApiProperty({ description: '标题' })
+  @Rule(RuleType['string']().empty(''))
+  'title': string = undefined;
+  @ApiProperty({ description: 'logo' })
+  @Rule(RuleType['array']().empty(''))
+  'logo': Array<any> = undefined;
+  @ApiProperty({ description: '联系电话' })
+  @Rule(RuleType['string']().empty(''))
+  'phone': string = undefined;
+  @ApiProperty({ description: '首页轮播图' })
+  @Rule(RuleType['array']().empty(''))
+  'file': Array<any> = undefined;
+  @ApiProperty({ description: '底部提示语' })
+  @Rule(RuleType['string']().empty(''))
+  'bottom_title': string = undefined;
+  @ApiProperty({ description: '用户协议' })
+  @Rule(RuleType['string']().empty(''))
+  'agreement': string = undefined;
+  @ApiProperty({ description: '简介' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+}
+
+export class CVO_config extends FVO_config {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_config extends CDTO_config {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_config extends FVO_config {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 92 - 0
src/interface/system/dictData.interface.ts

@@ -0,0 +1,92 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_dictData {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '字典类型编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '数据显示值' })
+  'label': string = undefined;
+  @ApiProperty({ description: '数据选择值' })
+  'value': string = undefined;
+  @ApiProperty({ description: '排序' })
+  'sort': number = undefined;
+  @ApiProperty({ description: '备注' })
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_dictData extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['code', 'label', 'is_use'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '字典类型编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '数据显示值' })
+  'label': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QVO_dictData extends FVO_dictData {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_dictData {
+  @ApiProperty({ description: '字典类型编码' })
+  @Rule(RuleType['string']().empty(''))
+  'code': string = undefined;
+  @ApiProperty({ description: '数据显示值' })
+  @Rule(RuleType['string']().empty(''))
+  'label': string = undefined;
+  @ApiProperty({ description: '数据选择值' })
+  @Rule(RuleType['string']().empty(''))
+  'value': string = undefined;
+  @ApiProperty({ description: '排序' })
+  @Rule(RuleType['number']().empty(''))
+  'sort': number = undefined;
+  @ApiProperty({ description: '备注' })
+  @Rule(RuleType['string']().empty(''))
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+}
+
+export class CVO_dictData extends FVO_dictData {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_dictData extends CDTO_dictData {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_dictData extends FVO_dictData {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 82 - 0
src/interface/system/dictType.interface.ts

@@ -0,0 +1,82 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_dictType {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '字典类型名称' })
+  'title': string = undefined;
+  @ApiProperty({ description: '编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '备注' })
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_dictType extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['title', 'code', 'is_use'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '字典类型名称' })
+  'title': string = undefined;
+  @ApiProperty({ description: '编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QVO_dictType extends FVO_dictType {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_dictType {
+  @ApiProperty({ description: '字典类型名称' })
+  @Rule(RuleType['string']().empty(''))
+  'title': string = undefined;
+  @ApiProperty({ description: '编码' })
+  @Rule(RuleType['string']().empty(''))
+  'code': string = undefined;
+  @ApiProperty({ description: '备注' })
+  @Rule(RuleType['string']().empty(''))
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+}
+
+export class CVO_dictType extends FVO_dictType {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_dictType extends CDTO_dictType {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_dictType extends FVO_dictType {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 127 - 0
src/interface/system/menus.interface.ts

@@ -0,0 +1,127 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_menus {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '菜单名称' })
+  'name': string = undefined;
+  @ApiProperty({ description: '路由名称' })
+  'route_name': string = undefined;
+  @ApiProperty({ description: '国际化编码' })
+  'i18n_code': string = undefined;
+  @ApiProperty({ description: '父级菜单' })
+  'parent_id': string = undefined;
+  @ApiProperty({ description: '显示顺序' })
+  'order_num': number = undefined;
+  @ApiProperty({ description: '路由地址' })
+  'path': string = undefined;
+  @ApiProperty({ description: '组件地址' })
+  'component': string = undefined;
+  @ApiProperty({ description: '菜单类型' })
+  'type': string = undefined;
+  @ApiProperty({ description: '图标' })
+  'icon': string = undefined;
+  @ApiProperty({ description: '功能列表' })
+  'config': Array<any> = undefined;
+  @ApiProperty({ description: '是否为默认菜单' })
+  'is_default': string = undefined;
+  @ApiProperty({ description: '备注' })
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否启用' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_menus extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['parent_id', 'type', 'is_use'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '父级菜单' })
+  'parent_id': string = undefined;
+  @ApiProperty({ description: '菜单类型' })
+  'type': string = undefined;
+  @ApiProperty({ description: '是否启用' })
+  'is_use': string = undefined;
+}
+
+export class QVO_menus extends FVO_menus {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_menus {
+  @ApiProperty({ description: '菜单名称' })
+  @Rule(RuleType['string']().empty(''))
+  'name': string = undefined;
+  @ApiProperty({ description: '路由名称' })
+  @Rule(RuleType['string']().empty(''))
+  'route_name': string = undefined;
+  @ApiProperty({ description: '国际化编码' })
+  @Rule(RuleType['string']().empty(''))
+  'i18n_code': string = undefined;
+  @ApiProperty({ description: '父级菜单' })
+  @Rule(RuleType['string']().empty(''))
+  'parent_id': string = undefined;
+  @ApiProperty({ description: '显示顺序' })
+  @Rule(RuleType['number']().empty(''))
+  'order_num': number = undefined;
+  @ApiProperty({ description: '路由地址' })
+  @Rule(RuleType['string']().empty(''))
+  'path': string = undefined;
+  @ApiProperty({ description: '组件地址' })
+  @Rule(RuleType['string']().empty(''))
+  'component': string = undefined;
+  @ApiProperty({ description: '菜单类型' })
+  @Rule(RuleType['string']().empty(''))
+  'type': string = undefined;
+  @ApiProperty({ description: '图标' })
+  @Rule(RuleType['string']().empty(''))
+  'icon': string = undefined;
+  @ApiProperty({ description: '功能列表' })
+  @Rule(RuleType['array']().empty(''))
+  'config': Array<any> = undefined;
+  @ApiProperty({ description: '是否为默认菜单' })
+  @Rule(RuleType['string']().empty(''))
+  'is_default': string = undefined;
+  @ApiProperty({ description: '备注' })
+  @Rule(RuleType['string']().empty(''))
+  'remark': string = undefined;
+  @ApiProperty({ description: '是否启用' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+}
+
+export class CVO_menus extends FVO_menus {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_menus extends CDTO_menus {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_menus extends FVO_menus {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 85 - 0
src/interface/system/role.interface.ts

@@ -0,0 +1,85 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from 'free-midway-component';
+import get = require('lodash/get');
+const dealVO = (cla, data) => {
+  for (const key in cla) {
+    const val = get(data, key);
+    if (val || val === 0) cla[key] = val;
+  }
+};
+export class FVO_role {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '角色名称' })
+  'name': string = undefined;
+  @ApiProperty({ description: '角色编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '菜单' })
+  'menu': Array<any> = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_role extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = ['code', 'is_use'];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+  @ApiProperty({ description: '角色编码' })
+  'code': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  'is_use': string = undefined;
+}
+
+export class QVO_role extends FVO_role {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_role {
+  @ApiProperty({ description: '角色名称' })
+  @Rule(RuleType['string']().empty(''))
+  'name': string = undefined;
+  @ApiProperty({ description: '角色编码' })
+  @Rule(RuleType['string']().empty(''))
+  'code': string = undefined;
+  @ApiProperty({ description: '菜单' })
+  @Rule(RuleType['array']().empty(''))
+  'menu': Array<any> = undefined;
+  @ApiProperty({ description: '简介' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+  @ApiProperty({ description: '是否使用' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+}
+
+export class CVO_role extends FVO_role {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_role extends CDTO_role {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['string']().empty(''))
+  _id: string = undefined;
+}
+
+export class UVAO_role extends FVO_role {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 3 - 0
src/locales/en_us/defaults.ts

@@ -0,0 +1,3 @@
+export default {
+  locale: 'language is: {locale}',
+};

+ 35 - 0
src/locales/en_us/errors.ts

@@ -0,0 +1,35 @@
+const codes = {
+  '-1': 'unknown error',
+  '-2': 'need params',
+  '-20': 'params error',
+  '-3': 'need query',
+  '-30': 'query error',
+  '-4': 'need body',
+  '-40': 'body error',
+  '-5': 'need args',
+  '-50': 'bad args',
+  '-10': 'data not found',
+  '-100': 'service fault',
+  '-500': 'request falut',
+  '-3000': 'ElasticSearch error',
+  '-3001': 'ElasticSearch error, index not found',
+  '400': 'Service encountered an error',
+  '400-1': 'Function temporarily unavailable',
+  '401': 'You do not have permission for the current function!',
+  '401-1': 'User not found',
+  '401-2': 'Password error',
+  '401-3': 'No login information detected, please log in again!',
+  '401-4': 'Account login has expired, please log in again!',
+  '401-5': 'This account has been logged in elsewhere, please log in again!',
+  '401-6': 'The user has been disabled',
+  '401-7': 'Users in the current role are unable to use',
+};
+const errCodes = {};
+const prefix = 'FRAMEERROR_';
+for (const key in codes) {
+  errCodes[`${prefix}${key}`] = codes[key];
+}
+export default {
+  test: 'error language is: {locale}',
+  ...errCodes,
+};

+ 11 - 0
src/locales/zh_cn/controller_method.ts

@@ -0,0 +1,11 @@
+export default {
+  RoleController: {
+    create: '角色创建',
+    query: '角色查询',
+    fetch: '角色单数据查询',
+    delete: '角色删除',
+    test: {
+      test: 2,
+    },
+  },
+};

+ 3 - 0
src/locales/zh_cn/defaults.ts

@@ -0,0 +1,3 @@
+export default {
+  locale: '当前语言为: {locale}',
+};

+ 37 - 0
src/locales/zh_cn/errors.ts

@@ -0,0 +1,37 @@
+const codes = {
+  '-1': '未知错误',
+  '-2': '缺少params',
+  '-20': 'params错误',
+  '-3': '缺少query',
+  '-30': '缺少query错误',
+  '-4': '缺少body',
+  '-40': 'body错误',
+  '-5': '需要参数',
+  '-50': '参数错误',
+  '-10': '未找到数据',
+  '-100': '服务发生错误',
+  '-500': '请求错误',
+  '-3000': 'ElasticSearch发生错误',
+  '-3001': 'ElasticSearch发生错误,未找到索引',
+  '400': '服务发生错误',
+  '400-1': '功能暂不开放',
+  '401': '您没有当前功能的权限!',
+  '401-1': '未找到用户',
+  '401-2': '密码错误',
+  '401-3': '未检测到登录信息,请重新登录!',
+  '401-4': '账号登录已失效,请重新登录!',
+  '401-5': '本账号已在其他地方登录,请重新登录!',
+  '401-6': '该用户已被禁用',
+  '401-7': '当前角色下的用户无法使用',
+  '401-8': '该比赛已报名请勿重复报名!',
+};
+const errCodes = {};
+const prefix = 'FRAMEERROR_';
+for (const key in codes) {
+  errCodes[`${prefix}${key}`] = codes[key];
+}
+
+export default {
+  locale: '错误测试: {locale}',
+  ...errCodes,
+};

+ 28 - 0
src/middleware/checkOnePointLogin.middleware.ts

@@ -0,0 +1,28 @@
+import { IMiddleware, Inject, MidwayWebRouterService } from '@midwayjs/core';
+import { Middleware } from '@midwayjs/decorator';
+import { NextFunction, Context } from '@midwayjs/koa';
+import { LoginService } from '../service/login.service';
+
+@Middleware()
+export class CheckOnePointLoginMiddleware implements IMiddleware<Context, NextFunction> {
+  @Inject()
+  webRouterService: MidwayWebRouterService;
+  resolve() {
+    return async (ctx: Context, next: NextFunction) => {
+      const routeInfo = await this.webRouterService.getMatchedRouterInfo(ctx.path, ctx.method);
+      if(!routeInfo) {
+        await next()
+        return
+      }
+      const desc = routeInfo.description;
+      if (desc === 'ignore') {
+        const loginService = await ctx.requestContext.getAsync(LoginService);
+        await loginService.onePointCheck();
+      }
+      await next();
+    };
+  }
+  static getName(): string {
+    return 'checkOnePonitLogin';
+  }
+}

+ 31 - 0
src/middleware/newsQuery.middleware.ts

@@ -0,0 +1,31 @@
+import { IMiddleware } from '@midwayjs/core';
+import { Middleware } from '@midwayjs/decorator';
+import { NextFunction, Context } from '@midwayjs/koa';
+@Middleware()
+export class newsQueryMiddleware implements IMiddleware<Context, NextFunction> {
+  resolve() {
+    return async (ctx: Context, next: NextFunction) => {
+      // 获取原始的查询参数对象
+      const rawQuery = ctx.request.query;
+      // 创建一个空对象来存储处理后的查询参数
+      const parsedQuery = {};
+      // 遍历原始的查询参数
+      for (const key in rawQuery) {
+        // 检查键是否以 [] 结尾
+        if (key.endsWith('[]')) {
+          // 去除 [] 后缀,并将值存储到数组中
+          const paramName = key.slice(0, -2);
+          if (!Array.isArray(parsedQuery[paramName])) {
+            parsedQuery[paramName] = [];
+          }
+          parsedQuery[paramName] = rawQuery[key];
+        } else {
+          // 非数组参数直接存储
+          parsedQuery[key] = rawQuery[key];
+        }
+      }
+      ctx.filter = parsedQuery;
+      await next();
+    };
+  }
+}

+ 24 - 0
src/middleware/setLocaleToCtx.middleware.ts

@@ -0,0 +1,24 @@
+import { IMiddleware } from '@midwayjs/core';
+import { Middleware } from '@midwayjs/decorator';
+import { NextFunction, Context } from '@midwayjs/koa';
+import { head, last } from 'lodash';
+@Middleware()
+export class SetLocaleToCtxMiddleware implements IMiddleware<Context, NextFunction> {
+  resolve() {
+    return async (ctx: Context, next: NextFunction) => {
+      const cookies = ctx.request.headers.cookie;
+      let arr = cookies.split(';');
+      arr = arr.filter(f => f.includes('locale='));
+      // 没找到locale就默认使用中文
+      if (arr.length <= 0) arr = ['locale=zh-cn'];
+      const a2 = head(arr).split('=');
+      const locale = last(a2);
+      ctx.locale = locale;
+      await next();
+    };
+  }
+
+  static getName(): string {
+    return 'setLocale';
+  }
+}

+ 11 - 0
src/service/core/course.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Course } from '../../entity/core/course.entity';
+type modelType = ReturnModelType<typeof Course>;
+@Provide()
+export class CourseService extends BaseService<modelType> {
+  @InjectEntityModel(Course)
+  model: modelType;
+}

+ 11 - 0
src/service/core/follow.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Follow } from '../../entity/core/follow.entity';
+type modelType = ReturnModelType<typeof Follow>;
+@Provide()
+export class FollowService extends BaseService<modelType> {
+  @InjectEntityModel(Follow)
+  model: modelType;
+}

+ 11 - 0
src/service/core/opinion.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Opinion } from '../../entity/core/opinion.entity';
+type modelType = ReturnModelType<typeof Opinion>;
+@Provide()
+export class OpinionService extends BaseService<modelType> {
+  @InjectEntityModel(Opinion)
+  model: modelType;
+}

+ 11 - 0
src/service/core/order.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Order } from '../../entity/core/order.entity';
+type modelType = ReturnModelType<typeof Order>;
+@Provide()
+export class OrderService extends BaseService<modelType> {
+  @InjectEntityModel(Order)
+  model: modelType;
+}

+ 11 - 0
src/service/core/student.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Student } from '../../entity/core/student.entity';
+type modelType = ReturnModelType<typeof Student>;
+@Provide()
+export class StudentService extends BaseService<modelType> {
+  @InjectEntityModel(Student)
+  model: modelType;
+}

+ 11 - 0
src/service/core/teacher.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Teacher } from '../../entity/core/teacher.entity';
+type modelType = ReturnModelType<typeof Teacher>;
+@Provide()
+export class TeacherService extends BaseService<modelType> {
+  @InjectEntityModel(Teacher)
+  model: modelType;
+}

+ 23 - 0
src/service/i18n.service.ts

@@ -0,0 +1,23 @@
+import { Config, Inject, Provide } from '@midwayjs/decorator';
+import { MidwayI18nService } from '@midwayjs/i18n';
+import { Context } from '@midwayjs/koa';
+
+@Provide()
+export class I18nService {
+  @Config('i18n.defaultLocale')
+  defaultLocale: string;
+  @Inject()
+  ctx: Context;
+  @Inject()
+  i18n: MidwayI18nService;
+
+  translate(code:any) {
+    return this.i18n.translate(`${code}`, { locale: this.ctx.locale });
+  }
+  translateError(code:any) {
+    return this.i18n.translate(`${code}`, { locale: this.ctx.locale, group: 'error' });
+  }
+  translateMethod(code:any) {
+    return this.i18n.translate(`${code}`, { locale: this.ctx.locale, group: 'error' });
+  }
+}

+ 162 - 0
src/service/init.service.ts

@@ -0,0 +1,162 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { Admin } from '../entity/system/admin.entity';
+import { Role } from '../entity/system/role.entity';
+import { Menus } from '../entity/system/menus.entity';
+import { DictData } from '../entity/system/dictData.entity';
+import { DictType } from '../entity/system/dictType.entity';
+import { Types } from 'mongoose';
+const ObjectId = Types.ObjectId;
+@Provide()
+export class InitService {
+  @InjectEntityModel(Admin)
+  adminModel: ReturnModelType<typeof Admin>;
+  @InjectEntityModel(Role)
+  roleModel: ReturnModelType<typeof Role>;
+  @InjectEntityModel(Menus)
+  menusModel: ReturnModelType<typeof Menus>;
+  @InjectEntityModel(DictType)
+  dictTypeModel: ReturnModelType<typeof DictType>;
+  @InjectEntityModel(DictData)
+  dictDataModel: ReturnModelType<typeof DictData>;
+
+  async adminUser() {
+    const data = {
+      account: 'admin',
+      password: '1qaz2wsx',
+      nick_name: '系统管理员',
+      is_super: '0',
+    };
+    const is_exist = await this.adminModel.count({ is_super: '0' });
+    if (!is_exist) return await this.adminModel.create(data);
+    return;
+  }
+
+  async initRole() {
+    const num = await this.roleModel.count();
+    if (num > 0) return;
+    const datas = [{ name: '管理员', code: 'admin' }];
+    await this.roleModel.insertMany(datas);
+  }
+  async initMenus() {
+    const num = await this.menusModel.count();
+    if (num > 0) return;
+    const datas: any = [{ name: '首页', order_num: 1, path: '/', component: '/home/index', type: '1', i18n_code: 'menus.home', route_name: 'home' }];
+    // 系统设置
+    const smId = new ObjectId();
+    const systemMenus = [
+      { _id: smId, name: '系统设置', path: '/system', order_num: 2, type: '0', i18n_code: 'menus.system', route_name: 'system', is_default: '0' },
+      {
+        name: '菜单设置',
+        parent_id: smId.toString(),
+        order_num: 1,
+        path: '/system/menus',
+        component: '/system/menus/index',
+        type: '1',
+        route_name: 'system_menus',
+        i18n_code: 'menus.system_menus',
+        config: [
+          {
+            zh: '添加',
+            code: 'add',
+          },
+          {
+            zh: '修改',
+            code: 'update',
+          },
+          {
+            zh: '添加下一级',
+            code: 'addNext',
+          },
+          {
+            zh: '删除',
+            code: 'delete',
+          },
+        ],
+        is_default: '0',
+      },
+      {
+        name: '角色管理',
+        parent_id: smId.toString(),
+        order_num: 2,
+        path: '/system/role',
+        component: '/system/role/index',
+        type: '1',
+        route_name: 'system_role',
+        i18n_code: 'menus.system_role',
+        is_default: '0',
+        config: [
+          {
+            zh: '添加',
+            code: 'add',
+          },
+          {
+            zh: '修改',
+            code: 'update',
+          },
+          {
+            zh: '使用',
+            code: 'toAbled',
+          },
+          {
+            zh: '禁用',
+            code: 'toDisabled',
+          },
+          {
+            zh: '删除',
+            code: 'delete',
+          },
+        ],
+      },
+      { name: '字典管理', parent_id: smId.toString(), order_num: 3, path: '/system/dict', component: '/system/dict/index', type: '1', i18n_code: 'menus.system_dict', route_name: 'system_dict', is_default: '0' },
+      { name: '字典数据', parent_id: smId.toString(), order_num: 4, path: '/system/dictData', component: '/system/dictData/index', type: '2', route_name: 'system_dict_data', i18n_code: 'menus.system_dict_data', is_default: '0' },
+      { name: '网址设置', route_name: 'system_config', i18n_code: 'menus.system_config', parent_id: smId.toString(), order_num: 5, path: '/system/config', component: '/system/config/index', type: '1', is_default: '0', parent_name: '系统设置' },
+    ];
+    // 用户管理
+    const umId = new ObjectId();
+    const userMenus = [
+      { _id: umId, name: '用户管理', order_num: 3, path: '/user', type: '0', i18n_code: 'menus.user', route_name: 'user', is_default: '1' },
+      { name: '管理员用户', parent_id: umId.toString(), order_num: 1, path: '/user/admin', component: '/user/admin/index', type: '1', i18n_code: 'menus.user_admin', route_name: 'user_admin' },
+      { name: '教师用户', parent_id: umId.toString(), order_num: 2, path: '/user/teacher', component: '/user/teacher/index', type: '1', i18n_code: 'menus.user_teacher', route_name: 'user_teacher' },
+      { name: '学生用户', parent_id: umId.toString(), order_num: 2, path: '/user/student', component: '/user/student/index', type: '1', i18n_code: 'menus.user_student', route_name: 'user_student' },
+    ];
+    const course = { name: '课程管理', route_name: 'course', i18n_code: 'menus.course', order_num: 4, path: '/core/course', component: '/core/course/index', type: '1', is_default: '1', is_use: '0' };
+    const order = { name: '订单管理', order_num: 5, path: '/core/order', component: '/core/order/index', type: '1', is_default: '1', is_use: '0', route_name: 'order', i18n_code: 'menus.order' };
+    const opinion = { name: '意见反馈管理', route_name: 'opinion', i18n_code: 'menus.opinion', order_num: 5, path: '/core/opinion', component: '/core/opinion/index', type: '1', is_default: '1', is_use: '0' };
+    datas.push(...systemMenus, ...userMenus, course, order, opinion);
+    // 项目业务菜单
+    const busMenus = [];
+    datas.push(...busMenus);
+    await this.menusModel.insertMany(datas);
+  }
+  async initDict() {
+    const isUseType = [
+      { title: '是否使用', code: 'isUse', is_use: '0' },
+      { title: '审核状态', code: 'examStatus', is_use: '0' },
+      { title: '性别', code: 'gender', is_use: '0' },
+      { title: '图标', code: 'icon', is_use: '0' },
+    ];
+    const isUseData = [
+      { code: 'isUse', label: '使用', value: '0', sort: 1, is_use: '0' },
+      { code: 'isUse', label: '禁用', value: '1', sort: 2, is_use: '0' },
+      { code: 'examStatus', label: '待审核', value: '0', sort: 1, is_use: '0' },
+      { code: 'examStatus', label: '已通过', value: '1', sort: 2, is_use: '0' },
+      { code: 'examStatus', label: '未通过', value: '-1', sort: 2, is_use: '0' },
+      { code: 'gender', label: '男', value: '0', sort: 1, is_use: '0' },
+      { code: 'gender', label: '女', value: '1', sort: 2, is_use: '0' },
+      { code: 'gender', label: '未知', value: '2', sort: 3, is_use: '0' },
+      { code: 'icon', label: 'House', value: 'House', sort: 1, is_use: '0' },
+      { code: 'icon', label: 'User', value: 'User', sort: 2, is_use: '0' },
+      { code: 'icon', label: 'Setting', value: 'Setting', sort: 3, is_use: '0' },
+      { code: 'icon', label: 'ScaleToOriginal', value: 'ScaleToOriginal', sort: 4, is_use: '0' },
+      { code: 'icon', label: 'Management', value: 'Management', sort: 5, is_use: '0' },
+      { code: 'icon', label: 'DataLine', value: 'DataLine', sort: 6, is_use: '0' },
+      { code: 'icon', label: 'Film', value: 'Film', sort: 7, is_use: '0' },
+      { code: 'icon', label: 'SwitchFilled', value: 'SwitchFilled', sort: 8, is_use: '0' },
+      { code: 'icon', label: 'VideoPlay', value: 'VideoPlay', sort: 9, is_use: '0' },
+    ];
+    await this.dictTypeModel.insertMany(isUseType);
+    await this.dictDataModel.insertMany(isUseData);
+  }
+}

+ 136 - 0
src/service/login.service.ts

@@ -0,0 +1,136 @@
+import { Config, Inject, Provide } from '@midwayjs/core';
+import { GetModel, ServiceError } from 'free-midway-component';
+import { get, isEqual, upperFirst } from 'lodash';
+import { LoginDTO, LoginType, UPwdDTO } from '../interface/login.interface';
+import { RoleService } from '../service/system/role.service';
+import { RedisService } from '@midwayjs/redis';
+import * as Crypto from 'crypto-js';
+import { Context } from '@midwayjs/koa';
+import { I18nService } from './i18n.service';
+import { FrameErrorEnum } from '../error/frame.error';
+@Provide()
+export class LoginService {
+  @Inject()
+  roleService: RoleService;
+  @Inject()
+  ctx: Context;
+  @Config('loginSign')
+  loginSign;
+  @Config('jwt.secret')
+  jwtSecret;
+  @Config('jwt.expiresIn')
+  jwtExpiresIn;
+  @Inject()
+  redisService: RedisService;
+  @Inject()
+  i18n: I18nService;
+
+  async onePointLogin(loginVo) {
+    const { _id, role } = loginVo;
+    const rediskey = `${this.loginSign}:${role}:${_id}`;
+    // 随机字符串 验证用户登录用
+    const str = this.randomStr();
+    // 拼接成token内的登录code 加密前数据
+    const val = `${rediskey}:${str}`;
+    // 加密后存入token内
+    const code = Crypto.AES.encrypt(val, this.jwtSecret).toString();
+    // 设置redis登录记录 将 随机字符串存在redis中. 验证时需要将加密的字符串解密,解密后和redis中的对比验证
+    await this.redisService.set(rediskey, str, 'EX', this.jwtExpiresIn);
+    return code;
+  }
+
+  async onePointCheck() {
+    const user = this.ctx.user;
+    if (!user) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.NOT_LOGIN), FrameErrorEnum.NOT_LOGIN);
+    const { _id, role, login_code } = user;
+    if (!login_code) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.NOT_LOGIN), FrameErrorEnum.NOT_LOGIN);
+    // 解密
+    const decodeResult = Crypto.AES.decrypt(login_code, this.jwtSecret);
+    const decode = Crypto.enc.Utf8.stringify(decodeResult).toString();
+    // 取出code
+    const codeArr = decode.split(':');
+    const code = codeArr[codeArr.length - 1];
+    // 拼接redis的key
+    const rediskey = `${this.loginSign}:${role}:${_id}`;
+    // 取出当前记录在案的 code
+    const redisCode = await this.redisService.get(rediskey);
+    if (!redisCode) throw new ServiceError(this.i18n.translateError(`${FrameErrorEnum.ACCOUNT_HAS_EXPIRED}`), FrameErrorEnum.ACCOUNT_HAS_EXPIRED);
+    // 判断是否一致
+    if (code === redisCode) {
+      // 一致,延时
+      await this.redisService.expire(rediskey, this.jwtExpiresIn);
+    } else {
+      throw new ServiceError(this.i18n.translateError(`${FrameErrorEnum.ACCOUNT_LOGGED_IN_ELESWHERE}`), FrameErrorEnum.ACCOUNT_LOGGED_IN_ELESWHERE);
+    }
+  }
+
+  /**
+   * 账密登录
+   * @param data 用户名和密码
+   * @param type 用户类型
+   * @returns 用户信息/空值
+   */
+  async loginByAccount(data: LoginDTO, type: LoginType) {
+    const model = GetModel(upperFirst(type));
+    const user = await model.findOne({ account: data.account }, '+password').lean();
+    if (!user) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_NOT_FOUND), FrameErrorEnum.USER_NOT_FOUND);
+    await this.checkAccountCanLogin(user, type);
+    if (!isEqual(user.password.secret, data.password)) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.BAD_PASSWORD), FrameErrorEnum.BAD_PASSWORD);
+    return user;
+  }
+
+  // 刷新token
+  async refresh(data, type: LoginType) {
+    if (!data.id) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_NOT_FOUND), FrameErrorEnum.USER_NOT_FOUND);
+    const model = GetModel(upperFirst(type));
+    const user = await model.findById(data.id).lean();
+    if (!user) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_NOT_FOUND), FrameErrorEnum.USER_NOT_FOUND);
+    return user;
+  }
+  /**
+   * 检查用户是否可以登录(从用户本身和角色检查)
+   * @param user 用户信息
+   * @param type 用户类型
+   */
+  async checkAccountCanLogin(user, type: LoginType) {
+    // 判断是否可以使用:
+    // 管理员: 超级管理员无视,直接往下; 普通管理员需要查看is_use是不是'0'
+    // 其他用户需要看status是不是'1';
+    if (type === 'Admin') {
+      if (get(user, 'is_super') === '1') {
+        if (get(user, 'is_use') === '1') throw new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_IS_DISABLED), FrameErrorEnum.USER_IS_DISABLED);
+      }
+    } else {
+      if (get(user, 'status') !== '1') throw new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_IS_DISABLED), FrameErrorEnum.USER_IS_DISABLED);
+      const role = await this.roleService.findOne({ code: type, is_use: '0' });
+      if (!role) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.ROLE_IS_DISABLED), FrameErrorEnum.ROLE_IS_DISABLED);
+    }
+  }
+  // 随机密码
+  async updatePwd(data: UPwdDTO, type: LoginType) {
+    const model = GetModel(upperFirst(type));
+    const user = await model.findById(data._id);
+    if (!user) new ServiceError(this.i18n.translateError(FrameErrorEnum.USER_NOT_FOUND), FrameErrorEnum.USER_NOT_FOUND);
+    user.password = data.password;
+    await user.save();
+  }
+  // 需要改的时候再用
+  // async wxAppLogin(openid: string) {
+  //   const tables = ['Doctor', 'Nurse', 'Patient'];
+  //   let user;
+  //   let role;
+  //   for (const table of tables) {
+  //     const model = GetModel(upperFirst(table));
+  //     user = await model.findOne({ openid }).lean();
+  //     if (user) {
+  //       role = table;
+  //       break;
+  //     }
+  //   }
+  //   if (user) return { ...user, role };
+  //   throw new ServiceError('未找到用户信息!', FrameworkErrorEnum.NOT_FOUND_DATA);
+  // }
+  randomStr(len = 32) {
+    return Math.random().toString(36).slice(-len);
+  }
+}

+ 11 - 0
src/service/system/admin.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Admin } from '../../entity/system/admin.entity';
+type modelType = ReturnModelType<typeof Admin>;
+@Provide()
+export class AdminService extends BaseService<modelType> {
+  @InjectEntityModel(Admin)
+  model: modelType;
+}

+ 11 - 0
src/service/system/config.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Config } from '../../entity/system/config.entity';
+type modelType = ReturnModelType<typeof Config>;
+@Provide()
+export class ConfigService extends BaseService<modelType> {
+  @InjectEntityModel(Config)
+  model: modelType;
+}

+ 11 - 0
src/service/system/dictData.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { DictData } from '../../entity/system/dictData.entity';
+type modelType = ReturnModelType<typeof DictData>;
+@Provide()
+export class DictDataService extends BaseService<modelType> {
+  @InjectEntityModel(DictData)
+  model: modelType;
+}

+ 11 - 0
src/service/system/dictType.service.ts

@@ -0,0 +1,11 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { DictType } from '../../entity/system/dictType.entity';
+type modelType = ReturnModelType<typeof DictType>;
+@Provide()
+export class DictTypeService extends BaseService<modelType> {
+  @InjectEntityModel(DictType)
+  model: modelType;
+}

+ 38 - 0
src/service/system/menus.service.ts

@@ -0,0 +1,38 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { Menus } from '../../entity/system/menus.entity';
+import orderBy = require('lodash/orderBy');
+import { Types } from 'mongoose';
+const ObjectId = Types.ObjectId;
+type modelType = ReturnModelType<typeof Menus>;
+@Provide()
+export class MenusService extends BaseService<modelType> {
+  @InjectEntityModel(Menus)
+  model: modelType;
+
+  async queryMenu(query: object = {}): Promise<Array<object>> {
+    const data = await this.model.find(query, { meta: 0, __v: 0 }).sort({ order_num: 1 }).lean();
+    let treeMenu = data.filter(f => !f.parent_id);
+    treeMenu = this.treeMenu(data, treeMenu);
+    return treeMenu;
+  }
+
+  treeMenu(allMenus, nowMenu) {
+    for (const nm of nowMenu) {
+      const { _id, parent_id } = nm;
+      // 查下下级其是否有目录
+      let children = allMenus.filter(f => new ObjectId(f.parent_id).equals(_id));
+      children = this.treeMenu(allMenus, children);
+      if (children.length > 0) nm.children = children;
+      // 换父级组件的名称
+      if (parent_id) {
+        const r = allMenus.find(f => new ObjectId(f._id).equals(parent_id));
+        if (r) nm.parent_name = r.name;
+      }
+    }
+    nowMenu = orderBy(nowMenu, ['order_num'], ['asc']);
+    return nowMenu;
+  }
+}

+ 73 - 0
src/service/system/role.service.ts

@@ -0,0 +1,73 @@
+import { Provide, Inject } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService, ServiceError, FrameworkErrorEnum } from 'free-midway-component';
+import { Role } from '../../entity/system/role.entity';
+import { Menus } from '../../entity/system/menus.entity';
+import { MenusService } from './menus.service';
+import { flattenDeep, uniq, get, lowerFirst, upperFirst, last } from 'lodash';
+import { Context } from '@midwayjs/koa';
+import { I18nService } from '../i18n.service';
+import { FrameErrorEnum } from '../../error/frame.error';
+type modelType = ReturnModelType<typeof Role>;
+@Provide()
+export class RoleService extends BaseService<modelType> {
+  @InjectEntityModel(Role)
+  model: modelType;
+  @InjectEntityModel(Menus)
+  menusModel: ReturnModelType<typeof Menus>;
+
+  @Inject()
+  menusService: MenusService;
+  @Inject()
+  ctx: Context;
+  @Inject()
+  i18n: I18nService;
+  //是否是超级管理员
+  isSuperAdmin() {
+    const user = this.ctx.user;
+    if (user.role === 'Admin' && user.is_super === '0') return true;
+  }
+
+  async getUserMenus(needCode = false) {
+    const user = this.ctx.user;
+    // 这里需要改下. 平台的超级管理员有这些权限
+    if (this.isSuperAdmin()) {
+      const menus = await this.menusService.queryMenu({ is_use: '0' });
+      return { menus };
+    }
+    let roleCode;
+    for (const val of user.role) {
+      roleCode = [lowerFirst(val), upperFirst(val)];
+    }
+    const role = await this.model.findOne({ code: roleCode, is_use: '0' }).lean();
+    if (!role) throw new ServiceError(this.i18n.translateError(FrameErrorEnum.ROLE_IS_DISABLED), FrameworkErrorEnum.SERVICE_FAULT);
+    const roleMenu = get(role, 'menu', []);
+    const menu = roleMenu.map(i => {
+      const arr = i.split('.');
+      return last(arr);
+    });
+    if (needCode) return roleMenu;
+    const menuList = await this.menusModel.find({ route_name: menu }).lean();
+    let treeMenu = menuList.filter(f => !f.parent_id);
+    treeMenu = this.menusService.treeMenu(menuList, treeMenu);
+    return { menus: treeMenu, role_code: roleMenu };
+  }
+
+  /**
+   * 根据角色id数组,整理菜单
+   * @param roleIdList 角色id数组
+   */
+  async getMenuByRoles(roleIdList) {
+    let menus = [];
+    // 第一步,整理所有菜单至一维数组
+    for (const r of roleIdList) {
+      menus.push(...flattenDeep(r));
+    }
+    menus = uniq(menus);
+    const allMenu = await this.menusModel.find({ _id: menus, is_use: '0' }, { meta: 0, __v: 0 }).sort({ order_num: 1 }).lean();
+    let treeMenu = allMenu.filter(f => !f.parent_id);
+    treeMenu = this.menusService.treeMenu(allMenu, treeMenu);
+    return treeMenu;
+  }
+}

+ 7 - 0
src/service/tool.service.ts

@@ -0,0 +1,7 @@
+import { Inject, Provide } from '@midwayjs/core';
+import { Context } from '@midwayjs/koa';
+@Provide()
+export class ToolService {
+  @Inject()
+  ctx: Context;
+}

+ 19 - 0
src/service/util.service.ts

@@ -0,0 +1,19 @@
+import { Provide } from '@midwayjs/decorator';
+import { GetModel } from 'free-midway-component';
+import { upperFirst } from 'lodash';
+@Provide()
+export class UtilService {
+  randomStr(len = 6) {
+    return Math.random().toString(36).slice(-len);
+  }
+
+  // 修改后信息后用户重新审核
+  async updateUserAfter(id, body, role_type) {
+    const { status } = body;
+    if (status === '0') {
+      const model = GetModel(upperFirst(role_type));
+      const data = await model.findById(id).lean();
+      await model.updateOne({ _id: data.user }, { status: '0' });
+    }
+  }
+}

+ 20 - 0
test/controller/api.test.ts

@@ -0,0 +1,20 @@
+import { createApp, close, createHttpRequest } from '@midwayjs/mock';
+import { Framework } from '@midwayjs/koa';
+
+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).get('/api/get_user').query({ uid: 123 });
+
+    // use expect by jest
+    expect(result.status).toBe(200);
+    expect(result.body.message).toBe('OK');
+
+    // close app
+    await close(app);
+  });
+});

+ 21 - 0
test/controller/home.test.ts

@@ -0,0 +1,21 @@
+import { createApp, close, createHttpRequest } from '@midwayjs/mock';
+import { Framework } from '@midwayjs/koa';
+
+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!');
+
+    // close app
+    await close(app);
+  });
+
+});

+ 28 - 0
tsconfig.json

@@ -0,0 +1,28 @@
+{
+  "compileOnSave": true,
+  "compilerOptions": {
+    "target": "es2018",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true,
+    "inlineSourceMap":true,
+    "noImplicitThis": true,
+    "noUnusedLocals": true,
+    "stripInternal": true,
+    "skipLibCheck": true,
+    "pretty": true,
+    "declaration": true,
+    "forceConsistentCasingInFileNames": true,
+    "typeRoots": [ "./typings", "./node_modules/@types"],
+    "outDir": "dist",
+    "rootDir": "src"
+  },
+  "exclude": [
+    "*.js",
+    "*.ts",
+    "dist",
+    "node_modules",
+    "test"
+  ]
+}