travel/service/app/command/SpiderDy.php

566 lines
21 KiB
PHP
Raw Normal View History

2024-06-24 11:52:30 +08:00
<?php
namespace app\command;
2024-08-12 11:13:05 +08:00
use app\admin\controller\AdminController;
2024-09-01 13:38:02 +08:00
use app\common\Error;
2024-06-24 11:52:30 +08:00
use app\model\Admins;
use app\model\Blacks;
2024-09-01 13:38:02 +08:00
use app\model\FilterMobiles;
2024-06-24 11:52:30 +08:00
use app\model\Finances;
use app\model\Logs;
use app\model\Orders;
use app\server\Douyin;
use app\server\Meituan;
use app\server\Kuaishou;
use app\server\Orders as ServerOrders;
2024-06-24 11:52:30 +08:00
use app\server\SMS;
use stdClass;
use support\Log;
use support\Redis;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class SpiderDy extends Command
{
protected static $defaultName = 'spider:dy';
protected static $defaultDescription = '抖音订单拉取器';
protected $_users = [];
2024-11-25 10:11:30 +08:00
protected $page = 3;
2024-06-24 11:52:30 +08:00
protected function configure()
{
$this->setName('spider:dy')
->setDescription('抖音订单拉取器')
->setDefinition(
new InputDefinition(array(
new InputOption('sn', 'sn', InputOption::VALUE_REQUIRED),
new InputOption('os', 'os', InputOption::VALUE_REQUIRED),
2024-11-25 10:11:30 +08:00
new InputOption('day', 'day', InputOption::VALUE_REQUIRED),
// 最大页数
new InputOption('max_page', 'max_page', InputOption::VALUE_OPTIONAL)
2024-06-24 11:52:30 +08:00
))
);
}
2024-08-12 11:13:05 +08:00
protected function users($routeType)
2024-06-24 11:52:30 +08:00
{
2024-08-20 23:45:58 +08:00
// if (count($this->_users) > 0) return $this->_users;
2024-08-12 11:13:05 +08:00
$users = Admins::where('status', 1)->where('is_order', 1)->where('route_type', $routeType)->select();
2024-06-24 11:52:30 +08:00
$us = [];
foreach ($users as $u) {
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
2024-07-05 14:19:27 +08:00
if (empty($ru)) continue;
$_u = new stdClass();
$_u->username = $u->id;
$us[] = $_u;
2024-06-24 11:52:30 +08:00
};
$this->_users = $us;
return $this->_users;
}
protected $_redis_pool = [];
2024-08-12 11:13:05 +08:00
protected function poolUser($status = 0, $categoryDesc = '')
2024-06-24 11:52:30 +08:00
{
2024-09-19 10:06:53 +08:00
$routeType = 10;
// 抖音境内外类型判断
$abroadKeyWords = ['曼谷', '港', '澳', '泰国', '普吉岛', '境外'];
foreach ($abroadKeyWords as $kw) {
if (mb_strpos($categoryDesc, $kw) !== false) {
$routeType = 20;
break;
}
}
2024-08-12 14:36:02 +08:00
$status .= $routeType;
2024-06-24 11:52:30 +08:00
if (empty($this->_redis_pool[$status])) {
$this->_redis_pool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
2024-08-12 11:13:05 +08:00
$users = $this->users($routeType);
2024-06-24 11:52:30 +08:00
$_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');
2024-07-28 16:50:38 +08:00
$output->writeln('START spider:dy:'.date('Y-m-d H:i:s'));
2024-06-24 11:52:30 +08:00
$os = $input->getOption('os');
$orderid = $input->getOption('sn');
$d = $input->getOption('day');
if (empty($d)) {
$d = 40;
}
2024-11-25 10:11:30 +08:00
if ($input->getOption('max_page')) {
$this->page = $input->getOption('max_page');
}
2024-06-24 11:52:30 +08:00
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);
2024-07-28 16:50:38 +08:00
// $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);
// }
2024-08-20 14:39:11 +08:00
// 8点20 开始分配订单
date_default_timezone_set('Asia/Shanghai');
2024-09-25 17:10:22 +08:00
$startTime = strtotime(date('Y-m-d 09:10'));
2024-08-20 14:39:11 +08:00
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);
}
2024-06-24 11:52:30 +08:00
if (date('H') >= 1 && date('H') <= 7) {
$this->reload($output);
}
2024-07-28 16:50:38 +08:00
$output->writeln('END spider:dy:'.date('Y-m-d H:i:s'));
2024-06-24 11:52:30 +08:00
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) {
2024-11-25 10:11:30 +08:00
$pages = $this->page;
2024-06-24 11:52:30 +08:00
$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:
2024-08-16 21:12:33 +08:00
case 5:
2024-06-24 11:52:30 +08:00
try {
2024-08-16 21:17:08 +08:00
$dou = new Douyin($k);
2024-08-20 14:39:11 +08:00
$list = $dou->get($page, $start, $end, '');
2024-06-24 11:52:30 +08:00
$pages = $dou->totalPage;
} catch (\Exception $e) {
Log::error(dirname(__FILE__) . __LINE__ . $e);
}
break;
default:
# code...
break;
}
foreach ($list as $order) {
2024-08-12 15:54:33 +08:00
// 过滤一日游
2024-08-20 10:46:04 +08:00
if ($order->os == 3 && strpos($order->category_desc, '一日游') !== false) {
2024-08-20 10:17:34 +08:00
Log::info("抖音 跳过订单:{$order->order_id}");
2024-08-12 15:54:33 +08:00
continue;
}
2024-06-24 11:52:30 +08:00
$item = Orders::where('os', $order->os)->where('sn', $order->sn)->find();
2024-06-24 11:52:30 +08:00
if (empty($item)) {
$order->is_zhibo = $this->zhibo($order->product_name, $order->os);
2024-09-01 13:38:02 +08:00
if (FilterMobiles::isFilterMobile($order->mobile)) {
$admin_id = 0;
2024-06-24 11:52:30 +08:00
} else {
2024-09-01 13:38:02 +08:00
$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 {
2024-10-14 09:06:23 +08:00
// $admin_id = $this->poolUser($order->orderStatus, $order->category_desc);
try {
$admin_id = \app\server\Orders::poolUser($order->orderStatus, $order->product_id);
} catch (\Exception $exception) {
Log::info(sprintf('dy create order fail:%s, order_id:%s', $exception->getMessage(), $order->sn));
continue;
}
2024-09-01 13:38:02 +08:00
}
2024-12-17 19:17:25 +08:00
2024-09-01 13:38:02 +08:00
if (empty($admin_id)) return null;
2024-06-24 11:52:30 +08:00
}
$order->admin_id = $admin_id;
$order->give_time = time();
//判断是否需要发短信
2024-12-17 19:17:25 +08:00
if ($order->order_status == 1 && $admin_id) {
2024-09-20 10:39:29 +08:00
$this->sms($admin_id, $order);
2024-06-24 11:52:30 +08:00
}
2024-09-20 10:39:29 +08:00
$work = \app\server\Orders::getLiveRoomWork($order->create_at, $order->product_id);
if ($work) {
$order->live_room_work_id = $work->id;
}
2024-06-24 11:52:30 +08:00
//新获得一个用户的,提示管理员有新的订单
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);
}
}
2024-08-12 15:54:33 +08:00
if ($item->order_status !== 2 && $order->order_status == 2) {
Redis::incrBy('CRM:USER:WRITE:OFF:' . $item->admin_id, 1);
}
2024-06-24 11:52:30 +08:00
$back = $item->save($order->toArray());
if ($back) {
$this->_finance(0, $item->id, $item->asset_price);
}
}
2024-06-24 11:52:30 +08:00
2024-08-20 09:26:37 +08:00
// 数据小于50, 结束
2024-09-10 10:29:22 +08:00
if (empty($list)) {
2024-08-20 09:26:37 +08:00
break;
}
2024-06-24 11:52:30 +08:00
$page++;
}
}
}
private function reload($output)
{
$back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX');
if (!$back) return;
2024-09-02 17:41:24 +08:00
$orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->whereIn('os', [3, 5])->wherein('status', [1, 2, 3])->select();
2024-06-24 11:52:30 +08:00
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:
2024-08-16 21:12:33 +08:00
case 5:
$m = new Douyin($item->os);
2024-08-20 14:39:11 +08:00
$it = $m->get(1, null, null, $item->sn);
2024-06-24 11:52:30 +08:00
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) {
2024-06-24 11:52:30 +08:00
$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
// ]);
// }
}
}