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; } }