> ## Documentation Index
> Fetch the complete documentation index at: https://tegg.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 装饰器

本文对常见的 HTTP 与 IoC 装饰器给出双视角说明：

* 使用视角：如何在控制器/服务中使用
* 内部视角：装饰器如何转化为元数据，如何影响容器解析与运行时行为

## 1. IoC 与对象作用域（核心）

这些装饰器定义了对象在容器中的“原型（Proto）”与生命周期，属于最基础的依赖注入体系。

### 1.1 @SingletonProto(params?)

* 作用：声明“应用级单例”。应用启动创建（或首次使用后常驻），直到应用关闭才释放。
* 适用场景：
  * 跨请求复用的昂贵资源：数据库连接池、Redis 客户端、第三方 SDK 客户端、HTTP 客户端等
  * 全局缓存、配置中心客户端、限流器、熔断器、监控/埋点上报器
  * 后台/定时任务执行器（长期存活、不会被释放）
* 常用参数：`accessLevel`（默认 PRIVATE，如需被其他模块注入设为 PUBLIC）
* 示例：

```ts theme={null}
import { SingletonProto, AccessLevel } from '@eggjs/tegg';

@SingletonProto({ accessLevel: AccessLevel.PUBLIC })
export class RepoClient {}
```

### 1.2 @ContextProto(params?)

* 作用：声明“请求级对象”。每次请求创建一个实例，请求结束后释放。
* 适用场景：
  * 强依赖当前请求上下文（ctx）的业务服务：鉴权信息、traceId、租户、灰度/分流策略等
  * 请求内中间态缓存、一次性事务/会话编排
  * 需要读取 ctx 或与中间件链路协作的 AOP/策略对象
* 常用参数：`accessLevel`（默认 PRIVATE）
* 示例：

```ts theme={null}
import { ContextProto } from '@eggjs/tegg';

@ContextProto()
export class OrderService {}
```

### 1.3 @Inject(token?) / @InjectOptional(token?)

* 作用：声明依赖注入点，由容器在创建对象时自动解析并注入对应实例。
* 区别：`@InjectOptional` 在依赖缺失时不会报错，注入为 `undefined`。
* 典型写法：构造器注入或属性注入。

```ts theme={null}
import { Inject, InjectOptional } from '@eggjs/tegg';

export class BizService {
  constructor(@Inject() private repo: RepoClient,
              @InjectOptional() private cache?: CacheClient) {}
}
```

* 关键规则（非常重要）：
  * ContextProto 可以注入 SingletonProto（和同为 ContextProto 的对象）
  * SingletonProto 不能注入 ContextProto（单例跨请求常驻，禁止持有短生命周期对象）

### 1.4（进阶）@MultiInstanceProto(params?)

* 作用：声明“多实例作用域”，可用于按租户/地域等维度隔离实例。
* 说明：高级场景使用，需结合自定义实例查找与生命周期管理，日常开发可不使用。

***

## 2. 控制器与路由（HTTP）

由 controller 插件提供，用于声明控制器、路由与参数绑定。

### 2.1 @HTTPController(options)

* 作用：声明控制器类，指定基路径、Host 约束、控制器级中间件等。
* 常用字段：`path`、`host`、`middleware`。

```ts theme={null}
import { HTTPController, Middleware } from '@eggjs/tegg';

@HTTPController({ path: '/api/v1' })
@Middleware(authMiddleware)
export class UserController {}
```

### 2.2 @HTTPMethod 及快捷装饰器

* 作用：声明方法级路由（HTTP 动作 + 路径）。
* 使用方式：可统一用 `@HTTPMethod({ method: 'GET', path: '/users/:id' })`，也可使用快捷装饰器（若已启用）：`@GET('/users/:id')`、`@POST('/users')` 等。

```ts theme={null}
import { HTTPMethod } from '@eggjs/tegg';

@HTTPMethod({ method: 'GET', path: '/users/:id' })
async getUser(@HTTPParam('id') id: string) { /* ... */ }
```

### 2.3 参数装饰器族（从请求中取值）

* `@HTTPBody()`：请求体
* `@HTTPParam(name)`：路径参数
* `@HTTPQuery(name)` / `@HTTPQueries()`：查询参数（单个/全部）
* `@HTTPHeaders(name)`：请求头
* `@Cookies(name)`：cookie
* `@Request()` / `@Context()`：注入原始 Request / 框架上下文对象

```ts theme={null}
@HTTPMethod({ method: 'POST', path: '/users' })
async create(
  @HTTPBody() body: any,
  @HTTPHeaders('x-request-id') rid: string,
  @Context() ctx: any,
) { /* ... */ }
```

### 2.4 @Middleware(mw)（控制器级/方法级）

* 作用：为控制器或方法声明中间件链；支持数组、按声明顺序执行。
* 场景：认证、鉴权、灰度、审计、参数校验、读写分离、埋点等。
* 示例：

```ts theme={null}
@HTTPController({ path: '/api/v1' })
@Middleware([authMiddleware, logMiddleware])
export class UserController {}
```

### 2.5 @Host(pattern)

Host 注解，用于指定 HTTP 方法仅在 host 匹配时执行。
可以添加在类/方法上。添加在类上时，对类上所有方法生效，添加在方法上时，只对当前方法生效。方法上的注解可以覆盖类上的注解

```ts theme={null}
import { Host } from '@eggjs/tegg';
@Host('foo.eggjs.com')
export class FooController {
  // 仅能通过 foo.eggjs.com 访问
  async hello() {
  }

  // 仅能通过 bar.eggjs.com 访问
  @Host('bar.eggjs.com')
  async bar() {
  }
}
```

***

## 3. 生命周期（核心/扩展）

### 3.1 @LifecycleInit

* 作用：对象完成依赖注入后、对外提供服务前执行初始化逻辑（如预热连接、加载索引/字典）。
* 场景：单例对象在应用启动阶段的预热；或上下文对象在首次使用前的准备工作。

```ts theme={null}
import { SingletonProto, LifecycleInit } from '@eggjs/tegg';

@SingletonProto()
export class DictService {
  @LifecycleInit()
  async init() { /* 预热字典/连接等 */ }
}
```

***

## 4. 任务与模型（官方插件，按需使用）

### 4.1 @Schedule(...)（schedule 插件）

* 作用：声明定时任务类，框架会按配置周期触发其实例的 subscribe 方法。
* 适用场景：数据同步、清理、归档、重试补偿等后台任务。
* 导入路径：从聚合包引入 `@eggjs/tegg/schedule`。

参数说明（与实现保持一致）：

* `param: ScheduleParams<T>`
  * `type: 'worker' | 'all'`（或 `ScheduleType.WORKER | ScheduleType.ALL`）——在单个 worker 进程执行，或所有 worker 均执行。
  * `scheduleData`：
    * Interval 定时：`{ interval: string | number }`（如 `'10s'`、`60000`）。
    * Cron 定时：`{ cron: string, cronOptions?: any }`（标准 cron 表达式）。
* `options?: ScheduleOptions`
  * `immediate?: boolean` 启动后立即执行一次（默认 false）。
  * `disable?: boolean` 关闭此任务（默认 false）。
  * `env?: string[]` 指定仅在这些环境运行。

使用示例：

```ts theme={null}
import { Schedule, ScheduleType, type ScheduleSubscriber } from '@eggjs/tegg/schedule';

// 每 10 秒运行一次，只在本地和测试环境运行，启动时立即跑一轮
@Schedule({
  type: ScheduleType.WORKER,
  scheduleData: { interval: '10s' },
}, {
  immediate: true,
  env: [ 'local', 'test' ],
})
export class CleanTmpJob implements ScheduleSubscriber {
  async subscribe() {
    // 执行清理逻辑
  }
}
```

```ts theme={null}
import { Schedule, ScheduleType, type ScheduleSubscriber } from '@eggjs/tegg/schedule';

// 每小时第 0 分钟运行一次，所有 worker 执行
@Schedule({
  type: ScheduleType.ALL,
  scheduleData: { cron: '0 0 * * * *' },
})
export class HourlyReportJob implements ScheduleSubscriber {
  async subscribe() {
    // 执行汇总/上报逻辑
  }
}
```

实现要点：装饰器内部会将类标记为单例并提升访问级别为 PUBLIC，框架根据 `type`/`scheduleData`/`options` 注册与调度该任务。

### 4.2 @Model(...)（orm 插件）

* 作用：声明 ORM 模型（基于 leoric），并绑定到指定数据源/表名。
* 适用场景：表模型定义、DAO 封装、领域仓储实现等。
* 导入路径：从聚合包引入 `@eggjs/tegg/orm`。

模型定义：

```ts theme={null}
import { Model, Attribute } from '@eggjs/tegg/orm';
import { Bone, DataTypes } from 'leoric';

@Model({
  dataSource: 'test', // 可选：不配置则使用默认数据源
  // tableName: 'pkgs', // 可选：缺省按类名推导
})
export class Pkg extends Bone {
  @Attribute(DataTypes.STRING)
  name: string;

  @Attribute(DataTypes.STRING)
  desc: string;

  // 可选：模型生命周期钩子
  static beforeCreate(instance: Pkg) {
    instance.name += '_before_create_hook';
  }
}
```

在 Service 中注入并使用模型：

```ts theme={null}
import { Inject, SingletonProto } from '@eggjs/tegg';
import { Pkg } from './model/Pkg';

@SingletonProto()
export class PkgService {
  @Inject()
  Pkg: typeof Pkg;

  async createPkg(data: { name: string; desc: string }) {
    return await this.Pkg.create(data as any);
  }

  async findPkg(name: string) {
    return await this.Pkg.findOne({ name });
  }
}
```

使用提示：需启用 ORM 插件并配置数据源（datasource），随后即可通过模型静态方法 `create`/`findOne`/`update` 等进行读写。

> 说明：上述为代表性装饰器，更多装饰器与高级特性（如 AOP 切面相关装饰器）请查阅对应插件的 README。

***

## 5. 选型与最佳实践

* 是否依赖 ctx（请求信息）
  * 需要 → 使用 `@ContextProto`
  * 不需要/需跨请求复用 → 使用 `@SingletonProto`
* 依赖方向
  * ContextProto 可注入 SingletonProto（以及同为 ContextProto 的对象）
  * SingletonProto 不可注入 ContextProto（避免生命周期倒挂）
* 性能与资源
  * 重/长连接资源放单例集中管理；请求内临时状态放上下文对象
* 访问级别（accessLevel）
  * 默认 `PRIVATE` 仅模块内可见；跨模块注入时设为 `PUBLIC`

***

## 6. 快速参考

* IoC：`@SingletonProto`、`@ContextProto`、`@Inject`、`@InjectOptional`
* 控制器：`@HTTPController`、`@HTTPMethod`（或 `@GET`/`@POST` 等）、参数装饰器族、`@Middleware`、`@Host`
* 生命周期：`@LifecycleInit`
* 任务与模型（插件）：`@Schedule`、`@Model`
