refactor(pipeline): 重构人脸匹配管线为核心管线模块

- 移除专用人脸匹配管线实现,统一使用通用管线模块
- 更新所有Stage类继承自通用管线Stage基类
- 调整包路径引用从face.pipeline到pipeline.core
- 修改上下文类实现通用管线上下文接口
- 删除冗余的人脸匹配专用注解和枚举类
- 更新工厂类引用至新的通用管线构建器
- 保持Stage功能逻辑不变仅调整继承结构
This commit is contained in:
2025-12-03 21:47:43 +08:00
parent ecd5378b26
commit 60b4473279
57 changed files with 338 additions and 898 deletions

View File

@@ -4,14 +4,14 @@ 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.ImageSRStage;
import com.ycwl.basic.image.pipeline.stages.SourcePhotoUpdateStage;
import com.ycwl.basic.image.pipeline.stages.CleanupStage;
import com.ycwl.basic.pipeline.core.Pipeline;
import com.ycwl.basic.pipeline.core.PipelineBuilder;
import com.ycwl.basic.mapper.AioDeviceMapper;
import com.ycwl.basic.mapper.MemberMapper;
import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity;

View File

@@ -1,97 +0,0 @@
package com.ycwl.basic.face.pipeline.core;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
/**
* 人脸匹配Pipeline Stage抽象基类
* 提供默认实现和通用逻辑
*
* @param <C> Context类型,必须继承FaceMatchingContext
*/
@Slf4j
public abstract class AbstractFaceMatchingStage<C extends FaceMatchingContext> implements PipelineStage<C> {
/**
* 最终的shouldExecute判断
* 整合了外部配置控制和业务逻辑判断
*/
@Override
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;
}
/**
* 模板方法:执行Stage前的准备工作
*/
protected void beforeExecute(C context) {
log.debug("[{}] 开始执行", getName());
}
/**
* 模板方法:执行Stage后的清理工作
*/
protected void afterExecute(C context, StageResult<C> result) {
if (result.isSuccess()) {
log.debug("[{}] 执行成功: {}", getName(), result.getMessage());
} else if (result.isSkipped()) {
log.debug("[{}] 已跳过: {}", getName(), result.getMessage());
} else if (result.isDegraded()) {
log.warn("[{}] 降级执行: {}", getName(), result.getMessage());
} else {
log.error("[{}] 执行失败: {}", getName(), result.getMessage(), result.getException());
}
}
/**
* 子类实现具体的处理逻辑
*/
protected abstract StageResult<C> doExecute(C context);
/**
* 最终执行方法(带钩子)
*/
@Override
public final StageResult<C> execute(C context) {
beforeExecute(context);
StageResult<C> result = doExecute(context);
afterExecute(context, result);
return result;
}
}

View File

@@ -7,6 +7,7 @@ import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.pipeline.core.PipelineContext;
import lombok.Getter;
import lombok.Setter;
@@ -20,7 +21,7 @@ import java.util.Map;
*/
@Getter
@Setter
public class FaceMatchingContext {
public class FaceMatchingContext implements PipelineContext {
// ==================== 核心字段(构造时必填)====================
@@ -170,6 +171,7 @@ public class FaceMatchingContext {
* @param defaultEnabled 默认值(当配置未指定时使用)
* @return true-启用, false-禁用
*/
@Override
public boolean isStageEnabled(String stageId, boolean defaultEnabled) {
return stageEnabledMap.getOrDefault(stageId, defaultEnabled);
}
@@ -180,6 +182,7 @@ public class FaceMatchingContext {
* @param stageId Stage唯一标识
* @return true-启用, false-禁用
*/
@Override
public boolean isStageEnabled(String stageId) {
return stageEnabledMap.getOrDefault(stageId, false);
}

View File

@@ -1,119 +0,0 @@
package com.ycwl.basic.face.pipeline.core;
import com.ycwl.basic.face.pipeline.exception.PipelineException;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* 人脸匹配管线
* 按顺序执行一系列Stage
*
* @param <C> Context类型,必须继承FaceMatchingContext
*/
@Slf4j
public class Pipeline<C extends FaceMatchingContext> {
private final List<PipelineStage<C>> stages;
private final String name;
public Pipeline(String name, List<PipelineStage<C>> stages) {
this.name = name;
this.stages = new ArrayList<>(stages);
}
/**
* 执行管线
*
* @param context 管线上下文
* @return 执行成功返回true
* @throws PipelineException 管线执行异常
*/
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<C> stage = stages.get(i);
String stageName = stage.getName();
log.debug("[{}] [{}/{}] 准备执行Stage: {}", name, i + 1, stages.size(), stageName);
if (!stage.shouldExecute(context)) {
log.debug("[{}] Stage {} 条件不满足,跳过执行", name, stageName);
continue;
}
long stageStartTime = System.currentTimeMillis();
StageResult<C> result = stage.execute(context);
long stageDuration = System.currentTimeMillis() - stageStartTime;
executedCount++;
logStageResult(stageName, result, stageDuration);
// 动态添加后续Stage
if (result.getNextStages() != null && !result.getNextStages().isEmpty()) {
List<PipelineStage<C>> nextStages = result.getNextStages();
log.info("[{}] Stage {} 动态添加了 {} 个后续Stage", name, stageName, nextStages.size());
for (int j = 0; j < nextStages.size(); j++) {
PipelineStage<C> nextStage = nextStages.get(j);
stages.add(i + 1 + j, nextStage);
log.debug("[{}] - 插入Stage: {} 到位置 {}", name, nextStage.getName(), i + 1 + j);
}
}
if (result.isFailed()) {
log.error("[{}] Stage {} 执行失败,管线终止", name, stageName);
return false;
}
}
long totalDuration = System.currentTimeMillis() - startTime;
log.info("[{}] 人脸匹配管线执行完成, 总Stage数: {}, 实际执行: {}, 耗时: {}ms",
name, stages.size(), executedCount, totalDuration);
return true;
} catch (Exception e) {
log.error("[{}] 人脸匹配管线执行异常", name, e);
throw new PipelineException("管线执行失败: " + e.getMessage(), e);
}
}
private void logStageResult(String stageName, StageResult<C> result, long duration) {
String statusIcon = switch (result.getStatus()) {
case SUCCESS -> "";
case SKIPPED -> "";
case DEGRADED -> "";
case FAILED -> "";
};
log.info("[{}] {} Stage {} - {} (耗时: {}ms)",
name, statusIcon, stageName, result.getStatus(), duration);
if (result.getMessage() != null) {
log.debug("[{}] 详情: {}", name, result.getMessage());
}
}
public String getName() {
return name;
}
public int getStageCount() {
return stages.size();
}
public List<String> getStageNames() {
return stages.stream().map(PipelineStage::getName).toList();
}
}

View File

@@ -1,80 +0,0 @@
package com.ycwl.basic.face.pipeline.core;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Pipeline构建器
* 使用Builder模式动态组装人脸匹配管线
*
* @param <C> Context类型,必须继承FaceMatchingContext
*/
public class PipelineBuilder<C extends FaceMatchingContext> {
private String name = "DefaultFaceMatchingPipeline";
private final List<PipelineStage<C>> stages = new ArrayList<>();
public PipelineBuilder() {
}
public PipelineBuilder(String name) {
this.name = name;
}
/**
* 设置管线名称
*/
public PipelineBuilder<C> name(String name) {
this.name = name;
return this;
}
/**
* 添加Stage
*/
public PipelineBuilder<C> addStage(PipelineStage<C> stage) {
if (stage != null) {
this.stages.add(stage);
}
return this;
}
/**
* 批量添加Stage
*/
public PipelineBuilder<C> addStages(List<PipelineStage<C>> stages) {
if (stages != null) {
this.stages.addAll(stages);
}
return this;
}
/**
* 条件性添加Stage
*/
public PipelineBuilder<C> addStageIf(boolean condition, PipelineStage<C> stage) {
if (condition && stage != null) {
this.stages.add(stage);
}
return this;
}
/**
* 按优先级排序Stage
*/
public PipelineBuilder<C> sortByPriority() {
this.stages.sort(Comparator.comparingInt(PipelineStage::getPriority));
return this;
}
/**
* 构建Pipeline
*/
public Pipeline<C> build() {
if (stages.isEmpty()) {
throw new IllegalStateException("人脸匹配管线至少需要一个Stage");
}
return new Pipeline<>(name, stages);
}
}

View File

@@ -1,50 +0,0 @@
package com.ycwl.basic.face.pipeline.core;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
/**
* 管线处理阶段接口
* 每个Stage负责一个独立的人脸匹配处理步骤
*
* @param <C> Context类型,必须继承FaceMatchingContext
*/
public interface PipelineStage<C extends FaceMatchingContext> {
/**
* 获取Stage名称(用于日志和监控)
*/
String getName();
/**
* 判断是否需要执行此Stage
* 支持条件性执行(如:只有新用户才设置任务状态)
*
* @param context 管线上下文
* @return true-执行, false-跳过
*/
boolean shouldExecute(C context);
/**
* 执行Stage处理逻辑
*
* @param context 管线上下文
* @return 执行结果
*/
StageResult<C> execute(C context);
/**
* 获取Stage的执行优先级(用于排序)
* 数值越小优先级越高,默认为100
*/
default int getPriority() {
return 100;
}
/**
* 获取Stage配置注解(用于反射读取可选性控制信息)
* @return Stage配置注解,如果未标注则返回null
*/
default StageConfig getStageConfig() {
return this.getClass().getAnnotation(StageConfig.class);
}
}

View File

@@ -1,101 +0,0 @@
package com.ycwl.basic.face.pipeline.core;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Stage执行结果
*
* @param <C> Context类型,必须继承FaceMatchingContext
*/
@Getter
public class StageResult<C extends FaceMatchingContext> {
public enum Status {
SUCCESS, // 执行成功
SKIPPED, // 跳过执行
FAILED, // 执行失败
DEGRADED // 降级执行
}
private final Status status;
private final String message;
private final Throwable exception;
private final List<PipelineStage<C>> nextStages;
private StageResult(Status status, String message, Throwable exception, List<PipelineStage<C>> nextStages) {
this.status = status;
this.message = message;
this.exception = exception;
this.nextStages = nextStages != null
? Collections.unmodifiableList(new ArrayList<>(nextStages))
: Collections.emptyList();
}
public static <C extends FaceMatchingContext> StageResult<C> success() {
return new StageResult<>(Status.SUCCESS, null, null, null);
}
public static <C extends FaceMatchingContext> StageResult<C> success(String message) {
return new StageResult<>(Status.SUCCESS, message, null, null);
}
/**
* 成功执行并动态添加后续Stage
*/
@SafeVarargs
public static <C extends FaceMatchingContext> StageResult<C> successWithNext(String message, PipelineStage<C>... stages) {
return new StageResult<>(Status.SUCCESS, message, null, Arrays.asList(stages));
}
/**
* 成功执行并动态添加后续Stage列表
*/
public static <C extends FaceMatchingContext> StageResult<C> successWithNext(String message, List<PipelineStage<C>> stages) {
return new StageResult<>(Status.SUCCESS, message, null, stages);
}
public static <C extends FaceMatchingContext> StageResult<C> skipped() {
return new StageResult<>(Status.SKIPPED, "条件不满足,跳过执行", null, null);
}
public static <C extends FaceMatchingContext> StageResult<C> skipped(String reason) {
return new StageResult<>(Status.SKIPPED, reason, null, null);
}
public static <C extends FaceMatchingContext> StageResult<C> failed(String message) {
return new StageResult<>(Status.FAILED, message, null, null);
}
public static <C extends FaceMatchingContext> StageResult<C> failed(String message, Throwable exception) {
return new StageResult<>(Status.FAILED, message, exception, null);
}
public static <C extends FaceMatchingContext> StageResult<C> degraded(String message) {
return new StageResult<>(Status.DEGRADED, message, null, null);
}
public boolean isSuccess() {
return status == Status.SUCCESS;
}
public boolean isSkipped() {
return status == Status.SKIPPED;
}
public boolean isFailed() {
return status == Status.FAILED;
}
public boolean isDegraded() {
return status == Status.DEGRADED;
}
public boolean canContinue() {
return status == Status.SUCCESS || status == Status.SKIPPED || status == Status.DEGRADED;
}
}

View File

@@ -1,29 +0,0 @@
package com.ycwl.basic.face.pipeline.enums;
/**
* Stage可选模式枚举
* 控制Stage是否支持外部配置
*/
public enum StageOptionalMode {
/**
* 强制执行
* 不检查外部配置,总是执行(除非业务逻辑判断跳过)
* 例如: PrepareContextStage、FaceRecognitionStage
*/
FORCE_ON,
/**
* 支持外部控制
* 检查外部配置来决定是否执行
* 例如: RecordMetricsStage、FaceRecoveryStage
*/
SUPPORT,
/**
* 不支持外部控制
* 完全由业务逻辑控制是否执行,不检查外部配置
* 例如: SourceRelationStage(需要sampleListIds不为空)
*/
UNSUPPORT
}

View File

@@ -1,8 +1,8 @@
package com.ycwl.basic.face.pipeline.factory;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.Pipeline;
import com.ycwl.basic.face.pipeline.core.PipelineBuilder;
import com.ycwl.basic.pipeline.core.Pipeline;
import com.ycwl.basic.pipeline.core.PipelineBuilder;
import com.ycwl.basic.face.pipeline.enums.FaceMatchingScene;
import com.ycwl.basic.face.pipeline.stages.*;
import com.ycwl.basic.service.pc.helper.ScenicConfigFacade;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.service.pc.processor.SourceRelationProcessor;
import lombok.extern.slf4j.Slf4j;
@@ -32,7 +32,7 @@ import java.util.List;
optionalMode = StageOptionalMode.UNSUPPORT,
description = "构建源文件关联关系"
)
public class BuildSourceRelationStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class BuildSourceRelationStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private SourceRelationProcessor sourceRelationProcessor;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.biz.TaskStatusBiz;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.service.pc.helper.ScenicConfigFacade;
import com.ycwl.basic.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +28,7 @@ import org.springframework.stereotype.Component;
optionalMode = StageOptionalMode.UNSUPPORT,
description = "根据配置创建视频任务"
)
public class CreateTaskStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class CreateTaskStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private ScenicConfigFacade scenicConfigFacade;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.pc.helper.SearchResultMerger;
@@ -34,7 +34,7 @@ import java.util.List;
optionalMode = StageOptionalMode.FORCE_ON,
description = "根据配置执行自定义人脸搜索"
)
public class CustomFaceSearchStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class CustomFaceSearchStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private TaskFaceService taskFaceService;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.mapper.VideoMapper;
import com.ycwl.basic.repository.MemberRelationRepository;
@@ -28,7 +28,7 @@ import org.springframework.stereotype.Component;
optionalMode = StageOptionalMode.FORCE_ON,
description = "删除人脸旧关系数据"
)
public class DeleteOldRelationsStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class DeleteOldRelationsStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private SourceMapper sourceMapper;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.task.TaskFaceService;
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +28,7 @@ import org.springframework.stereotype.Component;
optionalMode = StageOptionalMode.FORCE_ON,
description = "执行人脸识别搜索"
)
public class FaceRecognitionStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class FaceRecognitionStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private TaskFaceService taskFaceService;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.pc.processor.FaceRecoveryStrategy;
import lombok.extern.slf4j.Slf4j;
@@ -29,7 +29,7 @@ import org.springframework.stereotype.Component;
description = "执行人脸识别补救逻辑",
defaultEnabled = true
)
public class FaceRecoveryStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class FaceRecoveryStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceRecoveryStrategy faceRecoveryStrategy;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.repository.DeviceRepository;
@@ -40,7 +40,7 @@ import java.util.stream.Collectors;
description = "按设备照片数量限制筛选样本",
defaultEnabled = true
)
public class FilterByDevicePhotoLimitStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class FilterByDevicePhotoLimitStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private DeviceRepository deviceRepository;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -37,7 +37,7 @@ import java.util.stream.Collectors;
description = "按游览时间范围筛选样本",
defaultEnabled = true
)
public class FilterByTimeRangeStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class FilterByTimeRangeStage extends AbstractPipelineStage<FaceMatchingContext> {
@Override
public String getName() {

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.face.pipeline.helper.PuzzleGenerationOrchestrator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
description = "异步生成拼图模板",
defaultEnabled = true
)
public class GeneratePuzzleStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class GeneratePuzzleStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private PuzzleGenerationOrchestrator puzzleOrchestrator;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.service.pc.processor.VideoRecreationHandler;
import lombok.extern.slf4j.Slf4j;
@@ -32,7 +32,7 @@ import java.util.List;
description = "处理视频重切逻辑",
defaultEnabled = true
)
public class HandleVideoRecreationStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class HandleVideoRecreationStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private VideoRecreationHandler videoRecreationHandler;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import lombok.extern.slf4j.Slf4j;
@@ -30,7 +30,7 @@ import java.util.List;
optionalMode = StageOptionalMode.FORCE_ON,
description = "加载用户选择的人脸样本"
)
public class LoadFaceSamplesStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class LoadFaceSamplesStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceSampleMapper faceSampleMapper;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import lombok.extern.slf4j.Slf4j;
@@ -38,7 +38,7 @@ import java.util.List;
optionalMode = StageOptionalMode.UNSUPPORT,
description = "加载匹配样本实体到缓存"
)
public class LoadMatchedSamplesStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class LoadMatchedSamplesStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceSampleMapper faceSampleMapper;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.repository.MemberRelationRepository;
@@ -31,7 +31,7 @@ import java.util.List;
optionalMode = StageOptionalMode.FORCE_ON,
description = "持久化源文件关联关系"
)
public class PersistRelationsStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class PersistRelationsStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private SourceMapper sourceMapper;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
@@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
optionalMode = StageOptionalMode.FORCE_ON,
description = "准备人脸匹配上下文数据"
)
public class PrepareContextStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class PrepareContextStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceRepository faceRepository;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.service.pc.processor.BuyStatusProcessor;
import lombok.extern.slf4j.Slf4j;
@@ -33,7 +33,7 @@ import java.util.List;
description = "处理源文件购买状态",
defaultEnabled = true
)
public class ProcessBuyStatusStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class ProcessBuyStatusStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private BuyStatusProcessor buyStatusProcessor;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.service.pc.processor.SourceRelationProcessor;
import lombok.extern.slf4j.Slf4j;
@@ -33,7 +33,7 @@ import java.util.List;
description = "处理免费源文件逻辑",
defaultEnabled = true
)
public class ProcessFreeSourceStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class ProcessFreeSourceStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private SourceRelationProcessor sourceRelationProcessor;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.FaceMatchingScene;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.service.pc.helper.FaceMetricsRecorder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Component;
description = "记录自定义匹配指标",
defaultEnabled = true
)
public class RecordCustomMatchMetricsStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class RecordCustomMatchMetricsStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceMetricsRecorder metricsRecorder;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.service.pc.helper.FaceMetricsRecorder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Component;
description = "记录人脸识别指标",
defaultEnabled = true
)
public class RecordMetricsStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class RecordMetricsStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceMetricsRecorder metricsRecorder;

View File

@@ -1,11 +1,11 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.biz.TaskStatusBiz;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,7 +25,7 @@ import org.springframework.stereotype.Component;
optionalMode = StageOptionalMode.FORCE_ON,
description = "设置新用户任务状态"
)
public class SetTaskStatusStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class SetTaskStatusStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private TaskStatusBiz taskStatusBiz;

View File

@@ -1,10 +1,10 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.annotation.StageConfig;
import com.ycwl.basic.face.pipeline.core.AbstractFaceMatchingStage;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
optionalMode = StageOptionalMode.FORCE_ON,
description = "更新人脸识别结果到数据库"
)
public class UpdateFaceResultStage extends AbstractFaceMatchingStage<FaceMatchingContext> {
public class UpdateFaceResultStage extends AbstractPipelineStage<FaceMatchingContext> {
@Autowired
private FaceMapper faceMapper;

View File

@@ -1,45 +0,0 @@
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;
}

View File

@@ -8,6 +8,7 @@ import com.ycwl.basic.model.Crop;
import com.ycwl.basic.model.PrinterOrderItem;
import com.ycwl.basic.image.pipeline.util.TempFileManager;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.pipeline.core.PipelineContext;
import lombok.Getter;
import lombok.Setter;
@@ -23,7 +24,7 @@ import java.util.function.Consumer;
*/
@Getter
@Setter
public class PhotoProcessContext {
public class PhotoProcessContext implements PipelineContext {
// ==================== 核心字段(构造时必填)====================
@@ -192,6 +193,7 @@ public class PhotoProcessContext {
* @param defaultEnabled 默认值(当配置未指定时使用)
* @return true-启用, false-禁用
*/
@Override
public boolean isStageEnabled(String stageId, boolean defaultEnabled) {
return stageEnabledMap.getOrDefault(stageId, defaultEnabled);
}
@@ -202,6 +204,7 @@ public class PhotoProcessContext {
* @param stageId Stage唯一标识
* @return true-启用, false-禁用
*/
@Override
public boolean isStageEnabled(String stageId) {
return stageEnabledMap.getOrDefault(stageId, false);
}
@@ -292,6 +295,7 @@ public class PhotoProcessContext {
/**
* 清理所有临时文件
*/
@Override
public void cleanup() {
if (cleaned) {
return;

View File

@@ -1,50 +0,0 @@
package com.ycwl.basic.image.pipeline.core;
import com.ycwl.basic.image.pipeline.annotation.StageConfig;
/**
* 管线处理阶段接口
* 每个Stage负责一个独立的图片处理步骤
*
* @param <C> Context类型
*/
public interface PipelineStage<C extends PhotoProcessContext> {
/**
* 获取Stage名称(用于日志和监控)
*/
String getName();
/**
* 判断是否需要执行此Stage
* 支持条件性执行(如:只有竖图才需要旋转)
*
* @param context 管线上下文
* @return true-执行, false-跳过
*/
boolean shouldExecute(C context);
/**
* 执行Stage处理逻辑
*
* @param context 管线上下文
* @return 执行结果
*/
StageResult<C> execute(C context);
/**
* 获取Stage的执行优先级(用于排序)
* 数值越小优先级越高,默认为100
*/
default int getPriority() {
return 100;
}
/**
* 获取Stage配置注解(用于反射读取可选性控制信息)
* @return Stage配置注解,如果未标注则返回null
*/
default StageConfig getStageConfig() {
return this.getClass().getAnnotation(StageConfig.class);
}
}

View File

@@ -1,35 +0,0 @@
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;
}
}

View File

@@ -1,15 +0,0 @@
package com.ycwl.basic.image.pipeline.exception;
/**
* 管线处理异常基类
*/
public class PipelineException extends RuntimeException {
public PipelineException(String message) {
super(message);
}
public PipelineException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,10 +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.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
/**

View File

@@ -1,11 +1,11 @@
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.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,12 +1,12 @@
package com.ycwl.basic.image.pipeline.stages;
import cn.hutool.http.HttpUtil;
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.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

View File

@@ -2,12 +2,12 @@ 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 com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

View File

@@ -1,13 +1,13 @@
package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.pipeline.enums.ImageSource;
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.ImageSource;
import com.ycwl.basic.image.pipeline.enums.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.model.Crop;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,11 +1,11 @@
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.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;

View File

@@ -2,12 +2,12 @@ package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.enhancer.adapter.BceImageSR;
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 com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

View File

@@ -1,8 +1,8 @@
package com.ycwl.basic.image.pipeline.stages;
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.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
public class NoOpStage extends AbstractPipelineStage<PhotoProcessContext> {
@Override

View File

@@ -1,11 +1,11 @@
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.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,11 +1,11 @@
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.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,10 +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.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.service.pc.SourceService;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,10 +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.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;

View File

@@ -1,15 +1,15 @@
package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.enums.ImageType;
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.ImageType;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

View File

@@ -1,38 +1,38 @@
package com.ycwl.basic.face.pipeline.annotation;
package com.ycwl.basic.pipeline.annotation;
import com.ycwl.basic.face.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.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的元数据和可选性控制
* Stage 配置注解统一声明 Stage 的元信息与可选性控制
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StageConfig {
/**
* Stage唯一标识
* 用于外部配置控制
* Stage 唯一标识用于外部配置或监控引用
*/
String stageId();
/**
* 可选模式
* 可选模式默认不受外部控制
*/
StageOptionalMode optionalMode() default StageOptionalMode.UNSUPPORT;
/**
* 描述信息
* 描述信息便于文档与日志
*/
String description() default "";
/**
* 默认是否启用(optionalMode=SUPPORT时生效)
* 默认是否启用仅在 optionalMode=SUPPORT 时生效
*/
boolean defaultEnabled() default true;
}

View File

@@ -1,70 +1,46 @@
package com.ycwl.basic.image.pipeline.core;
package com.ycwl.basic.pipeline.core;
import com.ycwl.basic.image.pipeline.annotation.StageConfig;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
/**
* Pipeline Stage抽象基类
* 提供默认实现和通用逻辑
* 通用 Stage 抽象基类封装可选性判断与钩子逻辑
*/
@Slf4j
public abstract class AbstractPipelineStage<C extends PhotoProcessContext> implements PipelineStage<C> {
public abstract class AbstractPipelineStage<C extends PipelineContext> implements PipelineStage<C> {
/**
* 最终的shouldExecute判断
* 整合了外部配置控制和业务逻辑判断
*/
@Override
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);
log.debug("[{}] Stage 被外部配置禁用", stageId);
return false;
}
}
// UNSUPPORT不检查外部配置直接走业务逻辑
}
// 2. 执行业务逻辑判断
return shouldExecuteByBusinessLogic(context);
}
/**
* 子类实现业务逻辑判断
* 默认总是执行
*
* 子类可以覆盖此方法实现条件性执行
* 例如: 只有竖图才旋转, 只有普通照片才加水印等
*/
protected boolean shouldExecuteByBusinessLogic(C context) {
return true;
}
/**
* 模板方法:执行Stage前的准备工作
*/
protected void beforeExecute(C context) {
log.debug("[{}] 开始执行", getName());
}
/**
* 模板方法:执行Stage后的清理工作
*/
protected void afterExecute(C context, StageResult<C> result) {
if (result.isSuccess()) {
log.debug("[{}] 执行成功: {}", getName(), result.getMessage());
@@ -77,14 +53,8 @@ public abstract class AbstractPipelineStage<C extends PhotoProcessContext> imple
}
}
/**
* 子类实现具体的处理逻辑
*/
protected abstract StageResult<C> doExecute(C context);
/**
* 最终执行方法(带钩子)
*/
@Override
public final StageResult<C> execute(C context) {
beforeExecute(context);

View File

@@ -1,17 +1,16 @@
package com.ycwl.basic.image.pipeline.core;
package com.ycwl.basic.pipeline.core;
import com.ycwl.basic.image.pipeline.exception.PipelineException;
import com.ycwl.basic.pipeline.exception.PipelineException;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* 图片处理管线
* 按顺序执行一系列Stage
* 通用 Pipeline 实现负责顺序执行 Stage 并支持动态插入
*/
@Slf4j
public class Pipeline<C extends PhotoProcessContext> {
public class Pipeline<C extends PipelineContext> {
private final List<PipelineStage<C>> stages;
private final String name;
@@ -21,30 +20,27 @@ public class Pipeline<C extends PhotoProcessContext> {
this.stages = new ArrayList<>(stages);
}
/**
* 执行管线
*
* @param context 管线上下文
* @return 执行成功返回true
* @throws PipelineException 管线执行异常
*/
public boolean execute(C context) {
log.info("[{}] 开始执行管线, Stage数量: {}", name, stages.size());
log.info("[{}] Pipeline 开始执行, Stage 数量: {}", name, stages.size());
long startTime = System.currentTimeMillis();
int maxStages = 100; // 防止无限循环
int maxStages = 100;
int executedCount = 0;
if (context != null) {
context.beforePipeline();
}
try {
for (int i = 0; i < stages.size(); i++) {
if (executedCount >= maxStages) {
log.error("[{}] Stage执行数量超过最大限制({}),可能存在循环依赖", name, maxStages);
log.error("[{}] Stage 执行数量超过最大限制({}), 可能存在循环依赖", name, maxStages);
throw new PipelineException("Stage执行数量超过最大限制,可能存在循环依赖");
}
PipelineStage<C> stage = stages.get(i);
String stageName = stage.getName();
log.debug("[{}] [{}/{}] 准备执行Stage: {}", name, i + 1, stages.size(), stageName);
log.debug("[{}] [{}/{}] 准备执行 Stage: {}", name, i + 1, stages.size(), stageName);
if (!stage.shouldExecute(context)) {
log.debug("[{}] Stage {} 条件不满足,跳过执行", name, stageName);
@@ -58,31 +54,34 @@ public class Pipeline<C extends PhotoProcessContext> {
logStageResult(stageName, result, stageDuration);
// 动态添加后续Stage
if (result.getNextStages() != null && !result.getNextStages().isEmpty()) {
List<PipelineStage<C>> nextStages = result.getNextStages();
log.info("[{}] Stage {} 动态添加了 {} 个后续Stage", name, stageName, nextStages.size());
log.info("[{}] Stage {} 动态添加了 {} 个后续 Stage", name, stageName, nextStages.size());
for (int j = 0; j < nextStages.size(); j++) {
PipelineStage<C> nextStage = nextStages.get(j);
stages.add(i + 1 + j, nextStage);
log.debug("[{}] - 插入Stage: {} 到位置 {}", name, nextStage.getName(), i + 1 + j);
log.debug("[{}] - 插入 Stage: {} 到位置 {}", name, nextStage.getName(), i + 1 + j);
}
}
if (result.isFailed()) {
log.error("[{}] Stage {} 执行失败,管线终止", name, stageName);
log.error("[{}] Stage {} 执行失败, Pipeline 终止", name, stageName);
return false;
}
}
long totalDuration = System.currentTimeMillis() - startTime;
log.info("[{}] 管线执行完成, 总Stage数: {}, 实际执行: {}, 耗时: {}ms",
log.info("[{}] Pipeline 执行完成, 总 Stage 数: {}, 实际执行: {}, 耗时: {}ms",
name, stages.size(), executedCount, totalDuration);
if (context != null) {
context.afterPipeline();
}
return true;
} catch (Exception e) {
log.error("[{}] 管线执行异常", name, e);
log.error("[{}] Pipeline 执行异常", name, e);
throw new PipelineException("管线执行失败: " + e.getMessage(), e);
} finally {
safeCleanup(context);
@@ -124,7 +123,7 @@ public class Pipeline<C extends PhotoProcessContext> {
try {
context.cleanup();
} catch (Exception cleanupError) {
log.warn("[{}] 管线清理失败", name, cleanupError);
log.warn("[{}] Pipeline 清理失败", name, cleanupError);
}
}
}

View File

@@ -1,14 +1,13 @@
package com.ycwl.basic.image.pipeline.core;
package com.ycwl.basic.pipeline.core;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Pipeline构建器
* 使用Builder模式动态组装管线
* 通用 Pipeline 构建器
*/
public class PipelineBuilder<C extends PhotoProcessContext> {
public class PipelineBuilder<C extends PipelineContext> {
private String name = "DefaultPipeline";
private final List<PipelineStage<C>> stages = new ArrayList<>();
@@ -20,17 +19,11 @@ public class PipelineBuilder<C extends PhotoProcessContext> {
this.name = name;
}
/**
* 设置管线名称
*/
public PipelineBuilder<C> name(String name) {
this.name = name;
return this;
}
/**
* 添加Stage
*/
public PipelineBuilder<C> addStage(PipelineStage<C> stage) {
if (stage != null) {
this.stages.add(stage);
@@ -38,9 +31,6 @@ public class PipelineBuilder<C extends PhotoProcessContext> {
return this;
}
/**
* 批量添加Stage
*/
public PipelineBuilder<C> addStages(List<PipelineStage<C>> stages) {
if (stages != null) {
this.stages.addAll(stages);
@@ -48,9 +38,6 @@ public class PipelineBuilder<C extends PhotoProcessContext> {
return this;
}
/**
* 条件性添加Stage
*/
public PipelineBuilder<C> addStageIf(boolean condition, PipelineStage<C> stage) {
if (condition && stage != null) {
this.stages.add(stage);
@@ -58,20 +45,14 @@ public class PipelineBuilder<C extends PhotoProcessContext> {
return this;
}
/**
* 按优先级排序Stage
*/
public PipelineBuilder<C> sortByPriority() {
this.stages.sort(Comparator.comparingInt(PipelineStage::getPriority));
return this;
}
/**
* 构建Pipeline
*/
public Pipeline<C> build() {
if (stages.isEmpty()) {
throw new IllegalStateException("管线至少需要一个Stage");
throw new IllegalStateException("Pipeline 至少需要一个 Stage");
}
return new Pipeline<>(name, stages);
}

View File

@@ -0,0 +1,50 @@
package com.ycwl.basic.pipeline.core;
/**
* 通用的 Pipeline 上下文接口。
* 提供 Stage 开关控制与生命周期钩子,供不同业务场景复用。
*/
public interface PipelineContext {
/**
* 在 Pipeline 开始执行前调用,可用于埋点或预检查。
*/
default void beforePipeline() {
// 默认无操作
}
/**
* 在 Pipeline 全部 Stage 执行完成且未抛出异常后调用。
*/
default void afterPipeline() {
// 默认无操作
}
/**
* Pipeline 结束时的清理钩子,无论是否异常都会调用。
*/
default void cleanup() {
// 默认无操作
}
/**
* 判断指定 Stage 是否启用。
*
* @param stageId Stage 唯一标识
* @param defaultEnabled 配置缺失时的默认值
* @return 是否启用
*/
default boolean isStageEnabled(String stageId, boolean defaultEnabled) {
return defaultEnabled;
}
/**
* 判断指定 Stage 是否启用,默认关闭。
*
* @param stageId Stage 唯一标识
* @return 是否启用
*/
default boolean isStageEnabled(String stageId) {
return false;
}
}

View File

@@ -0,0 +1,38 @@
package com.ycwl.basic.pipeline.core;
import com.ycwl.basic.pipeline.annotation.StageConfig;
/**
* 通用 Pipeline Stage 接口,每个 Stage 负责独立处理步骤。
*/
public interface PipelineStage<C extends PipelineContext> {
/**
* Stage 名称,用于日志与监控。
*/
String getName();
/**
* 是否需要执行本 Stage。
*/
boolean shouldExecute(C context);
/**
* 执行处理逻辑。
*/
StageResult<C> execute(C context);
/**
* Stage 执行优先级,值越小优先级越高。
*/
default int getPriority() {
return 100;
}
/**
* Stage 配置注解,便于读取元信息。
*/
default StageConfig getStageConfig() {
return this.getClass().getAnnotation(StageConfig.class);
}
}

View File

@@ -1,4 +1,4 @@
package com.ycwl.basic.image.pipeline.core;
package com.ycwl.basic.pipeline.core;
import lombok.Getter;
@@ -8,16 +8,16 @@ import java.util.Collections;
import java.util.List;
/**
* Stage执行结果
* Stage 执行结果对象
*/
@Getter
public class StageResult<C extends PhotoProcessContext> {
public class StageResult<C extends PipelineContext> {
public enum Status {
SUCCESS, // 执行成功
SKIPPED, // 跳过执行
FAILED, // 执行失败
DEGRADED // 降级执行
SUCCESS,
SKIPPED,
FAILED,
DEGRADED
}
private final Status status;
@@ -34,46 +34,40 @@ public class StageResult<C extends PhotoProcessContext> {
: Collections.emptyList();
}
public static <C extends PhotoProcessContext> StageResult<C> success() {
public static <C extends PipelineContext> StageResult<C> success() {
return new StageResult<>(Status.SUCCESS, null, null, null);
}
public static <C extends PhotoProcessContext> StageResult<C> success(String message) {
public static <C extends PipelineContext> StageResult<C> success(String message) {
return new StageResult<>(Status.SUCCESS, message, null, null);
}
/**
* 成功执行并动态添加后续Stage
*/
@SafeVarargs
public static <C extends PhotoProcessContext> StageResult<C> successWithNext(String message, PipelineStage<C>... stages) {
public static <C extends PipelineContext> StageResult<C> successWithNext(String message, PipelineStage<C>... stages) {
return new StageResult<>(Status.SUCCESS, message, null, Arrays.asList(stages));
}
/**
* 成功执行并动态添加后续Stage列表
*/
public static <C extends PhotoProcessContext> StageResult<C> successWithNext(String message, List<PipelineStage<C>> stages) {
public static <C extends PipelineContext> StageResult<C> successWithNext(String message, List<PipelineStage<C>> stages) {
return new StageResult<>(Status.SUCCESS, message, null, stages);
}
public static <C extends PhotoProcessContext> StageResult<C> skipped() {
public static <C extends PipelineContext> StageResult<C> skipped() {
return new StageResult<>(Status.SKIPPED, "条件不满足,跳过执行", null, null);
}
public static <C extends PhotoProcessContext> StageResult<C> skipped(String reason) {
public static <C extends PipelineContext> StageResult<C> skipped(String reason) {
return new StageResult<>(Status.SKIPPED, reason, null, null);
}
public static <C extends PhotoProcessContext> StageResult<C> failed(String message) {
public static <C extends PipelineContext> StageResult<C> failed(String message) {
return new StageResult<>(Status.FAILED, message, null, null);
}
public static <C extends PhotoProcessContext> StageResult<C> failed(String message, Throwable exception) {
public static <C extends PipelineContext> StageResult<C> failed(String message, Throwable exception) {
return new StageResult<>(Status.FAILED, message, exception, null);
}
public static <C extends PhotoProcessContext> StageResult<C> degraded(String message) {
public static <C extends PipelineContext> StageResult<C> degraded(String message) {
return new StageResult<>(Status.DEGRADED, message, null, null);
}

View File

@@ -0,0 +1,22 @@
package com.ycwl.basic.pipeline.enums;
/**
* Stage 可选性模式,定义 Stage 是否允许受外部配置控制。
*/
public enum StageOptionalMode {
/**
* 不支持外部配置,完全由代码控制。
*/
UNSUPPORT,
/**
* 支持外部配置,可以通过开关控制启用或禁用。
*/
SUPPORT,
/**
* 强制开启,不允许被外部禁用。
*/
FORCE_ON
}

View File

@@ -1,7 +1,7 @@
package com.ycwl.basic.face.pipeline.exception;
package com.ycwl.basic.pipeline.exception;
/**
* Pipeline执行异常
* 通用 Pipeline 执行异常
*/
public class PipelineException extends RuntimeException {

View File

@@ -6,8 +6,6 @@ import com.ycwl.basic.enums.OrderStateEnum;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig;
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;
@@ -70,6 +68,8 @@ import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.service.printer.PrinterTaskPushService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.pipeline.core.Pipeline;
import com.ycwl.basic.pipeline.core.PipelineBuilder;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.ImageUtils;
import com.ycwl.basic.utils.JacksonUtil;