YearDataController.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Jobs\Export\ExportYearDataJob;
  4. use App\Jobs\Import\ImportYearDataJob;
  5. use App\Models\Contract;
  6. use App\Models\File;
  7. use App\Models\MafOrder;
  8. use App\Models\Order;
  9. use App\Models\Product;
  10. use App\Models\ProductSKU;
  11. use App\Models\Reclamation;
  12. use App\Models\Schedule;
  13. use App\Models\Ttn;
  14. use Illuminate\Http\JsonResponse;
  15. use Illuminate\Http\RedirectResponse;
  16. use Illuminate\Http\Request;
  17. use Illuminate\Support\Facades\DB;
  18. use Illuminate\Support\Facades\Storage;
  19. use Illuminate\Support\Str;
  20. use Illuminate\View\View;
  21. class YearDataController extends Controller
  22. {
  23. public function index(): View
  24. {
  25. return view('year-data.index', [
  26. 'active' => 'year-data',
  27. 'years' => $this->getAvailableYears(),
  28. 'exports' => $this->getRecentExports(),
  29. ]);
  30. }
  31. public function stats(Request $request): JsonResponse
  32. {
  33. $year = (int) $request->input('year');
  34. if ($year < 2020 || $year > 2100) {
  35. return response()->json(['error' => 'Некорректный год'], 422);
  36. }
  37. $stats = $this->collectStats($year);
  38. return response()->json([
  39. 'year' => $year,
  40. 'stats' => $stats,
  41. 'total' => array_sum($stats),
  42. ]);
  43. }
  44. public function export(Request $request): RedirectResponse
  45. {
  46. $year = (int) $request->input('year');
  47. if ($year < 2020 || $year > 2100) {
  48. return redirect()->back()->with('danger', 'Некорректный год');
  49. }
  50. ExportYearDataJob::dispatch($year, $request->user()->id);
  51. return redirect()->route('year-data.index')
  52. ->with('success', "Экспорт данных за {$year} год запущен. Вы получите уведомление о завершении.");
  53. }
  54. public function import(Request $request): RedirectResponse
  55. {
  56. $request->validate([
  57. 'year' => 'required|integer|min:2020|max:2100',
  58. 'import_file' => 'required|file|mimes:zip',
  59. ]);
  60. $year = (int) $request->input('year');
  61. $clearExisting = $request->boolean('clear_existing');
  62. // Сохраняем загруженный файл
  63. $file = $request->file('import_file');
  64. $path = 'import/year_data/' . Str::uuid() . '.zip';
  65. Storage::disk('public')->put($path, $file->getContent());
  66. $fullPath = storage_path('app/public/' . $path);
  67. ImportYearDataJob::dispatch($fullPath, $year, $request->user()->id, $clearExisting);
  68. $message = "Импорт данных за {$year} год запущен.";
  69. if ($clearExisting) {
  70. $message .= " Существующие данные будут очищены.";
  71. }
  72. $message .= " Вы получите уведомление о завершении.";
  73. return redirect()->route('year-data.index')->with('success', $message);
  74. }
  75. public function download(File $file)
  76. {
  77. if (!$file->path || !Storage::disk('public')->exists($file->path)) {
  78. abort(404, 'Файл не найден');
  79. }
  80. return Storage::disk('public')->download($file->path, $file->original_name);
  81. }
  82. private function getAvailableYears(): array
  83. {
  84. $years = [];
  85. $orderYears = Order::withoutGlobalScopes()->withTrashed()
  86. ->selectRaw('DISTINCT year')
  87. ->pluck('year')
  88. ->toArray();
  89. $productYears = Product::withoutGlobalScopes()->withTrashed()
  90. ->selectRaw('DISTINCT year')
  91. ->pluck('year')
  92. ->toArray();
  93. $mafOrderYears = MafOrder::withoutGlobalScopes()->withTrashed()
  94. ->selectRaw('DISTINCT year')
  95. ->pluck('year')
  96. ->toArray();
  97. $contractYears = Contract::selectRaw('DISTINCT year')
  98. ->pluck('year')
  99. ->toArray();
  100. $ttnYears = Ttn::selectRaw('DISTINCT year')
  101. ->pluck('year')
  102. ->toArray();
  103. $years = array_unique(array_merge($orderYears, $productYears, $mafOrderYears, $contractYears, $ttnYears));
  104. rsort($years);
  105. return $years;
  106. }
  107. private function getRecentExports(): \Illuminate\Database\Eloquent\Collection
  108. {
  109. return File::where('original_name', 'like', 'year_data_%')
  110. ->where('mime_type', 'application/zip')
  111. ->orderBy('created_at', 'desc')
  112. ->limit(10)
  113. ->get();
  114. }
  115. private function collectStats(int $year): array
  116. {
  117. $orderIds = Order::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id');
  118. $productIds = Product::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id');
  119. $mafOrderIds = MafOrder::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id');
  120. $productSkuIds = ProductSKU::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id');
  121. $reclamationCount = Reclamation::whereIn('order_id', $orderIds)->count();
  122. $fileIds = $this->collectFileIds($year, $orderIds, $productIds, $productSkuIds);
  123. return [
  124. 'Заказы (Orders)' => $orderIds->count(),
  125. 'Заказы МАФ (MafOrders)' => $mafOrderIds->count(),
  126. 'Продукты (Products)' => $productIds->count(),
  127. 'SKU продуктов (ProductSKU)' => $productSkuIds->count(),
  128. 'Рекламации (Reclamations)' => $reclamationCount,
  129. 'Расписания (Schedules)' => Schedule::whereIn('order_id', $orderIds)->count(),
  130. 'ТТН (Ttns)' => Ttn::where('year', $year)->count(),
  131. 'Контракты (Contracts)' => Contract::where('year', $year)->count(),
  132. 'Файлы (Files)' => $fileIds->count(),
  133. ];
  134. }
  135. private function collectFileIds(int $year, $orderIds, $productIds, $productSkuIds): \Illuminate\Support\Collection
  136. {
  137. $fileIds = collect();
  138. $fileIds = $fileIds->merge(
  139. DB::table('order_photo')->whereIn('order_id', $orderIds)->pluck('file_id')
  140. );
  141. $fileIds = $fileIds->merge(
  142. DB::table('order_document')->whereIn('order_id', $orderIds)->pluck('file_id')
  143. );
  144. $fileIds = $fileIds->merge(
  145. DB::table('order_statement')->whereIn('order_id', $orderIds)->pluck('file_id')
  146. );
  147. $reclamationIds = Reclamation::whereIn('order_id', $orderIds)->pluck('id');
  148. $fileIds = $fileIds->merge(
  149. DB::table('reclamation_photo_before')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id')
  150. );
  151. $fileIds = $fileIds->merge(
  152. DB::table('reclamation_photo_after')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id')
  153. );
  154. $fileIds = $fileIds->merge(
  155. DB::table('reclamation_document')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id')
  156. );
  157. $fileIds = $fileIds->merge(
  158. DB::table('reclamation_act')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id')
  159. );
  160. $fileIds = $fileIds->merge(
  161. Product::withoutGlobalScopes()->withTrashed()
  162. ->whereIn('id', $productIds)
  163. ->whereNotNull('certificate_id')
  164. ->pluck('certificate_id')
  165. );
  166. $fileIds = $fileIds->merge(
  167. ProductSKU::withoutGlobalScopes()->withTrashed()
  168. ->whereIn('id', $productSkuIds)
  169. ->whereNotNull('passport_id')
  170. ->pluck('passport_id')
  171. );
  172. $fileIds = $fileIds->merge(
  173. Ttn::where('year', $year)->whereNotNull('file_id')->pluck('file_id')
  174. );
  175. return $fileIds->unique();
  176. }
  177. }