diff --git a/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleGenerationRecordEntity.java b/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleGenerationRecordEntity.java
index 56f6e07c..da3dc989 100644
--- a/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleGenerationRecordEntity.java
+++ b/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleGenerationRecordEntity.java
@@ -76,6 +76,12 @@ public class PuzzleGenerationRecordEntity {
@TableField("result_image_url")
private String resultImageUrl;
+ /**
+ * 原始图片URL(未裁切的图片,用于打印)
+ */
+ @TableField("original_image_url")
+ private String originalImageUrl;
+
/**
* 文件大小(字节)
*/
diff --git a/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleTemplateEntity.java b/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleTemplateEntity.java
index 08906324..7dcc1d49 100644
--- a/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleTemplateEntity.java
+++ b/src/main/java/com/ycwl/basic/puzzle/entity/PuzzleTemplateEntity.java
@@ -97,6 +97,24 @@ public class PuzzleTemplateEntity {
@TableField("scenic_id")
private Long scenicId;
+ /**
+ * 自动添加到打印队列:1-开启 0-关闭
+ */
+ @TableField("auto_add_print")
+ private Integer autoAddPrint;
+
+ /**
+ * 是否可以打印:1-可以 0-不可以
+ */
+ @TableField("can_print")
+ private Integer canPrint;
+
+ /**
+ * 用户查看区域(裁切区域),格式:x,y,w,h
+ */
+ @TableField("user_area")
+ private String userArea;
+
/**
* 创建时间
*/
diff --git a/src/main/java/com/ycwl/basic/puzzle/mapper/PuzzleGenerationRecordMapper.java b/src/main/java/com/ycwl/basic/puzzle/mapper/PuzzleGenerationRecordMapper.java
index 3a18b1e5..f5ec8bb7 100644
--- a/src/main/java/com/ycwl/basic/puzzle/mapper/PuzzleGenerationRecordMapper.java
+++ b/src/main/java/com/ycwl/basic/puzzle/mapper/PuzzleGenerationRecordMapper.java
@@ -52,6 +52,7 @@ public interface PuzzleGenerationRecordMapper {
*/
int updateSuccess(@Param("id") Long id,
@Param("resultImageUrl") String resultImageUrl,
+ @Param("originalImageUrl") String originalImageUrl,
@Param("resultFileSize") Long resultFileSize,
@Param("resultWidth") Integer resultWidth,
@Param("resultHeight") Integer resultHeight,
diff --git a/src/main/java/com/ycwl/basic/puzzle/service/impl/PuzzleGenerateServiceImpl.java b/src/main/java/com/ycwl/basic/puzzle/service/impl/PuzzleGenerateServiceImpl.java
index 4f06bb94..c840a2b9 100644
--- a/src/main/java/com/ycwl/basic/puzzle/service/impl/PuzzleGenerateServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/puzzle/service/impl/PuzzleGenerateServiceImpl.java
@@ -17,6 +17,7 @@ import com.ycwl.basic.puzzle.service.IPuzzleGenerateService;
import com.ycwl.basic.puzzle.util.PuzzleDuplicationDetector;
import com.ycwl.basic.puzzle.util.PuzzleImageRenderer;
import com.ycwl.basic.repository.ScenicRepository;
+import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.utils.WxMpUtil;
import lombok.RequiredArgsConstructor;
@@ -60,6 +61,8 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
private final ScenicRepository scenicRepository;
@Lazy
private final PuzzleDuplicationDetector duplicationDetector;
+ @Lazy
+ private final PrinterService printerService;
@Override
public PuzzleGenerateResponse generate(PuzzleGenerateRequest request) {
@@ -136,30 +139,64 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
// 9. 渲染图片
BufferedImage resultImage = imageRenderer.render(template, elements, finalDynamicData);
- // 10. 上传到OSS
- String imageUrl = uploadImage(resultImage, template.getCode(), request.getOutputFormat(), request.getQuality());
- log.info("图片上传成功: url={}", imageUrl);
+ // 10. 上传原图到OSS(未裁切)
+ String originalImageUrl = uploadImage(resultImage, template.getCode(), request.getOutputFormat(), request.getQuality());
+ log.info("原图上传成功: url={}", originalImageUrl);
- // 11. 更新记录为成功
+ // 11. 处理用户区域裁切
+ String finalImageUrl = originalImageUrl; // 默认使用原图
+ BufferedImage finalImage = resultImage;
+
+ if (StrUtil.isNotBlank(template.getUserArea())) {
+ try {
+ BufferedImage croppedImage = cropImage(resultImage, template.getUserArea());
+ finalImageUrl = uploadImage(croppedImage, template.getCode() + "_cropped", request.getOutputFormat(), request.getQuality());
+ finalImage = croppedImage;
+ log.info("裁切后图片上传成功: userArea={}, url={}", template.getUserArea(), finalImageUrl);
+ } catch (Exception e) {
+ log.error("图片裁切失败,使用原图: userArea={}", template.getUserArea(), e);
+ // 裁切失败时使用原图
+ }
+ }
+
+ // 12. 更新记录为成功
long duration = (int) (System.currentTimeMillis() - startTime);
- long fileSize = estimateFileSize(resultImage, request.getOutputFormat());
+ long fileSize = estimateFileSize(finalImage, request.getOutputFormat());
recordMapper.updateSuccess(
record.getId(),
- imageUrl,
+ finalImageUrl,
+ originalImageUrl,
fileSize,
- resultImage.getWidth(),
- resultImage.getHeight(),
+ finalImage.getWidth(),
+ finalImage.getHeight(),
(int) duration
);
- log.info("拼图生成成功(新生成): recordId={}, imageUrl={}, duration={}ms",
- record.getId(), imageUrl, duration);
+ log.info("拼图生成成功(新生成): recordId={}, originalUrl={}, finalUrl={}, duration={}ms",
+ record.getId(), originalImageUrl, finalImageUrl, duration);
+
+ // 13. 检查是否自动添加到打印队列
+ if (template.getAutoAddPrint() != null && template.getAutoAddPrint() == 1) {
+ try {
+ Integer printRecordId = printerService.addUserPhotoFromPuzzle(
+ request.getUserId(),
+ resolvedScenicId,
+ request.getFaceId(),
+ originalImageUrl, // 使用原图URL添加到打印队列
+ record.getId()
+ );
+ log.info("自动添加到打印队列成功: recordId={}, printRecordId={}", record.getId(), printRecordId);
+ } catch (Exception e) {
+ log.error("自动添加到打印队列失败: recordId={}", record.getId(), e);
+ // 添加失败不影响拼图生成流程
+ }
+ }
return PuzzleGenerateResponse.success(
- imageUrl,
+ finalImageUrl,
fileSize,
- resultImage.getWidth(),
- resultImage.getHeight(),
+ finalImage.getWidth(),
+ finalImage.getHeight(),
(int) duration,
record.getId(),
false, // isDuplicate=false
@@ -405,4 +442,43 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
return templateScenicId;
}
+
+ /**
+ * 裁切图片
+ * @param image 原图
+ * @param userArea 裁切区域,格式:x,y,w,h
+ * @return 裁切后的图片
+ */
+ private BufferedImage cropImage(BufferedImage image, String userArea) {
+ if (StrUtil.isBlank(userArea)) {
+ return image;
+ }
+
+ try {
+ String[] parts = userArea.split(",");
+ if (parts.length != 4) {
+ throw new IllegalArgumentException("userArea格式错误,应为:x,y,w,h");
+ }
+
+ int x = Integer.parseInt(parts[0].trim());
+ int y = Integer.parseInt(parts[1].trim());
+ int w = Integer.parseInt(parts[2].trim());
+ int h = Integer.parseInt(parts[3].trim());
+
+ // 边界检查
+ if (x < 0 || y < 0 || w <= 0 || h <= 0) {
+ throw new IllegalArgumentException("裁切区域参数必须为正数");
+ }
+
+ if (x + w > image.getWidth() || y + h > image.getHeight()) {
+ throw new IllegalArgumentException("裁切区域超出图片边界");
+ }
+
+ // 执行裁切
+ return image.getSubimage(x, y, w, h);
+
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("userArea格式错误,参数必须为数字", e);
+ }
+ }
}
diff --git a/src/main/resources/mapper/PuzzleGenerationRecordMapper.xml b/src/main/resources/mapper/PuzzleGenerationRecordMapper.xml
index 0b2f7c79..00949f97 100644
--- a/src/main/resources/mapper/PuzzleGenerationRecordMapper.xml
+++ b/src/main/resources/mapper/PuzzleGenerationRecordMapper.xml
@@ -14,6 +14,7 @@
+
@@ -32,7 +33,7 @@
id, template_id, template_code, user_id, face_id, business_type,
generation_params, content_hash,
- result_image_url, result_file_size, result_width, result_height,
+ result_image_url, original_image_url, result_file_size, result_width, result_height,
status, error_message, generation_duration, retry_count,
scenic_id, client_ip, user_agent, create_time, update_time
@@ -77,13 +78,13 @@
INSERT INTO puzzle_generation_record (
template_id, template_code, user_id, face_id, business_type,
generation_params, content_hash,
- result_image_url, result_file_size, result_width, result_height,
+ result_image_url, original_image_url, result_file_size, result_width, result_height,
status, error_message, generation_duration, retry_count,
scenic_id, client_ip, user_agent, create_time, update_time
) VALUES (
#{templateId}, #{templateCode}, #{userId}, #{faceId}, #{businessType},
#{generationParams}, #{contentHash},
- #{resultImageUrl}, #{resultFileSize}, #{resultWidth}, #{resultHeight},
+ #{resultImageUrl}, #{originalImageUrl}, #{resultFileSize}, #{resultWidth}, #{resultHeight},
#{status}, #{errorMessage}, #{generationDuration}, #{retryCount},
#{scenicId}, #{clientIp}, #{userAgent}, NOW(), NOW()
)
@@ -94,6 +95,7 @@
UPDATE puzzle_generation_record
result_image_url = #{resultImageUrl},
+ original_image_url = #{originalImageUrl},
result_file_size = #{resultFileSize},
result_width = #{resultWidth},
result_height = #{resultHeight},
@@ -111,6 +113,7 @@
UPDATE puzzle_generation_record
SET status = 1,
result_image_url = #{resultImageUrl},
+ original_image_url = #{originalImageUrl},
result_file_size = #{resultFileSize},
result_width = #{resultWidth},
result_height = #{resultHeight},
diff --git a/src/main/resources/mapper/PuzzleTemplateMapper.xml b/src/main/resources/mapper/PuzzleTemplateMapper.xml
index 03a0bcf2..acff2264 100644
--- a/src/main/resources/mapper/PuzzleTemplateMapper.xml
+++ b/src/main/resources/mapper/PuzzleTemplateMapper.xml
@@ -18,6 +18,9 @@
+
+
+
@@ -27,7 +30,8 @@
id, name, code, canvas_width, canvas_height, background_type, background_color,
- background_image, cover_image, description, category, status, scenic_id, create_time, update_time, deleted, deleted_at
+ background_image, cover_image, description, category, status, scenic_id,
+ auto_add_print, can_print, user_area, create_time, update_time, deleted, deleted_at
@@ -68,10 +72,12 @@
useGeneratedKeys="true" keyProperty="id">
INSERT INTO puzzle_template (
name, code, canvas_width, canvas_height, background_type, background_color,
- background_image, cover_image, description, category, status, scenic_id, create_time, update_time, deleted
+ background_image, cover_image, description, category, status, scenic_id,
+ auto_add_print, can_print, user_area, create_time, update_time, deleted
) VALUES (
#{name}, #{code}, #{canvasWidth}, #{canvasHeight}, #{backgroundType}, #{backgroundColor},
- #{backgroundImage}, #{coverImage}, #{description}, #{category}, #{status}, #{scenicId}, NOW(), NOW(), 0
+ #{backgroundImage}, #{coverImage}, #{description}, #{category}, #{status}, #{scenicId},
+ #{autoAddPrint}, #{canPrint}, #{userArea}, NOW(), NOW(), 0
)
@@ -91,6 +97,9 @@
category = #{category},
status = #{status},
scenic_id = #{scenicId},
+ auto_add_print = #{autoAddPrint},
+ can_print = #{canPrint},
+ user_area = #{userArea},
update_time = NOW()
WHERE id = #{id} AND deleted = 0