startTime = microtime(TRUE); libxml_disable_entity_loader(false); $this->ctripUser = Yii::$app->params['ctrip']; parent::__construct($id, $module, $config); } public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'except' => ['set-room-price', 'set-room-info', 'update-room-quantity'], 'rules' => [ [ 'ips' => ['103.48.141.164', '210.13.114.57','114.80.1.*','140.207.218.*','114.80.10.*','140.206.211.*','103.48.141.160','210.13.114.32','45.251.106.192', '221.130.190.129','101.226.248.*','140.207.228.*' //携程IP访问白名单 , '127.0.0.1', '106.14.56.77', '180.168.4.58', '139.224.30.29' //蜘蛛IP访问白名单 , '116.236.255.166', '222.65.108.254' //纳格兰临时允许访问,网络环境地址:张江炬芯研发大厦A栋215 ], 'allow' => true, ], ],/*'rules' => [ [ 'ips' => ['101.226.248.76', '101.226.248.77', '101.226.248.78', '101.226.248.79', '140.207.228.76', '140.207.228.77', '140.207.228.78', '140.207.228.79', '103.48.141.164', '210.13.114.57', '114.80.10.1', '140.207.218.145', '221.130.190.129','101.226.248.*','140.207.228.*' //携程IP访问白名单 , '127.0.0.1', '106.14.56.77', '180.168.4.58', '139.224.30.29' //蜘蛛IP访问白名单 , '116.236.255.166', ], 'allow' => true, ], ],*/ ], ]; } public function actions() { return [ 'service' => [ 'class' => 'subdee\soapserver\SoapAction', 'serviceVar' => 'WSDL' // 'serviceUrl' => 'http://cs1.yiireal.com/hotel/ctrip/service?WSDL' 请求示例 ], ]; } protected function actionSoap($data_xml, $request_name) { $client = Yii::$app->soapClient; $param = Utils::xml_encode($data_xml, $request_name); $this->data = $data_xml; $res = $client->AdapterRequest(['requestXml' => $param]); return $res->AdapterRequestResult; } /** * User: wangxj * * soap 对外接口 * * @param string $xml * @param string $Invoketype * * @return string * * @soap */ public function Invoke($xml, $Invoketype) { $this->writeLog($Invoketype, $xml); $request = Utils::xml_to_array($xml); //将xml转化成数组 // if($request['']) $head = $request['HeaderInfo']; $user = $head['Authentication']['@attributes']; $soap = Yii::$app->params['ctrip']['soap']; if ($user['UserName'] == $soap['UserName'] && $user['Password'] == $soap['Password']) { // $action = $head['RequestType']['@attributes']['Name']; $body = $request[$Invoketype . 'Request']; $data['Message'] = '操作成功'; $data['ResultCode'] = 0; //携程!!!,这个接口大小写不规则,单独处理 $res = $this->$Invoketype($body); if ($Invoketype == "DomesticCheckRoomAvail") { //可定检查最有最外层的ResultCode,内层没有ReturnCode $data = $res; } else { $data['ResultCode'] = isset($res['ReturnCode']) ? $res['ReturnCode'] : $data['ResultCode']; $data['Message'] = isset($res['ReturnDescript']) ? $res['ReturnDescript'] : $data['Message']; if ($Invoketype == 'DomesticCancelHotelOrder') { $data['DomesticCancelhotelOrderResponse'] = $res; } else { $data[$Invoketype . 'Response'] = $res; } } } else { $data = [ 'Message' => '用户认证或授权错误(程序错误) ', 'ResultCode' => CtripOrder::RETURN_CODE_AUTH_FAIL, ]; } $data = Utils::data_to_xml($data); $data = Utils::formatResult($data); $this->writeLog($Invoketype, $data); return new \SoapVar($data, XSD_STRING); } /** * User:Steven * * Desc:DomesticCheckRoomAvail (可定检查) * 1.接口调用方:携程 * 2.接口返回方:合作方 * 3.调用频率:订单量的10倍以上 * 4.使用说明:携程在给酒店下订单前,调用合作方接口。通过传入客人入住信息,得 到该房型是否可订。该接口响应时间(含网络)需要在3秒以内 * @param array $body * @return mixed */ protected function DomesticCheckRoomAvail($body) { // $a='{"Hotel":"4386248","Arrival":"2018-07-12T00:00:00","Departure":"2018-07-13T00:00:00","Room":"145386478","RoomNumber":"1","Person":"1","BalanceType":"PP","RoomPrices":{"RoomPrice":{"EffectDate":"2018-07-12T00:00:00","Price":"729","CNYPrice":"0","Cost":"655.4","CNYCost":"0","BreakFast":"0"}},"Currency":"RMB","IsCompensate":"F","TraceLogID":"ChenckAfterOrder_6733588561","OperationType":"New","CheckAvailID":"0","CheckAvailType":"Query"}'; // $body=json_decode($a,true); $this->writeLog('DomesticCheckRoomAvail', \GuzzleHttp\json_encode($body)); $trip = new CtripOrder(['scenario' => 'check-room-avail']); if (!$trip->load_valid($body, '') || !$trip->validate()) { $roomInfo_arr['RequestResponse']['RequestResult'] = array(); $RequestResult['Message'] = '接受失败!缺少必要参数!'; $RequestResult['ResultCode'] = CtripOrder::RETURN_CODE_CHCEK_CANNOT_ANALYZE; $RequestResult['DomesticCheckRoomAvailResponse'] = [ "IsBookable" => 0, "ReturnDescript" => CtripOrder::RETURN_CODE_CHCEK_CANNOT_ANALYZE . ":返回无法解析", ]; $arr = array( "agentid" => 1000002, "title" => '【报警】可定检查:', "msg" => "[{$RequestResult['ResultCode']}]" . $RequestResult['Message'] . "\n" . "请尽快联系技术,核实渠道上接口参数是否正确", "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); return $RequestResult; } $res_arr = $trip->CheckRoomAvail(); $roomInfo_arr['RequestResponse']['RequestResult'] = array(); $RequestResult = $roomInfo_arr['RequestResponse']['RequestResult']; $RequestResult['Message'] = '接受成功!'; $RequestResult['ResultCode'] = 0; $availInfo['IsBookable'] = isset($res_arr['isBookable']) && $res_arr['isBookable'] === false ? 0 : 1; $total_price = ''; $date_list = Utils::createDateRangeArray($body['Arrival'], $body['Departure']); //$roomData = isset($body['RoomPrices']['RoomPrice']['EffectDate']) ? $body['RoomPrices'] : $body['RoomPrices']['RoomPrice']; $IsBookable = 1; //初始化可订 $ReturnDescript = ""; $ReturnCode = 0; foreach ($date_list as $value) { if (empty($res_arr)) { $IsBookable = 0; $ReturnCode = CtripOrder::RETURN_CODE_CHCEK_NO_ROOM_STATUS; $ReturnDescript = '房型信息不存在'; } else { foreach ($res_arr as $item) { if (date('Y-m-d', strtotime($value)) == $item['RUN_DATE']) { if ($item['AUTHORITY_STATUS'] != 1 || $item['IS_ONSALE'] != 1 || $item['CANCEL_FLAG_B'] != 0 || $item['CANCEL_FLAG_C'] != 0 || $item['RUN_STATUS'] == 329) { //不可订 $availInfo['IsBookable'] = 0; $ReturnCode = CtripOrder::RETURN_CODE_CHCEK_STOCK_FAIL; $item['AUTHORITY_STATUS'] != 1 ? $availInfo['ReturnDescript'] = '未授权!' : ($item['IS_ONSALE'] != 1 ? $availInfo['ReturnDescript'] = '已下线' : ($item['RUN_STATUS'] == 329 ? $availInfo['ReturnDescript'] = ' 关房' : '')); } else { //*****(重要)注意:携程要求不做库存的校验,即如果库存大于0,那么就返回可订,同时返回可用房量,如果等于0,即不可订 if ($body['IsHoldRoom'] == 'R') { //保留房 if ($item['REMAINING_COUNT'] > 0 && ($item['buyout'] + $item['retain'] + $item['inquiry']) > 0) { $availInfo['IsBookable'] = 1; } else { $IsBookable = 0;//不可定 $ReturnCode = CtripOrder::RETURN_CODE_CHCEK_STOCK_LESS; $ReturnDescript = $item['REMAINING_COUNT'] <= 0 ? '保留房库存不足!' : '基础房型库存不足!'; } /* if ($item['REMAINING_COUNT'] < $body['RoomNumber']) { $availInfo['IsBookable'] = 0;//不可定 $availInfo['ReturnDescript'] = '保留房库存不足!'; //错误描述: } else { if ($body['RoomNumber'] > ($item['buyout'] + $item['retain'])) { $availInfo['IsBookable'] = 0; $availInfo['ReturnDescript'] = '基础房型库存不足!'; //错误描述: } else { $availInfo['IsBookable'] = 1; } }*/ } else { //非保留房 if ($item['OVERSELL_FLAG'] == 1) { //开启超卖 if (($item['buyout'] + $item['retain'] + $item['inquiry']) > 0) { $availInfo['IsBookable'] = 1; } else { $IsBookable = 0;//不可定 $ReturnCode = CtripOrder::RETURN_CODE_CHCEK_STOCK_LESS; $ReturnDescript = '基础房型库存不足!(预订非保留)'; } } else { //限量售卖 if ($item['REMAINING_COUNT'] > 0 && ($item['buyout'] + $item['retain'] + $item['inquiry']) > 0) { $availInfo['IsBookable'] = 1; } else { $IsBookable = 0;//不可定 $ReturnCode = CtripOrder::RETURN_CODE_CHCEK_STOCK_LESS; $ReturnDescript = '库存不足!(预订非保留)'; } } /*if ($item['OVERSELL_FLAG'] == 1 || $item['REMAINING_COUNT'] >= $body['RoomNumber']) { if ($body['RoomNumber'] > ($item['buyout'] + $item['retain'] + $item['inquiry'])) { $availInfo['IsBookable'] = 0; $availInfo['ReturnDescript'] = '基础房型库存不足!(预订非保留)'; //错误描述: } else { //在渠道房量充足的情况下,判断基础房型的房量是否充足 $availInfo['IsBookable'] = 1; } } else { $availInfo['IsBookable'] = 0; $availInfo['ReturnDescript'] = '限量售卖,库存不足'; //错误描述: }*/ } } $total_price += $body['RoomNumber'] * $item['PROD_PRICE']; $roomPrice['RoomPrice'] = array(); $roomPrice['RoomPrice']['EffectDate'] = $item['RUN_DATE'] . 'T00:00:00'; $roomPrice['RoomPrice']['Cost'] = $item['PROD_PRICE']; //取分销价 $roomPrice['RoomPrice']['CNYCost'] = $item['PROD_PRICE']; $roomPrice['RoomPrice']['BreakFast'] = $item['BREAKFAST_INCLUDE']; $availInfo['RoomPrices'][] = $roomPrice; $AvailRoomQuantity['AvailRoomQuantity']['EffectDate'] = $item['RUN_DATE'] . 'T00:00:00'; $AvailRoomQuantity['AvailRoomQuantity']['AvailQuantity'] = $item['OVERSELL_FLAG'] == 1 ? 99 : $item['REMAINING_COUNT']; $availInfo['AvailRoomQuantitys'][] = $AvailRoomQuantity; } } } } $availInfo['IsBookable'] = $IsBookable; $availInfo['ReturnDescript'] = $ReturnDescript; if ($availInfo['IsBookable'] == 0) { $opera_hotel_room = new OperaHotelRoom(); $res = $opera_hotel_room->getHotelRoomInfo($body['Room']); $user = $this->_developerList . "|" . $res[0]['USER_NAME'] . '|yangxt'; $msg = '酒店名称:' . $res[0]['HOTEL_NAME'] . "\n房型名称:" . $res[0]['ROOM_NAME'] . "\n内部标示:" . $res[0]['INNER_IDENTIFY'] . "\n开始日期:" . $body['Arrival'] . "\n结束日期:" . $body['Departure'] . "\n携程房型ID:" . $res[0]['ChannelRoomId'] . "\n原因:" . $availInfo['ReturnDescript']; $arr = array( "agentid" => 1000002, "title" => '可定检查失败:', "msg" => $msg, "touser" => $user, ); zOfficeWechat::sendMsg($arr); } $availInfo['Currency'] = 'RMB'; $availInfo['Room'] = $body['Room']; $availInfo['InterFaceAmount'] = $total_price;//接口订单金额 $availInfos["DomesticCheckRoomAvailResponse"] = $availInfo; $availInfos["ResultCode"] = $ReturnCode; $availInfos['Message'] = $ReturnDescript; return $availInfos; } /** * User:Steven * * Desc: DomesticSubmitNewHotelOrder (新订订单) * 1-1.接口调用方:携程/合作方 * 1-2.接口返回方:合作方/携程 * 1-3.调用频率:频繁 * 1-4.使用说明:通过该接口,提交订单给对方系统 * $user_id,$hotel_id,$room_type,$org_id,$org_num,$room_list,$cus_list,$checkin_time,$if_cancel,$benefit,$total_commission,$order_id * * @param array $body * * param $OrderID integer 订单号 订单号 Y * param $InterFaceSendID integer 订单接口发送号 同一订单每次发送编号会不同,接收方需落 地保存 Y * param $Hotel integer Ctrip 酒店编号 酒店编号 Y * param $HotelName string 酒店名 酒店名 Y * param $Arrival string 入住日期 入住日期 Y * param $Departure string 离店日期 离店日期 Y * param $EarlyArrivalTime string 最早到店时间 最早到店时间 Y * param $LastArrivalTime string 最晚到店时间 最晚到店时间 Y * param $Person INT 实际入住人数 实际入住人数 Y * param $Notice string 备注 备注 N * param $IsHoldRoom string 是否保留房 是否保留房(F 或空-否,R-是) N * param $IsFlashLive string 是否闪住订单 是否闪住(F 或空-否,T-是) N * param $Guests object 用户集合  $GuestEntity array 单个用户 FirstName LastName ChinesName Y * param $mobilePhone integer 手机号 Ctrip 客服号码 Y * param $Currency string 币种 币种 N * param $Amount float 订单卖价总金额 订单卖价总金额(预付一般不传或传 0) Y * param $CNYAmount float 订单卖价总金额(人民 币) 订单卖价总金额(人民币),现付一般不传或 者传 0 Y * param $GuaranteeType string 担保方式 担保方式 Y * param $BalanceType string 价格类型 支付方式(PP 预付,FG 现付,PKG 包价) Y * param $Room integer Ctrip 房型编号 房型编号(供应商需要转成自己的对应编号) Y * param $RoomPerson INT 房型最大入住人数 房型最大入住人数 Y * param $RoomName string 房型名称 房型名称 Y * param $Quantity INT 预订间数 预订间数 Y * param $CostAmount float 订单底价总金额(原币 种) 订单底价金额(原币种),现付一般不传或者 传 0 Y * param $CNYCostAmount float 订单底价总金额(人民 币) 订单底价金额(人民币),现付一般不传或者 传 0 * param $RoomPrices object RoomPrice<> EffectDate Price 卖价 CNYPrice 订单卖价金额(原币种) Cost 底价 CNYCost 订单底价金额(原币种) BreakFast 早餐数量 ,预付一般不传或者 传 0 Y * param $AddOptionals array 附加服务   N * param $InterFaceConfirmNo integer 酒店确认号 酒店确认号 Y * param $HotelConfirmNo integer 供应商确认号 供应商确认号 Y * param $InterFaceAmount INT 接口订单金额 接口订单金额,现付产品卖价,预付产品底价 Y * param $ReturnCode INT 错误代码 错误代码 Y * param $ReturnDescript string 返回描述 返回描述 Y * param $OrderStatus INT 订单状态 订单状态(1:待确认;2:失败;3:确认;4:拒绝) Y * param $FGToPP string 是否现转预订单 是否现转预订单(T:是,F:否) Y * param $DepositAmount float 押金 默认值 0 Y * param $IsFreeSale string 是否 FreeSale F 或空-否,T-是 N * @return mixed * * @throws \yii\db\Exception */ protected function DomesticSubmitNewHotelOrder($body) { $trip = new CtripOrder(['scenario' => 'new-order']); if ($trip->load($body, '') && $trip->validate()) { $res = $trip->makeOrder(); if ($res == CtripOrder::RETURN_CODE_SUCCESS) { //保存发送号 $interFace = new OrderChannelInfo(); $interFace->ORDER_ID = $trip->Spider_order_id; $interFace->InterFaceSendID = $trip->InterFaceSendID; //先置旧的数据cancel_flag为1 OrderChannelInfo::updateAll(['CANCEL_FLAG' => 1], ['ORDER_ID' => $trip->Spider_order_id]); $interFace->save(); //需要修改订单的来源类型为对接 //如果是保留房,直接是已确认,返回状态3:已确认 if ($trip->IsHoldRoom == 'R') { $channel_status = CtripOrder::ORDER_STATUS_ACCEPT; $orderStatus = 3; $commonOrder = new CommonOrder(); $commonOrder->setOrderLog($trip->Spider_order_id, $trip->Spider_parent_room_type, $trip->Spider_room_type, "该订单为保留房订单,系统自动接单", Yii::$app->params['ctrip']['base_user_id'], $trip->Spider_hotel); } else { $channel_status = CtripOrder::ORDER_STATUS_WAITING; $orderStatus = 1; } $data = [ 'OrderStatus' => $orderStatus, 'InterFaceConfirmNO' => '', 'HotelConfirmNo' => $trip->Spider_order_id, 'InterFaceAmount' => $trip->CNYCostAmount, 'ReturnCode' => CtripOrder::RETURN_CODE_SUCCESS, 'ReturnDescript' => '预订成功', ]; OrderMain::updateAll(['DOCKING_TYPE' => CtripOrder::ORDER_DOCKING_TYPE, 'CHANNEL_ORDER_STATUS' => $channel_status, 'IF_LAST_PROD' => $trip->IF_LAST_PROD, 'UPDATE_TIME' => date('Y-m-d H:i:s', time())], ['and', ['CANCEL_FLAG' => 0], ['or', ['ORDER_ID' => $trip->Spider_order_id], ['PARENT_ORDER_ID' => $trip->Spider_order_id]]]); } else { if ($res == CtripOrder::RETURN_CODE_PROGRAM_FAIL) { $ctrip_status = 2; } else { $ctrip_status = 4; } $data = [ 'OrderStatus' => $ctrip_status, 'InterFaceConfirmNO' => '', 'HotelConfirmNo' => '', 'InterFaceAmount' => $trip->CNYCostAmount, 'ReturnCode' => $res, 'ReturnDescript' => CtripOrder::RETURN_MSG[$res], ]; } } else { $code = Utils::getModelError($trip); $data = [ 'OrderStatus' => 4, 'InterFaceConfirmNO' => '', 'HotelConfirmNo' => '', 'InterFaceAmount' => $trip->CNYCostAmount, 'ReturnCode' => $code, 'ReturnDescript' => CtripOrder::RETURN_MSG[$code], ]; } //OrderStatus==2的情况会走ebk,不在需要人工搬单 为4的情况会直接拒单 if ($data['OrderStatus'] == 2 || $data['OrderStatus'] == 4) { $msg = $data['OrderStatus'] == 2 ? "订单预定失败,失败原因:{$data['ReturnDescript']},订单已进入携程ebooking,请关注!" : "订单预定失败,失败原因:{$data['ReturnDescript']},订单已被拒绝,请核实该订单相关房态!"; $msg .= '订单号:' . $trip->OrderID; $this->sendAliMsgToRtx('【携程直连】新订订单失败:', $msg, 4, self::AGENT_COMMON_HOTEL_ID); } //新订单进来,都推送一次房态和数量 $this->post['hotel_id'] = $trip->Spider_hotel; $this->post['parent_room_type'] = $trip->Spider_parent_room_type; $this->post['room_type'] = $trip->Spider_room_type; $this->post['StartDate'] = substr($trip->Arrival, 0, 10); $this->post['EndDate'] = substr($trip->Departure, 0, 10); /*$this->actionSetRoomInfo(); $this->actionUpdateRoomQuantity();*/ return $data; } /** * Author:Steven * User:wangxj * * Desc: DomesticCancelHotelOrder (取消订单) * 1-1.接口调用方:携程/合作方 * 1-2.接口返回方:合作方/携程 * 1-3.调用频率:频繁 * 1-4.使用说明:通过该接口,提交订单给对方系统 * * @param array $body * @return array * @throws \Exception * @throws \yii\db\Exception */ protected function DomesticCancelHotelOrder($body) { $trip = new CtripOrder(['scenario' => 'cancel-order']); $trip->load($body, ''); $trip->saveLog($this->ctripUser['base_user_id'], "渠道商申请取消订单:" . $trip->Spider_order_id); $res = $trip->applyCancel(); if ($res == CtripOrder::RETURN_CODE_SUCCESS) { //保存发送号 $interFace = new OrderChannelInfo(); $interFace->ORDER_ID = $trip->Spider_order_id; $interFace->InterFaceSendID = $trip->InterFaceSendID; //先置旧的数据cancel_flag为1 OrderChannelInfo::updateAll(['CANCEL_FLAG' => 1], ['ORDER_ID' => $trip->Spider_order_id]); $interFace->save(); $data = [ 'OrderStatus' => 3, 'OrderID' => $trip->OrderID, 'InterFaceConfirmNO' => $trip->InterFaceConfirmNO, 'ReturnCode' => $res, 'ReturnDescript' => '同意取消', ]; // $trip->saveLog(2, '同意渠道取消订单'); //取消成功通知客服 /*if (in_array($res['data']->ORDER_STATUS, [OrderMain::ORDER_STATUS_WAITING_CONFIRM, OrderMain::ORDER_STATUS_CONFIRMED])) { $dict_type = DictType::findOne($res['data']->ORDER_STATUS); if ($dict_type !== null) { $extra = '(' . $dict_type->TYPE_NAME . ')'; } else $extra = '(未知订单状态)'; } else { $extra = ''; }*/ /*$arr = array( "agentid" => 1000002, "title" => '取消订单:', "msg" => '携程取消订单:' . $trip->Spider_order_id, "touser" => $this->_messageList, ); zOfficeWechat::sendMsg($arr);*/ } else { //渠道接单状态变为取消被拒 $data = [ 'OrderStatus' => 4, 'OrderID' => $trip->OrderID, 'InterFaceConfirmNO' => $trip->InterFaceConfirmNO, 'ReturnCode' => $res, 'ReturnDescript' => CtripOrder::RETURN_MSG[$res], ]; } $notice = $res == CtripOrder::RETURN_CODE_SUCCESS ? "同意取消订单" : "拒绝取消订单"; $this->sendAliMsgToRtx('【携程直连】携程申请取消订单:', "携程申请取消订单【" . $trip->Spider_order_id . "】," . $notice, 4, self::AGENT_COMMON_HOTEL_ID); return $data; } /** * User:Steven * Desc:DomesticGetOrderStatus(订单状态查询) * @param array $body * * @return mixed */ protected function DomesticGetOrderStatus($body) { $trip = new CtripOrder(['scenario' => 'order-status']); $rs['Message'] = ''; $rs['ResultCode'] = 0; foreach ($body['Orders'] as $item) { $trip->load($item, ''); $res = $trip->getOrderStatus(); $data['OrderID'] = $item['OrderID']; $data['InterFaceSendID'] = $item['InterFaceSendID']; $data['ReturnDescript'] = '接收成功'; if (!$res) //1:待确认 2:订单未发送到合作方或返回代码 为失败 3 :合作方确认订单 4:合作方拒绝订单 { $rs['Message'] = "未查到订单号为{$item['OrderID']}的订单信息!"; $order['Orders'] = array(); $rs['ResultCode'] = 9999; } else { switch ($res['CHANNEL_ORDER_STATUS']) { //OrderStatus新订只能传1待确认、3确认、4拒绝 case 550: //待处理 $OrderStatus = 1; break; case 551: //已接单 $OrderStatus = 3; break; case 552: //已拒绝 $OrderStatus = 4; break; } $dt = [ 'OrderStatus' => $OrderStatus, 'HotelConfirmNo' => $res['ORDER_ID'], 'ReturnCode' => 0, ]; $data['OrderEntity'] = $dt; $order['Orders'][] = $data; } } return $order; } /** * User:Steven * * Desc:SetRoomPrice(设置房型价格) * 1.接口调用方:合作方 * 2.接口返回方:携程 * 3.调用频率:不限制,建议不大于50万次/分钟(QPS<1万) * 4.使用说明:携程建议当价格变化时,立即进行推送。携程方面是异步处理价格。 * param $begin_date string * param $end_date string * param $hotel_id string * param $base_room_id string * param $room_id string * @return mixed */ public function actionSetRoomPrice(array $param = []) { $this->post = ($this->post == []) ? Yii::$app->request->post() : $this->post; if (!empty($param)) { $this->post = $param; } $post = $this->post; $post['StartDate'] = empty($post['StartDate']) ? date('Y-m-d') : $post['StartDate']; $post['EndDate'] = empty($post['EndDate']) ? date('Y-m-d', strtotime('+90 days')) : $post['EndDate']; //至少提供90天的数据,一次最多推送120天的房价房态数据,超过120天数据需要分批推送 $PriceInfos = array(); //获取酒店房价数据 $runHotelDsitrib = new RunHotelDistrib(); $res = $runHotelDsitrib->getRoomPrice($post); $HRInfo = OperaHotelRoom::find()->select(['a.ROOM_NAME', 'b.HOTEL_NAME'])->joinWith('operaHotel b') ->from('opera_hotel_room a') ->where(['a.HOTEL_ID' => $post['hotel_id'], 'a.BASE_ROOM_TYPE' => $post['parent_room_type'], 'a.ROOM_TYPE' => $post['room_type']]); if ($res['code'] != 0) { if ($res['code'] == 1 || $res['code'] == 3) { $arr = array( "agentid" => 1000002, "title" => $res['info'], "msg" => '酒店名称:' . $HRInfo['HOTEL_NAME'] . "\n" . '房型名称:' . $HRInfo['ROOM_NAME'] . '开始日期:' . $post['StartDate'] . "\n" . '结束日期:' . $post['EndDate'], "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); } return false; } $dayZone = $this->dateGroup($res['data']); if (isset($dayZone['code'])) { //警报:推送RTX $arr = array( "agentid" => 1000002, "title" => '(报警)设置房型价格接口:', "msg" => '酒店名称:' . $HRInfo['HOTEL_NAME'] . "\n" . '房型名称:' . $HRInfo['ROOM_NAME'] . '开始日期:' . $post['StartDate'] . "\n" . '结束日期:' . $post['EndDate'], "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); return false; } $rs = ''; //将分段后的时间分别推送房价格 $ctrip_room_id = RoomRelation::getChannelId($res['data'][0]['ROOM_ID'], Yii::$app->params['ctrip']['relation_supplier_id']); //携程的房型ID $ctrip_hotel_id = HotelRelation::getChannelId($post['hotel_id'], Yii::$app->params['ctrip']['relation_supplier_id']); //携程的房型ID foreach ($dayZone as $value) { foreach ($res['data'] as $key => $item) { if ($item['RUN_DATE'] >= $value['StartDate'] && $item['RUN_DATE'] <= $value['EndDate']) { $Prices['Price'] = array( 'AmountBeforeTaxFee' => $item['PROD_PRICE'], //税前 'AmountAfterTaxFee' => $item['PROD_PRICE'], //税后 'CostAmountBeforeTaxFee' => $item['PROD_PRICE'],//税前成本(底价) 'CostAmountAfterTaxFee' => $item['PROD_PRICE'], //税后成本 'Day' => '1' //连住天数,暂不用,默认为1 ); //PriceInfo节点代表一天的价格,每天的价格可以不同 $PriceInfo['PriceInfo'] = array( 'Prices' => $Prices, 'Stays' => 1, //连住,暂不用,默认为1 'BlanceType' => Yii::$app->params['ctrip']['balance_type'], //支付类型 'PriceType' => Yii::$app->params['ctrip']['price_type'], //价格类型 'Breakfast' => $item['BREAKFAST_INCLUDE'], //早餐 'Applicability' => '111111' //适用人群 ); $PriceInfos['PriceInfos'][] = $PriceInfo; unset($res['data'][$key]); } } //根据日期范围生成多天的 SetRoomPriceItem(目前我们系统的业务逻辑是:每次只能录入单个房型多天的价格) $SetRoomPriceItem['SetRoomPriceItem'] = array( 'PriceInfos' => $PriceInfos['PriceInfos'], 'RoomID' => $ctrip_room_id, 'StartDate' => $value['StartDate'] . 'T00:00:00', 'EndDate' => $value['EndDate'] . 'T00:00:00', 'Currency' => Yii::$app->params['ctrip']['currency_type'], ); $RoomTypeList = array( 'SetRoomPriceItems' => $SetRoomPriceItem, 'HotelID' => $ctrip_hotel_id, //Ctrip的酒店编号 'Submitor' => YII::$app->params['ctrip']['submitor'], //提交人,集团名拼音 'Title' => '房价推送成功', 'Priority' => 'N' //是否优先处理,暂不用,默认为N ); $soap = new SoapService($RoomTypeList, 'SetRoomPrice', 'SetAdaperRoomPriceRequest', '1.2'); $rs = $soap->exec(3, $post['user_id']); unset($PriceInfos['PriceInfos']); if (!$rs['flag'] || $rs['data']['RequestResult']['RequestResult'] != 0) { //警报:RTX通知 $arr = array( "agentid" => 1000002, "title" => '(报警)房价推送失败:', "msg" => json_encode($post), "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); } } return Json::encode($rs); } /** * User:Steven * Desc:将日期按照连续不连续进行分组 * @param $data * @return array */ public function dateGroup($data) { //将房态按时间分段 $dayZone = []; $firstDay = ''; $current = 0; foreach ($data as $key => $value) { if ($key == 0) { $dayZone[$current]['StartDate'] = $value['RUN_DATE']; $dayZone[$current]['EndDate'] = $value['RUN_DATE']; $firstDay = $value['RUN_DATE']; } else { $firstDay = strtotime($firstDay); $secondDay = strtotime($value['RUN_DATE']); $days = ($secondDay - $firstDay) / 86400; //用于判断当前日期与该组数据的StartDate差值是否是120,一次最多推送120天数据,大于120天分组推送 $diff = ($firstDay - strtotime($dayZone[$current]['StartDate'])) / 86400 / 90; if ($days == 1 && !($diff > 0 && is_int($diff))) { $dayZone[$current]['EndDate'] = $value['RUN_DATE']; } elseif ($diff > 0 && is_int($diff)) { $current++; $dayZone[$current]['StartDate'] = $value['RUN_DATE']; $dayZone[$current]['EndDate'] = $value['RUN_DATE']; } elseif ($days < 1) { return ['code' => 0, 'msg' => '子房型数据重复']; } else { $current++; $dayZone[$current]['StartDate'] = $value['RUN_DATE']; $dayZone[$current]['EndDate'] = $value['RUN_DATE']; } $firstDay = $value['RUN_DATE']; } } return $dayZone; } /** * User:Steven * * Desc:UpdateRoomQuantity(更新保留房数量) * 1-1.接口调用方:合作方 * 1-2.接口返回方:携程 * 1-3.调用频率:限制, 建议10次/分钟,并会视情况调整 * 1-4.使用说明:携程建议当房量变化时,立即进行推送。携程方面是异步处理房量更新操作。 * 一个请求最多只允许更新 10 个酒店下的房型,一个请求只允许 120 天内的房量, 一个请求 只允许发送少于 100 个房型。 * * param StartDate * param EndDate * param hotel_id * param parent_room_type * param room_type * * @return mixed * */ public function actionUpdateRoomQuantity(array $param = []) { $this->post = ($this->post == []) ? Yii::$app->request->post() : $this->post; if (!empty($param)) $this->post = $param; $post = $this->post; $runHotelDsitrib = new RunHotelDistrib(); $roomQuantity = $runHotelDsitrib->getRoomQuantity($post); if ($roomQuantity['code'] != 0) { //警报:RTX通知 if ($roomQuantity['code'] == 1 || $roomQuantity['code'] == 3) { $arr = array( "agentid" => 1000002, "title" => $roomQuantity['info'], "msg" => json_encode($post), "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); } return false; } $RoomQuantityEntityList = array(); $dateZone = $this->divisionDate($roomQuantity['data']); if (empty($roomQuantity['data']) || empty($dateZone)) { return false; } $push_rs = array(); $ctrip_room_id = RoomRelation::getChannelId($roomQuantity['data'][0]['ROOM_ID'], Yii::$app->params['ctrip']['relation_supplier_id']); //携程的房型ID $ctrip_hotel_id = HotelRelation::getChannelId($post['hotel_id'], Yii::$app->params['ctrip']['relation_supplier_id']); //携程的房型ID foreach ($dateZone as $key => $val) { foreach ($roomQuantity['data'] as $k => $value) { if ($val['StartDate'] <= $value['RUN_DATE'] && $val['EndDate'] >= $value['RUN_DATE']) { if ($value['RUN_STATUS'] == 329 || $value['AUTHORITY_STATUS'] == 0 || $value['IS_ONSALE'] == 0 || $value['BASE_ROOM_IS_ONSALE'] == 0 || $value['BASE_ROOM_RUN_STATUS'] != 1) { //关房操作(如果没有临时保留房须推送0;关房的时候,需要同时推送保留房数量 为0) $RoomQuantityEntity['RoomQuantityEntity'] = array( 'RoomTypeID' => $ctrip_room_id, //Ctrip 房型编号 'RoomQuantityCount' => '0', //临时保留房数 'RoomQuantityUpdateType' => 'AvailableCount', //临时保留房数更 新方式 'StartDate' => $value['RUN_DATE'] . 'T00:00:00', //开始时间 'EndDate' => $value['RUN_DATE'] . 'T00:00:00', 'FreeSale' => 'F', //Free Sale 目前只支持以下 两种: T F 'ReserveTime' => Utils::timeHour($value['ReserveTime']), //保留房最晚预订 时间 'RoomStatus' => 'N', //保留房不可超 N,满房 NoRoom;G,良好 Good;L,不可超 Limited G表示开房,N表示关房,L表示保留房限量售卖 'Restorable' => 'F', //保留房可恢复 目前只支持以下 两种:T,F ); } else { if ($value['OVERSELL_FLAG'] != 1) { // 1:允许超卖 0:不允许超卖 if ($value['REMAINING_COUNT'] <= 0) { //限量售卖(不超卖)的情况下 且库存为0 直接推成关房 $RoomStatus = 'N'; //G表示开房,N表示关房,L表示保留房限量售卖 } else { $RoomStatus = 'L'; //G表示开房,N表示关房,L表示保留房限量售卖 } } else { $RoomStatus = 'G'; } $RoomQuantityEntity['RoomQuantityEntity'] = array( 'RoomTypeID' => $ctrip_room_id, //Ctrip 房型编号 'RoomQuantityCount' => $value['REMAINING_COUNT'] < 0 ? 0 : $value['REMAINING_COUNT'], //临时保留房数 'RoomQuantityUpdateType' => 'AvailableCount', //临时保留房数更 新方式 'StartDate' => $value['RUN_DATE'] . 'T00:00:00', //开始时间 'EndDate' => $value['RUN_DATE'] . 'T00:00:00', 'FreeSale' => 'F', //Free Sale 目前只支持以下 两种: T F 'ReserveTime' => Utils::timeHour($value['ReserveTime']), //保留房最晚预订 时间 'RoomStatus' => $RoomStatus, //保留房不可超 N,满房 NoRoom;G,良好 Good;L,不可超 Limited G表示开房,N表示关房,L表示保留房限量售卖 'Restorable' => 'F', //保留房可恢复 目前只支持以下 两种:T,F ); } $RoomQuantityEntityList['room_info'][] = $RoomQuantityEntity; unset($roomQuantity['data'][$k]); } } if (empty($RoomQuantityEntityList['room_info'])) { continue; } //一个酒店 $hotel_id['HotelID'] = $ctrip_hotel_id; $SequenceID = \Yii::$app->cache->get('Ctrip_SequenceID'); if (!$SequenceID) { $rs = \Yii::$app->cache->set('Ctrip_SequenceID', time()); if (!$rs) { $arr = array( "agentid" => 1000002, "title" => '保留房消息序号SequenceID写入缓存失败', "msg" => json_encode($post), "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); return false; } $SequenceID = \Yii::$app->cache->get('Ctrip_SequenceID'); } \Yii::$app->cache->set('Ctrip_SequenceID', $SequenceID + 1); $UpdateRoomQuantityRequest = array( 'SequenceID' => $SequenceID, 'HotelIDList' => array( 'HotelIDEntity' => $hotel_id, ), 'RoomQuantityEntityList' => $RoomQuantityEntityList['room_info'], 'Editer' => YII::$app->params['ctrip']['submitor'], ); $soap = new SoapService($UpdateRoomQuantityRequest, 'UpdateRoomQuantity', 'UpdateRoomQuantityRequest', '1.2'); $res = $soap->exec(2, $post['user_id']); if (!$res['flag'] || $res['data']['Response']['CtripSoapResponse']['ResultCode'] != 0) { $result['code'] = 1; $result['msg'] = urlencode('保留房数量推送失败!'); $result['data'] = $res['data']['Response']; //将$begin_date, $end_date, $hotel_id, $base_room_id, $room_id信息拼接起来触发报警机制 $arr = array( "agentid" => 1000002, "title" => '(报警)更新保留房数量接口:', "msg" => '保留房数量推送失败!' . "\n" . '错误信息:' . $res['data']['Response']['CtripSoapResponse']['Message'], //json_encode($post), "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); } unset($RoomQuantityEntityList['room_info']); $push_rs[] = $res; } return json_encode($push_rs); } /** * User: wangxj * * 将时间分段进行推送,因为携程不允许中间日期不连续 * param int $hotel_id * param int $parent_room_type * param int $room_type * param string $StartDate * param string $EndDate * @param * * @return string * */ public function actionSetRoomInfo(array $param = []) { $this->post = ($this->post == []) ? Yii::$app->request->post() : $this->post; if (!empty($param)) $this->post = $param; $post = $this->post; $room = OperaHotelRoom::findOne(['HOTEL_ID' => $post['hotel_id'], 'PARENT_ROOM_TYPE' => $post['parent_room_type'], 'ROOM_TYPE' => $post['room_type'], 'CANCEL_FLAG' => 0]); if ($room == null) { return json_encode(['code' => 1, 'msg' => '子房型获取失败酒店id: ' . $post['hotel_id'] . ', 子房型id:' . $post['room_type']]); } $stocks = RunHotelDistrib::find() ->select(['DISTINCT(RUN_DATE)', 'CREATE_USER_ID', 'CREATE_TIME', 'UPDATE_USER_ID', 'UPDATE_TIME', 'DISTRIB_ID', 'HOTEL_ID', 'BASE_ROOM_TYPE', 'ROOM_TYPE', 'REMAINING_COUNT', 'SALED_COUNT', 'PROD_PRICE', 'CUS_PRICE', 'RUN_STATUS', 'OVERSELL_FLAG', 'CONSUME_STOCK_TYPE']) ->where(['HOTEL_ID' => $post['hotel_id'], 'BASE_ROOM_TYPE' => $post['parent_room_type'], 'ROOM_TYPE' => $post['room_type'], 'DISTRIB_ID' => Yii::$app->params['ctrip']['supplier_id']]) ->andFilterWhere(['between', 'RUN_DATE', $post['StartDate'], $post['EndDate']]) ->orderBy('RUN_DATE') // -> createCommand() -> getRawSql(); ->all(); //将房态按时间分段 $dayZone = []; $firstDay = ''; $current = 0; foreach ($stocks as $key => $stock) { if ($key == 0) { $dayZone[$current]['StartDate'] = $stock['RUN_DATE']; $dayZone[$current]['EndDate'] = $stock['RUN_DATE']; $firstDay = $stock['RUN_DATE']; } else { $firstDay = strtotime($firstDay); $secondDay = strtotime($stock['RUN_DATE']); $days = ($secondDay - $firstDay) / 86400; $diff = ($firstDay - strtotime($dayZone[$current]['StartDate'])) / 86400 / 90; if ($days == 1 && !($diff > 0 && is_int($diff))) { $dayZone[$current]['EndDate'] = $stock['RUN_DATE']; } elseif ($diff > 0 && is_int($diff)) { $current++; $dayZone[$current]['StartDate'] = $stock['RUN_DATE']; $dayZone[$current]['EndDate'] = $stock['RUN_DATE']; } elseif ($days < 1) { return json_encode(['code' => 1, 'msg' => '子房型数据重复,酒店:' . $post['hotel_id'] . "\n基础房型id:" . $post['parent_room_type'] . "\n子房型id:" . $post['room_type'] . "\n房型日期:" . $stock['RUN_DATE']]); } else { $current++; $dayZone[$current]['StartDate'] = $stock['RUN_DATE']; $dayZone[$current]['EndDate'] = $stock['RUN_DATE']; } $firstDay = $stock['RUN_DATE']; } } $result = ['code' => 0]; //将分段后的时间分别推送房态 foreach ($dayZone as $item) { $res = $this->pushRoomInfo($room, $item['StartDate'], $item['EndDate']); if ($res['code'] != 0) { $result = $res; } } return Json::encode($result); } /** * User: wangxj * * 推送房态 * @param OperaHotelRoom $room * @param string $StartDate * @param string $EndDate * @param * * @return array * */ protected function pushRoomInfo($room, $StartDate, $EndDate) { $actionName = 'SetRoomInfo'; $post = $this->post; $post['Spider_room'] = $room->ID; $post['StartDate'] = $StartDate; $post['EndDate'] = $EndDate; $model = new CtripRoomInfo(); if ($model->load($post, '') && $model->validate()) { //将 CtripRoomInfo对象中的数据作为房态粒度进行推送 $data_xml['StartDate'] = $model->StartDate . 'T00:00:00'; $data_xml['EndDate'] = $model->EndDate . 'T00:00:00'; $data_xml['RoomID'] = $model->RoomID; $data_xml['RoomInfoItems'] = $model->RoomInfoItems; $soap = new SoapService($data_xml, $actionName, $actionName . 'Request'); $res = $soap->exec(3, $post['user_id']); if ($res['data']['RequestResult']['ResultCode'] == 0) { $result['code'] = 0; } else { $result['code'] = $res['data']['RequestResult']['ResultCode']; $result['msg'] = $res['data']['RequestResult']['Message']; } } else { $result['code'] = 1; $result['msg'] = Utils::getModelError($model); } return $result; } /** * User: wangxj * * 保存日志方便查询 * @param string $Invoketype * @param string $string * */ protected function writeLog($Invoketype, $string) { $userIp = Yii::$app->request->userIP; $string = date('Y-m-d H:i:s') . " $Invoketype " . "【" . $userIp . "】" . PHP_EOL . $string . PHP_EOL; if (!file_exists(__DIR__ . '/../../../runtime/logs/soap/receive')) { mkdir(__DIR__ . '/../../../runtime/logs/soap/receive', 0777, true); } file_put_contents(__DIR__ . '/../../../runtime/logs/soap/receive/' . date('Y-m-d') . '.log', $string, FILE_APPEND); } /** * @param \yii\base\Action $action * @param mixed $result * @return mixed */ public function afterAction($action, $result) { //发送rtx $tmp_result = json_decode($result, true); if (isset($tmp_result['code']) && $tmp_result['code'] != '0' && !in_array($action->id, ['set-room-price', 'update-room-quantity'])) { //获取mapping失败,不发送rtx,又没有标识,以文字区别 if (isset($tmp_result['msg']) && $tmp_result['msg'] != '获取mapping失败') { $arr = array( "agentid" => 1000002, "title" => $action->id . ' code :' . $tmp_result['code'] . ' msg: ' . (isset($tmp_result['msg']) ? $tmp_result['msg'] : '无msg字段'), "msg" => json_encode($this->data) . '\n' . json_encode($this->post), "touser" => $this->_developerList, ); zOfficeWechat::sendMsg($arr); } } $endTime = microtime(TRUE); $scTime = $endTime - $this->startTime; $incrementTime = round(floatval($scTime), 3);//时间差 $string = date('Y-m-d H:i:s', time()) . ' '; $string .= $_SERVER['REQUEST_URI'] . PHP_EOL; $body = file_get_contents("php://input"); if (empty($body) == false) { if (is_array($body)) { $string .= 'body : ' . json_encode($body) . PHP_EOL; } else { $string .= 'body : ' . $body . PHP_EOL; } } $string .= 'PUT : ' . $result . PHP_EOL; $string .= 'time:' . $incrementTime . 's' . PHP_EOL . PHP_EOL; // file_put_contents(__DIR__ . '/../../../runtime/logs/getSoap/' . date('Y-m-d') . '.log', $string, FILE_APPEND); return parent::afterAction($action, $result); } public function divisionDate($data) { //将房态按时间分段 $dayZone = []; $firstDay = ''; $current = 0; foreach ($data as $key => $value) { if ($key == 0) { $dayZone[$current]['StartDate'] = $value['RUN_DATE']; $dayZone[$current]['EndDate'] = $value['RUN_DATE']; $firstDay = $value['RUN_DATE']; } else { $firstDay = strtotime($firstDay); //用于判断当前日期与该组数据的StartDate差值是否是120,一次最多推送120天数据,大于120天分组推送 $diff = ($firstDay - strtotime($dayZone[$current]['StartDate'])) / 86400 / 99; if ($diff < 1) { $dayZone[$current]['EndDate'] = $value['RUN_DATE']; } else { $current++; $dayZone[$current]['StartDate'] = $value['RUN_DATE']; $dayZone[$current]['EndDate'] = $value['RUN_DATE']; } $firstDay = $value['RUN_DATE']; } } return $dayZone; } } ?>