zs před 1 rokem
rodič
revize
be61a57ea7

+ 13 - 1
README.md

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 819 - 811
pnpm-lock.yaml


+ 3 - 2
src/config/config.default.ts

@@ -10,11 +10,12 @@ export default {
     localeTable: {
       en_us: {
         default: require('../locales/en_us/defaults'),
-        error: require('../locales/en_us/errors')
+        error: require('../locales/en_us/errors'),
       },
       zh_cn: {
         default: require('../locales/zh_cn/defaults'),
-        error: require('../locales/zh_cn/errors')
+        error: require('../locales/zh_cn/errors'),
+        methods: require('../locales/zh_cn/controller_method'),
       },
     },
   },

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

@@ -4,6 +4,7 @@ const redisHost = '127.0.0.1';
 const redisPwd = '123456';
 const redisDB = 6;
 const projectDB = 'vue3js-template-test';
+const recordDB = 'vue3js-template-test-record'
 const loginSign = 'tsFrameDev';
 export default {
   // use for cookie sign key, should change to your own and keep security
@@ -32,6 +33,16 @@ export default {
         },
         entities: ['./entity'],
       },
+      record:{
+        uri: `mongodb://${ip}:27017/${recordDB}`,
+        options: {
+          user: 'admin',
+          pass: 'admin',
+          authSource: 'admin',
+          useNewUrlParser: true,
+        },
+        entities: ['./entityRecord'],
+      }
     },
   },
   redis: {

+ 2 - 0
src/configuration.ts

@@ -13,6 +13,7 @@ import * as redis from '@midwayjs/redis';
 import { CheckOnePointLoginMiddleware } from './middleware/checkOnePointLogin.middleware';
 import * as i18n from '@midwayjs/i18n';
 import { SetLocaleToCtxMiddleware } from './middleware/setLocaleToCtx.middleware';
+import { DataRecordInit } from './decorator/dataRecord';
 @Configuration({
   imports: [
     koa,
@@ -44,5 +45,6 @@ export class MainConfiguration {
     // 注解
     VerifyTokenInit(this.decoratorService);
     CheckPermissionCodeInit(this.decoratorService);
+    DataRecordInit(this.decoratorService)
   }
 }

+ 68 - 0
src/controller/record/dataRecord.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 { DataRecordService } from '../../service/record/dataRecord.service';
+import { CDTO_dataRecord, CVO_dataRecord, FVO_dataRecord, QDTO_dataRecord, QVO_dataRecord, UDTO_dataRecord, UVAO_dataRecord } from '../../interface/record/dataRecord.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+@ApiTags(['数据操作记录'])
+@Controller('/dataRecord')
+export class DataRecordController extends BaseController {
+  @Inject()
+  service: DataRecordService;
+
+  @Post('/')
+  @Validate()
+  @ApiResponse({ type: CVO_dataRecord })
+  async create(@Body() data: CDTO_dataRecord) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_dataRecord(dbData);
+    return result;
+  }
+  @Get('/')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_dataRecord })
+  async query(@Query() filter: QDTO_dataRecord, @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_dataRecord(i);
+      data.push(newData);
+    }
+    const total = await this.service.count(filter);
+    return { data, total };
+  }
+
+  @Get('/:id')
+  @ApiResponse({ type: FVO_dataRecord })
+  async fetch(@Param('id') id: string) {
+    const data = await this.service.fetch(id);
+    const result = new FVO_dataRecord(data);
+    return result;
+  }
+
+  @Post('/:id')
+  @Validate()
+  @ApiResponse({ type: UVAO_dataRecord })
+  async update(@Param('id') id: string, @Body() body: UDTO_dataRecord) {
+    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.');
+  }
+}

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

@@ -7,6 +7,7 @@ import { Validate } from '@midwayjs/validate';
 import { MenusService } from '../../service/system/menus.service';
 import { verifyToken } from '../../decorator/verifyToken.decorator';
 import { checkPermissionCode } from '../../decorator/checkPermissionCode';
+import { dataRecord } from '../../decorator/dataRecord';
 @ApiTags(['角色表'])
 @Controller('/role')
 export class RoleController extends BaseController {
@@ -19,6 +20,7 @@ export class RoleController extends BaseController {
   @verifyToken()
   @Validate()
   @ApiResponse({ type: CVO_role })
+  @dataRecord()
   async create(@Body() data: CDTO_role) {
     const dbData = await this.service.create(data);
     const result = new CVO_role(dbData);
@@ -54,6 +56,7 @@ export class RoleController extends BaseController {
   @checkPermissionCode({ roleCode: 'system_role.update' })
   @Validate()
   @ApiResponse({ type: UVAO_role })
+  @dataRecord()
   async update(@Param('id') id: string, @Body() body: UDTO_role) {
     const result = await this.service.updateOne(id, body);
     return result;
@@ -62,6 +65,7 @@ export class RoleController extends BaseController {
   @Del('/:id')
   @verifyToken()
   @Validate()
+  @dataRecord()
   async delete(@Param('id') id: string) {
     await this.service.delete(id);
     return 'ok';

+ 69 - 0
src/decorator/dataRecord.ts

@@ -0,0 +1,69 @@
+import { JoinPoint, MidwayDecoratorService, REQUEST_OBJ_CTX_KEY, createCustomMethodDecorator } from '@midwayjs/core';
+import { MidwayI18nService } from '@midwayjs/i18n';
+import { get } from 'lodash';
+import { DataRecordService } from '../service/record/dataRecord.service';
+export const DATARECORD_KEY = 'decorator:data_record';
+export const dataRecord = (options?: any) => {
+  return createCustomMethodDecorator(DATARECORD_KEY, options);
+};
+
+const getMethodName = (mapping: Map<string, any>, controller: string, path: string) => {
+  const map = mapping.get(controller);
+  if (!map) return;
+  return get(map, path);
+};
+// const i18n = await ctx.requestContext.getAsync(MidwayI18nService);
+// const mapping = i18n.getLocaleMapping(ctx.locale, 'methods');
+// const i18nName = getMethodName(mapping, controllerName, methodName);
+// console.log(i18nName);
+
+const defaultMethods = ['create', 'update', 'delete'];
+export const DataRecordInit = (decoratorService: MidwayDecoratorService) => {
+  decoratorService.registerMethodHandler(DATARECORD_KEY, options => {
+    return {
+      around: async (joinPoint: JoinPoint) => {
+        let recordPreData = null;
+        let recordService = null;
+        try {
+          const instance = joinPoint.target;
+          const controllerName = options.target.name;
+          const methodName = options.propertyName;
+          const metaData = options.metadata;
+          const ctx = instance[REQUEST_OBJ_CTX_KEY];
+          recordService = await ctx.requestContext.getAsync(DataRecordService);
+          // 组织记录数据
+          recordPreData = await recordService.makeLogs(controllerName, methodName);
+          if (get(metaData, 'before')) {
+            recordPreData.origin_data = await instance.service[get(metaData, 'before')]();
+          } else if (defaultMethods.includes(methodName)) {
+            // 如果是create,update,delete 默认方法.那就默认处理.自己写的自己处理
+
+            recordPreData.origin_data = await recordService.getDefaultMethodOriginData(instance.service, methodName);
+          }
+          // 执行原函数
+          const result = await joinPoint.proceed(...joinPoint.args);
+          // 如果是create,update,delete 默认方法.那就默认处理.自己写的自己处理
+          if (get(metaData, 'after')) {
+            recordPreData.new_data = await instance.service[get(metaData, 'after')]();
+          } else if (defaultMethods.includes(methodName)) {
+            let od = null;
+            if (methodName === 'create') od = result;
+            else od = recordPreData.origin_data;
+            recordPreData.new_data = await recordService.getDefaultMethodNewData(instance.service, methodName, od);
+          }
+          recordPreData.status = '0';
+          return result;
+        } catch (error) {
+          if (recordPreData) {
+            // 有数据的情况下存日志有意义,否则就是只有个status没意义
+            recordPreData.status = '1';
+          }
+          throw error;
+        } finally {
+          // save logs
+          if (recordPreData) await recordService.create(recordPreData);
+        }
+      },
+    };
+  });
+};

+ 27 - 0
src/entityRecord/dataRecord.entity.ts

@@ -0,0 +1,27 @@
+import { modelOptions, prop } from '@typegoose/typegoose';
+import { BaseModel } from 'free-midway-component';
+@modelOptions({
+  schemaOptions: { collection: 'dataRecord' },
+})
+export class DataRecord extends BaseModel {
+  @prop({ required: false, index: false, zh: '操作人' })
+  operator: object;
+  @prop({ required: false, index: false, zh: 'ip地址' })
+  ip: string;
+  @prop({ required: false, index: false, zh: '时间' })
+  time: string;
+  @prop({ required: false, index: false, zh: '对象', remark: 'controller' })
+  controller: string;
+  @prop({ required: false, index: false, zh: '函数', remark: '函数名,记录code.i18n兑换用' })
+  method: string;
+  @prop({ required: false, index: false, zh: '原数据', remark: '多个表关联性处理时需要先将修改的数据都查出来,key:表名;value:数组(本表修改的数据都放里面)' })
+  origin_data: object;
+  @prop({ required: false, index: false, zh: '新数据' })
+  new_data: object;
+  @prop({ required: false, index: false, zh: '页面路由' })
+  router: string;
+  @prop({ required: false, index: false, zh: '设备' })
+  device: string;
+  @prop({ required: false, index: false, zh: '状态', remark: '0:成功;-1失败' })
+  status: string;
+}

+ 112 - 0
src/interface/record/dataRecord.interface.ts

@@ -0,0 +1,112 @@
+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_dataRecord {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  _id: string = undefined;
+  @ApiProperty({ description: '操作人' })
+  'operator': object = undefined;
+  @ApiProperty({ description: 'ip地址' })
+  'ip': string = undefined;
+  @ApiProperty({ description: '时间' })
+  'time': string = undefined;
+  @ApiProperty({ description: '对象' })
+  'controller': string = undefined;
+  @ApiProperty({ description: '函数' })
+  'method': string = undefined;
+  @ApiProperty({ description: '原数据' })
+  'origin_data': object = undefined;
+  @ApiProperty({ description: '新数据' })
+  'new_data': object = undefined;
+  @ApiProperty({ description: '页面路由' })
+  'router': string = undefined;
+  @ApiProperty({ description: '设备' })
+  'device': string = undefined;
+  @ApiProperty({ description: '状态' })
+  'status': string = undefined;
+}
+
+
+export class QDTO_dataRecord extends SearchBase {
+  constructor() {
+    const like_prop = [];
+    const props = [];
+    const mapping = [];
+    super({ like_prop, props, mapping });
+  }
+}
+
+
+export class QVO_dataRecord extends FVO_dataRecord {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+
+export class CDTO_dataRecord {
+  @ApiProperty({ description: '操作人' })
+@Rule(RuleType['object']().empty(''))
+  'operator': object = undefined;
+  @ApiProperty({ description: 'ip地址' })
+@Rule(RuleType['string']().empty(''))
+  'ip': string = undefined;
+  @ApiProperty({ description: '时间' })
+@Rule(RuleType['string']().empty(''))
+  'time': string = undefined;
+  @ApiProperty({ description: '对象' })
+@Rule(RuleType['string']().empty(''))
+  'controller': string = undefined;
+  @ApiProperty({ description: '函数' })
+@Rule(RuleType['string']().empty(''))
+  'method': string = undefined;
+  @ApiProperty({ description: '原数据' })
+@Rule(RuleType['object']().empty(''))
+  'origin_data': object = undefined;
+  @ApiProperty({ description: '新数据' })
+@Rule(RuleType['object']().empty(''))
+  'new_data': object = undefined;
+  @ApiProperty({ description: '页面路由' })
+@Rule(RuleType['string']().empty(''))
+  'router': string = undefined;
+  @ApiProperty({ description: '设备' })
+@Rule(RuleType['string']().empty(''))
+  'device': string = undefined;
+  @ApiProperty({ description: '状态' })
+@Rule(RuleType['string']().empty(''))
+  'status': string = undefined;
+}
+
+
+export class CVO_dataRecord extends FVO_dataRecord {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+
+export class UDTO_dataRecord extends CDTO_dataRecord {
+    @ApiProperty({ description: '数据id' })
+    @Rule(RuleType['string']().empty(''))
+    _id: string = undefined;
+}
+
+
+export class UVAO_dataRecord extends FVO_dataRecord {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

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

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

+ 73 - 0
src/service/record/dataRecord.service.ts

@@ -0,0 +1,73 @@
+import { Provide } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typegoose';
+import { ReturnModelType } from '@typegoose/typegoose';
+import { BaseService } from 'free-midway-component';
+import { DataRecord } from '../../entityRecord/dataRecord.entity';
+import { get } from 'lodash';
+import dayjs = require('dayjs');
+
+type modelType = ReturnModelType<typeof DataRecord>;
+@Provide()
+export class DataRecordService extends BaseService<modelType> {
+  @InjectEntityModel(DataRecord)
+  model: modelType;
+
+  async makeLogs(controllerName: string, methodName: string) {
+    const req = this.ctx.request;
+    const user = this.ctx.user;
+    const data = {
+      operator: user,
+      ip: get(req, 'header.x-forwarded-for', req.ip),
+      device: get(req, 'header.user-agent'),
+      time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+      controller: controllerName,
+      method: methodName,
+      router: get(req, 'header.referer'),
+    };
+    return data;
+  }
+  /**
+   * 为默认函数提供原数据
+   * defaultMethod: create, update, delete
+   * @param service 中间件提供的controller的默认service
+   * @param method 当前执行函数名
+   * @returns {object} 原数据
+   */
+  async getDefaultMethodOriginData(service: any, method: string) {
+    let data = null;
+    if (method === 'create') return data;
+    const id = get(this.ctx.request, 'params.id');
+    const modelName = get(service, 'model.modelName');
+    data = await service.fetch(id);
+    return { [modelName]: [data] };
+  }
+
+  /**
+   * 为默认函数提供新数据
+   * defaultMethod: create, update, delete
+   * @param service 中间件提供的controller的默认service
+   * @param method 当前执行函数名
+   * @param origin_data 原数据
+   * @returns {object} 新数据
+   */
+  async getDefaultMethodNewData(service: any, method: string, origin_data: object) {
+    let data = null;
+    if (method === 'delete') return data;
+    const modelName = get(service, 'model.modelName');
+    if (method === 'update') {
+      if (origin_data) {
+        const list = get(origin_data, modelName);
+        const arr = [];
+        for (const i of list) {
+          const d = await service.fetch(i._id);
+          arr.push(d);
+        }
+        data = arr;
+      }
+    } else if (method === 'create') {
+      data = [origin_data];
+    }
+
+    return { [modelName]: data };
+  }
+}

+ 2 - 1
src/service/system/role.service.ts

@@ -1,4 +1,4 @@
-import { Provide, Inject } from '@midwayjs/decorator';
+import { Provide, Inject, Init } from '@midwayjs/decorator';
 import { InjectEntityModel } from '@midwayjs/typegoose';
 import { ReturnModelType } from '@typegoose/typegoose';
 import { BaseService, ServiceError, FrameworkErrorEnum } from 'free-midway-component';
@@ -19,6 +19,7 @@ export class RoleService extends BaseService<modelType> {
   menusService: MenusService;
   @Inject()
   ctx: Context;
+
   //是否是超级管理员
   isSuperAdmin() {
     const user = this.ctx.user;