一、IBE介绍
IBE(Internet Booking Engine),即互联网订座引擎,是基于因特网的开放平台技术,它为各种用户应用系统提供访问中国航信传统订座业务系统的途径,是采用API(Application Programming interface)方式的接口。
二、IBE分类
IBE接口分为Internal-and-Parser和Client两种,但它们之间没有太大的区别。Internal-and-Parser包中指令类中具有的方法大多为静态方法,所以在每一次执行指令方法时会跟上主机配置。对于Client包中的指令类,只需在新建对象后设置主机配置就行了。
三、IBE的使用
3.1主机配置
主机配置可以采取两种配置方式。
1、配置文件ibeclient.properties,这里以山航为例,内容如下:
ibe.client.app=scairetd1
ibe.client.customn=0
ibe.client.office=SDH888
ibe.client.validationno=0
ibe.server.ip=202.106.139.75
ibe.server.port=6891
在调用IBE类时,它会自动查找该配置文件,各航空公司主机配置不同,且设有IP权限,如果不能连接主机,请先确认是否开通IP权限。
2、写公共方法
public static void connectHost(IBEClient ibe){
ibe.setAgentInfo("SDH888", "0", "20");
ibe.setConnectionInfo("202.106.139.75", 6891);
ibe.setAppName("scairetd1");
}
3.1查询航班
1、在这里我们还是按照E-TERM的操作流程来为旅客订票,首先是查询航班信息:
为了方便调用,这里采用的是client包中的方法类。类名:AV
方法:getAvailability
说明:注释中为Internal包中相关API的用法;主机配置各航空公司不同
public static AvResult av() throws ParseException {// 查询航班
AV avExample = new AV();// AV指令接口
//第一步、连接主机,AV实现了IBEClient接口,所以可以使用写的静态方法完成主机配置
connectHost(avExample);
//处理查询指令参数,需注意多数方法要求的时间参数格式不同
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2012-10-28");
//第二步、查询航班
AvResult ar = null;
try {
ar = avExample.getAvailability("tao", "pek", date, "sc");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(ar);
//返回查询结果
return ar;
}
3.2、建立PNR
我们可以根据上一步骤返回的结果生成航段信息,再添加其他信息组,就可以完成PNR的创建。这里采用字符串的方式传递参数,格式和E-TERM中一样。类名:SellSeat
public static String createPNR() throws Exception {// 新建PNR
SellSeat ss = new SellSeat();
//第一步、连接主机
connectHost(ss);
//第二步、添加旅客姓名
String name = "TEST/ADULT";
ss.addAdult(name);
//第三步、添加旅客乘坐航段信息
String airNo = "SC4651";
char fltClass = 'Y';
String orgCity = "TAO";
String dstCity = "PEK";
String actionCode = "NN";
int tktNum = 1;
String departureTime = "2012-10-26";
ss.addAirSeg(airNo, fltClass, orgCity, dstCity, actionCode, tktNum, departureTime);
//第四步、添加旅客身份证信息
String airline = "SC";
String idtype = "NI";
String id = "123456789";
ss.addSSR_FOID(airline, idtype, id, name);
//第五步、添加旅客联系组信息
String contactinfo = "15123339999";
ss.addContact(contactinfo);
//第六步、添加旅客出票时限
String dateLimit = "2012-10-25 12:00:00";
ss.setTimelimit(dateLimit);
//第七步、添加FC票价计算组
//字符串参数、格式为FC:出发城市(TAO) 承运航空(SC) 目的城市(PEK) 票面价(600.00) 运价基础(Y)总价(CNY)结束(END)
String fcStr = "FC:TAO SC PEK 600.00Y CNY600.00END";
ss.addFC(fcStr);
/*BookFC对象参数
BookFC fc= new BookFC();
fc.addFC("pek", "tao", "sc", "y", 600.00);//多航段多次添加,价格-1
时表示把该航段价格加到下一航段(最后一个航段),可用于多航段只了解票面总价的情况下使用,在后面的升舱过程由于无数据库保存FC项时可以用到
ss.addFC(fc);*/
//第八步、添加FN票价组
//字符串参数、格式为FN:票面总价(FCNY)/实收价格(SCNY)/代理费率(C3.00)/机建税(TCNY50.00CN)/燃油税(TCNY70.00YQ)
String fnStr = "FN:FCNY600.00/SCNY600.00/C3.00/TCNY50.00CN/TCNY70.00YQ";
ss.addFN(fnStr);
/* //BookFN对象参数
BookFN fn= new BookFN();
fn.setAmount(BookFN.F, "CNY", 600.00);//FCNY
fn.setAmount(BookFN.S, "CNY", 600.00);//SCNY
fn.setC(3.00);//代理费率
fn.addTax(BookFN.T, "CNY", 50.00, "CN");//机建
fn.addTax(BookFN.T, "CNY", 70.00, "YQ");//燃油
ss.addFN(fn);*/
//第九步、完成PNR必需信息输入,递交主机,生成PNR
SSResult ssr = ss.commit1();
// 返回PNR结果
System.out.println(ssr.getPnrno());
return ssr.getPnrno();
}
3.2、出票
类名:ETDZ
方法: issueTicket
public static void etdz(String pnr) throws Exception {// 出票
ETDZ etdz = new ETDZ();
connectHost(etdz); System.out.println(etdz.issueTicket(pnr, 2));//返回OK
}
3.3、改期升舱
以下只针对单程和往返客票,即旅客只有一张客票的情况下。
1、首先确认是否需要分离PNR,多旅客(成人、儿童)PNR中部分旅客改期升舱情况下使用。
类名:Pnrmanage、
方法:splitPNR
public static String split(String pnr) throws Exception {//分离PNR
PnrManage pm=new PnrManage();
connectHost(pm);
String name="test/adult";
//第一步、创建要分离的旅客集合
Vectorpassengers=new Vector();
BookPassenger passenger=new BookPassenger(name);//默认成人
//对于含儿童的PNR,一般用RT的返回结果设置旅客类型
//RTResult rs=new RTResult();
//rs=new RT().retrieve(pnr);
//passenger.setType(rs.getPassengerAt(n).getType());
passengers.add(passenger);
//第二步、分离旅客,返回新PNR号
String pnrnew=pm.splitPNR("NKHD2P", passengers, 0);//在散客中count参数无意义
return pnrnew;//新生成的PNR
}
2、改期:这里指不需要换开票面的改升操作。
类名:Pnrmanage
方法:changeETktAirSeg
public static void changeDate(String pnr) throws Exception {//改期
PnrManage pm = new PnrManage();
connectHost(pm);
//用旧的航段信息替换新的航段信息
String str = pm.changeETktAirSeg(pnr, "sc4651", "21sep", "sc4651","23sep", "nn");
System.out.println("PM:" + str);
}
3、升舱:换开票面的改升操作,生产上采用占位PNR的方式实现升舱,且大多原票面信息可以从数据库中提取,所以这里的升舱主要是为了加强对IBE的API学习,了解PNR中各类信息的提取
类名:Pnrmanage
方法:updatePnr,传入的参数为要改升的PNR,新添加信息bookinformation,删除所有index[]。
说明:这里以成人、儿童、带婴儿、往返航段、修改第一个航段为F舱为例,采用部分参数写死的方式,可以学习PNR信息提取相关API的用法。
注意事项:a、index[]中存放的该条记录在PNR中的行号
b、改升的时候不要把第一航段的出发日期改到第二航段之前
c、索引从0开始,行号从1开始。
d、OI项票联号,往返客票为1200
e、TKNE项要删除干净,因为要生成新票面,原TKNE中的票号已不能使用
f、出过票的PNR才能改升
public static void change(String pnr) throws Exception{
PnrManage pm=new PnrManage();
RT rt = new RT();
// 第一步、连接主机
connectHost(pm);
connectHost(rt);
//第二步、提取PNR
RTResult rtResult =rt.retrieve(pnr);
//第三步、创建要删除的信息pnr序号集合
List indextemp = new ArrayList();
//第四步、 创建要更新的信息组
BookInfomation bookInfomation = new BookInfomation();
//这里以成人、儿童、带婴儿、往返航段、修改第一个航段为F舱为例
//第五步、添加新的航段组。
PNRAirSeg oldPNRAirSeg=rtResult.getAirSegAt(0);//航段索引从0开始
BookAirSeg newBookAirSeg = new BookAirSeg();
newBookAirSeg.setActionCode("NN");
newBookAirSeg.setAirNo(oldPNRAirSeg.getAirNo());
newBookAirSeg.setDepartureTime(oldPNRAirSeg.getDepartureTime());//可以改期
newBookAirSeg.setOrgCity(oldPNRAirSeg.getOrgCity());
newBookAirSeg.setDesCity(oldPNRAirSeg.getDesCity());
newBookAirSeg.setFltClass('F');
newBookAirSeg.setTktNum(oldPNRAirSeg.getTktNum());
bookInfomation.addAirSeg(newBookAirSeg);
indextemp.add(oldPNRAirSeg.getIndex());//添加该pnr序号以待删除
/*
*第六步、添加OI项
*/
//首先获取原PNR票号
Map infTktNo = new HashMap();// 婴儿原票号
Map tktNo = new HashMap();// 成人和儿童原票号
for (int i = 0; i < rtResult.getTktnosCount(); i++) {
PNRTktNo pnrTktNo =rtResult.getTktnoAt(i) ;
if (null != pnrTktNo&&"".equals(pnrTktNo.getRemark())) {//成人和儿童的remark为空字符串
tktNo.put(pnrTktNo.getPsgrID(), pnrTktNo.getTktNo());
}
if (null != pnrTktNo&&"IN".equals(pnrTktNo.getRemark())){//婴儿的remark为IN
infTktNo.put(pnrTktNo.getPsgrID(), pnrTktNo.getTktNo());
}
}
//添加OI项
Set psgrids = tktNo.keySet();
Set infPsgrids = infTktNo.keySet();
String psgrid = "";
String ticketNo = "";
//成人和儿童OI
for (Iterator iterator = psgrids.iterator(); iterator.hasNext();) {
psgrid = (String) iterator.next();
ticketNo = tktNo.get(psgrid);
BookOI oi = new BookOI();
oi.setCoupon("1200");//单航段客票为1000,指同一张票面上的航段都需要OI
oi.setPsgrid(psgrid);
oi.setTktno(ticketNo);
bookInfomation.addOI(oi);
}
//婴儿OI
for (Iterator iterator = infPsgrids.iterator(); iterator.hasNext();) {
psgrid = (String) iterator.next();
ticketNo = infTktNo.get(psgrid);
BookOI oi = new BookOI();
oi.setCoupon("1200");
oi.setPsgrid(psgrid);
oi.setTktno(ticketNo);
oi.setInfant(true);//区别
bookInfomation.addOI(oi);
}
/*
*第七步、添加FC、FN
*/
/*
* 因为出过票,FC项已丢失,航段价无法获取,所以采用把新票面总价加到最后一个航段的方式创建FC
* 对于包含成人、儿童、婴儿的PNR,FC项不同,所以在添加FC项时需添加旅客标识
*/
/*
* 确认旅客信息类型
*/
int adultCnt = 0;// 成人旅客计数
int infantCnt = 0;// 婴儿旅客计数
int childCnt = 0;// 儿童旅客计数
List childNames = new ArrayList();// 儿童姓名集合
List adultNames = new ArrayList();// 成人姓名集合
Vector psgrs = rtResult.getPassengers();
for (int i = 0; i < psgrs.size(); i++) {
if (PNRPassenger.ADULT == psgrs.get(i).getPassengerType()) {// 判断是否含有成人旅客,确认成人旅客数量
adultCnt++;
adultNames.add(psgrs.get(i).getName());
}
if (3 == psgrs.get(i).getPassengerType()) {// 判断是否含有儿童旅客,确认儿童旅客数量,因为PNRPassenger.CHILD在API和实际值不同,所以采用数值
childCnt++;
childNames.add(psgrs.get(i).getName());
}
}
infantCnt = rtResult.getInfantsCount();// 判断是否含有婴儿旅客,确认婴儿数量
//成人FC、FN、FP
if(0!=adultCnt){
BookFC adultFc = new BookFC();
adultFc.addFC(rtResult.getAirSegAt(0).getOrgCity(),rtResult.getAirSegAt(0).getDesCity(),
"SC","F",-1, -1, -1, null, null, false, true,
null, -1, null, -1, null, null, null);//第四个参数为-1表示此处不填写价格
adultFc.addFC(rtResult.getAirSegAt(1).getOrgCity(),rtResult.getAirSegAt(1).getDesCity(),
"SC",String.valueOf(rtResult.getAirSegAt(1).getFltClass()),1200.00, -1, -1, null, null, false, true,
null, -1, null, -1, null, null, null);//在最后一个航段填写价格1200.00
for (int k = 0; k < adultNames.size(); k++) {//添加旅客标识
adultFc.addPsgrname(adultNames.get(k));
}
bookInfomation.addFC(adultFc);
BookFN adultFn = new BookFN();// 成人 FN
adultFn.setAmount(BookFN.R, "CNY", 1200.00);//新票面价
adultFn.setAmount(BookFN.S, "CNY", 200.00);//票面差价
adultFn.setAmount(BookFN.A, "CNY", 200.00);//总差价
adultFn.setC(0);
adultFn.addTax(BookFN.T, "CNY", 100.00, "CN");
adultFn.addTax(BookFN.T, "CNY", 140.00, "YQ");
for (int k = 0; k < adultNames.size(); k++) {
adultFn.addPsgrname(adultNames.get(k));
}
bookInfomation.addFN(adultFn);
BookFP fp = new BookFP();// FP不可省掉
fp.setFp("cc/23");
bookInfomation.addFP(fp);
}
//儿童FC、FN,票价不同
if(0!=childCnt){
BookFC childFc = new BookFC();
childFc.addFC(rtResult.getAirSegAt(0).getOrgCity(),rtResult.getAirSegAt(0).getDesCity(),
"SC","F",-1, -1, -1, null, null, false, true,
null, -1, null, -1, null, null, null);//第四个参数为-1表示此处不填写价格
childFc.addFC(rtResult.getAirSegAt(1).getOrgCity(),rtResult.getAirSegAt(1).getDesCity(),
"SC",String.valueOf(rtResult.getAirSegAt(1).getFltClass()),600.00, -1, -1, null, null, false, true,
null, -1, null, -1, null, null, null);//在最后一个航段填写价格
for (int k = 0; k < childNames.size(); k++) {//添加旅客标识
childFc.addPsgrname(childNames.get(k));
}
bookInfomation.addFC(childFc);
BookFN childFn = new BookFN();// 儿童FN
childFn.setAmount(BookFN.R, "CNY", 600.00);//RCNY
childFn.setAmount(BookFN.S, "CNY", 100.00);//SCNY
childFn.setAmount(BookFN.A, "CNY", 100.00);//ACNY
childFn.setC(0);
childFn.addTax(BookFN.T, "CNY", 140.00, "YQ");
childFn.addTax(BookFN.T, "CNY", BookFN.EXEMPTTAX, "CN");
for (int k = 0; k < childNames.size(); k++) {
childFn.addPsgrname(childNames.get(k));
}
bookInfomation.addFN(childFn);
if (0 == adultCnt) {//如果没有成人才追加FP
BookFP fp = new BookFP();// FP
fp.setFp("cc/23");
bookInfomation.addFP(fp);
}
}
//婴儿FC、FN、FP,票价不同,特殊标识infant
if (0 != infantCnt) {
BookFC infantFc = new BookFC();
infantFc.addFC(rtResult.getAirSegAt(0).getOrgCity(), rtResult.getAirSegAt(0).getDesCity(), "SC", "F", -1, -1, -1, null,
null, false, true, null, -1, null, -1, null, null, null);// 第四个参数为-1表示此处不填写价格
infantFc.addFC(rtResult.getAirSegAt(1).getOrgCity(), rtResult.getAirSegAt(1).getDesCity(), "SC", String.valueOf(rtResult
.getAirSegAt(1).getFltClass()), 120.00, -1, -1, null, null,
false, true, null, -1, null, -1, null, null, null);// 在最后一个航段填写价格
// 婴儿有特殊的标识项,所以可以不添加旅客标识
infantFc.setInfant(true);
bookInfomation.addFC(infantFc);
BookFN infantFn = new BookFN();// 婴儿FN
infantFn.setAmount(BookFN.F, "CNY", 60.00);
infantFn.setAmount(BookFN.S, "CNY", 60.00);
infantFn.setAmount(BookFN.A, "CNY", 60.00);
infantFn.setC(0);
infantFn.addTax(BookFN.T, "CNY", BookFN.EXEMPTTAX, "YQ");
infantFn.addTax(BookFN.T, "CNY", BookFN.EXEMPTTAX, "CN");
infantFn.setInfant(true);//区别标识
bookInfomation.addFN(infantFn);
BookFP fp = new BookFP();// FP
fp.setFp("cc/23");
fp.setInfant(true);// 区别
bookInfomation.addFP(fp);
}
bookInfomation.setTimelimit("2012-10-23 12:00:00");// TK:TL
/*
*第八步、获取要删除T、FN、TN、FP、剩下要修改航段的TKNE的PNR序号
*/
if (0 != rtResult.getTktsCount()) {// 出票组T
for (int i = 0; i < rtResult.getTktsCount(); i++) {
if (null != rtResult.getTktAt(i)) {
indextemp.add(rtResult.getTktAt(i).getIndex());
}
}
}
if (0 != rtResult.getFNsCount()) {// FN
for (int i = 0; i < rtResult.getFNsCount(); i++) {
if (null != rtResult.getFNAt(i)) {
indextemp.add(rtResult.getFNAt(i).getIndex());
}
}
}
if (0 != rtResult.getTktnosCount()) {// TN
for (int i = 0; i < rtResult.getTktnosCount(); i++) {
if (null != rtResult.getTktnoAt(i)) {
indextemp.add(rtResult.getTktnoAt(i).getIndex());
}
}
}
if (null != rtResult.getFps() && 0 != rtResult.getFps().size()) {// FP
for (int i = 0; i < rtResult.getFps().size(); i++) {
if (null != rtResult.getFpAt(i)) {
indextemp.add(rtResult.getFpAt(i).getIndex());
}
}
}
if (null != rtResult.getSsrs() && 0 != rtResult.getSSRsCount()) {// TKNE
for (int i = 0; i < rtResult.getSSRsCount(); i++) {
if (null != rtResult.getSSRAt(i)) {
if ("TKNE".equals(rtResult.getSSRAt(i).getSSRType())) {
indextemp.add(rtResult.getSSRAt(i).getIndex());
}
}
}
}
Integer[] indexsTemp=(Integer[])indextemp.toArray(new Integer[0]);
int[]index=new int[indexsTemp.length];
for(int i=0;ilength;i++){
index[i]=indexsTemp[i].intValue();
}
//第九步、升舱
String status=pm.updatePnr(pnr, bookInfomation, index);
//第十步、打印出票
if("OK".equals(status)){
etdz(pnr);
}
3.4退票
1、在E-TERM中填写退票单后票面上该航段会自动refunded,但使用IBE接口需要自己手动去refunded票面。
2、对于往返航段,已经退过一个航段的客票,需要提取该退票单并修改,以保留上次退票信息。
public static void trfd(String tktNo) throws Exception {// 退票
//第一步、 REFUNDED票面上的航段状态
ETRF etrf = new ETRF();
connectHost(etrf);
etrf.refundETkt(tktNo, 1, "CNY", 1200.00, "1", "");
//第二步、 填写退票单,连接主机
TRFD trfd = new TRFD();
connectHost(trfd);
TRFDResult result = null;
//第三步、 生成退票单。先尝试获取退票单,如果没有则新生成一个新退票单。
try {
result = trfd.genRefundForm("tm", 2, "D", tktNo);
} catch (IBEException e) {
if (!e.getMessage().contains("CAN NOT PRINT NEW REFUND")) {// 判断是否已经生成过退票单
throw e;
}
//第四步、 生成新的退票单
result = trfd.genRefundForm("AM", 2, "D", null);
result.setTktNo(tktNo.split("-")[1]);
result.setRefund("N");
result.setPassName("SC");
result.setAirlineCode(tktNo.split("-")[0]);
result.setCommissionRate(0);
}
//第五步、填写/修改退票单信息
result.setTax(0, 100.00, "CN");
result.setTax(1, 140.00, "YQ");
result.setDeduction(22);// 手续费
result.setGrossRefund(1000.00);
result.setCouponNo(0, "1200"); // 新票联号
String s = trfd.changeRefundForm_new(result, false);
System.out.println(s);
}