- 统一 unified-user.controller.ts 的响应格式 - 统一 template.controller.ts 的响应格式 - 统一 content-moderation.controller.ts 的响应格式 - 所有响应现在包含 code、message、data、timestamp、traceId 字段 - 提高响应格式一致性和可追踪性
843 lines
24 KiB
TypeScript
843 lines
24 KiB
TypeScript
import {
|
||
Controller,
|
||
Get,
|
||
Post,
|
||
Put,
|
||
Delete,
|
||
Param,
|
||
Body,
|
||
ParseIntPipe,
|
||
Query,
|
||
HttpException,
|
||
HttpStatus,
|
||
UseGuards,
|
||
Request,
|
||
} from '@nestjs/common';
|
||
import {
|
||
ApiTags,
|
||
ApiOperation,
|
||
ApiResponse as SwaggerApiResponse,
|
||
ApiParam,
|
||
ApiQuery,
|
||
ApiBody,
|
||
} from '@nestjs/swagger';
|
||
import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service';
|
||
import {
|
||
N8nTemplateEntity,
|
||
TemplateType,
|
||
} from '../entities/n8n-template.entity';
|
||
import {
|
||
CreateTemplateDto,
|
||
UpdateTemplateDto,
|
||
TemplateExecuteDto,
|
||
TemplateExecuteResponseDto,
|
||
TemplateListDto,
|
||
BatchExecuteDto,
|
||
} from '../dto/template.dto';
|
||
import {
|
||
TemplateExecutionEntity,
|
||
ExecutionStatus,
|
||
ExecutionType,
|
||
} from '../entities/template-execution.entity';
|
||
import { InjectRepository } from '@nestjs/typeorm';
|
||
import { Repository } from 'typeorm';
|
||
import { ApiCommonResponses } from '../decorators/api-common-responses.decorator';
|
||
import { PlatformAuthGuard } from 'src/platform/guards/platform-auth.guard';
|
||
import { ResponseUtil, ApiResponse } from '../utils/response.util';
|
||
import { UnifiedContentService } from '../content-moderation/services/unified-content.service';
|
||
import { AuditConclusion } from '../content-moderation/interfaces/content-moderation.interface';
|
||
|
||
@ApiTags('AI模板系统')
|
||
@Controller('templates')
|
||
@ApiCommonResponses()
|
||
export class TemplateController {
|
||
constructor(
|
||
private readonly templateFactory: N8nTemplateFactoryService,
|
||
@InjectRepository(N8nTemplateEntity)
|
||
private readonly templateRepository: Repository<N8nTemplateEntity>,
|
||
@InjectRepository(TemplateExecutionEntity)
|
||
private readonly executionRepository: Repository<TemplateExecutionEntity>,
|
||
private readonly unifiedContentService: UnifiedContentService,
|
||
) {}
|
||
|
||
@Post(':templateId/execute')
|
||
@ApiOperation({
|
||
summary: '执行模板生成',
|
||
description: '根据模板ID执行AI生成任务,支持图片和视频生成',
|
||
})
|
||
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
|
||
@ApiBody({ type: TemplateExecuteDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '执行成功',
|
||
type: TemplateExecuteResponseDto,
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 400,
|
||
description: '参数错误',
|
||
schema: {
|
||
example: {
|
||
code: 400,
|
||
message: 'imageUrl is required',
|
||
data: null,
|
||
},
|
||
},
|
||
})
|
||
async executeTemplateById(
|
||
@Param('templateId', ParseIntPipe) templateId: number,
|
||
@Body() body: { imageUrl: string },
|
||
) {
|
||
try {
|
||
const { imageUrl } = body;
|
||
|
||
if (!imageUrl) {
|
||
throw new HttpException('imageUrl is required', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
// 通过工厂服务创建模板实例并执行
|
||
const template = await this.templateFactory.createTemplate(templateId);
|
||
const result = await template.execute(imageUrl);
|
||
|
||
return ResponseUtil.success({
|
||
templateId,
|
||
templateCode: template.code,
|
||
templateName: template.name,
|
||
inputUrl: imageUrl,
|
||
outputUrl: result,
|
||
creditCost: template.creditCost,
|
||
}, '执行成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Template execution failed',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Post('code/:code/execute')
|
||
@UseGuards(PlatformAuthGuard)
|
||
@ApiOperation({
|
||
summary: '通过代码执行模板',
|
||
description: '根据模板代码执行AI生成任务,支持图片和视频生成。执行前会进行图片内容审核。',
|
||
})
|
||
@ApiParam({
|
||
name: 'code',
|
||
description: '模板代码',
|
||
example: 'character_figurine_v1',
|
||
})
|
||
@ApiBody({ type: TemplateExecuteDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '执行成功',
|
||
type: TemplateExecuteResponseDto,
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 403,
|
||
description: '图片审核未通过',
|
||
schema: {
|
||
example: {
|
||
code: 403,
|
||
message: '图片审核未通过: 包含不当内容',
|
||
data: null,
|
||
},
|
||
},
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 429,
|
||
description: '任务数量限制',
|
||
schema: {
|
||
example: {
|
||
code: 429,
|
||
message:
|
||
'当前账号有3个任务正在进行中,且距开始时间不足5分钟,请稍后再试',
|
||
data: null,
|
||
},
|
||
},
|
||
})
|
||
async executeTemplateByCode(
|
||
@Param('code') code: string,
|
||
@Body() body: { imageUrl: string },
|
||
@Request() req,
|
||
): Promise<ApiResponse<number>> {
|
||
try {
|
||
const { imageUrl } = body;
|
||
|
||
if (!imageUrl) {
|
||
throw new HttpException('imageUrl is required', HttpStatus.BAD_REQUEST);
|
||
}
|
||
|
||
const userId = req.user.userId;
|
||
|
||
// 检查用户当前的任务限制
|
||
await this.checkUserTaskLimit(userId);
|
||
|
||
// 首先获取模板配置以确定模板类型
|
||
const templateConfig = await this.templateFactory.getTemplateByCode(code);
|
||
if (!templateConfig) {
|
||
throw new HttpException('Template not found', HttpStatus.NOT_FOUND);
|
||
}
|
||
|
||
const platform = req.user.platform;
|
||
|
||
// 1. 先进行图片内容审核
|
||
const auditResult = await this.unifiedContentService.auditImage(
|
||
platform,
|
||
{
|
||
imageUrl,
|
||
userId,
|
||
businessType: 'template_execution',
|
||
extraData: {
|
||
templateId: templateConfig.id,
|
||
templateCode: code,
|
||
},
|
||
}
|
||
);
|
||
|
||
// 2. 检查审核结果
|
||
if (auditResult.conclusion !== AuditConclusion.PASS) {
|
||
const failureReason = auditResult.details.length > 0
|
||
? auditResult.details.map(d => d.description).join(', ')
|
||
: '包含不当内容';
|
||
|
||
throw new HttpException(
|
||
`图片审核未通过: ${failureReason}`,
|
||
HttpStatus.FORBIDDEN,
|
||
);
|
||
}
|
||
|
||
// 3. 审核通过,执行模板
|
||
const template = await this.templateFactory.createTemplateByCode(code);
|
||
const taskId = await template.execute(imageUrl);
|
||
|
||
// 4. 将任务保存到 TemplateExecutionEntity
|
||
const execution = this.executionRepository.create({
|
||
templateId: templateConfig.id,
|
||
userId,
|
||
platform: req.user.platform,
|
||
type:
|
||
templateConfig.templateType === TemplateType.VIDEO
|
||
? ExecutionType.VIDEO
|
||
: ExecutionType.IMAGE,
|
||
prompt: '', // 可以从请求参数中获取,如果有的话
|
||
inputImageUrl: imageUrl,
|
||
taskId: taskId, // 保存外部系统返回的任务ID,用于回调匹配
|
||
status: ExecutionStatus.PROCESSING,
|
||
progress: 0,
|
||
creditCost: templateConfig.creditCost,
|
||
startedAt: new Date(),
|
||
});
|
||
|
||
const savedExecution = await this.executionRepository.save(execution);
|
||
|
||
// 返回任务id (执行记录的ID) 以及审核信息
|
||
return ResponseUtil.success(savedExecution.id, '模板执行已启动');
|
||
} catch (error) {
|
||
console.error(error);
|
||
throw new HttpException(
|
||
error.message || 'Template execution failed',
|
||
error instanceof HttpException ? error.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户当前任务限制
|
||
* @param userId 用户ID
|
||
* @throws HttpException 如果超出任务限制
|
||
*/
|
||
private async checkUserTaskLimit(userId: string): Promise<void> {
|
||
// 查询用户当前进行中的任务
|
||
const processingTasks = await this.executionRepository.find({
|
||
where: {
|
||
userId,
|
||
status: ExecutionStatus.PROCESSING,
|
||
},
|
||
order: {
|
||
startedAt: 'ASC',
|
||
},
|
||
});
|
||
|
||
// 如果当前没有进行中的任务,允许执行
|
||
if (processingTasks.length === 0) {
|
||
return;
|
||
}
|
||
|
||
// 如果当前进行中任务数量已达到3个,需要检查时间限制
|
||
if (processingTasks.length >= 3) {
|
||
const now = new Date();
|
||
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
||
|
||
// 检查是否有任务在5分钟内开始
|
||
const recentTasks = processingTasks.filter(
|
||
(task) =>
|
||
task.startedAt && task.startedAt.getTime() > fiveMinutesAgo.getTime(),
|
||
);
|
||
|
||
if (recentTasks.length > 3) {
|
||
throw new HttpException(
|
||
`有${recentTasks.length}个任务正在进行中,请稍后再试`,
|
||
HttpStatus.TOO_MANY_REQUESTS,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
@Post(':templateId/batch-execute')
|
||
@ApiOperation({
|
||
summary: '批量执行模板',
|
||
description: '批量执行模板生成任务,支持多张图片同时处理',
|
||
})
|
||
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
|
||
@ApiBody({ type: BatchExecuteDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '批量执行成功',
|
||
schema: {
|
||
example: {
|
||
success: true,
|
||
data: {
|
||
templateId: 1,
|
||
totalCount: 2,
|
||
results: [
|
||
{ inputUrl: 'image1.jpg', outputUrl: 'output1.jpg' },
|
||
{ inputUrl: 'image2.jpg', outputUrl: 'output2.jpg' },
|
||
],
|
||
},
|
||
},
|
||
},
|
||
})
|
||
async batchExecuteTemplate(
|
||
@Param('templateId', ParseIntPipe) templateId: number,
|
||
@Body() body: { imageUrls: string[] },
|
||
) {
|
||
try {
|
||
const { imageUrls } = body;
|
||
|
||
if (!imageUrls || !Array.isArray(imageUrls) || imageUrls.length === 0) {
|
||
throw new HttpException(
|
||
'imageUrls array is required',
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
|
||
const results = await this.templateFactory.batchExecute(
|
||
templateId,
|
||
imageUrls,
|
||
);
|
||
|
||
return ResponseUtil.success({
|
||
templateId,
|
||
totalCount: imageUrls.length,
|
||
results: imageUrls.map((inputUrl, index) => ({
|
||
inputUrl,
|
||
outputUrl: results[index],
|
||
})),
|
||
}, '批量执行成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Batch execution failed',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get()
|
||
@ApiOperation({
|
||
summary: '获取所有模板列表',
|
||
description: '获取所有可用的AI生成模板,支持按类型筛选',
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: [TemplateListDto],
|
||
})
|
||
async getAllTemplates(): Promise<ApiResponse<any[]>> {
|
||
try {
|
||
const templates = await this.templateFactory.getAllTemplates();
|
||
const data = templates.map((template) => ({
|
||
id: template.id,
|
||
code: template.code,
|
||
name: template.name,
|
||
description: template.description,
|
||
creditCost: template.creditCost,
|
||
version: template.version,
|
||
templateType: template.templateType,
|
||
templateClass: template.templateClass,
|
||
tags: template.tags,
|
||
inputExample: template.inputExampleUrl,
|
||
outputExample: template.outputExampleUrl,
|
||
sortOrder: template.sortOrder,
|
||
isActive: template.isActive,
|
||
createdAt: template.createdAt,
|
||
updatedAt: template.updatedAt,
|
||
}));
|
||
return ResponseUtil.success(data, '获取模板列表成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch templates',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get('image')
|
||
@ApiOperation({
|
||
summary: '获取图片模板列表',
|
||
description: '获取所有图片类型的AI生成模板',
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: [TemplateListDto],
|
||
})
|
||
async getImageTemplates() {
|
||
try {
|
||
const templates = await this.templateFactory.getImageTemplates();
|
||
return ResponseUtil.success(templates, '获取图片模板列表成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch image templates',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get('video')
|
||
@ApiOperation({
|
||
summary: '获取视频模板列表',
|
||
description: '获取所有视频类型的AI生成模板',
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: [TemplateListDto],
|
||
})
|
||
async getVideoTemplates() {
|
||
try {
|
||
const templates = await this.templateFactory.getVideoTemplates();
|
||
return ResponseUtil.success(templates, '获取视频模板列表成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch video templates',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get('recommend')
|
||
@ApiOperation({
|
||
summary: '推荐模板',
|
||
description: '根据用户偏好标签推荐适合的模板',
|
||
})
|
||
@ApiQuery({
|
||
name: 'tags',
|
||
required: false,
|
||
description: '用户偏好标签,逗号分隔',
|
||
example: '人物,手办',
|
||
})
|
||
@ApiQuery({
|
||
name: 'limit',
|
||
required: false,
|
||
description: '推荐数量',
|
||
example: 5,
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '推荐成功',
|
||
schema: {
|
||
example: {
|
||
success: true,
|
||
data: {
|
||
userTags: ['人物', '手办'],
|
||
recommendedTemplates: [],
|
||
},
|
||
},
|
||
},
|
||
})
|
||
async recommendTemplates(
|
||
@Query('tags') tags?: string,
|
||
@Query('limit') limit: number = 5,
|
||
) {
|
||
try {
|
||
const userTags = tags ? tags.split(',').map((tag) => tag.trim()) : [];
|
||
const templates = await this.templateFactory.recommendTemplates(
|
||
userTags,
|
||
limit,
|
||
);
|
||
|
||
return ResponseUtil.success({
|
||
userTags,
|
||
recommendedTemplates: templates,
|
||
}, '模板推荐成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to recommend templates',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get(':templateId')
|
||
@ApiOperation({
|
||
summary: '获取模板详情',
|
||
description: '根据模板ID获取详细信息',
|
||
})
|
||
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: TemplateListDto,
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 404,
|
||
description: '模板不存在',
|
||
})
|
||
async getTemplateById(@Param('templateId', ParseIntPipe) templateId: number) {
|
||
try {
|
||
const template = await this.templateFactory.createTemplate(templateId);
|
||
|
||
return ResponseUtil.success({
|
||
id: templateId,
|
||
code: template.code,
|
||
name: template.name,
|
||
description: template.description,
|
||
creditCost: template.creditCost,
|
||
version: template.version,
|
||
tags: template.tags,
|
||
inputExample: template.input,
|
||
outputExample: template.output,
|
||
}, '获取模板详情成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Template not found',
|
||
HttpStatus.NOT_FOUND,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询模板执行进度
|
||
* @param taskId 任务ID
|
||
* @returns 执行进度信息
|
||
*/
|
||
@Get('execution/:taskId/progress')
|
||
async getExecutionProgress(
|
||
@Param('taskId', ParseIntPipe) taskId: number,
|
||
): Promise<ApiResponse<any>> {
|
||
try {
|
||
const execution = await this.executionRepository.findOne({
|
||
where: { id: taskId },
|
||
relations: ['template'],
|
||
});
|
||
|
||
if (!execution) {
|
||
throw new HttpException(
|
||
'Execution task not found',
|
||
HttpStatus.NOT_FOUND,
|
||
);
|
||
}
|
||
|
||
return ResponseUtil.success(
|
||
{
|
||
taskId: execution.id,
|
||
templateId: execution.templateId,
|
||
templateName: execution.template?.name,
|
||
userId: execution.userId,
|
||
platform: execution.platform,
|
||
type: execution.type,
|
||
status: execution.status,
|
||
progress: execution.progress,
|
||
inputImageUrl: execution.inputImageUrl,
|
||
outputUrl: execution.outputUrl,
|
||
thumbnailUrl: execution.thumbnailUrl,
|
||
errorMessage: execution.errorMessage,
|
||
creditCost: execution.creditCost,
|
||
startedAt: execution.startedAt,
|
||
completedAt: execution.completedAt,
|
||
executionDuration: execution.executionDuration,
|
||
createdAt: execution.createdAt,
|
||
updatedAt: execution.updatedAt,
|
||
executionResult: execution.executionResult,
|
||
},
|
||
'获取执行进度成功',
|
||
);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) {
|
||
throw error;
|
||
}
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch execution progress',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询用户的执行任务列表
|
||
* @param userId 用户ID
|
||
* @param status 状态筛选
|
||
* @param limit 数量限制
|
||
* @returns 用户执行任务列表
|
||
*/
|
||
@Get('executions/user/:userId')
|
||
async getUserExecutions(
|
||
@Param('userId') userId: string,
|
||
@Query('status') status?: string,
|
||
@Query('limit') limit: number = 20,
|
||
) {
|
||
try {
|
||
const queryBuilder = this.executionRepository
|
||
.createQueryBuilder('execution')
|
||
.leftJoinAndSelect('execution.template', 'template')
|
||
.where('execution.userId = :userId', { userId })
|
||
.orderBy('execution.createdAt', 'DESC')
|
||
.limit(limit);
|
||
|
||
if (status) {
|
||
queryBuilder.andWhere('execution.status = :status', { status });
|
||
}
|
||
|
||
const executions = await queryBuilder.getMany();
|
||
|
||
return ResponseUtil.success(executions.map((execution) => ({
|
||
taskId: execution.id,
|
||
templateId: execution.templateId,
|
||
templateName: execution.template?.name,
|
||
templateCode: execution.template?.code,
|
||
type: execution.type,
|
||
status: execution.status,
|
||
progress: execution.progress,
|
||
inputImageUrl: execution.inputImageUrl,
|
||
outputUrl: execution.outputUrl,
|
||
thumbnailUrl: execution.thumbnailUrl,
|
||
creditCost: execution.creditCost,
|
||
startedAt: execution.startedAt,
|
||
completedAt: execution.completedAt,
|
||
executionDuration: execution.executionDuration,
|
||
createdAt: execution.createdAt,
|
||
})), '获取用户执行任务列表成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch user executions',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Post()
|
||
@ApiOperation({
|
||
summary: '创建新模板',
|
||
description: '创建一个新的AI生成模板',
|
||
})
|
||
@ApiBody({ type: CreateTemplateDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '创建成功',
|
||
schema: {
|
||
example: {
|
||
success: true,
|
||
data: {
|
||
id: 1,
|
||
code: 'new_template_v1',
|
||
name: '新模板',
|
||
},
|
||
},
|
||
},
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 409,
|
||
description: '模板代码已存在',
|
||
})
|
||
async createTemplate(@Body() createTemplateDto: CreateTemplateDto) {
|
||
try {
|
||
const existingTemplate = await this.templateRepository.findOne({
|
||
where: { code: createTemplateDto.code },
|
||
});
|
||
|
||
if (existingTemplate) {
|
||
throw new HttpException(
|
||
`Template with code '${createTemplateDto.code}' already exists`,
|
||
HttpStatus.CONFLICT,
|
||
);
|
||
}
|
||
|
||
const template = this.templateRepository.create(createTemplateDto);
|
||
const savedTemplate = await this.templateRepository.save(template);
|
||
|
||
return ResponseUtil.success(savedTemplate, '模板创建成功');
|
||
} catch (error) {
|
||
if (error instanceof HttpException) {
|
||
throw error;
|
||
}
|
||
throw new HttpException(
|
||
error.message || 'Failed to create template',
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
}
|
||
|
||
@Put(':templateId')
|
||
@ApiOperation({
|
||
summary: '更新模板',
|
||
description: '更新指定模板的配置信息',
|
||
})
|
||
@ApiParam({ name: 'templateId', description: '模板ID', example: 1 })
|
||
@ApiBody({ type: UpdateTemplateDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '更新成功',
|
||
type: TemplateListDto,
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 404,
|
||
description: '模板不存在',
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 409,
|
||
description: '模板代码已存在',
|
||
})
|
||
async updateTemplate(
|
||
@Param('templateId', ParseIntPipe) templateId: number,
|
||
@Body() updateTemplateDto: UpdateTemplateDto,
|
||
) {
|
||
try {
|
||
const existingTemplate = await this.templateRepository.findOne({
|
||
where: { id: templateId },
|
||
});
|
||
|
||
if (!existingTemplate) {
|
||
throw new HttpException('Template not found', HttpStatus.NOT_FOUND);
|
||
}
|
||
|
||
if (
|
||
updateTemplateDto.code &&
|
||
updateTemplateDto.code !== existingTemplate.code
|
||
) {
|
||
const codeConflict = await this.templateRepository.findOne({
|
||
where: { code: updateTemplateDto.code },
|
||
});
|
||
|
||
if (codeConflict) {
|
||
throw new HttpException(
|
||
`Template with code '${updateTemplateDto.code}' already exists`,
|
||
HttpStatus.CONFLICT,
|
||
);
|
||
}
|
||
}
|
||
|
||
await this.templateRepository.update(templateId, updateTemplateDto);
|
||
const updatedTemplate = await this.templateRepository.findOne({
|
||
where: { id: templateId },
|
||
});
|
||
|
||
return ResponseUtil.success(updatedTemplate, '模板更新成功');
|
||
} catch (error) {
|
||
if (error instanceof HttpException) {
|
||
throw error;
|
||
}
|
||
throw new HttpException(
|
||
error.message || 'Failed to update template',
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除模板
|
||
* @param templateId 模板ID
|
||
* @returns 删除结果
|
||
*/
|
||
@Delete(':templateId')
|
||
async deleteTemplate(@Param('templateId', ParseIntPipe) templateId: number) {
|
||
try {
|
||
const template = await this.templateRepository.findOne({
|
||
where: { id: templateId },
|
||
});
|
||
|
||
if (!template) {
|
||
throw new HttpException('Template not found', HttpStatus.NOT_FOUND);
|
||
}
|
||
|
||
await this.templateRepository.delete(templateId);
|
||
|
||
return ResponseUtil.success(null, '模板删除成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to delete template',
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取模板详情(管理后台专用)
|
||
* @param templateId 模板ID
|
||
* @returns 模板完整详情(包含非激活状态的模板)
|
||
*/
|
||
@Get('admin/:templateId')
|
||
async getTemplateForAdmin(
|
||
@Param('templateId', ParseIntPipe) templateId: number,
|
||
) {
|
||
try {
|
||
const template = await this.templateRepository.findOne({
|
||
where: { id: templateId },
|
||
});
|
||
|
||
if (!template) {
|
||
throw new HttpException('Template not found', HttpStatus.NOT_FOUND);
|
||
}
|
||
|
||
return ResponseUtil.success(template, '获取模板详情成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Template not found',
|
||
HttpStatus.NOT_FOUND,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有模板列表(管理后台专用)
|
||
* @param page 页码
|
||
* @param limit 每页数量
|
||
* @param type 模板类型筛选
|
||
* @param isActive 激活状态筛选
|
||
* @returns 分页的模板列表(包含非激活状态的模板)
|
||
*/
|
||
@Get('admin')
|
||
async getAllTemplatesForAdmin(
|
||
@Query('page') page: number = 1,
|
||
@Query('limit') limit: number = 10,
|
||
@Query('type') type?: TemplateType,
|
||
@Query('isActive') isActive?: boolean,
|
||
) {
|
||
try {
|
||
const queryBuilder =
|
||
this.templateRepository.createQueryBuilder('template');
|
||
|
||
if (type) {
|
||
queryBuilder.andWhere('template.templateType = :type', { type });
|
||
}
|
||
|
||
if (isActive !== undefined) {
|
||
queryBuilder.andWhere('template.isActive = :isActive', { isActive });
|
||
}
|
||
|
||
queryBuilder
|
||
.orderBy('template.sortOrder', 'DESC')
|
||
.addOrderBy('template.createdAt', 'DESC')
|
||
.skip((page - 1) * limit)
|
||
.take(limit);
|
||
|
||
const [templates, total] = await queryBuilder.getManyAndCount();
|
||
|
||
return ResponseUtil.paginated(templates, total, page, limit, '获取模板列表成功');
|
||
} catch (error) {
|
||
throw new HttpException(
|
||
error.message || 'Failed to fetch templates',
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
}
|