插件后端开发

1. 后端入口

src/api/index.ts
export * from "./modules/app.module";
src/api/modules/app.module.ts
@Module({ imports: [CategoryModule, ArticleModule], exports: [CategoryModule, ArticleModule],
})
export class AppModule {}
这个AppModule会在主程序加载插件时被动态注册。

2. 标准模块结构

src/api/modules/{module-name}/
├── {module-name}.module.ts
├── controllers/
│ ├── console/
│ └── web/
├── services/
└── dto/

3. 扩展实体:@ExtensionEntity

插件实体推荐使用:
@ExtensionEntity
export class Article {}
不要直接用普通@Entity来写插件表,原因是:
@ExtensionEntity会自动把表挂到插件自己的 schema
避免多个插件表名冲突
便于插件独立迁移与管理
这意味着:
主系统表通常在publicschema
插件表通常在各自独立 schema

4. 扩展控制器装饰器

@ExtensionConsoleController(path, groupName)

后台接口前缀:
/{extensionIdentifier}/consoleapi/{path}
示例:
@ExtensionConsoleController("article", "文章管理")
export class ArticleController {}

@ExtensionWebController(path | options)

前台接口前缀:
/{extensionIdentifier}/api/{path}
示例:
@ExtensionWebController("article")
export class ArticleWebController {}
公开接口可以继续使用主程序通用装饰器:
@Public
@Get
findAll {}

5. 插件 Service 写法

插件后端 Service 与主程序保持一致,推荐继承BaseService<T>
基础写法:
@Injectable
export class ArticleService extends BaseService<Article> { constructor( @InjectRepository(Article) private readonly articleRepository: Repository<Article>, ) { super(articleRepository); }
}
常见模式:
1.
先做业务校验
2.
再调用super.create / super.updateById / super.paginate
3.
完成时处理联动逻辑
示例:
async createArticle(dto: CreateArticleDto, authorId: string) { if (dto.categoryId) { const category = await this.categoryService.findOneById(dto.categoryId); if (!category) { throw HttpErrorFactory.badRequest("Category does not exist"); } } const author = await this.userService.findUserById(authorId); if (!author) { throw HttpErrorFactory.notFound("Author not found"); } return super.create( { ...dto, author, viewCount: 0, }, { includeFields: ["author.id", "author.avatar", "author.nickname"] as const, }, );
}

6. 插件常用公共能力

插件后端也能直接复用这些公共能力:
BaseService
BaseController
Playground
Public
BuildFileUrl
UUIDValidationPipe
DictService
HttpErrorFactory
这部分用法与主程序一致。

319 篇文档 · 内容同步自官方帮助中心