diff --git a/app/Http/Controllers/Admin/ParkingLicensePlateController.php b/app/Http/Controllers/Admin/ParkingLicensePlateController.php new file mode 100644 index 0000000..4f3f2b4 --- /dev/null +++ b/app/Http/Controllers/Admin/ParkingLicensePlateController.php @@ -0,0 +1,174 @@ +responseService = $responseService; + $this->LicensePlateService = $LicensePlateService; + } + /** + * Display a listing of the resource. + */ + public function index(Request $request): JsonResponse + { + try { + $query = ParkingLicensePlate::query(); + + // 车位类型搜索 + $space_type_id = $request->input('space_type_id'); + $query->where('space_type_id', '=', $space_type_id); + + // 分页 + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + + $total = $query->count(); + $items = $query->latest()->forPage($page, $perPage)->get(); + + 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() + ); + } + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + try { + $this->saveValidator($request->all()); + $this->LicensePlateService->createModel($request->all()); + + return $this->responseService->success( + null, + __('admin.save_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.license_plate.create_failed') . ':' . $e->getMessage( + ) + ); + } + } + + /** + * @param array $data + * @param int $id + * @return void + * @throws ValidationException + */ + protected function saveValidator(array $data, int $id = 0): void + { + $rules = [ + 'number' => 'required', + ]; + $messages = [ + 'number.required' => __( + 'validation.license_plate.n_empty' + ) + ]; + if ($id) { + $this->validateId($id, ParkingLicensePlate::class); + } else { + $rules['space_type_id'] = 'required'; + $messages['space_type_id.required'] = __('validation.license_plate.type_empty'); + } + $validator = Validator::make($data, $rules, $messages); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + } + + /** + * @param Request $request + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function update(Request $request, string $id): JsonResponse + { + try { + $this->saveValidator($request->all(), $id); + $this->LicensePlateService->updateModel($request->all(), $id); + return $this->responseService->success( + null, + __('admin.update_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.license_plate.update_failed') . ':' . $e->getMessage( + ) + ); + } + } + + /** + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function destroy(string $id): JsonResponse + { + try { + $this->validateId($id, ParkingLicensePlate::class); + $this->LicensePlateService->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() + ); + } + } +} diff --git a/app/Http/Controllers/Admin/ParkingSpaceTypeController.php b/app/Http/Controllers/Admin/ParkingSpaceTypeController.php new file mode 100644 index 0000000..bfe4f79 --- /dev/null +++ b/app/Http/Controllers/Admin/ParkingSpaceTypeController.php @@ -0,0 +1,224 @@ +responseService = $responseService; + $this->SpaceTypeService = $SpaceTypeService; + } + + /** + * Display a listing of the resource. + */ + public function index(Request $request): JsonResponse + { + try { + $query = ParkingSpaceType::query(); + + // 分页 + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + + $total = $query->count(); + $items = $query->latest()->forPage($page, $perPage)->get(); + + 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() + ); + } + } + + /** + * Show the form for creating a new resource. + */ + public function create(): JsonResponse + { + try { + $data = [ + 'color_list' => $this->SpaceTypeService->getColorList(), + 'attr_list' => ParkingSpaceAttributes::getList() + ]; + return $this->responseService->success($data); + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.get_data_failed') . ':' . $e->getMessage() + ); + } + } + + /** + * @param Request $request + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function store(Request $request): JsonResponse + { + try { + $this->saveValidator($request->all()); + $this->SpaceTypeService->createModel($request->all()); + + return $this->responseService->success( + null, + __('admin.save_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.space_type.create_failed') . ':' . $e->getMessage( + ) + ); + } + } + + /** + * @param array $data + * @param int $id + * @return void + * @throws ValidationException + */ + protected function saveValidator(array $data, int $id = 0): void + { + $rules = [ + 'name' => 'required', + ]; + $messages = [ + 'name.required' => __( + 'validation.space_type.n_empty' + ) + ]; + if ($id) { + $this->validateId($id, ParkingSpaceAttributes::class); + } + $validator = Validator::make($data, $rules, $messages); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id): JsonResponse + { + return $this->extracted($id); + } + + protected function extracted(string $id): JsonResponse + { + + try { + $this->validateId($id, ParkingSpaceType::class); + $data = [ + 'color_list' => $this->SpaceTypeService->getColorList(), + 'attr_list' => ParkingSpaceAttributes::getList(), + 'item' => ParkingSpaceType::query()->findOrFail($id)->toArray() + ]; + return $this->responseService->success($data); + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.get_data_failed') . ':' . $e->getMessage() + ); + } + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id): JsonResponse + { + return $this->extracted($id); + } + + /** + * @param Request $request + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function update(Request $request, string $id): JsonResponse + { + try { + $this->saveValidator($request->all(), $id); + $this->SpaceTypeService->updateModel($request->all(), $id); + return $this->responseService->success( + null, + __('admin.update_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.space_type.update_failed') . ':' . $e->getMessage( + ) + ); + } + } + + /** + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function destroy(string $id): JsonResponse + { + try { + $this->validateId($id, ParkingSpaceType::class); + $this->SpaceTypeService->deleteModel($id); + return $this->responseService->success( + null, + __('admin.delete_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.space_type.destroy_failed') . ':' + . $e->getMessage() + ); + } + } +} diff --git a/app/Models/ParkingLicensePlate.php b/app/Models/ParkingLicensePlate.php new file mode 100644 index 0000000..d5c49e8 --- /dev/null +++ b/app/Models/ParkingLicensePlate.php @@ -0,0 +1,19 @@ +get()->select( + ['id as attributes_id', 'attributes'] + )->toArray(); + } } diff --git a/app/Models/ParkingSpaceType.php b/app/Models/ParkingSpaceType.php new file mode 100644 index 0000000..80b405d --- /dev/null +++ b/app/Models/ParkingSpaceType.php @@ -0,0 +1,24 @@ +logService = $logService; + } + + /** + * 创建车牌号码 + * @param array $data + * @return Model|Builder + * @throws Exception + */ + public function createModel(array $data): Model|Builder + { + try { + DB::beginTransaction(); + + if (ParkingLicensePlate::query()->where('number', $data['number']) + ->exists() + ) { + throw new Exception(__('service.license_plate.number_exists')); + } + + $model = ParkingLicensePlate::query()->create([ + 'number' => $data['number'], + 'space_type_id' => $data['space_type_id'], + 'created_at' => get_datetime() + ]); + + $this->logService->logCreated($model, '创建车牌号码'); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * @param array $data + * @param int $id + * @return Model|Builder + * @throws Exception + */ + public function updateModel(array $data, int $id): Model|Builder + { + try { + DB::beginTransaction(); + + // 验证 + $existsWhere = [ + ['number', '=', $data['number']], + ['id', '<>', $id] + ]; + if (ParkingLicensePlate::query()->where($existsWhere)->exists()) { + throw new Exception(__('service.license_plate.number_exists')); + } + + // 更新 + $model = ParkingLicensePlate::query()->findOrFail($id); + $oldValues = $model->toArray(); + + $model->update([ + 'number' => $data['number'], + 'updated_at' => get_datetime() + ]); + + $this->logService->logUpdated($model, $oldValues, '更新车牌号码'); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * @param $id + * @return bool + * @throws Exception + */ + public function deleteModel($id): bool + { + try { + DB::beginTransaction(); + + $model = ParkingLicensePlate::query()->findOrFail($id); + + $this->logService->logDeleted($model, '删除车牌号码'); + + $model->delete(); + + DB::commit(); + return true; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + +} diff --git a/app/Services/ParkingSpaceTypeService.php b/app/Services/ParkingSpaceTypeService.php new file mode 100644 index 0000000..4e2cd0a --- /dev/null +++ b/app/Services/ParkingSpaceTypeService.php @@ -0,0 +1,174 @@ +logService = $logService; + } + + /** + * 返回翻译的颜色选择列表 + * @return array + */ + public function getColorList(): array + { + $colorArr = []; + foreach ($this->color as $value) { + $colorArr[$value] = __('service.space_type.' . $value); + } + return get_select_data($colorArr); + } + + /** + * 创建车位属性 + * @param array $data + * @return Model|Builder + * @throws Exception + */ + public function createModel(array $data): Model|Builder + { + try { + DB::beginTransaction(); + + if (ParkingSpaceType::query()->where('name', $data['name'])->exists( + ) + ) { + throw new Exception(__('service.space_type.name_exists')); + } + + $createData = $this->getSaveData($data); + $createData['created_at'] = get_datetime(); + + $model = ParkingSpaceType::query()->create($createData); + + $this->logService->logCreated($model, '创建车位类型'); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * @param array $data + * @return array + */ + protected function getSaveData(array $data): array + { + $saveData = [ + 'name' => $data['name'], + 'is_default' => $data['is_default'], + 'default_color_occupy' => $data['default_color_occupy'] ?? '', + 'default_color_vacant' => $data['default_color_vacant'] ?? '', + 'default_color_warning' => $data['default_color_warning'] ?? '', + 'default_is_flicker' => $data['default_is_flicker'] ?? '0' + ]; + if (isset($data['attributes_id'])) { + $saveData['attributes_id'] = $data['attributes_id']; + } + $saveData['attr_color_occupy'] = $data['attr_color_occupy'] ?? ''; + $saveData['attr_color_vacant'] = $data['attr_color_vacant'] ?? ''; + $saveData['attr_color_warning'] = $data['attr_color_warning'] ?? ''; + + if (isset($data['attr_is_warning'])) { + $saveData['attr_is_warning'] = $data['attr_is_warning']; + } + return $saveData; + } + + /** + * @param array $data + * @param int $id + * @return Model|Builder + * @throws Exception + */ + public function updateModel(array $data, int $id): Model|Builder + { + try { + DB::beginTransaction(); + + // 验证 + $existsWhere = [ + ['name', '=', $data['name']], + ['id', '<>', $id] + ]; + if (ParkingSpaceType::query()->where($existsWhere)->exists()) { + throw new Exception(__('service.space_type.name_exists')); + } + + // 更新 + $model = ParkingSpaceType::query()->findOrFail($id); + $oldValues = $model->toArray(); + + $saveData = $this->getSaveData($data); + $saveData['updated_at'] = get_datetime(); + $model->update($saveData); + + $this->logService->logUpdated($model, $oldValues, '更新车位类型'); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * @param $id + * @return bool + * @throws Exception + */ + public function deleteModel($id): bool + { + try { + DB::beginTransaction(); + + $model = ParkingSpaceType::query()->findOrFail($id); + + $this->logService->logDeleted($model, '删除车位类型'); + + $model->delete(); + + DB::commit(); + return true; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } +} diff --git a/composer.json b/composer.json index 5b40f87..c36ab9d 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.0", "laravel/sanctum": "^3.2", - "laravel/tinker": "^2.8" + "laravel/tinker": "^2.8", + "maatwebsite/excel": "^3.1" }, "require-dev": { "fakerphp/faker": "^1.9.1", diff --git a/composer.lock b/composer.lock index f6db401..9303153 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bfe12996eeecb6fdc8713a9fd9d431f8", + "content-hash": "d44d0e67fdb59e187c377426d95d5cc0", "packages": [ { "name": "brick/math", @@ -135,6 +135,162 @@ ], "time": "2023-12-11T17:09:12+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -508,6 +664,67 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + }, + "time": "2025-10-17T16:34:55+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.4.0", @@ -1888,6 +2105,272 @@ ], "time": "2024-09-21T08:32:55+00:00" }, + { + "name": "maatwebsite/excel", + "version": "3.1.67", + "source": { + "type": "git", + "url": "https://github.com/SpartnerNL/Laravel-Excel.git", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e508e34a502a3acc3329b464dad257378a7edb4d", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "ext-json": "*", + "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", + "php": "^7.0||^8.0", + "phpoffice/phpspreadsheet": "^1.30.0", + "psr/simple-cache": "^1.0||^2.0||^3.0" + }, + "require-dev": { + "laravel/scout": "^7.0||^8.0||^9.0||^10.0", + "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0", + "predis/predis": "^1.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Excel": "Maatwebsite\\Excel\\Facades\\Excel" + }, + "providers": [ + "Maatwebsite\\Excel\\ExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Patrick Brouwers", + "email": "patrick@spartner.nl" + } + ], + "description": "Supercharged Excel exports and imports in Laravel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel", + "php", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.67" + }, + "funding": [ + { + "url": "https://laravel-excel.com/commercial-support", + "type": "custom" + }, + { + "url": "https://github.com/patrickbrouwers", + "type": "github" + } + ], + "time": "2025-08-26T09:13:16+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-01-27T12:07:53+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, { "name": "monolog/monolog", "version": "3.10.0", @@ -2395,6 +2878,114 @@ ], "time": "2024-11-21T10:36:35+00:00" }, + { + "name": "phpoffice/phpspreadsheet", + "version": "1.30.2", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "09cdde5e2f078b9a3358dd217e2c8cb4dac84be2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/09cdde5e2f078b9a3358dd217e2c8cb4dac84be2", + "reference": "09cdde5e2f078b9a3358dd217e2c8cb4dac84be2", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.15", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": ">=7.4.0 <8.5.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "doctrine/instantiator": "^1.5", + "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.2" + }, + "time": "2026-01-11T05:58:24+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.5", diff --git a/database/migrations/2026_03_09_143728_create_parking_space_type_table.php b/database/migrations/2026_03_09_143728_create_parking_space_type_table.php new file mode 100644 index 0000000..b178df5 --- /dev/null +++ b/database/migrations/2026_03_09_143728_create_parking_space_type_table.php @@ -0,0 +1,40 @@ +id(); + $table->string('name')->unique()->comment('车位类型名称'); + $table->tinyInteger('is_default')->default(0)->comment('是否为默认车位类型 0否 1是'); + $table->string('default_color_occupy', 50)->nullable()->comment('默认指示灯颜色(占用)'); + $table->string('default_color_vacant', 50)->nullable()->comment('默认指示灯颜色(空置)'); + $table->string('default_color_warning', 50)->nullable()->comment('默认报警指示灯颜色'); + $table->tinyInteger('default_is_warning')->default(0)->comment('默认是否报警 0否 1是'); + $table->tinyInteger('default_is_flicker')->default(0)->comment('默认是否闪烁 0否 1是'); + $table->integer('attributes_id')->nullable()->comment('车位属性ID'); + $table->string('attr_color_occupy', 50)->nullable()->comment('车位属性指示灯颜色(占用)'); + $table->string('attr_color_vacant', 50)->nullable()->comment('车位属性指示灯颜色(空置)'); + $table->string('attr_color_warning', 50)->nullable()->comment('车位属性报警指示灯颜色'); + $table->tinyInteger('attr_is_warning')->nullable()->comment('车位属性是否报警 0否 1是'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_space_type'); + } +}; diff --git a/database/migrations/2026_03_09_171738_create_parking_license_plate_table.php b/database/migrations/2026_03_09_171738_create_parking_license_plate_table.php new file mode 100644 index 0000000..2af8a2d --- /dev/null +++ b/database/migrations/2026_03_09_171738_create_parking_license_plate_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('number', 20)->unique()->comment('车牌号码'); + $table->integer('space_type_id')->comment('车位类型id'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_license_plate'); + } +}; diff --git a/resources/lang/zh-CN/controller.php b/resources/lang/zh-CN/controller.php new file mode 100644 index 0000000..d8e1968 --- /dev/null +++ b/resources/lang/zh-CN/controller.php @@ -0,0 +1,30 @@ + [ + 'store_success' => '添加角色成功', + 'update_success' => '更新角色成功' + ], + 'user' => [], + 'config' => [ + 'parking_lot' => '车场配置', + 'parking_lot_id' => '本地停车场ID', + 'parking_lot_sum' => '车场总车位数', + 'parking_lot_image_path' => '车位图片路径', + 'zombie_car_parking_duration' => '僵尸车停车时长', + 'numberplate_fuzzy_search' => '车牌模糊检索方式', + 'automatically_clear_photo_cycle' => '自动清除照片周期', + 'third_party' => '第三方平台配置', + 'report_data_switch' => '上报数据开关', + 'third_parking_lot_id' => '第三方平台车场ID', + 'report_api_url' => '上报接口地址', + 'report_cycle' => '上报周期', + ], + 'upload' => [ + 'empty_file' => '没有文件上传', + 'empty_image' => '没有图片上传' + ], + 'import' => [ + 'success' => '导入成功' + ] +]; diff --git a/resources/lang/zh-CN/exception.php b/resources/lang/zh-CN/exception.php new file mode 100644 index 0000000..385dc89 --- /dev/null +++ b/resources/lang/zh-CN/exception.php @@ -0,0 +1,60 @@ + '获取用户信息失败', + 'get_user_info_list_failed' => '获取用户信息失败', + 'admin_user' => [ + 'create_failed' => '创建用户失败', + 'update_failed' => '更新用户失败', + 'destroy_failed' => '删除用户失败' + ], + 'admin_role' => [ + 'create_failed' => '创建角色失败', + 'update_failed' => '更新角色失败', + 'destroy_failed' => '删除角色失败' + ], + 'update_admin_config_failed' => '更新配置失败', + 'get_data_failed' => '获取数据失败', + 'exception_handler' => [ + 'login' => '请先登录', + 'resource' => '资源不存在', + 'api' => '接口不存在', + 'role' => '没有操作权限', + 'frequent' => '请求过于频繁,请稍后再试', + 'system' => '系统错误' + ], + 'admin_translation' => [ + 'create_failed' => '创建翻译失败', + 'update_failed' => '更新翻译失败', + 'destroy_failed' => '删除翻译失败' + ], + 'admin_floor' => [ + 'create_failed' => '创建楼层失败', + 'update_failed' => '更新楼层失败', + 'destroy_failed' => '删除楼层失败' + ], + 'upload' => [ + 'error' => '上传失败' + ], + 'admin_vip_list' => [ + 'create_failed' => '创建VIP名单失败', + 'update_failed' => '更新VIP名单失败', + 'destroy_failed' => '删除VIP名单失败', + 'import_failed' => '上传失败' + ], + 'space_attributes' => [ + 'create_failed' => '创建车位属性失败', + 'update_failed' => '更新车位属性失败', + 'destroy_failed' => '删除车位属性失败' + ], + 'space_type' => [ + 'create_failed' => '创建车位类型失败', + 'update_failed' => '更新车位类型失败', + 'destroy_failed' => '删除车位类型失败' + ], + 'license_plate' => [ + 'create_failed' => '创建车牌号码失败', + 'update_failed' => '更新车牌号码失败', + 'destroy_failed' => '删除车牌号码失败' + ] +]; diff --git a/resources/lang/zh-CN/menu.php b/resources/lang/zh-CN/menu.php new file mode 100644 index 0000000..d02816c --- /dev/null +++ b/resources/lang/zh-CN/menu.php @@ -0,0 +1,13 @@ + '模式管理', + 'cat_status' => '车位状态', + 'special_car_manage' => '特别车位管理', + 'information_center' => '信息中心', + 'statistics_report' => '统计报表', + 'device_manage' => '设备管理', + 'system_manage' => '系统管理', + 'user_manage' => '用户管理', + 'role_manage' => '角色管理', +]; diff --git a/resources/lang/zh-CN/middleware.php b/resources/lang/zh-CN/middleware.php new file mode 100644 index 0000000..0b5a01f --- /dev/null +++ b/resources/lang/zh-CN/middleware.php @@ -0,0 +1,22 @@ + [ + 'token_exists' => '缺少认证令牌', + 'token_invalid' => '认证令牌无效', + 'user_disabled' => '用户已被禁用', + 'use_json' => '请求必须使用 JSON 格式', + 'login_invalid' => '登录失效' + ], + 'api' => [ + 'login' => '请先登录', + 'permission' => '没有操作权限', + 'resource' => '资源不存在', + 'data' => '数据验证失败', + 'system' => '系统错误' + ], + 'check' => [ + 'user_auth' => '你无权访问!' + ] + +]; diff --git a/resources/lang/zh-CN/service.php b/resources/lang/zh-CN/service.php new file mode 100644 index 0000000..380a972 --- /dev/null +++ b/resources/lang/zh-CN/service.php @@ -0,0 +1,36 @@ + [ + 'name_exists' => '角色名称已存在', + 'menu_error' => '角色编号参数有误' + ], + 'admin_user' => [ + 'name_exists' => '用户账号已存在' + ], + 'admin_translation' => [ + 'data_exists' => '翻译配置已存在' + ], + 'admin_floor' => [ + 'name_exists' => '楼层名称已存在' + ], + 'admin_vip_list' => [ + 'license_exists' => '车牌号码已存在' + ], + 'space_attributes' => [ + 'attributes_exists' => '车位属性名称已存在' + ], + 'space_type' => [ + 'red' => '红色', + 'green' => '绿色', + 'yellow' => '黄色', + 'blue' => '蓝色', + 'purple' => '紫色', + 'cyan' => '青色', + 'white' => '白色', + 'name_exists' => '车位类型名称已存在' + ], + 'license_plate' => [ + 'number_exists' => '车牌号码已存在' + ] +]; diff --git a/resources/lang/zh-CN/validation.php b/resources/lang/zh-CN/validation.php new file mode 100644 index 0000000..21d2024 --- /dev/null +++ b/resources/lang/zh-CN/validation.php @@ -0,0 +1,72 @@ + [ + 'u_empty' => '用户名不能为空', + 'p_empty' => '密码不能为空', + 'u_p_error' => '账号或密码错误', + 'u_disabled' => '账号已被禁用' + ], + 'admin_user' => [ + 'l_a_empty' => '登录账号不能为空', + 'l_a_alpha_num' => '登录账号只能是英文字母和数字', + 'l_a_between' => '登录账号长度必须4-20', + 'r_empty' => '用户角色不能为空', + 's_p_empty' => '所属停车场不能为空', + 'p_empty' => '初始密码不能为空', + 'p_between' => '初始密码长度必须12-30', + 'email' => '电邮地址格式错误', + 's_empty' => '状态不能为空', + 's_in' => '状态至必须是0或1' + ], + 'admin_role' => [ + 'n_empty' => '角色名称不能为空', + 'n_max' => '角色名称最多50个字符', + 'm_empty' => '角色权限不能为空', + 'm_array' => '角色权限数据必须是数组', + 'r_max' => '角色名称最多50个字符' + ], + 'admin_config' => [ + 'content_empty' => '配置数据不能为空', + 'parking_lot_id_empty' => '本地停车场ID配置不能为空', + 'parking_lot_sum_empty' => '车场总车位数配置不能为空', + 'parking_lot_sum_num' => '车场总车位数配置必须是数字', + 'zombie_car_empty' => '僵尸车停车时长配置不能为空', + 'zombie_car_num' => '僵尸车停车时长配置必须是数字' + ], + 'id_empty' => '编号不能为空', + 'id_numeric' => '编号必须是数字', + 'admin_translation' => [ + 'en_empty' => '英文翻译不能为空', + 'zh_cn_empty' => '中文简体翻译不能为空', + 'zh_tw_empty' => '中文繁体翻译不能为空' + ], + 'admin_floor' => [ + 'n_empty' => '楼层名称不能为空', + 'n_max' => '楼层名称最多50个字符', + 'r_array' => '楼层名称数据必须是数组', + ], + 'upload' => [ + 'i_empty' => '图片文件不能为空', + 'i_image' => '上传文件必须是图片', + 'i_max' => '上传图片文件不可超过10MB' + ], + 'admin_list_vip' => [ + 'l_empty' => '车牌号码不能为空', + 'l_max' => '车牌号码最多20个字符', + 'file_empty' => '导入文件不能为空', + 'file_mimes' => '导入文件类型必须是xlsx|xls|csv', + 'file_max' => '导入文件最大不超过2048' + ], + 'space_attributes' => [ + 'a_empty' => '车位属性名称不能为空', + 'i_empty' => '汇入图示不能为空' + ], + 'space_type' => [ + 'n_empty' => '车位类型名称不能为空' + ], + 'license_plate' => [ + 'n_empty' => '车牌号码不能为空', + 'type_empty' => '车位类型不能为空' + ] +];