Merge branch 'jingquan'

# Conflicts:
#	fys-admin/src/main/java/com/fuyuanshen/app/service/AudioProcessService.java
This commit is contained in:
2025-11-07 17:10:54 +08:00
8 changed files with 187 additions and 37 deletions

View File

@ -50,6 +50,7 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
private final DeviceRepairRecordsMapper baseMapper;
private final DeviceRepairImagesMapper imagesMapper;
private final ISysOssService ossService;
private final FileHashUtil fileHashUtil;
/**
* 查询设备维修记录
@ -210,19 +211,13 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
// 1. 计算文件哈希
String hash = null;
try {
hash = FileHashUtil.hash(file);
hash = fileHashUtil.hash(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 2. 先根据 hash 查库(秒传)
SysOssVo exist = ossService.selectByHash(hash);
if (exist == null) {
// 2.2 不存在,真正上传
exist = ossService.upload(file);
// 2.3 把 hash 写回记录(供下次去重)
ossService.updateHashById(exist.getOssId(), hash);
}
SysOssVo exist = ossService.updateHash(file,hash);
DeviceRepairImages image = new DeviceRepairImages();
image.setRecordId(recordId);

View File

@ -34,6 +34,7 @@ import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.equipment.mapper.*;
import com.fuyuanshen.equipment.service.*;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
import com.fuyuanshen.system.service.ISysOssService;
@ -79,6 +80,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceFenceAccessRecordMapper deviceFenceAccessRecordMapper;
private final FileHashUtil fileHashUtil;
/**
@ -204,7 +206,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
// 保存图片并获取URL
if (deviceForm.getFile() != null) {
SysOssVo upload = ossService.upload(deviceForm.getFile());
String fileHash = fileHashUtil.hash(deviceForm.getFile());
SysOssVo upload = ossService.updateHash(deviceForm.getFile(),fileHash);
// 设置图片路径
deviceForm.setDevicePic(upload.getUrl());
}
@ -278,8 +281,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
// 处理上传的图片
if (deviceForm.getFile() != null) {
// 设置图片路径
SysOssVo oss = ossService.upload(deviceForm.getFile());
String fileHash = fileHashUtil.hash(deviceForm.getFile());
SysOssVo oss = ossService.updateHash(deviceForm.getFile(),fileHash);
// 强制将HTTP替换为HTTPS
if (oss.getUrl() != null && oss.getUrl().startsWith("http://")) {
oss.setUrl(oss.getUrl().replaceFirst("^http://", "https://"));

View File

@ -1,28 +1,70 @@
package com.fuyuanshen.equipment.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
/**
* 文件哈希工具类
*/
@Component // 如果使用 Spring 可以注入
public class FileHashUtil {
/* 算法常量 */
private static final String ALGORITHM = "SHA-256";
/* 缓冲区大小 8 KB */
private static final int BUFFER_SIZE = 8192;
public static String hash(MultipartFile file) throws IOException {
MessageDigest digest = DigestUtils.getDigest(ALGORITHM);
/**
* 计算上传文件的 SHA-256 十六进制哈希
*
* @param file 上传文件;不能为 null且必须非空
* @return 64 位小写十六进制字符串
* @throws IllegalArgumentException 参数不合法
* @throws IOException 流读取失败
* @throws IllegalStateException 算法运行时异常(不会触发)
*/
public String hash(MultipartFile file) throws IOException {
validate(file);
/* 每个请求新建实例,保证线程安全 */
MessageDigest digest = newDigest();
/* try-with-resources 自动关闭流 */
try (InputStream in = file.getInputStream()) {
byte[] buf = new byte[8192];
byte[] buf = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buf)) != -1) {
digest.update(buf, 0, len);
}
}
/* JDK 17+ 的 HexFormat比 Apache Commons 更快且无需额外依赖 */
return HexFormat.of().formatHex(digest.digest());
}
/* -------------------- 私有辅助方法 -------------------- */
private static void validate(MultipartFile file) {
if (file == null) {
throw new IllegalArgumentException("MultipartFile 不能为 null");
}
if (file.isEmpty()) {
throw new IllegalArgumentException("上传文件不能为空");
}
}
private static MessageDigest newDigest() {
try {
return MessageDigest.getInstance(ALGORITHM);
} catch (NoSuchAlgorithmException e) {
/* SHA-256 是 JDK 必现算法,走到这里说明 JDK 实现损坏 */
throw new IllegalStateException("算法 " + ALGORITHM + " 不可用", e);
}
}
}

View File

@ -60,6 +60,14 @@ public interface ISysOssService {
*/
int updateHashById(long ossId,String fileHash);
/**
* 更新文件 hash 值
*
* @param file 文件对象
* @return 匹配的 SysOssVo 列表
*/
SysOssVo updateHash(MultipartFile file, String hash);
/**
* 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
*

View File

@ -191,6 +191,32 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
storage.download(sysOss.getFileName(), response.getOutputStream(), response::setContentLengthLong);
}
/**
* 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
*
* @param file 要上传的 MultipartFile 对象
* @return 保存到数据库的 SysOssVo 对象
*/
@Override
public SysOssVo updateHash(MultipartFile file, String hash) {
// 2. 先根据 hash 查库(秒传)
SysOssVo exist = baseMapper.selectByHash(hash);
if (exist != null) {
return exist;
}
String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
OssClient storage = OssFactory.instance();
UploadResult uploadResult;
try {
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
} catch (IOException e) {
throw new ServiceException(e.getMessage());
}
// 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult,hash);
}
/**
* 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
*
@ -210,7 +236,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
throw new ServiceException(e.getMessage());
}
// 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult,null);
}
/**
@ -226,7 +252,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
OssClient storage = OssFactory.instance();
UploadResult uploadResult = storage.uploadSuffix(file, suffix);
// 保存文件信息
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult,null);
}
@ -255,18 +281,19 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
uploadResult = storage.uploadSuffix(data, suffix, "image/jpeg"); // 假设是图片类型,可以根据实际需要修改
// 保存文件信息
return buildResultEntity(fileName, suffix, storage.getConfigKey(), uploadResult);
return buildResultEntity(fileName, suffix, storage.getConfigKey(), uploadResult,null);
}
@NotNull
private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult) {
private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult, String hash) {
SysOss oss = new SysOss();
oss.setUrl(uploadResult.getUrl());
oss.setFileSuffix(suffix);
oss.setFileName(uploadResult.getFilename());
oss.setOriginalName(originalfileName);
oss.setService(configKey);
oss.setFileHash(hash); // 设置哈希值
baseMapper.insert(oss);
SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
return this.matchingUrl(sysOssVo);