头像检测。否符合要求

This commit is contained in:
spllzh
2025-10-05 16:29:45 +08:00
parent 6f96287920
commit 725a5294d0
32 changed files with 1024 additions and 0 deletions

View File

@@ -129,5 +129,6 @@ public class PasswordUtil {

View File

@@ -113,5 +113,6 @@ public class ServiceManager {

View File

@@ -91,5 +91,6 @@ public class AliyunConfig {

View File

@@ -0,0 +1,169 @@
package com.rj.controller;
import com.rj.service.IFaceDetectLogService;
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 java.util.HashMap;
import java.util.Map;
/**
* 人脸检测控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/face-detect")
@Tag(name = "人脸检测", description = "人脸检测相关接口")
public class FaceDetectController {
@Autowired
private IFaceDetectLogService faceDetectLogService;
/**
* 人脸检测接口
*/
@PostMapping("/detect")
@Operation(summary = "人脸检测", description = "检测图片中的人脸信息")
public ResponseEntity<Map<String, Object>> detectFace(
@Parameter(description = "图片URL", required = true)
@RequestParam String imageUrl) {
Map<String, Object> result = new HashMap<>();
try {
log.info("开始人脸检测图片URL: {}", imageUrl);
// 执行人脸检测
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(imageUrl);
result.put("success", detectLog.getSuccess());
result.put("message", detectLog.getSuccess() ? "人脸检测成功" : "人脸检测失败");
result.put("imageUrl", imageUrl);
result.put("requestId", detectLog.getRequestId());
result.put("faceCount", detectLog.getFaceCount());
result.put("processingTimeMs", detectLog.getProcessingTimeMs());
if (!detectLog.getSuccess()) {
result.put("errorMessage", detectLog.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);
}
}
/**
* 分页查询人脸检测日志
*/
@GetMapping("/logs")
@Operation(summary = "分页查询人脸检测日志", description = "分页查询人脸检测日志列表")
public ResponseEntity<Map<String, Object>> getLogs(
@Parameter(description = "页码", example = "1")
@RequestParam(defaultValue = "1") Integer current,
@Parameter(description = "每页大小", example = "10")
@RequestParam(defaultValue = "10") Integer size,
@Parameter(description = "是否成功")
@RequestParam(required = false) Boolean success,
@Parameter(description = "开始时间")
@RequestParam(required = false) String startTime,
@Parameter(description = "结束时间")
@RequestParam(required = false) String endTime) {
Map<String, Object> result = new HashMap<>();
try {
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.rj.entity.FaceDetectLog> page =
faceDetectLogService.getPageList(current, size, success, startTime, endTime);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", page.getRecords());
result.put("total", page.getTotal());
result.put("current", page.getCurrent());
result.put("size", page.getSize());
result.put("pages", page.getPages());
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("/logs/{id}")
@Operation(summary = "查询日志详情", description = "根据ID查询人脸检测日志详情")
public ResponseEntity<Map<String, Object>> getLogById(
@Parameter(description = "日志ID", required = true)
@PathVariable Long id) {
Map<String, Object> result = new HashMap<>();
try {
log.info("查询人脸检测日志详情ID: {}", id);
com.rj.entity.FaceDetectLog log = faceDetectLogService.getById(id);
if (log != null) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", log);
} else {
result.put("success", false);
result.put("message", "日志不存在");
}
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);
}
}
/**
* 查询人脸检测统计信息
*/
@GetMapping("/statistics")
@Operation(summary = "查询统计信息", description = "查询人脸检测统计信息")
public ResponseEntity<Map<String, Object>> getStatistics(
@Parameter(description = "开始时间")
@RequestParam(required = false) String startTime,
@Parameter(description = "结束时间")
@RequestParam(required = false) String endTime) {
Map<String, Object> result = new HashMap<>();
try {
Map<String, Object> statistics = faceDetectLogService.getStatistics(startTime, endTime);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", statistics);
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);
}
}
}

View File

@@ -212,5 +212,6 @@ public class AuthController {

View File

@@ -351,5 +351,6 @@ public class MenuController {

View File

@@ -321,5 +321,6 @@ public class RoleController {

View File

@@ -327,5 +327,6 @@ public class UserRoleController {

View File

@@ -94,5 +94,6 @@ public class DifyWorkflowResponseDto {

View File

@@ -0,0 +1,108 @@
package com.rj.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 人脸检测日志实体
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("face_detect_log")
public class FaceDetectLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 请求ID
*/
@TableField("request_id")
private String requestId;
/**
* 使用的模型
*/
@TableField("model")
private String model;
/**
* 图片URL
*/
@TableField("image_url")
private String imageUrl;
/**
* 请求时间
*/
@TableField("request_time")
private LocalDateTime requestTime;
/**
* 响应时间
*/
@TableField("response_time")
private LocalDateTime responseTime;
/**
* HTTP状态码
*/
@TableField("status_code")
private Integer statusCode;
/**
* 是否成功
*/
@TableField("success")
private Boolean success;
/**
* 检测到的人脸数量
*/
@TableField("face_count")
private Integer faceCount;
/**
* 完整响应数据
*/
@TableField("response_data")
private String responseData;
/**
* 错误信息
*/
@TableField("error_message")
private String errorMessage;
/**
* 处理时间(毫秒)
*/
@TableField("processing_time_ms")
private Long processingTimeMs;
/**
* 创建时间
*/
@TableField("created_at")
private LocalDateTime createdAt;
/**
* 更新时间
*/
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -37,5 +37,6 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -0,0 +1,12 @@
package com.rj.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rj.entity.FaceDetectLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 人脸检测日志Mapper
*/
@Mapper
public interface FaceDetectLogMapper extends BaseMapper<FaceDetectLog> {
}

View File

@@ -78,5 +78,6 @@ public class LoginResponse {

View File

@@ -97,5 +97,6 @@ public class AudioStatisticsScheduler {

View File

@@ -0,0 +1,45 @@
package com.rj.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.rj.entity.FaceDetectLog;
import java.util.Map;
/**
* 人脸检测日志服务接口
*/
public interface IFaceDetectLogService extends IService<FaceDetectLog> {
/**
* 保存人脸检测日志
* @param faceDetectLog 日志对象
* @return 是否保存成功
*/
boolean saveFaceDetectLog(FaceDetectLog faceDetectLog);
/**
* 执行人脸检测并保存日志
* @param imageUrl 图片URL
* @return 人脸检测日志对象
*/
FaceDetectLog detectFaceAndSave(String imageUrl);
/**
* 分页查询人脸检测日志
* @param current 当前页
* @param size 每页大小
* @param success 成功状态筛选
* @param startTime 开始时间
* @param endTime 结束时间
* @return 分页结果
*/
com.baomidou.mybatisplus.extension.plugins.pagination.Page<FaceDetectLog> getPageList(
Integer current, Integer size, Boolean success, String startTime, String endTime);
/**
* 获取统计信息
* @param startTime 开始时间
* @param endTime 结束时间
* @return 统计信息
*/
Map<String, Object> getStatistics(String startTime, String endTime);
}

View File

@@ -58,5 +58,6 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -93,5 +93,6 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -0,0 +1,338 @@
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";
@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) {
if (apiKey == null || apiKey.trim().isEmpty()) {
log.error("DASHSCOPE_API_KEY未配置");
return createErrorLog(imageUrl, "DASHSCOPE_API_KEY未配置");
}
// 创建日志记录
FaceDetectLog faceDetectLog = new FaceDetectLog();
String requestId = UUID.randomUUID().toString();
LocalDateTime requestTime = LocalDateTime.now();
faceDetectLog.setRequestId(requestId);
faceDetectLog.setModel("liveportrait-detect");
faceDetectLog.setImageUrl(imageUrl);
faceDetectLog.setRequestTime(requestTime);
long startTime = System.currentTimeMillis();
try {
// 创建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", "liveportrait-detect");
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);
// 创建请求实体
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
// 发送请求
log.info("发送人脸检测请求请求ID: {}, 图片URL: {}", requestId, imageUrl);
ResponseEntity<String> response = restTemplate.exchange(
API_URL,
HttpMethod.POST,
requestEntity,
String.class
);
// 计算处理时间
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("人脸检测响应,状态码: {}, 处理时间: {}ms", response.getStatusCode(), processingTime);
// 解析JSON响应
if (response.getStatusCode() == HttpStatus.OK) {
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);
} 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);
log.info("检测到人脸数量: {}", faceCount);
for (int i = 0; i < faces.size(); i++) {
JsonNode face = faces.get(i);
log.debug("人脸 {}: {}", i + 1, face.toString());
}
}
// 记录请求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);
}
}
} 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) {
FaceDetectLog faceDetectLog = new FaceDetectLog();
faceDetectLog.setRequestId(UUID.randomUUID().toString());
faceDetectLog.setModel("liveportrait-detect");
faceDetectLog.setImageUrl(imageUrl);
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) {
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);
}
// 按请求时间倒序排列
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;
}
}

View File

@@ -60,5 +60,6 @@ public interface IMenuService extends IService<Menu> {

View File

@@ -60,5 +60,6 @@ public interface IUserRoleService extends IService<UserRole> {

View File

@@ -64,5 +64,6 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM

View File

@@ -64,5 +64,6 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR

View File

@@ -64,5 +64,6 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i

View File

@@ -74,5 +74,6 @@ spring:

View File

@@ -1,4 +1,9 @@
# DashScope API配置
dashscope:
api:
key: ${DASHSCOPE_API_KEY}
langchain4j:
open-ai:
chat-model:

View File

@@ -252,3 +252,4 @@
</script>
</body>
</html>

View File

@@ -12,3 +12,4 @@ AFTER `sales_name`;

View File

@@ -0,0 +1,22 @@
-- 人脸检测日志表
CREATE TABLE IF NOT EXISTS `face_detect_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`request_id` varchar(64) NOT NULL COMMENT '请求ID',
`model` varchar(100) NOT NULL COMMENT '使用的模型',
`image_url` varchar(500) NOT NULL COMMENT '图片URL',
`request_time` datetime NOT NULL COMMENT '请求时间',
`response_time` datetime DEFAULT NULL COMMENT '响应时间',
`status_code` int(11) DEFAULT NULL COMMENT 'HTTP状态码',
`success` tinyint(1) DEFAULT 0 COMMENT '是否成功',
`face_count` int(11) DEFAULT 0 COMMENT '检测到的人脸数量',
`response_data` longtext COMMENT '完整响应数据',
`error_message` text COMMENT '错误信息',
`processing_time_ms` bigint(20) DEFAULT NULL COMMENT '处理时间(毫秒)',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_request_id` (`request_id`),
KEY `idx_request_time` (`request_time`),
KEY `idx_success` (`success`),
KEY `idx_face_count` (`face_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='人脸检测日志表';