diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppPuzzleController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppPuzzleController.java new file mode 100644 index 00000000..eb13a13a --- /dev/null +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppPuzzleController.java @@ -0,0 +1,232 @@ +package com.ycwl.basic.controller.mobile; + +import com.ycwl.basic.biz.OrderBiz; +import com.ycwl.basic.constant.SourceType; +import com.ycwl.basic.model.mobile.order.IsBuyRespVO; +import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO; +import com.ycwl.basic.model.pc.face.entity.FaceEntity; +import com.ycwl.basic.pricing.dto.PriceCalculationRequest; +import com.ycwl.basic.pricing.dto.PriceCalculationResult; +import com.ycwl.basic.pricing.dto.ProductItem; +import com.ycwl.basic.pricing.enums.ProductType; +import com.ycwl.basic.pricing.service.IPriceCalculationService; +import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity; +import com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper; +import com.ycwl.basic.repository.FaceRepository; +import com.ycwl.basic.service.printer.PrinterService; +import com.ycwl.basic.utils.ApiResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/mobile/puzzle/v1") +@RequiredArgsConstructor +public class AppPuzzleController { + + private final PuzzleGenerationRecordMapper recordMapper; + private final FaceRepository faceRepository; + private final IPriceCalculationService iPriceCalculationService; + private final PrinterService printerService; + private final OrderBiz orderBiz; + + /** + * 根据faceId查询三拼图数量 + */ + @GetMapping("/count/{faceId}") + public ApiResponse countByFaceId(@PathVariable("faceId") Long faceId) { + if (faceId == null) { + return ApiResponse.fail("faceId不能为空"); + } + int count = recordMapper.countByFaceId(faceId); + return ApiResponse.success(count); + } + + /** + * 根据faceId查询所有三拼图记录 + */ + @GetMapping("/list/{faceId}") + public ApiResponse> listByFaceId(@PathVariable("faceId") Long faceId) { + if (faceId == null) { + return ApiResponse.fail("faceId不能为空"); + } + List records = recordMapper.listByFaceId(faceId); + List result = records.stream() + .map(this::convertToContentPageVO) + .collect(Collectors.toList()); + return ApiResponse.success(result); + } + + /** + * 根据recordId查询单个三拼图记录 + */ + @GetMapping("/detail/{recordId}") + public ApiResponse getByRecordId(@PathVariable("recordId") Long recordId) { + if (recordId == null) { + return ApiResponse.fail("recordId不能为空"); + } + PuzzleGenerationRecordEntity record = recordMapper.getById(recordId); + if (record == null) { + return ApiResponse.fail("未找到对应的拼图记录"); + } + ContentPageVO result = convertToContentPageVO(record); + return ApiResponse.success(result); + } + + /** + * 根据recordId下载拼图资源 + */ + @GetMapping("/download/{recordId}") + public ApiResponse> download(@PathVariable("recordId") Long recordId) { + if (recordId == null) { + return ApiResponse.fail("recordId不能为空"); + } + PuzzleGenerationRecordEntity record = recordMapper.getById(recordId); + if (record == null) { + return ApiResponse.fail("未找到对应的拼图记录"); + } + String resultImageUrl = record.getResultImageUrl(); + if (resultImageUrl == null || resultImageUrl.isEmpty()) { + return ApiResponse.fail("该拼图记录没有可用的图片URL"); + } + return ApiResponse.success(Collections.singletonList(resultImageUrl)); + } + + /** + * 根据recordId查询拼图价格 + */ + @GetMapping("/price/{recordId}") + public ApiResponse getPriceByRecordId(@PathVariable("recordId") Long recordId) { + if (recordId == null) { + return ApiResponse.fail("recordId不能为空"); + } + PuzzleGenerationRecordEntity record = recordMapper.getById(recordId); + if (record == null) { + return ApiResponse.fail("未找到对应的拼图记录"); + } + FaceEntity face = faceRepository.getFace(record.getFaceId()); + if (face == null) { + return ApiResponse.fail("未找到对应的人脸信息"); + } + + PriceCalculationRequest calculationRequest = new PriceCalculationRequest(); + ProductItem productItem = new ProductItem(); + productItem.setProductType(ProductType.PHOTO_LOG); + productItem.setProductId(record.getTemplateId().toString()); + productItem.setPurchaseCount(1); + productItem.setScenicId(face.getScenicId().toString()); + calculationRequest.setProducts(Collections.singletonList(productItem)); + calculationRequest.setUserId(face.getMemberId()); + calculationRequest.setFaceId(record.getFaceId()); + calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠 + PriceCalculationResult calculationResult = iPriceCalculationService.calculatePrice(calculationRequest); + + return ApiResponse.success(calculationResult); + } + + /** + * 将拼图导入到打印列表 + */ + @PostMapping("/import-to-print/{recordId}") + public ApiResponse importToPrint(@PathVariable("recordId") Long recordId) { + if (recordId == null) { + return ApiResponse.fail("recordId不能为空"); + } + + // 查询拼图记录 + PuzzleGenerationRecordEntity record = recordMapper.getById(recordId); + if (record == null) { + return ApiResponse.fail("未找到对应的拼图记录"); + } + + // 检查是否有图片URL + String resultImageUrl = record.getResultImageUrl(); + if (resultImageUrl == null || resultImageUrl.isEmpty()) { + return ApiResponse.fail("该拼图记录没有可用的图片URL"); + } + + // 获取人脸信息 + FaceEntity face = faceRepository.getFace(record.getFaceId()); + if (face == null) { + return ApiResponse.fail("未找到对应的人脸信息"); + } + + // 调用服务添加到打印列表 + Integer memberPrintId = printerService.addUserPhotoFromPuzzle( + face.getMemberId(), + face.getScenicId(), + record.getFaceId(), + resultImageUrl, + recordId + ); + + if (memberPrintId == null) { + return ApiResponse.fail("添加到打印列表失败"); + } + + return ApiResponse.success(memberPrintId); + } + + /** + * 将PuzzleGenerationRecordEntity转换为ContentPageVO + */ + private ContentPageVO convertToContentPageVO(PuzzleGenerationRecordEntity record) { + ContentPageVO vo = new ContentPageVO(); + + // 内容类型为3(拼图) + vo.setContentType(3); + + // 源素材类型为3(拼图) + vo.setSourceType(3); + vo.setGroup("拼图"); + + // 只要存在记录,lockType不为0(设置为-1表示已生成) + vo.setLockType(-1); + + // 通过faceId填充scenicId的信息 + FaceEntity face = faceRepository.getFace(record.getFaceId()); + if (record.getFaceId() != null) { + vo.setScenicId(face.getScenicId()); + } + + // contentId为生成记录id + vo.setContentId(record.getId()); + + // templateCoverUrl和生成的图是一致的 + vo.setTemplateCoverUrl(record.getResultImageUrl()); + + // 设置模板ID + vo.setTemplateId(record.getTemplateId()); + IsBuyRespVO isBuyRespVO = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), 3, face.getId()); + if (isBuyRespVO.isBuy()) { + vo.setIsBuy(1); + } else { + vo.setIsBuy(0); + PriceCalculationRequest calculationRequest = new PriceCalculationRequest(); + ProductItem productItem = new ProductItem(); + productItem.setProductType(ProductType.PHOTO_LOG); + productItem.setProductId(record.getTemplateId().toString()); + productItem.setPurchaseCount(1); + productItem.setScenicId(face.getScenicId().toString()); + calculationRequest.setProducts(Collections.singletonList(productItem)); + calculationRequest.setUserId(face.getMemberId()); + calculationRequest.setFaceId(record.getFaceId()); + calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠 + PriceCalculationResult calculationResult = iPriceCalculationService.calculatePrice(calculationRequest); + if (calculationResult.getFinalAmount().compareTo(BigDecimal.ZERO) > 0) { + vo.setFreeCount(0); + } else { + vo.setFreeCount(1); + } + } + return vo; + } +} diff --git a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java index 8ddabda0..bd138d89 100644 --- a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java @@ -45,6 +45,13 @@ import com.ycwl.basic.model.pc.video.entity.VideoEntity; import com.ycwl.basic.model.repository.TaskUpdateResult; import com.ycwl.basic.model.task.resp.SearchFaceRespVo; +import com.ycwl.basic.pricing.dto.PriceCalculationRequest; +import com.ycwl.basic.pricing.dto.PriceCalculationResult; +import com.ycwl.basic.pricing.dto.ProductItem; +import com.ycwl.basic.pricing.enums.ProductType; +import com.ycwl.basic.pricing.service.IPriceCalculationService; +import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity; +import com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper; import com.ycwl.basic.repository.DeviceRepository; import com.ycwl.basic.repository.FaceRepository; import com.ycwl.basic.repository.MemberRelationRepository; @@ -173,6 +180,11 @@ public class FaceServiceImpl implements FaceService { private BuyStatusProcessor buyStatusProcessor; @Autowired private VideoRecreationHandler videoRecreationHandler; + @Autowired + private PuzzleGenerationRecordMapper puzzleGenerationRecordMapper; + @Autowired + private IPriceCalculationService iPriceCalculationService; + @Override public ApiResponse> pageQuery(FaceReqQuery faceReqQuery) { PageHelper.startPage(faceReqQuery.getPageNum(),faceReqQuery.getPageSize()); @@ -438,6 +450,39 @@ public class FaceServiceImpl implements FaceService { } }).collect(Collectors.toList()); + int count = puzzleGenerationRecordMapper.countByFaceId(faceId); + if (count > 0) { + ContentPageVO sfpContent = new ContentPageVO(); + List records = puzzleGenerationRecordMapper.listByFaceId(faceId); + sfpContent.setName("三拼图"); + sfpContent.setGroup("plog"); + sfpContent.setScenicId(face.getScenicId()); + sfpContent.setContentType(3); + sfpContent.setSourceType(3); + sfpContent.setLockType(-1); + sfpContent.setContentId(records.getFirst().getId()); + sfpContent.setTemplateId(records.getFirst().getTemplateId()); + sfpContent.setTemplateCoverUrl(records.getFirst().getResultImageUrl()); + sfpContent.setGoodsType(3); + sfpContent.setSort(0); + PriceCalculationRequest calculationRequest = new PriceCalculationRequest(); + ProductItem productItem = new ProductItem(); + productItem.setProductType(ProductType.PHOTO_LOG); + productItem.setProductId(records.getFirst().getTemplateId().toString()); + productItem.setPurchaseCount(1); + productItem.setScenicId(face.getScenicId().toString()); + calculationRequest.setProducts(Collections.singletonList(productItem)); + calculationRequest.setUserId(face.getMemberId()); + calculationRequest.setFaceId(face.getId()); + calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠 + PriceCalculationResult calculationResult = iPriceCalculationService.calculatePrice(calculationRequest); + if (calculationResult.getFinalAmount().compareTo(BigDecimal.ZERO) > 0) { + sfpContent.setFreeCount(0); + } else { + sfpContent.setFreeCount(1); + } + contentList.add(1, sfpContent); + } SourceReqQuery sourceReqQuery = new SourceReqQuery(); sourceReqQuery.setScenicId(face.getScenicId()); sourceReqQuery.setFaceId(faceId); @@ -513,7 +558,6 @@ public class FaceServiceImpl implements FaceService { } } }); - return contentList; } diff --git a/src/main/java/com/ycwl/basic/service/pc/impl/OrderServiceImpl.java b/src/main/java/com/ycwl/basic/service/pc/impl/OrderServiceImpl.java index 05c75313..ff137178 100644 --- a/src/main/java/com/ycwl/basic/service/pc/impl/OrderServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/pc/impl/OrderServiceImpl.java @@ -920,12 +920,14 @@ public class OrderServiceImpl implements OrderService { FaceEntity face = faceRepository.getFace(request.getFaceId()); ProductItem productItem = request.getProducts().getFirst(); Integer type = switch (productItem.getProductType()) { + case PHOTO_LOG -> 5; case PHOTO_SET -> 2; case VLOG_VIDEO -> 0; case RECORDING_SET -> 1; default -> 0; }; Long goodsId = switch (productItem.getProductType()) { + case PHOTO_LOG -> Long.valueOf(productItem.getProductId()); case PHOTO_SET, RECORDING_SET -> face.getId(); case VLOG_VIDEO -> { List videos = memberRelationRepository.listRelationByFaceAndTemplate(face.getId(), Long.valueOf(productItem.getProductId())); diff --git a/src/main/java/com/ycwl/basic/service/printer/PrinterService.java b/src/main/java/com/ycwl/basic/service/printer/PrinterService.java index bdccbf50..5a0ee3ab 100644 --- a/src/main/java/com/ycwl/basic/service/printer/PrinterService.java +++ b/src/main/java/com/ycwl/basic/service/printer/PrinterService.java @@ -90,4 +90,15 @@ public interface PrinterService { * @return 成功数量 */ int rejectPrintTasks(List taskIds); + + /** + * 从拼图记录添加到打印列表 + * @param memberId 用户ID + * @param scenicId 景区ID + * @param faceId 人脸ID + * @param resultImageUrl 拼图结果图片URL + * @param puzzleRecordId 拼图记录ID + * @return 添加成功的MemberPrint记录ID + */ + Integer addUserPhotoFromPuzzle(Long memberId, Long scenicId, Long faceId, String resultImageUrl, Long puzzleRecordId); } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java index 5deac2f3..d7115634 100644 --- a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java @@ -1209,4 +1209,104 @@ public class PrinterServiceImpl implements PrinterService { return selectedPrinter; } + + @Override + public Integer addUserPhotoFromPuzzle(Long memberId, Long scenicId, Long faceId, String resultImageUrl, Long puzzleRecordId) { + if (resultImageUrl == null || resultImageUrl.isEmpty()) { + log.error("拼图图片URL为空: memberId={}, scenicId={}, puzzleRecordId={}", memberId, scenicId, puzzleRecordId); + return null; + } + + // 检查是否已经存在未打印的记录(status=0),避免重复导入 + List existingPhotos = printerMapper.listRelationByFaceId(memberId, scenicId, faceId); + if (existingPhotos != null && !existingPhotos.isEmpty()) { + for (MemberPrintResp photo : existingPhotos) { + // 检查是否是同一个拼图记录且状态为0(未打印) + if (photo.getSourceId() != null && photo.getSourceId().equals(puzzleRecordId) + && photo.getStatus() != null && photo.getStatus() == 0) { + log.info("拼图照片已存在于打印列表中,直接返回: memberId={}, scenicId={}, puzzleRecordId={}, memberPrintId={}", + memberId, scenicId, puzzleRecordId, photo.getId()); + return photo.getId().intValue(); + } + } + } + + MemberPrintEntity entity = new MemberPrintEntity(); + entity.setMemberId(memberId); + entity.setScenicId(scenicId); + entity.setFaceId(faceId); + entity.setSourceId(puzzleRecordId); // 使用拼图记录ID作为sourceId + entity.setOrigUrl(resultImageUrl); + + // 获取打印尺寸并裁剪图片 + String cropUrl = resultImageUrl; // 默认使用原图 + try { + // 从打印机表获取尺寸 + Integer printWidth = null; + Integer printHeight = null; + + List printers = printerMapper.listByScenicId(scenicId); + if (printers != null && !printers.isEmpty()) { + PrinterResp firstPrinter = printers.get(0); + printWidth = firstPrinter.getPreferW(); + printHeight = firstPrinter.getPreferH(); + log.debug("从打印机获取尺寸: scenicId={}, printerId={}, width={}, height={}", + scenicId, firstPrinter.getId(), printWidth, printHeight); + } + + // 如果打印机没有配置或配置无效,使用默认值 + if (printWidth == null || printWidth <= 0) { + printWidth = 1020; + log.debug("打印机宽度未配置或无效,使用默认值: width={}", printWidth); + } + if (printHeight == null || printHeight <= 0) { + printHeight = 1520; + log.debug("打印机高度未配置或无效,使用默认值: height={}", printHeight); + } + + // 使用smartCropAndFill裁剪图片 + File croppedFile = ImageUtils.smartCropAndFill(resultImageUrl, printWidth, printHeight); + + try { + // 上传裁剪后的图片 + String[] split = resultImageUrl.split("\\."); + String ext = split.length > 0 ? split[split.length - 1] : "jpg"; + + cropUrl = StorageFactory.use().uploadFile(null, croppedFile, "printer", UUID.randomUUID() + "." + ext); + + log.info("拼图照片裁剪成功: memberId={}, scenicId={}, puzzleRecordId={}, 原图={}, 裁剪后={}, 尺寸={}x{}", + memberId, scenicId, puzzleRecordId, resultImageUrl, cropUrl, printWidth, printHeight); + } finally { + // 清理临时文件 + if (croppedFile != null && croppedFile.exists()) { + croppedFile.delete(); + } + } + } catch (Exception e) { + log.error("拼图照片裁剪失败,使用原图: memberId={}, scenicId={}, puzzleRecordId={}, url={}", + memberId, scenicId, puzzleRecordId, resultImageUrl, e); + // 出现异常则使用原图 + cropUrl = resultImageUrl; + } + + entity.setCropUrl(cropUrl); + entity.setStatus(0); + + try { + int rows = printerMapper.addUserPhoto(entity); + if (rows > 0 && entity.getId() != null) { + log.info("拼图照片添加到打印列表成功: memberId={}, scenicId={}, puzzleRecordId={}, memberPrintId={}", + memberId, scenicId, puzzleRecordId, entity.getId()); + return entity.getId(); + } else { + log.error("拼图照片添加到打印列表失败: memberId={}, scenicId={}, puzzleRecordId={}", + memberId, scenicId, puzzleRecordId); + return null; + } + } catch (Exception e) { + log.error("拼图照片添加到打印列表异常: memberId={}, scenicId={}, puzzleRecordId={}", + memberId, scenicId, puzzleRecordId, e); + return null; + } + } }