gongchunyi
6 天以前 089964a497c2528e88ddc610af5f88f631303431
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceFileServiceImpl.java
@@ -1,10 +1,31 @@
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>
@@ -15,6 +36,167 @@
 * @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;
    }
}