package com.rj.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.rj.dto.VideoSynthesisRequestDto; import com.rj.entity.VideoSynthesisLog; import com.rj.mapper.VideoSynthesisLogMapper; import com.rj.service.IVideoSynthesisService; import com.rj.service.MinIOService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; import java.util.HashMap; import java.util.Map; /** * 视频合成控制器 * * @author rj * @date 2025-01-02 */ @Slf4j @RestController @RequestMapping("/api/video-synthesis") @Tag(name = "视频合成,根据头像和音频生成视频", description = "视频合成相关接口,根据头像和音频生成视频") public class VideoSynthesisFromController { @Autowired private IVideoSynthesisService videoSynthesisService; @Autowired private VideoSynthesisLogMapper videoSynthesisLogMapper; @Autowired private MinIOService minIOService; /** * 视频合成接口 */ @PostMapping("/synthesize") @Operation(summary = "视频合成", description = "根据图片和音频生成视频") public ResponseEntity> synthesizeVideo(@Valid @RequestBody VideoSynthesisRequestDto requestDto) { Map result = new HashMap<>(); try { log.info("开始视频合成,图片URL: {}, 音频URL: {}", requestDto.getImageUrl(), requestDto.getAudioUrl()); // 执行视频合成 VideoSynthesisLog synthesisLog = videoSynthesisService.synthesizeVideoAndSave(requestDto); // 构建响应结果 result.put("success", synthesisLog.getSuccess()); result.put("message", synthesisLog.getTaskStatus()); result.put("requestId", synthesisLog.getRequestId()); result.put("taskId", synthesisLog.getTaskId()); result.put("taskStatus", synthesisLog.getTaskStatus()); result.put("videoUrl", synthesisLog.getVideoUrl()); result.put("videoDuration", synthesisLog.getVideoDuration()); result.put("videoRatio", synthesisLog.getVideoRatio()); result.put("processingTimeMs", synthesisLog.getProcessingTimeMs()); result.put("imageId", synthesisLog.getImageId()); result.put("audioId", synthesisLog.getAudioId()); result.put("ownerName", synthesisLog.getOwnerName()); result.put("ownerPhone", synthesisLog.getOwnerPhone()); result.put("modelProvider", synthesisLog.getModelProvider()); if (!synthesisLog.getSuccess()) { result.put("errorMessage", synthesisLog.getErrorMessage()); return ResponseEntity.badRequest().body(result); } return ResponseEntity.ok(result); } catch (Exception e) { log.error("视频合成失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "视频合成失败: " + e.getMessage()); return ResponseEntity.internalServerError().body(result); } } /** * 根据请求ID查询视频合成结果 */ @GetMapping("/result/{requestId}") @Operation(summary = "查询合成结果", description = "根据请求ID查询视频合成结果") public ResponseEntity> getSynthesisResult( @Parameter(description = "请求ID", required = true) @PathVariable String requestId) { Map result = new HashMap<>(); try { VideoSynthesisLog synthesisLog = videoSynthesisService.getVideoSynthesisLogByRequestId(requestId); if (synthesisLog == null) { result.put("success", false); result.put("message", "未找到对应的视频合成记录"); return ResponseEntity.notFound().build(); } result.put("success", true); result.put("message", "查询成功"); result.put("requestId", synthesisLog.getRequestId()); result.put("taskId", synthesisLog.getTaskId()); result.put("taskStatus", synthesisLog.getTaskStatus()); result.put("videoUrl", synthesisLog.getVideoUrl()); result.put("videoDuration", synthesisLog.getVideoDuration()); result.put("videoRatio", synthesisLog.getVideoRatio()); result.put("processingTimeMs", synthesisLog.getProcessingTimeMs()); result.put("requestTime", synthesisLog.getRequestTime()); result.put("responseTime", synthesisLog.getResponseTime()); result.put("imageId", synthesisLog.getImageId()); result.put("audioId", synthesisLog.getAudioId()); result.put("ownerName", synthesisLog.getOwnerName()); result.put("ownerPhone", synthesisLog.getOwnerPhone()); result.put("modelProvider", synthesisLog.getModelProvider()); if (!synthesisLog.getSuccess()) { result.put("errorMessage", synthesisLog.getErrorMessage()); } return ResponseEntity.ok(result); } catch (Exception e) { log.error("查询视频合成结果失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "查询失败: " + e.getMessage()); return ResponseEntity.internalServerError().body(result); } } /** * 根据任务ID查询视频合成结果 */ @GetMapping("/result/task/{taskId}") @Operation(summary = "根据任务ID查询结果", description = "根据任务ID查询视频合成结果") public ResponseEntity> getSynthesisResultByTaskId( @Parameter(description = "任务ID", required = true) @PathVariable String taskId) { Map result = new HashMap<>(); try { VideoSynthesisLog synthesisLog = videoSynthesisService.getVideoSynthesisLogByTaskId(taskId); if (synthesisLog == null) { result.put("success", false); result.put("message", "未找到对应的视频合成记录"); return ResponseEntity.notFound().build(); } result.put("success", true); result.put("message", "查询成功"); result.put("requestId", synthesisLog.getRequestId()); result.put("taskId", synthesisLog.getTaskId()); result.put("taskStatus", synthesisLog.getTaskStatus()); result.put("videoUrl", synthesisLog.getVideoUrl()); result.put("videoDuration", synthesisLog.getVideoDuration()); result.put("videoRatio", synthesisLog.getVideoRatio()); result.put("processingTimeMs", synthesisLog.getProcessingTimeMs()); result.put("requestTime", synthesisLog.getRequestTime()); result.put("responseTime", synthesisLog.getResponseTime()); result.put("imageId", synthesisLog.getImageId()); result.put("audioId", synthesisLog.getAudioId()); result.put("ownerName", synthesisLog.getOwnerName()); result.put("ownerPhone", synthesisLog.getOwnerPhone()); result.put("modelProvider", synthesisLog.getModelProvider()); if (!synthesisLog.getSuccess()) { result.put("errorMessage", synthesisLog.getErrorMessage()); } return ResponseEntity.ok(result); } catch (Exception e) { log.error("根据任务ID查询视频合成结果失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "查询失败: " + e.getMessage()); return ResponseEntity.internalServerError().body(result); } } /** * 分页查询视频合成日志列表 */ @GetMapping("/list") @Operation(summary = "查询视频合成日志列表", description = "分页查询视频合成日志") public ResponseEntity> getVideoSynthesisLogs( @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNum, @Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer pageSize, @Parameter(description = "任务状态") @RequestParam(required = false) String taskStatus, @Parameter(description = "模型") @RequestParam(required = false) String model, @Parameter(description = "模型供应商") @RequestParam(required = false) String modelProvider, @Parameter(description = "归属人姓名") @RequestParam(required = false) String ownerName, @Parameter(description = "归属人电话") @RequestParam(required = false) String ownerPhone, @Parameter(description = "是否成功") @RequestParam(required = false) Boolean success, @Parameter(description = "开始时间") @RequestParam(required = false) String startTime, @Parameter(description = "结束时间") @RequestParam(required = false) String endTime) { Map result = new HashMap<>(); try { Page page = new Page<>(pageNum, pageSize); QueryWrapper queryWrapper = new QueryWrapper<>(); // 添加查询条件 if (taskStatus != null && !taskStatus.trim().isEmpty()) { queryWrapper.eq("task_status", taskStatus); } if (model != null && !model.trim().isEmpty()) { queryWrapper.like("model", model); } if (modelProvider != null && !modelProvider.trim().isEmpty()) { queryWrapper.like("model_provider", modelProvider); } if (ownerName != null && !ownerName.trim().isEmpty()) { queryWrapper.like("owner_name", ownerName); } if (ownerPhone != null && !ownerPhone.trim().isEmpty()) { queryWrapper.like("owner_phone", ownerPhone); } if (success != null) { queryWrapper.eq("success", success); } if (startTime != null && !startTime.trim().isEmpty()) { queryWrapper.ge("request_time", startTime); } if (endTime != null && !endTime.trim().isEmpty()) { queryWrapper.le("request_time", endTime); } // 按请求时间倒序排列 queryWrapper.orderByDesc("request_time"); // 执行查询 Page pageResult = videoSynthesisLogMapper.selectPage(page, queryWrapper); result.put("success", true); result.put("message", "查询成功"); result.put("data", pageResult.getRecords()); result.put("total", pageResult.getTotal()); result.put("pageNum", pageResult.getCurrent()); result.put("pageSize", pageResult.getSize()); result.put("pages", pageResult.getPages()); return ResponseEntity.ok(result); } catch (Exception e) { log.error("查询视频合成日志失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "查询失败: " + e.getMessage()); return ResponseEntity.status(500).body(result); } } /** * 根据MinIO路径播放视频 */ @GetMapping("/play/{minioPath}") @Operation(summary = "播放视频", description = "根据MinIO路径生成预签名URL播放视频") public ResponseEntity> playVideo( @Parameter(description = "MinIO文件路径", required = true) @PathVariable String minioPath, @Parameter(description = "过期时间(秒),默认7天", required = false) @RequestParam(value = "expires", defaultValue = "900") int expires) { Map result = new HashMap<>(); try { log.info("开始生成视频播放URL,MinIO路径: {}, 过期时间: {}秒", minioPath, expires); // 验证路径格式 if (minioPath == null || minioPath.trim().isEmpty()) { result.put("success", false); result.put("message", "MinIO路径不能为空"); return ResponseEntity.badRequest().body(result); } // 清理路径,移除开头的斜杠 String cleanPath = minioPath.startsWith("/") ? minioPath.substring(1) : minioPath; // 生成预签名URL String presignedUrl = minIOService.getPresignedUrl(cleanPath, expires); if (presignedUrl == null || presignedUrl.trim().isEmpty()) { result.put("success", false); result.put("message", "生成预签名URL失败"); return ResponseEntity.internalServerError().body(result); } // 构建响应结果 result.put("success", true); result.put("message", "视频播放URL生成成功"); result.put("minioPath", minioPath); result.put("presignedUrl", presignedUrl); result.put("expires", expires); result.put("expiresInDays", expires / 86400); // 转换为天数 log.info("视频播放URL生成成功: {}", presignedUrl); return ResponseEntity.ok(result); } catch (Exception e) { log.error("生成视频播放URL失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "生成视频播放URL失败: " + e.getMessage()); return ResponseEntity.internalServerError().body(result); } } /** * 根据任务ID播放视频 */ @GetMapping("/play/task/{taskId}") @Operation(summary = "根据任务ID播放视频", description = "根据任务ID查找视频并生成播放URL") public ResponseEntity> playVideoByTaskId( @Parameter(description = "任务ID", required = true) @PathVariable String taskId, @Parameter(description = "过期时间(秒),默认7天", required = false) @RequestParam(value = "expires", defaultValue = "604800") int expires) { Map result = new HashMap<>(); try { log.info("开始根据任务ID播放视频,任务ID: {}, 过期时间: {}秒", taskId, expires); // 根据任务ID查询视频合成记录 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("task_id", taskId); VideoSynthesisLog synthesisLog = videoSynthesisLogMapper.selectOne(queryWrapper); if (synthesisLog == null) { result.put("success", false); result.put("message", "未找到对应的视频合成记录"); return ResponseEntity.notFound().build(); } if (synthesisLog.getVideoUrl() == null || synthesisLog.getVideoUrl().trim().isEmpty()) { result.put("success", false); result.put("message", "视频URL为空,可能视频还未生成完成"); return ResponseEntity.badRequest().body(result); } // 从videoUrl中提取MinIO路径 String videoUrl = synthesisLog.getVideoUrl(); String minioPath; // 判断是否为MinIO URL格式 if (videoUrl.contains("/minio/")) { // 从MinIO URL中提取路径 String[] parts = videoUrl.split("/minio/"); if (parts.length > 1) { minioPath = parts[1]; } else { result.put("success", false); result.put("message", "无法从视频URL中提取MinIO路径"); return ResponseEntity.badRequest().body(result); } } else { // 假设直接是MinIO路径 minioPath = videoUrl; } // 生成预签名URL String presignedUrl = minIOService.getPresignedUrl(minioPath, expires); if (presignedUrl == null || presignedUrl.trim().isEmpty()) { result.put("success", false); result.put("message", "生成预签名URL失败"); return ResponseEntity.internalServerError().body(result); } // 构建响应结果 result.put("success", true); result.put("message", "视频播放URL生成成功"); result.put("taskId", taskId); result.put("requestId", synthesisLog.getRequestId()); result.put("videoName", synthesisLog.getVideoName()); result.put("minioPath", minioPath); result.put("presignedUrl", presignedUrl); result.put("expires", expires); result.put("expiresInDays", expires / 86400); result.put("taskStatus", synthesisLog.getTaskStatus()); result.put("success", synthesisLog.getSuccess()); log.info("根据任务ID生成视频播放URL成功: {}", presignedUrl); return ResponseEntity.ok(result); } catch (Exception e) { log.error("根据任务ID生成视频播放URL失败: {}", e.getMessage(), e); result.put("success", false); result.put("message", "生成视频播放URL失败: " + e.getMessage()); return ResponseEntity.internalServerError().body(result); } } }