头像视频合成支持动态模型
This commit is contained in:
@@ -139,6 +139,10 @@ public class PasswordUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -123,6 +123,10 @@ public class ServiceManager {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,9 @@ public class AliyunConfig {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ public class FaceDetectController {
|
||||
@RequestParam(value = "ownerName", required = false) String ownerName,
|
||||
@Parameter(description = "所属人电话", required = false)
|
||||
@RequestParam(value = "ownerPhone", required = false) String ownerPhone,
|
||||
@Parameter(description = "模型名称", required = false)
|
||||
@RequestParam(value = "model", required = false) String model,
|
||||
@Parameter(description = "头像名称", required = false)
|
||||
@RequestParam(value = "avatarName", required = false) String avatarName) {
|
||||
|
||||
@@ -98,6 +100,8 @@ public class FaceDetectController {
|
||||
@RequestParam(value = "ownerPhone", required = false) String ownerPhone,
|
||||
@Parameter(description = "头像名称", required = false)
|
||||
@RequestParam(value = "avatarName", required = false) String avatarName,
|
||||
@Parameter(description = "模型名称", required = false)
|
||||
@RequestParam(value = "model", required = false) String model,
|
||||
@Parameter(description = "短链接有效期(秒),默认1小时", required = false)
|
||||
@RequestParam(value = "expiresInSeconds", defaultValue = "3600") int expiresInSeconds) {
|
||||
|
||||
@@ -149,8 +153,8 @@ public class FaceDetectController {
|
||||
log.info("短链接生成成功: {}", shortUrl);
|
||||
|
||||
// 4. 进行人脸检测
|
||||
log.info("开始进行人脸检测...");
|
||||
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(shortUrl, ownerName, ownerPhone, avatarName);
|
||||
log.info("开始进行人脸检测,使用模型: {}", model != null ? model : "默认模型");
|
||||
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(shortUrl, ownerName, ownerPhone, avatarName, model);
|
||||
|
||||
// 5. 构建响应结果
|
||||
result.put("success", true);
|
||||
@@ -165,6 +169,7 @@ public class FaceDetectController {
|
||||
result.put("ownerName", ownerName);
|
||||
result.put("ownerPhone", ownerPhone);
|
||||
result.put("avatarName", avatarName);
|
||||
result.put("model", model);
|
||||
|
||||
// 人脸检测结果
|
||||
result.put("faceDetection", detectLog.toString());
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -35,6 +36,9 @@ public class VideoSynthesisFromController {
|
||||
|
||||
@Autowired
|
||||
private VideoSynthesisLogMapper videoSynthesisLogMapper;
|
||||
|
||||
@Autowired
|
||||
private MinIOService minIOService;
|
||||
|
||||
/**
|
||||
* 视频合成接口
|
||||
@@ -256,4 +260,144 @@ public class VideoSynthesisFromController {
|
||||
return ResponseEntity.status(500).body(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据MinIO路径播放视频
|
||||
*/
|
||||
@GetMapping("/play/{minioPath}")
|
||||
@Operation(summary = "播放视频", description = "根据MinIO路径生成预签名URL播放视频")
|
||||
public ResponseEntity<Map<String, Object>> playVideo(
|
||||
@Parameter(description = "MinIO文件路径", required = true)
|
||||
@PathVariable String minioPath,
|
||||
@Parameter(description = "过期时间(秒),默认7天", required = false)
|
||||
@RequestParam(value = "expires", defaultValue = "900") int expires) {
|
||||
|
||||
Map<String, Object> 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<Map<String, Object>> playVideoByTaskId(
|
||||
@Parameter(description = "任务ID", required = true)
|
||||
@PathVariable String taskId,
|
||||
@Parameter(description = "过期时间(秒),默认7天", required = false)
|
||||
@RequestParam(value = "expires", defaultValue = "604800") int expires) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
log.info("开始根据任务ID播放视频,任务ID: {}, 过期时间: {}秒", taskId, expires);
|
||||
|
||||
// 根据任务ID查询视频合成记录
|
||||
QueryWrapper<VideoSynthesisLog> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,6 +361,10 @@ public class MenuController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -331,6 +331,10 @@ public class RoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -337,6 +337,10 @@ public class UserRoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -105,6 +105,9 @@ public class DifyWorkflowResponseDto {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,12 @@ public class VideoSynthesisRequestDto {
|
||||
@Schema(description = "头部动作强度", example = "0.7")
|
||||
private BigDecimal headMoveStrength = new BigDecimal("0.7");
|
||||
|
||||
@Schema(description = "人脸区域坐标 [x1, y1, x2, y2]", example = "[302,286,610,593]")
|
||||
private String faceBbox;
|
||||
|
||||
@Schema(description = "动态区域坐标 [x1, y1, x2, y2]", example = "[71,9,840,778]")
|
||||
private String extBbox;
|
||||
|
||||
@Schema(description = "输入数据", example = "{\"image_url\": \"http://xxx/1.jpg\", \"audio_url\": \"http://xxx/1.wav\"}")
|
||||
private Map<String, String> input;
|
||||
|
||||
|
||||
@@ -94,6 +94,18 @@ public class FaceDetectLog implements Serializable {
|
||||
@TableField("face_count")
|
||||
private Integer faceCount;
|
||||
|
||||
/**
|
||||
* 人脸区域坐标 [x1, y1, x2, y2]
|
||||
*/
|
||||
@TableField("face_bbox")
|
||||
private String faceBbox;
|
||||
|
||||
/**
|
||||
* 动态区域坐标 [x1, y1, x2, y2]
|
||||
*/
|
||||
@TableField("ext_bbox")
|
||||
private String extBbox;
|
||||
|
||||
/**
|
||||
* 完整响应数据
|
||||
*/
|
||||
|
||||
@@ -157,6 +157,18 @@ public class VideoSynthesisLog {
|
||||
@TableField("video_temp_url")
|
||||
private String videoTempUrl;
|
||||
|
||||
/**
|
||||
* 人脸区域坐标 [x1, y1, x2, y2]
|
||||
*/
|
||||
@TableField("face_bbox")
|
||||
private String faceBbox;
|
||||
|
||||
/**
|
||||
* 动态区域坐标 [x1, y1, x2, y2]
|
||||
*/
|
||||
@TableField("ext_bbox")
|
||||
private String extBbox;
|
||||
|
||||
/**
|
||||
* 视频时长(秒)
|
||||
*/
|
||||
|
||||
@@ -48,6 +48,9 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,10 @@ public class AudioStatisticsScheduler {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.rj.config.AliyunConfig;
|
||||
import com.rj.entity.VideoSynthesisLog;
|
||||
import com.rj.mapper.VideoSynthesisLogMapper;
|
||||
import com.rj.service.MinIOService;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -24,6 +25,7 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 视频合成任务状态检查定时器
|
||||
@@ -48,6 +50,9 @@ public class VideoSynthesisStatusScheduler {
|
||||
@Autowired
|
||||
private MinIOService minIOService;
|
||||
|
||||
@Autowired
|
||||
MinIOUrlGenerator urlGenerator;
|
||||
|
||||
/**
|
||||
* 每5分钟执行一次任务状态检查
|
||||
*/
|
||||
@@ -169,7 +174,7 @@ public class VideoSynthesisStatusScheduler {
|
||||
|
||||
// 更新临时访问URL(使用MinIO的预签名URL)
|
||||
try {
|
||||
String tempUrl = generateTempVideoUrl(task.getVideoName(), task.getRequestId());
|
||||
String tempUrl = generateTempVideoUrl("video_synthesis/"+task.getVideoName(), task.getRequestId());
|
||||
task.setVideoTempUrl(tempUrl);
|
||||
log.info("更新临时访问URL: {}", tempUrl);
|
||||
} catch (Exception e) {
|
||||
@@ -193,6 +198,24 @@ public class VideoSynthesisStatusScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
// 解析人脸区域坐标
|
||||
if (output.containsKey("face_bbox")) {
|
||||
String faceBbox = output.getString("face_bbox");
|
||||
if (faceBbox != null && !faceBbox.trim().isEmpty()) {
|
||||
task.setFaceBbox(faceBbox);
|
||||
log.info("更新人脸区域坐标: {}", faceBbox);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析动态区域坐标
|
||||
if (output.containsKey("ext_bbox")) {
|
||||
String extBbox = output.getString("ext_bbox");
|
||||
if (extBbox != null && !extBbox.trim().isEmpty()) {
|
||||
task.setExtBbox(extBbox);
|
||||
log.info("更新动态区域坐标: {}", extBbox);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
int updateResult = videoSynthesisLogMapper.updateById(task);
|
||||
if (updateResult > 0) {
|
||||
@@ -365,6 +388,7 @@ public class VideoSynthesisStatusScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成临时视频访问URL
|
||||
*
|
||||
@@ -374,14 +398,7 @@ public class VideoSynthesisStatusScheduler {
|
||||
*/
|
||||
private String generateTempVideoUrl(String videoName, String requestId) {
|
||||
try {
|
||||
// 构建临时访问URL
|
||||
// 格式: https://your-domain.com/video/temp/{requestId}?name={videoName}
|
||||
String baseUrl = "https://your-domain.com"; // 这里应该从配置文件读取
|
||||
String encodedVideoName = java.net.URLEncoder.encode(videoName, "UTF-8");
|
||||
String tempUrl = String.format("%s/video/temp/%s?name=%s", baseUrl, requestId, encodedVideoName);
|
||||
|
||||
log.info("生成临时视频URL: 视频名称={}, 请求ID={}, URL={}", videoName, requestId, tempUrl);
|
||||
return tempUrl;
|
||||
return urlGenerator.generateTempUrl(videoName,7, TimeUnit.DAYS).getUrl();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成临时视频URL失败: {}", e.getMessage(), e);
|
||||
|
||||
@@ -33,6 +33,17 @@ public interface IFaceDetectLogService extends IService<FaceDetectLog> {
|
||||
*/
|
||||
FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName);
|
||||
|
||||
/**
|
||||
* 执行人脸检测并保存日志(带扩展信息和模型名)
|
||||
* @param imageUrl 图片URL
|
||||
* @param ownerName 所属人姓名
|
||||
* @param ownerPhone 所属人电话
|
||||
* @param avatarName 头像名称
|
||||
* @param model 模型名称
|
||||
* @return 人脸检测日志对象
|
||||
*/
|
||||
FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName, String model);
|
||||
|
||||
/**
|
||||
* 分页查询人脸检测日志
|
||||
* @param current 当前页
|
||||
|
||||
@@ -69,6 +69,9 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,13 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
* 执行人脸检测并保存日志(带扩展信息)
|
||||
*/
|
||||
public FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName) {
|
||||
return detectFaceAndSave(imageUrl, ownerName, ownerPhone, avatarName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行人脸检测并保存日志(带扩展信息和模型名)
|
||||
*/
|
||||
public FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName, String model) {
|
||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||
log.error("DASHSCOPE_API_KEY未配置");
|
||||
return createErrorLog(imageUrl, "DASHSCOPE_API_KEY未配置", ownerName, ownerPhone, avatarName);
|
||||
@@ -71,7 +78,10 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
LocalDateTime requestTime = LocalDateTime.now();
|
||||
|
||||
faceDetectLog.setRequestId(requestId);
|
||||
faceDetectLog.setModel("liveportrait-detect"); //TODO 模型名称
|
||||
// 设置模型名称,如果未提供则使用默认值
|
||||
String modelName = (model != null && !model.trim().isEmpty()) ? model : "liveportrait-detect";
|
||||
faceDetectLog.setModel(modelName);
|
||||
log.info("使用模型: {}", modelName);
|
||||
faceDetectLog.setImageUrl(imageUrl);
|
||||
faceDetectLog.setOwnerName(ownerName);
|
||||
faceDetectLog.setOwnerPhone(ownerPhone);
|
||||
@@ -83,8 +93,14 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
try {
|
||||
// 创建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("model", "liveportrait-detect");
|
||||
|
||||
requestBody.put("model", modelName);
|
||||
|
||||
if (modelName.equals("emo-detect-v1")){
|
||||
Map<String, Object> ratioo = new HashMap<>();
|
||||
ratioo.put("ratio", "1:1");
|
||||
requestBody.put("parameters", ratioo);
|
||||
}
|
||||
|
||||
Map<String, String> input = new HashMap<>();
|
||||
input.put("image_url", imageUrl);
|
||||
requestBody.put("input", input);
|
||||
@@ -93,8 +109,8 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(apiKey);
|
||||
|
||||
// 创建请求实体
|
||||
log.info("发送人脸检测请求,requestBody : {}, 请求参数headers: {}", requestBody , headers);
|
||||
// 创建请求实体 parameters.ratio 希望检测确认的画幅,可选 "1:1"或"3:4"。默认值为"1:1"。
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
// 发送请求
|
||||
@@ -131,23 +147,50 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
faceDetectLog.setSuccess(isSuccess);
|
||||
|
||||
log.info("人脸检测结果 - pass: {}", isSuccess);
|
||||
} else {
|
||||
// 如果没有pass字段,则根据HTTP状态码判断
|
||||
isSuccess = response.getStatusCode().is2xxSuccessful();
|
||||
faceDetectLog.setSuccess(isSuccess);
|
||||
}
|
||||
// else {
|
||||
// // 如果没有pass字段,则根据HTTP状态码判断
|
||||
// isSuccess = response.getStatusCode().is2xxSuccessful();
|
||||
// faceDetectLog.setSuccess(isSuccess);
|
||||
// }
|
||||
|
||||
// 检查是否有检测到人脸
|
||||
int faceCount = 0;
|
||||
if (jsonResponse.has("output") && jsonResponse.get("output").has("faces")) {
|
||||
JsonNode faces = jsonResponse.get("output").get("faces");
|
||||
faceCount = faces.size();
|
||||
faceDetectLog.setFaceCount(faceCount);
|
||||
if (jsonResponse.has("output")) {
|
||||
JsonNode output = jsonResponse.get("output");
|
||||
|
||||
log.info("检测到人脸数量: {}", faceCount);
|
||||
for (int i = 0; i < faces.size(); i++) {
|
||||
JsonNode face = faces.get(i);
|
||||
log.debug("人脸 {}: {}", i + 1, face.toString());
|
||||
// 根据pass字段判断是否检测到人脸
|
||||
if (output.has("pass")) {
|
||||
boolean pass = output.get("pass").asBoolean();
|
||||
faceDetectLog.setFaceCount(1);
|
||||
|
||||
log.info("人脸检测结果 - pass: {}, 检测到人脸数量: {}", pass, faceDetectLog.getFaceCount());
|
||||
|
||||
// 记录message信息
|
||||
if (output.has("message")) {
|
||||
String message = output.get("message").asText();
|
||||
log.info("检测消息: {}", message);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析人脸区域坐标(如果API返回中包含)
|
||||
if (output.has("face_bbox")) {
|
||||
JsonNode faceBbox = output.get("face_bbox");
|
||||
if (faceBbox.isArray()) {
|
||||
String faceBboxStr = faceBbox.toString();
|
||||
faceDetectLog.setFaceBbox(faceBboxStr);
|
||||
log.info("人脸区域坐标: {}", faceBboxStr);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析动态区域坐标(如果API返回中包含)
|
||||
if (output.has("ext_bbox")) {
|
||||
JsonNode extBbox = output.get("ext_bbox");
|
||||
if (extBbox.isArray()) {
|
||||
String extBboxStr = extBbox.toString();
|
||||
faceDetectLog.setExtBbox(extBboxStr);
|
||||
log.info("动态区域坐标: {}", extBboxStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +206,12 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
if (usage.has("image_count")) {
|
||||
int imageCount = usage.get("image_count").asInt();
|
||||
log.info("处理的图片数量: {}", imageCount);
|
||||
// 可以将image_count保存到数据库字段中,如果有的话
|
||||
faceDetectLog.setFaceCount(imageCount);
|
||||
}
|
||||
|
||||
// 记录其他usage信息
|
||||
log.info("使用情况: {}", usage.toString());
|
||||
}
|
||||
} else {
|
||||
// HTTP状态码不是200,直接标记为失败
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.rj.dto.VideoSynthesisRequestDto;
|
||||
import com.rj.entity.VideoSynthesisLog;
|
||||
import com.rj.mapper.VideoSynthesisLogMapper;
|
||||
import com.rj.service.IVideoSynthesisService;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -16,6 +17,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 视频合成服务实现类
|
||||
@@ -33,6 +35,8 @@ public class VideoSynthesisServiceImpl implements IVideoSynthesisService {
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
MinIOUrlGenerator minIOUrlGenerator ;
|
||||
|
||||
@Value("${dashscope.api.key}")
|
||||
private String apiKey;
|
||||
@@ -62,6 +66,18 @@ public class VideoSynthesisServiceImpl implements IVideoSynthesisService {
|
||||
log.info("使用默认视频名称: {}", synthesisLog.getVideoName());
|
||||
}
|
||||
|
||||
// 设置人脸区域坐标(如果请求中有提供)
|
||||
if (request.getFaceBbox() != null && !request.getFaceBbox().trim().isEmpty()) {
|
||||
synthesisLog.setFaceBbox(request.getFaceBbox());
|
||||
log.info("设置人脸区域坐标: {}", request.getFaceBbox());
|
||||
}
|
||||
|
||||
// 设置动态区域坐标(如果请求中有提供)
|
||||
if (request.getExtBbox() != null && !request.getExtBbox().trim().isEmpty()) {
|
||||
synthesisLog.setExtBbox(request.getExtBbox());
|
||||
log.info("设置动态区域坐标: {}", request.getExtBbox());
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始视频合成,请求ID: {}, 图片URL: {}, 音频URL: {}",
|
||||
requestId, request.getImageUrl(), request.getAudioUrl());
|
||||
@@ -110,6 +126,7 @@ public class VideoSynthesisServiceImpl implements IVideoSynthesisService {
|
||||
|
||||
// 生成临时访问URL(7天有效期)
|
||||
try {
|
||||
log.info("即将用音频名称: {}, 生成临时访问URL", synthesisLog.getVideoName());
|
||||
String tempUrl = generateTempVideoUrl(synthesisLog.getVideoName(), requestId);
|
||||
synthesisLog.setVideoTempUrl(tempUrl);
|
||||
log.info("生成临时访问URL: {}", tempUrl);
|
||||
@@ -199,14 +216,8 @@ public class VideoSynthesisServiceImpl implements IVideoSynthesisService {
|
||||
*/
|
||||
private String generateTempVideoUrl(String videoName, String requestId) {
|
||||
try {
|
||||
// 构建临时访问URL
|
||||
// 格式: https://your-domain.com/video/temp/{requestId}?name={videoName}
|
||||
String baseUrl = "https://your-domain.com"; // 这里应该从配置文件读取
|
||||
String encodedVideoName = java.net.URLEncoder.encode(videoName, "UTF-8");
|
||||
String tempUrl = String.format("%s/video/temp/%s?name=%s", baseUrl, requestId, encodedVideoName);
|
||||
|
||||
log.info("生成临时视频URL: 视频名称={}, 请求ID={}, URL={}", videoName, requestId, tempUrl);
|
||||
return tempUrl;
|
||||
|
||||
return minIOUrlGenerator.generateTempUrl(videoName, 7, TimeUnit.DAYS).getUrl();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成临时视频URL失败: {}", e.getMessage(), e);
|
||||
|
||||
@@ -70,6 +70,10 @@ public interface IMenuService extends IService<Menu> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ spring:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -265,3 +265,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ AFTER `sales_name`;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,3 +14,6 @@ ADD INDEX `idx_audio_name` (`audio_name`);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -144,3 +144,6 @@ public class TtsRequestLogShortUrlTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user