feat: 按模块拆分 Knife4j 接口文档

This commit is contained in:
sunlei 2026-06-02 20:27:47 +08:00
parent 7a93e49af6
commit 8ae097e44d
24 changed files with 125 additions and 30 deletions

View File

@ -17,7 +17,7 @@ import { AdminAuthService } from './admin-auth.service';
import { JwtAuthGuard } from './jwt-auth.guard';
import { WordpressService } from '@/wordpress/wordpress.service';
@ApiTags('admin-auth')
@ApiTags('Admin - 认证')
@Controller()
@UseGuards(JwtAuthGuard)
export class AdminAuthController {

View File

@ -60,7 +60,7 @@ class CompPageDto
}
@Controller('component')
@ApiTags('component')
@ApiTags('Admin - 组件管理')
@ApiExtraModels(PaginatedDto)
@UseGuards(JwtAuthGuard)
export class ComponentController {

View File

@ -14,7 +14,7 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { AdminDept } from './admin-dept.entity';
import { AdminDeptService } from './admin-dept.service';
@ApiTags('admin-dept')
@ApiTags('Admin - 部门管理')
@Controller('system/dept')
@UseGuards(JwtAuthGuard)
export class AdminDeptController {

View File

@ -35,7 +35,7 @@ const chartDictExample = [
},
];
@ApiTags('dict')
@ApiTags('Admin - 字典管理')
@Controller('dict')
@UseGuards(JwtAuthGuard)
export class DictController {

View File

@ -66,7 +66,7 @@ const DEMO_ROWS: DemoTableRow[] = Array.from({ length: 100 }, (_, index) => {
};
});
@ApiTags('admin-example')
@ApiTags('Admin - 示例')
@Controller()
@UseGuards(JwtAuthGuard)
export class AdminExampleController {

View File

@ -16,7 +16,7 @@ import { AdminUser } from '../user/admin-user.entity';
import { AdminMenu } from './admin-menu.entity';
import { AdminMenuService } from './admin-menu.service';
@ApiTags('admin-menu')
@ApiTags('Admin - 菜单管理')
@Controller()
@UseGuards(JwtAuthGuard)
export class AdminMenuController {

View File

@ -14,7 +14,7 @@ import { vbenPage, vbenSuccess } from '@/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { AdminRoleService } from './admin-role.service';
@ApiTags('admin-role')
@ApiTags('Admin - 角色管理')
@Controller('system/role')
@UseGuards(JwtAuthGuard)
export class AdminRoleController {

View File

@ -13,7 +13,7 @@ const TIMEZONE_OPTIONS = [
{ label: 'Asia/Seoul (GMT+9)', value: 'Asia/Seoul' },
];
@ApiTags('admin-timezone')
@ApiTags('Admin - 时区')
@Controller('timezone')
@UseGuards(JwtAuthGuard)
export class AdminTimezoneController {

View File

@ -5,7 +5,7 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { AdminUser } from './admin-user.entity';
import { AdminUserService } from './admin-user.service';
@ApiTags('admin-user')
@ApiTags('Admin - 用户管理')
@Controller('user')
@UseGuards(JwtAuthGuard)
export class AdminUserController {

View File

@ -1,7 +1,12 @@
import { Controller, Get, Redirect } from '@nestjs/common';
import { ApiMovedPermanentlyResponse, ApiOperation } from '@nestjs/swagger';
import {
ApiMovedPermanentlyResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { AppService } from './app.service';
@ApiTags('基础能力 - 根入口')
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

View File

@ -1,8 +1,56 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import type { OpenAPIObject } from '@nestjs/swagger';
import { urlencoded, json } from 'express';
import { knife4jSetup } from 'nestjs-knife4j-plus';
import type { Service } from 'nestjs-knife4j-plus';
type SwaggerPathMatcher = (path: string) => boolean;
interface SwaggerDocumentGroup {
matcher: SwaggerPathMatcher;
name: string;
path: string;
}
const adminSwaggerPathPrefixes = [
'/auth',
'/component',
'/dict',
'/menu',
'/system',
'/timezone',
'/user',
'/demo',
'/status',
'/table',
'/test',
'/upload',
];
const swaggerGroups: SwaggerDocumentGroup[] = [
{
matcher: (path) => matchPathPrefixes(path, adminSwaggerPathPrefixes),
name: 'Admin 后台管理',
path: 'api/admin',
},
{
matcher: (path) => path.startsWith('/qqbot'),
name: 'QQBot 机器人',
path: 'api/qqbot',
},
{
matcher: (path) => path.startsWith('/wordpress'),
name: 'WordPress 博客',
path: 'api/wordpress',
},
{
matcher: (path) => path === '/' || path.startsWith('/minio'),
name: '基础能力',
path: 'api/basic',
},
];
async function bootstrap() {
const app = await NestFactory.create(AppModule);
@ -15,15 +63,57 @@ async function bootstrap() {
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
const services: Service[] = [
{
name: '全量接口',
url: '/api-json',
},
];
swaggerGroups.forEach((group) => {
const groupDocument = filterSwaggerDocument(document, group.matcher);
SwaggerModule.setup(group.path, app, groupDocument);
services.push({
name: group.name,
url: `/${group.path}-json`,
});
});
// 启用knife4j增强关键代码
knife4jSetup(app, [
{
name: '1.0', // 文档版本名称
url: `/api-json`, // Swagger openapi JSON地址
},
]);
knife4jSetup(app, services);
await app.listen(48085);
}
function filterSwaggerDocument(
document: OpenAPIObject,
matcher: SwaggerPathMatcher,
): OpenAPIObject {
const paths = Object.fromEntries(
Object.entries(document.paths).filter(([path]) => matcher(path)),
) as OpenAPIObject['paths'];
const usedTags = new Set<string>();
Object.values(paths).forEach((pathItem) => {
Object.values(pathItem || {}).forEach((operation) => {
const tags = (operation as any)?.tags;
if (Array.isArray(tags)) {
tags.forEach((tag) => usedTags.add(tag));
}
});
});
return {
...document,
paths,
tags: document.tags?.filter((tag) => usedTags.has(tag.name)),
};
}
function matchPathPrefixes(path: string, prefixes: string[]) {
return prefixes.some(
(prefix) => path === prefix || path.startsWith(`${prefix}/`),
);
}
bootstrap();

View File

@ -50,7 +50,7 @@ const PROXY_RESOURCE_EXTENSION_RE =
/\.(avif|bmp|css|eot|gif|ico|jpe?g|otf|png|svg|ttf|webp|woff2?)(?:[?#].*)?$/i;
@Controller('minio')
@ApiTags('minio')
@ApiTags('基础能力 - MinIO')
@UseGuards(JwtAuthGuard)
export class MinioClientController {
constructor(

View File

@ -21,7 +21,7 @@ import { QqbotAccountService } from './qqbot-account.service';
import { QqbotNapcatLoginService } from './qqbot-napcat-login.service';
import { QqbotReverseWsService } from '../connection/qqbot-reverse-ws.service';
@ApiTags('qqbot-account')
@ApiTags('QQBot - 账号连接')
@Controller('qqbot/account')
@UseGuards(JwtAuthGuard)
export class QqbotAccountController {

View File

@ -21,7 +21,7 @@ import { QqbotCommandEngineService } from './qqbot-command-engine.service';
import { QqbotCommandService } from './qqbot-command.service';
import { normalizeBoolean } from '../qqbot.utils';
@ApiTags('qqbot-command')
@ApiTags('QQBot - 在线命令')
@Controller('qqbot/command')
@UseGuards(JwtAuthGuard)
export class QqbotCommandController {

View File

@ -4,7 +4,7 @@ import { JwtAuthGuard } from '@/admin/auth/jwt-auth.guard';
import { vbenSuccess } from '@/common';
import { QqbotDashboardService } from './qqbot-dashboard.service';
@ApiTags('qqbot-dashboard')
@ApiTags('QQBot - 工作台')
@Controller('qqbot/dashboard')
@UseGuards(JwtAuthGuard)
export class QqbotDashboardController {

View File

@ -8,7 +8,7 @@ import {
} from './qqbot-message.dto';
import { QqbotMessageService } from './qqbot-message.service';
@ApiTags('qqbot-message')
@ApiTags('QQBot - 会话与消息')
@Controller('qqbot')
@UseGuards(JwtAuthGuard)
export class QqbotMessageController {

View File

@ -19,7 +19,7 @@ import {
} from './qqbot-permission.dto';
import { QqbotPermissionService } from './qqbot-permission.service';
@ApiTags('qqbot-permission')
@ApiTags('QQBot - 权限名单')
@Controller('qqbot/permission')
@UseGuards(JwtAuthGuard)
export class QqbotPermissionController {

View File

@ -14,7 +14,7 @@ import { QqbotEventPluginRegistryService } from './qqbot-event-plugin-registry.s
import { QqbotPluginRegistryService } from './qqbot-plugin-registry.service';
import type { QqbotPluginTriggerMode } from './qqbot-plugin.types';
@ApiTags('qqbot-plugin')
@ApiTags('QQBot - 插件能力')
@Controller('qqbot/plugin')
@UseGuards(JwtAuthGuard)
export class QqbotPluginController {

View File

@ -19,7 +19,7 @@ import {
import { QqbotRuleService } from './qqbot-rule.service';
import { normalizeBoolean } from '../qqbot.utils';
@ApiTags('qqbot-rule')
@ApiTags('QQBot - 自动回复规则')
@Controller('qqbot/rule')
@UseGuards(JwtAuthGuard)
export class QqbotRuleController {

View File

@ -18,7 +18,7 @@ import {
} from './qqbot-send.dto';
import { QqbotSendService } from './qqbot-send.service';
@ApiTags('qqbot-send')
@ApiTags('QQBot - 发送日志')
@Controller('qqbot/send')
@UseGuards(JwtAuthGuard)
export class QqbotSendController {

View File

@ -21,7 +21,7 @@ import {
} from './wordpress.dto';
import { WordpressService } from './wordpress.service';
@ApiTags('wordpress-article')
@ApiTags('WordPress - 文章')
@ApiHeader({
name: 'X-WordPress-Authorization',
required: false,

View File

@ -12,7 +12,7 @@ import { JwtAuthGuard } from '@/admin/auth/jwt-auth.guard';
import { Public, vbenSuccess } from '@/common';
import { WordpressService } from './wordpress.service';
@ApiTags('wordpress-auth')
@ApiTags('WordPress - 认证')
@ApiHeader({
name: 'X-WordPress-Authorization',
required: false,

View File

@ -21,7 +21,7 @@ import {
} from './wordpress.dto';
import { WordpressService } from './wordpress.service';
@ApiTags('wordpress-category')
@ApiTags('WordPress - 分类')
@ApiHeader({
name: 'X-WordPress-Authorization',
required: false,

View File

@ -21,7 +21,7 @@ import {
} from './wordpress.dto';
import { WordpressService } from './wordpress.service';
@ApiTags('wordpress-tag')
@ApiTags('WordPress - 标签')
@ApiHeader({
name: 'X-WordPress-Authorization',
required: false,