1. xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"
default-lazy-init="true">
<context:component-scan base-package="com.qb.loanew" />
<bean id="qbconfig" class="com.qianbao.config.client.spring.ConfigerFactoryBean"></bean>
<bean class="com.qb.loanew.common.FBPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
<property name="propertiesArray">
<list>
<bean class="java.util.Properties" factory-bean="qbconfig"
factory-method="getProperties">
</bean>
</list>
</property>
</bean>
<task:annotation-driven/>
<bean id ="withholdTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
<property name ="corePoolSize" value ="5" />
<property name ="keepAliveSeconds" value ="300" />
<property name ="maxPoolSize" value ="20" />
<property name ="queueCapacity" value ="100" />
</bean>
<bean id ="withdrawTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
<property name ="corePoolSize" value ="5" />
<property name ="keepAliveSeconds" value ="300" />
<property name ="maxPoolSize" value ="20" />
<property name ="queueCapacity" value ="100" />
</bean>
<import resource="applicationContext-db.xml"/>
<import resource="applicationContext-dubbo.xml" />
</beans>
注意:
(1)命名空间配置 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
(2)自动扫描注解 <context:component-scan base-package="com.qb.loanew" />
(3)任务驱动 <task:annotation-driven/>
2.
package com.qb.loanew.service.impl;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qb.loanew.common.HttpClientFB;
import com.qb.loanew.service.ITnXdCapitalRoutingService;
import com.qb.loanew.user.vo.TnXdWithdrawLogVo;
import com.qb.loanew.user.vo.TnXdWithholdLogVo;
import com.zrj.base.common.lang.DESEncrypt;
import com.zrj.platform.base.common.ApiResult;
import com.zrj.platform.base.log.AppLogger;
@Service
public class TnXdCapitalRoutingService implements ITnXdCapitalRoutingService,InitializingBean,DisposableBean{
private AppLogger logger = new AppLogger(TnXdCapitalRoutingService.class);
private HttpClientFB httpClient;
@Value("${trade.api.address}")
private String notifyAddress;
@Value("${trade.api.query.address}")
private String queryAddress;
@Value("${trade.withhold.notify.callback.address}")
private String whCallbackUrl;
@Value("${trade.withdraw.notify.callback.address}")
private String wdCallbackUrl;
@Value("${trade.api.paysecretkey}")
private String paySecretKey;
@Value("${trade.merchantNo}")
private String merchantNo;
@Override
@Async(value = "withholdTaskExecutor")
public void asynWithhold(TnXdWithholdLogVo vo) {
logger.doInfo("异步发起代扣.tradeNo:"+vo.getTradeNo(), null);
launchWithhold(vo);
}
@Override
@Async(value = "withdrawTaskExecutor")
public void asynWithdraw(TnXdWithdrawLogVo vo) {
logger.doInfo("异步发起提现.batchNo:"+vo.getBatchNo(), null);
launchWithdraw(vo);
}
@Override
public void synWithdraw(TnXdWithdrawLogVo vo) {
logger.doInfo("同步发起提现.batchNo:"+vo.getBatchNo(), null);
launchWithdraw(vo);
}
@Override
public void synWithhold(TnXdWithholdLogVo vo) {
logger.doInfo("同步发起代扣.tradeNo:"+vo.getTradeNo(), null);
launchWithhold(vo);
}
@Override
public Map<String,String> queryOrderStatus(String tradeNo){
return queryOrderStatusFromRouter(tradeNo);
}
@Override
public void destroy() throws Exception {
if(httpClient != null){
httpClient.closeClientResouces();
}
}
@Override
public void afterPropertiesSet() throws Exception {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100);
HttpHost addr = new HttpHost(notifyAddress);
cm.setMaxPerRoute(new HttpRoute(addr), 60);
HttpHost queryAddr = new HttpHost(queryAddress);
cm.setMaxPerRoute(new HttpRoute(queryAddr), 60);
httpClient = new HttpClientFB("UTF-8",
"application/json", true, cm);
}
/**
* 发起代扣
* @param vo
*/
private void launchWithhold(TnXdWithholdLogVo vo){
logger.doInfo("开始处理代扣数据: tradeNo="+vo.getTradeNo(),null);
Map<String, String> paramMap = new HashMap<>();
paramMap.put("tradeNo", vo.getTradeNo());
paramMap.put("platform", "2000");
paramMap.put("merchantNo", merchantNo);
paramMap.put("amount", vo.getWithholdAmount().multiply(new BigDecimal(100)).longValue() + "");
paramMap.put("tradeCurrency", "CNY");
paramMap.put("notifyAddress", whCallbackUrl);
paramMap.put("cardType", "DC");
paramMap.put("cardNo", vo.getBankCardNo());
paramMap.put("accountName", vo.getAccountName());
paramMap.put("holderIdType", "IDCARD");
paramMap.put("holderIdNo", vo.getHolderIdNo());
paramMap.put("holderMobile", vo.getHolderMobile());
paramMap.put("bankName", vo.getBankName());
paramMap.put("bankCode",vo.getBankCode());
paramMap.put("loanSerialNumber", vo.getTradeNo());
paramMap.put("subBusinessNo", "1001");// 固定
paramMap.put("subBusinessName", "贷后"); // 固定
paramMap.put("payTool", "8"); // 固定 闪付代扣
paramMap.put("agentDeductType", "2");//代扣类型1:普通代扣 2:闪付代扣
String jsonParam = JSON.toJSONString(paramMap);
logger.doInfo("代扣请求数据: tradeNo="+vo.getTradeNo()+", 参数串="+jsonParam,null);
String secretBody = null;
try {
secretBody = DESEncrypt.encrypt(paySecretKey, jsonParam);
} catch (Exception e) {
logger.doError("",e);
return;
}
Map<String, Object> context = new HashMap<>();
context.put("context", secretBody);
context.put("mainPlatform", "2000");
String body = JSON.toJSONString(context, true);
HttpEntity httpEntity = null;
try {
httpEntity = httpClient.doPost(notifyAddress, null, body, paramMap, 3000);
String response = EntityUtils.toString(httpEntity);
ApiResult<String> apiResult = JSONObject.parseObject(response, ApiResult.class);
logger.doInfo("message:"+apiResult.getMessage(),null);
String result = DESEncrypt.decrypt(paySecretKey, apiResult.getResult());
logger.doInfo("代扣返回结果为:result="+result+" tradeNo:"+vo.getTradeNo(),null);
} catch (Exception e) {
logger.doError("",e);
}finally{
httpClient.closeSelfResouces();
}
}
/**
* 发起提现
* @param vo
*/
private void launchWithdraw(TnXdWithdrawLogVo vo){
logger.doInfo("开始处理提现数据: batchNo="+vo.getBatchNo(),null);
Map<String, String> paramMap = new HashMap<>();
paramMap.put("tradeNo", vo.getBatchNo());
paramMap.put("platform", "2000");//约定
paramMap.put("merchantNo", merchantNo);
paramMap.put("amount", vo.getWithdrawAmount().multiply(new BigDecimal(100)).longValue() + "");
paramMap.put("tradeCurrency", "CNY");
paramMap.put("notifyAddress", wdCallbackUrl);
paramMap.put("cardType", "DC");
paramMap.put("cardNo", vo.getBankCardNo());
paramMap.put("accountName", vo.getAccountName());
paramMap.put("holderIdType", "IDCARD");
paramMap.put("holderIdNo", vo.getAccountIdCard());
paramMap.put("holderMobile", vo.getAccountMobile());
paramMap.put("bankName", vo.getBankName());
paramMap.put("bankCode",vo.getBankCode());
//paramMap.put("accountType","02");
paramMap.put("loanSerialNumber", vo.getBatchNo());
paramMap.put("subBusinessNo", "1001");// 固定
paramMap.put("subBusinessName", "车贷"); // 固定
paramMap.put("payTool", "1"); // 固定
String jsonParam = JSON.toJSONString(paramMap);
logger.doInfo("提现请求数据: batchNo="+vo.getBatchNo()+", 参数串="+jsonParam,null);
String secretBody = null;
try {
secretBody = DESEncrypt.encrypt(paySecretKey, jsonParam);
} catch (Exception e) {
logger.doError("",e);
return;
}
Map<String, Object> context = new HashMap<>();
context.put("context", secretBody);
context.put("mainPlatform", "2000");
String body = JSON.toJSONString(context, true);
HttpEntity httpEntity = null;
try {
httpEntity = httpClient.doPost(notifyAddress, null, body, paramMap, 3000);
String response = EntityUtils.toString(httpEntity);
ApiResult<String> apiResult = JSONObject.parseObject(response, ApiResult.class);
String result = DESEncrypt.decrypt(paySecretKey, apiResult.getResult());
logger.doInfo("提现返回结果为:result="+result+" batchNo:"+vo.getBatchNo(),null);
} catch (Exception e) {
logger.doError("",e);
}finally{
httpClient.closeSelfResouces();
}
}
public Map<String,String> queryOrderStatusFromRouter(String tradeNo){
Map<String, String> paramMap = new HashMap<String, String>();
paramMap.put("tradeNo", tradeNo);
paramMap.put("platform", "2000");
paramMap.put("merchantNo", merchantNo);
String jsonParam = JSON.toJSONString(paramMap);
logger.doInfo("查询订单数据: tradeNo="+tradeNo+", 参数串="+jsonParam,null);
String secretBody = null;
try {
secretBody = DESEncrypt.encrypt(paySecretKey, jsonParam);
} catch (Exception e) {
logger.doError("",e);
throw new RuntimeException(e);
}
Map<String, Object> context = new HashMap<>();
context.put("context", secretBody);
context.put("mainPlatform", "2000");
String body = JSON.toJSONString(context, true);
HttpEntity httpEntity = null;
JSONObject jsonObject = null;
try {
httpEntity = httpClient.doPost(queryAddress, null, body, paramMap, 3000);
String response = EntityUtils.toString(httpEntity);
ApiResult<JSONObject> apiResult = JSONObject.parseObject(response, ApiResult.class);
logger.doInfo("message:"+apiResult.getMessage(),null);
if("20000038".equals(apiResult.getStatus())){
logger.doInfo("查无此单。tradeNo:"+tradeNo, null);
return null;
}
jsonObject = apiResult.getResult();
logger.doInfo("查询订单返回结果:result="+jsonObject+" tradeNo:"+tradeNo,null);
} catch (Exception e) {
logger.doError("",e);
return null;
}finally{
httpClient.closeSelfResouces();
}
String orderId = jsonObject.getString("orderId");
if(orderId == null || orderId.trim().length() ==0){
logger.doInfo("查无此单。tradeNo:"+tradeNo, null);
return null;
}
int orderStatus = jsonObject.getIntValue("orderStatus");
if(orderStatus != 3 && orderStatus != 4){
throw new RuntimeException("资产路由已收单,正在等待行方处理!tradeNo:"+tradeNo);
}
// 银行支付端返回的状态码
Map<String,String> map = new HashMap<String,String>();
map.put("tradeNo", tradeNo);
map.put("payNo", orderId);
map.put("payReponseCode", orderStatus+"");
map.put("payReponseMsg", jsonObject.getString("msg"));
map.put("loanTime", jsonObject.getString("loanTime"));
return map;
}
}
注意:
(1) 配置异步注解 @Async(value = "withholdTaskExecutor")
(2) 异步操作和业务调用类要分离,在TnXdCapitalRoutingService内部调用异步方式无效.