package com.rj.service.impl; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.rj.config.MinIOConfig; import com.rj.dto.CustomerPhotoPreviewResult; import com.rj.dto.CustomerPhotoUploadResult; import com.rj.dto.CustomerStageUpdateResult; import com.rj.entity.CustomerManagement; import com.rj.mapper.CustomerManagementMapper; import com.rj.service.ICustomerManagementService; import com.rj.service.MinIOService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.time.LocalDateTime; import java.util.UUID; /** *

* 客户管理表 服务实现类 *

* * @author 李中华 ,spllzh * @since 2025-08-07 */ @Slf4j @Service public class CustomerManagementServiceImpl extends ServiceImpl implements ICustomerManagementService { @Autowired private MinIOService minIOService; @Autowired private MinIOConfig minioConfig; @Override public CustomerPhotoUploadResult uploadCustomerPhoto(String id, MultipartFile file) { try { if (id == null || id.trim().isEmpty()) { return CustomerPhotoUploadResult.badRequest("客户ID不能为空"); } if (file == null || file.isEmpty()) { return CustomerPhotoUploadResult.badRequest("请选择要上传的文件"); } CustomerManagement customer = getById(id.trim()); if (customer == null) { return CustomerPhotoUploadResult.badRequest("客户不存在"); } String original = file.getOriginalFilename(); String ext = ""; if (original != null && original.contains(".")) { ext = original.substring(original.lastIndexOf(".")); } String objectName = "customer-management/photo/" + id.trim() + "/" + UUID.randomUUID().toString().replace("-", "") + ext; String url = minIOService.uploadFileWithName(file, objectName); log.info("客户照片已上传 MinIO,customerId={}, url={}", id, url); return CustomerPhotoUploadResult.ok(url); } catch (Exception e) { log.error("客户照片上传失败: {}", e.getMessage(), e); return CustomerPhotoUploadResult.serverError("上传失败:" + e.getMessage()); } } @Override public CustomerStageUpdateResult updateCustomerStage(String id, String stage) { try { if (id == null || id.trim().isEmpty()) { return CustomerStageUpdateResult.badRequest("客户ID不能为空"); } String idTrim = id.trim(); if (getById(idTrim) == null) { return CustomerStageUpdateResult.badRequest("客户不存在"); } LocalDateTime now = LocalDateTime.now(); boolean updated = update( new LambdaUpdateWrapper() .eq(CustomerManagement::getId, idTrim) .set(CustomerManagement::getStage, stage) .set(CustomerManagement::getUpdateTime, now)); if (!updated) { return CustomerStageUpdateResult.badRequest("阶段更新失败"); } return CustomerStageUpdateResult.ok(getById(idTrim)); } catch (Exception e) { log.error("客户阶段更新失败: {}", e.getMessage(), e); return CustomerStageUpdateResult.serverError("阶段更新异常:" + e.getMessage()); } } @Override public CustomerPhotoPreviewResult openCustomerPhotoPreview(String url, String id, String photoType) { String photoUrl = null; if (url != null && !url.trim().isEmpty()) { photoUrl = url.trim(); } else if (id != null && !id.trim().isEmpty() && photoType != null && !photoType.trim().isEmpty()) { CustomerManagement customer = getById(id.trim()); if (customer == null) { return CustomerPhotoPreviewResult.notFound(); } String type = photoType.trim().toLowerCase(); switch (type) { case "front": photoUrl = customer.getFrontPhotoUrl(); break; case "side": photoUrl = customer.getSidePhotoUrl(); break; case "life": photoUrl = customer.getLifePhotoUrl(); break; default: return CustomerPhotoPreviewResult.badRequest(); } } else { return CustomerPhotoPreviewResult.badRequest(); } if (photoUrl == null || photoUrl.trim().isEmpty()) { return CustomerPhotoPreviewResult.notFound(); } String loc = photoUrl.trim(); String nameProbe = lastPathSegmentForPreview(loc); if (!isImageFile(nameProbe)) { log.warn("预览拒绝非图片扩展名: {}", nameProbe); return CustomerPhotoPreviewResult.badRequest(); } String objectName = resolveMinioObjectNameForPreview(loc); if (objectName == null || objectName.isEmpty()) { log.warn("无法从 URL 解析 MinIO 对象名: {}", loc); return CustomerPhotoPreviewResult.badRequest(); } try { InputStream inputStream = minIOService.downloadFile(objectName); String contentType = imageContentType(nameProbe); return CustomerPhotoPreviewResult.ok(inputStream, contentType); } catch (Exception e) { log.error("从 MinIO 读取客户照片失败, objectName={}", objectName, e); return CustomerPhotoPreviewResult.notFound(); } } private static String lastPathSegmentForPreview(String urlOrPath) { if (urlOrPath == null) { return ""; } String s = urlOrPath.trim(); int q = s.indexOf('?'); if (q >= 0) { s = s.substring(0, q); } int slash = Math.max(s.lastIndexOf('/'), s.lastIndexOf('\\')); return slash >= 0 ? s.substring(slash + 1) : s; } private static boolean isImageFile(String fileName) { if (fileName == null || fileName.isEmpty()) { return false; } String ext = getFileExtension(fileName).toLowerCase(); switch (ext) { case "jpg": case "jpeg": case "png": case "gif": case "webp": case "bmp": case "ico": return true; default: return false; } } private static String getFileExtension(String fileName) { if (fileName == null) { return ""; } int i = fileName.lastIndexOf('.'); return i >= 0 ? fileName.substring(i + 1) : ""; } private static String imageContentType(String fileName) { String ext = getFileExtension(fileName).toLowerCase(); switch (ext) { case "jpg": case "jpeg": return MediaType.IMAGE_JPEG_VALUE; case "png": return MediaType.IMAGE_PNG_VALUE; case "gif": return MediaType.IMAGE_GIF_VALUE; case "webp": return "image/webp"; case "bmp": return "image/bmp"; case "ico": return "image/x-icon"; default: return MediaType.APPLICATION_OCTET_STREAM_VALUE; } } private String resolveMinioObjectNameForPreview(String fileUrl) { if (fileUrl == null || fileUrl.trim().isEmpty()) { return null; } String trimmed = fileUrl.trim(); String withoutQuery = trimmed; int q = trimmed.indexOf('?'); if (q >= 0) { withoutQuery = trimmed.substring(0, q); } String prefix = minioConfig.getFileUrlPrefix(); if (prefix != null && !prefix.isEmpty() && withoutQuery.startsWith(prefix)) { return withoutQuery.substring(prefix.length()); } return extractObjectNameFromUrlFallback(trimmed); } private static String extractObjectNameFromUrlFallback(String url) { if (url == null || url.trim().isEmpty()) { return null; } try { String urlWithoutParams = url.contains("?") ? url.substring(0, url.indexOf('?')) : url; String[] parts = urlWithoutParams.split("/"); for (int i = parts.length - 1; i >= 0; i--) { if (parts[i] != null && !parts[i].trim().isEmpty()) { return parts[i]; } } return null; } catch (Exception e) { return null; } } }