You've already forked FrameTour-BE
feat(puzzle): 实现免费拼图下载时自动添加水印功能
- 在AppPuzzleController中新增水印相关依赖注入 - 添加免费拼图判断逻辑,区分付费与免费拼图下载流程 - 实现addWatermarkForFreePuzzle方法处理水印添加 - 集成景区信息、人脸信息、二维码等水印模板数据 - 添加水印任务创建与等待机制,支持30秒超时处理 - 增加水印操作的日志记录与异常处理机制 - 优化免费拼图下载的安全性保护措施
This commit is contained in:
@@ -1,20 +1,32 @@
|
|||||||
package com.ycwl.basic.controller.mobile;
|
package com.ycwl.basic.controller.mobile;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
import com.ycwl.basic.biz.OrderBiz;
|
import com.ycwl.basic.biz.OrderBiz;
|
||||||
|
import com.ycwl.basic.constant.FreeStatus;
|
||||||
|
import com.ycwl.basic.image.watermark.edge.PuzzleDefaultWatermarkTemplateBuilder;
|
||||||
|
import com.ycwl.basic.image.watermark.edge.WatermarkEdgeTaskCreator;
|
||||||
|
import com.ycwl.basic.image.watermark.edge.WatermarkRequest;
|
||||||
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
|
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
|
||||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||||
|
import com.ycwl.basic.model.pc.puzzle.entity.MemberPuzzleEntity;
|
||||||
|
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||||
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
|
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
|
||||||
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||||
import com.ycwl.basic.pricing.enums.ProductType;
|
import com.ycwl.basic.pricing.enums.ProductType;
|
||||||
import com.ycwl.basic.pricing.service.IPriceCalculationService;
|
import com.ycwl.basic.pricing.service.IPriceCalculationService;
|
||||||
|
import com.ycwl.basic.puzzle.edge.task.PuzzleEdgeRenderTaskService;
|
||||||
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
|
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
|
||||||
|
import com.ycwl.basic.puzzle.mapper.MemberPuzzleMapper;
|
||||||
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
|
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
|
||||||
import com.ycwl.basic.repository.FaceRepository;
|
import com.ycwl.basic.repository.FaceRepository;
|
||||||
|
import com.ycwl.basic.repository.ScenicRepository;
|
||||||
|
import com.ycwl.basic.service.pc.FaceService;
|
||||||
import com.ycwl.basic.service.printer.PrinterService;
|
import com.ycwl.basic.service.printer.PrinterService;
|
||||||
import com.ycwl.basic.utils.ApiResponse;
|
import com.ycwl.basic.utils.ApiResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -26,6 +38,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/mobile/puzzle/v1")
|
@RequestMapping("/api/mobile/puzzle/v1")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -36,6 +49,10 @@ public class AppPuzzleController {
|
|||||||
private final IPriceCalculationService iPriceCalculationService;
|
private final IPriceCalculationService iPriceCalculationService;
|
||||||
private final PrinterService printerService;
|
private final PrinterService printerService;
|
||||||
private final OrderBiz orderBiz;
|
private final OrderBiz orderBiz;
|
||||||
|
private final MemberPuzzleMapper memberPuzzleMapper;
|
||||||
|
private final WatermarkEdgeTaskCreator watermarkEdgeTaskCreator;
|
||||||
|
private final FaceService faceService;
|
||||||
|
private final ScenicRepository scenicRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据faceId查询三拼图数量
|
* 根据faceId查询三拼图数量
|
||||||
@@ -82,6 +99,7 @@ public class AppPuzzleController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据recordId下载拼图资源
|
* 根据recordId下载拼图资源
|
||||||
|
* 如果是免费赠送的拼图,会添加水印后返回
|
||||||
*/
|
*/
|
||||||
@GetMapping("/download/{recordId}")
|
@GetMapping("/download/{recordId}")
|
||||||
public ApiResponse<List<String>> download(@PathVariable("recordId") Long recordId) {
|
public ApiResponse<List<String>> download(@PathVariable("recordId") Long recordId) {
|
||||||
@@ -96,9 +114,88 @@ public class AppPuzzleController {
|
|||||||
if (resultImageUrl == null || resultImageUrl.isEmpty()) {
|
if (resultImageUrl == null || resultImageUrl.isEmpty()) {
|
||||||
return ApiResponse.fail("该拼图记录没有可用的图片URL");
|
return ApiResponse.fail("该拼图记录没有可用的图片URL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询该拼图的关联记录,判断是否为免费赠送
|
||||||
|
Long faceId = record.getFaceId();
|
||||||
|
if (faceId != null) {
|
||||||
|
MemberPuzzleEntity memberPuzzle = memberPuzzleMapper.getByFaceAndRecord(faceId, recordId);
|
||||||
|
if (memberPuzzle != null && FreeStatus.isFree(memberPuzzle.getIsFree())) {
|
||||||
|
// 免费赠送的拼图,需要添加水印
|
||||||
|
String watermarkedUrl = addWatermarkForFreePuzzle(record);
|
||||||
|
if (watermarkedUrl != null) {
|
||||||
|
return ApiResponse.success(Collections.singletonList(watermarkedUrl));
|
||||||
|
}
|
||||||
|
// 如果水印添加失败,记录日志并返回原图
|
||||||
|
log.warn("免费拼图水印添加失败,返回原图: recordId={}", recordId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ApiResponse.success(Collections.singletonList(resultImageUrl));
|
return ApiResponse.success(Collections.singletonList(resultImageUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为免费赠送的拼图添加水印
|
||||||
|
*
|
||||||
|
* @param record 拼图记录
|
||||||
|
* @return 带水印的图片URL,失败返回null
|
||||||
|
*/
|
||||||
|
private String addWatermarkForFreePuzzle(PuzzleGenerationRecordEntity record) {
|
||||||
|
try {
|
||||||
|
Long faceId = record.getFaceId();
|
||||||
|
FaceEntity face = faceRepository.getFace(faceId);
|
||||||
|
if (face == null) {
|
||||||
|
log.warn("添加水印失败:未找到人脸信息, faceId={}", faceId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取景区信息
|
||||||
|
ScenicEntity scenic = scenicRepository.getScenic(face.getScenicId());
|
||||||
|
String scenicLine = scenic != null ? scenic.getName() : "";
|
||||||
|
|
||||||
|
// 获取二维码URL
|
||||||
|
String qrcodeUrl = faceService.bindWxaCode(faceId);
|
||||||
|
|
||||||
|
// 格式化日期时间
|
||||||
|
String datetimeLine = record.getCreateTime() != null
|
||||||
|
? DateUtil.format(record.getCreateTime(), "yyyy-MM-dd HH:mm")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// 构建水印请求
|
||||||
|
WatermarkRequest request = WatermarkRequest.builder()
|
||||||
|
.originalImageUrl(record.getResultImageUrl())
|
||||||
|
.imageWidth(record.getResultWidth() != null ? record.getResultWidth() : 0)
|
||||||
|
.imageHeight(record.getResultHeight() != null ? record.getResultHeight() : 0)
|
||||||
|
.qrcodeUrl(qrcodeUrl)
|
||||||
|
.faceUrl(face.getFaceUrl())
|
||||||
|
.scenicLine(scenicLine)
|
||||||
|
.datetimeLine(datetimeLine)
|
||||||
|
.outputFormat("JPEG")
|
||||||
|
.outputQuality(90)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 创建水印任务并等待结果
|
||||||
|
PuzzleEdgeRenderTaskService.TaskWaitResult result = watermarkEdgeTaskCreator.createAndWait(
|
||||||
|
PuzzleDefaultWatermarkTemplateBuilder.STYLE,
|
||||||
|
request,
|
||||||
|
record.getId(),
|
||||||
|
faceId,
|
||||||
|
"free_puzzle_download",
|
||||||
|
30_000L // 30秒超时
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
log.info("免费拼图水印添加成功: recordId={}, url={}", record.getId(), result.getImageUrl());
|
||||||
|
return result.getImageUrl();
|
||||||
|
} else {
|
||||||
|
log.error("免费拼图水印添加失败: recordId={}, error={}", record.getId(), result.getErrorMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("免费拼图水印添加异常: recordId={}", record.getId(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据recordId查询拼图价格
|
* 根据recordId查询拼图价格
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user