登录
转载

火车票订票功能怎么做?

专栏API的典型场景应用
发布于 2021-02-21 阅读 12
  • 程序员
  • API
  • Spring Boot
转载

聚合数据火车票代订API

关于聚合

聚合优点是,它提供了一套完整的代订火车票流程。从查询车票信息,车站简码到提交订单,取消未支付订单,出票,最后退票整体流程已经完全覆盖。不过它的退票有点恶心,需要人工联系。所以我们就抛弃了它的退票功能。
具体官网请参见:https://www.juhe.cn/docs/api/id/173
具体官网说明文档:http://code.juhe.cn/docs/201

开始实现

首先是整个的核心TicketUtil工具类,提供了提交,取消,状态查询和出票功能,并且检测对应返回结果。由于订单状态不会实时返回,聚合数据这边提供了一种方式设置回调,但想想,把系统的主动权交给聚合,所以本屌就抛弃了回调方式,而是使用主动请求方式去获取订单状态。后面会提供对应的demo。首先咱们需要做好整个demo的根基,先看看对应的几个工具类。

public static final String APPKEY = "XXXXXXXXXXX";//您所申请的appkey
 /**
     * 订单状态查询
     * 接口url:http://op.juhe.cn/trainTickets/orderStatus
     */
    public static ResponseResult getOrderStatus(String orderid){
        String url="http://op.juhe.cn/trainTickets/orderStatus?orderid="+orderid+"&key="+APPKEY;
        String data = HttpUtils.get(url);
        return checkRespnseData(data);
    }
/**
 * 提交订单,并返回聚合数据订单编号
 * @param params
 * @return
 */
public static ResponseResult submitOrder(JSONObject params) {
    params.put("key", APPKEY);
    //预留订单号
    params.put("user_orderid", getUserOrderId());

    String url = "http://op.juhe.cn/trainTickets/submit";
    String data = HttpUtils.sendPost(url, params);
    ResponseResult responseResult = checkRespnseData(data);
    return responseResult;
}

/**
 * 取消订单
 * @param orderid
 * @return
 */
public static ResponseResult cancelOrder(String orderid){
    String url = "http://op.juhe.cn/trainTickets/cancel?dtype=json&orderid=" + orderid + "&key=" + APPKEY;
    String data = HttpUtils.get(url);
    ResponseResult responseResult = checkRespnseData(data);
    if(responseResult.getCode().equals(500) && responseResult.getData().equals("1")){
        responseResult.setCode(200);
    }
    return responseResult;
}

/**
 * 请求出票接口
 * @param orderid
 * @return
 */
public static ResponseResult outTicket(String orderid){
    String url="http://op.juhe.cn/trainTickets/pay?orderid="+orderid+"&key="+APPKEY;
    String data = HttpUtils.get(url);
    ResponseResult responseResult = checkRespnseData(data);
    return responseResult;
}
private static ResponseResult checkRespnseData(String data){
    if (StringUtil.isEmpty(data)){
        return ResponseResult.FAIL("接口请求异常");
    }
    JSONObject obj = JSONObject.parseObject(data);
    String errorCode = obj.getString("error_code");
    String reason = obj.getString("reason");
    //判断是否调用成功 error_code = 0时调用成功
    if ("0".equals(errorCode)) {
        String result = obj.getString("result");
        if (StringUtil.isEmpty(result)) {
            return ResponseResult.FAIL("返回值为空");
        }
        JSONObject jsonResult = new JSONObject();
        //如果解析json异常,则证明处为出票阶段,因为result中只包含一个String串
        try {
            jsonResult = JSONObject.parseObject(result);
        }catch (Exception e){
            return ResponseResult.SUCCESS(result);
        }

        String status = jsonResult.getString("status");
        //如果没有status数据,则证明请求成功,返回的数据为对应的聚合orderid
        if(StringUtil.isEmpty(status)){
            return ResponseResult.SUCCESS(jsonResult);
        }
        //如果status等于1或者5,则订单订票不成功或者出票不成功,但如果是取消订单操作则需要将状态为1的情况转换成success
        if( status.equals("1") || status.equals("5")){
            String message = jsonResult.getString("msg");
            return ResponseResult.FAIL(message,status);
        }
        return ResponseResult.SUCCESS(jsonResult);
    }else{
        return ResponseResult.FAIL(reason,errorCode);
    }
}
  • 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

返回结果类ResponseResult

public class ResponseResult {
private static final Integer SUCCESS_CODE = 200;
private static final Integer FAIL_CODE = 500;
private static final Integer NO_LOGIN_CODE = 305;

@ApiModelProperty(value = "类型编号,200:成功,500:不成功,305:未登录")
private Integer code;
@ApiModelProperty(value = "返回消息内容")
private String message;
@ApiModelProperty(value = "返回数据")
private Object data;

public ResponseResult(Integer code, String message, Object data) {
    this.code = code;
    this.message = message;
    this.data = data;
}

public ResponseResult(Integer code, String message) {
    this.code = code;
    this.message = message;
}

public ResponseResult(Integer code) {
    this.code = code;
}

public ResponseResult(Integer code, Object data) {
    this.code = code;
    this.data = data;
}

public static ResponseResult SUCCESS(String message, Object data){
    return new ResponseResult(SUCCESS_CODE,message,data);
}

public static ResponseResult SUCCESS(Object data){
    return new ResponseResult(SUCCESS_CODE,data);
}

public static ResponseResult SUCCESS(){
    return new ResponseResult(SUCCESS_CODE);
}

public static ResponseResult SUCCESS(String message){
    return new ResponseResult(SUCCESS_CODE,message);
}
public static ResponseResult FAIL(){
    return new ResponseResult(FAIL_CODE);
}

public static ResponseResult FAIL(String message){
    return new ResponseResult(FAIL_CODE,message);
}

public static ResponseResult FAIL(Object data){
    return new ResponseResult(FAIL_CODE,data);
}

public static ResponseResult FAIL(String message,Object data){
    return new ResponseResult(FAIL_CODE,message,data);
}

public static ResponseResult NO_LOGIN(){
    return new ResponseResult(NO_LOGIN_CODE);
}

public static ResponseResult LOGIN_INVIDA(){
    return new ResponseResult(NO_LOGIN_CODE);
}

public Integer getCode() {
    return code;
}

public void setCode(Integer code) {
    this.code = code;
}

public String getMessage() {
    return message;
}

public void setMessage(String message) {
    this.message = message;
}

public Object getData() {
    return data;
}

public void setData(Object data) {
    this.data = data;
}

}

  • 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

HttpUtils类

public static String sendPost(String url, JSONObject params) {
        String urlParams = "";
        for (Iterator iter = params.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String value = String.valueOf(params.get(name));
            if(StringUtil.isEmpty(urlParams)){
                urlParams = urlParams + name + "=" + value;
            }else{
                urlParams = urlParams + "&" + name + "=" + value;
            }
            //System.out.println(name +"-"+value);
        }
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(urlParams);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

public static String get(String url) {
String result = “”;
BufferedReader br = null;
InputStream inputstream = null;
InputStreamReader isr = null;

    try {
        URL readerUrl = new URL(url);
        URLConnection conn = readerUrl.openConnection();
        conn.connect();
        inputstream = conn.getInputStream();
        isr = new InputStreamReader(inputstream);
        br = new BufferedReader(isr);
        result = br.readLine();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    } finally {
        try {
            assert inputstream != null;
            inputstream.close();
            assert br != null;
            br.close();
            isr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return result;
}
  • 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

订单状态异步定时任务请求

该类提供异步定时请求聚合数据订单状态查询功能,当查询到对应咱们所需要的状态接口,执行自定义逻辑业务,发短信或者订单入库等等,想怎么做就看各位看官的心情了。
此类我角色很有很多线程安全问题,由于本屌不怎么擅长,所以优化神马的就交给各位看官大大了。
另请自行定义对应的接口类哈

public class ScheduleServiceImp extends BaseService implements ScheduleService {
@Value(value = "${cronQueryStatus}")
private String cronQueryStatus;//自己写cron表达式哈 本屌在springboot配置文件中添加了cronQueryStatus: 0/1 * * * * ?

@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;

private ScheduledFuture<?> futureSubmit;

private ScheduledFuture<?> futureOut;

@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
    return new ThreadPoolTaskScheduler();
}

public synchronized void startCronForSubmitOrder(String orderid) {
    futureSubmit = threadPoolTaskScheduler.schedule(new Runnable(){
        volatile int i = 0;
        @Override
        public void run() {
            synchronized (this){
                System.out.println("开始查询订单状态");
                //查询订单状态3次返回异常结果,中断查询
                if(i >= 3){
                    stopSubmitCron();
                }
                ResponseResult orderStatus = TicketsUtil.getOrderStatus(orderid);
                if(orderStatus.getCode().equals(500)){
                    i++;
                    System.out.println(i);
                    log.error(orderStatus.getMessage());
                    return;
                }
                JSONObject orderInfo = (JSONObject) orderStatus.getData();
                String status = orderInfo.getString("status");
                //如果订单状态是0:提交订单,待处理,3:已支付,等待出票,则返回继续查询,该定时任务为了执行刚提交的订单等待占座,因此各位看官及时把握
                if(status.equals("0") || status.equals("3")|| status.equals("4")|| status.equals("5")){
                    return;
                }
                if(doYourSelfFunction()){ //此处添加自己业务方法
                    stopSubmitCron();
                }
            }
        }
    }, new Trigger() {
        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            return new CronTrigger(cronQueryStatus).nextExecutionTime(triggerContext);
        }
    });
    System.out.println("开始查询提交订单查询定时任务");
}

public synchronized void startCronForOutOrder(String orderid) {
    futureOut = threadPoolTaskScheduler.schedule(new Runnable(){
        volatile int i = 0;
        @Override
        public void run() {
            synchronized (this){
                System.out.println("开始查询订单出票状态");
                //查询订单状态3次返回异常结果,中断查询
                if(i >= 3){
                    stopOutCron();
                }
                ResponseResult orderStatus = TicketsUtil.getOrderStatus(orderid);
                if(orderStatus.getCode().equals(500)){
                    i++;
                    System.out.println(i);
                    log.error(orderStatus.getMessage());
                    return;
                }
                JSONObject orderInfo = (JSONObject) orderStatus.getData();
                String status = orderInfo.getString("status");
                if(status.equals("0") || status.equals("3") || status.equals("1") || status.equals("2")){
                    return;
                }
                if(modifyOrderStatus(orderInfo)){
                    stopOutCron();
                }
            }
        }
    }, new Trigger() {
        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            return new CronTrigger(cronQueryStatus).nextExecutionTime(triggerContext);
        }
    });
    System.out.println("开始出票订单查询定时任务");
}

public boolean cronSubmitStatus() {
    if (futureSubmit != null) {
        return futureSubmit.isCancelled();
    }
    return false;
}

public boolean cronOutStatus() {
    if (futureOut != null) {
        return futureOut.isCancelled();
    }
    return false;
}

public void stopSubmitCron() {
    if (futureSubmit != null) {
        futureSubmit.cancel(true);
    }
    System.out.println("取消提交订单查询定时任务");
}

public void stopOutCron() {
    if (futureOut != null) {
        futureOut.cancel(true);
    }
    System.out.println("取消出票订单查询定时任务");
}
  • 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
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120

Controller类来啦

本屌只提供提交,取消和出票三接口demo哈

@PostMapping("/postIndent")
    public ResponseResult postIndent(@RequestBody JSONObject params){
        ResponseResult responseResult = TicketsUtil.submitOrder(params);
        if(responseResult.getCode().equals(500)){
            return responseResult;
        }
        JSONObject jsonResult = (JSONObject) responseResult.getData();
    String orderId = jsonResult.getString("orderid");

    Long startTime = System.currentTimeMillis();
    //触发定时任务请求订单状态
    scheduleService.startCronForSubmitOrder(orderId);
    while(!scheduleService.cronSubmitStatus()) {
        continue;
    }
    Long endTime = System.currentTimeMillis();
    System.out.println((endTime - startTime)/1000);
    ResponseResult statusOrder = TicketsUtil.getOrderStatus(orderId);
    if(statusOrder.getCode().equals(500)){
        return statusOrder;
    }
    return returnResultByStatus(orderId);
}

//取消待支付的訂單
@GetMapping("/cancel")
public ResponseResult cancelOrder(String orderid){
    ResponseResult responseResult = TicketsUtil.cancelOrder(orderid);
    if(responseResult.getCode().equals(500)){
        return responseResult;
    }

    return responseResult;
}


//支付预留接口  請求出票
@ApiOperation(value = "请求出票", notes = "请求出票")
@GetMapping("/outTicket")
public ResponseResult outTicket(String orderid){
    boolean payResult = pay();//自定支付方法
    //如果支付失败,则return,成功执行出票方法,毕竟先收钱,后办事嘛
    if (payResult){
    	return responseResult;
    }
    ResponseResult responseResult = TicketsUtil.outTicket(orderid);
    if(responseResult.getCode().equals(500)){
        return responseResult;
    }
    //触发定时任务请求订单状态
    scheduleService.startCronForOutOrder(orderid);
    return returnResultByStatus(orderid);
}
//自行处理返回提示,仅提供参照
private ResponseResult returnResultByStatus(String orderid){
    String message = "1";
    switch (status){
        case "0" : message = "提交订单成功,等待处理"; break;
        case "1" : message = "提交订单成功,处理失败"; break;
        case "2" : message = "订单处理成功,等待支付"; break;
        case "3" : message = "订单支付成功,等待出票"; break;
        case "4" : message = "订单出票成功"; break;
        case "5" : message = "订单出票失败"; break;
    }
    return ResponseResult.SUCCESS(message,orderid);
}
  • 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

测试

提交订单json demo,切记及时保存orderid,这个也是你后续的所有操作的唯一id。
另有一张是1元钱的车票 泰山到上高镇的请各位自行封装哈

{"start_time":"07:00","arrive_time":"08:30","arrive_days":"0","run_time_minute":"90","run_time":"1:30","dtype":"json","train_date":"2019-04-24","is_accept_standing":"yes","choose_seats":"1A","from_station_code":"XPY","to_station_code":"XAY","checi":"K680","passengers":"[{\"passengerid\":2,\"passengersename\":\"看官的姓名\",\"piaotype\":\"1\",\"piaotypename\":\"成人票\",\"passporttypeseid\":\"1\",\"passporttypeseidname\":\"二代身份证\",\"passportseno\":\"看官的身份证号\",\"price\":\"11.0\",\"zwcode\":\"1\",\"zwname\":\"硬座\"}]"}
  • 1

执行完出票后结果返回出票成功,就需要各位看官拨打12306的客服电话人工查询了,这个是真扯的地方。第一次发表文章,写的不好,代码也不咋滴,但还是希望能够分享出来供各位看官参考参考。

<link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-d7a94ec6ab.css" rel="stylesheet"> <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-80ad9b4f5b.css" rel="stylesheet">

评论区

励志做一条安静的鳄鱼。

0

0

0