调整 音频合成
This commit is contained in:
@@ -126,6 +126,11 @@ public class PasswordUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -110,6 +110,11 @@ public class ServiceManager {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -88,6 +88,11 @@ public class AliyunConfig {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,48 +1,26 @@
|
||||
package com.rj.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* RestTemplate 配置类
|
||||
* RestTemplate配置类
|
||||
*
|
||||
* @author rj
|
||||
* @date 2025-01-02
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
/**
|
||||
* 创建RestTemplate Bean
|
||||
* 支持音频流响应处理
|
||||
*
|
||||
* @return RestTemplate实例
|
||||
*/
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(30000); // 连接超时30秒
|
||||
factory.setReadTimeout(60000); // 读取超时60秒
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(factory);
|
||||
|
||||
// 添加支持音频流的HttpMessageConverter
|
||||
ByteArrayHttpMessageConverter audioConverter = new ByteArrayHttpMessageConverter();
|
||||
audioConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("audio/mpeg")));
|
||||
|
||||
List<org.springframework.http.converter.HttpMessageConverter<?>> messageConverters =
|
||||
new ArrayList<>(restTemplate.getMessageConverters());
|
||||
messageConverters.add(audioConverter);
|
||||
restTemplate.setMessageConverters(messageConverters);
|
||||
|
||||
return restTemplate;
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.rj.controller;
|
||||
|
||||
import com.rj.service.IFaceDetectLogService;
|
||||
import com.rj.service.MinIOService;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -8,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -24,6 +27,12 @@ public class FaceDetectController {
|
||||
@Autowired
|
||||
private IFaceDetectLogService faceDetectLogService;
|
||||
|
||||
@Autowired
|
||||
private MinIOService minioService;
|
||||
|
||||
@Autowired
|
||||
private MinIOUrlGenerator urlGenerator;
|
||||
|
||||
/**
|
||||
* 人脸检测接口
|
||||
*/
|
||||
@@ -31,7 +40,13 @@ public class FaceDetectController {
|
||||
@Operation(summary = "人脸检测", description = "检测图片中的人脸信息")
|
||||
public ResponseEntity<Map<String, Object>> detectFace(
|
||||
@Parameter(description = "图片URL", required = true)
|
||||
@RequestParam String imageUrl) {
|
||||
@RequestParam String imageUrl,
|
||||
@Parameter(description = "所属人姓名", required = false)
|
||||
@RequestParam(value = "ownerName", required = false) String ownerName,
|
||||
@Parameter(description = "所属人电话", required = false)
|
||||
@RequestParam(value = "ownerPhone", required = false) String ownerPhone,
|
||||
@Parameter(description = "头像名称", required = false)
|
||||
@RequestParam(value = "avatarName", required = false) String avatarName) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
@@ -39,7 +54,7 @@ public class FaceDetectController {
|
||||
log.info("开始人脸检测,图片URL: {}", imageUrl);
|
||||
|
||||
// 执行人脸检测
|
||||
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(imageUrl);
|
||||
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(imageUrl, ownerName, ownerPhone, avatarName);
|
||||
|
||||
result.put("success", detectLog.getSuccess());
|
||||
result.put("message", detectLog.getSuccess() ? "人脸检测成功" : "人脸检测失败");
|
||||
@@ -47,6 +62,9 @@ public class FaceDetectController {
|
||||
result.put("requestId", detectLog.getRequestId());
|
||||
result.put("faceCount", detectLog.getFaceCount());
|
||||
result.put("processingTimeMs", detectLog.getProcessingTimeMs());
|
||||
result.put("ownerName", detectLog.getOwnerName());
|
||||
result.put("ownerPhone", detectLog.getOwnerPhone());
|
||||
result.put("avatarName", detectLog.getAvatarName());
|
||||
|
||||
if (!detectLog.getSuccess()) {
|
||||
result.put("errorMessage", detectLog.getErrorMessage());
|
||||
@@ -63,6 +81,135 @@ public class FaceDetectController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 头衔上传接口
|
||||
* 实现:文件上传 -> 生成短链接 -> 人脸检测
|
||||
*/
|
||||
@PostMapping("/avatar/upload")
|
||||
@Operation(summary = "头衔上传", description = "上传头像文件,生成短链接并进行人脸检测")
|
||||
public ResponseEntity<Map<String, Object>> uploadAvatar(
|
||||
@Parameter(description = "头像文件", required = true)
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "用户ID", required = false)
|
||||
@RequestParam(value = "userId", required = false) String userId,
|
||||
@Parameter(description = "所属人姓名", required = false)
|
||||
@RequestParam(value = "ownerName", required = false) String ownerName,
|
||||
@Parameter(description = "所属人电话", required = false)
|
||||
@RequestParam(value = "ownerPhone", required = false) String ownerPhone,
|
||||
@Parameter(description = "头像名称", required = false)
|
||||
@RequestParam(value = "avatarName", required = false) String avatarName,
|
||||
@Parameter(description = "短链接有效期(秒),默认1小时", required = false)
|
||||
@RequestParam(value = "expiresInSeconds", defaultValue = "3600") int expiresInSeconds) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
log.info("开始处理头衔上传,文件名: {}, 大小: {} bytes, 用户ID: {}",
|
||||
file.getOriginalFilename(), file.getSize(), userId);
|
||||
expiresInSeconds = 30*24*3600; // 默认30天
|
||||
// 1. 验证文件
|
||||
if (file.isEmpty()) {
|
||||
result.put("success", false);
|
||||
result.put("message", "文件不能为空");
|
||||
return ResponseEntity.badRequest().body(result);
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || (!contentType.startsWith("image/"))) {
|
||||
result.put("success", false);
|
||||
result.put("message", "只支持图片文件格式");
|
||||
return ResponseEntity.badRequest().body(result);
|
||||
}
|
||||
|
||||
// 验证文件大小(限制为10MB,与Spring配置保持一致)
|
||||
if (file.getSize() > 10 * 1024 * 1024) {
|
||||
result.put("success", false);
|
||||
result.put("message", "文件大小不能超过10MB");
|
||||
return ResponseEntity.badRequest().body(result);
|
||||
}
|
||||
|
||||
// 2. 上传文件到MinIO
|
||||
log.info("开始上传文件到MinIO...");
|
||||
String fileUrl = minioService.uploadFile(file);
|
||||
log.info("文件上传成功,URL: {}", fileUrl);
|
||||
|
||||
// 3. 生成短链接(临时访问URL)
|
||||
log.info("开始生成短链接,有效期: {}秒", expiresInSeconds);
|
||||
String fileName = extractFileNameFromUrl(fileUrl);
|
||||
MinIOUrlGenerator.UrlInfo urlInfo = urlGenerator.generateTempUrl(fileName, expiresInSeconds);
|
||||
|
||||
if (!urlInfo.isSuccess()) {
|
||||
result.put("success", false);
|
||||
result.put("message", "生成短链接失败: " + urlInfo.getErrorMessage());
|
||||
return ResponseEntity.internalServerError().body(result);
|
||||
}
|
||||
|
||||
String shortUrl = urlInfo.getUrl();
|
||||
log.info("短链接生成成功: {}", shortUrl);
|
||||
|
||||
// 4. 进行人脸检测
|
||||
log.info("开始进行人脸检测...");
|
||||
com.rj.entity.FaceDetectLog detectLog = faceDetectLogService.detectFaceAndSave(shortUrl, ownerName, ownerPhone, avatarName);
|
||||
|
||||
// 5. 构建响应结果
|
||||
result.put("success", true);
|
||||
result.put("message", "头衔上传处理完成");
|
||||
result.put("originalFileName", file.getOriginalFilename());
|
||||
result.put("fileSize", file.getSize());
|
||||
result.put("fileUrl", fileUrl);
|
||||
result.put("shortUrl", shortUrl);
|
||||
result.put("shortUrlExpiresAt", urlInfo.getFormattedExpiresAt());
|
||||
result.put("shortUrlExpiresInSeconds", expiresInSeconds);
|
||||
result.put("userId", userId);
|
||||
result.put("ownerName", ownerName);
|
||||
result.put("ownerPhone", ownerPhone);
|
||||
result.put("avatarName", avatarName);
|
||||
|
||||
// 人脸检测结果
|
||||
result.put("faceDetection", Map.of(
|
||||
"success", detectLog.getSuccess(),
|
||||
"faceCount", detectLog.getFaceCount(),
|
||||
"processingTimeMs", detectLog.getProcessingTimeMs(),
|
||||
"requestId", detectLog.getRequestId()
|
||||
));
|
||||
|
||||
if (!detectLog.getSuccess()) {
|
||||
result.put("faceDetectionError", detectLog.getErrorMessage());
|
||||
log.warn("人脸检测失败: {}", detectLog.getErrorMessage());
|
||||
} else {
|
||||
log.info("人脸检测成功,检测到 {} 张人脸", detectLog.getFaceCount());
|
||||
}
|
||||
|
||||
log.info("头衔上传处理完成,文件URL: {}, 短链接: {}", fileUrl, shortUrl);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从URL中提取文件名
|
||||
*/
|
||||
private String extractFileNameFromUrl(String fileUrl) {
|
||||
if (fileUrl == null || fileUrl.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 从URL中提取文件名部分
|
||||
// 例如: http://101.35.52.237:19005/car/filename.jpg -> filename.jpg
|
||||
int lastSlashIndex = fileUrl.lastIndexOf('/');
|
||||
if (lastSlashIndex != -1 && lastSlashIndex < fileUrl.length() - 1) {
|
||||
return fileUrl.substring(lastSlashIndex + 1);
|
||||
}
|
||||
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询人脸检测日志
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rj.controller;
|
||||
|
||||
import com.rj.service.ITtsRequestLogService;
|
||||
import com.rj.service.MinIOService;
|
||||
import com.rj.utils.MinIOUtil;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
@@ -379,7 +380,8 @@ public class MinIOController {
|
||||
return ResponseEntity.internalServerError().body(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ITtsRequestLogService ttsRequestLogService;
|
||||
@GetMapping("/temp-url/{fileName}/{expiresInSeconds}")
|
||||
@Operation(summary = "生成临时访问URL(指定过期时间)", description = "为指定文件生成临时访问URL,可指定过期时间")
|
||||
public ResponseEntity<Map<String, Object>> generateTempUrlWithExpires(
|
||||
@@ -397,7 +399,7 @@ public class MinIOController {
|
||||
}
|
||||
|
||||
MinIOUrlGenerator.UrlInfo urlInfo = urlGenerator.generateTempUrl(fileName, expiresInSeconds);
|
||||
|
||||
ttsRequestLogService.setShortUrlByAudioName(urlInfo);
|
||||
if (urlInfo.isSuccess()) {
|
||||
result.put("success", true);
|
||||
result.put("message", "临时访问URL生成成功");
|
||||
|
||||
@@ -74,7 +74,7 @@ public class TtsRequestLogController {
|
||||
}
|
||||
|
||||
// 按请求时间倒序排列
|
||||
queryWrapper.orderByDesc("request_time");
|
||||
queryWrapper.orderByDesc("update_time");
|
||||
|
||||
// 执行查询
|
||||
Page<TtsRequestLog> pageResult = ttsRequestLogMapper.selectPage(page, queryWrapper);
|
||||
|
||||
@@ -212,6 +212,8 @@ public class AuthController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -348,6 +348,11 @@ public class MenuController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -318,6 +318,11 @@ public class RoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -155,6 +155,31 @@ public class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据电话查询用户
|
||||
*/
|
||||
@GetMapping("/getByPhone")
|
||||
@Operation(summary = "根据电话查询用户", description = "根据用户电话查询用户信息")
|
||||
public ResponseEntity<Map<String, Object>> getUserByPhone(
|
||||
@Parameter(description = "用户电话", required = true)
|
||||
@RequestParam String phone) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getPhone, phone);
|
||||
List<User> userList = userService.list(queryWrapper);
|
||||
result.put("success", true);
|
||||
result.put("message", "查询成功");
|
||||
result.put("data", userList);
|
||||
result.put("count", userList.size());
|
||||
return ResponseEntity.ok(result);
|
||||
} catch (Exception e) {
|
||||
result.put("success", false);
|
||||
result.put("message", "查询异常:" + e.getMessage());
|
||||
return ResponseEntity.internalServerError().body(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询用户列表
|
||||
*/
|
||||
@@ -168,7 +193,9 @@ public class UserController {
|
||||
@Parameter(description = "用户名(模糊查询)")
|
||||
@RequestParam(required = false) String userName,
|
||||
@Parameter(description = "邮箱(模糊查询)")
|
||||
@RequestParam(required = false) String email) {
|
||||
@RequestParam(required = false) String email,
|
||||
@Parameter(description = "电话(模糊查询)")
|
||||
@RequestParam(required = false) String phone) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
Page<User> page = new Page<>(current, size);
|
||||
@@ -181,6 +208,9 @@ public class UserController {
|
||||
if (email != null && !email.trim().isEmpty()) {
|
||||
queryWrapper.like(User::getEmail, email);
|
||||
}
|
||||
if (phone != null && !phone.trim().isEmpty()) {
|
||||
queryWrapper.like(User::getPhone, phone);
|
||||
}
|
||||
|
||||
// 按用户ID倒序排列
|
||||
queryWrapper.orderByDesc(User::getUserId);
|
||||
|
||||
@@ -324,6 +324,11 @@ public class UserRoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ public class DifyWorkflowResponseDto {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -98,6 +98,11 @@ public class SiliconFlowTtsRequest {
|
||||
* 创建人电话
|
||||
*/
|
||||
private String creatorPhone;
|
||||
|
||||
/**
|
||||
* audioName
|
||||
*/
|
||||
private String audioName;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
|
||||
@@ -23,10 +23,10 @@ public class FaceDetectLog implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
* 主键ID(UUID)
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 请求ID
|
||||
@@ -46,6 +46,24 @@ public class FaceDetectLog implements Serializable {
|
||||
@TableField("image_url")
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 所属人姓名
|
||||
*/
|
||||
@TableField("owner_name")
|
||||
private String ownerName;
|
||||
|
||||
/**
|
||||
* 所属人电话
|
||||
*/
|
||||
@TableField("owner_phone")
|
||||
private String ownerPhone;
|
||||
|
||||
/**
|
||||
* 头像名称
|
||||
*/
|
||||
@TableField("avatar_name")
|
||||
private String avatarName;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
|
||||
@@ -108,6 +108,12 @@ public class TtsRequestLog {
|
||||
@TableField("minio_url")
|
||||
private String minioUrl;
|
||||
|
||||
/**
|
||||
* 音频文件名
|
||||
*/
|
||||
@TableField("audio_name")
|
||||
private String audioName;
|
||||
|
||||
/**
|
||||
* 短链临时访问URL
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,10 @@ public class User implements Serializable {
|
||||
@TableField("email")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户电话")
|
||||
@TableField("phone")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "密码")
|
||||
@TableField("password")
|
||||
private String password;
|
||||
|
||||
@@ -6,11 +6,13 @@ CREATE TABLE `user` (
|
||||
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
|
||||
`user_name` varchar(255) DEFAULT NULL COMMENT '用户名称',
|
||||
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
|
||||
`phone` varchar(20) DEFAULT NULL COMMENT '用户电话',
|
||||
`password` varchar(255) DEFAULT NULL COMMENT '密码',
|
||||
`original_password` varchar(255) DEFAULT NULL COMMENT '原始密码',
|
||||
`token` varchar(255) DEFAULT NULL COMMENT 'token令牌',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
|
||||
PRIMARY KEY (`user_id`)
|
||||
PRIMARY KEY (`user_id`),
|
||||
KEY `idx_phone` (`phone`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
-- 2. 角色表 (role)
|
||||
|
||||
@@ -34,6 +34,11 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ public class LoginResponse {
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@@ -78,6 +81,8 @@ public class LoginResponse {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@ public class AudioStatisticsScheduler {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,16 @@ public interface IFaceDetectLogService extends IService<FaceDetectLog> {
|
||||
*/
|
||||
FaceDetectLog detectFaceAndSave(String imageUrl);
|
||||
|
||||
/**
|
||||
* 执行人脸检测并保存日志(带扩展信息)
|
||||
* @param imageUrl 图片URL
|
||||
* @param ownerName 所属人姓名
|
||||
* @param ownerPhone 所属人电话
|
||||
* @param avatarName 头像名称
|
||||
* @return 人脸检测日志对象
|
||||
*/
|
||||
FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName);
|
||||
|
||||
/**
|
||||
* 分页查询人脸检测日志
|
||||
* @param current 当前页
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.rj.service;
|
||||
|
||||
import com.rj.entity.TtsRequestLog;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
|
||||
/**
|
||||
* TTS请求日志服务接口
|
||||
@@ -25,4 +26,19 @@ public interface ITtsRequestLogService {
|
||||
* @return 请求日志
|
||||
*/
|
||||
TtsRequestLog getTtsRequestLogById(String id);
|
||||
|
||||
/**
|
||||
* 根据音频名称设置短链接
|
||||
*
|
||||
* @return 是否设置成功
|
||||
*/
|
||||
boolean setShortUrlByAudioName(MinIOUrlGenerator.UrlInfo urlInfo );
|
||||
|
||||
/**
|
||||
* 根据音频名称设置短链接(使用默认15分钟过期时间)
|
||||
*
|
||||
* @param audioName 音频名称
|
||||
* @return 是否设置成功
|
||||
*/
|
||||
boolean setShortUrlByAudioName(String audioName);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class SiliconFlowTtsService {
|
||||
|
||||
// 确保voice参数正确设置
|
||||
if (request.getVoice() == null || request.getVoice().trim().isEmpty()) {
|
||||
// 根据模型设置默认voice参数
|
||||
// 根据模型设置默认voice参数 FunAudioLLM/CosyVoice2-0.5B:claire
|
||||
String model = request.getModel();
|
||||
if (model != null && model.contains("fnlp/MOSS-TTSD-v0.5")) {
|
||||
request.setVoice("fnlp/MOSS-TTSD-v0.5:claire");
|
||||
@@ -145,7 +145,7 @@ public class SiliconFlowTtsService {
|
||||
}
|
||||
|
||||
// 保存请求日志到数据库
|
||||
saveTtsRequestLog(requestLog, request, ttsResponse, startTime, null);
|
||||
saveTtsRequestLog(requestLog, request, ttsResponse, startTime, null, fileName);
|
||||
|
||||
log.info("TTS请求成功,音频数据长度: {} bytes, 估算时长: {:.2f}秒, MinIO URL: {}, 临时URL: {}",
|
||||
audioBytes.length, estimatedDuration, minioUrl, tempUrlInfo != null ? tempUrlInfo.getUrl() : "无");
|
||||
@@ -158,9 +158,10 @@ public class SiliconFlowTtsService {
|
||||
} catch (Exception e) {
|
||||
log.error("TTS请求异常: {}", e.getMessage(), e);
|
||||
|
||||
|
||||
// 保存错误日志到数据库
|
||||
SiliconFlowTtsResponse errorResponse = SiliconFlowTtsResponse.error("TTS请求异常: " + e.getMessage());
|
||||
saveTtsRequestLog(requestLog, request, errorResponse, startTime, e.getMessage());
|
||||
saveTtsRequestLog(requestLog, request, errorResponse, startTime, e.getMessage(), null);
|
||||
|
||||
return errorResponse;
|
||||
}
|
||||
@@ -296,9 +297,10 @@ public class SiliconFlowTtsService {
|
||||
* @param response TTS响应
|
||||
* @param startTime 开始时间
|
||||
* @param errorMessage 错误信息
|
||||
* @param audioFileName 音频文件名
|
||||
*/
|
||||
private void saveTtsRequestLog(TtsRequestLog requestLog, SiliconFlowTtsRequest request,
|
||||
SiliconFlowTtsResponse response, long startTime, String errorMessage) {
|
||||
SiliconFlowTtsResponse response, long startTime, String errorMessage, String audioFileName) {
|
||||
try {
|
||||
long processingTime = System.currentTimeMillis() - startTime;
|
||||
|
||||
@@ -315,11 +317,13 @@ public class SiliconFlowTtsService {
|
||||
requestLog.setSampleRate(request.getSampleRate());
|
||||
requestLog.setCreatorName(request.getCreatorName());
|
||||
requestLog.setCreatorPhone(request.getCreatorPhone());
|
||||
requestLog.setAudioName(request.getAudioName());
|
||||
|
||||
// 设置响应信息
|
||||
requestLog.setStatus(response.isSuccess() ? "SUCCESS" : "FAILED");
|
||||
requestLog.setDuration(response.getDuration());
|
||||
requestLog.setMinioUrl(response.getMinioUrl()); // 临时访问URL
|
||||
requestLog.setAudioName(audioFileName); // 音频文件名
|
||||
requestLog.setShortUrl(response.getShortUrl()); // 短链URL(与MinIO URL相同)
|
||||
requestLog.setShortUrlExpireTime(response.getShortUrlExpireTime());
|
||||
requestLog.setProcessingTimeMs(processingTime);
|
||||
|
||||
@@ -55,6 +55,11 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,9 +53,16 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
* 执行人脸检测并保存日志
|
||||
*/
|
||||
public FaceDetectLog detectFaceAndSave(String imageUrl) {
|
||||
return detectFaceAndSave(imageUrl, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行人脸检测并保存日志(带扩展信息)
|
||||
*/
|
||||
public FaceDetectLog detectFaceAndSave(String imageUrl, String ownerName, String ownerPhone, String avatarName) {
|
||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||
log.error("DASHSCOPE_API_KEY未配置");
|
||||
return createErrorLog(imageUrl, "DASHSCOPE_API_KEY未配置");
|
||||
return createErrorLog(imageUrl, "DASHSCOPE_API_KEY未配置", ownerName, ownerPhone, avatarName);
|
||||
}
|
||||
|
||||
// 创建日志记录
|
||||
@@ -66,6 +73,9 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
faceDetectLog.setRequestId(requestId);
|
||||
faceDetectLog.setModel("liveportrait-detect");
|
||||
faceDetectLog.setImageUrl(imageUrl);
|
||||
faceDetectLog.setOwnerName(ownerName);
|
||||
faceDetectLog.setOwnerPhone(ownerPhone);
|
||||
faceDetectLog.setAvatarName(avatarName);
|
||||
faceDetectLog.setRequestTime(requestTime);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -217,10 +227,20 @@ public class FaceDetectLogServiceImpl extends ServiceImpl<FaceDetectLogMapper, F
|
||||
* 创建错误日志
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -3,10 +3,14 @@ package com.rj.service.impl;
|
||||
import com.rj.entity.TtsRequestLog;
|
||||
import com.rj.mapper.TtsRequestLogMapper;
|
||||
import com.rj.service.ITtsRequestLogService;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* TTS请求日志服务实现类
|
||||
*
|
||||
@@ -20,6 +24,9 @@ public class TtsRequestLogServiceImpl implements ITtsRequestLogService {
|
||||
@Autowired
|
||||
private TtsRequestLogMapper ttsRequestLogMapper;
|
||||
|
||||
@Autowired
|
||||
private MinIOUrlGenerator urlGenerator;
|
||||
|
||||
@Override
|
||||
public boolean saveTtsRequestLog(TtsRequestLog requestLog) {
|
||||
try {
|
||||
@@ -41,4 +48,48 @@ public class TtsRequestLogServiceImpl implements ITtsRequestLogService {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setShortUrlByAudioName(MinIOUrlGenerator.UrlInfo urlInfo ) {
|
||||
try {
|
||||
if (urlInfo == null || urlInfo.getFileName().trim().isEmpty()) {
|
||||
log.error("音频名称不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据音频名称查询日志记录
|
||||
LambdaQueryWrapper<TtsRequestLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TtsRequestLog::getAudioName, urlInfo.getFileName());
|
||||
TtsRequestLog requestLog = ttsRequestLogMapper.selectOne(queryWrapper);
|
||||
|
||||
if (requestLog == null) {
|
||||
log.error("未找到音频名称为 {} 的日志记录", urlInfo.getFileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新日志记录的短链接信息
|
||||
requestLog.setShortUrl(urlInfo.getUrl());
|
||||
requestLog.setShortUrlExpireTime(urlInfo.getExpiresAt());
|
||||
requestLog.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
int result = ttsRequestLogMapper.updateById(requestLog);
|
||||
if (result > 0) {
|
||||
log.info("成功为音频 {} 设置短链接: {}, 过期时间: {}",
|
||||
urlInfo.getFileName(), urlInfo.getUrl(), urlInfo.getFormattedExpiresAt());
|
||||
return true;
|
||||
} else {
|
||||
log.error("更新音频 {} 的短链接失败", urlInfo.getFileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("根据音频名称设置短链接失败: 音频名称={}, 错误={}", urlInfo.getFileName(), e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setShortUrlByAudioName(String audioName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,11 @@ public interface IMenuService extends IService<Menu> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ public interface IUserRoleService extends IService<UserRole> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.rj.service.sys.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.rj.common.PasswordUtil;
|
||||
import com.rj.entity.sys.User;
|
||||
import com.rj.mapper.sys.UserMapper;
|
||||
@@ -13,9 +12,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -54,6 +51,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
loginResponse.setUserId(user.getUserId());
|
||||
loginResponse.setUserName(user.getUserName());
|
||||
loginResponse.setEmail(user.getEmail());
|
||||
loginResponse.setPhone(user.getPhone());
|
||||
loginResponse.setAvatar(user.getAvatar());
|
||||
loginResponse.setToken(token);
|
||||
loginResponse.setLoginTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MinIOUrlGenerator {
|
||||
urlInfo.setExpiresAt(LocalDateTime.now().plusSeconds(expiresInSeconds));
|
||||
urlInfo.setSuccess(true);
|
||||
|
||||
log.info("生成临时访问URL成功: 文件={}, 有效期={}秒", fileName, expiresInSeconds);
|
||||
log.info("生成临时访问URL成功: 文件={}, 有效期={}秒,链接是= {}", fileName, expiresInSeconds,presignedUrl);
|
||||
return urlInfo;
|
||||
} else {
|
||||
return createErrorUrlInfo(fileName, "生成预签名URL失败");
|
||||
|
||||
@@ -399,7 +399,15 @@ public class MinIOUtil {
|
||||
*/
|
||||
public String getPresignedObjectUrl(String bucketName, String objectName, int expires) {
|
||||
try {
|
||||
return minioClient.getPresignedObjectUrl(
|
||||
// 检查文件是否存在
|
||||
if (!objectExists(bucketName, objectName)) {
|
||||
log.error("文件不存在: bucket={}, object={}", bucketName, objectName);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("生成预签名URL: bucket={}, object={}, expires={}秒", bucketName, objectName, expires);
|
||||
|
||||
String presignedUrl = minioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucketName)
|
||||
@@ -407,12 +415,37 @@ public class MinIOUtil {
|
||||
.expiry(expires, TimeUnit.SECONDS)
|
||||
.build()
|
||||
);
|
||||
|
||||
log.info("生成的预签名URL: {}", presignedUrl);
|
||||
return presignedUrl;
|
||||
} catch (Exception e) {
|
||||
log.error("获取预签名URL失败: {}", e.getMessage());
|
||||
log.error("获取预签名URL失败: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否存在
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean objectExists(String bucketName, String objectName) {
|
||||
try {
|
||||
minioClient.statObject(
|
||||
StatObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build()
|
||||
);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.debug("对象不存在: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件预签名URL(默认7天过期)
|
||||
*
|
||||
|
||||
@@ -71,6 +71,11 @@ spring:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,8 +69,22 @@ spring:
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
|
||||
mybatis:
|
||||
# 文件上传配置
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件最大大小(50MB)
|
||||
max-file-size: 50MB
|
||||
# 总上传大小限制(100MB)
|
||||
max-request-size: 100MB
|
||||
# 文件写入磁盘的阈值(1MB)
|
||||
file-size-threshold: 1MB
|
||||
# 是否延迟文件解析
|
||||
resolve-lazily: false
|
||||
|
||||
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
mybatis:
|
||||
configuration:
|
||||
# 开启SQL日志 - 这是最重要的配置
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
@@ -47,6 +47,11 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -253,3 +253,8 @@
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,3 +13,8 @@ AFTER `sales_name`;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
-- 人脸检测日志表
|
||||
CREATE TABLE IF NOT EXISTS `face_detect_log` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`id` varchar(36) NOT NULL COMMENT '主键ID(UUID)',
|
||||
`request_id` varchar(64) NOT NULL COMMENT '请求ID',
|
||||
`model` varchar(100) NOT NULL COMMENT '使用的模型',
|
||||
`image_url` varchar(500) NOT NULL COMMENT '图片URL',
|
||||
`owner_name` varchar(100) DEFAULT NULL COMMENT '所属人姓名',
|
||||
`owner_phone` varchar(20) DEFAULT NULL COMMENT '所属人电话',
|
||||
`avatar_name` varchar(200) DEFAULT NULL COMMENT '头像名称',
|
||||
`request_time` datetime NOT NULL COMMENT '请求时间',
|
||||
`response_time` datetime DEFAULT NULL COMMENT '响应时间',
|
||||
`status_code` int(11) DEFAULT NULL COMMENT 'HTTP状态码',
|
||||
@@ -16,6 +19,8 @@ CREATE TABLE IF NOT EXISTS `face_detect_log` (
|
||||
`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_owner_phone` (`owner_phone`),
|
||||
KEY `idx_avatar_name` (`avatar_name`),
|
||||
KEY `idx_request_time` (`request_time`),
|
||||
KEY `idx_success` (`success`),
|
||||
KEY `idx_face_count` (`face_count`)
|
||||
|
||||
@@ -234,11 +234,9 @@ class MinIOServiceIntegrationTest {
|
||||
@Order(12)
|
||||
@DisplayName("获取文件URL")
|
||||
void testGetFileUrl() {
|
||||
String fileUrl = minioService.getFileUrl(TEST_FILE_NAME);
|
||||
String fileUrl = minioService.getFileUrl("微信图片_20251005120205_56_49.jpg");
|
||||
assertNotNull(fileUrl);
|
||||
assertTrue(fileUrl.contains(TEST_FILE_NAME));
|
||||
assertTrue(fileUrl.startsWith("http"));
|
||||
|
||||
|
||||
System.out.println("✅ 获取文件URL成功:");
|
||||
System.out.println(" 文件名: " + TEST_FILE_NAME);
|
||||
System.out.println(" 文件URL: " + fileUrl);
|
||||
|
||||
Reference in New Issue
Block a user