diff --git a/app/Http/Controllers/Admin/ParkingLicensePlateController.php b/app/Http/Controllers/Admin/ParkingLicensePlateController.php index 5cf1367..d223bee 100644 --- a/app/Http/Controllers/Admin/ParkingLicensePlateController.php +++ b/app/Http/Controllers/Admin/ParkingLicensePlateController.php @@ -3,15 +3,20 @@ namespace App\Http\Controllers\Admin; use App\Exceptions\CustomException; +use App\Exports\ParkingLicensePlateImportTemplateExport; +use App\Imports\ParkingLicensePlateImport; use App\Models\ParkingLicensePlate; use App\Services\ApiResponseService; use App\Services\ParkingLicensePlateService; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; +use Maatwebsite\Excel\Facades\Excel; use Psr\SimpleCache\InvalidArgumentException; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class ParkingLicensePlateController extends BaseController { @@ -174,6 +179,95 @@ class ParkingLicensePlateController extends BaseController } } + /** + * @param $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function clear($id): JsonResponse + { + try { + $this->validateId($id, ParkingLicensePlate::class); + $this->LicensePlateService->clearModel($id); + return $this->responseService->success( + null, + __('controller.license_plate.clear_success') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.license_plate.clear_failed') . ':' + . $e->getMessage() + ); + } + } + + /** + * @return BinaryFileResponse + */ + public function importTemplate(): BinaryFileResponse + { + return Excel::download( + new ParkingLicensePlateImportTemplateExport(), + __('exports.license_plate.import_template') . time() .'.xlsx' + ); + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ValidationException + */ + public function import(Request $request): JsonResponse + { + try { + // 1. 验证上传的文件 + $request->validate([ + 'file' => 'required|mimes:xlsx,xls,csv|max:2048' // 限制文件类型和大小 + ]); + $validator = Validator::make($request->all(), [ + 'file' => 'required|mimes:xlsx,xls,csv|max:2048' + ], [ + 'file.required' => __('validation.admin_list_vip.file_empty'), + 'file.mimes' => __('validation.admin_list_vip.file_mimes'), + 'file.max' => __('validation.admin_list_vip.file_max'), + ]); + if ($validator->fails()) { + throw new ValidationException($validator); + } + + // 2. 获取上传的文件 + $file = $request->file('file'); + + // 3. 正确的做法:先存储文件,再获取绝对路径进行导入 + // 将文件存储到 storage/app/imports 目录下 + $path = $file->store('imports'); + + // 4. 执行导入(使用存储后的绝对路径) + // storage_path('app') 获取 storage/app 的绝对路径 + Excel::import( + new ParkingLicensePlateImport(), + storage_path('app/' . $path) + ); + + // 5. (可选)导入完成后删除临时文件 + Storage::delete($path); + + return $this->responseService->success( + __('controller.import.success') + ); + } catch (ValidationException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.admin_vip_list.import_failed') . ':' + . $e->getMessage() + ); + } + } + /** * @return JsonResponse * @throws InvalidArgumentException diff --git a/app/Imports/ParkingVipListImport.php b/app/Imports/ParkingVipListImport.php index 07a02c8..7df1611 100644 --- a/app/Imports/ParkingVipListImport.php +++ b/app/Imports/ParkingVipListImport.php @@ -3,12 +3,24 @@ namespace App\Imports; use App\Models\ParkingVipList; +use App\Services\OperationLogService; use Illuminate\Support\Facades\Auth; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithHeadingRow; class ParkingVipListImport implements ToModel, WithHeadingRow { + + /** + * @var OperationLogService + */ + private OperationLogService $logService; + + public function __construct() + { + $this->logService = new OperationLogService(); + } + /** * @param array $row * @return ParkingVipList @@ -19,11 +31,13 @@ class ParkingVipListImport implements ToModel, WithHeadingRow if (!ParkingVipList::query()->where('license', $license) ->exists() ) { - return new ParkingVipList([ + $model = new ParkingVipList([ 'license' => $license, 'user_id' => Auth::guard('sanctum')->user()['id'], 'created_at' => get_datetime() ]); + $this->logService->logCreated($model, '创建VIP名单'); + return $model; } break; } diff --git a/app/Services/ParkingLicensePlateService.php b/app/Services/ParkingLicensePlateService.php index 7c15036..1274445 100644 --- a/app/Services/ParkingLicensePlateService.php +++ b/app/Services/ParkingLicensePlateService.php @@ -121,4 +121,33 @@ class ParkingLicensePlateService } } + public function clearModel($space_type_id): bool + { + try { + DB::beginTransaction(); + $oldLicensePlate = ParkingLicensePlate::query()->where( + 'space_type_id', + $space_type_id + ) + ->select()->get()->toArray(); + if (empty($oldLicensePlate)) { + throw new Exception(__('service.license_plate.not_clear')); + } + $this->logService->logDeletedData( + new ParkingLicensePlate(), + '删除车牌号码', + $oldLicensePlate + ); + + ParkingLicensePlate::query()->where('space_type_id', $space_type_id) + ->delete(); + + DB::commit(); + return true; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + } diff --git a/resources/lang/zh-CN/controller.php b/resources/lang/zh-CN/controller.php index d8e1968..fe1772d 100644 --- a/resources/lang/zh-CN/controller.php +++ b/resources/lang/zh-CN/controller.php @@ -21,10 +21,13 @@ return [ 'report_cycle' => '上报周期', ], 'upload' => [ - 'empty_file' => '没有文件上传', + 'empty_file' => '没有文件上传', 'empty_image' => '没有图片上传' ], 'import' => [ 'success' => '导入成功' + ], + 'license_plate' => [ + 'clear_success' => '清除成功' ] ]; diff --git a/resources/lang/zh-CN/exception.php b/resources/lang/zh-CN/exception.php index 385dc89..3fdaccd 100644 --- a/resources/lang/zh-CN/exception.php +++ b/resources/lang/zh-CN/exception.php @@ -55,6 +55,7 @@ return [ 'license_plate' => [ 'create_failed' => '创建车牌号码失败', 'update_failed' => '更新车牌号码失败', - 'destroy_failed' => '删除车牌号码失败' + 'destroy_failed' => '删除车牌号码失败', + 'clear_failed' => '清除车牌号码失败' ] ]; diff --git a/resources/lang/zh-CN/exports.php b/resources/lang/zh-CN/exports.php index 2e64228..f405d6b 100644 --- a/resources/lang/zh-CN/exports.php +++ b/resources/lang/zh-CN/exports.php @@ -8,5 +8,8 @@ return [ 'global' => [ 'index' => '序号', 'admin' => '操作员' + ], + 'license_plate' => [ + 'import_template' => '车牌管理导入模板' ] ]; diff --git a/resources/lang/zh-CN/service.php b/resources/lang/zh-CN/service.php index 8e11f22..82696e7 100644 --- a/resources/lang/zh-CN/service.php +++ b/resources/lang/zh-CN/service.php @@ -32,6 +32,7 @@ return [ 'license_exists' => '请先清空车牌,即可删除' ], 'license_plate' => [ - 'number_exists' => '车牌号码已存在' + 'number_exists' => '车牌号码已存在', + 'not_clear' => '没有车牌需要清除' ] ]; diff --git a/routes/admin/api.php b/routes/admin/api.php index 0b958c0..7db8c39 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -45,6 +45,8 @@ Route::group(['prefix' => 'admin'], function () { Route::post('/licensePlate', [ParkingLicensePlateController::class, 'store']); Route::put('/licensePlate/{id}', [ParkingLicensePlateController::class, 'update']); Route::delete('/licensePlate/{id}', [ParkingLicensePlateController::class, 'destroy']); + Route::get('/licensePlate/clear/{id}', [ParkingLicensePlateController::class, 'clear']); + Route::post('/licensePlate/import', [ParkingLicensePlateController::class, 'import']); Route::get('/licensePlate/rule', [ParkingLicensePlateController::class, 'rule']); // 车位属性管理 Route::get('/spaceAttr', [ParkingSpaceAttributesController::class, 'index']); @@ -109,4 +111,5 @@ Route::group(['prefix' => 'admin'], function () { // 导出 Route::get('/vipList/import_template', [VipListController::class, 'importTemplate']); Route::get('/vipList/export', [VipListController::class, 'export']); + Route::get('/licensePlate/import_template', [ParkingLicensePlateController::class, 'importTemplate']); });