BaseApi.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. <?php
  2. namespace addons\StarChain\common\api;
  3. use Exception;
  4. use GuzzleHttp\Client;
  5. use GuzzleHttp\Pool;
  6. use yii\base\BaseObject;
  7. use GuzzleHttp\Psr7\Request;
  8. use Psr\Http\Message\ResponseInterface;
  9. use yii\helpers\Json;
  10. /**
  11. * API
  12. * @see https://sce-opc.380star.com/#/passport/api-service
  13. */
  14. class BaseApi extends BaseObject
  15. {
  16. /**
  17. * 自身实例
  18. *
  19. * @var null|BaseApi
  20. */
  21. private static $instance = null;
  22. /**
  23. * appKey
  24. */
  25. public $app_key;
  26. /**
  27. * appSecret
  28. *
  29. * @var string
  30. */
  31. public $app_secret;
  32. /**
  33. * 版本号
  34. *
  35. * @var string
  36. */
  37. protected $version = '1.0';
  38. /**
  39. * 限制
  40. *
  41. * @var integer
  42. */
  43. protected $concurrency = 25;
  44. /**
  45. * Url
  46. *
  47. * @var string
  48. */
  49. private $url = 'https://sce-opz.380star.com';
  50. /**
  51. * 开启数据展示
  52. *
  53. * @var boolean
  54. */
  55. private $debug = false;
  56. /**
  57. * true 抛出异常 fasle 不抛出异常 返回空数据
  58. *
  59. * @var boolean
  60. */
  61. private $exception = true;
  62. /**
  63. * 错误码
  64. *
  65. * @var array
  66. */
  67. protected $err_code = [
  68. 611001 => '查询消息池表tenantId不存在',
  69. 515024 => '请购买选品库',
  70. 300001 => '参数无效,内容:s',
  71. 300000 => 'spuId的数量不得超过500',
  72. 300002 => 'sku编码数量不得超过200',
  73. 507033 => '同一时间请勿重复提交',
  74. 507003 => '必填参数为空',
  75. 507087 => '商品匹配错误',
  76. 507080 => '该收货地址不在配送范围',
  77. 507086 => '暂无报价',
  78. 507089 => '获取运营商信息失败',
  79. 507090 => '预付款扣减失败',
  80. 507017 => '订单不存在',
  81. 507055 => '确认收货失败',
  82. 507043 => '该订单当前状态不能取消',
  83. ];
  84. /**
  85. * 初始化
  86. *
  87. * @param array $config
  88. */
  89. private function __construct(array $config)
  90. {
  91. if (empty($config['app_key']) || empty($config['app_secret'])) {
  92. throw new Exception('缺少appKey,或者appSecret');
  93. }
  94. parent::__construct($config);
  95. }
  96. /**
  97. * 禁止克隆
  98. *
  99. * @return void
  100. */
  101. public function __clone()
  102. {
  103. }
  104. /**
  105. * 获取自身实例化
  106. *
  107. * @param array $config
  108. * @return BaseApi
  109. */
  110. public static function getInstance($config = [])
  111. {
  112. if (self::$instance instanceof self) {
  113. return self::$instance;
  114. } else {
  115. return new self($config);
  116. }
  117. }
  118. /**
  119. * 获取分类信息 1.0版
  120. *
  121. * @param array $params
  122. * @param string $method
  123. * @return array
  124. */
  125. public function getCategorys($params = [], $method = 'post')
  126. {
  127. $data = $params;
  128. return $this->guzz('/scce/cmc/cmc/spu/open/getCategorys', $data, $method);
  129. }
  130. /**
  131. * 查询商品库SPUID列表 1.0版
  132. *
  133. * @param array $params
  134. * @param string $method
  135. * @return array
  136. */
  137. public function getSpuIdList($params = [], $method = 'post')
  138. {
  139. $data = array_merge([
  140. 'hasVideo' => '',
  141. 'createStartTime' => '',
  142. 'pageIndex' => '',
  143. 'createEndTime' => '',
  144. 'brandId' => '',
  145. 'pageSize' => '24',
  146. 'firstCategoryId' => '',
  147. 'secondCategoryId' => '',
  148. 'thirdCategoryId' => '',
  149. 'status' => ''
  150. ], $params);
  151. return $this->guzz('/scce/cmc/cmc/spu/open/getSpuIdList', $data, $method);
  152. }
  153. /**
  154. * 查询SPU商品详情 1.0版
  155. *
  156. * @param array $params
  157. * @param string $method
  158. * @return array
  159. */
  160. public function getSpuBySpuIds($params = [], $method = 'post')
  161. {
  162. $data['spuIdList'] = $params;
  163. return $this->guzz('/scce/cmc/cmc/spu/open/getSpuBySpuIds', $data, $method);
  164. }
  165. /**
  166. * 查询SKU规格信息 1.0版(单个查询)
  167. *
  168. * @param string $id
  169. * @param string $method
  170. * @return array
  171. */
  172. public function listSkuBySpuId($id, $method = 'post')
  173. {
  174. $data['spuId'] = $id;
  175. return $this->guzz('/scce/cmc/cmc/spu/open/listSkuBySpuId', $data, $method);
  176. }
  177. /**
  178. * 查询SKU规格信息 1.0版(复数查询)
  179. *
  180. * @param array $ids
  181. * @param string $method
  182. * @return array
  183. */
  184. public function listSkuBySpuIds($ids = [], $method = 'post')
  185. {
  186. $requests = function ($data) use ($method) {
  187. foreach ($data as $id) {
  188. yield $this->getRequest('/scce/cmc/cmc/spu/open/listSkuBySpuId', ['spuId' => $id], $method);
  189. }
  190. };
  191. // 创建关系数据用于存储返回数据
  192. $result = [];
  193. foreach ($ids as $v) {
  194. $result[] = [
  195. 'spuId' => $v
  196. ];
  197. }
  198. return $this->pool($requests, $ids, $result);
  199. }
  200. /**
  201. * 查询SPU商品详情 1.0版(pool)
  202. *
  203. * @param array $spu_ids
  204. * @param string $method
  205. * @return array
  206. */
  207. public function getSpuBySpuIdsPool($spu_ids = [], $method = 'post')
  208. {
  209. $requests = function ($spu_ids) use ($method) {
  210. foreach ($spu_ids as $id) {
  211. $data['spuIdList'] = [$id];
  212. yield $this->getRequest('/scce/cmc/cmc/spu/open/getSpuBySpuIds', $data, $method);
  213. }
  214. };
  215. return $this->pool($requests, $spu_ids);
  216. }
  217. /**
  218. * 获取省市区地址 1.0版
  219. *
  220. * @param string $region_code
  221. * @param string $method
  222. * @return array
  223. */
  224. public function getRegionByCodeOpen($region_code = '', $method = 'post')
  225. {
  226. $data['regionCode'] = $region_code;
  227. return $this->guzz('/scce/pbc/pbc/region/getRegionByCodeOpen', $data, $method);
  228. }
  229. /**
  230. * 查询商品运费 1.0版
  231. *
  232. * @param array $freight
  233. * [
  234. * spuInfoList => [
  235. * quantity => 商品数量
  236. * spuId => 商品spuId
  237. * ],
  238. * orderSn => 订单号
  239. * cityId => 收货城市ID
  240. * ]
  241. * @param string $method
  242. * @return string
  243. */
  244. public function calculate($freight, $method = 'post')
  245. {
  246. return $this->guzz('/scce/cmc/cmc/freight/calculate', $freight, $method);
  247. }
  248. /**
  249. * 创建订单 1.0版
  250. *
  251. * @param array $data
  252. * @param string $method
  253. * @return array
  254. */
  255. public function submitOrder($data, $method = 'post')
  256. {
  257. return $this->guzz('/scce/ctc/ctc/reseller/order/submitOrder', $data, $method);
  258. }
  259. /**
  260. * 查询消息池表 1.0版
  261. *
  262. * @param integer $enum 消息枚举值
  263. * @param string $content 消息内容
  264. * @param string $method
  265. * @return array
  266. */
  267. public function messagePool($enum = null, $content = null, $method = 'post')
  268. {
  269. $data = [];
  270. $enum && $data['enumNum'] = $enum;
  271. $content && $data['content'] = $content;
  272. return $this->guzz('/scce/opm/opm/messagePool/list/page', $data, $method);
  273. }
  274. /**
  275. * 删除消息 1.0版
  276. *
  277. * @param array $ids 需要删除的消息IDS
  278. * @param integer $enum 消息枚举值
  279. * @param string $method
  280. * @return array
  281. */
  282. public function removeMessagePoolByParam(array $ids, $enum = null, $method = 'post')
  283. {
  284. $data['id'] = implode(',', $ids);
  285. $enum && $data['enumNum'] = $enum;
  286. return $this->guzz('/scce/opm/opm/messagePool/removeMessagePoolByParam', $data, $method);
  287. }
  288. /**
  289. * 查询订单详情 1.0版
  290. *
  291. * @param string $order_sn
  292. * @param string $method
  293. * @return array
  294. */
  295. public function getOrderDetail($order_sn, $method = 'post')
  296. {
  297. $data['orderSn'] = $order_sn;
  298. return $this->guzz('/scce/ctc/ctc/reseller/order/getOrderDetail', $data, $method);
  299. }
  300. /**
  301. * 查询物流信息 1.0版
  302. *
  303. * @param string $order_sn 订单编号
  304. * @param string $delivery_sn 物流单号
  305. * @param string $method
  306. * @return array
  307. */
  308. public function getOrderExpressListByOs($order_sn, $delivery_sn = null, $method = 'post')
  309. {
  310. $data['orderSn'] = $order_sn;
  311. $delivery_sn && $data['deliverySn'] = $delivery_sn;
  312. return $this->guzz('/scce/ctc/ctc/tOrderExpress/getOrderExpressListByOs', $data, $method);
  313. }
  314. /**
  315. * 售后申请 1.0版
  316. *
  317. * @param array $data
  318. * @param string $method
  319. * @return array
  320. */
  321. public function applyRefund($data, $method = 'post')
  322. {
  323. return $this->guzz('/scce/ctc/ctc/reseller/orderReturnGoods/applyRefund', $data, $method);
  324. }
  325. /**
  326. * 查询售后单详细信息 1.0版
  327. *
  328. * @param string $return_sn
  329. * @param string $method
  330. * @return array
  331. */
  332. public function orderReturnGoodsDetail($return_sn, $method = 'post')
  333. {
  334. $data['returnSn'] = $return_sn;
  335. return $this->guzz('/scce/ctc/ctc/reseller/orderReturnGoods/detail', $data, $method);
  336. }
  337. /**
  338. * 确认收货 1.0版
  339. *
  340. * @param string $order_sn 订单编号
  341. * @param string $operator 操作人
  342. * @param string $method
  343. * @return boolean
  344. */
  345. public function confirmReceive($order_sn, $operator, $method = 'post')
  346. {
  347. $data['orderSn'] = $order_sn;
  348. $data['operator'] = $operator;
  349. return $this->guzz('/scce/ctc/ctc/reseller/order/confirmReceive', $data, $method);
  350. }
  351. /**
  352. * 退货/换货邮寄卖家 1.0版
  353. *
  354. * @param string $returnSn
  355. * @param string $deliverySn
  356. * @param string $deliveryCorpName
  357. * @param string $method
  358. * @return array
  359. */
  360. public function sendReturnGoods($return_sn, $delivery_sn, $delivery_corp_name, $method = 'post')
  361. {
  362. $data['returnSn'] = $return_sn;
  363. $data['deliverySn'] = $delivery_sn;
  364. $data['deliveryCorpName'] = $delivery_corp_name;
  365. return $this->guzz('/scce/ctc/ctc/reseller/orderReturnGoods/sendReturnGoods', $data, $method);
  366. }
  367. /**
  368. * 换货确认收货 1.0版
  369. *
  370. * @param string $returnSn
  371. * @param string $method
  372. * @return array
  373. */
  374. public function receiptRefund($return_sn, $method = 'post')
  375. {
  376. $data['returnSn'] = $return_sn;
  377. return $this->guzz('/scce/ctc/ctc/reseller/orderReturnGoods/receiptRefund', $data, $method);
  378. }
  379. /**
  380. * 批量校验下单商品销售范围和库存 1.0版
  381. *
  382. * @param array $itemDTOList
  383. * @param string $areaRegionCode
  384. * @return array
  385. */
  386. public function checkSkuStock(array $itemDTOList, string $areaRegionCode, $method = 'post')
  387. {
  388. $data = compact('itemDTOList', 'areaRegionCode');
  389. return $this->guzz('/scce/cmc/cmc/spu/open/checkSkuStock', $data, $method);
  390. }
  391. /**
  392. * 请求
  393. *
  394. * @param string $api
  395. * @param array $data
  396. * @param string $method
  397. * @return array
  398. */
  399. private function request($api, $data = [], $method = 'get')
  400. {
  401. try {
  402. $result = $this->curl($this->url . $api, $data, $method);
  403. if ($result['status'] == 200) {
  404. return $result['data'];
  405. }
  406. throw new Exception(
  407. $result['msg']
  408. // $this->errorMsg($result['status'])
  409. );
  410. } catch (Exception $e) {
  411. dd($e);
  412. }
  413. }
  414. /**
  415. * 错误信息
  416. *
  417. * @param integer $code
  418. * @return string
  419. */
  420. private function errorMsg($code)
  421. {
  422. return isset($this->err_code[$code]) ? $this->err_code[$code] : '未知错误';
  423. }
  424. /**
  425. * 执行curl
  426. *
  427. * @param string $url
  428. * @param array $data
  429. * @param string $method
  430. * @return array
  431. */
  432. private function curl($url, $data = [], $method = 'get')
  433. {
  434. if ($method == 'get' && !empty($data)) {
  435. $str = http_build_query($data);
  436. $url = strpos($url, '?') === false ? $url . '?' . $str : $url . '&' . $str;
  437. }
  438. // Header头
  439. $headers = $this->formatHeader($this->getHeaders($data));
  440. if ($this->debug) {
  441. echo "<pre>";
  442. var_dump($headers);
  443. }
  444. $curl = curl_init();
  445. $timeout = 5;
  446. curl_setopt($curl, CURLOPT_URL, $url);
  447. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  448. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  449. curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
  450. if ($method == 'post') {
  451. curl_setopt($curl, CURLOPT_POST, 1);
  452. curl_setopt($curl, CURLOPT_POSTFIELDS, Json::encode($data));
  453. }
  454. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  455. $curl_data = curl_exec($curl);
  456. if (curl_error($curl)) {
  457. $error = '错误:' . $url . '=>' . curl_error($curl);
  458. throw new Exception($error);
  459. }
  460. return Json::decode($curl_data);
  461. }
  462. /**
  463. * 请求池
  464. *
  465. * @param array $requests
  466. * @return array
  467. */
  468. public function pool($requests, $data, $result = null)
  469. {
  470. $relation = $result === null ? false : true;
  471. $pool = new Pool($this->getClient(), $requests($data), [
  472. 'concurrency' => $this->concurrency,
  473. 'fulfilled' => function ($response, $index) use (&$result, $relation) {
  474. // this is delivered each successful response
  475. $relation
  476. ? $result[$index] = array_merge(
  477. $result[$index],
  478. ['data' => $this->getResponseData($response, $index)]
  479. )
  480. : $result[] = $this->getResponseData($response, $index);
  481. },
  482. 'rejected' => function ($reason, $index) {
  483. // this is delivered each failed request
  484. },
  485. ]);
  486. $promise = $pool->promise();
  487. $promise->wait();
  488. return $result;
  489. }
  490. /**
  491. * 使用Guzz请求
  492. *
  493. * @param string $api
  494. * @param array $data
  495. * @param string $method
  496. * @return array
  497. */
  498. private function guzz($api, $data = [], $method = 'post')
  499. {
  500. // 客户端
  501. $client = $this->getClient();
  502. // Header头
  503. $data = array_filter($data, function ($item) {
  504. return $item !== null && $item !== '';
  505. });
  506. $response = $client->send($this->getRequest($api, $data, $method));
  507. return $this->getResponseData($response);
  508. }
  509. /**
  510. * 获取响应数据
  511. *
  512. * @param ResponseInterface $response
  513. * @param integer|null $index
  514. * @return array|void
  515. */
  516. private function getResponseData($response, $index = null)
  517. {
  518. $code = $response->getStatusCode();
  519. $reason = $response->getReasonPhrase();
  520. $content = $response->getBody()->getContents();
  521. \Yii::$app->custom->logs('星链响应信息', $content);
  522. if ($code == 200) {
  523. $data = Json::decode($content);
  524. // 返回状态码
  525. if ($data['status'] == 200) {
  526. return $data['data'] ?? [];
  527. }
  528. if (!$this->exception) {
  529. return [];
  530. }
  531. throw new Exception($data['msg']);
  532. }
  533. throw new Exception('查询失败');
  534. }
  535. /**
  536. * 获取请求
  537. *
  538. * @param array $data
  539. * @param string $method
  540. * @return Request
  541. */
  542. public function getRequest($api, $data, $method)
  543. {
  544. \Yii::$app->custom->logs('星链请求信息', $api);
  545. \Yii::$app->custom->logs('请求数据', $data);
  546. if ($this->debug) {
  547. echo "API:";
  548. var_dump($api);
  549. echo "<br>";
  550. echo "DATA:";
  551. var_dump($data);
  552. echo "<br>";
  553. }
  554. $body = empty($data) ? '{}' : Json::encode($data);
  555. return new Request(strtoupper($method), $api, $this->getHeaders($data), $body);
  556. }
  557. /**
  558. * 获取guzz客户端
  559. *
  560. * @return Client
  561. */
  562. public function getClient()
  563. {
  564. return new Client([
  565. 'base_uri' => $this->url,
  566. 'verify' => false
  567. ]);
  568. }
  569. /**
  570. * 获取请求头
  571. *
  572. * @param array $body
  573. * @return array
  574. */
  575. private function getHeaders($body = [])
  576. {
  577. mt_srand();
  578. // appKey、version、timeStamp、randomNumber
  579. $headers = [
  580. 'appKey' => $this->app_key,
  581. 'version' => $this->version,
  582. 'timeStamp' => time() - 3 . '000',
  583. // 'timeStamp' => '1638424611703',
  584. 'randomNumber' => mt_rand(1000000, 9999999),
  585. // 'randomNumber' => '5441080065398753214',
  586. ];
  587. // 签名
  588. $headers['sign'] = $this->getSign($headers, $body);
  589. $headers['Content-Type'] = 'application/json';
  590. \Yii::$app->custom->logs('星链请求头', $headers);
  591. if ($this->debug) {
  592. echo "HEADERS:";
  593. var_dump($headers);
  594. echo "<br>";
  595. }
  596. return $headers;
  597. }
  598. /**
  599. * 转换header头 curl使用
  600. *
  601. * @param array $headers
  602. * @return array
  603. */
  604. private function formatHeader($headers)
  605. {
  606. // 转换
  607. $result = [];
  608. foreach ($headers as $k => $v) {
  609. $result[] = $k . ':' . $v;
  610. }
  611. return $result;
  612. }
  613. /**
  614. * 获取签名
  615. *
  616. * @param array $headers
  617. * @param array $body
  618. * @return string
  619. */
  620. private function getSign($headers, $body)
  621. {
  622. $arr = array_merge($headers, $body);
  623. ksort($arr);
  624. $str = '';
  625. foreach ($arr as $k => $v) {
  626. $str .= $k . '=' . (is_array($v) ? Json::encode($v) : $v) . '&';
  627. }
  628. $str .= $this->app_secret;
  629. $str = trim($str);
  630. \Yii::$app->custom->logs('星链签名', $str);
  631. return md5($str);
  632. }
  633. /**
  634. * 设置不抛异常
  635. *
  636. * @param boolean $boolean
  637. * @return void
  638. */
  639. public function setNotExcption($boolean = false)
  640. {
  641. return $this->exception = false;
  642. }
  643. }