装饰器
装饰器(Decorators)往往在许多经典 OOP 编程语言中都有所体现。Kotori 的装饰器式模块设计参考了某些企业级后端框架。它在功能上与前面文档演示中所一直用的导出式等价,这仅仅是一种风格的选择问题,尽管目前确实更推荐使用装饰器式。
获取装饰器对象
通过 kotori 导出的 plugins()
方法获取,其接收一个字符串数组或带有 name
属性的对象,两者实质一样,目的均在于获取模块名字作为模块上下文的标识符:
tsx
// 手动传入模块名字
const plugin2 = plugins({
name: 'plugin2'
})
// 直接导入 package.json
const plugin3 = plugins(require('../package.json'))
// 直接提供 package.json 所在路径,推荐使用
const plugin = plugins([__dirname, '../'])
基础装饰器
基础装饰器用于定义插件的核心特性,包括服务注入和配置模式:
tsx
@plugin.import
export class BasicPlugin extends KotoriPlugin<Tsu.infer<typeof BasicPlugin.schema>> {
// 注入服务
@plugin.inject
public static inject = ['db', 'cache']
// 配置模式
@plugin.schema
public static schema = Tsu.Object({
maxRetries: Tsu.Number().min(0).max(5).default(3),
enableDebug: Tsu.Boolean().default(false),
mode: Tsu.Union(Tsu.Literal('simple'), Tsu.Literal('advanced')).default('simple')
})
@plugin.on({ type: 'ready' })
public async onReady() {
// 访问上下文对象
this.ctx.logger.info('Plugin is ready!')
// 访问配置数据
this.ctx.logger.info(this.config.maxRetries)
}
}
KotoriPlugin<C>
接收一个可选的泛型参数,表示插件的配置数据类型,配合 Tsu.infer<S>
使用更为优雅。
@plugin.import
- 将类标记为 Kotori 插件
- 必须继承
KotoriPlugin
类
@plugin.inject
- 声明插件依赖的服务
- 注入的服务可通过
this.ctx
访问
@plugin.schema
- 定义插件配置的数据结构
- 使用
Tsu
验证器进行类型检查
事件处理
事件装饰器用于处理各种系统事件:
tsx
@plugin.import
export class EventPlugin extends KotoriPlugin {
// 处理就绪事件
@plugin.on({ type: 'ready' })
public async onReady() {
this.ctx.logger.info('Plugin is ready!')
}
// 处理销毁事件
@plugin.on({ type: 'dispose' })
public async onDispose() {}
// 处理错误事件
@plugin.on({ type: 'error' })
public async onError(error: Error) {
this.ctx.logger.error('Error occurred:', error)
}
}
@plugin.on
支持的事件类型包括:
ready
: 插件就绪message
: 收到消息error
: 发生错误- 更多事件类型...
命令处理
命令装饰器用于创建和处理用户命令:
tsx
@plugin.import
export class CommandPlugin extends KotoriPlugin {
// 简单命令
@plugin.command({
template: 'greet <name>'
})
public async greet({ args }: { args: string[] }) {
return `Hello, ${args[0]}!`
}
// 带选项的复杂命令
@plugin.command({
template: 'calc <num1:number> <num2:number>',
access: UserAccess.ADMIN,
options: [
['m', 'mode:string'],
['d', 'decimal:number']
]
})
public async calc({ args, options }: { args: number[]; options: { mode?: string; decimal?: number } }) {
const [num1, num2] = args
const mode = options.mode || 'add'
const decimal = options.decimal || 2
let result: number
switch (mode) {
case 'add':
result = num1 + num2
break
case 'sub':
result = num1 - num2
break
default:
result = num1 + num2
}
return `Result: ${result.toFixed(decimal)}`
}
}
@plugin.command
命令配置选项:
template
: 命令模板,定义参数格式access
: 访问权限级别options
: 命令选项定义- 更多配置...
中间件和其他功能
其他实用装饰器:
tsx
@plugin.import
export class MiddlewarePlugin extends KotoriPlugin {
// 优先级中间件
@plugin.midware({ priority: 10 })
public async logMiddleware(next: () => Promise<void>, _session: SessionMsg) {
const start = Date.now()
await next()
const time = Date.now() - start
this.ctx.logger.debug(`Request processed in ${time}ms`)
}
// 正则匹配处理
@plugin.regexp({ match: /^#(\w+)=(.*)$/ })
public async handleKeyValue(match: RegExpExecArray) {
const [, key, value] = match
return `Setting ${key} to ${value}`
}
// 定时任务
@plugin.task({ cron: '0 */30 * * * *' })
public async scheduledTask() {
this.ctx.logger.info('Running scheduled task')
}
}
@plugin.midware
- 定义处理中间件
- 可设置优先级
- 支持异步处理
@plugin.regexp
- 基于正则表达式的消息处理
- 自动提取匹配组
@plugin.task
- 创建定时任务
- 使用 cron 表达式定义执行时间
TIP
装饰器的执行顺序是确定的:
- @plugin.import
- @plugin.inject 和 @plugin.schema
- 其他装饰器(按声明顺序)
建议与习惯
- 模块类的导出并非必要(因为通过装饰器外部已经可以获取到类对象),仅仅在于可以让 IDE 的未使用提示闭嘴
- 继承
KotoriPlugin
类并非必要,仅仅在于它会手动设置上下文与配置数据便于获取,但建议最好写上 - 使用
@plugin.inject
或@plugin.schema
时,其属性必须为静态属性,因为装饰器无法获取实例属性 - 成员修饰符与名字不影响均布影响效果,但建议按照约定来
- 一个成员方法中需要使用
this.ctx
或this.config
时,建议将其声明为实例属性,否则声明为静态属性