travel/service/app/server/Tongcheng.php

279 lines
9.4 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\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}详情获取失败");
// }
$order->orderAmount = $order->orderAmount * 100;
$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 = $order->orderAmount/$order->productCount;
$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->code != 1000) {
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') {
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++;
Log::info("Killed process with PID: {$process['pid']}\n");
}
if ($processesKilled === 0) {
Log::info("No processes to kill.\n");
}
} else {
Log::info("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;
}
$res = json_decode($body);
if (!isset($res['code']) || $res['code'] != 1000) {
Log::info( "同城 body{$body} \n\n");
(new ThirdApiService())->weComNotice($this->os);
}
return $res;
}
}