插件后端工具与封装文档
templates/extension-starter/src/api。导入总览
| 类型 | 导入方式 | 用途 |
|---|---|---|
| 基础 Controller/Service | @chatbuddy-ai/base | 日志、分页结果、CRUD、事务、分页查询 |
| 插件 Controller/Entity 装饰器 | @chatbuddy-ai/core/decorators | 插件前台接口、控制台接口、插件实体 |
| 通用装饰器 | @chatbuddy-ai/decorators/... | 公开接口、当前用户、文件 URL、响应转换 |
| DTO | @chatbuddy-ai/dto | 分页查询 DTO |
| Pipe | @chatbuddy-ai/pipe/param-validate.pipe | UUID 参数校验 |
| 错误封装 | @chatbuddy-ai/errors | 统一业务错误 |
| 数据库 | @chatbuddy-ai/db/@nestjs/typeorm、@chatbuddy-ai/db/typeorm | TypeORM Module、Repository、装饰器、查询操作符 |
| 平台实体 | @chatbuddy-ai/db/entities | 使用平台已有实体,如User |
| 插件 SDK | @chatbuddy-ai/extension-sdk | 平台用户、AI、计费、tsup 构建配置 |
| 工具函数 | @chatbuddy-ai/utils | where 构造、路径、类型转换、版本、文件、安全等 |
插件 Controller 装饰器
ExtensionWebController
import { ExtensionWebController } from "@chatbuddy-ai/core/decorators";import { BaseController } from "@chatbuddy-ai/base";
import { ExtensionWebController } from "@chatbuddy-ai/core/decorators";
import { Public } from "@chatbuddy-ai/decorators/public.decorator";
import { UUIDValidationPipe } from "@chatbuddy-ai/pipe/param-validate.pipe";
import { Get, Param, Query } from "@nestjs/common"; @ExtensionWebController("article")
export class ArticleWebController extends BaseController { constructor(private readonly articleService: ArticleService) { super; } @Get @Public async findAll(@Query query: QueryArticleDto) { return this.articleService.list(query); } @Get(":id") @Public async findOne(@Param("id", UUIDValidationPipe) id: string) { return this.articleService.findOneById(id); }
}| 写法 | 介绍 |
|---|---|
@ExtensionWebController("article") | 配置 Controller path |
@ExtensionWebController({ path: "article" }) | 对象写法 |
@ExtensionWebController({ path: "article", skipAuth: true }) | 整个 Controller 跳过鉴权 |
| 能力 | 介绍 |
|---|---|
| 插件路由前缀 | 默认/{extensionIdentifier}/api/{path} |
| 环境前缀 | 如果存在VITE_APP_WEB_API_PREFIX,使用该前缀 |
| 插件 metadata | 配置插件包名和 Web Controller 标记 |
| 鉴权控制 | skipAuth会配置公开 metadata |
| path 校验 | path 不能包含/和: |
ExtensionConsoleController
import { ExtensionConsoleController } from "@chatbuddy-ai/core/decorators";import { BaseController } from "@chatbuddy-ai/base";
import { ExtensionConsoleController } from "@chatbuddy-ai/core/decorators";
import { Playground } from "@chatbuddy-ai/decorators/playground.decorator";
import { type UserPlayground } from "@chatbuddy-ai/db";
import { UUIDValidationPipe } from "@chatbuddy-ai/pipe/param-validate.pipe";
import { Body, Delete, Get, Param, Post, Query } from "@nestjs/common"; @ExtensionConsoleController("article", "文章管理")
export class ArticleController extends BaseController { constructor(private readonly articleService: ArticleService) { super; } @Post async create(@Body dto: CreateArticleDto, @Playground user: UserPlayground) { return this.articleService.createArticle(dto, user.id); } @Get async findAll(@Query query: QueryArticleDto) { return this.articleService.list(query); } @Delete(":id") async remove(@Param("id", UUIDValidationPipe) id: string) { await this.articleService.delete(id); return { success: true }; }
}| 写法 | 介绍 |
|---|---|
@ExtensionConsoleController("article", "文章管理") | 配置 path 和权限组名称 |
@ExtensionConsoleController({ path: "article" }, "文章管理") | 对象写法 |
@ExtensionConsoleController({ path: "article", skipAuth: true }, "文章管理") | 跳过鉴权 |
| 能力 | 介绍 |
|---|---|
| 插件路由前缀 | 默认/{extensionIdentifier}/consoleapi/{path} |
| 环境前缀 | 如果存在VITE_APP_CONSOLE_API_PREFIX,使用该前缀 |
| 插件 metadata | 配置插件包名和 Console Controller 标记 |
| 权限组 metadata | 自动配置code: "${extensionIdentifier}@${path}"和name: groupName |
| path 校验 | path 不能包含/和: |
插件 Entity
ExtensionEntity
import { ExtensionEntity } from "@chatbuddy-ai/core/decorators";import { ExtensionEntity } from "@chatbuddy-ai/core/decorators";
import { Column, CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn,
} from "@chatbuddy-ai/db/typeorm"; @ExtensionEntity
export class Article { @PrimaryGeneratedColumn("uuid") id: string; @Column({ length: 200, comment: "文章标题" }) title: string; @Column({ type: "text", nullable: true, comment: "摘要" }) summary?: string; @CreateDateColumn createdAt: Date; @UpdateDateColumn updatedAt: Date;
}| 写法 | 介绍 |
|---|---|
@ExtensionEntity | 表名默认用类名转 snake_case |
@ExtensionEntity("article") | 指定表名 |
@ExtensionEntity({ name: "article" }) | 使用 TypeORMEntityOptions |
| 能力 | 介绍 |
|---|---|
| 插件 schema | 根据插件安装目录推导 schema |
| 表名处理 | 支持默认表名和自定义表名 |
| TypeORM Entity | 内部应用 TypeORMEntity装饰器 |
基础 Controller
BaseController
import { BaseController } from "@chatbuddy-ai/base";| 能力 | 介绍 |
|---|---|
logger | 自动创建 Nest Logger,context 为子类名 |
paginationResult | 组装标准分页响应 |
import { BaseController } from "@chatbuddy-ai/base"; export class ArticleController extends BaseController { async list(dto: QueryArticleDto) { this.logger.log("query article list"); return this.articleService.list(dto); }
}{ items: T[]; total: number; page: number; pageSize: number; totalPages: number;
}基础 Service
BaseService
import { BaseService } from "@chatbuddy-ai/base";import { BaseService } from "@chatbuddy-ai/base";
import { InjectRepository } from "@chatbuddy-ai/db/@nestjs/typeorm";
import { Repository } from "@chatbuddy-ai/db/typeorm";
import { HttpErrorFactory } from "@chatbuddy-ai/errors";
import { Injectable } from "@nestjs/common"; import { Article } from "../../../db/entities/article.entity";
import { CreateArticleDto, QueryArticleDto } from "../dto"; @Injectable
export class ArticleService extends BaseService<Article> { constructor( @InjectRepository(Article) private readonly articleRepository: Repository<Article>, ) { super(articleRepository); } async createArticle(dto: CreateArticleDto) { return this.create(dto); } async list(query: QueryArticleDto) { const where = query.title ? this.ilike("title", query.title) : {}; return this.paginate(query, { where, order: { createdAt: "DESC" }, }); } async publish(id: string) { const article = await this.findOneById(id); if (!article) { throw HttpErrorFactory.notFound("文章不存在"); } return this.updateById(id, { status: "published" }); }
}| 方法 | 介绍 |
|---|---|
create | 创建单条记录 |
createMany | 批量创建 |
updateById | 按 id 更新 |
update | 按条件更新 |
findOneById | 按 id 查询 |
findOne | 按条件查询单条 |
findAll | 查询列表 |
count | 计数 |
delete | 移除 |
deleteMany | 批量移除 |
restore | 恢复软移除记录 |
paginate | Repository 分页 |
paginateQueryBuilder | QueryBuilder 分页 |
createTransaction | 创建事务 |
withTransaction | 在事务中执行 |
withRetry | 重试执行 |
applyLockToFindOptions | 查询锁配置 |
| 方法 | 介绍 |
|---|---|
ilike(field, value) | PostgreSQL 不区分大小写模糊搜索 |
textSearch(field, value) | PostgreSQL 全文搜索 |
jsonQuery(field, path, value) | JSON 字段查询 |
arrayContains(field, values) | 数组包含查询 |
| 选项 | 介绍 |
|---|---|
includeFields | 只返回指定字段,支持author.nickname |
excludeFields | 排除指定字段,支持嵌套字段 |
return this.findOneById(id, { relations: ["author"], includeFields: ["id", "title", "author.id", "author.nickname"] as const,
});DTO 和 Pipe
PaginationDto
import { PaginationDto } from "@chatbuddy-ai/dto";| 字段 | 默认值 | 介绍 |
|---|---|---|
page | 1 | 当前页 |
pageSize | 15 | 每页数量 |
import { PaginationDto } from "@chatbuddy-ai/dto";
import { IsOptional, IsString, IsUUID } from "class-validator"; export class QueryArticleDto extends PaginationDto { @IsOptional @IsString title?: string; @IsOptional @IsUUID categoryId?: string;
}UUIDValidationPipe
import { UUIDValidationPipe } from "@chatbuddy-ai/pipe/param-validate.pipe";import { Get, Param } from "@nestjs/common"; @Get(":id")
async findOne(@Param("id", UUIDValidationPipe) id: string) { return this.articleService.findOneById(id);
}通用装饰器
Public
import { Public } from "@chatbuddy-ai/decorators/public.decorator";@Get("published")
@Public
async getPublished { return this.articleService.getPublishedArticles;
}Playground
import { Playground } from "@chatbuddy-ai/decorators/playground.decorator";import { type UserPlayground } from "@chatbuddy-ai/db"; @Post
async create(@Body dto: CreateArticleDto, @Playground user: UserPlayground) { return this.articleService.createArticle(dto, user.id);
}async create(@Playground("id") userId: string) { return userId;
}BuildFileUrl
import { BuildFileUrl } from "@chatbuddy-ai/decorators";@Get(":id")
@BuildFileUrl(["cover", "author.avatar", "items.*.thumbnail"])
async findOne(@Param("id", UUIDValidationPipe) id: string) { return this.articleService.findOneById(id);
}| 写法 | 介绍 |
|---|---|
"cover" | 普通字段 |
"author.avatar" | 嵌套字段 |
"items.*.thumbnail" | 数组通配字段 |
{ field: "images", isArray: true } | 数组字段 |
SkipTransform
import { SkipTransform } from "@chatbuddy-ai/decorators";@Get("raw")
@SkipTransform
async raw { return "plain text";
}错误封装
HttpErrorFactory
import { HttpErrorFactory } from "@chatbuddy-ai/errors";| 方法 | 介绍 |
|---|---|
badRequest(message) | 参数或业务状态错误 |
unauthorized(message) | 未登录或认证失败 |
forbidden(message) | 无权限 |
notFound(message) | 资源不存在 |
internal(message) | 服务内部错误 |
if (!article) { throw HttpErrorFactory.notFound("文章不存在");
} if (!category) { throw HttpErrorFactory.badRequest("栏目不存在");
}import { ApplicationError, HttpError, HttpStatus } from "@chatbuddy-ai/errors";数据库工具
TypeORM Module 和 Repository
import { InjectRepository, TypeOrmModule } from "@chatbuddy-ai/db/@nestjs/typeorm";
import { Repository } from "@chatbuddy-ai/db/typeorm";import { TypeOrmModule } from "@chatbuddy-ai/db/@nestjs/typeorm";
import { Module } from "@nestjs/common"; import { Article } from "../../db/entities/article.entity";
import { ArticleController } from "./controllers/console/article.controller";
import { ArticleWebController } from "./controllers/web/article.web.controller";
import { ArticleService } from "./services/article.service"; @Module({ imports: [TypeOrmModule.forFeature([Article])], controllers: [ArticleController, ArticleWebController], providers: [ArticleService], exports: [ArticleService],
})
export class ArticleModule {}constructor( @InjectRepository(Article) private readonly articleRepository: Repository<Article>,
) {}TypeORM 装饰器和操作符
import { Column, CreateDateColumn, In, Like, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn,
} from "@chatbuddy-ai/db/typeorm";| 类型 | 介绍 |
|---|---|
Column | 字段 |
PrimaryGeneratedColumn | 主键 |
CreateDateColumn | 创建时间 |
UpdateDateColumn | 更新时间 |
ManyToOne、OneToMany、JoinColumn | 关联 |
In、Like、ILike、Raw | 查询操作符 |
Repository | TypeORM Repository |
FindOptionsWhere | 查询条件类型 |
平台实体
import { User } from "@chatbuddy-ai/db/entities";import { User } from "@chatbuddy-ai/db/entities";
import { JoinColumn, ManyToOne } from "@chatbuddy-ai/db/typeorm"; @ManyToOne( => User, { nullable: true })
@JoinColumn({ name: "authorId" })
author?: User;插件 SDK
PublicUserService
import { PublicUserService } from "@chatbuddy-ai/extension-sdk";constructor(private readonly userService: PublicUserService) {} async attachAuthor(authorId: string) { const author = await this.userService.findUserById(authorId); if (!author) { throw HttpErrorFactory.notFound("作者不存在"); } return author;
}AiPublicModule 和 PublicAiModelService
import { AiPublicModule, PublicAiModelService } from "@chatbuddy-ai/extension-sdk";import { AiPublicModule } from "@chatbuddy-ai/extension-sdk";
import { Module } from "@nestjs/common"; @Module({ imports: [AiPublicModule], providers: [GenerateService], exports: [GenerateService],
})
export class GenerateModule {}import { PublicAiModelService } from "@chatbuddy-ai/extension-sdk"; constructor(private readonly aiModelService: PublicAiModelService) {}ExtensionBillingModule 和 ExtensionBillingService
import { ExtensionBillingModule, ExtensionBillingService } from "@chatbuddy-ai/extension-sdk";import { ExtensionBillingModule } from "@chatbuddy-ai/extension-sdk";
import { Module } from "@nestjs/common"; @Module({ imports: [ExtensionBillingModule], providers: [GenerateService], exports: [GenerateService],
})
export class GenerateModule {}import { ExtensionBillingService } from "@chatbuddy-ai/extension-sdk"; constructor(private readonly billingService: ExtensionBillingService) {}import type { ExtensionPowerDeductionOptions } from "@chatbuddy-ai/extension-sdk";工具函数
buildWhere
import { buildWhere } from "@chatbuddy-ai/utils";undefined条件,构造 TypeORM where。import { Like } from "@chatbuddy-ai/db/typeorm";
import { buildWhere } from "@chatbuddy-ai/utils"; const where = buildWhere<Article>({ title: query.title ? Like(`%${query.title}%`) : undefined, categoryId: query.categoryId, status: query.status,
}); return this.paginate(query, { where });类型转换工具
import { asArray, asBoolean, asDate, asDefined, asError, asJson, asNumber, asObject, asString,
} from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
asArray | 转数组 |
asString | 转字符串 |
asNumber | 转数字 |
asBoolean | 转布尔 |
asObject | 转对象 |
asDate | 转日期 |
asJson | 解析 JSON |
asDefined | 过滤空值 |
asError | 转 Error |
路径工具
import { joinPaths, joinRouterPaths, validatePath } from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
joinPaths | 拼接普通路径 |
joinRouterPaths | 拼接路由路径 |
validatePath | 校验路径片段 |
插件标识工具
import { parseExtensionIdentifier, parsePackageName } from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
parseExtensionIdentifier | 解析插件标识 |
parsePackageName | 解析包名 |
安全和版本工具
import { checkVersionCompatibility, decryptValue, maskSensitiveValue } from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
maskSensitiveValue | 脱敏字符串 |
decryptValue | 解密值 |
checkVersionCompatibility | 检查版本兼容 |
Promise 和流工具
import { createResolvablePromise, DelayedPromise, StreamUtils } from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
createResolvablePromise | 创建可外部 resolve/reject 的 Promise |
DelayedPromise | 延迟 Promise 工具 |
StreamUtils | 流处理工具 |
消息内容工具
import { extractFilesFromMessageContent, extractTextFromMessageContent } from "@chatbuddy-ai/utils";| 工具 | 介绍 |
|---|---|
extractTextFromMessageContent | 从消息内容中提取文本 |
extractFilesFromMessageContent | 从消息内容中提取文件 |
Nest 常用导入
import { Body, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { Injectable, Module } from "@nestjs/common";| 装饰器 | 场景 |
|---|---|
@Module | 声明模块 |
@Injectable | 声明 Service |
@Get | GET 接口 |
@Post | POST 接口 |
@Patch | PATCH 接口 |
@Delete | DELETE 接口 |
@Body | 求体 |
@Query | 查询参数 |
@Param | 路径参数 |
共 319 篇文档 · 内容同步自官方帮助中心