Files
bw-mini-app-server/src/controllers/image-composition.controller.ts
iHeyTang 73ca6723ad feat: 添加图片拼接功能和相关API文档更新
- 在应用中引入ImageCompositionController和ImageCompositionService
- 实现图片拼接功能,支持多种排列方式和输出格式
- 更新Swagger文档,添加图片拼接API描述和示例
- 修改模板执行API描述,简化审核流程说明
2025-09-25 20:42:25 +08:00

216 lines
5.7 KiB
TypeScript

import {
Controller,
Post,
Body,
HttpException,
HttpStatus,
UseGuards,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse as SwaggerApiResponse,
ApiBody,
} from '@nestjs/swagger';
import { ImageCompositionService } from '../services/image-composition.service';
import {
ImageCompositionDto,
ImageCompositionResponseDto,
ArrangementDirection,
} from '../dto/image-composition.dto';
import { ApiCommonResponses } from '../decorators/api-common-responses.decorator';
import { PlatformAuthGuard } from '../platform/guards/platform-auth.guard';
import { ResponseUtil, ApiResponse } from '../utils/response.util';
@ApiTags('图片处理')
@Controller('image-composition')
@ApiCommonResponses()
export class ImageCompositionController {
constructor(
private readonly imageCompositionService: ImageCompositionService,
) {}
@Post('compose')
@UseGuards(PlatformAuthGuard)
@ApiOperation({
summary: '图片拼接',
description: '将多张图片按指定位置拼接到一个画布上,支持层级、透明度等设置',
})
@ApiBody({
type: ImageCompositionDto,
description: '图片拼接配置 - 简化版本',
examples: {
horizontal: {
summary: '水平排列示例',
value: {
images: [
'https://picsum.photos/300/200',
'https://picsum.photos/300/200',
'https://picsum.photos/300/200'
],
direction: 'horizontal',
outputFormat: 'jpeg',
maxWidth: 1920,
maxHeight: 1080,
quality: 80
},
},
vertical: {
summary: '垂直排列示例',
value: {
images: [
'https://picsum.photos/200/300',
'https://picsum.photos/200/300',
'https://picsum.photos/200/300'
],
direction: 'vertical',
outputFormat: 'webp',
maxWidth: 800,
maxHeight: 1200,
quality: 85
},
},
grid: {
summary: '网格排列示例',
value: {
images: [
'https://picsum.photos/200/200',
'https://picsum.photos/200/200',
'https://picsum.photos/200/200',
'https://picsum.photos/200/200',
'https://picsum.photos/200/200'
],
direction: 'grid',
outputFormat: 'jpeg',
maxWidth: 1024,
maxHeight: 1024,
quality: 75
},
},
},
})
@SwaggerApiResponse({
status: 200,
description: '图片拼接成功',
type: ImageCompositionResponseDto,
})
@SwaggerApiResponse({
status: 400,
description: '请求参数错误',
})
@SwaggerApiResponse({
status: 401,
description: '未授权访问',
})
@SwaggerApiResponse({
status: 500,
description: '服务器内部错误',
})
async composeImages(
@Body() compositionDto: ImageCompositionDto,
): Promise<ApiResponse<ImageCompositionResponseDto>> {
try {
// 基础参数验证
this.validateCompositionRequest(compositionDto);
const result = await this.imageCompositionService.composeImages(compositionDto);
return ResponseUtil.success(result, '图片拼接成功');
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`图片拼接失败: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
/**
* 验证拼接请求参数 - 简化版本
*/
private validateCompositionRequest(dto: ImageCompositionDto): void {
// 验证图片数量
if (!dto.images || dto.images.length === 0) {
throw new HttpException(
'至少需要提供一张图片',
HttpStatus.BAD_REQUEST,
);
}
if (dto.images.length > 20) {
throw new HttpException(
'图片数量不能超过20张',
HttpStatus.BAD_REQUEST,
);
}
// 验证排列方向
const validDirections = Object.values(ArrangementDirection);
if (!validDirections.includes(dto.direction)) {
throw new HttpException(
`排列方向必须是以下值之一: ${validDirections.join(', ')}`,
HttpStatus.BAD_REQUEST,
);
}
// 验证图片URL格式
for (const imageUrl of dto.images) {
if (!imageUrl || typeof imageUrl !== 'string') {
throw new HttpException(
'图片URL不能为空',
HttpStatus.BAD_REQUEST,
);
}
// 检查是否为有效的URL或base64格式
const isValidUrl = this.isValidImageUrl(imageUrl);
const isValidBase64 = imageUrl.startsWith('data:image/');
if (!isValidUrl && !isValidBase64) {
throw new HttpException(
'图片必须是有效的HTTP(S) URL或base64格式',
HttpStatus.BAD_REQUEST,
);
}
}
// 验证尺寸参数
if (dto.maxWidth !== undefined && (dto.maxWidth < 100 || dto.maxWidth > 4096)) {
throw new HttpException(
'最大宽度必须在100-4096像素之间',
HttpStatus.BAD_REQUEST,
);
}
if (dto.maxHeight !== undefined && (dto.maxHeight < 100 || dto.maxHeight > 4096)) {
throw new HttpException(
'最大高度必须在100-4096像素之间',
HttpStatus.BAD_REQUEST,
);
}
// 验证质量参数
if (dto.quality !== undefined && (dto.quality < 1 || dto.quality > 100)) {
throw new HttpException(
'图片质量必须在1-100之间',
HttpStatus.BAD_REQUEST,
);
}
}
/**
* 验证图片URL格式
*/
private isValidImageUrl(url: string): boolean {
try {
const parsedUrl = new URL(url);
return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
} catch {
return false;
}
}
}