后台程序手册

装备 src\config

Midway官方-多环境装备,一般只需要变更开发(local)或布置(prod)环境的装备文件对默许装备进行参数的掩盖。

  • default 悉数默许装备
  • unittest 测试装备(不用管,暂时没用)
  • local 和 prod 根据环境装备与默许装备差异的参数进行掩盖

中心 src\framework

封装的各种东西类函数,应用于整个程序。

项目结构

framework
├── cache               目录-缓存
├── constants           目录-常量特点值界说
├── datasource          目录-数据源
├── decorator           目录-装修器界说
├── enums               目录-枚举类型界说
├── filter              目录-全局反常过滤器界说
├── middleware          目录-中心件界说
├── model               目录-模型目标界说
├── service             目录-服务函数界说
└── utils               目录-东西函数界说

装修器界说 src\framework\decorator

Midway官方-自界说装修器

办法装修器

@PreAuthorize 用户身份授权认证校验

默许界说的接口都是不需要验证权限人物可以揭露拜访的,在界说接口拜访的办法上面增加注解 @PreAuthorize 来操控拜访权限。

超级管理员拥有一切权限,不受权限约束。

办法上运用注解示例:

/// 数据权限示例
@Get()
@PreAuthorize({ hasPermissions: ['system:user:query'] })
async getInfo(): Promise<String> { return "PreAuthorize Permissions OK!"}
// 只需含有其间一项权限 system:user:list 和 system:user:query
@PreAuthorize({ hasPermissions: ['system:user:list', 'system:user:query'] })
// 必须一起匹配含有其间权限 system:user:list 和 system:user:query
@PreAuthorize({ matchPermissions: ['system:user:list', 'system:user:query'] })
/// 人物权限示例
@Get()
@PreAuthorize({ hasRoles: ['user'] })
async getInfo(): Promise<String> { return "PreAuthorize Roles OK!"}
// 只需含有其间一项权限 user 和 admin
@PreAuthorize({ hasRoles: ['user', 'admin'] })
// 必须一起匹配含有其间人物 user 和 admin
@PreAuthorize({ matchRoles: ['user', 'admin'] })
/// 不需要验证权限人物,登录即可拜访
@Get()
@PreAuthorize()
async getInfo(): Promise<String> { return "PreAuthorize OK!"}
@RepeatSubmit 避免表单重复提交

在界说接口拜访办法上增加注解 @RepeatSubmit 来避免表单重复提交,限定单位时间内制止相同内容重复恳求。

办法上运用注解示例:

/// 避免重复提交示例
@Del('/refreshCache')
@RepeatSubmit(60)
async refreshCache(): Promise<String> { return "RepeatSubmit OK!" }
// 间隔时间(单位秒) 默许:5
@RepeatSubmit()
// 间隔时间设置60秒,同内容参数60秒内只能提交一次
@RepeatSubmit(60)
@RateLimit 限流

在界说接口拜访办法上增加注解 @RateLimit 来约束单位时间内最多可以拜访次数。

办法上运用注解示例:

/// 限流示例
@Get('/captchaImage')
@RateLimit({ time: 300, count: 60, limitType: LimitTypeEnum.IP })
async captchaImage(): Promise<Result> { return "RateLimit OK!" }
// 120秒内,最多恳求60次,无类型时默许针对办法进行约束
@RateLimit({ time: 120, count: 60 })
// 300秒内,最多恳求10次,指定类型针对拜访者IP进行约束
@RateLimit({ time: 300, count: 10, limitType: LimitTypeEnum.IP })
// 720秒内,最多恳求1000次,指定类型针对登录用户进行约束
@RateLimit({ time: 720, count: 1000, limitType: LimitTypeEnum.USER })
@OperLog 拜访操作日志记载

在界说接口拜访办法上增加注解 @OperLog 来对恳求参数和呼应数据进行记载。

请在用户身份授权认证校验后运用以便获取登录用户信息

办法上运用注解示例:

/// 拜访操作日志记载示例
@Put()
@PreAuthorize()
@OperLog({
  title: '修改信息',
  businessType: OperatorBusinessTypeEnum.UPDATE,
})
async edit(): Promise<Result> { return "RateLimit OK!" }
// 日志标题叫xx信息,日志类型为更新行为
@OperLog({ title: 'xx信息',  businessType: OperatorBusinessTypeEnum.UPDATE })
// 日志标题叫xx信息,日志类型为授权行为,操作类型为其他
@OperLog({ title: 'xx信息',  businessType: OperatorBusinessTypeEnum.GRANT, operatorType: OperatorTypeEnum.OTHER })
// 日志标题叫xx信息,日志类型为更新行为,操作类型默许为后台用户, 不记载恳求参数,不记载呼应参数
@OperLog({ title: 'xx信息',  businessType: OperatorBusinessTypeEnum.UPDATE, isSaveRequestData: false, isSaveResponseData: false })

中心件界说 src\framework\middleware

洋葱模型,常用于验证日志等。

疏忽和匹配 不能一起运用
options.match and options.ignore can not both present

// 路由将疏忽此中心件
ignore(ctx: Context): boolean {
    return ctx.path === '/api/info'
}
// 匹配到的路由会履行此中心件
match(ctx: Context): boolean { return false }

全局反常过滤器界说 src\framework\filter

当恳求结束时,存在反常坚持返回数据前

东西函数界说 src\framework\utils

常用可控函数

服务模块开发接口 src\modules

对范畴功能进行划分为不同的模块并完成其服务接口

xxx                 目录-xxx模块
├── controller      目录-接口路由操控层
├── model           目录-数据目标模型层
├── repository      目录-CURD数据存储层
├── service         目录-业务逻辑服务层
└── ...

接口路由操控层 src\modules\xxx\controller

界说恳求路由负责参数接收验证

数据目标模型层 src\modules\xxx\model

数据库中表的字段特点进行目标模型建立

CURD数据存储层 src\modules\xxx\repository

repository
├── impl                      目录-存储层接口完成
├── Ixxx.ts                   文件-存储层接口界说
└── ...

对服务层处理后的数据进行存储,一般对各表间存储联系进行相关操作。

在运用时 ts 只能运用完成层的部分,所以界说接口后需要在 impl 目录中完成接口具体的函数行为。

数据库业务

在某些时分需要确保数据的一致性,对数据库的 SQL 操作。

常见场景:用户转账时,A用户-100 => B用户+100,中心产生反常时导致B没收到A的转账

import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
import { DynamicDataSource } from '../../../../framework/datasource/DynamicDataSource';
@Provide()
@Scope(ScopeEnum.Singleton)
export class xxxRepositoryImpl {
  @Inject()
  private db: DynamicDataSource;
  /**
   * 更新操作
   * 
   * @returns true | false
   */
  async update_xxx(): Promise<boolean> {
    // 获取衔接并创建新的queryRunner
    const queryRunner = this.db.executeRunner();
    // 对此业务履行一些操作
    try {
      // 开始业务
      await queryRunner.startTransaction();
      // sql句子
      let sql_str = `UPDATE tbl_xxx SET xx = ? WHERE id= ?`;
      const up_row1 = await queryRunner.query(sql_str, [101, "101"]);
      const up_row2 = await queryRunner.query(sql_str, [102, "102"]);
      if (parseInt(up_row1.affectedRows) <= 0 || parseInt(up_row2.affectedRows) <= 0) {
        // 有错误做出回滚更改 后释放衔接
        await queryRunner.rollbackTransaction();
        return false;
      }
      // 提交业务写入库 后释放衔接
      await queryRunner.commitTransaction();
      return true;
    } catch (err) {
      // 有错误做出回滚更改 后释放衔接
      await queryRunner.rollbackTransaction();
      throw new Error('服务数据反常');
    } finally {
      //释放衔接
      await queryRunner.release();
    }
  }
}

业务逻辑服务层 src\modules\xxx\service

service
├── impl                      目录-服务层接口完成
├── Ixxx.ts                   文件-服务层接口界说
└── ...

对恳求参数进行处理逻辑操作的层面,会包含很多条件判别或调用其他依靠库等。

在运用时 ts 只能运用完成层的部分,所以界说接口后需要在 impl 目录中完成接口具体的函数行为。

队列使命处理 src\modules\xxx\processor

声明履行办法函数逻辑后,在调度使命中创建指定履行办法称号(test)。

@Processor('test')
export class TestProcessor implements IProcessor {
  @Inject()
  private ctx: Context;
  async execute(options: ProcessorOptions): Promise<ProcessorData> {
    const log = this.ctx.getLogger();
    const ctxJob = this.ctx.job;
    // 履行一次得到是直接得到传入的jobId
    // 重复使命得到编码格式的jobId => repeat:编码Jobid:履行时间戳
    log.info('原始jonId: %s | 当前jobId %s', options.jobId, ctxJob.id);
    // 返回成果,用于记载履行成果
    return options;
  }
}

通用类型界说 src\typings

界说参数结构的类型

内部静态资源文件 src\assets


运用服务函数读取文件流
FileService.readAssetsFile(asserPath: string)
示例:
this.fileService.readAssetsFile('/template/excel/user_import_template.xlsx')