get($mall_id); if (empty($config['is_enable'])) { continue; } if (empty($config['app_version'])) { $config['app_version'] = WangDianTongEnum::DEFAULT_APP_VERSION; } // 执行 // 不需要分页,因为 每次取出来就直接执行ack了 $this->{$config['app_version']}($mall_id, $limit, $config); } } protected function flagship(int $mall_id, int $limit, array $config) { $page_no = 0; while (true) { $model = new SalesLogisticsSyncGetSyncListExtModel(); $mParams = new SalesLogisticsSyncGetSyncListExtItemModel(); $mParams->shop_no = $config['shop_no']; // $mParams->is_part_sync = 1; $mParams->is_own_platform = true; $m_params = $mParams->toArray(); $model->params = $m_params; $model->page_size = $limit; $model->page_no = $page_no; $model->calc_total = 0; $req = new SalesLogisticsSyncGetSyncListExtRequest($config); $resp = $req->send($model); try { if (!empty($resp->getError())) { throw new \Exception($resp->getError()); } $res = $resp->toArray(); if (!isset($res['status']) || $res['status'] != StatusEnum::DISABLED) { throw new \Exception($res['message'] ?? var_export($res, true)); } if (empty($res['data'])) { break; } $ack_list = []; $sync_list = []; // 执行处理 foreach ($res['data'] as $k => $v) { /** * @var $logistics AddonsWangDianTongLogisticsCompany */ $logistics = AddonsWangDianTongLogisticsCompany::getOne([ ['express_code' => $v['logistics_code']] ]); if (empty($logistics)) { // 那么就是其他 $logistics = AddonsWangDianTongLogisticsCompany::getOne([ ['erp_type' => 2000] ]); } // 云库存订单 if (strpos($v['tid'], "cs_") === 0) { $flag = WangDianTongLogic::syncLogistics([ 'mall_id' => $mall_id, 'express_code' => $logistics->express_code, 'express_no' => $v['logistics_no'], 'id' => substr($v['tid'], 3), // 取出订单ID ], $logistics->toArray()); } else { // 平台订单 // 因为旺店通物流订单有拆单的功能。拆单的话需要独立处理 $orderDetails = []; if (!empty($v['is_part_sync']) && !empty($v['oids'])) { $orderExplodes = explode(',', $v['oids']); // 将oids字段拆成数组 $orderDetails = array_map(function ($item) { // 过滤掉前面的主订单,获取子订单ID $explode = explode('_', $item); return end($explode); }, $orderExplodes); } $order = Order::getOne([ ['order_no' => $v['tid']] ], true, ['detail' => function($query) use ($orderDetails) { $query->andWhere(['is_virtual' => StatusEnum::DISABLED])->andWhere(['status' => StatusEnum::ENABLED]); if (!empty($orderDetails)) { // 如果是拆单那面就获取相应的子订单 $query->andWhere(['id' => $orderDetails]); } }]); if (empty($order)) throw new \Exception("订单不存在: {$v['tid']}"); if (!empty($order['detail'])) { // 发货 $flag = $this->ship([ 'mall_id' => $mall_id, 'express_id' => $logistics->express_id > 0 ? $logistics->express_id : 10000, // 如果没有就是其他快递公司 'logistics_name'=> $logistics->express_id > 0 ? $logistics->express_name : $logistics->erp_name, 'logistics_code'=> $v['logistics_no'], 'order_id' => $order['id'], // 取出订单ID 'memo' => $logistics->express_id == 0 || $logistics->express_id == 10000 ? "快递:{$logistics->erp_name}" : '', 'order_detail_ids' => array_column($order['detail'], 'id'), ]); $flag = is_string($flag) ? $flag : true; } else { $flag = true; } } $ackModel = new SyncItemModel(); $ackModel->sync_id = $v['sync_id']; $ackModel->status = $flag === true || $flag == '订单已经发货' ? StatusEnum::DISABLED : StatusEnum::ENABLED; // 0 success, 1 fail $ackModel->error_msg = $flag === true ? '' : (is_string($flag) ? $flag : ''); // 相关描述信息,可在erp的物流同步界面看到 $ack_list[] = $ackModel->toArray(); $v['flag'] = $flag; $sync_list[$v['tid']] = [ 'mall_id' => $mall_id, 'rec_id' => $v['sync_id'], 'ack_status'=> WangDianTongEnum::LOGISTICS_ACK_STATUS_PROCESS, 'order_id' => $order['id'] ?? $v['tid'], 'created_at' => time(), 'updated_at' => time(), 'logistics_code' => $v['logistics_no'], 'logistics_name' => $logistics->express_id > 0 ? $logistics->express_name : $logistics->erp_name, 'logistics_code_erp' => $v['logistics_no'], 'logistics_name_erp' => $v['logistics_name'], 'sync_res' => Json::encode($v) ]; } // 存储同步信息 $this->saveSync($sync_list); // 过滤发货不成功的 $ack_list = array_filter($ack_list, function ($val) { return $val['status'] == StatusEnum::DISABLED; }); // 发完后需要通知那边进行确认 // ack需要在这一步,因为同步接口没有分页参数,如果当前逻辑中不做ack那么永远都是第一页? // 重试? if (!empty($ack_list)) { // 确认过滤后的数据 $flag = $this->retry(function () use ($config, $ack_list) { return $this->flagshipAck($config, array_values($ack_list)); }, 1); \Yii::error(__METHOD__ . " flagshipAck $flag"); // if ($flag !== true) throw new \Exception($flag); // ack失败,那么就需要重试 - 如果重试还是失败那么需要怎么处理? $this->updateSync($ack_list, $flag !== true ? WangDianTongEnum::LOGISTICS_ACK_STATUS_FAIL : WangDianTongEnum::LOGISTICS_ACK_STATUS_SUCCESS, is_string($flag) ? $flag : '', 'sync_id'); } $p_total = count($res['data']); if ($p_total < $limit) break; } catch (\Exception $e) { sleep(1); \Yii::error(FormatHelper::exception($e, __METHOD__)); break; } $page_no++; } } protected function enterprise(int $mall_id, int $limit, array $config) { while (true) { $model = new LogisticsSyncQuery(); $model->limit = $limit; $model->is_part_sync_able = 1; $model->shop_no = $config['shop_no']; $req = new \addons\WangDianTong\common\expand\sdk\wangdian\src\LogisticsSyncQuery($config); $resp = $req->send($model); try { if ($resp->getError()) { // 执行处理 throw new \Exception($resp->getError()); } $res = $resp->toArray(); if (!isset($res['code']) || $res['code'] != StatusEnum::DISABLED) { throw new \Exception($res['message'] ?? var_export($res, true)); } // 其他逻辑 - 手动发货商品 if (!empty($res['trades'])) { $ack_list = []; $sync_list = []; foreach ($res['trades'] as $trade) { /** * @var $logistics AddonsWangDianTongLogisticsCompany */ $logistics = AddonsWangDianTongLogisticsCompany::getOne([ ['erp_type' => $trade['logistics_type']] ]); if (empty($logistics)) { // 那么就是其他 $logistics = AddonsWangDianTongLogisticsCompany::getOne([ ['erp_type' => 2000] ]); } // 云库存订单 if (strpos($trade['tid'], "cs_") === 0) { $flag = WangDianTongLogic::syncLogistics([ 'mall_id' => $mall_id, 'express_code' => $logistics->express_code, 'express_no' => $trade['logistics_no'], 'id' => substr($trade['tid'], 3), // 取出订单ID ], $logistics->toArray()); } else { // 平台订单 $order = Order::getOne([ ['order_no' => $trade['tid']] ], true, ['detail' => function($query) { $query->andWhere(['is_virtual' => StatusEnum::DISABLED])->andWhere(['status' => StatusEnum::ENABLED]); }]); if (empty($order)) throw new \Exception("订单不存在: {$trade['tid']}"); if (!empty($order['detail'])) { $flag = $this->ship([ 'mall_id' => $mall_id, 'express_id' => $logistics->express_id > 0 ? $logistics->express_id : 10000, // 如果没有就是其他快递公司 'logistics_name'=> $logistics->express_id > 0 ? $logistics->express_name : $logistics->erp_name, 'logistics_code'=> $trade['logistics_no'], 'order_id' => $order['id'], // 取出订单ID 'memo' => '', 'order_detail_ids' => array_column($order['detail'], 'id'), ]); if (is_string($flag)) { \Yii::error("同步物流失败: {$order['id']} -- $flag " . __METHOD__); } $flag = is_string($flag) ? $flag : true; } else { $flag = true; } } $ackModel = new LogisticsItem(); $ackModel->rec_id = $trade['rec_id']; $ackModel->status = $flag === true || $flag == '订单已经发货' ? StatusEnum::DISABLED : StatusEnum::ENABLED; // 0 success, 1 fail $ackModel->message = $flag === true ? '' : (is_string($flag) ? $flag : ''); // 相关描述信息,可在erp的物流同步界面看到 $ack_list[] = $ackModel->toArray(); $sync_list[$order['id'] ?? $trade['tid']] = [ 'mall_id' => $mall_id, 'rec_id' => $trade['rec_id'], 'ack_status'=> WangDianTongEnum::LOGISTICS_ACK_STATUS_PROCESS, 'order_id' => $order['id'] ?? $trade['tid'], 'created_at' => time(), 'updated_at' => time(), 'logistics_code' => $trade['logistics_no'], 'logistics_name' => $logistics->express_id > 0 ? $logistics->express_name : $logistics->erp_name, 'logistics_code_erp' => $trade['logistics_no'], 'logistics_name_erp' => $trade['logistics_name'], 'sync_res' => Json::encode($trade) ]; } // 存储同步信息 $this->saveSync($sync_list); // 过滤发货不成功的 $ack_list = array_filter($ack_list, function ($val) { return $val['status'] == StatusEnum::DISABLED; }); // 发完后需要通知那边进行确认 // ack需要在这一步,因为同步接口没有分页参数,如果当前逻辑中不做ack那么永远都是第一页? // 重试? if (!empty($ack_list)) { // 确认过滤后的数据 $flag = $this->retry(function () use ($config, $ack_list) { return $this->ack($config, array_values($ack_list)); }); // if ($flag !== true) throw new \Exception($flag); // ack失败,那么就需要重试 - 如果重试还是失败那么需要怎么处理? $this->updateSync($ack_list, $flag !== true ? WangDianTongEnum::LOGISTICS_ACK_STATUS_FAIL : WangDianTongEnum::LOGISTICS_ACK_STATUS_SUCCESS, is_string($flag) ? $flag : ''); } } // 如果总数小于或者等于100条那么就跳出当前循环 if ($res['total_count'] <= $limit) break; } catch (\Exception $e) { sleep(1); var_dump($e->getMessage(), $e->getLine(), $e->getFile()); \Yii::error(FormatHelper::exception($e, __METHOD__)); break; } } } /** * @param array $data * @return OrderExpress|string */ protected function ship(array $data) { try { $flag = OrderExpress::delivery([ 'mall_id' => $data['mall_id'], 'shipping_type' => 1, // 0无需物流,1快递配送 'express_id' => $data['express_id'], // 快递公司ID 'id' => $data['order_id'], // 订单ID 'express_name' => $data['logistics_name'], // 快递公司名称 'express_no' => $data['logistics_code'], // 快递单号 'memo' => $data['memo'] ?? '', // 商家备注 'order_detail_ids' => $data['order_detail_ids'], // 子订单ID 'shipping_change' => null, // 是否为修改 'customer_name' => '', // 京东物流编号 'operator_name' => 'Erp发货', 'operator_id' => 0 ]); if ($flag) return $flag; return OrderExpress::getStaticError(); } catch (\Exception $e) { return $e->getMessage(); } } /** * @param array $config * @param array $ack_list * @return string|true */ protected function flagshipAck(array $config, array $ack_list) { try { $req = new SalesLogisticsSyncUpdateRequest($config); $model = new SalesLogisticsSyncUpdateModel(); $model->syncList = $ack_list; $resp = $req->send($model); if ($resp->getError()) throw new \Exception($resp->getError()); $res = $req->toArray(); if (!isset($res['status']) || $res['status'] != StatusEnum::DISABLED) throw new \Exception($res['message'] ?? var_export($res, true)); return true; } catch (\Exception $e) { return $e->getMessage(); } } /** * @param array $config * @param array $ack_list * @return string|true */ protected function ack(array $config, array $ack_list) { try { $model = new LogisticsSyncAck(); $model->logistics_list = Json::encode($ack_list); $req = new \addons\WangDianTong\common\expand\sdk\wangdian\src\LogisticsSyncAck($config); $resp = $req->send($model); if ($resp->getError()) throw new \Exception($resp->getError()); $res = $req->toArray(); if (!isset($res['code']) || $res['code'] != StatusEnum::DISABLED) throw new \Exception($res['message'] ?? var_export($res, true)); return true; } catch (\Exception $e) { return $e->getMessage(); } } /** * 保存同步信息 * @param array $sync_list * @return void * @throws Exception */ protected function saveSync(array $sync_list) { $order_ids = array_keys($sync_list); $local_order_ids = AddonsWangDianTongLogisticsSync::find()->where(['order_id' => $order_ids])->select('order_id')->column(); if (!empty($local_order_ids)) { foreach ($sync_list as $key => $item) { if (in_array($key, $local_order_ids)) unset($sync_list[$key]); } } if (!empty($sync_list)) { $sync_list = array_values($sync_list); AddonsWangDianTongLogisticsSync::find() ->createCommand() ->batchInsert(AddonsWangDianTongLogisticsSync::tableName(), array_keys($sync_list[0]), $sync_list) ->execute(); } } /** * 同步更新 * @param array $ack_list * @param int $ack_status * @param string $remark * @return void */ protected function updateSync(array $ack_list, int $ack_status, string $remark = '', string $column_key = 'rec_id') { AddonsWangDianTongLogisticsSync::updateAll(['ack_status' => $ack_status, 'remark' => $remark], ['and', ['rec_id' => array_column($ack_list, $column_key)]]); } }