You've already forked FrameTour-BE
refactor(image): 重构图片旋转和恢复逻辑
- 将 needRotation 标志重命名为 rotationApplied - 修改条件旋转阶段的执行逻辑,基于实际旋转角度判断 - 实现通用的图片恢复旋转功能,支持90/180/270度恢复 - 添加恢复旋转角度计算方法 getRestoreAngle - 更新水印阶段的旋转状态检查逻辑 - 完善单元测试覆盖各种旋转场景 - 优化日志记录和错误处理流程
This commit is contained in:
@@ -13,6 +13,7 @@ Image Pipeline 是一个通用的、可扩展的图片处理管线框架,用
|
||||
- **配置驱动**: 支持通过外部配置控制 Stage 的启用/禁用
|
||||
- **类型安全**: 使用泛型和枚举确保类型安全
|
||||
- **解耦设计**: Context 独立于业务模型,支持多种使用场景
|
||||
- **自动清理**: 无论成功或失败都会在 finally 中兜底调用 `context.cleanup()`
|
||||
|
||||
## 包结构
|
||||
|
||||
@@ -207,6 +208,8 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
| ImageEnhanceStage | 图像增强(超分) | SUPPORT | 可配置 |
|
||||
| ImageQualityCheckStage | 图像质量检测 | SUPPORT | 仅普通照片 |
|
||||
|
||||
> **提示**:`ImageEnhanceStage` 的默认构造函数会尝试从 `BCE_IMAGE_APP_ID/BCE_IMAGE_API_KEY/BCE_IMAGE_SECRET_KEY` 环境变量读取百度云凭据;若未配置则自动跳过执行。
|
||||
|
||||
### 存储 Stage
|
||||
|
||||
| Stage | 职责 | Optional Mode | 执行条件 |
|
||||
@@ -214,6 +217,12 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
| UploadStage | 上传图片到存储服务 | FORCE_ON | 总是执行 |
|
||||
| SourcePhotoUpdateStage | 更新源图片记录 | UNSUPPORT | 总是执行 |
|
||||
|
||||
### 辅助 Stage
|
||||
|
||||
| Stage | 职责 | Optional Mode | 执行条件 |
|
||||
|-------|------|---------------|---------|
|
||||
| NoOpStage | 调试/占位 Stage,不做任何处理 | UNSUPPORT | 仅用于保持流程完整或调试 |
|
||||
|
||||
## 典型管线示例
|
||||
|
||||
### 1. 打印照片处理管线
|
||||
@@ -553,7 +562,7 @@ A: 返回 `StageResult.failed()`,管线会立即终止。如果希望继续执
|
||||
|
||||
### Q: 临时文件什么时候被清理?
|
||||
|
||||
A: 由 `CleanupStage` 负责,通常放在管线最后。也可以手动调用 `context.cleanup()`。
|
||||
A: 由 `CleanupStage` 负责,通常放在管线最后。也可以手动调用 `context.cleanup()`。此外,`Pipeline` 在 finally 中还会再调用一次 `context.cleanup()`,保证失败或异常时也能释放所有临时文件。
|
||||
|
||||
### Q: 如何获取最终处理结果?
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public abstract class AbstractPipelineStage<C extends PhotoProcessContext> imple
|
||||
/**
|
||||
* 模板方法:执行Stage后的清理工作
|
||||
*/
|
||||
protected void afterExecute(C context, StageResult result) {
|
||||
protected void afterExecute(C context, StageResult<C> result) {
|
||||
if (result.isSuccess()) {
|
||||
log.debug("[{}] 执行成功: {}", getName(), result.getMessage());
|
||||
} else if (result.isSkipped()) {
|
||||
@@ -80,15 +80,15 @@ public abstract class AbstractPipelineStage<C extends PhotoProcessContext> imple
|
||||
/**
|
||||
* 子类实现具体的处理逻辑
|
||||
*/
|
||||
protected abstract StageResult doExecute(C context);
|
||||
protected abstract StageResult<C> doExecute(C context);
|
||||
|
||||
/**
|
||||
* 最终执行方法(带钩子)
|
||||
*/
|
||||
@Override
|
||||
public final StageResult execute(C context) {
|
||||
public final StageResult<C> execute(C context) {
|
||||
beforeExecute(context);
|
||||
StageResult result = doExecute(context);
|
||||
StageResult<C> result = doExecute(context);
|
||||
afterExecute(context, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ public class PhotoProcessContext {
|
||||
private File processedFile;
|
||||
private boolean isLandscape = true;
|
||||
private boolean rotationApplied = false;
|
||||
private boolean cleaned = false;
|
||||
|
||||
/**
|
||||
* 图像需要旋转的角度(用于后续Stage使用)
|
||||
@@ -227,7 +228,11 @@ public class PhotoProcessContext {
|
||||
* 清理所有临时文件
|
||||
*/
|
||||
public void cleanup() {
|
||||
if (cleaned) {
|
||||
return;
|
||||
}
|
||||
tempFileManager.cleanup();
|
||||
cleaned = true;
|
||||
}
|
||||
|
||||
// ==================== Builder ====================
|
||||
|
||||
@@ -52,7 +52,7 @@ public class Pipeline<C extends PhotoProcessContext> {
|
||||
}
|
||||
|
||||
long stageStartTime = System.currentTimeMillis();
|
||||
StageResult result = stage.execute(context);
|
||||
StageResult<C> result = stage.execute(context);
|
||||
long stageDuration = System.currentTimeMillis() - stageStartTime;
|
||||
executedCount++;
|
||||
|
||||
@@ -60,12 +60,12 @@ public class Pipeline<C extends PhotoProcessContext> {
|
||||
|
||||
// 动态添加后续Stage
|
||||
if (result.getNextStages() != null && !result.getNextStages().isEmpty()) {
|
||||
List<PipelineStage<?>> nextStages = result.getNextStages();
|
||||
List<PipelineStage<C>> 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<C>) nextStage);
|
||||
PipelineStage<C> nextStage = nextStages.get(j);
|
||||
stages.add(i + 1 + j, nextStage);
|
||||
log.debug("[{}] - 插入Stage: {} 到位置 {}", name, nextStage.getName(), i + 1 + j);
|
||||
}
|
||||
}
|
||||
@@ -84,10 +84,12 @@ public class Pipeline<C extends PhotoProcessContext> {
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] 管线执行异常", name, e);
|
||||
throw new PipelineException("管线执行失败: " + e.getMessage(), e);
|
||||
} finally {
|
||||
safeCleanup(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void logStageResult(String stageName, StageResult result, long duration) {
|
||||
private void logStageResult(String stageName, StageResult<C> result, long duration) {
|
||||
String statusIcon = switch (result.getStatus()) {
|
||||
case SUCCESS -> "✓";
|
||||
case SKIPPED -> "○";
|
||||
@@ -114,4 +116,15 @@ public class Pipeline<C extends PhotoProcessContext> {
|
||||
public List<String> getStageNames() {
|
||||
return stages.stream().map(PipelineStage::getName).toList();
|
||||
}
|
||||
|
||||
private void safeCleanup(C context) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
context.cleanup();
|
||||
} catch (Exception cleanupError) {
|
||||
log.warn("[{}] 管线清理失败", name, cleanupError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public interface PipelineStage<C extends PhotoProcessContext> {
|
||||
* @param context 管线上下文
|
||||
* @return 执行结果
|
||||
*/
|
||||
StageResult execute(C context);
|
||||
StageResult<C> execute(C context);
|
||||
|
||||
/**
|
||||
* 获取Stage的执行优先级(用于排序)
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.List;
|
||||
* Stage执行结果
|
||||
*/
|
||||
@Getter
|
||||
public class StageResult {
|
||||
public class StageResult<C extends PhotoProcessContext> {
|
||||
|
||||
public enum Status {
|
||||
SUCCESS, // 执行成功
|
||||
@@ -23,9 +23,9 @@ public class StageResult {
|
||||
private final Status status;
|
||||
private final String message;
|
||||
private final Throwable exception;
|
||||
private final List<PipelineStage<?>> nextStages;
|
||||
private final List<PipelineStage<C>> nextStages;
|
||||
|
||||
private StageResult(Status status, String message, Throwable exception, List<PipelineStage<?>> nextStages) {
|
||||
private StageResult(Status status, String message, Throwable exception, List<PipelineStage<C>> nextStages) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.exception = exception;
|
||||
@@ -34,47 +34,47 @@ public class StageResult {
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
public static StageResult success() {
|
||||
return new StageResult(Status.SUCCESS, null, null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> success() {
|
||||
return new StageResult<>(Status.SUCCESS, null, null, null);
|
||||
}
|
||||
|
||||
public static StageResult success(String message) {
|
||||
return new StageResult(Status.SUCCESS, message, null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> success(String message) {
|
||||
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));
|
||||
public static <C extends PhotoProcessContext> StageResult<C> successWithNext(String message, PipelineStage<C>... stages) {
|
||||
return new StageResult<>(Status.SUCCESS, message, null, Arrays.asList(stages));
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功执行并动态添加后续Stage列表
|
||||
*/
|
||||
public static StageResult successWithNext(String message, List<PipelineStage<?>> stages) {
|
||||
return new StageResult(Status.SUCCESS, message, null, stages);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> successWithNext(String message, List<PipelineStage<C>> stages) {
|
||||
return new StageResult<>(Status.SUCCESS, message, null, stages);
|
||||
}
|
||||
|
||||
public static StageResult skipped() {
|
||||
return new StageResult(Status.SKIPPED, "条件不满足,跳过执行", null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> skipped() {
|
||||
return new StageResult<>(Status.SKIPPED, "条件不满足,跳过执行", null, null);
|
||||
}
|
||||
|
||||
public static StageResult skipped(String reason) {
|
||||
return new StageResult(Status.SKIPPED, reason, null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> skipped(String reason) {
|
||||
return new StageResult<>(Status.SKIPPED, reason, null, null);
|
||||
}
|
||||
|
||||
public static StageResult failed(String message) {
|
||||
return new StageResult(Status.FAILED, message, null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> failed(String message) {
|
||||
return new StageResult<>(Status.FAILED, message, null, null);
|
||||
}
|
||||
|
||||
public static StageResult failed(String message, Throwable exception) {
|
||||
return new StageResult(Status.FAILED, message, exception, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> failed(String message, Throwable exception) {
|
||||
return new StageResult<>(Status.FAILED, message, exception, null);
|
||||
}
|
||||
|
||||
public static StageResult degraded(String message) {
|
||||
return new StageResult(Status.DEGRADED, message, null, null);
|
||||
public static <C extends PhotoProcessContext> StageResult<C> degraded(String message) {
|
||||
return new StageResult<>(Status.DEGRADED, message, null, null);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class CleanupStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
try {
|
||||
int fileCount = context.getTempFileManager().getTempFileCount();
|
||||
context.cleanup();
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ConditionalRotateStage extends AbstractPipelineStage<PhotoProcessCo
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
try {
|
||||
File currentFile = context.getCurrentFile();
|
||||
if (currentFile == null || !currentFile.exists()) {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class DownloadStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
String url = context.getOriginalUrl();
|
||||
if (StringUtils.isBlank(url)) {
|
||||
return StageResult.failed("原图URL为空");
|
||||
|
||||
@@ -38,11 +38,7 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
|
||||
* 构造函数 - 使用默认配置
|
||||
*/
|
||||
public ImageEnhanceStage() {
|
||||
this.enhancerConfig = new BceEnhancerConfig();
|
||||
this.enhancerConfig.setAppId("119554288");
|
||||
this.enhancerConfig.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
|
||||
this.enhancerConfig.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");
|
||||
this.enhancerConfig.setQps(1.0f);
|
||||
this(buildConfigFromEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +62,7 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
// 检查配置是否完整
|
||||
if (!isConfigValid()) {
|
||||
log.warn("图像增强配置不完整,跳过增强处理。请在ImageEnhanceStage中配置百度云API凭证");
|
||||
@@ -151,6 +147,15 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
|
||||
return true;
|
||||
}
|
||||
|
||||
private static BceEnhancerConfig buildConfigFromEnvironment() {
|
||||
BceEnhancerConfig config = new BceEnhancerConfig();
|
||||
config.setAppId(System.getenv("BCE_IMAGE_APP_ID"));
|
||||
config.setApiKey(System.getenv("BCE_IMAGE_API_KEY"));
|
||||
config.setSecretKey(System.getenv("BCE_IMAGE_SECRET_KEY"));
|
||||
config.setQps(1.0f);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存MultipartFile到本地文件
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,7 @@ public class ImageOrientationStage extends AbstractPipelineStage<PhotoProcessCon
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
try {
|
||||
File currentFile = context.getCurrentFile();
|
||||
if (currentFile == null || !currentFile.exists()) {
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ImageQualityCheckStage extends AbstractPipelineStage<PhotoProcessCo
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
File currentFile = context.getCurrentFile();
|
||||
if (currentFile == null || !currentFile.exists()) {
|
||||
return StageResult.failed("当前文件不存在");
|
||||
@@ -107,7 +107,7 @@ public class ImageQualityCheckStage extends AbstractPipelineStage<PhotoProcessCo
|
||||
* 示例:也可以根据其他条件动态添加不同的Stage
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private StageResult checkAndAddMultipleStages(PhotoProcessContext context) {
|
||||
private StageResult<PhotoProcessContext> checkAndAddMultipleStages(PhotoProcessContext context) {
|
||||
// 示例:根据不同条件添加多个Stage
|
||||
|
||||
// 假设检测到需要多个增强操作
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.ycwl.basic.image.pipeline.core.StageResult;
|
||||
|
||||
public class NoOpStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
return StageResult.skipped("无操作");
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class PuzzleBorderStage extends AbstractPipelineStage<PhotoProcessContext
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
try {
|
||||
File currentFile = context.getCurrentFile();
|
||||
if (currentFile == null || !currentFile.exists()) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class RestoreOrientationStage extends AbstractPipelineStage<PhotoProcessC
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
try {
|
||||
File currentFile = context.getCurrentFile();
|
||||
if (currentFile == null || !currentFile.exists()) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.io.File;
|
||||
@Slf4j
|
||||
@StageConfig(
|
||||
stageId = "source_photo_update",
|
||||
optionalMode = StageOptionalMode.FORCE_ON,
|
||||
optionalMode = StageOptionalMode.UNSUPPORT,
|
||||
description = "源图片上传和数据库更新",
|
||||
defaultEnabled = true
|
||||
)
|
||||
@@ -47,7 +47,7 @@ public class SourcePhotoUpdateStage extends AbstractPipelineStage<PhotoProcessCo
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
File fileToUpload = context.getCurrentFile();
|
||||
if (fileToUpload == null || !fileToUpload.exists()) {
|
||||
return StageResult.failed("没有可上传的文件");
|
||||
|
||||
@@ -33,7 +33,7 @@ public class UploadStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
File fileToUpload = context.getCurrentFile();
|
||||
if (fileToUpload == null || !fileToUpload.exists()) {
|
||||
return StageResult.failed("没有可上传的文件");
|
||||
|
||||
@@ -55,7 +55,7 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
if (config == null || config.getWatermarkType() == null) {
|
||||
log.info("未配置水印类型,跳过水印处理");
|
||||
return StageResult.skipped("未配置水印");
|
||||
@@ -75,7 +75,7 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
}
|
||||
|
||||
try {
|
||||
StageResult result = applyWatermark(context, type);
|
||||
StageResult<PhotoProcessContext> result = applyWatermark(context, type);
|
||||
|
||||
if (i > 0) {
|
||||
String degradeMsg = String.format("降级: %s -> %s",
|
||||
@@ -115,7 +115,7 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
/**
|
||||
* 应用水印
|
||||
*/
|
||||
private StageResult applyWatermark(PhotoProcessContext context, ImageWatermarkOperatorEnum type)
|
||||
private StageResult<PhotoProcessContext> applyWatermark(PhotoProcessContext context, ImageWatermarkOperatorEnum type)
|
||||
throws Exception {
|
||||
|
||||
File currentFile = context.getCurrentFile();
|
||||
|
||||
@@ -34,13 +34,21 @@ public class TempFileManager {
|
||||
}
|
||||
|
||||
private Path initTempDirectory() {
|
||||
Path baseDir = Path.of(System.getProperty("java.io.tmpdir"), "photo_process", processId);
|
||||
try {
|
||||
Path systemTempDir = Files.createTempDirectory("photo_process_");
|
||||
log.debug("创建临时目录: {}", systemTempDir);
|
||||
return systemTempDir;
|
||||
Files.createDirectories(baseDir);
|
||||
log.debug("创建隔离临时目录: {}", baseDir);
|
||||
return baseDir;
|
||||
} catch (IOException e) {
|
||||
log.warn("无法创建系统临时目录,使用当前目录", e);
|
||||
return Path.of(".");
|
||||
log.warn("无法创建隔离临时目录,尝试使用系统临时目录", e);
|
||||
try {
|
||||
Path fallback = Files.createTempDirectory("photo_process_" + processId + "_");
|
||||
log.debug("创建备用临时目录: {}", fallback);
|
||||
return fallback;
|
||||
} catch (IOException ex) {
|
||||
log.warn("无法创建系统临时目录,使用当前目录", ex);
|
||||
return Path.of(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class PipelineBuilderTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
return StageResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,20 @@ class PipelineTest {
|
||||
assertTrue(names.contains("MockStage-stage2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipelineFailureStillCleansTempFiles() {
|
||||
Pipeline<PhotoProcessContext> pipeline = new PipelineBuilder<PhotoProcessContext>("CleanupGuarantee")
|
||||
.addStage(new TempFileFailStage())
|
||||
.build();
|
||||
|
||||
PhotoProcessContext context = createTestContext();
|
||||
|
||||
boolean success = pipeline.execute(context);
|
||||
|
||||
assertFalse(success);
|
||||
assertEquals(0, context.getTempFileManager().getTempFileCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建测试用的 Context
|
||||
*/
|
||||
@@ -137,9 +151,9 @@ class PipelineTest {
|
||||
*/
|
||||
private static class MockStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
private final String id;
|
||||
private final StageResult result;
|
||||
private final StageResult<PhotoProcessContext> result;
|
||||
|
||||
MockStage(String id, StageResult result) {
|
||||
MockStage(String id, StageResult<PhotoProcessContext> result) {
|
||||
this.id = id;
|
||||
this.result = result;
|
||||
}
|
||||
@@ -150,7 +164,7 @@ class PipelineTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -178,8 +192,22 @@ class PipelineTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
return StageResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TempFileFailStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TempFileFailStage";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
context.getTempFileManager().createTempFile("fail", ".tmp");
|
||||
return StageResult.failed("force failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testSuccess() {
|
||||
StageResult result = StageResult.success();
|
||||
StageResult<PhotoProcessContext> result = StageResult.success();
|
||||
|
||||
assertEquals(StageResult.Status.SUCCESS, result.getStatus());
|
||||
assertTrue(result.isSuccess());
|
||||
@@ -25,7 +25,7 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testSuccessWithMessage() {
|
||||
StageResult result = StageResult.success("处理完成");
|
||||
StageResult<PhotoProcessContext> result = StageResult.success("处理完成");
|
||||
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("处理完成", result.getMessage());
|
||||
@@ -36,7 +36,7 @@ class StageResultTest {
|
||||
MockStage nextStage1 = new MockStage("next1");
|
||||
MockStage nextStage2 = new MockStage("next2");
|
||||
|
||||
StageResult result = StageResult.successWithNext("添加后续Stage", nextStage1, nextStage2);
|
||||
StageResult<PhotoProcessContext> result = StageResult.successWithNext("添加后续Stage", nextStage1, nextStage2);
|
||||
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("添加后续Stage", result.getMessage());
|
||||
@@ -47,19 +47,19 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testSuccessWithNextStagesList() {
|
||||
List<PipelineStage<?>> stages = List.of(
|
||||
List<PipelineStage<PhotoProcessContext>> stages = List.of(
|
||||
new MockStage("next1"),
|
||||
new MockStage("next2")
|
||||
);
|
||||
|
||||
StageResult result = StageResult.successWithNext("添加列表", stages);
|
||||
StageResult<PhotoProcessContext> result = StageResult.successWithNext("添加列表", stages);
|
||||
|
||||
assertEquals(2, result.getNextStages().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSkipped() {
|
||||
StageResult result = StageResult.skipped();
|
||||
StageResult<PhotoProcessContext> result = StageResult.skipped();
|
||||
|
||||
assertEquals(StageResult.Status.SKIPPED, result.getStatus());
|
||||
assertTrue(result.isSkipped());
|
||||
@@ -69,7 +69,7 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testSkippedWithReason() {
|
||||
StageResult result = StageResult.skipped("图片类型不匹配");
|
||||
StageResult<PhotoProcessContext> result = StageResult.skipped("图片类型不匹配");
|
||||
|
||||
assertTrue(result.isSkipped());
|
||||
assertEquals("图片类型不匹配", result.getMessage());
|
||||
@@ -77,7 +77,7 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testFailed() {
|
||||
StageResult result = StageResult.failed("下载失败");
|
||||
StageResult<PhotoProcessContext> result = StageResult.failed("下载失败");
|
||||
|
||||
assertEquals(StageResult.Status.FAILED, result.getStatus());
|
||||
assertTrue(result.isFailed());
|
||||
@@ -88,7 +88,7 @@ class StageResultTest {
|
||||
@Test
|
||||
void testFailedWithException() {
|
||||
Exception exception = new RuntimeException("网络错误");
|
||||
StageResult result = StageResult.failed("下载失败", exception);
|
||||
StageResult<PhotoProcessContext> result = StageResult.failed("下载失败", exception);
|
||||
|
||||
assertTrue(result.isFailed());
|
||||
assertEquals("下载失败", result.getMessage());
|
||||
@@ -97,7 +97,7 @@ class StageResultTest {
|
||||
|
||||
@Test
|
||||
void testDegraded() {
|
||||
StageResult result = StageResult.degraded("使用备用方案");
|
||||
StageResult<PhotoProcessContext> result = StageResult.degraded("使用备用方案");
|
||||
|
||||
assertEquals(StageResult.Status.DEGRADED, result.getStatus());
|
||||
assertTrue(result.isDegraded());
|
||||
@@ -128,9 +128,9 @@ class StageResultTest {
|
||||
@Test
|
||||
void testNextStagesImmutable() {
|
||||
MockStage stage1 = new MockStage("stage1");
|
||||
StageResult result = StageResult.successWithNext("test", stage1);
|
||||
StageResult<PhotoProcessContext> result = StageResult.successWithNext("test", stage1);
|
||||
|
||||
List<PipelineStage<?>> nextStages = result.getNextStages();
|
||||
List<PipelineStage<PhotoProcessContext>> nextStages = result.getNextStages();
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> {
|
||||
nextStages.add(new MockStage("stage2"));
|
||||
@@ -153,7 +153,7 @@ class StageResultTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StageResult doExecute(PhotoProcessContext context) {
|
||||
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
|
||||
return StageResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ class ImageEnhanceStageTest {
|
||||
BceEnhancerConfig config = stage.getEnhancerConfig();
|
||||
|
||||
assertNotNull(config);
|
||||
assertEquals("119554288", config.getAppId());
|
||||
assertEquals("OX6QoijgKio3eVtA0PiUVf7f", config.getApiKey());
|
||||
assertEquals("dYatXReVriPeiktTjUblhfubpcmYfuMk", config.getSecretKey());
|
||||
assertEquals(1.0f, config.getQps());
|
||||
assertEquals(System.getenv("BCE_IMAGE_APP_ID"), config.getAppId());
|
||||
assertEquals(System.getenv("BCE_IMAGE_API_KEY"), config.getApiKey());
|
||||
assertEquals(System.getenv("BCE_IMAGE_SECRET_KEY"), config.getSecretKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user