From 79e7583b387c83324be0e26a4dc16f97711b527b Mon Sep 17 00:00:00 2001
From: yupeng <roc__yu@163.com>
Date: 星期二, 21 一月 2025 15:53:38 +0800
Subject: [PATCH] 新增银行接口模块

---
 bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java          |   18 
 bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java                     |  210 +++++++++++
 ruoyi-admin/pom.xml                                                             |    6 
 bankapi/pom.xml                                                                 |   56 ++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java   |   30 +
 bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java           |  158 ++++++++
 bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java            |  175 +++++++++
 bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java              |  108 +++++
 bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java |  198 ++++++++++
 pom.xml                                                                         |    1 
 bankapi/src/main/resources/META-INF/spring.factories                            |    2 
 bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java                       |   17 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java            |   89 ----
 bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java                   |   33 +
 14 files changed, 1,014 insertions(+), 87 deletions(-)

diff --git a/bankapi/pom.xml b/bankapi/pom.xml
new file mode 100644
index 0000000..4867d1f
--- /dev/null
+++ b/bankapi/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ruoyi</groupId>
+        <artifactId>ruoyi</artifactId>
+        <version>3.8.6</version>
+    </parent>
+    <description>
+        银行接口模块
+    </description>
+    <groupId>org.taxi591</groupId>
+    <artifactId>bankapi</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <dependencies>
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.5.15</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>
\ No newline at end of file
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java b/bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java
new file mode 100644
index 0000000..a59a542
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java
@@ -0,0 +1,17 @@
+package com.taxi591.bankapi;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties({BankProperties.class})
+@ConditionalOnProperty(value = BankProperties.ENBALE_PREFIX, matchIfMissing = true)
+@ComponentScan("com.taxi591.bankapi.service")
+public class BankConfig {
+
+
+
+
+}
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java b/bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java
new file mode 100644
index 0000000..fd391c9
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java
@@ -0,0 +1,33 @@
+package com.taxi591.bankapi;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import static com.taxi591.bankapi.BankProperties.PREFIX;
+
+@Data
+@ConfigurationProperties(prefix = PREFIX)
+public class BankProperties {
+
+    public static final String PREFIX = "com.taxi591.bank";
+
+    public static final String ENBALE_PREFIX = PREFIX+".enable";
+
+    private Boolean enable = true;
+
+    /**
+     * 证书路径
+     */
+    private String pfxPath;
+    /**
+     * 密码
+     */
+    private String keystorePassword;
+
+    /**
+     * 公钥路径
+     */
+    private String cerPath;
+
+
+}
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java b/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java
new file mode 100644
index 0000000..9aadf9b
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java
@@ -0,0 +1,175 @@
+package com.taxi591.bankapi.dto;
+
+import com.alibaba.fastjson2.JSON;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *  直连商户平台缴费销账输入对象,需要转换成json串发送给第三方系统
+ *  @author DELL
+ *
+ */
+@Data
+public class ChargeBillRequest implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/** 格式 */
+	private String format;
+
+	/** 消息 */
+	private Message message;
+
+	@Override
+	public String toString() {
+		return "ChargeBillRequest[format=" + format + ",message=" + message.toString() + "]";
+	}
+
+	/**
+	 *
+	 * 账单查询内部消息对象实体message内部类
+	 *
+	 */
+	@Data
+	public class Message implements Serializable {
+
+		private static final long serialVersionUID = 1L;
+
+		/** 消息头部 */
+		private Head head;
+
+		/** 消息体  */
+		private Info info;
+
+		@Override
+		public String toString() {
+			return JSON.toJSONString(this);
+		}
+
+		/**
+		 *  message子对象head消息头内部类
+		 */
+		@Data
+		public class Head implements Serializable {
+
+			private static final long serialVersionUID = 1L;
+
+			/**  渠道编码 */
+			private String channel;
+
+			/**  交易码  */
+			private String transCode;
+
+			/**  交易上行下送标志位  */
+			private String transFlag;
+
+			/**  缴费中心交易序列号 */
+			private String transSeqNum;
+
+			/**   时间戳  */
+			private String timeStamp;
+
+			/**   4为分行iGoal码  */
+			private String branchCode;
+
+			@Override
+			public String toString() {
+				return JSON.toJSONString(this);
+			}
+		}
+
+		/**
+		 * message子对象info消息实体内部类
+		 */
+		@Data
+		public class Info implements Serializable {
+
+			private static final long serialVersionUID = 1L;
+
+			/** 缴费项目编号*/
+			private String epayCode;
+
+			/** 第三方商户编号*/
+			private String merchantId;
+
+			/** 缴费中心流水号*/
+			private String traceNo;
+
+			/** 输入要素1*/
+			private String input1;
+
+			/** 输入要素2*/
+			private String input2;
+
+			/** 输入要素3*/
+			private String input3;
+
+			/** 输入要素4*/
+			private String input4;
+
+			/** 输入要素5*/
+			private String input5;
+
+			/** 农行16位客户号*/
+			private String userId;
+
+			/** 缴费金额计算规则*/
+			private String amtRule;
+
+			/** 合并支付的子账单数*/
+			private String payBillCount;
+
+			/** 合并支付的子账单累加总金额*/
+			private String payBillAmt;
+
+			/** 合并支付的子账单*/
+			private String payBillNo;
+
+			/** 套餐名称*/
+			private String optionName;
+
+			/** 套餐编码*/
+			private String optionCode;
+
+			/** 套餐金额*/
+			private String optionAmt;
+
+			/** 支付方式交易码*/
+			private String payType;
+
+			/** 缴费支付账号*/
+			private String payAcc;
+
+			/** 支付系统流水号*/
+			private String transPaySeq;
+
+			/** 支付系统日期*/
+			private String transDate;
+
+			/** 支付系统时间*/
+			private String transTime;
+
+			/** 会计日期*/
+			private String settleDate;
+
+			/** 清算模式*/
+			private String clearType;
+
+			/** 缓存域信息*/
+			private String cacheMem;
+
+			/** 销账报文重发次数,通过此字段识别销账报文是否为重发的,0表示首次、1表示重发一次,2表示重发2次,最多重发3次*/
+			private String resendTimes;
+
+			/** 第三方支付平台商户订单号 第三方平台例如微信支付宝的支付订单号 add 2020-01-13*/
+			private String numOpenMerchantOrder;
+
+
+			@Override
+			public String toString() {
+				return JSON.toJSONString(this);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java b/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java
new file mode 100644
index 0000000..ec6d9d8
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java
@@ -0,0 +1,158 @@
+package com.taxi591.bankapi.dto;
+
+import com.alibaba.fastjson2.JSON;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *  直连商户平台账单销账返回对象
+ *  @author DELL
+ * 
+ */
+@Data
+public class ChargeBillResponse implements Serializable {
+	
+	private static final long serialVersionUID = 1L;
+	
+	/** 格式 */
+	private String format;
+	
+	/** 消息体 */
+	private Message message;
+
+
+	public ChargeBillResponse(){
+
+	}
+
+	/**
+	 * 构造函数,通过输入对象,构造返回对象数据信息
+	 * @param request
+	 */
+	public ChargeBillResponse(ChargeBillRequest request) {
+		this.setFormat(request.getFormat());
+		this.setMessage(new Message(request.getMessage()));
+	}
+	
+	@Override
+	public String toString() {
+		return JSON.toJSONString(this);
+	}
+	/**
+	 *  
+	 * 账单查询内部消息对象返回实体message内部类
+	 * 
+	 */
+	@Data
+	public class Message implements Serializable {
+		
+		private static final long serialVersionUID = 1L;
+		
+		/** 消息头部 */
+		private Head head;
+		
+		/** 消息实体  */
+		private Info info;
+		
+		public Message() {
+			this.head = new Head();
+			this.info = new Info();
+		}
+		
+		public Message(ChargeBillRequest.Message requestMessage){
+			this.setHead(new Head(requestMessage.getHead()));
+			this.setInfo(new Info(requestMessage.getInfo()));
+		}
+		
+		@Override
+		public String toString() {
+			return JSON.toJSONString(this);
+		}
+
+		/**
+		 *  
+		 * 账单销账内部消息对象返回实体Head内部类
+		 * 
+		 */
+		@Data
+		public class Head implements Serializable {
+			
+			private static final long serialVersionUID = 1L;
+			
+			/**  渠道 */
+			private String channel;
+			
+			/**  交易码  */
+			private String transCode;
+			
+			/**  交易上行下送标志  */
+			private String transFlag;
+			
+			/**  缴费中心交易序列号 */
+			private String transSeqNum;
+			
+			/** 时间戳  */
+			private String timeStamp;
+			
+			/**  查询返回码 */
+			private String returnCode ;
+			
+			/**  返回提示信息  */
+			private String returnMessage;
+			
+			public Head() {
+				
+			}
+			
+			public Head(ChargeBillRequest.Message.Head reqMessHead) {
+				this.setChannel(reqMessHead.getChannel());
+				this.setTransSeqNum(reqMessHead.getTransSeqNum());
+				this.setTransCode(reqMessHead.getTransCode());
+			}
+
+			@Override
+			public String toString() {
+				return JSON.toJSONString(this);
+			}
+
+		}
+
+		/**
+		 *  
+		 * 账单查询内部消息对象返回实体Info内部类
+		 * 
+		 */
+		@Data
+		public class Info implements Serializable {
+			
+			private static final long serialVersionUID = 1L;
+			
+			/** 缴费项目唯一标识号*/
+			private String epayCode;
+			
+			/** 缴费中心流水号*/
+			private String traceNo;
+			
+			/** 退款标志位*/
+			private String refundFlag;
+			
+			/** 第三方支付平台商户订单号 第三方平台例如微信支付宝的支付订单号 add 2020-01-13*/
+			private String numOpenMerchantOrder;
+
+			public Info() {
+				
+			}
+
+			public Info(ChargeBillRequest.Message.Info reqMessInfo) {
+			    this.setEpayCode(reqMessInfo.getEpayCode());
+			    this.setTraceNo(reqMessInfo.getTraceNo());
+			}
+			
+			@Override
+			public String toString() {
+				return JSON.toJSONString(this);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java b/bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java
new file mode 100644
index 0000000..d4fb154
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java
@@ -0,0 +1,18 @@
+package com.taxi591.bankapi.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class CovertPayBackResult implements Serializable {
+    /**
+     * 解析结果
+     */
+    private ChargeBillRequest result;
+    /**
+     * 返回银行应答内容
+     */
+    private String back;
+
+}
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java b/bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java
new file mode 100644
index 0000000..cd16b9b
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java
@@ -0,0 +1,108 @@
+package com.taxi591.bankapi.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.TypeReference;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DateUtils;
+import com.taxi591.bankapi.dto.ChargeBillRequest;
+import com.taxi591.bankapi.dto.ChargeBillResponse;
+import com.taxi591.bankapi.dto.CovertPayBackResult;
+import com.taxi591.bankapi.utils.Base64;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.UnsupportedEncodingException;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Service
+@Slf4j
+public class BankService {
+
+    @Autowired
+    SignatureAndVerification signatureAndVerification;
+
+    static final String TIMESTAMP_PATTERN = "yyyyMMddHHmmssSSS";
+
+    /**
+     * 创建银行应答
+     * @param request 银行请求对象
+     * @param dealResult 是否处理成功
+     * @return
+     */
+    public String createResponse(ChargeBillRequest request,Boolean dealResult){
+        ChargeBillResponse response = new ChargeBillResponse(request);
+        response.getMessage().getHead().setReturnCode(dealResult?"0000":"1111");
+        response.getMessage().getHead().setReturnMessage(dealResult?"处理成功":"处理失败");
+        response.getMessage().getInfo().setRefundFlag("false");
+        response.getMessage().getHead().setTimeStamp(DateUtils.dateTimeNow(TIMESTAMP_PATTERN));
+        if (!dealResult){
+            if  (Integer.parseInt(request.getMessage().getInfo().getResendTimes())==3){
+                response.getMessage().getInfo().setRefundFlag("true");
+            }else{
+                response.getMessage().getInfo().setRefundFlag("false");
+            }
+        }
+        String respJSON = JSON.toJSONString(response);
+        String sign = signatureAndVerification.signWhithsha1withrsa(respJSON);
+        String respStr = null;
+        try {
+            respStr = sign + "||" + new String(org.apache.commons.codec.binary.Base64.encodeBase64(respJSON.getBytes("utf-8")));
+        } catch (UnsupportedEncodingException e) {
+        }
+        return respStr;
+    }
+
+    /**
+     * 处理支付回调数据
+     * @param httpRequest  http请求对象
+     * @param consumer 处理函数
+     * @return
+     */
+    public CovertPayBackResult covertPayCallBack(HttpServletRequest httpRequest, Function<ChargeBillRequest,Boolean> consumer) {
+        CovertPayBackResult result = new CovertPayBackResult();
+        try {
+            // 接收报文
+            String requestContent = SignatureAndVerification.getRequestBody(httpRequest).trim();
+            String sign = requestContent.substring(0,
+                    requestContent.indexOf("||"));;
+            String requestBody = requestContent.substring(sign
+                    .length() + 2);;
+            Pattern p=Pattern.compile("\"");
+            Matcher m=p.matcher(requestBody);
+            while(m.find()){
+                requestBody=requestBody.replace(m.group(), "");
+            }
+            String request = new String(Base64.decodeFast(requestBody));
+            log.info("-----ChargeBillController------------解析完成后的requestBody-------{}" + request);
+            ChargeBillRequest chargeBillRequest = JSON.parseObject(request,
+                    new TypeReference<ChargeBillRequest>() {
+                    });
+            if (chargeBillRequest==null){
+                log.error("支付回调解析失败:{}",requestContent);
+                throw new ServiceException("支付回调解析失败");
+            }
+            boolean isok = signatureAndVerification.read_cer_and_verify_sign(requestBody,sign);
+            if (!isok){
+                throw new ServiceException("支付回调验签失败");
+            }
+            Boolean dealBack = true;
+            if (consumer!=null){
+                dealBack = consumer.apply(chargeBillRequest);
+            }
+            result.setResult(chargeBillRequest);
+            result.setBack(createResponse(chargeBillRequest,dealBack));
+        }catch (Exception e){
+            log.error("解析报文发生异常",e);
+        }
+        return result;
+    }
+
+
+
+
+
+}
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java b/bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java
new file mode 100644
index 0000000..948bdee
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java
@@ -0,0 +1,198 @@
+package com.taxi591.bankapi.service;
+
+import com.taxi591.bankapi.BankProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 验签和加签工具类
+ * @author yzz
+ *
+ */
+@Component
+@Slf4j
+public class SignatureAndVerification {
+
+	@Autowired
+	BankProperties bankProperties;
+
+
+
+	public static String getRequestBody(HttpServletRequest request)
+			throws IOException {
+		/** 读取httpbody内容 */
+		StringBuilder httpBody = new StringBuilder();
+		BufferedReader br = null;
+		try {
+			br = new BufferedReader(new InputStreamReader(
+					request.getInputStream()));
+			String line = null;
+			while ((line = br.readLine()) != null) {
+				httpBody.append(line);
+			}
+		} catch (IOException ex) {
+			throw ex;
+		} finally {
+			if (br != null) {
+				try {
+					br.close();
+				} catch (IOException ex) {
+					ex.printStackTrace();
+				}
+			}
+		}
+		return httpBody.toString();
+	}
+	/**
+	 * 加签名
+	 * @param dataString
+	 * @return
+	 */
+	public  String signWhithsha1withrsa(String dataString) {
+		String signatureString = null;
+		String filePath=bankProperties.getPfxPath();
+		try {
+			KeyStore ks = KeyStore.getInstance("PKCS12");
+			FileInputStream fis = new FileInputStream(filePath);
+			char[] nPassword = null;
+			if ((bankProperties.getKeystorePassword() == null)
+					|| bankProperties.getKeystorePassword().trim().equals("")) {
+				nPassword = null;
+			} else {
+				nPassword = bankProperties.getKeystorePassword().toCharArray();
+			}
+			ks.load(fis, nPassword);
+			fis.close();
+
+			Enumeration<String> enums = ks.aliases();
+			String keyAlias = null;
+			if (enums.hasMoreElements()) 
+			{
+				keyAlias = (String) enums.nextElement();
+			}
+			System.out.println("is key entry=" + ks.isKeyEntry(keyAlias));
+			PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
+			java.security.cert.Certificate cert = ks.getCertificate(keyAlias);
+			// SHA1withRSA算法进行签名
+			Signature sign = Signature.getInstance("SHA1withRSA");
+			sign.initSign(prikey);
+			byte[] data = dataString.getBytes("utf-8");
+			byte[] dataBase= Base64.encodeBase64(data);
+			// 更新用于签名的数据
+			sign.update(dataBase);
+			byte[] signature = sign.sign();
+			signatureString = new String(Base64.encodeBase64(signature));
+			System.out.println("加密完成,signature is : " + signatureString);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return signatureString;
+	}
+
+	/**
+	 * 读取cer并验证公钥签名
+	 * @return 
+	 */
+	public  boolean read_cer_and_verify_sign(String requestBody, String signature) {
+		String filePath=bankProperties.getCerPath();
+		X509Certificate cert = null;
+		boolean flag = false;
+		try {
+			CertificateFactory cf = CertificateFactory.getInstance("X.509");
+			cert = (X509Certificate) cf
+					.generateCertificate(new FileInputStream(new File(
+							filePath)));
+			PublicKey publicKey = cert.getPublicKey();
+			String publicKeyString = new String(Base64.encodeBase64(publicKey
+					.getEncoded()));
+			System.out.println("-----------------公钥--------------------");
+			System.out.println(publicKeyString);
+			System.out.println("-----------------公钥--------------------");
+			Signature verifySign = Signature.getInstance("SHA1withRSA");
+			verifySign.initVerify(publicKey);
+			// 用于验签的数据
+			verifySign.update(requestBody.getBytes("utf-8"));
+			flag = verifySign.verify(Base64
+					.decodeBase64(signature));
+		} catch (Exception e) {
+			log.error("验签失败,发生异常:{},{}",requestBody,signature,e);
+			flag = false;
+		}
+		return flag;
+	}
+	
+	/**
+	 * 接收报文返回requestBody和使用base64解析后的requestBody以及缴费中心传送的签名
+	 */
+
+	public Map<String,String> requestBodyOfBase64(HttpServletRequest request){
+		Map<String,String> requestMap=new HashMap<String,String>();
+		// 接收报文
+		String requestContent=null;
+		try {
+			requestContent = getRequestBody(request)
+					.trim();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		String signatureString = null;
+		String requestBody = null;
+		if (requestContent.contains("||")) {
+			signatureString = requestContent.substring(0,
+					requestContent.indexOf("||"));
+			requestBody = requestContent.substring(signatureString
+					.length() + 2);
+		}else {
+			try {
+				requestBody = new String(requestContent.getBytes("GB2312"));
+				
+				log.info("转码后的报文:{}",requestBody);
+				
+			} catch (UnsupportedEncodingException e) {
+				e.printStackTrace();
+			}
+		}
+		log.info("截取报文的requestBody解密前:{}", requestBody);
+		
+		String requestBodyOfDecoded = new String(
+				Base64.decodeBase64(requestBody));
+		
+		/*if (requestBodyOfDecoded.contains("</Message>")) {
+			requestBody = requestBodyOfDecoded.substring(
+					requestContent.indexOf("</Message>"),requestContent.indexOf("</Message>")+10);
+			
+			signatureString = requestBodyOfDecoded.substring(
+					requestContent.indexOf("<Signature>")+11,requestContent.indexOf("</Signature>"));
+		}*/
+		
+		log.info("截取报文的signatureString:{}", signatureString);
+		
+		
+		System.out.println("-----解析完成后的requestBody-------" + requestBodyOfDecoded);
+		
+		//使用base64解析完成后的requestBody
+		requestMap.put("requestBodyOfDecoded",requestBodyOfDecoded);
+		//解析前的requestBody
+		requestMap.put("requestBody",requestBody);
+		//获取缴费中心传送过来的签名
+		requestMap.put("signatureString",signatureString);
+		return requestMap;
+		
+	} 
+	
+}
diff --git a/bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java b/bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java
new file mode 100644
index 0000000..9b92495
--- /dev/null
+++ b/bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java
@@ -0,0 +1,210 @@
+
+package com.taxi591.bankapi.utils;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @version 2.2
+ * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
+ */
+public class Base64 {
+
+    public static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+    public static final int[]  IA = new int[256];
+    static {
+        Arrays.fill(IA, -1);
+        for (int i = 0, iS = CA.length; i < iS; i++)
+            IA[CA[i]] = i;
+        IA['='] = 0;
+    }
+
+    /**
+     * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
+     * fast as #decode(char[]). The preconditions are:<br>
+     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
+     * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
+     * the encoded string<br>
+     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
+     *
+     * @param chars The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0.
+     */
+    public static byte[] decodeFast(char[] chars, int offset, int charsLen) {
+        // Check special case
+        if (charsLen == 0) {
+            return new byte[0];
+        }
+
+        int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[chars[sIx]] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[chars[eIx]] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = chars[eIx] == '=' ? (chars[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
+        int cCnt = eIx - sIx + 1; // Content count including possible separators
+        int sepCnt = charsLen > 76 ? (chars[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[chars[sIx++]] << 18 | IA[chars[sIx++]] << 12 | IA[chars[sIx++]] << 6 | IA[chars[sIx++]];
+
+            // Add the bytes
+            bytes[d++] = (byte) (i >> 16);
+            bytes[d++] = (byte) (i >> 8);
+            bytes[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19) {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len) {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[chars[sIx++]] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                bytes[d++] = (byte) (i >> r);
+        }
+
+        return bytes;
+    }
+
+    public static byte[] decodeFast(String chars, int offset, int charsLen) {
+        // Check special case
+        if (charsLen == 0) {
+            return new byte[0];
+        }
+
+        int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[chars.charAt(sIx)] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[chars.charAt(eIx)] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = chars.charAt(eIx) == '=' ? (chars.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
+        int cCnt = eIx - sIx + 1; // Content count including possible separators
+        int sepCnt = charsLen > 76 ? (chars.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[chars.charAt(sIx++)] << 18 | IA[chars.charAt(sIx++)] << 12 | IA[chars.charAt(sIx++)] << 6 | IA[chars.charAt(sIx++)];
+
+            // Add the bytes
+            bytes[d++] = (byte) (i >> 16);
+            bytes[d++] = (byte) (i >> 8);
+            bytes[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19) {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len) {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[chars.charAt(sIx++)] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                bytes[d++] = (byte) (i >> r);
+        }
+
+        return bytes;
+    }
+
+    /**
+     * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as fast
+     * as decode(String). The preconditions are:<br>
+     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
+     * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
+     * the encoded string<br>
+     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
+     *
+     * @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0.
+     */
+    public static byte[] decodeFast(String s) {
+        // Check special case
+        int sLen = s.length();
+        if (sLen == 0) {
+            return new byte[0];
+        }
+
+        int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
+        int cCnt = eIx - sIx + 1; // Content count including possible separators
+        int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6
+                    | IA[s.charAt(sIx++)];
+
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            dArr[d++] = (byte) (i >> 8);
+            dArr[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19) {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len) {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[s.charAt(sIx++)] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                dArr[d++] = (byte) (i >> r);
+        }
+
+        return dArr;
+    }
+}
\ No newline at end of file
diff --git a/bankapi/src/main/resources/META-INF/spring.factories b/bankapi/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..719afa0
--- /dev/null
+++ b/bankapi/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.taxi591.bankapi.BankConfig
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index a5aa325..0bcee27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -187,6 +187,7 @@
         <module>ruoyi-generator</module>
         <module>ruoyi-common</module>
         <module>ruoyi-applet</module>
+        <module>bankapi</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 89a6462..3aa3a62 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -16,7 +16,11 @@
     </description>
 
     <dependencies>
-
+        <dependency>
+            <groupId>org.taxi591</groupId>
+            <artifactId>bankapi</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>com.aliyun.oss</groupId>
             <artifactId>aliyun-sdk-oss</artifactId>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java
new file mode 100644
index 0000000..2fa8329
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java
@@ -0,0 +1,30 @@
+package com.ruoyi.web.controller.api;
+
+import com.taxi591.bankapi.dto.CovertPayBackResult;
+import com.taxi591.bankapi.service.BankService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("out/bank")
+public class BankOutController {
+
+    @Autowired
+    BankService bankService;
+    @PostMapping(value = "payCallback")
+    public @ResponseBody String payCallback(HttpServletRequest request){
+        CovertPayBackResult result = bankService.covertPayCallBack(request, (billRequest) -> {
+            return true;
+        });
+        return result.getBack();
+    }
+
+
+
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
index 27273cf..0c89771 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -1,93 +1,10 @@
 package com.ruoyi.common.utils.poi;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.math.BigDecimal;
-import java.text.DecimalFormat;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletResponse;
-
-import com.sun.rowset.internal.Row;
-import javafx.scene.control.Cell;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.RegExUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
-//import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
-//import org.apache.poi.hssf.usermodel.HSSFPicture;
-//import org.apache.poi.hssf.usermodel.HSSFPictureData;
-//import org.apache.poi.hssf.usermodel.HSSFShape;
-//import org.apache.poi.hssf.usermodel.HSSFSheet;
-//import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-//import org.apache.poi.ooxml.POIXMLDocumentPart;
-//import org.apache.poi.ss.usermodel.BorderStyle;
-//import org.apache.poi.ss.usermodel.Cell;
-//import org.apache.poi.ss.usermodel.CellStyle;
-//import org.apache.poi.ss.usermodel.CellType;
-//import org.apache.poi.ss.usermodel.ClientAnchor;
-//import org.apache.poi.ss.usermodel.DataValidation;
-//import org.apache.poi.ss.usermodel.DataValidationConstraint;
-//import org.apache.poi.ss.usermodel.DataValidationHelper;
-//import org.apache.poi.ss.usermodel.DateUtil;
-//import org.apache.poi.ss.usermodel.Drawing;
-//import org.apache.poi.ss.usermodel.FillPatternType;
-//import org.apache.poi.ss.usermodel.Font;
-//import org.apache.poi.ss.usermodel.HorizontalAlignment;
-//import org.apache.poi.ss.usermodel.IndexedColors;
-//import org.apache.poi.ss.usermodel.Name;
-//import org.apache.poi.ss.usermodel.PictureData;
-//import org.apache.poi.ss.usermodel.Row;
-//import org.apache.poi.ss.usermodel.Sheet;
-//import org.apache.poi.ss.usermodel.VerticalAlignment;
-//import org.apache.poi.ss.usermodel.Workbook;
-//import org.apache.poi.ss.usermodel.WorkbookFactory;
-//import org.apache.poi.ss.util.CellRangeAddress;
-//import org.apache.poi.ss.util.CellRangeAddressList;
-//import org.apache.poi.util.IOUtils;
-//import org.apache.poi.xssf.streaming.SXSSFWorkbook;
-//import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
-//import org.apache.poi.xssf.usermodel.XSSFDataValidation;
-//import org.apache.poi.xssf.usermodel.XSSFDrawing;
-//import org.apache.poi.xssf.usermodel.XSSFPicture;
-//import org.apache.poi.xssf.usermodel.XSSFShape;
-//import org.apache.poi.xssf.usermodel.XSSFSheet;
-//import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-//import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-//import com.ruoyi.common.annotation.Excel;
-//import com.ruoyi.common.annotation.Excel.ColumnType;
-//import com.ruoyi.common.annotation.Excel.Type;
-import com.ruoyi.common.annotation.Excels;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.core.text.Convert;
-import com.ruoyi.common.exception.UtilException;
-import com.ruoyi.common.utils.DateUtils;
-import com.ruoyi.common.utils.DictUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.file.FileTypeUtils;
-import com.ruoyi.common.utils.file.FileUtils;
-import com.ruoyi.common.utils.file.ImageUtils;
-import com.ruoyi.common.utils.reflect.ReflectUtils;
+
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Excel相关处理

--
Gitblit v1.7.1