| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- <?php
- namespace addons\WangDianTong\common\service;
- use addons\CloudStock\common\logic\order\WangDianTongLogic;
- use addons\WangDianTong\common\enums\WangDianTongEnum;
- use addons\WangDianTong\common\expand\sdk\wangdian\model\LogisticsItem;
- use addons\WangDianTong\common\expand\sdk\wangdian\model\LogisticsSyncAck;
- use addons\WangDianTong\common\expand\sdk\wangdian\model\LogisticsSyncQuery;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\model\SalesLogisticsSyncGetSyncListExtItemModel;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\model\SalesLogisticsSyncGetSyncListExtModel;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\model\SalesLogisticsSyncUpdateModel;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\model\SyncItemModel;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\request\SalesLogisticsSyncGetSyncListExtRequest;
- use addons\WangDianTong\common\expand\sdk\wangdian\qjb\request\SalesLogisticsSyncUpdateRequest;
- use addons\WangDianTong\common\models\AddonsWangDianTongLogisticsCompany;
- use addons\WangDianTong\common\models\AddonsWangDianTongLogisticsSync;
- use addons\WangDianTong\common\traits\RequestRetryTrait;
- use common\enums\StatusEnum;
- use common\helpers\FormatHelper;
- use common\models\mall\Mall;
- use common\models\order\Order;
- use common\models\order\OrderExpress;
- use yii\db\Exception;
- use yii\helpers\Json;
- class LogisticsSyncQueryService
- {
- use RequestRetryTrait;
- public function sync()
- {
- $mall_ids = Mall::getAllMallIds();
- $limit = 100;
- foreach ($mall_ids as $mall_id) {
- $config = (new SettingService())->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)]]);
- }
- }
|