<?php
declare(strict_types=1);
namespace app\crontab\stat;
use app\model\order\Order;
use app\model\stat\StatOrderData;
use support\Response;
use madong\utils\Json;
use think\facade\Db;
class CronOrderData
{
private array $logMessages = [];
const CHANNEL_TYPE_ALL = 0;
const CHANNEL_TYPE_SELF = 1;
const CHANNEL_TYPE_UHAOZU = 2;
const CHANNEL_TYPE_ZUHAOWAN = 3;
public function run(?string $date = null, bool $force = false): Response
{
try {
$this->logMessages = [];
$statDate = $date ?: date('Y-m-d', strtotime('-1 day'));
$currentTime = date('Y-m-d H:i:s');
$this->addLog("========== 订单数据统计开始 ==========");
$this->addLog("统计日期:{$statDate}");
$this->addLog("执行时间:{$currentTime}");
if ($force) {
$this->addLog("模式:强制重新统计");
}
if (!$force) {
$existCount = StatOrderData::where('stat_date', $statDate)->count();
if ($existCount > 0) {
$this->addLog("日期 {$statDate} 已统计过,跳过统计");
$this->printLogs();
return Json::success("数据已存在,跳过统计");
}
}
$dateStart = $statDate . ' 00:00:00';
$dateEnd = $statDate . ' 23:59:59';
$this->addLog("时间范围:{$dateStart} 至 {$dateEnd}");
Db::startTrans();
try {
if ($force) {
$deleteCount = StatOrderData::where('stat_date', $statDate)->delete();
if ($deleteCount > 0) {
$this->addLog("删除已有数据:{$deleteCount} 条");
}
}
$year = date('Y', strtotime($statDate));
$suffix = '_' . $year;
$allData = $this->calculateAllData($statDate, $dateStart, $dateEnd, $suffix);
$this->saveStatData($statDate, self::CHANNEL_TYPE_ALL, 0, $allData);
$this->addLog("全部渠道统计完成");
$selfData = $this->calculateSelfData($statDate, $dateStart, $dateEnd, $suffix);
$this->saveStatData($statDate, self::CHANNEL_TYPE_SELF, 0, $selfData);
$this->addLog("自营渠道统计完成");
$uhaoZuData = $this->calculateUhaoZuData($statDate, $dateStart, $dateEnd, $suffix);
$this->saveStatData($statDate, self::CHANNEL_TYPE_UHAOZU, 0, $uhaoZuData);
$this->addLog("U号租渠道统计完成");
$zuHaoWanData = $this->calculateZuHaoWanData($statDate, $dateStart, $dateEnd, $suffix);
$this->saveStatData($statDate, self::CHANNEL_TYPE_ZUHAOWAN, 0, $zuHaoWanData);
$this->addLog("租号玩渠道统计完成");
Db::commit();
$this->addLog("========== 统计汇总 ==========");
$this->addLog("全部渠道 - 下单数:{$allData['created_orders']},支付订单数:{$allData['paid_orders']},完成订单数:{$allData['completed_orders']}");
$this->addLog("自营渠道 - 下单数:{$selfData['created_orders']},支付订单数:{$selfData['paid_orders']},完成订单数:{$selfData['completed_orders']}");
$this->addLog("U号租 - 下单数:{$uhaoZuData['created_orders']},支付订单数:{$uhaoZuData['paid_orders']},完成订单数:{$uhaoZuData['completed_orders']}");
$this->addLog("租号玩 - 下单数:{$zuHaoWanData['created_orders']},支付订单数:{$zuHaoWanData['paid_orders']},完成订单数:{$zuHaoWanData['completed_orders']}");
$this->addLog("========== 订单数据统计完成 ==========");
$this->printLogs();
return Json::success("订单数据统计完成");
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
} catch (\Exception $e) {
$this->addLog("订单数据统计失败:{$e->getMessage()} 在第 {$e->getLine()} 行");
$this->printLogs();
return Json::fail("订单数据统计失败:" . $e->getMessage());
}
}
private function calculateAllData(string $statDate, string $dateStart, string $dateEnd, string $suffix): array
{
$createdOrders = (int)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->count();
$gmv = (string)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->sum('hire_original_prices');
$paidOrders = (int)Order::suffix($suffix)
->whereIn('hire_pay_status', [1, 4])
->whereBetween('create_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->count();
$orderPaymentConversionRate = '0.0000';
if ($createdOrders > 0) {
$orderPaymentConversionRate = bcdiv((string)$paidOrders, (string)$createdOrders, 4);
}
$completedOrders = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->count();
$completionRate = '0.0000';
if ($paidOrders > 0) {
$completionRate = bcdiv((string)$completedOrders, (string)$paidOrders, 4);
}
$totalCompletedRevenue = (string)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->sum(Db::raw('hire_real_pay_prices - hire_refund_prices'));
$avgOrderAmount = '0.00';
if ($completedOrders > 0) {
$avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
}
$totalCompletedDuration = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->whereIn('order_type', [1, 2])
->sum('hire_real_times');
$avgOrderDuration = 0;
if ($completedOrders > 0) {
$avgOrderDuration = (int)bcdiv((string)$totalCompletedDuration, (string)$completedOrders, 0);
}
return [
'created_orders' => $createdOrders,
'gmv' => $gmv,
'paid_orders' => $paidOrders,
'order_payment_conversion_rate' => (int)bcmul($orderPaymentConversionRate, '10000', 0),
'completed_orders' => $completedOrders,
'completion_rate' => (int)bcmul($completionRate, '10000', 0),
'total_completed_revenue' => $totalCompletedRevenue,
'avg_order_amount' => $avgOrderAmount,
'total_completed_duration' => $totalCompletedDuration,
'avg_order_duration' => $avgOrderDuration
];
}
private function calculateSelfData(string $statDate, string $dateStart, string $dateEnd, string $suffix): array
{
$createdOrders = (int)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->count();
$gmv = (string)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->sum('hire_original_prices');
$paidOrders = (int)Order::suffix($suffix)
->whereIn('hire_pay_status', [1, 4])
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->count();
$orderPaymentConversionRate = '0.0000';
if ($createdOrders > 0) {
$orderPaymentConversionRate = bcdiv((string)$paidOrders, (string)$createdOrders, 4);
}
$completedOrders = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->count();
$completionRate = '0.0000';
if ($paidOrders > 0) {
$completionRate = bcdiv((string)$completedOrders, (string)$paidOrders, 4);
}
$totalCompletedRevenue = (string)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->sum(Db::raw('hire_real_pay_prices - hire_refund_prices'));
$avgOrderAmount = '0.00';
if ($completedOrders > 0) {
$avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
}
$totalCompletedDuration = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', '')
->whereIn('order_type', [1, 2])
->sum('hire_real_times');
$avgOrderDuration = 0;
if ($completedOrders > 0) {
$avgOrderDuration = (int)bcdiv((string)$totalCompletedDuration, (string)$completedOrders, 0);
}
return [
'created_orders' => $createdOrders,
'gmv' => $gmv,
'paid_orders' => $paidOrders,
'order_payment_conversion_rate' => (int)bcmul($orderPaymentConversionRate, '10000', 0),
'completed_orders' => $completedOrders,
'completion_rate' => (int)bcmul($completionRate, '10000', 0),
'total_completed_revenue' => $totalCompletedRevenue,
'avg_order_amount' => $avgOrderAmount,
'total_completed_duration' => $totalCompletedDuration,
'avg_order_duration' => $avgOrderDuration
];
}
private function calculateUhaoZuData(string $statDate, string $dateStart, string $dateEnd, string $suffix): array
{
$createdOrders = (int)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->count();
$gmv = (string)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->sum('hire_original_prices');
$paidOrders = (int)Order::suffix($suffix)
->whereIn('hire_pay_status', [1, 4])
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->count();
$orderPaymentConversionRate = '0.0000';
if ($createdOrders > 0) {
$orderPaymentConversionRate = bcdiv((string)$paidOrders, (string)$createdOrders, 4);
}
$completedOrders = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->count();
$completionRate = '0.0000';
if ($paidOrders > 0) {
$completionRate = bcdiv((string)$completedOrders, (string)$paidOrders, 4);
}
$totalCompletedRevenue = (string)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->sum(Db::raw('hire_real_pay_prices - hire_refund_prices'));
$avgOrderAmount = '0.00';
if ($completedOrders > 0) {
$avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
}
$totalCompletedDuration = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'uhaozu')
->whereIn('order_type', [1, 2])
->sum('hire_real_times');
$avgOrderDuration = 0;
if ($completedOrders > 0) {
$avgOrderDuration = (int)bcdiv((string)$totalCompletedDuration, (string)$completedOrders, 0);
}
return [
'created_orders' => $createdOrders,
'gmv' => $gmv,
'paid_orders' => $paidOrders,
'order_payment_conversion_rate' => (int)bcmul($orderPaymentConversionRate, '10000', 0),
'completed_orders' => $completedOrders,
'completion_rate' => (int)bcmul($completionRate, '10000', 0),
'total_completed_revenue' => $totalCompletedRevenue,
'avg_order_amount' => $avgOrderAmount,
'total_completed_duration' => $totalCompletedDuration,
'avg_order_duration' => $avgOrderDuration
];
}
private function calculateZuHaoWanData(string $statDate, string $dateStart, string $dateEnd, string $suffix): array
{
$createdOrders = (int)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->count();
$gmv = (string)Order::suffix($suffix)
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->sum('hire_original_prices');
$paidOrders = (int)Order::suffix($suffix)
->whereIn('hire_pay_status', [1, 4])
->whereBetween('create_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->count();
$orderPaymentConversionRate = '0.0000';
if ($createdOrders > 0) {
$orderPaymentConversionRate = bcdiv((string)$paidOrders, (string)$createdOrders, 4);
}
$completedOrders = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->count();
$completionRate = '0.0000';
if ($paidOrders > 0) {
$completionRate = bcdiv((string)$completedOrders, (string)$paidOrders, 4);
}
$totalCompletedRevenue = (string)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->sum(Db::raw('hire_real_pay_prices - hire_refund_prices'));
$avgOrderAmount = '0.00';
if ($completedOrders > 0) {
$avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
}
$totalCompletedDuration = (int)Order::suffix($suffix)
->whereIn('order_status', [0, 1])
->whereIn('hire_pay_status', [1, 4])
->whereBetween('hire_actual_complete_time', [$dateStart, $dateEnd])
->where('third_type', 'zuhaowan')
->whereIn('order_type', [1, 2])
->sum('hire_real_times');
$avgOrderDuration = 0;
if ($completedOrders > 0) {
$avgOrderDuration = (int)bcdiv((string)$totalCompletedDuration, (string)$completedOrders, 0);
}
return [
'created_orders' => $createdOrders,
'gmv' => $gmv,
'paid_orders' => $paidOrders,
'order_payment_conversion_rate' => (int)bcmul($orderPaymentConversionRate, '10000', 0),
'completed_orders' => $completedOrders,
'completion_rate' => (int)bcmul($completionRate, '10000', 0),
'total_completed_revenue' => $totalCompletedRevenue,
'avg_order_amount' => $avgOrderAmount,
'total_completed_duration' => $totalCompletedDuration,
'avg_order_duration' => $avgOrderDuration
];
}
private function saveStatData(string $statDate, int $channelType, int $channelId, array $data): void
{
StatOrderData::create([
'stat_date' => $statDate,
'channel_type' => $channelType,
'channel_id' => $channelId,
'created_orders' => $data['created_orders'],
'gmv' => $data['gmv'],
'paid_orders' => $data['paid_orders'],
'order_payment_conversion_rate' => $data['order_payment_conversion_rate'],
'completed_orders' => $data['completed_orders'],
'completion_rate' => $data['completion_rate'],
'total_completed_revenue' => $data['total_completed_revenue'],
'avg_order_amount' => $data['avg_order_amount'],
'total_completed_duration' => $data['total_completed_duration'],
'avg_order_duration' => $data['avg_order_duration']
]);
}
private function addLog(string $message): void
{
$this->logMessages[] = $message;
}
private function printLogs(): void
{
$fullLog = implode("\n", $this->logMessages);
sea_log($fullLog);
}
}