请求报文

# 请求报文

请求报文是指调用第三方接口需要发送的数据。
调用第三方接口无非是 数据处理、组装报文数据、发送请求、响应结果、数据解析返回 这些步骤。
往往一个通道会有很多接口,每个接口或多或少都存在重复性代码,为不做重复的事在开发过程中总结并封装成了通用功能。

在系统中分为:报文模板、报文处理器、请求对象定义、响应对象定义、通用报文发送器、

# 报文处理器

  • 报文处理器写一次就够了,多个接口调用同一个处理器
/**
 * @title  快递100通道处理器
 */
public class Kuaidi100Handler extends AbstractChannelHandler {
	
	/**
	 * 公共参数设置
	 * @param {Object} Channel channel 通道配置参数
	 * @param {Object} Kv data 请求参数
	 */
    @Override
    public void commonPara(Channel channel,Kv data) {
        data.put("customer", channel.getChannelMerchantCode());
        data.put("key", channel.getConfigs().getStr("key"));
    }

	/**
	 * 签名处理
	 * @param {Object} Channel channel 通道配置参数
	 * @param {Object} String src 待签名数据
	 */
    @Override
    public String sign(Channel channel,String src) {
        return DigestUtils.md5Hex(src).toUpperCase();
    }

	/**
	 * 发送数据报文
	 */
    @Override
    public <T extends ChannelResponse> T send(AbstractChannelRequest<T> request){
        //请求参数
		Kv data = request.getData();
		//通道配置参数
        Channel channel = request.getChannel();
		//获取报文数据
        String datagram=getSendDatagram(data);

        //使用LinkedHashMap防止重排序
        Map<String,String> jsonObject = JSONObject.parseObject(datagram,new TypeReference<LinkedHashMap<String,String>>() {}, Feature.OrderedField);

		//获取请求地址
        String url=getSendUrl(channel,request.getRequestType().getCode(),data);
		//发送报文
        ChannelResponse channelResponse=DatagramSender.post(url,channel.getCharset(),jsonObject);

        //把结果丢给实现类处理
        T response = request.getResponseBean();
        response.set(channelResponse,channel);
        response.resolver();

        return response;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 请求对象定义

继承通道请求抽象类:AbstractChannelRequest

@Data
public class KuaiDiQueryRequest extends AbstractChannelRequest<KuaiDiQueryResponse> {

    public KuaiDiQueryRequest() {
        //默认快递100通道处理器处理,下面两个参数不能填错
        this.setChannelCode(ChannelEnum.Code.KT100);//通道编号
        this.setRequestType(ChannelEnum.BT.KT_QUERY);//业务类型
    }

    @NotNull(message = "手机号不能为空")
    @NotEmpty(message = "手机号不能为空")
    private String phone;

    @NotNull(message = "运单编号不能为空")
    @NotEmpty(message = "运单编号不能为空")
    private String expressNo;

    @NotNull(message = "快递类型不能为空")
    @NotEmpty(message = "快递类型不能为空")
    private String logisticsType;

    //选填
    private String form;

    @NotNull(message = "到达地区不能为空")
    @NotEmpty(message = "到达地区不能为空")
    private String to;

    public void setPhone(String phone) {
        this.phone = phone;
        this.setData("phone",phone);
    }

    public void setExpressNo(String expressNo) {
        this.expressNo = expressNo;
        this.setData("expressNo",expressNo);
    }

    public void setLogisticsType(String logisticsType) {
        this.logisticsType = logisticsType;
        this.setData("logisticsType",logisticsType);
    }

    public void setTo(String to) {
        this.to = to;
        this.setData("to",to);
    }

    @Override
    public KuaiDiQueryResponse getResponseBean() {
		//返回响应对象
        return new KuaiDiQueryResponse();
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 响应对象定义

继承通道响应类:ChannelResponse
以下为ChannelResponse对象中主要数据

  • 响应原始数据:responseData
  • 响应结果编码:resultCode (这个可以判断是否请求成功)
  • 通道配置信息:channel
@Data
public class KuaiDiQueryResponse extends ChannelResponse {

    //结果实例
    //{"result":false,"returnCode":"500","message":"查询无结果,请隔段时间再查"}
    //{"message":"ok","nu":"75474284244821","ischeck":"1","condition":"F00","com":"zhongtong","status":"200","state":"3","data":[{"time":"2021-06-07 17:09:20","ftime":"2021-06-07 17:09:20","context":"【北京市】 您的快递已签收, 签收人在【近邻宝的北京大学】(北京市海淀区颐和园路5号)领取。如有疑问请电联:(16619807456), 投诉电话:(15601138815), 您的快递已经妥投。风里来雨里去, 只为客官您满意。上有老下有小, 赏个好评好不好?【请在评价快递员处帮忙点亮五颗星星哦~】"},{"time":"2021-06-07 15:45:18","ftime":"2021-06-07 15:45:18","context":"【北京市】 快件已被【近邻宝的北京大学】代收,如有问题请电联(15601138815),感谢使用中通快递,期待再次为您服务!"},{"time":"2021-06-07 13:54:17","ftime":"2021-06-07 13:54:17","context":"【北京市】 【北京中关村一部】 的近邻宝16619807456(16619807456) 正在第1次派件, 请保持电话畅通,并耐心等待(95720为中通快递员外呼专属号码,请放心接听)"},{"time":"2021-06-07 13:54:10","ftime":"2021-06-07 13:54:10","context":"【北京市】 快件已经到达 【北京中关村一部】"},{"time":"2021-06-07 08:07:39","ftime":"2021-06-07 08:07:39","context":"【保定市】 快件离开 【京南转运中心】 已发往 【北京中关村一部】"},{"time":"2021-06-07 08:02:10","ftime":"2021-06-07 08:02:10","context":"【保定市】 快件已经到达 【京南转运中心】"},{"time":"2021-06-06 00:47:33","ftime":"2021-06-06 00:47:33","context":"【揭阳市】 快件离开 【潮汕中心】 已发往 【京南转运中心】"},{"time":"2021-06-06 00:45:48","ftime":"2021-06-06 00:45:48","context":"【揭阳市】 快件已经到达 【潮汕中心】"},{"time":"2021-06-05 22:23:31","ftime":"2021-06-05 22:23:31","context":"【揭阳市】 快件离开 【揭阳】 已发往 【京南转运中心】"},{"time":"2021-06-05 18:22:17","ftime":"2021-06-05 18:22:17","context":"【揭阳市】 【揭阳】(0663-8580666、0663-8386858、0663-8554100) 的 黄俊鹏榕城业务(18122668557) 已揽收"}]}

    //    200	查询成功	查询成功
    //    400	找不到对应公司	提交数据不完整或者账号未充值, 检查提交的格式是否为x-www-form-urlencoded的post格式
    //    500	查询无结果,请隔段时间再查	表示查询失败,或没有POST提交
    //    501	服务器错误	快递100的服务器出理间隙或临时性异常,有时如果因为不按规范提交请求,比如快递公司参数没有按照文档规定填写等,也会报此错误
    //    502	服务器繁忙	快递100的服务器出理间隙或临时性异常,请联系快递100排查原因
    //    503	验证签名失败	请检查加密方式,param + key + customer 的顺序进行MD5加密,加密后字符串转大写
    //    601	key已过期	没有可用单量,账号需要充值使用

    private Integer expressStatus;

    private Integer status;

    private String remark;

    private Date solicitationTime;//揽收时间
    private Date dispatchTime;//派件时间
    private Date signTime;//签收时间
    private String lastInfo;


    /**
     * @title  解析数据
     * @author Yudao 王冬明(woyuwodao@gmail.com)
     * @date 2021/6/16 下午2:13
     * @version 1.0
     * @package kim.lln.common.channel.response
     */
    public void resolver(){
        if(requestSuccess()){
            String responseData = getResponseData();
            if(StringUtil.isBlank(responseData)){
                setResultCode(RC.RESULT_FAIL.getCode());
                setResultDesc("响应数据为空");
            }else{
                Channel channel = getChannel();
                JSONObject responseJson = JSONObject.parseObject(responseData);
                String state = responseJson.getString("state");//判断签收状态是否存在
                if(state==null){
                    //查询失败
                    String resultCode = responseJson.getString("returnCode");//结果编号
                    String resultDesc = responseJson.getString("message");//结果描述
                    String remark=channel.getConfigs().getStr("state"+resultCode);

                    setRemark(remark==null?"查询物流失败":remark);
                    setResultCode(resultCode);
                    setResultDesc(resultDesc);
                }else{
                    //查询成功
                    expressStatus=Integer.valueOf(state);
                    //    1.正在打包 2.打包完毕 3.已托管物流 4.已完成(已签收、拒签、其他物流状态)
                    //    0     在途	快件处于运输过程中
                    //    1     揽收	快件已由快递公司揽收
                    //    2     疑难	快递100无法解析的状态,或者是需要人工介入的状态, 比方说收件人电话错误。
                    //    3     签收	正常签收
                    //    4     退签	货物退回发货人并签收
                    //    5     派件	货物正在进行派件
                    //    6     退回	货物正处于返回发货人的途中
                    //    7     转投	货物转给其他快递公司邮寄
                    //    10    待清关	货物等待清关
                    //    11    清关中	货物正在清关流程中
                    //    12    已清关	货物已完成清关流程
                    //    13    清关异常	货物在清关过程中出现异常
                    //    14    拒签	收件人明确拒收
                    if(expressStatus==2
                            || expressStatus==3
                            || expressStatus==4
                            || expressStatus==14
                    ){
                        status= 4;//已完成OrderEnum.FlowStatus.FINISH.getCode()
                    }
                    String remark=channel.getConfigs().getStr("state"+state);
                    String resultDesc=responseJson.getString("status")+"|"+state+"|"+responseJson.getString("message");

                    JSONArray datas = responseJson.getJSONArray("data");
                    for (int i = 0; i < datas.size(); i++) {
                        JSONObject data = datas.getJSONObject(i);
                        if(data!=null){
                            String context = data.getString("context")+"";
                            if(context.contains("签收")){
                                String ftime = data.getString("ftime");
                                setSignTime(DateUtil.parse(ftime));
                            }else if(context.contains("派件") || context.contains("派送")){
                                String ftime = data.getString("ftime");
                                setDispatchTime(DateUtil.parse(ftime));
                            }else if(context.contains("揽收") || context.contains("收取快件")){
                                String ftime = data.getString("ftime");
                                setSolicitationTime(DateUtil.parse(ftime));
                            }
                            this.lastInfo=context;
                        }
                    }

                    setRemark(remark);
                    setResultDesc(resultDesc);
                    setResultCode(RC.SUCCESS);//查询成功
                }
            }
        }else{
            setResultCode(RC.RESULT_FAIL.getCode());
            setResultDesc("请求失败");
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

# 报文模板定义

模板标识符: 请求地址:业务类型_URL 定义报文执行步骤:业务类型_STEP

  •   /**无需步骤**/
      NONE,
      /**签名**/
      SIGN,
      /**加密**/
      ENCRYPT,
      /**结果 用于获取数据并发送请求**/
      RESULT,
      /**结果 用于获取数据而不发送请求**/
      GET_DATA,
    

执行步骤:业务类型_数字 步骤从0开始


### 定义业务信息参考ChannelEnum.java BT
### 定义步骤参考ChannelDatagramStep.java SIGN,ENCRYPT,RESULT

-----------------KT_QUERY订单查询-------------------
#tp("KT_QUERY_URL")
https://poll.kuaidi100.com/poll/query.do
#end

### 定义步骤
#tp("KT_QUERY_STEP")
GET_DATA,SIGN,RESULT
#end

### 报文参数-不能有空格
#tp("KT_QUERY_0")
{"com":"#(logisticsType)","phone":"#(phone)","to":"#(to)","resultv2":"0","num":"#(expressNo)","show":"0","order":"desc"}
#end

### 报文签名
#tp("KT_QUERY_1")
#(KT_QUERY0)#(key)#(customer)
#end

### 报文结果
#tp("KT_QUERY_2")
{"param":#(KT_QUERY0),"sign":"#(KT_QUERY1)","customer":"#(customer)"}
#end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 调用通道接口


KuaiDiQueryRequest request=new KuaiDiQueryRequest();
request.setMerchantCode(dto.getMerchantCode());//商户号
request.setChannelCode(ChannelEnum.Code.KT100);//通道编号 可以不设置
request.setRequestType(ChannelEnum.BT.KT_QUERY);//业务类型 每个接口都是一个业务类型
request.setExpressNo(dto.getExpressNo());//运单号
request.setPhone(phone);//手机号
request.setTo(to);//发货地址
request.setLogisticsType(CommonEnum.LogisticsType.getByCode(dto.getLogisticsType()).getKt100());//物流编码

//执行查询
KuaiDiQueryResponse response = DatagramHandlerUtil.handler(request);

//response为查询结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
更新时间: 12/10/2021, 12:58:24 AM