Index: pom.xml
===================================================================
--- pom.xml	(版本 31411)
+++ pom.xml	(版本 31412)
@@ -14,9 +14,16 @@
 		<artifactId>spring-boot-starter-parent</artifactId>
 		<version>1.4.1.RELEASE</version>
 	</parent>
+
+
 	<dependencies>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-actuator</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
 		<!-- Redis -->
@@ -49,8 +56,11 @@
 			<artifactId>mybatis-spring-boot-starter</artifactId>
 			<version>1.1.1</version>
 		</dependency>
-		<!-- SqlServer <dependency> <groupId>com.microsoft.sqlserver</groupId> 
-			<artifactId>mssql-jdbc</artifactId> <version>6.4.0.jre8</version> </dependency> -->
+		<dependency>
+			<groupId>com.microsoft.sqlserver</groupId>
+			<artifactId>mssql-jdbc</artifactId>
+			<version>6.4.0.jre8</version>
+		</dependency>
 		<!-- MySql数据库驱动 -->
 		<dependency>
 			<groupId>mysql</groupId>
@@ -126,22 +136,11 @@
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-context-support</artifactId>
 		</dependency>
-		<!-- 爬取工具 -->
-		<dependency>
-			<groupId>us.codecraft</groupId>
-			<artifactId>webmagic-core</artifactId>
-			<version>0.7.3</version>
-		</dependency>
-		<dependency>
-			<groupId>us.codecraft</groupId>
-			<artifactId>webmagic-extension</artifactId>
-			<version>0.7.3</version>
-		</dependency>
 		<!-- activemq -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-activemq</artifactId>
-		</dependency>
+<!--		<dependency>-->
+<!--			<groupId>org.springframework.boot</groupId>-->
+<!--			<artifactId>spring-boot-starter-activemq</artifactId>-->
+<!--		</dependency>-->
 		<!-- jwt -->
 		<dependency>
 			<groupId>com.nimbusds</groupId>
@@ -184,7 +183,38 @@
 			<artifactId>pagehelper</artifactId>
 			<version>5.1.6</version>
 		</dependency>
-
+		<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
+		<dependency>
+			<groupId>org.mapstruct</groupId>
+			<artifactId>mapstruct</artifactId>
+			<version>1.4.2.Final</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
+		<dependency>
+			<groupId>org.mapstruct</groupId>
+			<artifactId>mapstruct-processor</artifactId>
+			<version>1.4.2.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>com.lowagie</groupId>
+			<artifactId>itext</artifactId>
+			<version>2.1.7</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.jasperreports</groupId>
+			<artifactId>jasperreports</artifactId>
+			<version>6.8.0</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.lowagie</groupId>
+					<artifactId>itext</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-amqp</artifactId>
+		</dependency>
 		<!-- ftp -->
 		<dependency>
 			<groupId>commons-net</groupId>
@@ -207,7 +237,6 @@
 			<artifactId>dom4j</artifactId>
 			<version>2.1.3</version>
 		</dependency>
-
 	</dependencies>
 	<build>
 		<plugins>
@@ -270,6 +299,14 @@
 				<filtering>false</filtering>
 			</resource>
 			<resource>
+				<directory>src/main/java/com/novaone/fdao/Mapper</directory>
+				<targetPath>com/novaone/fdao/Mapper</targetPath>
+				<includes>
+					<include>**/*.xml</include>
+				</includes>
+				<filtering>false</filtering>
+			</resource>
+			<resource>
 				<directory>src/main/resources</directory>
 				<includes>
 					<include>**/**</include>
Index: .
===================================================================
--- .	(版本 31411)
+++ .	(版本 31412)

Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1 ##
+*.idea
Index: src/main/java/com/novaone/common/Application.java
===================================================================
--- src/main/java/com/novaone/common/Application.java	(版本 31411)
+++ src/main/java/com/novaone/common/Application.java	(版本 31412)
@@ -23,7 +23,6 @@
 @SpringBootApplication
 @EnableTransactionManagement
 @ComponentScan(basePackages={"com.novaone"})//指定spring管理的bean所在的包
-@MapperScan("com.novaone.dao")//指定mybatis的mapper接口所在的包
 @ServletComponentScan
 @EnableScheduling
 public class Application {
Index: src/main/java/com/novaone/common/DruidDataSourceConfiguration.java
===================================================================
--- src/main/java/com/novaone/common/DruidDataSourceConfiguration.java	(版本 31411)
+++ src/main/java/com/novaone/common/DruidDataSourceConfiguration.java	(版本 31412)
@@ -4,6 +4,8 @@
 
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -12,20 +14,24 @@
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 
 import com.alibaba.druid.pool.DruidDataSource;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
 @Configuration
+@MapperScan(basePackages = {"com.novaone.dao"}, sqlSessionFactoryRef = "mysqlSqlSessionFactory")//指定mybatis的mapper接口所在的包
 public class DruidDataSourceConfiguration {
+
     @Bean("druidDataSource")
-	@ConfigurationProperties(prefix = "spring.datasource")
+    @Primary
+    @ConfigurationProperties(prefix = "spring.datasource.druid.master")
 	public DataSource druidDataSource(){
 		DataSource druidDataSource = new DruidDataSource();
 		return druidDataSource;
 	}
 
-    @Bean(name = "vopSqlSessionFactory")
+    @Bean(name = "mysqlSqlSessionFactory")
     @Qualifier
     @Primary
-    public SqlSessionFactory vopSqlSessionFactory(@Qualifier("druidDataSource") DataSource druidDataSource)
+    public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("druidDataSource") DataSource druidDataSource)
             throws Exception {
         final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
         sessionFactoryBean.setDataSource(druidDataSource);
@@ -36,4 +42,18 @@
 
         return sessionFactoryBean.getObject();
     }
+
+    @Bean(name = "mysqlTransactionManager") // 配置事务
+    @Primary
+    public DataSourceTransactionManager mysqlTransactionManager(
+            @Qualifier("druidDataSource") DataSource druidDataSource) {
+        return new DataSourceTransactionManager(druidDataSource);
+    }
+
+    @Bean(name = "mysqlSqlSessionTemplate")
+    @Primary
+    public SqlSessionTemplate testSqlSessionTemplate(
+            @Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
+        return new SqlSessionTemplate(sqlSessionFactory);
+    }
 }
Index: src/main/java/com/novaone/common/FruiteaseDBConfig.java
===================================================================
--- src/main/java/com/novaone/common/FruiteaseDBConfig.java	(不存在的)
+++ src/main/java/com/novaone/common/FruiteaseDBConfig.java	(版本 31412)
@@ -0,0 +1,58 @@
+package com.novaone.common;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+
+import javax.sql.DataSource;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-05-27 16:42
+ */
+@Configuration
+@MapperScan(basePackages = { "com.novaone.fdao" }, sqlSessionFactoryRef = "fruiteaseSqlSessionFactory")
+public class FruiteaseDBConfig {
+    @Bean("fruiteaseDataSource")
+    @ConfigurationProperties(prefix = "spring.datasource.druid.slave")
+    public DataSource fruiteaseDataSource() {
+        return new DruidDataSource();
+    }
+
+    @Bean(name = "fruiteaseSqlSessionFactory")
+    public SqlSessionFactory fruiteaseSqlSessionFactory(
+            @Qualifier("fruiteaseDataSource") DataSource fruiteaseDataSource) throws Exception {
+        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
+        sessionFactoryBean.setDataSource(fruiteaseDataSource);
+        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
+        sessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/com/novaone/fdao/Mapper/*.xml"));
+        sessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
+
+        return sessionFactoryBean.getObject();
+    }
+
+    @Bean(name = "fruiteaseTransactionManager") // 配置事务
+    public DataSourceTransactionManager fruiteaseTransactionManager(
+            @Qualifier("fruiteaseDataSource") DataSource fruiteaseDataSource) {
+        return new DataSourceTransactionManager(fruiteaseDataSource);
+    }
+
+
+
+
+    @Bean(name = "fruiteaseSqlSessionTemplate")
+    public SqlSessionTemplate fruiteaseSqlSessionTemplate(
+            @Qualifier("fruiteaseSqlSessionFactory") SqlSessionFactory fruiteaseSqlSessionFactory) {
+        return new SqlSessionTemplate(fruiteaseSqlSessionFactory);
+    }
+}
Index: src/main/java/com/novaone/common
===================================================================
--- src/main/java/com/novaone/common	(版本 31411)
+++ src/main/java/com/novaone/common	(版本 31412)

Property changes on: src/main/java/com/novaone/common
___________________________________________________________________
Added: svn:ignore
## -0,0 +1 ##
+TestUtils.java
Index: src/main/java/com/novaone/common/exception/BocB2eException.java
===================================================================
--- src/main/java/com/novaone/common/exception/BocB2eException.java	(不存在的)
+++ src/main/java/com/novaone/common/exception/BocB2eException.java	(版本 31412)
@@ -0,0 +1,173 @@
+package com.novaone.common.exception;
+
+
+import org.apache.commons.lang3.StringUtils;
+
+/*
+ * @program: BankEnterprise
+ * @description: 发起Bocb2e请求异常
+ * @author: Ma.ChengJian
+ * @create: 2022-02-10 14:43
+ */
+public class BocB2eException extends RuntimeException {
+
+    /**
+     * 接口编号
+     */
+    private String apiNo;
+    /**
+     * 请求报文
+     */
+    private String reqXml;
+    /**
+     * 响应报文
+     */
+    private String respXml;
+    /**
+     * 状态码
+     */
+    private String rspCode;
+    /**
+     * 状态描述
+     */
+    private String rspMsg;
+
+    public BocB2eException() {
+    }
+
+    public BocB2eException(String message,String apiNo) {
+        super(message);
+        this.apiNo = apiNo;
+    }
+
+    public BocB2eException(BocB2eExceptionBuilder builder){
+        super(builder.message,builder.cause);
+        this.apiNo = builder.apiNo;
+        this.reqXml = builder.reqXml;
+        this.respXml = builder.respXml;
+        this.rspCode = builder.respCode;
+        this.rspMsg = builder.respMsg;
+    }
+
+    public BocB2eException(Throwable cause) {
+        super(cause);
+    }
+
+    public BocB2eException(String apiNo,String reqXml, String respXml) {
+        this.reqXml = reqXml;
+        this.respXml = respXml;
+        this.apiNo = apiNo;
+    }
+
+    public BocB2eException(String apiNo,String message, String reqXml, String respXml) {
+        super(message);
+        this.reqXml = reqXml;
+        this.respXml = respXml;
+        this.apiNo = apiNo;
+    }
+
+    public BocB2eException(String message, Throwable cause, String reqXml) {
+        super(message, cause);
+        this.reqXml = reqXml;
+    }
+
+    public BocB2eException(String message, Throwable cause, String reqXml, String respXml) {
+        super(message, cause);
+        this.reqXml = reqXml;
+        this.respXml = respXml;
+    }
+
+    public BocB2eException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public static BocB2eExceptionBuilder builder(){
+        return new BocB2eExceptionBuilder();
+    }
+
+    public static class BocB2eExceptionBuilder{
+
+        private String apiNo;
+
+        private String reqXml;
+
+        private String respXml;
+
+        private String respCode;
+
+        private String respMsg;
+
+        private String message;
+
+        private Throwable cause;
+
+
+        public BocB2eExceptionBuilder() {
+
+        }
+
+        public BocB2eExceptionBuilder respCode(String respCode){
+            this.respCode = respCode;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder respMsg(String respMsg){
+            this.respMsg = respMsg;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder message(String message){
+            this.message = message;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder cause(Throwable cause){
+            this.cause = cause;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder reqXml(String reqXml){
+            this.reqXml = reqXml;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder respXml(String respXml){
+            this.respXml = respXml;
+            return this;
+        }
+
+        public BocB2eExceptionBuilder apiNo(String apiNo){
+            this.apiNo = apiNo;
+            return this;
+        }
+
+        public BocB2eException build(){
+            this.message = StringUtils.isNotEmpty(this.respCode) && StringUtils.isNotEmpty(this.respMsg)?this.message+"(respCode:"+this.respCode+" respmsg:"+this.respMsg+")":this.message;
+            return new BocB2eException(this);
+        }
+    }
+
+    public String getApiNo() {
+        return apiNo;
+    }
+
+    public void setApiNo(String apiNo) {
+        this.apiNo = apiNo;
+    }
+
+    public String getReqXml() {
+        return reqXml;
+    }
+
+    public void setReqXml(String reqXml) {
+        this.reqXml = reqXml;
+    }
+
+    public String getRespXml() {
+        return respXml;
+    }
+
+    public void setRespXml(String respXml) {
+        this.respXml = respXml;
+    }
+}
Index: src/main/java/com/novaone/common/exception/BusinessException.java
===================================================================
--- src/main/java/com/novaone/common/exception/BusinessException.java	(不存在的)
+++ src/main/java/com/novaone/common/exception/BusinessException.java	(版本 31412)
@@ -0,0 +1,91 @@
+package com.novaone.common.exception;
+
+import com.novaone.entity.TradingRecord;
+import lombok.Data;
+
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-06-06 16:45
+ */
+@Data
+public class BusinessException extends RuntimeException{
+
+
+    private Type type;
+
+    private TradingRecord businessDate;
+
+    public BusinessException(Type type) {
+        this.type = type;
+    }
+
+    public BusinessException(String message, Type type) {
+        super(message);
+        this.type = type;
+    }
+
+    public BusinessException(String message, Throwable cause, Type type) {
+        super(message, cause);
+        this.type = type;
+    }
+
+    public BusinessException(String message, Throwable cause, Type type, TradingRecord date) {
+        super(message, cause);
+        this.type = type;
+        this.businessDate = date;
+    }
+
+    public BusinessException(Throwable cause, Type type) {
+        super(cause);
+        this.type = type;
+    }
+
+    /**
+     * 状态类型
+     */
+    public enum Type {
+        /**
+         * 水果推送失败
+         */
+        SQLSERVER_ERR(0),
+        /**
+         * MySQL异常
+         */
+        MYSQL_ERR(1),
+        /**
+         * 接口调用失败
+         */
+        API_CALL_FAIL(2),
+        /**
+         * 回单处理失败;上传失败
+         */
+        BANK_BILL_FAIL(3),
+        /**
+         * 找不到回单信息
+         */
+        NO_SUCH_BILL_INFO(4),
+        /**
+         * 没有回单模板
+         */
+        UNKNOWN_TEMPLATE(5),
+        /**
+         * 银行接口未知的响应状态码
+         */
+        UNKNOWN_RESPONSE(6);
+
+        private final int value;
+
+        Type(int value) {
+            this.value = value;
+        }
+
+        public int value() {
+            return this.value;
+        }
+
+    }
+}
Index: src/main/java/com/novaone/common/exception/ValidationException.java
===================================================================
--- src/main/java/com/novaone/common/exception/ValidationException.java	(不存在的)
+++ src/main/java/com/novaone/common/exception/ValidationException.java	(版本 31412)
@@ -0,0 +1,49 @@
+package com.novaone.common.exception;
+
+import com.novaone.entity.BocB2eParam;
+
+import java.util.List;
+import java.util.Map;
+
+/*
+ * @program: BankEnterprise
+ * @description: Bocb2e请求参参数校验异常
+ * @author: Ma.ChengJian
+ * @create: 2022-01-25 10:39
+ */
+public class ValidationException extends RuntimeException{
+
+    private Map<BocB2eParam, Map<String, String>>  result;
+
+    private String apiNo;
+
+    public ValidationException() {
+    }
+
+    public ValidationException(String message, Map<BocB2eParam, Map<String, String>>  result, String apiNo) {
+        super(message);
+        this.result = result;
+        this.apiNo = apiNo;
+    }
+
+    public ValidationException(Map<BocB2eParam, Map<String, String>>  result, String apiNo) {
+        this.result = result;
+        this.apiNo = apiNo;
+    }
+
+    public Map<BocB2eParam, Map<String, String>>  getResult() {
+        return result;
+    }
+
+    public void setResult(Map<BocB2eParam, Map<String, String>>  result) {
+        this.result = result;
+    }
+
+    public String getApiNo() {
+        return apiNo;
+    }
+
+    public void setApiNo(String apiNo) {
+        this.apiNo = apiNo;
+    }
+}
Index: src/main/java/com/novaone/control/PayController.java
===================================================================
--- src/main/java/com/novaone/control/PayController.java	(版本 31411)
+++ src/main/java/com/novaone/control/PayController.java	(版本 31412)
@@ -7,38 +7,68 @@
  * @create: 2022-01-25 09:28
  */
 
+import com.alibaba.fastjson.JSONArray;
 import com.novaone.entity.JsonModel;
-import com.novaone.entity.PayVO;
-import com.novaone.service.BOCPayService;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.BOCClient;
 import com.novaone.service.BocInterfaceService;
 import com.novaone.service.ConfigService;
-import com.novaone.util.B2e0043Type;
+import com.novaone.payment.MessageSender;
+import com.novaone.service.PayService;
+import com.novaone.payment.B2eValidateUtil;
 import com.novaone.util.CommonEnum;
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
 
 @Slf4j
 @RestController
 @RequestMapping("/pay")
-public class PayController {
+@Data
+public class  PayController {
     @Resource
-    private BOCPayService bocPayService;
+    private BOCClient bocPayService;
     @Resource
     private BocInterfaceService bocInterfaceService;
     @Resource
-    private ConfigService configService;
+    private PayService payService;
+    @Resource
+    private MessageSender send;
+    @Resource
+    private BOCClient client;
 
+    public static final String STATE_QUEUE = "stateQuery";
 
+    public static final String BILL_CREAT = "billCreate";
+
+
     /**
-     * 通过中行账户支付
+     * 交易状态查询队列、回单生成队列消息推送
      */
-    @PostMapping
-    public JsonModel payment(@RequestBody PayVO vo){
-        bocPayService.payment(vo);
-        return null;
+    @PostMapping("/message")
+    public JsonModel sendMessage(HttpServletRequest request){
+        //要推送的消息类型
+        final String[] msgTypes = request.getParameterValues("msgType");
+        log.info("手动重推消息类型:" + Arrays.toString(msgTypes));
+        if (msgTypes == null) {
+            return error("msgType 不得为空，可选值:" + STATE_QUEUE +"," + BILL_CREAT);
+        }
+        for (String s : msgTypes) {
+            if (!s.equals(STATE_QUEUE) && !s.equals(BILL_CREAT)) {
+                return error("msgType 包含错误的值，正确的可选值：" + STATE_QUEUE +"," + BILL_CREAT + " 您传递的msgType:" + Arrays.toString(msgTypes));
+            }
+        }
+        try {
+            return succeed("处理成功，已返回推送的消息数据",payService.resendMesg(Arrays.asList(msgTypes)));
+        } catch (Exception e) {
+            log.error("消息推送失败",e);
+            return error(e.getMessage());
+        }
     }
 
 
@@ -45,14 +75,46 @@
     /**
      * 查询中行账户余额
      */
-    @PostMapping("/balance")
-    public JsonModel balanceEnquiry(@RequestBody PayVO vo){
-        return null;
+    @GetMapping("/balance/{acc}")
+    public JsonModel balanceEnquiry(@PathVariable String acc){
+        try {
+            log.info("处理账户余额查询请求 账号:{}",acc);
+            BocB2eParam bocB2eParam = new BocB2eParam();
+            bocB2eParam.setPayAcc(acc);
+            return succeed("账户余额查询查操作成功",bocPayService.balanceEnquiry(Collections.singletonList(bocB2eParam)).getJSONObject(0));
+        } catch (Exception e) {
+            log.error("余额查询失败",e);
+            return error(e.getMessage());
+        }
     }
 
 
     /**
+     * 查询交易处理状态
+     * @param payNo 交易业务编号
+     */
+    @PostMapping("/status")
+    public JsonModel queryTransactionStatus(@RequestParam String [] payNo){
+        log.info("处理交易状态查询请求,查询交易业务员编号:" + Arrays.toString(payNo));
+        List<BocB2eParam> params = new ArrayList<>();
+        for (String s : payNo) {
+            final BocB2eParam param = new BocB2eParam();
+            param.setPayNo(s);
+            params.add(param);
+        }
+        try {
+            return succeed(client.queryPayState(params));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return error(e.getMessage());
+        }
+    }
+
+
+
+    /**
      * 调用B2e0043接口，CNAPS、中行机构号文件下载
+     * 文件下载之前置机D盘
      * @param type 下载的文件类型
      *            0：CNAPS文件
      *            1：中行机构号文件
@@ -60,13 +122,10 @@
      */
     @GetMapping("/download/{type}")
     public JsonModel download(@PathVariable("type") String type){
-        JsonModel res = new JsonModel();
         //校验参数，必须符合要求文件类型
-        if (!checkType(type)){
-            res.setCode(CommonEnum.API_ERROR.getCode());
-            res.setDes(CommonEnum.API_ERROR.getRes() + ":传递的参数不符合要求");
+        if (!B2eValidateUtil.checkType(type)){
             log.error("传递了错误的参数type = {}",type);
-            return res;
+            return error(CommonEnum.API_ERROR.getRes() + ":传递的参数不符合要求");
         }
         //前置机校验并签到
         try {
@@ -73,30 +132,45 @@
             bocInterfaceService.checkConfig();
             bocInterfaceService.sign();
         } catch (Exception e) {
-            res.setCode(CommonEnum.API_ERROR.getCode());
-            res.setDes(CommonEnum.API_ERROR.getRes() + ":" + e.getMessage());
             log.error("前置机校验失败",e);
-            return res;
+            return error(CommonEnum.API_ERROR.getRes() + ":" + e.getMessage());
         }
         //文件下载
         try {
-            bocPayService.download(type);
-            res.setCode(CommonEnum.API_SUCCESS.getCode());
-            res.setDes(CommonEnum.API_SUCCESS.getRes());
+            bocPayService.fileDownload(type);
+            return succeed();
         } catch (Exception e) {
-            res.setCode(CommonEnum.API_ERROR.getCode());
-            res.setDes(CommonEnum.API_ERROR.getRes() + ":" + e.getMessage());
             log.error("b2e0043接口调用失败",e);
+            return error(CommonEnum.API_ERROR.getRes() + ":" + e.getMessage());
         }
-        return res;
     }
 
-    private boolean checkType(String type){
-        for (B2e0043Type value : B2e0043Type.values()) {
-            if (value.getType().equals(type)) {
-                return true;
-            }
-        }
-        return false;
+
+
+    JsonModel error(String message){
+        JsonModel resp = new JsonModel();
+        resp.setCode(CommonEnum.API_ERROR.getCode());
+        resp.setDes(CommonEnum.API_ERROR.getRes() + ":" + message);
+        return resp;
     }
+
+    JsonModel succeed(){
+        return succeed(CommonEnum.API_SUCCESS.getRes(),null);
+    }
+
+    JsonModel succeed(String message){
+        return succeed(message,null);
+    }
+
+    JsonModel succeed(Object data){
+        return succeed(CommonEnum.API_SUCCESS.getRes(),data);
+    }
+
+    JsonModel succeed(String message,Object data){
+        JsonModel resp = new JsonModel();
+        resp.setCode(CommonEnum.API_SUCCESS.getCode());
+        resp.setDes(message);
+        resp.setResult(data);
+        return resp;
+    }
 }
Index: src/main/java/com/novaone/control
===================================================================
--- src/main/java/com/novaone/control	(版本 31411)
+++ src/main/java/com/novaone/control	(版本 31412)

Property changes on: src/main/java/com/novaone/control
___________________________________________________________________
Added: svn:ignore
## -0,0 +1 ##
+BillDownController.java
Index: src/main/java/com/novaone/dao/ConfigBocMapper.java
===================================================================
--- src/main/java/com/novaone/dao/ConfigBocMapper.java	(版本 31411)
+++ src/main/java/com/novaone/dao/ConfigBocMapper.java	(版本 31412)
@@ -4,6 +4,7 @@
 
 import com.novaone.entity.ConfigBoc;
 import com.novaone.entity.VO.ConfigBocVO;
+import org.apache.ibatis.annotations.Select;
 
 public interface ConfigBocMapper {
     int deleteByPrimaryKey(String id);
@@ -21,4 +22,11 @@
     List<ConfigBocVO> selectAll();
     
     ConfigBocVO selectOne();
+
+    /**
+     * 查询测试环境前置机的连接信息
+     * @return
+     */
+    @Select("SELECT c.id, termid, trnid, custid, protocol, ip, PORT, USER, PASSWORD, bank_id, serialnum, NAME, CODE FROM config_boc c INNER JOIN bank ON bank.id = c.bank_id WHERE c.id = 'test_20220317'")
+    ConfigBocVO selectTest();
 }
\ No newline at end of file
Index: src/main/java/com/novaone/dao/Mapper
===================================================================
--- src/main/java/com/novaone/dao/Mapper	(版本 31411)
+++ src/main/java/com/novaone/dao/Mapper	(版本 31412)

Property changes on: src/main/java/com/novaone/dao/Mapper
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,3 ##
+test
+TransactionRecordMapper.xml
+PaymentMapper.xml
Index: src/main/java/com/novaone/entity/BocB2eParam.java
===================================================================
--- src/main/java/com/novaone/entity/BocB2eParam.java	(不存在的)
+++ src/main/java/com/novaone/entity/BocB2eParam.java	(版本 31412)
@@ -0,0 +1,219 @@
+package com.novaone.entity;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.novaone.common.exception.BocB2eException;
+import com.novaone.test.enttity.Payment;
+import com.novaone.test.enttity.TransactionRecord;
+import lombok.*;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.Range;
+import org.springframework.validation.BindException;
+
+import javax.validation.constraints.Pattern;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Optional;
+
+/*
+ * @program: BankEnterprise
+ * @description: BocB2e接口请求参数
+ * @author: Ma.ChengJian
+ * @create: 2022-01-21 18:01
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BocB2eParam implements Serializable {
+
+    /**
+     * 该对象对应的付款信息在MySQL中的数据
+     */
+    private TradingRecord record;
+    /**
+     * 申请单编号
+     */
+    private String payBillNo;
+
+
+
+    /**
+     * 支付接口基本参数。
+     */
+    //客户业务编号（转账指令ID）。
+    private String payNo;
+    //客户处理优先级
+    private String cusPriority;
+    //银行处理优先级
+    private String bankPriority;
+    //要求转账日期
+    @JSONField(format = "yyyy-MM-dd")
+    private Date scheduleDate;
+    //收款行是否中行。1:中行;0:他行
+    private String bocflag;
+    //附言
+    String furinfo;
+
+
+    /**
+     * 付款信息信息
+     */
+    //付款行联行号
+    private String payBankNo;
+    //付款行账号/查询余额的账号
+    @Pattern(regexp = "\\w{1,20}",message = "付款账号必须为1-20位字符串")
+    private String payAcc;
+    //付款人名称
+    @Length(max = 70, message = "付款人名称必须为0-70位字符串")
+    private String payName;
+    //付款金额
+    @Pattern(regexp = "^[0-9]{1,13}(.[0-9]{1,2})?$",message = "付款金额最多13位整数，最多2位小数。")
+    private String payMoney;
+    //支付货币类型。只支持人民币
+    private final String payCurrency = "CNY";
+
+
+    /**
+     * 收款信息
+     */
+    //收款行联行号
+    private String payeeBankNo;
+    //收款账号
+    private String payeeAcc;
+    //收款人名称
+    private String payeeName;
+    //收款人类型  00：对公  04：对私
+    private String payeeType;
+    //收款人开户行名称
+    private String payeeBankName;
+
+    public static class PayeeType{
+
+        public static final String CORPORATE = "00";
+
+        public static final String PRIVATE = "04";
+
+    }
+
+    /**
+     * 逐笔电子回单下载(b2e0500)部分参数
+     * 查询的对方账户信息以及付款账户信息，使用付款人信息和收款人信息中的参数。
+     */
+    //交易类型：A-全部、C-来账、D-往账、N-内部往来、P部分内部往来、W外部交易
+    private String billDownTratype;
+    //起始日期.非空数字8位，格式YYYYMMDD
+    private Date billDownFrom;
+    //截止日期.非空数字8位，格式YYYYMMDD
+    private Date billDownTo;
+    //金额下限
+    private String billDownMinamt;
+    //金额上线
+    private String billDownMaxamt;
+    //逐笔电子回单下载时的客户申请号。为空时默认为：当前时间毫秒值_bill
+    private String billDownInsid;
+    //无交易时是否生成空回单文件。Y：是 N：否，可空，默认Y。
+    private String billDownEmptyflag;
+    //查询返回参数，电子回单zip文件名称
+    private String billDownZipName;
+
+
+    /**
+     * 电子回单查询(b2e0531)部分参数
+     * 查询账号、联行号使用付款信息中的字段。
+     * 交易类型、日期范围、金额范围使用逐笔电子回单下载参数。
+     * 对方账户信息参数使用收款人信息参数。
+     *
+     */
+    //电子回单信息中国的交易流水号
+    private String billSerialNo;
+    //开始位置
+    private int billBeginNum = 1;
+    private boolean billHasNext = false;
+
+
+    /**
+     * 今日及历史交易信息查询的部分参数。
+     * 查询账号参数和联行号参数，使用付款人信息中的付款账号参数和联行号参数。
+     */
+    //查询的账单类型。
+    private String historicalType;
+    //查询的时间上限
+    private Date historicalDateFrom;
+    //查询的时间下限
+    private Date historicalDateTo;
+    //查询的金额上线
+    private String historicalMoneyFrom;
+    //查询的金额下限
+    private String historicalMoneyTo;
+    //查询的交易起始位置
+    private int historicalBeginNum = 1;
+    //查询数量
+    private String historicalRecnum = "50";
+    //来往账标识
+    private String historicalDirection = "0";
+
+    private boolean historicalHasNext = false;
+
+    public static class HistoricalType{
+
+        public static final String CURRENT = "2001";
+
+        public static final String HISTORICAL = "2002";
+
+        public static final String YESTERDAY = "2005";
+
+    }
+
+
+    /**
+     *  CNAPS、中行机构号文件下载(b2e0043)接口部分参数
+     *  tasktpy：下载文件类型。0：CNAPS文件、1：中行机构号文件、2：电子汇票行号文件。
+     */
+    private  String tasktpy;
+
+    /**
+     * 交易状态查询(b2e0007)参数
+     * obssid：网银交易流水号。
+     */
+    private String obssid;
+
+
+    public static BocB2eParam getInstance(Payment pay){
+        final BocB2eParam param = new BocB2eParam();
+        param.setPayName(pay.getPayName());
+        param.setPayAcc(pay.getPayBankAccount());
+        param.setPayeeName(pay.getPayeeName());
+        param.setPayeeBankName(pay.getPayeeBankName());
+        param.setPayeeAcc(pay.getPayeeBankAccount());
+        param.setPayMoney(pay.getPayAmount().toString());
+        param.setFurinfo(pay.getRemark());
+        param.setPayBillNo(pay.getBillNo());
+        return param;
+    }
+
+    @SneakyThrows
+    public static BocB2eParam getInstance(TradingRecord order){
+        final BocB2eParam param = new BocB2eParam();
+        //付款人
+        param.setPayName(order.getPayName());
+        param.setPayAcc(order.getPayAcc());
+        //收款人
+        param.setPayeeName(order.getPayeeName());
+        param.setPayeeAcc(order.getPayeeAcc());
+        param.setPayeeBankName(order.getPayeeBankName());
+        param.setPayeeBankNo(order.getPayeeBankNo());
+        param.setPayeeType(Optional.ofNullable(order.getPayeeAccType()).orElseThrow(() -> new IllegalArgumentException("申请单必须明确收款账户类型")) == TradingRecord.PayeeAccType.CORPORATE ? "00" : "04");
+        param.setRecord(order);
+        param.setPayNo(order.getPayNo());
+        param.setPayMoney(order.getPayAmount().toString());
+        param.setBankPriority(String.valueOf(order.getUrgent()));
+        if (order.getScheduleDate() != null) {
+            param.setScheduleDate(new SimpleDateFormat("yyyyMMdd").parse(order.getScheduleDate()));
+        }
+        param.setFurinfo(order.getDescription());
+        return param;
+    }
+}
Index: src/main/java/com/novaone/entity/CompanyBankAcc.java
===================================================================
--- src/main/java/com/novaone/entity/CompanyBankAcc.java	(不存在的)
+++ src/main/java/com/novaone/entity/CompanyBankAcc.java	(版本 31412)
@@ -0,0 +1,47 @@
+package com.novaone.entity;
+
+import java.util.Date;
+import lombok.Data;
+
+@Data
+public class CompanyBankAcc {
+    /**
+    * 主键id
+    */
+    private Long id;
+
+    /**
+    * 公司名称
+    */
+    private String company;
+
+    /**
+    * 银行名称
+    */
+    private String bankName;
+
+    /**
+    * 银行账号
+    */
+    private String bankAccount;
+
+    /**
+    * 币种
+    */
+    private String currency;
+
+    /**
+    * 是否禁用。1:禁用；0：启用
+    */
+    private Boolean disabled;
+
+    /**
+    * 创建时间
+    */
+    private Date createTime;
+
+    /**
+    * 修改时间
+    */
+    private Date updateTime;
+}
\ No newline at end of file
Index: src/main/java/com/novaone/entity/PayVO.java
===================================================================
--- src/main/java/com/novaone/entity/PayVO.java	(版本 31411)
+++ src/main/java/com/novaone/entity/PayVO.java	(不存在的)
@@ -1,63 +0,0 @@
-package com.novaone.entity;
-
-import com.alibaba.fastjson.annotation.JSONField;
-import lombok.Data;
-import org.springframework.validation.BindException;
-
-import java.util.Date;
-
-/*
- * @program: BankEnterprise
- * @description: 支付业务数据
- * @author: Ma.ChengJian
- * @create: 2022-01-21 18:01
- */
-@Data
-public class PayVO {
-
-    /**
-     * 通用信息。
-     */
-    //客户业务编号。
-    private String payNo;
-    //客户处理优先级
-    private String cusPriority;
-    //银行处理优先级
-    private String bankPriority;
-    //要求转账日期
-    @JSONField(format = "yyyy-MM-dd")
-    private Date scheduleDate;
-    //收款行是否中行。1:中行;0:他行
-    private String isBOC;
-
-
-    //BindException
-    /**
-     * 付款信息信息
-     */
-    //付款行联行号
-    private String payBankNo;
-    //付款行账号/查询余额的账号
-    private String payAcc;
-    //付款人名称
-    private String payName;
-    //付款金额
-    private String payMoney;
-    //支付货币类型。只支持人民币
-    private final String payCurrency = "CNY";
-
-
-    /**
-     * 收款信息
-     */
-    //收款行联行号
-    private String payeeBankNo;
-    //收款账号
-    private String payeeAcc;
-    //收款人名称
-    private String payeeName;
-    //收款人类型  00：对公  04：对私
-    private String payeeType;
-    //收款人开户行名称
-    private String payeeBankName;
-}
Index: src/main/java/com/novaone/entity/PaymentBill.java
===================================================================
--- src/main/java/com/novaone/entity/PaymentBill.java	(不存在的)
+++ src/main/java/com/novaone/entity/PaymentBill.java	(版本 31412)
@@ -0,0 +1,400 @@
+package com.novaone.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.NoArgsConstructor;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ * @program: BankEnterprise
+ * @description: 付款单
+ * @author: Ma.ChengJian
+ * @create: 2022-05-20 17:17
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class PaymentBill {
+    /**
+     * 申请单编号
+     */
+    private String sqdbh;
+    /**
+     * 预匹配编号
+     */
+    private String yppbh;
+    /**
+     * 预匹配编号。用户在水果通系统页面看到的预匹配编号。
+     */
+    private String yppbhValue;
+    /**
+     * 申请人公司名称
+     */
+    private String sqrgsmc;
+    /**
+     * 申请人公司编码
+     */
+    private String sqrgsbm;
+    /**
+     * 支付银行
+     */
+    private String zfyh;
+    /**
+     * 支付银行账号
+     */
+    private String zfyhzh;
+    /**
+     * 折人民币金额
+     */
+    private BigDecimal zrmbje;
+    /**
+     * 实际收款人开户行名称
+     */
+    private String sjskrkhyh;
+    /**
+     * 实际收款人银行账号
+     */
+    private String sjskryhzh;
+    /**
+     * 实际收款人名称
+     */
+    private String sjskrmc;
+    /**
+     * 付款方式
+     */
+    private String fkfs;
+    /**
+     * 币种
+     */
+    private String wbbb;
+    /**
+     * 支付时间
+     */
+    private Date zfsj;
+    /**
+     * 实际支付日期
+     */
+    private Date sjzfrq;
+    /**
+     * 支付备注
+     */
+    private String zfbz;
+    /**
+     * 合并支付备注
+     */
+    private String hbzfbz;
+    /**
+     * 审核确认支付。用户勾选：取值1
+     */
+    private Integer shqrzf;
+    /**
+     * 银行处理是否加急。用户勾选：取值1
+     */
+    private Integer yhcljj;
+    /**
+     * 申请单支付状态。字段值：未支付、支付中、支付成功、支付失败
+     */
+    private String zfzt;
+    /**
+     * 申请单支付失败原因
+     */
+    private String zfsbyy;
+    /**
+     * 支付回单。
+     */
+    private String zfhd;
+    /**
+     * 制表日期
+     */
+    private String zbrq;
+    /**
+     * 制表人
+     */
+    private String zbr;
+    /**
+     * 回单原始名称
+     */
+    private String receiptName;
+    /**
+     * 回单目录
+     */
+    private String receiptAddress;
+    /**
+     * 回单再ftp中的名字
+     */
+    private String receiptSysName;
+
+
+
+
+    public static class ZFZT
+    {
+        /**
+         * 初始化未支付
+         */
+        public static final String UNPAID = "未支付";
+        /**
+         * 支付处理中
+         */
+        public static final String WAIT = "支付中";
+        /**
+         * 支付成功
+         */
+        public static final String SUCCEED = "支付成功";
+        /**
+         * 支付失败
+         */
+        public static final String FAIL = "支付失败";
+    }
+
+    public PaymentBill(String yppbhValue,String zfzt) {
+        this.yppbhValue = yppbhValue;
+        this.zfzt = zfzt;
+    }
+
+    public PaymentBill(String yppbhValue,String zfzt,String zfsbyy) {
+        this.yppbhValue = yppbhValue;
+        this.zfzt = zfzt;
+        this.zfsbyy = zfsbyy;
+    }
+
+    public String getSqdbh() {
+        return sqdbh;
+    }
+
+    public void setSqdbh(String sqdbh) {
+        this.sqdbh = sqdbh;
+    }
+
+    public String getYppbhValue() {
+        return yppbhValue;
+    }
+
+    public void setYppbhValue(String yppbhValue) {
+        this.yppbhValue = yppbhValue;
+    }
+
+    public String getSqrgsmc() {
+        return sqrgsmc;
+    }
+
+    public void setSqrgsmc(String sqrgsmc) {
+        this.sqrgsmc = sqrgsmc;
+    }
+
+    public String getSqrgsbm() {
+        return sqrgsbm;
+    }
+
+    public void setSqrgsbm(String sqrgsbm) {
+        this.sqrgsbm = sqrgsbm;
+    }
+
+    public BigDecimal getZrmbje() {
+        return zrmbje;
+    }
+
+    public void setZrmbje(BigDecimal zrmbje) {
+        this.zrmbje = zrmbje;
+    }
+
+    public String getSjskrkhyh() {
+        return sjskrkhyh;
+    }
+
+    public void setSjskrkhyh(String sjskrkhyh) {
+        this.sjskrkhyh = sjskrkhyh;
+    }
+
+    public String getZbrq() {
+        return zbrq;
+    }
+
+    public void setZbrq(String zbrq) {
+        this.zbrq = zbrq;
+    }
+
+    public String getSjskryhzh() {
+        return sjskryhzh;
+    }
+
+    public void setSjskryhzh(String sjskryhzh) {
+        this.sjskryhzh = sjskryhzh;
+    }
+
+    public String getSjskrmc() {
+        return sjskrmc;
+    }
+
+    public void setSjskrmc(String sjskrmc) {
+        this.sjskrmc = sjskrmc;
+    }
+
+    public String getFkfs() {
+        return fkfs;
+    }
+
+    public void setFkfs(String fkfs) {
+        this.fkfs = fkfs;
+    }
+
+    public String getWbbb() {
+        return wbbb;
+    }
+
+    public String getReceiptName() {
+        return receiptName;
+    }
+
+    public void setReceiptName(String receiptName) {
+        this.receiptName = receiptName;
+    }
+
+    public String getReceiptAddress() {
+        return receiptAddress;
+    }
+
+    public void setReceiptAddress(String receiptAddress) {
+        this.receiptAddress = receiptAddress;
+    }
+
+    public String getReceiptSysName() {
+        return receiptSysName;
+    }
+
+    public void setReceiptSysName(String receiptSysName) {
+        this.receiptSysName = receiptSysName;
+    }
+
+    public void setWbbb(String wbbb) {
+        this.wbbb = wbbb;
+    }
+
+    public Date getZfsj() {
+        return zfsj;
+    }
+
+    public void setZfsj(Date zfsj) {
+        this.zfsj = zfsj;
+    }
+
+    public Date getSjzfrq() {
+        return sjzfrq;
+    }
+
+    public void setSjzfrq(Date sjzfrq) {
+        this.sjzfrq = sjzfrq;
+    }
+
+    public String getZfbz() {
+        return zfbz;
+    }
+
+    public void setZfbz(String zfbz) {
+        this.zfbz = zfbz;
+    }
+
+    public Integer getShqrzf() {
+        return shqrzf;
+    }
+
+    public void setShqrzf(Integer shqrzf) {
+        this.shqrzf = shqrzf;
+    }
+
+    public Integer getYhcljj() {
+        return yhcljj;
+    }
+
+    public void setYhcljj(Integer yhcljj) {
+        this.yhcljj = yhcljj;
+    }
+
+    public String getZfzt() {
+        return zfzt;
+    }
+
+    public void setZfzt(String zfzt) {
+        this.zfzt = zfzt;
+    }
+
+    public String getZfsbyy() {
+        return zfsbyy;
+    }
+
+    public void setZfsbyy(String zfsbyy) {
+        this.zfsbyy = zfsbyy;
+    }
+
+    public String getZfhd() {
+        return zfhd;
+    }
+
+    public void setZfhd(String zfhd) {
+        this.zfhd = zfhd;
+    }
+
+    public String getYppbh() {
+        return yppbh;
+    }
+
+    public void setYppbh(String yppbh) {
+        this.yppbh = yppbh;
+    }
+
+    public String getZfyh() {
+        return zfyh;
+    }
+
+    public void setZfyh(String zfyh) {
+        this.zfyh = zfyh;
+    }
+
+    public String getZfyhzh() {
+        return zfyhzh;
+    }
+
+    public void setZfyhzh(String zfyhzh) {
+        this.zfyhzh = zfyhzh;
+    }
+
+    public String getHbzfbz() {
+        return hbzfbz;
+    }
+
+    public void setHbzfbz(String hbzfbz) {
+        this.hbzfbz = hbzfbz;
+    }
+
+    public String getZbr() {
+        return zbr;
+    }
+
+    public void setZbr(String zbr) {
+        this.zbr = zbr;
+    }
+
+
+    public static PaymentBill basicConvert(TradingRecord record,String zfzt){
+        return basicConvert( record, zfzt, null);
+    }
+
+
+    public static PaymentBill basicConvert(TradingRecord record,String zfzt,String zfsbyy){
+        if (StringUtils.isEmpty(record.getType()) || StringUtils.isEmpty(record.getPayBillNo())){
+            throw new RuntimeException("TradingRecord的type、payBillNo属性不得为空");
+        }
+        final PaymentBill paymentBill = new PaymentBill();
+        if (record.getType() == TradingRecord.Type.SINGLE) {
+            paymentBill.setSqdbh(record.getPayBillNo());
+        }else {
+            paymentBill.setYppbhValue(record.getPayBillNo());
+        }
+        paymentBill.setZfzt(zfzt);
+        paymentBill.setZfsbyy(zfsbyy);
+        return paymentBill;
+    }
+}
Index: src/main/java/com/novaone/entity/TradingBillRecord.java
===================================================================
--- src/main/java/com/novaone/entity/TradingBillRecord.java	(不存在的)
+++ src/main/java/com/novaone/entity/TradingBillRecord.java	(版本 31412)
@@ -0,0 +1,75 @@
+package com.novaone.entity;
+
+import java.util.Date;
+import lombok.Data;
+import org.apache.commons.lang.StringUtils;
+
+@Data
+public class TradingBillRecord {
+    /**
+    * id
+    */
+    private Long id;
+
+    /**
+    * 交易在客户端的唯一编号
+    */
+    private String payNo;
+
+    /**
+    * 回单处理状态。0：新建记录，回单未处理；1:回单上传成功；2:回单上传失败；3:处理中未查询到回单信息；4:交易成功2小时内未查询到回单信息；5:未知的回单模板；6：系统删除并将path记录银行回单路径
+    */
+    private Integer state;
+
+    /**
+    * 回单名称
+    */
+    private String name;
+
+    /**
+    * 回单访问路径
+    */
+    private String path;
+
+    /**
+    * 回单处理失败描述
+    */
+    private String message;
+
+    /**
+    * 创建时间
+    */
+    private Date creatTime;
+
+    /**
+    * 修改时间
+    */
+    private Date updateTime;
+
+    public static class State{
+
+        public static Integer INIT = 0;
+
+        public static Integer SUCCESS = 1;
+
+        public static Integer FAILED = 2;
+
+        public static Integer BILL_WAIT = 3;
+
+        public static Integer NO_SEARCH_BILL_INFO = 4;
+
+        public static Integer UNKNOWN_TEMPLATE = 5;
+
+        public static Integer RPA_BILL_DELETED = 6;
+    }
+
+
+    public static TradingBillRecord getInstance(TradingRecord record){
+        if (StringUtils.isEmpty(record.getPayNo())){
+            throw new RuntimeException("TradingRecord payNo属性不得为空");
+        }
+        final TradingBillRecord billRecord = new TradingBillRecord();
+        billRecord.setPayNo(record.getPayNo());
+        return billRecord;
+    }
+}
\ No newline at end of file
Index: src/main/java/com/novaone/entity/TradingHistory.java
===================================================================
--- src/main/java/com/novaone/entity/TradingHistory.java	(不存在的)
+++ src/main/java/com/novaone/entity/TradingHistory.java	(版本 31412)
@@ -0,0 +1,276 @@
+package com.novaone.entity;
+
+import lombok.Data;
+
+@Data
+public class TradingHistory {
+    private Long id;
+
+    /**
+    * 付款信息
+    */
+    private PayInfo fractn;
+    /**
+     * 收款信息
+     */
+    private PayeeInfo toactn;
+    /**
+    * 被代理行号
+    */
+    private String mactibkn;
+    /**
+    * 被代理账号
+    */
+    private String mactacn;
+
+    /**
+    * 被代理账户名
+    */
+    private String mactname;
+
+    /**
+    * 被代理账户开
+被代理账户开户行名
+    */
+    private String mactbank;
+
+    /**
+    * 旧线是10位的凭证号或传票号，新线是9位流水号(不
+补0)+3位记录号
+    */
+    private String vchnum;
+
+    /**
+    * 记录标识号(9位JournalNumber+9位RecordNum(原)+9位RecordNum)
+    */
+    private String transid;
+
+    /**
+    * 客户业务编号后12位
+    */
+    private String insid;
+
+    /**
+    * 交易日期YYYYMMDD（非空）
+    */
+    private String txndate;
+
+    /**
+    * 交易时间HH24MISS
+    */
+    private String txntime;
+
+    /**
+    * 金额（非空）
+    */
+    private String txnamt;
+
+    /**
+    * 交易后余额 
+    */
+    private String acctbal;
+
+    /**
+    * 可用余额
+    */
+    private String avlbal;
+
+    /**
+    * 冻结金额
+    */
+    private String frzamt;
+
+    /**
+    * 透支额度
+    */
+    private String overdramt;
+
+    /**
+    * 可用透支额度
+    */
+    private String avloverdramt;
+
+    /**
+    * 用途
+    */
+    private String useinfo;
+
+    /**
+    * 附言 (网银行内交易：OBSS+交易流水号后12位+GIRO+客户业务编号后12位//用途；网银跨行交易：OBSS+交易流水号后12位+GIRO+客户业务编号后12位用途//用途，)
+    */
+    private String furinfo;
+
+    /**
+    * 业务类型
+        01-国内汇款
+        02-国外汇款
+        03-人行大额
+        04-人行小额
+        05-现金存款
+        06-转帐收入
+        07-汇票
+        08-本票
+        09-支票
+        10-冲账
+        11-冲正
+        12-承兑汇票
+        13-托收承付
+        14-保证金
+        15-现金取款
+        16-转帐支出
+        17-贷款放款
+        18-贷款还款
+        21-实时汇划
+        22-退汇
+        31-结息
+        32-批量收费
+        41-收费
+        99-其他
+    */
+    private String transtype;
+
+    /**
+    * 新业务类型
+    */
+    private String bustype;
+
+    /**
+    * 货币名称（非空、如CNY或者001）
+    */
+    private String trncur;
+
+    /**
+    * 来往账标识
+（1-来账，2-
+往账）
+    */
+    private String direction;
+
+    /**
+    * 费用账户:收费交易通过一笔单独的交易来展示,所以该项返回空
+    */
+    private String feeact;
+
+    /**
+    * 费用金额 :收
+费交易通过一
+笔单独的交易
+来展示,所以
+该项返回空
+    */
+    private String feeamt;
+
+    /**
+    * 费用货币:收
+费交易通过一
+笔单独的交易
+来展示,所以
+该项返回空
+    */
+    private String feecur;
+
+    /**
+    * 起息日期
+YYYYMMDD
+    */
+    private String valdat;
+
+    /**
+    * 凭证类型，具
+体解释见附件
+5
+    */
+    private String vouchtp;
+
+    /**
+    * 凭证号码（交
+易涉及的我行
+各类支票、汇
+票、本票、银
+行卡等各种凭
+证类型及他行
+各种凭证类型
+码）
+    */
+    private String vouchnum;
+
+    /**
+    * 汇率 
+    */
+    private String fxrate;
+
+    /**
+    * 整合信息，格
+式为：F:附言
+//A:摘要
+//U:用途//R:备
+注
+例如：
+F:转账
+//A:OBSS00033
+9699429GIRO5
+82031100002//
+U:支付费用
+//R:
+    */
+    private String interinfo;
+
+    /**
+    * 渠道标识
+    */
+    private String channelflg;
+
+    /**
+    * 商户号
+    */
+    private String commnum;
+
+    @Data
+    public static class PayInfo{
+        /**
+         * 付款行号
+         */
+        private String ibknum;
+        /**
+         * 付款账号
+         */
+        private String actacn;
+        /**
+         * 付款人
+         */
+        private String acntname;
+        /**
+         * 付款人开户行名
+         */
+        private String ibkname;
+        /**
+         * 汇款行业务编号
+         */
+        private String outref;
+    }
+
+    @Data
+    public static class PayeeInfo{
+        /**
+         * 收款行号
+         */
+        private String toibkn;
+        /**
+         * 收款账号
+         */
+        private String actacn;
+        /**
+         * 收款人
+         */
+        private String toname;
+        /**
+         * 收款人开户行名
+         */
+        private String tobank;
+        /**
+         * 收款行业务编号
+         */
+        private String tobref;
+    }
+
+}
\ No newline at end of file
Index: src/main/java/com/novaone/entity/TradingLog.java
===================================================================
--- src/main/java/com/novaone/entity/TradingLog.java	(不存在的)
+++ src/main/java/com/novaone/entity/TradingLog.java	(版本 31412)
@@ -0,0 +1,108 @@
+package com.novaone.entity;
+
+import java.util.Date;
+import lombok.Data;
+import org.apache.commons.lang.StringUtils;
+
+@Data
+public class TradingLog {
+    /**
+    * id
+    */
+    private Long id;
+
+    /**
+    * 对应交易的编号
+    */
+    private String payNo;
+
+    /**
+    * 中行接口编号
+    */
+    private String apiNo;
+
+    /**
+    * 接口响应状态码
+    */
+    private String respCode;
+
+    /**
+    * 接口响应状态描述
+    */
+    private String respMsg;
+
+    /**
+    * 银行及业务处理结果描述
+    */
+    private String mesage;
+
+    /**
+     * 业务处理状态
+     */
+    private Integer state;
+
+    /**
+    * 创建时间
+    */
+    private Date creatTime;
+
+    public TradingLog() {
+    }
+
+    public TradingLog(String mesage, Integer state) {
+        this.mesage = mesage;
+        this.state = state;
+        this.creatTime = new Date();
+    }
+
+    /**
+     * 业务处理状态:-1:接口响应处理错误；0:接口调用异常；1:发起汇款交易成功；2:发起汇款交易失败；3:交易处理中；4:交易处理成功；5:交易处理失败；6;未知的响应状态码；7:未查询到回单信息；8:回单处理失败；9:回单处理成功；10:申请单数据校验失败；11:申请单数据校验成功；12水果通数据库写入异常;13:未知的异常;14:余额不足
+     */
+    public static class State{
+
+        public static Integer API_RESPONSES_FALL = -1;
+
+        public static Integer API_CALL_FALL = 0;
+
+        public static Integer SUCCESS_PAYMENT = 1;
+
+        public static Integer FAILED_PAYMENT = 2;
+
+        public static Integer WAIT_BANK = 3;
+
+        public static Integer SUCCESS_BANK = 4;
+
+        public static Integer FAILED_BANK = 5;
+
+        public static Integer UNKNOWN_CODE = 6;
+
+        public static Integer NO_SEARCH_BILL_INFO = 7;
+
+        public static Integer FAILED_BILL = 8;
+
+        public static Integer SUCCESS_BILL = 9;
+
+        public static Integer PAYMENT_BILL_CHECK_ERR = 10;
+
+        public static Integer PAYMENT_BILL_CHECK_SUCCESS = 11;
+
+        public static Integer SQLSERVER_ERR = 12;
+
+        public static Integer UNKNOWN_ERR = 13;
+
+        public static final int BALANCE_INSUFFICIENT = 14;
+    }
+
+    public static TradingLog getInstanceByTradingRecord(TradingRecord record){
+        if (StringUtils.isEmpty(record.getPayNo())){
+            throw new RuntimeException("TradingRecord payNo属性不得为空");
+        }
+        final TradingLog tradingLog = new TradingLog();
+        tradingLog.setPayNo(record.getPayNo());
+        tradingLog.setCreatTime(new Date());
+        return tradingLog;
+    }
+
+
+
+}
\ No newline at end of file
Index: src/main/java/com/novaone
===================================================================
--- src/main/java/com/novaone	(版本 31411)
+++ src/main/java/com/novaone	(版本 31412)

Property changes on: src/main/java/com/novaone
___________________________________________________________________
Added: svn:ignore
## -0,0 +1 ##
+test
Index: src/main/java/com/novaone/entity/TradingRecord.java
===================================================================
--- src/main/java/com/novaone/entity/TradingRecord.java	(不存在的)
+++ src/main/java/com/novaone/entity/TradingRecord.java	(版本 31412)
@@ -0,0 +1,471 @@
+package com.novaone.entity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.Builder;
+import lombok.Data;
+
+public class TradingRecord {
+    /**
+    * id
+    */
+    private Long id;
+
+    /**
+    * 交易在客户端的唯一编号
+    */
+    private String payNo;
+
+    /**
+    * 申请单编号/预处理编号。
+    */
+    private String payBillNo;
+
+    /**
+    * 交易对应申请单类型 false:合并支付  true:非合并支付
+    */
+    private Integer type;
+
+    /**
+    * 申请人公司名称
+    */
+    private String company;
+
+    /**
+    * 付款账号
+    */
+    private String payAcc;
+
+    /**
+    * 付款人名称
+    */
+    private String payName;
+    /**
+     * 付款银行
+     */
+    private String payBank;
+
+    /**
+    * 转账金额
+    */
+    private BigDecimal payAmount;
+
+    /**
+    * 收款人开户行名称
+    */
+    private String payeeBankName;
+
+    /**
+    * 收款行联行号
+    */
+    private String payeeBankNo;
+
+    /**
+    * 收款账号
+    */
+    private String payeeAcc;
+
+    /**
+    * 收款人名称
+    */
+    private String payeeName;
+
+    /**
+    * 收款账户类型。0:对私账号；1：对公账号
+    */
+    private Integer payeeAccType;
+
+    /**
+    * 银行处理优先级 false:普通；ture:加急
+    */
+    private Integer urgent;
+
+    /**
+    * 发起转账时的留言（中文不得超过100个）
+    */
+    private String description;
+
+    /**
+    * 银行处理状态。记录银行处理状态码
+    */
+    private String bankState;
+
+    /**
+    * 业务状态。0:待支付；1:支付中；2:支付成功；3:支付失败；4:交易失败，账户余额不足
+    */
+    private Integer businessState;
+
+    /**
+    * 交易处理结果描述
+    */
+    private String message;
+
+    /**
+    * 要求转账日期 格式YYYYMMDD
+    */
+    private String scheduleDate;
+
+    /**
+    * 创建时间
+    */
+    private Date creatTime;
+
+    /**
+    * 修改时间
+    */
+    private Date updateTime;
+    /**
+     * 该交易对应的付款申请单数据
+     */
+    private List<PaymentBill> paymentBills;
+
+    private TradingLog tradingLog;
+
+    private TradingBillRecord billRecord;
+
+    private MateData mateData = new MateData(0,0);
+
+
+
+    public static class MateData{
+
+        private int count;
+
+        private long firstTime;
+
+        public MateData() {
+        }
+
+        public MateData(int count, long firstTime) {
+            this.count = count;
+            this.firstTime = firstTime;
+        }
+
+
+        public void reset(){
+            this.setCount(1);
+            this.setFirstTime(System.currentTimeMillis());
+        }
+
+        public int increment(){
+            return ++this.count;
+        }
+
+        public int getCount() {
+            return count;
+        }
+
+        public void setCount(int count) {
+            this.count = count;
+        }
+
+        public long getFirstTime() {
+            return firstTime;
+        }
+
+        public void setFirstTime(long firstTime) {
+            this.firstTime = firstTime;
+        }
+    }
+
+
+    public static class Type {
+        /**
+         * 初始化未支付
+         */
+        public static final int MERGE = 0;
+        /**
+         * 支付处理中
+         */
+        public static final int SINGLE = 1;
+    }
+
+    public static class Urgent {
+        /**
+         * 初始化未支付
+         */
+        public static final int NOT = 0;
+        /**
+         * 支付处理中
+         */
+        public static final int YES = 1;
+    }
+
+    public static class PayeeAccType {
+        /**
+         * 对私账户类型
+         */
+        public static final int PRIVATE = 0;
+        /**
+         * 对公账户类型
+         */
+        public static final int CORPORATE = 1;
+    }
+
+
+
+    public static class BusinessState
+    {
+        /**
+         * PRA付款操作处理中！
+         */
+        public static final int LOCK = -1;
+        /**
+         * 初始化未支付
+         */
+        public static final int INIT = 0;
+        /**
+         * 支付处理中
+         */
+        public static final int WAIT = 1;
+        /**
+         * 支付成功
+         */
+        public static final int SUCCEED = 2;
+        /**
+         * 支付失败
+         */
+        public static final int FAIL = 3;
+        /**
+         * 付款操作时接口调用异常
+         */
+        public static final int REQUEST_ERR = 4;
+        /**
+         * 余额不足支付失败
+         */
+        public static final int BALANCE_INSUFFICIENT = 5;
+    }
+
+    public static class Message{
+        /**
+         * 合并支付申请单校验失败
+         */
+        public static final String MERGE_CHECK_ERR = "合并支付交易所包含的付款申请单数据完整性或者一致性校验失败！被合并支付的多笔付款申请单参数必须完整，并且收付款信息必须一致！";
+        /**
+         * 单笔支付申请单校验失败
+         */
+        public static final String SINGLE_CHECK_ERR = "参数校验失败，放弃支付";
+        /**
+         * 根据申请人公司名称不能查询到付款银行
+         */
+        public static final String PAY_BANK_ERR = "根据申请人公司名称未能匹配到付款行信息,放弃支付";
+        /**
+         * 发起付款交易失败
+         */
+        public static final String PAY_FAILED = "汇款交易发起失败";
+    }
+
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getPayNo() {
+        return payNo;
+    }
+
+    public void setPayNo(String payNo) {
+        this.payNo = payNo;
+    }
+
+    public String getPayBillNo() {
+        return payBillNo;
+    }
+
+    public void setPayBillNo(String payBillNo) {
+        this.payBillNo = payBillNo;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+
+    public MateData getMateData() {
+        return mateData;
+    }
+
+    public void setMateData(MateData mateData) {
+        this.mateData = mateData;
+    }
+
+    public String getCompany() {
+        return company;
+    }
+
+    public void setCompany(String company) {
+        this.company = company;
+    }
+
+    public String getPayAcc() {
+        return payAcc;
+    }
+
+    public void setPayAcc(String payAcc) {
+        this.payAcc = payAcc;
+    }
+
+    public String getPayName() {
+        return payName;
+    }
+
+    public void setPayName(String payName) {
+        this.payName = payName;
+    }
+
+    public BigDecimal getPayAmount() {
+        return payAmount;
+    }
+
+    public void setPayAmount(BigDecimal payAmount) {
+        this.payAmount = payAmount;
+    }
+
+    public String getPayeeBankName() {
+        return payeeBankName;
+    }
+
+    public void setPayeeBankName(String payeeBankName) {
+        this.payeeBankName = payeeBankName;
+    }
+
+    public String getPayeeBankNo() {
+        return payeeBankNo;
+    }
+
+    public void setPayeeBankNo(String payeeBankNo) {
+        this.payeeBankNo = payeeBankNo;
+    }
+
+    public String getPayeeAcc() {
+        return payeeAcc;
+    }
+
+    public List<PaymentBill> getPaymentBills() {
+        return paymentBills;
+    }
+
+    public void setPaymentBills(List<PaymentBill> paymentBills) {
+        this.paymentBills = paymentBills;
+    }
+
+    public void setPayeeAcc(String payeeAcc) {
+        this.payeeAcc = payeeAcc;
+    }
+
+    public String getPayeeName() {
+        return payeeName;
+    }
+
+    public void setPayeeName(String payeeName) {
+        this.payeeName = payeeName;
+    }
+
+    public Integer getPayeeAccType() {
+        return payeeAccType;
+    }
+
+    public void setPayeeAccType(Integer payeeAccType) {
+        this.payeeAccType = payeeAccType;
+    }
+
+    public Integer getUrgent() {
+        return urgent;
+    }
+
+    public void setUrgent(Integer urgent) {
+        this.urgent = urgent;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getBankState() {
+        return bankState;
+    }
+
+    public void setBankState(String bankState) {
+        this.bankState = bankState;
+    }
+
+    public Integer getBusinessState() {
+        return businessState;
+    }
+
+    public void setBusinessState(Integer businessState) {
+        this.businessState = businessState;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getScheduleDate() {
+        return scheduleDate;
+    }
+
+    public void setScheduleDate(String scheduleDate) {
+        this.scheduleDate = scheduleDate;
+    }
+
+    public TradingBillRecord getBillRecord() {
+        return billRecord;
+    }
+
+    public void setBillRecord(TradingBillRecord billRecord) {
+        this.billRecord = billRecord;
+    }
+
+    public Date getCreatTime() {
+        return creatTime;
+    }
+
+    public void setCreatTime(Date creatTime) {
+        this.creatTime = creatTime;
+    }
+
+    public String getPayBank() {
+        return payBank;
+    }
+
+    public void setPayBank(String payBank) {
+        this.payBank = payBank;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public TradingLog getTradingLog() {
+        return tradingLog;
+    }
+
+    public void setTradingLog(TradingLog tradingLog) {
+        this.tradingLog = tradingLog;
+    }
+}
\ No newline at end of file
Index: src/main/java/com/novaone/entity/bocb2e/B2e0005XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0005XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0005XmlBuilder.java	(版本 31412)
@@ -0,0 +1,67 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.dom4j.Element;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+/**
+ * @program: BankEnterprise
+ * @description:  b2e0005 xml请求报文必须包含的参数包括：
+ *                  1.查询的账号（必须）
+ *                  2.联行号（可选）
+ * @author: Ma.ChengJian
+ * @create: 2022-01-24 15:27
+ */
+@Slf4j
+public class B2e0005XmlBuilder extends Bocb2eXMLBuilder {
+
+    public B2e0005XmlBuilder() {
+        this.apiNo = "b2e0005";
+    }
+
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        Element trnB2e0005Rq = trans.addElement("trn-b2e0005-rq");
+        for (BocB2eParam param : this.params) {
+            Element account = trnB2e0005Rq.addElement("b2e0005-rq").addElement("account");
+            account.addElement("actacn").addText(param.getPayAcc());
+            if (StringUtils.isEmpty(param.getPayBankNo())) {
+                continue;
+            }
+            account.addElement("ibknum").addText(param.getPayBankNo());
+        }
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.isEmpty() || this.params.size() > 100){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量必须在1-100个之间。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        Iterator<BocB2eParam> it = this.params.iterator();
+        while (it.hasNext()) {
+            BocB2eParam param = it.next();
+            if (!B2eValidateUtil.payAcc(param.getPayAcc())) {
+                HashMap<String, String> resule = new HashMap<>();
+                resule.put("payAcc","余额查询的账号必须为1-20位字符串");
+                validateResult.put(param,resule);
+                it.remove();
+            }
+        }
+        if (this.params.isEmpty()){
+            throw new ValidationException(this.apiNo + " 检验通过的请求交易参数为0,异常参数包括:" + JSON.toJSONString(validateResult),validateResult,this.apiNo);
+        }else if (this.callback != null){
+            callback.callback(this.validateResult,this.apiNo);
+        }else if (!validateResult.isEmpty()){
+            log.error("校验未通过的请求参数将被丢弃:{}",JSON.toJSONString(validateResult.keySet()));
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0007XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0007XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0007XmlBuilder.java	(版本 31412)
@@ -0,0 +1,68 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.dom4j.Element;
+
+import java.util.*;
+
+/*
+ * @program: BankEnterprise
+ * @description: B2e0007 XML构造
+ * @author: Ma.ChengJian
+ * @create: 2022-02-14 16:02
+ */
+@Slf4j
+public class B2e0007XmlBuilder extends Bocb2eXMLBuilder{
+
+    public B2e0007XmlBuilder() {
+        this.apiNo = "b2e0007";
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        Element trnB2e0007Rq = trans.addElement("trn-b2e0007-rq");
+        for (BocB2eParam param : this.params) {
+            Element b2e0007Rq = trnB2e0007Rq.addElement("b2e0007-rq");
+            b2e0007Rq.addElement("insid").addText(param.getPayNo());
+            b2e0007Rq.addElement("obssid").addText(Optional.ofNullable(param.getObssid()).orElse(""));
+        }
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.isEmpty() || this.params.size() > 100){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量必须在1-100个之间。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        Iterator<BocB2eParam> it = this.params.iterator();
+        while (it.hasNext()) {
+            Map<String ,String> map = null;
+            BocB2eParam param = it.next();
+            if (StringUtils.isEmpty(param.getObssid()) && StringUtils.isEmpty(param.getPayNo())) {
+                map = this.put("payNo、obssid","payNo、obssid参数不能同时为空",map);
+            }else if (StringUtils.isNotEmpty(param.getPayNo()) && !B2eValidateUtil.payNo(param.getPayNo())) {
+                map = this.put("payNo","交易状态查询时转账指令为0-12位字符串",map);
+                if (!B2eValidateUtil.checkObssid(param.getObssid())){
+                    this.put("obssid","交易状态查询时网银交易流水号必须为0-19位字符串表示的数字，且不应大于"+Long.MAX_VALUE,map);
+                }
+            }
+            if (map != null) {
+                it.remove();
+                validateResult.put(param,map);
+            }
+        }
+        if (this.params.isEmpty()){
+            throw new ValidationException(this.apiNo + " 校验通过的请求交易参数为0,异常参数包括:" + JSON.toJSONString(validateResult),validateResult,this.apiNo);
+        }else if (callback != null && !validateResult.isEmpty()){
+            callback.callback(validateResult,apiNo);
+        }else if (!validateResult.isEmpty()){
+            log.error("校验未通过的请求参数将被丢弃:{}",JSON.toJSONString(validateResult.keySet()));
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0009XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0009XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0009XmlBuilder.java	(版本 31412)
@@ -0,0 +1,124 @@
+package com.novaone.entity.bocb2e;
+
+/*
+ * @program: BankEnterprise
+ * @description: 公对公转账汇款(b2e0009)请求报文构造
+ * @author: Ma.ChengJian
+ * @create: 2022-02-10 13:39
+ */
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.dom4j.Element;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Slf4j
+public class B2e0009XmlBuilder extends Bocb2eXMLBuilder {
+
+
+    public B2e0009XmlBuilder () {
+        this.apiNo = "b2e0009";
+    }
+
+    public B2e0009XmlBuilder(List<BocB2eParam> params) {
+        this.params = params;
+        this.apiNo = "b2e0009";
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        //请求数据
+        Element trnB2e0043Rq = trans.addElement("trn-b2e0009-rq");
+        for (BocB2eParam param : params) {
+            Element b2e0009Rq = trnB2e0043Rq.addElement("b2e0009-rq");
+            /**
+             * 基本业务参数
+             */
+            //指令ID
+            b2e0009Rq.addElement("insid").addText(param.getPayNo());
+            //转账金额
+            b2e0009Rq.addElement("trnamt").addText(param.getPayMoney());
+            //货币类型
+            b2e0009Rq.addElement("trncur").addText(param.getPayCurrency());
+            //银行处理优先级
+            b2e0009Rq.addElement("priolv").addText(Optional.ofNullable(param.getBankPriority()).orElse("0"));
+            //要求转账日期
+            b2e0009Rq.addElement("trfdate").addText(new SimpleDateFormat("yyyyMMdd").format(Optional.ofNullable(param.getScheduleDate()).orElse(new Date())));
+            //用途.字符串长度0-200.(一个汉字=2个字符)注:不支持"|"
+            b2e0009Rq.addElement("furinfo").addText(Optional.ofNullable(param.getFurinfo()).orElse(""));
+            /**
+             * 付款人账户信息
+             */
+            Element fractn = b2e0009Rq.addElement("fractn");
+            fractn.addElement("actacn").addText(param.getPayAcc());//付款行账号
+            fractn.addElement("actnam").addText(Optional.ofNullable(param.getPayName()).orElse(""));//付款人名称
+            /**
+             * 收款人账户信息
+             */
+            Element toactn = b2e0009Rq.addElement("toactn");
+            toactn.addElement("actacn").addText(param.getPayeeAcc());//收款账号
+            toactn.addElement("toname").addText(param.getPayeeName());//收款人名称
+            toactn.addElement("tobknm").addText(Optional.ofNullable(param.getPayeeBankName()).orElse(""));//收款人开户行名称
+            toactn.addElement("toibkn").addText(Optional.ofNullable(param.getPayeeBankNo()).orElse(""));//收款人开户行名称
+
+        }
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.isEmpty() || this.params.size() > 1000){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量必须在1-100个之间。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        Iterator<BocB2eParam> it = params.iterator();
+        while (it.hasNext()) {
+            BocB2eParam param = it.next();
+            Map<String ,String> map = null;
+            if (!B2eValidateUtil.payNo(param.getPayNo())) {
+                map = put("payNo","必须为非空字符串1-12位", null);
+            }
+            if (!B2eValidateUtil.money(param.getPayMoney())){
+                map = put("payMoney","支付金额最多13位整数，最多2位小数",map);
+            }
+            if (!B2eValidateUtil.payCurrency(param.getPayCurrency())){
+                map = put("payCurrency","货币类型只支持001、CNY",map);
+            }
+            if (!B2eValidateUtil.priority(param.getBankPriority())){
+                map = put("bankPriority","银行处理优先级可选参数包括0、1，当该参数为null时默认为0",map);
+            }
+            if (!B2eValidateUtil.scheduleDate(param.getScheduleDate())){
+                map = put("scheduleDate","要求转账日期范围为系统当前日期（含当日）之后的一个月内，当参数为空时默认为系统当前时间",map);
+            }
+            if (!B2eValidateUtil.payAcc(param.getPayAcc())){
+                map = put("payAcc","付款账号必须为1-20位字符串",map);
+            }
+            if (!B2eValidateUtil.payeeAcc(param.getPayeeAcc())){
+                map = put("payeeAcc","收款账号必须为非空字符串长度1-35位",map);
+            }
+            if (!B2eValidateUtil.payeeName(param.getPayeeName())) {
+                map = put("payeeName","收款人名称必须为非空字符串长度1-70",map);
+            }
+            if (StringUtils.isEmpty(param.getPayeeBankNo()) && StringUtils.isEmpty(param.getPayeeBankName())) {
+                map = put("payeeBankName","收款行联行号为空时开户行名称不得为空",map);
+            }
+            if (map != null){
+                it.remove();
+                validateResult.put(param,map);
+            }
+        }
+        if (params.isEmpty()){
+            throw new ValidationException(this.apiNo + " 校验通过的请求交易参数为0,异常参数包括:" + JSON.toJSONString(validateResult,true),validateResult,this.apiNo);
+        }else if(!validateResult.isEmpty() && callback != null){
+            callback.callback(validateResult,apiNo);
+        }else if (!validateResult.isEmpty()){
+            log.error("校验未通过的请求参数将被丢弃:{}",JSON.toJSONString(validateResult.keySet()));
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0035XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0035XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0035XmlBuilder.java	(版本 31412)
@@ -0,0 +1,90 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import org.dom4j.Element;
+import org.springframework.util.StringUtils;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-24 17:37
+ */
+public class B2e0035XmlBuilder extends Bocb2eXMLBuilder{
+
+    public B2e0035XmlBuilder() {
+        this.apiNo = "b2e0035";
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
+        BocB2eParam param = params.get(0);
+        Element b2e0035Rq = trans.addElement("trn-b2e0035-rq").addElement("b2e0035-rq");
+        b2e0035Rq.addElement("ibknum").addText("");//查询账号的联行号
+        b2e0035Rq.addElement("actacn").addText(param.getPayAcc());//查询的账号
+        b2e0035Rq.addElement("type").addText(param.getHistoricalType());//查询的交易类型
+        b2e0035Rq.addElement("begnum").addText(param.getHistoricalBeginNum() + "");//起始位置
+        b2e0035Rq.addElement("recnum").addText(param.getHistoricalRecnum());//记录数
+        b2e0035Rq.addElement("direction").addText(param.getHistoricalDirection());//来往账标识。0-全部 1-来账，2-往账,3-内部往来，4-外部交易，5 部分内部往来其中3、4、5只有现金3.0客户可以上送
+        /**
+         * 查询的日期范围
+         */
+        Element datescope = b2e0035Rq.addElement("datescope");
+        datescope.addElement("from").addText(sf.format(param.getHistoricalDateFrom()));
+        datescope.addElement("to").addText(sf.format(param.getHistoricalDateTo()));
+        /**
+         * 查寻的金额范围
+         */
+        Element amountscope = b2e0035Rq.addElement("amountscope");
+        amountscope.addElement("from").addText(param.getHistoricalMoneyFrom());//上限（含）
+        amountscope.addElement("to").addText(param.getHistoricalMoneyTo());//下限（含）
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.size() != 1){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量只能包含1个。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        BocB2eParam param = params.get(0);
+        HashMap<String, String> result = new HashMap<>();
+        if (!B2eValidateUtil.payAcc(param.getPayAcc())){
+            result.put("payAcc","必须指定查询账号,且长度为1-20位");
+        }
+        List<String> types = Arrays.asList(BocB2eParam.HistoricalType.CURRENT, BocB2eParam.HistoricalType.YESTERDAY, BocB2eParam.HistoricalType.HISTORICAL);
+        if (!types.contains(param.getHistoricalType())){
+            result.put("historicalType","必须指定查询类型。可选的参数值:" + types);
+        }
+        if (param.getHistoricalDateFrom() == null){
+            result.put("historicalDateFrom","必须指定查询时间上限");
+        }
+        if (param.getHistoricalDateTo() == null){
+            result.put("historicalDateTo","必须指定查询时间下限");
+        }
+        if (StringUtils.isEmpty(param.getHistoricalMoneyFrom())){
+            param.setHistoricalMoneyFrom("");
+        }
+        if (StringUtils.isEmpty(param.getHistoricalMoneyTo())){
+            param.setHistoricalMoneyTo("");
+        }
+        List<String> flags = Arrays.asList("0", "1", "2", "3", "4", "5");
+        if (!flags.contains(param.getHistoricalDirection())){
+            result.put("historicalDirection","可选的来往账标识:" + flags);
+        }
+        if (!Pattern.compile("[0-9]{2}").matcher(param.getHistoricalRecnum()).matches() || Integer.parseInt(param.getHistoricalRecnum()) > 50) {
+            result.put("historicalRecnum","查询数量不能大于50且必须两位，当小于两位时前面补0");
+        }
+        if (!result.isEmpty()){
+            validateResult.put(param,result);
+            throw new ValidationException(this.apiNo + " 参数校验异常。" + JSON.toJSONString(result),validateResult,this.apiNo);
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0043XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0043XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0043XmlBuilder.java	(版本 31412)
@@ -0,0 +1,63 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.util.B2e0043Type;
+import org.dom4j.Element;
+
+import java.util.*;
+
+/*
+ * @program: BankEnterprise
+ * @description: 构造 CNAPS、中行机构号文件下载(b2e0043)接口请求报文
+ * @author: Ma.ChengJian
+ * @create: 2022-02-07 15:03
+ */
+public class B2e0043XmlBuilder extends Bocb2eXMLBuilder{
+
+    public B2e0043XmlBuilder() {
+        this.apiNo = "b2e0043";
+    }
+
+    public B2e0043XmlBuilder(List<BocB2eParam> param) {
+        this.params = param;
+        this.apiNo = "b2e0043";
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        //请求数据
+        Element trnB2e0043Rq = trans.addElement("trn-b2e0043-rq");
+        Element tasktpy = trnB2e0043Rq.addElement("b2e0043-rq").addElement("tasktpy");
+        tasktpy.addText(this.params.get(0).getTasktpy());
+        return this;
+    }
+
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        Map<String,String> result = null;
+        if (this.params == null || this.params.size() != 1){
+            throw new ValidationException(this.apiNo + " 请求参数校验异常。必须包含1笔交易参数，实际指定的交易数量:"  + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,apiNo);
+        }else if (!checkType(this.params.get(0).getTasktpy())){
+            result = new HashMap<>();
+            result.put("tasktpy","下载的文件类型可选值包括0、1、2");
+            validateResult.put(params.get(0),result);
+        }
+        if (!validateResult.isEmpty()){
+            throw new ValidationException(this.apiNo + " 参数校验异常。错误的交易参数:" + JSON.toJSONString(result),validateResult,this.apiNo);
+        }
+        return this;
+    }
+
+    private boolean checkType(String type){
+        for (B2e0043Type value : B2e0043Type.values()) {
+            if (value.getType().equals(type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0061XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0061XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0061XmlBuilder.java	(版本 31412)
@@ -0,0 +1,137 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.dom4j.Element;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-02-10 10:20
+ */
+@Slf4j
+public class B2e0061XmlBuilder extends Bocb2eXMLBuilder {
+
+
+    public B2e0061XmlBuilder() {
+        this.apiNo = "b2e0061";
+    }
+
+    /**
+     * 使用BocB2eParam创建一个B2e0061请求报文构造者。
+     */
+    public B2e0061XmlBuilder(List<BocB2eParam> params) {
+        this.params = params;
+        this.apiNo = "b2e0061";
+    }
+
+    /**
+     * 校验公对私转账汇款(b2e0061)请求参数,并删除不符和要求的参数。
+     * @throws ValidationException
+     */
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        Iterator<BocB2eParam> it = params.iterator();
+        while (it.hasNext()) {
+            Map<String,String> result = null;
+            BocB2eParam param = it.next();
+            if (!B2eValidateUtil.payNo(param.getPayNo())) {
+                result = put("payNo","必须为非空字符串1-12位",result);
+            }
+            if (!B2eValidateUtil.money(param.getPayMoney())){
+                result = put("payMoney","支付金额最多13位整数，最多2位小数",result);
+            }
+            if (!B2eValidateUtil.payCurrency(param.getPayCurrency())){
+                result = put("payCurrency","货币类型只支持001、CNY",result);
+            }
+            if (!B2eValidateUtil.priority(param.getBankPriority())){
+                result = put("bankPriority","银行处理优先级可选参数包括0、1，当该参数为null时默认为0",result);
+            }
+            if (!B2eValidateUtil.scheduleDate(param.getScheduleDate())){
+                result = put("scheduleDate","要求转账日期范围为系统当前日期（含当日）之后的一个月内，当参数为空时默认为系统当前时间",result);
+            }
+            if (!B2eValidateUtil.payAcc(param.getPayAcc())){
+                result = put("payAcc","付款账号必须为1-20位字符串",result);
+            }
+            if (!B2eValidateUtil.payeeAcc(param.getPayeeAcc())){
+                result = put("payeeAcc","收款账号必须为非空字符串长度1-35位",result);
+            }
+            if (StringUtils.isEmpty(param.getPayeeBankNo()) && StringUtils.isEmpty(param.getPayeeBankName())) {
+                result = put("payeeBankName","收款行联行号为空时开户行名称不得为空",result);
+            }
+            if (!B2eValidateUtil.payeeName(param.getPayeeName())) {
+                result = put("payeeName","收款人名称必须为非空字符串长度1-70",result);
+            }
+            if (!Arrays.asList("0","1").contains(param.getBocflag())){
+                result = put("bocflag","必须指定收款账号是否为中行，1:中行 0:他行",result);
+            }
+            if (!B2eValidateUtil.priority(param.getCusPriority())) {
+                result = put("cusPriority","客户处理优先级可选参数包括0、1，当该参数为null时默认为0",result);
+            }
+            if (result != null){
+                it.remove();
+                validateResult.put(param,result);
+            }
+        }
+        if (params.isEmpty()) {
+            throw new ValidationException(this.apiNo + " 校验通过的请求交易参数为0,异常参数包括:" + JSON.toJSONString(validateResult,true),validateResult,this.apiNo);
+        }else if (!validateResult.isEmpty() && callback != null){
+            callback.callback(validateResult,apiNo);
+        }else if (!validateResult.isEmpty()){
+            log.error("校验未通过的请求参数将被丢弃:{}",JSON.toJSONString(validateResult.keySet()));
+        }
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        //请求数据
+        Element trnB2e0043Rq = trans.addElement("trn-b2e0061-rq");
+        //每笔交易
+        for (BocB2eParam param : params) {
+            Element b2e0061Rq = trnB2e0043Rq.addElement("b2e0061-rq");
+            /**
+             * 基本业务参数
+             */
+            //指令ID
+            b2e0061Rq.addElement("insid").addText(param.getPayNo());
+            //转账金额
+            b2e0061Rq.addElement("trnamt").addText(param.getPayMoney());
+            //货币类型
+            b2e0061Rq.addElement("trncur").addText(param.getPayCurrency());
+            //银行处理优先级
+            b2e0061Rq.addElement("priolv").addText(Optional.ofNullable(param.getBankPriority()).orElse("0"));
+            //客户处理优先级
+            b2e0061Rq.addElement("cuspriolv").addText(Optional.ofNullable(param.getCusPriority()).orElse("0"));
+            //要求转账日期
+            b2e0061Rq.addElement("trfdate").addText(new SimpleDateFormat("yyyyMMdd").format(Optional.ofNullable(param.getScheduleDate()).orElse(new Date())));
+            //是否是中行账号
+            b2e0061Rq.addElement("bocflag").addText(param.getBocflag());
+            //用途.字符串长度0-200.(一个汉字=2个字符)注:不支持"|"
+            b2e0061Rq.addElement("furinfo").addText(Optional.ofNullable(param.getFurinfo()).orElse(""));
+            /**
+             * 付款人账户信息
+             */
+            Element fractn = b2e0061Rq.addElement("fractn");
+            fractn.addElement("actacn").addText(param.getPayAcc());//付款行账号
+            fractn.addElement("actnam").addText(Optional.ofNullable(param.getPayName()).orElse(""));//付款人名称
+            /**
+             * 收款人账户信息
+             */
+            Element toactn = b2e0061Rq.addElement("toactn");
+            toactn.addElement("actacn").addText(param.getPayeeAcc());//收款账号
+            toactn.addElement("toname").addText(param.getPayeeName());//收款人名称
+            toactn.addElement("toibkn").addText(Optional.ofNullable(param.getPayeeBankNo()).orElse(""));//收款人名称
+            toactn.addElement("tobknm").addText(Optional.ofNullable(param.getPayeeBankName()).orElse(""));//收款人开户行名称
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0251Builder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0251Builder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0251Builder.java	(版本 31412)
@@ -0,0 +1,65 @@
+package com.novaone.entity.bocb2e;
+
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.entity.bocb2e.Bocb2eXMLBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-01-21 21:03
+ */
+@Slf4j
+//public class B2e0251XmlBuilder extends Bocb2eXMLBuilder {
+public class B2e0251Builder {
+
+//    private Element bocb2e = document.addElement("bocb2e");
+//
+//    private BocB2eParam bo;
+//
+//    @Override
+//    public Bocb2eXMLBuilder rootAttribute(String version, String locale) {
+//        if (StringUtils.isEmpty(version)) {
+//            version = "120";
+//        }
+//        if (StringUtils.isEmpty(locale)) {
+//            version = "zh_CN";
+//        }
+//        bocb2e.addAttribute("version",version);
+//        bocb2e.addAttribute("locale",locale);
+//        return this;
+//    }
+//
+//    @Override
+//    public Bocb2eXMLBuilder trans() {
+//        Element trnB2e0251Rq = bocb2e.addElement("trans").addElement("trn-b2e0251-rq");
+//        Element b2e0251Rq = trnB2e0251Rq.addElement("b2e0251-rq");
+//        b2e0251Rq.addElement("trnamt").addText("1000.00");
+//        b2e0251Rq.addElement("priolv").addText("客户处理优先级");
+//        b2e0251Rq.addElement("insid").addText("客户业务ID");
+//
+//        Element payerInfo = b2e0251Rq.addElement("fractn");
+//        payerInfo.addElement("actacn").addText("付款账号");
+//        payerInfo.addElement("actnam").addText("付款人名称");
+//
+//        Element payeeInfo = b2e0251Rq.addElement("toactn");
+//        payeeInfo.addElement("toibkn").addText("收款银行标志号非空");
+//        payeeInfo.addElement("actacn").addText("收款人账号");
+//        payeeInfo.addElement("toname").addText("收款人名称");
+//        payeeInfo.addElement("type").addText("收款人类型");
+//        return this;
+//    }
+//
+//
+//    @Override
+//    public Bocb2eXMLBuilder validate() throws ValidationException {
+//        return this;
+//    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2e0500XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2e0500XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2e0500XmlBuilder.java	(版本 31412)
@@ -0,0 +1,113 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import org.dom4j.Element;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/*
+ * @program: BankEnterprise
+ * @description: 构造逐笔电子回单下载(b2e0500)请求XML报文
+ * @author: Ma.ChengJian
+ * @create: 2022-02-11 14:29
+ */
+public class B2e0500XmlBuilder extends Bocb2eXMLBuilder{
+
+
+
+    /**
+     * 使用BocB2eParam创建一个B2e0500请求报文构造者。
+     */
+    public B2e0500XmlBuilder() {
+        this.apiNo = "b2e0500";
+    }
+
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        BocB2eParam vo = params.get(0);
+        Element trnB2e0050Rq = bocb2e.addElement("trans").addElement("trn-b2e0500-rq");
+        Element b2e0500Rq = trnB2e0050Rq.addElement("b2e0500-rq");
+        b2e0500Rq.addElement("tratype").addText(Optional.ofNullable(vo.getBillDownTratype()).orElse("D"));//交易类型
+        b2e0500Rq.addElement("insid").addText(vo.getBillDownInsid());//发起付款时的客户业务编号
+            b2e0500Rq.addElement("emptyflag").addText(Optional.ofNullable(vo.getBillDownEmptyflag()).orElse("N"));//无交易时是否生成空回单文件
+
+        //金额范围
+        Element amtscope = b2e0500Rq.addElement("amtscope");
+        amtscope.addElement("minamt").addText(Optional.ofNullable(vo.getBillDownMinamt()).orElse(vo.getPayMoney()));
+        amtscope.addElement("maxamt").addText(Optional.ofNullable(vo.getBillDownMaxamt()).orElse(vo.getPayMoney()));
+
+        //日期范围
+        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
+        Element datescope = b2e0500Rq.addElement("datescope");
+        datescope.addElement("from").addText(format.format(vo.getBillDownFrom()));//起始日期
+        datescope.addElement("to").addText(format.format(vo.getBillDownTo()));//截止日期
+
+        //对方信息
+        Element domaccount = b2e0500Rq.addElement("domaccount");
+        domaccount.addElement("transact").addText(Optional.ofNullable(vo.getPayeeAcc()).orElse(""));
+        domaccount.addElement("transname").addText(Optional.ofNullable(vo.getPayeeName()).orElse(""));
+
+        //multidata → confirmdata → actacn
+        Element confirmdata = b2e0500Rq.addElement("multidata").addElement("confirmdata");
+        confirmdata.addElement("actacn").addText(vo.getPayAcc());//查询账号非空1-32位字母或数字。允许符号*/_-
+        confirmdata.addElement("ibknum").addText("");
+        return this;
+    }
+
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.size() != 1){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量只能包含1个。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        BocB2eParam vo = params.get(0);
+        Map<String,String> param = new HashMap<>();
+        if (ObjectUtils.isEmpty(vo.getBillDownTo()) && !ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownTo(new Date());
+        }
+        if (ObjectUtils.isEmpty(vo.getBillDownFrom()) && !ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownFrom(new Date());
+        }
+        if (StringUtils.isEmpty(vo.getBillDownInsid())){
+            vo.setBillDownInsid(System.currentTimeMillis() + "_" + "bill");
+        }
+        if (!B2eValidateUtil.billInsid(vo.getBillDownInsid())){
+            param.put("billInsid","逐笔电子回单下载指定的客户交易编号必须为1-32为");
+        }
+        if (!B2eValidateUtil.payAcc(vo.getPayAcc())){
+            param.put("payAcc","逐笔电子回单下载必须指定查询账号,且长度为1-12位");
+        }
+        if (ObjectUtils.isEmpty(vo.getBillDownFrom())) {
+            param.put("from","逐笔电子回单下载起始日期不得为空");
+        }
+        if (ObjectUtils.isEmpty(vo.getBillDownTo())) {
+            param.put("to","逐笔电子回单下载结束日期不得为空");
+        }
+        if (vo.getBillDownTratype() != null && !Pattern.compile("^[ACDNPW]$").matcher(vo.getBillDownTratype()).matches()) {
+            param.put("tratype","逐笔电子回单下载交易类型不为空时可选项为A、C、D、N、P、W，为空时默认为D");
+        }
+        if (!StringUtils.isEmpty(vo.getBillDownEmptyflag()) && !Pattern.compile("^[YN]?$").matcher(vo.getBillDownEmptyflag()).matches()){
+            param.put("emptyflag","无交易时是否生成空回单文件可选值Y、N，为空时默认为Y");
+        }
+
+        if (StringUtils.isEmpty(vo.getBillDownMaxamt()) && StringUtils.isEmpty(vo.getPayMoney())) {
+            vo.setBillDownMaxamt("999999999999999999.99");
+        }
+        if (StringUtils.isEmpty(vo.getBillDownMinamt()) && StringUtils.isEmpty(vo.getPayMoney())) {
+            vo.setBillDownMinamt("0");
+        }
+        if (!param.isEmpty()) {
+            validateResult.put(vo,param);
+            throw new ValidationException(this.apiNo + " 参数校验异常。" + JSON.toJSONString(param),validateResult,this.apiNo);
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/B2ee0531XmlBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/B2ee0531XmlBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/B2ee0531XmlBuilder.java	(版本 31412)
@@ -0,0 +1,90 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.payment.B2eValidateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.dom4j.Element;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-17 14:30
+ */
+@Slf4j
+public class B2ee0531XmlBuilder extends Bocb2eXMLBuilder{
+
+    public B2ee0531XmlBuilder() {
+        this.apiNo = "b2e0531";
+    }
+
+    @Override
+    public Bocb2eXMLBuilder trans() {
+        BocB2eParam vo = params.get(0);
+        Element trnB2e0531Rq = bocb2e.addElement("trans").addElement("trn-b2e0531-rq");
+        Element b2e0531Rq = trnB2e0531Rq.addElement("b2e0531-rq");
+        Element multidata = b2e0531Rq.addElement("multidata").addElement("confirmdata");
+        multidata.addElement("actacn").addText(vo.getPayAcc());
+//        multidata.addElement("ibknum").addText("");
+        b2e0531Rq.addElement("trantype").addText("A");//交易类型 A:全部 D：往账
+        b2e0531Rq.addElement("begnum").addText(vo.getBillBeginNum() + "");
+        //日期范围
+        Element datescope = b2e0531Rq.addElement("datescope");
+        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
+        datescope.addElement("from").addText(sf.format(vo.getBillDownFrom()));
+        datescope.addElement("to").addText(sf.format(vo.getBillDownTo()));
+
+        //金额范围
+        Element amtscope = b2e0531Rq.addElement("amtscope");
+        amtscope.addElement("minamt").addText(Optional.ofNullable(vo.getBillDownMinamt()).orElse(vo.getPayMoney()));
+        amtscope.addElement("maxamt").addText(Optional.ofNullable(vo.getBillDownMaxamt()).orElse(vo.getPayMoney()));
+
+        //对方信息
+        Element domaccount = b2e0531Rq.addElement("domaccount");
+        domaccount.addElement("transact").addText(Optional.ofNullable(vo.getPayeeAcc()).orElse(""));
+        domaccount.addElement("transname").addText(Optional.ofNullable(vo.getPayeeName()).orElse(""));
+
+        b2e0531Rq.addElement("recnum").addText("");
+        return this;
+    }
+
+    @Override
+    public Bocb2eXMLBuilder validate() throws ValidationException {
+        if (this.params == null || this.params.size() != 1){
+            throw new ValidationException(this.apiNo + " 请求查询交易数量只能包含1个。给定的交易参数数量:" + Optional.ofNullable(this.params).orElse(Collections.emptyList()).size(),validateResult,this.apiNo);
+        }
+        BocB2eParam vo = params.get(0);
+        Map<String,String> result = new HashMap<>();
+        if (ObjectUtils.isEmpty(vo.getBillDownTo()) && !ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownTo(vo.getScheduleDate());
+        }else if(ObjectUtils.isEmpty(vo.getBillDownTo()) && ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownTo(new Date());
+        }
+        if (ObjectUtils.isEmpty(vo.getBillDownFrom()) && !ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownFrom(vo.getScheduleDate());
+        }else if(ObjectUtils.isEmpty(vo.getBillDownFrom()) && ObjectUtils.isEmpty(vo.getScheduleDate())){
+            vo.setBillDownFrom(new Date());
+        }
+        if (!B2eValidateUtil.payAcc(vo.getPayAcc())){
+            result.put("payAcc","电子回单查询必须指定查询账号,且长度为1-20位");
+        }
+
+        if (StringUtils.isEmpty(vo.getBillDownMaxamt()) && StringUtils.isEmpty(vo.getPayMoney())) {
+            vo.setBillDownMaxamt("999999999999999999.99");
+        }
+        if (StringUtils.isEmpty(vo.getBillDownMinamt()) && StringUtils.isEmpty(vo.getPayMoney())) {
+            vo.setBillDownMinamt("0");
+        }
+        if (!result.isEmpty()) {
+            validateResult.put(vo,result);
+            throw new ValidationException(this.apiNo + " 参数校验异常。" + JSON.toJSONString(result),validateResult,this.apiNo);
+        }
+        return this;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/BocRequestContext.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/BocRequestContext.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/BocRequestContext.java	(版本 31412)
@@ -0,0 +1,250 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.common.exception.BocB2eException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.payment.BocStatus;
+import com.novaone.util.CommonInfo;
+import com.novaone.util.CreateXml;
+import com.novaone.util.HttpUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.json.xml.XMLSerializer;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-02-11 13:46
+ */
+@Slf4j
+public class BocRequestContext {
+    /**
+     * 支付参数
+     */
+    protected List<BocB2eParam> param;
+    /**
+     * 请求报文
+     */
+    protected String reqXml;
+    /**
+     * 响应报文
+     */
+    protected String respXml;
+    /**
+     * 接口编号
+     */
+    protected String apiNo;
+    /**
+     * XML报文构造
+     */
+    protected XmlBuildContext xmlBuildContext;
+    /**
+     * 用于构造XML请求报文的头部，以及获取前置机的IP端口
+     */
+    protected ConfigBocVO configBocVO;
+
+    public BocRequestContext() {
+
+    }
+
+    public BocRequestContext(XmlBuildContext buildContext) {
+        this.xmlBuildContext = buildContext;
+        this.param = buildContext.getParam();
+        this.configBocVO = buildContext.getConfigBocVO();
+        this.apiNo = buildContext.getXmlBuilder().getApiNo();
+        this.initReqXml();
+    }
+
+    public BocRequestContext(List<BocB2eParam> param, XmlBuildContext xmlBuildContext, ConfigBocVO configBocVO) {
+        this.param = param;
+        this.xmlBuildContext = xmlBuildContext;
+        this.configBocVO = configBocVO;
+        this.apiNo = xmlBuildContext.getXmlBuilder().getApiNo();
+        this.xmlBuildContext.setConfigBocVO(configBocVO);
+        this.initReqXml();
+    }
+
+    /**
+     * 初始化请求上下文环境
+     * @param param 调用银行接口的业务参数
+     * @param xmlBuildContext XML报文构建环境
+     * @param configBocVO null
+     */
+    public BocRequestContext(BocB2eParam param, XmlBuildContext xmlBuildContext, ConfigBocVO configBocVO) {
+        this(Collections.singletonList(param), xmlBuildContext, configBocVO);
+    }
+
+
+    /**
+     * 请求中行接口，完成付款。。
+     */
+    public BocRequestContext request(){
+        this.respXml = callBoc(this.reqXml,this.apiNo);
+        return this;
+    }
+
+
+    protected String callBoc(String reqXml,String apiNo){
+        String url = configBocVO.getProtocol() + "://" + configBocVO.getIp() + ":" + configBocVO.getPort() + "/B2EC/E2BServlet";
+        log.info("{} 请求URL:{}  请求XML:{}", apiNo, url, reqXml);
+        try {
+            String respXML = postRequest(url, reqXml, StandardCharsets.UTF_8);
+            log.info("{} 接口访问成功 响应XML:{}", apiNo, respXML);
+            return respXML;
+        } catch (Exception e) {
+            throw BocB2eException.builder().message("接口调用异常").cause(e).reqXml(reqXml).apiNo(apiNo).build();
+        }
+    }
+
+
+    /**
+     * 获取响应数据
+     */
+    public JSONArray getResponseArray(){
+        if (StringUtils.isEmpty(this.respXml)) {
+            throw new RuntimeException(this.apiNo + "接口正常发起请求前无法解析响应数据..");
+        }
+        return getRespArr(this.respXml,this.apiNo);
+    }
+
+
+    /**
+     * 初始化请求报文
+     */
+    protected void initReqXml(){
+        this.reqXml = xmlBuildContext.constructXML();
+    }
+
+
+    /**
+     * 获取响应XML报文中，业务处理结果。
+     * @return xml报文中业务处理结果的JSON对象。
+     */
+    public JSONObject getResponseJson(){
+        if (StringUtils.isEmpty(this.respXml)) {
+            throw new BocB2eException(this.apiNo + "接口正常发起请求前无法解析响应数据..",this.apiNo);
+
+        }
+        JSONObject respJson = this.parseXMl2RespData(this.respXml,this.apiNo);
+        checkRespStatus(respJson,this.apiNo);
+        return respJson;
+    }
+
+    /**
+     * 解析响应报文的Trns部分,返回业务处理结果数据。当请求没有被正常处理时抛出异常
+     * @param respXml 响应XML报文
+     * @return xml报文中业务处理结果的JSON对象。
+     */
+    protected JSONArray getRespArr(String respXml,String apiNo){
+        JSONObject trnRsJson = parseXMl2RespData(respXml,apiNo);
+        //请求报文处理状态，当没有正常处理时抛出异常。
+        checkRespStatus(trnRsJson,apiNo);
+        Object res = trnRsJson.get(apiNo + "-rs");
+        if (res instanceof JSONObject) {
+            return new JSONArray(Collections.singletonList(res));
+        }
+        //返回业务处理结果部分。
+        return trnRsJson.getJSONArray(apiNo + "-rs");
+    }
+
+
+    /**
+     * 校验响应处理状态
+     */
+    private void checkRespStatus(JSONObject trnRsJson,String apiNo) {
+        JSONObject reqState = trnRsJson.getJSONObject("status");
+        String rspcod = reqState.getString("rspcod");
+        String rspmsg = reqState.getString("rspmsg");
+        if (!BocStatus.OK.code().equals(rspcod) && !BocStatus.UNFINISHED.code().equals(rspcod) && !BocStatus.EMPTY_RESULT.code().equals(rspcod)) {
+            throw BocB2eException.builder().message(apiNo + "接口返回了失败的响应状态").respCode(rspcod).respMsg(rspmsg).reqXml(this.reqXml).respXml(this.respXml).apiNo(this.apiNo).build();
+        }
+    }
+
+
+    /**
+     * 解析XML返回接口业务数据部分
+     */
+    private JSONObject parseXMl2RespData(String respXml,String apiNo) {
+        JSONObject trans = this.parseRespXml2Trans(respXml);
+        JSONObject trnRsJson = trans.getJSONObject("trn-"+ apiNo +"-rs");
+        return trnRsJson;
+    }
+
+
+    /**
+     * 解析响应的XML报文,返回报文Trns部分。当响应没有正常处理请求报文时抛出异常。
+     * @param respXml 接口响应的XML报文。
+     */
+    protected JSONObject parseRespXml2Trans(String respXml){
+        JSONObject respJson = this.xmlToJson(respXml);
+        JSONObject trans = respJson.getJSONObject("trans");
+        JSONObject b2eerr = trans.getJSONObject("trn-b2eerror-rs");
+        if (!ObjectUtils.isEmpty(b2eerr)) {
+            JSONObject statue = b2eerr.getJSONObject("status");
+            BocB2eException e = BocB2eException.builder().message("接口调用失败").respCode(statue.getString("rspcod")).respMsg(statue.getString("rspmsg")).reqXml(this.reqXml).respXml(this.respXml).apiNo(this.apiNo).build();
+            throw e;
+        }
+        return trans;
+    }
+
+    private JSONObject xmlToJson(String xml) {
+        XMLSerializer xmlSerializer = new XMLSerializer();
+        String json = xmlSerializer.read(xml).toString();
+        return JSON.parseObject(json);
+    }
+
+
+
+    private String postRequest(String url, String xml, Charset charset) throws Exception {
+        BufferedReader in = null;
+        PrintWriter out = null;
+        StringBuilder result = new StringBuilder();
+        try {
+            URL connURL = new URL(url);
+            java.net.HttpURLConnection httpConn = (HttpURLConnection) connURL.openConnection();
+            httpConn.setConnectTimeout(1000 * 60 * 2);
+            httpConn.setReadTimeout(1000 * 60 * 2);
+            httpConn.setRequestProperty("Accept-Charset", "UTF-8");
+            httpConn.setRequestProperty("Source-Host", CommonInfo.HOST);
+            httpConn.setRequestMethod("POST");
+            httpConn.setDoInput(true);
+            httpConn.setDoOutput(true);
+            out = new PrintWriter(new OutputStreamWriter(httpConn.getOutputStream(), charset));
+            out.write(xml);
+            out.flush();
+            in = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), charset));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            return result.toString();
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/Bocb2eXMLBuilder.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/Bocb2eXMLBuilder.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/Bocb2eXMLBuilder.java	(版本 31412)
@@ -0,0 +1,120 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.ValidationException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.payment.ValidationExceptionCallback;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.springframework.util.ObjectUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/*
+ * @program: BankEnterprise
+ * @description: 获取一个BocB2e请求XML。
+ * @author: Ma.ChengJian
+ * @create: 2022-01-21 19:31
+ */
+@Data
+@Slf4j
+public abstract class Bocb2eXMLBuilder {
+
+    protected Document document = DocumentHelper.createDocument();
+
+    protected Element bocb2e = document.addElement("bocb2e");
+
+    protected Element trans = bocb2e.addElement("trans");
+
+    protected Map<BocB2eParam, Map<String, String>>  validateResult = new HashMap<>();
+
+    protected ValidationExceptionCallback callback;
+
+    protected ConfigBocVO config;
+
+    protected List<BocB2eParam> params;
+
+    protected String apiNo;
+
+    public Bocb2eXMLBuilder xmlEncoding(){
+        document.setXMLEncoding("UTF-8");
+        return this;
+    }
+
+    /**
+     * 设置根节点的version、locale属性值
+     * @param version 设置给根节点version的属性值
+     * @param locale 设置给根节点locale的属性值。
+     */
+    public Bocb2eXMLBuilder rootAttribute(String version,String locale){
+        if (StringUtils.isEmpty(version)) {
+            version = "120";
+        }
+        if (StringUtils.isEmpty(locale)) {
+            locale = "zh_CN";
+        }
+        bocb2e.addAttribute("version",version);
+        bocb2e.addAttribute("locale",locale);
+        return this;
+    }
+
+    /**
+     * 构造XML的head部分
+     */
+    public Bocb2eXMLBuilder head(){
+        Element head = bocb2e.addElement("head");
+        head.addElement("termid").setText(config.getTermid());
+        head.addElement("trnid").setText(Optional.ofNullable(config.getTrnid()).orElse(""));
+        head.addElement("custid").setText(config.getCustid());
+        head.addElement("cusopr").setText(config.getUser());
+        head.addElement("trncod").setText(this.apiNo);
+        head.addElement("token").setText(Optional.ofNullable(System.getProperty("bankSignToken")).orElse(""));
+        return this;
+    }
+
+    /**
+     * 构造XML的trans部分
+     */
+    public abstract Bocb2eXMLBuilder trans();
+
+    /**
+     * 构造整个XML请求文档
+     */
+    public String build(){
+        log.info("构造 {} 请求报文 报文交易数量:{} 全部交易参数:{}",apiNo,params.size(), JSON.toJSONString(params));
+        return document.asXML();
+    }
+
+    /**
+     * 初始化构造XML所需要的参数。
+     * @param params 调用端请求参数，包含构造请求XML所需要的参数。
+     */
+    public Bocb2eXMLBuilder initParam(List<BocB2eParam> params,ConfigBocVO configBocVO, ValidationExceptionCallback callback){
+        if (ObjectUtils.isEmpty(this.params)) {
+            this.params = params;
+//            log.info("初始化{} 报文交易参数 初始化参数数量:{} 全部初始化参数:{}",apiNo,params.size(), JSON.toJSONString(params));
+        }
+        if (ObjectUtils.isEmpty(this.config)) this.config = configBocVO;
+        if (this.callback == null) this.callback = callback;
+        return this;
+    }
+
+    public abstract Bocb2eXMLBuilder validate() throws ValidationException;
+
+
+    protected Map<String ,String> put(String key, String value, Map<String ,String> map){
+        if (map == null) {
+            map = new HashMap<String ,String>();
+        }
+        map.put(key,value);
+        return map;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/PayRequestContext.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/PayRequestContext.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/PayRequestContext.java	(版本 31412)
@@ -0,0 +1,145 @@
+package com.novaone.entity.bocb2e;
+
+import com.alibaba.fastjson.JSONArray;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.TradingRecord;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.payment.ValidationExceptionCallback;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/*
+ * @program: BankEnterprise
+ * @description: 根据支付参数调用中行合适的付款接口完成支付
+ * @author: Ma.ChengJian
+ * @create: 2022-01-25 11:05
+ */
+@Slf4j
+public class PayRequestContext extends BocRequestContext {
+
+    /**
+     * 对公转账的参数
+     */
+    private List<BocB2eParam> toCorporates;
+    /**
+     * 对私转账参数
+     */
+    private List<BocB2eParam> toPersonals;
+    /**
+     * 对公转账XML
+     */
+    private String b2e0009ReqXml;
+    private String b2e0009RespXml;
+    /**
+     * 对私转账XML
+     */
+    private String b2e0061ReqXml;
+    private String b2e0061RespXml;
+
+    private String b2e0009ApiNo;
+
+    private String b2e0061ApiNo;
+
+    private ValidationExceptionCallback callback;
+
+    /**
+     * 根据支付的参数构造一个PayRequestContext
+     */
+    public PayRequestContext(BocB2eParam param, ConfigBocVO configBocVO){
+        this(Collections.singletonList(param),configBocVO,null);
+    }
+
+
+    private void groupByPayeeName(){
+        Map<String, List<BocB2eParam>> map = this.param.stream().collect(Collectors.groupingBy(BocB2eParam::getPayeeType));
+        this.toPersonals = map.get(BocB2eParam.PayeeType.PRIVATE);
+        this.toCorporates = map.get(BocB2eParam.PayeeType.CORPORATE);
+        log.info("转账汇款全部待交易数量:{} 公对公交易数量:{} 公对私交易数量:{}",param.size(),toCorporates==null?0:toCorporates.size(),toPersonals==null?0:toPersonals.size());
+    }
+
+
+
+    public PayRequestContext(List<BocB2eParam> params, ConfigBocVO configBocVO, ValidationExceptionCallback callback) {
+        this.callback = callback;
+        this.param = params;
+        this.configBocVO = configBocVO;
+        groupByPayeeName();
+        this.initReqXml();
+    }
+
+
+    public PayRequestContext(List<BocB2eParam> params, ConfigBocVO configBocVO) {
+        this(params,configBocVO,null);
+    }
+
+
+    public PayRequestContext(BocB2eParam param, ConfigBocVO configBocVO, ValidationExceptionCallback callback){
+        this(Collections.singletonList(param),configBocVO,callback);
+    }
+
+
+    /**
+     * 判断收款账号是否为中行账号。
+     * 发起对私转账汇款时需要明确收款账号是否为中行账号。
+     */
+    private boolean isBoc(BocB2eParam bocB2eParam){
+        //收款账号开户行名称出现”中行“或者”中国银行“视为中行账号。
+        return bocB2eParam.getPayeeBankName().contains("中行") || bocB2eParam.getPayeeBankName().contains("中国银行");
+    }
+
+
+
+    /**
+     * 根据收款账号类型创建XML请求报文。
+     * 如果是对公账号则需要向银行系统发起b2e0009转账汇款请求。不是对公账号发起b2e0061转账汇款请求。
+     */
+    @Override
+    protected void initReqXml(){
+        if (!ObjectUtils.isEmpty(toPersonals)){
+            //判断并出示换对私装转账是否时收款行是否时中行
+            toPersonals.forEach(bocParam->{
+                if (!"1".equals(bocParam.getBocflag()) && !"0".equals(bocParam.getBocflag())) {
+                    bocParam.setBocflag(this.isBoc(bocParam)?"1":"0");
+                }
+            });
+            B2e0061XmlBuilder b2e0061XmlBuilder = new B2e0061XmlBuilder();
+            b2e0061ApiNo = b2e0061XmlBuilder.getApiNo();
+            b2e0061ReqXml = XmlBuildContext.builder().configBocVO(configBocVO).xmlBuilder(b2e0061XmlBuilder).param(toPersonals).callback(callback).build().constructXML();
+        }
+        if (!ObjectUtils.isEmpty(toCorporates)){
+            B2e0009XmlBuilder b2e0009XmlBuilder = new B2e0009XmlBuilder();
+            b2e0009ApiNo = b2e0009XmlBuilder.getApiNo();
+            b2e0009ReqXml = XmlBuildContext.builder().configBocVO(configBocVO).callback(callback).xmlBuilder(b2e0009XmlBuilder).param(toCorporates).build().constructXML();
+        }
+    }
+
+    @Override
+    public BocRequestContext request() {
+        if (!StringUtils.isEmpty(b2e0009ReqXml)){
+            b2e0009RespXml = this.callBoc(b2e0009ReqXml, b2e0009ApiNo);
+        }
+        if (!StringUtils.isEmpty(b2e0061ReqXml)){
+            b2e0061RespXml = this.callBoc(b2e0061ReqXml, b2e0061ApiNo);
+        }
+        return this;
+    }
+
+    /**
+     * JSONArray元素1：对公转账的响应数据
+     * JSONArray元素2：对私转账的响应数据
+     */
+    public JSONArray getResponseArray(){
+        JSONArray array = new JSONArray();
+        if (!StringUtils.isEmpty(b2e0061RespXml)){
+            array.addAll(getRespArr(b2e0061RespXml,b2e0061ApiNo));
+        }
+        if (!StringUtils.isEmpty(b2e0009RespXml)){
+            array.addAll(getRespArr(b2e0009RespXml,b2e0009ApiNo));
+        }
+        return array;
+    }
+}
Index: src/main/java/com/novaone/entity/bocb2e/XmlBuildContext.java
===================================================================
--- src/main/java/com/novaone/entity/bocb2e/XmlBuildContext.java	(不存在的)
+++ src/main/java/com/novaone/entity/bocb2e/XmlBuildContext.java	(版本 31412)
@@ -0,0 +1,115 @@
+package com.novaone.entity.bocb2e;
+
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.payment.ValidationExceptionCallback;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description: XML构建环境
+ * @author: Ma.ChengJian
+ * @create: 2022-01-22 17:53
+ */
+@Data
+@Slf4j
+public class XmlBuildContext {
+
+
+    private Bocb2eXMLBuilder xmlBuilder;
+
+    private String attributeVersion;
+
+    private String  attributeLocale;
+
+    private ConfigBocVO configBocVO;
+
+    private ValidationExceptionCallback callback;
+
+    private List<BocB2eParam> param;
+
+
+    public XmlBuildContext(XmlBuildContextBuiler contextBuiler) {
+        this.callback = contextBuiler.callback;
+        this.xmlBuilder = contextBuiler.xmlBuilder;
+        this.configBocVO = contextBuiler.configBocVO;
+        this.attributeVersion = contextBuiler.attributeVersion;
+        this.attributeLocale = contextBuiler.attributeLocale;
+        this.param = contextBuiler.params;
+    }
+
+    public static class XmlBuildContextBuiler{
+
+        private Bocb2eXMLBuilder xmlBuilder;
+
+        private ConfigBocVO configBocVO;
+
+        private String attributeVersion;
+
+        private String  attributeLocale;
+
+        private ValidationExceptionCallback callback;
+
+        private List<BocB2eParam> params = new ArrayList<>();
+
+        public XmlBuildContextBuiler xmlBuilder(Bocb2eXMLBuilder xmlBuilder){
+            this.xmlBuilder = xmlBuilder;
+            return this;
+        }
+
+        public XmlBuildContextBuiler setAttributeVersion(String version){
+            this.attributeVersion = version;
+            return this;
+        }
+
+        public XmlBuildContextBuiler setAttributeLocale(String locale){
+            this.attributeLocale = locale;
+            return this;
+        }
+
+        public XmlBuildContextBuiler configBocVO(ConfigBocVO configBocVO){
+            this.configBocVO = configBocVO;
+            return this;
+        }
+
+        public XmlBuildContext build(){
+            return new XmlBuildContext(this);
+        }
+
+        public XmlBuildContextBuiler param(List<BocB2eParam> params){
+            this.params.addAll(params);
+            return this;
+        }
+
+        public XmlBuildContextBuiler callback(ValidationExceptionCallback callback){
+            this.callback = callback;
+            return this;
+        }
+
+        public XmlBuildContextBuiler param(BocB2eParam param){
+            this.params.add(param);
+            return this;
+        }
+    }
+
+    public static XmlBuildContextBuiler builder(){
+        return new XmlBuildContextBuiler();
+    }
+
+
+    public String constructXML(){
+        return xmlBuilder
+                .initParam(param,configBocVO,callback)
+                .validate()
+                .head()
+                .trans()
+                .rootAttribute(attributeVersion,attributeLocale)
+                .xmlEncoding()
+                .build();
+    }
+
+}
Index: src/main/java/com/novaone/fdao/Mapper/PaymentBillMapper.xml
===================================================================
--- src/main/java/com/novaone/fdao/Mapper/PaymentBillMapper.xml	(不存在的)
+++ src/main/java/com/novaone/fdao/Mapper/PaymentBillMapper.xml	(版本 31412)
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.novaone.fdao.PaymentBillMapper">
+  <resultMap id="BaseResultMap" type="com.novaone.entity.PaymentBill">
+    <result column="sqdbh" property="sqdbh" />
+    <result column="yppbh" property="yppbh" />
+    <result column="yppbhValue" property="yppbhValue" />
+    <result column="sqrgsmc" property="sqrgsmc" />
+    <result column="sqrgsbm" property="sqrgsbm" />
+    <result column="zfyh" property="zfyh" />
+    <result column="zfyhzh" property="zfyhzh" />
+    <result column="zrmbje" property="zrmbje" />
+    <result column="sjskrkhyh" property="sjskrkhyh" />
+    <result column="sjskryhzh" property="sjskryhzh" />
+    <result column="sjskrmc" property="sjskrmc" />
+    <result column="fkfs" property="fkfs" />
+    <result column="wbbb" property="wbbb" />
+    <result column="zfsj" property="zfsj" />
+    <result column="sjzfrq" property="sjzfrq" />
+    <result column="zfbz" property="zfbz" />
+    <result column="shqrzf" property="shqrzf" />
+    <result column="yhcljj" property="yhcljj" />
+    <result column="zfzt" property="zfzt" />
+    <result column="zfsbyy" property="zfsbyy" />
+    <result column="zfhd" property="zfhd" />
+    <result column="receipt_name" property="receiptName" />
+    <result column="receipt_address" property="receiptAddress" />
+    <result column="receipt_sys_name" property="receiptSysName" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--@mbg.generated-->
+    sqdbh, yppbh, yppbhValue, sqrgsmc, sqrgsbm, zfyh, zfyhzh, zrmbje, sjskrkhyh,
+    sjskryhzh, sjskrmc, fkfs, wbbb, zfsj, sjzfrq, zfbz, shqrzf, yhcljj, zfzt,
+    zfsbyy, zfhd,zbrq,zbr
+  </sql>
+
+ <select id="selectUnpaid" resultMap="BaseResultMap" resultType="com.novaone.entity.PaymentBill">
+    SELECT
+    <include refid="Base_Column_List"></include>
+    FROM yw_hddz_fksqd
+    WHERE shqrzf = 1 AND ISNUll(zfzt,'未支付') = '未支付'
+ </select>
+
+
+ <insert id="insert" parameterType="com.novaone.entity.PaymentBill">
+    INSERT INTO yw_hddz_fksqd (sqdbh, yppbh, yppbhValue, sqrgsmc, sqrgsbm, zfyh, zfyhzh, zrmbje, sjskrkhyh,
+    sjskryhzh, sjskrmc, fkfs, wbbb, zfsj, sjzfrq, zfbz, shqrzf, yhcljj, zfzt,
+    zfsbyy, zfhd, zbrq,zbr)
+    values (#{sqdbh},#{yppbh},#{yppbhValue},#{sqrgsmc},#{sqrgsbm},#{zfyh},#{zfyhzh},#{zrmbje},#{sjskrkhyh},#{sjskryhzh},#{sjskrmc},
+    #{fkfs},#{wbbb},#{zfsj},#{sjzfrq},#{zfbz},#{shqrzf},#{yhcljj},#{zfzt},#{zfsbyy},#{zfhd},#{zbrq},#{zbr});
+</insert>
+
+
+
+  <update id="batchUpdateBySqdbh" parameterType="java.util.List">
+    <!--@mbg.generated-->
+    update yw_hddz_fksqd
+    <trim prefix="set" suffixOverrides=",">
+      <trim prefix="zfsj = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfsj != null">
+            when sqdbh = #{item.sqdbh} then #{item.zfsj}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="sjzfrq = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.sjzfrq != null">
+            when sqdbh = #{item.sqdbh} then #{item.sjzfrq}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfzt = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfzt != null and item.zfzt != ''">
+            when sqdbh = #{item.sqdbh,jdbcType=BIGINT} then #{item.zfzt}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfsbyy = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfsbyy != null and item.zfsbyy != ''">
+            when sqdbh = #{item.sqdbh} then #{item.zfsbyy,jdbcType=VARCHAR}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfhd = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfhd != null and item.zfhd != ''">
+            when sqdbh = #{item.sqdbh} then #{item.zfhd}
+          </if>
+        </foreach>
+      </trim>
+    </trim>
+    where sqdbh in
+    <foreach close=")" collection="list" item="item" open="(" separator=", ">
+      #{item.sqdbh}
+    </foreach>
+  </update>
+
+
+
+    <update id="batchUpdateByYppbhValue" parameterType="java.util.List">
+    <!--@mbg.generated-->
+    update yw_hddz_fksqd
+    <trim prefix="set" suffixOverrides=",">
+      <trim prefix="zfsj = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfsj != null">
+            when yppbhValue = #{item.yppbhValue} then #{item.zfsj}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="sjzfrq = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.sjzfrq != null">
+            when yppbhValue = #{item.yppbhValue} then #{item.sjzfrq}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfzt = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfzt != null and item.zfzt != ''">
+            when yppbhValue = #{item.yppbhValue} then #{item.zfzt}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfsbyy = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfsbyy != null and item.zfsbyy != ''">
+            when yppbhValue = #{item.yppbhValue} then #{item.zfsbyy,jdbcType=VARCHAR}
+          </if>
+        </foreach>
+      </trim>
+      <trim prefix="zfhd = case" suffix="end,">
+        <foreach collection="list" index="index" item="item">
+          <if test="item.zfhd != null and item.zfhd != ''">
+            when yppbhValue = #{item.yppbhValue} then #{item.zfhd}
+          </if>
+        </foreach>
+      </trim>
+    </trim>
+    where yppbhValue in
+    <foreach close=")" collection="list" item="item" open="(" separator=", ">
+      #{item.yppbhValue}
+    </foreach>
+  </update>
+
+  <select id="selectByYppbhValue" resultMap="BaseResultMap">
+    SELECT
+    <include refid="Base_Column_List"></include>
+    FROM yw_hddz_fksqd
+    WHERE yppbhValue = #{yppbhValue}
+  </select>
+
+  <select id="findBankBillBySqdbh" resultMap="BaseResultMap">
+    -- 根据申请单编号查询银行回单
+    SELECT yhf.sqdbh,bdr.receipt_name,bdr.receipt_address,bdr.receipt_sys_name
+    FROM yw_hddz_fksqd yhf JOIN jn_blending_relation jbr
+    ON yhf.sqdbh = jbr.apply_no
+    JOIN ba_bank_data_receipt_relation bbdrr
+    ON jbr.bd_id = bbdrr.bd_id JOIN ba_data_receipt bdr ON bdr.id = bbdrr.r_id WHERE yhf.sqdbh = #{sqdbh}
+ </select>
+
+</mapper>
\ No newline at end of file
Index: src/main/java/com/novaone/fdao/PaymentBillMapper.java
===================================================================
--- src/main/java/com/novaone/fdao/PaymentBillMapper.java	(不存在的)
+++ src/main/java/com/novaone/fdao/PaymentBillMapper.java	(版本 31412)
@@ -0,0 +1,30 @@
+package com.novaone.fdao;
+
+import com.novaone.entity.PaymentBill;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-06-01 13:44
+ */
+@Mapper
+public interface PaymentBillMapper {
+
+    public List<PaymentBill> selectUnpaid();
+
+    public int insert(PaymentBill bill);
+
+    public int batchUpdateBySqdbh(List<PaymentBill> bills);
+
+    public int batchUpdateByYppbhValue(List<PaymentBill> bills);
+
+    public List<PaymentBill> selectByYppbhValue(String yppbhValue);
+
+    public PaymentBill findBankBillBySqdbh(String sqdbh);
+
+}
Index: src/main/java/com/novaone/payment/B2eValidateUtil.java
===================================================================
--- src/main/java/com/novaone/payment/B2eValidateUtil.java	(不存在的)
+++ src/main/java/com/novaone/payment/B2eValidateUtil.java	(版本 31412)
@@ -0,0 +1,122 @@
+package com.novaone.payment;
+
+import com.novaone.util.B2e0043Type;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.util.ObjectUtils;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+/*
+ * @program: BankEnterprise
+ * @description: Bocb2e请求验参数校验工具类
+ * @author: Ma.ChengJian
+ * @create: 2022-02-10 10:28
+ */
+public class B2eValidateUtil {
+
+    /**
+     * 校验客户业务编号
+     */
+    public static boolean payNo(String payNo){
+        return StringUtils.isNotEmpty(payNo) && payNo.length() > 0&& payNo.length() < 13;
+    }
+
+    /**
+     * 电子回单下载时指定的客户业务编号
+     */
+    public static boolean billInsid(String billInsid){
+        return StringUtils.isNotEmpty(billInsid) && billInsid.length() > 0&& billInsid.length() < 33;
+    }
+    /**
+     * 校验付款金额
+     */
+    public static boolean money(String money){
+        return Pattern.compile("^[0-9]{1,13}(.[0-9]{1,2})?$").matcher(money).matches();
+    }
+
+    /**
+     * 校验货币类型
+     */
+    public static boolean payCurrency(String money){
+        return"001".equals(money) || "CNY".equals(money);
+    }
+
+    /**
+     * 校验银行处理优先级或者客户处理优先级参数。
+     * 必须为 0或1 当为null时，以0发起请求。
+     */
+    public static boolean priority(String priority){
+        return priority == null || priority.equals("0") || priority.equals("1");
+    }
+
+    /**
+     * 校验要求转账日期。
+     */
+    public static boolean scheduleDate(Date date){
+        if (ObjectUtils.isEmpty(date)) {
+            return true;
+        }
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        Date today = calendar.getTime();
+        calendar.add(Calendar.MONTH, 1);
+        Date nextMonth = calendar.getTime();
+        return date.getTime() - today.getTime() > -1 && nextMonth.getTime() - date.getTime() > -1;
+    }
+
+    /**
+     * 付款账号格式校验 非空 1-20位
+     * */
+    public static boolean payAcc(String payAcc){
+        return StringUtils.isNotEmpty(payAcc) && payAcc.length() > 0 && payAcc.length() < 21;
+    }
+
+    /**
+     * 收款账号校验 非空且长度1-35位
+     */
+    public static boolean payeeAcc(String payeeAcc){
+        return StringUtils.isNotEmpty(payeeAcc) && payeeAcc.length() > 0 && payeeAcc.length() < 36;
+    }
+
+    /**
+     * 校验收款人名称 非空且长度1-70位
+     */
+    public static boolean payeeName(String payeeName){
+        return StringUtils.isNotEmpty(payeeName) && payeeName.length() > 0 && payeeName.length() < 71;
+    }
+
+
+    /**
+     *  校验CNAPS、中行机构号文件下载(b2e0043)接口参数
+     */
+    public static boolean checkType(String type){
+        for (B2e0043Type value : B2e0043Type.values()) {
+            if (value.getType().equals(type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * 校验网银交易流水号
+     * 最长19位，且不超过9223372036854775807
+     */
+    public static boolean checkObssid(String obssid){
+        if(StringUtils.isEmpty(obssid) || Pattern.compile("^[0-9]{0,18}$").matcher(obssid).matches())return true;
+        if (Pattern.compile("^[0-9]{0,19}$").matcher(obssid).matches()){
+            try {
+                Long.parseLong(obssid);
+            } catch (NumberFormatException e) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+}
Index: src/main/java/com/novaone/payment/BOCClient.java
===================================================================
--- src/main/java/com/novaone/payment/BOCClient.java	(不存在的)
+++ src/main/java/com/novaone/payment/BOCClient.java	(版本 31412)
@@ -0,0 +1,69 @@
+package com.novaone.payment;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.common.exception.BocB2eException;
+import com.novaone.entity.BocB2eParam;
+
+import java.io.File;
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-01-18 21:19
+ */
+public interface BOCClient {
+
+    /**
+     * 支付
+     */
+    public JSONArray payment(List<BocB2eParam> params) throws BocB2eException;
+
+    /**
+     * 查询支付状态
+     */
+    public JSONArray queryPayState(List<BocB2eParam> param);
+
+    /**
+     * 签到
+     */
+    public JSONObject signIn();
+
+    /**
+     * 余额查询
+     */
+    public JSONArray balanceEnquiry(List<BocB2eParam> param);
+
+    /**
+     *  查询电子回单信息
+     */
+    public JSONArray queryBill(BocB2eParam param);
+
+    /**
+     * 查询电子回单的文件名称
+     */
+    public JSONArray queryBillZipName(BocB2eParam param);
+
+    /**
+     * 今日及历史交易记录查询
+     */
+    public JSONArray queryHistorical(BocB2eParam param);
+
+
+
+    /**
+     * 中行机构号文件下载
+     */
+    public void fileDownload(String type);
+
+    /**
+     *  CNAPS、中行机构号文件下载(b2e0043)
+     * @param type
+     */
+    void download(String type);
+
+
+}
Index: src/main/java/com/novaone/payment/BOCClientImpl.java
===================================================================
--- src/main/java/com/novaone/payment/BOCClientImpl.java	(不存在的)
+++ src/main/java/com/novaone/payment/BOCClientImpl.java	(版本 31412)
@@ -0,0 +1,442 @@
+package com.novaone.payment;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.jcraft.jsch.*;
+import com.novaone.common.exception.BocB2eException;
+import com.novaone.common.exception.BusinessException;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.VO.ConfigBocVO;
+import com.novaone.entity.bocb2e.*;
+import com.novaone.service.ConfigService;
+import com.novaone.util.HttpUtil;
+import jodd.io.ZipUtil;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.json.xml.XMLSerializer;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Expand;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.*;
+import java.math.BigDecimal;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-01-25 10:54
+ */
+@Slf4j
+@Component
+public class BOCClientImpl implements BOCClient {
+
+    @Resource
+    private ConfigService configService;
+
+    private final Map<String,Long> expiration = new Hashtable<>(1);
+
+    private String KEY = "lastTime";
+
+    public BOCClientImpl() {
+        expiration.put(KEY,0L);
+    }
+
+    /**
+     * 发起支付请求
+     */
+    @Override
+    public synchronized JSONArray payment(List<BocB2eParam> params) throws BocB2eException {
+        return new PayRequestContext(params,this.getConfigBocVO(),(v1,v2)->log.error("接口 {} {}个参数检验失败，无法发起交易请求。失败参数：{}",v2,v1.size(),JSON.toJSONString(v1.keySet())))
+         .request().getResponseArray();
+    }
+
+
+
+
+    /**
+     * 交易状态查询
+     * @param param 参数，需要包含发起转账付款时的客户业务编号或者付款成功时银行返回的网银交易流水号。
+     */
+    public synchronized JSONArray queryPayState(List<BocB2eParam> param) {
+        return requestBoc(new B2e0007XmlBuilder(), param, this.getConfigBocVO()).getResponseArray();
+    }
+
+
+
+
+    @Override
+    public JSONObject signIn() {
+        return null;
+    }
+
+
+
+
+    /**
+     * 查询指定中行账户的余额
+     */
+    @Override
+    public synchronized JSONArray balanceEnquiry(List<BocB2eParam> param) {
+        long current = 0;
+        final Long expirationTime = expiration.get(KEY);
+        while (expirationTime > (current = System.currentTimeMillis())) {
+            long sleepTime = expirationTime - current;
+            log.warn("余额查询接口访问频繁，等待{}毫秒后继续处理访问请求...",sleepTime);
+            try {
+                Thread.sleep(sleepTime);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        JSONArray resJson = requestBoc(new B2e0005XmlBuilder(), param, this.getConfigBocVO()).getResponseArray();
+        expiration.put(KEY,System.currentTimeMillis() + 5000);
+        return resJson;
+    }
+
+
+
+
+
+    /**
+     * 查询当前交易对应的回单的交易流水号。
+     * 如果当前页没有查询到回单对应的流水号则自动查找下一页，如果不能找到则返回null。
+     * 回单信息返回结果：
+     *      cusref字段有时候是单传的客户业务编号，有时候会含有其他东西。
+     *      feename（费用名称） = 转账汇款手续费 时，不包含cusref字段。
+     *      receiptname（回单名称） = 客户付费回单
+     * @param param 回单信息查询时必要的参数。必须保证已经完成付款
+     */
+    private String queryBillSerialNo(BocB2eParam param) {
+        List<JSONObject> bills = new ArrayList<>();
+        do {
+            JSONArray billResp = this.queryBill(param);
+            for (int a = 0; a < billResp.size(); a++) {
+                JSONObject bill = billResp.getJSONObject(a);
+                bills.add(bill);
+                //回单名称为客户付费回单时，说明改回但信息是转账汇款手续费回单。此时回单信息了不包含”cusref“字段。
+                if (bill.getString("receiptname").contains("客户付费回单")){
+                    log.info("查询到汇款手续费回单:{}",bill.toJSONString());
+                    continue;
+                }
+                if (bill.getString("cusref").contains(param.getPayNo())) {
+                    param.setBillSerialNo(bill.getString("receiptjrnlno").replaceAll("-",""));
+                    log.info("客户业务编号：{} 对应的回单流水号：{} 回单信息：{}",param.getPayNo(),bill.getString("receiptjrnlno").replaceAll("-",""),bill.toJSONString());
+                    return bill.getString("receiptjrnlno").replaceAll("-","");
+                }
+            }
+        }while (param.isBillHasNext());
+        param.setBillBeginNum(1);
+        log.info("没有找到对应业务编号{}的回单信息 查询到的回单数量:{} 全部回单:{}",param.getPayNo(),bills.size(), JSON.toJSONString(bills));
+        return null;
+    }
+
+
+
+
+    @Override
+    public synchronized JSONArray queryBill(BocB2eParam param) {
+        JSONArray transInfo = new JSONArray();
+        JSONObject responseJson = requestBoc(new B2ee0531XmlBuilder(), Collections.singletonList(param), this.getConfigBocVO()).getResponseJson();
+        JSONObject resData = responseJson.getJSONObject("b2e0531-rs");
+        log.info("回单信息查询结果:{}",responseJson.toJSONString());
+        final JSONObject status = resData.getJSONObject("status");
+        //B003=交易数为0
+        if (BocStatus.EMPTY_RESULT.code().equals(status.getString("rspcod"))){
+            throw new BusinessException("没有找到交易" + param.getPayNo() + "对应的回单信息，接口响应状态:" + resData.getJSONObject("status").toJSONString(),BusinessException.Type.NO_SUCH_BILL_INFO);
+        }
+        else if (!(BocStatus.OK.code().equals(status.getString("rspcod")) || "B002".equals(status.getString("rspcod")))){
+            throw new BusinessException("回电信息查询接口响应错误 respStatus:" + resData.getJSONObject("status").toJSONString(),BusinessException.Type.UNKNOWN_RESPONSE);
+        }
+        Integer backnum = responseJson.getInteger("backnum");//返回条目数
+        if (backnum > 1) {
+            transInfo.addAll(resData.getJSONArray("transinfo"));
+        }else {
+            transInfo.add(resData.getJSONObject("transinfo"));
+        }
+        if ((param.getBillBeginNum() + backnum) <= responseJson.getInteger("totalnum")){
+            param.setBillHasNext(true);
+            param.setBillBeginNum(param.getBillBeginNum() + backnum);
+        }else {
+            param.setBillHasNext(false);
+            param.setBillBeginNum(1);
+        }
+        return transInfo;
+    }
+
+
+
+
+    /**
+     * 每次查询时会将符合查询条件的回单文件打包为压缩文件并将文件名称返回。
+     * 在FTP服务器根据本次查询的文件名称下载时，压缩包内包含符合本次查询条件的全部回单文件。
+     *
+     */
+    @Override
+    public JSONArray queryBillZipName(BocB2eParam param) {
+        JSONArray resJson = requestBoc(new B2e0500XmlBuilder(), Collections.singletonList(param), this.getConfigBocVO()).getResponseArray();
+        log.info("逐笔电子回单下载接口业务处理结果:{}",resJson.toString());
+        return resJson;
+    }
+
+
+
+    @Override
+    public synchronized JSONArray queryHistorical(BocB2eParam param) {
+        JSONObject respJson = requestBoc(new B2e0035XmlBuilder(), Collections.singletonList(param), this.getConfigBocVO()).getResponseJson();
+        if (BocStatus.EMPTY_RESULT.code().equals(respJson.getJSONObject("status").getString("rspcod"))) {
+            return new JSONArray(0);
+        }
+        Integer currentTotal= respJson.getInteger("totalnum");
+        int beginNum = param.getHistoricalBeginNum();
+        if ((beginNum + currentTotal) <= respJson.getInteger("notenum")){
+            param.setHistoricalHasNext(true);
+            param.setHistoricalBeginNum(beginNum + currentTotal);
+        }else {
+            param.setHistoricalHasNext(false);
+            param.setHistoricalBeginNum(1);
+        }
+        if (currentTotal < 2){
+            return new JSONArray(Collections.singletonList(respJson.getJSONObject("b2e0035-rs")));
+        }
+        return respJson.getJSONArray("b2e0035-rs");
+    }
+
+
+    /**
+     * 中行机构号文件下载。
+     */
+    public void fileDownload(String type){
+        final BocB2eParam param = new BocB2eParam();
+        param.setTasktpy(type);
+        JSONArray respJson = this.requestBoc(new B2e0043XmlBuilder(), Collections.singletonList(param), this.getConfigBocVO()).getResponseArray();
+        log.info(respJson.toString());
+    }
+
+
+
+    /**
+     * 调用B2e0043接口，CNAPS、中行机构号文件下载
+     * @param type 下载的文件类型
+     *            0：CNAPS文件
+     *            1：中行机构号文件
+     *            2：电子汇票行号文件
+     */
+    @Override
+    @SneakyThrows
+    public void download(String type) {
+        ConfigBocVO configBocVO = this.getConfigBocVO();
+        String xml = b2e0043XMLBuild(type,configBocVO);
+        String url = configBocVO.getProtocol() + "://" + configBocVO.getIp() + ":" + configBocVO.getPort() + "/B2EC/E2BServlet";
+        log.info("b2e0043请求URL:{}  XML:{}",url,xml);
+        String res = HttpUtil.postXml(url, xml, "UTF-8");
+        log.info("b2e0043接口响应：{}",res);
+        JSONObject jsonObject = JSON.parseObject(new XMLSerializer().read(res).toString());
+        log.info("b2e0043 xml转JSON：{}",jsonObject.toString());
+        JSONObject resTrans = jsonObject.getJSONObject("trans");
+        JSONObject b2eerr = resTrans.getJSONObject("trn-b2eerror-rs");
+        if (!b2eerr.isEmpty()) {
+            log.error("b2e0043 接口调用失败：{}",b2eerr.toString());
+            throw new RuntimeException("b2e0043接口请求失败:" + b2eerr.toString());
+        }
+        //获取b2e0043接口响应报文中的压缩文件数据。
+        String data = resTrans.getJSONObject("trn-b2e0043-rs").getString("data");
+        download(data,type); //解压并下载文件
+    }
+
+
+    private void download(String data, String type){
+        log.info("b2e0043 文件类型：{}  文件数据：{}",type,data);
+        //Base64解码压缩文件数据
+        byte[] getData = data.getBytes();
+        byte[] bData = Base64.decodeBase64(getData);
+        String fileName = type + "_" + System.currentTimeMillis();
+        //将压缩文件存储
+        File zipFile = new File("/mnt/BankEnterprise/download/department/" + fileName + ".zip");
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = new FileOutputStream(zipFile);
+            outputStream.write(bData);
+            outputStream.flush();
+        } catch (IOException e) {
+            log.error("b2e0043 压缩文件本地存储异常",e);
+            throw new RuntimeException("压缩文件存储失败",e);
+        } finally {
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        //将压缩文件解压到相同的目录下，解压成功则删除压缩文件
+        try {
+            File file = new File("/mnt/BankEnterprise/download/department/" + fileName + ".csv");
+            ZipUtil.unzip(zipFile,file);
+            zipFile.delete();
+            log.info("b2e0043 文件下载成功");
+        } catch (IOException e) {
+            log.error("b2e0043 文件解压异常,文件路径{}",zipFile.getAbsolutePath(),e);
+            throw new RuntimeException("文件解压失败",e);
+        }
+    }
+
+
+
+
+    private String b2e0043XMLBuild(String type,ConfigBocVO config){
+        Document document = DocumentHelper.createDocument();
+        document.setXMLEncoding("UTF-8");
+        Element bocb2e = document.addElement("bocb2e");
+        //设置根节点属性
+        bocb2e.addAttribute("version","120");
+        bocb2e.addAttribute("locale","zh_CN");
+//        bocb2e.addAttribute("security","true");
+//        bocb2e.addAttribute("lang","chs");
+        //头部
+        Element head = bocb2e.addElement("head");
+        head.addElement("termid").setText(config.getTermid());
+        head.addElement("trnid").setText(Optional.ofNullable(config.getTrnid()).orElse(""));
+        head.addElement("custid").setText(config.getCustid());
+        head.addElement("cusopr").setText(config.getUser());
+        head.addElement("trncod").setText("b2e0043");
+        head.addElement("token").setText(Optional.ofNullable(System.getProperty("bankSignToken")).orElse(""));
+        //请求数据
+        Element trnB2e0043Rq = bocb2e.addElement("trans").addElement("trn-b2e0043-rq");
+        Element tasktpy = trnB2e0043Rq.addElement("b2e0043-rq").addElement("tasktpy");
+        tasktpy.addText(type);
+        return document.asXML();
+    }
+
+
+
+
+    /**
+     * 请求中行接口
+     */
+    private BocRequestContext requestBoc(Bocb2eXMLBuilder builder,List<BocB2eParam> params,ConfigBocVO configBocVO){
+        XmlBuildContext buildContext = XmlBuildContext.builder()
+                .xmlBuilder(builder)
+                .configBocVO(configBocVO)
+                .param(params)
+                .build();
+        return new BocRequestContext(buildContext).request();
+    }
+
+
+
+    private ConfigBocVO getConfigBocVO(){
+        //正式环境信息
+        return configService.queryConfigOne();
+        //测试环境信息
+//        return configService.queryConfigTest();
+    }
+
+
+
+
+    /**
+     * 从银行FTP服务器下载回单压缩文件。
+     * 当调用”逐笔电子回单下载(b2e0500)“接口查询到回单压缩文件后，可能不能立刻从FT品种下载到压缩文件，这种情况需要等待。
+     */
+    private File downLoadZip(String fileName,int retry) throws SftpException {
+        Session sshSession = null;
+        ChannelSftp channelSftp = null;
+        InputStream inputStream = null;
+        File billeFile = null;
+        BufferedOutputStream buf = null;
+        try {
+            JSch jsch = new JSch();
+            sshSession = jsch.getSession("293517309", "101.231.206.140", 21121);
+            sshSession.setPassword("Boc@1234");
+            Properties config = new Properties();
+            config.put("StrictHostKeyChecking", "no");
+            sshSession.setConfig(config);// 为Session对象设置properties
+            sshSession.connect();//// 通过Session建立连接
+            //下载文件
+            channelSftp = (ChannelSftp) sshSession.openChannel("sftp");
+            // 远程连接
+            channelSftp.connect();
+            inputStream = channelSftp.get(fileName);
+            billeFile = new File("F:\\" + fileName);
+            buf = new BufferedOutputStream(new FileOutputStream(billeFile));
+            byte[] bytes = new byte[1024*8];
+            int len = -1;
+            while ((len = inputStream.read(bytes)) != -1) {
+                buf.write(bytes,0,len);
+                buf.flush();
+            }
+            log.info("回单下载成功，文件路径:{}",billeFile.getAbsolutePath());
+        } catch (JSchException | IOException e) {
+            e.printStackTrace();
+        } catch (SftpException e) {//按照压缩文件名找不到文件时抛出此异常。
+            log.error("找不到文件：{} 休眠5秒，重试{}次",retry,fileName,e);
+            if (retry < 1)throw e;
+            sleep(5);
+            return downLoadZip(fileName, --retry);
+        } finally {
+            if (buf != null) {
+                try {
+                    buf.close();
+                } catch (IOException e){}
+            }
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {}
+            }
+            channelSftp.disconnect();
+            sshSession.disconnect();
+        }
+        return billeFile;
+    }
+
+
+    public List<File> unzip(File zipFile, String destDir) throws Exception{
+        log.info("开始解压缩文件:"+zipFile.getName()+",解压目录:" + destDir);
+        File des = new File(destDir);
+        try {
+            Project proj = new Project();
+            Expand expand = new Expand();
+            expand.setProject(proj);
+            expand.setTaskType("unzip");
+            expand.setTaskName("unzip");
+            expand.setEncoding("UTF-8");
+            expand.setSrc(zipFile);
+            expand.setDest(des);
+            expand.execute();
+        }catch (Exception e) {
+            throw e;
+        }
+        File[] files = des.listFiles();
+        if(files.length>0) {
+            List<File> fileList = Arrays.asList(files);
+            return fileList;
+        }
+        return null;
+    }
+
+    private void sleep(int sec){
+        try {
+            TimeUnit.SECONDS.sleep(sec);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+}
Index: src/main/java/com/novaone/payment/BocStatus.java
===================================================================
--- src/main/java/com/novaone/payment/BocStatus.java	(不存在的)
+++ src/main/java/com/novaone/payment/BocStatus.java	(版本 31412)
@@ -0,0 +1,66 @@
+package com.novaone.payment;
+
+import com.thoughtworks.selenium.webdriven.commands.Submit;
+
+import java.util.Objects;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-06-07 13:42
+ */
+public enum BocStatus {
+
+    /**
+     * 成功
+     */
+    OK("B001"),
+    /**
+     * 未完
+     */
+    UNFINISHED("B002"),
+    /**
+     * 回单信息空的查询结果
+     */
+    EMPTY_RESULT("B003"),
+    /**
+     * 重复提交了相同的交易指令
+     */
+    EXIST("B051"),
+    /**
+     * 交易不存在或银行未处理
+     */
+    NONE("B052"),
+    /**
+     * 交易失败被银行退回。
+     */
+    FAILED_BANK("B053"),
+    /**
+     * 交易处理中，等待一样处理
+     */
+    WAIT_BANK("B054"),
+    /**
+     * 交易提交成功之银行成功
+     */
+    SUBMIT("B059"),
+    /**
+     * 支付金额超出转账笔限额(或市场细分笔限额)
+     */
+    QUOTA_EXCEEDED("B094"),
+    /**
+     * 账号未在网银系统维护
+     */
+    UNKNOWN_ACCOUNT("B098");
+
+
+    private final String code;
+
+    BocStatus(String code) {
+        this.code = code;
+    }
+
+    public String code() {
+        return this.code;
+    }
+}
Index: src/main/java/com/novaone/payment/CodeGenerate.java
===================================================================
--- src/main/java/com/novaone/payment/CodeGenerate.java	(不存在的)
+++ src/main/java/com/novaone/payment/CodeGenerate.java	(版本 31412)
@@ -0,0 +1,38 @@
+package com.novaone.payment;
+
+import com.novaone.dao.TradingRecordCodeDao;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-05-16 11:20
+ */
+@Component
+public class CodeGenerate {
+
+    @Resource
+    private TradingRecordCodeDao tradingRecordCodeDao;
+
+    public String generate(){
+        final TradingRecordCodeDao.Code code = new TradingRecordCodeDao.Code();
+        code.setPrefix(new SimpleDateFormat("yyyyMMdd").format(new Date()));
+        try {
+            tradingRecordCodeDao.insert(code);
+        } catch (DataIntegrityViolationException e) {
+            if (e.getCause().getMessage().contains("Data too long for column 'max_serial' at row 1")) {
+                throw new RuntimeException("序列号达到最大值无法继续生成！");
+            }
+            throw  e;
+        }
+        return code.getPrefix() + code.getMaxSerial();
+    }
+}
Index: src/main/java/com/novaone/payment/JschUtil.java
===================================================================
--- src/main/java/com/novaone/payment/JschUtil.java	(不存在的)
+++ src/main/java/com/novaone/payment/JschUtil.java	(版本 31412)
@@ -0,0 +1,178 @@
+package com.novaone.payment;
+
+import com.jcraft.jsch.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import java.io.ByteArrayInputStream;
+import java.util.Properties;
+
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-06-09 23:54
+ */
+@Component
+public class JschUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(JschUtil.class);
+
+    ChannelSftp chSftp;
+
+    @Value("${bankbillftp.host}")
+    private String host;
+
+    @Value("${bankbillftp.port}")
+    private int port;
+
+    @Value("${bankbillftp.username}")
+    private String username;
+
+    @Value("${bankbillftp.password}")
+    private String password;
+
+
+    public JschUtil() {
+    }
+
+
+    private Session getSession() throws JSchException {
+        JSch jSch = new JSch();
+        Session session  = jSch.getSession(username, host, port);
+        session.setPassword(password);
+        Properties config = new Properties();
+        config.put("StrictHostKeyChecking", "no");
+        session.setConfig(config);
+        session.setTimeout(3000);
+        session.connect();
+        return session;
+    }
+
+
+    /**
+     * 关闭连接
+     */
+    public void close(Session session, ChannelSftp chSftp) {
+        if (session != null && session.isConnected()) {
+            session.disconnect();
+        }
+        if (chSftp != null && chSftp.isConnected()) {
+            chSftp.quit();
+        }
+    }
+
+
+
+    /**
+     * 文件上传至主机
+     * @param directory 当前文件路径
+     * @param uploadFile 上传至主机的路径
+     */
+    public void upload(String directory, String uploadFile) {
+        Session session = null;
+        try {
+            session = getSession();
+            chSftp = (ChannelSftp)session.openChannel("sftp");
+            chSftp.connect();
+            chSftp.setFilenameEncoding("UTF-8");
+            chSftp.put(directory, uploadFile);
+            logger.info("文件上传成功");
+        } catch (JSchException | SftpException e) {
+            logger.warn(e.getMessage());
+        } finally {
+            close(session,chSftp);
+        }
+    }
+
+
+
+
+    public void upload(byte[] bytes, String uploadFile) throws JSchException, SftpException {
+        Session session = null;
+        try {
+            session = getSession();
+            chSftp = (ChannelSftp)session.openChannel("sftp");
+            chSftp.connect();
+            chSftp.setFilenameEncoding("UTF-8");
+            chSftp.put(new ByteArrayInputStream(bytes), uploadFile);
+        } finally {
+            close(session,chSftp);
+        }
+    }
+
+
+
+
+    /**
+     * 将主机文件下载至本地
+     * @param directory  下载到本地的位置
+     * @param downloadFile  下载文件在虚拟机的位置
+     */
+    public void download(String directory, String downloadFile) {
+        Session session = null;
+        try {
+            session = getSession();
+            chSftp = (ChannelSftp)session.openChannel("sftp");
+            chSftp.connect();
+            chSftp.setFilenameEncoding("UTF-8");
+            chSftp.get(directory, downloadFile);
+        } catch (JSchException | SftpException e) {
+            logger.warn(e.getMessage());
+        }  finally {
+            close(session,chSftp);
+        }
+    }
+
+
+    public void delete(String directory) {
+        Session session = null;
+        try {
+            session = getSession();
+            chSftp = (ChannelSftp)session.openChannel("sftp");
+            chSftp.connect();
+            chSftp.setFilenameEncoding("UTF-8");
+            chSftp.rm(directory);
+            logger.info("文件{}删除成功",directory);
+        } catch (JSchException | SftpException e) {
+            throw new RuntimeException("文件" + directory + "删除失败",e);
+        }  finally {
+            close(session,chSftp);
+        }
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}
+
Index: src/main/java/com/novaone/payment/MessageSender.java
===================================================================
--- src/main/java/com/novaone/payment/MessageSender.java	(不存在的)
+++ src/main/java/com/novaone/payment/MessageSender.java	(版本 31412)
@@ -0,0 +1,125 @@
+package com.novaone.payment;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.TradingRecord;
+import org.springframework.amqp.core.MessageDeliveryMode;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+import java.util.Objects;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-30 13:13
+ */
+@Component
+public class MessageSender {
+
+    @Resource
+    private RabbitTemplate template;
+    @Resource
+    private RestTemplate restTemplate;
+
+    private final String auth;
+
+    private final String virtual;
+
+    public MessageSender(RabbitProperties rabbit) {
+        this.auth = Base64.getEncoder().encodeToString((rabbit.getUsername() + ":" + rabbit.getPassword()).getBytes(StandardCharsets.UTF_8));
+        this.virtual = rabbit.getVirtualHost();
+    }
+
+
+
+    /**
+     * 发送申请单预处理完成消息。
+     */
+    public void sendPayment(JSONObject json){
+        template.convertAndSend(RabbitConfig.EX_PAYMENT,RabbitConfig.KEY_PAYMENT, json.toString() ,message -> {
+            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
+            message.getMessageProperties().setContentType("application/json");
+            return message;
+        });
+    }
+
+
+
+
+
+    /**
+     * 发送消息到带查询支付状态队列
+     * @param param 完成付款操作的业务参数
+     */
+    public void sendPaymentStateQueue(TradingRecord param){
+        sendPaymentStateQueue(param, 30);
+    }
+
+    public void sendPaymentStateQueue(TradingRecord param, int sec){
+        if (Objects.isNull(param.getMateData())) {
+            throw new IllegalArgumentException("TradingRecord MateData 不得为空");
+        }
+        template.convertAndSend(RabbitConfig.EX_PAYMENT,RabbitConfig.KEY_STATUS, JSON.toJSONString(param),message -> {
+            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
+                message.getMessageProperties().setContentType("application/json");
+                message.getMessageProperties().setDelay(sec * 1000);
+            return message;
+        });
+    }
+
+
+    /**
+     * 发送消息到回单信息查询队列
+     * @param param 已经完成支付支付参数
+     */
+    public void sendBillInfoQueue(TradingRecord param)
+    {
+        sendBillInfoQueue(param, 30);
+    }
+
+    public void sendBillInfoQueue(TradingRecord param, int sec){
+        if (Objects.isNull(param.getMateData())) {
+            throw new IllegalArgumentException("TradingRecord MateData 不得为空");
+        }
+        template.convertAndSend(RabbitConfig.EX_PAYMENT,RabbitConfig.KEY_INFO, JSON.toJSONString(param),message -> {
+            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
+            message.getMessageProperties().setContentType("application/json");
+            message.getMessageProperties().setDelay(sec * 1000);
+            return message;
+        });
+    }
+
+
+    public void purge(String queue){
+        purge(virtual, queue);
+    }
+
+
+    public void purge(String host, String queue){
+        final String url = "http://" + template.getConnectionFactory().getHost() + ":15672" + "/api/queues/" + host + "/" + queue + "/contents";
+        final HttpHeaders headers = new HttpHeaders();
+        headers.add("Authorization","Basic " + auth);
+        final ResponseEntity<byte[]> exchange = restTemplate.exchange(url, HttpMethod.DELETE,  new HttpEntity(null, headers), byte[].class);
+        if (!exchange.getStatusCode().is2xxSuccessful()) {
+            if (exchange.getStatusCode().value() == 404){
+                throw new RuntimeException(queue + " Not Found");
+            }else {
+                throw new RuntimeException(exchange.getStatusCode().value() + " " + exchange.getStatusCode().getReasonPhrase());
+            }
+        }
+    }
+}
Index: src/main/java/com/novaone/payment/PayTimer.java
===================================================================
--- src/main/java/com/novaone/payment/PayTimer.java	(不存在的)
+++ src/main/java/com/novaone/payment/PayTimer.java	(版本 31412)
@@ -0,0 +1,125 @@
+package com.novaone.payment;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.common.exception.BusinessException;
+import com.novaone.dao.TradingRecordMapper;
+import com.novaone.entity.*;
+import com.novaone.service.PayService;
+import com.novaone.service.PaymentBillService;
+import com.novaone.service.TradingRecordService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.time.DateUtils;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-04-18 15:12
+ */
+@Slf4j
+@Component
+public class PayTimer {
+
+    @Resource
+    private PaymentBillService paymentBillService;
+    @Resource
+    private PayService payService;
+    @Resource
+    private TradingRecordMapper recordMapper;
+    @Resource
+    private MessageSender sender;
+    @Resource
+    private TradingRecordService recordService;
+
+
+
+    /**
+     * 每隔五分钟处理一次未支付申请单。
+     */
+    @Scheduled(cron = "0 0/5 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 * * ? ")
+    public void preprocess(){
+        try {
+            //查询yw_hddz_fksqd表中待支付的申请单
+            List<PaymentBill> paymentBills = paymentBillService.selectUnpaid();
+            log.info("本次查询到水果通系统待支付付款申请单{}笔,列表:{}",paymentBills.size(), JSON.toJSONString(paymentBills));
+            final long frist = System.currentTimeMillis();
+            int tradingRecordNum = payService.preprocess(paymentBills);
+            log.info("处理{}笔申请单耗时{}秒",paymentBills.size(),(System.currentTimeMillis() - frist) / 1000);
+            //如果水果通没有校验通过的待支付申请单，则查看MySQL中是否有未支付的数据
+            if (tradingRecordNum == 0) {
+                tradingRecordNum = recordService.countUnpaid();
+                log.info("本次查询到MySQL中待处理的未支付交易数据{}笔",tradingRecordNum);
+            }
+            //当有预处理成功的申请单时，通知汇款交易发起节点处理。
+            if (tradingRecordNum > 0){
+                final JSONObject message = new JSONObject();
+                message.put("id",UUID.randomUUID().toString());
+                message.put("tradingRecordNum",tradingRecordNum);
+                sender.sendPayment(message);
+                log.info("申请单预处理完成消息发送成功:{}",JSON.toJSONString(message));
+            }
+        } catch (BusinessException e){
+            log.error("申请单处理异常了",e);
+            final TradingLog tradingLog = new TradingLog();
+            tradingLog.setCreatTime(new Date());
+            tradingLog.setApiNo("申请单预处理");
+            tradingLog.setMesage(substring(e.getMessage()));
+            if (e.getType().value() == BusinessException.Type.SQLSERVER_ERR.value()) {
+                tradingLog.setState(TradingLog.State.SQLSERVER_ERR);
+            }
+            tradingLog.setState(TradingLog.State.UNKNOWN_ERR);
+            recordService.logInsert(tradingLog);
+        } catch (Exception e) {
+            log.error("申请单预处理过程中出现了异常",e);
+            final TradingLog tradingLog = new TradingLog();
+            tradingLog.setCreatTime(new Date());
+            tradingLog.setApiNo("申请单预处理");
+            tradingLog.setMesage(substring(e.getMessage()));
+            tradingLog.setState(TradingLog.State.UNKNOWN_ERR);
+            recordService.logInsert(tradingLog);
+        }
+    }
+
+
+    /**
+     * 查询历史记录
+     */
+    @Scheduled(cron = "0 0 5 ? * *")
+    public void queryHistorical(){
+        final Date date = DateUtils.addDays(new Date(), -1);
+        //查询昨日完成付款成功的交易记录
+        final List<TradingRecord> yesterdayRecord = recordService.selectByScheduleDate(date);
+        final Set<String> payAcc = yesterdayRecord.stream().map(item -> item.getPayAcc()).collect(Collectors.toSet());
+        log.info("查询到{}完成的交易记录{}条，列表{}",new SimpleDateFormat("yyyy-MM-dd").format(date),yesterdayRecord.size(),payAcc);
+        //处理指定付款账号的交易历史记录
+        payService.captureHistorical(payAcc, date);
+    }
+
+
+    /**
+     * 清理RPA生成的系统回单
+     */
+    @Scheduled(cron = "0 0 6 ? * 2")
+    public void clearBillInfoPdf(){
+        //查询成功生成并上传系统回单的交易记录
+        final List<TradingRecord> recordList = recordMapper.selectByBillState(Arrays.asList(TradingBillRecord.State.SUCCESS));
+        for (TradingRecord record : recordList) {
+            payService.clearBillInfoPdf(record);
+        }
+    }
+
+    private String substring(String src){
+        if (src.length() <= 1000) {
+            return src;
+        }
+        return src.substring(0,1001);
+    }
+}
Index: src/main/java/com/novaone/payment/RabbitClient.java
===================================================================
--- src/main/java/com/novaone/payment/RabbitClient.java	(不存在的)
+++ src/main/java/com/novaone/payment/RabbitClient.java	(版本 31412)
@@ -0,0 +1,212 @@
+package com.novaone.payment;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-18 23:54
+ */
+import com.alibaba.fastjson.JSON;
+import com.novaone.common.exception.BusinessException;
+import com.novaone.dao.TradingLogMapper;
+import com.novaone.entity.*;
+import com.novaone.service.PayService;
+import com.novaone.service.TradingRecordService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.messaging.handler.annotation.Payload;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component
+public class RabbitClient {
+
+    @Resource
+    private MessageSender sender;
+    @Resource
+    private TradingRecordService recordService;
+    @Resource
+    private PayService payService;
+    @Resource
+    private TradingLogMapper logMapper;
+
+    private final Map<Integer,Integer> delay = new HashMap(6);
+
+    public RabbitClient() {
+        delay.put(2, 60);
+        delay.put(3, 60 * 2);
+        delay.put(4, 60 * 3);
+        delay.put(5, 60 * 4);
+        delay.put(6, 60 * 10);
+        delay.put(7, 60 * 10);
+    }
+
+
+    /**
+     * 待付款账单消费队列
+     * 要是使用@Payload注解实现将消息内容转换为Java对象，必须保证消息的contentType类型为application/json,否则装换将会失败抛出异常。
+     */
+    @RabbitListener(queues = RabbitConfig.QU_PAYMENT)
+    public void payment(@Payload Map<String,String> body) {
+        log.info("[汇款交易发起节点]收到申请单预处理完成消息:{}",body);
+        try {
+            final long first = System.currentTimeMillis();
+            //1. 查询全部待支付申请单。并将查询结果现改为支付中在进行后续支付及付款校验操作。
+            final List<TradingRecord> tradingRecords = recordService.selectUnpaid();
+            log.info("本次支付交易数量:{} 列表:{}",tradingRecords.size(),JSON.toJSONString(tradingRecords));
+            if (tradingRecords.isEmpty()) return;
+            //2. 账户余额校验处理，返回可以支付的交易。如果一个账号余额不足以支付本次的全部交易，则放弃支付该账号对应的全部交易。
+            final Set<TradingRecord> records = payService.doBalance(tradingRecords);
+            log.info("余额校验通过{}笔,payNo list:{}",records.size(),records.stream().map(TradingRecord::getPayNo).collect(Collectors.toSet()));
+            //3. 付款操作，逐笔发起付款交易。
+            for (TradingRecord record : records) {
+                log.info("开始处理交易{}的付款操作,pay_bill_no:{}",record.getPayNo(),record.getPayBillNo());
+                //交易发起成功。当返回true时标识交易已经发起成功，投递一条状态查询消息。
+                if (payService.payment(record)) {
+                    //4. 发送付款请求成功消息
+                    record.getMateData().reset();
+                    sender.sendPaymentStateQueue(record);
+                    log.info("[汇款交易发起节点]交易{}发起成功,发送消息到支付状态查询队列成功",record.getPayBillNo());
+                }
+            }
+            log.info("[汇款交易发起节点]ID:{} {}笔交易处理完成 耗时:{}秒",body.get("id"),records.size(),(System.currentTimeMillis() - first) / 1000);
+        }catch (BusinessException e) {
+            doBusinessEx(e.getBusinessDate(), e, "交易发起");
+        }catch (Exception e) {
+            log.error("交易发起未知异常了", e);
+            final TradingLog tradingLog = new TradingLog();
+            tradingLog.setCreatTime(new Date());
+            unknownErr(tradingLog,e,"申请单预处理");
+        }
+    }
+
+
+
+
+
+
+    /**
+     * 查询支付状态
+     */
+    @RabbitListener(queues = RabbitConfig.QU_PAYMENT_STATUS)
+    public void queryStatus(@Payload TradingRecord body) {
+        try {
+            log.info("[交易状态查询]处理交易:{} 消息首次投递时间{} count:{}  body:{}",body.getPayNo(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(body.getMateData().getFirstTime()),body.getMateData().getCount(),JSON.toJSONString(body));
+            if (payService.doPayState(body)) {
+                //在途状态重新发回状态查询队列。
+                body.getMateData().increment();
+                sender.sendPaymentStateQueue(body);
+                log.info("交易{}重新投递至队列,等待继续查询处理状态...",body.getPayNo());
+            }
+        }catch (BusinessException e){
+            doBusinessEx(body,e,"交易状态查询");
+        } catch (Exception e){
+            log.error("状态查询结束，发生了未知异常",e);
+            unknownErr(TradingLog.getInstanceByTradingRecord(body), e,"交易状态查询");
+        }
+    }
+
+
+
+
+    /**
+     * 交易电子回单处理
+     */
+    @RabbitListener(queues = RabbitConfig.QU_BILL_INFO)
+    public void report(@Payload TradingRecord body){
+        log.info("[交易回单生成]处理交易:{} 消息首次投递时间{} count:{} body:{}", body.getPayNo(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(body.getMateData().getFirstTime()),body.getMateData().getCount(),JSON.toJSONString(body));
+        try {
+            payService.doBankBill(body);
+        } catch (BusinessException e) {
+            doBusinessEx(body, e,"回单处理");
+        } catch (Exception e){
+            log.error("回单处理时发生了未知异常",e);
+            unknownErr(TradingLog.getInstanceByTradingRecord(body), e,"回单处理");
+        }
+    }
+
+
+
+
+    private void resendMsgToBillInfoQueue(TradingRecord body) {
+        final int count = body.getMateData().increment();
+        Integer sec = null;
+        //如果没有超过最大投递次数，则消息重回队列。
+        if ((sec = delay.get(count)) != null) {
+            sender.sendBillInfoQueue(body,sec);
+            log.info("message resend to bill_info_queue payNo:{} count:{} delaySec:{}",body.getPayNo(), count, sec);
+            return;
+        }
+        //三十分钟内没有查询到回单信息
+        log.info("结束对于交易{}的回单处理,已达最大重试次数",body.getPayNo());
+        final TradingBillRecord billRecord = TradingBillRecord.getInstance(body);
+        billRecord.setUpdateTime(new Date());
+        billRecord.setState(TradingBillRecord.State.NO_SEARCH_BILL_INFO);
+        billRecord.setMessage("30分钟内未能成功生成回单文件");
+        recordService.updateBillRecord(billRecord);
+    }
+
+
+
+    private void doBusinessEx(TradingRecord record,BusinessException e,String taskName) {
+        TradingLog tradingLog = null;
+        if (Objects.isNull(record)){
+            log.info("doBusinessEx TradingRecord is null");
+            tradingLog = new TradingLog();
+            tradingLog.setCreatTime(new Date());
+        }else {
+            tradingLog = TradingLog.getInstanceByTradingRecord(record);
+        }
+        tradingLog.setApiNo(taskName);
+        tradingLog.setMesage(e.getMessage());
+        switch (e.getType()){
+            case SQLSERVER_ERR:
+                log.error("[{}]与SQL Server 交互异常",taskName,e);
+                tradingLog.setState(TradingLog.State.SQLSERVER_ERR);
+                break;
+            case API_CALL_FAIL:
+                log.error("[{}]银行接口调用过程时发生了异常",taskName,e);
+                tradingLog.setState(TradingLog.State.API_CALL_FALL);
+                break;
+            case BANK_BILL_FAIL:
+                log.error("[{}]回单处理上传时发生了异常",taskName,e);
+                tradingLog.setState(TradingLog.State.FAILED_BILL);
+                break;
+            case NO_SUCH_BILL_INFO:
+                log.error("[{}]没有查询到回单信息 {}",taskName,e.getMessage());
+                tradingLog.setState(TradingLog.State.NO_SEARCH_BILL_INFO);
+                //消息重回队列，达到最大重试次数则将回单状态置为失败
+                resendMsgToBillInfoQueue(record);
+                break;
+            case UNKNOWN_TEMPLATE:
+                log.error("[{}]模板不存在",taskName,e);
+                tradingLog.setState(TradingLog.State.FAILED_BILL);
+                break;
+            case UNKNOWN_RESPONSE:
+                log.error("[{}]银行返回了未知的响应状态码",taskName,e);
+                tradingLog.setState(TradingLog.State.UNKNOWN_CODE);
+                break;
+            default:
+                log.error("未知的异常类型",e);
+                return;
+        }
+        recordService.logInsert(tradingLog);
+    }
+
+
+    private void unknownErr(TradingLog tradingLog, Exception e,String taskName) {
+        tradingLog.setState(TradingLog.State.UNKNOWN_ERR);
+        tradingLog.setMesage(e.getMessage());
+        tradingLog.setApiNo(taskName);
+        try {
+            recordService.logInsert(tradingLog);
+        } catch (Exception ex) {
+            e.printStackTrace();
+        }
+    }
+}
Index: src/main/java/com/novaone/payment/RabbitConfig.java
===================================================================
--- src/main/java/com/novaone/payment/RabbitConfig.java	(不存在的)
+++ src/main/java/com/novaone/payment/RabbitConfig.java	(版本 31412)
@@ -0,0 +1,119 @@
+package com.novaone.payment;
+
+import org.springframework.amqp.core.*;
+import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-18 17:44
+ */
+@Configuration
+public class RabbitConfig {
+
+    public static final String EX_PAYMENT = "payment_exchange";
+
+    public static final String EX_DIE = "die_exchange";
+
+    /**
+     * 待付款
+     */
+    public static final String QU_PAYMENT = "payment_queue";
+    /**
+     * 转账汇款交易处理失败
+     */
+    public static final String QU_PAYMENT_FAIL = "payment_queue_fail";
+    /**
+     * 提交转账汇款交易失败
+     */
+    public static final String QU_PAYMENT_SUBMIT_FAIL = "payment_submit_queue_fail";
+    /**
+     * 待查询交易状态
+     */
+    public static final String QU_PAYMENT_STATUS = "payment_status_queue";
+    /**
+     * 待查询交易回单信息
+     */
+    public static final String QU_BILL_INFO = "bill_info_queue";
+    /**
+     * 待下载回单
+     */
+    public static final String QU_BILL_DOWN = "bill_down_queue";
+
+    public static final String KEY_PAYMENT = "pay";
+    public static final String KEY_PAYMENT_ERR = "pay_err";
+    public static final String KEY_PAYMENT_SUBMIT_ERR = "pay_submit_err";
+    public static final String KEY_STATUS = "status";
+    public static final String KEY_INFO = "bill_info";
+    public static final String KEY_DOWN = "bill_down";
+
+    @Bean("paymentExchange")
+    public Exchange paymentExchange(){
+        return ExchangeBuilder.directExchange(EX_PAYMENT).durable().delayed().build();
+    }
+
+
+
+
+    @Bean
+    public Queue paymentQueue(){
+        return QueueBuilder.durable(QU_PAYMENT).build();
+    }
+
+
+
+    @Bean
+    public Queue queryStatusQueue(){
+        return QueueBuilder.durable(QU_PAYMENT_STATUS).build();
+    }
+
+
+    @Bean
+    public Queue billInfoQueue(){
+        return QueueBuilder.durable(QU_BILL_INFO).build();
+    }
+
+
+
+    @Bean
+    public Binding bindEx2PaymentQueue(@Qualifier("paymentQueue") Queue queue, @Qualifier("paymentExchange") Exchange exchange){
+        return BindingBuilder.bind(queue).to(exchange).with(KEY_PAYMENT).noargs();
+    }
+
+
+
+
+
+    @Bean
+    public Binding bindEx2StatusQueue(@Qualifier("queryStatusQueue") Queue queue, @Qualifier("paymentExchange") Exchange exchange){
+        return BindingBuilder.bind(queue).to(exchange).with(KEY_STATUS).noargs();
+    }
+
+
+    @Bean
+    public Binding bindEx2InfoQueue(@Qualifier("billInfoQueue") Queue queue, @Qualifier("paymentExchange") Exchange exchange){
+        return BindingBuilder.bind(queue).to(exchange).with(KEY_INFO).noargs();
+    }
+
+
+
+    @Bean
+    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
+        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
+        factory.setConnectionFactory(connectionFactory);
+        factory.setMessageConverter(new Jackson2JsonMessageConverter());
+        return factory;
+    }
+
+    @Bean
+    public RestTemplate restTemplate(){
+        return new RestTemplate();
+    }
+}
Index: src/main/java/com/novaone/payment/TradingRecordConverter.java
===================================================================
--- src/main/java/com/novaone/payment/TradingRecordConverter.java	(不存在的)
+++ src/main/java/com/novaone/payment/TradingRecordConverter.java	(版本 31412)
@@ -0,0 +1,80 @@
+package com.novaone.payment;
+
+import com.alibaba.fastjson.JSON;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.PaymentBill;
+import com.novaone.entity.TradingRecord;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.ap.shaded.freemarker.template.SimpleDate;
+import org.mapstruct.factory.Mappers;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-05-23 13:44
+ */
+@Mapper
+public interface TradingRecordConverter {
+
+    TradingRecordConverter INSTANCE = Mappers.getMapper(TradingRecordConverter.class);
+
+
+    @Mappings({
+            @Mapping(ignore = true, target = "payBillNo"),
+            @Mapping(target = "type", ignore = true),
+            @Mapping(target = "company", source = "sqrgsmc"),
+            @Mapping(target = "payAmount", source = "zrmbje"),
+            @Mapping(target = "payeeBankName", source = "sjskrkhyh"),
+            @Mapping(target = "payeeAcc", source = "sjskryhzh"),
+            @Mapping(target = "payeeName", source = "sjskrmc"),
+            @Mapping(target = "urgent", source = "yhcljj"),
+            @Mapping(target = "creatTime", expression = "java(new java.util.Date())"),
+            @Mapping(target = "updateTime", expression = "java(new java.util.Date())"),
+            @Mapping(target = "description", source = "zfbz"),
+            @Mapping(target = "id", ignore = true),
+            @Mapping(target = "payAcc", ignore = true),
+            @Mapping(target = "payBank", ignore = true)
+    })
+    TradingRecord convertToTradingRecord(PaymentBill bill);
+
+
+
+    /**
+     * 使用一个单笔支付申请单数据的数据创建一个TradingRecord
+     * @param bill 单笔支付类型申请单
+     */
+    default TradingRecord convertToTradingRecordBySingle(PaymentBill bill){
+        if (bill == null) {
+            return new TradingRecord();
+        }
+        final TradingRecord tradingRecord = convertToTradingRecord(bill);
+        //0:合并支付；1：单笔支付
+        tradingRecord.setType(TradingRecord.Type.SINGLE);
+        tradingRecord.setPayBillNo(bill.getSqdbh());
+        return tradingRecord;
+    }
+
+
+    /**
+     * 使用一个合并后的申请单数据创建TradingRecord
+     * @param bill 多笔合并后申请单数据
+     */
+    default TradingRecord convertToTradingRecordByMerge(PaymentBill bill){
+        if (bill == null) {
+            return new TradingRecord();
+        }
+        final TradingRecord tradingRecord = convertToTradingRecord(bill);
+        //0:合并支付；1：单笔支付
+        tradingRecord.setType(TradingRecord.Type.MERGE);
+        tradingRecord.setPayBillNo(bill.getYppbhValue());
+        return tradingRecord;
+    }
+}
Index: src/main/java/com/novaone/payment/ValidationExceptionCallback.java
===================================================================
--- src/main/java/com/novaone/payment/ValidationExceptionCallback.java	(不存在的)
+++ src/main/java/com/novaone/payment/ValidationExceptionCallback.java	(版本 31412)
@@ -0,0 +1,15 @@
+package com.novaone.payment;
+
+import com.novaone.entity.BocB2eParam;
+
+import java.util.Map;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-03-24 10:46
+ */
+public interface ValidationExceptionCallback {
+    public void callback(Map<BocB2eParam, Map<String, String>> errParam,String apiNo);
+}
Index: src/main/java/com/novaone/quartz/config/SchedulerConfig.java
===================================================================
--- src/main/java/com/novaone/quartz/config/SchedulerConfig.java	(版本 31411)
+++ src/main/java/com/novaone/quartz/config/SchedulerConfig.java	(版本 31412)
@@ -75,7 +75,7 @@
 			}
 		}
 
-        scheduler.start();
+//        scheduler.start(); todo 测试状态太不启动quartz
         return scheduler;
     }
 
Index: src/main/java/com/novaone/service/BOCPayService.java
===================================================================
--- src/main/java/com/novaone/service/BOCPayService.java	(版本 31411)
+++ src/main/java/com/novaone/service/BOCPayService.java	(不存在的)
@@ -1,30 +0,0 @@
-package com.novaone.service;
-
-import com.novaone.entity.PayVO;
-
-/*
- * @program: BankEnterprise
- * @description: 通过中行转账支付业务
- * @author: Ma.ChengJian
- * @create: 2022-01-18 21:19
- */
-public interface BOCPayService {
-
-    /**
-     * 支付
-     */
-    void payment(PayVO vo);
-
-    /**
-     * 余额查询你
-     * @return 余额
-     */
-    String balanceEnquiry(PayVO vo);
-
-    /**
-     *  CNAPS、中行机构号文件下载(b2e0043)
-     * @param type
-     */
-    void download(String type);
-
-}
Index: src/main/java/com/novaone/service/ConfigService.java
===================================================================
--- src/main/java/com/novaone/service/ConfigService.java	(版本 31411)
+++ src/main/java/com/novaone/service/ConfigService.java	(版本 31412)
@@ -116,7 +116,13 @@
      * @Exception 异常对象
      */
     String getNo();
+
+
     /**
+     * 测试环境前置机
+     */
+    public ConfigBocVO queryConfigTest();
+    /**
      * 
        
      * queryConfigOne(获取一个BOC配置)    
Index: src/main/java/com/novaone/service/PayService.java
===================================================================
--- src/main/java/com/novaone/service/PayService.java	(不存在的)
+++ src/main/java/com/novaone/service/PayService.java	(版本 31412)
@@ -0,0 +1,67 @@
+package com.novaone.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.entity.PaymentBill;
+import com.novaone.entity.BocB2eParam;
+import com.novaone.entity.TradingRecord;
+import com.novaone.test.enttity.Payment;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-04-13 16:05
+ */
+public interface PayService {
+
+    /**
+     *  预处理付款申请单
+     */
+    int preprocess(List<PaymentBill> paymentBills);
+
+    /**
+     * 转账汇款
+     */
+    List<BocB2eParam> payment(Set<TradingRecord> records);
+
+
+    public boolean payment(TradingRecord record);
+
+
+
+    /**
+     * 查询支付状态
+     */
+    boolean doPayState(TradingRecord param);
+
+    /**
+     * 银行生成处理
+     */
+    public void doBankBill(TradingRecord param);
+
+    /**
+     * 导出PDF
+     */
+    byte [] exportBillToPdf(JSONObject pdfParam);
+
+    /**
+     * 余额校验及处理
+     */
+    Set<TradingRecord> doBalance(List<TradingRecord> tradingRecords);
+
+
+    public Map<String,Object> resendMesg(List<String> msgType);
+
+
+    void captureHistorical(Set<String> accounts, Date date);
+
+    /**
+     * 当给定交易下载到银行回单时，清理RPA生成的回单
+     */
+    void clearBillInfoPdf(TradingRecord records);
+}
Index: src/main/java/com/novaone/service/PaymentBillService.java
===================================================================
--- src/main/java/com/novaone/service/PaymentBillService.java	(不存在的)
+++ src/main/java/com/novaone/service/PaymentBillService.java	(版本 31412)
@@ -0,0 +1,32 @@
+package com.novaone.service;
+
+import com.novaone.entity.PaymentBill;
+
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description: 付款申请单
+ * @author: Ma.ChengJian
+ * @create: 2022-06-08 09:39
+ */
+public interface PaymentBillService {
+
+    public List<PaymentBill> selectUnpaid();
+
+    public int insert(PaymentBill bill);
+
+    public int batchUpdateBySqdbhOrYppbh(List<PaymentBill> singleList,List<PaymentBill> mergeList);
+
+    public int batchUpdateBySqdbh(List<PaymentBill> bills);
+
+    public int updateBySqdbh(PaymentBill bills);
+
+    public int updateByYppbhValue(PaymentBill bills);
+
+    public int batchUpdateByYppbhValue(List<PaymentBill> bills);
+
+    public List<PaymentBill> selectByYppbhValue(String yppbhValue);
+
+    public PaymentBill findBankBillBySqdbh(String sqdbh);
+}
Index: src/main/java/com/novaone/service/TradingRecordService.java
===================================================================
--- src/main/java/com/novaone/service/TradingRecordService.java	(不存在的)
+++ src/main/java/com/novaone/service/TradingRecordService.java	(版本 31412)
@@ -0,0 +1,57 @@
+package com.novaone.service;
+
+import java.util.Date;
+import java.util.List;
+
+import com.novaone.entity.TradingBillRecord;
+import com.novaone.entity.TradingLog;
+import com.novaone.entity.TradingRecord;
+public interface TradingRecordService{
+
+
+    int deleteByPrimaryKey(Long id);
+
+    int insert(TradingRecord record);
+
+    int insertOrUpdate(TradingRecord record);
+
+    int insertOrUpdateSelective(TradingRecord record);
+
+    int insertSelective(TradingRecord record);
+
+    TradingRecord selectByPrimaryKey(Long id);
+
+    List<TradingRecord> selectUnpaid();
+
+    List<TradingRecord> selectByBusinessState(List<Integer> status);
+
+    List<TradingRecord> selectByBillState(List<Integer> status);
+
+
+    List<TradingRecord> selectByScheduleDate(Date scheduleDate);
+
+    int countUnpaid();
+
+    int updateByPrimaryKeySelective(TradingRecord record);
+
+    int updateByPrimaryKey(TradingRecord record);
+
+    int updateBatch(List<TradingRecord> list);
+
+    int updateBatchSelective(List<TradingRecord> list);
+
+    int updateBatchSelectiveNoTransaction(List<TradingRecord> list);
+
+    int batchInsert(List<TradingRecord> list);
+
+    int logBatchInsert(List<TradingLog> logs);
+
+    int logInsert(TradingLog logs);
+
+    int insertBillRecord(TradingBillRecord bill);
+
+    int updateBillRecord(TradingBillRecord bill);
+
+
+
+}
Index: src/main/java/com/novaone/service/impl/BOCPayServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/BOCPayServiceImpl.java	(版本 31411)
+++ src/main/java/com/novaone/service/impl/BOCPayServiceImpl.java	(不存在的)
@@ -1,136 +0,0 @@
-package com.novaone.service.impl;
-
-import com.novaone.entity.PayVO;
-import com.novaone.entity.VO.ConfigBocVO;
-import com.novaone.service.BOCPayService;
-import com.novaone.service.BocInterfaceService;
-import com.novaone.service.ConfigService;
-import com.novaone.util.CommonInfo;
-import com.novaone.util.CreateXml;
-import com.novaone.util.HttpUtil;
-import jodd.io.ZipUtil;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import net.sf.json.JSONObject;
-import org.apache.commons.codec.binary.Base64;
-import org.dom4j.Document;
-import org.dom4j.DocumentHelper;
-import org.dom4j.Element;
-import org.springframework.stereotype.Service;
-import javax.annotation.Resource;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/*
- * @program: BankEnterprise
- * @description:
- * @author: Ma.ChengJian
- * @create: 2022-01-25 10:54
- */
-@Slf4j
-@Service
-public class BOCPayServiceImpl implements BOCPayService {
-
-    @Resource
-    private ConfigService configService;
-
-
-    /**
-     * 支付
-     */
-    @Override
-    public void
-    payment(PayVO vo) {
-    }
-
-    /**
-     * 查询指定中行账户的余额
-     */
-    @Override
-    public String balanceEnquiry(PayVO vo) {
-        return null;
-    }
-
-    /**
-     * 调用B2e0043接口，CNAPS、中行机构号文件下载
-     * @param type 下载的文件类型
-     *            0：CNAPS文件
-     *            1：中行机构号文件
-     *            2：电子汇票行号文件
-     */
-    @Override
-    @SneakyThrows
-    public void download(String type) {
-        ConfigBocVO configBocVO = configService.queryConfigOne();
-        String xml = b2e0043XMLBuild(type,configBocVO);
-        String url = configBocVO.getProtocol() + "://" + configBocVO.getIp() + ":" + configBocVO.getPort() + "/B2EC/E2BServlet";
-        log.info("b2e0043请求URL:{}  XML:{}",url,xml);
-        String res = HttpUtil.postXml(url, xml, "GB2312");
-        log.info("b2e0043接口响应：{}",res);
-        JSONObject jsonObject = CreateXml.xmlToJson(res);
-        log.info("b2e0043 xml转JSON：{}",jsonObject.toString());
-        //获取b2e0043接口响应报文中的压缩文件数据。
-        String data = jsonObject.getJSONObject("trans").getJSONObject("trn-b2e0043-rs").getString("data");
-        download(data,type); //解压并下载文件
-    }
-
-    private void download(String data,String type){
-        log.info("b2e0043 文件类型：{}  文件数据：{}",type,data);
-        //Base64解码压缩文件数据
-        byte[] getData = data.getBytes();
-        byte[] bData = Base64.decodeBase64(getData);
-        String fileName = type + "_" + System.currentTimeMillis();
-        //将压缩文件存储
-        File zipFile = new File("/mnt/BankEnterprise/download/department/" + fileName + ".zip");
-        FileOutputStream outputStream = null;
-        try {
-            outputStream = new FileOutputStream(zipFile);
-            outputStream.write(bData);
-            outputStream.flush();
-        } catch (IOException e) {
-            log.error("b2e0043 压缩文件本地存储异常",e);
-            e.printStackTrace();
-        } finally {
-            if (outputStream != null) {
-                try {
-                    outputStream.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-        //将压缩文件解压到相同的目录下，解压成功则删除压缩文件
-        try {
-            File file = new File("/mnt/BankEnterprise/download/department/" + fileName + ".csv");
-            ZipUtil.unzip(zipFile,file);
-            zipFile.delete();
-            log.info("文件下载成功");
-        } catch (IOException e) {
-            log.error("b2e0043 文件解压异常,文件路径{}",zipFile.getAbsolutePath(),e);
-            e.printStackTrace();
-        }
-    }
-
-    private String b2e0043XMLBuild(String type,ConfigBocVO config){
-        Document document = DocumentHelper.createDocument();
-        document.setXMLEncoding("GB2312");
-        Element bocb2e = document.addElement("bocb2e");
-        //设置根节点属性
-        bocb2e.addAttribute("version","100");
-        bocb2e.addAttribute("security","true");
-        bocb2e.addAttribute("lang","chs");
-        //头部
-        Element head = bocb2e.addElement("head");
-        head.addElement("termid").setText(config.getTermid());
-        head.addElement("trnid").setText("");
-        head.addElement("custid").setText(config.getCustid());
-        head.addElement("cusopr").setText(config.getUser());
-        head.addElement("trncod").setText("b2e0043");
-        //请求数据
-        Element trnB2e0043Rq = bocb2e.addElement("trans").addElement("trn-b2e0043-rq");
-        Element tasktpy = trnB2e0043Rq.addElement("b2e0043-rq").addElement("tasktpy");
-        tasktpy.addText(type);
-        return document.asXML();
-    }
-}
Index: src/main/java/com/novaone/service/impl/BillServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/BillServiceImpl.java	(版本 31411)
+++ src/main/java/com/novaone/service/impl/BillServiceImpl.java	(版本 31412)
@@ -59,6 +59,7 @@
 
     @Transactional
     @Override
+    //下载回单文件
     public void downloadFtpZip() throws Exception {
         SimpleDateFormat f = new SimpleDateFormat("yyyyMMdd");
         Calendar instance = Calendar.getInstance();
Index: src/main/java/com/novaone/service/impl/BocInterfaceServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/BocInterfaceServiceImpl.java	(版本 31411)
+++ src/main/java/com/novaone/service/impl/BocInterfaceServiceImpl.java	(版本 31412)
@@ -76,6 +76,9 @@
         JSONObject statusObject = json.getJSONObject("trans").getJSONObject("trn-b2e0001-rs").getJSONObject("status");
         String code = statusObject.getString("rspcod");
         if(code.equals(CommonInfo.BANK_SUCCESS)) {
+            String token = json.getJSONObject("head").getString("token");
+            System.setProperty("bankSignToken",token);
+            log.info("前置机签到成功 sign token:{}",token);
             return true;
         }
         return false;
Index: src/main/java/com/novaone/service/impl/ConfigServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/ConfigServiceImpl.java	(版本 31411)
+++ src/main/java/com/novaone/service/impl/ConfigServiceImpl.java	(版本 31412)
@@ -144,4 +144,10 @@
     public ConfigBocVO queryConfigOne() {
         return bocDao.selectOne();
     }
+
+    @Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)
+    @Override
+    public ConfigBocVO queryConfigTest() {
+        return bocDao.selectTest();
+    }
 }
Index: src/main/java/com/novaone/service/impl/PayServiceBocImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/PayServiceBocImpl.java	(不存在的)
+++ src/main/java/com/novaone/service/impl/PayServiceBocImpl.java	(版本 31412)
@@ -0,0 +1,1094 @@
+package com.novaone.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.novaone.common.exception.BusinessException;
+import com.novaone.dao.CompanyBankAccMapper;
+import com.novaone.payment.JschUtil;
+import com.novaone.common.exception.BocB2eException;
+import com.novaone.control.PayController;
+import com.novaone.dao.TradingBillRecordMapper;
+import com.novaone.dao.TradingHistoryMapper;
+import com.novaone.dao.TradingLogMapper;
+import com.novaone.entity.*;
+import com.novaone.service.PayService;
+import com.novaone.payment.*;
+import com.novaone.service.PaymentBillService;
+import com.novaone.service.TradingRecordService;
+import org.apache.commons.lang3.StringUtils;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jasperreports.engine.JREmptyDataSource;
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+import net.sf.jasperreports.engine.JasperPrint;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Resource;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-04-13 16:12
+ */
+@Slf4j
+@Service
+@Transactional
+public class PayServiceBocImpl implements PayService {
+
+    @Resource
+    private BOCClient client;
+    @Resource
+    private PaymentBillService paymentService;
+    @Resource
+    private TradingRecordService recordService;
+    @Resource
+    private TradingLogMapper logMapper;
+    @Resource
+    private CodeGenerate generate;
+    @Resource
+    private JschUtil ftpClient;
+    @Resource
+    private TradingBillRecordMapper billRecordMapper;
+    @Resource
+    private TradingHistoryMapper historyMapper;
+    @Resource
+    private MessageSender sender;
+    @Resource
+    private CompanyBankAccMapper bankAccMapper;
+
+    private final String SINGLE = "single";
+
+    private final String TEMPLATE = "#{zfbz} /-#{number}/-";
+
+    private final String UPLOAD_DIR = "/mnt/file/rpa_create/";
+
+    private final List<String> TEMPLATE_ID = Arrays.asList("001","501");
+
+    /**
+     * 申请单批量预处理
+     * @return 写入到MySql中的交易数量
+     */
+    public  int preprocess(List<PaymentBill> paymentBills) {
+        final Map<String, List<PaymentBill>> collect = paymentBills.stream().collect(Collectors.groupingBy(item -> {
+            if (StringUtils.isEmpty(Optional.ofNullable(item.getYppbhValue()).orElse("").trim())) {
+                return SINGLE;
+            }
+            return item.getYppbhValue();
+        }));
+        //单笔支付申请单处理
+        int succeedCount = this.singleHandle(Optional.ofNullable(collect.get(SINGLE)).orElse(Collections.emptyList()));
+        collect.remove(SINGLE);//删除单笔支付的申请单，剩余的都是合并支付
+        //合并支付申请单处理
+        succeedCount += this.mergeHandle(collect);
+        return succeedCount;
+    }
+
+
+
+    /**
+     * 处理单笔支付申请单。
+     *      1. 校验通过：写入MUsql；记录日志；
+     *      2。 校验失败：写入MySQL;记录日志；更新SQL Server状态为失败状态，跟新失败原因
+     * @param paymentBills 单笔支付申请单
+     * @return 处理成功的申请单数量
+     */
+    private int singleHandle(List<PaymentBill> paymentBills){
+        if (paymentBills.isEmpty()) {
+            return 0;
+        }
+        log.info("开始处理单笔支付付款申请单{}笔 列表:{}",paymentBills.size(),JSON.toJSONString(paymentBills));
+        final ArrayList<TradingRecord> tradingRecords = new ArrayList<>(paymentBills.size());
+        List<TradingLog> logs = new ArrayList<>(paymentBills.size());
+        int succeed = 0,failed = 0;
+        //逐笔校验申请单参数完整性。
+        for (PaymentBill bill : paymentBills) {
+            final TradingLog tradingLog = new TradingLog();
+            tradingLog.setCreatTime(new Date());
+            final TradingRecord tradingRecord = TradingRecordConverter.INSTANCE.convertToTradingRecordBySingle(bill);
+            tradingRecord.setPaymentBills(Collections.singletonList(bill));
+            tradingLog.setApiNo("申请单预处理");
+            //校验申请单参数是否完整。
+            if (basicCheck(bill)){
+                //完善数据:收款人账户类型、payNo、支付备注
+                refineData(tradingRecord);
+                //参数完整性校验通过则继续根据申请人公司名称完善付款信息
+                if (initPayInfo(tradingRecord)){
+                    tradingRecord.setBusinessState(TradingRecord.BusinessState.INIT);
+                    tradingLog.setState(TradingLog.State.PAYMENT_BILL_CHECK_SUCCESS);
+                    tradingLog.setMesage(JSON.toJSONString(bill));
+                    succeed++;
+                }else {
+                    tradingRecord.setBusinessState(TradingRecord.BusinessState.FAIL);
+                    tradingRecord.setMessage(TradingRecord.Message.PAY_BANK_ERR);
+                    bill.setZfzt(PaymentBill.ZFZT.FAIL);
+                    bill.setZfsbyy(TradingRecord.Message.PAY_BANK_ERR);
+                    tradingLog.setState(TradingLog.State.PAYMENT_BILL_CHECK_ERR);
+                    tradingLog.setMesage(TradingRecord.Message.PAY_BANK_ERR + JSON.toJSONString(bill));
+                    failed++;
+                }
+            }else {
+                log.warn("申请单{}参数校验失败无法发起汇款:{}",bill.getSqdbh(),JSON.toJSONString(bill));
+                refineData(tradingRecord);
+                tradingRecord.setBusinessState(TradingRecord.BusinessState.FAIL);
+                tradingRecord.setMessage(TradingRecord.Message.SINGLE_CHECK_ERR);
+                bill.setZfzt(PaymentBill.ZFZT.FAIL);
+                bill.setZfsbyy(TradingRecord.Message.SINGLE_CHECK_ERR);
+                tradingLog.setState(TradingLog.State.PAYMENT_BILL_CHECK_ERR);
+                tradingLog.setMesage(TradingRecord.Message.SINGLE_CHECK_ERR + JSON.toJSONString(bill));
+                failed++;
+            }
+            log.info("申请单编号为:{}的数据转换为TradingRecord：{}",bill.getSqdbh(),JSON.toJSONString(tradingRecord));
+            tradingLog.setPayNo(tradingRecord.getPayNo());
+            logs.add(tradingLog);
+            tradingRecords.add(tradingRecord);
+        }
+        try {
+            //根据校验结果的通过申请单编号批量改为支付状态，修改成功则将TradingRecord批量写入MySQL
+            paymentService.batchUpdateBySqdbh(paymentBills);
+        } catch (Exception e) {
+            throw new BusinessException("申请单预处理结果更新水果通时异常: " + JSON.toJSONString(paymentBills),e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        try {
+            //成功和失败一次性批量插入MySQL
+            recordService.batchInsert(tradingRecords);
+            recordService.logBatchInsert(logs);
+        } catch (Exception e) {
+            throw new BusinessException("申请单预处理结果写入MySQL时异常: " + JSON.toJSONString(tradingRecords),e,BusinessException.Type.MYSQL_ERR);
+        }
+        log.info("单笔支付申请单处理完成,校验成功{}笔，校验失败{}笔",succeed,failed);
+        return succeed;
+    }
+
+
+
+
+    /**
+     * 校验付款单中除申请单编号外的必要参数
+     * @param bill 付款申请单
+     */
+    private boolean basicCheck(PaymentBill bill){
+        return bill.getSqdbh() != null && StringUtils.isNotEmpty(bill.getSqdbh().trim())      //申请的那编号不为空
+                && bill.getSqrgsmc() != null && StringUtils.isNotEmpty(bill.getSqrgsmc().trim())     //申请人公司不为空
+                && bill.getZrmbje() != null && bill.getZrmbje().compareTo(new BigDecimal(0)) > 0 //付款金额不能为空且大于0
+//                && bill.getYhcljj() != null     //不校验银行处理加急参数，非加急状态码外的其他值视为不加急
+                && bill.getZfbz() == null || bill.getZfbz().length() <= 200               //支付备注不能超过200
+                && bill.getSjskryhzh() != null && StringUtils.isNotEmpty(bill.getSjskryhzh().trim())     //收款账号不能为空
+                && bill.getSjskrkhyh() != null && StringUtils.isNotEmpty(bill.getSjskrkhyh().trim())     //收款人开户行不能为空
+                && bill.getSjskrmc() != null && StringUtils.isNotEmpty(bill.getSjskrmc().trim())        //收款人名称不为空
+                && StringUtils.isNotEmpty(bill.getWbbb()) && (bill.getWbbb().toUpperCase(Locale.ROOT).equals("CNY") || bill.getWbbb().toUpperCase(Locale.ROOT).equals("RMB"));//币种不为空并且为CNY或者RMB
+    }
+
+
+
+
+    /**
+     * 合并支付生清单处理。
+     * @param mergeBills 合并支付申请单
+     */
+    private int mergeHandle(Map<String, List<PaymentBill>> mergeBills){
+        mergeBills.remove(SINGLE);
+        if (mergeBills.isEmpty()) {
+            return 0;
+        }
+        log.info("开始处理{}笔合并交易，预匹配编号:{}",mergeBills.size(),mergeBills.keySet());
+        final Map<Boolean, List<Map.Entry<String, List<PaymentBill>>>> checkResult = mergeBills.entrySet().stream().collect(Collectors.groupingBy(entry -> this.checkMerge(entry)));
+        List<TradingRecord> checkSucceed = convertToTradingRecordBySucceed(checkResult.get(Boolean.TRUE));//参数校验成功的
+        List<TradingRecord> checkFailed = convertToTradingRecordByFail(checkResult.get(Boolean.FALSE));//参数校验失败的
+        //参数校验成功的继续根据申请人公司名称完善付款账号信息。
+        final List<TradingRecord> initFailed = checkSucceed.stream().filter(item -> {
+            final boolean flag = initPayInfo(item);
+            log.info("交易{}根据申请人公司名称“{}”完善付款行信息结束:{}",item.getPayNo(),item.getCompany(),JSON.toJSONString(item));
+            return !flag;
+        }).collect(Collectors.toList());
+        //1.付款单处理结果据预匹配编号更新SqlServer，校验成功的数据不需要修改SQLserver状态。参数校验失败的和根据申请情人公司名称完善付款信息失败的更新水果通为交易失败状态。
+        final ArrayList<PaymentBill> failedBills = new ArrayList<>(checkFailed.size() + initFailed.size());
+        checkFailed.forEach(item -> failedBills.add(new PaymentBill(item.getPayBillNo(),PaymentBill.ZFZT.FAIL,TradingRecord.Message.MERGE_CHECK_ERR)));
+        initFailed.forEach(item -> failedBills.add(new PaymentBill(item.getPayBillNo(),PaymentBill.ZFZT.FAIL,TradingRecord.Message.PAY_BANK_ERR)));
+        try {
+            paymentService.batchUpdateByYppbhValue(failedBills);
+        } catch (Exception e) {
+            throw new BusinessException("校验失败的申请单更新水果通时异常: " + JSON.toJSONString(failedBills),e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        //2. 写入Mysql
+        final ArrayList<TradingRecord> tradingRecords = new ArrayList<>(checkSucceed.size() + checkFailed.size());
+        tradingRecords.addAll(checkSucceed);
+        tradingRecords.addAll(checkFailed);
+        final List<TradingLog> logs = tradingRecords.stream().map(item -> {
+            final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(item);
+            tradingLog.setApiNo("申请单预处理");
+            if (item.getBusinessState() == TradingRecord.BusinessState.INIT){
+                tradingLog.setState(TradingLog.State.PAYMENT_BILL_CHECK_SUCCESS);
+            }else if (item.getBusinessState() == TradingRecord.BusinessState.FAIL){
+                tradingLog.setState(TradingLog.State.PAYMENT_BILL_CHECK_ERR);
+            }
+            tradingLog.setMesage(JSON.toJSONString(item.getPaymentBills()));
+            return tradingLog;
+        }).collect(Collectors.toList());
+        try {
+            recordService.batchInsert(tradingRecords);
+            recordService.logBatchInsert(logs);
+        }catch (Exception e) {
+            throw new BusinessException("申请单预处理结果写入MySQL时异常: " + JSON.toJSONString(tradingRecords),e,BusinessException.Type.MYSQL_ERR);
+        }
+        return checkSucceed.size()-initFailed.size();
+    }
+
+
+    /**
+     * 将校验成功的合并支付类型申请单转为TradingRecord。
+     * @param mergeList 校验成功的合并支付类型申请单数据
+     * @return 多笔申请单合并后的TradingRecord（交易记录）
+     */
+    private List<TradingRecord> convertToTradingRecordBySucceed(List<Map.Entry<String, List<PaymentBill>>> mergeList){
+        if (Objects.isNull(mergeList) || mergeList.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<TradingRecord> succeedRecord = new ArrayList<>(mergeList.size());
+        for (Map.Entry<String, List<PaymentBill>> entry : mergeList) {
+            //获取一条付款申请单用于完善tradingRecord
+            final PaymentBill paymentBill = entry.getValue().get(0);
+            final TradingRecord record = TradingRecordConverter.INSTANCE.convertToTradingRecordByMerge(paymentBill);
+            refineData(record);
+            record.setBusinessState(TradingRecord.BusinessState.INIT);
+            record.setPaymentBills(entry.getValue());
+            //计算合并金额
+            BigDecimal totalAmount = BigDecimal.ZERO;
+            for (PaymentBill Bill : entry.getValue()) {
+                totalAmount = totalAmount.add(Bill.getZrmbje());
+            }
+            record.setPayAmount(totalAmount);
+            succeedRecord.add(record);
+            log.info("预匹配编号为{}的{}笔申请单被合并为交易{}",entry.getKey(), entry.getValue().size(), record.getPayNo());
+        }
+        return Collections.unmodifiableList(succeedRecord);
+    }
+
+
+
+    /**
+     * 将校验成功的合并支付类型申请单转为TradingRecord。
+     * @param mergeList 校验失败的合并支付类型申请单数据
+     * @return 多笔申请单合并后的TradingRecord（交易记录）
+     */
+    private List<TradingRecord> convertToTradingRecordByFail(List<Map.Entry<String, List<PaymentBill>>> mergeList) {
+        if (Objects.isNull(mergeList) || mergeList.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<TradingRecord> failRecord = new ArrayList<>(mergeList.size());
+        for (Map.Entry<String, List<PaymentBill>> entry : mergeList) {
+            final TradingRecord tradingRecord = new TradingRecord();
+            tradingRecord.setPayBillNo(entry.getKey());
+            tradingRecord.setPaymentBills(entry.getValue());
+            //0:合并支付；1：单笔支付
+            tradingRecord.setType(TradingRecord.Type.MERGE);
+            tradingRecord.setPayNo(generate.generate());
+            tradingRecord.setBusinessState(TradingRecord.BusinessState.FAIL);
+            tradingRecord.setMessage(TradingRecord.Message.MERGE_CHECK_ERR);
+            tradingRecord.setCreatTime(new Date());
+            tradingRecord.setUpdateTime(new Date());
+            //获取一条付款申请单用于完善tradingRecord
+            final PaymentBill paymentBill = entry.getValue().get(0);
+            tradingRecord.setDescription(TEMPLATE.replace("#{zfbz}",paymentBill.getZfbz()).replace("#{number}",paymentBill.getYppbhValue()));
+            failRecord.add(tradingRecord);
+            log.info("预匹配编号为{}的{}笔申请单校验失败", entry.getKey(), entry.getValue().size());
+        }
+        return Collections.unmodifiableList(failRecord);
+    }
+
+
+
+
+    /**
+     * 校验合并支付申请单。
+     * @param bills 拥有相同预匹配编号的全部需要被合并的申请单。
+     * @return 全部被合并申请单必须参数完整且收付款人信息一致，否则返回false。
+     */
+    private boolean checkMerge(Map.Entry<String, List<PaymentBill>> bills){
+        log.info("校验预匹配编号为{}的合并交易，待合并申请单数量:{},申请单列表:{}",bills.getKey(),bills.getValue().size(),JSON.toJSONString(bills.getValue()));
+        final PaymentBill paymentBill = bills.getValue().get(0);
+        for (PaymentBill bill : bills.getValue()) {
+            //校验申请单数据完整性
+            if (!basicCheck(bill)) {
+                log.error("预匹配编号 {} 申请单编号 {} 参数检验不完整放弃支付",bill.getYppbhValue(), bill.getSqdbh());
+                return false;
+            }
+            //校验申请单收付款信息是否一致
+            if (
+                    !paymentBill.getSjskrkhyh().equals(bill.getSjskrkhyh())  //收款人开户银行
+                    || !paymentBill.getSjskryhzh().equals(bill.getSjskryhzh())//收款人账号
+                    || !paymentBill.getSjskrmc().equals(bill.getSjskrmc())      //收款人名称
+                    || !paymentBill.getSqrgsmc().equals(bill.getSqrgsmc())       //申请人公司名称
+            ){
+                log.error("预匹配编号为{}的{}笔需合并支付的申请单收付款信息不完全一致，放弃支付。必须保证全部申请单收付款人信息及申请人公司名称一致",bills.getKey(),bills.getValue().size());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 完善付款人信息，校验收款账号类型
+     * @param tradingRecord 校验成功的交易数据
+     *      合并支付备注格式：备注内容 /-预匹配编号/-
+     *      非合并支付格式备注：备注内容 /-申请单编号/-
+     * @return 完善收款账号类型、付款账户信息、交易记录业务状态后的TradingRecord
+     */
+    private TradingRecord refineData(TradingRecord tradingRecord){
+        //校验收款公司类型
+        tradingRecord.setPayeeAccType(checkPayeeName(tradingRecord.getPayeeName()));
+        tradingRecord.setPayNo(generate.generate());
+        if (tradingRecord.getDescription() == null){
+            tradingRecord.setDescription("");
+        }
+        tradingRecord.setDescription(TEMPLATE.replace("#{zfbz}",tradingRecord.getDescription()).replace("#{number}",tradingRecord.getPayBillNo()));
+        return tradingRecord;
+    }
+
+
+    /**
+     * 根据收款人名称判定收款人账户类型。
+     * @param payeeName 收款人名称
+     * @return 收款人账号类型
+     */
+    private Integer checkPayeeName(String payeeName){
+        return payeeName.length() - payeeName.replaceAll("[\u4e00-\u9fa5]", "").length() <= 4 ? TradingRecord.PayeeAccType.PRIVATE: TradingRecord.PayeeAccType.CORPORATE;
+    }
+
+
+
+    /**
+     * 向银行发起交易。
+     * @param record 待支付数据
+     * @return 返回true表示该交易需要查询支付处理状态。
+     */
+    public boolean payment(TradingRecord record){
+        if (record.getBusinessState() == TradingRecord.BusinessState.REQUEST_ERR) {
+            //需要先确认支付状态在发起付款请求
+            final JSONObject resp = queryPayStats(record);
+            log.info("查询到交易{}在银行的处理状态为{}",record.getPayNo(),resp.toJSONString());
+            //如果交易已经成功提交到银行,则针对银行返回的交易处理结果进行操作
+            if (!BocStatus.NONE.code().equals(resp.getString("rspcod"))) {
+                //成功、失败、未知状态完成处理并返回false；银行处理中则返回true。
+                return resultOperate(record, resp);
+            }
+        }
+        final BocB2eParam payReq = BocB2eParam.getInstance(record);
+        final JSONObject payResp;
+        try {
+            //向银行发起交易请求
+            payResp = client.payment(Collections.singletonList(payReq)).getJSONObject(0);
+        } catch (BocB2eException e) {
+            record.setBusinessState(TradingRecord.BusinessState.REQUEST_ERR);//网络异常，需要确认支付状态后在发起支付操作
+            recordService.updateByPrimaryKey(record);
+            throw new BusinessException("交易" + record.getPayNo() + "发起汇款交易请求银行接口时异常:" + e.getMessage(),e,BusinessException.Type.API_CALL_FAIL,record);
+        }
+        return doPayResult(record, payResp);
+    }
+
+
+    /**
+     * 申请人公司名称参数完善交易的付款行信息
+     * @param record 交易
+     * @return 根据申请情人公司名称查询到付款银行则返回true，否则返回false
+     */
+    private boolean initPayInfo(TradingRecord record){
+        final CompanyBankAcc companyBankAcc = bankAccMapper.selectByCompany(record.getCompany());
+        //根据申请人公司名称查询到银行信息则完善付款账号信息
+        if (companyBankAcc != null){
+            record.setPayName(companyBankAcc.getCompany());
+            record.setPayBank(companyBankAcc.getBankName());
+            record.setPayAcc(companyBankAcc.getBankAccount());
+            return true;
+        }
+        record.setBusinessState(TradingRecord.BusinessState.FAIL);
+        record.setMessage(TradingRecord.Message.PAY_BANK_ERR);
+        log.error("交易{}根据申请人公司名称“{}”没能查询到付款账号信息",record.getPayNo(),record.getCompany());
+        return false;
+    }
+
+
+    /**
+     * 交易发起成功：
+     *  1. 更新MySQL数据状态为“支付中”
+     *  2. 记录日志表。
+     *  3. 返回true
+     * 交易失败（非B0001的响应码）：
+     *  1. 更新MySQL数据状态为“支付失败”
+     *  2. 更新SqlServer中申请单数据状态为“支付失败”，并更新支付失败原因。
+     *  3. 向日志表中插入一条日志。
+     *  4. 返回false
+     * @return 交易请求是否返回B001的响应状态码
+     */
+    private boolean doPayResult(TradingRecord record, JSONObject payResp) {
+        final JSONObject status = payResp.getJSONObject("status");
+        final String bankCode = status.getString("rspcod");
+        final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(record);
+        tradingLog.setApiNo("交易发起");
+        tradingLog.setRespCode(bankCode);
+        tradingLog.setRespMsg(status.getString("rspmsg"));
+        record.setUpdateTime(new Date());
+        record.setBankState(bankCode);
+        boolean payFlag = false;
+        //是否返回了B001的响应状态码
+        if (!BocStatus.OK.code().equals(bankCode)) {
+            log.error("交易发起失败：{}", payResp.toJSONString());
+            tradingLog.setState(TradingLog.State.FAILED_PAYMENT);
+            record.setMessage(TradingRecord.Message.PAY_FAILED + status.toJSONString());
+            record.setBusinessState(TradingRecord.BusinessState.FAIL);
+            final PaymentBill paymentBill = new PaymentBill();
+            if (BocStatus.EXIST.code().equals(bankCode)) {
+                paymentBill.setZfsbyy("交易可能已经支付,先确认后做进一步处理,银行端返回的描述信息:" + status.toJSONString());
+            }else {
+                paymentBill.setZfsbyy("银行未能正常处理该笔交易,银行端返回的描述信息:" + status.toJSONString());
+            }
+            paymentBill.setZfzt(PaymentBill.ZFZT.FAIL);
+            try {
+                //判断当前交易是否为为合并支付。跟新SqlServer
+                if (record.getType() == TradingRecord.Type.MERGE) {
+                    paymentBill.setYppbhValue(record.getPayBillNo());
+                    paymentService.updateByYppbhValue(paymentBill);
+                }else {
+                    paymentBill.setSqdbh(record.getPayBillNo());
+                    paymentService.updateBySqdbh(paymentBill);
+                }
+            } catch (Exception e) {
+                throw new BusinessException("交易发起失败数据更新水果通异常",e,BusinessException.Type.SQLSERVER_ERR, record);
+            }
+        }else {
+            log.info("交易发起成功：{}", payResp.toJSONString());
+            tradingLog.setState(TradingLog.State.SUCCESS_PAYMENT);
+            record.setBusinessState(TradingRecord.BusinessState.WAIT);
+            if (StringUtils.isEmpty(record.getScheduleDate())){
+                record.setScheduleDate(new SimpleDateFormat("yyyyMMdd").format(new Date()));
+            }
+            payFlag = true;
+        }
+        //更新MySQL。更新交易状态并写入日志
+        logMapper.insert(tradingLog);
+        recordService.updateByPrimaryKeySelective(record);
+        return payFlag;
+    }
+
+
+
+
+
+    /**
+     * 发起胡款交易
+     *  根据付款账号分组，校验账户余额是否足够本次付款。足够支付；不够支付结束支付（失败）
+     */
+    @Override
+    public synchronized List<BocB2eParam> payment(Set<TradingRecord> records) {
+        if (records.isEmpty()) {
+            return Collections.emptyList();
+        }
+        //发起付款请求
+        final List<BocB2eParam> params = records.stream().map(BocB2eParam::getInstance).collect(Collectors.toList());
+        JSONArray payRespArr = client.payment(params);
+        Map<String,JSONObject> payRespMap = new HashMap<>(payRespArr.size());
+        for (int i = 0; i < payRespArr.size(); i++) {
+            final JSONObject payResp = payRespArr.getJSONObject(i);
+            payRespMap.put(payResp.getString("insid"),payResp);
+        }
+        //4. 处理交易发起结果
+        final List<BocB2eParam> bocB2eParams = doPaymentResult(payRespMap, params);
+        return bocB2eParams;
+    }
+
+    /**
+     * 处理付款请求发起结果
+     */
+    private List<BocB2eParam> doPaymentResult(Map<String,JSONObject> payRespMap, List<BocB2eParam> params) {
+        final ArrayList<BocB2eParam> successRecord = new ArrayList<>(params.size());
+        final ArrayList<PaymentBill> failedSingleBill = new ArrayList<>();
+        final ArrayList<PaymentBill> failedMergeBill = new ArrayList<>();
+        //依次判断每笔交易是否发起成功，并根据结果分组。
+        for (BocB2eParam param : params) {
+            //获取交易对应的银行返回数据
+            final JSONObject payResp = payRespMap.get(param.getPayNo());
+            final JSONObject status = payResp.getJSONObject("status");
+            final String bankCode = status.getString("rspcod");
+            final TradingRecord record = param.getRecord();//对应的交易记录
+            record.setUpdateTime(new Date());
+            record.setBankState(bankCode);
+            //如果非B001的响应。修改数据状态，修改申请单状态，创建失败日志
+            if (!BocStatus.OK.code().equals(bankCode)) {
+                log.error("交易发起失败：{}",payResp.toJSONString());
+                final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(record);
+                tradingLog.setRespCode(bankCode);
+                tradingLog.setRespMsg(status.getString("rspmsg"));
+                tradingLog.setState(TradingLog.State.FAILED_PAYMENT);
+                record.setTradingLog(tradingLog);
+                record.setMessage(TradingRecord.Message.PAY_FAILED + status.toJSONString());
+                record.setBusinessState(TradingRecord.BusinessState.FAIL);
+                if (record.getType() == TradingRecord.Type.SINGLE) {
+                    failedSingleBill.add(PaymentBill.basicConvert(record,PaymentBill.ZFZT.FAIL,"银行未能正常处理提交的转账请求，银行端返回的描述信息:" + status.toJSONString()));
+                }else {
+                    failedMergeBill.add(PaymentBill.basicConvert(record,PaymentBill.ZFZT.FAIL,"银行未能正常处理提交的转账请求，银行端返回的描述信息:" + status.toJSONString()));
+                }
+                continue;
+            }
+            //交易提交成共状态，等待银行处理中
+            record.setBusinessState(TradingRecord.BusinessState.WAIT);
+            successRecord.add(param);
+            log.error("交易发起成功：{}",payResp.toJSONString());
+        }
+        //成功数据更新Mysql状态为支付中，失败更新为支付失败
+        final List<TradingRecord> tradingRecords = params.stream().map(item -> item.getRecord()).collect(Collectors.toList());
+        try {
+            recordService.updateBatchSelective(tradingRecords);//tradingRecords.stream().filter(item -> item.getTradingLog() != null).map(TradingRecord::getTradingLog).collect(Collectors.toList())
+        } catch (Exception e) {
+            throw new BusinessException("交易发起结果数据更新MySQL异常",e,BusinessException.Type.MYSQL_ERR);
+        }
+        try {
+            //失败申请单更新水果通数据库
+            paymentService.batchUpdateBySqdbhOrYppbh(failedSingleBill,failedMergeBill);
+        } catch (Exception e) {
+            throw new BusinessException("交易发起失败数据更新水果通异常",e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        return successRecord;
+    }
+
+
+
+
+    /**
+     * 校验并处理账户余额。返回余额充足可以支付的交易数据
+     * @param tradingRecords 未支付的全部交易数据
+     * @return 当前付款账号可以支付的交易数据
+     */
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public Set<TradingRecord> doBalance(List<TradingRecord> tradingRecords){
+        final HashSet<TradingRecord> success = new HashSet<>();
+        final HashSet<TradingRecord> failed = new HashSet<>();
+        final Map<String, List<TradingRecord>> checkParam = tradingRecords.stream().collect(Collectors.groupingBy(TradingRecord::getPayAcc));
+        final List<BocB2eParam> reqParam = checkParam.keySet().stream().map(item -> {
+            final BocB2eParam param = new BocB2eParam();
+            param.setPayAcc(item);
+            return param;
+        }).collect(Collectors.toList());
+        final JSONArray array;
+        try {
+            array = client.balanceEnquiry(reqParam);
+        } catch (BocB2eException e) {
+            //把交易数据状态还原
+            recordService.updateBatchSelectiveNoTransaction(tradingRecords);
+            throw new BusinessException("账户余额查询失败,银行接口未能正常处理请求；失败交易:" + tradingRecords.stream().map(TradingRecord::getPayNo).collect(Collectors.joining(",")) + " " + e.getMessage(),e,BusinessException.Type.API_CALL_FAIL);
+        }
+        //校验每个账号是否足够完成过本次全部交易
+        for (int i = 0; i < array.size(); i++) {
+            final String actacn = array.getJSONObject(i).getJSONObject("account").getString("actacn");
+            final JSONObject status = array.getJSONObject(i).getJSONObject("status");
+            if (BocStatus.OK.code().equals(status.getString("rspcod"))){
+                final JSONObject balanceInfo = array.getJSONObject(i).getJSONObject("balance");
+                //frzamt=冻结金额；stpamt=圈存金额
+                String bokbal = balanceInfo.getString("bokbal");//账面当前余额
+                String avabal = balanceInfo.getString("avabal");//有效金额
+                final List<TradingRecord> records = checkParam.get(actacn);
+                BigDecimal count = BigDecimal.ZERO;
+                for (TradingRecord record : records) {
+                    count = count.add(record.getPayAmount());
+                }
+                log.info("账户:{} 账面余额:{} 有效金额:{} 本次支付{}笔交易，合计金额:{}",actacn,bokbal,avabal,checkParam.get(actacn).size(),count.toString());
+                if (count.compareTo(new BigDecimal(avabal)) < 0) {
+                    success.addAll(checkParam.get(actacn));
+                }else {
+                    String message = "时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "账号:" + actacn + "可用余额:" + avabal + " 本次" + checkParam.get(actacn).size() + "笔交易合计金额" + count.toString() + "不足以完成支付(多笔合并支付的申请单为一笔交易)";
+                    checkParam.get(actacn).forEach(item -> resetToBalanceFailed(message,status,item,TradingLog.State.BALANCE_INSUFFICIENT,TradingRecord.BusinessState.BALANCE_INSUFFICIENT));
+                    failed.addAll(checkParam.get(actacn));
+                }
+            }else if (BocStatus.UNKNOWN_ACCOUNT.code().equals(status.getString("rspcod"))){
+                String message = "账号:" + actacn + "无法通过银企平台发起交易需提前在网银系统维护,银行端返回的描述信息:" + status.toJSONString();
+                checkParam.get(actacn).forEach(item -> resetToBalanceFailed(message,status,item,TradingLog.State.FAILED_PAYMENT,TradingRecord.BusinessState.FAIL));
+                failed.addAll(checkParam.get(actacn));
+                log.error(message);
+            }else {
+                String message = "账号:" + actacn + "查询余额时银行返回了未知的处理结果,无法进行下一步支付操作,银行端返回的描述信息:" + status.toJSONString();
+                checkParam.get(actacn).forEach(item -> resetToBalanceFailed(message,status,item,TradingLog.State.FAILED_PAYMENT,TradingRecord.BusinessState.FAIL));
+                failed.addAll(checkParam.get(actacn));
+            }
+        }
+        log.info("余额校验结束{}笔交易余额检验处理失败 列表：{}",failed.size(),JSON.toJSONString(failed));
+        //余额校验操作过程中的失败数据，更新数据状态并记录日志
+        update(failed);
+        return success;
+    }
+
+
+
+    /**
+     * 推送消息到队列
+     * @param msgType 要推送的消息类型
+     */
+    @Override
+    public Map<String,Object> resendMesg(List<String> msgType) {
+        List<TradingRecord> sendToQueryState = null;
+        if (msgType.contains(PayController.STATE_QUEUE)) {
+            sendToQueryState = recordService.selectByBusinessState(Arrays.asList(1));
+            if (!sendToQueryState.isEmpty()){
+                //清空队列
+                sender.purge(RabbitConfig.QU_PAYMENT_STATUS);
+                for (TradingRecord record : sendToQueryState) {
+                    record.getMateData().reset();
+                    sender.sendPaymentStateQueue(record);
+                }
+            }
+            log.info("查询交易状态为1的交易数据{}条,发送到状态查询队列成功 列表:{}", sendToQueryState.size(),JSON.toJSONString(sendToQueryState));
+        }
+        List<TradingRecord> sendToBillInfoQueue = null;
+        if (msgType.contains(PayController.BILL_CREAT)){
+            sendToBillInfoQueue = recordService.selectByBillState(Arrays.asList(0,3));
+           if (!sendToBillInfoQueue.isEmpty()){
+               sender.purge(RabbitConfig.QU_BILL_INFO);
+               for (TradingRecord record : sendToBillInfoQueue) {
+                   record.getMateData().reset();
+                   sender.sendBillInfoQueue(record,10);
+               }
+           }
+            log.info("查询回单状态为0,3的交易数据{}条,发送到状态查询队列成功 列表:{}", sendToBillInfoQueue.size(), JSON.toJSONString(sendToBillInfoQueue));
+        }
+        final HashMap<String, Object> data = new HashMap<>();
+        data.put("record",sendToQueryState);
+        data.put("billRecord",sendToBillInfoQueue);
+        return data;
+    }
+
+
+    /**
+     * 抓取交易历史记录
+     * @param accounts 账号
+     * @param date 日期
+     */
+    @Override
+    public void captureHistorical(Set<String> accounts,Date date) {
+        final List<BocB2eParam> reqParam = accounts.stream().map(item -> {
+            final BocB2eParam param = new BocB2eParam();
+            param.setPayAcc(item);
+            param.setHistoricalType(BocB2eParam.HistoricalType.HISTORICAL);
+            param.setHistoricalDirection("0");
+            param.setHistoricalDateFrom(date);
+            param.setHistoricalDateTo(date);
+            return param;
+        }).collect(Collectors.toList());
+        int totalNum = 0,insertNum = 0;
+        for (BocB2eParam param : reqParam) {
+            int accNum = 0;
+            try {
+                do {
+                    final JSONArray accountResult = client.queryHistorical(param);
+                    totalNum += accountResult.size();
+                    accNum += accountResult.size();
+                    for (int i = 0; i < accountResult.size(); i++) {
+                        final TradingHistory tradingHistory = JSON.toJavaObject(accountResult.getJSONObject(i), TradingHistory.class);
+                        resetEmptyFieldOfTradingHistory(tradingHistory);
+                        insertNum += historyMapper.insertSelective(tradingHistory);
+                    }
+                }while (param.isHistoricalHasNext());
+            } catch (Exception e) {
+                final TradingLog tradingLog = new TradingLog();
+                tradingLog.setState(TradingLog.State.API_CALL_FALL);
+                tradingLog.setMesage("账号:" + param.getPayAcc() + "查询交易记录异常" + e.getMessage());
+                tradingLog.setApiNo("交易历史查询");
+                tradingLog.setCreatTime(new Date());
+                recordService.logInsert(tradingLog);
+                log.error("账号:{}查询交易记录异常",param.getPayAcc(),e);
+            }
+            log.info("账号:{}本次查询到{}记录",param.getPayAcc(),accNum);
+        }
+        log.info("交易历史记录查询到{}条数据，新增数据库{}条",totalNum,insertNum);
+    }
+
+
+    /**
+     * 清理交易的回单文件
+     */
+    @Override
+    public void clearBillInfoPdf(TradingRecord records) {
+        final String pdfPath = findBankBill(records);
+        if (StringUtils.isEmpty(pdfPath)) {
+            log.warn("交易:{} 预匹配编号/申请单编号:{} 类型:{} 未能查询到水果通系统有银行电子回单文件",records.getPayNo(),records.getPayBillNo(),records.getType());
+            return;
+        }
+        final TradingBillRecord billRecord = records.getBillRecord();
+        //删除系统回单
+        final String billInfoPdfPath = billRecord.getPath();
+        ftpClient.delete(billInfoPdfPath);
+        billRecord.setPath("deleted");
+        billRecord.setState(TradingBillRecord.State.RPA_BILL_DELETED);
+        billRecord.setUpdateTime(new Date());
+        final PaymentBill paymentBill = new PaymentBill();
+        paymentBill.setZfhd("deleted");
+        if (records.getType() == TradingRecord.Type.MERGE) {
+            paymentBill.setYppbhValue(records.getPayBillNo());
+            paymentService.updateByYppbhValue(paymentBill);
+        }else {
+            paymentBill.setSqdbh(records.getPayBillNo());
+            paymentService.updateBySqdbh(paymentBill);
+        }
+        billRecordMapper.updateByPayNoSelective(billRecord);
+        log.info("交易:{} 预匹配编号/申请单编号:{} 类型:{} 系统回单文件:{} 删除成功",records.getPayNo(),records.getPayBillNo(),records.getType(),billInfoPdfPath);
+    }
+
+
+    /**
+     * 交易对应的银行回单
+     */
+    private String findBankBill(TradingRecord records){
+        String sqdbh = null;
+        if (TradingRecord.Type.SINGLE == records.getType()) {
+            sqdbh = records.getPayBillNo();
+        }else {
+            sqdbh = paymentService.selectByYppbhValue(records.getPayBillNo()).get(0).getSqdbh();
+        }
+        final PaymentBill bankBill = paymentService.findBankBillBySqdbh(sqdbh);
+        if (bankBill == null) {
+            return null;
+        }
+        return bankBill.getReceiptAddress() + bankBill.getReceiptSysName();
+    }
+
+
+
+
+
+    private void resetEmptyFieldOfTradingHistory(Object obj) {
+        if (obj == null) {
+            return;
+        }
+        final Class objClass = obj.getClass();
+        final Field[] fields = objClass.getDeclaredFields();
+        for (Field field : fields) {
+            final Class<?> type = field.getType();
+            String name = field.getName();
+            final char[] chars = name.toCharArray();
+            chars[0] -= 32;
+            name = String.valueOf(chars);
+            try {
+                final Method get = objClass.getMethod("get" + name);
+                if (type.equals(String.class)) {
+                    final String invoke = (String)get.invoke(obj);
+                    if (invoke.equals("[]")) {
+                        objClass.getMethod("set" + name,String.class).invoke(obj,"");
+                    }
+                }else if(type.equals(TradingHistory.PayeeInfo.class) || type.equals(TradingHistory.PayInfo.class)) {
+                    resetEmptyFieldOfTradingHistory(get.invoke(obj));
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+    /**
+     * 交易数据对象置为余额查询失败状态
+     */
+    private void resetToBalanceFailed(String message, JSONObject status, TradingRecord tradingRecord,Integer logState,Integer recordState) {
+        tradingRecord.setBusinessState(recordState);
+        tradingRecord.setBankState(status.getString("rspcod"));
+        tradingRecord.setMessage(message);
+        final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(tradingRecord);
+        tradingLog.setRespCode(status.getString("rspcod"));
+        tradingLog.setRespMsg(status.getString("rspmsg"));
+        tradingLog.setApiNo("余额校验");
+        tradingLog.setMesage(message);
+        tradingLog.setState(logState);
+        tradingRecord.setTradingLog(tradingLog);
+    }
+
+
+    /**
+     * 余额校验不足的数据做失败处理，更新水果通和MySQL
+     * @param tradingRecords 余额校验不足无法完成支付的数据
+     */
+    private void update(Set<TradingRecord> tradingRecords){
+        if (tradingRecords.isEmpty()) {return;}
+        //区分合并支付和非合并支付数据
+        final Map<Integer, List<TradingRecord>> collect = tradingRecords.stream().collect(Collectors.groupingBy(TradingRecord::getType));
+        final List<PaymentBill> billSingle = Optional.ofNullable(collect.get(TradingRecord.Type.SINGLE)).orElse(Collections.emptyList()).stream().map(item -> {
+            final PaymentBill bill = new PaymentBill();
+            bill.setSqdbh(item.getPayBillNo());
+            bill.setZfzt(PaymentBill.ZFZT.FAIL);
+            bill.setZfsbyy(item.getMessage());
+            return bill;
+        }).collect(Collectors.toList());
+        final List<PaymentBill> billMerge = Optional.ofNullable(collect.get(TradingRecord.Type.MERGE)).orElse(Collections.emptyList()).stream().map(item -> {
+            final PaymentBill bill = new PaymentBill();
+            bill.setYppbhValue(item.getPayBillNo());
+            bill.setZfzt(PaymentBill.ZFZT.FAIL);
+            bill.setZfsbyy(item.getMessage());
+            return bill;
+        }).collect(Collectors.toList());
+        try {
+            paymentService.batchUpdateBySqdbhOrYppbh(billSingle,billMerge);
+        } catch (Exception e) {
+            throw new BusinessException(e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        try {
+            //更新数据并记录日志。
+            recordService.updateBatchSelective(new ArrayList<>(tradingRecords));
+        } catch (Exception e) {
+            throw new BusinessException(e,BusinessException.Type.MYSQL_ERR);
+        }
+    }
+
+
+
+
+    /**
+     *  查询交易对应的状态，如果成功和失败则更新数据库状态。
+     */
+    @Override
+    public boolean doPayState(TradingRecord record) {
+        final JSONObject status = queryPayStats(record);
+        log.info("交易{}业务处理状态:{}",record.getPayNo(),status.getString("rspcod") + "=" + status.getString("rspmsg"));
+        return resultOperate(record, status);
+    }
+
+
+    /**
+     * 处理交易状态查询结果：
+     *  1. 交易成功：更细Mysql交易数据为成功状态；更新SqlServer申请单状态为支付成功，更新支付时间、更新实际支付日期；向日志表写入成功日志；向回单记录表初始不啊一条记录；投递回单生成消息返回false
+     *  2. 交易失败：更细Mysql交易数据为失败状态；更新SqlServer申请单状态为支付失败，记录失败原因；向日志表写入成功日志。返回fasle
+     *  3.交易处理中：返回true（需要将消息重新投递到队列）
+     *  4. 未知的响应状态码，做失败处理。返回false
+     * @param record 交易数据
+     * @param status 交易处理状态
+     * @return 是否需要投递到状态查询队列
+     */
+    private boolean resultOperate(TradingRecord record, JSONObject status) {
+        final String rspcod = status.getString("rspcod");
+        if (BocStatus.OK.code().equals(rspcod)) {
+            //交易成功处理
+            log.info("状态查询完成,付款行处理成功 交易:{}", record.getPayNo());
+            doPaymentSuccess(record, status, rspcod);
+            return false;
+        }else if (BocStatus.SUBMIT.code().equals(rspcod) || BocStatus.WAIT_BANK.code().equals(rspcod)){
+            log.info("状态查询完成,付款行处理中 交易:{}", record.getPayNo());
+            return true;
+        } else if (BocStatus.FAILED_BANK.code().equals(rspcod)){
+            //交易失败
+            log.info("状态查询完成,付款行处理失败 交易:{}", record.getPayNo());
+            doPaymentFailed(record, status, "银行返回了失败的处理结果:" + status.toJSONString());
+            return false;
+        }else {
+            //未知的状态
+            log.info("状态查询完成,付款行返回了未知的处理状态:{} 交易:{}", status.toJSONString(), record.getPayNo());
+            doPaymentFailed(record, status, "银行返回了未知的处理结果:" + status.toJSONString());
+            return false;
+        }
+    }
+
+
+
+    private JSONObject queryPayStats(TradingRecord record) {
+        final BocB2eParam queryReq = BocB2eParam.getInstance(record);
+        final JSONObject status;
+        try {
+            status = client.queryPayState(Collections.singletonList(queryReq)).getJSONObject(0).getJSONObject("status");
+        } catch (Exception e) {
+            throw new BusinessException(e.getMessage(),e,BusinessException.Type.API_CALL_FAIL);
+        }
+        return status;
+    }
+
+
+
+    /**
+     * 交易失败：更细Mysql交易数据为失败状态；更新SqlServer申请单状态为支付失败，记录失败原因；向日志表写入成功日志。返回fasle
+     */
+    private void doPaymentFailed(TradingRecord record, JSONObject status, String zfsbyy) {
+        //MySQL交易数据设置为失败状态
+        record.setBusinessState(TradingRecord.BusinessState.FAIL);
+        record.setMessage(status.toJSONString());
+        record.setBankState(status.getString("rspcod"));
+        record.setUpdateTime(new Date());
+        //记录失败日志
+        final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(record);
+        tradingLog.setRespCode(status.getString("rspcod"));
+        tradingLog.setRespMsg(status.getString("rspmsg"));
+        tradingLog.setState(TradingLog.State.FAILED_BANK);
+        tradingLog.setApiNo("交易状态查询");
+        record.setTradingLog(tradingLog);
+        //修改SqlServer申请单为失败支付状态
+        final PaymentBill paymentBill = new PaymentBill();
+        paymentBill.setZfzt(PaymentBill.ZFZT.FAIL);
+        paymentBill.setZfsbyy(zfsbyy);
+        //更新MySQL同时插入日志
+        recordService.updateByPrimaryKeySelective(record);
+        try {
+            //更新水果通。
+            if (record.getType() == TradingRecord.Type.SINGLE) {
+                paymentBill.setSqdbh(record.getPayBillNo());
+                paymentService.batchUpdateBySqdbh(Arrays.asList(paymentBill));
+            }else{
+                paymentBill.setYppbhValue(record.getPayBillNo());
+                paymentService.batchUpdateByYppbhValue(Arrays.asList(paymentBill));
+            }
+        } catch (Exception e) {
+            throw new BusinessException(e,BusinessException.Type.SQLSERVER_ERR);
+        }
+    }
+
+
+
+    /**
+     * 交易成功：更细Mysql交易数据为成功状态；更新SqlServer申请单状态为支付成功，更新支付时间、更新实际支付日期；向日志表写入成功日志；向回单记录表初始不啊一条记录；投递回单生成消息
+     */
+    private void doPaymentSuccess(TradingRecord record, JSONObject status, String rspcod) {
+        record.setBusinessState(TradingRecord.BusinessState.SUCCEED);
+        record.setUpdateTime(new Date());
+        final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(record);
+        tradingLog.setRespCode(rspcod);
+        tradingLog.setRespMsg(status.getString("rspmsg"));
+        tradingLog.setState(TradingLog.State.SUCCESS_BANK);
+        tradingLog.setApiNo("交易状态查询");
+        record.setTradingLog(tradingLog);
+        //初始化回单记录
+        final TradingBillRecord billRecord = TradingBillRecord.getInstance(record);
+        billRecord.setState(TradingBillRecord.State.INIT);
+        billRecord.setCreatTime(new Date());
+        billRecord.setUpdateTime(new Date());
+        //跟新SqlServer相关字段
+        final PaymentBill paymentBill = new PaymentBill();
+        paymentBill.setZfsj(new Date());
+        paymentBill.setSjzfrq(new Date());
+        paymentBill.setZfzt(PaymentBill.ZFZT.SUCCEED);
+        //更新MySQL同时插入日志
+        recordService.updateByPrimaryKeySelective(record);
+        //初始化一条回单操作记录
+        recordService.insertBillRecord(billRecord);
+        try {
+            //更新水果通，首先需要判断当前交易是否为单笔支付。
+            if (record.getType() == TradingRecord.Type.SINGLE) {
+                paymentBill.setSqdbh(record.getPayBillNo());
+                paymentService.updateBySqdbh(paymentBill);
+            }else{
+                paymentBill.setYppbhValue(record.getPayBillNo());
+                paymentService.updateByYppbhValue(paymentBill);
+            }
+        } catch (Exception e) {
+            throw new BusinessException(e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        //投递回单生成消息。
+        record.getMateData().reset();
+        sender.sendBillInfoQueue(record,30);
+        log.info("交易{}银行处理成功，投递到回单生成处理队列。30后开始排队等待处理交易回单",record.getPayNo());
+    }
+
+
+    /**
+     * 生成交易回单
+     * */
+    public void doBankBill(TradingRecord record){
+        final BocB2eParam param = BocB2eParam.getInstance(record);
+        JSONObject pdfParam = getBillByPayNo(param);
+        final byte[] bytes = exportBillToPdf(pdfParam);
+        String fileName = record.getScheduleDate() + "+" + param.getPayAcc() + "+" + param.getBillSerialNo();
+        String filePath = UPLOAD_DIR + fileName + ".pdf";
+        try {
+            ftpClient.upload(bytes,filePath);
+            log.info("交易:{} 系统回单上传成功,文件路径:{}",param.getPayNo(),filePath);
+        } catch (Exception e) {
+            log.error("交易:{} 系统回单上传失败,预期的文件路径:{}",param.getPayNo(),filePath);
+            throw new BusinessException("系统回单上传失败", e, BusinessException.Type.BANK_BILL_FAIL);
+        }
+        //更新水果通和Mysql数据状态
+        final PaymentBill paymentBill = new PaymentBill();
+        paymentBill.setZfhd(filePath);
+        try {
+            if (TradingRecord.Type.SINGLE == record.getType()) {
+                paymentBill.setSqdbh(record.getPayBillNo());
+                paymentService.batchUpdateBySqdbh(Collections.singletonList(paymentBill));
+            }else {
+                paymentBill.setYppbhValue(record.getPayBillNo());
+                paymentService.batchUpdateByYppbhValue(Collections.singletonList(paymentBill));
+            }
+        } catch (Exception e) {
+            throw new BusinessException("回单上传生成更新水果通时异常",e,BusinessException.Type.SQLSERVER_ERR);
+        }
+        //更新回单状态,写入一条日志
+        final TradingBillRecord billRecord = TradingBillRecord.getInstance(record);
+        billRecord.setPath(filePath);
+        billRecord.setName(fileName);
+        billRecord.setUpdateTime(new Date());
+        billRecord.setState(TradingBillRecord.State.SUCCESS);
+        recordService.updateBillRecord(billRecord);
+        final TradingLog tradingLog = TradingLog.getInstanceByTradingRecord(record);
+        tradingLog.setApiNo("回单处理");
+        tradingLog.setState(TradingLog.State.SUCCESS_BILL);
+        tradingLog.setMesage("上传回单成功");
+        recordService.logInsert(tradingLog);
+    }
+
+
+
+    @Override
+    @SneakyThrows
+    public byte[] exportBillToPdf(JSONObject pdfParam) {
+        try(InputStream inputStream = BOCClientImpl.class.getClassLoader().getResourceAsStream("report/templates/" + pdfParam.get("receiptmould") + ".jasper")){
+            pdfParam.put("boczhang",imageBase64("report/images/zhang.png"));
+            pdfParam.put("boclogo",imageBase64("report/images/boclog.png"));
+            JasperPrint jasperPrint = JasperFillManager.fillReport(inputStream, pdfParam, new JREmptyDataSource());
+            return JasperExportManager.exportReportToPdf(jasperPrint);
+        }
+    }
+
+
+
+
+    private JSONObject getBillByPayNo(BocB2eParam param) {
+        do {
+            JSONArray billResp = client.queryBill(param);
+            for (int a = 0; a < billResp.size(); a++) {
+                JSONObject bill = billResp.getJSONObject(a);
+                //回单名称为客户付费回单时，说明改回但信息是转账汇款手续费回单。此时回单信息了不包含”cusref“字段。
+                if (bill.getString("receiptname").contains("客户付费回单")){
+                    continue;
+                }
+                if (bill.getString("cusref").contains(param.getPayNo())) {
+                    log.info("客户业务编号：{} 对应的回单模板：{} 回单信息：{}", param.getPayNo(), bill.getString("receiptmould"), bill.toJSONString());
+                    if (!TEMPLATE_ID.contains(bill.get("receiptmould"))) {
+                        throw new BusinessException("系统中不存在的回单模板 id：" + bill.get("receiptmould"),BusinessException.Type.UNKNOWN_TEMPLATE);
+                    }
+                    param.setBillSerialNo(bill.getString("receiptjrnlno").replaceAll("-",""));
+                    return bill;
+                }
+            }
+        }while (param.isBillHasNext());
+        throw new BusinessException("没有找到交易" + param.getPayNo() + "对应的回单信息",BusinessException.Type.NO_SUCH_BILL_INFO);
+    }
+
+
+
+    private String imageBase64(String fileName) throws IOException {
+        byte[] imag = new byte[1024 * 8];
+        int len = -1;
+        try(ByteArrayOutputStream out = new ByteArrayOutputStream();
+            InputStream in = BOCClientImpl.class.getClassLoader().getResourceAsStream(fileName);) {
+            if (in == null)return null;
+            while ((len = in.read(imag)) != -1) {
+                out.write(imag,0,len);
+            }
+            byte[] encode = java.util.Base64.getEncoder().encode(out.toByteArray());
+            return new String(encode);
+        } catch (IOException e) {
+            throw e;
+        }
+    }
+}
Index: src/main/java/com/novaone/service/impl/PaymentBillServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/PaymentBillServiceImpl.java	(不存在的)
+++ src/main/java/com/novaone/service/impl/PaymentBillServiceImpl.java	(版本 31412)
@@ -0,0 +1,97 @@
+package com.novaone.service.impl;
+
+import com.novaone.entity.PaymentBill;
+import com.novaone.fdao.PaymentBillMapper;
+import com.novaone.service.PaymentBillService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * @program: BankEnterprise
+ * @description:
+ * @author: Ma.ChengJian
+ * @create: 2022-06-08 09:40
+ */
+@Service
+@Slf4j
+@Transactional(transactionManager = "fruiteaseTransactionManager")
+public class PaymentBillServiceImpl implements PaymentBillService {
+
+    @Resource
+    private PaymentBillMapper paymentBillMapper;
+
+
+    /**
+     * 读取SQL Server中审核确认RPA支付且未被PRA处理的申请单数据”
+     * @return 待RPA支付的付款申请单列表
+     */
+    @Override
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public synchronized List<PaymentBill> selectUnpaid() {
+        final List<PaymentBill> paymentBills = paymentBillMapper.selectUnpaid();
+        if (!paymentBills.isEmpty()) {
+            paymentBills.forEach(item -> item.setZfzt(PaymentBill.ZFZT.WAIT));
+            paymentBillMapper.batchUpdateBySqdbh(paymentBills);
+        }
+        return paymentBills;
+    }
+
+    @Override
+    public int insert(PaymentBill bill) {
+        return paymentBillMapper.insert(bill);
+    }
+
+    /**
+     * 批量根据申请单编号或者预匹配编号更新数据
+     * @param singleList 单笔支付申请单列表，通过申请单编号更新
+     * @param mergeList 合并支付申请单列表，通过yppbhValue更新。
+     */
+    @Override
+    public int batchUpdateBySqdbhOrYppbh(List<PaymentBill> singleList, List<PaymentBill> mergeList) {
+        int i = this.batchUpdateBySqdbh(singleList);
+        i += this.batchUpdateByYppbhValue(mergeList);
+        return i;
+    }
+
+    @Override
+    public int batchUpdateBySqdbh(List<PaymentBill> bills) {
+        if (bills == null || bills.isEmpty()) {
+            return 0;
+        }
+        return paymentBillMapper.batchUpdateBySqdbh(bills);
+    }
+
+    @Override
+    public int updateBySqdbh(PaymentBill bill) {
+        return batchUpdateBySqdbh(Collections.singletonList(bill));
+    }
+
+    @Override
+    public int updateByYppbhValue(PaymentBill bill) {
+        return batchUpdateByYppbhValue(Collections.singletonList(bill));
+    }
+
+    @Override
+    public int batchUpdateByYppbhValue(List<PaymentBill> bills) {
+        if (bills == null || bills.isEmpty()) {
+            return 0;
+        }
+        return paymentBillMapper.batchUpdateByYppbhValue(bills);
+    }
+
+    @Override
+    public List<PaymentBill> selectByYppbhValue(String yppbhValue) {
+        return paymentBillMapper.selectByYppbhValue(yppbhValue);
+    }
+
+    @Override
+    public PaymentBill findBankBillBySqdbh(String sqdbh) {
+        return paymentBillMapper.findBankBillBySqdbh(sqdbh);
+    }
+}
Index: src/main/java/com/novaone/service/impl/TradingRecordServiceImpl.java
===================================================================
--- src/main/java/com/novaone/service/impl/TradingRecordServiceImpl.java	(不存在的)
+++ src/main/java/com/novaone/service/impl/TradingRecordServiceImpl.java	(版本 31412)
@@ -0,0 +1,188 @@
+package com.novaone.service.impl;
+
+import com.novaone.dao.TradingBillRecordMapper;
+import com.novaone.dao.TradingLogMapper;
+import com.novaone.entity.TradingBillRecord;
+import com.novaone.entity.TradingLog;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.novaone.dao.TradingRecordMapper;
+import com.novaone.entity.TradingRecord;
+import com.novaone.service.TradingRecordService;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@Slf4j
+@Transactional
+public class TradingRecordServiceImpl implements TradingRecordService{
+
+    @Resource
+    private TradingRecordMapper tradingRecordMapper;
+    @Resource
+    private TradingBillRecordMapper billRecordMapper;
+    @Resource
+    private TradingLogMapper logMapper;
+
+    @Override
+    public int deleteByPrimaryKey(Long id) {
+        return tradingRecordMapper.deleteByPrimaryKey(id);
+    }
+
+    @Override
+    public int insert(TradingRecord record) {
+        return tradingRecordMapper.insert(record);
+    }
+
+    @Override
+    public int insertOrUpdate(TradingRecord record) {
+        return tradingRecordMapper.insertOrUpdate(record);
+    }
+
+    @Override
+    public int insertOrUpdateSelective(TradingRecord record) {
+        return tradingRecordMapper.insertOrUpdateSelective(record);
+    }
+
+    @Override
+    public int insertSelective(TradingRecord record) {
+        return tradingRecordMapper.insertSelective(record);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public TradingRecord selectByPrimaryKey(Long id) {
+        return tradingRecordMapper.selectByPrimaryKey(id);
+    }
+
+
+
+    public int countUnpaid(){
+        return tradingRecordMapper.countByBusinessState(Arrays.asList(TradingRecord.BusinessState.INIT,TradingRecord.BusinessState.REQUEST_ERR));
+    }
+
+
+
+    @Override
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public List<TradingRecord> selectUnpaid() {
+        final List<TradingRecord> tradingRecords = tradingRecordMapper.selectByBusinessState(Arrays.asList(TradingRecord.BusinessState.INIT,TradingRecord.BusinessState.REQUEST_ERR));
+        if (tradingRecords == null || tradingRecords.isEmpty()){
+            return Collections.emptyList();
+        }
+        final List<TradingRecord> locks = tradingRecords.stream().map(item -> {
+            final TradingRecord record = new TradingRecord();
+            record.setId(item.getId());
+            record.setBusinessState(TradingRecord.BusinessState.LOCK);
+            record.setUpdateTime(new Date());
+            return record;
+        }).collect(Collectors.toList());
+        tradingRecordMapper.updateBatchSelective(locks);
+        return tradingRecords;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<TradingRecord> selectByBusinessState(List<Integer> status) {
+        if (status == null || status.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return tradingRecordMapper.selectByBusinessState(status);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<TradingRecord> selectByBillState(List<Integer> status) {
+        if (status == null || status.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return tradingRecordMapper.selectByBillState(status);
+    }
+
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<TradingRecord> selectByScheduleDate(Date scheduleDate) {
+        return tradingRecordMapper.selectByScheduleDate(scheduleDate);
+    }
+
+    @Override
+    public int updateByPrimaryKeySelective(TradingRecord record) {
+        final int i = tradingRecordMapper.updateByPrimaryKeySelective(record);
+        if (record.getTradingLog() != null) {
+            logMapper.insert(record.getTradingLog());
+            record.setTradingLog(null);
+        }
+        return i;
+    }
+
+    @Override
+    public int updateByPrimaryKey(TradingRecord record) {
+        return tradingRecordMapper.updateByPrimaryKey(record);
+    }
+
+    @Override
+    public int updateBatch(List<TradingRecord> list) {
+        return tradingRecordMapper.updateBatch(list);
+    }
+
+    @Override
+    public int updateBatchSelective(List<TradingRecord> list) {
+        return updateBatchSelectivePrivate(list);
+    }
+
+
+    @Override
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public int updateBatchSelectiveNoTransaction(List<TradingRecord> list) {
+        return updateBatchSelectivePrivate(list);
+    }
+
+    private int updateBatchSelectivePrivate(List<TradingRecord> list) {
+        int i = tradingRecordMapper.updateBatchSelective(list);
+        final List<TradingLog> logs = list.stream().filter(item -> item.getTradingLog() != null).map(TradingRecord::getTradingLog).collect(Collectors.toList());
+        if (logs.isEmpty()) {
+            return i;
+        }
+        logMapper.batchInsert(logs);
+        list.forEach(item->item.setTradingLog(null));
+        return i;
+    }
+
+    @Override
+    public int batchInsert(List<TradingRecord> list) {
+        if (list == null || list.isEmpty()) {
+            log.warn("batchInsert(List<TradingRecord> list) list is Empty");
+            return 0;
+        }
+        return tradingRecordMapper.batchInsert(list);
+    }
+
+    @Override
+    public int logBatchInsert(List<TradingLog> logs) {
+        if (logs == null || logs.isEmpty()) {
+            return 0;
+        }
+        return logMapper.batchInsert(logs);
+    }
+
+    @Override
+    public int logInsert(TradingLog log) {
+        return logMapper.insert(log);
+    }
+
+
+    @Override
+    public int insertBillRecord(TradingBillRecord bill) {
+        return billRecordMapper.insert(bill);
+    }
+
+    @Override
+    public int updateBillRecord(TradingBillRecord bill) {
+        return billRecordMapper.updateByPayNoSelective(bill);
+    }
+}
Index: src/main/resources/application.yml
===================================================================
--- src/main/resources/application.yml	(版本 31411)
+++ src/main/resources/application.yml	(版本 31412)
@@ -1,43 +1,88 @@
 spring:
   datasource:
-#    url: jdbc:mysql://rm-bp13i26r6189ex0l2o.mysql.rds.aliyuncs.com:3306/bank_enterprise?useUnicode=true&characterEncoding=utf8
-#    username: nova123
-#    password: novax0l2#
-    url: jdbc:mysql://rm-bp1d94x90a6ww204o.mysql.rds.aliyuncs.com:3306/bank_enterprise?useUnicode=true&characterEncoding=utf8
-    username: nova123
-    password: NovaFreshPort2017
-    type: com.alibaba.druid.pool.DruidDataSource
-    driver-class-name: com.mysql.jdbc.Driver
-    minIdle: 2
-    initialSize: 2
-    maxActive: 200
-    maxWait: 60000
-    timeBetweenEvictionRunsMillis: 60000
-    minEvictableIdleTimeMillis: 300000
-    validationQuery: SELECT 1 FROM DUAL
-    testWhileIdle: true
-    testOnBorrow: false
-    testOnReturn: false
-    poolPreparedStatements: true
-    maxPoolPreparedStatementPerConnectionSize: 20
-    filters: stat,wall,log4j
-    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+    druid:
+      master:
+#        url: jdbc:mysql://rm-bp13i26r6189ex0l2o.mysql.rds.aliyuncs.com:3306/bank_enterprise?useUnicode=true&characterEncoding=utf8
+#        username: nova123
+#        password: novax0l2#
+#        # ↓ ↓ ↓ ↓ ↓ ↓ 正式环境 ↓ ↓ ↓ ↓ ↓ ↓
+#        url: jdbc:mysql://rm-bp1d94x90a6ww204o.mysql.rds.aliyuncs.com:3306/bank_enterprise?useUnicode=true&characterEncoding=utf8
+#        username: nova123
+#        password: NovaFreshPort2017
+        # 本地环境
+        url: jdbc:mysql://127.0.0.1:3306/bank_enterprise?useUnicode=true&characterEncoding=utf8&useSSL=false
+        username: root
+        password: '0116'
+        type: com.alibaba.druid.pool.DruidDataSource
+        driver-class-name: com.mysql.jdbc.Driver
+        minIdle: 2
+        initialSize: 2
+        maxActive: 200
+        maxWait: 60000
+        timeBetweenEvictionRunsMillis: 60000
+        minEvictableIdleTimeMillis: 300000
+        validationQuery: SELECT 1 FROM DUAL
+        testWhileIdle: true
+        testOnBorrow: false
+        testOnReturn: false
+        poolPreparedStatements: true
+        maxPoolPreparedStatementPerConnectionSize: 20
+        filters: stat,wall,log4j
+        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+      slave:
+        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+#        url: jdbc:sqlserver://47.97.182.194:1433;DatabaseName=zjnb
+        url: jdbc:sqlserver://rm-bp107kof940zw01k6qo.sqlserver.rds.aliyuncs.com;DatabaseName=zjnb_rq #测试环境
+        username: oheng
+        password: Hongshen_110
+        validation-query: SELECT 1
+        min-idle: 5
+        max-active: 20
+        max-wait: 60000
+        time-between-eviction-runs-millis: 60000
+        min-evictable-idle-time-millis: 300000
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+        pool-prepared-statements: true
+        max-pool-prepared-statement-per-connection-size: 20
+        use-global-data-source-stat: true
+        connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
   mvc:
     view:
       prefix: /WEB-INF/jsp/
       suffix: .jsp
+
   redis:
-#    database: 4
-    database: 4
-    host: 47.96.73.216
-    port: 6379
-    password: nova123
-    pool:
-      max-active: 8
-      max-wait: -1
-      max-idle: 8
-      min-idle: 0
-    timeout: 0
+    database: 0
+    host: 152.136.218.68
+    port: 6380
+    password: "0116"
+#    host: 47.96.73.216
+#    port: 6379
+#    password: nova123
+#    pool:
+#      max-active: 8
+#      max-wait: -1
+#      max-idle: 8
+#      min-idle: 0
+#    timeout: 0
+
+  rabbitmq:
+    host: 127.0.0.1 # ip
+    port: 5672
+    username: guest
+    password: guest
+    virtual-host: payment
+#    publisher-confirms: true
+#    publisher-returns: true
+    listener:
+      prefetch: 1
+#    listener:
+#      simple:
+#        acknowledge-mode: manual #开启手动Ack
+
+
 server:
   port: 9001
   tomcat:
@@ -45,6 +90,7 @@
 #  port: 9003
 logging:
   config: classpath:logback.xml
+
 audience:
   client-id: 098f6bcd4621d373cade4e832627b4f6
   base64-secret: MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
@@ -53,4 +99,10 @@
 security:
   mode: 2
 intpla:
-  server: http://intpla.freshport.com
\ No newline at end of file
+  server: http://intpla.freshport.com
+
+bankbillftp:
+  host: 120.55.41.247
+  port: 22
+  username: root
+  password: Nova247#
Index: src/main/resources/jasperreports_extension.properties
===================================================================
--- src/main/resources/jasperreports_extension.properties	(不存在的)
+++ src/main/resources/jasperreports_extension.properties	(版本 31412)
@@ -0,0 +1,2 @@
+net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
+net.sf.jasperreports.extension.simple.font.families.lobstertwo=report/stsong/fonts.xml
\ No newline at end of file
Index: src/main/resources/report/images/boclog.png
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/images/boclog.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/images/zhang.png
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/images/zhang.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/stsong/fonts.xml
===================================================================
--- src/main/resources/report/stsong/fonts.xml	(不存在的)
+++ src/main/resources/report/stsong/fonts.xml	(版本 31412)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fontFamilies>
+    <fontFamily name="华文宋体">
+        <normal>report/stsong/stsong.ttf</normal>
+        <bold>report/stsong/SimSunBold.ttf</bold>
+        <italic>report/stsong/stsong.ttf</italic>
+        <boldItalic>report/stsong/stsong.ttf</boldItalic>
+        <pdfEncoding>Identity-H</pdfEncoding>
+        <pdfEmbedded>true</pdfEmbedded>
+        <exportFonts>
+            <export key="net.sf.jasperreports.html">'华文宋体',Arial,Helvetica,sans-serif</export>
+            <export key="net.sf.jasperreports.xhtml">'华文宋体',Arial,Helvetica,sans-serif</export>
+        </exportFonts>
+    </fontFamily>
+    <fontFamily name="宋体">
+        <normal>report/stsong/SimSun.ttf</normal>
+        <bold>report/stsong/SimSunBold.ttf</bold>
+        <italic>report/stsong/SimSun.ttf</italic>
+        <boldItalic>report/stsong/SimSun.ttf</boldItalic>
+        <pdfEncoding>Identity-H</pdfEncoding>
+        <pdfEmbedded>true</pdfEmbedded>
+        <exportFonts/>
+    </fontFamily>
+</fontFamilies>
\ No newline at end of file
Index: src/main/resources/report/stsong/stsong.ttf
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/stsong/stsong.ttf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/stsong/SimSun.ttf
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/stsong/SimSun.ttf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/stsong/SimSunBold.ttf
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/stsong/SimSunBold.ttf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/templates/001.jasper
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/templates/001.jasper
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/main/resources/report/templates/501.jasper
===================================================================
无法显示: 文件标记为二进制类型。
svn:mime-type = application/octet-stream

Property changes on: src/main/resources/report/templates/501.jasper
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/test/java/com/novaone/T.java
===================================================================
--- src/test/java/com/novaone/T.java	(版本 31411)
+++ src/test/java/com/novaone/T.java	(不存在的)
@@ -1,71 +0,0 @@
-package com.novaone;
-
-import javax.annotation.Resource;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import com.novaone.common.Application;
-import com.novaone.entity.VO.ConfigBocVO;
-import com.novaone.service.BocInterfaceService;
-import com.novaone.service.ConfigService;
-import com.novaone.util.CommonEnum;
-import com.novaone.util.CommonInfo;
-import com.novaone.util.CreateXml;
-import com.novaone.util.HttpUtil;
-
-import lombok.extern.slf4j.Slf4j;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-
-@Slf4j
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = Application.class)
-public class T {
-    @Resource
-    private BocInterfaceService bocInterfaceService;
-    @Resource
-    private ConfigService configService;
-
-    @Test
-    public void test() {
-        
-      //验证前置机
-        boolean connection;
-        try {
-            bocInterfaceService.checkConfig();
-        } catch (Exception e1) {
-            e1.printStackTrace();
-        }
-        //签到
-        try {
-            bocInterfaceService.sign();
-        } catch (Exception e1) {
-            e1.printStackTrace();
-        }
-        final ConfigBocVO config = configService.queryConfigOne();
-//        String xml = CreateXml.CreateQueryHistoryOBCXml("446876936591", "40303", config, "1");
-        String xml = CreateXml.CreateReceiptInfo("440376264213", "40303", config);
-        log.info(xml);
-        String url = config.getProtocol() + "://" + config.getIp() + ":" + config.getPort() + "/B2EC/E2BServlet";
-        log.info("请求地址:" + url);
-        String result;
-        try {
-            result = HttpUtil.postXml(url, xml, "UTF-8");
-            log.info("返回参数:" + result);
-            JSONObject json = CreateXml.xmlToJson(result);
-            System.out.println("json:" + json);
-            JSONObject trnB2e0500Rs = json.getJSONObject("trans").getJSONObject("trn-b2e0500-rs");
-            String code = trnB2e0500Rs.getJSONObject("status").getString("rspcod");
-            if(code.equals(CommonInfo.BANK_SUCCESS)) {
-                JSONObject b2e0035Rsa = trnB2e0500Rs.getJSONObject("b2e0500-rs");
-                String fileName = b2e0035Rsa.getString("filename");
-                System.out.println("===========:"+fileName);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
Index: src/test/java/com/novaone
===================================================================
--- src/test/java/com/novaone	(版本 31411)
+++ src/test/java/com/novaone	(不存在的)

Property changes on: src/test/java/com/novaone
___________________________________________________________________
Deleted: svn:ignore
## -1 +0,0 ##
-service
Index: src/test/java/com
===================================================================
--- src/test/java/com	(版本 31411)
+++ src/test/java/com	(版本 31412)

Property changes on: src/test/java/com
___________________________________________________________________
Added: svn:global-ignores
## -0,0 +1 ##
+novaone
Added: svn:ignore
## -0,0 +1 ##
+novaone
