From a4496db344e2ecfd950d45f9fe5bcea9fe55c12d Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 11 Feb 2026 20:12:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(printer):=20=E6=89=A9=E5=B1=95=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E8=AE=A2=E5=8D=95=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E6=94=AF=E4=BB=98=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改CreateVirtualOrderRequest添加needActualPayment字段 - 更新SourceController接口方法签名以传递实际支付参数 - 在PrinterServiceImpl中实现两种订单模式:0元立即购买和待支付订单 - 添加价格计算逻辑,支持通过价格计算服务获取真实价格 - 实现微信Native支付集成,为待支付订单生成支付二维码 - 添加Redis临时存储机制,用于支付完成后恢复needEnhance配置 - 更新createVirtualOrder方法重载,支持完整的参数组合 - 添加详细的日志记录以便跟踪订单创建和支付状态变化 --- .../basic/controller/pc/SourceController.java | 6 +- .../req/CreateVirtualOrderRequest.java | 7 + .../basic/service/printer/PrinterService.java | 13 ++ .../printer/impl/PrinterServiceImpl.java | 129 +++++++++++++++--- 4 files changed, 135 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/ycwl/basic/controller/pc/SourceController.java b/src/main/java/com/ycwl/basic/controller/pc/SourceController.java index b3a68b91..e3d6905e 100644 --- a/src/main/java/com/ycwl/basic/controller/pc/SourceController.java +++ b/src/main/java/com/ycwl/basic/controller/pc/SourceController.java @@ -53,8 +53,9 @@ public class SourceController { } /** - * 创建虚拟用户0元订单 + * 创建虚拟用户订单 * 用于后台直接从source创建订单,不需要真实用户 + * 支持立即0元购买或创建待支付订单(由needActualPayment控制) * * @param request 请求参数 * @return 订单信息 @@ -67,7 +68,8 @@ public class SourceController { request.getScenicId(), request.getPrinterId(), request.getNeedEnhance(), - request.getPrintImgUrl() + request.getPrintImgUrl(), + request.getNeedActualPayment() ); return ApiResponse.success(result); } catch (Exception e) { diff --git a/src/main/java/com/ycwl/basic/model/printer/req/CreateVirtualOrderRequest.java b/src/main/java/com/ycwl/basic/model/printer/req/CreateVirtualOrderRequest.java index b78ed5af..a97e16d4 100644 --- a/src/main/java/com/ycwl/basic/model/printer/req/CreateVirtualOrderRequest.java +++ b/src/main/java/com/ycwl/basic/model/printer/req/CreateVirtualOrderRequest.java @@ -31,4 +31,11 @@ public class CreateVirtualOrderRequest { * 打印图片URL(可选,如果提供则使用此URL进行打印) */ private String printImgUrl; + + /** + * 是否需要实际支付(可选,默认false) + * false/null: 创建0元虚拟订单,立即完成购买 + * true: 创建待支付订单(计算实际价格),由前端处理支付流程 + */ + private Boolean needActualPayment; } 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 8b694ed0..c936f5e6 100644 --- a/src/main/java/com/ycwl/basic/service/printer/PrinterService.java +++ b/src/main/java/com/ycwl/basic/service/printer/PrinterService.java @@ -144,6 +144,19 @@ public interface PrinterService { */ Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl); + /** + * 创建虚拟用户订单(支持实际支付模式) + * + * @param sourceId source记录ID + * @param scenicId 景区ID + * @param printerId 打印机ID(可选) + * @param needEnhance 是否需要图像增强(可选) + * @param printImgUrl 打印图片URL(可选) + * @param needActualPayment 是否需要实际支付(true: 创建待支付订单, false/null: 0元立即购买) + * @return 订单信息 + */ + Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl, Boolean needActualPayment); + /** * 根据accessKey获取打印机详情 * @param accessKey 打印机accessKey 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 d7f2fcdb..77eaf189 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 @@ -1,5 +1,12 @@ package com.ycwl.basic.service.printer.impl; +import com.wechat.pay.java.service.payments.nativepay.NativePayService; +import com.wechat.pay.java.service.payments.nativepay.model.Amount; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse; +import com.ycwl.basic.pay.adapter.IPayAdapter; +import com.ycwl.basic.pay.adapter.WxMpPayAdapter; +import com.ycwl.basic.service.pc.ScenicService; import com.ycwl.basic.biz.OrderBiz; import com.ycwl.basic.constant.NumberConstant; import com.ycwl.basic.enums.OrderStateEnum; @@ -162,6 +169,9 @@ public class PrinterServiceImpl implements PrinterService { @Autowired @Lazy private WatermarkEdgeService watermarkEdgeService; + @Autowired + @Lazy + private ScenicService scenicService; @Override public List listByScenicId(Long scenicId) { @@ -1126,7 +1136,14 @@ public class PrinterServiceImpl implements PrinterService { @Override public void setUserIsBuyItem(Long memberId, Long id, Long orderId) { - setUserIsBuyItem(memberId, id, orderId, null); + // 尝试从 Redis 读取虚拟订单存储的 needEnhance 配置 + Boolean needEnhance = null; + String enhanceFlag = redisTemplate.opsForValue().get("virtual_order_enhance:" + orderId); + if (enhanceFlag != null) { + needEnhance = Boolean.parseBoolean(enhanceFlag); + redisTemplate.delete("virtual_order_enhance:" + orderId); + } + setUserIsBuyItem(memberId, id, orderId, needEnhance); } @Override @@ -1680,16 +1697,21 @@ public class PrinterServiceImpl implements PrinterService { @Override public Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId) { - return createVirtualOrder(sourceId, scenicId, printerId, null, null); + return createVirtualOrder(sourceId, scenicId, printerId, null, null, null); } @Override public Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance) { - return createVirtualOrder(sourceId, scenicId, printerId, needEnhance, null); + return createVirtualOrder(sourceId, scenicId, printerId, needEnhance, null, null); } @Override public Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl) { + return createVirtualOrder(sourceId, scenicId, printerId, needEnhance, printImgUrl, null); + } + + @Override + public Map createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl, Boolean needActualPayment) { // 1. 查询source记录 SourceEntity source = sourceMapper.getEntity(sourceId); FaceSampleEntity faceSample = faceSampleMapper.getEntity(source.getFaceSampleId()); @@ -1752,7 +1774,7 @@ public class PrinterServiceImpl implements PrinterService { throw new BaseException("打印机不属于该景区"); } - // 6. 创建0元订单 + // 6. 创建订单 OrderEntity order = new OrderEntity(); Long orderId = SnowFlakeUtil.getLongId(); redisTemplate.opsForValue().set("printer_size:" + orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS); @@ -1776,13 +1798,52 @@ public class PrinterServiceImpl implements PrinterService { return orderItem; }).collect(Collectors.toList()); - // 设置价格为0 - order.setPrice(BigDecimal.ZERO); - order.setSlashPrice(BigDecimal.ZERO); - order.setPayPrice(BigDecimal.ZERO); - order.setFaceId(faceId); - order.setStatus(OrderStateEnum.PAID.getState()); - order.setPayAt(new Date()); + boolean actualPayment = Boolean.TRUE.equals(needActualPayment); + + if (actualPayment) { + // 需要实际支付:通过价格计算服务获取真实价格 + PriceCalculationRequest priceRequest = new PriceCalculationRequest(); + priceRequest.setUserId(virtualMemberId); + priceRequest.setScenicId(scenicId); + + List productItems = new ArrayList<>(); + ProductItem photoItem = new ProductItem(); + photoItem.setProductType(ProductType.PHOTO_PRINT); + photoItem.setProductId(scenicId.toString()); + photoItem.setQuantity(1); + photoItem.setPurchaseCount(1); + photoItem.setScenicId(scenicId.toString()); + // 通过 source 的 deviceId 设置 attributeKeys + SourceEntity priceSource = sourceRepository.getSource(sourceId); + if (priceSource != null && priceSource.getDeviceId() != null) { + photoItem.setAttributeKeys(List.of(String.valueOf(priceSource.getDeviceId()))); + } + productItems.add(photoItem); + + priceRequest.setProducts(productItems); + priceRequest.setAutoUseCoupon(false); + priceRequest.setPreviewOnly(false); + + PriceCalculationResult priceResult = priceCalculationService.calculatePrice(priceRequest); + + order.setPrice(priceResult.getFinalAmount()); + order.setSlashPrice(priceResult.getOriginalAmount()); + order.setPayPrice(priceResult.getFinalAmount()); + order.setStatus(OrderStateEnum.UNPAID.getState()); + log.info("创建待支付虚拟订单: orderId={}, price={}", orderId, priceResult.getFinalAmount()); + + // 将 needEnhance 存入 Redis,支付完成后 setUserIsBuyItem 可读取 + if (needEnhance != null) { + redisTemplate.opsForValue().set("virtual_order_enhance:" + orderId, needEnhance.toString(), 24, TimeUnit.HOURS); + } + } else { + // 虚拟0元购买:价格为0,直接标记已支付 + order.setPrice(BigDecimal.ZERO); + order.setSlashPrice(BigDecimal.ZERO); + order.setPayPrice(BigDecimal.ZERO); + order.setStatus(OrderStateEnum.PAID.getState()); + order.setPayAt(new Date()); + } // 保存订单 orderMapper.add(order); @@ -1792,19 +1853,51 @@ public class PrinterServiceImpl implements PrinterService { throw new BaseException("订单添加失败"); } - log.info("创建0元订单成功: orderId={}, virtualMemberId={}, faceId={}", orderId, virtualMemberId, faceId); + log.info("创建虚拟订单成功: orderId={}, virtualMemberId={}, faceId={}, actualPayment={}", orderId, virtualMemberId, faceId, actualPayment); - // 7. 触发购买后逻辑(调用setUserIsBuyItem) - setUserIsBuyItem(virtualMemberId, memberPrintId.longValue(), orderId, needEnhance); - log.info("触发购买后逻辑完成: orderId={}", orderId); - - // 8. 返回结果 Map result = new HashMap<>(); result.put("orderId", orderId); result.put("faceId", faceId); result.put("virtualMemberId", virtualMemberId); result.put("memberPrintId", memberPrintId); - result.put("needPay", false); + + if (actualPayment) { + if (order.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) { + // 计算后价格为0,直接走免费逻辑 + order.setStatus(OrderStateEnum.PAID.getState()); + order.setPayAt(new Date()); + orderMapper.updateOrder(order); + setUserIsBuyItem(virtualMemberId, memberPrintId.longValue(), orderId, needEnhance); + log.info("待支付订单计算后价格为0,直接完成购买: orderId={}", orderId); + result.put("needPay", false); + } else { + // 通过 Native 支付生成二维码 + IPayAdapter payAdapter = scenicService.getScenicPayAdapter(scenicId); + if (payAdapter instanceof WxMpPayAdapter adapter) { + NativePayService nativePayService = new NativePayService.Builder().config(adapter.getConfig()).build(); + PrepayRequest prepayRequest = new PrepayRequest(); + prepayRequest.setAppid(adapter._config().getAppId()); + prepayRequest.setMchid(adapter._config().getMerchantId()); + prepayRequest.setDescription("照片打印"); + prepayRequest.setOutTradeNo(String.valueOf(orderId)); + prepayRequest.setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/" + scenicId + "/payNotify"); + Amount amount = new Amount(); + amount.setTotal(order.getPayPrice().multiply(new BigDecimal(100)).intValue()); + prepayRequest.setAmount(amount); + PrepayResponse prepayResponse = nativePayService.prepay(prepayRequest); + result.put("payCode", prepayResponse.getCodeUrl()); + } else { + throw new BaseException("该景区不支持 Native 支付"); + } + result.put("needPay", true); + result.put("price", order.getPayPrice()); + } + } else { + // 立即购买模式:触发购买后逻辑 + setUserIsBuyItem(virtualMemberId, memberPrintId.longValue(), orderId, needEnhance); + log.info("触发购买后逻辑完成: orderId={}", orderId); + result.put("needPay", false); + } return result; }