DouShopService.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. <?php
  2. namespace App\Service;
  3. use App\Model\BasicType;
  4. use App\Model\Product;
  5. use App\Model\SalesOrder;
  6. use App\Model\SalesOrderInfo;
  7. use App\Model\SalesOrderProductInfo;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Log;
  10. use Illuminate\Support\Facades\Redis;
  11. class DouShopService extends Service
  12. {
  13. public $shopId = '59866821';
  14. public $appKey = '7385361854375216667';
  15. public $appSecret = '1e8de180-8c02-49fc-960c-77062fdb9361';
  16. public $host = 'https://openapi-fxg.jinritemai.com';
  17. const RedisKey = 'DOUDIANACCESSTOKEN';
  18. //获取token
  19. public $url_create = '/token/create';
  20. //订单列表
  21. public $url_order_list = '/order/searchList';
  22. //订单详情
  23. public $url_order_detail = '/order/orderDetail';
  24. //------------------获取token
  25. public function getToken($data){
  26. if(empty($data['shop_id'])) return [false, '请选择店铺不能为空'];
  27. $key = $this->appKey . $data['shop_id'] . self::RedisKey;
  28. $token = Redis::get($key);
  29. if(! empty($token)) return [true, $token];
  30. list($bool,$msg) = $this->curlForToken($key, $data['shop_id']);
  31. if(! $bool) return [false, $msg];
  32. return [true, $msg];
  33. }
  34. public function curlForToken($key, $shop_id){
  35. $param['shop_id'] = $shop_id;
  36. $param['grant_type'] = "authorization_self";
  37. $param['code'] = "";
  38. list($url, $paramJson) = $this->organization($param,$this->url_create);
  39. //发送请求
  40. list($bool,$return) = $this->post_helper($url,$paramJson);
  41. if(! $bool) return [false, $return];
  42. if($return['code'] != 10000) return [false, $return['sub_msg']];
  43. file_put_contents('token_'. $shop_id . '.txt',json_encode($return));
  44. $token = $return['data']['access_token'];
  45. $expires_in = $return['data']['expires_in'] - 3600;
  46. Redis::setex($key,$expires_in,$token);
  47. return [true,$token];
  48. }
  49. //------------------获取token
  50. //------------------获取订单列表
  51. public function getOrderList($data,$user){
  52. list($bool,$token) = $this->getToken($data);
  53. if(! $bool) return [false, $token];
  54. list($bool,$return) = $this->curlForOrderList($token,$data);
  55. if(! $bool) return [false, $return];
  56. return [true, $return];
  57. }
  58. public function curlForOrderList($token,$data){
  59. if(empty($data['create_time'][0]) || empty($data['create_time'][0])) return [false, '下单时间不能为空'];
  60. //下单时间:开始结束,秒级时间戳
  61. $return = $this->changeDateToTimeStampAboutRange($data['create_time']);
  62. $param['create_time_start'] = $return[0];
  63. $param['create_time_end'] = $return[1];
  64. if(! empty($data['product'])) {
  65. //商品,number型代表商品ID,其它代表商品名称
  66. $param['product'] = $data['product'];
  67. }
  68. if(isset($data['b_type'])){
  69. //【下单端】 0、站外 1、火山 2、抖音 3、头条 4、西瓜 5、微信 6、值点app 7、头条lite 8、懂车帝 9、皮皮虾 11、抖音极速版 12、TikTok 13、musically 14、穿山甲 15、火山极速版 16、服务市场 26、番茄小说 27、UG教育营销电商平台 28、Jumanji 29、电商SDK
  70. $param['b_type'] = $data['b_type'];
  71. }
  72. if(! empty($data['after_sale_status_desc'])){
  73. //售后状态:all-全部,in_aftersale-售后中,refund-退款中,refund_success-退款成功,refund_fail-退款失败,exchange_success-换货成功 aftersale_close-售后关闭
  74. $param['after_sale_status_desc'] = $data['after_sale_status_desc'];
  75. }
  76. if(! empty($data['tracking_no'])){
  77. //物流单号
  78. $param['tracking_no'] = $data['tracking_no'];
  79. }
  80. if(isset($data['presell_type'])){
  81. //预售类型:0-普通订单;1-全款预售;2-定金预售;3-定金找货;
  82. $param['presell_type'] = $data['presell_type'];
  83. }
  84. if(isset($data['order_type'])){
  85. //订单类型 0、普通订单 2、虚拟商品订单 4、电子券(poi核销) 5、三方核销
  86. $param['order_type'] = $data['order_type'];
  87. }
  88. if(! empty($data['abnormal_order'])) {
  89. //异常订单,1-异常取消,2-风控审核中
  90. $param['abnormal_order'] = $data['abnormal_order'];
  91. }
  92. if(isset($data['trade_type'])){
  93. //交易类型:0-普通;1-拼团;2-定金预售;3-订金找货;4-拍卖;5-0元单;6-回收;7-寄卖;10-寄样;11-0元抽奖(超级福袋);12-达人买样;13-普通定制;16-大众竞拍;18-小时达;102-定金预售的赠品单;103-收款;
  94. $param['trade_type'] = $data['trade_type'];
  95. }
  96. if(! empty($data['update_time'][0]) && ! empty($data['update_time'][0])){
  97. //更新时间:开始结束,秒级时间戳
  98. $return = $this->changeDateToTimeStampAboutRange($data['update_time']);
  99. $param['update_time_start'] = $return[0];
  100. $param['update_time_end'] = $return[1];
  101. }
  102. if(empty($data['size'])) {
  103. //单页大小,限制100以内
  104. $param['size'] = 20;
  105. }else{
  106. if($data['size'] > 100) return [false, '单页大小,限制100以内'];
  107. $param['size'] = $data['size'];
  108. }
  109. if(! isset($data['page'])) {
  110. $param['page'] = 0;
  111. }else{
  112. $param['page'] = $data['page'];
  113. }
  114. if(! empty($data['order_by'])){
  115. //排序条件(create_time 订单创建时间;update_time 订单更新时间;默认create_time;)
  116. $param['order_by'] = $data['order_by'];
  117. }
  118. //排序类型,小到大或大到小,默认大到小
  119. if(! isset($data['order_asc'])){
  120. $param['order_asc'] = false;
  121. }else{
  122. $param['order_asc'] = $data['order_asc'];
  123. }
  124. if(! empty($data['fulfil_status'])) {
  125. //履约状态;如小时达未接单"no_accept"
  126. $param['fulfil_status'] = $data['fulfil_status'];
  127. }
  128. //组织数据
  129. list($url, $paramJson) = $this->organization($param,$this->url_order_list,$token);
  130. //发送请求
  131. list($bool,$return) = $this->post_helper($url,$paramJson);
  132. if(! $bool) return [false, $return];
  133. if($return['code'] != 10000) return [false, $return['sub_msg']];
  134. return [true, $return['data'] ?? []];
  135. }
  136. //------------------获取订单列表
  137. //------------------获取订单详情
  138. public function getOrderDetail($data,$user){
  139. list($bool,$token) = $this->getToken($data);
  140. if(! $bool) return [false, $token];
  141. list($bool,$return) = $this->curlForOrderDetail($token,$data);
  142. if(! $bool) return [false, $return];
  143. return [true, $return];
  144. }
  145. public function curlForOrderDetail($token,$data){
  146. if(empty($data['shop_order_id'])) return [false, '店铺订单号不能为空'];
  147. $param['shop_order_id'] = $data['shop_order_id'];
  148. //组织数据
  149. list($url, $paramJson) = $this->organization($param,$this->url_order_detail,$token);
  150. //发送请求
  151. list($bool,$return) = $this->post_helper($url,$paramJson);
  152. if(! $bool) return [false, $return];
  153. if($return['code'] != 10000) return [false, $return['sub_msg']];
  154. return [true, $return['data'] ?? []];
  155. }
  156. //------------------获取订单详情
  157. //------------------导入线上订单
  158. public function insertDouOrder($data,$user){
  159. // [
  160. // 'shop_id' => 111,
  161. // 'order_list'=>[
  162. // [
  163. // 'order_id' => '',
  164. // 'order_amount' => '',
  165. // 'product' => [
  166. // [
  167. // 'product_name' => '',
  168. // 'code' => '',
  169. // 'item_num' => '',
  170. // 'sum_amount' => '',
  171. // ]
  172. // ],
  173. // ],
  174. // [
  175. // 'order_id' => '',
  176. // 'order_amount' => '',
  177. // 'product' => [
  178. // [
  179. // 'product_name' => '',
  180. // 'code' => '',
  181. // 'item_num' => '',
  182. // 'sum_amount' => '',
  183. // ]
  184. // ],
  185. // ]
  186. // ],
  187. // ];
  188. if(empty($data['order_list'])) return [false, '订单数据不能为空'];
  189. if(empty($data['shop_id'])) return [false, '店铺ID不能为空'];
  190. $basic = BasicType::where('del_time',0)
  191. ->where('code', $data['shop_id'])
  192. ->first();
  193. if(empty($basic->id)) return [false, '未找到店铺绑定的平台,请联系管理员进行绑定'];
  194. $plat_type = $basic->id;
  195. $customer_short_name = 0;
  196. $basic = BasicType::where('del_time',0)
  197. ->whereIn('type',[29])
  198. ->where('title','抖音平台')
  199. ->where('top_depart_id',$user['head']['id'])
  200. ->select('id')->first();
  201. if(! empty($basic->id)) $customer_short_name = $basic->id;
  202. $search = "" ;
  203. $order_id = [];
  204. foreach ($data['order_list'] as $value){
  205. if(empty($value['order_id'])) return [false, '订单ID不能为空'];
  206. $order_id[] = $value['order_id'];
  207. if(! isset($value['order_amount']) || $value['order_amount'] < 0) return [false, '订单金额不能为空'];
  208. //promotion_amount 优惠金额
  209. if(empty($value['product'])) return [false,'订单下的商品不能为空'];
  210. foreach ($value['product'] as $p){
  211. if(empty($p['product_name'])) return [false,'商品名称不能为空'];
  212. if(empty($p['code'])) return [false, '商品:' . $p['product_name'] . '的商家编码为空,请进入抖店商家后台补充完整'];
  213. if(empty($p['item_num'])) return [false, '商品:' . $p['product_name'] . '的数量不能为空'];
  214. if(empty($p['sum_amount'])) return [false, '商品:' . $p['sum_amount'] . '的金额不能为空'];
  215. $search .= "(code = '".$p['code']."') or";
  216. }
  217. }
  218. $search = rtrim($search,' or');
  219. $model = Product::ProductClear($user,[]);
  220. $product = $model->whereRaw($search)
  221. ->where('del_time',0)
  222. ->select('title','id','code','cost','retail_price')
  223. ->get()->toArray();
  224. $product_map = [];
  225. foreach ($product as $value){
  226. $product_map[$value['code']] = [
  227. 'id' => $value['id'],
  228. 'cost' => $value['cost'],
  229. 'retail_price' => $value['retail_price'],
  230. ];
  231. }
  232. $sale = SalesOrder::where('del_time',0)
  233. ->whereIn('plat_order',$order_id)
  234. ->pluck('plat_order','id')
  235. ->toArray();
  236. $head = $user['head']['id'] ?? 0;
  237. $prefix = SalesOrder::$prefix[salesOrder::Model_type_four];
  238. $time = time();
  239. $tmp = [
  240. 'sales_order_type' => SalesOrder::Order_type_one,
  241. 'model_type' => SalesOrder::Model_type_four,
  242. 'order_number' => '',
  243. 'customer_short_name' => $customer_short_name,
  244. 'plat_type' => $plat_type,
  245. 'plat_order' => '',
  246. 'sign_time' => $time,
  247. 'product_total' => 0,
  248. 'contract_fee' => 0,
  249. 'discount_fee' => 0,
  250. 'cdefine29' => '',//分社施工
  251. 'cdefine32' => '',//达人昵称
  252. 'cdefine30' => '',//业务员
  253. 'rate' => 100,
  254. 'depart_id' => $head,
  255. 'top_depart_id' => $head,
  256. 'crt_id' => $user['id'],
  257. 'crt_time' => $time,
  258. 'upd_time' => $time,
  259. ];
  260. $tmp_detail = [
  261. 'sales_order_id' => 0,
  262. 'product_id' => 0,
  263. 'cost' => 0,
  264. 'retail_price' => 0,
  265. 'basic_type_id' => 0,
  266. 'price' => 0,
  267. 'final_amount' => 0,
  268. 'number' => '',
  269. 'crt_time' => $time,
  270. ];
  271. $insert = $insert_detail = $insert_detail_man = [];
  272. foreach ($data['order_list'] as $value){
  273. if(isset($sale[$value['order_id']])) return [false, '平台单号:' . $value['order_id'] . '在系统中已录入!'];
  274. $tmp['order_number'] = OrderNoService::createSalesOrderNumber($prefix);
  275. $tmp['plat_order'] = $value['order_id'];
  276. $tmp['discount_fee'] = 0;
  277. $tmp['contract_fee'] = $value['order_amount'] / 100;
  278. foreach ($value['product'] as $p){
  279. if(! isset($product_map[$p['code']])) return [false, '产品编码:' . $p['code'] . '在系统中不存在或已被删除'];
  280. $tmp['product_total'] += $p['sum_amount'] / 100;
  281. //产品子表
  282. $product_tmp = $product_map[$p['code']] ?? [];
  283. $tmp_detail['product_id'] = $product_tmp['id'];
  284. $tmp_detail['cost'] = $product_tmp['cost'];
  285. $tmp_detail['retail_price'] = $product_tmp['retail_price'];
  286. $tmp_detail['price'] = $product_tmp['retail_price'];
  287. $tmp_detail['final_amount'] = $p['sum_amount'] / 100;
  288. $tmp_detail['number'] = $p['item_num'];
  289. $insert_detail[$value['order_id']][] = $tmp_detail;
  290. }
  291. //主表
  292. $insert[$value['order_id']] = $tmp;
  293. //人员子表
  294. $insert_detail_man[$value['order_id']] = [
  295. 'type' => SalesOrderInfo::type_one,
  296. 'data_id' => $user['id'],
  297. 'crt_time' => $time,
  298. ];
  299. }
  300. $insert = array_values($insert);
  301. $insert_detail = array_values($insert_detail);
  302. $insert_detail_man = array_values($insert_detail_man);
  303. try{
  304. DB::beginTransaction();
  305. if(! empty($insert)) SalesOrder::insert($insert);
  306. $last_insert_id = SalesOrder::where('crt_time',$time)
  307. ->select('id')
  308. ->get()->toArray();
  309. $last_insert_id = array_column($last_insert_id,'id');
  310. if(! empty($insert_detail)){
  311. $insert2 = [];
  312. foreach ($last_insert_id as $key => $value){
  313. if(isset($insert_detail[$key])) {
  314. foreach ($insert_detail[$key] as $val){
  315. $val['sales_order_id'] = $value;
  316. $insert2[] = $val;
  317. }
  318. }
  319. }
  320. SalesOrderProductInfo::insert($insert2);
  321. }
  322. if(! empty($insert_detail_man)){
  323. $insert3 = [];
  324. foreach ($last_insert_id as $key => $value){
  325. if(isset($insert_detail_man[$key])) {
  326. $insert_detail_man[$key]['sales_order_id'] = $value;
  327. $insert3[] = $insert_detail_man[$key];
  328. }
  329. }
  330. SalesOrderInfo::insert($insert3);
  331. }
  332. DB::commit();
  333. }catch (\Exception $e){
  334. DB::rollBack();
  335. return [false, $e->getMessage() . $e->getLine() . $e->getCode()];
  336. }
  337. return [true, ''];
  338. }
  339. //------------------导入线上订单
  340. //序列化排序
  341. public function marshal($param){
  342. if($param == null) return "{}";
  343. $arr = $param;
  344. $this->recKSort($arr); // 对关联数组中的kv,执行排序,需要递归
  345. $json = json_encode($arr, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); // 重新序列化,确保所有key按字典序排序
  346. return $json;
  347. }
  348. // 关联数组排序,递归
  349. public function recKSort(&$arr)
  350. {
  351. $kstring = true;
  352. foreach ($arr as $k => &$v) {
  353. if (!is_string($k)) {
  354. $kstring = false;
  355. }
  356. if (is_array($v)) {
  357. $this->recKSort($v);
  358. }
  359. }
  360. if ($kstring) {
  361. ksort($arr);
  362. }
  363. }
  364. //加密
  365. public function sign($method, $timestamp, $paramJson){
  366. $appKey = $this->appKey;
  367. $appSecret = $this->appSecret;
  368. $paramPattern = 'app_key' . $appKey . 'method' . $method . 'param_json' . $paramJson . 'timestamp' . $timestamp . 'v2';
  369. $signPattern = $appSecret . $paramPattern . $appSecret;
  370. return hash_hmac("sha256", $signPattern, $appSecret);
  371. }
  372. //获取方法
  373. public function getMethod($urlPath) {
  374. if (strlen($urlPath) == 0) {
  375. return $urlPath;
  376. }
  377. $methodPath = "";
  378. if (substr($urlPath, 0, 1) == "/") {
  379. $methodPath = substr($urlPath, 1, strlen($urlPath));
  380. } else {
  381. $methodPath = $urlPath;
  382. }
  383. return str_replace("/", ".", $methodPath);
  384. }
  385. //组织请求参数
  386. public function organization($param,$method,$token = ""){
  387. $paramJson = $this->marshal($param);
  388. //时间戳
  389. $timestamp = time();
  390. //方法
  391. $method_2 = $this->getMethod($method);
  392. //签名
  393. $sign = $this->sign($method_2,$timestamp,$paramJson);
  394. //地址
  395. $url = $this->host . $method . '?app_key='. $this->appKey.'&method='. $method_2 .'&param_json='. $paramJson .'&timestamp='. $timestamp .'&v=2&sign=' . $sign . '&access_token='. $token . '&sign_method=hmac-sha256';
  396. return [$url, $paramJson];
  397. }
  398. //发送请求
  399. public function post_helper($url, $data, $timeout = 20){
  400. $header = array("Content-type:application/json;charset='utf-8'");
  401. Log::channel('apiLog')->info('抖店POST', ["api" => $url , "param" => $data ,"header" => $header]);
  402. $ch = curl_init();
  403. curl_setopt($ch, CURLOPT_URL, $url);
  404. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  405. curl_setopt($ch, CURLOPT_ENCODING, '');
  406. curl_setopt($ch, CURLOPT_POST, 1);
  407. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  408. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  409. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  410. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  411. if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  412. $r = curl_exec($ch);
  413. if ($r === false) {
  414. // 获取错误号
  415. $errorNumber = curl_errno($ch);
  416. // 获取错误信息
  417. $errorMessage = curl_error($ch);
  418. $message = "cURL Error #{$errorNumber}: {$errorMessage}";
  419. Log::channel('apiLog')->info('抖店POST结果', ["message" => $message]);
  420. return [false, "cURL Error #{$errorNumber}: {$errorMessage}"];
  421. }
  422. curl_close($ch);
  423. Log::channel('apiLog')->info('抖店POST结果', ["message" => json_decode($r, true)]);
  424. return [true, json_decode($r, true)];
  425. }
  426. }