chenxiaofei před 1 měsícem
rodič
revize
2bd8ff4e88

+ 87 - 12
iot-platform-manager/src/main/java/com/platform/api/manager/UploadService.java

@@ -19,6 +19,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.LocalDate;
+import java.util.Locale;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 
@@ -85,16 +86,71 @@ public class UploadService {
     }
 
     private DecodedImage decodeBase64Image(String base64Image) {
+        String extension = getExtensionFromBase64Image(base64Image);
+        String content = extractBase64Content(base64Image);
+        byte[] bytes = decodeBase64Content(content);
+        return new DecodedImage(bytes, extension);
+    }
+
+    private String extractBase64Content(String base64Image) {
         String content = base64Image.trim();
-        String extension = "jpg";
-        int commaIndex = content.indexOf(',');
-        if (content.startsWith("data:") && commaIndex > -1) {
-            String header = content.substring(0, commaIndex);
-            extension = getExtensionFromDataUri(header);
-            content = content.substring(commaIndex + 1);
+        String lowerContent = content.toLowerCase(Locale.ROOT);
+        int base64MarkerIndex = lowerContent.indexOf(";base64,");
+        if (base64MarkerIndex > -1) {
+            content = content.substring(base64MarkerIndex + ";base64,".length());
+        } else {
+            int commaIndex = content.indexOf(',');
+            if (lowerContent.startsWith("data:") && commaIndex > -1) {
+                content = content.substring(commaIndex + 1);
+            }
+        }
+
+        int startIndex = firstBase64CharIndex(content);
+        if (startIndex > 0) {
+            content = content.substring(startIndex);
+        }
+        int endIndex = firstNonBase64ContentIndex(content);
+        if (endIndex > -1) {
+            content = content.substring(0, endIndex);
+        }
+        return content;
+    }
+
+    private int firstBase64CharIndex(String content) {
+        for (int i = 0; i < content.length(); i++) {
+            if (isBase64ContentChar(content.charAt(i))) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    private int firstNonBase64ContentIndex(String content) {
+        for (int i = 0; i < content.length(); i++) {
+            if (!isBase64ContentChar(content.charAt(i))) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private boolean isBase64ContentChar(char ch) {
+        return (ch >= 'A' && ch <= 'Z')
+                || (ch >= 'a' && ch <= 'z')
+                || (ch >= '0' && ch <= '9')
+                || ch == '+'
+                || ch == '/'
+                || ch == '='
+                || Character.isWhitespace(ch);
+    }
+
+    private byte[] decodeBase64Content(String content) {
+        String normalizedContent = content.replaceAll("[\\r\\n\\t]", "").replace(" ", "+");
+        try {
+            return Base64.getDecoder().decode(normalizedContent);
+        } catch (IllegalArgumentException e) {
+            return Base64.getDecoder().decode(content.replaceAll("\\s", ""));
         }
-        byte[] bytes = Base64.getDecoder().decode(content.replaceAll("\\s", ""));
-        return new DecodedImage(bytes, extension);
     }
 
     private Path buildLocalImagePath(String licensePlate, String extension) {
@@ -105,21 +161,40 @@ public class UploadService {
     }
 
     private String getExtensionFromDataUri(String header) {
-        if (header.contains("image/png")) {
+        String lowerHeader = header.toLowerCase(Locale.ROOT);
+        if (lowerHeader.contains("image/png")) {
             return "png";
         }
-        if (header.contains("image/gif")) {
+        if (lowerHeader.contains("image/gif")) {
             return "gif";
         }
-        if (header.contains("image/webp")) {
+        if (lowerHeader.contains("image/webp")) {
             return "webp";
         }
-        if (header.contains("image/bmp")) {
+        if (lowerHeader.contains("image/bmp")) {
             return "bmp";
         }
         return "jpg";
     }
 
+    private String getExtensionFromBase64Image(String base64Image) {
+        String content = base64Image.trim();
+        String lowerContent = content.toLowerCase(Locale.ROOT);
+        int base64MarkerIndex = lowerContent.indexOf(";base64,");
+        if (base64MarkerIndex > -1) {
+            int dataUriIndex = lowerContent.lastIndexOf("data:", base64MarkerIndex);
+            if (dataUriIndex > -1) {
+                return getExtensionFromDataUri(content.substring(dataUriIndex, base64MarkerIndex));
+            }
+        }
+
+        int commaIndex = content.indexOf(',');
+        if (lowerContent.startsWith("data:") && commaIndex > -1) {
+            return getExtensionFromDataUri(content.substring(0, commaIndex));
+        }
+        return "jpg";
+    }
+
     private record DecodedImage(byte[] bytes, String extension) {
     }
     

+ 4 - 2
iot-platform-manager/src/main/java/com/platform/api/manager/WeighbridgeRecordManage.java

@@ -1,5 +1,6 @@
 package com.platform.api.manager;
 
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -115,11 +116,12 @@ public class WeighbridgeRecordManage {
     }
 
     public LicensePlateValidateResponse handleWeighbridgePushV2(WeighbridgePushRequest request) {
-        log.info("处理地磅数据上报 - 车牌:{}, 地磅编号:{}, 重量:{}, 时间戳:{}",
+        log.info("处理地磅数据上报 - 车牌:{}, 地磅编号:{}, 重量:{}, 时间戳:{},图片:{}",
                 request.getLicensePlate(),
                 request.getWeighbridgeCode(),
                 request.getGrossWeight(),
-                request.getTimestamp());
+                request.getTimestamp(),
+                JSON.toJSONString(request.getImages()));
         LicensePlateValidateResponse licensePlateValidateResponse = new LicensePlateValidateResponse();
         if (StringUtils.isAnyBlank(request.getLicensePlate(), request.getWeighbridgeCode()) || Objects.isNull(request.getGrossWeight()) || Objects.isNull(request.getTimestamp())) {
             return getLicensePlateValidateResponse(request, licensePlateValidateResponse);

+ 3 - 2
iot-platform-manager/src/main/java/com/platform/service/XpCloudPrintService.java

@@ -202,7 +202,7 @@ public class XpCloudPrintService {
             return orderNo;
         } catch (IotException e) {
             // 业务异常直接抛出
-            throw e;
+           throw new IotException("调用芯烨云小票打印异常:" + e.getMessage());
         } catch (Exception e) {
             // 捕获其他未知异常,记录错误日志并转换为业务异常抛出
             log.error("调用芯烨云小票打印异常, sn={}", reqVo.getSn(), e);
@@ -323,7 +323,8 @@ public class XpCloudPrintService {
 
         // 4. 如果存在空白字段,则抛出业务异常,提示具体缺失的字段名
         if (StringUtils.isNotBlank(blankField)) {
-            throw new IotException("小票内容字段不能为空: " + blankField);
+            log.error("小票内容字段不能为空: {}", blankField);
+            //throw new IotException("小票内容字段不能为空: " + blankField);
         }
     }
 

+ 44 - 0
iot-platform-manager/src/test/java/com/platform/api/manager/UploadServiceTest.java

@@ -0,0 +1,44 @@
+package com.platform.api.manager;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class UploadServiceTest {
+
+    @Test
+    void processBase64ImagesToLocalAsyncShouldExtractDataUrlPayload() throws Exception {
+        UploadService uploadService = new UploadService();
+        Path storagePath = Files.createTempDirectory("weighbridge-images");
+        ReflectionTestUtils.setField(uploadService, "imageStoragePath", storagePath.toString());
+
+        String image = "\"data:image/png;base64,iVBORw0KGgo=\\n\"]}";
+        String savedPath = uploadService.processBase64ImagesToLocalAsync(List.of(image), "test")
+                .get();
+
+        Path path = Path.of(savedPath);
+        assertTrue(Files.exists(path));
+        assertTrue(path.getFileName().toString().endsWith(".png"));
+        assertArrayEquals(new byte[]{
+                (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
+        }, Files.readAllBytes(path));
+    }
+
+    @Test
+    void processBase64ImagesToLocalAsyncShouldHandleSpaceConvertedPlusSign() throws Exception {
+        UploadService uploadService = new UploadService();
+        Path storagePath = Files.createTempDirectory("weighbridge-images");
+        ReflectionTestUtils.setField(uploadService, "imageStoragePath", storagePath.toString());
+
+        String savedPath = uploadService.processBase64ImagesToLocalAsync(List.of("data:image/jpeg;base64, w=="), "test")
+                .get();
+
+        assertArrayEquals(new byte[]{(byte) 0xFB}, Files.readAllBytes(Path.of(savedPath)));
+    }
+}