Springboot集成聚合数据火车票API

本文介绍如何在SpringBoot项目中集成聚合数据的火车票代订API,包括订单状态查询、提交订单、取消订单和出票等功能的实现。通过自定义控制器类和异步定时任务,实现了对订单状态的主动请求,避免了依赖回调方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

聚合数据火车票代订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);
        }
    }

返回结果类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;
    }

}

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;
    }

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

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

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("取消出票订单查询定时任务");
    }

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);
    }

测试

提交订单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\":\"硬座\"}]"}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值