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; }