linbl пре 3 недеља
комит
825b20517c
41 измењених фајлова са 4874 додато и 0 уклоњено
  1. 157 0
      pom.xml
  2. 26 0
      src/main/java/com/ruoyi/RuoYiApplication.java
  3. 71 0
      src/main/java/com/ruoyi/web/backgroud/client/BackgroundClientHandler.java
  4. 85 0
      src/main/java/com/ruoyi/web/backgroud/client/BackgroundClientNetty.java
  5. 104 0
      src/main/java/com/ruoyi/web/bo/GatherBo.java
  6. 21 0
      src/main/java/com/ruoyi/web/bo/ReleaseBo.java
  7. 25 0
      src/main/java/com/ruoyi/web/bo/ResponseBo.java
  8. 13 0
      src/main/java/com/ruoyi/web/bo/Steps.java
  9. 21 0
      src/main/java/com/ruoyi/web/bo/XML82Bo.java
  10. 165 0
      src/main/java/com/ruoyi/web/config/AesEncryptUtil.java
  11. 89 0
      src/main/java/com/ruoyi/web/config/ConfigComponent.java
  12. 144 0
      src/main/java/com/ruoyi/web/config/DateUtils.java
  13. 30 0
      src/main/java/com/ruoyi/web/config/HmacSHAUtil.java
  14. 44 0
      src/main/java/com/ruoyi/web/config/IdUtils.java
  15. 68 0
      src/main/java/com/ruoyi/web/config/Md5Utils.java
  16. 218 0
      src/main/java/com/ruoyi/web/config/MessageXML.java
  17. 245 0
      src/main/java/com/ruoyi/web/config/RestUtil.java
  18. 484 0
      src/main/java/com/ruoyi/web/config/UUID.java
  19. 18 0
      src/main/java/com/ruoyi/web/config/WebServiceConfig.java
  20. 328 0
      src/main/java/com/ruoyi/web/controller/ProcessController.java
  21. 16 0
      src/main/java/com/ruoyi/web/entity/Base.java
  22. 19 0
      src/main/java/com/ruoyi/web/entity/Customs.java
  23. 47 0
      src/main/java/com/ruoyi/web/entity/CustomsRes.java
  24. 106 0
      src/main/java/com/ruoyi/web/entity/GatherData.java
  25. 17 0
      src/main/java/com/ruoyi/web/entity/GatherFeedBack.java
  26. 26 0
      src/main/java/com/ruoyi/web/entity/Release.java
  27. 15 0
      src/main/java/com/ruoyi/web/entity/Reservation.java
  28. 20 0
      src/main/java/com/ruoyi/web/entity/VerificationRes.java
  29. 17 0
      src/main/java/com/ruoyi/web/entity/WeightRes.java
  30. 490 0
      src/main/java/com/ruoyi/web/service/BulkloadService.java
  31. 876 0
      src/main/java/com/ruoyi/web/service/ContainerService.java
  32. 113 0
      src/main/java/com/ruoyi/web/service/CustomsService.java
  33. 82 0
      src/main/java/com/ruoyi/web/service/IntermodalTransporService.java
  34. 125 0
      src/main/java/com/ruoyi/web/service/ProcessService.java
  35. 153 0
      src/main/java/com/ruoyi/web/service/ReservationService.java
  36. 125 0
      src/main/java/com/ruoyi/web/service/SendSocket81Service.java
  37. 11 0
      src/main/java/com/ruoyi/web/task/TaskDTO.java
  38. 19 0
      src/main/java/com/ruoyi/web/task/TokenTask.java
  39. 61 0
      src/main/resources/application-druid.yml
  40. 55 0
      src/main/resources/application.yml
  41. 125 0
      src/main/resources/logback.xml

+ 157 - 0
pom.xml

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.ruoyi</groupId>
+    <artifactId>DataExchange</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <properties>
+        <java.version>8</java.version>
+        <java.run.main.class>com.ruoyi.RuoYiApplication</java.run.main.class>
+    </properties>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.3.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.59.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.18</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>20.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>3.0.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>2.0.6</version>
+        </dependency>
+    </dependencies>
+
+
+    <!-- 构建配置 -->
+    <build>
+        <finalName>DataExchange</finalName>
+        <!-- 配置资源目录  -->
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <fork>true</fork>
+                    <mainClass>com.ruoyi.RuoYiApplication</mainClass>
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
+            </plugin>
+            <!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <classpathPrefix>lib/</classpathPrefix>
+                            <mainClass>${java.run.main.class}</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <!-- 拷贝依赖的jar包到lib目录 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>
+                                ${project.build.directory}/lib
+                            </outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <!-- https://gitee.com/roseboy/classfinal -->
+                <groupId>net.roseboy</groupId>
+                <artifactId>classfinal-maven-plugin</artifactId>
+                <version>1.2.1</version>
+                <configuration>
+                    <!--加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码-->
+                    <password>ABCabc123</password>
+                    <!--东岸-->
+                    <!--<code>20DFEE057FA73408A5D454AF9D5E8F0ED41D8CD98F00B204E9800998ECF8427ED41D8CD98F00B204E9800998ECF8427E</code>-->
+                    <!--钦州联运中心-->
+                    <code>D72E7F75D4CDEEFAD7AC77891557F586D41D8CD98F00B204E9800998ECF8427ED41D8CD98F00B204E9800998ECF8427E</code>
+                    <packages>com.ruoyi</packages> <!--可以多个-->
+                    <excludes>org.spring</excludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>classFinal</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 26 - 0
src/main/java/com/ruoyi/RuoYiApplication.java

@@ -0,0 +1,26 @@
+package com.ruoyi;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.ApplicationPidFileWriter;
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * 启动程序
+ *
+ */
+@SpringBootApplication
+@Slf4j
+public class RuoYiApplication {
+    public static void main(String[] args) {
+        Properties properties = System.getProperties();
+        String rootPath = properties.getProperty("user.dir");
+        SpringApplication application = new SpringApplication(RuoYiApplication.class);
+        application.addListeners(new ApplicationPidFileWriter(rootPath + File.separator + "app.pid"));
+        application.run(args);
+        log.info("数据交换中心***********************");
+    }
+}

+ 71 - 0
src/main/java/com/ruoyi/web/backgroud/client/BackgroundClientHandler.java

@@ -0,0 +1,71 @@
+package com.ruoyi.web.backgroud.client;
+
+import com.ruoyi.web.bo.XML82Bo;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import java.io.StringReader;
+
+@Slf4j
+@ChannelHandler.Sharable
+public class BackgroundClientHandler extends SimpleChannelInboundHandler<String> {
+
+    private BackgroundClientNetty client;
+
+    public BackgroundClientHandler(BackgroundClientNetty client) {
+        this.client = client;
+    }
+
+    private XML82Bo xmlToObject(String xmlString) {
+        XML82Bo xml82Bo = new XML82Bo();
+        try {
+            JAXBContext context = JAXBContext.newInstance(XML82Bo.class);
+            Unmarshaller unmarshaller = context.createUnmarshaller();
+            StringReader reader = new StringReader(xmlString);
+            xml82Bo = (XML82Bo) unmarshaller.unmarshal(reader);
+        } catch (JAXBException e) {
+            e.printStackTrace();
+        }
+
+        return xml82Bo;
+    }
+
+    //接收服务端返回的82报文
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
+        /**
+         <?xml version="1.0" encoding="GB2312"?>
+         <GATHER_FEEDBACK>
+             <AREA_ID>7230000LZB</AREA_ID>
+             <CHNL_NO>72300HE101</CHNL_NO>
+             <SESSION_ID>72300HE10120201102092243</SESSION_ID>
+             <CHECK_RESULT>Y</CHECK_RESULT>
+             <OP_HINT>【智能卡口】海关放行</OP_HINT>
+             <LED_HINT>请通行</LED_HINT>
+             <EXTENDED_CONTENT></EXTENDED_CONTENT>
+         </GATHER_FEEDBACK>
+         */
+        System.out.println("---------客户端接收返回的82报文:"+ msg);
+        XML82Bo xml82Bo = xmlToObject(msg);
+
+
+    }
+
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        ctx.close();
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        log.error("-------------后端服务器连接断开,{}-----------,进行重连", ctx.channel().remoteAddress());
+        client.connect();
+    }
+}

+ 85 - 0
src/main/java/com/ruoyi/web/backgroud/client/BackgroundClientNetty.java

@@ -0,0 +1,85 @@
+package com.ruoyi.web.backgroud.client;
+
+import com.ruoyi.web.config.ConfigComponent;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.timeout.IdleStateHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Configuration
+public class BackgroundClientNetty {
+
+    @Resource
+    ConfigComponent component;
+    Bootstrap bootstrap;
+    Channel channel;
+
+    @Bean("backgroundClientHandler")
+    public BackgroundClientHandler backgroundClientHandler() {
+        return new BackgroundClientHandler(this);
+    }
+
+    @Bean("backgroundClient")
+    public Bootstrap bootstrap() {
+        EventLoopGroup group = new NioEventLoopGroup();
+        bootstrap = new Bootstrap();
+        return bootstrap.group(group)
+                .channel(NioSocketChannel.class)
+                .remoteAddress(component.getHost(), component.getBeatPort())
+                .handler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(SocketChannel socketChannel) throws Exception {
+                        socketChannel.pipeline()
+                                .addLast("decoder", new StringDecoder())
+                                .addLast("encoder", new StringEncoder())
+                                .addLast(new IdleStateHandler(0, 5, 0))
+                                .addLast();
+                    }
+                });
+    }
+
+
+    public void connect() {
+        ChannelFuture future = bootstrap().connect();
+        future.addListener((ChannelFutureListener) item -> {
+            if(item.isSuccess()) {
+                channel = item.channel();
+                log.info("-----------后端服务器连接成功,{},{}-----------",component.getHost(), component.getBeatPort());
+            } else {
+                log.error("-----------后端服务器连接失败,{},{}-----------进行重连",component.getHost(), component.getBeatPort());
+                item.channel().eventLoop().schedule(this::connect, 5, TimeUnit.SECONDS);
+            }
+        });
+        try {
+            future.channel().closeFuture().sync();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void send(String message) {
+        if(channel != null && channel.isActive()) {
+            channel.writeAndFlush(message);
+        } else {
+            log.error("未建立连接,无法发送消息!");
+        }
+    }
+
+    public void close() {
+        if(channel != null) {
+            channel.close();
+        }
+    }
+
+}

+ 104 - 0
src/main/java/com/ruoyi/web/bo/GatherBo.java

@@ -0,0 +1,104 @@
+package com.ruoyi.web.bo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 采集数据(0x21)
+ */
+@Data
+public class GatherBo {
+
+    private List<Steps> steps;
+
+    //集装箱或散货接口调用状态:0=默认值 1=通过
+    private Integer jsStatus;
+    //海关接口调用状态:0=默认值 1=通过
+    private Integer containerStatus;
+
+    //0=不验空箱,1=需验空箱
+    private Integer isEmpty;
+    //0=不验重箱,1=需验重箱
+    private Integer isLoaded;
+
+    //道口号
+    private String crossCode;
+    //前箱号
+    private String cntrnoF;
+    //前箱的 UNISO 代码
+    private String cntrnoFType;
+    //后箱号
+    private String cntrnoA;
+    //后箱的UNISO 代码
+    private String cntrnoAType;
+
+    //进出标识,I进E出
+    private String ieFlag;
+    //场站号,固定写为场所分配的场站号
+    private String areaId;
+    //通道号,已经从通道号配置文件中读取
+    private String chnlNo;
+    //通道业务类型,通常为A,已经读取
+    private String getherMode;
+    //会话编号,自动生成
+    private String sessionId;
+
+    //ic卡配置信息
+    //读取到的ic卡号
+    private String icId;
+    private String icGoodsName;
+    private String icBillNo;
+    private String icNo;
+    private String icType;
+    private String icExtendedContent;
+
+    private String drCustomsNo;
+    private String veCustomsNo;
+    //读取到的车牌号信息
+    private String veLicenseNo;
+    private String esealId;
+    //读取到的地磅重量
+    private double grossWt;
+
+    //veRfid信息
+    //rfid卡号信息
+    private String veRfidId;
+    //rfid绑定的光学车牌信息
+    private String veLicenseNoRfid;
+    private String veCustomsNoRfid;
+    private String veWtRfid;
+    private String veCompanyRfid;
+    private String vePerformanceRfid;
+
+    //drRfid信息
+    private String drRfidId;
+    private String drName;
+    private String drCustomsNoRfid;
+    private String drCompany;
+    private String drPerformance;
+
+    private String operatorId;
+    private String operateTime;
+
+
+    private String domesticLicenseNo;
+    //车牌图片保存路径,如有箱号,有箱号图片路径
+    private String domesticLicenseColor;
+    private String foreignLicenseNo;
+    private String foreignLicenseColor;
+    private String confidenceRatio;
+
+    /**
+     * 散货过磅地点
+     */
+    private String placeCode;
+    /**
+     * 散货道口号
+     */
+    private String gateNo;
+    /**
+     * 接口是否发送——散货过磅地点:0=不发送,1=发送
+     */
+    private Integer isSendPlaceCode;
+}

+ 21 - 0
src/main/java/com/ruoyi/web/bo/ReleaseBo.java

@@ -0,0 +1,21 @@
+package com.ruoyi.web.bo;
+
+import lombok.Data;
+
+/**
+ * 放行结果信息(车辆通过卡口后)(0x32)——>海关
+ */
+@Data
+public class ReleaseBo {
+
+    //进出标识,i进e出
+    private String ieFlag;
+    //场站号,固定写为场所分配的场站号
+    private String areaId;
+    //通道号,已经从通道号配置文件中读取
+    private String chnlNo;
+
+    //计数器值
+    private String esealUseCount;
+
+}

+ 25 - 0
src/main/java/com/ruoyi/web/bo/ResponseBo.java

@@ -0,0 +1,25 @@
+package com.ruoyi.web.bo;
+
+import lombok.Data;
+
+@Data
+public class ResponseBo {
+
+    private Integer code;
+
+    private String msg;
+
+    private String examineState;
+
+    private String RegistrationNo;
+
+    //集装箱或散货接口调用状态:0=默认值 1=通过
+    private Integer jsStatus;
+    //海关接口调用状态:0=默认值 1=通过
+    private Integer containerStatus;
+
+    private String notes;
+
+    private String flag;
+
+}

+ 13 - 0
src/main/java/com/ruoyi/web/bo/Steps.java

@@ -0,0 +1,13 @@
+package com.ruoyi.web.bo;
+
+import lombok.Data;
+
+@Data
+public class Steps {
+
+    private String name;
+
+    private String api;
+
+    private String remark;
+}

+ 21 - 0
src/main/java/com/ruoyi/web/bo/XML82Bo.java

@@ -0,0 +1,21 @@
+package com.ruoyi.web.bo;
+
+import lombok.Data;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * 82报文
+ */
+@Data
+@XmlRootElement(name = "GATHER_FEEDBACK")
+public class XML82Bo {
+
+    private String AREA_ID;
+    private String CHNL_NO;
+    private String SESSION_ID;
+    private String CHECK_RESULT;
+    private String OP_HINT;
+    private String LED_HINT;
+    private String EXTENDED_CONTENT;
+}

+ 165 - 0
src/main/java/com/ruoyi/web/config/AesEncryptUtil.java

@@ -0,0 +1,165 @@
+package com.ruoyi.web.config;
+
+
+import sun.misc.BASE64Decoder;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @Description: AES 加密
+ */
+public class AesEncryptUtil {
+
+    /**
+     * 使用AES-128-CBC加密模式
+     */
+    private static String KEY = "1234567890adbcde";
+    private static String key = "SC35fdGrQofSMT5a";
+
+    /**
+     * 加密方法
+     * @param data  要加密的数据
+     * @param key 加密key
+     * @return 加密的结果
+     * @throws Exception
+     */
+    public static String encrypt(String data, String key) throws Exception {
+        try {
+
+            //"算法/模式/补码方式"NoPadding PkcsPadding
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            int blockSize = cipher.getBlockSize();
+
+            byte[] dataBytes = data.getBytes();
+            int plaintextLength = dataBytes.length;
+            if (plaintextLength % blockSize != 0) {
+                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
+            }
+
+            byte[] plaintext = new byte[plaintextLength];
+            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
+
+            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
+
+            cipher.init(Cipher.ENCRYPT_MODE, keyspec);
+            byte[] encrypted = cipher.doFinal(plaintext);
+
+            return new String(encrypted);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 解密方法
+     * @param data 要解密的数据
+     * @param key  解密key
+     * @return 解密的结果
+     * @throws Exception
+     */
+    public static String desEncrypt(String data, String key) throws Exception {
+        byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
+
+        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
+
+        cipher.init(Cipher.ENCRYPT_MODE, keyspec);
+
+        byte[] original = cipher.doFinal(encrypted1);
+        String originalString = new String(original);
+        //加密解码后的字符串会出现\u0000
+        return originalString.replaceAll("\\u0000", "");
+    }
+
+    /**
+     * 使用默认的key加密
+     * @param data
+     * @return
+     * @throws Exception
+     */
+    public static String encrypt(String data) throws Exception {
+        return encrypt(data, KEY);
+    }
+
+    /**
+     * 使用默认的key解密
+     * @param data
+     * @return
+     * @throws Exception
+     */
+    public static String desEncrypt(String data) throws Exception {
+        return desEncrypt(data, KEY);
+    }
+
+
+
+
+
+    public static String AESEncrypt(String data)  {
+
+        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
+
+        Cipher cipher = null; // 选择AES算法、工作模式为ECB、填充方式为PKCS5Padding
+        byte[] encryptedBytes =null;
+        try {
+            cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 初始化加密器
+            encryptedBytes = cipher.doFinal(data.getBytes()); // 对明文进行加密操作
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException
+                | IllegalBlockSizeException | InvalidKeyException e) {
+            e.printStackTrace();
+        }
+
+        System.out.println("Encrypted Text: " + DatatypeConverter.printHexBinary(encryptedBytes));
+        return DatatypeConverter.printHexBinary(encryptedBytes);
+    }
+
+    public static String AESDecrypt(String data)  {
+        byte[] result = new byte[data.length()/2];
+        for (int i = 0; i < data.length()/2; i++) {
+            result[i] =Integer.valueOf(data.substring(i * 2, i * 2 + 2), 16).byteValue();
+        }
+
+        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
+
+        Cipher cipher = null; // 选择AES算法、工作模式为ECB、填充方式为PKCS5Padding
+        byte[] decryptedBytes = null;
+        String decryptStr =null;
+        try {
+            cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            cipher.init(Cipher.DECRYPT_MODE, secretKey); // 初始化解密器
+            decryptedBytes = cipher.doFinal(result); // 对密文进行解密操作
+            System.out.println("Decrypted Text: " + new String(decryptedBytes));
+            decryptStr=new String(decryptedBytes);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException
+                | IllegalBlockSizeException | InvalidKeyException e) {
+            e.printStackTrace();
+        }
+        return decryptStr;
+    }
+
+//    /**
+//     * 测试
+//     */
+//    public static void main(String args[]) throws Exception {
+//        String test1 = "sa";
+//        String test =new String(test1.getBytes(),"UTF-8");
+//        String data = null;
+//        String key =  KEY;
+//        // /g2wzfqvMOeazgtsUVbq1kmJawROa6mcRAzwG1/GeJ4=
+//        data = encrypt(test, key, iv);
+//        System.out.println("数据:"+test);
+//        System.out.println("加密:"+data);
+//        String jiemi =desEncrypt(data, key, iv).trim();
+//        System.out.println("解密:"+jiemi);
+//    }
+
+}

+ 89 - 0
src/main/java/com/ruoyi/web/config/ConfigComponent.java

@@ -0,0 +1,89 @@
+package com.ruoyi.web.config;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Data
+@Accessors(chain = true)
+@Component
+public class ConfigComponent {
+
+    @Value("${manager.host}")
+    private String host;
+    @Value("${manager.beatPort}")
+    private Integer beatPort;
+
+    @Value("${intermodalTranspor.gateIdentifyUrl}")
+    private String gateIdentifyUrl;
+    @Value("${intermodalTranspor.appid}")
+    private String appid;
+
+    @Value("${reservation.reservationUrl}")
+    private String reservationUrl;
+
+    @Value("${customs.gatherUrl}")
+    private String gatherUrl;
+
+    @Value("${customs.releaseUrl}")
+    private String releaseUrl;
+
+    @Value("${bulkload.LogOnUrl}")
+    private String LogOnUrl;
+    @Value("${bulkload.LogOffUrl}")
+    private String LogOffUrl;
+    @Value("${bulkload.InVerificationUrl}")
+    private String InVerificationUrl;
+    @Value("${bulkload.OutVerificationUrl}")
+    private String OutVerificationUrl;
+    @Value("${bulkload.InWeightUrl}")
+    private String InWeightUrl;
+    @Value("${bulkload.OutWeightUrl}")
+    private String OutWeightUrl;
+    @Value("${bulkload.InReportUrl}")
+    private String InReportUrl;
+    @Value("${bulkload.OutReportUrl}")
+    private String OutReportUrl;
+    @Value("${bulkload.InWeightReportUrl}")
+    private String InWeightReportUrl;
+    @Value("${bulkload.OutWeightReportUrl}")
+    private String OutWeightReportUrl;
+    @Value("${bulkload.PrintCountUrl}")
+    private String PrintCountUrl;
+
+    @Value("${container.SAVETRUCKINFORUrl}")
+    private String SAVETRUCKINFORUrl;
+    @Value("${container.SAVEWEIGHTINFORUrl}")
+    private String SAVEWEIGHTINFORUrl;
+    @Value("${container.SAVECONTAINERINFORUrl}")
+    private String SAVECONTAINERINFORUrl;
+    @Value("${container.SAVEDMGCDINFORUrl}")
+    private String SAVEDMGCDINFORUrl;
+    @Value("${container.GETCARDSUrl}")
+    private String GETCARDSUrl;
+    @Value("${container.GETOUTCARDSUrl}")
+    private String GETOUTCARDSUrl;
+    @Value("${container.SAVEPLANINFORUrl}")
+    private String SAVEPLANINFORUrl;
+    @Value("${container.TRUCKPASSUrl}")
+    private String TRUCKPASSUrl;
+    @Value("${container.CONFIRMOUTYARDUrl}")
+    private String CONFIRMOUTYARDUrl;
+    @Value("${container.GetBookingInfoUrl}")
+    private String GetBookingInfoUrl;
+    @Value("${container.UpdateSealNoUrl}")
+    private String UpdateSealNoUrl;
+    @Value("${container.GetDamageCodeUrl}")
+    private String GetDamageCodeUrl;
+    @Value("${container.SavelDamageInfoUrl}")
+    private String SavelDamageInfoUrl;
+
+    @Value("${bulkload.UserName}")
+    private String UserName;
+    @Value("${bulkload.PassWord}")
+    private String PassWord;
+    @Value("${bulkload.AuthToken}")
+    private String AuthToken;
+
+}

+ 144 - 0
src/main/java/com/ruoyi/web/config/DateUtils.java

@@ -0,0 +1,144 @@
+package com.ruoyi.web.config;
+
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ */
+public class DateUtils {
+    public static String YYYY = "yyyy";
+
+    public static String YYYY_MM = "yyyy-MM";
+
+    public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+    /**
+     * 获取当前Date型日期
+     *
+     * @return Date() 当前日期
+     */
+    public static Date getNowDate() {
+        return new Date();
+    }
+
+    /**
+     * 获取当前日期, 默认格式为yyyy-MM-dd
+     *
+     * @return String
+     */
+    public static String getDate() {
+        return dateTimeNow(YYYY_MM_DD);
+    }
+
+    public static String getYear() {
+        return dateTimeNow(YYYY);
+    }
+
+    public static final String getTime() {
+        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+    }
+
+    public static final String dateTimeNow() {
+        return dateTimeNow(YYYYMMDDHHMMSS);
+    }
+
+    public static final String dateTimeNow(final String format) {
+        return parseDateToStr(format, new Date());
+    }
+
+    public static final String dateTime(final Date date) {
+        return parseDateToStr(YYYY_MM_DD, date);
+    }
+
+    public static final String parseDateToStr(final String format, final Date date) {
+        return new SimpleDateFormat(format).format(date);
+    }
+
+    public static final Date dateTime(final String format, final String ts) {
+        try {
+            return new SimpleDateFormat(format).parse(ts);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 获取服务器启动时间
+     */
+    public static Date getServerStartDate() {
+        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+        return new Date(time);
+    }
+
+    /**
+     * 计算相差天数
+     */
+    public static int differentDaysByMillisecond(Date date1, Date date2) {
+        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
+    }
+
+    /**
+     * 计算两个时间差
+     */
+    public static String getDatePoor(Date endDate, Date nowDate) {
+        long nd = 1000 * 24 * 60 * 60;
+        long nh = 1000 * 60 * 60;
+        long nm = 1000 * 60;
+        // long ns = 1000;
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - nowDate.getTime();
+        // 计算差多少天
+        long day = diff / nd;
+        // 计算差多少小时
+        long hour = diff % nd / nh;
+        // 计算差多少分钟
+        long min = diff % nd % nh / nm;
+        // 计算差多少秒//输出结果
+        // long sec = diff % nd % nh % nm / ns;
+        //return day + "天" + hour + "小时" + min + "分钟";
+        return "" + min;
+    }
+
+    /**
+     * 增加 LocalDateTime ==> Date
+     */
+    public static Date toDate(LocalDateTime temporalAccessor) {
+        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+
+    /**
+     * 增加 LocalDate ==> Date
+     */
+    public static Date toDate(LocalDate temporalAccessor) {
+        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
+        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+    public static boolean isToday(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH);
+        int day = calendar.get(Calendar.DAY_OF_MONTH);
+        Calendar today = Calendar.getInstance();
+        int todayYear = today.get(Calendar.YEAR);
+        int todayMonth = today.get(Calendar.MONTH);
+        int todayDay = today.get(Calendar.DAY_OF_MONTH);
+        return (year == todayYear && month == todayMonth && day == todayDay);
+    }
+
+
+}

+ 30 - 0
src/main/java/com/ruoyi/web/config/HmacSHAUtil.java

@@ -0,0 +1,30 @@
+package com.ruoyi.web.config;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @Description: HMAC加密
+ */
+public class HmacSHAUtil {
+
+    public static String calculateHMACSHA256(String key, String message) {
+        try {
+            Mac mac = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            mac.init(secretKeySpec);
+            byte[] hmac = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
+            StringBuilder sb = new StringBuilder();
+            for (byte b : hmac) {
+                sb.append(String.format("%02x", b));
+            }
+            return sb.toString();
+        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 44 - 0
src/main/java/com/ruoyi/web/config/IdUtils.java

@@ -0,0 +1,44 @@
+package com.ruoyi.web.config;
+
+/**
+ * ID生成器工具类
+ *
+ * @author ruoyi
+ */
+public class IdUtils {
+    /**
+     * 获取随机UUID
+     *
+     * @return 随机UUID
+     */
+    public static String randomUUID() {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String simpleUUID() {
+        return UUID.randomUUID().toString(true);
+    }
+
+    /**
+     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 随机UUID
+     */
+    public static String fastUUID() {
+        return UUID.fastUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String fastSimpleUUID() {
+        return UUID.fastUUID().toString(true);
+    }
+}

+ 68 - 0
src/main/java/com/ruoyi/web/config/Md5Utils.java

@@ -0,0 +1,68 @@
+package com.ruoyi.web.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * Md5加密方法
+ *
+ * @author ruoyi
+ */
+public class Md5Utils
+{
+    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
+
+    private static byte[] md5(String s)
+    {
+        MessageDigest algorithm;
+        try
+        {
+            algorithm = MessageDigest.getInstance("MD5");
+            algorithm.reset();
+            algorithm.update(s.getBytes("UTF-8"));
+            byte[] messageDigest = algorithm.digest();
+            return messageDigest;
+        }
+        catch (Exception e)
+        {
+            log.error("MD5 Error...", e);
+        }
+        return null;
+    }
+
+    private static final String toHex(byte hash[])
+    {
+        if (hash == null)
+        {
+            return null;
+        }
+        StringBuffer buf = new StringBuffer(hash.length * 2);
+        int i;
+
+        for (i = 0; i < hash.length; i++)
+        {
+            if ((hash[i] & 0xff) < 0x10)
+            {
+                buf.append("0");
+            }
+            buf.append(Long.toString(hash[i] & 0xff, 16));
+        }
+        return buf.toString();
+    }
+
+    public static String hash(String s)
+    {
+        try
+        {
+            return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+        }
+        catch (Exception e)
+        {
+            log.error("not supported charset...{}", e);
+            return s;
+        }
+    }
+}

+ 218 - 0
src/main/java/com/ruoyi/web/config/MessageXML.java

@@ -0,0 +1,218 @@
+package com.ruoyi.web.config;
+
+import com.ruoyi.web.config.DateUtils;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.Release;
+
+public class MessageXML {
+
+    //81报文
+    public static String get81Xml(GatherData gatherData) {
+        //会话编号: 场站号 + 通道号 + 当前时间戳后六位
+        Long timestamp = System.currentTimeMillis();
+        String timeStr = String.valueOf(timestamp);
+        String last6 = timeStr.substring(timeStr.length() - 1);
+        String SESSION_ID = gatherData.getAREA_ID() + gatherData.getCHNL_NO() + last6;
+
+        String xml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n" +
+                "<GATHER_INFO>\n" +
+                //进出标识,I进E出
+                "<I_E_FLAG>"+ gatherData.getI_E_FLAG() +"</I_E_FLAG>\n" +
+                //场站号,固定写为场所分配的场站号
+                "<AREA_ID>"+ gatherData.getAREA_ID() +"</AREA_ID>\n" +
+                //通道号,已经从通道号配置文件中读取
+                "<CHNL_NO>"+ gatherData.getCHNL_NO() +"</CHNL_NO>\n" +
+                //通道业务类型,通常为A,已经读取
+                "<GETHER_MODE>"+ gatherData.getGETHER_MODE() +"</GETHER_MODE>\n" +
+                //会话编号,自动生成
+                "<SESSION_ID>"+ SESSION_ID +"</SESSION_ID>\n" +
+
+                //IC卡配置信息
+                "<IC_INFO>\n" +
+                //读取到的IC卡号
+                "<IC_ID>"+ gatherData.getIC_ID() +"</IC_ID>\n" +
+                "<IC_NO>"+ gatherData.getIC_NO() +"</IC_NO>\n" +
+                "<IC_Type>"+ gatherData.getIC_Type() +"</IC_Type>\n" +
+                "<IC_EXTENDED_CONTENT>"+ gatherData.getIC_EXTENDED_CONTENT() +"</IC_EXTENDED_CONTENT>\n" +
+                "</IC_INFO>\t\n" +
+
+                "<FORM_INFO>\n" +
+                "<FORM_TYPE></FORM_TYPE>\n" +
+                "<FORM_ID></FORM_ID>\n" +
+                "</FORM_INFO>\n" +
+
+                "<DR_CUSTOMS_NO>"+ gatherData.getDR_CUSTOMS_NO() +"</DR_CUSTOMS_NO>\n" +
+                "<VE_CUSTOMS_NO>"+ gatherData.getVE_CUSTOMS_NO() +"</VE_CUSTOMS_NO>\n" +
+                //读取到的车牌号信息
+                "<VE_LICENSE_NO>"+ gatherData.getVE_LICENSE_NO() +"/VE_LICENSE_NO>\n" +
+                "<CONTA_ID></CONTA_ID>\n" +
+                "<CONTA_ID></CONTA_ID>\n" +
+                "<ESEAL_ID>"+ gatherData.getESEAL_ID() +"</ESEAL_ID>\n" +
+                //读取到的地磅重量
+                "<GROSS_WT>"+ gatherData.getGROSS_WT() +"</GROSS_WT>\n" +
+
+                //RFID信息
+                "<VE_RFID>\n" +
+                //RFID卡号信息
+                "<RFID_ID>"+ gatherData.getVE_RFID_ID() +"</RFID_ID>\n" +
+                //RFID绑定的光学车牌信息
+                "<VE_LICENSE_NO>"+ gatherData.getVE_LICENSE_NO_RFID() +"</VE_LICENSE_NO>\n" +
+                "<VE_CUSTOMS_NO>"+ gatherData.getVE_CUSTOMS_NO_RFID() +"</VE_CUSTOMS_NO>\n" +
+                "<VE_WT>"+ gatherData.getVE_WT_RFID() +"</VE_WT>\n" +
+                "<VE_COMPANY>"+ gatherData.getVE_COMPANY_RFID() +"</VE_COMPANY>\n" +
+                "<VE_PERFORMANCE>"+ gatherData.getVE_PERFORMANCE_RFID() +"</VE_PERFORMANCE>\n" +
+                "</VE_RFID>\t\n" +
+
+                "<DR_RFID>\n" +
+                "<RFID_ID>"+ gatherData.getDR_RFID_ID() +"</RFID_ID>\n" +
+                "<DR_NAME>"+ gatherData.getDR_NAME() +"</DR_NAME>\n" +
+                "<DR_CUSTOMS_NO>"+ gatherData.getDR_CUSTOMS_NO_RFID() +"</DR_CUSTOMS_NO>\n" +
+                "<DR_COMPANY>"+ gatherData.getDR_COMPANY() +"</DR_COMPANY>\n" +
+                "<DR_PERFORMANCE>"+ gatherData.getDR_PERFORMANCE() +"</DR_PERFORMANCE>\n" +
+                "</DR_RFID>\t\n" +
+
+                "<PHOTO>\n" +
+                "<PHOTO_GUID></PHOTO_GUID>\n" +
+                "<PHOTO_PERSPECTIVE></PHOTO_PERSPECTIVE>\n" +
+                "</PHOTO>\n" +
+
+                "<OPERATOR_ID>"+ gatherData.getOPERATOR_ID() +"</OPERATOR_ID>\n" +
+                "<OPERATE_TIME>"+ gatherData.getOPERATE_TIME() +"</OPERATE_TIME>\t\n" +
+
+                "<EXTEND>\n" +
+                "<NAME></NAME>\n" +
+                "<VALUE></VALUE>\n" +
+                "</EXTEND>\t\n" +
+                "<CONTA_RECOGNITION>\n" +
+                "<CONTA_ID></CONTA_ID>\n" +
+                "<CONTA_TYPE></CONTA_TYPE>\n" +
+                "<CONTA_F_PIC></CONTA_F_PIC>\n" +
+                "<CONTA_B_PIC></CONTA_B_PIC>\n" +
+                "<CONTA_FL_PIC></CONTA_FL_PIC>\n" +
+                "<CONTA_FR_PIC></CONTA_FR_PIC>\n" +
+                "<CONTA_BL_PIC></CONTA_BL_PIC>\n" +
+                "<CONTA_BR_PIC></CONTA_BR_PIC>\n" +
+                "<CONTA_LOCK></CONTA_LOCK>\n" +
+                "<CONFIDENCE_RATIO></CONFIDENCE_RATIO>\n" +
+                "</CONTA_RECOGNITION>\n" +
+
+                "<VE_LICENSE_RECOGNITION>\n" +
+                "<DOMESTIC_LICENSE_NO>"+ gatherData.getDOMESTIC_LICENSE_NO() +"</DOMESTIC_LICENSE_NO>\n" +
+                //车牌图片保存路径,如有箱号,有箱号图片路径
+                "<DOMESTIC_LICENSE_COLOR>"+ gatherData.getDOMESTIC_LICENSE_COLOR() +"</DOMESTIC_LICENSE_COLOR>\n" +
+                "<FOREIGN_LICENSE_NO>"+ gatherData.getFOREIGN_LICENSE_NO() +"</FOREIGN_LICENSE_NO>\n" +
+                "<FOREIGN_LICENSE_COLOR>"+ gatherData.getFOREIGN_LICENSE_COLOR() +"</FOREIGN_LICENSE_COLOR>\n" +
+                "<CONFIDENCE_RATIO>"+ gatherData.getCONFIDENCE_RATIO() +"</CONFIDENCE_RATIO>\n" +
+                "</VE_LICENSE_RECOGNITION>\n" +
+
+                "</GATHER_INFO>";
+
+        return xml;
+    }
+
+    //采集数据报文(0x21)——>海关
+    public static String gatherXml(GatherData gatherData) {
+        //会话编号: 场站号 + 通道号 + 当前时间戳后六位
+        Long timestamp = System.currentTimeMillis();
+        String timeStr = String.valueOf(timestamp);
+        String last6 = timeStr.substring(timeStr.length() - 1);
+        String SESSION_ID = gatherData.getAREA_ID() + gatherData.getCHNL_NO() + last6;
+
+        String xml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n" +
+                "<GATHER_INFO>\n" +
+                //进出标识,I进E出
+                "<I_E_FLAG>"+ gatherData.getI_E_FLAG() +"</I_E_FLAG>\n" +
+                //场站号,固定写为场所分配的场站号
+                "<AREA_ID>"+ gatherData.getAREA_ID() +"</AREA_ID>\n" +
+                //通道号,已经从通道号配置文件中读取
+                "<CHNL_NO>"+ gatherData.getCHNL_NO() +"</CHNL_NO>\n" +
+                //通道业务类型,通常为A,已经读取
+                "<GETHER_MODE>"+ gatherData.getGETHER_MODE() +"</GETHER_MODE>\n" +
+                //会话编号,自动生成
+                "<SESSION_ID>"+ SESSION_ID +"</SESSION_ID>\n" +
+
+                //IC卡配置信息
+                "<IC_INFO>\n" +
+                //读取到的IC卡号
+                "<IC_ID>"+ gatherData.getIC_ID() +"</IC_ID>\n" +
+                "<IC_GOODS_NAME>"+ gatherData.getIC_GOODS_NAME() +"</IC_GOODS_NAME>\n" +
+                "<IC_BILL_NO>"+ gatherData.getIC_BILL_NO() +"</IC_BILL_NO>\n" +
+                "<IC_NO>"+ gatherData.getIC_NO() +"</IC_NO>\n" +
+                "<IC_Type>"+ gatherData.getIC_Type() +"</IC_Type>\n" +
+                "<IC_EXTENDED_CONTENT>"+ gatherData.getIC_EXTENDED_CONTENT() +"</IC_EXTENDED_CONTENT>\n" +
+                "</IC_INFO>\t\n" +
+
+                "<DR_CUSTOMS_NO>"+ gatherData.getDR_CUSTOMS_NO() +"</DR_CUSTOMS_NO>\n" +
+                "<VE_CUSTOMS_NO>"+ gatherData.getVE_CUSTOMS_NO() +"</VE_CUSTOMS_NO>\n" +
+                //读取到的车牌号信息
+                "<VE_LICENSE_NO>"+ gatherData.getVE_LICENSE_NO() +"/VE_LICENSE_NO>\n" +
+                "<ESEAL_ID>"+ gatherData.getESEAL_ID() +"</ESEAL_ID>\n" +
+                //读取到的地磅重量
+                "<GROSS_WT>"+ gatherData.getGROSS_WT() +"</GROSS_WT>\n" +
+
+                //RFID信息
+                "<VE_RFID>\n" +
+                //RFID卡号信息
+                "<RFID_ID>"+ gatherData.getVE_RFID_ID() +"</RFID_ID>\n" +
+                //RFID绑定的光学车牌信息
+                "<VE_LICENSE_NO>"+ gatherData.getVE_LICENSE_NO_RFID() +"</VE_LICENSE_NO>\n" +
+                "<VE_CUSTOMS_NO>"+ gatherData.getVE_CUSTOMS_NO_RFID() +"</VE_CUSTOMS_NO>\n" +
+                "<VE_WT>"+ gatherData.getVE_WT_RFID() +"</VE_WT>\n" +
+                "<VE_COMPANY>"+ gatherData.getVE_COMPANY_RFID() +"</VE_COMPANY>\n" +
+                "<VE_PERFORMANCE>"+ gatherData.getVE_PERFORMANCE_RFID() +"</VE_PERFORMANCE>\n" +
+                "</VE_RFID>\t\n" +
+
+                "<DR_RFID>\n" +
+                "<RFID_ID>"+ gatherData.getDR_RFID_ID() +"</RFID_ID>\n" +
+                "<DR_NAME>"+ gatherData.getDR_NAME() +"</DR_NAME>\n" +
+                "<DR_CUSTOMS_NO>"+ gatherData.getDR_CUSTOMS_NO_RFID() +"</DR_CUSTOMS_NO>\n" +
+                "<DR_COMPANY>"+ gatherData.getDR_COMPANY() +"</DR_COMPANY>\n" +
+                "<DR_PERFORMANCE>"+ gatherData.getDR_PERFORMANCE() +"</DR_PERFORMANCE>\n" +
+                "</DR_RFID>\t\n" +
+
+                "<OPERATOR_ID>"+ gatherData.getOPERATOR_ID() +"</OPERATOR_ID>\n" +
+                "<OPERATE_TIME>"+ gatherData.getOPERATE_TIME() +"</OPERATE_TIME>\t\n" +
+
+                "<VE_LICENSE_RECOGNITION>\n" +
+                "<DOMESTIC_LICENSE_NO>"+ gatherData.getDOMESTIC_LICENSE_NO() +"</DOMESTIC_LICENSE_NO>\n" +
+                //车牌图片保存路径,如有箱号,有箱号图片路径
+                "<DOMESTIC_LICENSE_COLOR>"+ gatherData.getDOMESTIC_LICENSE_COLOR() +"</DOMESTIC_LICENSE_COLOR>\n" +
+                "<FOREIGN_LICENSE_NO>"+ gatherData.getFOREIGN_LICENSE_NO() +"</FOREIGN_LICENSE_NO>\n" +
+                "<FOREIGN_LICENSE_COLOR>"+ gatherData.getFOREIGN_LICENSE_COLOR() +"</FOREIGN_LICENSE_COLOR>\n" +
+                "<CONFIDENCE_RATIO>"+ gatherData.getCONFIDENCE_RATIO() +"</CONFIDENCE_RATIO>\n" +
+                "</VE_LICENSE_RECOGNITION>\n" +
+
+                "</GATHER_INFO>";
+
+        return xml;
+    }
+
+
+    //放行结果(0x32)——>海关
+    public static String ReleaseResXml(Release release) {
+        //报文序列号:YYYYMMDDHHmmSS+序号(6位)
+        int random = (int) (Math.random() * 1000000);
+        String num = String.format("%06d", random);
+        String SEQ_NO = DateUtils.dateTimeNow() + num;
+
+        String xml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n" +
+                "<COMMAND_INFO>\n" +
+
+                //进出标识,I进E出
+                "<I_E_TYPE>" + release.getI_E_FLAG() + "</I_E_TYPE>\n" +
+                //场站号,固定写为场所分配的场站号
+                "<AREA_ID>" + release.getAREA_ID() + "</AREA_ID>\n" +
+                //通道号,已经从通道号配置文件中读取
+                "<CHNL_NO>" + release.getCHNL_NO() + "</CHNL_NO>\n" +
+                //报文序列号
+                "<SEQ_NO>"+ SEQ_NO +"</SEQ_NO>\n" +
+                //返回结果
+                " <EXECUTE_RESULT>放行</EXECUTE_RESULT>\n" +
+                //计数器值
+                "< ESEAL_USE_COUNT>" + release.getESEAL_USE_COUNT() + "</ ESEAL_USE_COUNT>\n" +
+
+                "</COMMAND_INFO>";
+
+        return xml;
+    }
+}

+ 245 - 0
src/main/java/com/ruoyi/web/config/RestUtil.java

@@ -0,0 +1,245 @@
+package com.ruoyi.web.config;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.*;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * 调用 Restful 接口 Util
+ *
+ * @author sunjianlei
+ */
+@Slf4j
+public class RestUtil {
+
+    /**
+     * RestAPI 调用器
+     */
+    private final static RestTemplate RT;
+
+    static {
+        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
+        requestFactory.setConnectTimeout(5000);
+        requestFactory.setReadTimeout(10000);
+        RT = new RestTemplate(requestFactory);
+        // 解决乱码问题
+        RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+    }
+
+    public static RestTemplate getRestTemplate() {
+        return RT;
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url, HttpHeaders headers) {
+        log.info("req get :{},{}",url,JSONObject.toJSONString(headers));
+        return getNative(url, headers, null, null).getBody();
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url, HttpHeaders headers, JSONObject variables) {
+        return getNative(url, headers, variables, null).getBody();
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url, HttpHeaders headers, JSONObject variables, JSONObject params) {
+        return getNative(url, headers, variables, params).getBody();
+    }
+
+    /**
+     * 发送 get 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> getNative(String url, HttpHeaders headers, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.GET, headers, variables, params);
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url, HttpHeaders headers) {
+        log.info("req post:{},{}",url,JSONObject.toJSONString(headers));
+        return postNative(url, headers, null, null).getBody();
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url, HttpHeaders headers, JSONObject params) {
+        log.info("req post:{},{},{}",url,JSONObject.toJSONString(headers),params.toJSONString());
+        return postNative(url, headers, null, params).getBody();
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url, HttpHeaders headers, JSONObject variables, JSONObject params) {
+        return postNative(url, headers, variables, params).getBody();
+    }
+
+    /**
+     * 发送 POST 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> postNative(String url, HttpHeaders headers, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.POST, headers, variables, params);
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url) {
+        return putNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url, JSONObject params) {
+        return putNative(url, null, params).getBody();
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url, JSONObject variables, JSONObject params) {
+        return putNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 put 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> putNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.PUT, getHeaderApplicationJson(), variables, params);
+    }
+
+    /**
+     * 发送 delete 请求
+     */
+    public static JSONObject delete(String url) {
+        return deleteNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 delete 请求
+     */
+    public static JSONObject delete(String url, JSONObject variables, JSONObject params) {
+        return deleteNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 delete 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> deleteNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.DELETE, null, variables, params, JSONObject.class);
+    }
+
+    /**
+     * 发送请求
+     */
+    public static ResponseEntity<JSONObject> request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, JSONObject params) {
+        return request(url, method, headers, variables, params, JSONObject.class);
+    }
+
+    /**
+     * 发送请求
+     *
+     * @param url          请求地址
+     * @param method       请求方式
+     * @param headers      请求头  可空
+     * @param variables    请求url参数 可空
+     * @param params       请求body参数 可空
+     * @param responseType 返回类型
+     * @return ResponseEntity<responseType>
+     */
+    public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, Object params, Class<T> responseType) {
+        if (StringUtils.isEmpty(url)) {
+            throw new RuntimeException("url 不能为空");
+        }
+        if (method == null) {
+            throw new RuntimeException("method 不能为空");
+        }
+        if (headers == null) {
+            headers = new HttpHeaders();
+        }
+        // 请求体
+        String body = "";
+        if (params != null) {
+            if (params instanceof JSONObject) {
+                body = ((JSONObject) params).toJSONString();
+
+            } else {
+                body = params.toString();
+            }
+        }
+        // 拼接 url 参数
+        if (variables != null && !variables.isEmpty()) {
+            url += ("?" + asUrlVariables(variables));
+        }
+        // 发送请求
+        HttpEntity<String> request = new HttpEntity<>(body, headers);
+        return RT.exchange(url, method, request, responseType);
+    }
+
+    /**
+     * 获取JSON请求头
+     */
+    public static HttpHeaders getHeaderApplicationJson() {
+        return getHeader(MediaType.APPLICATION_JSON_VALUE);
+    }
+
+    /**
+     * 获取请求头
+     */
+    public static HttpHeaders getHeader(String mediaType) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.parseMediaType(mediaType));
+        headers.add("Accept", mediaType);
+        return headers;
+    }
+
+    /**
+     * 将 JSONObject 转为 a=1&b=2&c=3...&n=n 的形式
+     */
+    public static String asUrlVariables(JSONObject variables) {
+        Map<String, Object> source = variables;
+        Iterator<String> it = source.keySet().iterator();
+        StringBuilder urlVariables = new StringBuilder();
+        while (it.hasNext()) {
+            String key = it.next();
+            String value = "";
+            Object object = source.get(key);
+            if (object != null) {
+                if (!StringUtils.isEmpty(object.toString())) {
+                    value = object.toString();
+                }
+            }
+            urlVariables.append("&").append(key).append("=").append(value);
+        }
+        // 去掉第一个&
+        return urlVariables.substring(1);
+    }
+
+
+    public static void main(String[] args){
+
+        String str = "{\"0\":\"zhangsan\",\"1\":\"lisi\",\"2\":\"wangwu\",\"3\":\"maliu\"}";
+        String strArr = "{{\"0\":\"zhangsan\",\"1\":\"lisi\",\"2\":\"wangwu\",\"3\":\"maliu\"},{\"00\":\"zhangsan\",\"11\":\"lisi\",\"22\":\"wangwu\",\"33\":\"maliu\"}}";
+        JSONObject variables = JSONObject.parseObject(str);
+        String s = asUrlVariables(variables);
+        System.out.println("!!!!!!!!\n"+s);
+
+    }
+}

+ 484 - 0
src/main/java/com/ruoyi/web/config/UUID.java

@@ -0,0 +1,484 @@
+package com.ruoyi.web.config;
+
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 提供通用唯一识别码(universally unique identifier)(UUID)实现
+ *
+ * @author ruoyi
+ */
+public final class UUID implements java.io.Serializable, Comparable<UUID>
+{
+    private static final long serialVersionUID = -1185015143654744140L;
+
+    /**
+     * SecureRandom 的单例
+     *
+     */
+    private static class Holder
+    {
+        static final SecureRandom numberGenerator = getSecureRandom();
+    }
+
+    /** 此UUID的最高64有效位 */
+    private final long mostSigBits;
+
+    /** 此UUID的最低64有效位 */
+    private final long leastSigBits;
+
+    /**
+     * 私有构造
+     *
+     * @param data 数据
+     */
+    private UUID(byte[] data)
+    {
+        long msb = 0;
+        long lsb = 0;
+        assert data.length == 16 : "data must be 16 bytes in length";
+        for (int i = 0; i < 8; i++)
+        {
+            msb = (msb << 8) | (data[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++)
+        {
+            lsb = (lsb << 8) | (data[i] & 0xff);
+        }
+        this.mostSigBits = msb;
+        this.leastSigBits = lsb;
+    }
+
+    /**
+     * 使用指定的数据构造新的 UUID。
+     *
+     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
+     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
+     */
+    public UUID(long mostSigBits, long leastSigBits)
+    {
+        this.mostSigBits = mostSigBits;
+        this.leastSigBits = leastSigBits;
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID fastUUID()
+    {
+        return randomUUID(false);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID()
+    {
+        return randomUUID(true);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID(boolean isSecure)
+    {
+        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
+
+        byte[] randomBytes = new byte[16];
+        ng.nextBytes(randomBytes);
+        randomBytes[6] &= 0x0f; /* clear version */
+        randomBytes[6] |= 0x40; /* set to version 4 */
+        randomBytes[8] &= 0x3f; /* clear variant */
+        randomBytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(randomBytes);
+    }
+
+    /**
+     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
+     *
+     * @param name 用于构造 UUID 的字节数组。
+     *
+     * @return 根据指定数组生成的 {@code UUID}
+     */
+    public static UUID nameUUIDFromBytes(byte[] name)
+    {
+        MessageDigest md;
+        try
+        {
+            md = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException nsae)
+        {
+            throw new InternalError("MD5 not supported");
+        }
+        byte[] md5Bytes = md.digest(name);
+        md5Bytes[6] &= 0x0f; /* clear version */
+        md5Bytes[6] |= 0x30; /* set to version 3 */
+        md5Bytes[8] &= 0x3f; /* clear variant */
+        md5Bytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(md5Bytes);
+    }
+
+    /**
+     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
+     *
+     * @param name 指定 {@code UUID} 字符串
+     * @return 具有指定值的 {@code UUID}
+     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
+     *
+     */
+    public static UUID fromString(String name)
+    {
+        String[] components = name.split("-");
+        if (components.length != 5)
+        {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+        for (int i = 0; i < 5; i++)
+        {
+            components[i] = "0x" + components[i];
+        }
+
+        long mostSigBits = Long.decode(components[0]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[2]).longValue();
+
+        long leastSigBits = Long.decode(components[3]).longValue();
+        leastSigBits <<= 48;
+        leastSigBits |= Long.decode(components[4]).longValue();
+
+        return new UUID(mostSigBits, leastSigBits);
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最低有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中的最低有效 64 位。
+     */
+    public long getLeastSignificantBits()
+    {
+        return leastSigBits;
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最高有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中最高有效 64 位。
+     */
+    public long getMostSignificantBits()
+    {
+        return mostSigBits;
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
+     * <p>
+     * 版本号具有以下含意:
+     * <ul>
+     * <li>1 基于时间的 UUID
+     * <li>2 DCE 安全 UUID
+     * <li>3 基于名称的 UUID
+     * <li>4 随机生成的 UUID
+     * </ul>
+     *
+     * @return 此 {@code UUID} 的版本号
+     */
+    public int version()
+    {
+        // Version is bits masked by 0x000000000000F000 in MS long
+        return (int) ((mostSigBits >> 12) & 0x0f);
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
+     * <p>
+     * 变体号具有以下含意:
+     * <ul>
+     * <li>0 为 NCS 向后兼容保留
+     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
+     * <li>6 保留,微软向后兼容
+     * <li>7 保留供以后定义使用
+     * </ul>
+     *
+     * @return 此 {@code UUID} 相关联的变体号
+     */
+    public int variant()
+    {
+        // This field is composed of a varying number of bits.
+        // 0 - - Reserved for NCS backward compatibility
+        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
+        // 1 1 0 Reserved, Microsoft backward compatibility
+        // 1 1 1 Reserved for future definition.
+        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
+    }
+
+    /**
+     * 与此 UUID 相关联的时间戳值。
+     *
+     * <p>
+     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
+     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
+     *
+     * <p>
+     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
+     */
+    public long timestamp() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (mostSigBits & 0x0FFFL) << 48//
+                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
+                | mostSigBits >>> 32;
+    }
+
+    /**
+     * 与此 UUID 相关联的时钟序列值。
+     *
+     * <p>
+     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
+     * <p>
+     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
+     * UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的时钟序列
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public int clockSequence() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+    }
+
+    /**
+     * 与此 UUID 相关的节点值。
+     *
+     * <p>
+     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
+     * <p>
+     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的节点值
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public long node() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return leastSigBits & 0x0000FFFFFFFFFFFFL;
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @return 此{@code UUID} 的字符串表现形式
+     * @see #toString(boolean)
+     */
+    @Override
+    public String toString()
+    {
+        return toString(false);
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
+     * @return 此{@code UUID} 的字符串表现形式
+     */
+    public String toString(boolean isSimple)
+    {
+        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
+        // time_low
+        builder.append(digits(mostSigBits >> 32, 8));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_mid
+        builder.append(digits(mostSigBits >> 16, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_high_and_version
+        builder.append(digits(mostSigBits, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // variant_and_sequence
+        builder.append(digits(leastSigBits >> 48, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // node
+        builder.append(digits(leastSigBits, 12));
+
+        return builder.toString();
+    }
+
+    /**
+     * 返回此 UUID 的哈希码。
+     *
+     * @return UUID 的哈希码值。
+     */
+    @Override
+    public int hashCode()
+    {
+        long hilo = mostSigBits ^ leastSigBits;
+        return ((int) (hilo >> 32)) ^ (int) hilo;
+    }
+
+    /**
+     * 将此对象与指定对象比较。
+     * <p>
+     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
+     *
+     * @param obj 要与之比较的对象
+     *
+     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        if ((null == obj) || (obj.getClass() != UUID.class))
+        {
+            return false;
+        }
+        UUID id = (UUID) obj;
+        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
+    }
+
+    // Comparison Operations
+
+    /**
+     * 将此 UUID 与指定的 UUID 比较。
+     *
+     * <p>
+     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
+     *
+     * @param val 与此 UUID 比较的 UUID
+     *
+     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
+     *
+     */
+    @Override
+    public int compareTo(UUID val)
+    {
+        // The ordering is intentionally set up so that the UUIDs
+        // can simply be numerically compared as two numbers
+        return (this.mostSigBits < val.mostSigBits ? -1 : //
+                (this.mostSigBits > val.mostSigBits ? 1 : //
+                        (this.leastSigBits < val.leastSigBits ? -1 : //
+                                (this.leastSigBits > val.leastSigBits ? 1 : //
+                                        0))));
+    }
+
+    // -------------------------------------------------------------------------------------------------------------------
+    // Private method start
+    /**
+     * 返回指定数字对应的hex值
+     *
+     * @param val 值
+     * @param digits 位
+     * @return 值
+     */
+    private static String digits(long val, int digits)
+    {
+        long hi = 1L << (digits * 4);
+        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+    }
+
+    /**
+     * 检查是否为time-based版本UUID
+     */
+    private void checkTimeBase()
+    {
+        if (version() != 1)
+        {
+            throw new UnsupportedOperationException("Not a time-based UUID");
+        }
+    }
+
+    /**
+     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
+     *
+     * @return {@link SecureRandom}
+     */
+    public static SecureRandom getSecureRandom()
+    {
+        try
+        {
+            return SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 获取随机数生成器对象<br>
+     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
+     *
+     * @return {@link ThreadLocalRandom}
+     */
+    public static ThreadLocalRandom getRandom()
+    {
+        return ThreadLocalRandom.current();
+    }
+}

+ 18 - 0
src/main/java/com/ruoyi/web/config/WebServiceConfig.java

@@ -0,0 +1,18 @@
+package com.ruoyi.web.config;
+
+import cn.hutool.http.webservice.SoapClient;
+
+public class WebServiceConfig {
+
+    public static SoapClient loadClient(String url, String methodName, String namespaceURI) {
+
+        SoapClient client = SoapClient.create(url)
+                // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                .setMethod(methodName, namespaceURI)
+                // 设置参数,此处自动添加方法的前缀:web
+                .setParam("theIpAddress", "218.21.240.106");
+
+
+        return client;
+    }
+}

+ 328 - 0
src/main/java/com/ruoyi/web/controller/ProcessController.java

@@ -0,0 +1,328 @@
+package com.ruoyi.web.controller;
+
+import cn.hutool.core.util.XmlUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.webservice.SoapClient;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.bo.GatherBo;
+import com.ruoyi.web.bo.ReleaseBo;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.bo.Steps;
+import com.ruoyi.web.config.AesEncryptUtil;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.Release;
+import com.ruoyi.web.service.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+@RestController
+@RequestMapping("/interaction")
+@Slf4j
+public class ProcessController {
+
+    @Autowired
+    private ProcessService processService;
+    @Autowired
+    private CustomsService customsService;
+    @Autowired
+    private ReservationService reservationService;
+    @Autowired
+    private BulkloadService bulkloadService;
+
+    //@GetMapping("/LogOn")
+    public ResponseBo LogOn(String a,double b, String c) {
+        GatherData gatherData = new GatherData();
+        /*gatherData.setIsEmpty(bo.getIsEmpty());
+        gatherData.setIsLoaded(bo.getIsLoaded());
+
+        gatherData.setCrossCode(bo.getCrossCode());
+        gatherData.setCntrnoF(bo.getCntrnoF());
+        gatherData.setCntrnoA(bo.getCntrnoA());*/
+
+        //进出标识,I进E出
+        gatherData.setI_E_FLAG(c);
+        //场站号,固定写为场所分配的场站号
+        /*gatherData.setAREA_ID(bo.getAreaId());
+        //通道号,已经从通道号配置文件中读取
+        gatherData.setCHNL_NO(bo.getChnlNo());
+        //通道业务类型,通常为A,已经读取
+        gatherData.setGETHER_MODE(bo.getGetherMode());*/
+        //会话编号,自动生成
+        //gatherData.setSESSION_ID(bo.getSessionId());
+
+        //IC卡配置信息
+        //读取到的IC卡号
+        /*gatherData.setIC_ID(bo.getIcId());
+        gatherData.setIC_GOODS_NAME(bo.getIcGoodsName());
+        gatherData.setIC_BILL_NO(bo.getIcBillNo());
+        gatherData.setIC_NO(bo.getIcNo());
+        gatherData.setIC_Type(bo.getIcType());
+        gatherData.setIC_EXTENDED_CONTENT(bo.getIcExtendedContent());
+
+        gatherData.setDR_CUSTOMS_NO(bo.getDrCustomsNo());
+        gatherData.setVE_CUSTOMS_NO(bo.getVeCustomsNo());*/
+        //读取到的车牌号信息
+        gatherData.setVE_LICENSE_NO(a);
+        //gatherData.setESEAL_ID(bo.getEsealId());
+        //读取到的地磅重量
+        gatherData.setGROSS_WT(b);
+
+        //VE_RFID信息
+        //RFID卡号信息
+        //gatherData.setVE_RFID_ID(bo.getVeRfidId());
+        //RFID绑定的光学车牌信息
+        /*gatherData.setVE_LICENSE_NO_RFID(bo.getVeLicenseNoRfid());
+        gatherData.setVE_CUSTOMS_NO_RFID(bo.getVeCustomsNoRfid());
+        gatherData.setVE_WT_RFID(bo.getVeWtRfid());
+        gatherData.setVE_COMPANY_RFID(bo.getVeCompanyRfid());
+        gatherData.setVE_PERFORMANCE_RFID(bo.getVePerformanceRfid());
+
+        //DR_RFID信息
+        gatherData.setDR_RFID_ID(bo.getDrRfidId());
+        gatherData.setDR_NAME(bo.getDrName());
+        gatherData.setDR_CUSTOMS_NO_RFID(bo.getDrCustomsNoRfid());
+        gatherData.setDR_COMPANY(bo.getDrCompany());
+        gatherData.setDR_PERFORMANCE(bo.getDrPerformance());
+
+        gatherData.setOPERATOR_ID(bo.getOperatorId());
+        gatherData.setOPERATE_TIME(bo.getOperateTime());
+
+
+        gatherData.setDOMESTIC_LICENSE_NO(bo.getDomesticLicenseNo());
+        //车牌图片保存路径,如有箱号,有箱号图片路径
+        gatherData.setDOMESTIC_LICENSE_COLOR(bo.getDomesticLicenseColor());
+        gatherData.setFOREIGN_LICENSE_NO(bo.getForeignLicenseNo());
+        gatherData.setFOREIGN_LICENSE_COLOR(bo.getForeignLicenseColor());
+        gatherData.setCONFIDENCE_RATIO(bo.getConfidenceRatio());*/
+        ResponseBo res = bulkloadService.LogOn(gatherData);
+        return res;
+    }
+
+    //@GetMapping("/process_test")
+    public ResponseBo process_test() {
+        Steps steps = new Steps();
+        steps.setName("集装箱");
+        steps.setApi("GetBookingInfo");
+        steps.setRemark("");
+        List<Steps> stepsList = new ArrayList<>();
+        stepsList.add(steps);
+
+        GatherData gatherData = new GatherData();
+        gatherData.setSteps(stepsList);
+
+        gatherData.setJsStatus(0);
+        gatherData.setContainerStatus(0);
+
+        gatherData.setIsEmpty(0);
+        gatherData.setIsLoaded(0);
+
+        //gatherData.setCrossCode(bo.getCrossCode());
+        gatherData.setCntrnoF("TEST2024011");
+        gatherData.setCntrnoFType("22G1");
+        gatherData.setCntrnoA("TEST2024012");
+        gatherData.setCntrnoAType("22G1");
+
+        //进出标识,I进E出
+        gatherData.setI_E_FLAG("I");
+
+        //读取到的车牌号信息
+        gatherData.setVE_LICENSE_NO("桂A12345");
+        //读取到的地磅重量——>KG转换为吨
+        BigDecimal grossWt = new BigDecimal(2600);
+        grossWt = grossWt.divide(BigDecimal.valueOf(1000),2, RoundingMode.HALF_UP);
+        gatherData.setGROSS_WT(grossWt.doubleValue());
+
+        return processService.process(gatherData);
+    }
+
+    public static void main(String[] args) {
+        String content = "C59EDA96487C750AEE056B41FE888FDFB228F42ECCDEE7C288E1B6A84405F9F2AF3505556F25D15BABAAD97631A0DE536CC9D5E02F94C2FFFB754BC12F0D2F75C556994960087433D3D8F358394C20D5B3E3636C423CBED72657A55500EBBF944E9DD496E294EB9555C71D29C1967C5F56411DCD5D2CE7924C54265C548B46C15E512B9A95442578CAC1C71A31DEC443229EFF20DBCAB63CF40C30E59E712DB59948BFF5AF3E8C01177AA07BF0929FF6887DF0103EFCE8CE71268E3E4E3113C71E08C9B6DD84E4AA7FDBCAD73B6C9EE0EBA004FB14286D032BB9C8D5D0D81C555C97A443F0FDF635B28AEF5BE28ECEBE6AF0DCB4770B66F4C4ED6149800E9400ED6D89A4C09025497606449659BF81C8D133DDD6B2A0FEB92617C96C1BC558C069617F200977E0B4FCAA7E25574711CB";
+        String decrypt = AesEncryptUtil.AESDecrypt(content);
+        System.out.println(decrypt);
+    }
+    @PostMapping("/process")
+    public ResponseBo process(@RequestBody String content) {
+        log.info("---------------开始---------------");
+        String decrypt = AesEncryptUtil.AESDecrypt(content);
+        GatherBo bo = JSONObject.parseObject(decrypt, GatherBo.class);
+        log.info("GatherBo:{}", JSONUtil.toJsonStr(bo));
+
+
+        GatherData gatherData = new GatherData();
+        gatherData.setSteps(bo.getSteps());
+
+        gatherData.setJsStatus(bo.getJsStatus());
+        gatherData.setContainerStatus(bo.getContainerStatus());
+
+        gatherData.setIsEmpty(bo.getIsEmpty());
+        gatherData.setIsLoaded(bo.getIsLoaded());
+
+        gatherData.setCrossCode(bo.getCrossCode());
+        gatherData.setCntrnoF(bo.getCntrnoF());
+        gatherData.setCntrnoA(bo.getCntrnoA());
+        gatherData.setCntrnoFType(bo.getCntrnoFType());
+        gatherData.setCntrnoAType(bo.getCntrnoAType());
+
+
+        //进出标识,I进E出
+        gatherData.setI_E_FLAG(bo.getIeFlag());
+        //场站号,固定写为场所分配的场站号
+        gatherData.setAREA_ID(bo.getAreaId());
+        //通道号,已经从通道号配置文件中读取
+        gatherData.setCHNL_NO(bo.getChnlNo());
+        //通道业务类型,通常为A,已经读取
+        gatherData.setGETHER_MODE(bo.getGetherMode());
+        //会话编号,自动生成
+        //gatherData.setSESSION_ID(bo.getSessionId());
+
+        //IC卡配置信息
+        //读取到的IC卡号
+        gatherData.setIC_ID(bo.getIcId());
+        gatherData.setIC_GOODS_NAME(bo.getIcGoodsName());
+        gatherData.setIC_BILL_NO(bo.getIcBillNo());
+        gatherData.setIC_NO(bo.getIcNo());
+        gatherData.setIC_Type(bo.getIcType());
+        gatherData.setIC_EXTENDED_CONTENT(bo.getIcExtendedContent());
+
+        gatherData.setDR_CUSTOMS_NO(bo.getDrCustomsNo());
+        gatherData.setVE_CUSTOMS_NO(bo.getVeCustomsNo());
+        //读取到的车牌号信息
+        gatherData.setVE_LICENSE_NO(bo.getVeLicenseNo());
+        gatherData.setESEAL_ID(bo.getEsealId());
+        //读取到的地磅重量——>KG转换为吨在散货service方法中处理,此处不做处理
+        gatherData.setGROSS_WT(bo.getGrossWt());
+
+        //VE_RFID信息
+        //RFID卡号信息
+        gatherData.setVE_RFID_ID(bo.getVeRfidId());
+        //RFID绑定的光学车牌信息
+        gatherData.setVE_LICENSE_NO_RFID(bo.getVeLicenseNoRfid());
+        gatherData.setVE_CUSTOMS_NO_RFID(bo.getVeCustomsNoRfid());
+        gatherData.setVE_WT_RFID(bo.getVeWtRfid());
+        gatherData.setVE_COMPANY_RFID(bo.getVeCompanyRfid());
+        gatherData.setVE_PERFORMANCE_RFID(bo.getVePerformanceRfid());
+
+        //DR_RFID信息
+        gatherData.setDR_RFID_ID(bo.getDrRfidId());
+        gatherData.setDR_NAME(bo.getDrName());
+        gatherData.setDR_CUSTOMS_NO_RFID(bo.getDrCustomsNoRfid());
+        gatherData.setDR_COMPANY(bo.getDrCompany());
+        gatherData.setDR_PERFORMANCE(bo.getDrPerformance());
+
+        gatherData.setOPERATOR_ID(bo.getOperatorId());
+        gatherData.setOPERATE_TIME(bo.getOperateTime());
+
+
+        gatherData.setDOMESTIC_LICENSE_NO(bo.getDomesticLicenseNo());
+        //车牌图片保存路径,如有箱号,有箱号图片路径
+        gatherData.setDOMESTIC_LICENSE_COLOR(bo.getDomesticLicenseColor());
+        gatherData.setFOREIGN_LICENSE_NO(bo.getForeignLicenseNo());
+        gatherData.setFOREIGN_LICENSE_COLOR(bo.getForeignLicenseColor());
+        gatherData.setCONFIDENCE_RATIO(bo.getConfidenceRatio());
+        gatherData.setGateNo(bo.getGateNo());
+        gatherData.setPlaceCode(bo.getPlaceCode());
+        gatherData.setIsSendPlaceCode(bo.getIsSendPlaceCode());
+
+        return processService.process(gatherData);
+    }
+
+    @PostMapping("/sendRelease")
+    public ResponseBo sendRelease(@RequestBody String content) {
+        String decrypt = AesEncryptUtil.AESDecrypt(content);
+        ReleaseBo bo = JSONObject.parseObject(decrypt, ReleaseBo.class);
+
+        Release release = new Release();
+        release.setI_E_FLAG(bo.getIeFlag());
+        release.setAREA_ID(bo.getAreaId());
+        release.setCHNL_NO(bo.getChnlNo());
+        release.setESEAL_USE_COUNT(bo.getEsealUseCount());
+        return customsService.sendReleaseData(release);
+    }
+
+    @PostMapping("/reservation")
+    public ResponseBo reservation(@RequestBody String content) {
+        String veLicenseNo = AesEncryptUtil.AESDecrypt(content);
+
+        ResponseBo res = reservationService.reservation(veLicenseNo);
+        return res;
+    }
+
+    //@GetMapping("/GetBookingInfo")
+    public String GetBookingInfo(String veLicenseNo) {
+        /*SoapClient client = SoapClient.create("http://10.26.1.34:8531/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                .header("SOAPAction","http://tempuri.org/ISmartGate/GetBookingInfo")
+                .header("Content-Type","text/xml; charset=utf-8")
+                // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                .setMethod("tem:GetBookingInfo","http://tempuri.org/")
+                // 设置参数,此处自动添加方法的前缀:web
+                .setParam("trkno", veLicenseNo,false)
+                .setParam("msg","",false);
+        System.out.println(client.getMsgStr(true));
+        HttpResponse response= client.sendForResponse();
+        String body = response.body();
+        String xml = client.send(true);
+
+        Document document= XmlUtil.parseXml(body);
+        Element rootElement = XmlUtil.getRootElement(document);
+        Element element1=XmlUtil.getElement(rootElement,"s:Body");
+        Element element2=XmlUtil.getElement(element1,"GetBookingInfoResponse");
+        Node getBookingInfoResult = XmlUtil.getElement(element2, "GetBookingInfoResult").getFirstChild();
+        String nodeValue1 = getBookingInfoResult.getNodeValue();
+        Node msg = XmlUtil.getElement(element2, "msg").getFirstChild();
+        String nodeValue2 = msg.getNodeValue();*/
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = "I3";
+        //车牌号
+        String i_trkno = veLicenseNo;
+        //提箱/进箱标志 	提箱 1;进箱 2
+        String I_GAT_DRFG = "1";
+        //卡车自重
+        Integer I_WEIGHT = 2600;
+        //IC卡ID号,填写固定值999999
+        String I_ICID = "999999";
+        //IC卡类型,填写固定值9
+        String I_ICTYPE = "9";
+
+        SoapClient client = SoapClient.create("http://10.26.1.34:8531/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                .header("SOAPAction","http://tempuri.org/ISmartGate/GET_CARDS")
+                .header("Content-Type","text/xml; charset=utf-8")
+                // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                .setMethod("tem:GET_CARDS","http://tempuri.org/")
+                // 设置参数,此处自动添加方法的前缀:web
+                .setParam("I_GAT_GATENO", "I3")
+                .setParam("i_trkno", "桂AF92120")
+                .setParam("I_CNT_VAL", "36773461");
+        log.info("获取进场小票信息接口:\n{}",client.getMsgStr(true));
+        HttpResponse response= client.sendForResponse();
+        String body = response.body();
+        log.info("获取进场小票信息接口输出报文:\n{}",body);
+
+        Document document= XmlUtil.parseXml(body);
+        Element rootElement = XmlUtil.getRootElement(document);
+        Element element1=XmlUtil.getElement(rootElement,"s:Body");
+        Element element2=XmlUtil.getElement(element1,"GET_CARDSResponse");
+        Node node1 = XmlUtil.getElement(element2, "GET_CARDSResult").getFirstChild();
+        String result = node1 != null ? node1.getNodeValue() : "";
+        log.info("进场小票:\n{}",result);
+        /*System.out.println("body:"+body);
+        System.out.println("result:"+result);*/
+        //log.info("xml:{}",xml);
+        return body;
+    }
+
+}

+ 16 - 0
src/main/java/com/ruoyi/web/entity/Base.java

@@ -0,0 +1,16 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 基础返回结果对象
+ */
+@Data
+public class Base {
+
+    private Integer code;
+
+    private String msg;
+
+    private String data;
+}

+ 19 - 0
src/main/java/com/ruoyi/web/entity/Customs.java

@@ -0,0 +1,19 @@
+package com.ruoyi.web.entity;
+
+import com.ruoyi.web.config.IdUtils;
+import lombok.Data;
+
+/**
+ * 发送海关的通讯实体
+ */
+@Data
+public class Customs {
+    /// 协议类型 ProtocolType 取值参考,  Bayonet1_1, Bayonet1_2, Bayonet1_3, Bayonet1_4
+    public String ProtocolType;
+
+    /// 对应协议的xml字符串, 1.1, 1.2, 1.3 报文照旧, 1.4 报文暂同 1.3
+    public String Content;
+
+    /// 未加密的随机生成的guid
+    public String Guid = IdUtils.simpleUUID();
+}

+ 47 - 0
src/main/java/com/ruoyi/web/entity/CustomsRes.java

@@ -0,0 +1,47 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 海关返回的放行结果(0x31)
+ */
+@Data
+public class CustomsRes {
+
+    //报文序列号
+    private String SEQ_NO;
+    //进出标识,I进E出
+    private String I_E_FLAG;
+    //场站号,固定写为场所分配的场站号
+    private String AREA_ID;
+    //通道号,已经从通道号配置文件中读取
+    private String CHNL_NO;
+
+    //返回结果
+    private String EXCUTE_COMMAND;
+
+    //GPS
+    //海关车辆编号
+    private String VE_NAME;
+    //GPS ID号
+    private String GPS_ID;
+    //启运地海关编码
+    private String ORIGIN_CUSTOMS;
+    //指运地海关编码
+    private String DEST_CUSTOMS;
+
+    //SEAL
+    //安全智能锁号码
+    private String ESEAL_ID;
+    //安全智能锁密钥
+    private String SEAL_KEY;
+
+    //操作类型
+    private String OP_TYPE;
+    //抬杠原因
+    private String OP_REASON;
+    //操作人代码
+    private String OP_ID;
+
+
+}

+ 106 - 0
src/main/java/com/ruoyi/web/entity/GatherData.java

@@ -0,0 +1,106 @@
+package com.ruoyi.web.entity;
+
+import com.ruoyi.web.bo.Steps;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 采集数据(0x21)
+ */
+@Data
+public class GatherData {
+
+    private List<Steps> steps;
+
+    private Set<String> cntrnoList;
+
+    //集装箱或散货接口调用状态:0=默认值 1=通过
+    private Integer jsStatus;
+    //海关接口调用状态:0=默认值 1=通过
+    private Integer containerStatus;
+
+    //0=不验空箱,1=需验空箱
+    private Integer isEmpty;
+    //0=不验重箱,1=需验重箱
+    private Integer isLoaded;
+
+    //道口号
+    private String crossCode;
+    //前箱号
+    private String cntrnoF;
+    //前箱的 UNISO 代码
+    private String cntrnoFType;
+    //后箱号
+    private String cntrnoA;
+    //后箱的UNISO 代码
+    private String cntrnoAType;
+
+    //进出标识,I进E出
+    private String I_E_FLAG;
+    //场站号,固定写为场所分配的场站号
+    private String AREA_ID;
+    //通道号,已经从通道号配置文件中读取
+    private String CHNL_NO;
+    //通道业务类型,通常为A,已经读取
+    private String GETHER_MODE;
+    //会话编号,自动生成
+    private String SESSION_ID;
+
+    //IC卡配置信息
+    //读取到的IC卡号
+    private String IC_ID;
+    private String IC_GOODS_NAME;
+    private String IC_BILL_NO;
+    private String IC_NO;
+    private String IC_Type;
+    private String IC_EXTENDED_CONTENT;
+
+    private String DR_CUSTOMS_NO;
+    private String VE_CUSTOMS_NO;
+    //读取到的车牌号信息
+    private String VE_LICENSE_NO;
+    private String ESEAL_ID;
+    //读取到的地磅重量
+    private double GROSS_WT;
+
+    //VE_RFID信息
+    //RFID卡号信息
+    private String VE_RFID_ID;
+    //RFID绑定的光学车牌信息
+    private String VE_LICENSE_NO_RFID;
+    private String VE_CUSTOMS_NO_RFID;
+    private String VE_WT_RFID;
+    private String VE_COMPANY_RFID;
+    private String VE_PERFORMANCE_RFID;
+
+    //DR_RFID信息
+    private String DR_RFID_ID;
+    private String DR_NAME;
+    private String DR_CUSTOMS_NO_RFID;
+    private String DR_COMPANY;
+    private String DR_PERFORMANCE;
+
+    private String OPERATOR_ID;
+    private String OPERATE_TIME;
+
+    //车牌图片保存路径,如有箱号,有箱号图片路径
+    private String DOMESTIC_LICENSE_NO;
+    private String DOMESTIC_LICENSE_COLOR;
+    private String FOREIGN_LICENSE_NO;
+    private String FOREIGN_LICENSE_COLOR;
+    private String CONFIDENCE_RATIO;
+    /**
+     * 散货过磅地点
+     */
+    private String placeCode;
+    /**
+     * 散货道口号
+     */
+    private String gateNo;
+    /**
+     * 接口是否发送——散货过磅地点:0=不发送,1=发送
+     */
+    private Integer isSendPlaceCode;
+}

+ 17 - 0
src/main/java/com/ruoyi/web/entity/GatherFeedBack.java

@@ -0,0 +1,17 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+@Data
+public class GatherFeedBack {
+
+    private String AREA_ID;
+    private String CHNL_NO;
+    private String SESSION_ID;
+    //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone="GMT+8")
+    private String FEEDBACK_TIME;
+    private String CHECK_RESULT;    //N
+    private String OP_HINT;         //箱号不匹配,预约的箱号【CICU8066818】未采集到   |   箱号正确匹配
+    private String LED_HINT;        //箱号不匹配,预约的箱号【CICU8066818】未采集到   |   箱号正确匹配
+
+}

+ 26 - 0
src/main/java/com/ruoyi/web/entity/Release.java

@@ -0,0 +1,26 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 放行结果信息(车辆通过卡口后)(0x32)——>海关
+ */
+@Data
+public class Release {
+
+    //报文序列号
+    private String SEQ_NO;
+    //进出标识,I进E出
+    private String I_E_FLAG;
+    //场站号,固定写为场所分配的场站号
+    private String AREA_ID;
+    //通道号,已经从通道号配置文件中读取
+    private String CHNL_NO;
+
+    //返回结果
+    private String EXCUTE_COMMAND;
+
+    //计数器值
+    private String ESEAL_USE_COUNT;
+
+}

+ 15 - 0
src/main/java/com/ruoyi/web/entity/Reservation.java

@@ -0,0 +1,15 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 预约接口返回结果对象
+ */
+@Data
+public class Reservation {
+
+    private String carNumber;//"桂E12345",
+    private String sExamineState;//散杂货预约状态	00为没有预约,01为已有预约
+    private String jExamineState;//集装箱预约状态	00为没有预约,01为已有预约
+
+}

+ 20 - 0
src/main/java/com/ruoyi/web/entity/VerificationRes.java

@@ -0,0 +1,20 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 散货系统-预约确认接口-返回结果对象
+ * {"THRFLAG":"N","RESULT":true,"MESSAGE":""}
+ */
+@Data
+public class VerificationRes {
+
+    //校验结果 ,为 true 或 false
+    private String RESULT;
+
+    //提示信息
+    private String MESSAGE;
+
+    //第三方预约标志,如果车号为第三方预约返回Y 码头预约返回 N
+    private String THRFLAG;
+}

+ 17 - 0
src/main/java/com/ruoyi/web/entity/WeightRes.java

@@ -0,0 +1,17 @@
+package com.ruoyi.web.entity;
+
+import lombok.Data;
+
+/**
+ * 散货系统-称重接口-返回结果对象
+ */
+@Data
+public class WeightRes {
+
+    //校验结果 ,为 true 或 false
+    private String RESULT;
+
+    //预约号
+    private String RegistrationNo;
+
+}

+ 490 - 0
src/main/java/com/ruoyi/web/service/BulkloadService.java

@@ -0,0 +1,490 @@
+package com.ruoyi.web.service;
+
+import cn.hutool.crypto.digest.HMac;
+import cn.hutool.crypto.digest.HmacAlgorithm;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.RestUtil;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.VerificationRes;
+import com.ruoyi.web.entity.WeightRes;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ConcurrentReferenceHashMap;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 散货系统接口调用-业务处理
+ */
+@Service
+@Slf4j
+public class BulkloadService {
+
+  //  private static HttpHeaders headers = new HttpHeaders();
+    private final static Map<String,HttpHeaders> HEADERS_MAP=new ConcurrentReferenceHashMap<>();
+    private final static Map<String,String> THRFLAG_MAP=new ConcurrentReferenceHashMap<>();
+    @Autowired
+    private ConfigComponent configComponent;
+
+    /** 登录接口 */
+    public ResponseBo LogOn(GatherData gatherData) {
+        HttpHeaders headers = new HttpHeaders();
+        gatherData.setJsStatus(0);
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        String UserName = configComponent.getUserName();
+        String PassWord = configComponent.getPassWord();
+
+        String AuthToken = configComponent.getAuthToken();
+        /*HMac mac = new HMac(HmacAlgorithm.HmacSHA256, UserName.getBytes());
+        AuthToken = mac.digestBase64(PassWord, false);*/
+
+        //请求地址
+        String url = configComponent.getLogOnUrl();
+        //请求体
+        String body = "{\"UserName\":\""+UserName+"\",\"AuthToken\":\""+AuthToken+"\"}";
+        JSONObject params = JSONObject.parseObject(body);
+
+       // headers.clear();
+        headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_VALUE));
+        headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
+        String AccessToken = "";
+        try {
+            JSONObject result = RestUtil.post(url, headers, params);
+            AccessToken = result.getString("AccessToken");
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("登录失败!:{}",message);
+            res.setCode(500);
+            res.setMsg("登录失败!"+message);
+            return res;
+        }
+        headers.add("authorization","Bearer " + AccessToken);
+        HEADERS_MAP.put(gatherData.getVE_LICENSE_NO(),headers);
+        log.info("登陆接口AccessToken={}",AccessToken);
+        if(StringUtils.isEmpty(AccessToken)) {
+            res.setCode(500);
+            res.setMsg("登录失败!");
+            return res;
+        }
+        res.setCode(200);
+        if("I".equalsIgnoreCase(gatherData.getI_E_FLAG())) {//进港
+            res = this.InVerification(gatherData);
+        } else {//出港
+            res = this.OutWeight(gatherData,THRFLAG_MAP.get(gatherData.getVE_LICENSE_NO()));
+        }
+        return res;
+    }
+
+    /** 登出接口 */
+    public void LogOff(String carNo) {
+        //请求地址
+        String url = configComponent.getLogOffUrl();
+        try{
+          HttpHeaders headers=  HEADERS_MAP.get(carNo);
+           RestUtil.post(url, headers);
+           HEADERS_MAP.remove(carNo);
+        }catch (Exception e){
+            log.error("login off error:{}",e.getMessage());
+            HEADERS_MAP.remove(carNo);
+        }
+
+    }
+
+    /** 进港:预约确认接口 */
+    public ResponseBo InVerification(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+
+        //请求地址:接口是否发送——散货过磅地点:默认0=不发送,1=发送
+        String url = configComponent.getInVerificationUrl() + "?TruckNo=" + gatherData.getVE_LICENSE_NO();
+        if(gatherData.getIsSendPlaceCode() == 1) {
+            url = configComponent.getInVerificationUrl() + "?TruckNo=" + gatherData.getVE_LICENSE_NO() + "&PLACE_CODE=" + gatherData.getPlaceCode();
+        }
+
+        log.info("进港:预约确认接口url:{}",url);
+        VerificationRes verificationRes = new VerificationRes();
+        try {
+            HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+            JSONObject result = RestUtil.get(url, headers);
+            log.info("进港:预约确认接口返回结果:{}",result.toString());
+            verificationRes = JSONObject.parseObject(result.toString(), VerificationRes.class);
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("进港:预约确认接口报错:{}",message);
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(verificationRes.getMESSAGE());
+        if("false".equalsIgnoreCase(verificationRes.getRESULT())) {
+            res.setCode(500);
+            res.setFlag("散货无预约");
+            //退出登录
+            this.LogOff(gatherData.getVE_LICENSE_NO());
+            return res;
+        }
+
+        THRFLAG_MAP.put(gatherData.getVE_LICENSE_NO(), verificationRes.getTHRFLAG());
+        res = this.InWeight(gatherData,verificationRes.getTHRFLAG());
+        return res;
+    }
+
+    /** 出港:预约确认接口 */
+    public ResponseBo OutVerification(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+
+        //请求地址:接口是否发送——散货过磅地点:默认0=不发送,1=发送
+        String url = configComponent.getOutVerificationUrl() + "?TruckNo=" + gatherData.getVE_LICENSE_NO();
+        if(gatherData.getIsSendPlaceCode() == 1) {
+            url = configComponent.getOutVerificationUrl() + "?TruckNo=" + gatherData.getVE_LICENSE_NO() + "&PLACE_CODE=" + gatherData.getPlaceCode();
+        }
+
+        log.info("出港:预约确认接口url:{}",url);
+        VerificationRes verificationRes = new VerificationRes();
+        try {
+            HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+            JSONObject result = RestUtil.get(url, headers);
+            log.info("出港:预约确认接口返回结果:{}",result.toString());
+            verificationRes = JSONObject.parseObject(result.toString(), VerificationRes.class);
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("出港:预约确认接口报错:{}",message);
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(verificationRes.getMESSAGE());
+        if("false".equalsIgnoreCase(verificationRes.getRESULT())) {
+            res.setCode(500);
+            res.setFlag("散货无预约");
+            //退出登录
+            this.LogOff(gatherData.getVE_LICENSE_NO());
+            return res;
+        }
+        res = this.OutWeight(gatherData,verificationRes.getTHRFLAG());
+        return res;
+    }
+
+    /** 进港:卡车称重接口 */
+    public ResponseBo InWeight(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE = gatherData.getPlaceCode();
+        //过磅重量NUMBER(12,4)(必输)
+        //读取到的地磅重量——>KG转换为吨
+        BigDecimal grossWt = new BigDecimal(gatherData.getGROSS_WT());
+        grossWt = grossWt.divide(BigDecimal.valueOf(1000),2, RoundingMode.HALF_UP);
+        double WEIGHT = grossWt.doubleValue();
+        //时间戳
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        String UCINO = sdf.format(new Date());
+
+        //请求地址
+        String url = configComponent.getInWeightUrl();
+        //请求体
+        String body = "{\"TRUCK_NO\": \""+TRUCK_NO+"\", \"PLACE_CODE\": \""+PLACE_CODE+"\", \"WEIGHT\":"+WEIGHT+", \"THR_FLAG\":\""+THIRDFLAG+"\"}";
+
+        JSONObject params = JSONObject.parseObject(body);
+        log.info("进港:卡车称重接口url:{}",url);
+        log.info("进港:卡车称重接口params:{}",params);
+        try {
+            //{"MESSAGE":"一次过磅称重成功","RESULT":true}
+            HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+            JSONObject jsonObject = RestUtil.post(url, headers, params);
+            log.info("进港:卡车称重接口返回结果:{}",jsonObject);
+            if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+                res.setCode(500);
+                res.setMsg(jsonObject.getString("Message"));
+                return res;
+            }
+            /*if(jsonObject != null && jsonObject.toString().contains("RESULT")) {
+                res.setCode(200);
+                res.setMsg(jsonObject.getString("MESSAGE"));
+                return res;
+            }*/
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("进港:卡车称重接口报错:{}",message);
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+
+        res.setCode(200);
+        //res = this.GetInReport(gatherData,THIRDFLAG);
+        res.setJsStatus(1);
+        res.setMsg("散货接口通过");
+        //退出登录
+        this.LogOff(gatherData.getVE_LICENSE_NO());
+        return res;
+    }
+
+    /** 出港:卡车称重接口 */
+    public ResponseBo OutWeight(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE = gatherData.getPlaceCode();
+        //过磅重量NUMBER(12,4)(必输)
+        //读取到的地磅重量——>KG转换为吨
+        BigDecimal grossWt = new BigDecimal(gatherData.getGROSS_WT());
+        grossWt = grossWt.divide(BigDecimal.valueOf(1000),2, RoundingMode.HALF_UP);
+        double WEIGHT = grossWt.doubleValue();
+        //时间戳
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        String UCINO = sdf.format(new Date());
+
+        //请求地址
+        String url = configComponent.getOutWeightUrl();
+        //请求体
+        String body = "{\"TRUCK_NO\": \""+TRUCK_NO+"\", \"PLACE_CODE\": \""+PLACE_CODE+"\", \"WEIGHT\":"+WEIGHT+", \"THR_FLAG\":\""+THIRDFLAG+"\"}";
+
+        JSONObject params = JSONObject.parseObject(body);
+        log.info("出港:卡车称重接口url:{}",url);
+        log.info("出港:卡车称重接口params:{}",params);
+        HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+        try {
+            JSONObject jsonObject = RestUtil.post(url, headers, params);
+            log.info("出港:卡车称重接口返回结果:{}",jsonObject);
+            if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+                res.setCode(500);
+                res.setMsg(jsonObject.getString("Message"));
+                return res;
+            }
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("出港:卡车称重接口报错:{}",message);
+            if(message != null && message.contains("Read timed out;")) {
+                UCINO = sdf.format(new Date());
+                body = "{\"TRUCK_NO\": \""+TRUCK_NO+"\", \"PLACE_CODE\": \""+PLACE_CODE+"\", \"WEIGHT\":"+WEIGHT+", \"THR_FLAG\":\""+THIRDFLAG+"\"}";
+                params = JSONObject.parseObject(body);
+                try {
+                    JSONObject jsonObject2 = RestUtil.post(url, headers, params);
+                    log.info("出港2:卡车称重接口返回结果:{}",jsonObject2);
+                    if(jsonObject2 != null && jsonObject2.toString().contains("ErrorCode")) {
+                        res.setCode(500);
+                        res.setMsg(jsonObject2.getString("Message"));
+                        return res;
+                    }
+                    res.setCode(200);
+                    res = this.GetOutReport(gatherData,THIRDFLAG);
+                } catch (Exception e2) {
+                    String message2 = e2.getMessage();
+                    if(message2.contains("Message")) {
+                        message2 = message2.substring(message2.indexOf("Message\":\"")+10,message2.indexOf("\"}]"));
+                    }
+                    log.info("出港2:卡车称重接口报错:{}",message2);
+                    if(message2 != null && message2.contains("不存在过磅信息或有多条不完整记录")) {
+                        res.setCode(200);
+                        res = this.GetOutReport(gatherData,THIRDFLAG);
+                        return res;
+                    }
+                    res.setCode(500);
+                    res.setMsg(message2);
+                    return res;
+                }
+            }
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+        /*WeightRes weightRes = JSONObject.parseObject(jsonObject.toString(), WeightRes.class);
+        * res.setRegistrationNo(weightRes.getRegistrationNo());*/
+        res.setCode(200);
+        res = this.GetOutReport(gatherData,THIRDFLAG);
+        return res;
+    }
+
+    /** 进港:卡车放行报告接口 */
+    public ResponseBo GetInReport(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE =gatherData.getPlaceCode();
+        //道口号(必输)
+        String GATE_NO = gatherData.getGateNo();
+
+        //请求地址
+        String url = configComponent.getInReportUrl();
+        //请求体
+        String body = "{\"TRUCK_NO\": \""+TRUCK_NO+"\", \"PLACE_CODE\": \""+PLACE_CODE+"\", \"THR_FLAG\":\""+THIRDFLAG+"\"}";
+
+        JSONObject params = JSONObject.parseObject(body);
+        log.info("进港:卡车放行报告接口url:{}",url);
+        log.info("进港:卡车放行报告接口params:{}",params);
+        try {
+            HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+            JSONObject jsonObject = RestUtil.post(url, headers, params);
+            log.info("进港:卡车放行报告接口返回结果:{}",jsonObject);
+            if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+                res.setCode(500);
+                res.setMsg(jsonObject.getString("Message"));
+                return res;
+            }
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("进港:卡车放行报告接口报错:{}",message);
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+        res.setJsStatus(1);
+        res.setCode(200);
+        res.setMsg("散货接口通过");
+        //退出登录
+        this.LogOff(gatherData.getVE_LICENSE_NO());
+        return res;
+    }
+
+    /** 出港:卡车放行报告接口 */
+    public ResponseBo GetOutReport(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE = gatherData.getPlaceCode();
+        //道口号(必输)
+        String GATE_NO = gatherData.getGateNo();
+
+        //请求地址
+        String url = configComponent.getOutReportUrl();
+        //请求体
+        String body = "{\"TRUCK_NO\": \""+TRUCK_NO+"\", \"PLACE_CODE\": \""+PLACE_CODE+"\", \"THR_FLAG\":\""+THIRDFLAG+"\"}";
+
+        JSONObject params = JSONObject.parseObject(body);
+        log.info("出港:卡车放行报告接口url:{}",url);
+        log.info("出港:卡车放行报告接口params:{}",params);
+        try {
+            HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+            JSONObject jsonObject = RestUtil.post(url, headers, params);
+            log.info("出港:卡车放行报告接口返回结果:{}",jsonObject);
+            if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+                res.setCode(500);
+                res.setMsg(jsonObject.getString("Message"));
+                return res;
+            }
+        } catch (Exception e) {
+            String message = e.getMessage();
+            if(message.contains("Message")) {
+                message = message.substring(message.indexOf("Message\":\"")+10,message.indexOf("\"}]"));
+            }
+            log.info("出港:卡车放行报告接口报错:{}",message);
+            res.setCode(500);
+            res.setMsg(message);
+            return res;
+        }
+        res.setJsStatus(1);
+        res.setCode(200);
+        res.setMsg("散货接口通过");
+        /*ResponseBo bo = this.OutWeightReport(gatherData, THIRDFLAG);
+        res.setNotes(bo.getNotes());*/
+        //退出登录
+        this.LogOff(gatherData.getVE_LICENSE_NO());
+        THRFLAG_MAP.remove(gatherData.getVE_LICENSE_NO());
+        return res;
+    }
+
+    /** 进港:获取卡车进港称重报告(打印小票) */
+    public ResponseBo InWeightReport(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE = gatherData.getPlaceCode();
+        //道口号(必输)
+        String GATE_NO = gatherData.getGateNo();
+
+        HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+        //请求地址
+        String url = configComponent.getInWeightReportUrl() + "?TRUCK_NO="+TRUCK_NO + "&PLACE_CODE="+PLACE_CODE + "&THIRDFLAG="+THIRDFLAG + "&GATE_NO="+GATE_NO;
+        JSONObject jsonObject = RestUtil.get(url, headers);
+        if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+            res.setCode(500);
+            res.setMsg(jsonObject.getString("Message"));
+            return res;
+        }
+        res.setCode(200);
+        res.setMsg("获取进港小票成功");
+        res.setNotes(jsonObject.toJSONString());
+        return res;
+    }
+
+    /** 出港:获取卡车出港称重报告(打印小票) */
+    public ResponseBo OutWeightReport(GatherData gatherData, String THIRDFLAG) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号(必输)
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        //过磅地点(必输)
+        String PLACE_CODE = gatherData.getPlaceCode();
+        //道口号(必输)
+        String GATE_NO = gatherData.getGateNo();
+
+        //请求地址
+        String url = configComponent.getOutWeightReportUrl() + "?TRUCK_NO="+TRUCK_NO + "&PLACE_CODE="+PLACE_CODE + "&THIRDFLAG="+THIRDFLAG + "&GATE_NO="+GATE_NO;
+        HttpHeaders headers=  HEADERS_MAP.get(gatherData.getVE_LICENSE_NO());
+        JSONObject jsonObject = RestUtil.get(url, headers);
+        if(jsonObject != null && jsonObject.toString().contains("ErrorCode")) {
+            res.setCode(500);
+            res.setMsg(jsonObject.getString("Message"));
+            return res;
+        }
+        log.info("获取出港小票成功:\n{}",jsonObject.toJSONString());
+        res.setMsg("获取出港小票成功");
+        res.setNotes(jsonObject.toJSONString());
+        return res;
+    }
+
+}

+ 876 - 0
src/main/java/com/ruoyi/web/service/ContainerService.java

@@ -0,0 +1,876 @@
+package com.ruoyi.web.service;
+
+import cn.hutool.core.util.XmlUtil;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.webservice.SoapClient;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.RestUtil;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.VerificationRes;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 集装箱生产系统接口调用-业务处理
+ */
+@Service
+@Slf4j
+public class ContainerService {
+
+    private static final HttpHeaders headers = new HttpHeaders();
+    @Autowired
+    private ConfigComponent configComponent;
+
+
+    /** 获取预约信息接口 */
+    public ResponseBo GetBookingInfo(GatherData gatherData) {
+        gatherData.setJsStatus(0);
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //车牌号
+        String I_TRKNO = gatherData.getVE_LICENSE_NO();
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/GetBookingInfo")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:GetBookingInfo","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("trkno", I_TRKNO)
+                    .setParam("msg","");
+            log.info("集装箱预约信息接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("集装箱预约信息接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"GetBookingInfoResponse");
+            Node node1 = XmlUtil.getElement(element2, "GetBookingInfoResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("集装箱进场预约信息接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg(e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            if(MSG.contains("集卡无预约信息")) res.setMsg("没有预约");
+            res.setCode(500);
+            res.setFlag("集装箱无预约");
+            return res;
+        }
+
+
+        List<String> jinList = new ArrayList<>();
+        List<String> tiList = new ArrayList<>();
+        ResponseBo jinRes = new ResponseBo();
+        jinRes.setCode(200);
+        ResponseBo tiRes = new ResponseBo();
+        tiRes.setCode(200);
+
+        String[] planNoArr = MSG.split(";");
+        if(planNoArr.length <= 0) {
+            res.setCode(500);
+            res.setMsg("获取预约信息接口:预约信息为空");
+            return res;
+        }
+        Set<String> cntrnoList = new HashSet<>();
+        for (String str : planNoArr) {
+            String[] arr = str.split(":");
+            if(arr.length != 4) {
+                res.setCode(500);
+                res.setMsg("获取预约信息接口:长度不符——>"+MSG);
+                return res;
+            }
+
+            if("11".equals(arr[0]) || "12".equals(arr[0])) {//进重箱 或 进空箱
+                if(0 == gatherData.getIsEmpty()) {
+                    log.info("["+gatherData.getCHNL_NO()+"]"+"不需验空箱");
+                } else {
+                    log.info("["+gatherData.getCHNL_NO()+"]需验空箱");
+                    if("1".equals(arr[4])) {//已验
+                        log.info("验箱通过:["+arr[1]+"]已验空箱");
+                    } else {//未验
+                        log.info("未验空箱:["+arr[1]+"],请退回!!!");
+                        res.setMsg("未验空箱:["+arr[1]+"],请退回!!!");
+                        res.setCode(500);
+                        return res;
+                    }
+                }
+                if(0 == gatherData.getIsLoaded()) {
+                    log.info("["+gatherData.getCHNL_NO()+"]"+"不需验重箱");
+                } else {
+                    log.info("["+gatherData.getCHNL_NO()+"]需验重箱");
+                    if("1".equals(arr[4])) {//已验
+                        log.info("验箱通过:["+arr[1]+"]已验重箱");
+                    } else {//未验
+                        log.info("未验重箱,请退回!!!");
+                        res.setMsg("未验重箱:["+arr[1]+"],请退回!!!");
+                        res.setCode(500);
+                        return res;
+                    }
+                }
+                jinList.add(str);
+                if(!StringUtils.isEmpty(arr[1])) cntrnoList.add(arr[1]);
+            } else {
+                tiList.add(str);
+            }
+        }
+        gatherData.setCntrnoList(cntrnoList);
+
+        if(jinList.size() > 0) {
+            jinRes = this.SAVE_TRUCK_INFOR(gatherData, "2", jinList);
+        }
+        if(tiList.size() > 0 && 200 == jinRes.getCode()) {
+            tiRes = this.SAVE_TRUCK_INFOR(gatherData, "1", tiList);
+        }
+        if(200 == jinRes.getCode() && 200 == tiRes.getCode()) {
+            res = this.TRUCK_PASS(gatherData);
+        } else {
+            res.setCode(500);
+            if(500 == jinRes.getCode()) res.setMsg(jinRes.getMsg());
+            if(500 == tiRes.getCode()) res.setMsg(tiRes.getMsg());
+            return res;
+        }
+
+        return res;
+    }
+
+    /** 保存集卡进出道口信息接口 */
+    public ResponseBo SAVE_TRUCK_INFOR(GatherData gatherData, String flag, List<String> planNoArr) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //提箱/进箱标志 	提箱 1;进箱 2
+        String I_GAT_DRFG = flag;
+        //卡车自重
+        Double I_WEIGHT = gatherData.getGROSS_WT();
+        Integer weight = I_WEIGHT != null ? I_WEIGHT.intValue() : 0;
+        //IC卡ID号,填写固定值999999
+        String I_ICID = "999999";
+        //IC卡类型,填写固定值9
+        String I_ICTYPE = "9";
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_TRUCK_INFOR")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:SAVE_TRUCK_INFOR","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_GAT_DRFG", I_GAT_DRFG)
+                    .setParam("i_weight", weight)
+                    .setParam("I_ICID", I_ICID)
+                    .setParam("i_ictype", I_ICTYPE)
+                    .setParam("msg","");
+            log.info("保存集卡进出道口信息接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("保存集卡进出道口信息接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"SAVE_TRUCK_INFORResponse");
+            Node node1 = XmlUtil.getElement(element2, "SAVE_TRUCK_INFORResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("保存集卡进出道口信息接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("保存集卡进出道口信息接口报错:"+e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            res.setCode(500);
+            return res;
+        }
+        if("1".equals(flag)) {//1 提箱
+            ResponseBo saveWeightInfor = this.SAVE_WEIGHT_INFOR(gatherData);
+            ResponseBo savePlanInfor = this.SAVE_PLAN_INFOR(gatherData, planNoArr);
+            if(200 == saveWeightInfor.getCode() && 200 == savePlanInfor.getCode()) {
+                //提箱业务传入预约号
+                String notes = "";
+                String[] arr = planNoArr.get(0).split(":");
+                if (!StringUtils.isEmpty(arr[2])) {
+                    notes = this.GET_CARDS(gatherData, arr[2]).getMsg();
+                }
+
+                res.setNotes(notes);
+            } else {
+                res.setCode(500);
+                if(500 == saveWeightInfor.getCode()) res.setMsg(saveWeightInfor.getMsg());
+                if(500 == savePlanInfor.getCode()) res.setMsg(savePlanInfor.getMsg());
+                return res;
+            }
+
+        } else {//2 进箱
+            ResponseBo saveWeightInfor = this.SAVE_WEIGHT_INFOR(gatherData);
+            ResponseBo saveContainerInfor = this.SAVE_CONTAINER_INFOR(gatherData);
+            ResponseBo saveDmgcdInfor = this.SAVE_DMGCD_INFOR(gatherData);
+            if(200 == saveWeightInfor.getCode() && 200 == saveContainerInfor.getCode() && 200 == saveDmgcdInfor.getCode()) {
+                //进箱业务传入箱号
+                String notes = "";
+                String[] arr = planNoArr.get(0).split(":");
+                if(!StringUtils.isEmpty(arr[1])) {
+                    notes = this.GET_CARDS(gatherData, arr[1]).getMsg();
+                }
+
+                res.setNotes(notes);
+            } else {
+                res.setCode(500);
+                if(500 == saveWeightInfor.getCode()) res.setMsg(saveWeightInfor.getMsg());
+                if(500 == saveContainerInfor.getCode()) res.setMsg(saveContainerInfor.getMsg());
+                if(500 == saveDmgcdInfor.getCode()) res.setMsg(saveDmgcdInfor.getMsg());
+                return res;
+            }
+        }
+        return res;
+    }
+
+    /** 保存磅秤重量接口 */
+    public ResponseBo SAVE_WEIGHT_INFOR(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //地磅重量
+        Double I_GAT_WEIGHT = gatherData.getGROSS_WT();
+        Integer weight = I_GAT_WEIGHT != null ? I_GAT_WEIGHT.intValue() : 0;
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_WEIGHT_INFOR")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:SAVE_WEIGHT_INFOR","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_GAT_WEIGHT", weight)
+                    .setParam("msg","");
+            log.info("保存磅秤重量接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("保存磅秤重量接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"SAVE_WEIGHT_INFORResponse");
+            Node node1 = XmlUtil.getElement(element2, "SAVE_WEIGHT_INFORResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("保存磅秤重量接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("保存磅秤重量接口报错:"+e.getMessage());
+            return res;
+        }
+
+
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            res.setCode(500);
+            return res;
+        }
+        return res;
+    }
+
+    /** 保存进箱信息接口 */
+    public ResponseBo SAVE_CONTAINER_INFOR(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //前箱箱号
+        String I_GAT_CNTRNO1 = gatherData.getCntrnoF();
+        //前箱的 UNISO 代码
+        String I_GAT_CTYPE1 = gatherData.getCntrnoFType();
+        //后箱箱号
+        String I_GAT_CNTRNO2 = gatherData.getCntrnoA();
+        //后箱的 UNISO 代码
+        String I_GAT_CTYPE2 = gatherData.getCntrnoAType();
+        if(StringUtils.isEmpty(I_GAT_CNTRNO1) && StringUtils.isEmpty(I_GAT_CNTRNO2)) {
+            res.setMsg("箱号为空");
+            res.setCode(500);
+            return res;
+        }
+        Set<String> cntrnoList = gatherData.getCntrnoList();
+        for (String cntrno : cntrnoList) {
+            if(!cntrno.equalsIgnoreCase(I_GAT_CNTRNO1) && !cntrno.equalsIgnoreCase(I_GAT_CNTRNO2)) {
+                res.setMsg("箱号识别错误!系统箱号为:"+cntrnoList.toString());
+                res.setCode(500);
+                return res;
+            }
+        }
+
+        //填写固定值 0
+        String I_GAT_NO_GETTP1 = "0";
+        String I_GAT_NO_GETTP2 = "0";
+        //请传入空值
+        String i_gat_cntphoto_id = "";
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_CONTAINER_INFOR")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:SAVE_CONTAINER_INFOR","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_GAT_CNTRNO1", I_GAT_CNTRNO1)
+                    .setParam("I_GAT_CTYPE1", I_GAT_CTYPE1)
+                    .setParam("I_GAT_CNTRNO2", I_GAT_CNTRNO2)
+                    .setParam("I_GAT_CTYPE2", I_GAT_CTYPE2)
+                    .setParam("I_GAT_NO_GETTP1", I_GAT_NO_GETTP1)
+                    .setParam("I_GAT_NO_GETTP2", I_GAT_NO_GETTP2)
+                    .setParam("i_gat_cntphoto_id", i_gat_cntphoto_id)
+                    .setParam("msg","");
+            log.info("保存进箱信息接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("保存进箱信息接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"SAVE_CONTAINER_INFORResponse");
+            Node node1 = XmlUtil.getElement(element2, "SAVE_CONTAINER_INFORResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("保存进箱信息接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("保存进箱信息接口报错:"+e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            res.setCode(500);
+            return res;
+        }
+        return res;
+    }
+
+    /** 保存验残结果接口 */
+    public ResponseBo SAVE_DMGCD_INFOR(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //残损 ID,请传入空值
+        String I_GAT_DMGID = "";
+        //传入固定值 NN
+        String I_GAT_DMGCD1 = "NN";
+        //传入固定值 NN
+        String I_GAT_DMGCD2 = "NN";
+        //验残员工号
+        String I_EMPNO = "12839";
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_DMGCD_INFOR")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:SAVE_DMGCD_INFOR","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_GAT_DMGID", I_GAT_DMGID)
+                    .setParam("I_GAT_DMGCD1", I_GAT_DMGCD1)
+                    .setParam("I_GAT_DMGCD2", I_GAT_DMGCD2)
+                    .setParam("I_EMPNO", I_EMPNO)
+                    .setParam("msg","");
+            log.info("保存验残结果接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("保存验残结果接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"SAVE_DMGCD_INFORResponse");
+            Node node1 = XmlUtil.getElement(element2, "SAVE_DMGCD_INFORResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("保存验残结果接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("保存验残结果接口报错:"+e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            res.setCode(500);
+            return res;
+        }
+        return res;
+    }
+
+
+    /** 保存计划信息接口 */
+    public ResponseBo SAVE_PLAN_INFOR(GatherData gatherData, List<String> planNoArr) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //提箱/进箱标志 	提箱 1;进箱 2
+        String I_GAT_DRFG = "1";
+        //预约号
+        String[] str = planNoArr.get(0).split(":");
+        String I_GAT_PLANNO = str[2];
+        if(StringUtils.isEmpty(I_GAT_PLANNO)) {
+            res.setCode(500);
+            res.setMsg("保存计划信息接口——保存前箱:预约号为空");
+            return res;
+        }
+        //前后标志( F 前 A 后)
+        String I_FBFG = "F";
+        //校验码 ,请传入空值
+        String I_GAT_CHECKCD = "";
+        //进场类型 ,请传入空值
+        String I_GAT_INTYPE = "";
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_PLAN_INFOR")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:SAVE_PLAN_INFOR","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("i_GAT_DRFG", I_GAT_DRFG)
+                    .setParam("I_GAT_PLANNO", I_GAT_PLANNO)
+                    .setParam("I_GAT_CHECKCD", I_GAT_CHECKCD)
+                    .setParam("I_FBFG", I_FBFG)
+                    .setParam("I_GAT_INTYPE", I_GAT_INTYPE)
+                    .setParam("msg","");
+            log.info("保存计划信息接口——保存前箱:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("保存计划信息接口——保存前箱输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"SAVE_PLAN_INFORResponse");
+            Node node1 = XmlUtil.getElement(element2, "SAVE_PLAN_INFORResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("保存计划信息接口——保存前箱报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("保存计划信息接口——保存前箱报错:"+e.getMessage());
+            return res;
+        }
+        res.setCode(200);
+        res.setMsg(MSG);
+        if("false".equals(result)) {
+            res.setCode(500);
+            return res;
+        }
+
+        if(planNoArr.size() == 2) {
+            str = planNoArr.get(1).split(":");
+            I_GAT_PLANNO = str[2];
+            if(StringUtils.isEmpty(I_GAT_PLANNO)) {
+                res.setCode(500);
+                res.setMsg("保存计划信息接口——保存后箱:预约号为空");
+                return res;
+            }
+            //前后标志( F 前 A 后)
+            I_FBFG = "A";
+            String result1 = "";
+            String MSG1 = "";
+            try {
+                SoapClient client1 = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                        .header("SOAPAction","http://tempuri.org/ISmartGate/SAVE_PLAN_INFOR")
+                        .header("Content-Type","text/xml; charset=utf-8")
+                        // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                        .setMethod("tem:SAVE_PLAN_INFOR","http://tempuri.org/")
+                        // 设置参数,此处自动添加方法的前缀:web
+                        .setParam("I_EMPTNO", I_EMPTNO)
+                        .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                        .setParam("i_trkno", i_trkno)
+                        .setParam("i_GAT_DRFG", I_GAT_DRFG)
+                        .setParam("I_GAT_PLANNO", I_GAT_PLANNO)
+                        .setParam("I_GAT_CHECKCD", I_GAT_CHECKCD)
+                        .setParam("I_FBFG", I_FBFG)
+                        .setParam("I_GAT_INTYPE", I_GAT_INTYPE)
+                        .setParam("msg","");
+                log.info("保存计划信息接口——保存后箱:\n{}",client1.getMsgStr(true));
+                HttpResponse response1 = client1.sendForResponse();
+                String body1 = response1.body();
+                log.info("保存计划信息接口——保存后箱输出报文:\n{}",body1);
+                Document document1 = XmlUtil.parseXml(body1);
+                Element rootElement1 = XmlUtil.getRootElement(document1);
+                Element element11 =XmlUtil.getElement(rootElement1,"s:Body");
+                Element element21 =XmlUtil.getElement(element11,"SAVE_PLAN_INFORResponse");
+                Node node11 = XmlUtil.getElement(element21, "SAVE_PLAN_INFORResult").getFirstChild();
+                result1 = node11 != null ? node11.getNodeValue() : "";
+                Node node21 = XmlUtil.getElement(element21, "msg").getFirstChild();
+                MSG1 = node21 != null ? node21.getNodeValue() : "";
+            } catch (Exception e) {
+                log.info("保存计划信息接口——保存后箱报错:{}",e.getMessage());
+                res.setCode(500);
+                res.setMsg("保存计划信息接口——保存后箱报错:"+e.getMessage());
+                return res;
+            }
+
+            res.setCode(200);
+            res.setMsg(MSG1);
+            if("false".equals(result1)) {
+                res.setCode(500);
+                return res;
+            }
+        }
+        return res;
+    }
+
+
+
+    /** 获取进场小票信息接口 */
+    public ResponseBo GET_CARDS(GatherData gatherData, String I_CNT_VAL) {
+        ResponseBo res = new ResponseBo();
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+
+        String result = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/GET_CARDS")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:GET_CARDS","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_CNT_VAL", I_CNT_VAL);
+            log.info("获取进场小票信息接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            body = body.replaceAll("&#xD;","\n").replaceAll("&#x8;","");
+            log.info("获取进场小票信息接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"GET_CARDSResponse");
+            Node node1 = XmlUtil.getElement(element2, "GET_CARDSResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            log.info("进场小票:\n{}",result);
+        } catch (Exception e) {
+            log.info("获取进场小票信息接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("获取进场小票信息接口报错:"+e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg(result);
+        /*if(StringUtils.isEmpty(result)) {
+            res.setCode(500);
+            return res;
+        }*/
+        return res;
+    }
+
+    /** 车辆放行接口 */
+    public ResponseBo TRUCK_PASS(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/TRUCK_PASS")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:TRUCK_PASS","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("msg", "");
+            log.info("车辆放行接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("车辆放行接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"TRUCK_PASSResponse");
+            Node node1 = XmlUtil.getElement(element2, "TRUCK_PASSResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            log.info("车辆放行接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("车辆放行接口报错:"+e.getMessage());
+            return res;
+        }
+
+        if("false".equals(result)) {
+            res.setCode(500);
+            res.setMsg(MSG);
+            return res;
+        }
+        res.setCode(200);
+        res.setMsg("车辆放行");
+        res.setJsStatus(1);
+        return res;
+    }
+
+
+    /** 车辆出场确认接口 */
+    public ResponseBo CONFIRM_OUTYARD(GatherData gatherData) {
+        gatherData.setJsStatus(0);
+        ResponseBo res = new ResponseBo();
+        res.setJsStatus(gatherData.getJsStatus());
+        res.setContainerStatus(gatherData.getContainerStatus());
+        //操作员工,请使用员工号
+        String I_EMPTNO = "00000";
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();//"I3";
+        //车牌号
+        String i_trkno = gatherData.getVE_LICENSE_NO();
+        //箱号 1 (进箱传真实箱号  提箱传 null)
+        String I_GAT_CNTRNO1 = gatherData.getCntrnoF();
+        //箱号 2 (进箱传真实箱号  提箱传 null)
+        String I_GAT_CNTRNO2 = gatherData.getCntrnoA();
+        //记录车辆出场时的重量信息
+        Double I_GAT_WEIGHT = gatherData.getGROSS_WT();
+        Integer weight = I_GAT_WEIGHT != null ? I_GAT_WEIGHT.intValue() : 0;
+
+        String result = "";
+        String MSG = "";
+        try {
+            SoapClient client = SoapClient.create("http://10.3.2.214:8097/TOPSService/SHB.TOPS.Contract.Gate.SmartGate.ISmartGate")
+                    .header("SOAPAction","http://tempuri.org/ISmartGate/CONFIRM_OUTYARD")
+                    .header("Content-Type","text/xml; charset=utf-8")
+                    // 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
+                    .setMethod("tem:CONFIRM_OUTYARD","http://tempuri.org/")
+                    // 设置参数,此处自动添加方法的前缀:web
+                    .setParam("I_EMPTNO", I_EMPTNO)
+                    .setParam("I_GAT_GATENO", I_GAT_GATENO)
+                    .setParam("i_trkno", i_trkno)
+                    .setParam("I_GAT_CNTRNO1", I_GAT_CNTRNO1)
+                    .setParam("I_GAT_CNTRNO2", I_GAT_CNTRNO2)
+                    .setParam("I_GAT_WEIGHT", weight)
+                    .setParam("msg", "");
+            log.info("车辆出场确认接口:\n{}",client.getMsgStr(true));
+            HttpResponse response= client.sendForResponse();
+            String body = response.body();
+            log.info("车辆出场确认接口输出报文:\n{}",body);
+            Document document= XmlUtil.parseXml(body);
+            Element rootElement = XmlUtil.getRootElement(document);
+            Element element1=XmlUtil.getElement(rootElement,"s:Body");
+            Element element2=XmlUtil.getElement(element1,"CONFIRM_OUTYARDResponse");
+            Node node1 = XmlUtil.getElement(element2, "CONFIRM_OUTYARDResult").getFirstChild();
+            result = node1 != null ? node1.getNodeValue() : "";
+            Node node2 = XmlUtil.getElement(element2, "msg").getFirstChild();
+            MSG = node2 != null ? node2.getNodeValue() : "";
+        } catch (Exception e) {
+            //{"code":500,"msg":"车辆出场确认接口报错:Parse XML from stream error!","examineState":"有集装箱预约","jsStatus":0,"containerStatus":1,"notes":null,"flag":null,"registrationNo":null}
+            log.info("车辆出场确认接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg("车辆出场确认接口报错:"+e.getMessage());
+            return res;
+        }
+
+        if("false".equals(result)) {
+            if(MSG.contains("确认集卡号是否正确")) {
+                res.setFlag("集装箱无预约");
+            }
+            res.setCode(500);
+            res.setMsg(MSG);
+            return res;
+        }
+        res.setCode(200);
+        res.setMsg("车辆放行");
+        res.setJsStatus(1);
+        return res;
+    }
+
+
+
+
+
+
+    /** 获取出场小票信息接口 */
+    public void GET_OUTCARDS(GatherData gatherData) {
+        //道口号,例:1、2、12
+        String I_GAT_GATENO = gatherData.getCrossCode();
+        //车牌号
+        String I_TRKNO = gatherData.getVE_LICENSE_NO();
+
+        //请求地址
+        String url = configComponent.getGETOUTCARDSUrl()+"?I_GAT_GATENO="+I_GAT_GATENO+"&I_TRKNO="+I_TRKNO;
+
+        //返回预约结果对象
+        JSONObject jsonObject = RestUtil.get(url, headers);
+        String slips = jsonObject.getString("Slips");
+    }
+
+    /** 修改铅封号接口 */
+    public void UpdateSealNo(GatherData gatherData) {
+        //箱号(必填)
+        String cntrNo = "";
+        //铅封号(必填)
+        String sealNo = gatherData.getVE_LICENSE_NO();
+        //修改人
+        String userName = "";
+        //错误信息,例:修改失败
+        String MSG = "";
+
+        //请求地址
+        String url = configComponent.getUpdateSealNoUrl();
+        //请求体
+        String body = "{\"cntrNo\":\""+cntrNo+"\",\"sealNo\":\""+sealNo+"\",\"userName\":\""+userName+"\"," +
+                "\"MSG\":\""+MSG+"\"}";
+        JSONObject params = JSONObject.parseObject(body);
+
+        //返回预约结果对象
+        /*{"RESULT":true,"MESSAGE":""}*/
+        JSONObject jsonObject = RestUtil.post(url, headers, params);
+        VerificationRes result = JSONObject.parseObject(jsonObject.toString(), VerificationRes.class);
+
+    }
+
+    /** 获取残损代码接口 */
+    public void GetDamageCode(GatherData gatherData) {
+        //残损代码
+        String damageCodes = "";
+        //错误信息,例:获取代码失败
+        String MSG = "";
+
+        //请求地址
+        String url = configComponent.getGetDamageCodeUrl();
+        //请求体
+        String body = "{\"damageCodes\":\""+damageCodes+"\",\"MSG\":\""+MSG+"\"}";
+        JSONObject params = JSONObject.parseObject(body);
+
+        //返回预约结果对象
+        /*{"RESULT":true,"MESSAGE":""}*/
+        JSONObject jsonObject = RestUtil.post(url, headers, params);
+        VerificationRes result = JSONObject.parseObject(jsonObject.toString(), VerificationRes.class);
+
+    }
+
+    /** 残损登记接口 */
+    public void SavelDamageInfo(GatherData gatherData) {
+        //箱号(必填)
+        String cntrNo = "";
+        //原/工残 X-- 不明残损 O--原残 W--工残(必填)
+        String originalDamageFlag = "";
+        //残损代码(必填)
+        String damageCode = "";
+        //备注
+        String remark = "";
+        //登记员
+        String userName = "";
+        //错误信息,例:登记失败
+        String MSG = "";
+
+        //请求地址
+        String url = configComponent.getSavelDamageInfoUrl();
+        //请求体
+        String body = "{\"cntrNo\":\""+cntrNo+"\",\"originalDamageFlag\":\""+originalDamageFlag+"\",\"damageCode\":\""+damageCode+"\",\"remark\":\""+remark+"\"," +
+                "\"userName\":\""+userName+"\",\"MSG\":\""+MSG+"\"}";
+        JSONObject params = JSONObject.parseObject(body);
+
+        //返回预约结果对象
+        /*{"RESULT":true,"MESSAGE":""}*/
+        JSONObject jsonObject = RestUtil.post(url, headers, params);
+        VerificationRes result = JSONObject.parseObject(jsonObject.toString(), VerificationRes.class);
+
+    }
+
+}

+ 113 - 0
src/main/java/com/ruoyi/web/service/CustomsService.java

@@ -0,0 +1,113 @@
+package com.ruoyi.web.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.config.Md5Utils;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.AesEncryptUtil;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.MessageXML;
+import com.ruoyi.web.config.RestUtil;
+import com.ruoyi.web.entity.Customs;
+import com.ruoyi.web.entity.CustomsRes;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.Release;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+
+/**
+ * 海关接口调用-业务处理
+ */
+@Service
+public class CustomsService {
+
+    private static final HttpHeaders headers = new HttpHeaders();
+    @Autowired
+    private ConfigComponent configComponent;
+
+    /** 采集数据通过验证后,调海关接口,发送json格式的采集数据 */
+    public ResponseBo sendGatherData(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setContainerStatus(0);
+        //采集数据转报文格式字符串
+        String Content = MessageXML.gatherXml(gatherData);
+        //加密报文字符串
+        String encryptContent = "";
+        try {
+            encryptContent = AesEncryptUtil.encrypt(Content);
+        } catch (Exception e) {
+            System.out.println("----------------------------------数据加密错误:\n"+e.getMessage());
+        }
+
+        Customs customs = new Customs();
+        customs.setContent(encryptContent);
+
+        String guid = customs.getGuid();
+        String token = Md5Utils.hash(guid);
+        String tokenStr = "{\"token\":\"" + token + "\"}";
+        //请求地址
+        String url = configComponent.getGatherUrl();
+        //拼接 url 参数
+        JSONObject variables = JSONObject.parseObject(tokenStr);
+        //请求体
+        JSONObject params = JSONObject.parseObject(JSONObject.toJSONString(customs));
+
+        /*JSONObject result = RestUtil.post(url, headers, variables, params);
+        //返回放行结果对象
+        CustomsRes customsRes = JSONObject.parseObject(result.toString(), CustomsRes.class);*/
+        CustomsRes customsRes = new CustomsRes();
+        customsRes.setOP_REASON("放行");
+
+        res.setCode(200);
+        res.setMsg(customsRes.getOP_REASON());
+        if(!"放行".equals(customsRes.getOP_REASON())) {
+            res.setCode(500);
+            return res;
+        }
+        res.setContainerStatus(1);
+        return res;
+    }
+
+    /** 车辆通过卡口后,调海关接口,发送放行结果信息给海关系统 */
+    public ResponseBo sendReleaseData(Release release) {
+        ResponseBo res = new ResponseBo();
+        //放行结果转报文格式字符串
+        String Content = MessageXML.ReleaseResXml(release);
+        //加密报文字符串
+        String encryptContent = "";
+        try {
+            encryptContent = AesEncryptUtil.encrypt(Content);
+        } catch (Exception e) {
+            System.out.println("----------------------------------数据加密错误:\n"+e.getMessage());
+        }
+
+        Customs customs = new Customs();
+        customs.setContent(encryptContent);
+
+        String guid = customs.getGuid();
+        String token = Md5Utils.hash(guid);
+        String tokenStr = "{\"token\":\"" + token + "\"}";
+        //请求地址
+        String url = configComponent.getReleaseUrl();
+        //拼接 url 参数
+        JSONObject variables = JSONObject.parseObject(tokenStr);
+        //请求体
+        JSONObject params = JSONObject.parseObject(JSONObject.toJSONString(customs));
+
+        /*JSONObject result = RestUtil.post(url, headers, variables, params);
+        //返回放行结果对象(暂定返回0x31报文)
+        CustomsRes customsRes = JSONObject.parseObject(result.toString(), CustomsRes.class);*/
+
+        CustomsRes customsRes = new CustomsRes();
+        customsRes.setOP_REASON("放行");
+
+        res.setCode(200);
+        res.setMsg(customsRes.getOP_REASON());
+        if(!"放行".equals(customsRes.getOP_REASON())) {
+            res.setCode(500);
+            return res;
+        }
+        return res;
+    }
+
+}

+ 82 - 0
src/main/java/com/ruoyi/web/service/IntermodalTransporService.java

@@ -0,0 +1,82 @@
+package com.ruoyi.web.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.RestUtil;
+import com.ruoyi.web.entity.GatherData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 钦州联运中心智慧闸口接口调用-业务处理
+ */
+@Service
+@Slf4j
+public class IntermodalTransporService {
+    @Autowired
+    private ConfigComponent configComponent;
+
+
+    /** 钦州闸口识别对接接口 */
+    public ResponseBo gateIdentify(GatherData gatherData) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_VALUE));
+        ResponseBo res = new ResponseBo();
+
+        //车牌号(必输)
+        String plateNum = gatherData.getVE_LICENSE_NO();
+        //过磅重量NUMBER(18,3)(必输)
+        //读取到的地磅重量——>KG转换为吨
+        BigDecimal grossWt = new BigDecimal(gatherData.getGROSS_WT());
+        grossWt = grossWt.divide(BigDecimal.valueOf(1000),2, RoundingMode.HALF_UP);
+        double weight = grossWt.doubleValue();
+        //进出信息,in 进 out 出
+        String laneMode = "I".equalsIgnoreCase(gatherData.getI_E_FLAG()) ? "in" : "out";
+        //前箱箱号
+        String boxNo = gatherData.getCntrnoF();
+        //后箱箱号
+        String boxNoSecond = gatherData.getCntrnoA();
+        //重磅/空磅:E/F
+        String weightType = "";
+
+        //请求地址
+        String url = configComponent.getGateIdentifyUrl();
+        //请求体
+        String body = "{\"appid\":\""+configComponent.getAppid()+"\"," +
+                "\"data\":{\"plateNum\":\""+plateNum+"\",\"weight\":\""+weight+"\",\"laneMode\":\""+laneMode+"\"," +
+                "\"boxNo\":\""+boxNo+"\",\"boxNoSecond\":\""+boxNoSecond+"\",\"weightType\":\""+weightType+"\"}}";
+        JSONObject params = JSONObject.parseObject(body);
+        log.info("钦州闸口识别对接接口url:{}",url);
+        log.info("钦州闸口识别对接接口params:{}",params);
+        try {
+            JSONObject jsonObject = RestUtil.post(url, headers, params);
+            log.info("钦州闸口识别对接接口返回结果:{}",jsonObject);
+            if(jsonObject != null && jsonObject.toString().contains("success") && jsonObject.getBoolean("success")) {
+                res.setCode(200);
+                res.setMsg(jsonObject.getString("message"));
+            } else {
+                res.setCode(500);
+                res.setMsg(jsonObject.getString("message"));
+            }
+        } catch (Exception e) {
+            log.info("钦州闸口识别对接接口报错:{}",e.getMessage());
+            res.setCode(500);
+            res.setMsg(e.getMessage());
+            return res;
+        }
+
+        res.setCode(200);
+        res.setMsg("请通行");
+
+        return res;
+    }
+
+
+}

+ 125 - 0
src/main/java/com/ruoyi/web/service/ProcessService.java

@@ -0,0 +1,125 @@
+package com.ruoyi.web.service;
+
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.bo.Steps;
+import com.ruoyi.web.entity.GatherData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class ProcessService {
+    @Autowired
+    private ReservationService reservationService;
+    @Autowired
+    private CustomsService customsService;
+    @Autowired
+    private ContainerService containerService;
+    @Autowired
+    private BulkloadService bulkloadService;
+    @Autowired
+    private IntermodalTransporService intermodalTransporService;
+    @Autowired
+    private SendSocket81Service sendSocket81Service;
+
+    public ResponseBo process(GatherData gatherData) {
+        Integer jsStatus = gatherData.getJsStatus();
+        Integer containerStatus = gatherData.getContainerStatus();
+
+        List<Steps> stepsList = gatherData.getSteps();
+        if(stepsList.contains("gateIdentify")) {
+            //当前接口为联运中心智慧闸口接口,全部调用
+        } else if(0 == jsStatus && 0 == containerStatus) {
+            //为初始值0,所有接口都要调用
+        } else if(1 == jsStatus && 0 == containerStatus) {
+            stepsList.removeIf(steps ->
+                    "reservation".equalsIgnoreCase(steps.getApi()) //预约接口
+                            || "GetBookingInfo".equalsIgnoreCase(steps.getApi()) //集装箱接口
+                            || "LogOn".equalsIgnoreCase(steps.getApi()) //散货接口
+                            || "reservationToSorJ".equalsIgnoreCase(steps.getApi())); //预约接口——>集装箱接口/散货接口(通道为综合类型)
+        } else if(1 == jsStatus && 1 == containerStatus) {
+            //当前情况暂定为第二次作业,重走新流程,,所有接口都要调用
+        }
+
+
+        List<Steps> collect = stepsList.stream().filter(steps -> "sendGatherData".equalsIgnoreCase(steps.getApi())).collect(Collectors.toList());
+        if(collect.size() == 0) {//没有海关,海关状态改为2
+            containerStatus = 2;
+        }
+        List<Steps> collect1 = stepsList.stream().filter(steps -> "GetBookingInfo".equalsIgnoreCase(steps.getApi())||"LogOn".equalsIgnoreCase(steps.getApi())).collect(Collectors.toList());
+        if(collect1.size() == 0) {//没有集装箱或散货,集装箱/散货状态改为2
+            jsStatus = 2;
+        }
+
+
+        String TRUCK_NO = gatherData.getVE_LICENSE_NO();
+        ResponseBo res = new ResponseBo();
+        String examineState = "";
+        log.info("---------------stepsList:"+stepsList.toString());
+        for (Steps steps:stepsList) {
+            if("reservation".equalsIgnoreCase(steps.getApi())) {//预约接口
+                res = reservationService.reservation(TRUCK_NO);
+                res.setJsStatus(gatherData.getJsStatus());
+                res.setContainerStatus(gatherData.getContainerStatus());
+                examineState = res.getExamineState();
+                if(500 == res.getCode()) return res;
+            }
+            else if("sendGatherData".equalsIgnoreCase(steps.getApi())) {//海关接口-发送采集数据
+                res = customsService.sendGatherData(gatherData);
+                containerStatus = gatherData.getContainerStatus();
+                if(500 == res.getCode()) return res;
+            }
+            else if("sendSocket".equalsIgnoreCase(steps.getApi())) {//socket方式发送81报文
+                sendSocket81Service.sendSocket(gatherData);
+                containerStatus = gatherData.getContainerStatus();
+                if(500 == res.getCode()) return res;
+            }
+            else if("GetBookingInfo".equalsIgnoreCase(steps.getApi())) {//集装箱接口
+                examineState = "有集装箱预约";
+                if("I".equalsIgnoreCase(gatherData.getI_E_FLAG())) {
+                    res = containerService.GetBookingInfo(gatherData);
+                } else {
+                    res = containerService.CONFIRM_OUTYARD(gatherData);
+                }
+                jsStatus = res.getJsStatus();
+                if(500 == res.getCode()) return res;
+            }
+            else if("LogOn".equalsIgnoreCase(steps.getApi())) {//散货接口
+                examineState = "有散杂货预约";
+                res = bulkloadService.LogOn(gatherData);
+                jsStatus = res.getJsStatus();
+                if(500 == res.getCode()) return res;
+            }
+            else if("reservationToSorJ".equalsIgnoreCase(steps.getApi())) {//预约接口——>集装箱接口/散货接口(通道为综合类型)
+                res = reservationService.reservationToSorJ(gatherData);
+                jsStatus = res.getJsStatus();
+                examineState = res.getExamineState();
+                if(500 == res.getCode()) return res;
+            }
+            else if("reservationToSorJ_Test".equalsIgnoreCase(steps.getApi())) {//预约接口——>集装箱接口/散货接口(通道为综合类型)——>测试(没有预约接口)
+                res = reservationService.reservationToSorJ_Test(gatherData);
+                jsStatus = res.getJsStatus();
+                examineState = res.getExamineState();
+                if(500 == res.getCode()) return res;
+            }
+            else if("gateIdentify".equalsIgnoreCase(steps.getApi())) {//联运中心接口
+                examineState = "联运中心";
+                res = intermodalTransporService.gateIdentify(gatherData);
+                if(500 == res.getCode()) return res;
+            }
+        }
+
+        res.setExamineState(examineState);
+        res.setJsStatus(jsStatus);
+        res.setContainerStatus(containerStatus);
+        log.info("---------------res:"+res.toString());
+        log.info("---------------结束---------------");
+        return res;
+    }
+
+
+}

+ 153 - 0
src/main/java/com/ruoyi/web/service/ReservationService.java

@@ -0,0 +1,153 @@
+package com.ruoyi.web.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.RestUtil;
+import com.ruoyi.web.entity.Base;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.Reservation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+/**
+ * 散杂货、集装箱预约状态查询接口调用-业务处理
+ */
+@Slf4j
+@Service
+public class ReservationService {
+
+    private static final HttpHeaders headers = new HttpHeaders();
+    @Autowired
+    private ConfigComponent configComponent;
+    @Autowired
+    private ContainerService containerService;
+    @Autowired
+    private BulkloadService bulkloadService;
+
+    /**
+     * 预约接口
+     * @param TRUCK_NO 车牌号
+     */
+    public ResponseBo reservation(String TRUCK_NO) {
+        ResponseBo res = new ResponseBo();
+        //请求地址
+        String url = configComponent.getReservationUrl() + "?carNumber=" + TRUCK_NO;
+
+        //返回预约结果对象
+        //获取预约状态接口调用失败,默认集装箱处理。
+        /*{
+            "code": 200,
+            "msg": "获取散杂货、集装箱预约状态成功",
+            "data": {
+                "carNumber": "桂E12345",
+                "sExamineState": "01"
+                "jExamineState": "00"
+            }
+        }*/
+        /*JSONObject result = RestUtil.get(url, headers);
+        Base base = JSONObject.parseObject(result.toString(), Base.class);
+        res.setCode(base.getCode());
+        res.setMsg(base.getMsg());
+        if(200 == base.getCode()) {
+            Reservation reservation = JSONObject.parseObject(base.getData(), Reservation.class);
+
+            if("00".equals(reservation.getSExamineState())&&"01".equals(reservation.getJExamineState())) {
+                res.setExamineState("有集装箱预约");
+            } else if("01".equals(reservation.getSExamineState())&&"00".equals(reservation.getJExamineState())) {
+                res.setExamineState("有散杂货预约");
+            } else if("00".equals(reservation.getSExamineState())&&"00".equals(reservation.getJExamineState())) {
+                res.setExamineState("没有预约");
+                res.setMsg("没有预约");
+                res.setCode(500);
+            }
+        }*/
+
+        res.setExamineState("没有预约");
+        res.setMsg("没有预约");
+        res.setCode(500);
+        return res;
+    }
+
+    /**
+     * 预约接口——>集装箱接口/散货接口(通道为综合类型)
+     */
+    public ResponseBo reservationToSorJ(GatherData gatherData) {
+        String examineState = "";
+        ResponseBo res = new ResponseBo();
+        //请求地址
+        String url = configComponent.getReservationUrl() + "?carNumber=" + gatherData.getVE_LICENSE_NO();
+
+        //返回预约结果对象
+        //获取预约状态接口调用失败,默认集装箱处理。
+        /*{
+            "code": 200,
+            "msg": "获取散杂货、集装箱预约状态成功",
+            "data": {
+                "carNumber": "桂E12345",
+                "sExamineState": "01"
+                "jExamineState": "00"
+            }
+        }*/
+        /*JSONObject result = RestUtil.get(url, headers);
+        Base base = JSONObject.parseObject(result.toString(), Base.class);
+        res.setCode(base.getCode());
+        res.setMsg(base.getMsg());*/
+        Base base = new Base();
+        base.setCode(200);
+        base.setMsg("获取散杂货、集装箱预约状态成功");
+        base.setData("{\"carNumber\": \"桂E12345\",\"sExamineState\": \"01\",\"jExamineState\": \"00\"}");
+
+        res.setCode(base.getCode());
+        res.setMsg(base.getMsg());
+        if(200 == base.getCode()) {
+            Reservation reservation = JSONObject.parseObject(base.getData(), Reservation.class);
+
+            if("00".equals(reservation.getSExamineState())&&"01".equals(reservation.getJExamineState())) {
+                res = containerService.GetBookingInfo(gatherData);
+                res.setExamineState("有集装箱预约");
+            } else if("01".equals(reservation.getSExamineState())&&"00".equals(reservation.getJExamineState())) {
+                res = bulkloadService.LogOn(gatherData);
+                res.setExamineState("有散杂货预约");
+            } else if("00".equals(reservation.getSExamineState())&&"00".equals(reservation.getJExamineState())) {
+                res.setExamineState("没有预约");
+                res.setCode(500);
+                res.setMsg("没有预约");
+            }
+        }
+        return res;
+    }
+
+    /**
+     * 预约接口——>集装箱接口/散货接口(通道为综合类型)——>测试
+     */
+    public ResponseBo reservationToSorJ_Test(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        //if("I".equalsIgnoreCase(gatherData.getI_E_FLAG())) {//进港
+            res = bulkloadService.LogOn(gatherData);
+            res.setExamineState("有散杂货预约");
+            if(("散货无预约".equals(res.getFlag()) || res.getMsg().contains("登录失败")) && 500 == res.getCode()) {
+                res = containerService.GetBookingInfo(gatherData);
+                res.setExamineState("有集装箱预约");
+                if("集装箱无预约".equals(res.getFlag()) && 500 == res.getCode()) {
+                    log.info("---------------------没有预约------------------");
+                    res.setExamineState("没有预约");
+                    return res;
+                }
+            }
+            return res;
+        /*} else {
+            res = containerService.CONFIRM_OUTYARD(gatherData);
+            res.setExamineState("有集装箱预约");
+            if("集装箱无预约".equals(res.getFlag()) && 500 == res.getCode() && StringUtils.isEmpty(gatherData.getCntrnoF()) && StringUtils.isEmpty(gatherData.getCntrnoA())) {
+                res = bulkloadService.LogOn(gatherData);
+                res.setExamineState("有散杂货预约");
+            }
+            return res;
+        }*/
+
+    }
+}

+ 125 - 0
src/main/java/com/ruoyi/web/service/SendSocket81Service.java

@@ -0,0 +1,125 @@
+package com.ruoyi.web.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.web.backgroud.client.BackgroundClientNetty;
+import com.ruoyi.web.bo.GatherBo;
+import com.ruoyi.web.bo.ResponseBo;
+import com.ruoyi.web.config.AesEncryptUtil;
+import com.ruoyi.web.config.ConfigComponent;
+import com.ruoyi.web.config.Md5Utils;
+import com.ruoyi.web.config.MessageXML;
+import com.ruoyi.web.entity.Customs;
+import com.ruoyi.web.entity.CustomsRes;
+import com.ruoyi.web.entity.GatherData;
+import com.ruoyi.web.entity.Release;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * Netty客户端发送81报文
+ */
+@Service
+public class SendSocket81Service {
+
+    private static final HttpHeaders headers = new HttpHeaders();
+    @Autowired
+    private ConfigComponent configComponent;
+
+    @Resource
+    private BackgroundClientNetty client;
+
+    public void sendSocket(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        String xml = MessageXML.get81Xml(gatherData);
+        client.send(xml);
+    }
+
+    /** 采集数据通过验证后,调海关接口,发送json格式的采集数据 */
+    public ResponseBo sendGatherData(GatherData gatherData) {
+        ResponseBo res = new ResponseBo();
+        res.setContainerStatus(0);
+        //采集数据转报文格式字符串
+        String Content = MessageXML.gatherXml(gatherData);
+        //加密报文字符串
+        String encryptContent = "";
+        try {
+            encryptContent = AesEncryptUtil.encrypt(Content);
+        } catch (Exception e) {
+            System.out.println("----------------------------------数据加密错误:\n"+e.getMessage());
+        }
+
+        Customs customs = new Customs();
+        customs.setContent(encryptContent);
+
+        String guid = customs.getGuid();
+        String token = Md5Utils.hash(guid);
+        String tokenStr = "{\"token\":\"" + token + "\"}";
+        //请求地址
+        String url = configComponent.getGatherUrl();
+        //拼接 url 参数
+        JSONObject variables = JSONObject.parseObject(tokenStr);
+        //请求体
+        JSONObject params = JSONObject.parseObject(JSONObject.toJSONString(customs));
+
+        /*JSONObject result = RestUtil.post(url, headers, variables, params);
+        //返回放行结果对象
+        CustomsRes customsRes = JSONObject.parseObject(result.toString(), CustomsRes.class);*/
+        CustomsRes customsRes = new CustomsRes();
+        customsRes.setOP_REASON("放行");
+
+        res.setCode(200);
+        res.setMsg(customsRes.getOP_REASON());
+        if(!"放行".equals(customsRes.getOP_REASON())) {
+            res.setCode(500);
+            return res;
+        }
+        res.setContainerStatus(1);
+        return res;
+    }
+
+    /** 车辆通过卡口后,调海关接口,发送放行结果信息给海关系统 */
+    public ResponseBo sendReleaseData(Release release) {
+        ResponseBo res = new ResponseBo();
+        //放行结果转报文格式字符串
+        String Content = MessageXML.ReleaseResXml(release);
+        //加密报文字符串
+        String encryptContent = "";
+        try {
+            encryptContent = AesEncryptUtil.encrypt(Content);
+        } catch (Exception e) {
+            System.out.println("----------------------------------数据加密错误:\n"+e.getMessage());
+        }
+
+        Customs customs = new Customs();
+        customs.setContent(encryptContent);
+
+        String guid = customs.getGuid();
+        String token = Md5Utils.hash(guid);
+        String tokenStr = "{\"token\":\"" + token + "\"}";
+        //请求地址
+        String url = configComponent.getReleaseUrl();
+        //拼接 url 参数
+        JSONObject variables = JSONObject.parseObject(tokenStr);
+        //请求体
+        JSONObject params = JSONObject.parseObject(JSONObject.toJSONString(customs));
+
+        /*JSONObject result = RestUtil.post(url, headers, variables, params);
+        //返回放行结果对象(暂定返回0x31报文)
+        CustomsRes customsRes = JSONObject.parseObject(result.toString(), CustomsRes.class);*/
+
+        CustomsRes customsRes = new CustomsRes();
+        customsRes.setOP_REASON("放行");
+
+        res.setCode(200);
+        res.setMsg(customsRes.getOP_REASON());
+        if(!"放行".equals(customsRes.getOP_REASON())) {
+            res.setCode(500);
+            return res;
+        }
+        return res;
+    }
+
+}

+ 11 - 0
src/main/java/com/ruoyi/web/task/TaskDTO.java

@@ -0,0 +1,11 @@
+package com.ruoyi.web.task;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+public class TaskDTO implements Serializable {
+}

+ 19 - 0
src/main/java/com/ruoyi/web/task/TokenTask.java

@@ -0,0 +1,19 @@
+package com.ruoyi.web.task;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Properties;
+
+@Component
+@Slf4j
+public class TokenTask {
+    @Scheduled(fixedDelay = 7*24*60*60*1000)
+    public void tokenTask(){
+        Properties properties = System.getProperties();
+        String rootPath = properties.getProperty("user.dir");
+        log.info("root path:{}",rootPath);
+
+    }
+}

+ 61 - 0
src/main/resources/application-druid.yml

@@ -0,0 +1,61 @@
+# 数据源配置
+spring:
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://localhost:3306/dataexchange?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                username: root
+                password: 123456
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url:
+                username:
+                password:
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置连接超时时间
+            connectTimeout: 30000
+            # 配置网络超时时间
+            socketTimeout: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter:
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /druid/*
+                # 控制台管理用户名和密码
+                login-username: ruoyi
+                login-password: 123456
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true

+ 55 - 0
src/main/resources/application.yml

@@ -0,0 +1,55 @@
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为8080
+  port: 9292
+#  servlet:
+#    # 应用的访问路径
+#    context-path: /
+
+manager:
+  host: 10.3.2.214
+  beatPort: 8050
+
+reservation:
+  reservationUrl:
+
+customs:
+  gatherUrl:
+  releaseUrl:
+
+
+bulkload:
+  LogOnUrl: http://10.3.2.214:8082/api/v1/Common/LogOn
+  LogOffUrl: http://10.3.2.214:8082/api/v1/Common/LogOff
+  InVerificationUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckIn/EnterpriseGate/Verification
+  OutVerificationUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckOut/EnterpriseGate/Verification
+  InWeightUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckIn/Weight
+  OutWeightUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckOut/Weight
+  InReportUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckIn/Report
+  OutReportUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckOut/Report
+  InWeightReportUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckIn/WeightReport
+  OutWeightReportUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckOut/WeightReport
+  PrintCountUrl: http://10.3.2.214:8082/api/v1/QZ/WMS/TruckOut/WeightReport/PrintCount
+  UserName: 90003
+  PassWord: sw10002
+  AuthToken: 7mKQSmO83s8sMhu6v+/r43LU2AXida1fZWgUL9JsG98=
+
+container:
+  SAVETRUCKINFORUrl:
+  SAVEWEIGHTINFORUrl:
+  SAVECONTAINERINFORUrl:
+  SAVEDMGCDINFORUrl:
+  GETCARDSUrl:
+  GETOUTCARDSUrl:
+  SAVEPLANINFORUrl:
+  TRUCKPASSUrl:
+  CONFIRMOUTYARDUrl:
+  GetBookingInfoUrl:
+  UpdateSealNoUrl:
+  GetDamageCodeUrl:
+  SavelDamageInfoUrl:
+
+
+intermodalTranspor:
+  appid: 189045106511183872
+  gateIdentifyUrl: https://bgwlgl.bbwport.com/api/cargo-bgwl-dispatch/dispatch/api/detect/gateIdentify.do

+ 125 - 0
src/main/resources/logback.xml

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <contextName>dataExchange</contextName>
+    <property name="LOG_PATH" value="logs"/>
+    <property name="APP_DIR" value="exchange"/>
+    <property name="log.maxFileSize" value="10MB"/>
+    <property name="log.maxFileCnt" value="90"/>
+    <property name="log.totalSizeCap" value="10GB"/>
+
+    <appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_DIR}/log_debug.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/${APP_DIR}/debug/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>${log.maxFileSize}</maxFileSize>
+            <maxHistory>${log.maxFileCnt}</maxHistory>
+            <totalSizeCap>${log.totalSizeCap}</totalSizeCap>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level{10}[%25.25thread{24}]%-40.40logger{39}|-Line:%-3L:%msg%n
+            </pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>debug</level>
+            <onMatch>ACCEPT</onMatch>
+            <!--            <onMismatch>DENY</onMismatch>-->
+        </filter>
+    </appender>
+
+    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_DIR}/log_info.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/${APP_DIR}/info/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>${log.maxFileSize}</maxFileSize>
+            <maxHistory>${log.maxFileCnt}</maxHistory>
+            <totalSizeCap>${log.totalSizeCap}</totalSizeCap>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level{10}[%25.25thread{24}]%-40.40logger{39}|-Line:%-3L:%msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!--encoder 默认配置为PatternLayoutEncoder-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd
+                HH:mm:ss.SSS}|%highlight(%-5level{10})[%boldYellow(%25.25thread{24})]%gray(%-50.50logger{49})|-Line:%boldYellow(%-3L):%msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>info</level>
+        </filter>
+    </appender>
+
+    <appender name="COMM_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_DIR}/comm_info.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/${APP_DIR}/comm/comm_info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>${log.maxFileSize}</maxFileSize>
+            <maxHistory>${log.maxFileCnt}</maxHistory>
+            <totalSizeCap>${log.totalSizeCap}</totalSizeCap>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+    <logger name="com.cryann.hsms.driver.netty" additivity="false">
+        <appender-ref ref="COMM_LOG"/>
+    </logger>
+
+    <appender name="SXFX_info_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_DIR}/SxFx_info.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/${APP_DIR}/SxFx/SxFx_info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>${log.maxFileSize}</maxFileSize>
+            <maxHistory>${log.maxFileCnt}</maxHistory>
+            <totalSizeCap>${log.totalSizeCap}</totalSizeCap>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd
+                HH:mm:ss.SSS}|%highlight(%-5level{10})[%boldYellow(%25.25thread{24})]%gray(%-50.50logger{49})|-Line:%boldYellow(%-3L):%msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <logger name="org.springframework" level="WARN"/>
+    <logger name="org.hibernate" level="WARN"/>
+    <logger name="com.apache.ibatis" level="TRACE"/>
+    <logger name="java.sql.Connection" level="DEBUG"/>
+    <logger name="java.sql.Statement" level="DEBUG"/>
+    <logger name="java.sql.PreparedStatement" level="DEBUG"/>
+
+    <!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 -->
+    <root level="INFO">
+        <!-- 生产环境将请stdout,testfile去掉 -->
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE_DEBUG"/>
+        <!--<appender-ref ref="FILEWARN"/>-->
+        <appender-ref ref="FILE_INFO"/>
+    </root>
+</configuration>