diff --git a/app/Http/Controllers/Admin/ParkingInformationController.php b/app/Http/Controllers/Admin/ParkingInformationController.php new file mode 100644 index 0000000..ed8fe1e --- /dev/null +++ b/app/Http/Controllers/Admin/ParkingInformationController.php @@ -0,0 +1,230 @@ +service = $service; + } + + /** + * Display a listing of the resource. + */ + public function index(Request $request): JsonResponse + { + try { + $query = ParkingInformation::query(); + + // 车位号码搜索 + if ($request->has('parking_space_number')) { + $parking_space_number = $request->input('parking_space_number'); + if ($parking_space_number) { + $space_ids = ParkingSpace::getId($parking_space_number); + if ($space_ids) { + $query->whereIn('space_id', $space_ids); + } else { + $query->where('id', 0); + } + } + } + + if ($request->has('license_plate')) { + $license_plate = $request->input('license_plate'); + if ($license_plate) { + $license_plate_ids = ParkingLicensePlate::getId( + $license_plate + ); + if ($license_plate_ids) { + $query->whereIn('license_plate_id', $license_plate_ids); + } else { + $query->where('id', 0); + } + } + } + + if ($request->has('entry_start_date')) { + $entry_start_date = $request->input('entry_start_date'); + if ($entry_start_date && strtotime($entry_start_date)) { + $query->where('entry_time', '>=', $entry_start_date); + } + } + + if ($request->has('entry_end_date')) { + $entry_end_date = $request->input('entry_end_date'); + if ($entry_end_date && strtotime($entry_end_date)) { + $query->where('entry_time', '<=', $entry_end_date); + } + } + + if ($request->has('parking_space_type')) { + $type = $request->input('parking_space_type'); + if ($type) { + $query->where('space_type_id', '=', $type); + } + } + + // 分页 + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + + $total = $query->count(); + $items = $query->latest()->forPage($page, $perPage)->select() + ->get()->each(function ($item) { + $item['floor_region_name'] = AdminFloorRegion::getName( + $item['floor_region_id'] + ); + $item['parking_space'] = ParkingSpace::getNumber( + $item['space_id'] + ); + $item['license_plate'] = ParkingLicensePlate::getNumber( + $item['license_plate_id'] + ); + $item['parking_space_type'] = ParkingSpaceType::getName( + $item['space_type_id'] + ); + unset($item['floor_region_id'], $item['space_id'], $item['license_plate_id'], $item['space_type_id']); + return $item; + }); + + return $this->responseService->success([ + 'items' => $items, + 'total' => $total, + 'page' => $page, + 'per_page' => $perPage, + 'last_page' => ceil($total / $perPage), + ]); + } catch (Exception $e) { + $m_prefix = __('exception.exception_handler.resource'); + return $this->responseService->systemError( + $m_prefix . ':' . $e->getMessage() + ); + } + } + + /** + * 列表搜索数据 + * @return JsonResponse + */ + public function search(): JsonResponse + { + try { + $data = [ + 'parking_space_type_list' => ParkingSpaceType::getData() + ]; + return $this->responseService->success($data); + } catch (Exception $e) { + $m_prefix = __('exception.exception_handler.resource'); + return $this->responseService->systemError( + $m_prefix . ':' . $e->getMessage() + ); + } + } + + public function store(Request $request): JsonResponse + { + try { + $data = $request->all(); + $rules = [ + 'license_plate' => 'required', + ]; + $messages = [ + 'license_plate.required' => __( + 'validation.license_plate.n_empty' + ) + ]; + $validator = Validator::make($data, $rules, $messages); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + + $this->service->createModel($data); + + return $this->responseService->success( + null, + __('admin.save_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.parking_information.create_failed') . ':' + . $e->getMessage() + ); + } + + } + + /** + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function destroy(string $id): JsonResponse + { + try { + $this->validateId($id, ParkingInformation::class); + $this->service->deleteModel($id); + return $this->responseService->success( + null, + __('admin.delete_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.license_plate.destroy_failed') . ':' + . $e->getMessage() + ); + } + } + + + /** + * @return JsonResponse + * @throws InvalidArgumentException + */ + public function rule(): JsonResponse + { + try { + return $this->responseService->success( + $this->methodShow('information') + ); + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.get_data_failed') . ':' . $e->getMessage() + ); + } + } +} diff --git a/app/Http/Controllers/Admin/ParkingLicensePlateController.php b/app/Http/Controllers/Admin/ParkingLicensePlateController.php index d223bee..e255413 100644 --- a/app/Http/Controllers/Admin/ParkingLicensePlateController.php +++ b/app/Http/Controllers/Admin/ParkingLicensePlateController.php @@ -276,7 +276,7 @@ class ParkingLicensePlateController extends BaseController { try { return $this->responseService->success( - $this->methodShow('licensePlate') + $this->methodShow('licensePlate', ['import', 'clear']) ); } catch (Exception $e) { return $this->responseService->systemError( diff --git a/app/Models/ParkingInformation.php b/app/Models/ParkingInformation.php new file mode 100644 index 0000000..061bb32 --- /dev/null +++ b/app/Models/ParkingInformation.php @@ -0,0 +1,34 @@ +where('id', $id)->value('number') ?? ''; } + + public static function getId($number): array|Collection + { + return self::query()->where('number', 'like', "%{$number}%")->pluck( + 'id' + )->toArray(); + } } diff --git a/app/Models/ParkingSpace.php b/app/Models/ParkingSpace.php index 30ac340..4186558 100644 --- a/app/Models/ParkingSpace.php +++ b/app/Models/ParkingSpace.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; class ParkingSpace extends Model { @@ -39,4 +40,16 @@ class ParkingSpace extends Model strtotime($value) ); } + + public static function getNumber($id) + { + return self::query()->where('id', $id)->value('number'); + } + + public static function getId($number): array|Collection + { + return self::query()->where('number', 'like', "%{$number}%")->pluck( + 'id' + )->toArray(); + } } diff --git a/app/Services/ParkingInformationService.php b/app/Services/ParkingInformationService.php new file mode 100644 index 0000000..2c24d04 --- /dev/null +++ b/app/Services/ParkingInformationService.php @@ -0,0 +1,152 @@ +menuTitle = 'parking_information'; + } + + + /** + * @param $data + * @return Builder|Model + * @throws Exception + */ + public function createModel($data): Builder|Model + { + try { + DB::beginTransaction(); + + $license_plate = $data['license_plate']; + $license_plate = ParkingLicensePlate::query()->where( + 'number', + $license_plate + )->first(['id', 'space_type_id']); + $license_plate_id = 0; + if ($license_plate) { + $license_plate_id = $license_plate['id']; + $space_type_id = $license_plate['space_type_id']; + } else { + $space_type_data = ParkingSpaceType::getDefaultData(); + $space_type_id = $space_type_data['id'] ?? 0; + } + + $space_id = 0; + $floor_region_id = 0; + if ($space_type_id) { + $parkingSpaceWhere = [ + 'space_type_id' => $space_type_id, + 'status' => 0, + 'license_plate_id' => 0 + ]; + $res = ParkingSpace::query() + ->where($parkingSpaceWhere) + ->first(['id', 'floor_id']); + if ($res) { + $space_id = $res['id']; + if ($res['floor_id']) { + $floor_region_id = AdminFloorRegion::query()->where( + 'floor_id', + $res['floor_id'] + )->value('id'); + } + } + } + + if ($space_id) { + $space = ParkingSpace::query()->where('status', 0) + ->where('license_plate_id', 0)->first( + ['id', 'floor_id', 'space_type_id'] + ); + if ($space) { + $space_id = $space['id']; + if (!$space_type_id) { + $space_type_id = $space['space_type_id']; + } + if (!$floor_region_id && $space['floor_id']) { + $floor_region_id = AdminFloorRegion::query()->where( + 'floor_id', + $space['floor_id'] + )->value('id'); + } + } + } + + if (!$space_type_id) { + $createData = [ + 'number' => $license_plate, + 'space_type_id' => $space_type_id + ]; + (new ParkingLicensePlateService( + $this->logService + ))->createModel($createData); + $license_plate_id = ParkingLicensePlate::query()->where( + $createData + )->value('id'); + } + if (!$license_plate_id) { + throw new Exception( + __('service.parking_information.not_default') + ); + } + + if (ParkingInformation::query()->where('license_plate_id', $license_plate_id) + ->exists() + ) { + throw new Exception(__('service.license_plate.number_exists')); + } + + $model = ParkingInformation::query()->create([ + 'floor_region_id' => $floor_region_id, + 'space_id' => $space_id, + 'license_plate_id' => $license_plate_id, + 'entry_time' => get_datetime(), + 'space_type_id' => $space_type_id, + 'created_at' => get_datetime() + ]); + + $this->logService->logCreated($model, 'parking_information.create'); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + public function deleteModel($id): bool + { + try { + DB::beginTransaction(); + + $model = ParkingInformation::query()->findOrFail($id); + + $this->logService->logDeleted($model, 'parking_information.delete'); + + $model->delete(); + + DB::commit(); + return true; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + +} diff --git a/database/migrations/2026_03_23_143449_create_parking_space_table.php b/database/migrations/2026_03_23_143449_create_parking_space_table.php index 20a259f..219b1c2 100644 --- a/database/migrations/2026_03_23_143449_create_parking_space_table.php +++ b/database/migrations/2026_03_23_143449_create_parking_space_table.php @@ -16,7 +16,7 @@ return new class extends Migration $table->integer('floor_id')->comment('楼层id'); $table->string('number', 50)->default('')->comment('车位号码'); $table->integer('space_attr_id')->comment('车位属性id'); - $table->integer('license_plate_id')->nullable()->comment('车牌号码'); + $table->integer('license_plate_id')->default(0)->comment('车牌号码'); $table->timestamp('berthing_time')->nullable()->comment('停泊时间'); $table->string('recognition', 50)->default('')->nullable()->comment('车牌识别度'); $table->tinyInteger('status')->default(0)->comment('状态 0空置 1占用'); diff --git a/database/migrations/2026_04_23_143449_create_parking_information_table.php b/database/migrations/2026_04_23_143449_create_parking_information_table.php new file mode 100644 index 0000000..5c86984 --- /dev/null +++ b/database/migrations/2026_04_23_143449_create_parking_information_table.php @@ -0,0 +1,33 @@ +id(); + $table->integer('floor_region_id')->comment('楼层区域id'); + $table->integer('space_id')->default('')->comment('车位id'); + $table->integer('license_plate_id')->default('')->comment('车牌id'); + $table->integer('space_type_id')->default('')->comment('车位类型id'); + $table->timestamp('entry_time')->comment('驶入时间'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_electronic_map'); + } +}; diff --git a/resources/lang/zh-CN/exception.php b/resources/lang/zh-CN/exception.php index 6e9f21f..c89aa9a 100644 --- a/resources/lang/zh-CN/exception.php +++ b/resources/lang/zh-CN/exception.php @@ -61,5 +61,8 @@ return [ 'parking_space' => [ 'update_type_failed' => '修改车位类型失败', 'update_attr_failed' => '修改车位属性失败' + ], + 'parking_information' => [ + 'create_failed' => '新增失败' ] ]; diff --git a/resources/lang/zh-CN/log.php b/resources/lang/zh-CN/log.php index 11f9327..d6dad09 100644 --- a/resources/lang/zh-CN/log.php +++ b/resources/lang/zh-CN/log.php @@ -54,5 +54,9 @@ return [ ], 'map' => [ 'save' => '绘制电子地图保存' + ], + 'parking_information' => [ + 'create' => '创建车位资讯', + 'delete' => '更新车位资讯' ] ]; diff --git a/resources/lang/zh-CN/service.php b/resources/lang/zh-CN/service.php index 51a886a..2173f53 100644 --- a/resources/lang/zh-CN/service.php +++ b/resources/lang/zh-CN/service.php @@ -51,5 +51,8 @@ return [ 'no_cars' => '车位状态无车更新有车', 'yes_cars' => '车位状态有车更新无车', 'number_update' => '车牌号更新' + ], + 'parking_information' => [ + 'not_default' => '未设置默认车位类型' ] ]; diff --git a/routes/admin/api.php b/routes/admin/api.php index 5f5c09d..ba40bfb 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -18,6 +18,7 @@ use App\Http\Controllers\Admin\TranslationController; use App\Http\Controllers\Admin\UploadController; use App\Http\Controllers\Admin\UserController; use Illuminate\Support\Facades\Route; +use App\Http\Controllers\Admin\ParkingInformationController; Route::group(['prefix' => 'admin'], function () { @@ -73,6 +74,11 @@ Route::group(['prefix' => 'admin'], function () { Route::get('/map/buildingFloorList', [ParkingElectronicMapController::class, 'buildingFloorList']); Route::get('/map/parkingSpaceList/{id}', [ParkingElectronicMapController::class, 'getParkingSpaceList']); Route::post('/map/save', [ParkingElectronicMapController::class, 'save']); + // 车辆停车资讯 + Route::get('/information', [ParkingInformationController::class, 'index']); + Route::get('/information/search', [ParkingInformationController::class, 'search']); + Route::post('/information', [ParkingInformationController::class, 'store']); + Route::delete('/information/{id}', [ParkingInformationController::class, 'destroy']); // VIP名单 Route::get('/vipList', [VipListController::class, 'index']);