| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- <?php
- namespace plugin\saiadmin\app\service;
- use DateTimeImmutable;
- /** Statistics::business_device_data 的经营口径;未知成本/有效分成不按零处理。 */
- class DeviceOperationMetrics
- {
- public static function calculate(array $row, array $daily, DateTimeImmutable $now): array
- {
- $amount = (float) $row['total_amount'];
- $today = (float) $row['today_amount'];
- $yesterday = (float) $row['yesterday_amount'];
- $rate = isset($row['merchant_rate']) ? (float) $row['merchant_rate'] : null;
- $managerRate = isset($row['manager_rate']) ? (float) $row['manager_rate'] : null;
- $cost = isset($row['cost_price']) ? (float) $row['cost_price'] : null;
- $started = empty($row['bind_time']) ? null : new DateTimeImmutable($row['bind_time']);
- $days = $started && $started <= $now
- ? (int) $started->setTime(0, 0)->diff($now->setTime(0, 0))->days + 1 : 0;
- $average = $days > 0 ? $amount / $days : null;
- $performance = self::performance($yesterday, $average ?? 0.0);
- $recovery = self::recovery($cost, $managerRate, $started, $days, $daily);
- return array_merge($row, [
- 'id' => (int) $row['id'],
- 'goods_type' => (int) $row['goods_type'],
- 'manage_agent_id' => (int) $row['manage_agent_id'],
- 'merchant_id' => (int) ($row['merchant_id'] ?? 0),
- 'total_amount' => round($amount, 2),
- 'today_amount' => round($today, 2),
- 'yesterday_amount' => round($yesterday, 2),
- 'total_orders' => (int) $row['total_orders'],
- 'today_orders' => (int) $row['today_orders'],
- 'yesterday_orders' => (int) $row['yesterday_orders'],
- 'paid_orders' => (int) $row['paid_orders'],
- 'unit_price' => (int) $row['paid_orders'] > 0 ? round($amount / (int) $row['paid_orders'], 4) : 0,
- 'merchant_rate' => $rate,
- 'manager_rate' => $managerRate,
- 'cost_price' => $cost,
- 'total_profit' => self::profit($amount, $rate),
- 'today_profit' => self::profit($today, $rate),
- 'yesterday_profit' => self::profit($yesterday, $rate),
- // 旧页面“盈利率”实际为净交易额 / 成本,并非利润率。
- 'profit_rate_value' => $cost !== null && $cost > 0 ? round($amount / $cost * 100, 4) : null,
- 'daily_growth' => $yesterday > 0 ? round(($today - $yesterday) / $yesterday * 100, 2) : null,
- 'operated_days' => $days,
- 'daily_average_amount' => $average === null ? null : round($average, 4),
- ], $recovery, $performance);
- }
- private static function profit(float $amount, ?float $rate): ?float
- {
- // 无交易额即可确定收益为 0;有交易额但缺分成时保持未知。
- if ($amount === 0.0) {
- return 0.0;
- }
- return $rate === null ? null : round($amount * $rate / 100, 4);
- }
- private static function performance(float $yesterday, float $average): array
- {
- $score = $average > 0 ? $yesterday / $average : 0;
- $labels = ['最差', '差', '一般', '中等', '好', '良', '优良', '优秀'];
- $grade = 7;
- foreach ([0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2] as $index => $threshold) {
- if ($score <= $threshold) {
- $grade = $index;
- break;
- }
- }
- return ['performance_grade' => $grade, 'today_performance' => $labels[$grade]];
- }
- /** 按日期正序找首次回本日,收入查询已排除本次铺货之前的订单。 */
- private static function recovery(?float $cost, ?float $rate, ?DateTimeImmutable $started, int $days, array $daily): array
- {
- $result = ['is_cost_recovery' => null, 'cost_recovery_date' => null, 'cost_recovery_days' => null];
- if ($cost === null || $rate === null || $started === null || $days <= 0) {
- return $result;
- }
- $result['is_cost_recovery'] = false;
- $recoveryDate = $cost === 0.0 ? $started->format('Y-m-d') : null;
- $cumulative = 0.0;
- foreach ($daily as $day) {
- if ($recoveryDate !== null) {
- break;
- }
- if ($day['stat_date'] < $started->format('Y-m-d')) {
- continue;
- }
- $cumulative += (float) $day['amount'] * $rate / 100;
- if (round($cumulative, 4) >= $cost) {
- $recoveryDate = $day['stat_date'];
- }
- }
- if ($recoveryDate !== null) {
- $result['is_cost_recovery'] = true;
- $result['cost_recovery_date'] = $recoveryDate;
- $result['cost_recovery_days'] = (int) $started->setTime(0, 0)->diff(new DateTimeImmutable($recoveryDate))->days + 1;
- }
- return $result;
- }
- }
|