java农业银行-企业银行ERP接口开发(1-前期准备)

公司突然要对接农行的企业银行接口,实现给员工费用报销,充值退款等操作,现记录一下,避免下次开发不记得,我用的是java开发,sockte请求方式对接,全程基于我个人理解,有些地方可能写得不对,多多见谅


开发的接口如下(随便抽一两个说说就好了):

1. 汇兑-单笔对公(CFRT02)交易

2. 查询帐户明细(新全量)(CQRC09)交易

3. 查询金融交易处理状态(CQRT71)交易

4. 本行汇总批量代发(实时)(IBAF04)

5. 他行汇总批量代发(IBBF23)交易

6. 批平台处理结果查询(IBAQ06)


农行对接交易大致流程如下图,企业财务系统ERP(可以理解为客户端或者我们的管理系统)发起交易请求至农行企业通讯平台ICT(这个相当于一个中转站,是一个软件,部署在服务器上面的),通讯平台ICT就会发送请求至农行管理服务端ICS上面,最后就是处理交易。这次对接主要就是弄ERP(客户端)到ICT这部分


前期准备 


农行企业通讯平台ICT(中转站)如下图,相信要对接开发的朋友肯定会有,打开登录后,会看到一个监听端口,默认是15999,不是必须要这个东西在你电脑上,服务器上面有也行,只要知道对方的ip地址以及端口号就可以了


SocketClient工具类,用来进行socket请求用的,至于什么是socket请求,自己百度了,下面是我写的请求方法

public class SocketClient {

    private final Logger log = LoggerFactory.getLogger(getClass());

    // ip
    private String host;

    // 端口
    private Integer port;

    /**
     * TCP Socket
     * */
    public Map<String, Object> tcp(String message, String charsetName) {
        /**
         * 返回结果:<key, Value>
         *  data: 应答数据
         *  errorMessage:程序错误信息
         *  isAlreadyResq: 是否已经发送请求
         * */
        Map<String, Object> resultMap = new HashMap<>();
        if (StringUtils.isBlank(this.host)) {
            try {
                this.host = this.getLocalIp();
            } catch (UnknownHostException e) {
                log.error("获取本机ip失败", e);
            }
            if (StringUtils.isBlank(this.host)) {
                resultMap.put("errorMessage", "获取服务器IP地址失败");
                return resultMap;
            }
        }

        try {
            // 与服务端建立连接
            Socket socket = new Socket(this.host, this.port);
            // 60s超时
            socket.setSoTimeout(60000);
            // 建立连接后获得输出流,发送数据
            socket.getOutputStream().write(message.getBytes(charsetName));
            // 通过shutdownOutput 告诉接收端已经发送完数据,客户端后续只能接受数据
            socket.shutdownOutput();
            resultMap.put("isAlreadyResq", true);
            // 获得响应数据流
            InputStream socketInputStream = socket.getInputStream();
            // 接收服务端返回来的数据
            byte[] bytes = null;
            //读取服务器返回的消息
            if (socketInputStream != null && !socketInputStream.markSupported()) {
                bytes = IOUtils.toByteArray(socketInputStream);
                resultMap.put("errorMessage", null);
                resultMap.put("data", bytes);
            } else {
                resultMap.put("errorMessage", "农行直联无响应数据");
            }
            // 关闭输入流
            socketInputStream.close();
            // 关闭socket
            socket.close();
        } catch (ConnectException e) {
            String error = new StringBuffer("socket连接失败,ip:").append(this.host).append(",端口:").append(this.port).toString();
            resultMap.put("errorMessage", "socket连接失败,请检查农行银企通平台是否已开启");
            log.error(error);
        } catch (SocketTimeoutException e) {
            resultMap.put("isAlreadyResq", true);
            resultMap.put("errorMessage", "socket请求超时");
            log.error("socket请求超时,请求数据为:" + message);
        } catch (IOException e) {
            resultMap.put("isAlreadyResq", true);
            resultMap.put("errorMessage", "socket请求异常");
            log.error("socket请求异常", e);
        }
        return resultMap;
    }

    /**
     * 获取本机Ip
     * */
    private static String getLocalIp() throws UnknownHostException {
        InetAddress address = null;  //获取本机IP地址
        address = InetAddress.getLocalHost();
        String localIP = address.getHostAddress();  //获取本机IP地址字符串
        return localIP;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public SocketClient(String host, Integer port) {
        this.host = host;
        this.port = port;
    }

    public SocketClient(Integer port) {
        this.port = port;
    }

    public SocketClient() {}
}

XmlUtil工具类,用来把对象转成xml格式字符串,或xml格式字符串转成对象

public class XmlUtil {
    /**
     * 将String类型的xml转换成对象
     */
     public static Object convertXmlStrToObject(Class<?> clazz, String xmlStr) throws JAXBException {
         JAXBContext context = JAXBContext.newInstance(clazz);
         // 进行将Xml转成对象的核心接口
         Unmarshaller unmarshal = context.createUnmarshaller();
         StringReader sr = new StringReader(xmlStr);
         Object xmlObject = unmarshal.unmarshal(sr);
         return xmlObject;
     }

    /**
     * Object 转 xml字符串
     * isFormat:是否需要格式化xml内容
     * isDelHead:是否删除头部的 < ?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
     * */
    public static String objectToXml(Object obj, boolean isFormat, boolean isDelHead) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        if (isFormat) {
            // 格式化内容
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        }
        StringWriter writer = new StringWriter();
        marshaller.marshal(obj, writer);
        String xmlResult = writer.toString();
        if (isDelHead) {
            // 去掉开头的<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
            return xmlResult.replaceAll("\\<\\?xml.*\\?>", "");
        }
        return xmlResult;
    }
}

 说说农行的接口对接,其实是通过xml报文形式交互,说白点就是,我发一串xml格式的字符串(请求报文)过去,农行响应返回一串xml字符串(应答报文)回来,然后我们解析返回来的xml字符串,大致就是这个意思。

下面是其中一个请求报文,查账户余额的

而每个接口请求的报文,和返回来的报文,有一部分是相同的,有些是不同的,就是有公共字段,这些字段可以在开发文档里面看得到,我放出来一下


接下来说说开发对接思路,因为请求和响应都是一串xml字符串,为了方便开发,我就把他们定义成了一个实体对象,方便在java代码中操作,我分成了请求报文对象和应答报文对象,因为有公共字段,我又定义了两个基类存放报文公共字段,让后面的实体对象继承。

请求报文公共对象

/**
 * ERP TO ICT 请求报文公共字段
 * */
@XmlRootElement(name = "ap")
@XmlAccessorType(XmlAccessType.FIELD)
public class RequestBaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 交易代码
     * */
    @XmlElement(name = "CCTransCode")
    private String cctransCode;

    /**
     * 产品标识 固定为ICC
     * */
    @XmlElement(name = "ProductID")
    private String productID;

    /**
     * 渠道标识 固定为ERP
     * */
    @XmlElement(name = "ChannelType")
    private String channelType;

    /**
     * 企业技监局代码/客户号
     * */
    @XmlElement(name = "CorpNo")
    private String corpNo;

    /**
     * 企业操作员编号
     * */
    @XmlElement(name = "OpNo")
    private String opNo;

    /**
     * 认证码
     * */
    @XmlElement(name = "AuthNo")
    private String authNo;

    /**
     * 请求渠道流水号
     * */
    @XmlElement(name = "ReqSeqNo")
    private String reqSeqNo;

    /**
     * 请求日期 yyyyMMdd
     * */
    @XmlElement(name = "ReqDate")
    private String reqDate;

    /**
     * 请求时间 HHmmss
     * */
    @XmlElement(name = "ReqTime")
    private String reqTime;

    /**
     * 数字签名
     * */
    @XmlElement(name = "Sign")
    private String sign;

    /**
     * 查询时是否加密 0 不加密 1 加密 请求接口时设置
     * */
    @XmlTransient
    private String isEncryption;
}

应答报文公共对象

/**
 * ICT TO ERP 应答报文公共字段
 * */
@XmlAccessorType(XmlAccessType.FIELD)
public class ResponseBaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 交易代码
     * */
    @XmlElement(name = "CCTransCode")
    private String cCTransCode;

    /**
     * 返回来源
     * */
    @XmlElement(name = "RespSource")
    private String respSource;

    /**
     * 应答流水号
     * */
    @XmlElement(name = "RespSeqNo")
    private String respSeqNo;

    /**
     * 返回日期
     * */
    @XmlElement(name = "RespDate")
    private String respDate;

    /**
     * 返回时间
     * */
    @XmlElement(name = "RespTime")
    private String respTime;

    /**
     * 返回码
     * */
    @XmlElement(name = "RespCode")
    private String respCode;

    /**
     * 返回信息
     * */
    @XmlElement(name = "RespInfo")
    private String respInfo;

    /**
     * 返回扩展信息
     * */
    @XmlElement(name = "RxtInfo")
    private String rxtInfo;

    /**
     * 文件标识 0-无文件 1-有文件
     * */
    @XmlElement(name = "FileFlag")
    private Integer fileFlag;

    /**
     * ???
     * */
    @XmlElement(name = "Cme")
    private Cme cme;

    /**
     * ???
     * */
    @XmlElement(name = "Cmp")
    private Cmp cmp;

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Cme {
        /**
         * 记录数
         * */
        @XmlElement(name = "RecordNum")
        private Integer recordNum;

        /**
         * 字段名 |分隔
         * */
        @XmlElement(name = "RespHead")
        private String respHead;

        /**
         * 字段数
         * */
        @XmlElement(name = "FieldNum")
        private Integer fieldNum;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Cmp {
        /**
         * 私有数据区
         * */
        @XmlElement(name = "RespPrvData")
        private String respPrvData;

        /**
         * 文件名
         * */
        @XmlElement(name = "BatchFileName")
        private String batchFileName;

        /**
         * 续查标志
         * */
        @XmlElement(name = "ContFlag")
        private String contFlag;

        /**
         * 流水状态 具体见TransStaEnum
         * */
        @XmlElement(name = "TransSta")
        private String transSta;
    }
}

公司没有用Lombok,我把getset方法删掉了,自己加吧。至于字段上面那些注解,就是用来将对象跟xml进行转换用的。

@XmlRootElement(name = "ap")  定义xml根节点为ap

@XmlAccessorType(XmlAccessType.FIELD) 把所有字段都绑定上xml

@XmlElement(name = "CCTransCode") 定义这个字段对应的xml标签名

@XmlTransient 转换时忽略此字段

由于接口有点多,对应实体对象就不一一放出来了,放一个出来就可以了


总之实体对象这方面,我的做法就是一个接口对应两个实体对象(一个请求,一个响应),具体请求接口实体对象 继承 请求实体基类,响应的实体对象 继承 响应基类,下面是我的代码结构

前期准备结束,具体开发看下期文章。

码字不易,于你有利,勿忘点赞

望长城内外,惟余莽莽;大河上下,顿失滔滔

1 数据字典定义 6 1.1 字典键值索引(0) 6 1.2 不得转让标识(200) 6 1.3 签收标记(201) 6 1.4 类别(202) 6 1.5 票据类别(203) 7 1.6 追索类型(204) 7 1.7 (再) 贴现种类(205) 7 1.8 到期无条件支付承诺(206) 7 1.9 线上清算标记(207) 7 1.10 申请回复类型(208) 7 1.11 业务类型(209) 7 1.12 回复类型(210) 8 1.13 撤销类型(211) 8 1.14 提示类型(212) 8 1.15 结清状态(213) 9 1.16 业务通知查询类型(214) 9 1.17 处理标识(215) 9 1.18 逾期标识(216) 9 1.19 到期付款标识(217) 9 1.20 追索理由代码(218) 9 1.21 贴现入账行行号(219) 9 1.22 交易代码(电票交易代码,业务结果查询时输入) 10 2 交易接口 11 2.1 通用查询交易 11 2.1.1 查询客户签约信息(CQRT05) 11 2.1.2 查询行名(CQRT06) 12 2.1.3 通用申请类首页查询(CQRM02) 13 2.1.4 查询可回复票据(CQRM01) 16 2.1.5 查询票据信息(多笔)(CQRM44) 19 2.1.6 查询票据详细信息(CQRT14) 21 2.1.7 查询业务种类(CQRM04) 24 2.1.8 查询票据流转信息(CQRM06) 27 2.1.9 业务结果查询(通过账号)(CMLR34) 29 2.2 出票 31 2.2.1 出票申请(CFRT51) 31 2.2.2 电票出票结果查询(CQRM42) 34 2.3 提示收票 36 2.3.1 查询可提示收票票据(CQRT08) 36 2.3.2 提示收票申请(CFRT53) 41 2.4 背书转让 44 2.4.1 查询可背书转让票据(CQRT10) 44 2.4.2 背书转让申请(CFRT55) 46 2.5 承兑回复 48 2.5.1 查询可承兑回复票据(CQRT07) 48 2.5.2 承兑回复申请(CFRT52) 53 2.6 收票回复/背书转让回复 54 2.6.1 查询可收票回复票据(CQRT09) 54 2.6.2 查询可背书回复票据(CQRT11) 58 2.6.3 收票回复申请(CFRT54)/背书回复申请(CFRT71) 62 2.7 质押回复 64 2.7.1 查询可质押回复票据(CQRT18) 64 2.7.2 质押回复申请(CFRT61) 67 2.8 质押解除回复 69 2.8.1 查询可质押解除回复票据(CQRT20) 69 2.8.2 质押解除回复申请(CFRT63) 73 2.9 保证回复 74 2.9.1 保证回复详细查询(CQRT21) 74 2.9.2 保证回复申请(CFRT65) 78 2.10 同意清偿回复 80 2.10.1 查询可同意清偿回复票据(CQRT24) 80 2.10.2 同意清偿回复申请(CFRT68) 83 2.11 提示付款 85 2.11.1 提示付款明细查询(CQRT13) 85 2.11.2 提示付款申请(CFRT58) 87 2.12 质押 89 2.12.1 质押申请(CFRT60) 89 2.13 解质押申请 91 2.13.1 质押解除明细查询(CQRT19) 91 2.13.2 质押解除申请(CFRT62) 93 2.14 保证申请 96 2.14.1 保证申请(CFRT64) 96 2.15 追索申请 98 2.15.1 追索通知明细查询(CQRM10) 98 2.15.2 根据ID查被追索人(CQRT22) 101 2.15.3 追索通知申请(CFRT66) 103 2.16 贴现 105 2.16.1 贴现实付金额试算报文(CQRT12) 105 2.16.2 贴现申请报文(CFRT56) 106 2.17 撤销 110 2.17.1 查询可撤销票据(CQRM03) 110 2.17.2 撤销(CFRT57) 112 2.18 撤票 114 2.18.1 撤票明细查询(CQRT25) 114 2.18.2 撤票明细申请(CFRT69) 118 2.19 同意清偿申请 121 2.19.1 追索同意清偿明细查询(CQRT23) 121 2.19.2 追索同意清偿申请(CFRT67) 124 2.20 支付信用信息查询 127 2.20.1 查询信用记录(CQRT15) 127 2.20.2 查询申请提交(CQRT16) 128 2.20.3 查询申请(CQRT17) 129
农业银行 ERP 接口开发是指开发适用于农业银行企业资源规划(ERP)系统的接口ERP 系统可以帮助银行实现信息的集成、业务流程的优化和效率的提升。接口开发主要是为了与其他系统进行数据的互通和交换。 在农业银行 ERP 接口开发过程中,首先需要进行需求分析和系统设计。根据银行的具体需求,确定需要开发接口功能、数据格式、传输方式等。接下来,开发人员根据设计方案进行编码和测试,确保接口的正确性和稳定性。 银行 ERP 接口开发的关键是与现有系统的对接。农业银行可能拥有多个系统,如核心银行系统、账户管理系统、风险管理系统等。在开发过程中,需要与这些系统进行数据的交换和同步。因此,开发人员需要熟悉银行系统的架构和接口规范,并与相关系统的开发人员进行紧密合作。 另外,农业银行 ERP 接口开发还需要考虑数据的安全性和稳定性。银行作为金融机构,对数据的保密和可靠性要求非常高。因此,在接口开发过程中,需要采取合适的安全措施,如身份验证、数据加密和防止数据篡改等。同时,开发人员还需要测试接口在各种情况下的稳定性和扩展性,以确保接口能够在高负载和异常情况下正常工作。 综上所述,农业银行 ERP 接口开发是一个复杂而重要的任务。它需要开发人员具备深入的银行业务和系统知识,同时还要有丰富的编码和测试经验。只有通过合理的需求分析、系统设计和与相关系统的对接,才能开发出满足农业银行需求的稳定、安全的接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值