package com.rj.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.rj.entity.FaceDetectLog; import com.rj.mapper.FaceDetectLogMapper; import com.rj.service.IFaceDetectLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * 图像检测日志服务实现 */ @Slf4j @Service public class FaceDetectLogServiceImpl extends ServiceImpl implements IFaceDetectLogService { private static final String API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/face-detect"; private static final String API_URL_AA = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/aa-detect"; @Value("${dashscope.api.key:}") private String apiKey; private final RestTemplate restTemplate; private final ObjectMapper objectMapper; public FaceDetectLogServiceImpl() { this.restTemplate = new RestTemplate(); this.objectMapper = new ObjectMapper(); } @Override public boolean saveFaceDetectLog(FaceDetectLog faceDetectLog) { try { return save(faceDetectLog); } catch (Exception e) { log.error("保存图像检测日志失败: {}", e.getMessage(), e); return false; } } /** * 执行图像检测并保存日志 */ public FaceDetectLog detectFaceAndSave(String imageUrl) { return detectFaceAndSave(imageUrl, null, null, null); } /** * 执行图像检测并保存日志(带扩展信息) */ 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); } // 创建日志记录 FaceDetectLog faceDetectLog = new FaceDetectLog(); String requestId = UUID.randomUUID().toString(); LocalDateTime requestTime = LocalDateTime.now(); faceDetectLog.setRequestId(requestId); // 设置模型名称,如果未提供则使用默认值 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); faceDetectLog.setAvatarName(avatarName); faceDetectLog.setRequestTime(requestTime); long startTime = System.currentTimeMillis(); try { // 创建请求体 Map requestBody = new HashMap<>(); requestBody.put("model", modelName); if (modelName.equals("emo-detect-v1") || modelName.equals("emoji-detect-v1")){ Map ratioo = new HashMap<>(); ratioo.put("ratio", "1:1"); requestBody.put("parameters", ratioo); } Map input = new HashMap<>(); input.put("image_url", imageUrl); requestBody.put("input", input); // 设置请求头 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> requestEntity = new HttpEntity<>(requestBody, headers); ResponseEntity response = null; if ( modelName.equals("animate-anyone-detect-gen2")){ response = restTemplate.exchange( API_URL_AA, HttpMethod.POST, requestEntity, String.class ); }else { response = restTemplate.exchange( API_URL, HttpMethod.POST, requestEntity, String.class ); } // 发送请求 log.info("发送图像检测请求,response {}, 图片URL: {}", response, imageUrl); // 计算处理时间 long endTime = System.currentTimeMillis(); long processingTime = endTime - startTime; // 更新日志记录 faceDetectLog.setResponseTime(LocalDateTime.now()); faceDetectLog.setStatusCode(response.getStatusCode().value()); faceDetectLog.setProcessingTimeMs(processingTime); faceDetectLog.setResponseData(response.getBody()); // 处理响应 log.info("图像检测响应,response: {}, 处理时间: {}ms", response.toString(), processingTime); // 解析JSON响应 if (response.getStatusCode() == HttpStatus.OK) { faceDetectLog.setSuccess( true); JsonNode jsonResponse = objectMapper.readTree(response.getBody()); // 根据pass字段判断是否成功 boolean isSuccess = false; if (jsonResponse.has("output") && jsonResponse.get("output").has("pass")) { isSuccess = jsonResponse.get("output").get("pass").asBoolean(); faceDetectLog.setSuccess(isSuccess); log.info("图像检测结果 - 头像 pass: {}", isSuccess); } if (jsonResponse.has("output") && jsonResponse.get("output").has("check_pass")) { isSuccess = jsonResponse.get("output").get("check_pass").asBoolean(); faceDetectLog.setSuccess(isSuccess); log.info("图像检测结果 (animate-anyone-detect-gen2 舞动人像 )- check_pass: {}", isSuccess); } // 检查是否有检测到图像 int faceCount = 0; if (jsonResponse.has("output")) { JsonNode output = jsonResponse.get("output"); // 根据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); } } } //emoji 开始 if (jsonResponse.has("output") && jsonResponse.get("output").has("ext_bbox_face")) { String ext_bbox = jsonResponse.get("output").get("ext_bbox_face").toString(); faceDetectLog.setExtBbox(ext_bbox); } if (jsonResponse.has("output") && jsonResponse.get("output").has("bbox_face")) { String bboxFace = jsonResponse.get("output").get("bbox_face").toString(); faceDetectLog.setFaceBbox(bboxFace); } //emoji 结束 // 记录请求ID if (jsonResponse.has("request_id")) { String apiRequestId = jsonResponse.get("request_id").asText(); log.info("API请求ID: {}", apiRequestId); } // 记录使用情况 if (jsonResponse.has("usage")) { JsonNode usage = jsonResponse.get("usage"); 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,直接标记为失败 faceDetectLog.setSuccess(false); } // 保存到数据库 boolean saved = saveFaceDetectLog(faceDetectLog); if (saved) { log.info("图像检测日志已保存,请求ID: {}", requestId); } else { log.error("保存图像检测日志失败,请求ID: {}", requestId); } return faceDetectLog; } catch (HttpClientErrorException e) { return handleError(faceDetectLog, e, "客户端错误 (4xx): " + e.getStatusCode(), startTime); } catch (HttpServerErrorException e) { return handleError(faceDetectLog, e, "服务器错误 (5xx): " + e.getStatusCode(), startTime); } catch (Exception e) { return handleError(faceDetectLog, e, "图像检测请求失败: " + e.getMessage(), startTime); } } /** * 处理错误并保存到数据库 */ private FaceDetectLog handleError(FaceDetectLog faceDetectLog, Exception e, String errorMessage, long startTime) { long endTime = System.currentTimeMillis(); long processingTime = endTime - startTime; faceDetectLog.setResponseTime(LocalDateTime.now()); faceDetectLog.setSuccess(false); faceDetectLog.setProcessingTimeMs(processingTime); faceDetectLog.setErrorMessage(errorMessage); if (e instanceof HttpClientErrorException) { faceDetectLog.setStatusCode(((HttpClientErrorException) e).getStatusCode().value()); faceDetectLog.setResponseData(((HttpClientErrorException) e).getResponseBodyAsString()); } else if (e instanceof HttpServerErrorException) { faceDetectLog.setStatusCode(((HttpServerErrorException) e).getStatusCode().value()); faceDetectLog.setResponseData(((HttpServerErrorException) e).getResponseBodyAsString()); } log.error("图像检测失败: {}", errorMessage); log.error("错误详情: {}", e.getMessage()); // 保存错误日志到数据库 try { saveFaceDetectLog(faceDetectLog); log.info("错误日志已保存,请求ID: {}", faceDetectLog.getRequestId()); } catch (Exception saveException) { log.error("保存错误日志失败: {}", saveException.getMessage()); } return faceDetectLog; } /** * 创建错误日志 */ private FaceDetectLog createErrorLog(String imageUrl, String errorMessage) { return createErrorLog(imageUrl, errorMessage, null, null, null); } /** * 创建错误日志(带扩展信息) */ private FaceDetectLog createErrorLog(String imageUrl, String errorMessage, String ownerName, String ownerPhone, String avatarName) { FaceDetectLog faceDetectLog = new FaceDetectLog(); faceDetectLog.setRequestId(UUID.randomUUID().toString()); faceDetectLog.setModel("liveportrait-detect"); faceDetectLog.setImageUrl(imageUrl); faceDetectLog.setOwnerName(ownerName); faceDetectLog.setOwnerPhone(ownerPhone); faceDetectLog.setAvatarName(avatarName); faceDetectLog.setRequestTime(LocalDateTime.now()); faceDetectLog.setResponseTime(LocalDateTime.now()); faceDetectLog.setSuccess(false); faceDetectLog.setErrorMessage(errorMessage); faceDetectLog.setFaceCount(0); try { saveFaceDetectLog(faceDetectLog); } catch (Exception e) { log.error("保存错误日志失败: {}", e.getMessage()); } return faceDetectLog; } /** * 分页查询图像检测日志 */ @Override public com.baomidou.mybatisplus.extension.plugins.pagination.Page getPageList( Integer current, Integer size, Boolean success, String startTime, String endTime, String ownerName, String ownerPhone, String avatarName) { com.baomidou.mybatisplus.extension.plugins.pagination.Page page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(current, size); com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper queryWrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); // 添加查询条件 if (success != null) { queryWrapper.eq(FaceDetectLog::getSuccess, success); } if (startTime != null && !startTime.trim().isEmpty()) { queryWrapper.ge(FaceDetectLog::getRequestTime, startTime); } if (endTime != null && !endTime.trim().isEmpty()) { queryWrapper.le(FaceDetectLog::getRequestTime, endTime); } if (ownerName != null && !ownerName.trim().isEmpty()) { queryWrapper.like(FaceDetectLog::getOwnerName, ownerName); } if (ownerPhone != null && !ownerPhone.trim().isEmpty()) { queryWrapper.like(FaceDetectLog::getOwnerPhone, ownerPhone); } if (avatarName != null && !avatarName.trim().isEmpty()) { queryWrapper.like(FaceDetectLog::getAvatarName, avatarName); } // 按请求时间倒序排列 queryWrapper.orderByDesc(FaceDetectLog::getRequestTime); return page(page, queryWrapper); } /** * 获取统计信息 */ @Override public Map getStatistics(String startTime, String endTime) { Map statistics = new HashMap<>(); // 构建查询条件 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper queryWrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); // 时间范围筛选 if (startTime != null && !startTime.trim().isEmpty()) { queryWrapper.ge(FaceDetectLog::getRequestTime, startTime); } if (endTime != null && !endTime.trim().isEmpty()) { queryWrapper.le(FaceDetectLog::getRequestTime, endTime); } // 查询总数 long totalCount = count(queryWrapper); // 查询成功数 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper successWrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); // 时间范围筛选 if (startTime != null && !startTime.trim().isEmpty()) { successWrapper.ge(FaceDetectLog::getRequestTime, startTime); } if (endTime != null && !endTime.trim().isEmpty()) { successWrapper.le(FaceDetectLog::getRequestTime, endTime); } successWrapper.eq(FaceDetectLog::getSuccess, true); long successCount = count(successWrapper); // 查询失败数 long failureCount = totalCount - successCount; // 计算成功率 double successRate = totalCount > 0 ? (double) successCount / totalCount * 100 : 0; // 查询平均处理时间 java.util.List logs = list(queryWrapper); double avgProcessingTime = logs.stream() .filter(log -> log.getProcessingTimeMs() != null) .mapToLong(FaceDetectLog::getProcessingTimeMs) .average() .orElse(0.0); // 查询总图像数量 int totalFaceCount = logs.stream() .filter(log -> log.getFaceCount() != null) .mapToInt(FaceDetectLog::getFaceCount) .sum(); // 构建统计结果 statistics.put("totalCount", totalCount); statistics.put("successCount", successCount); statistics.put("failureCount", failureCount); statistics.put("successRate", Math.round(successRate * 100.0) / 100.0); statistics.put("avgProcessingTimeMs", Math.round(avgProcessingTime)); statistics.put("totalFaceCount", totalFaceCount); log.info("统计信息查询完成,总数: {}, 成功数: {}, 失败数: {}, 成功率: {}%", totalCount, successCount, failureCount, successRate); return statistics; } }