# Swagger API 文档配置指南 ## 1. Swagger 基础配置 ### 1.1 安装依赖 ```bash pnpm add @nestjs/swagger swagger-ui-express pnpm add -D @types/swagger-ui-express ``` ### 1.2 基础配置文件 ```typescript // src/config/swagger.config.ts import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { INestApplication } from '@nestjs/common'; export function setupSwagger(app: INestApplication): void { const config = new DocumentBuilder() .setTitle('多平台小程序统一后台API') .setDescription(` ## 功能特性 - 🔐 统一用户认证 (微信/支付宝/百度/字节跳动等) - 🔄 平台数据同步 (数据库异步处理) - 🧩 可扩展架构 (支持后续添加支付、推送等功能) - 📊 灵活数据存储 (MySQL + JSON) ## 认证方式 使用JWT Bearer Token进行API认证 ## AI模板系统 - 🎨 动态模板管理 (数据库配置 + 代码执行) - 🚀 N8n工作流集成 (图片/视频生成) - 📊 使用统计分析 (性能监控 + 用户行为) - 🎛️ 运营管理后台 (A/B测试 + 个性化推荐) ## 响应格式 所有API响应都遵循统一格式: \`\`\`json { "code": 200, "message": "success", "data": {}, "timestamp": 1703001000000, "traceId": "trace-uuid" } \`\`\` `) .setVersion('1.0.0') .setContact('开发团队', 'https://example.com', 'dev@example.com') .setLicense('MIT', 'https://opensource.org/licenses/MIT') .addBearerAuth( { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', name: 'JWT', description: 'Enter JWT token', in: 'header', }, 'JWT-auth', ) .addTag('🔐 用户管理', '用户注册、登录、信息管理') .addTag('🔄 平台适配', '各平台特定接口和数据同步') .addTag('🧩 扩展服务', '预留的扩展功能接口') .addTag('📊 数据统计', '业务数据统计和分析') .addServer('http://localhost:3000', '开发环境') .addServer('https://api-dev.example.com', '测试环境') .addServer('https://api.example.com', '生产环境') .build(); const document = SwaggerModule.createDocument(app, config, { operationIdFactory: (controllerKey: string, methodKey: string) => methodKey, }); SwaggerModule.setup('api/docs', app, document, { swaggerOptions: { persistAuthorization: true, tagsSorter: 'alpha', operationsSorter: 'alpha', docExpansion: 'none', filter: true, showRequestDuration: true, }, customSiteTitle: '多平台API文档', customfavIcon: '/favicon.ico', customJs: [ 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-bundle.min.js', ], customCssUrl: [ 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.min.css', ], }); } ``` ## 2. 通用DTO定义 ### 2.1 统一响应格式 ```typescript // src/dto/common-response.dto.ts import { ApiProperty } from '@nestjs/swagger'; export class CommonResponseDto { @ApiProperty({ description: '响应状态码', example: 200 }) code: number; @ApiProperty({ description: '响应消息', example: 'success' }) message: string; @ApiProperty({ description: '响应数据' }) data: T; @ApiProperty({ description: '时间戳', example: 1703001000000 }) timestamp: number; @ApiProperty({ description: '追踪ID', example: 'trace-uuid-123' }) traceId: string; } export class PaginationDto { @ApiProperty({ description: '页码', example: 1, minimum: 1 }) page: number; @ApiProperty({ description: '每页数量', example: 10, minimum: 1, maximum: 100 }) limit: number; } export class PaginationResponseDto { @ApiProperty({ description: '数据列表' }) items: T[]; @ApiProperty({ description: '总数量', example: 100 }) total: number; @ApiProperty({ description: '当前页码', example: 1 }) page: number; @ApiProperty({ description: '每页数量', example: 10 }) limit: number; @ApiProperty({ description: '总页数', example: 10 }) totalPages: number; } ``` ### 2.2 平台枚举定义 ```typescript // src/dto/platform.dto.ts import { ApiProperty } from '@nestjs/swagger'; export enum PlatformType { WECHAT = 'wechat', ALIPAY = 'alipay', BAIDU = 'baidu', BYTEDANCE = 'bytedance', JD = 'jd', QQ = 'qq', FEISHU = 'feishu', KUAISHOU = 'kuaishou', H5 = 'h5', RN = 'rn' } export const PlatformDescriptions = { [PlatformType.WECHAT]: '微信小程序', [PlatformType.ALIPAY]: '支付宝小程序', [PlatformType.BAIDU]: '百度智能小程序', [PlatformType.BYTEDANCE]: '字节跳动小程序', [PlatformType.JD]: '京东小程序', [PlatformType.QQ]: 'QQ小程序', [PlatformType.FEISHU]: '飞书小程序', [PlatformType.KUAISHOU]: '快手小程序', [PlatformType.H5]: 'H5应用', [PlatformType.RN]: 'React Native应用', }; ``` ## 3. 用户管理API文档 ### 3.1 用户登录DTO ```typescript // src/dto/user.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsEnum, IsOptional, IsObject } from 'class-validator'; import { PlatformType } from './platform.dto'; export class UserLoginDto { @ApiProperty({ description: '平台类型', enum: PlatformType, example: PlatformType.WECHAT, enumName: 'PlatformType', }) @IsEnum(PlatformType) platform: PlatformType; @ApiProperty({ description: '平台授权码/临时登录凭证', example: '081234567890abcdef', minLength: 1, maxLength: 200, }) @IsString() code: string; @ApiProperty({ description: '加密用户数据 (微信小程序专用)', required: false, example: 'encrypted_user_data_string', }) @IsOptional() @IsString() encryptedData?: string; @ApiProperty({ description: '加密向量 (微信小程序专用)', required: false, example: 'iv_string', }) @IsOptional() @IsString() iv?: string; @ApiProperty({ description: '额外的平台特定数据', required: false, type: 'object', example: { sessionKey: 'session_key_value' }, }) @IsOptional() @IsObject() extra?: Record; } export class UserInfoDto { @ApiProperty({ description: '用户ID', example: 'user-uuid-123' }) id: string; @ApiProperty({ description: '统一用户ID', example: 'unified-user-123' }) unifiedUserId: string; @ApiProperty({ description: '用户昵称', example: '张三' }) nickname: string; @ApiProperty({ description: '头像URL', example: 'https://example.com/avatar.jpg' }) avatarUrl: string; @ApiProperty({ description: '手机号', example: '13800138000', required: false }) phone?: string; @ApiProperty({ description: '邮箱', example: 'user@example.com', required: false }) email?: string; @ApiProperty({ description: '用户状态', example: 1 }) status: number; @ApiProperty({ description: '创建时间', example: '2023-12-01T10:00:00Z' }) createdAt: Date; @ApiProperty({ description: '更新时间', example: '2023-12-01T10:00:00Z' }) updatedAt: Date; } export class UserLoginResponseDto { @ApiProperty({ description: 'JWT访问令牌', example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', }) token: string; @ApiProperty({ description: '刷新令牌', example: 'refresh_token_string_here', }) refreshToken: string; @ApiProperty({ description: '用户信息', type: UserInfoDto, }) userInfo: UserInfoDto; @ApiProperty({ description: '平台特定数据', type: 'object', required: false, example: { openid: 'wx_openid_123' }, }) platformSpecific?: Record; } ``` ## 4. 扩展服务API文档 ### 4.1 扩展数据相关DTO ```typescript // src/dto/extension.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsEnum, IsOptional, IsObject } from 'class-validator'; import { PlatformType } from './platform.dto'; export class CreateExtensionDataDto { @ApiProperty({ description: '用户ID', example: 'user-uuid-123', }) @IsString() userId: string; @ApiProperty({ description: '平台类型', enum: PlatformType, example: PlatformType.WECHAT, }) @IsEnum(PlatformType) platform: PlatformType; @ApiProperty({ description: '数据类型', example: 'custom', maxLength: 50, }) @IsString() dataType: string; @ApiProperty({ description: '外部引用ID', required: false, example: 'ref-123456', maxLength: 100, }) @IsOptional() @IsString() referenceId?: string; @ApiProperty({ description: '扩展数据内容', type: 'object', example: { customField1: 'value1', customField2: 'value2', settings: { enabled: true } }, }) @IsObject() data: Record; @ApiProperty({ description: '元数据', required: false, type: 'object', example: { source: 'api', version: '1.0' }, }) @IsOptional() @IsObject() metadata?: Record; } export class ExtensionDataResponseDto { @ApiProperty({ description: '扩展数据ID', example: 'ext-uuid-123' }) id: string; @ApiProperty({ description: '用户ID', example: 'user-uuid-123' }) userId: string; @ApiProperty({ description: '平台类型', enum: PlatformType, example: PlatformType.WECHAT, }) platform: PlatformType; @ApiProperty({ description: '数据类型', example: 'custom' }) dataType: string; @ApiProperty({ description: '外部引用ID', example: 'ref-123456' }) referenceId: string; @ApiProperty({ description: '扩展数据内容', type: 'object', }) data: Record; @ApiProperty({ description: '元数据', type: 'object', }) metadata: Record; @ApiProperty({ description: '状态', example: 'active' }) status: string; @ApiProperty({ description: '创建时间', example: '2023-12-01T10:00:00Z' }) createdAt: Date; @ApiProperty({ description: '更新时间', example: '2023-12-01T10:00:00Z' }) updatedAt: Date; } ``` ## 5. 错误响应文档 ### 5.1 通用错误响应 ```typescript // src/dto/error-response.dto.ts import { ApiProperty } from '@nestjs/swagger'; export class ErrorResponseDto { @ApiProperty({ description: '错误状态码', example: 400 }) code: number; @ApiProperty({ description: '错误消息', example: '请求参数错误' }) message: string; @ApiProperty({ description: '错误数据', example: null }) data: null; @ApiProperty({ description: '时间戳', example: 1703001000000 }) timestamp: number; @ApiProperty({ description: '追踪ID', example: 'trace-uuid-123' }) traceId: string; @ApiProperty({ description: '详细错误信息', required: false, example: ['字段验证失败'], }) details?: string[]; } // 常用错误响应示例 export const CommonErrorResponses = { BadRequest: { status: 400, description: '请求参数错误', type: ErrorResponseDto, }, Unauthorized: { status: 401, description: '未授权访问', type: ErrorResponseDto, }, Forbidden: { status: 403, description: '禁止访问', type: ErrorResponseDto, }, NotFound: { status: 404, description: '资源不存在', type: ErrorResponseDto, }, InternalServerError: { status: 500, description: '服务器内部错误', type: ErrorResponseDto, }, }; ``` ## 6. 装饰器使用示例 ### 6.1 Controller装饰器 ```typescript // src/decorators/api-common-responses.decorator.ts import { applyDecorators } from '@nestjs/common'; import { ApiResponse } from '@nestjs/swagger'; import { CommonErrorResponses } from '../dto/error-response.dto'; export function ApiCommonResponses() { return applyDecorators( ApiResponse(CommonErrorResponses.BadRequest), ApiResponse(CommonErrorResponses.Unauthorized), ApiResponse(CommonErrorResponses.InternalServerError), ); } export function ApiAuthResponses() { return applyDecorators( ApiResponse(CommonErrorResponses.Unauthorized), ApiResponse(CommonErrorResponses.Forbidden), ); } ``` ### 6.2 使用示例 ```typescript // src/controllers/user.controller.ts import { Controller, Post, Body, Get, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, } from '@nestjs/swagger'; import { ApiCommonResponses, ApiAuthResponses } from '../decorators/api-common-responses.decorator'; @ApiTags('🔐 用户管理') @Controller('users') @ApiCommonResponses() export class UserController { @Post('login') @ApiOperation({ summary: '用户登录', description: '支持多平台用户登录,返回JWT令牌和用户信息', }) @ApiResponse({ status: 200, description: '登录成功', type: UserLoginResponseDto, }) async login(@Body() loginDto: UserLoginDto) { return this.userService.login(loginDto); } @Get('profile') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiAuthResponses() @ApiOperation({ summary: '获取用户信息', description: '获取当前登录用户的详细信息', }) @ApiResponse({ status: 200, description: '获取成功', type: UserInfoDto, }) async getProfile(@CurrentUser() user: any) { return this.userService.getProfile(user.id); } } ``` ## 7. AI模板管理 API ### 7.1 模板数据传输对象 ```typescript // src/dto/template.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsNumber, IsEnum, IsOptional, IsArray, IsBoolean } from 'class-validator'; export enum TemplateType { IMAGE = 'image', VIDEO = 'video' } export class TemplateListDto { @ApiProperty({ description: '模板ID', example: 1 }) id: number; @ApiProperty({ description: '模板代码', example: 'character_figurine_v1' }) code: string; @ApiProperty({ description: '模板名称', example: '人物手办' }) name: string; @ApiProperty({ description: '模板描述', example: '将人物照片制作成精细的角色手办模型' }) description: string; @ApiProperty({ enum: TemplateType, description: '模板类型', example: 'video' }) templateType: TemplateType; @ApiProperty({ description: '积分消耗', example: 28 }) creditCost: number; @ApiProperty({ description: '版本号', example: '1.0.0' }) version: string; @ApiProperty({ description: '输入示例URL', required: false }) inputExampleUrl?: string; @ApiProperty({ description: '输出示例URL', required: false }) outputExampleUrl?: string; @ApiProperty({ type: [String], description: '标签数组', example: ['人物', '手办', '模型'] }) tags: string[]; @ApiProperty({ description: '是否启用', example: true }) isActive: boolean; @ApiProperty({ description: '创建时间', example: '2024-01-01T00:00:00Z' }) createdAt: Date; } export class TemplateExecuteDto { @ApiProperty({ description: '输入图片URL', example: 'https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg' }) @IsString() imageUrl: string; } export class TemplateExecuteResponseDto { @ApiProperty({ description: '执行状态', example: true }) success: boolean; @ApiProperty({ description: '生成结果URL(图片模板)', required: false }) imageUrl?: string; @ApiProperty({ description: '生成结果URL(视频模板)', required: false }) videoUrl?: string; @ApiProperty({ description: '缩略图URL', required: false }) thumbnailUrl?: string; @ApiProperty({ description: '任务ID', example: 'req_1704067200_abc123' }) taskId: string; @ApiProperty({ description: '执行耗时(毫秒)', example: 5000 }) executionTime: number; @ApiProperty({ description: '消耗积分', example: 28 }) creditCost: number; } export class TemplateStatsDto { @ApiProperty({ description: '总模板数', example: 8 }) total: number; @ApiProperty({ description: '启用模板数', example: 8 }) enabled: number; @ApiProperty({ description: '图片模板数', example: 3 }) imageTemplates: number; @ApiProperty({ description: '视频模板数', example: 5 }) videoTemplates: number; } ``` ### 7.2 模板管理控制器 ```typescript // src/controllers/template.controller.ts import { Controller, Get, Post, Param, Body, Query, UseGuards, ParseIntPipe } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiParam, ApiQuery } from '@nestjs/swagger'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { CurrentUser } from '../decorators/current-user.decorator'; import { ApiCommonResponses, ApiAuthResponses } from '../decorators/api-common-responses.decorator'; import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service'; import { TemplateListDto, TemplateExecuteDto, TemplateExecuteResponseDto, TemplateStatsDto } from '../dto/template.dto'; @ApiTags('🎨 AI模板系统') @Controller('templates') @ApiCommonResponses() export class TemplateController { constructor( private readonly templateFactory: N8nTemplateFactoryService, ) {} @Get() @ApiOperation({ summary: '获取所有模板列表', description: '获取所有可用的AI生成模板,支持按类型筛选' }) @ApiQuery({ name: 'type', required: false, enum: ['image', 'video'], description: '模板类型筛选' }) @ApiResponse({ status: 200, description: '获取成功', type: [TemplateListDto] }) async getTemplates(@Query('type') type?: 'image' | 'video') { if (type === 'image') { return this.templateFactory.getTemplatesByType('image'); } else if (type === 'video') { return this.templateFactory.getTemplatesByType('video'); } return this.templateFactory.getAllTemplates(); } @Get('stats') @ApiOperation({ summary: '获取模板统计信息', description: '获取模板总数、类型分布等统计信息' }) @ApiResponse({ status: 200, description: '获取成功', type: TemplateStatsDto }) async getTemplateStats() { const allTemplates = await this.templateFactory.getAllTemplates(); const imageTemplates = allTemplates.filter(t => t.templateType === 'image'); const videoTemplates = allTemplates.filter(t => t.templateType === 'video'); return { total: allTemplates.length, enabled: allTemplates.filter(t => t.isActive).length, imageTemplates: imageTemplates.length, videoTemplates: videoTemplates.length, }; } @Get(':templateId') @ApiOperation({ summary: '获取模板详情', description: '根据模板ID获取详细信息' }) @ApiParam({ name: 'templateId', description: '模板ID', example: 1 }) @ApiResponse({ status: 200, description: '获取成功', type: TemplateListDto }) async getTemplateById(@Param('templateId', ParseIntPipe) templateId: number) { return this.templateFactory.getTemplateById(templateId); } @Get('code/:templateCode') @ApiOperation({ summary: '通过代码获取模板详情', description: '根据模板代码获取详细信息' }) @ApiParam({ name: 'templateCode', description: '模板代码', example: 'character_figurine_v1' }) @ApiResponse({ status: 200, description: '获取成功', type: TemplateListDto }) async getTemplateByCode(@Param('templateCode') templateCode: string) { return this.templateFactory.getTemplateByCode(templateCode); } @Post(':templateId/execute') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiAuthResponses() @ApiOperation({ summary: '执行模板生成', description: '根据模板ID执行AI生成任务,支持图片和视频生成' }) @ApiParam({ name: 'templateId', description: '模板ID', example: 1 }) @ApiResponse({ status: 200, description: '执行成功', type: TemplateExecuteResponseDto }) @ApiResponse({ status: 400, description: '参数错误', schema: { example: { code: 400, message: '输入图片URL不能为空', data: null } } }) @ApiResponse({ status: 402, description: '积分不足', schema: { example: { code: 402, message: '积分不足,当前余额: 10,需要: 28', data: null } } }) async executeTemplate( @Param('templateId', ParseIntPipe) templateId: number, @Body() executeDto: TemplateExecuteDto, @CurrentUser() user: any ) { const startTime = Date.now(); // 获取模板配置 const templateConfig = await this.templateFactory.getTemplateById(templateId); // 创建动态模板实例并执行 let template; if (templateConfig.templateType === 'image') { template = await this.templateFactory.createImageTemplate(templateId); } else { template = await this.templateFactory.createVideoTemplate(templateId); } const result = await template.execute(executeDto.imageUrl); const executionTime = Date.now() - startTime; return { success: true, imageUrl: templateConfig.templateType === 'image' ? result : undefined, videoUrl: templateConfig.templateType === 'video' ? result : undefined, taskId: `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`, executionTime, creditCost: templateConfig.creditCost }; } @Post('code/:templateCode/execute') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiAuthResponses() @ApiOperation({ summary: '通过代码执行模板', description: '根据模板代码执行AI生成任务,支持图片和视频生成' }) @ApiParam({ name: 'templateCode', description: '模板代码', example: 'character_figurine_v1' }) @ApiResponse({ status: 200, description: '执行成功', type: TemplateExecuteResponseDto }) async executeTemplateByCode( @Param('templateCode') templateCode: string, @Body() executeDto: TemplateExecuteDto, @CurrentUser() user: any ) { const startTime = Date.now(); // 创建动态模板实例并执行 const template = await this.templateFactory.createTemplateByCode(templateCode); const result = await template.execute(executeDto.imageUrl); const executionTime = Date.now() - startTime; return { success: true, imageUrl: template.templateType === 'image' ? result : undefined, videoUrl: template.templateType === 'video' ? result : undefined, taskId: `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`, executionTime, creditCost: template.creditCost }; } } ``` ### 7.3 API使用示例 #### 获取所有模板 ```bash curl -X GET "http://localhost:3000/api/templates" \ -H "accept: application/json" ``` 响应示例: ```json { "code": 200, "message": "success", "data": [ { "id": 1, "code": "character_figurine_v1", "name": "人物手办", "description": "将人物照片制作成精细的角色手办模型,展示在收藏家房间中,并生成抚摸手办的视频", "templateType": "video", "creditCost": 28, "version": "1.0.0", "inputExampleUrl": "https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg", "outputExampleUrl": "https://file.302.ai/gpt/imgs/20250828/2283106b31faf2066e1a72d955f65bca.jpg", "tags": ["人物", "手办", "模型", "收藏", "PVC", "角色模型", "视频生成"], "isActive": true, "createdAt": "2024-01-01T00:00:00Z" } ] } ``` #### 执行模板生成 ```bash curl -X POST "http://localhost:3000/api/templates/1/execute" \ -H "accept: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "imageUrl": "https://cdn.roasmax.cn/upload/3d590851eb584e92aa415a964e93260e.jpg" }' ``` 响应示例: ```json { "code": 200, "message": "success", "data": { "success": true, "videoUrl": "https://n8n.bowongai.com/files/generated_video_abc123.mp4", "taskId": "req_1704067200_abc123", "executionTime": 5000, "creditCost": 28 } } ``` ### 7.4 错误处理 ```typescript // 常见错误响应 export const TemplateApiErrors = { TEMPLATE_NOT_FOUND: { code: 404, message: '模板不存在', data: null }, INSUFFICIENT_CREDITS: { code: 402, message: '积分不足', data: { required: 28, current: 10 } }, INVALID_IMAGE_URL: { code: 400, message: '无效的图片URL', data: null }, TEMPLATE_EXECUTION_FAILED: { code: 500, message: 'AI生成失败,请稍后重试', data: null }, TEMPLATE_DISABLED: { code: 403, message: '模板已禁用', data: null } }; ``` ## 8. 环境配置 ### 8.1 开发环境配置 ```typescript // src/main.ts import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { AppModule } from './app.module'; import { setupSwagger } from './config/swagger.config'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 全局验证管道 app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, })); // API前缀 app.setGlobalPrefix('api/v1'); // CORS配置 app.enableCors({ origin: process.env.NODE_ENV === 'production' ? ['https://example.com'] : true, credentials: true, }); // 仅在非生产环境启用Swagger if (process.env.NODE_ENV !== 'production') { setupSwagger(app); } const port = process.env.PORT || 3000; await app.listen(port); console.log('🚀 应用启动成功!'); console.log(`📖 API文档地址: http://localhost:${port}/api/docs`); } bootstrap(); ``` ## 9. 访问API文档 启动应用后,访问以下地址查看API文档: - **开发环境**: http://localhost:3000/api/docs - **JSON格式**: http://localhost:3000/api/docs-json - **YAML格式**: http://localhost:3000/api/docs-yaml API文档包含: - 📋 完整的接口列表和参数说明 - 🔐 JWT认证测试功能 - 📝 请求/响应示例 - 🧪 在线接口测试功能 - 📊 数据模型定义 - 🎨 AI模板系统接口 (支持图片/视频生成) - 💾 动态模板配置管理 - 📈 模板使用统计和监控