| | |
| | | package com.ruoyi.device.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.constant.Constants; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.device.mapper.DeviceMaintenanceFileMapper; |
| | | import com.ruoyi.device.pojo.DeviceMaintenanceFile; |
| | | import com.ruoyi.device.service.DeviceMaintenanceFileService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.framework.config.RuoYiConfig; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | * @since 2026-01-27 09:48:09 |
| | | */ |
| | | @Service |
| | | public class DeviceMaintenanceFileServiceImpl extends ServiceImpl<DeviceMaintenanceFileMapper, DeviceMaintenanceFile> implements DeviceMaintenanceFileService { |
| | | @Slf4j |
| | | public class DeviceMaintenanceFileServiceImpl extends ServiceImpl<DeviceMaintenanceFileMapper, DeviceMaintenanceFile> |
| | | implements DeviceMaintenanceFileService { |
| | | |
| | | @Resource |
| | | private TempFileMapper tempFileMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | @Value("${file.temp-dir}") |
| | | private String tempDir; |
| | | |
| | | @Override |
| | | public DeviceMaintenanceFile uploadFile(MultipartFile file, Integer deviceMaintenanceId) throws IOException { |
| | | if (deviceMaintenanceId == null || deviceMaintenanceId <= 0) { |
| | | throw new ServiceException("设备保养记录ID不能为空"); |
| | | } |
| | | if (file == null || file.isEmpty()) { |
| | | throw new ServiceException("上传失败,文件不能为空"); |
| | | } |
| | | Path formalPath = storeMultipartFile(file, deviceMaintenanceId); |
| | | return insertFileRecord(deviceMaintenanceId, file.getOriginalFilename(), formalPath, (int) file.getSize()); |
| | | } |
| | | |
| | | @Override |
| | | public DeviceMaintenanceFile bindFromTemp(String tempId, Integer deviceMaintenanceId, String name) throws IOException { |
| | | if (!StringUtils.hasText(tempId)) { |
| | | throw new ServiceException("临时文件ID不能为空"); |
| | | } |
| | | if (deviceMaintenanceId == null || deviceMaintenanceId <= 0) { |
| | | throw new ServiceException("设备保养记录ID不能为空"); |
| | | } |
| | | TempFile tempFile = tempFileMapper.selectById(tempId); |
| | | if (tempFile == null) { |
| | | throw new ServiceException("临时文件不存在或已过期"); |
| | | } |
| | | Path source = Paths.get(tempFile.getTempPath()); |
| | | if (!Files.exists(source)) { |
| | | tempFileMapper.deleteById(tempId); |
| | | throw new ServiceException("临时文件已失效,请重新上传"); |
| | | } |
| | | Path formalPath = copyToFormalDir(source, deviceMaintenanceId, tempFile.getOriginalName()); |
| | | Files.deleteIfExists(source); |
| | | tempFileMapper.deleteById(tempId); |
| | | String fileName = StringUtils.hasText(name) ? name : tempFile.getOriginalName(); |
| | | return insertFileRecord(deviceMaintenanceId, fileName, formalPath, null); |
| | | } |
| | | |
| | | @Override |
| | | public DeviceMaintenanceFile saveRecord(DeviceMaintenanceFile deviceMaintenanceFile) throws IOException { |
| | | if (deviceMaintenanceFile == null) { |
| | | throw new ServiceException("附件信息不能为空"); |
| | | } |
| | | if (StringUtils.hasText(deviceMaintenanceFile.getTempId())) { |
| | | return bindFromTemp( |
| | | deviceMaintenanceFile.getTempId(), |
| | | deviceMaintenanceFile.getDeviceMaintenanceId(), |
| | | deviceMaintenanceFile.getName()); |
| | | } |
| | | String url = deviceMaintenanceFile.getUrl(); |
| | | if (isTempStoragePath(url)) { |
| | | throw new ServiceException("请勿直接保存临时目录文件,请使用上传接口或传递 tempId"); |
| | | } |
| | | if (StringUtils.hasText(url) && !url.startsWith(Constants.RESOURCE_PREFIX)) { |
| | | deviceMaintenanceFile.setUrl(buildAccessLink(Paths.get(url))); |
| | | } |
| | | save(deviceMaintenanceFile); |
| | | enrichAccessUrl(deviceMaintenanceFile); |
| | | return deviceMaintenanceFile; |
| | | } |
| | | |
| | | @Override |
| | | public void enrichAccessUrl(List<DeviceMaintenanceFile> files) { |
| | | if (files == null) { |
| | | return; |
| | | } |
| | | files.forEach(this::enrichAccessUrl); |
| | | } |
| | | |
| | | private void enrichAccessUrl(DeviceMaintenanceFile file) { |
| | | if (file == null || !StringUtils.hasText(file.getUrl())) { |
| | | return; |
| | | } |
| | | String url = file.getUrl().trim(); |
| | | if (url.startsWith(Constants.RESOURCE_PREFIX) || url.startsWith("http://") || url.startsWith("https://")) { |
| | | return; |
| | | } |
| | | try { |
| | | file.setUrl(buildAccessLink(Paths.get(url))); |
| | | } catch (Exception e) { |
| | | log.warn("附件URL转换失败 id={} url={}", file.getId(), url); |
| | | } |
| | | } |
| | | |
| | | private Path storeMultipartFile(MultipartFile file, Integer deviceMaintenanceId) throws IOException { |
| | | Path formalDirPath = resolveFormalDirPath(); |
| | | Files.createDirectories(formalDirPath); |
| | | String originalFilename = file.getOriginalFilename(); |
| | | if (!StringUtils.hasText(originalFilename)) { |
| | | throw new ServiceException("文件名不能为空"); |
| | | } |
| | | Path formalPath = formalDirPath.resolve(buildFormalFilename(deviceMaintenanceId, originalFilename)); |
| | | file.transferTo(formalPath.toFile()); |
| | | return formalPath; |
| | | } |
| | | |
| | | private Path copyToFormalDir(Path source, Integer deviceMaintenanceId, String originalFilename) throws IOException { |
| | | Path formalDirPath = resolveFormalDirPath(); |
| | | Files.createDirectories(formalDirPath); |
| | | String name = StringUtils.hasText(originalFilename) ? originalFilename : source.getFileName().toString(); |
| | | Path formalPath = formalDirPath.resolve(buildFormalFilename(deviceMaintenanceId, name)); |
| | | Files.copy(source, formalPath, StandardCopyOption.REPLACE_EXISTING); |
| | | return formalPath; |
| | | } |
| | | |
| | | private Path resolveFormalDirPath() { |
| | | String dir = uploadDir; |
| | | if (!dir.endsWith("/") && !dir.endsWith("\\")) { |
| | | dir = dir + "/"; |
| | | } |
| | | return Paths.get(dir + "deviceMaintenance/" + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); |
| | | } |
| | | |
| | | private String buildFormalFilename(Integer deviceMaintenanceId, String originalFilename) { |
| | | String ext = FilenameUtils.getExtension(originalFilename); |
| | | String base = deviceMaintenanceId + "_" + System.currentTimeMillis() + "_" |
| | | + UUID.randomUUID().toString().substring(0, 8); |
| | | return StringUtils.hasText(ext) ? base + "." + ext : base; |
| | | } |
| | | |
| | | private DeviceMaintenanceFile insertFileRecord(Integer deviceMaintenanceId, String name, Path formalPath, Integer fileSize) { |
| | | DeviceMaintenanceFile record = new DeviceMaintenanceFile(); |
| | | record.setName(name); |
| | | record.setUrl(buildAccessLink(formalPath)); |
| | | record.setFileSize(fileSize); |
| | | record.setDeviceMaintenanceId(deviceMaintenanceId); |
| | | save(record); |
| | | return record; |
| | | } |
| | | |
| | | private boolean isTempStoragePath(String url) { |
| | | if (!StringUtils.hasText(url)) { |
| | | return false; |
| | | } |
| | | String normalized = url.replace("\\", "/").toLowerCase(); |
| | | return normalized.contains("/temp/") || normalized.contains("temp/uploads"); |
| | | } |
| | | |
| | | private String buildAccessLink(Path formalFilePath) { |
| | | String normalizedPath = formalFilePath.toString().replace("\\", "/"); |
| | | String profile = RuoYiConfig.getProfile(); |
| | | String normalizedProfile = profile == null ? "" : profile.replace("\\", "/"); |
| | | |
| | | String relativePath = normalizedPath; |
| | | if (StringUtils.hasText(normalizedProfile) && normalizedPath.startsWith(normalizedProfile)) { |
| | | relativePath = normalizedPath.substring(normalizedProfile.length()); |
| | | } |
| | | if (!relativePath.startsWith("/")) { |
| | | relativePath = "/" + relativePath; |
| | | } |
| | | return Constants.RESOURCE_PREFIX + relativePath; |
| | | } |
| | | } |