This commit is contained in:
jianghanbo 2024-09-02 17:41:24 +08:00
parent ea7ad218c6
commit 9f05d996e7
11 changed files with 781 additions and 9 deletions

View File

@ -18,6 +18,9 @@
<!-- <div class="right-menu-item hover-effect"> <!-- <div class="right-menu-item hover-effect">
<el-button @click="drawer = true">QA常见问题</el-button> <el-button @click="drawer = true">QA常见问题</el-button>
</div>--> </div>-->
<div v-if="$store.getters.is_anchor" class="right-menu-item hover-effect">
<el-button @click="dialogWorks = true">预约待处理10</el-button>
</div>
<div v-if="$store.getters.is_anchor" class="right-menu-item hover-effect"> <div v-if="$store.getters.is_anchor" class="right-menu-item hover-effect">
<el-button @click="dialogWorks = true">排班{{ $store.getters.name }}</el-button> <el-button @click="dialogWorks = true">排班{{ $store.getters.name }}</el-button>
</div> </div>

View File

@ -94,8 +94,9 @@
style="width: 120px" style="width: 120px"
> >
<el-option key="" label="请选择" value="" /> <el-option key="" label="请选择" value="" />
<el-option key="1" label="已预约" value="1" />
<el-option key="0" label="未预约" value="0" /> <el-option key="0" label="未预约" value="0" />
<el-option key="1" label="已预约(未处理)" value="1" />
<el-option key="2" label="已预约(已处理)" value="2" />
</el-select> </el-select>
<el-button <el-button
@ -169,6 +170,14 @@
> >
同步 同步
</el-button> </el-button>
<el-button v-if="scope.row.appointment_status == 1"
size="small"
icon="el-icon-edit"
@click="onOneClickYyHandle(scope.row)"
>
预约已处理
</el-button>
</el-button-group> </el-button-group>
</template> </template>
</el-table-column> </el-table-column>
@ -240,7 +249,7 @@
}" }"
type="primary" type="primary"
> >
{{ scope.row.appointment_status == 1 ? "已预约" : "未预约" }} {{ scope.row.appointment_status == 1 ? "已预约(未处理)" : (scope.row.appointment_status == 2 ? "已预约(已处理)":"未预约") }}
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -462,6 +471,13 @@
}}</el-radio> }}</el-radio>
</template> </template>
</el-form-item> </el-form-item>
<el-form-item label="预约跟进状态">
<template v-for="(v, k) in status_arr">
<el-radio v-if="k > 0" v-model="item.status" :label="k" border>{{
v
}}</el-radio>
</template>
</el-form-item>
<!-- <el-form-item label="快捷跟进" style="width: 600px;"> <!-- <el-form-item label="快捷跟进" style="width: 600px;">
<el-select v-model="value" placeholder="请选择" @change="onChange"> <el-select v-model="value" placeholder="请选择" @change="onChange">
<el-form-item style="display: inline-flex;text-align: left;width: 770px;"> <el-form-item style="display: inline-flex;text-align: left;width: 770px;">
@ -926,6 +942,25 @@ export default {
}); });
}); });
}, },
onOneClickYyHandle(item) {
this.$axios
.post("/admin/order/changeAppointmentStatus", { id: item.id })
.then((res) => {
this.dialogVisible = false;
this.$notify({
title: "成功",
message: "已处理",
type: "success",
});
// this.getList();
})
.catch((err) => {
this.$notify.error({
title: "错误",
message: err,
});
});
},
}, },
}; };
</script> </script>

View File

@ -38,8 +38,8 @@ module.exports = {
}, },
proxy: { proxy: {
'/dev-api': { // 接口地址 以 api开头的都走下面的配置 '/dev-api': { // 接口地址 以 api开头的都走下面的配置
target: 'https://www.szjinao.cn', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2 // target: 'https://www.szjinao.cn', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
// target: 'http://192.168.1.4:8787', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2 target: 'http://192.168.0.6:8787', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
ws: true, // 是否支持 websocket 请求 支持 ws: true, // 是否支持 websocket 请求 支持
changeOrigin: true, // 是否启用跨域 changeOrigin: true, // 是否启用跨域
pathRewrite: { pathRewrite: {

View File

@ -71,6 +71,7 @@ class IndexController extends base
// 跟进提醒 // 跟进提醒
$followMessage = 0; $followMessage = 0;
$followOrderId = ''; $followOrderId = '';
$appointmentNum = 1;
if ($request->admin->id > 1) { if ($request->admin->id > 1) {
$start = strtotime(date('Y-m-d')) * 1000; $start = strtotime(date('Y-m-d')) * 1000;
$end = strtotime(date('Y-m-d', strtotime('+1 days'))) * 1000 - 1; $end = strtotime(date('Y-m-d', strtotime('+1 days'))) * 1000 - 1;
@ -82,9 +83,14 @@ class IndexController extends base
if ($followMessage) { if ($followMessage) {
$followOrderId = $followMessageList[0]['id']; $followOrderId = $followMessageList[0]['id'];
} }
// 预约未处理数量
$appointmentNum = Orders::where(['admin_id' => $request->admin->id])
->where('appointment_status', 1)
->count();
} }
return $this->success(['new' => $new ?? 0, 'follow' => $follow, 'back' => $backs, 'order_write_off' => $orderWriteOff, 'follow_message' => $followMessage, 'follow_order_id' => $followOrderId]); return $this->success(['new' => $new ?? 0, 'follow' => $follow, 'back' => $backs, 'order_write_off' => $orderWriteOff, 'follow_message' => $followMessage, 'follow_order_id' => $followOrderId, 'appointment_num' => $appointmentNum]);
} }
public function line() public function line()

View File

@ -165,6 +165,8 @@ class OrderController extends base
$statss = Orders::AllOssStatus; $statss = Orders::AllOssStatus;
}elseif($key ==5) { }elseif($key ==5) {
$statss = Orders::DouyinStatus; $statss = Orders::DouyinStatus;
}elseif($key ==6) {
$statss = Orders::TongchengStatus;
} }
$_ch = []; $_ch = [];
@ -585,4 +587,39 @@ class OrderController extends base
return $this->error(2006, '出错了:' . $e->getMessage()); return $this->error(2006, '出错了:' . $e->getMessage());
} }
} }
/**
* 预约处理
* @param Request $request
* @return \support\Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function changeAppointmentStatus(Request $request)
{
try {
$ids = $request->post('id', 0);
$idArr = explode(',', $ids);
$flow = [];
foreach ($idArr as $id) {
Log::info('同步订单ID' . $id);
$order = Orders::where('id', $id)->find();
if (empty($order)) {
return $this->error(2004, '记录没有找到.');
}
// 订单是待使用状态,先同步第三方状态
$order['appointment_status'] = 2;
$order->save();
}
return $this->success(implode("\n", $flow));
}catch(\Exception $e) {
return $this->error(2006, '出错了:' . $e->getMessage());
}
}
} }

View File

@ -425,7 +425,7 @@ class SpiderDy extends Command
{ {
$back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX'); $back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX');
if (!$back) return; if (!$back) return;
$orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->where('os', 3)->wherein('status', [1, 2, 3])->select(); $orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->whereIn('os', [3, 5])->wherein('status', [1, 2, 3])->select();
foreach ($orders as $order) { foreach ($orders as $order) {
$this->reloadStatus($order->sn, $order->os, $output); $this->reloadStatus($order->sn, $order->os, $output);
} }

View File

@ -0,0 +1,408 @@
<?php
namespace app\command;
use app\admin\controller\AdminController;
use app\model\Admins;
use app\model\Blacks;
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 app\server\Tongcheng;
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 SpiderTc extends Command
{
protected static $defaultName = 'spider:tc';
protected static $defaultDescription = '同程订单拉取器';
protected $_users = [];
protected function configure()
{
$this->setName('spider:tc')
->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 = AdminController::ROUTE_LISTS[mb_substr($categoryDesc, 0, 2)] ?? 10;
$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;
}
/** @var InputInterface $output */
protected $output;
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
// $name = $input->getArgument('name');
$this->output = $output;
$output->writeln('START spider:tc:'.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;
}
// echo "状态1\n";
// $this->poolUser(1);
// echo "状态2\n";
// $this->poolUser(2);
// echo "状态3\n";
// $this->poolUser(3);
// echo "状态4\n";
// $this->poolUser(4);
// echo "状态5\n";
// $this->poolUser(5);
// 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 08:20'));
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:tc:'.date('Y-m-d H:i:s'));
return self::SUCCESS;
}
private function orders($start = null, $end = null, $order_id = false)
{
foreach (Orders::OSS as $k => $os) {
$pages = 6;
$page = 1;
while (true) {
if ($page > $pages) break;
$list = [];
switch ($k) {
case 6:
try {
$dou = new Tongcheng($k);
$list = $dou->get($page, $start, $end, '');
$this->output->writeln('test:' . json_encode($list));
$pages = $dou->totalPage;
} catch (\Exception $e) {
$this->output->writeln('error:' . $e->getMessage());
Log::error(dirname(__FILE__) . __LINE__ . $e);
}
break;
default:
# code...
break;
}
foreach ($list as $order) {
$item = Orders::where('os', $order->os)->where('sn', $order->sn)->find();
if (empty($item)) {
$order->is_zhibo = 2;
$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();
//判断是否需要发短信 200-待使用205-预约中抖音210-已预约抖音310-已履约抖音300-已完成
if (in_array($order->order_status, [200, 205, 210, 310, 300]) ) {
$this->sms($admin_id, $order);
}
//新获得一个用户的,提示管理员有新的订单
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 (count($list) < 50) {
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)->where('os', 6)->wherein('status', [1, 2, 3])->select();
foreach ($orders as $order) {
$this->reloadStatus($order->sn, $order->os, $output);
}
}
/**
* @param $admin_id
* @param $order
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function sms($admin_id, $order) {
$user = Admins::cache(true)->where('id', $admin_id)->find();
$templateId = 264956;// 261607
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
echo "发送短信\n";
print_r([$order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
$has = Blacks::where('mobile', $order->mobile)->find();
if (empty($has) && !empty($order->mobile)) {
SMS::juhe_sms_send($order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]);
} else {
echo "黑名单不发送短信\n";
sleep(10);
}
} else {
echo "不发送短信\n";
print_r([$order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
}
}
private function reloadStatus($orderid, $os, $output)
{
$w[] = ['sn', '=', $orderid];
if ($os) $w[] = ['os', '=', $os];
$item = Orders::where($w)->find();
if (empty($item)) {
$output->writeln('没有找到订单');
}
$m = new Tongcheng($os);
$it = $m->get(1, null, null, $item->sn);
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 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
// ]);
// }
}
}

View File

@ -16,6 +16,9 @@ class Orders extends base
const DouyinReservationStatus = [1 => '未预约', 2 => '待接单', 3 => '已预约', 4 => '已取消', 5 => '已完成', 6 => '取消/退款申请']; const DouyinReservationStatus = [1 => '未预约', 2 => '待接单', 3 => '已预约', 4 => '已取消', 5 => '已完成', 6 => '取消/退款申请'];
const DouyinStatus = [1 => '未核销', 2 => '已核销', 3 => '申请退款中', 4 => '已退款', 5 => '部分核销']; const DouyinStatus = [1 => '未核销', 2 => '已核销', 3 => '申请退款中', 4 => '已退款', 5 => '部分核销'];
// 订单状态(100 - 待付款150-用户取消200-待使用205-预约中抖音210-已预约抖音310-已履约抖音300-已完成400-已关闭)
const TongchengStatus = [100 => '待付款', 150 => '用户取消', 200 => '待使用', 205 => '预约中(抖音)', 210 => '已预约(抖音)', 310 => '已履约(抖音)', 300 => '已完成', 400 => '已关闭'];
const AllOssStatus = [1 => '待使用', 2 => '已核销', 3 => '已退款']; const AllOssStatus = [1 => '待使用', 2 => '已核销', 3 => '已退款'];
// 全部 未核销 已核销 已退款 未付款 已取消 待支付 申请退款中 部分核销 // 全部 未核销 已核销 已退款 未付款 已取消 待支付 申请退款中 部分核销
// 0 1 2 3 4 5 6 7 8 // 0 1 2 3 4 5 6 7 8
@ -27,7 +30,7 @@ class Orders extends base
]; ];
const StatusName = ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单']; const StatusName = ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'];
const OSS = [1 => '美团', 2 => '快手', 3 => '抖音(甄选)', '5' => '抖音(新国旅)', 4 => '全平台']; const OSS = [1 => '美团', 2 => '快手', 3 => '抖音(甄选)', '5' => '抖音(新国旅)', '6' => '同程', 4 => '全平台'];
const timeType = ['create_time' => '添加记录时间', 'update_time' => '修改记录时间', 'last_follow' => '最后跟进时间', 'next_follow' => '下次跟进时间', 'travel_date' => '出行时间', 'create_at' => '下单时间']; const timeType = ['create_time' => '添加记录时间', 'update_time' => '修改记录时间', 'last_follow' => '最后跟进时间', 'next_follow' => '下次跟进时间', 'travel_date' => '出行时间', 'create_at' => '下单时间'];
@ -39,6 +42,10 @@ class Orders extends base
return self::OrderStatus[$this->order_status] ?? '未知'; return self::OrderStatus[$this->order_status] ?? '未知';
elseif ($this->os == 3) elseif ($this->os == 3)
return self::DouyinStatus[$this->order_status] ?? '未知'; return self::DouyinStatus[$this->order_status] ?? '未知';
elseif ($this->os == 5)
return self::DouyinStatus[$this->order_status] ?? '未知';
elseif ($this->os == 6)
return self::TongchengStatus[$this->order_status] ?? '未知';
else else
return self::KuaishouStatus[$this->order_status] ?? '未知'; return self::KuaishouStatus[$this->order_status] ?? '未知';
} }

View File

@ -108,7 +108,7 @@ class Orders {
$got = $it[0]; $got = $it[0];
// 查询未预约状态 // 查询未预约状态
if ($order->appointment_status != 1) { if ($order->appointment_status == 0) {
$appointment_status = $dy->_orderMakeAppointmentStatus($order->sn); $appointment_status = $dy->_orderMakeAppointmentStatus($order->sn);
if ($appointment_status !== null) { if ($appointment_status !== null) {
$got['appointment_status'] = 1; $got['appointment_status'] = 1;

View File

@ -0,0 +1,273 @@
<?php
namespace app\server;
use app\model\Orders;
use Exception;
use support\Log;
use support\Redis;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
/**
* 同程订单处理
*/
class Tongcheng {
private $os = '';
protected $cookie;
private $gate = 'https://ebk.17u.cn';
public $totalPage = 6;
public function __construct($os = 6)
{
$this->cookie = $this->_cookie();
$this->os = $os;
}
/**
* @throws ModelNotFoundException
* @throws DataNotFoundException
* @throws DbException
* @throws Exception
*/
public function get($page, $start = null, $end = null, $orderId = '')
{
if (empty($start) || empty($end)) {
$start = date('Y-m-d 00:00:00');
$end = date('Y-m-d 23:59:59');
}
if ($orderId) {
$start = $end = null;
}
$_list = [];
$list = $this->_certificateList($start, $end, $page, $orderId);
if (empty($list->data->list) || $list->code != 1000) {
$this->totalPage = 1;
return $_list;
}
Log::info($start . '--' . $end . " 同程 page:{$page} total_count:" . $list->data->totalCount . " page_count:" . $list->data->totalPage);
foreach ($list->data->list as $order) {
Log::info("同程 订单号:{$order->orderId} \n\n");
// $orderDetail = $this->_orderDetail($order->order_id, $order->status);
// if (empty($orderDetail)) {
// Log::info("同程 订单详情拉取失败:{$order->order_id}");
// throw new Exception("同程拉单{$order->order_id}详情获取失败");
// }
$item = new Orders();
$item->os = $this->os;
$item->sn = $order->orderId;
$item->product_id = $order->lineId;
$item->product_name = $order->title;
$item->category_id = $order->agentFinderId;
$item->category_desc = $order->agentFinderNickname;
$item->create_at = strtotime($order->createTime) * 1000;
$item->mobile = $order->mobile;
$item->travel_date = null;
$item->total_price = $order->orderAmount;
$item->quantity = $order->productCount;
$item->unit_price = bcdiv($order->orderAmount, $order->productCount, 2);
$item->actual_price = $order->orderAmount;
$item->order_status = $order->orderStatus; // 订单状态(100 - 待付款150-用户取消200-待使用205-预约中抖音210-已预约抖音310-已履约抖音300-已完成400-已关闭)
$item->appointment_status = in_array($order->orderStatus, [210, 310, 300]) ? 1 : 0; // 是否已预约
$item->asset_status = $order->status ?? 0; // 核销状态
$item->asset_price = $order->orderStatus == 300 ? $order->orderAmount : 0; // 核销金额
//抖音的核销金额需要查核销订单
$_list[] = $item;
}
return $_list; //返回半成品
}
/**
* @throws Exception
*/
public function _certificateList($start, $end, $page, $orderId)
{
$params = [
'begin' => $start,// 开始时间,格式2024-09-01 00:00:00
'end' => $end,// 结束时间 格式2024-09-01 00:00:00
'orderStatus' => "",// 订单状态
'current' => $page, // 当前数量
'size' => 50, // 显示数量
'orderId' => $orderId, // 订单号
];
$this->_getRetriesLock();
$list = $this->_curl("/merchant/api/market-live/wxVideoV2/supplierOrderByPage", $params, 'POST');
if (empty($list) || $list->code != 1000) {
$this->_getRetriesLock();
$list = $this->_curl("/merchant/api/market-live/wxVideoV2/supplierOrderByPage", $params, 'POST');
if (empty($list) || $list->status_code != 0) {
Log::error('===查询时间:' . $start . '--' . $end . '====certificate_list: ' . json_encode($list));
throw new Exception("同程拉单失败,Err:" . json_encode($list));
}
}
return $list;
}
public function _getRetriesLock(): bool
{
$pattern = 'php webman spider:tc';
$maxProcesses = 8;
$this->_killMiddleProcesses($pattern, $maxProcesses);
$maxRetries = 5;
$retries = 0;
while ($retries < $maxRetries) {
$back = Redis::set('SpiderTc:CertificateList:lock', time(), 'EX', 1, 'NX');
if ($back) {
break;
}
$retries++;
sleep(1);
}
return true;
}
public function _killMiddleProcesses($pattern, $maxProcesses)
{
// 检查操作系统是否为 Linux
if (PHP_OS !== 'Linux') {
echo "当前系统不是 Linux跳过执行。\n";
return false;
}
// 获取当前进程的 PID
$currentPid = getmypid();
// 获取进程列表
$command = "ps aux | grep '$pattern' | grep -v grep | grep -v '$currentPid' | awk '{print $2,$10}'";
$output = [];
exec($command, $output);
$processList = [];
foreach ($output as $line) {
list($pid, $runtime) = explode(' ', $line);
$runtimeParts = explode(':', $runtime);
$hours = intval($runtimeParts[0]);
$minutes = intval($runtimeParts[1]);
$totalMinutes = $hours * 60 + $minutes;
$processList[] = ['pid' => $pid, 'runtime' => $totalMinutes];
}
// 按运行时间排序
usort($processList, function ($a, $b) {
return $a['runtime'] <=> $b['runtime'];
});
// 过滤掉当前 PID 和运行时间最短的 PID
if (!empty($processList)) {
array_shift($processList); // 移除运行时间最短的
}
$processList = array_filter($processList, function ($process) use ($currentPid) {
return $process['pid'] != $currentPid;
});
$processCount = count($processList);
// 如果进程数量超过最大值,则终止中间部分的进程
if ($processCount > $maxProcesses) {
$processesToKill = $processCount - $maxProcesses;
$processesKilled = 0;
// 杀死运行时间最短的进程
foreach ($processList as $process) {
if ($processesKilled >= $processesToKill) {
break;
}
exec("kill -9 {$process['pid']}");
$processesKilled++;
echo "Killed process with PID: {$process['pid']}\n";
}
if ($processesKilled === 0) {
echo "No processes to kill.\n";
}
} else {
echo "Process count is not over the limit.\n";
}
return true;
}
public function _orderDetail($orderId = null, $status = null)
{
$orderDetail = $this->_curl("/life/trip/v2/travel_agency/book/detail", [
'order_id' => $orderId,
'book_order_id' => '',
'status' => $status,
]);
return $orderDetail->data ?? null;
}
public function _cookie($data = '')
{
$key = 'Tongcheng:cookie-' . $this->os;
if ($data) Redis::set($key, $data);
return Redis::get($key);
}
public function _curl($url, $params, $method = 'GET')
{
// 请求次数统计
$key = sprintf("Tongcheng:ReqiestCount:%s", date('Y-m-d'));
$requestCount = Redis::incrBy($key, 1);
if ($requestCount == 1) {
Redis::expire($key, 604800); // 7天
}
$cookiePath = sprintf(runtime_path() . '%s_tc_cookie.txt', $this->os);
$http = $this->gate . $url;
if ($method == 'GET') {
$http = $http . '?' . http_build_query($params);
}
$ch = curl_init($http);
$header = [
'Content-type: application/json'
];
if ($method == 'GET' || $method == 'POST') {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
} else {
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiePath);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_COOKIE, $this->_cookie());
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiePath);
if ($method == 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
}
$body = curl_exec($ch);
curl_close($ch);
if (!in_array($method, ['GET', 'POST'])) {
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($body, 0, $header_size);
$in = explode("\n", $body);
foreach ($in as $i) {
if (stripos($i, 'x-ware-csrf-token') !== false) {
$inn = explode(',', $i);
return $inn[1];
}
}
return null;
}
return json_decode($body);
}
}

View File

@ -1 +1,4 @@
{"status":200,"message":"\u6210\u529f","data":{"sellerOrderResultDOS":[],"showVerify":false,"page":{"currentPage":2,"pageSize":10,"totalCount":0,"noMore":false},"canVerify":false,"canBatchDelivery":false,"canAsyncExportOrder":true},"success":true,"traceId":"EL2AgICAsoKsChjdAyDS_sLB8TEowpfGgQ8="} {"status":200,"message":"\u6210\u529f","data":{"sellerOrderResultDOS":[],"showVerify":false,"page":{"currentPage":2,"pageSize":10,"totalCount":0,"noMore":false},"canVerify":false,"canBatchDelivery":false,"canAsyncExportOrder":true},"success":true,"traceId":"EL2AgICAsoKsChjdAyDS_sLB8TEowpfGgQ8="}
ALTER TABLE `travel`.`orders`
MODIFY COLUMN `appointment_status` tinyint NOT NULL DEFAULT 0 COMMENT '是否已预约0-未预约1-已预约未处理2-已预约已处理)' AFTER `next_remind_time`;