439 lines
19 KiB
Java
439 lines
19 KiB
Java
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<FaceDetectLogMapper, FaceDetectLog> 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<String, Object> requestBody = new HashMap<>();
|
||
requestBody.put("model", modelName);
|
||
|
||
if (modelName.equals("emo-detect-v1") || modelName.equals("emoji-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);
|
||
|
||
// 设置请求头
|
||
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);
|
||
ResponseEntity<String> 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<FaceDetectLog> getPageList(
|
||
Integer current, Integer size, Boolean success, String startTime, String endTime,
|
||
String ownerName, String ownerPhone, String avatarName) {
|
||
|
||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<FaceDetectLog> page =
|
||
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(current, size);
|
||
|
||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FaceDetectLog> 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<String, Object> getStatistics(String startTime, String endTime) {
|
||
Map<String, Object> statistics = new HashMap<>();
|
||
|
||
// 构建查询条件
|
||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FaceDetectLog> 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<FaceDetectLog> 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<FaceDetectLog> 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;
|
||
}
|
||
} |