feat(printer): 实现带图像增强选项的重新打印功能

- 在 ReprintRequest 中新增 needEnhance 字段以支持图像增强
- 将 reprint 接口的实现从 controller 下移到 printerService
- 实现 handleReprint 方法,支持根据 needEnhance 条件性添加图像增强阶段
- 重构 reprint 流程,引入 Pipeline 处理图像下载、旋转、增强、水印等步骤
- 增强 reprint 异常处理,失败时回退到原始裁剪图
- 移除 ImageEnhanceStage 中对 TODO 占位符的判断逻辑
- 提供 updateTaskStatusAndPrinter 兜底方法用于无 MemberPrint 场景
This commit is contained in:
2025-11-27 16:04:40 +08:00
parent cbc0584706
commit a7ef2cb35a
5 changed files with 168 additions and 6 deletions

View File

@@ -71,7 +71,7 @@ public class PrinterController {
// 重新打印(将状态设置为0-未开始,并更新打印机名称)
@PostMapping("/task/reprint/{id}")
public ApiResponse<Integer> reprint(@PathVariable("id") Integer id, @RequestBody ReprintRequest request) {
int result = printTaskMapper.updateStatusAndPrinter(id, 0, request.getPrinterName());
int result = printerService.handleReprint(id, request);
return ApiResponse.success(result);
}

View File

@@ -71,7 +71,7 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
File currentFile = context.getCurrentFile();
if (currentFile == null || !currentFile.exists()) {
return StageResult.failed("当前文件不存在");
return StageResult.skipped("当前文件不存在");
}
try {
@@ -137,10 +137,9 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
String apiKey = enhancerConfig.getApiKey();
String secretKey = enhancerConfig.getSecretKey();
// 检查是否还是TODO占位符
if (appId == null || appId.startsWith("TODO_") ||
apiKey == null || apiKey.startsWith("TODO_") ||
secretKey == null || secretKey.startsWith("TODO_")) {
if (appId == null ||
apiKey == null ||
secretKey == null) {
return false;
}

View File

@@ -11,4 +11,8 @@ public class ReprintRequest {
* 打印机名称
*/
private String printerName;
/**
* 是否增强打印
*/
private Boolean needEnhance;
}

View File

@@ -4,6 +4,7 @@ import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.order.PriceObj;
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
import com.ycwl.basic.model.pc.printer.req.ReprintRequest;
import com.ycwl.basic.model.pc.printer.resp.MemberPrintResp;
import com.ycwl.basic.model.pc.printer.resp.PrinterResp;
import com.ycwl.basic.model.printer.req.FromSourceReq;
@@ -101,4 +102,6 @@ public interface PrinterService {
* @return 添加成功的MemberPrint记录ID
*/
Integer addUserPhotoFromPuzzle(Long memberId, Long scenicId, Long faceId, String resultImageUrl, Long puzzleRecordId);
int handleReprint(Integer id, ReprintRequest request);
}

View File

@@ -4,6 +4,7 @@ import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.constant.NumberConstant;
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;
@@ -13,6 +14,7 @@ import com.ycwl.basic.image.pipeline.enums.PipelineScene;
import com.ycwl.basic.image.pipeline.stages.CleanupStage;
import com.ycwl.basic.image.pipeline.stages.ConditionalRotateStage;
import com.ycwl.basic.image.pipeline.stages.DownloadStage;
import com.ycwl.basic.image.pipeline.stages.ImageEnhanceStage;
import com.ycwl.basic.image.pipeline.stages.ImageOrientationStage;
import com.ycwl.basic.image.pipeline.stages.PuzzleBorderStage;
import com.ycwl.basic.image.pipeline.stages.RestoreOrientationStage;
@@ -39,6 +41,7 @@ import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
import com.ycwl.basic.model.pc.order.entity.OrderItemEntity;
import com.ycwl.basic.model.pc.printer.req.ReprintRequest;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
@@ -83,6 +86,7 @@ import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -1317,4 +1321,156 @@ public class PrinterServiceImpl implements PrinterService {
return null;
}
}
@Override
public int handleReprint(Integer id, ReprintRequest request) {
// 1. 参数校验和任务获取
if (id == null) {
log.error("handleReprint: 任务ID为空");
return 0;
}
PrintTaskEntity task = printTaskMapper.selectById(id);
if (task == null) {
log.error("handleReprint: 打印任务不存在, taskId={}", id);
return 0;
}
// 2. 获取 MemberPrint 记录
Integer mpId = task.getMpId();
if (mpId == null) {
log.warn("handleReprint: 任务的mpId为空, taskId={}, 直接更新状态和打印机名称", id);
return updateTaskStatusAndPrinter(task, request);
}
List<Long> ids = Arrays.asList(mpId.longValue());
List<MemberPrintResp> memberPrints = printerMapper.getUserPhotoByIds(ids);
if (memberPrints == null || memberPrints.isEmpty()) {
log.warn("handleReprint: MemberPrint记录不存在, mpId={}, taskId={}, 直接更新状态和打印机名称", mpId, id);
return updateTaskStatusAndPrinter(task, request);
}
MemberPrintResp memberPrint = memberPrints.get(0);
// 3. 重新处理照片(带增强选项)
Boolean needEnhance = request.getNeedEnhance();
if (needEnhance == null) {
needEnhance = false; // 默认不增强
}
// 3.1 创建图片处理上下文
PrinterOrderItem orderItem = PrinterOrderItem.fromMemberPrintResp(memberPrint);
PhotoProcessContext context = PhotoProcessContext.fromPrinterOrderItem(orderItem, memberPrint.getScenicId());
// 3.2 设置景区配置和场景
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(memberPrint.getScenicId());
context.setScenicConfigManager(scenicConfig);
context.setScene(PipelineScene.IMAGE_PRINT);
// 3.3 判断图片来源
if (memberPrint.getSourceId() != null && memberPrint.getSourceId() > 0) {
context.setSource(ImageSource.IPC);
} else if (memberPrint.getSourceId() == null) {
context.setSource(ImageSource.PHONE);
} else {
context.setSource(ImageSource.UNKNOWN);
}
// 3.4 构建管线(关键:条件性添加 ImageEnhanceStage)
Pipeline<PhotoProcessContext> pipeline;
String newPrintUrl = null;
try {
if (context.getImageType() == ImageType.NORMAL_PHOTO) {
// 准备水印配置(重打印不需要二维码,传 null)
WatermarkConfig watermarkConfig = prepareWatermarkConfig(context, null);
prepareStorageAdapter(context);
BceEnhancerConfig config = new BceEnhancerConfig();
config.setQps(1);
config.setAppId("119554288");
config.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
config.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");
// 创建管线,条件性添加增强 Stage
pipeline = new PipelineBuilder<PhotoProcessContext>("ReprintPipeline")
.addStage(new DownloadStage())
.addStage(new ImageOrientationStage())
.addStage(new ConditionalRotateStage())
.addStageIf(needEnhance, new ImageEnhanceStage(config))
.addStage(new WatermarkStage(watermarkConfig))
.addStage(new RestoreOrientationStage())
.addStage(new UploadStage())
.addStage(new CleanupStage())
.build();
} else {
// 拼图
prepareStorageAdapter(context);
pipeline = createPuzzlePipeline();
}
// 3.5 执行管线
boolean success = pipeline.execute(context);
if (success && context.getResultUrl() != null) {
newPrintUrl = context.getResultUrl();
log.info("handleReprint: 照片重新处理成功, taskId={}, mpId={}, enhance={}, newUrl={}",
id, mpId, needEnhance, newPrintUrl);
} else {
log.warn("handleReprint: 照片重新处理失败, taskId={}, 使用原图", id);
newPrintUrl = memberPrint.getCropUrl(); // 使用原裁剪图
}
} catch (Exception e) {
log.error("handleReprint: 照片重新处理异常, taskId={}, 使用原图", id, e);
newPrintUrl = memberPrint.getCropUrl();
} finally {
context.cleanup();
}
// 4. 更新打印任务
String printerName = request.getPrinterName();
if (StringUtils.isBlank(printerName)) {
// 如果请求中未指定,使用轮询逻辑获取
PrinterEntity printer = printerMapper.getById(task.getPrinterId());
if (printer != null) {
printerName = getNextPrinter(printer);
} else {
printerName = task.getPrinterName(); // 保持原有打印机
}
}
task.setStatus(TASK_STATUS_PENDING); // 状态设置为待处理(0)
task.setPrinterName(printerName);
task.setUrl(newPrintUrl);
task.setUpdateTime(new Date());
int rows = printTaskMapper.updateById(task);
log.info("handleReprint: 任务更新完成, taskId={}, printerName={}, status=0, rows={}",
id, printerName, rows);
return rows;
}
/**
* 当无法获取 MemberPrint 时,直接更新任务状态和打印机名称
*/
private int updateTaskStatusAndPrinter(PrintTaskEntity task, ReprintRequest request) {
String printerName = request.getPrinterName();
if (StringUtils.isBlank(printerName)) {
// 如果请求中未指定,使用轮询逻辑获取
PrinterEntity printer = printerMapper.getById(task.getPrinterId());
if (printer != null) {
printerName = getNextPrinter(printer);
} else {
printerName = task.getPrinterName(); // 保持原有打印机
}
}
// 使用现有的 updateStatusAndPrinter 方法
int rows = printTaskMapper.updateStatusAndPrinter(task.getId(), TASK_STATUS_PENDING, printerName);
log.info("handleReprint: 无MemberPrint记录,直接更新任务, taskId={}, printerName={}, status=0, rows={}",
task.getId(), printerName, rows);
return rows;
}
}