travel/service/app/command/SpiderDy.php

566 lines
21 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\command;
use app\admin\controller\AdminController;
use app\common\Error;
use app\model\Admins;
use app\model\Blacks;
use app\model\FilterMobiles;
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;
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 = [];
protected $page = 3;
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),
new InputOption('day', 'day', InputOption::VALUE_REQUIRED),
// 最大页数
new InputOption('max_page', 'max_page', InputOption::VALUE_OPTIONAL)
))
);
}
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 ($input->getOption('max_page')) {
$this->page = $input->getOption('max_page');
}
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 = $this->page;
$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);
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;
}
}
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
// ]);
// }
}
}