使用说明
推送目前不保证数据不丢失,需要有主动拉取做兜底,所以接入前请先保证有主动拉取任务存在,且主动拉取也需要缓存ChangeTime(艺龙数据变更时间),通过对比保证本地数据最新。
由于推送的吞吐量比较大,不能保证推送的顺序,所以代理拿到数据后,需要拿key=OrderId、value=Time做缓存操作。当新来数据的Time>=缓存Time,则更新本地数据,如果新来数据Time<缓存Time,则舍弃该数据,保证代理的数据始终是最新的。
目前处于新服务接入阶段,接入前,请联系艺龙进行连调。不再支持推送,请接入拉取接口
输入参数
节点 | 名称 | 类型 | 可为空 | 说明 |
---|---|---|---|---|
type | 推送类型 | Enum | N | Inventory:库存增量 Rate:价格增量 Order:订单增量 State:状态增量 Data:酒店增量 HotelDetail:酒店详情增量 说明:用来标示是哪种类型数据,该接口始终为:Order |
data | 数据 | String | N | 加密后的数据,需要解密,解密方式见“加解密方式”,密钥为appkey后8位,数据可能有多条,为Order的集合,见Order |
guid | 唯一标示 | String | N | 出现问题,方便定位到哪条数据 |
Order节点
节点 | 名称 | 类型 | 可为空 | 说明 |
---|---|---|---|---|
LastId | 增长id | Long | N | |
Time | 变化时间 | DateTime | N | |
OrderId | 订单ID | Long | N | |
Status | 订单状态 | String(2) | N | |
ArrivalDate | 入住日期 | DateTime | N | |
DepartureDate | 离店日期 | DateTime | N | |
TotalPrice | 总价 | Decimal | N | |
NumberOfRooms | 房间数量 | Int | N | |
AffiliateConfirmationId | 合作伙伴从成单接口传入的订单号 | String | Y | |
PayStatus | 支付状态 | Int | Y | -1 -- 无支付信息 1 -- 等待担保或支付 2 -- 担保或支付中 3 -- 担保或支付成功 4 -- 担保或支付失败 5 -- 暂缓 |
响应结果
节点 | 名称 | 类型 | 可为空 | 说明 |
---|---|---|---|---|
code | code | int | N | 0:表示接收成功 -1:表示接收失败 建议:接收成功,立马返回结果,异步进行处理。 |
errorMsg | 错误信息 | String | N | 说明:发送失败需要给出失败信息 |
输出参数示例
{ "type": "Order", "guid": "031a9db5-6850-4d6b-a058-6e3e99679a20", "data": [ { "LastId":281602338162858, "Time":"2018-12-19T20:06:04+08:00", "OrderId":460678130, "Status":"A", "PayStatus":3, "ArrivalDate":"2018-12-19T00:00:00+08:00", "DepartureDate":"2018-12-20T00:00:00+08:00", "TotalPrice":198, "NumberOfRooms":1, "AffiliateConfirmationId":"AH5C1A32C02123296425" } ] }
接收接口示例
@RequestMapping(value="/incr/order/consumer",method=RequestMethod.POST) public @ResponseBody String getIncrData(HttpServletRequest request){ PushResponse pushResponse=new PushResponse(); try { String type=request.getParameter("type"); Assert.notNull(type,"type not null"); String data=request.getParameter("data"); Assert.notNull(data,"data not null"); String guid=request.getParameter("guid"); logger.info("message type="+type+",data="+data+",guid="+guid); switch (type) { case "Order": //处理订单增量 //除订单增量需要解密外,其他需解密 if(StringUtils.isNotBlank(appkeyLast8)){ decrypt=SecurityUtil.decrypt(data,appkeyLast8); } break; case "Rate": //处理价格增量 break; case "Inventory": //处理库存增量 break; case "Data": //处理酒店增量 break; } pushResponse.setCode(0); pushResponse.setErrorMsg(null); } catch (Exception e) { e.printStackTrace(); logger.error("error:"+e.getMessage()); // TODO: handle exception pushResponse.setCode(-1); pushResponse.setErrorMsg(e.getMessage()); } return JSONObject.toJSONString(pushResponse); }
加密解密方式
public class SecurityUtil { private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";// 默认的加密算法 /** * AES 加密操作 * @param content 待加密内容 * @param key 密钥 */ public static String encrypt(String content, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器 byte[] result = cipher.doFinal(byteContent);// 加密 return Base64.encodeBase64String(result);// 通过Base64转码返回 } /** * AES 解密操作 * @param content 待解密内容 * @param key 密钥 * @return */ public static String decrypt(String content, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { // 实例化 Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); // 使用密钥初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key)); // 执行操作 byte[] result = cipher.doFinal(Base64.decodeBase64(content)); return new String(result, "utf-8"); } /** * 生成加密秘钥 * * @return * @throws NoSuchAlgorithmException */ private static SecretKeySpec getSecretKey(final String key) throws NoSuchAlgorithmException { // 返回生成指定算法密钥生成器的 KeyGenerator 对象 KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); // AES 要求密钥长度为 128 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(key.getBytes()); kg.init(128, secureRandom); // 生成一个密钥 SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥 } }