'success', self::CTRIP_HOTEL_CODE => '携程创建子酒店失败', self::SYS_ERR0R => '系统错误', self::MASTER_HOTEL_EMPTY => '母酒店ID不能为空', self::REPEAT_CREATE => '携程子酒店已创建,但cs系统不存在子酒店id', self::MAPPING_HOTEL_ERROR => '酒店关联失败', self::UNMAPPING_CODE => '请检查 Mapping 关系!', self::PARAM_ERR0R => '请求参数缺失', self::ADD_HOTEL_ERR => '添加酒店信息失败', self::GET_MASTER_HOTEL_ERR => '获取代理通酒店信息失败', ]; const CTRIP_ID = 669; //携程 const QUNAR_ID = 1667; //去哪 const ELONG_ID = 1668; //艺龙 const CHANNELA_ID = 1669; //分销A const B2B_ID = 1670; //b2b public function __construct(array $config = []) { $this->supplierID = Yii::$app->params['ctrip_switch']['switch_supplier_id']; $this->ctripConf = Yii::$app->params['ctrip_switch']; parent::__construct($config); } /** * Created by PhpStorm. * NOTES:设置场景 * User: Steven * Date: 2018/4/28 * Time: 11:11 * Class scenarios * @return array */ public function scenarios() { $scenarios = parent::scenarios(); $scenarios['updateRoomSaleName'] = ['zz_room_id', 'zz_channel_id', 'zz_hotel_id']; $scenarios['setRoomOnlineOffline'] = ['zz_room_id', 'zz_channel_id', 'roomStatus']; $scenarios['CheckMapping'] = ['zz_hotel_id', 'zz_channel_id', 'zz_room_id']; // 验证的zz_room_id 实为room_type $scenarios['PushRoomData'] = ['zz_hotel_id', 'zz_room_id', 'zz_channel_id', 'roomDataEntitys']; return $scenarios; } /** * Created by PhpStorm. * NOTES: * User: Steven * Date: 2018/4/28 * Time: 11:11 * Class rules * @return array */ public function rules() { return [ //酒店试单 [['zz_room_id', 'zz_channel_id', 'zz_hotel_id'], 'required', 'on' => ['updateRoomSaleName']], [['zz_room_id', 'zz_channel_id', 'roomStatus'], 'required', 'on' => ['setRoomOnlineOffline']], [['zz_hotel_id', 'zz_channel_id', 'zz_room_id'], 'required', 'on' => ['CheckMapping']], [['zz_hotel_id', 'zz_channel_id', 'zz_room_id', 'roomDataEntitys'], 'required', 'on' => ['CheckMapping']], ]; } public function loadPushData($data, $formName = null) { $scope = $formName === null ? $this->formName() : $formName; if ($scope === '' && !empty($data)) { $this->setAttributes($data, false); if (!$this->getProductInfo()) { return false; } return true; } elseif (isset($data[$scope])) { $this->setAttributes($data[$scope]); return true; } else { return false; } } /** * Notes:request请求 * User: Steven * Date: 2018/4/16 * Time: 15:28 * @param $target_url * @param $data * @return mixed */ public function request($target_url, $data) { $data['requestor'] = $this->getCommonParams(); $timestamp = $this->getTimestamp(); $signature = $this->setSignature($this->ctripConf['switch_supplier_id'], $timestamp, $this->ctripConf['interfacekey']); $client = new Client(['baseUrl' => $this->ctripConf['base_url']]); $request = $client->createRequest() ->setHeaders(['content-type' => 'application/json;charset=UTF-8']) ->addHeaders(["timestamp" => $timestamp]) ->addHeaders(['signature' => $signature]) ->setFormat(Client::FORMAT_JSON) ->setMethod('post') ->setUrl($target_url) ->setData($data); $t1 = microtime(true); self::writeLog($target_url . '-' . $timestamp, json_encode($request->getData())); $response = $request->send(); $t2 = microtime(true); self::writeLog($target_url . '-' . $timestamp . ' 耗时:' . round($t2 - $t1, 3) . 's', json_encode($response->getData()) . PHP_EOL); return json_decode($response->content); } /** * Notes:生成公共节点 * User: Steven * Date: 2018/4/19 * Time: 14:38 * @return array */ private function getCommonParams() { return [ 'invoker' => $this->ctripConf['requestor']['invoker'], 'operatorName' => $this->ctripConf['requestor']['operatorName'], 'opClientIP' => $this->ctripConf['requestor']['opClientIP'], 'userId' => $this->ctripConf['requestor']['userId'], 'languageType' => $this->ctripConf['requestor']['languageType'], ]; } /** * Notes:加密验证逻辑 * User: Steven * Date: 2018/4/16 * Time: 15:42 * @param $supplierID //供应商ID,int类型 * @param $timestamp * @param $interfacekey //密钥Key * @param $signature * @return bool */ private function getSignature($supplierID, $timestamp, $interfacekey, $signature) { //加密算法是md5(base64) $signature_str = strtoupper(base64_encode(md5($supplierID . $timestamp . $interfacekey, true))); return $signature == $signature_str; } /** * Notes:生成加密串 * User: Steven * Date: 2018/4/16 * Time: 17:58 * @param $supplierID * @param $timestamp * @param $interfacekey * @return string */ private function setSignature($supplierID, $timestamp, $interfacekey) { //加密算法是md5(base64) $signature = strtoupper(base64_encode(md5($supplierID . $timestamp . $interfacekey, true))); return $signature; } /** * Notes:获取当前时间的毫秒数 * User: Steven * Date: 2018/4/16 * Time: 18:01 * @return float */ public function getTimestamp() { list($msec, $sec) = explode(' ', microtime()); $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000); return $msectime; } /** * Created by PhpStorm. * NOTES:获取接口所需要的渠道ID * User: Steven * Date: 2018/4/27 * Time: 16:25 * Class getChannelID * @param $channel_str * @return string */ public function getChannelID($channel_str) { $channel_arr = explode(',', $channel_str); $channel_id_str = ''; foreach ($channel_arr as $value) { $channel_id_str .= $channel_id_str == '' ? '' : ','; switch ($value) { case self::CTRIP_ID: $channel_id_str .= 'Ctrip'; break; case self::QUNAR_ID: $channel_id_str .= 'Qunar'; break; case self::ELONG_ID: $channel_id_str .= 'Elong'; break; case self::CHANNELA_ID: $channel_id_str .= 'ChannelA'; break; case self::B2B_ID: $channel_id_str .= 'B2B'; break; default: break; } } return $channel_id_str; } /** * Notes:根据代理通渠道标识获取CS蜘蛛渠道ID * User: Steven * Date: 2018/5/11 * Time: 11:37 * @param $dltOrderId * @return int */ public static function SupplierDict($supplier_str) { //supplier:手工单,这种订单是人工在代理通录入的 switch ($supplier_str) { case 'EBK': case 'Email': case 'SMS': case 'FAX': case 'DirectConn': case 'NotCtripOrder': return 669; break; case 'Elong': return 1668; break; case 'Qunar': return 1667; break; case 'B2B': case 'B2BOffLine': return 1670; break; case 'TC': return 1669; break; default: return 0; } } /** * Created by PhpStorm. * NOTES:批量插入数据 * User: Steven * Date: 2018/4/20 * Time: 11:37 * Class batchInsert * @param $model 表名称 * @param $table_key 表字段 * @param $table_value 表字段的值 * @return int 受影响行数 * @throws yii\db\Exception */ public function batchInsert($model, $table_key, $table_value) { $res = Yii::$app->db->createCommand() ->batchInsert($model, $table_key, $table_value) ->execute(); $query = CtripCountryList::find() ->from('user') ->indexBy('username'); $query->each(); return $res; } /** * Notes:携程代理通直连请求日志 * User: Steven * Date: 2018/5/22 * Time: 16:00 * @param $Invoketype * @param $string */ public static function writeLog($Invoketype, $string) { $dir = "/../../../runtime/logs/ctripSwitchLog"; $string = date('Y-m-d H:i:s') . " $Invoketype " . PHP_EOL . $string . PHP_EOL; if (!file_exists(__DIR__ . $dir)) { mkdir(__DIR__ . $dir, 0777, true); } file_put_contents(__DIR__ . $dir . '/' . date('Y-m-d') . '.log', $string, FILE_APPEND); } /** * @Author: wanglg * @DESC: 获取房型在某渠道的信息 * @param $params // hotel_id,parent_room_type, room_type,channel_id, start_date, end_date * @return $this */ public function getProductInfo() { // 查询某房型在一定日期段内的房态房价信息 $products = OperaHotelRoom::find() ->leftJoin('opera_room_distrib b', 'a.ID=b.ROOM_ID and b.cancel_flag=0 and b.distrib_id=' . $this->zz_channel_id) ->leftJoin('run_hotel_distrib c', 'a.hotel_id=c.hotel_id and a.parent_room_type=c.base_room_type and a.room_type=c.room_type and c.distrib_id=b.distrib_id') ->leftJoin('run_hotel_sub_room d', 'a.hotel_id=d.hotel_id and a.parent_room_type=d.base_room_type and a.room_type=d.room_type and c.run_date=d.run_date') ->leftJoin('run_hotel e', 'a.hotel_id=e.hotel_id and a.parent_room_type=e.base_room_type and c.run_date=e.run_date') ->leftJoin('opera_hotel_gift f', 'c.gift_id=f.id and a.hotel_id=f.hotel_id and f.cancel_flag=0') ->from('opera_hotel_room a') ->where(['a.hotel_id' => $this->zz_hotel_id, 'a.parent_room_type' => $this->zz_base_room_id, 'a.room_type' => $this->zz_room_id, 'a.cancel_flag' => 0,]); $products->andWhere(['between', 'c.run_date', $this->start_date, $this->end_date]); $products->addSelect(['if(c.prod_price = 0, c.cus_price, c.prod_price) as room_price', 'a.breakfast_include', 'c.distrib_id', 'c.run_date', 'c.run_status', 'b.channel_mapping_id', 'c.oversell_flag', 'c.remaining_count', 'c.saled_count', 'b.latest_comfirm_time', 'b.distrib_room_name', 'b.authority_status', 'a.lastest_book_time', 'd.is_onsale as base_is_onsale', 'd.run_status as base_run_status', 'c.gift_id', 'f.gift_name', 'f.gift_content', 'e.is_onsale base_room_onsale', 'SUM(case e.stock_type when 228 then e.remaining_count else 0 end) as buyout', 'SUM(case e.stock_type when 229 then e.remaining_count else 0 end) as inquiry', 'SUM(case e.stock_type when 230 then e.remaining_count else 0 end) as retain',]); $products->groupBy('c.run_date'); $res = $products->asArray()->all(); foreach ($res as $key => $value) { // 获取渠道信息, 处理渠道名称,如果是推送房价、房态、动态规则,渠道名称首字母大写, Ctrip,Qunar, QunarB,QunarD,QunarY,QunarT ,Elong,B2B ,ChannelA,B2BOffline ,Share // 如果是房量,渠道名称全部为小写 ctrip,qunar,elong,manual $channel_price = $value['distrib_id'] == Yii::$app->params['ctrip']['supplier_id'] ? 'Ctrip' : ($value['distrib_id'] == Yii::$app->params['qunar']['supplier_id'] ? 'Qunar' : ($value['distrib_id'] == Yii::$app->params['elong']['supplier_id'] ? 'Elong' : ($value['distrib_id'] == Yii::$app->params['channela']['supplier_id'] ? 'ChannelA' : ($value['distrib_id'] == Yii::$app->params['channela']['supplier_id'] ? 'B2B' : '')))); $channel_quanity = $value['distrib_id'] == Yii::$app->params['ctrip']['supplier_id'] ? 'ctrip' : ($value['distrib_id'] == Yii::$app->params['qunar']['supplier_id'] ? 'qunar' : ($value['distrib_id'] == Yii::$app->params['elong']['supplier_id'] ? 'elong' : ($value['distrib_id'] == Yii::$app->params['channela']['supplier_id'] ? 'channela' : ($value['distrib_id'] == Yii::$app->params['channela']['supplier_id'] ? 'b2b' : '')))); if (!$channel_quanity || !$channel_price) { return false; } if ($value['base_run_status'] == 0 || $value['base_is_onsale'] == 0 || $value['run_status'] == 329 || $value['authority_status'] == 0) { $sale_status = 0; } else { if ($value['oversell_flag'] == 0) { $sale_status = $value['remaining_count'] <= 0 ? 0 : 2; } else { $sale_status = 1; } } $pre_quantity = $value['remaining_count'] <= 0 ? 0 : $value['remaining_count']; $data = [ 'roomId' => (int)$value['channel_mapping_id'], 'startDate' => $value['run_date'], 'endDate' => $value['run_date'], 'weekDayIndex' => '1111111', //依次周一值周日,为1设置生效为0不生效 'roomPriceModel' => [ //房价信息 'roomPrice' => (float)$value['room_price'], 'tax' => (float)0, 'currency' => 'CNY', 'breakfast' => (int)$value['breakfast_include'], 'channel' => $channel_price, ], 'roomStatusModel' => [ // 房态信息 'saleStatus' => (int)$sale_status, // 0. 满房 1. 销售 2. 限量 'channel' => $channel_price, ], 'roomInventoryModel' => [ // 房量信息 'preservedQuantity' => (int)$pre_quantity, // 保留房数量 'unPreservedQuantity' => (int)($value['buyout'] + $value['inquiry'] + $value['retain'] - $pre_quantity), //非保留房数量 'autoCloseRoom' => 0, // 自动关房 'channel' => $channel_quanity, // 渠道 ], 'saleRuleModel' => [ 'channel' => $channel_price, ], ]; // 如果当前渠道是b2b 或 channelA ,动态售卖规则中的 礼盒信息和最晚立即确认时间不能同时为空 if (empty($value['gift_id']) && $value['latest_comfirm_time'] == -1 && (strtolower($channel_price) == 'b2b' || strtolower($channel_quanity) == 'channela')) { return false; } if (!empty($value['gift_id'])) { $data['saleRuleModel']['roomGiftRule'] = [ // 礼盒规则 'giftId' => $value['gift_id'], // 礼盒ID 'takeEffectType' => 1, //1:入住生效 2:离店生效 3:在店生效 'giftDesc' => $value['distrib_id'] == Yii::$app->params['ctrip']['supplier_id'] ? $value['gift_content'] : '', // 礼盒信息 ]; } // 如果有设置渠道最晚预订时间推送相关信息 if ($value['latest_comfirm_time'] != -1) { $last_confirm = explode(',', $value['latest_comfirm_time']); if ($value['distrib_id'] != Yii::$app->params['ctrip']['supplier_id']) { $last_book_time = explode(',', $value['lastest_book_time']); $data['saleRuleModel']['ctripSellRule'] = [ 'latestconfirmTimeOfDays' => $last_confirm[0], 'latestconfirmTimeOfHours' => Utils::timeHour($last_confirm[1]), 'latestBookingTimeOfDays' => $last_book_time[0], 'latestBookingTimeOfHours' => Utils::timeHour($last_book_time[1]), 'cancelType' => 1, ]; } else { $data['saleRuleModel']['sellingRule'] = [ 'latestconfirmTimeOfDays' => (int)$last_confirm[0], 'latestconfirmTimeOfHours' => Utils::timeHour($last_confirm[1]), ]; } } $this->roomDataEntitys[] = $data; } return $this; } /** * @Author: wanglg * @DESC:判断酒店、房型、基础房型是否直连 * @param :hotel_id 酒店ID * @param :room_type 子房型type * @param :channel_id 关联渠道ID:可不传,默认为携程 * @return array */ public function isMapping($hotel_id, $room_type = 0, $channel_id = 669) { // 判断渠道 $request = ['zz_channel_id' => $channel_id, 'zz_hotel_id' => $hotel_id, 'zz_room_id' => $room_type]; $request['zz_channel_id'] = empty($request['zz_channel_id']) ? Yii::$app->params['ctrip']['supplier_id'] : $request['zz_channel_id']; $model = new CtripSwitch(['scenario' => 'CheckMapping']); if (!$model->load($request, '') || !$model->validate()) { return ['code' => CtripSwitch::PARAM_ERR0R, 'msg' => CtripSwitch::RETURN_MSG[CtripSwitch::PARAM_ERR0R]]; } $query = ChannelHotelMapping::find() ->select(['a.sub_hotel_id']) ->from('channel_hotel_mapping a')->where(['a.hotel_id' => $model->zz_hotel_id, 'a.channel_id' => $model->zz_channel_id, 'a.cancel_flag' => 0]); // 子房型type不为空,关联查询子房型是否直连 if (!empty($model->zz_room_id)) { $query->addSelect(['b.sub_base_room_id', 'd.channel_mapping_id']); $query->leftJoin('opera_hotel_room c', 'a.hotel_id=c.hotel_id and c.room_type=' . $model->zz_room_id) ->leftJoin('channel_base_room_mapping b', 'b.base_room_id=c.parent_room_type and b.cancel_flag=0 and a.sub_hotel_id=b.sub_hotel_id and a.channel_id=b.channel_id and a.hotel_id=b.hotel_id and a.master_hotel_id = b.master_hotel_id') ->leftJoin('opera_room_distrib d', 'd.room_id=c.id and d.distrib_id=a.channel_id'); } $mapping_res = $query->asArray()->one(); // 代理通酒店id未查到说明酒店未直连 if (empty($mapping_res['sub_hotel_id'])) { return ['code' => CtripSwitch::UNMAPPING_CODE, 'msg' => CtripSwitch::RETURN_MSG[CtripSwitch::UNMAPPING_CODE]]; } // 存在子房型但是结果集中没有查到代理通售卖房型id,说明子房型未直连 if (!empty($model->zz_room_id) && (empty($mapping_res['channel_mapping_id']) || empty($mapping_res['sub_base_room_id']))) { return ['code' => CtripSwitch::UNMAPPING_CODE, 'msg' => CtripSwitch::RETURN_MSG[CtripSwitch::UNMAPPING_CODE]]; } // 成功,返回直连id return ['code' => CtripSwitch::SUCCESS_CODE, 'msg' => CtripSwitch::RETURN_MSG[CtripSwitch::SUCCESS_CODE], 'data' => $mapping_res]; } /** * Notes:根据携程酒店ID ,房型ID 查找mapping 的蜘蛛Id * User: Steven * Date: 2018/5/11 * Time: 19:38 */ public static function getZzIDByCtripID($distrib_id, $room_id) { $res = OperaRoomDistrib::find()->select(['b.HOTEL_ID', 'b.ID as ROOM_ID']) ->leftJoin('opera_hotel_room b', 'a.ROOM_ID=b.ID') ->from('opera_room_distrib a') ->where(['a.CHANNEL_MAPPING_ID' => 37138191, 'a.DISTRIB_ID' => 669, 'a.CANCEL_FLAG' => 0, 'b.CANCEL_FLAG' => 0]) ->asArray()->one(); return $res; } /** * @Author: wanglg * @DESC:通过传入需要处理的周天数据,例如 周二周三[2, 3] * @param $week_arr * @return array */ public function weekDays($week_arr) { $init_arr = array(0, 0, 0, 0, 0, 0, 0); foreach ($week_arr as $value) { $init_arr[$value - 1] = 1; } return $init_arr; } }