How To Develop Mog - 1
这是 《如何开发 Mog》 系列的第一篇章,不过对于这个项目的结构我可能就不会怎么讲了,可能就稍微略过了,整体下来可能这个系列比较混乱,是开发的过程中就捎带写下罢了
如何新增配置
在 Mog v1 的时候,ConfigService 已经被抽离为一个 Library,你可以前往 libs/config
里看到有关这个服务的所有信息
你可以进入 apps/configs-service
查看服务的文件
在 ConfigService 里基本都是关键文件,除了那些启动用的 main.ts
和 module
啥的, 其他新增配置都需要碰的,不过我大致列举一下吧:
- config.dto.ts - 不同配置 Key 对应的 Dto
- config.interface.ts - 配置 interface
- config.default.ts - 默认配置
开发的顺序也是如列表那样的,需要先写好 Dto。
那么我现在以 计划任务 这个 feature 作为例子,介绍如何新增一个配置计划任务的配置项。
事实上,这个 PR 已经相对来说不能够来作为例子了,因为它是 Stage 5 实现的,在 Stage 6 我们将其全部迁移至了独立服务.
你只能够学习如何加配置,但是不能学习文件的储存位置,新的储存位置在 apps/configs-service
里面
计划任务配置项
在我设想的计划任务配置项中它需要有以下内容:
- 任务名
- Cron 表达式
- 任务描述
- 任务操作类型
- 任务操作
- 任务执行后对数据操作类型
- 任务执行后对数据操作
因此让我们前往 config.dto.ts
定义 DTO,大致是这个样子的:
// config.dto.ts
export class ScheduleDto {
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
cron: string;
@IsString()
description?: string;
@IsString()
@IsNotEmpty()
@IsEnum(ScheduleType)
type: ScheduleType;
@IsNotEmpty()
action: any; // 这随着 type 的不同而不同, 用于储存如:url,method,body的数据
@IsString()
@IsNotEmpty()
@IsEnum(AfterSchedule)
after: AfterSchedule;
@IsNotEmpty()
afterAction: string; // 这随着 after 的不同而不同
}
接着我们前往 config.interface.ts
,在 ConfigsInterface 里新增一个 schedule:
export abstract class ConfigsInterface {
// ...
@Type(() => ScheduleDto)
@ValidateNested()
schedule: ScheduleDto[];
}
这样就可以辽~~在你更新此配置项的时候,ConfigService 便会自动检查你的数据格式是否合法了。让我们前进到获取数据库配置吧。
读取计划任务配置项
由于这个 feat 的特性,我需要在初始化的时候就读取数据库,但是一般情况下是不需要的。
在 constructor 里引入 ConfigService (记得 Module 里要 import )
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { ServicesEnum } from '~/shared/constants/services.constant';
import { REDIS_TRANSPORTER } from '~/shared/constants/transporter.constants';
@Module({
imports: [
ClientsModule.register([
{
name: ServicesEnum.config,
...REDIS_TRANSPORTER,
},
]),
],
controllers: [
// YOUR CONTROLLERS
],
})
export class YourModule {}
接着在 constructor 里引入 ClientProxy
, 但请记住需要 Inject
export class NotificationService {
constructor(
@Inject(ServicesEnum.config) private readonly configService: ClientProxy,
) {}
}
这样便可得到配置
const config = transportReqToMicroservice(
this.configService,
ConfigEvents.ConfigGetByMaster,
'schedule',
);
其他的 ConfigEvents
你可以在 shared/constants/event.constant.ts
里找到
transportReqToMicroservice
是转接请求到活动传播的一个小工具,它可以便捷地处理返回的RpcException
,并转化为HttpException
。
如何新增活动
Mog 有一个文件储存了全部的 Events:shared/constants/event.constant.ts
里面是不同的 enum,它们遵循一个命名原则:<service_mininum_name> + Events
如:StoreService -> StoreEvents, ThemesService -> ThemesEvents
我以新增 StoreService 中创建文件活动为例子,为你讲述如何新增活动:
新增活动名
要确定是否需要鉴权,如果需要鉴权,在活动最后需要加上
auth
如:
export enum StoreEvents {
//...
StoreFileCreateByMaster = 'store.file.create.auth',
}
有关其他活动的具体要求,在后文有关新增服务时会着重讲述。
传播活动与监听活动
服务活动有两种触发方式,分别是Request-response 和 Event-based,有关区别自行看文档。我在这个 feat 中需要使用 request-response 方式
前往对应 service 的 controller,撰写代码,如:(已假设你已写好 service 的相关逻辑)
@MessagePattern({ cmd: StoreEvents.StoreFileCreateByMaster })
async storeFileCreate(data: { name: string, content: string | Buffer }) {
return await this.storeServiceService.create(data.name, data.content);
}
再前往 Gateway (core)中对应 module 的 controller,撰写代码,如:
@Post("/create")
@ApiOperation({ summary: '创建文件' })
@Auth()
create(@Body('name') name: string, @Body('content') content: Buffer) {
return transportReqToMicroservice(
this.store,
StoreEvents.StoreFileCreateByMaster,
{ name, content },
);
}