本文主要是介绍java农业银行-企业银行ERP接口开发(4-接口对接)(批量代发结果查询),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
没看我之前的文章的先看一下《前期准备》,再来看这篇文章java农业银行-企业银行ERP接口开发(1-前期准备)https://blog.csdn.net/new_public/article/details/133882741
这篇文章我们主要讲:
批平台处理结果查询(IBAQ06)
最近一直想出这篇文章,奈何被一条sql搞了半个月,sql极其复杂,条件无敌多,最让人难受的是,开发环境没有数据,只有测试环境有,公司又不给我测试环境数据库的链接,搞得我看不了sql解释计划,不知道是走没走索引,只能调一下又上测试环境,结果出问题,又继续调.....
回归正题,这篇文章主要讲调用农行批量代发接口后,对代发结果进行查询。
先看请求报文,没有什么太多的特有字段,只有一个交易请求流水号。
这个流水号就是调用批量代发接口的时候,传的流水号<ReqSeqNo>请求方流水号</ReqSeqNo>,所以我在批量汇总代发接口文章里面强调把这个流水号记录到库表,后期用来查询交易结果的。
根据请求报文创建一个实体对象,继承公共请求对象(公共请求对象去第一篇文章拿)。
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;/*** IBAQ06(批平台处理结果查询)请求报文字段* */
@XmlRootElement(name = "ap")
public class IBAQ06RequestDTO extends RequestBaseEntity {/*** ???* */@XmlElement(name = "Corp")private Corp corp;@XmlAccessorType(XmlAccessType.FIELD)public static class Corp {/*** 原批交易请求流水号* */@XmlElement(name = "CustomNo")private String customNo;public String getCustomNo() {return customNo;}public void setCustomNo(String customNo) {this.customNo = customNo;}}public Corp getCorp() {return corp;}public void setCorp(Corp corp) {this.corp = corp;}
}
然后看接口的应答报文
有没有注意到,标签名竟然是中文,好家伙,我都震惊了。
根据应答报文建立应答报文实体对象(里面有个文件对象,是用来解析应答报文文件的时候用的。由于查询IBAF04交易结果和查询IBBF23交易结果的返回文件里面的字段不一致,所以定义了两个文件对象,一个是IBAF04FileObject,一个是IBBF23FileObject,后面处理查询结果的时候,要区别一下是发起哪个接口进行交易的,从而选择不同的文件对象进行解析),并继承公共应答报文对象。get set 方法自己加,或者打个Lombok注解。
import javax.xml.bind.annotation.*;
import java.util.List;/*** IBAQ06(批平台处理结果查询)应答报文字段* */
@XmlRootElement(name = "ap")
@XmlAccessorType(XmlAccessType.FIELD)
public class IBAQ06ResponseDTO extends ResponseBaseEntity {/*** 原批交易类别(批交易码)* */@XmlElement(name = "TransType")private String transType;/*** ????* */@XmlElement(name = "BatchPlat")private BatchPlat batchPlat;/*** IBAF04本行应答报文返回的文件对象* */@XmlTransientprivate List<IBAF04FileObject> IBAF04FileObjectList;/*** IBBF23他行应答报文返回的文件对象* */@XmlTransientprivate List<IBBF23FileObject> IBBF23FileObjectList;@XmlAccessorType(XmlAccessType.FIELD)public static class BatchPlat {/*** 批处理* */@XmlElement(name = "批处理")private BatchHandler batchHandler;/*** 批处理信息类* */@XmlAccessorType(XmlAccessType.FIELD)public static class BatchHandler {/*** 状态* */@XmlElement(name = "状态")private String status;/*** 子状态* */@XmlElement(name = "子状态")private String substate;/*** 描述* */@XmlElement(name = "描述")private String describe;/*** 成功金额* */@XmlElement(name = "成功金额")private String successAmt;/*** 成功笔数* */@XmlElement(name = "成功笔数")private String successNum;/*** 失败金额* */@XmlElement(name = "失败金额")private String failAmt;/*** 失败笔数* */@XmlElement(name = "失败笔数")private String failNum;/*** 异常金额* */@XmlElement(name = "异常金额")private String exceptionAmt;/*** 异常笔数* */@XmlElement(name = "异常笔数")private String exceptionNum;/*** 落地金额* */@XmlElement(name = "落地金额")private String groundAmt;/*** 落地笔数* */@XmlElement(name = "落地笔数")private String groundNum;}}// 文件对象public static class IBAF04FileObject {/*** 客户编号* */private String customerCode;/*** 收款人姓名* */private String crAccName;/*** 收款人账号* */private String crBankNo;/*** 币种* */private String cur;/*** 账簿号* */private String accountBookCode;/*** 交易金额* */private Double amt;/*** 摘要* */private String excerpt;/*** 合约校验标志 01-强制校验 02-均不校验* */private String contractFlag;/*** 户名校验标志 0-否 1-是* */private String accountFlag;/*** 客户移动电话校验标识 0-否 1-是* */private String phoneFlag;/*** 移动电话* */private String phone;/*** 注册证件类型代码* */private String idCardType;/*** 证件号码* */private String idCardCode;/*** 处理结果* */private String handlerResult;/*** 返回码* */private String responseCode;/*** 返回码中文描述* */private String responseCodeDesc;}// 文件对象public static class IBBF23FileObject {/*** 交易序号* */private String tradeNo;/*** 客户请求序号* */private String customerCode;/*** 系统流水号* */private String systemSerialNo;/*** 交易日期* */private String tradeDate;/*** 交易类型* */private String tradeType;/*** 交易金额* */private Double amt;/*** 处理结果* */private String handlerResult;/*** 结果描述* */private String resultDesc;/*** 对方户名* */private String crAccName;/*** 对方账号* */private String crBankNo;/*** 用途* */private String useTo;}}
来到业务场景,调用批量代发后,记录请求流水号,后面定时去刷交易结果就行了,大概发起后十分钟就可以去刷了,具体看批量付款的收款对象有多少个吧,太多就要等得久一些,前期测试一个收款账户,失败的话,两三分钟内出结果,成功的话可能得四五分钟这样。
AbcErpToIctSocket农行请求工具类(去第二篇文章,有专门解释,这里不解释这个类了)增加一个方法,专门用来查询批量代发结果的。
/*** IBAQ06 批平台处理结果查询* @transCode 批量代发交易接口码 IBAF04 | IBBF23* */public Map<String, Object> ibaq06(IBAQ06RequestDTO requestEntity, String transCode) {if (requestEntity == null) {log.error("IBAQ06报文请求对象不能为空!");return null;}requestEntity.setCctransCode(CctransCodeEnum.IBAQ06.code());requestEntity.setIsEncryption("0");// 开始socket请求Map<String, Object> socketResponseMap = socket(requestEntity, IBAQ06ResponseDTO.class);// 解析报文返回的文件对象Object responseObject = socketResponseMap.get("responseEntity");if (responseObject != null) {IBAQ06ResponseDTO ibaq06Response = (IBAQ06ResponseDTO) responseObject;// 本行if (StringUtils.equals(transCode, "IBAF04")) {List<IBAQ06ResponseDTO.IBAF04FileObject> fileObjectList = new ArrayList<>();this.analysisFileObject(ibaq06Response, fileObjectList, IBAQ06ResponseDTO.IBAF04FileObject.class);if (!CollectionUtils.isEmpty(fileObjectList)) {ibaq06Response.setIBAF04FileObjectList(fileObjectList);}} else if (StringUtils.equals(transCode, "IBBF23")) {// 他行List<IBAQ06ResponseDTO.IBBF23FileObject> fileObjectList = new ArrayList<>();this.analysisFileObject(ibaq06Response, fileObjectList, IBAQ06ResponseDTO.IBBF23FileObject.class);if (!CollectionUtils.isEmpty(fileObjectList)) {ibaq06Response.setIBBF23FileObjectList(fileObjectList);}}}return socketResponseMap;}
上面的socket方法去第二篇文章看,这里不说了,这里重点说analysisFileObject方法,这个是用来解析应答文件的。
/*** 解析应答报文返回的文件* @responseEntity 应答报文对象* @fileObjectList 文件对象List,用来装载已经解析好的对象* @fileObjectClass 文件对象class* */private <R extends ResponseBaseEntity> void analysisFileObject(R responseEntity, List fileObjectList, Class fileObjectClass) {// 如果有文件标识,解析文件if (responseEntity != null && responseEntity.getFileFlag() != null && responseEntity.getFileFlag() == 1) {// 文件名String batchFileName = responseEntity.getCmp().getBatchFileName();// 取出文件类所有字段Field[] fields = fileObjectClass.getDeclaredFields();// 字段转成数组, 值为字段名String[] respHeadArray = new String[fields.length];for (int index = 0; index < fields.length; index++) {respHeadArray[index] = fields[index].getName();}if (StringUtils.isNotBlank(batchFileName)) {// 组合文件全路径String fileFullPath = new StringBuffer(ictFileDownloadPath).append("/").append(batchFileName).toString();File file = new File(fileFullPath);// 如果文件存在if (file.exists()) {try {// 读取文件 FileUtils 是 org.apache.commons.io 包下的String fileContent = FileUtils.readFileToString(file, "GBK");// 如果文件内容有值if (StringUtils.isNotBlank(fileContent)) {// 文件内容转成数组String[] contentArray = fileContent.split("\\n");ObjectMapper objectMapper = new ObjectMapper();// 文件里面每一行各个文件的分隔符String fieldSeparator = "\\|\\_\\|";// 遍历文件内容for (int contentIndex = 0; contentIndex < contentArray.length; contentIndex++) {Map<String, String> map = new HashMap<>();// 把每一行的值转成数组,后面拼一个空格,避免等会转换成数组的时候,如果后面都是|_||_|,会丢失后面的几个空字符串String[] contentValueArray = new StringBuffer(contentArray[contentIndex]).append(" ").toString().split(fieldSeparator);// 循环设置字段值for (int i = 0; i < respHeadArray.length; i++) {// uncapitalize 首字母小写 StringUtils 是org.apache.commons.lang3 包下的map.put(StringUtils.uncapitalize(respHeadArray[i]), contentValueArray[i]);}// map转成实体fileObjectList.add(objectMapper.convertValue(map, fileObjectClass));}}} catch (IOException e) {log.error("应答报文MFS文本文件解析失败", e);}}}}}
接下来就是测试了,new一个请求报文对象,并设置交易流水号即可。
// 记得注入 AbcErpToIctSocket 工具类// @Autowired// private AbcErpToIctSocket abcErpToIctSocket;IBAQ06RequestDTO ibaq06Request = new IBAQ06RequestDTO();IBAQ06RequestDTO.Corp corp = new IBAQ06RequestDTO.Corp();corp.setCustomNo("PO23111316564908xxxx");ibaq06Request.setCorp(corp);Map<String, Object> stringObjectMap = abcErpToIctSocket.ibaq06(ibaq06Request);
请求报文是这样子的
0285
<ap><CCTransCode>IBAQ06</CCTransCode><ProductID>ICC</ProductID><ChannelType>ERP</ChannelType><CorpNo></CorpNo><OpNo></OpNo><AuthNo></AuthNo><ReqSeqNo></ReqSeqNo><ReqDate>20231122</ReqDate><ReqTime>134006</ReqTime><Sign></Sign><Corp><CustomNo>PO23111316564908xxxx</CustomNo></Corp>
</ap>
应答报文是这样子的,由于填写信息不正确,所以失败了。
0695 <ap><Cmp><RespPrvData></RespPrvData><BatchFileName></BatchFileName><CmeSeqNo>SS20231122134000xxxx</CmeSeqNo></Cmp><BatchPlat><批处理><状态>1</状态><描述>CFRC48失败[EAPAYS1003-调用boe失败!-[rtbasic]输入过渡账户【273201*****0786】有误!]</描述></批处理></BatchPlat><CCTransCode>IBAQ06</CCTransCode><RespDate>20231122</RespDate><RespTime>134008</RespTime><RespSeqNo></RespSeqNo><RespSource>0</RespSource><RespCode>0000</RespCode><RespInfo>交易成功</RespInfo><RxtInfo>批量交易处理失败!</RxtInfo><Cme><RecordNum>0</RecordNum><FieldNum>0</FieldNum></Cme><FileFlag>0</FileFlag>
</ap>
对于应答报文的处理,我是这么写的,对应业务代码自己补齐。
// 记得注入 AbcErpToIctSocket 工具类
// @Autowired
// private AbcErpToIctSocket abcErpToIctSocket;IBAQ06RequestDTO ibaq06Request = new IBAQ06RequestDTO();
IBAQ06RequestDTO.Corp corp = new IBAQ06RequestDTO.Corp();
corp.setCustomNo("PO23111316564908xxxx");
ibaq06Request.setCorp(corp);
Map<String, Object> responseObjectMap = abcErpToIctSocket.ibaq06(ibaq06Request);
// 查询结果前期判断
if (this.socketRequestAfterDetermine(responseObjectMap)) {// 如果应答对象有值if (responseObjectMap.get("responseEntity") != null) {IBAQ06ResponseDTO ibaq06Response = (IBAQ06ResponseDTO) responseObjectMap.get("responseEntity");IBAQ06ResponseDTO.BatchPlat.BatchHandler batchHandler = ibaq06Response.getBatchPlat().getBatchHandler();if (StringUtils.equals(batchHandler.getStatus(), "H")) {// 批处理状态为H,则说明正在处理中,稍后再查询} else if (StringUtils.equals(batchHandler.getStatus(), "1")) {// 批处理状态为1,则说明整批失败(全部失败)} else if (StringUtils.equals(batchHandler.getStatus(), "5")) {// 批处理状态为5,则说明交易处理完成// 如果落地笔数或异常笔数都没有,则说明整批都交易完成,下次不用再定时查询了if (StringUtils.equals(batchHandler.getExceptionNum(), "0") &&StringUtils.equals(batchHandler.getGroundNum(), "0")) {}// 设置成功笔数,成功金额Double.valueOf(batchHandler.getSuccessAmt());Integer.parseInt(batchHandler.getSuccessNum());// 失败金额,失败笔数Double.valueOf(batchHandler.getFailAmt());Integer.parseInt(batchHandler.getFailNum());// 处理每条收款信息的结果,fileObjectList 是前面根据应答文件名获取文件解析后获得的,里面是每一个收款账户的收款信息以及交易状态// 这里必须要确定前面是哪个接口发起的批量交易,这就要在发起批量交易时,记录交易码到数据库里面了List fileObjectList = null;// 本行if (StringUtils.equals(批量代发接口交易码, "IBAF04")) {fileObjectList = ibaq06Response.getIBAF04FileObjectList();} else {// 他行fileObjectList = ibaq06Response.getIBBF23FileObjectList();}if (!CollectionUtils.isEmpty(fileObjectList)) {fileObjectList.forEach(fileObject -> {IBAQ06ResponseDTO.IBAF04FileObject ibaf04FileObject = null;IBAQ06ResponseDTO.IBBF23FileObject ibbf23FileObject = null;// 本行if (StringUtils.equals(批量代发接口交易码, "IBAF04")) {ibaf04FileObject = (IBAQ06ResponseDTO.IBAF04FileObject) fileObject;} else {// 他行ibbf23FileObject = (IBAQ06ResponseDTO.IBBF23FileObject) fileObject;}// 客户编号 这里说一下,这个客户编号是我们批量代发的时候设置上的,这时候会原样返回,方便我们用于回写业务数据String sourceId = ibaf04FileObject != null ? ibaf04FileObject.getCustomerCode() : ibbf23FileObject.getCustomerCode();// 处理结果String handlerResult = ibaf04FileObject != null ? ibaf04FileObject.getHandlerResult() : ibbf23FileObject.getHandlerResult();// 判断这条记录是成功了还是失败 5-失败、E-不合法 这两种状态为明确失败;4-成功 为明确成功;其他状态为状态不确定,等下次查询// 只有明确是成功还是失败了,再根据sourceId更新业务数据if (IBAQ06HandlerStatusEnum.Fail.code().equals(handlerResult) ||IBAQ06HandlerStatusEnum.FailE.code().equals(handlerResult) ||IBAQ06HandlerStatusEnum.Success.code().equals(handlerResult)) {// 每条收款人信息的交易返回码,以及返回码描述,可能是成功信息,也可能是失败原因if (ibaf04FileObject != null) {ibaf04FileObject.getResponseCode();ibaf04FileObject.getResponseCodeDesc();} else {ibbf23FileObject.getResultDesc();}}});}}}
}
socketRequestAfterDetermine 请求后处理方法
/**
* 交易结果查询后,前期通用判断
* @resMap socket请求结果
* @return 是否请求成功
* */
// 有些代码我删掉了,自己完善
private boolean socketRequestAfterDetermine(Map<String, Object> resMap) {if (resMap == null) {// 查询出错return false;} else if (resMap.get("errorMessage") != null) { // 有错误信息resMap.get("errorMessage").toString();return false;} else if (resMap.get("isAlreadyResq") == null) { // 没有发送socket请求return false;} else if (resMap.get("responseEntity") == null) { // 应答报文对象if (resMap.get("responseMessage") != null) {resMap.get("responseMessage").toString();}return false;}return true;
}
这里啰嗦一下,事务最好还是手动开启,每查一次交易结果,就开启一次事务,最后处理完业务数据后就提交事务,然后再开启事务查下一个批量代发交易结果,把它们都分开成独立的一样,因为下一次的查询跟前面的没有任何关系。
枚举类
/*** IBAQ06 批量支付结果查询响应文件里面每条记录的具体支付状态* */
public enum IBAQ06HandlerStatusEnum {Success("4", "成功"),Fail("5", "失败"),FailE("E", "失败");// 其他状态,为不确定状态private String code;private String title;IBAQ06HandlerStatusEnum(String code, String title) {this.code = code;this.title = title;}public String code() {return this.code;}public String title() {return this.title;}
}
好了,又是上班时间写文章,得抓紧干活了,不然明天就被骂了。
码字不易,于你有利,勿忘点赞。
一点飞鸿影下,青山绿水,白草红叶黄花
这篇关于java农业银行-企业银行ERP接口开发(4-接口对接)(批量代发结果查询)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!