diff --git a/src/main/java/com/ycwl/basic/image/enhancer/ImageEnhancerFactory.java b/src/main/java/com/ycwl/basic/image/enhancer/ImageEnhancerFactory.java index cbdc8676..141564cf 100644 --- a/src/main/java/com/ycwl/basic/image/enhancer/ImageEnhancerFactory.java +++ b/src/main/java/com/ycwl/basic/image/enhancer/ImageEnhancerFactory.java @@ -1,4 +1,62 @@ package com.ycwl.basic.image.enhancer; +import com.ycwl.basic.image.enhancer.adapter.BceImageEnhancer; +import com.ycwl.basic.image.enhancer.adapter.BceImageSR; +import com.ycwl.basic.image.enhancer.adapter.IEnhancer; +import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig; + +/** + * 图像增强器工厂 + * 用于创建不同类型的图像增强器实例 + */ public class ImageEnhancerFactory { + + /** + * 增强器类型枚举 + */ + public enum EnhancerType { + /** + * 图像清晰度增强 + * 使用imageDefinitionEnhance接口,适合提升整体清晰度 + */ + DEFINITION_ENHANCE, + + /** + * 图像超分辨率 + * 使用imageQualityEnhance接口,适合放大图片同时保持质量 + */ + SUPER_RESOLUTION + } + + /** + * 创建图像增强器 + * + * @param type 增强器类型 + * @param config 百度云配置 + * @return 图像增强器实例 + */ + public static IEnhancer createEnhancer(EnhancerType type, BceEnhancerConfig config) { + IEnhancer enhancer = switch (type) { + case DEFINITION_ENHANCE -> new BceImageEnhancer(); + case SUPER_RESOLUTION -> new BceImageSR(); + }; + + if (enhancer instanceof BceImageEnhancer) { + ((BceImageEnhancer) enhancer).setConfig(config); + } else if (enhancer instanceof BceImageSR) { + ((BceImageSR) enhancer).setConfig(config); + } + + return enhancer; + } + + /** + * 创建默认的图像增强器(清晰度增强) + * + * @param config 百度云配置 + * @return 图像增强器实例 + */ + public static IEnhancer createDefaultEnhancer(BceEnhancerConfig config) { + return createEnhancer(EnhancerType.DEFINITION_ENHANCE, config); + } } diff --git a/src/main/java/com/ycwl/basic/image/pipeline/core/AbstractPipelineStage.java b/src/main/java/com/ycwl/basic/image/pipeline/core/AbstractPipelineStage.java index df4aa3cd..4ad23962 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/core/AbstractPipelineStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/core/AbstractPipelineStage.java @@ -1,5 +1,7 @@ package com.ycwl.basic.image.pipeline.core; +import com.ycwl.basic.image.pipeline.annotation.StageConfig; +import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; import lombok.extern.slf4j.Slf4j; /** @@ -10,11 +12,46 @@ import lombok.extern.slf4j.Slf4j; public abstract class AbstractPipelineStage implements PipelineStage { /** - * 默认总是执行 - * 子类可以覆盖此方法实现条件性执行 + * 最终的shouldExecute判断 + * 整合了外部配置控制和业务逻辑判断 */ @Override - public boolean shouldExecute(C context) { + public final boolean shouldExecute(C context) { + // 1. 检查Stage配置注解 + StageConfig config = getStageConfig(); + if (config != null) { + String stageId = config.stageId(); + StageOptionalMode mode = config.optionalMode(); + + // FORCE_ON:强制执行,不检查外部配置 + if (mode == StageOptionalMode.FORCE_ON) { + return shouldExecuteByBusinessLogic(context); + } + + // SUPPORT:检查外部配置 + if (mode == StageOptionalMode.SUPPORT) { + boolean externalEnabled = context.isStageEnabled(stageId, config.defaultEnabled()); + if (!externalEnabled) { + log.debug("[{}] Stage被外部配置禁用", stageId); + return false; + } + } + + // UNSUPPORT:不检查外部配置,直接走业务逻辑 + } + + // 2. 执行业务逻辑判断 + return shouldExecuteByBusinessLogic(context); + } + + /** + * 子类实现业务逻辑判断 + * 默认总是执行 + * + * 子类可以覆盖此方法实现条件性执行 + * 例如: 只有竖图才旋转, 只有普通照片才加水印等 + */ + protected boolean shouldExecuteByBusinessLogic(C context) { return 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 66fd65c9..224ac60b 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 @@ -13,6 +13,8 @@ import lombok.Getter; import lombok.Setter; import java.io.File; +import java.util.HashMap; +import java.util.Map; /** * 图片处理管线上下文 @@ -41,6 +43,13 @@ public class PhotoProcessContext { */ private ImageSource source; + /** + * Stage开关配置表 + * Key: stageId, Value: 是否启用 + * 整合了景区配置和请求参数 + */ + private Map stageEnabledMap = new HashMap<>(); + private File originalFile; private File processedFile; private boolean isLandscape = true; @@ -61,6 +70,30 @@ public class PhotoProcessContext { this.tempFileManager = new TempFileManager(orderItem.getId().toString()); } + /** + * 从景区配置和请求参数中加载Stage开关配置 + * + * @param scenicConfigManager 景区配置管理器 + * @param requestParams 请求参数中的Stage配置 + */ + public void loadStageConfig(ScenicConfigManager scenicConfigManager, Map requestParams) { + // 请求参数覆盖 + if (requestParams != null) { + stageEnabledMap.putAll(requestParams); + } + } + + /** + * 判断指定Stage是否启用 + * + * @param stageId Stage唯一标识 + * @param defaultEnabled 默认值(当配置未指定时使用) + * @return true-启用, false-禁用 + */ + public boolean isStageEnabled(String stageId, boolean defaultEnabled) { + return stageEnabledMap.getOrDefault(stageId, defaultEnabled); + } + /** * 是否为拼图类型 */ diff --git a/src/main/java/com/ycwl/basic/image/pipeline/core/Pipeline.java b/src/main/java/com/ycwl/basic/image/pipeline/core/Pipeline.java index 14466748..0b1c147d 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/core/Pipeline.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/core/Pipeline.java @@ -31,9 +31,16 @@ public class Pipeline { public boolean execute(C context) { log.info("[{}] 开始执行管线, Stage数量: {}", name, stages.size()); long startTime = System.currentTimeMillis(); + int maxStages = 100; // 防止无限循环 + int executedCount = 0; try { for (int i = 0; i < stages.size(); i++) { + if (executedCount >= maxStages) { + log.error("[{}] Stage执行数量超过最大限制({}),可能存在循环依赖", name, maxStages); + throw new PipelineException("Stage执行数量超过最大限制,可能存在循环依赖"); + } + PipelineStage stage = stages.get(i); String stageName = stage.getName(); @@ -47,9 +54,22 @@ public class Pipeline { long stageStartTime = System.currentTimeMillis(); StageResult result = stage.execute(context); long stageDuration = System.currentTimeMillis() - stageStartTime; + executedCount++; logStageResult(stageName, result, stageDuration); + // 动态添加后续Stage + if (result.getNextStages() != null && !result.getNextStages().isEmpty()) { + List> nextStages = result.getNextStages(); + log.info("[{}] Stage {} 动态添加了 {} 个后续Stage", name, stageName, nextStages.size()); + + for (int j = 0; j < nextStages.size(); j++) { + PipelineStage nextStage = nextStages.get(j); + stages.add(i + 1 + j, (PipelineStage) nextStage); + log.debug("[{}] - 插入Stage: {} 到位置 {}", name, nextStage.getName(), i + 1 + j); + } + } + if (result.isFailed()) { log.error("[{}] Stage {} 执行失败,管线终止", name, stageName); return false; @@ -57,7 +77,8 @@ public class Pipeline { } long totalDuration = System.currentTimeMillis() - startTime; - log.info("[{}] 管线执行完成, 耗时: {}ms", name, totalDuration); + log.info("[{}] 管线执行完成, 总Stage数: {}, 实际执行: {}, 耗时: {}ms", + name, stages.size(), executedCount, totalDuration); return true; } catch (Exception e) { diff --git a/src/main/java/com/ycwl/basic/image/pipeline/core/PipelineStage.java b/src/main/java/com/ycwl/basic/image/pipeline/core/PipelineStage.java index 548de51b..ad9452c4 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/core/PipelineStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/core/PipelineStage.java @@ -1,5 +1,7 @@ package com.ycwl.basic.image.pipeline.core; +import com.ycwl.basic.image.pipeline.annotation.StageConfig; + /** * 管线处理阶段接口 * 每个Stage负责一个独立的图片处理步骤 @@ -37,4 +39,12 @@ public interface PipelineStage { default int getPriority() { return 100; } + + /** + * 获取Stage配置注解(用于反射读取可选性控制信息) + * @return Stage配置注解,如果未标注则返回null + */ + default StageConfig getStageConfig() { + return this.getClass().getAnnotation(StageConfig.class); + } } diff --git a/src/main/java/com/ycwl/basic/image/pipeline/core/StageResult.java b/src/main/java/com/ycwl/basic/image/pipeline/core/StageResult.java index 2092f03c..5fef69a4 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/core/StageResult.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/core/StageResult.java @@ -2,6 +2,11 @@ package com.ycwl.basic.image.pipeline.core; import lombok.Getter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * Stage执行结果 */ @@ -18,39 +23,56 @@ public class StageResult { private final Status status; private final String message; private final Throwable exception; + private final List> nextStages; - private StageResult(Status status, String message, Throwable exception) { + private StageResult(Status status, String message, Throwable exception, List> nextStages) { this.status = status; this.message = message; this.exception = exception; + this.nextStages = nextStages != null ? new ArrayList<>(nextStages) : Collections.emptyList(); } public static StageResult success() { - return new StageResult(Status.SUCCESS, null, null); + return new StageResult(Status.SUCCESS, null, null, null); } public static StageResult success(String message) { - return new StageResult(Status.SUCCESS, message, null); + return new StageResult(Status.SUCCESS, message, null, null); + } + + /** + * 成功执行并动态添加后续Stage + */ + @SafeVarargs + public static StageResult successWithNext(String message, PipelineStage... stages) { + return new StageResult(Status.SUCCESS, message, null, Arrays.asList(stages)); + } + + /** + * 成功执行并动态添加后续Stage列表 + */ + public static StageResult successWithNext(String message, List> stages) { + return new StageResult(Status.SUCCESS, message, null, stages); } public static StageResult skipped() { - return new StageResult(Status.SKIPPED, "条件不满足,跳过执行", null); + return new StageResult(Status.SKIPPED, "条件不满足,跳过执行", null, null); } public static StageResult skipped(String reason) { - return new StageResult(Status.SKIPPED, reason, null); + return new StageResult(Status.SKIPPED, reason, null, null); } public static StageResult failed(String message) { - return new StageResult(Status.FAILED, message, null); + return new StageResult(Status.FAILED, message, null, null); } public static StageResult failed(String message, Throwable exception) { - return new StageResult(Status.FAILED, message, exception); + return new StageResult(Status.FAILED, message, exception, null); } public static StageResult degraded(String message) { - return new StageResult(Status.DEGRADED, message, null); + return new StageResult(Status.DEGRADED, message, null, null); } public boolean isSuccess() { diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/CleanupStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/CleanupStage.java index c898b8e2..c156113c 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/stages/CleanupStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/CleanupStage.java @@ -1,8 +1,10 @@ package com.ycwl.basic.image.pipeline.stages; +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.StageOptionalMode; import lombok.extern.slf4j.Slf4j; /** @@ -10,6 +12,12 @@ import lombok.extern.slf4j.Slf4j; * 总是在管线最后执行,清理所有临时文件 */ @Slf4j +@StageConfig( + stageId = "cleanup", + optionalMode = StageOptionalMode.FORCE_ON, + description = "清理临时文件", + defaultEnabled = true +) public class CleanupStage extends AbstractPipelineStage { @Override 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 afbf86e7..49acab5b 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 @@ -1,8 +1,10 @@ package com.ycwl.basic.image.pipeline.stages; +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.StageOptionalMode; import com.ycwl.basic.utils.ImageUtils; import lombok.extern.slf4j.Slf4j; @@ -13,6 +15,11 @@ import java.io.File; * 如果是竖图,旋转90度变成横图(便于后续水印处理) */ @Slf4j +@StageConfig( + stageId = "rotate", + optionalMode = StageOptionalMode.UNSUPPORT, + description = "竖图旋转90度" +) public class ConditionalRotateStage extends AbstractPipelineStage { private static final int OFFSET_LEFT_FOR_PORTRAIT = 40; @@ -23,7 +30,7 @@ public class ConditionalRotateStage extends AbstractPipelineStage { @Override diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageEnhanceStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageEnhanceStage.java new file mode 100644 index 00000000..431f0c02 --- /dev/null +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/ImageEnhanceStage.java @@ -0,0 +1,177 @@ +package com.ycwl.basic.image.pipeline.stages; + +import com.ycwl.basic.image.enhancer.adapter.BceImageEnhancer; +import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig; +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.ImageSource; +import com.ycwl.basic.image.pipeline.enums.StageOptionalMode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * 图像增强Stage + * 使用百度云图像增强API提升图片清晰度和质量 + * + * 支持两种增强模式: + * 1. 图像清晰度增强 (BceImageEnhancer) + * 2. 图像超分辨率 (BceImageSR) + */ +@Slf4j +@StageConfig( + stageId = "image_enhance", + optionalMode = StageOptionalMode.SUPPORT, + description = "图像增强处理", + defaultEnabled = false // 默认不启用,需要外部配置开启 +) +public class ImageEnhanceStage extends AbstractPipelineStage { + + private BceEnhancerConfig enhancerConfig; + + /** + * 构造函数 - 使用默认配置 + */ + public ImageEnhanceStage() { + this.enhancerConfig = new BceEnhancerConfig(); + this.enhancerConfig.setAppId("119554288"); + this.enhancerConfig.setApiKey("OX6QoijgKio3eVtA0PiUVf7f"); + this.enhancerConfig.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk"); + this.enhancerConfig.setQps(1.0f); + } + + /** + * 构造函数 - 使用自定义配置 + * + * @param enhancerConfig 图像增强配置 + */ + public ImageEnhanceStage(BceEnhancerConfig enhancerConfig) { + this.enhancerConfig = enhancerConfig; + } + + @Override + public String getName() { + return "ImageEnhanceStage"; + } + + @Override + protected boolean shouldExecuteByBusinessLogic(PhotoProcessContext context) { + // 仅对照片源为IPC的图片进行增强 + return context.getSource() == ImageSource.IPC; + } + + @Override + protected StageResult doExecute(PhotoProcessContext context) { + // 检查配置是否完整 + if (!isConfigValid()) { + log.warn("图像增强配置不完整,跳过增强处理。请在ImageEnhanceStage中配置百度云API凭证"); + return StageResult.skipped("配置不完整,跳过增强"); + } + + File currentFile = context.getCurrentFile(); + if (currentFile == null || !currentFile.exists()) { + return StageResult.failed("当前文件不存在"); + } + + try { + log.debug("开始图像增强: {}", currentFile.getName()); + + // 创建百度云图像增强客户端 + BceImageEnhancer enhancer = new BceImageEnhancer(); + enhancer.setConfig(enhancerConfig); + + // 调用图像增强API + // 注意:百度云API需要传入图片URL,这里使用本地文件的绝对路径 + String imageUrl = currentFile.getAbsolutePath(); + MultipartFile enhancedImage = enhancer.enhance(imageUrl); + + if (enhancedImage == null || enhancedImage.isEmpty()) { + log.warn("图像增强返回空结果,可能是API调用失败"); + return StageResult.degraded("增强失败,使用原图"); + } + + // 保存增强后的图片到临时文件 + File enhancedFile = context.getTempFileManager() + .createTempFile("enhanced", ".jpg"); + + saveMultipartFileToFile(enhancedImage, enhancedFile); + + if (!enhancedFile.exists() || enhancedFile.length() == 0) { + return StageResult.degraded("增强结果保存失败,使用原图"); + } + + // 更新处理后的文件 + context.updateProcessedFile(enhancedFile); + + long originalSize = currentFile.length(); + long enhancedSize = enhancedFile.length(); + double sizeRatio = (double) enhancedSize / originalSize; + + log.info("图像增强完成: 原始{}KB -> 增强后{}KB (比例: {})", + originalSize / 1024, + enhancedSize / 1024, + String.format("%.2f", sizeRatio)); + + return StageResult.success(String.format("已增强 (%dKB -> %dKB)", + originalSize / 1024, + enhancedSize / 1024)); + + } catch (Exception e) { + log.error("图像增强失败: {}", e.getMessage(), e); + + // 增强失败时返回降级状态,继续使用原图 + return StageResult.degraded("增强失败: " + e.getMessage()); + } + } + + /** + * 检查配置是否有效 + */ + private boolean isConfigValid() { + if (enhancerConfig == null) { + return false; + } + + String appId = enhancerConfig.getAppId(); + String apiKey = enhancerConfig.getApiKey(); + String secretKey = enhancerConfig.getSecretKey(); + + // 检查是否还是TODO占位符 + if (appId == null || appId.startsWith("TODO_") || + apiKey == null || apiKey.startsWith("TODO_") || + secretKey == null || secretKey.startsWith("TODO_")) { + return false; + } + + return true; + } + + /** + * 保存MultipartFile到本地文件 + */ + private void saveMultipartFileToFile(MultipartFile multipartFile, File targetFile) throws IOException { + try (FileOutputStream fos = new FileOutputStream(targetFile)) { + fos.write(multipartFile.getBytes()); + fos.flush(); + } + } + + /** + * 获取当前配置(用于调试) + */ + public BceEnhancerConfig getEnhancerConfig() { + return enhancerConfig; + } + + /** + * 设置配置(用于动态配置) + */ + public void setEnhancerConfig(BceEnhancerConfig enhancerConfig) { + this.enhancerConfig = enhancerConfig; + } +} 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 346d7545..3ddcc036 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 @@ -1,9 +1,11 @@ package com.ycwl.basic.image.pipeline.stages; import com.ycwl.basic.model.Crop; +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.StageOptionalMode; import com.ycwl.basic.utils.ImageUtils; import lombok.extern.slf4j.Slf4j; @@ -14,6 +16,11 @@ import java.io.File; * 检测图片是横图还是竖图,并记录到Context */ @Slf4j +@StageConfig( + stageId = "orientation", + optionalMode = StageOptionalMode.UNSUPPORT, + description = "图片方向检测" +) public class ImageOrientationStage extends AbstractPipelineStage { @Override @@ -22,7 +29,7 @@ public class ImageOrientationStage extends AbstractPipelineStage { + + /** + * 质量阈值:图片尺寸小于此阈值认为质量不佳 + * 例如:小于100KB的图片可能需要增强 + */ + private static final long QUALITY_THRESHOLD_BYTES = 100 * 1024; // 100KB + + /** + * 分辨率阈值:图片分辨率低于此值认为质量不佳 + */ + private static final int MIN_WIDTH = 800; + private static final int MIN_HEIGHT = 600; + + @Override + public String getName() { + return "ImageQualityCheckStage"; + } + + @Override + protected boolean shouldExecuteByBusinessLogic(PhotoProcessContext context) { + // 仅对普通照片执行质量检测 + return context.isNormalPhoto(); + } + + @Override + protected StageResult doExecute(PhotoProcessContext context) { + File currentFile = context.getCurrentFile(); + if (currentFile == null || !currentFile.exists()) { + return StageResult.failed("当前文件不存在"); + } + + try { + // 检查文件大小 + long fileSize = currentFile.length(); + log.debug("图像质量检测: 文件大小={}KB, 阈值={}KB", + fileSize / 1024, + QUALITY_THRESHOLD_BYTES / 1024); + + boolean needsEnhancement = false; + String reason = ""; + + // 检查:文件大小 + if (fileSize < QUALITY_THRESHOLD_BYTES) { + needsEnhancement = true; + reason = String.format("文件过小(%dKB < %dKB)", + fileSize / 1024, + QUALITY_THRESHOLD_BYTES / 1024); + } + + // TODO: 可以添加更多质量检测维度 + // 例如:使用BufferedImage读取图片获取分辨率 + // 例如:使用OpenCV进行图片质量评估 + // 例如:检查图片的EXIF信息 + + // 如果需要增强,动态添加ImageEnhanceStage + if (needsEnhancement) { + log.info("检测到图片质量不佳({}), 动态添加ImageEnhanceStage", reason); + + ImageEnhanceStage enhanceStage = new ImageEnhanceStage(); + + // 使用successWithNext返回,动态添加后续Stage + return StageResult.successWithNext( + "质量不佳,添加增强Stage: " + reason, + enhanceStage + ); + } + + // 质量良好,无需增强 + log.info("图像质量良好,无需增强: {}KB", fileSize / 1024); + return StageResult.success("质量良好,无需增强"); + + } catch (Exception e) { + log.error("图像质量检测失败", e); + // 检测失败时不影响管线,继续执行 + return StageResult.degraded("质量检测失败: " + e.getMessage()); + } + } + + /** + * 示例:也可以根据其他条件动态添加不同的Stage + */ + @SuppressWarnings("unused") + private StageResult checkAndAddMultipleStages(PhotoProcessContext context) { + // 示例:根据不同条件添加多个Stage + + // 假设检测到需要多个增强操作 + ImageEnhanceStage enhanceStage = new ImageEnhanceStage(); + // 其他Stage... + + // 可以一次性添加多个Stage + return StageResult.successWithNext( + "检测到需要多重处理", + enhanceStage + // 可以添加更多Stage + ); + } +} diff --git a/src/main/java/com/ycwl/basic/image/pipeline/stages/PuzzleBorderStage.java b/src/main/java/com/ycwl/basic/image/pipeline/stages/PuzzleBorderStage.java index a5147611..419ffdff 100644 --- a/src/main/java/com/ycwl/basic/image/pipeline/stages/PuzzleBorderStage.java +++ b/src/main/java/com/ycwl/basic/image/pipeline/stages/PuzzleBorderStage.java @@ -1,8 +1,10 @@ package com.ycwl.basic.image.pipeline.stages; +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.StageOptionalMode; import com.ycwl.basic.utils.ImageUtils; import lombok.extern.slf4j.Slf4j; @@ -13,6 +15,12 @@ import java.io.File; * 为拼图添加白边框并向上偏移 */ @Slf4j +@StageConfig( + stageId = "puzzle_border", + optionalMode = StageOptionalMode.SUPPORT, + description = "拼图边框处理", + defaultEnabled = true +) public class PuzzleBorderStage extends AbstractPipelineStage { private static final int BORDER_LR = 20; @@ -25,7 +33,7 @@ public class PuzzleBorderStage extends AbstractPipelineStage { @Override @@ -21,7 +28,7 @@ public class RestoreOrientationStage extends AbstractPipelineStage 默认assets-ext存储 */ @Slf4j +@StageConfig( + stageId = "upload", + optionalMode = StageOptionalMode.FORCE_ON, + description = "上传图片到存储", + defaultEnabled = true +) public class UploadStage extends AbstractPipelineStage { private static final String DEFAULT_STORAGE = "assets-ext"; 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 7a1becd6..8a4ac97b 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 @@ -4,9 +4,11 @@ import com.ycwl.basic.image.watermark.ImageWatermarkFactory; import com.ycwl.basic.image.watermark.entity.WatermarkInfo; import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum; import com.ycwl.basic.image.watermark.operator.IOperator; +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.StageOptionalMode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -20,6 +22,12 @@ import java.util.List; * 支持三级降级: 配置的水印类型 -> PRINTER_DEFAULT -> 无水印 */ @Slf4j +@StageConfig( + stageId = "watermark", + optionalMode = StageOptionalMode.SUPPORT, + description = "水印处理", + defaultEnabled = true +) public class WatermarkStage extends AbstractPipelineStage { @Override @@ -28,7 +36,7 @@ public class WatermarkStage extends AbstractPipelineStage { } @Override - public boolean shouldExecute(PhotoProcessContext context) { + protected boolean shouldExecuteByBusinessLogic(PhotoProcessContext context) { return context.isNormalPhoto(); }