Commit 6bb6da3b authored by Java-范 志勇's avatar Java-范 志勇

整合帐单拉取功能

parent 886187e4
...@@ -170,6 +170,16 @@ ...@@ -170,6 +170,16 @@
<artifactId>commons-dbcp</artifactId> <artifactId>commons-dbcp</artifactId>
<version>1.4</version> <version>1.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
......
package cn.gq.financial.app; package cn.gq.financial.app;
import cn.gq.financial.bills.AbstractBills;
import cn.gq.financial.bills.UMPayBills;
import cn.gq.financial.bills.WeichatBills;
import cn.gq.financial.bills.PayType;
import cn.gq.financial.service.FinancialRepayDetailService; import cn.gq.financial.service.FinancialRepayDetailService;
import cn.gq.financial.utils.Constants; import cn.gq.financial.utils.Constants;
import com.google.common.base.Joiner;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
...@@ -12,51 +18,65 @@ import cn.gq.financial.model.bill.UMPayBill; ...@@ -12,51 +18,65 @@ import cn.gq.financial.model.bill.UMPayBill;
import cn.gq.financial.model.bill.WXBill; import cn.gq.financial.model.bill.WXBill;
import cn.gq.financial.model.bill.YeepayBill; import cn.gq.financial.model.bill.YeepayBill;
import java.util.concurrent.TimeUnit; import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
public class RepayFinancialAPP { public class RepayFinancialAPP {
private static final Logger LOGGER = LoggerFactory.getLogger(RepayFinancialAPP.class); private static final Logger LOGGER = LoggerFactory.getLogger(RepayFinancialAPP.class);
static final String CONFIG_LOCAL = "applicationContext.xml"; static final String CONFIG_LOCAL = "applicationContext.xml";
static Integer month = 8;
// static String SRC_BILL_PATH = "E:\\financial-system-data\\weichat\\baitiao"; //微信白条
// static String SRC_BILL_PATH = "E:\\financial-system-data\\weichat\\xianjindai"; //微信现金贷
// static String SRC_BILL_PATH = "E:\\financial-system-data\\umpay\\baitiao"; //联动优势白条 static final String ROOT_DIR = "E:\\financial-system-data\\";
// static String SRC_BILL_PATH = "E:\\financial-system-data\\umpay\\xianjindai"; //联动优势现金贷 /**
* Tip: 对帐单时 只需修改currentMonth和payType
* currentMonth 当前是几月就输入几 无须减1 后续代码里面已经处理
*/
static Integer currentMonth = 9;
static String SRC_BILL_PATH = "E:\\financial-system-data\\yeepay\\xianjindai"; //易宝现金贷 static PayType payType = PayType.UMPay_Xjd;
static String payAprroach = "易宝"; static AbstractBills bill;
static String path = "";
static String payAccount = "易宝-现金贷账户";
private static ClassPathXmlApplicationContext context = null; private static ClassPathXmlApplicationContext context = null;
static Class<? extends Bill> clazz = null; static Class<? extends Bill> clazz = null;
static { static {
switch (payAprroach) { path = Joiner.on(File.separator).join(ROOT_DIR, payType.path());
switch (payType.getChannelName()) {
case "微信": case "微信":
bill = new WeichatBills(currentMonth, path);
clazz = WXBill.class; clazz = WXBill.class;
break; break;
case "易宝": case "易宝":
clazz = YeepayBill.class; clazz = YeepayBill.class;
break; break;
case "联动": case "联动":
bill = new UMPayBills(currentMonth, path);
clazz = UMPayBill.class; clazz = UMPayBill.class;
break; break;
} }
// 拉取帐单前先清理目录
if (Paths.get(path).toFile().exists()) {
try {
FileUtils.cleanDirectory(Paths.get(path).toFile());
} catch (IOException e) {
e.printStackTrace();
}
}
bill.pullBills(payType);
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
try { try {
LOGGER.info("======开始处理还款账单======"); LOGGER.info("======开始处理[" + path + "]目录下面的还款账单======");
long begin = System.currentTimeMillis(); long begin = System.currentTimeMillis();
RepayHandler handler = getSpringContext().getBean(RepayHandler.class); RepayHandler handler = getSpringContext().getBean(RepayHandler.class);
//按照参数,对账 //按照参数,对账
handler.repayBillDetailHandler(SRC_BILL_PATH, clazz, payAccount, month + 1); handler.repayBillDetailHandler(Joiner.on(File.separator).join(ROOT_DIR, payType.path()), clazz, payType.getChannelAccount(), currentMonth);
FinancialRepayDetailService repayDetailService = getSpringContext().getBean(FinancialRepayDetailService.class); FinancialRepayDetailService repayDetailService = getSpringContext().getBean(FinancialRepayDetailService.class);
if (Constants.result.size() != 0) { if (Constants.result.size() != 0) {
repayDetailService.saveDetails(Constants.result); repayDetailService.saveDetails(Constants.result);
......
package cn.gq.financial.bills;
import cn.gq.financial.utils.MD5Utils;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Calendar;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA
* User: zhiyong.fan
* Date: 2016/10/11
* Time: 10:27
* Desc: 帐单拉取抽象类
*/
public abstract class AbstractBills {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBills.class);
/**
* 日期格式
*/
protected static FastDateFormat dateFormat = FastDateFormat.getInstance("yyyyMMdd");
/**
* 连接请求配置
*/
protected RequestConfig config = RequestConfig.custom().setConnectTimeout(300 * 1000).build();
/**
* 帐单日期 以天为单位
*/
protected List<String> billDates;
/**
* 帐单存放目录
*/
protected String billDir;
/**
* 当前月
*/
protected int currentMonth;
/**
* 请求接口地址
*/
protected String url;
public AbstractBills() {
}
public AbstractBills(int currentMonth, String billDir, String url) {
billDates = Lists.newArrayList();
this.currentMonth = currentMonth;
this.billDir = billDir;
this.url = url;
initBillDates();
}
/**
* 拉取帐单
*
* @param payType 支付方式
*/
public void pullBills(PayType payType) {
LOGGER.info("正在拉取[" + payType.getChannelAccount() + "]帐单信息, 请稍等......");
String xml = "";
UrlEncodedFormEntity encodedFormEntity;
for (String billDate : billDates) {
HttpPost post = new HttpPost(url);
post.setConfig(config);
EnumMap<PayType, Map<String, String>> params = assembleReqUrlParams(billDate);
Map<String, String> body = params.get(payType);
if (StringUtils.equals("微信", payType.getChannelName())) {
String signer = MD5Utils.build(Joiner.on("&").join(body.entrySet())).toUpperCase();
xml = "<xml>\n" +
" <appid>" + body.get("appid") + "</appid>\n" +
" <bill_date>" + body.get("bill_date") + "</bill_date>\n" +
" <bill_type>" + body.get("bill_type") + "</bill_type>\n" +
" <mch_id>" + body.get("mch_id") + "</mch_id>\n" +
" <nonce_str>" + body.get("nonce_str") + "</nonce_str>\n" +
" <sign>" + signer + "</sign>\n" +
"</xml>";
StringEntity entity = new StringEntity(xml, "UTF-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("text/xml");
post.setEntity(entity);
} else {
encodedFormEntity = new UrlEncodedFormEntity(assembleRequestBody(body), Charset.forName("UTF-8"));
post.setEntity(encodedFormEntity);
}
doRequest(post, billDate);
}
LOGGER.info("[" + payType.getChannelAccount() + "]帐单信息拉取完成......");
}
/**
* 组装post请求消息体
*
* @param params post请求消息体
* @return 消息体集合
*/
private List<NameValuePair> assembleRequestBody(Map<String, String> params) {
List<NameValuePair> pairs = Lists.newArrayList();
params.entrySet().forEach(entry -> pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue())));
return pairs;
}
/**
* 组装请求参数 由子类实现
*
* @param billDate 帐单日期
* @return 参数信息集合 key:支付方式 value:支付方式对应的帐户参数
*/
protected abstract EnumMap<PayType, Map<String, String>> assembleReqUrlParams(String billDate);
/**
* 拉取帐单
*
* @param post HttpPost 对象
* @param billDate 帐单日期
*/
protected void doRequest(HttpPost post, String billDate) {
CloseableHttpClient client = HttpClients.custom().build();
CloseableHttpResponse response = null;
try {
response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
writeFile(billDir, billDate, EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")).replaceAll("`", ""));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 写帐单内容到本地
*
* @param billDir 帐单本地存放目录
* @param billName 帐单名称
* @param data 帐单内容
*/
protected void writeFile(String billDir, String billName, String data) {
if (!Files.exists(Paths.get(billDir, billName))) {
try {
try {
FileUtils.touch(Paths.get(billDir, billName + ".txt").toFile());
} catch (IOException e) {
e.printStackTrace();
}
FileUtils.write(Paths.get(billDir, billName + ".txt").toFile(), data, Charset.forName("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 初始化帐单日期
*/
private void initBillDates() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, currentMonth - 1);
int totalDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
for (int i = 1; i <= totalDays; i++) {
calendar.set(Calendar.DAY_OF_MONTH, i);
billDates.add(dateFormat.format(calendar));
}
}
}
package cn.gq.financial.bills;
import com.google.common.base.Joiner;
import java.io.File;
/**
* Created with IntelliJ IDEA
* User: zhiyong.fan
* Date: 2016/10/11
* Time: 11:53
* Desc: 支付方式枚举类
*/
public enum PayType {
WeiChat_Xjd("微信", "微信-现金贷账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("weichat", "xjd");
}
},
WeiChat_BaiTiao("微信", "微信-白条账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("weichat", "baitiao");
}
},
UMPay_Xjd("联动", "联动-现金贷账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("umpay", "xjd");
}
},
UMPay_BaiTiao("联动", "联动-白条账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("umpay", "baitiao");
}
},
YeePay_Xjd("易宝", "易宝-现金贷账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("yeepay", "xjd");
}
},
YeePay_BaiTiao("易宝", "易宝-白条账户") {
@Override
public String path() {
return Joiner.on(File.separator).join("yeepay", "baitiao");
}
};
/**
* 支付渠道名称
*/
private String channelName;
/**
* 支付渠道帐户
*/
private String channelAccount;
/**
* 指定每类帐单在本地存放路径
*
* @return 帐单在本地的存放路径
*/
public abstract String path();
PayType(String channelName, String channelAccount) {
this.channelName = channelName;
this.channelAccount = channelAccount;
}
public String getChannelName() {
return channelName;
}
public void setChannelName(String channelName) {
this.channelName = channelName;
}
public String getChannelAccount() {
return channelAccount;
}
public void setChannelAccount(String channelAccount) {
this.channelAccount = channelAccount;
}
}
package cn.gq.financial.bills;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.EnumMap;
import java.util.Map;
/**
* Created with IntelliJ IDEA
* User: zhiyong.fan
* Date: 2016/10/11
* Time: 10:29
* Desc: 联动优势帐单
*/
public class UMPayBills extends AbstractBills {
/**
* 联运优势帐单摘取接口地址
*/
protected static final String UMPAY_BILLS_URS = "http://pay.soopay.net/spay/pay/payservice.do";
public UMPayBills(int currentMonth, String billDir) {
super(currentMonth, billDir, UMPAY_BILLS_URS);
}
@Override
public EnumMap<PayType, Map<String, String>> assembleReqUrlParams(String billDate) {
// 联动优势现金贷账户接口请求参数
String umPayXjdPrivateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAK/N7RNiSj9qPMeOZjH7Yoa4wHMSXGri7zuRouIpNclg7aTTyLPUKEuM/aMqFTHCbW0Da+BJIDkp6TahRreb0dNAokMWF62XxsYzZJYfzwex2K6AWhv0/h2U5e+WEVP2lk0FclkOQU6+JhbRy18GxmOKPCKv7LyfCcQ0i13n6JmZAgMBAAECgYEAl0GzkhPlEb6T4JXx81r4IDVgzyCjBwuNDVF2NOOD9WlBzaixa5dDvAzoRLP2XEON5lclP2lrIgOfNXN45oxX7975WELOjmsbMYBlyEVOAWxObbwSfIYLQXktsuwFE/o2HSedWf0rbYcOeliqysVTYXmxMBhs5UvjVybJy4Ox8aECQQDXdyAfGnWG7LQhevv4PREzHyZPUY0bRaUb1SiOh/4vnwr80+6VSxO1k8QA/QSUibUak5GuS6Yq5UFVlfJ/e55tAkEA0OC32m/jXdqxweaDCAGCurEGCrIOge79n73+AK5NBJowJZLQKPn1nNWMJKzi0Cv2AtQUfYsNGPJUNPQepUi8XQJAVk/NGUiTHeqCOgs3Fnj4A9+NjCah/PPIMT8RUQ9fHpp6X09SepGpooo/8RlqFUqYtr24lPaO6Q4PPwajLrhJfQJAdcdgpCEXGaLpPKaYF/mSF2ceOwWvvIBgLqTZBtVBxGiU4XrpOVHItsiLH1sFAdDoBSxE6Y1wszJhXQKSDSz4WQJBAKE37tii5nZ/2eGMxyg2gB6GBIZL10ZMUA6adrQmYM8o4172DRrFc/vL0n1TbzK6bwf561H+fdsY6bXSzYlvsb8=";
Map<String, String> umPayXjdInfo = Maps.newLinkedHashMap();
umPayXjdInfo.put("service", "download_settle_file");
umPayXjdInfo.put("version", "4.0");
umPayXjdInfo.put("mer_id", "3812");
umPayXjdInfo.put("settle_date", billDate);
umPayXjdInfo.put("sign_type", "RSA");
umPayXjdInfo.put("sign", sign(Joiner.on("&").join(umPayXjdInfo.entrySet()), Base64.getDecoder().decode(umPayXjdPrivateKey)));
// 联动优势白条账户接口请求参数
String umPayBaitiaoPrivateKey = "MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOGw+eVtC9PTEglhMGzFtAF9jzAbzAYs43+CGzb6veHg+28fJFQTzVsBJ4eo3kfIMI7GiWshqLZjUo4NE98QzwOFR1jY25g/4sPRdm/OYKKAqg+Tvn1cYPiNNKse2zfnxjxypCfoIsRsY4KLNkd9ifbxiIn66o/PGSbCvYgmjRsHAgMBAAECgYEA0XtRGFB07/I0BZy+M9f10oRWiZLjdr/nkYyT8h4ebTBEcMn5EEkpDNUvec2WNA5uc7d4CYY+cLwT/mHJFf0mByJPkhBCsBDJ4QProqab8DqphQrGlt4qZKmaOA/PPEHxwCEPTTY8/j4KGWn4x+1VfTNqZoaRRaiw0IR60sN+YXECQQDxgTamu0kAy9L6K3vO6sswUt/Agd94od80Z2+Miqc5qY7c7VJMv/O/c2h44QV+yOG/64sf1+GDIhs7oTql0hFrAkEA7zzI+A4LI5vRt1TBo3gz44cn7ckFQ0gJ2fSO0IbnhU6OaePJtRlIeAtvDoccjEDQvmeIGP84U1yKEgadlpwX1QJBAMufb83q0SW7tTo7WZtf+kvXRFMS7bHPgdu0bqgn69FBdnQuIRsXKVQ94VMyHykEBNuFpPsHUVv4cM+5JDLVyNsCQQCfb4cPqWxLAuE0qMuTwdvy2nQFyF41hKDsHEvBNOa+tKe8cg5PHtZpClWs/OZNojR7cEwHPK+w/o8VjEshwE0xAkEAxmr5C1HXas3b+vTZ12OLSJG1ocz6frNT3OmVPWToYvwLbMxDc2aHMVvdQ3RgpFR5PuOvy8X7NMYv5FqWB0FvIQ==";
Map<String, String> umPayBaitiaoInfo = Maps.newLinkedHashMap();
umPayBaitiaoInfo.put("service", "download_settle_file");
umPayBaitiaoInfo.put("version", "4.0");
umPayBaitiaoInfo.put("mer_id", "3661");
umPayBaitiaoInfo.put("settle_date", billDate);
umPayBaitiaoInfo.put("sign_type", "RSA");
umPayBaitiaoInfo.put("sign", sign(Joiner.on("&").join(umPayXjdInfo.entrySet()), Base64.getDecoder().decode(umPayBaitiaoPrivateKey)));
EnumMap<PayType, Map<String, String>> umPayInfo = Maps.newEnumMap(PayType.class);
umPayInfo.put(PayType.UMPay_Xjd, umPayXjdInfo);
umPayInfo.put(PayType.UMPay_BaiTiao, umPayBaitiaoInfo);
return umPayInfo;
}
/**
* 加密明文
*
* @param plainText 明文内容
* @param privateKey 私钥
* @return 密文数据
*/
private String sign(String plainText, byte[] privateKey) {
String signedStr = "";
PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(privateKey);
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pk = kf.generatePrivate(peks);
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(pk);
signature.update(plainText.getBytes("GBK"));
byte[] signedData = signature.sign();
signedStr = Base64.getEncoder().encodeToString(signedData);
} catch (Exception e) {
e.printStackTrace();
}
return signedStr;
}
}
package cn.gq.financial.bills;
import com.google.common.collect.Maps;
import java.util.EnumMap;
import java.util.Map;
/**
* Created with IntelliJ IDEA
* User: zhiyong.fan
* Date: 2016/10/11
* Time: 10:28
* Desc: 微信帐单
*/
public class WeichatBills extends AbstractBills {
/**
* 微信帐单拉取接口地址
*/
protected static final String WEICHAT_BILLS_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";
public WeichatBills(int currentMonth, String billDir) {
super(currentMonth, billDir, WEICHAT_BILLS_URL);
}
@Override
public EnumMap<PayType, Map<String, String>> assembleReqUrlParams(String billDate) {
// 微信现金贷帐户接口请求参数
Map<String, String> weiChatXjdInfo = Maps.newLinkedHashMap();
weiChatXjdInfo.put("appid", "wx7bf214ccdcbd2e16");
weiChatXjdInfo.put("bill_date", billDate);
weiChatXjdInfo.put("bill_type", "ALL");
weiChatXjdInfo.put("mch_id", "1369557602");
weiChatXjdInfo.put("nonce_str", "D51B6C086BAC6BDD612954A2608A3DB7");
weiChatXjdInfo.put("key", "D51B6C086BAC6BDD612954A2608A3DB7");
// 微信白条帐户接口请求参数
Map<String, String> weiChatBaitiaoInfo = Maps.newLinkedHashMap();
weiChatBaitiaoInfo.put("appid", "wx7bf214ccdcbd2e16");
weiChatBaitiaoInfo.put("bill_date", billDate);
weiChatBaitiaoInfo.put("bill_type", "ALL");
weiChatBaitiaoInfo.put("mch_id", "1369983002");
weiChatBaitiaoInfo.put("nonce_str", "53E9BC685BF2CE47D4C064D3737F890B");
weiChatBaitiaoInfo.put("key", "53E9BC685BF2CE47D4C064D3737F890B");
EnumMap<PayType, Map<String, String>> weiChatInfo = Maps.newEnumMap(PayType.class);
weiChatInfo.put(PayType.WeiChat_Xjd, weiChatXjdInfo);
weiChatInfo.put(PayType.WeiChat_BaiTiao, weiChatBaitiaoInfo);
return weiChatInfo;
}
}
package cn.gq.financial.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Created with IntelliJ IDEA
* User: zhiyong.fan
* Date: 2016/10/11
* Time: 22:16
* Desc: ${DESC}
*/
public class MD5Utils {
public static String build(String content) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
messageDigest.update(content.getBytes());
byte[] domain = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
// converting domain to String
for (int i = 0; i < domain.length; i++) {
if (Integer.toHexString(0xFF & domain[i]).length() == 1) {
md5StrBuff.append("0").append(Integer.toHexString(0xFF & domain[i]));
} else
md5StrBuff.append(Integer.toHexString(0xFF & domain[i]));
}
return md5StrBuff.toString();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment