Ver código fonte

二维码功能

zhangchen 1 mês atrás
pai
commit
86570d97a9

+ 12 - 0
alien-store/pom.xml

@@ -258,6 +258,18 @@
             <version>1.0.0</version>
         </dependency>
 
+        <!-- 核销码二维码生成(ZXing) -->
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+
         <dependency>
             <groupId>shop.alien</groupId>
             <artifactId>alien-config</artifactId>

+ 42 - 0
alien-store/src/main/java/shop/alien/store/controller/ReservationVerificationQrController.java

@@ -0,0 +1,42 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.store.service.ReservationVerificationQrService;
+
+/**
+ * 核销码二维码生成测试接口(方便联调与测试)
+ *
+ * @author system
+ */
+@Slf4j
+@Api(tags = {"测试-核销码二维码"})
+@CrossOrigin
+@RestController
+@RequestMapping("/test/reservationVerificationQr")
+@RequiredArgsConstructor
+public class ReservationVerificationQrController {
+
+    private final ReservationVerificationQrService reservationVerificationQrService;
+
+    @ApiOperation("根据核销码生成二维码并上传 OSS,返回图片 URL")
+    @ApiImplicitParam(name = "verificationCode", value = "核销码(如 16 位数字)", required = true, paramType = "query", dataType = "String")
+    @GetMapping("/generate")
+    public R<String> generateAndUpload(@RequestParam String verificationCode) {
+        log.info("ReservationVerificationQrController.generateAndUpload?verificationCode={}", verificationCode);
+        if (StringUtils.isBlank(verificationCode)) {
+            return R.fail("核销码不能为空");
+        }
+        String url = reservationVerificationQrService.generateQrImageAndUploadToOss(verificationCode.trim());
+        if (StringUtils.isBlank(url)) {
+            return R.fail("生成或上传二维码失败,请检查 OSS 配置与日志");
+        }
+        return R.data(url);
+    }
+}

+ 17 - 0
alien-store/src/main/java/shop/alien/store/service/ReservationVerificationQrService.java

@@ -0,0 +1,17 @@
+package shop.alien.store.service;
+
+/**
+ * 预订订单核销码二维码服务:根据核销码生成二维码图片并上传 OSS,返回访问 URL
+ *
+ * @author system
+ */
+public interface ReservationVerificationQrService {
+
+    /**
+     * 根据核销码内容生成二维码图片并上传 OSS
+     *
+     * @param verificationCode 核销码(二维码内存储的文本)
+     * @return OSS 上图片的完整访问 URL,失败返回 null
+     */
+    String generateQrImageAndUploadToOss(String verificationCode);
+}

+ 75 - 0
alien-store/src/main/java/shop/alien/store/service/impl/ReservationVerificationQrServiceImpl.java

@@ -0,0 +1,75 @@
+package shop.alien.store.service.impl;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.store.service.ReservationVerificationQrService;
+import shop.alien.util.ali.AliOSSUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 预订订单核销码二维码:使用 ZXing 生成二维码图片并上传 OSS
+ *
+ * @author system
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ReservationVerificationQrServiceImpl implements ReservationVerificationQrService {
+
+    private static final int QR_SIZE = 300;
+    private static final String OSS_DIR = "qrcode/reservation/verification/";
+
+    private final AliOSSUtil aliOSSUtil;
+
+    @Override
+    public String generateQrImageAndUploadToOss(String verificationCode) {
+        if (StringUtils.isBlank(verificationCode)) {
+            log.warn("核销码为空,跳过二维码生成");
+            return null;
+        }
+        byte[] pngBytes = generateQrCodePng(verificationCode.trim());
+        if (pngBytes == null || pngBytes.length == 0) {
+            return null;
+        }
+        String ossPath = OSS_DIR + sanitizeForOssPath(verificationCode.trim()) + "_" + System.currentTimeMillis() + ".png";
+        try (InputStream in = new ByteArrayInputStream(pngBytes)) {
+            String url = aliOSSUtil.uploadFile(in, ossPath);
+            if (StringUtils.isNotBlank(url)) {
+                log.info("核销码二维码已上传 OSS, verificationCode={}, url={}", verificationCode, url);
+                return url;
+            }
+        } catch (IOException e) {
+            log.error("核销码二维码上传 OSS 异常, verificationCode={}", verificationCode, e);
+        }
+        return null;
+    }
+
+    private byte[] generateQrCodePng(String content) {
+        try {
+            QRCodeWriter writer = new QRCodeWriter();
+            BitMatrix matrix = writer.encode(content, BarcodeFormat.QR_CODE, QR_SIZE, QR_SIZE);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            MatrixToImageWriter.writeToStream(matrix, "PNG", out);
+            return out.toByteArray();
+        } catch (WriterException | IOException e) {
+            log.error("生成核销码二维码图片失败, content={}", content, e);
+            return null;
+        }
+    }
+
+    private static String sanitizeForOssPath(String code) {
+        if (code == null) return "";
+        return code.replaceAll("[^a-zA-Z0-9_-]", "_");
+    }
+}

+ 6 - 0
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantAlipayPaymentStrategyImpl.java

@@ -28,6 +28,7 @@ import shop.alien.store.service.MerchantPaymentOrderService;
 import shop.alien.store.service.RefundRecordService;
 import shop.alien.store.service.StorePaymentConfigService;
 import shop.alien.store.service.ReservationOrderPaymentTimeoutService;
+import shop.alien.store.service.ReservationVerificationQrService;
 import shop.alien.store.service.UserReservationOrderService;
 import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
 import shop.alien.util.common.UniqueRandomNumGenerator;
@@ -69,6 +70,7 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
     private final MerchantPaymentOrderService merchantPaymentOrderService;
     private final RefundRecordService refundRecordService;
     private final ReservationOrderPaymentTimeoutService reservationOrderPaymentTimeoutService;
+    private final ReservationVerificationQrService reservationVerificationQrService;
     private final StringRedisTemplate stringRedisTemplate;
 
     @Override
@@ -252,6 +254,10 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
                 order.setOrderStatus(1);
                 if (StringUtils.isBlank(order.getVerificationCode())) {
                     order.setVerificationCode("YS" + UniqueRandomNumGenerator.generateUniqueCode(10));
+                    String qrUrl = reservationVerificationQrService.generateQrImageAndUploadToOss(order.getVerificationCode());
+                    if (StringUtils.isNotBlank(qrUrl)) {
+                        order.setVerificationUrl(qrUrl);
+                    }
                 }
                 order.setUpdatedTime(now);
                 userReservationOrderService.updateById(order);

+ 6 - 0
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantWechatPaymentStrategyImpl.java

@@ -17,6 +17,7 @@ import shop.alien.store.service.MerchantPaymentOrderService;
 import shop.alien.store.service.RefundRecordService;
 import shop.alien.store.service.StorePaymentConfigService;
 import shop.alien.store.service.ReservationOrderPaymentTimeoutService;
+import shop.alien.store.service.ReservationVerificationQrService;
 import shop.alien.store.service.UserReservationOrderService;
 import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
 import shop.alien.store.strategy.payment.impl.WeChatPaymentStrategyImpl;
@@ -66,6 +67,7 @@ public class MerchantWechatPaymentStrategyImpl implements MerchantPaymentStrateg
     private final MerchantPaymentOrderService merchantPaymentOrderService;
     private final RefundRecordService refundRecordService;
     private final ReservationOrderPaymentTimeoutService reservationOrderPaymentTimeoutService;
+    private final ReservationVerificationQrService reservationVerificationQrService;
     private final StringRedisTemplate stringRedisTemplate;
 
     @Override
@@ -234,6 +236,10 @@ public class MerchantWechatPaymentStrategyImpl implements MerchantPaymentStrateg
                 order.setOrderStatus(1);
                 if (StringUtils.isBlank(order.getVerificationCode())) {
                     order.setVerificationCode("YS" + UniqueRandomNumGenerator.generateUniqueCode(10));
+                    String qrUrl = reservationVerificationQrService.generateQrImageAndUploadToOss(order.getVerificationCode());
+                    if (StringUtils.isNotBlank(qrUrl)) {
+                        order.setVerificationUrl(qrUrl);
+                    }
                 }
                 order.setUpdatedTime(now);
                 userReservationOrderService.updateById(order);