feat(watermark): 添加边缘端水印处理功能

- 引入 WatermarkEdgeService 支持边缘端渲染
- 在 WatermarkConfig 中添加边缘端相关配置参数
- 在 WatermarkStage 中实现边缘端处理逻辑和降级机制
- 修改 ImageWatermarkOperatorEnum 的默认输出格式为 jpg
- 移除已废弃的 DefaultImageWatermarkOperator 类
- 更新 GoodsServiceImpl 使用边缘端处理水印
- 优化 PuzzleEdgeWorkerIpInterceptor 允许本地回环地址访问
- 修正 PrinterDefaultWatermarkTemplateBuilder 样式常量名称
This commit is contained in:
2026-01-16 17:25:19 +08:00
parent 83e47ed843
commit a5a9ff09f2
9 changed files with 460 additions and 105 deletions

View File

@@ -1,6 +1,8 @@
package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.watermark.edge.WatermarkEdgeService;
import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import lombok.Builder;
import lombok.Getter;
@@ -41,4 +43,28 @@ public class WatermarkConfig {
*/
@Builder.Default
private final Double scale = 1.0;
/**
* 边缘端水印服务(可选)
* 如果设置,将优先尝试使用边缘端处理
*/
private final WatermarkEdgeService edgeService;
/**
* 存储适配器(边缘端处理时需要)
* 用于上传原图和二维码到临时位置
*/
private final IStorageAdapter storageAdapter;
/**
* 是否启用边缘端处理
*/
@Builder.Default
private final boolean edgeEnabled = false;
/**
* 边缘端处理超时时间(毫秒)
*/
@Builder.Default
private final long edgeTimeoutMs = 30000L;
}

View File

@@ -3,6 +3,7 @@ package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.enums.ImageType;
import com.ycwl.basic.image.watermark.ImageWatermarkFactory;
import com.ycwl.basic.image.watermark.edge.WatermarkEdgeService;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum;
import com.ycwl.basic.image.watermark.operator.IOperator;
@@ -10,6 +11,7 @@ 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.adapters.IStorageAdapter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -21,6 +23,7 @@ import java.util.List;
/**
* 水印处理Stage
* 支持三级降级: 配置的水印类型 -> PRINTER_DEFAULT -> 无水印
* 支持边缘端渲染(可选)
*/
@Slf4j
@StageConfig(
@@ -127,6 +130,19 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
File watermarkedFile = context.getTempFileManager()
.createTempFile("watermark_" + type.getType(), "." + fileExt);
// 尝试边缘端处理
if (shouldUseEdgeProcessing(type)) {
File edgeResult = tryEdgeProcessing(context, type, currentFile, watermarkedFile);
if (edgeResult != null && edgeResult.exists()) {
context.updateProcessedFile(edgeResult);
log.info("边缘端水印应用成功: type={}, size={}KB", type.getType(), edgeResult.length() / 1024);
return StageResult.success(String.format("水印(边缘端): %s (%dKB)",
type.getType(), edgeResult.length() / 1024));
}
log.warn("边缘端水印处理失败,降级到本地处理: type={}", type.getType());
}
// 本地处理(降级或直接使用)
WatermarkInfo watermarkInfo = buildWatermarkInfo(context, currentFile, watermarkedFile, type);
IOperator operator = ImageWatermarkFactory.get(type);
@@ -143,6 +159,46 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
type.getType(), result.length() / 1024));
}
/**
* 判断是否应使用边缘端处理
*/
private boolean shouldUseEdgeProcessing(ImageWatermarkOperatorEnum type) {
if (!config.isEdgeEnabled()) {
return false;
}
WatermarkEdgeService edgeService = config.getEdgeService();
if (edgeService == null) {
return false;
}
IStorageAdapter storageAdapter = config.getStorageAdapter();
return storageAdapter != null;
}
/**
* 尝试使用边缘端处理
*
* @return 处理后的文件,失败返回 null
*/
private File tryEdgeProcessing(PhotoProcessContext context,
ImageWatermarkOperatorEnum type,
File currentFile,
File watermarkedFile) {
try {
WatermarkEdgeService edgeService = config.getEdgeService();
IStorageAdapter storageAdapter = config.getStorageAdapter();
// 构建水印信息用于边缘端处理
WatermarkInfo info = buildWatermarkInfo(context, currentFile, watermarkedFile, type);
// 调用边缘端服务处理
return edgeService.processWatermarkFromFile(info, type, storageAdapter);
} catch (Exception e) {
log.error("边缘端水印处理异常: type={}, error={}", type.getType(), e.getMessage(), e);
return null;
}
}
/**
* 构建水印参数
*/