| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419 |
- <?php
- namespace common\components\delivery\services\adapter;
- use bizGhs\order\classes\OrderItemClass;
- use bizHd\order\classes\OrderGoodsClass;
- use common\components\delivery\helpers\HttpClient;
- use common\components\delivery\platform\fengniao\Fengniao;
- use common\components\util;
- use Yii;
- /**
- * 蜂鸟适配器
- *
- * 基于蜂鸟开放平台API进行封装
- * 文档参考:蜂鸟业务接口调用说明
- *
- * 接口规范:
- * - 调用方式:HTTPS/POST
- * - 编码格式:UTF-8
- * - Content-Type:application/json
- * - 正式环境:https://open-anubis.ele.me/anubis-webapi/v3/invoke/{method}
- * - 沙箱环境:https://exam-anubis.ele.me/anubis-webapi/v3/invoke/{method}
- */
- class FengniaoAdapter extends Fengniao implements Adapter
- {
- /**
- * 获取已开通城市列表
- *
- * 通过此接口可获取到蜂鸟所有已开通的城市列表信息
- *
- * @return array 包含城市列表的响应数据
- */
- public function cityList()
- {
- $businessData = [];
-
- // 构建请求参数
- $payload = $this->buildRequestPayload('cityList', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/cityList';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] cityList Response: " . json_encode($resp));
- return $resp;
- }
- /**
- * 转化订单数据
- * @param $order
- * @param string $type 订单类型:xhOrder 或 xhGhsOrder
- * @param $shop
- * @param $params
- * @return array
- * @throws \Exception
- */
- public function formatOrderData($order, $type, $shop, $params)
- {
- // 订单类型为预约单时必传,如需要送达时间 – 推单时间 < 60min,则蜂鸟配送开放平台自动将订单类型置为即时单
- $timeDiff = strtotime($params['pickupTime']) - time();
- $orderType = 1;
- if ($timeDiff > 3600) {
- $orderType = 3;
- }
- $sn = $order['orderSn'];
- // 批发
- if($type == 'xhGhsOrder'){
- $itemInfos = OrderItemClass::getAllByCondition(['orderSn' => $sn], null, 'id, name, unitPrice, num, unitWeight');
- //$goodsWeight = 0;
- $goodsItemList = [];
- foreach ($itemInfos as $item){
- $goodsItemList[] = [
- 'item_actual_amount_cent' => $item['unitPrice'] * 100 * $item['num'],
- 'item_amount_cent' => $item['unitPrice'] * 100,
- 'item_id' => $item['id'],
- 'item_name' => $item['name'],
- 'item_quantity' => $item['num'],
- ];
- //$goodsWeight += $item['unitWeight'];
- }
- $goodsCount = count($goodsItemList);
- // 零售
- }else if($type == 'xhOrder'){
- $itemInfos = OrderGoodsClass::getAllByCondition(['orderSn' => $sn], null, 'id, name, unitPrice, num');
- //$goodsWeight = 0;
- $goodsItemList = [];
- foreach ($itemInfos as $item){
- $goodsItemList[] = [
- 'item_actual_amount_cent' => $item['unitPrice'] * 100 * $item['num'],
- 'item_amount_cent' => $item['unitPrice'] * 100,
- 'item_id' => $item['id'],
- 'item_name' => $item['name'],
- 'item_quantity' => $item['num'],
- ];
- }
- $goodsCount = count($goodsItemList);
- }else{
- Yii::error('订单编号出错: '.$sn);
- util::fail('订单编号出错');
- }
- $goodsWeight = floatval($params['weight']);//取客户端传来的重量
- $orderData = [
- //'out_shop_code' => $params['mainId'], // 当使用门店发单 out_shop_code 和 chain_store_id 必填1个
- 'chain_store_id' => $this->getShopId(), // 动态修改为门店号 -- (点对点不传)
- 'partner_order_code' => $order['orderSn'],
- 'receiver_primary_phone' => Functions::getMobile($order),
- 'receiver_name' => $order['customName'],
- 'receiver_latitude' => (float)$order['lat'],
- 'receiver_longitude' => (float)$order['long'],
- 'receiver_address' => $order['fullAddress'],
- 'position_source' => 3, // 坐标经纬度来源 -- 3 高德地图
- 'goods_count' => $goodsCount,
- 'goods_weight' => (float)$goodsWeight,
- 'goods_total_amount_cent' => (int)($order['prePrice'] * 100), //订单商品总金额
- 'goods_actual_amount_cent' => (int)($order['actPrice'] * 100), //订单货物实付金额
- 'goods_item_list' => $goodsItemList,
- 'order_type' => $orderType,
- 'order_remark' => $params['remark'],
- 'serial_number' => $order['sendNum'] // 配送编号,方便寻找订单对应的花材
- ];
- if($orderType == 3) {
- // 预计出餐时间 单位:毫秒 -- 预约单该字段必填
- $orderData['expect_fetch_time'] = strtotime($params['pickupTime']) * 1000;
- // 需要送达时间 = 取件时间 + 30分钟
- $orderData['require_receive_time'] = (strtotime($params['pickupTime']) + 1800) * 1000;
- }
- return $orderData;
- }
- /**
- * 创建订单(需用户授权)
- *
- * 根据蜂鸟官方文档实现下单功能。
- * 若需校验配送费价格,需先走预下单接口,并在下单时填上预下单信息。
- *
- * @param array $data 订单数据,结构参考:
- * [
- * // ========== 必填参数 ==========
- * 'partner_order_code' => '外部订单号(用于幂等)',
- * 'receiver_primary_phone' => '收货人主要联系方式(手机号、400电话、座机等)',
- * 'receiver_name' => '收货人姓名',
- * 'receiver_latitude' => '收货人纬度',
- * 'receiver_longitude' => '收货人经度',
- * 'receiver_address' => '收货人地址',
- * 'position_source' => '经纬度来源(1:腾讯, 2:百度, 3:高德,推荐3)',
- * 'goods_count' => '货物件数',
- * 'goods_weight' => '货物总重量(kg)',
- * 'goods_total_amount_cent' => '订单商品总金额(分)',
- * 'goods_actual_amount_cent' => '订单货物实付金额(分)',
- * 'goods_item_list' => '货物明细列表',
- * 'order_type' => '订单类型(1:即时单, 3:预约单)',
- *
- * // ========== 可选参数 ==========
- * // 门店信息(二选一)
- * 'chain_store_id' => '门店ID',
- * 'out_shop_code' => '外部门店ID',
- *
- * // 预下单关联
- * 'pre_create_order_t_index_id' => '预下单标识(用于校验配送费价格)',
- * 'actual_delivery_amount_cent' => '优惠后配送费总价(分,不为null则校验价格)',
- *
- * // 取货点信息(点对点特殊场景)
- * 'transport_address' => '取货点地址',
- * 'transport_name' => '取货点名称',
- * 'transport_latitude' => '取货点纬度',
- * 'transport_longitude' => '取货点经度',
- * 'transport_tel' => '取货点联系人电话',
- *
- * // 时间相关
- * 'order_add_time' => '下单时间(毫秒)',
- * 'require_receive_time' => '需要送达时间(毫秒,预约单时重要)',
- * 'expect_fetch_time' => '预计出餐时间(毫秒,预约单必填)',
- *
- * // 订单备注和来源
- * 'order_remark' => '用户备注',
- * 'order_source' => '商户订单来源(0:手发单, 2:美团, 4:口碑, 6:饿了么等)',
- * 'order_source_order_id' => '订单来源单号',
- * 'serial_number' => '商家订单流水号(长度<=8)',
- *
- * // 骑手交互
- * 'fetch_code' => '取餐码',
- * 'write_off_code' => '核销码',
- * 'cancel_code' => '取消码',
- * 'customer_ext_tel' => '分机号',
- *
- * // 收货人备用信息
- * 'receiver_second_phone' => '收货人备用联系方式',
- *
- * // 小费和优惠
- * 'order_tip_amount_cent' => '订单小费金额(分)',
- * 'use_coupon' => '是否使用优惠券(0:否, 1:是,默认使用)',
- * 'warehouse_id' => '优惠券记录ID',
- *
- * // 商品选择
- * 'base_goods_id' => '基础商品ID',
- * 'service_goods_id' => '服务商品ID',
- * 'appoint_extra_goods_ids' => '指定增值商品ID列表',
- *
- * 'goods_item_list' => [ // 货物明细(必填)
- * [
- * 'item_id' => '商品编号',
- * 'item_name' => '商品名称',
- * 'item_amount_cent' => '商品原价(分,必填)',
- * 'item_actual_amount_cent' => '商品实际支付金额(分,必填)',
- * 'item_size' => '商品尺寸(1:小, 2:中, 3:大)',
- * 'item_quantity' => '商品数量',
- * 'item_remark' => '商品备注',
- * ],
- * ],
- * ]
- * @return array API响应结果
- * 成功:['code' => 0, 'data' => ['order_id' => 订单ID]]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- */
- public function createOrder($data)
- {
- // 构建货物明细列表
- $goodsItemList = [];
- if (!empty($data['goods_item_list']) && is_array($data['goods_item_list'])) {
- foreach ($data['goods_item_list'] as $item) {
- $goodsItem = [
- 'item_actual_amount_cent' => isset($item['item_actual_amount_cent']) ? (int)$item['item_actual_amount_cent'] : 0,
- 'item_amount_cent' => isset($item['item_amount_cent']) ? (int)$item['item_amount_cent'] : 0,
- ];
-
- if (!empty($item['item_id'])) {
- $goodsItem['item_id'] = $item['item_id'];
- }
- if (!empty($item['item_name'])) {
- $goodsItem['item_name'] = $item['item_name'];
- }
- if (isset($item['item_size'])) {
- $goodsItem['item_size'] = (int)$item['item_size'];
- }
- if (isset($item['item_quantity'])) {
- $goodsItem['item_quantity'] = (int)$item['item_quantity'];
- }
- if (!empty($item['item_remark'])) {
- $goodsItem['item_remark'] = $item['item_remark'];
- }
-
- $goodsItemList[] = $goodsItem;
- }
- }
- // 构建业务数据 - 必填字段
- $businessData = [
- 'partner_order_code' => $data['partner_order_code'],
- 'receiver_primary_phone' => $data['receiver_primary_phone'],
- 'receiver_name' => $data['receiver_name'],
- 'receiver_latitude' => (double)($data['receiver_latitude']),
- 'receiver_longitude' => (double)($data['receiver_longitude']),
- 'receiver_address' => $data['receiver_address'],
- 'position_source' => $data['position_source'],
- 'goods_count' => (int)$data['goods_count'],
- 'goods_weight' => (float)$data['goods_weight'],
- 'goods_total_amount_cent' => $data['goods_total_amount_cent'],
- 'goods_actual_amount_cent' => $data['goods_actual_amount_cent'],
- 'goods_item_list' => $goodsItemList,
- 'order_type' => $data['order_type'],
- ];
- // 可选参数 - 门店信息(二选一:chain_store_id 或 out_shop_code)
- if (!empty($data['chain_store_id'])) {
- $businessData['chain_store_id'] = (int)$data['chain_store_id'];
- }
- if (!empty($data['out_shop_code'])) {
- $businessData['out_shop_code'] = $data['out_shop_code'];
- }
- // 可选参数 - 预下单关联(用于价格校验)
- if (!empty($data['pre_create_order_t_index_id'])) {
- $businessData['pre_create_order_t_index_id'] = $data['pre_create_order_t_index_id'];
- }
- if (isset($data['actual_delivery_amount_cent']) && $data['actual_delivery_amount_cent'] !== null) {
- $businessData['actual_delivery_amount_cent'] = (int)$data['actual_delivery_amount_cent'];
- }
- // 可选参数 - 取货点信息(点对点特殊场景)
- if (!empty($data['transport_address'])) {
- $businessData['transport_address'] = $data['transport_address'];
- }
- if (!empty($data['transport_name'])) {
- $businessData['transport_name'] = $data['transport_name'];
- }
- if (isset($data['transport_latitude']) && $data['transport_latitude'] !== '') {
- $businessData['transport_latitude'] = (double)$data['transport_latitude'];
- }
- if (isset($data['transport_longitude']) && $data['transport_longitude'] !== '') {
- $businessData['transport_longitude'] = (double)$data['transport_longitude'];
- }
- if (!empty($data['transport_tel'])) {
- $businessData['transport_tel'] = $data['transport_tel'];
- }
- // 可选参数 - 时间相关
- if (!empty($data['order_add_time'])) { // 单位:毫秒
- $businessData['order_add_time'] = (int)$data['order_add_time'];
- }
- if (!empty($data['require_receive_time'])) {
- $businessData['require_receive_time'] = $data['require_receive_time'];
- }
- // 预计出餐时间 单位:毫秒 预约单该字段必填
- if (!empty($data['expect_fetch_time'])) {
- $businessData['expect_fetch_time'] = (int)$data['expect_fetch_time'];
- }
- // 可选参数 - 订单备注和来源
- if (!empty($data['order_remark'])) {
- $businessData['order_remark'] = $data['order_remark'];
- }
- if (!empty($data['order_source'])) {
- $businessData['order_source'] = $data['order_source'];
- }
- if (!empty($data['order_source_order_id'])) {
- $businessData['order_source_order_id'] = $data['order_source_order_id'];
- }
- if (!empty($data['serial_number'])) {
- $businessData['serial_number'] = $data['serial_number'];
- }
- // 可选参数 - 骑手交互
- if (isset($data['fetch_code'])) {
- $businessData['fetch_code'] = (int)$data['fetch_code'];
- }
- if (isset($data['write_off_code'])) {
- $businessData['write_off_code'] = (int)$data['write_off_code'];
- }
- if (isset($data['cancel_code'])) {
- $businessData['cancel_code'] = (int)$data['cancel_code'];
- }
- if (!empty($data['customer_ext_tel'])) {
- $businessData['customer_ext_tel'] = $data['customer_ext_tel'];
- }
- // 可选参数 - 收货人备用信息
- if (!empty($data['receiver_second_phone'])) {
- $businessData['receiver_second_phone'] = $data['receiver_second_phone'];
- }
- // 可选参数 - 小费和优惠
- if (isset($data['order_tip_amount_cent'])) {
- $businessData['order_tip_amount_cent'] = (int)$data['order_tip_amount_cent'];
- }
- if (isset($data['use_coupon'])) {
- $businessData['use_coupon'] = (int)$data['use_coupon'];
- }
- if (!empty($data['warehouse_id'])) {
- $businessData['warehouse_id'] = (int)$data['warehouse_id'];
- }
- // 可选参数 - 商品选择
- // 注意:基础商品ID和服务商品ID不能传0(0有意义),应传null或不传
- if (isset($data['base_goods_id']) && $data['base_goods_id'] !== null && $data['base_goods_id'] != 0) {
- $businessData['base_goods_id'] = (int)$data['base_goods_id'];
- }
- if (isset($data['service_goods_id']) && $data['service_goods_id'] !== null && $data['service_goods_id'] != 0) {
- $businessData['service_goods_id'] = (int)$data['service_goods_id'];
- }
- if (!empty($data['appoint_extra_goods_ids']) && is_array($data['appoint_extra_goods_ids'])) {
- $businessData['appoint_extra_goods_ids'] = array_map('intval', $data['appoint_extra_goods_ids']);
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('createOrder', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/createOrder';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] createOrder Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- if (isset($respData['order_id'])) {
- return [
- 'code' => 0,
- 'platform' => 'fengniao',
- 'data' => [
- 'orderId' => $respData['order_id'],
- 'fee' => 0, // 请从"查询订单详情"接口获取 -- order_actual_amount_cent
- 'distance' => 0, // 请从"查询订单详情"接口获取 -- order_distance
- ]
- ];
- }
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 构建运费报价请求信息(供并发请求使用)
- *
- * @param array $data 报价参数
- * @param string $orderTime 配送时间
- * @return array|null 包含 url, payload, headers, timeout 的请求信息
- */
- public function buildPriceRequest($data, $orderTime)
- {
- // 订单类型为预约单时必传,如需要送达时间 – 推单时间 < 60min,则蜂鸟配送开放平台自动将订单类型置为即时单
- $timeDiff = strtotime($orderTime) - time();
- $orderType = 1;
- if ($timeDiff > 3600 ) {
- $orderType = 3;
- }
- // 构建货物明细列表
- $goodsItemList = [];
- if (!empty($data['goods_item_list']) && is_array($data['goods_item_list'])) {
- foreach ($data['goods_item_list'] as $item) {
- $goodsItem = [
- 'item_actual_amount_cent' => isset($item['item_actual_amount_cent']) ? (int)$item['item_actual_amount_cent'] : 0,
- 'item_amount_cent' => isset($item['item_amount_cent']) ? (int)$item['item_amount_cent'] : 0,
- ];
-
- if (!empty($item['item_id'])) {
- $goodsItem['item_id'] = $item['item_id'];
- }
- if (!empty($item['item_name'])) {
- $goodsItem['item_name'] = $item['item_name'];
- }
- if (isset($item['item_size'])) {
- $goodsItem['item_size'] = (int)$item['item_size'];
- }
- if (isset($item['item_quantity'])) {
- $goodsItem['item_quantity'] = (int)$item['item_quantity'];
- }
- if (!empty($item['item_remark'])) {
- $goodsItem['item_remark'] = $item['item_remark'];
- }
-
- $goodsItemList[] = $goodsItem;
- }
- }
- // 构建业务数据 - 必填字段
- $businessData = [
- 'partner_order_code' => $data['partner_order_code'],
- 'position_source' => isset($data['position_source']) ? (int)$data['position_source'] : 3, // 默认高德地图
- 'receiver_address' => $data['receiver_address'],
- 'receiver_longitude' => (string)($data['receiver_longitude']),
- 'receiver_latitude' => (string)($data['receiver_latitude']),
- 'goods_total_amount_cent' => $data['goods_total_amount_cent'],
- 'goods_actual_amount_cent' => $data['goods_actual_amount_cent'],
- 'goods_weight' => (float)$data['goods_weight'],
- 'goods_count' => (int)$data['goods_count'],
- 'goods_item_list' => $goodsItemList,
- 'order_type' => $orderType,
- ];
- // 可选参数 - 门店信息(二选一:chain_store_id 或 out_shop_code)
- if (!empty($data['chain_store_id'])) {
- $businessData['chain_store_id'] = (int)$data['chain_store_id'];
- }
- if (!empty($data['out_shop_code'])) {
- $businessData['out_shop_code'] = $data['out_shop_code'];
- }
- // 可选参数 - 取货点信息
- if (!empty($data['transport_address'])) {
- $businessData['transport_address'] = $data['transport_address'];
- }
- if (isset($data['transport_longitude']) && $data['transport_longitude'] !== '') {
- $businessData['transport_longitude'] = (double)$data['transport_longitude'];
- }
- if (isset($data['transport_latitude']) && $data['transport_latitude'] !== '') {
- $businessData['transport_latitude'] = (double)$data['transport_latitude'];
- }
- if (!empty($data['transport_tel'])) {
- $businessData['transport_tel'] = $data['transport_tel'];
- }
- // 可选参数 - 时间相关
- if (!empty($data['order_add_time'])) {
- $businessData['order_add_time'] = (int)$data['order_add_time'];
- }
- // 订单类型为预约单时必传,如需要送达时间 –--- 推单时间 < 60min,则蜂鸟配送开放平台自动将订单类型置为即时单
- if ($orderType == 3) {
- $businessData['require_receive_time'] = strtotime($orderTime)*1000;
- }
- // 可选参数 - 订单相关
- if (isset($data['order_tip_amount_cent'])) {
- $businessData['order_tip_amount_cent'] = (int)$data['order_tip_amount_cent'];
- }
- if (!empty($data['order_source'])) {
- $businessData['order_source'] = $data['order_source'];
- }
- if (!empty($data['order_source_order_id'])) {
- $businessData['order_source_order_id'] = $data['order_source_order_id'];
- }
- // 可选参数 - 优惠券及商品选择
- if (isset($data['use_coupon'])) {
- $businessData['use_coupon'] = (int)$data['use_coupon'];
- }
- // 注意:基础商品ID和服务商品ID不能传0(0有意义),应传null或不传
- if (isset($data['base_goods_id']) && $data['base_goods_id'] !== null && $data['base_goods_id'] != 0) {
- $businessData['base_goods_id'] = (int)$data['base_goods_id'];
- }
- if (isset($data['service_goods_id']) && $data['service_goods_id'] !== null && $data['service_goods_id'] != 0) {
- $businessData['service_goods_id'] = (int)$data['service_goods_id'];
- }
- if (!empty($data['appoint_extra_goods_ids']) && is_array($data['appoint_extra_goods_ids'])) {
- $businessData['appoint_extra_goods_ids'] = array_map('intval', $data['appoint_extra_goods_ids']);
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('preCreateOrder', $businessData);
- $url = $this->baseUrl . '/preCreateOrder';
- return [
- 'url' => $url,
- 'data' => $payload,
- 'headers' => ['Content-Type' => 'application/json'],
- 'timeout' => 5.0,
- ];
- }
- /**
- * 处理运费报价响应
- *
- * @param array $resp API 响应
- * @return array|null 解析后的报价结果
- */
- public function processPriceResponse($resp)
- {
- //Yii::info("[FengniaoAdapter] preCreateOrder Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return null;
- }
- // 解析商品信息列表
- $goodsInfos = [];
- if (!empty($respData['goods_infos']) && is_array($respData['goods_infos'])) {
- foreach ($respData['goods_infos'] as $goodsInfo) {
- // 解析价格明细
- $priceDetail = [];
- if (!empty($goodsInfo['price_detail']) && is_array($goodsInfo['price_detail'])) {
- $pd = $goodsInfo['price_detail'];
- $priceDetail = [
- 'start_price_cent' => (int)($pd['start_price_cent'] ?? 0),
- 'distance_price_cent' => (int)($pd['distance_price_cent'] ?? 0),
- 'weight_price_cent' => (int)($pd['weight_price_cent'] ?? 0),
- 'pressure_surcharge_cent' => (int)($pd['pressure_surcharge_cent'] ?? 0),
- 'time_period_surcharge_cent' => (int)($pd['time_period_surcharge_cent'] ?? 0),
- 'river_crossing_surcharge_cent' => (int)($pd['river_crossing_surcharge_cent'] ?? 0),
- 'category_surcharge_cent' => (int)($pd['category_surcharge_cent'] ?? 0),
- 'temporary_surcharge_cent' => (int)($pd['temporary_surcharge_cent'] ?? 0),
- 'order_price_surcharge_cent' => (int)($pd['order_price_surcharge_cent'] ?? 0),
- ];
- }
- $goodsInfos[] = [
- 'base_goods_id' => (int)($goodsInfo['base_goods_id'] ?? 0),
- 'service_goods_id' => (int)($goodsInfo['service_goods_id'] ?? 0),
- 't_index_id' => $goodsInfo['t_index_id'] ?? '',
- 'is_valid' => (int)($goodsInfo['is_valid'] ?? 0),
- 'disable_reason' => $goodsInfo['disable_reason'] ?? '',
- 'slogan' => $goodsInfo['slogan'] ?? '',
- 'actual_delivery_amount_cent' => (int)($goodsInfo['actual_delivery_amount_cent'] ?? 0),
- 'total_delivery_amount_cent' => (int)($goodsInfo['total_delivery_amount_cent'] ?? 0),
- 'predict_delivery_minutes' => (int)($goodsInfo['predict_delivery_minutes'] ?? 0),
- 'predict_delivery_time' => (int)($goodsInfo['predict_delivery_time'] ?? 0),
- 'can_add_tip' => (int)($goodsInfo['can_add_tip'] ?? 0),
- 'warehouse_id' => $goodsInfo['warehouse_id'] ?? null,
- 'price_detail' => $priceDetail,
- ];
- }
- }
- return [
- 'platform' => 'fengniao',
- 'distance' => (int)($respData['distance'] ?? 0),
- 'time' => (int)($respData['time'] ?? 0),
- 'goods_infos' => $goodsInfos,
- ];
- }
- Yii::error('fengniao 报价失败:'.json_encode($resp));
- return [
- 'platform' => 'fengniao',
- 'code' => $resp['code'],
- 'error' => $resp['msg']
- ];
- }
- /**
- * @deprecated
- * 获取运费报价(预下单接口)
- *
- * 根据蜂鸟官方文档实现报价功能,返回所有可选的商品列表
- * 每项包含当前使用该商品下单时对应的价格等信息
- *
- * @param array $data 报价参数
- * @param string $orderTime 配送时间
- * @return array|null 包含可用商品列表及报价信息的结果
- * @deprecated 使用 buildPriceRequest 和 processPriceResponse 替代
- */
- public function getPrice($data, $orderTime)
- {
- // $request = $this->buildPriceRequest($data, $orderTime);
- // $resp = HttpClient::post($request['url'], $request['data'], $request['headers']);
- // return $this->processPriceResponse($resp);
- }
- /**
- * 获取可用订单取消原因列表
- *
- * 根据蜂鸟官方文档获取该订单可用的取消原因列表。
- * 用户取消订单时需先调用此接口获取可选的取消原因。
- *
- * @param array $data 查询参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- * ]
- * @return array API响应结果
- * 成功:[
- * 'code' => 0,
- * 'data' => [
- * 'cancel_reason_list' => [
- * [
- * 'order_cancel_code' => 取消原因code,
- * 'order_cancel_desc' => '取消原因描述',
- * ],
- * // ... 更多原因
- * ]
- * ]
- * ]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- */
- public function getCancelReasonList($data)
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $orderId = $data['order_id'] ?? $data;
- $partnerOrderCode = null;
- // 如果 $orderId 是 XSD 开头,则给 partnerOrderCode 赋值为 $orderId
- if (strpos($orderId, 'XSD') === 0) {
- $partnerOrderCode = $orderId;
- $orderId = null;
- }
-
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] getCancelReasonList failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = (int)$orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('getCancelReasonList', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/getCancelReasonList';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- //Yii::info("[FengniaoAdapter] getCancelReasonList Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- // 解析取消原因列表
- $cancelReasonList = [];
- if (!empty($respData['cancel_reason_list']) && is_array($respData['cancel_reason_list'])) {
- foreach ($respData['cancel_reason_list'] as $reason) {
- $cancelReasonList[] = [
- 'order_cancel_code' => (int)($reason['order_cancel_code'] ?? 0),
- 'order_cancel_desc' => $reason['order_cancel_desc'] ?? '',
- ];
- }
- }
- return [
- 'code' => 0,
- 'data' => [
- 'reasonList' => $cancelReasonList,
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 订单取消
- *
- * 根据蜂鸟官方文档实现订单取消功能。
- * 支持商户取消和用户取消两种场景。
- *
- * @param array $data 取消参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- *
- * // ========== 必填参数 ==========
- * 'order_cancel_role' => '取消的角色(1:商户取消, 2:用户取消)',
- * 'order_cancel_code' => '取消原因code',
- * 'order_cancel_other_reason' => '取消原因补充(20字以内)',
- *
- * // ========== 可选参数 ==========
- * 'need_reverse_order' => '是否期望发起售中逆向(Boolean)',
- * 'actual_cancel_cost_cent' => '取消价格(分)',
- * ]
- * @return array API响应结果
- * 成功:['code' => 0, 'data' => ['result' => true]]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- */
- public function cancelOrder($orderId, $params)
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $partnerOrderCode = null;
- // 如果 $orderId 是 XSD_ 开头,则给 partnerOrderCode 赋值为 $orderId
- if (strpos($orderId, 'XSD') === 0) {
- $partnerOrderCode = $orderId;
- $orderId = null;
- }
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] cancelOrder failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 必填参数 - 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = $orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 必填参数 - 取消角色
- if (isset($params['order_cancel_role'])) {
- $businessData['order_cancel_role'] = (int)$params['order_cancel_role'];
- } else {
- Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_role is required");
- return [
- 'code' => -1,
- 'msg' => 'order_cancel_role is required (1:商户取消, 2:用户取消)',
- 'data' => null
- ];
- }
- // 必填参数 - 取消原因code
- if (isset($params['order_cancel_code'])) {
- $businessData['order_cancel_code'] = (int)$params['order_cancel_code'];
- } else {
- Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_code is required");
- return [
- 'code' => -1,
- 'msg' => 'order_cancel_code is required',
- 'data' => null
- ];
- }
- // 必填参数 - 取消原因补充
- if (!empty($params['order_cancel_reason'])) {
- $businessData['order_cancel_other_reason'] = $params['order_cancel_reason'];
- } else {
- Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_other_reason is required");
- return [
- 'code' => -1,
- 'msg' => 'order_cancel_other_reason is required (20字以内)',
- 'data' => null
- ];
- }
- // 可选参数 - 是否期望发起售中逆向
- if (isset($params['need_reverse_order']) && $params['need_reverse_order'] !== null) {
- $businessData['need_reverse_order'] = (bool)$params['need_reverse_order'];
- }
- // 可选参数 - 取消价格
- if (isset($params['actual_cancel_cost_cent']) && $params['actual_cancel_cost_cent'] !== null) {
- $businessData['actual_cancel_cost_cent'] = (int)$params['actual_cancel_cost_cent'];
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('cancelOrder', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/cancelOrder';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] cancelOrder Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'platform' => 'fengniao',
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- return [
- 'code' => 0,
- 'platform' => 'fengniao',
- 'data' => [
- 'result' => (bool)($respData['result'] ?? false),
- 'reverse_order_ok' => (bool)($respData['reverse_order_ok'] ?? false),
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'platform' => 'fengniao',
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 订单预取消接口
- *
- * 根据蜂鸟官方文档获取取消价格。
- * 需要查询并校验取消价格时使用,若强校验价格需在正式取消接口中把出参价格传过去。
- *
- * @param array $data 预取消查询参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- *
- * // ========== 必填参数 ==========
- * 'order_cancel_code' => '取消原因code(从可用取消原因列表接口返回结果选择)',
- * ]
- * @return array API响应结果
- * 成功:[
- * 'code' => 0,
- * 'data' => [
- * 'actual_cancel_cost_cent' => '取消实际需金额(分,默认使用已有免责取消权益使用后金额为0)',
- * ]
- * ]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- *
- * @note
- * - 获取取消价格,用于查询并校验取消价格
- * - actual_cancel_cost_cent 为取消实际需要支付的金额(分),通常为 0(使用免责取消权益)
- * - 若需强校验价格,应在正式取消接口(cancelOrder)中把此出参价格传过去
- * - 流程:先调用此方法获取取消价格 → 校验价格 → 调用 cancelOrder 正式取消
- */
- public function preCancelOrder($data)
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $orderId = $data['order_id'];
- $partnerOrderCode = null;
- // 如果 $orderId 是 XSD_ 开头,则给 partnerOrderCode 赋值为 $orderId
- if (strpos($orderId, 'XSD') === 0) {
- $partnerOrderCode = $orderId;
- $orderId = null;
- }
-
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] preCancelOrder failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 验证 order_cancel_code(取消原因code)必填
- if (!isset($data['order_cancel_code'])) {
- Yii::warning("[FengniaoAdapter] preCancelOrder failed: order_cancel_code is required");
- return [
- 'code' => -1,
- 'msg' => 'order_cancel_code is required (from getCancelReasonList)',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = (int)$orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 必填参数 - 取消原因code
- $businessData['order_cancel_code'] = (int)$data['order_cancel_code'];
- // 构建请求参数
- $payload = $this->buildRequestPayload('preCancelOrder', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/preCancelOrder';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] preCancelOrder Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- return [
- 'code' => 0,
- 'data' => [
- 'actual_cancel_cost_cent' => (int)($respData['actual_cancel_cost_cent'] ?? 0),
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 追加小费
- *
- * 根据蜂鸟官方文档为订单追加小费。
- * 订单小费总金额为每次累加。
- *
- * @param array $data 加小费参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- *
- * // ========== 必填参数 ==========
- * 'third_index_id' => '本次加小费唯一标识(Long类型)',
- * 'add_tip_amount_cent' => '加小费金额(分)',
- * ]
- * @return array API响应结果
- * 成功:['code' => 0, 'data' => ['result' => true]]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- *
- * @note
- * - 追加小费,订单小费总金额为每次累加
- * - 不是所有订单都支持加小费,受合同服务 & 接单运力 & 当时订单状态等多个因素影响
- * - 订单详情接口有返回是否可加小费字段(if_can_add_tip)
- * - 联调环境使用加小费接口需先使用联调工具把订单状态改为1,否则订单状态会不支持加小费
- * - third_index_id用于幂等,相同third_index_id的后续请求会被忽略
- */
- public function addTip($data = [])
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $orderId = $data['orderId'] ?? null;
- $partnerOrderCode = $data['partner_order_code'] ?? null;
- $tips = $data['tips'] ?? null;
- if($tips != null){
- $data['add_tip_amount_cent'] = $tips;
- }
-
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] addTip failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 验证 third_index_id(幂等标识)
- if (!isset($data['third_index_id'])) {
- Yii::warning("[FengniaoAdapter] addTip failed: third_index_id is required");
- $data['third_index_id'] = time();
- // return [
- // 'code' => -1,
- // 'msg' => 'third_index_id is required for idempotency',
- // 'data' => null
- // ];
- }
- // 验证 add_tip_amount_cent(加小费金额)
- if (!isset($data['add_tip_amount_cent']) || $data['add_tip_amount_cent'] <= 0) {
- Yii::warning("[FengniaoAdapter] addTip failed: add_tip_amount_cent must be greater than 0");
- return [
- 'code' => -1,
- 'msg' => 'add_tip_amount_cent is required and must be greater than 0',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = (int)$orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 必填参数
- $businessData['third_index_id'] = (int)$data['third_index_id'];
- $businessData['add_tip_amount_cent'] = (int)$data['add_tip_amount_cent'];
- // 构建请求参数
- $payload = $this->buildRequestPayload('addTip', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/addTip';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] addTip Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- return [
- 'code' => 0,
- 'data' => [
- 'result' => (bool)($respData['result'] ?? false),
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 查询订单详情
- *
- * 根据蜂鸟官方文档获取订单的详细信息。
- * 包括订单状态、配送员信息、事件节点、价格明细等完整信息。
- *
- * @param array $data 查询参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- * ]
- * @return array API响应结果
- */
- //public function getOrderDetail($data = [])
- public function selectOrder($data)
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $orderId = $data['order_id'] ?? $data;
- $partnerOrderCode = null;
- // 如果 $orderId 是 XSD 开头,则给 partnerOrderCode 赋值为 $orderId
- if (strpos($orderId, 'XSD') === 0) {
- $partnerOrderCode = $orderId;
- $orderId = null;
- }
-
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] getOrderDetail failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = $orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('getOrderDetail', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/getOrderDetail';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] getOrderDetail Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- // 解析价格明细
- $priceDetail = [];
- if (!empty($respData['price_detail']) && is_array($respData['price_detail'])) {
- $pd = $respData['price_detail'];
- $priceDetail = [
- 'start_price_cent' => (int)($pd['start_price_cent'] ?? 0),
- 'distance_price_cent' => (int)($pd['distance_price_cent'] ?? 0),
- 'weight_price_cent' => (int)($pd['weight_price_cent'] ?? 0),
- 'pressure_surcharge_cent' => (int)($pd['pressure_surcharge_cent'] ?? 0),
- 'time_period_surcharge_cent' => (int)($pd['time_period_surcharge_cent'] ?? 0),
- 'river_crossing_surcharge_cent' => (int)($pd['river_crossing_surcharge_cent'] ?? 0),
- 'category_surcharge_cent' => (int)($pd['category_surcharge_cent'] ?? 0),
- 'temporary_surcharge_cent' => (int)($pd['temporary_surcharge_cent'] ?? 0),
- 'order_price_surcharge_cent' => (int)($pd['order_price_surcharge_cent'] ?? 0),
- ];
- }
- // 解析事件日志列表
- $eventLogDetails = [];
- if (!empty($respData['event_log_details']) && is_array($respData['event_log_details'])) {
- foreach ($respData['event_log_details'] as $log) {
- $eventLogDetails[] = [
- 'order_status' => (int)($log['order_status'] ?? 0),
- 'occur_time' => (int)($log['occur_time'] ?? 0),
- 'carrier_driver_name' => $log['carrier_driver_name'] ?? '',
- 'carrier_driver_phone' => $log['carrier_driver_phone'] ?? '',
- ];
- }
- }
- // 解析逆向单运单事件节点
- $reverseEventLogDetails = [];
- if (!empty($respData['reverse_event_log_details']) && is_array($respData['reverse_event_log_details'])) {
- $reverse = $respData['reverse_event_log_details'];
- $reverseEventLogDetails = [
- 'order_status' => (int)($reverse['order_status'] ?? 0),
- 'occur_time' => (int)($reverse['occur_time'] ?? 0),
- 'carrier_driver_name' => $reverse['carrier_driver_name'] ?? '',
- 'carrier_driver_phone' => $reverse['carrier_driver_phone'] ?? '',
- ];
- }
- // 解析天气信息
- $weatherInfo = [];
- if (!empty($respData['weather_info']) && is_array($respData['weather_info'])) {
- $weather = $respData['weather_info'];
- $weatherInfo = [
- 'weatherRate' => (int)($weather['weatherRate'] ?? 0),
- 'weatherDesc' => $weather['weatherDesc'] ?? '',
- ];
- }
- // 解析取货照片列表
- $deliveryFetchPhotos = [];
- if (!empty($respData['delivery_fetch_photos']) && is_array($respData['delivery_fetch_photos'])) {
- $deliveryFetchPhotos = $respData['delivery_fetch_photos'];
- }
- return [
- 'code' => 0,
- 'data' => [
- // 基本信息
- 'order_id' => (int)($respData['order_id'] ?? 0),
- 'partner_order_code' => $respData['partner_order_code'] ?? '',
- 'tracking_id' => (int)($respData['tracking_id'] ?? 0),
- 'order_status' => (int)($respData['order_status'] ?? 0),
- 'serial_number' => $respData['serial_number'] ?? '',
- 'remark' => $respData['remark'] ?? '',
-
- // 配送员信息
- 'carrier_driver_id' => $respData['carrier_driver_id'] ?? '',
- 'carrier_driver_name' => $respData['carrier_driver_name'] ?? '',
- 'carrier_driver_phone' => $respData['carrier_driver_phone'] ?? '',
- 'temperature' => $respData['temperature'] ?? '',
-
- // 金额信息(单位:分)
- 'order_total_amount_cent' => (int)($respData['order_total_amount_cent'] ?? 0),
- 'order_actual_amount_cent' => (int)($respData['order_actual_amount_cent'] ?? 0),
- 'order_tip_amount_cent' => (int)($respData['order_tip_amount_cent'] ?? 0),
- 'overtime_compensation_cost_cent' => (int)($respData['overtime_compensation_cost_cent'] ?? 0),
-
- // 配送信息
- 'order_distance' => (double)($respData['order_distance'] ?? 0),
- 'estimate_arrive_time' => (int)($respData['estimate_arrive_time'] ?? 0),
- 'if_can_add_tip' => (int)($respData['if_can_add_tip'] ?? 0),
- 'fetch_code' => $respData['fetch_code'] ?? '',
- 'write_off_code' => $respData['write_off_code'] ?? '',
- 'cancel_code' => $respData['cancel_code'] ?? '',
-
- // 价格明细
- 'price_detail' => $priceDetail,
-
- // 事件日志
- 'event_log_details' => $eventLogDetails,
- 'reverse_event_log_details' => $reverseEventLogDetails,
-
- // 异常信息
- 'abnormal_code' => $respData['abnormal_code'] ?? '',
- 'abnormal_desc' => $respData['abnormal_desc'] ?? '',
-
- // 索赔信息
- 'claim_id' => (int)($respData['claim_id'] ?? 0),
- 'claim_status' => (int)($respData['claim_status'] ?? 0),
- 'claim_status_desc' => $respData['claim_status_desc'] ?? '',
- 'claim_reason_desc' => $respData['claim_reason_desc'] ?? '',
-
- // 投诉信息
- 'complaint_id' => (int)($respData['complaint_id'] ?? 0),
- 'complaint_status' => (int)($respData['complaint_status'] ?? 0),
- 'complaint_response_desc' => $respData['complaint_response_desc'] ?? '',
- 'complaint_reason_desc' => $respData['complaint_reason_desc'] ?? '',
-
- // 其他
- 'delivery_fetch_photos' => $deliveryFetchPhotos,
- 'weather_info' => $weatherInfo,
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- /**
- * 查询骑手信息
- *
- * 根据蜂鸟官方文档获取订单对应的骑手(配送员)的实时位置和联系信息。
- * 返回骑手的ID、姓名、电话以及实时位置坐标(高德地图坐标系)。
- *
- * @param array $data 查询参数,结构参考:
- * [
- * // ========== 必填参数(二选一)==========
- * 'order_id' => '订单ID',
- * 'partner_order_code' => '外部订单号',
- * // 注意:order_id 和 partner_order_code 必填一个
- * ]
- * @return array API响应结果
- * 成功:[
- * 'code' => 0,
- * 'data' => [
- * 'carrier_driver_id' => '骑手ID',
- * 'carrier_driver_name' => '骑手姓名',
- * 'carrier_driver_phone' => '骑手电话',
- * 'carrier_driver_latitude' => '骑手位置纬度',
- * 'carrier_driver_longitude' => '骑手位置经度',
- * // 返回的是高德地图的经纬度
- * ]
- * ]
- * 失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
- */
- public function getKnightInfo($data = [])
- {
- // 验证 order_id 和 partner_order_code 必填一个
- $orderId = $data['order_id'] ?? null;
- $partnerOrderCode = $data['partner_order_code'] ?? null;
-
- if (empty($orderId) && empty($partnerOrderCode)) {
- Yii::warning("[FengniaoAdapter] getKnightInfo failed: order_id and partner_order_code cannot both be empty");
- return [
- 'code' => -1,
- 'msg' => 'order_id and partner_order_code must have one',
- 'data' => null
- ];
- }
- // 构建业务数据
- $businessData = [];
- // 订单标识(二选一)
- if (!empty($orderId)) {
- $businessData['order_id'] = (int)$orderId;
- }
- if (!empty($partnerOrderCode)) {
- $businessData['partner_order_code'] = $partnerOrderCode;
- }
- // 构建请求参数
- $payload = $this->buildRequestPayload('getKnightInfo', $businessData);
- // 发送请求
- $url = $this->baseUrl . '/getKnightInfo';
- $resp = HttpClient::post($url, $payload, [
- 'Content-Type' => 'application/json',
- ]);
- Yii::info("[FengniaoAdapter] getKnightInfo Response: " . json_encode($resp));
- // 处理响应
- if (isset($resp['code']) && $resp['code'] == '200' && isset($resp['business_data'])) {
- // business_data 是 JSON 字符串,需要解析
- $respData = is_string($resp['business_data']) ? json_decode($resp['business_data'], true) : $resp['business_data'];
-
- if (is_null($respData)) {
- Yii::error("[FengniaoAdapter] Failed to parse business_data: " . $resp['business_data']);
- return [
- 'code' => -1,
- 'msg' => 'Failed to parse response data',
- 'data' => null
- ];
- }
- return [
- 'code' => 0,
- 'data' => [
- 'carrier_driver_id' => $respData['carrier_driver_id'] ?? '',
- 'carrier_driver_name' => $respData['carrier_driver_name'] ?? '',
- 'carrier_driver_phone' => $respData['carrier_driver_phone'] ?? '',
- 'carrier_driver_latitude' => $respData['carrier_driver_latitude'] ?? '',
- 'carrier_driver_longitude' => $respData['carrier_driver_longitude'] ?? '',
- ]
- ];
- }
- return [
- 'code' => (int)($resp['code'] ?? -1),
- 'msg' => $resp['msg'] ?? 'Unknown error',
- 'data' => null
- ];
- }
- }
|