第3方接口交互-日志方案
1. 打印log日志
2. 异步记录到日志表中
日志表
CREATE TABLE `d_interaction_log` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增长id',
`client_code` varchar(20) NOT NULL COMMENT '系统标识',
`direction` char(1) NOT NULL COMMENT '数据流向:出(S)/入(R)',
`key_code` varchar(50) DEFAULT NULL COMMENT '业务主键/applycd',
`serialnumber` varchar(50) DEFAULT NULL COMMENT '流水号',
`interface_name` varchar(200) DEFAULT NULL COMMENT '接口名称',
`remotehost` varchar(200) DEFAULT NULL COMMENT '访问主机地址',
`input_params` mediumtext COMMENT '入参',
`output_params` mediumtext COMMENT '出参',
`request_time` datetime DEFAULT NULL COMMENT '开始请求时间',
`response_time` datetime DEFAULT NULL COMMENT '收到响应时间',
`time_length` int(11) DEFAULT NULL COMMENT '耗时:ms',
`result` int(11) DEFAULT NULL COMMENT '调用结果:1,成功;0,失败',
`remark` varchar(500) DEFAULT NULL COMMENT '备注,发生错误时放入异常信息',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`user_id` varchar(50) DEFAULT NULL COMMENT '用户id',
PRIMARY KEY (`id`),
KEY `IDX_KEYCODE` (`key_code`),
KEY `IDX_SERNUM` (`serialnumber`)
) ENGINE=InnoDB AUTO_INCREMENT=1DEFAULT CHARSET=utf8 COMMENT='与外部业务系统交互日志'
日志 bean
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
@TableName("d_interaction_log")
@Getter
@Setter
public class InteractionLog implements Serializable {
private static final long serialVersionUID = 1L;
/** 自增长id */
@TableId(type = IdType.AUTO)
private Integer id;
/** 系统标识 */
private String clientCode;
/** 数据流向:出(S)/入(R) */
private String direction;
/** 业务主键/applycd */
private String keyCode;
/** 流水号 */
private String serialnumber;
/** 接口名称 */
private String interfaceName;
/** 访问主机地址 */
private String remotehost;
/** 入参 */
private String inputParams;
/** 出参 */
private String outputParams;
/** 开始请求时间 */
private Date requestTime;
/** 收到响应时间 */
private Date responseTime;
/** 耗时:ms */
private Integer timeLength;
/** 调用结果:1,成功;0,失败 */
private Integer result;
/** 备注,发生错误时放入异常信息 */
private String remark;
/** 创建时间 */
private Date createTime;
/** 用户id */
private String userId;
};
import org.demo.model.entity.InteractionLog;
import org.cango.midbase.mybatis.CangoBaseMapper;
public interface InteractionLogMapper extends CangoBaseMapper<InteractionLog> {
}
public interface AsyncService {
/**
* 异步保存日志
* @param interLog
*/
void saveInterLog(InteractionLog interLog);
}
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
@Autowired
private InteractionLogMapper interLogMapper;
@Override
@Async("asyncServiceExecutor")
public void saveInterLog(InteractionLog interLog) {
interLogMapper.insert(interLog);
}
}
异步线程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolTaskExecutorConfig {
@Bean
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(20);
//配置最大线程数
executor.setMaxPoolSize(50);
//空闲时间
executor.setKeepAliveSeconds(60);
//配置队列大小
executor.setQueueCapacity(5000);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("thread-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
package org.demo.service.invoke.paycenter.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.cango.base.exception.CangoBusinessException;
import org.demo.config.cache.LocalMapCache;
import org.demo.enums.PayCenterPayChannelEnum;
import org.demo.integration.ChargeBackPaymentRes;
import org.demo.integration.GetQrPayResultResponseModel;
import org.demo.integration.QrResponseModel;
import org.demo.model.dto.req.GetChargeBackResultReq;
import org.demo.model.dto.req.PayReq;
import org.demo.model.dto.req.paycenter.InsPayOrderReq;
import org.demo.model.dto.req.paycenter.PayOrderQueryReq;
import org.demo.model.dto.req.paycenter.QuotationRes;
import org.demo.model.dto.req.paycenter.TradeRefundReq;
import org.demo.model.dto.res.PayRes;
import org.demo.model.entity.CWorkOrderPayment;
import org.demo.model.entity.InteractionLog;
import org.demo.model.entity.quotation.QuotationEntity;
import org.demo.service.invoke.paycenter.PayCenterService;
import org.demo.service.system.AsyncService;
import org.demo.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 支付中心交互
*/
@Service
@Slf4j
public class PayCenterServiceImpl implements PayCenterService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private AsyncService asyncService;
@Override
public ChargeBackPaymentRes queryResult(GetChargeBackResultReq req) {
AssertsUtil.assertsNull(req.getApplyId(), "applyId不能为空");
AssertsUtil.assertsNull(req.getClientId(), "clientId不能为空");
Date start = Calendar.getInstance().getTime();
JSONObject param = new JSONObject();
String responseBody = "";
String remark = null;
Integer result = 1;
// 退款结果查询 url
final String url = "http://xxx.yyy";
try {
// 加解密密钥
final String slatKey = "xxxxx";
// 签名
req.setSign(AESUtil.sign(req, slatKey));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity(req, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
responseBody = responseEntity.getBody();
JSONObject st = JSONObject.parseObject(responseBody);
final String resultCode = st.getString("resultCode");
final String resultMsg = st.getString("resultMsg");
JSONObject obj = st.getJSONObject("obj");
if (!"0000".equals(resultCode)) {
throw new CangoBusinessException("0004", new Object[]{resultMsg});
}
if (obj == null) {
throw new CangoBusinessException("0004", new Object[]{"接口未返回信息"});
}
return JSONObject.parseObject(obj.toJSONString(), ChargeBackPaymentRes.class);
} catch (Exception e) {
log.error("【退款结果查询】失败,入参:{}", req, e);
remark = e.getMessage();
result = 0;
throw new CangoBusinessException("0004", new Object[]{"退款结果查询失败:" + remark});
} finally {
log.info("【退款结果查询】入参:{}, 出参:{}, 入参明文:{}", param, responseBody, req);
// 异步写入日志
InteractionLog interLog = new InteractionLog();
// 系统标识
interLog.setClientCode(TARGET_CLIENT_CODE);
// 数据流向:出(S)/入(R)
interLog.setDirection("S");
// 业务主键
interLog.setKeyCode(req.getApplyId());
// 流水号
interLog.setSerialnumber(req.getApplyId());
// 接口名称
interLog.setInterfaceName(url);
// 入参
interLog.setInputParams(req.toString());
// 出参
interLog.setOutputParams(responseBody);
// 开始请求时间
interLog.setRequestTime(start);
Date end = Calendar.getInstance().getTime();
// 收到响应时间
interLog.setResponseTime(end);
int tl = (int) (end.getTime() - start.getTime());
// 耗时:ms
interLog.setTimeLength(tl);
interLog.setRemotehost(CommonUtil.getHost(url));
// 备注
if (null != remark && remark.length() > 500) {
remark = remark.substring(0, 499);
}
interLog.setRemark(remark);
// 调用结果
interLog.setResult(result);
interLog.setCreateTime(new Date());
asyncService.saveInterLog(interLog);
}
}
}
不积跬步,无以至千里;不积小流,无以成江海。