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.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; /** *

* 设备保养附件 服务实现类 *

* * @author 芯导软件(江苏)有限公司 * @since 2026-01-27 09:48:09 */ @Service @Slf4j public class DeviceMaintenanceFileServiceImpl extends ServiceImpl 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 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; } }