setName('spider:dy') ->setDescription('抖音订单拉取器') ->setDefinition( new InputDefinition(array( new InputOption('sn', 'sn', InputOption::VALUE_REQUIRED), new InputOption('os', 'os', InputOption::VALUE_REQUIRED), new InputOption('day', 'day', InputOption::VALUE_REQUIRED) )) ); } protected function users($routeType) { // if (count($this->_users) > 0) return $this->_users; $users = Admins::where('status', 1)->where('is_order', 1)->where('route_type', $routeType)->select(); $us = []; foreach ($users as $u) { $ru = Redis::get('CRM:USER:ONLINE:' . $u->id); if (empty($ru)) continue; $_u = new stdClass(); $_u->username = $u->id; $us[] = $_u; }; $this->_users = $us; return $this->_users; } protected $_redis_pool = []; protected function poolUser($status = 0, $categoryDesc = '') { $routeType = 10; // 抖音境内外类型判断 $abroadKeyWords = ['曼谷', '港', '澳', '泰国', '普吉岛', '境外']; foreach ($abroadKeyWords as $kw) { if (mb_strpos($categoryDesc, $kw) !== false) { $routeType = 20; break; } } $status .= $routeType; if (empty($this->_redis_pool[$status])) { $this->_redis_pool[$status] = Redis::hGetAll('CRM:Pool:' . $status); $users = $this->users($routeType); $_users = []; if (empty($this->_redis_pool[$status])) { foreach ($users as $user) { $_users[$user->username] = 0; Redis::hSet('CRM:Pool:' . $status, $user->username, 0); } $this->_redis_pool[$status] = $_users; } else { asort($this->_redis_pool[$status]); $key_users = array_keys($this->_redis_pool[$status]); $username = $key_users[0]; $max = $this->_redis_pool[$status][$username]; $_users = []; foreach ($users as $user) { $_users[] = $user->username; if (!in_array($user->username, $key_users)) { $this->_redis_pool[$status][$username] = $max; Redis::hSet('CRM:Pool:' . $status, $user->username, $max); } } foreach ($this->_redis_pool[$status] as $username => $val) { if (!in_array($username, $_users)) { unset($this->_redis_pool[$status][$username]); Redis::hDel('CRM:Pool:' . $status, $username); } } } } $username = null; try { $pool = $this->_redis_pool[$status]; if (empty($pool)) $pool = []; asort($pool); $keys = array_keys($pool); // if(!!config('app.debug', true)) return 1; if (empty($keys)) { Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户'); throw new \Exception('没有可以分配的用户'); } $username = $keys[0]; $this->_redis_pool[$status][$username] += 1; Redis::hIncrBy('CRM:Pool:' . $status, $username, 1); } catch (\Exception $e) { Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户', (array)json_encode($e)); throw new \Exception('没有可以分配的用户'); } return $username; } /** * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output): int { // $name = $input->getArgument('name'); $output->writeln('START spider:dy:'.date('Y-m-d H:i:s')); $os = $input->getOption('os'); $orderid = $input->getOption('sn'); $d = $input->getOption('day'); if (empty($d)) { $d = 40; } if ($orderid) { $this->reloadStatus($orderid, $os, $output); return 1; } // $this->checks($output); // $output->writeln('CHECK spider:dy'); // $day = new Douyin(); // print_r($day->get(1)); // return 1; // sleep(5); // $time = strtotime(date('Y-m-d')); // for ($i = 0; $i <= $d; $i++) { // $day = $time - $i * 24 * 3600; // $start = date('Y-m-d', $day); // $end = date('Y-m-d 23:59:59', $day); // $this->orders($start, $end, false); // // $this->_kuaishouOrder($start, $end, false); // } // 8点20 开始分配订单 date_default_timezone_set('Asia/Shanghai'); $startTime = strtotime(date('Y-m-d 09:10')); if (time() > $startTime) { $start = date('Y-m-d 00:00:00', strtotime("-{$d} days")); $end = date('Y-m-d 23:59:59'); $this->orders($start, $end, false); } if (date('H') >= 1 && date('H') <= 7) { $this->reload($output); } $output->writeln('END spider:dy:'.date('Y-m-d H:i:s')); return self::SUCCESS; } private function checks($output) { $i = 0; while (true) { $i++; sleep(1); $val = Redis::Rpop('Travel:Order:check:lits'); if (empty($val) || $i > 100) return; //{\"id\":35,\"check_sn\":\"1\\u963f\\u8fbe\\u6211\\u7684as\\u7684\"} $json = json_decode($val); $res = (new Meituan())->voucher($json->check_sn); //{"code":0,"message":"成功","data":{"orderId":"4944968014698378527","mobile":"18641285757","categoryId":100602,"travelDate":"2024-03-29","isConsumed":true,"categoryDesc":"出发地参团","travelBeginDate":"2024-03-29","travelEndDate":"2024-03-29"},"texInfo":null,"success":true,"fail":false} if ($res->code === 0) { //核销成功 $order_id = $res->data->orderId; $item = Orders::where('os', 1)->where('sn', $order_id)->find(); if (!$item) { $this->orders($output, $order_id); $item = Orders::where('os', 1)->where('sn', $order_id)->find(); } if (empty($item)) { Redis::Lpush('Travel:Order:check:lits', $val); continue; } // $checkOrder = Orders::where('check_sn', $json->check_sn)->find(); //不为空表示已经验证过了 if (!empty($checkOrder)) { continue; } //查看订单是否存在 $back = false; //修改数据库成功了没有 if (!empty($json->id)) { $order = Orders::where('id', $json->id)->find(); if ($order->sn == $order_id) { $order->is_check = 1; $order->check_sn = $json->check_sn; $back = $order->save(); } } elseif (!empty($json->admin_id)) { //订单在自己的名下 $order = Orders::where('sn', $order_id)->where('admin_id', $json->admin_id)->find(); if (!empty($order)) { $order->is_check = 1; $order->check_sn = $json->check_sn; $back = $order->save(); } else { $order = Orders::where('sn', $order_id)->find(); // print_r($order->toArray()); if (!empty($order) && $order->admin_id == 0) { $order->is_check = 1; $order->check_sn = $json->check_sn; $order->admin_id = $json->admin_id; $back = $order->save(); } elseif (!empty($order) && $order->admin_id !== $json->admin_id) { $item = new Orders(); $item->os = 1; $item->sn = $order->sn; $item->product_id = $order->product_id; $item->product_name = $order->product_name; $item->category_id = $order->category_id; $item->create_at = $order->create_at; $item->travel_date = $order->travel_date; $item->mobile = $order->mobile; $item->unit_price = $order->unit_price; $item->total_price = $order->total_price; $item->actual_price = $order->actual_price; $item->quantity = $order->quantity; $item->order_status = $order->order_status; $item->refund_status = $order->refund_status; $item->category_desc = $order->category_desc; $item->admin_id = $json->admin_id; $item->check_sn = $json->check_sn; $item->is_check = 1; $back = $item->save(); $order->is_change = $json->admin_id; $back2 = $order->save(); } } } if (!$back) { Redis::Lpush('Travel:Order:check:lits', $val); //写回去 Redis::Lpush('Travel:Order:check:lits:err', $val); } } else { if ($res->code == 12) { if ($json->id > 0) { //核销码无效 $order = Orders::where('id', $json->id)->find(); $order->is_check = 2; $order->save(); } Logs::create([ 'admin_id' => $json->admin_id ?? 0, 'action' => 11, 'order_id' => $json->id ?? 0, 'check_sn' => $json->check_sn ]); } if ($res->code > 0 && $res->code != 12) { Redis::Lpush('Travel:Order:check:lits', $val); Redis::Lpush('Travel:Order:check:lits:err', $val); } } } } private function orders($start = null, $end = null, $order_id = false) { foreach (Orders::OSS as $k => $os) { $pages = 3; $page = 1; while (true) { if ($page > $pages) break; $list = []; switch ($k) { /*case 1: try { $mei = new Meituan(); $list = $mei->get($page, $start, $end); $pages = $mei->totalPage; } catch (\Exception $e) { Log::error(dirname(__FILE__) . __LINE__ . $e); } break;*/ /*case 2: try { $kuai = new Kuaishou(); $list = $kuai->get($page, $start, $end); $pages = $kuai->totalPage; } catch (\Exception $e) { Log::error(dirname(__FILE__) . __LINE__ . $e); } break;*/ case 3: case 5: try { $dou = new Douyin($k); $list = $dou->get($page, $start, $end, ''); $pages = $dou->totalPage; } catch (\Exception $e) { Log::error(dirname(__FILE__) . __LINE__ . $e); } break; default: # code... break; } foreach ($list as $order) { // 过滤一日游 if ($order->os == 3 && strpos($order->category_desc, '一日游') !== false) { Log::info("抖音 跳过订单:{$order->order_id}"); continue; } $item = Orders::where('os', $order->os)->where('sn', $order->sn)->find(); if (empty($item)) { $order->is_zhibo = $this->zhibo($order->product_name, $order->os); if (FilterMobiles::isFilterMobile($order->mobile)) { $admin_id = 0; } else { $oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile) ->where("create_time", '>=', time() - 24 * 3600 * 3)->find(); if (!empty($oldMobile)) { $admin_id = $oldMobile->admin_id; } else { $admin_id = $this->poolUser($order->orderStatus, $order->category_desc); } if (empty($admin_id)) return null; } $order->admin_id = $admin_id; $order->give_time = time(); //判断是否需要发短信 if ($order->order_status == 1) { $this->sms($admin_id, $order); } $work = \app\server\Orders::getLiveRoomWork($order->create_at, $order->product_id); if ($work) { $order->live_room_work_id = $work->id; } //新获得一个用户的,提示管理员有新的订单 Redis::incrBy('CRM:USER:ONLINE:NEW:' . $admin_id, 1); $item = new Orders(); } if ($order->travel_date && empty($order->travel_end)) { $days = $this->_days($order); if ($days) { $order->travel_end = date('Y-m-d 00:00:00', strtotime($order->travel_date) + $days * 24 * 3600); } } if ($item->order_status !== 2 && $order->order_status == 2) { Redis::incrBy('CRM:USER:WRITE:OFF:' . $item->admin_id, 1); } $back = $item->save($order->toArray()); if ($back) { $this->_finance(0, $item->id, $item->asset_price); } } // 数据小于50, 结束 if (empty($list)) { break; } $page++; } } } private function reload($output) { $back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX'); if (!$back) return; $orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->whereIn('os', [3, 5])->wherein('status', [1, 2, 3])->select(); foreach ($orders as $order) { $this->reloadStatus($order->sn, $order->os, $output); } } private function reloadStatus($orderid, $os, $output) { $w[] = ['sn', '=', $orderid]; if ($os) $w[] = ['os', '=', $os]; $item = Orders::where($w)->find(); if (empty($item)) { $output->writeln('没有找到订单'); } $it = null; switch ($item->os) { /*case 1: $m = new Meituan(); $it = $m->get(1, null, null, $item->sn); break; case 2: $m = new Kuaishou(); $it = $m->get(1, null, null, $item->sn); break;*/ case 3: case 5: $m = new Douyin($item->os); $it = $m->get(1, null, null, $item->sn); break; default: # code... break; } if ($it) { $back = $item->save($it[0]); if ($back) { $this->_finance(0, $item->id, $item->asset_price); } } else { $output->writeln('没有拉取到数据:' . $orderid); } } private function _days($order) { if (stripos($order->product_name, '一日') !== false) { return 1; } preg_match('/(\d)天/', $order->product_name, $all); if (!empty($all) && intval($all[1]) > 0) { return $all[1]; } return 0; } private function sms($admin_id, $order) { $user = Admins::cache(true)->where('id', $admin_id)->find(); if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) { $has = Blacks::where('mobile', $order->mobile)->find(); if (empty($has) && !empty($order->mobile)) { SMS::juhe_sms_send($order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]); } else { sleep(10); } } else { print_r([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]); } } private function zhibo($title, $os = 0) { if ($os == 3) { return 2; } if ( strlen($title) - 3 == strripos($title, "!") || strlen($title) - 1 == strripos($title, "!") ) { return 1; } elseif (strlen($title) - 3 == strripos($title, "甄")) { return 2; } return 0; } private function _finance($type = 1, $order_id = 0, $price = 0) { // $fa = Finances::where('order_id', $order_id)->where('type', $type)->find(); $back = Redis::set('CRM:ORDER:LOCK:' . $order_id, time(), 'EX', 6, 'NX'); if (!$back) return; $total = Finances::where('order_id', $order_id)->sum('total'); //总的关于这个订单的金额 //如果总金额大于提交上来的核销金额,那就是退费的 //如果提交上来的金额小于总金额,那就是核销的 if ($total > $price) { $type = 2; $fee = -($total - $price); } elseif ($total < $price) { $type = 1; $fee = $price - $total; } else { return; } Finances::create([ 'order_id' => $order_id, 'type' => $type, 'total' => $fee, 'status' => 1 ]); return; // if(empty($fa)) { // if($type == 2) { // $has = Finances::where('order_id', $order_id)->where('type', 1)->find(); // if(empty($has)) return; // } // Finances::create([ // 'order_id' => $order_id, // 'type' => $type, // 'total' => $price, // 'status' => 1 // ]); // } } }