聚合数据火车票代订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的客服电话人工查询了,这个是真扯的地方。第一次发表文章,写的不好,代码也不咋滴,但还是希望能够分享出来供各位看官参考参考。