package com.tourguide.modules.review.service; import com.tourguide.common.exception.BusinessException; import com.tourguide.config.properties.StorageProperties; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Service public class ReviewImageStorageService { private static final long MAX_FILE_SIZE = 5L * 1024 * 1024; private static final Map CONTENT_TYPE_EXTENSIONS = createContentTypeExtensions(); private final StorageProperties storageProperties; public ReviewImageStorageService(StorageProperties storageProperties) { this.storageProperties = storageProperties; } public String store(MultipartFile file) { if (file == null || file.isEmpty()) { throw new BusinessException(400, "请选择评价图片"); } if (file.getSize() > MAX_FILE_SIZE) { throw new BusinessException(400, "评价图片不能超过5MB"); } String extension = CONTENT_TYPE_EXTENSIONS.get(file.getContentType()); if (extension == null) { throw new BusinessException(400, "评价图片仅支持 JPG、PNG 或 WebP 格式"); } Path directory = Paths.get(storageProperties.getReviewImageDirectory()).toAbsolutePath().normalize(); String filename = UUID.randomUUID().toString().replace("-", "") + extension; Path target = directory.resolve(filename).normalize(); if (!target.startsWith(directory)) { throw new BusinessException(400, "评价图片文件路径不合法"); } try { Files.createDirectories(directory); try (InputStream inputStream = file.getInputStream()) { Files.copy(inputStream, target, StandardCopyOption.REPLACE_EXISTING); } } catch (IOException exception) { throw new BusinessException(500, "评价图片保存失败,请稍后重试"); } return normalizePublicPath(storageProperties.getReviewImagePublicPath()) + "/" + filename; } private String normalizePublicPath(String publicPath) { String normalized = publicPath == null ? "/uploads/review-images" : publicPath.trim(); if (!normalized.startsWith("/")) normalized = "/" + normalized; while (normalized.endsWith("/")) normalized = normalized.substring(0, normalized.length() - 1); return normalized; } private static Map createContentTypeExtensions() { Map extensions = new HashMap(); extensions.put("image/jpeg", ".jpg"); extensions.put("image/png", ".png"); extensions.put("image/webp", ".webp"); return extensions; } }