import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi'; import { FileService } from '@/server/modules/files/file.service'; import { MinioService } from '@/server/modules/files/minio.service'; import { ErrorSchema } from '@/server/utils/errorHandler'; import { AppDataSource } from '@/server/data-source'; import { AuthContext } from '@/server/types/context'; import { authMiddleware } from '@/server/middleware/auth.middleware'; // 创建分片上传策略请求Schema const CreateMultipartUploadPolicyDto = z.object({ fileKey: z.string().openapi({ description: '文件键名', example: 'documents/report.pdf' }), totalSize: z.coerce.number().int().positive().openapi({ description: '文件总大小(字节)', example: 10485760 }), partSize: z.coerce.number().int().positive().openapi({ description: '分片大小(字节)', example: 5242880 }), type: z.string().max(50).nullable().optional().openapi({ description: '文件类型', example: 'application/pdf' }), name: z.string().max(255).openapi({ description: '文件名称', example: '项目计划书.pdf' }) }); // 创建分片上传策略路由定义 const createMultipartUploadPolicyRoute = createRoute({ method: 'post', path: '/', middleware: [authMiddleware], request: { body: { content: { 'application/json': { schema: CreateMultipartUploadPolicyDto } } } }, responses: { 200: { description: '生成分片上传策略成功', content: { 'application/json': { schema: z.object({ uploadId: z.string().openapi({ description: '分片上传ID', example: '123e4567-e89b-12d3-a456-426614174000' }), bucket: z.string().openapi({ description: '存储桶名称', example: 'my-bucket' }), key: z.string().openapi({ description: '文件键名', example: 'documents/report.pdf' }), host: z.string().openapi({ description: 'MinIO主机地址', example: 'minio.example.com' }), partUrls: z.array(z.string()).openapi({ description: '分片上传URL列表', example: [ 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=1', 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=2' ] }) }) } } }, 400: { description: '请求参数错误', content: { 'application/json': { schema: ErrorSchema } } }, 500: { description: '服务器错误', content: { 'application/json': { schema: ErrorSchema } } } } }); // 创建文件服务实例 const fileService = new FileService(AppDataSource); // 创建路由实例 const app = new OpenAPIHono().openapi(createMultipartUploadPolicyRoute, async (c) => { try { const data = await c.req.json(); const user = c.get('user'); // 计算分片数量 const partCount = Math.ceil(data.totalSize / data.partSize); const result = await fileService.createMultipartUploadPolicy({ ...data, uploadUserId: user.id }, partCount); return c.json({ uploadId: result.uploadId, bucket: result.bucket, key: result.key, host: `${process.env.MINIO_USE_SSL ? 'https' : 'http'}://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}`, partUrls: result.uploadUrls }, 200); } catch (error) { const message = error instanceof Error ? error.message : '生成分片上传策略失败'; return c.json({ code: 500, message }, 500); } }); export default app;