MemberDailyReport.php 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Services\MemberDailyReportService;
  4. use Carbon\Carbon;
  5. use Illuminate\Console\Command;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Schema;
  8. class MemberDailyReport extends Command
  9. {
  10. protected $signature = 'report:member-daily
  11. {date? : 结束日期 Y-m-d,默认今天}
  12. {--days=1 : 从 date 往前共重算几天(最多 31,定时任务用)}
  13. {--from= : 补算历史:从这天(Y-m-d)重算到 date,不限天数}
  14. {--all : 补算全部历史:从最早有数据的一天重算到 date}';
  15. protected $description = '生成会员每日盈亏报表(补算历史用 --from 或 --all)';
  16. public function handle(MemberDailyReportService $reports): int
  17. {
  18. $end = $this->date((string) ($this->argument('date') ?: now()->toDateString()));
  19. if (!$end) return self::FAILURE;
  20. if ($this->option('all')) {
  21. $start = $this->earliestDate();
  22. if ($start === null) {
  23. $this->info('没有任何会员数据,无需补算');
  24. return self::SUCCESS;
  25. }
  26. $start = Carbon::parse($start)->startOfDay();
  27. } elseif ($this->option('from')) {
  28. $start = $this->date((string) $this->option('from'));
  29. if (!$start) return self::FAILURE;
  30. } else {
  31. $start = $end->copy()->subDays(max(1, min(31, (int) $this->option('days'))) - 1);
  32. }
  33. if ($start->gt($end)) {
  34. $this->error('开始日期不能晚于结束日期');
  35. return self::FAILURE;
  36. }
  37. $total = (int) $start->diffInDays($end) + 1;
  38. if ($total > 1) $this->info("重算 {$start->toDateString()} ~ {$end->toDateString()},共 {$total} 天");
  39. $rows = 0;
  40. // 从最近的一天往前算:补算过程中最新的数据先可用
  41. for ($day = $end->copy(), $i = 1; $day->gte($start); $day->subDay(), ++$i) {
  42. $count = $reports->refresh($day->toDateString());
  43. $rows += $count;
  44. $this->info(($total > 1 ? "[{$i}/{$total}] " : '') . $day->toDateString() . ':' . $count . ' 行');
  45. }
  46. if ($total > 1) $this->info("完成,共写入 {$rows} 行");
  47. return self::SUCCESS;
  48. }
  49. private function date(string $value): ?Carbon
  50. {
  51. $parsed = Carbon::createFromFormat('!Y-m-d', $value);
  52. if (!$parsed || $parsed->format('Y-m-d') !== $value) {
  53. $this->error('日期必须是有效的 Y-m-d:' . $value);
  54. return null;
  55. }
  56. return $parsed;
  57. }
  58. /** 最早有会员数据的日期:会员注册、充值、提现、三方充提、资金流水、已结算注单中最早的一天。 */
  59. private function earliestDate(): ?string
  60. {
  61. $dates = [];
  62. foreach ([['users', 'created_at'], ['recharges', 'created_at'], ['withdraws', 'created_at'], ['payment_orders', 'created_at'],
  63. ['balance_logs', 'created_at'], ['share_bet_facts', 'settled_at']] as [$table, $column]) {
  64. if (!Schema::hasTable($table)) continue;
  65. $min = DB::table($table)->min($column);
  66. if ($min !== null && $min !== '') $dates[] = substr((string) $min, 0, 10);
  67. }
  68. return $dates === [] ? null : min($dates);
  69. }
  70. }