TransferStatsCommand.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. namespace App\Module\Transfer\Commands;
  3. use App\Module\Transfer\Models\TransferOrder;
  4. use App\Module\Transfer\Models\TransferApp;
  5. use App\Module\Transfer\Enums\TransferStatus;
  6. use App\Module\Transfer\Enums\TransferType;
  7. use Illuminate\Console\Command;
  8. use Illuminate\Support\Facades\DB;
  9. /**
  10. * Transfer统计命令
  11. */
  12. class TransferStatsCommand extends Command
  13. {
  14. /**
  15. * 命令签名
  16. */
  17. protected $signature = 'transfer:stats
  18. {--period=today : 统计周期 (today|yesterday|week|month)}
  19. {--app= : 指定应用ID}
  20. {--export= : 导出到文件}';
  21. /**
  22. * 命令描述
  23. */
  24. protected $description = '显示Transfer模块的统计信息';
  25. /**
  26. * 执行命令
  27. */
  28. public function handle(): int
  29. {
  30. $period = $this->option('period');
  31. $appId = $this->option('app');
  32. $exportFile = $this->option('export');
  33. $this->info("Transfer模块统计报告 - {$period}");
  34. $this->line(str_repeat('=', 60));
  35. try {
  36. // 获取时间范围
  37. [$startDate, $endDate] = $this->getDateRange($period);
  38. $this->info("统计时间: {$startDate} 至 {$endDate}");
  39. $this->line('');
  40. // 基础统计
  41. $this->showBasicStats($startDate, $endDate, $appId);
  42. // 按应用统计
  43. $this->showAppStats($startDate, $endDate, $appId);
  44. // 按状态统计
  45. $this->showStatusStats($startDate, $endDate, $appId);
  46. // 按小时统计(仅当日)
  47. if ($period === 'today') {
  48. $this->showHourlyStats($startDate, $endDate, $appId);
  49. }
  50. // 导出数据
  51. if ($exportFile) {
  52. $this->exportStats($startDate, $endDate, $appId, $exportFile);
  53. }
  54. return 0;
  55. } catch (\Exception $e) {
  56. $this->error("统计失败: " . $e->getMessage());
  57. return 1;
  58. }
  59. }
  60. /**
  61. * 获取日期范围
  62. */
  63. private function getDateRange(string $period): array
  64. {
  65. return match ($period) {
  66. 'today' => [
  67. now()->startOfDay()->toDateTimeString(),
  68. now()->endOfDay()->toDateTimeString()
  69. ],
  70. 'yesterday' => [
  71. now()->subDay()->startOfDay()->toDateTimeString(),
  72. now()->subDay()->endOfDay()->toDateTimeString()
  73. ],
  74. 'week' => [
  75. now()->startOfWeek()->toDateTimeString(),
  76. now()->endOfWeek()->toDateTimeString()
  77. ],
  78. 'month' => [
  79. now()->startOfMonth()->toDateTimeString(),
  80. now()->endOfMonth()->toDateTimeString()
  81. ],
  82. default => throw new \InvalidArgumentException("无效的统计周期: {$period}")
  83. };
  84. }
  85. /**
  86. * 显示基础统计
  87. */
  88. private function showBasicStats(string $startDate, string $endDate, ?string $appId): void
  89. {
  90. $query = TransferOrder::whereBetween('created_at', [$startDate, $endDate]);
  91. if ($appId) {
  92. $query->where('transfer_app_id', $appId);
  93. }
  94. $totalOrders = $query->count();
  95. $totalAmount = $query->sum('amount');
  96. $inOrders = (clone $query)->where('type', TransferType::IN)->count();
  97. $outOrders = (clone $query)->where('type', TransferType::OUT)->count();
  98. $inAmount = (clone $query)->where('type', TransferType::IN)->sum('amount');
  99. $outAmount = (clone $query)->where('type', TransferType::OUT)->sum('amount');
  100. $completedOrders = (clone $query)->where('status', TransferStatus::COMPLETED)->count();
  101. $failedOrders = (clone $query)->where('status', TransferStatus::FAILED)->count();
  102. $this->info('基础统计:');
  103. $this->table(
  104. ['指标', '数值'],
  105. [
  106. ['总订单数', number_format($totalOrders)],
  107. ['总金额', number_format($totalAmount, 2)],
  108. ['转入订单', number_format($inOrders)],
  109. ['转出订单', number_format($outOrders)],
  110. ['转入金额', number_format($inAmount, 2)],
  111. ['转出金额', number_format($outAmount, 2)],
  112. ['成功订单', number_format($completedOrders)],
  113. ['失败订单', number_format($failedOrders)],
  114. ['成功率', $totalOrders > 0 ? number_format($completedOrders / $totalOrders * 100, 2) . '%' : '0%'],
  115. ]
  116. );
  117. $this->line('');
  118. }
  119. /**
  120. * 显示按应用统计
  121. */
  122. private function showAppStats(string $startDate, string $endDate, ?string $appId): void
  123. {
  124. $query = DB::table('transfer_orders as o')
  125. ->join('transfer_apps as a', 'o.transfer_app_id', '=', 'a.id')
  126. ->whereBetween('o.created_at', [$startDate, $endDate])
  127. ->select([
  128. 'a.title as app_name',
  129. DB::raw('COUNT(*) as total_orders'),
  130. DB::raw('SUM(o.amount) as total_amount'),
  131. DB::raw('SUM(CASE WHEN o.type = 1 THEN 1 ELSE 0 END) as in_orders'),
  132. DB::raw('SUM(CASE WHEN o.type = 2 THEN 1 ELSE 0 END) as out_orders'),
  133. DB::raw('SUM(CASE WHEN o.status = 100 THEN 1 ELSE 0 END) as completed_orders'),
  134. ])
  135. ->groupBy('a.id', 'a.title')
  136. ->orderBy('total_amount', 'desc');
  137. if ($appId) {
  138. $query->where('a.id', $appId);
  139. }
  140. $stats = $query->get();
  141. if ($stats->isNotEmpty()) {
  142. $this->info('按应用统计:');
  143. $this->table(
  144. ['应用名称', '总订单', '总金额', '转入', '转出', '成功', '成功率'],
  145. $stats->map(function ($stat) {
  146. $successRate = $stat->total_orders > 0
  147. ? number_format($stat->completed_orders / $stat->total_orders * 100, 1) . '%'
  148. : '0%';
  149. return [
  150. $stat->app_name,
  151. number_format($stat->total_orders),
  152. number_format($stat->total_amount, 2),
  153. number_format($stat->in_orders),
  154. number_format($stat->out_orders),
  155. number_format($stat->completed_orders),
  156. $successRate,
  157. ];
  158. })
  159. );
  160. $this->line('');
  161. }
  162. }
  163. /**
  164. * 显示按状态统计
  165. */
  166. private function showStatusStats(string $startDate, string $endDate, ?string $appId): void
  167. {
  168. $query = TransferOrder::whereBetween('created_at', [$startDate, $endDate])
  169. ->select([
  170. 'status',
  171. DB::raw('COUNT(*) as count'),
  172. DB::raw('SUM(amount) as amount')
  173. ])
  174. ->groupBy('status')
  175. ->orderBy('status');
  176. if ($appId) {
  177. $query->where('transfer_app_id', $appId);
  178. }
  179. $stats = $query->get();
  180. if ($stats->isNotEmpty()) {
  181. $this->info('按状态统计:');
  182. $this->table(
  183. ['状态', '订单数', '金额'],
  184. $stats->map(function ($stat) {
  185. $status = TransferStatus::from($stat->status);
  186. return [
  187. $status->getDescription(),
  188. number_format($stat->count),
  189. number_format($stat->amount, 2),
  190. ];
  191. })
  192. );
  193. $this->line('');
  194. }
  195. }
  196. /**
  197. * 显示按小时统计
  198. */
  199. private function showHourlyStats(string $startDate, string $endDate, ?string $appId): void
  200. {
  201. $query = TransferOrder::whereBetween('created_at', [$startDate, $endDate])
  202. ->select([
  203. DB::raw('HOUR(created_at) as hour'),
  204. DB::raw('COUNT(*) as count'),
  205. DB::raw('SUM(amount) as amount')
  206. ])
  207. ->groupBy(DB::raw('HOUR(created_at)'))
  208. ->orderBy('hour');
  209. if ($appId) {
  210. $query->where('transfer_app_id', $appId);
  211. }
  212. $stats = $query->get();
  213. if ($stats->isNotEmpty()) {
  214. $this->info('按小时统计:');
  215. $this->table(
  216. ['小时', '订单数', '金额'],
  217. $stats->map(function ($stat) {
  218. return [
  219. sprintf('%02d:00', $stat->hour),
  220. number_format($stat->count),
  221. number_format($stat->amount, 2),
  222. ];
  223. })
  224. );
  225. $this->line('');
  226. }
  227. }
  228. /**
  229. * 导出统计数据
  230. */
  231. private function exportStats(string $startDate, string $endDate, ?string $appId, string $filename): void
  232. {
  233. $this->info("正在导出统计数据到 {$filename}...");
  234. // 这里可以实现CSV或Excel导出逻辑
  235. $this->info('导出功能待实现');
  236. }
  237. }