From 7b18d7c2afd78d481a8c10bc6916c1a2294a6405 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Tue, 25 Nov 2025 19:17:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(image):=20=E5=AE=9E=E7=8E=B0=E6=BA=90?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=B6=85=E5=88=86=E8=BE=A8=E7=8E=87=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E6=B5=81=E6=B0=B4=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入Pipeline模式重构图片处理流程 - 新增SourcePhotoUpdateStage用于上传并更新源图片URL - 扩展PhotoProcessContext支持超分场景配置 - 增加SOURCE_PHOTO_SUPER_RESOLUTION枚举值 - 修改各Stage判断逻辑适配新的图片类型系统 - 调整SourceService接口支持File类型参数 - 优化超分处理日志记录和异常处理机制 --- .../extern/AioDeviceController.java | 71 ++++-- .../pipeline/annotation/StageConfig.java | 45 ++++ .../pipeline/core/PhotoProcessContext.java | 209 +++++++++++++++--- .../basic/image/pipeline/enums/ImageType.java | 59 +++++ .../image/pipeline/enums/PipelineScene.java | 8 +- .../pipeline/enums/StageOptionalMode.java | 35 +++ .../stages/ConditionalRotateStage.java | 3 +- .../image/pipeline/stages/DownloadStage.java | 5 +- .../stages/ImageOrientationStage.java | 3 +- .../stages/ImageQualityCheckStage.java | 3 +- .../pipeline/stages/PuzzleBorderStage.java | 3 +- .../stages/RestoreOrientationStage.java | 3 +- .../stages/SourcePhotoUpdateStage.java | 82 +++++++ .../image/pipeline/stages/WatermarkStage.java | 3 +- .../ycwl/basic/service/pc/SourceService.java | 3 +- .../service/pc/impl/SourceServiceImpl.java | 5 +- .../printer/impl/PrinterServiceImpl.java | 11 +- 17 files changed, 491 insertions(+), 60 deletions(-) create mode 100644 src/main/java/com/ycwl/basic/image/pipeline/annotation/StageConfig.java create mode 100644 src/main/java/com/ycwl/basic/image/pipeline/enums/ImageType.java create mode 100644 src/main/java/com/ycwl/basic/image/pipeline/enums/StageOptionalMode.java create mode 100644 src/main/java/com/ycwl/basic/image/pipeline/stages/SourcePhotoUpdateStage.java diff --git a/src/main/java/com/ycwl/basic/controller/extern/AioDeviceController.java b/src/main/java/com/ycwl/basic/controller/extern/AioDeviceController.java index 4a4e7399..17d65ba9 100644 --- a/src/main/java/com/ycwl/basic/controller/extern/AioDeviceController.java +++ b/src/main/java/com/ycwl/basic/controller/extern/AioDeviceController.java @@ -4,6 +4,13 @@ import cn.hutool.http.HttpUtil; import com.ycwl.basic.annotation.IgnoreToken; import com.ycwl.basic.image.enhancer.adapter.BceImageEnhancer; import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig; +import com.ycwl.basic.image.pipeline.core.Pipeline; +import com.ycwl.basic.image.pipeline.core.PipelineBuilder; +import com.ycwl.basic.image.pipeline.core.PhotoProcessContext; +import com.ycwl.basic.image.pipeline.stages.DownloadStage; +import com.ycwl.basic.image.pipeline.stages.ImageEnhanceStage; +import com.ycwl.basic.image.pipeline.stages.SourcePhotoUpdateStage; +import com.ycwl.basic.image.pipeline.stages.CleanupStage; import com.ycwl.basic.mapper.AioDeviceMapper; import com.ycwl.basic.mapper.MemberMapper; import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity; @@ -41,7 +48,9 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j @@ -129,27 +138,39 @@ public class AioDeviceController { redisTemplate.opsForValue().set("aio:faceId:"+resp.getFaceId().toString()+":pass", "1", 1, TimeUnit.DAYS); return; } - log.info("超分开始!"); + log.info("超分开始!共{}张图片待处理", sourcePhotoList.size()); + sourcePhotoList.forEach(photo -> { if (StringUtils.contains(photo.getUrl(), "_q_")) { + log.debug("跳过已增强的图片: {}", photo.getUrl()); return; } + try { - File dstFile = new File(photo.getGoodsId()+".jpg"); - long fileSize = HttpUtil.downloadFile(photo.getUrl(), dstFile); - log.info("超分开始:{}", fileSize); - BceImageEnhancer enhancer = getEnhancer(); - MultipartFile enhancedFile = enhancer.enhance(dstFile.getName()); - log.info("超分结束:{}", photo.getUrl()); - String url = sourceService.uploadAndUpdateUrl(photo.getGoodsId(), enhancedFile); - log.info("上传结束:->{}", url); + // 创建超分Pipeline + Pipeline superResolutionPipeline = createSuperResolutionPipeline(photo.getGoodsId()); + + // 使用静态工厂方法创建Context + PhotoProcessContext context = PhotoProcessContext.forSuperResolution( + photo.getGoodsId(), photo.getUrl(), photo.getScenicId() + ); + + // 启用图像增强Stage + Map stageConfig = new HashMap<>(); + stageConfig.put("image_enhance", true); + context.loadStageConfig(null, stageConfig); + + // 执行Pipeline + boolean success = superResolutionPipeline.execute(context); + + if (success) { + log.info("超分成功: {} -> {}", photo.getUrl(), context.getResultUrl()); + } else { + log.error("超分失败: {}", photo.getGoodsId()); + } + } catch (Exception e) { log.error("超分失败:{}", photo.getGoodsId(), e); - } finally { - File _file = new File(photo.getGoodsId()+".jpg"); - if (_file.exists()) { - _file.delete(); - } } }); redisTemplate.opsForValue().set("aio:faceId:"+sourcePhotoList.getFirst().getFaceId().toString()+":pass", "1", 1, TimeUnit.DAYS); @@ -206,6 +227,28 @@ public class AioDeviceController { return ApiResponse.success(orderService.queryOrder(orderId)); } + /** + * 创建源图片超分辨率增强Pipeline + * + * @param sourceId 源图片ID + * @return 超分Pipeline + */ + private Pipeline createSuperResolutionPipeline(Long sourceId) { + // 创建带有百度云配置的ImageEnhanceStage + BceEnhancerConfig config = new BceEnhancerConfig(); + config.setQps(1); + config.setAppId("119554288"); + config.setApiKey("OX6QoijgKio3eVtA0PiUVf7f"); + config.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk"); + + return new PipelineBuilder("SourcePhotoSuperResolutionPipeline") + .addStage(new DownloadStage()) // 1. 下载图片 + .addStage(new ImageEnhanceStage(config)) // 2. 图像增强(超分) + .addStage(new SourcePhotoUpdateStage(sourceService, sourceId)) // 3. 上传并更新数据库 + .addStage(new CleanupStage()) // 4. 清理临时文件 + .build(); + } + private BceImageEnhancer getEnhancer() { BceImageEnhancer enhancer = new BceImageEnhancer(); BceEnhancerConfig config = new BceEnhancerConfig(); diff --git a/src/main/java/com/ycwl/basic/image/pipeline/annotation/StageConfig.java b/src/main/java/com/ycwl/basic/image/pipeline/annotation/StageConfig.java new file mode 100644 index 00000000..a8cb3f9c --- /dev/null +++ b/src/main/java/com/ycwl/basic/image/pipeline/annotation/StageConfig.java @@ -0,0 +1,45 @@ +package com.ycwl.basic.image.pipeline.annotation; + +import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Stage配置注解 + * 用于声明Stage的元数据和可选性控制信息 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface StageConfig { + + /** + * Stage的唯一标识 + * 用于外部配置引用该Stage + * 例如: "watermark", "download", "upload" + */ + String stageId(); + + /** + * 可选性模式 + * 默认为UNSUPPORT(不支持外部控制) + */ + StageOptionalMode optionalMode() default StageOptionalMode.UNSUPPORT; + + /** + * Stage描述信息 + * 用于文档和日志说明 + */ + String description() default ""; + + /** + * 默认是否启用 + * 仅当optionalMode=SUPPORT时有效 + * 当外部配置未明确指定时,使用此默认值 + */ + boolean defaultEnabled() default true; +} diff --git a/src/main/java/com/ycwl/basic/image/pipeline/core/PhotoProcessContext.java b/src/main/java/com/ycwl/basic/image/pipeline/core/PhotoProcessContext.java index 224ac60b..e19f9d4d 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/core/PhotoProcessContext.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/core/PhotoProcessContext.java @@ -3,6 +3,7 @@ package com.ycwl.basic.image.pipeline.core; import com.ycwl.basic.image.watermark.entity.WatermarkInfo; import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum; import com.ycwl.basic.image.pipeline.enums.ImageSource; +import com.ycwl.basic.image.pipeline.enums.ImageType; import com.ycwl.basic.image.pipeline.enums.PipelineScene; import com.ycwl.basic.integration.common.manager.ScenicConfigManager; import com.ycwl.basic.model.Crop; @@ -15,6 +16,8 @@ import lombok.Setter; import java.io.File; import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; /** * 图片处理管线上下文 @@ -24,10 +27,43 @@ import java.util.Map; @Setter public class PhotoProcessContext { - private final PrinterOrderItem orderItem; - private final TempFileManager tempFileManager; + // ==================== 核心字段(构造时必填)==================== + + /** + * 处理过程唯一标识 + * 用于 TempFileManager 创建隔离的临时文件目录 + */ + private final String processId; + + /** + * 原图 URL + */ + private final String originalUrl; + + /** + * 景区 ID + */ private final Long scenicId; + /** + * 临时文件管理器 + */ + private final TempFileManager tempFileManager; + + // ==================== 图片元信息 ==================== + + /** + * 图片类型 + */ + private ImageType imageType = ImageType.NORMAL_PHOTO; + + /** + * 裁剪/旋转信息 + */ + private Crop crop; + + // ==================== 管线配置 ==================== + /** * 景区配置管理器,用于获取景区相关配置 */ @@ -50,6 +86,8 @@ public class PhotoProcessContext { */ private Map stageEnabledMap = new HashMap<>(); + // ==================== 处理过程状态 ==================== + private File originalFile; private File processedFile; private boolean isLandscape = true; @@ -62,14 +100,78 @@ public class PhotoProcessContext { private String dateFormat; private File qrcodeFile; private Integer offsetLeft; - private Crop crop; - public PhotoProcessContext(PrinterOrderItem orderItem, Long scenicId) { - this.orderItem = orderItem; - this.scenicId = scenicId; - this.tempFileManager = new TempFileManager(orderItem.getId().toString()); + // ==================== 回调 ==================== + + /** + * 结果 URL 回调 + * 用于在 setResultUrl 时通知外部更新相关数据 + */ + private Consumer resultUrlCallback; + + // ==================== 构造函数(私有)==================== + + private PhotoProcessContext(Builder builder) { + this.processId = builder.processId; + this.originalUrl = builder.originalUrl; + this.scenicId = builder.scenicId; + this.tempFileManager = new TempFileManager(processId); + + this.imageType = builder.imageType; + this.crop = builder.crop; + this.scene = builder.scene; + this.source = builder.source; + this.resultUrlCallback = builder.resultUrlCallback; } + // ==================== 静态工厂方法 ==================== + + /** + * 从 PrinterOrderItem 创建 Context(打印场景兼容方法) + * + * @param orderItem 打印订单项 + * @param scenicId 景区ID + * @return PhotoProcessContext + */ + public static PhotoProcessContext fromPrinterOrderItem(PrinterOrderItem orderItem, Long scenicId) { + return PhotoProcessContext.builder() + .processId(orderItem.getId().toString()) + .originalUrl(orderItem.getCropUrl()) + .scenicId(scenicId) + .imageType(ImageType.fromSourceId(orderItem.getSourceId())) + .crop(orderItem.getCrop()) + .resultUrlCallback(url -> orderItem.setCropUrl(url)) + .build(); + } + + /** + * 为超分辨率场景创建 Context + * + * @param itemId 项目ID(用于临时文件隔离) + * @param url 原图URL + * @param scenicId 景区ID + * @return PhotoProcessContext + */ + public static PhotoProcessContext forSuperResolution(Long itemId, String url, Long scenicId) { + return PhotoProcessContext.builder() + .processId(itemId.toString()) + .originalUrl(url) + .scenicId(scenicId) + .imageType(ImageType.NORMAL_PHOTO) + .source(ImageSource.IPC) + .scene(PipelineScene.SOURCE_PHOTO_SUPER_RESOLUTION) + .build(); + } + + /** + * 获取 Builder + */ + public static Builder builder() { + return new Builder(); + } + + // ==================== 业务方法 ==================== + /** * 从景区配置和请求参数中加载Stage开关配置 * @@ -94,33 +196,14 @@ public class PhotoProcessContext { return stageEnabledMap.getOrDefault(stageId, defaultEnabled); } - /** - * 是否为拼图类型 - */ - public boolean isPuzzle() { - return orderItem.getSourceId() != null && orderItem.getSourceId() == 0; - } - - /** - * 是否为普通照片 - */ - public boolean isNormalPhoto() { - return orderItem.getSourceId() != null && orderItem.getSourceId() > 0; - } - - /** - * 获取原图URL - */ - public String getOriginalUrl() { - return orderItem.getCropUrl(); - } - /** * 设置最终处理结果URL */ public void setResultUrl(String url) { this.resultUrl = url; - this.orderItem.setCropUrl(url); + if (resultUrlCallback != null) { + resultUrlCallback.accept(url); + } } /** @@ -145,4 +228,72 @@ public class PhotoProcessContext { public void cleanup() { tempFileManager.cleanup(); } + + // ==================== Builder ==================== + + public static class Builder { + private String processId; + private String originalUrl; + private Long scenicId; + private ImageType imageType = ImageType.NORMAL_PHOTO; + private Crop crop; + private PipelineScene scene; + private ImageSource source; + private Consumer resultUrlCallback; + + public Builder processId(String processId) { + this.processId = processId; + return this; + } + + public Builder originalUrl(String originalUrl) { + this.originalUrl = originalUrl; + return this; + } + + public Builder scenicId(Long scenicId) { + this.scenicId = scenicId; + return this; + } + + public Builder imageType(ImageType imageType) { + this.imageType = imageType; + return this; + } + + public Builder crop(Crop crop) { + this.crop = crop; + return this; + } + + public Builder scene(PipelineScene scene) { + this.scene = scene; + return this; + } + + public Builder source(ImageSource source) { + this.source = source; + return this; + } + + public Builder resultUrlCallback(Consumer callback) { + this.resultUrlCallback = callback; + return this; + } + + public PhotoProcessContext build() { + // 参数校验 + if (originalUrl == null || originalUrl.isBlank()) { + throw new IllegalArgumentException("originalUrl is required"); + } + if (scenicId == null) { + throw new IllegalArgumentException("scenicId is required"); + } + // processId 可以自动生成 + if (processId == null || processId.isBlank()) { + processId = UUID.randomUUID().toString(); + } + return new PhotoProcessContext(this); + } + } } diff --git a/src/main/java/com/ycwl/basic/image/pipeline/enums/ImageType.java b/src/main/java/com/ycwl/basic/image/pipeline/enums/ImageType.java new file mode 100644 index 00000000..6e97d4d4 --- /dev/null +++ b/src/main/java/com/ycwl/basic/image/pipeline/enums/ImageType.java @@ -0,0 +1,59 @@ +package com.ycwl.basic.image.pipeline.enums; + +/** + * 图片类型枚举 + * 用于区分管线处理的图片类型,替代通过 sourceId 判断的逻辑 + */ +public enum ImageType { + + /** + * 普通照片 + * 对应原 sourceId > 0 的情况(IPC设备拍摄) + */ + NORMAL_PHOTO("normal", "普通照片"), + + /** + * 拼图 + * 对应原 sourceId == 0 的情况 + */ + PUZZLE("puzzle", "拼图"), + + /** + * 手机上传 + * 对应原 sourceId == null 的情况 + */ + MOBILE_UPLOAD("mobile", "手机上传"); + + private final String code; + private final String description; + + ImageType(String code, String description) { + this.code = code; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getDescription() { + return description; + } + + /** + * 从 sourceId 推断图片类型 + * 用于兼容现有数据结构 + * + * @param sourceId 源ID(null=手机上传, 0=拼图, >0=普通照片) + * @return 对应的图片类型 + */ + public static ImageType fromSourceId(Long sourceId) { + if (sourceId == null) { + return MOBILE_UPLOAD; + } else if (sourceId == 0) { + return PUZZLE; + } else { + return NORMAL_PHOTO; + } + } +} diff --git a/src/main/java/com/ycwl/basic/image/pipeline/enums/PipelineScene.java b/src/main/java/com/ycwl/basic/image/pipeline/enums/PipelineScene.java index 9be30aff..14a3ac1b 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/enums/PipelineScene.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/enums/PipelineScene.java @@ -16,7 +16,13 @@ public enum PipelineScene { * 图片增强场景 * 包括图片美化、滤镜处理等 */ - IMAGE_ENHANCE("image_enhance", "图片增强"); + IMAGE_ENHANCE("image_enhance", "图片增强"), + + /** + * 源图片超分辨率增强场景 + * IPC设备拍摄的源图片进行质量提升 + */ + SOURCE_PHOTO_SUPER_RESOLUTION("source_photo_sr", "源图片超分辨率增强"); private final String code; private final String description; diff --git a/src/main/java/com/ycwl/basic/image/pipeline/enums/StageOptionalMode.java b/src/main/java/com/ycwl/basic/image/pipeline/enums/StageOptionalMode.java new file mode 100644 index 00000000..6bdab5c4 --- /dev/null +++ b/src/main/java/com/ycwl/basic/image/pipeline/enums/StageOptionalMode.java @@ -0,0 +1,35 @@ +package com.ycwl.basic.image.pipeline.enums; + +import lombok.Getter; + +/** + * Stage可选性模式枚举 + * 定义Stage是否支持外部配置控制 + */ +@Getter +public enum StageOptionalMode { + + /** + * 不支持外部控制 + * Stage的执行完全由代码中的业务逻辑决定 + */ + UNSUPPORT("不支持外部控制"), + + /** + * 支持外部控制 + * Stage可以通过景区配置或请求参数进行开启/关闭 + */ + SUPPORT("支持外部控制"), + + /** + * 强制开启 + * Stage必须执行,不允许外部配置关闭 + */ + FORCE_ON("强制开启"); + + private final String description; + + StageOptionalMode(String description) { + this.description = description; + } +} diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/ConditionalRotateStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/ConditionalRotateStage.java index 49acab5b..e4281639 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/stages/ConditionalRotateStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/ConditionalRotateStage.java @@ -4,6 +4,7 @@ import com.ycwl.basic.image.pipeline.annotation.StageConfig; import com.ycwl.basic.image.pipeline.core.AbstractPipelineStage; import com.ycwl.basic.image.pipeline.core.PhotoProcessContext; import com.ycwl.basic.image.pipeline.core.StageResult; +import com.ycwl.basic.image.pipeline.enums.ImageType; import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; import com.ycwl.basic.utils.ImageUtils; import lombok.extern.slf4j.Slf4j; @@ -31,7 +32,7 @@ public class ConditionalRotateStage extends AbstractPipelineStage { try { String fileExtension = determineFileExtension(context); - String filePrefix = context.isPuzzle() ? "puzzle" : "print"; + String filePrefix = context.getImageType() == ImageType.PUZZLE ? "puzzle" : "print"; File downloadFile = context.getTempFileManager() .createTempFile(filePrefix, fileExtension); @@ -63,7 +64,7 @@ public class DownloadStage extends AbstractPipelineStage { } private String determineFileExtension(PhotoProcessContext context) { - if (context.isPuzzle()) { + if (context.getImageType() == ImageType.PUZZLE) { return ".png"; } String url = context.getOriginalUrl(); diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageOrientationStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageOrientationStage.java index 3ddcc036..0731f081 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageOrientationStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageOrientationStage.java @@ -5,6 +5,7 @@ import com.ycwl.basic.image.pipeline.annotation.StageConfig; import com.ycwl.basic.image.pipeline.core.AbstractPipelineStage; import com.ycwl.basic.image.pipeline.core.PhotoProcessContext; import com.ycwl.basic.image.pipeline.core.StageResult; +import com.ycwl.basic.image.pipeline.enums.ImageType; import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; import com.ycwl.basic.utils.ImageUtils; import lombok.extern.slf4j.Slf4j; @@ -30,7 +31,7 @@ public class ImageOrientationStage extends AbstractPipelineStage { + + private final SourceService sourceService; + private final Long sourceId; + + /** + * 构造函数 + * + * @param sourceService 源图片服务 + * @param sourceId 源图片ID + */ + public SourcePhotoUpdateStage(SourceService sourceService, Long sourceId) { + this.sourceService = sourceService; + this.sourceId = sourceId; + } + + @Override + public String getName() { + return "SourcePhotoUpdateStage"; + } + + @Override + protected StageResult doExecute(PhotoProcessContext context) { + File fileToUpload = context.getCurrentFile(); + if (fileToUpload == null || !fileToUpload.exists()) { + return StageResult.failed("没有可上传的文件"); + } + + if (sourceService == null) { + return StageResult.failed("SourceService未注入"); + } + + if (sourceId == null) { + return StageResult.failed("SourceId为空"); + } + + try { + + // 调用SourceService上传并更新URL + String uploadedUrl = sourceService.uploadAndUpdateUrl(sourceId, fileToUpload); + + // 设置结果URL到Context + context.setResultUrl(uploadedUrl); + + log.info("源图片上传并更新成功: sourceId={}, url={}", sourceId, uploadedUrl); + + return StageResult.success("已上传并更新: " + uploadedUrl); + + } catch (Exception e) { + log.error("源图片上传并更新失败: sourceId={}", sourceId, e); + return StageResult.failed("上传并更新失败: " + e.getMessage(), e); + } + } + +} diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/WatermarkStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/WatermarkStage.java index 8a4ac97b..c784dd5b 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/stages/WatermarkStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/WatermarkStage.java @@ -8,6 +8,7 @@ import com.ycwl.basic.image.pipeline.annotation.StageConfig; import com.ycwl.basic.image.pipeline.core.AbstractPipelineStage; import com.ycwl.basic.image.pipeline.core.PhotoProcessContext; import com.ycwl.basic.image.pipeline.core.StageResult; +import com.ycwl.basic.image.pipeline.enums.ImageType; import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -37,7 +38,7 @@ public class WatermarkStage extends AbstractPipelineStage { @Override protected boolean shouldExecuteByBusinessLogic(PhotoProcessContext context) { - return context.isNormalPhoto(); + return context.getImageType() == ImageType.NORMAL_PHOTO; } @Override diff --git a/src/main/java/com/ycwl/basic/service/pc/SourceService.java b/src/main/java/com/ycwl/basic/service/pc/SourceService.java index b3d2ec83..9664d994 100644 --- a/src/main/java/com/ycwl/basic/service/pc/SourceService.java +++ b/src/main/java/com/ycwl/basic/service/pc/SourceService.java @@ -6,6 +6,7 @@ import com.ycwl.basic.model.pc.source.req.SourceReqQuery; import com.ycwl.basic.model.pc.source.resp.SourceRespVO; import com.ycwl.basic.utils.ApiResponse; +import java.io.File; import java.util.List; /** @@ -21,5 +22,5 @@ public interface SourceService { ApiResponse update(SourceEntity source); ApiResponse cutVideo(Long id); - String uploadAndUpdateUrl(Long id, org.springframework.web.multipart.MultipartFile file); + String uploadAndUpdateUrl(Long id, File file); } diff --git a/src/main/java/com/ycwl/basic/service/pc/impl/SourceServiceImpl.java b/src/main/java/com/ycwl/basic/service/pc/impl/SourceServiceImpl.java index d5adf265..6f333ef3 100644 --- a/src/main/java/com/ycwl/basic/service/pc/impl/SourceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/pc/impl/SourceServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import com.ycwl.basic.storage.StorageFactory; +import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; @@ -216,14 +217,14 @@ public class SourceServiceImpl implements SourceService { } @Override - public String uploadAndUpdateUrl(Long id, MultipartFile file) { + public String uploadAndUpdateUrl(Long id, File file) { SourceRespVO source = sourceMapper.getById(id); if (source == null) { throw new BaseException("该素材不存在"); } try { IStorageAdapter adapter = scenicService.getScenicStorageAdapter(source.getScenicId()); - String uploadedUrl = adapter.uploadFile(file, PHOTO_PATH, id + "_q_.jpg"); + String uploadedUrl = adapter.uploadFile("image/jpeg", file, PHOTO_PATH, id + "_q_.jpg"); SourceEntity sourceUpd = new SourceEntity(); sourceUpd.setId(id); diff --git a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java index c0723c66..ac4dc00b 100644 --- a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java @@ -8,6 +8,7 @@ import com.ycwl.basic.image.pipeline.core.PhotoProcessContext; import com.ycwl.basic.image.pipeline.core.Pipeline; import com.ycwl.basic.image.pipeline.core.PipelineBuilder; import com.ycwl.basic.image.pipeline.enums.ImageSource; +import com.ycwl.basic.image.pipeline.enums.ImageType; import com.ycwl.basic.image.pipeline.enums.PipelineScene; import com.ycwl.basic.image.pipeline.stages.CleanupStage; import com.ycwl.basic.image.pipeline.stages.ConditionalRotateStage; @@ -776,7 +777,7 @@ public class PrinterServiceImpl implements PrinterService { private String processPhotoWithPipeline(MemberPrintResp item, Long scenicId, File qrCodeFile) { PrinterOrderItem orderItem = PrinterOrderItem.fromMemberPrintResp(item); - PhotoProcessContext context = new PhotoProcessContext(orderItem, scenicId); + PhotoProcessContext context = PhotoProcessContext.fromPrinterOrderItem(orderItem, scenicId); context.setQrcodeFile(qrCodeFile); try { @@ -799,13 +800,13 @@ public class PrinterServiceImpl implements PrinterService { context.setSource(ImageSource.UNKNOWN); } - if (context.isNormalPhoto()) { + if (context.getImageType() == ImageType.NORMAL_PHOTO) { prepareNormalPhotoContext(context); - } else if (context.isPuzzle()) { + } else if (context.getImageType() == ImageType.PUZZLE) { prepareStorageAdapter(context); } - Pipeline pipeline = context.isPuzzle() + Pipeline pipeline = context.getImageType() == ImageType.PUZZLE ? createPuzzlePipeline() : createNormalPhotoPipeline(); @@ -814,7 +815,7 @@ public class PrinterServiceImpl implements PrinterService { if (success && context.getResultUrl() != null) { log.info("照片处理成功: photoId={}, type={}, url={}", item.getId(), - context.isPuzzle() ? "拼图" : "普通照片", + context.getImageType() == ImageType.PUZZLE ? "拼图" : "普通照片", context.getResultUrl()); return context.getResultUrl(); } else {