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