OrderExpressLog.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <?php
  2. namespace common\models\order;
  3. use addons\CloudStock\common\models\CloudStockAgentOrder;
  4. use common\enums\AddonsEnum;
  5. use addons\Logistics\common\service\ApiService;
  6. use addons\Logistics\common\service\ExpressService;
  7. use common\enums\ExpressEnum;
  8. use common\enums\RabbitMqEnum;
  9. use common\enums\StatusEnum;
  10. use common\events\order\UpdateExpressLogEvent;
  11. use common\helpers\FormatHelper;
  12. use common\models\common\Express;
  13. use common\models\common\MallSetting;
  14. use common\models\logistics\LogisticsSearch;
  15. use common\models\mall\MallAddons;
  16. use common\traits\ErrorTrait;
  17. use Yii;
  18. use yii\helpers\Json;
  19. /**
  20. * This is the model class for table "{{%order_express_log}}".
  21. *
  22. *
  23. * @property int $id
  24. * @property int|null $mall_id
  25. * @property int|null $order_express_id qimall_order_express对应id
  26. * @property string|null $express_no 运单编号
  27. * @property int|null $express_id 快递公司id
  28. * @property string|null $express_name 物流公司名称
  29. * @property string|null $customer_name 京东物流编号
  30. * @property string|null $express_code 物流公司编码
  31. * @property int|null $order_id 订单id
  32. * @property int $table_express_status 物流状态0/未完成 1/已完成
  33. * @property int $express_status 当前物流状态(物流公司状态)
  34. * @property int|null $status 状态[-1:删除;0:禁用;1启用]
  35. * @property string|null $list 物流具体轨迹信息
  36. * @property int|null $created_at 创建时间
  37. * @property int|null $updated_at 更新时间
  38. * @property int $cs_order_id 更新时间
  39. */
  40. class OrderExpressLog extends \common\models\base\BaseActiveRecord
  41. {
  42. //use ErrorTrait;
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public static function tableName()
  47. {
  48. return '{{%order_express_log}}';
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function rules()
  54. {
  55. return [
  56. [['mall_id', 'order_express_id', 'express_id', 'order_id', 'table_express_status', 'express_status', 'status', 'created_at', 'updated_at', 'cs_order_id'], 'integer'],
  57. [['list'], 'string'],
  58. [['express_no', 'express_code'], 'string', 'max' => 50],
  59. [['express_name', 'customer_name'], 'string', 'max' => 100],
  60. ];
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function attributeLabels()
  66. {
  67. return [
  68. 'id' => 'ID',
  69. 'mall_id' => 'Mall ID',
  70. 'order_express_id' => 'qimall_order_express对应id',
  71. 'express_no' => '运单编号',
  72. 'express_id' => '快递公司id',
  73. 'express_name' => '物流公司名称',
  74. 'customer_name' => '京东物流编号',
  75. 'express_code' => '物流公司编码',
  76. 'order_id' => '订单id',
  77. 'table_express_status' => '物流状态0/未完成 1/已完成',
  78. 'express_status' => '当前物流状态(物流公司状态)',
  79. 'status' => '状态[-1:删除;0:禁用;1启用]',
  80. 'list' => '物流具体轨迹信息',
  81. 'created_at' => '创建时间',
  82. 'updated_at' => '更新时间',
  83. ];
  84. }
  85. protected static function order(array $params)
  86. {
  87. try {
  88. $params['where'] = [['order_id' => $params['order_id'],'status' => StatusEnum::ENABLED]];
  89. $express_list = OrderExpress::lists($params);
  90. $pic_urls = OrderDetail::lists(['select' => ['pic_url', 'id'], 'where' => [['order_id' => $params['order_id']]]]);
  91. $list = [];
  92. $pic = [];
  93. $order_detail_ids = [];
  94. if ($pic_urls) {
  95. foreach ($pic_urls as $key) {
  96. $pic[$key['id']] = $key['pic_url'];
  97. }
  98. }
  99. if ($express_list) {
  100. foreach ($express_list as $k) {
  101. $order_detail_ids[$k['id']] = json_decode($k['order_detail_ids'], true);
  102. }
  103. $express_ids = array_column($express_list, 'id');
  104. $list = OrderExpressLog::lists(['where' => [['in', 'order_express_id', $express_ids]]]);
  105. if (!is_null($list)) {
  106. $time = time();
  107. $hour = Yii::$app->services->setting->mall->get($params['mall_id'], 'logistics.update');// 获取更新物流时间设置
  108. if (empty($hour) && is_array($hour)) $hour = 8;// 默认8小时更新一次
  109. foreach ($list as &$item) {
  110. $item['pic_url'] = '';
  111. $now_express_id = $order_detail_ids[$item['order_express_id']] ?? [];
  112. foreach ($now_express_id as $keys) {
  113. $item['pic_url'] = isset($pic[$keys]) ? $pic[$keys] : '';
  114. }
  115. $item['status'] = ExpressEnum::getStatusMap($item['express_status']);
  116. // unset($item['order_express_id']);
  117. // unset($item['order_id']);
  118. // 检查物流是否已完成,未完成且距离上次修改时间超过一个小时
  119. if ($item['table_express_status'] == StatusEnum::DISABLED && $item['updated_at'] + $hour * 3600 < $time) {
  120. $params['express_status'] = $item['express_status'];
  121. $item['list'] = OrderExpressLog::execExpressLog($params['mall_id'], $item);
  122. }
  123. unset($item['express_status']);
  124. $item['list'] = json_decode($item['list'], true);
  125. // 因为七件事订单物流和快递鸟数据不一致,所以需要处理
  126. $request_type = Yii::$app->services->setting->mall->get($params['mall_id'], 'logistics.request_type');
  127. if ($request_type == 1) { // 如果是快递鸟需要倒序
  128. if ($item['list']['Traces']) {
  129. $item['list']['Traces'] = array_reverse($item['list']['Traces']);
  130. }
  131. }
  132. }
  133. }
  134. }
  135. } catch (\Exception $e) {
  136. $list = [];
  137. }
  138. return $list;
  139. }
  140. protected static function cloudStock($params)
  141. {
  142. try {
  143. $list = OrderExpressLog::lists(['where' => [['cs_order_id' => $params['order_id']]]]);
  144. if (!is_null($list)) {
  145. $time = time();
  146. $hour = Yii::$app->services->setting->mall->get($params['mall_id'], 'logistics.update');// 获取更新物流时间设置
  147. if (empty($hour) && is_array($hour)) $hour = 8;// 默认8小时更新一次
  148. $cOrder = CloudStockAgentOrder::getOne([
  149. ['id' => $params['order_id']]
  150. ], true, ['goods']);
  151. foreach ($list as &$item) {
  152. $item['pic_url'] = $cOrder['goods']['cover_pic'] ?? '';
  153. $item['goods_name'] = $cOrder['goods']['goods_name'] ?? '';
  154. $item['num'] = $cOrder['num'];
  155. $item['status'] = ExpressEnum::getStatusMap($item['express_status']);
  156. unset($item['order_express_id']);
  157. unset($item['order_id']);
  158. // 检查物流是否已完成,未完成且距离上次修改时间超过一个小时
  159. if ($item['table_express_status'] == StatusEnum::DISABLED && $item['updated_at'] + $hour * 3600 < $time) {
  160. $params['express_status'] = $item['express_status'];
  161. $item['list'] = OrderExpressLog::execExpressLog($params['mall_id'], $item);
  162. }
  163. unset($item['express_status']);
  164. $item['list'] = json_decode($item['list'], true);
  165. if ($item['list']['Traces']) {
  166. $item['list']['Traces'] = array_reverse($item['list']['Traces']);
  167. }
  168. }
  169. }
  170. } catch (\Exception $e) {
  171. $list = [];
  172. }
  173. return $list;
  174. }
  175. /**
  176. * 获取物流轨迹
  177. * User: wniu
  178. * Date: 2021/10/12
  179. * Time: 5:36 下午
  180. * @param $params
  181. * @return array
  182. */
  183. public static function getExpressLog($params): array
  184. {
  185. if (!empty($params['type']) && $params['type'] == AddonsEnum::ADDONS_NAME_CLOUD_STOCK) {
  186. return self::cloudStock($params);
  187. }
  188. return self::order($params);
  189. }
  190. /**
  191. * 保存物流轨迹记录
  192. * User: wniu
  193. * Date: 2021/10/13
  194. * Time: 5:04 下午
  195. * *
  196. * @param $params
  197. * @param false $is_update
  198. * @return bool
  199. */
  200. public static function saveLog($params, bool $is_update = false): bool
  201. {
  202. unset($params['status']);
  203. if ($is_update) {
  204. $where[] = ['order_express_id' => $params['id']];
  205. $order_express_log = self::getOne($where);
  206. if (empty($order_express_log) && $params['shipping_type'] != StatusEnum::ENABLED) return true;
  207. //如果没有物流轨迹信息,需新增
  208. if (empty($order_express_log)){
  209. $order_express_log = new self();
  210. $order_express_log->order_express_id = $params['id'];
  211. $order_express_log->order_id = OrderExpress::find()
  212. ->select('order_id')
  213. ->where(['id' => $params['id']])
  214. ->scalar();
  215. }
  216. if ($order_express_log->express_id != $params['express_id']) {
  217. $params['express_code'] = self::getExpressCode($params['express_id']);
  218. }
  219. if (!$order_express_log->load($params, '') || !$order_express_log->save()) throw new \Exception($order_express_log::getStaticError());
  220. } else {
  221. if ($params['shipping_type'] == 1) {
  222. $params['express_code'] = self::getExpressCode($params['express_id']);
  223. $order_express_log = new self();
  224. if (!$order_express_log->load($params, '') || !$order_express_log->save()) throw new \Exception($order_express_log::getStaticError());
  225. }
  226. }
  227. return true;
  228. }
  229. /**
  230. * 获取物流code码
  231. * User: wniu
  232. * Date: 2021/10/13
  233. * Time: 5:04 下午
  234. * *
  235. * @param $express_id
  236. * @return mixed
  237. */
  238. public static function getExpressCode($express_id)
  239. {
  240. $where[] = ['id' => $express_id];
  241. $express_code = Express::getOne($where, false, [], false, 'code');
  242. return $express_code->code;
  243. }
  244. /**
  245. * 更新物流轨迹信息
  246. * User: wniu
  247. * Date: 2021/10/14
  248. * Time: 3:23 下午
  249. *
  250. * @throws InvalidArgumentException
  251. */
  252. public static function updateExpressLog()
  253. {
  254. try {
  255. $mall_list = \common\models\mall\Mall::getEnableMallList();
  256. foreach ($mall_list as $mall) {
  257. $mall_id = $mall['id'];
  258. $express_account['kdn_business_iD'] = MallSetting::conf($mall_id, 'logistics.kdniao_mch_id');
  259. $express_account['kdn_app_key'] = MallSetting::conf($mall_id, 'logistics.kdniao_api_key');
  260. $express_account['kdn_request_type'] = MallSetting::conf($mall_id, 'logistics.kdniao_request_type');
  261. $part = 1;
  262. $limit = 200;
  263. //测试 -start
  264. // $express_account['kdn_business_iD'] = '1757494';
  265. // $express_account['kdn_app_key'] = '49533209-91e9-4cb7-a55d-232169a6a91d';
  266. // $params = [];
  267. // $part_where = [['=', 'mall_id', $mall_id], ['=', 'table_express_status', ExpressEnum::TABLE_EXPRESS_WAIT]];
  268. // $part_data = self::batchGetExpressLog($params, $part, $limit);
  269. // self::updateLog($part_data, $express_account);
  270. //测试 -end
  271. $select = "id,mall_id,order_id,express_no,customer_name,express_code,express_id";
  272. $part_where = [['=', 'mall_id', $mall_id], ['=', 'status', StatusEnum::ENABLED], ['=', 'table_express_status', ExpressEnum::TABLE_EXPRESS_WAIT]];
  273. $params = ['where' => $part_where, 'select' => $select];
  274. while ($part_data = self::batchGetExpressLog($params, $part, $limit)) {
  275. if ($part_data) {
  276. self::updateLog($part_data, $express_account);
  277. }
  278. $part++;
  279. }
  280. }
  281. return true;
  282. } catch (\Exception $e) {
  283. $exception = FormatHelper::exception($e, "物流更新");
  284. Yii::error($exception);
  285. self::setStaticError($exception);
  286. return false;
  287. }
  288. }
  289. /**
  290. * 分片查询
  291. * @Author: lun
  292. * @DateTime: 2021/12/4 0004 10:21
  293. * @Copyright: copyright (c) 2021 广东七件事集团
  294. * @param $params
  295. * @param $user_id
  296. * @param $offset
  297. * @param $limit
  298. * @return bool|mixed
  299. */
  300. public static function batchGetExpressLog($params, $part, $limit)
  301. {
  302. $params['offset'] = ($part - 1) * $limit;
  303. $params['limit'] = $limit;
  304. return self::lists($params, false);
  305. }
  306. /**
  307. * @throws \Flex\Express\Exceptions\HttpException
  308. * @throws \Flex\Express\Exceptions\InvalidArgumentException
  309. */
  310. public static function updateLog($part_data, $express_account)
  311. {
  312. try {
  313. foreach ($part_data as $item) {
  314. //顺丰快递,需提供手机后4位
  315. if ($item->express_code == "SF") {
  316. $phone = Order::getOne([['id' => $item->order_id]], true, '', '', 'mobile');
  317. if (!$phone) {
  318. continue;
  319. }
  320. $item['customer_name'] = "'" . mb_substr($phone['mobile'], 0 - 4) . "'";
  321. }
  322. $new_data = LogisticsSearch::getExpressBird($item, $express_account);
  323. if ($new_data) {
  324. $new_data->updated_at = time();
  325. $new_data->save();
  326. }
  327. }
  328. return true;
  329. } catch (\Exception $e) {
  330. Yii::error('物流更新任务处理出错:' . $e->getMessage());
  331. self::setStaticError($e->getMessage());
  332. return false;
  333. }
  334. }
  335. /**
  336. * 物流信息获取 - 每条数据每隔N个小时才能获取一次
  337. * @Author: lun
  338. * @DateTime: 2022/7/25 0025 15:18
  339. * @Copyright: copyright (c) 2021 广东七件事集团
  340. * @param $params
  341. * @throws \Flex\Express\Exceptions\HttpException
  342. * @throws \Flex\Express\Exceptions\InvalidArgumentException
  343. */
  344. public static function execExpressLog($mall_id, $express)
  345. {
  346. try {
  347. // 获取物流配置
  348. $express_config = Yii::$app->services->setting->mall->get($mall_id,'logistics');
  349. if ($express_config['kdniao_request_type'] == 3) { // 阿里云
  350. if (MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_LOGISTICS)) {
  351. $lExpress = ExpressService::getCode($express['express_id']);
  352. if (!empty($lExpress)) {
  353. if (in_array($lExpress->code, ['SFEXPRESS', 'ZTO'])) {
  354. $phone = Order::getOne([['id' => $express['order_id']]], true, '', '', 'mobile');
  355. if ($phone) $phone = mb_substr($phone['mobile'], 0 - 4);
  356. }
  357. $new_express = LogisticsSearch::getExpressAliyun([
  358. 'mall_id' => $mall_id,
  359. 'type' => $lExpress->code,
  360. 'no' => $express['express_no'] . (!empty($phone) ? ":{$phone}" : ''),
  361. 'id' => $express['id']
  362. ]);
  363. if (!$new_express) {
  364. $new_express['list']['err'] = LogisticsSearch::getStaticError();
  365. $new_express['list']['Traces'] = [];
  366. }
  367. } else {
  368. $new_express['list']['err'] = '快递公司未匹配物流接入插件快递公司';
  369. $new_express['list']['Traces'] = [];
  370. }
  371. } else {
  372. $new_express['list']['err'] = '未安装物流接入插件';
  373. $new_express['list']['Traces'] = [];
  374. }
  375. if (is_array($new_express['list'])) $new_express['list'] = Json::encode($new_express['list']);
  376. } else {
  377. //顺丰快递、中通快递,需提供手机后4位
  378. $expressCode = ['SF', 'ZTO'];
  379. if (in_array($express['express_code'], $expressCode)) {
  380. $phone = Order::getOne([['id' => $express['order_id']]], true, '', '', 'mobile');
  381. if ($phone) $express['customer_name'] = mb_substr($phone['mobile'], 0 - 4);
  382. }
  383. // 获取物流信息
  384. $new_express = LogisticsSearch::getExpressBird($express, $express_config);
  385. }
  386. // 修改物流信息
  387. self::updateAll([
  388. 'list' => $new_express['list'],
  389. 'express_status' => $new_express['express_status'] ?? 0,
  390. 'table_express_status' => $new_express['table_express_status'] ?? 0,
  391. 'updated_at' => time()
  392. ], ['id' => $express['id']]);
  393. $event = new UpdateExpressLogEvent($mall_id);
  394. Yii::$app->services->events->dispatch($event,['id'=>$new_express['id']], $event->listeners);
  395. return $new_express['list'];
  396. } catch (\Exception $e) {
  397. Yii::error($e->getMessage());
  398. self::setStaticError($e->getMessage());
  399. return false;
  400. }
  401. }
  402. }