调整 音频合成

This commit is contained in:
spllzh
2025-10-06 12:17:34 +08:00
parent 725a5294d0
commit 20378e8115
43 changed files with 508 additions and 60 deletions

View File

@@ -126,6 +126,11 @@ public class PasswordUtil {

View File

@@ -110,6 +110,11 @@ public class ServiceManager {

View File

@@ -88,6 +88,11 @@ public class AliyunConfig {

View File

@@ -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();
}
}

View File

@@ -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;
}
/**
* 分页查询人脸检测日志
*/

View File

@@ -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生成成功");

View File

@@ -74,7 +74,7 @@ public class TtsRequestLogController {
}
// 按请求时间倒序排列
queryWrapper.orderByDesc("request_time");
queryWrapper.orderByDesc("update_time");
// 执行查询
Page<TtsRequestLog> pageResult = ttsRequestLogMapper.selectPage(page, queryWrapper);

View File

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

View File

@@ -348,6 +348,11 @@ public class MenuController {

View File

@@ -318,6 +318,11 @@ public class RoleController {

View File

@@ -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);

View File

@@ -324,6 +324,11 @@ public class UserRoleController {

View File

@@ -91,6 +91,11 @@ public class DifyWorkflowResponseDto {

View File

@@ -98,6 +98,11 @@ public class SiliconFlowTtsRequest {
* 创建人电话
*/
private String creatorPhone;
/**
* audioName
*/
private String audioName;
/**
* 构造函数

View File

@@ -23,10 +23,10 @@ public class FaceDetectLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
* 主键IDUUID
*/
@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;
/**
* 请求时间
*/

View File

@@ -108,6 +108,12 @@ public class TtsRequestLog {
@TableField("minio_url")
private String minioUrl;
/**
* 音频文件名
*/
@TableField("audio_name")
private String audioName;
/**
* 短链临时访问URL
*/

View File

@@ -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;

View File

@@ -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)

View File

@@ -34,6 +34,11 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -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 {

View File

@@ -94,6 +94,11 @@ public class AudioStatisticsScheduler {

View File

@@ -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 当前页

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -55,6 +55,11 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -90,6 +90,11 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -57,6 +57,11 @@ public interface IMenuService extends IService<Menu> {

View File

@@ -57,6 +57,11 @@ public interface IUserRoleService extends IService<UserRole> {

View File

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

View File

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

View File

@@ -61,6 +61,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i

View File

@@ -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")));

View File

@@ -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失败");

View File

@@ -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天过期
*

View File

@@ -71,6 +71,11 @@ spring:

View File

@@ -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

View File

@@ -253,3 +253,8 @@
</body>
</html>

View File

@@ -13,3 +13,8 @@ AFTER `sales_name`;

View File

@@ -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 '主键IDUUID',
`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`)

View File

@@ -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);