ソースを参照

init repository

lhm 3 年 前
コミット
d01c592c06

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+# mvn
+.mvn
+mvnw.cmd
+mvnw

+ 91 - 0
pom.xml

@@ -0,0 +1,91 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.4.5</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.emato.file</groupId>
+    <artifactId>tunnel</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>tunnel</name>
+    <description>Demo project for Spring Boot</description>
+    <properties>
+        <java.version>1.8</java.version>
+        <jackson.version>2.11.3</jackson.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.amqp</groupId>
+            <artifactId>spring-rabbit-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.7.5</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 19 - 0
src/main/java/com/emato/file/tunnel/TunnelApplication.java

@@ -0,0 +1,19 @@
+package com.emato.file.tunnel;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableConfigurationProperties
+@ConfigurationPropertiesScan
+@EnableScheduling
+public class TunnelApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(TunnelApplication.class, args);
+    }
+
+}

+ 149 - 0
src/main/java/com/emato/file/tunnel/config/RabbitMQConfig.java

@@ -0,0 +1,149 @@
+package com.emato.file.tunnel.config;
+
+import com.emato.file.tunnel.config.properties.RabbitMQProperties;
+import com.emato.file.tunnel.mq.callback.SimpleConfirmCallBack;
+import com.emato.file.tunnel.mq.callback.SimpleReturnsCallBack;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.amqp.core.*;
+import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
+import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitAdmin;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
+import org.springframework.amqp.support.converter.MessageConversionException;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-24 16:04
+ */
+@Configuration
+public class RabbitMQConfig {
+
+    @Autowired
+    private RabbitProperties rabbitProperties;
+
+    @Autowired
+    private RabbitMQProperties rabbitMQProperties;
+
+
+    @Bean
+    public ConnectionFactory connectionFactory () {
+        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
+        connectionFactory.setUsername(rabbitProperties.getUsername());
+        connectionFactory.setPassword(rabbitProperties.getPassword());
+        connectionFactory.setHost(rabbitProperties.getHost());
+        connectionFactory.setVirtualHost(rabbitProperties.getVirtualHost());
+        connectionFactory.setPort(rabbitProperties.getPort());
+        // 共用同一个Channel
+        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
+        // 获取配置的Channel缓存大小
+        connectionFactory.setChannelCacheSize(rabbitProperties.getCache().getChannel().getSize());
+        // 消息到达broke后触发回调
+        connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
+        return connectionFactory;
+    }
+
+    /**
+     * SimpleRabbitListenerContainerFactory发现消息中有content_type有text就会默认将其转换成string类型
+     * @return rabbit监听器容器工厂
+     */
+    @Bean
+    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
+        SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
+        containerFactory.setConnectionFactory(connectionFactory);
+        containerFactory.setConcurrentConsumers(3);
+        containerFactory.setMessageConverter(new MessageConverter() {
+            @Override
+            public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
+                return new Message(object.toString().getBytes(), messageProperties);
+            }
+
+            @Override
+            public Object fromMessage(Message message) throws MessageConversionException {
+                return new String(message.getBody());
+            }
+        });
+        containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
+        return containerFactory;
+    }
+
+    @Bean
+    public AmqpAdmin amqpAdmin (ConnectionFactory connectionFactory) {
+        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
+
+        rabbitAdmin.declareExchange(reportDirectExchange());
+        rabbitAdmin.declareExchange(receiptDirectExchange());
+
+        rabbitAdmin.declareQueue(reportQueue());
+        rabbitAdmin.declareQueue(receiptQueue());
+
+        rabbitAdmin.declareBinding(receiptBinding());
+        rabbitAdmin.declareBinding(reportBinding());
+
+        return rabbitAdmin;
+    }
+
+    /** **************************************** 回执队列相关配置 ******************************************* */
+
+    @Bean
+    public DirectExchange receiptDirectExchange () {
+        return new DirectExchange(rabbitMQProperties.getE_normal_tunnel_to_csp_receipt());
+    }
+
+    @Bean
+    public RabbitTemplate receiptRabbitTemplate (ConnectionFactory connectionFactory) {
+        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
+        rabbitTemplate.setExchange(rabbitMQProperties.getE_normal_tunnel_to_csp_receipt());
+        rabbitTemplate.setConfirmCallback(new SimpleConfirmCallBack());
+        rabbitTemplate.setReturnsCallback(new SimpleReturnsCallBack());
+        return rabbitTemplate;
+    }
+
+    @Bean
+    public Queue receiptQueue () {
+        return new Queue(rabbitMQProperties.getQ_normal_tunnel_to_csp_receipt());
+    }
+
+    @Bean
+    public Binding receiptBinding () {
+        return BindingBuilder.bind(receiptQueue()).to(receiptDirectExchange()).with(rabbitMQProperties.getK_normal_tunnel_to_csp_receipt());
+    }
+
+    /** **************************************** 回执队列相关配置完毕 ******************************************* */
+
+
+    /** **************************************** 上报报文相关配置 ******************************************* */
+
+    @Bean
+    public DirectExchange reportDirectExchange () {
+        return new DirectExchange(rabbitMQProperties.getE_normal_csp_to_tunnel_report());
+    }
+
+    @Bean
+    public RabbitTemplate reportRabbitTemplate (ConnectionFactory connectionFactory) {
+        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
+        rabbitTemplate.setExchange(rabbitMQProperties.getE_normal_csp_to_tunnel_report());
+        rabbitTemplate.setConfirmCallback(new SimpleConfirmCallBack());
+        rabbitTemplate.setReturnsCallback(new SimpleReturnsCallBack());
+        return rabbitTemplate;
+    }
+
+    @Bean
+    public Queue reportQueue () {
+        return new Queue(rabbitMQProperties.getQ_normal_csp_to_tunnel_report());
+    }
+
+    @Bean
+    public Binding reportBinding () {
+        return BindingBuilder.bind(reportQueue()).to(reportDirectExchange()).with(rabbitMQProperties.getK_normal_csp_to_tunnel_report());
+    }
+
+    /** **************************************** 上报报文相关配置完成 ******************************************* */
+}

+ 22 - 0
src/main/java/com/emato/file/tunnel/config/properties/CPortProperties.java

@@ -0,0 +1,22 @@
+package com.emato.file.tunnel.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 深关通相关配置
+ * @author lhm
+ * @version 1.0
+ * 2021-08-24 16:00
+ */
+@ConfigurationProperties(prefix = "cport")
+@Data
+public class CPortProperties {
+
+    private String pushDir;
+
+    private String pullDir;
+
+    private Integer scanHandleSize;
+
+}

+ 40 - 0
src/main/java/com/emato/file/tunnel/config/properties/RabbitMQProperties.java

@@ -0,0 +1,40 @@
+package com.emato.file.tunnel.config.properties;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-24 16:10
+ */
+@PropertySource(value = {"classpath:mq.properties"})
+@Component
+@Data
+public class RabbitMQProperties {
+
+    /**
+     * 上报的队列、交换机、路由键
+     */
+    @Value("${q.normal.csp.to.tunnel.report}")
+    private String q_normal_csp_to_tunnel_report;
+    @Value("${e.normal.csp.to.tunnel.report}")
+    private String e_normal_csp_to_tunnel_report;
+    @Value("${k.normal.csp.to.tunnel.report}")
+    private String k_normal_csp_to_tunnel_report;
+
+    /**
+     * 回执的队列、交换机、路由键
+     */
+    @Value("${q.normal.tunnel.to.csp.receipt}")
+    private String q_normal_tunnel_to_csp_receipt;
+    @Value("${e.normal.tunnel.to.csp.receipt}")
+    private String e_normal_tunnel_to_csp_receipt;
+    @Value("${k.normal.tunnel.to.csp.receipt}")
+    private String k_normal_tunnel_to_csp_receipt;
+
+
+
+}

+ 42 - 0
src/main/java/com/emato/file/tunnel/controller/TestController.java

@@ -0,0 +1,42 @@
+package com.emato.file.tunnel.controller;
+
+import com.emato.file.tunnel.scheduler.ScanPullDirectoryTask;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-25 19:10
+ */
+@RestController
+public class TestController {
+
+    @Autowired
+    private ScanPullDirectoryTask scanPullDirectoryTask;
+
+    @GetMapping("/send/{size}")
+    public String test (@PathVariable("size") int size) {
+
+        CompletableFuture.runAsync(() -> {
+            scanPullDirectoryTask.sendCEB621(size);
+        });
+
+        return "发送成功";
+    }
+
+    @GetMapping("/scan")
+    public String scan () {
+
+        CompletableFuture.runAsync(() -> {
+            scanPullDirectoryTask.scanDirectory();
+        });
+
+        return "扫描成功";
+    }
+
+}

+ 65 - 0
src/main/java/com/emato/file/tunnel/listener/ReportCEB621Listener.java

@@ -0,0 +1,65 @@
+package com.emato.file.tunnel.listener;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.UUID;
+import cn.hutool.core.util.XmlUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.emato.file.tunnel.config.properties.CPortProperties;
+import com.rabbitmq.client.Channel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.support.AmqpHeaders;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-25 10:27
+ */
+@RabbitListener(
+        containerFactory = "rabbitListenerContainerFactory",
+        queues = {"q.normal.csp.to.tunnel.report"}
+)
+@Slf4j
+@Component
+public class ReportCEB621Listener {
+
+    @Autowired
+    private CPortProperties cPortProperties;
+
+    /**
+     * 处理上报的621报文
+     * @param message   报文数据(xml字符串)
+     * @param channel   信道
+     */
+    @RabbitHandler
+    public void process (String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
+        log.info("----- 监听621报文上报, 收到消息, 开始上报! -----");
+        try {
+            JSONObject jsonObject = JSONUtil.parseObj(message);
+            String msgFileName = jsonObject.get("msgFileName", String.class);
+            String content = jsonObject.get("msgFileContent", String.class);
+            String path = cPortProperties.getPushDir()  + "\\" + msgFileName;
+            // 去除空格和换行符
+            XmlUtil.cleanInvalid(content);
+            XmlUtil.cleanComment(content);
+            File file = FileUtil.writeString(content, path, StandardCharsets.UTF_8);
+            channel.basicAck(tag, false);
+            log.info("----- 监听621报文上报, 文件: {} , 上报结束! -----", file.getName());
+        } catch (Exception e) {
+            channel.basicNack(tag, false, true);
+            log.error(String.format("----- 监听621报文上报, 上报异常! 消息已重新入队! 消息体: %s, 异常: %s -----", message, e));
+        }
+    }
+
+
+}

+ 31 - 0
src/main/java/com/emato/file/tunnel/mq/callback/SimpleConfirmCallBack.java

@@ -0,0 +1,31 @@
+package com.emato.file.tunnel.mq.callback;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.connection.CorrelationData;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-25 16:46
+ */
+@Slf4j
+public class SimpleConfirmCallBack implements RabbitTemplate.ConfirmCallback {
+
+    /**
+     * Confirmation callback.
+     *
+     * @param correlationData correlation data for the callback.
+     * @param ack             true for ack, false for nack
+     * @param cause           An optional cause, for nack, when available, otherwise null.
+     */
+    @Override
+    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
+        if (!ack) {
+            log.info("----- 消息唯一标识: {}, 确认结果: {}, 失败原因: {} -----", correlationData, ack, cause);
+        }
+    }
+}

+ 24 - 0
src/main/java/com/emato/file/tunnel/mq/callback/SimpleReturnsCallBack.java

@@ -0,0 +1,24 @@
+package com.emato.file.tunnel.mq.callback;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.ReturnedMessage;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+
+/**
+ * @author lhm
+ * @version 1.0
+ * 2021-08-25 16:49
+ */
+@Slf4j
+public class SimpleReturnsCallBack implements RabbitTemplate.ReturnsCallback {
+
+    /**
+     * Returned message callback.
+     *
+     * @param returned the returned message and metadata.
+     */
+    @Override
+    public void returnedMessage(ReturnedMessage returned) {
+        log.warn("----- 该消息: {} 没有到queue! -----", returned.toString());
+    }
+}

+ 210 - 0
src/main/java/com/emato/file/tunnel/scheduler/ScanPullDirectoryTask.java

@@ -0,0 +1,210 @@
+package com.emato.file.tunnel.scheduler;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.exceptions.UtilException;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.XmlUtil;
+import cn.hutool.json.JSONUtil;
+import com.emato.file.tunnel.config.properties.CPortProperties;
+import com.emato.file.tunnel.config.properties.RabbitMQProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.w3c.dom.Document;
+
+import javax.annotation.PostConstruct;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 扫描回执目录 定时任务
+ * @author lhm
+ * @version 1.0
+ * 2021-08-24 16:21
+ */
+@Component
+@Slf4j
+public class ScanPullDirectoryTask {
+
+    @Autowired
+    private CPortProperties cPortProperties;
+
+    @Autowired
+    private RabbitTemplate receiptRabbitTemplate;
+
+    @Autowired
+    private RabbitMQProperties rabbitMQProperties;
+
+    @Scheduled(fixedRate = 3000)
+    public void scanDirectory() {
+        String pullDir = cPortProperties.getPullDir();
+        log.info("----- 开始扫描目录: {} -----", pullDir);
+        List<String> fileNames = FileUtil.listFileNames(pullDir);
+        // 根据设置的值处理多少个文件
+        if (CollectionUtil.isEmpty(fileNames)) {
+            log.info("----- 扫描目录完成, 暂无回执文件! -----");
+            return;
+        }
+        if (fileNames.size() >= cPortProperties.getScanHandleSize()) {
+            fileNames = fileNames.subList(0, cPortProperties.getScanHandleSize());
+        }
+        // 获取回执目录的所有文件名
+        List<String> pullFilePaths = fileNames.parallelStream().map(fileName -> pullDir + "\\" + fileName).filter(fileName -> fileName.indexOf("xml") > 0).collect(Collectors.toList());
+        pullFilePaths.forEach(pullFilePath -> {
+            File file = FileUtil.file(pullFilePath);
+            HashMap<String, String> map = new HashMap<>();
+            String fileName = file.getName();
+            InputStream inputStream = null;
+            try {
+                if (file.exists()) {
+                    inputStream = new FileInputStream(file);
+                    Document document = XmlUtil.readXML(inputStream);
+                    boolean delete = file.delete();
+                    String content = XmlUtil.format(document);
+                    // 报文回执信息发送给csp
+                    map.put("fileName", fileName);
+                    map.put("fileContent", content);
+                    receiptRabbitTemplate.convertAndSend(JSONUtil.toJsonStr(map));
+                    if (!delete) {
+                        log.error("----- 文件: {}, 删除失败! -----", fileName);
+                    }
+                }
+            } catch (UtilException e) {
+                log.error("----- 解析该文件: {}, 出现错误: {} -----", fileName, e.getMessage());
+            } catch (FileNotFoundException e) {
+                log.error("----- 文件【{}】不存在! -----", pullFilePath);
+            } catch (Exception e) {
+                log.error(String.format("----- 未知异常: %s -----", e));
+            } finally {
+                if (null != inputStream) {
+                    try {
+                        inputStream.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        });
+
+        int size = CollectionUtils.isEmpty(pullFilePaths) ? 0 : pullFilePaths.size();
+        log.info("----- 扫描目录完成, 回执文件共 {} 个 -----", size);
+    }
+
+//    @Scheduled(fixedRate = 10000)
+    public void sendCEB621 (int size) {
+        String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<configuration>\n" +
+                "    <!-- 日志存放路径 -->\n" +
+                "\t<property name=\"log.path\" value=\"D:\\\\reportlogs\" />\n" +
+                "    <!-- 日志输出格式 -->\n" +
+                "\t<property name=\"log.pattern\" value=\"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n\" />\n" +
+                "\n" +
+                "\t<!-- 控制台输出 -->\n" +
+                "\t<appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n" +
+                "\t\t<encoder>\n" +
+                "\t\t\t<pattern>${log.pattern}</pattern>\n" +
+                "\t\t</encoder>\n" +
+                "\t</appender>\n" +
+                "\n" +
+                "\t<!-- 系统日志输出 -->\n" +
+                "\t<appender name=\"file_info\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n" +
+                "\t    <file>${log.path}/sys-info.log</file>\n" +
+                "        <!-- 循环政策:基于时间创建日志文件 -->\n" +
+                "\t\t<rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n" +
+                "            <!-- 日志文件名格式 -->\n" +
+                "\t\t\t<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}-%i.log</fileNamePattern>\n" +
+                "\t\t\t<!-- 日志最大的历史 60天 -->\n" +
+                "\t\t\t<maxHistory>60</maxHistory>\n" +
+                "            <!-- 分割后单个日志文件大小 -->\n" +
+                "            <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\n" +
+                "                <maxFileSize>10MB</maxFileSize>\n" +
+                "            </timeBasedFileNamingAndTriggeringPolicy>\n" +
+                "\t\t</rollingPolicy>\n" +
+                "\t\t<encoder>\n" +
+                "\t\t\t<pattern>${log.pattern}</pattern>\n" +
+                "\t\t</encoder>\n" +
+                "\t\t<filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n" +
+                "            <!-- 过滤的级别 -->\n" +
+                "            <level>INFO</level>\n" +
+                "            <!-- 匹配时的操作:接收(记录) -->\n" +
+                "            <onMatch>ACCEPT</onMatch>\n" +
+                "            <!-- 不匹配时的操作:拒绝(不记录) -->\n" +
+                "            <onMismatch>DENY</onMismatch>\n" +
+                "        </filter>\n" +
+                "\t</appender>\n" +
+                "\n" +
+                "\t<appender name=\"file_error\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n" +
+                "\t    <file>${log.path}/sys-error.log</file>\n" +
+                "        <!-- 循环政策:基于时间创建日志文件 -->\n" +
+                "        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n" +
+                "            <!-- 日志文件名格式 -->\n" +
+                "            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}-%i.log</fileNamePattern>\n" +
+                "\t\t\t<!-- 日志最大的历史 60天 -->\n" +
+                "\t\t\t<maxHistory>60</maxHistory>\n" +
+                "            <!-- 分割后单个日志文件大小 -->\n" +
+                "            <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\n" +
+                "                <maxFileSize>10MB</maxFileSize>\n" +
+                "            </timeBasedFileNamingAndTriggeringPolicy>\n" +
+                "        </rollingPolicy>\n" +
+                "        <encoder>\n" +
+                "            <pattern>${log.pattern}</pattern>\n" +
+                "        </encoder>\n" +
+                "        <filter class=\"ch.qos.logback.classic.filter.LevelFilter\">\n" +
+                "            <!-- 过滤的级别 -->\n" +
+                "            <level>ERROR</level>\n" +
+                "\t\t\t<!-- 匹配时的操作:接收(记录) -->\n" +
+                "            <onMatch>ACCEPT</onMatch>\n" +
+                "\t\t\t<!-- 不匹配时的操作:拒绝(不记录) -->\n" +
+                "            <onMismatch>DENY</onMismatch>\n" +
+                "        </filter>\n" +
+                "    </appender>\n" +
+                "\n" +
+                "\t<!-- Spring日志级别控制  -->\n" +
+                "\t<logger name=\"org.springframework\" level=\"warn\" />\n" +
+                "\n" +
+                "\t<root level=\"info\">\n" +
+                "\t\t<appender-ref ref=\"console\" />\n" +
+                "\t</root>\n" +
+                "\n" +
+                "\t<!--系统操作日志-->\n" +
+                "    <root level=\"info\">\n" +
+                "        <appender-ref ref=\"file_info\" />\n" +
+                "        <appender-ref ref=\"file_error\" />\n" +
+                "    </root>\n" +
+                "</configuration>\n";
+        content = content.replaceAll("\n", "");
+        content = content.replaceAll("\t", "");
+        content = new String(content.getBytes(), StandardCharsets.UTF_8);
+        for (int i = 0; i < size; i++) {
+//            reportRabbitTemplate.convertAndSend(rabbitMQProperties.getK_normal_csp_to_tunnel_report(), content);
+        }
+        log.info("----- 发送完成 -----");
+    }
+
+    @PostConstruct
+    public void init () {
+
+        String pullDir = cPortProperties.getPullDir();
+        String pushDir = cPortProperties.getPushDir();
+
+        File pullDirName = new File(pullDir);
+        File pushDirName = new File(pushDir);
+
+        if (!pullDirName.exists()) {
+            pullDirName.mkdirs();
+            log.info("----- 检测到目录未创建, 创建目录: {} -----", pullDirName);
+        }
+
+        if (!pushDirName.exists()) {
+            pushDirName.mkdirs();
+            log.info("----- 检测到目录未创建, 创建目录: {} -----", pushDirName);
+        }
+    }
+
+}

+ 27 - 0
src/main/resources/application.properties

@@ -0,0 +1,27 @@
+server.port=11111
+# \u5F00\u53D1\u73AF\u5883mq\u76F8\u5173\u914D\u7F6E
+spring.rabbitmq.port=5672
+spring.rabbitmq.host=127.0.0.1
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+spring.rabbitmq.virtual-host=/
+# \u6D4B\u8BD5\u73AF\u5883mq\u76F8\u5173\u914D\u7F6E
+#spring.rabbitmq.port=5672
+#spring.rabbitmq.host=120.24.174.90
+#spring.rabbitmq.username=admin
+#spring.rabbitmq.password=Abc-123#
+#spring.rabbitmq.virtual-host=/
+
+# \u5FC3\u8DF3\u8D85\u65F6\u65F6\u95F4 \u5355\u4F4D: s
+spring.rabbitmq.requested-heartbeat=60
+# \u4FE1\u9053\u53EF\u7F13\u5B58\u7684\u6D88\u606F\u4E2A\u6570
+spring.rabbitmq.cache.channel.size=50
+
+# \u6DF1\u5173\u901A\u626B\u63CF\u7684\u76EE\u5F55
+#cport.push_dir=H:\\lhm\\push
+cport.push_dir=D:\\report\\report
+# \u6DF1\u5173\u901A\u56DE\u6267\u76EE\u5F55
+#cport.pull_dir=H:\\lhm\\pull
+cport.pull_dir=D:\\report\\receipt
+# \u8BBE\u7F6E\u56DE\u6267\u5355\u6B21\u5904\u7406\u7684\u4E2A\u6570
+cport.scan_handle_size=90

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

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="D:\\reportlogs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}-%i.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+            <!-- 分割后单个日志文件大小 -->
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>10MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}-%i.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+            <!-- 分割后单个日志文件大小 -->
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>10MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+</configuration>

+ 15 - 0
src/main/resources/mq.properties

@@ -0,0 +1,15 @@
+########################## \u6D88\u606F\u961F\u5217\u914D\u7F6E ############################
+#                 \u4E0A\u62A5\u961F\u5217                  #
+q.normal.csp.to.tunnel.report=q.normal.csp.to.tunnel.report
+#                 \u56DE\u6267\u961F\u5217                  #
+q.normal.tunnel.to.csp.receipt=q.normal.tunnel.to.csp.receipt
+
+#                 \u4E0A\u62A5\u4EA4\u6362\u673A                  #
+e.normal.csp.to.tunnel.report=e.normal.csp.to.tunnel.report
+#                 \u56DE\u6267\u4EA4\u6362\u673A                  #
+e.normal.tunnel.to.csp.receipt=e.normal.tunnel.to.csp.receipt
+
+#                 \u4E0A\u62A5\u961F\u5217\u8DEF\u7531\u952E                  #
+k.normal.csp.to.tunnel.report=k.normal.csp.to.tunnel.report
+#                 \u56DE\u6267\u961F\u5217\u8DEF\u7531\u952E                  #
+k.normal.tunnel.to.csp.receipt=k.normal.tunnel.to.csp.receipt

+ 13 - 0
src/test/java/com/emato/file/tunnel/TunnelApplicationTests.java

@@ -0,0 +1,13 @@
+package com.emato.file.tunnel;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TunnelApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}