refactor(image): 重构图片旋转和恢复逻辑

- 将 needRotation 标志重命名为 rotationApplied
- 修改条件旋转阶段的执行逻辑,基于实际旋转角度判断
- 实现通用的图片恢复旋转功能,支持90/180/270度恢复
- 添加恢复旋转角度计算方法 getRestoreAngle
- 更新水印阶段的旋转状态检查逻辑
- 完善单元测试覆盖各种旋转场景
- 优化日志记录和错误处理流程
This commit is contained in:
2025-11-26 20:15:02 +08:00
parent 40d5874560
commit 1945639f90
23 changed files with 147 additions and 79 deletions

View File

@@ -113,7 +113,7 @@ class PipelineBuilderTest {
}
@Override
protected StageResult doExecute(PhotoProcessContext context) {
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
return StageResult.success();
}
}

View File

@@ -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");
}
}
}

View File

@@ -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();
}
}

View File

@@ -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