From 333c4d3ca7e531fe8b4a1dc69a0a27b8b1528ac0 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 26 Nov 2025 14:56:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(image):=20=E9=87=8D=E6=9E=84=E6=B0=B4?= =?UTF-8?q?=E5=8D=B0=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 PhotoProcessContext 中的水印相关字段 - 新增 WatermarkConfig 类封装水印配置 - 修改 WatermarkStage 通过构造函数注入配置 - 调整 PrinterServiceImpl 中水印配置的传递方式 - 更新单元测试以适应新的配置注入方式 - 统一从配置对象读取水印参数而非上下文 - 优化日志记录与偏移量计算逻辑 --- .../pipeline/core/PhotoProcessContext.java | 6 --- .../stages/ConditionalRotateStage.java | 5 +- .../pipeline/stages/WatermarkConfig.java | 37 +++++++++++++ .../image/pipeline/stages/WatermarkStage.java | 37 ++++++++----- .../printer/impl/PrinterServiceImpl.java | 52 ++++++++++++------- .../pipeline/stages/WatermarkStageTest.java | 23 ++++++-- 6 files changed, 113 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/ycwl/basic/image/pipeline/stages/WatermarkConfig.java 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 1550916c..9d6ccd80 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 @@ -101,12 +101,6 @@ public class PhotoProcessContext { private String resultUrl; private IStorageAdapter storageAdapter; - private WatermarkInfo watermarkInfo; - private ImageWatermarkOperatorEnum watermarkType; - private String scenicText; - private String dateFormat; - private File qrcodeFile; - private Integer offsetLeft; // ==================== 回调 ==================== 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 30f07ba4..78aa2d9d 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 @@ -23,8 +23,6 @@ import java.io.File; ) public class ConditionalRotateStage extends AbstractPipelineStage { - private static final int OFFSET_LEFT_FOR_PORTRAIT = 40; - @Override public String getName() { return "ConditionalRotateStage"; @@ -62,9 +60,8 @@ public class ConditionalRotateStage extends AbstractPipelineStage { + private static final int OFFSET_LEFT_FOR_PORTRAIT = 40; + + private final WatermarkConfig config; + + /** + * 构造函数 + * + * @param config 水印配置 + */ + public WatermarkStage(WatermarkConfig config) { + this.config = config; + } + @Override public String getName() { return "WatermarkStage"; @@ -43,12 +56,13 @@ public class WatermarkStage extends AbstractPipelineStage { @Override protected StageResult doExecute(PhotoProcessContext context) { - ImageWatermarkOperatorEnum watermarkType = context.getWatermarkType(); - if (watermarkType == null) { + if (config == null || config.getWatermarkType() == null) { log.info("未配置水印类型,跳过水印处理"); return StageResult.skipped("未配置水印"); } + ImageWatermarkOperatorEnum watermarkType = config.getWatermarkType(); + List fallbackChain = buildFallbackChain(watermarkType); log.debug("水印降级链: {}", fallbackChain); @@ -138,30 +152,29 @@ public class WatermarkStage extends AbstractPipelineStage { info.setOriginalFile(originalFile); info.setWatermarkedFile(watermarkedFile); - String scenicText = context.getScenicText(); + // 从 config 读取景区文字 + String scenicText = config.getScenicText(); if (StringUtils.isNotBlank(scenicText)) { info.setScenicLine(scenicText); } + // 从 config 读取日期格式 Date now = new Date(); - String dateFormat = context.getDateFormat(); - if (StringUtils.isBlank(dateFormat)) { - dateFormat = "yyyy.MM.dd"; - } + String dateFormat = config.getDateFormat(); info.setDatetime(now); info.setDtFormat(dateFormat); - File qrcodeFile = context.getQrcodeFile(); + // 从 config 读取二维码文件 + File qrcodeFile = config.getQrcodeFile(); if (qrcodeFile != null && qrcodeFile.exists()) { info.setQrcodeFile(qrcodeFile); } - Integer offsetLeft = context.getOffsetLeft(); - if (offsetLeft != null) { - info.setOffsetLeft(offsetLeft); + // 根据旋转状态自己处理 offsetLeft + if (context.isNeedRotation()) { + info.setOffsetLeft(OFFSET_LEFT_FOR_PORTRAIT); } - context.setWatermarkInfo(info); return info; } } 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 ac4dc00b..dcdb38f7 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 @@ -17,6 +17,7 @@ import com.ycwl.basic.image.pipeline.stages.ImageOrientationStage; import com.ycwl.basic.image.pipeline.stages.PuzzleBorderStage; import com.ycwl.basic.image.pipeline.stages.RestoreOrientationStage; import com.ycwl.basic.image.pipeline.stages.UploadStage; +import com.ycwl.basic.image.pipeline.stages.WatermarkConfig; import com.ycwl.basic.image.pipeline.stages.WatermarkStage; import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum; import com.ycwl.basic.integration.common.manager.DeviceConfigManager; @@ -742,13 +743,15 @@ public class PrinterServiceImpl implements PrinterService { /** * 创建普通照片处理管线 + * + * @param watermarkConfig 水印配置 */ - private Pipeline createNormalPhotoPipeline() { + private Pipeline createNormalPhotoPipeline(WatermarkConfig watermarkConfig) { return new PipelineBuilder("NormalPhotoPipeline") .addStage(new DownloadStage()) .addStage(new ImageOrientationStage()) .addStage(new ConditionalRotateStage()) - .addStage(new WatermarkStage()) + .addStage(new WatermarkStage(watermarkConfig)) .addStage(new RestoreOrientationStage()) .addStage(new UploadStage()) .addStage(new CleanupStage()) @@ -778,7 +781,6 @@ public class PrinterServiceImpl implements PrinterService { PrinterOrderItem orderItem = PrinterOrderItem.fromMemberPrintResp(item); PhotoProcessContext context = PhotoProcessContext.fromPrinterOrderItem(orderItem, scenicId); - context.setQrcodeFile(qrCodeFile); try { // 设置景区配置管理器到context @@ -800,16 +802,20 @@ public class PrinterServiceImpl implements PrinterService { context.setSource(ImageSource.UNKNOWN); } + Pipeline pipeline; if (context.getImageType() == ImageType.NORMAL_PHOTO) { - prepareNormalPhotoContext(context); - } else if (context.getImageType() == ImageType.PUZZLE) { + // 准备水印配置 + WatermarkConfig watermarkConfig = prepareWatermarkConfig(context, qrCodeFile); + // 准备存储适配器 prepareStorageAdapter(context); + // 创建普通照片管线 + pipeline = createNormalPhotoPipeline(watermarkConfig); + } else { + // 拼图 + prepareStorageAdapter(context); + pipeline = createPuzzlePipeline(); } - Pipeline pipeline = context.getImageType() == ImageType.PUZZLE - ? createPuzzlePipeline() - : createNormalPhotoPipeline(); - boolean success = pipeline.execute(context); if (success && context.getResultUrl() != null) { @@ -832,29 +838,35 @@ public class PrinterServiceImpl implements PrinterService { } /** - * 准备普通照片的Context配置 - * 从context中的scenicConfigManager获取配置 + * 准备普通照片的水印配置 + * 从 scenicConfigManager 获取配置并构建 WatermarkConfig + * + * @param context 照片处理上下文 + * @param qrCodeFile 二维码文件 + * @return WatermarkConfig */ - private void prepareNormalPhotoContext(PhotoProcessContext context) { + private WatermarkConfig prepareWatermarkConfig(PhotoProcessContext context, File qrCodeFile) { ScenicConfigManager scenicConfig = context.getScenicConfigManager(); if (scenicConfig == null) { - log.warn("scenicConfigManager未设置,跳过配置准备"); - return; + log.warn("scenicConfigManager未设置,返回空水印配置"); + return WatermarkConfig.builder().build(); } + ImageWatermarkOperatorEnum watermarkType = null; String printWatermarkType = scenicConfig.getString("print_watermark_type"); if (StringUtils.isNotBlank(printWatermarkType)) { - ImageWatermarkOperatorEnum watermarkType = ImageWatermarkOperatorEnum.getByCode(printWatermarkType); - context.setWatermarkType(watermarkType); + watermarkType = ImageWatermarkOperatorEnum.getByCode(printWatermarkType); } String scenicText = scenicConfig.getString("print_watermark_scenic_text", ""); - context.setScenicText(scenicText); - String dateFormat = scenicConfig.getString("print_watermark_dt_format", "yyyy.MM.dd"); - context.setDateFormat(dateFormat); - prepareStorageAdapter(context); + return WatermarkConfig.builder() + .watermarkType(watermarkType) + .scenicText(scenicText) + .dateFormat(dateFormat) + .qrcodeFile(qrCodeFile) + .build(); } /** diff --git a/src/test/java/com/ycwl/basic/image/pipeline/stages/WatermarkStageTest.java b/src/test/java/com/ycwl/basic/image/pipeline/stages/WatermarkStageTest.java index b5f73476..2af7b18f 100644 --- a/src/test/java/com/ycwl/basic/image/pipeline/stages/WatermarkStageTest.java +++ b/src/test/java/com/ycwl/basic/image/pipeline/stages/WatermarkStageTest.java @@ -3,6 +3,7 @@ package com.ycwl.basic.image.pipeline.stages; 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.watermark.enums.ImageWatermarkOperatorEnum; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -14,14 +15,18 @@ class WatermarkStageTest { @Test void testGetName() { - WatermarkStage stage = new WatermarkStage(); + WatermarkConfig config = WatermarkConfig.builder().build(); + WatermarkStage stage = new WatermarkStage(config); assertEquals("WatermarkStage", stage.getName()); } @Test void testShouldExecute_NormalPhoto() { - WatermarkStage stage = new WatermarkStage(); + WatermarkConfig config = WatermarkConfig.builder() + .watermarkType(ImageWatermarkOperatorEnum.PRINTER_DEFAULT) + .build(); + WatermarkStage stage = new WatermarkStage(config); PhotoProcessContext context = PhotoProcessContext.builder() .originalUrl("https://example.com/test.jpg") @@ -34,7 +39,10 @@ class WatermarkStageTest { @Test void testShouldExecute_Puzzle_ShouldSkip() { - WatermarkStage stage = new WatermarkStage(); + WatermarkConfig config = WatermarkConfig.builder() + .watermarkType(ImageWatermarkOperatorEnum.PRINTER_DEFAULT) + .build(); + WatermarkStage stage = new WatermarkStage(config); PhotoProcessContext context = PhotoProcessContext.builder() .originalUrl("https://example.com/puzzle.png") @@ -47,7 +55,10 @@ class WatermarkStageTest { @Test void testShouldExecute_MobileUpload_ShouldSkip() { - WatermarkStage stage = new WatermarkStage(); + WatermarkConfig config = WatermarkConfig.builder() + .watermarkType(ImageWatermarkOperatorEnum.PRINTER_DEFAULT) + .build(); + WatermarkStage stage = new WatermarkStage(config); PhotoProcessContext context = PhotoProcessContext.builder() .originalUrl("https://example.com/mobile.jpg") @@ -60,7 +71,9 @@ class WatermarkStageTest { @Test void testExecute_NoWatermarkType_ShouldSkip() { - WatermarkStage stage = new WatermarkStage(); + // 创建没有 watermarkType 的配置 + WatermarkConfig config = WatermarkConfig.builder().build(); + WatermarkStage stage = new WatermarkStage(config); PhotoProcessContext context = PhotoProcessContext.builder() .originalUrl("https://example.com/test.jpg")