编辑代码

<?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;     // U号租
    const CHANNEL_TYPE_ZUHAOWAN = 3;   // 租号玩

    /**
     * 定时任务入口
     * @param string|null $date 可选的指定日期,格式:Y-m-d
     * @param bool $force 是否强制重新统计(覆盖已有数据)
     * @return Response
     */
    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("自营渠道统计完成");

                // 统计U号租数据
                $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:创建的订单的总金额
        $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();

        // 下单-支付转化率:支付订单数/下单数 (存储时乘以10000)
        $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();

        // 完单率:完成订单数/支付订单数 (存储时乘以10000)
        $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_order_prices'));

        // 订单平均金额:完单总流水/完成订单数
        $avgOrderAmount = '0.00';
        if ($completedOrders > 0) {
            $avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
        }

        // 租赁总时长:已完成订单的总的租赁时长(保留1位小数,乘以10存储)
        $totalCompletedDurationRaw = (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('hire_real_times');

        // 将小数时长乘以10存储为整数
        $totalCompletedDuration = (int)bcmul($totalCompletedDurationRaw, '10', 0);

        // 订单平均时长:总时长/完成订单数(保留1位小数,乘以10存储)
        $avgOrderDuration = 0;
        if ($completedOrders > 0) {
            $avgDurationRaw = bcdiv($totalCompletedDurationRaw, (string)$completedOrders, 1);
            $avgOrderDuration = (int)bcmul($avgDurationRaw, '10', 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
    {
        // 下单数:创建订单的数量(自营:third_type为空)
        $createdOrders = (int)Order::suffix($suffix)
            ->whereBetween('create_time', [$dateStart, $dateEnd])
            ->where('third_type', '')
            ->whereIn('order_type', [1, 2])
            ->count();

        // GMV:创建的订单的总金额(自营)
        $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_order_prices'));

        // 订单平均金额:完单总流水/完成订单数
        $avgOrderAmount = '0.00';
        if ($completedOrders > 0) {
            $avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
        }

        // 租赁总时长:已完成订单的总的租赁时长(自营)
        $totalCompletedDurationRaw = (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('hire_real_times');

        // 将小数时长乘以10存储为整数
        $totalCompletedDuration = (int)bcmul($totalCompletedDurationRaw, '10', 0);

        // 订单平均时长:总时长/完成订单数
        $avgOrderDuration = 0;
        if ($completedOrders > 0) {
            $avgDurationRaw = bcdiv($totalCompletedDurationRaw, (string)$completedOrders, 1);
            $avgOrderDuration = (int)bcmul($avgDurationRaw, '10', 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
        ];
    }

    /**
     * 计算U号租数据
     */
    private function calculateUhaoZuData(string $statDate, string $dateStart, string $dateEnd, string $suffix): array
    {
        // 下单数:创建订单的数量(U号租:third_type='uhaozu')
        $createdOrders = (int)Order::suffix($suffix)
            ->whereBetween('create_time', [$dateStart, $dateEnd])
            ->where('third_type', 'uhaozu')
            ->whereIn('order_type', [1, 2])
            ->count();

        // GMV:创建的订单的总金额(U号租)
        $gmv = (string)Order::suffix($suffix)
            ->whereBetween('create_time', [$dateStart, $dateEnd])
            ->where('third_type', 'uhaozu')
            ->whereIn('order_type', [1, 2])
            ->sum('hire_original_prices');

        // 支付订单数:订单实际被支付的订单总数(U号租)
        $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);
        }

        // 完成订单数:订单状态是已完成的状态(U号租)
        $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);
        }

        // 完单总流水:已完成订单的用户实际支付的总金额(U号租)
        $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_order_prices'));

        // 订单平均金额:完单总流水/完成订单数
        $avgOrderAmount = '0.00';
        if ($completedOrders > 0) {
            $avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
        }

        // 租赁总时长:已完成订单的总的租赁时长(U号租)
        $totalCompletedDurationRaw = (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('hire_real_times');

        // 将小数时长乘以10存储为整数
        $totalCompletedDuration = (int)bcmul($totalCompletedDurationRaw, '10', 0);

        // 订单平均时长:总时长/完成订单数
        $avgOrderDuration = 0;
        if ($completedOrders > 0) {
            $avgDurationRaw = bcdiv($totalCompletedDurationRaw, (string)$completedOrders, 1);
            $avgOrderDuration = (int)bcmul($avgDurationRaw, '10', 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
    {
        // 下单数:创建订单的数量(租号玩:third_type='zuhaowan')
        $createdOrders = (int)Order::suffix($suffix)
            ->whereBetween('create_time', [$dateStart, $dateEnd])
            ->where('third_type', 'zuhaowan')
            ->whereIn('order_type', [1, 2])
            ->count();

        // GMV:创建的订单的总金额(租号玩)
        $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_order_prices'));

        // 订单平均金额:完单总流水/完成订单数
        $avgOrderAmount = '0.00';
        if ($completedOrders > 0) {
            $avgOrderAmount = bcdiv($totalCompletedRevenue, (string)$completedOrders, 2);
        }

        // 租赁总时长:已完成订单的总的租赁时长(租号玩)
        $totalCompletedDurationRaw = (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('hire_real_times');

        // 将小数时长乘以10存储为整数
        $totalCompletedDuration = (int)bcmul($totalCompletedDurationRaw, '10', 0);

        // 订单平均时长:总时长/完成订单数
        $avgOrderDuration = 0;
        if ($completedOrders > 0) {
            $avgDurationRaw = bcdiv($totalCompletedDurationRaw, (string)$completedOrders, 1);
            $avgOrderDuration = (int)bcmul($avgDurationRaw, '10', 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);
    }
}