diff --git a/app/Http/Controllers/Admin/ParkingDepartureReasonController.php b/app/Http/Controllers/Admin/ParkingDepartureReasonController.php new file mode 100644 index 0000000..a8be138 --- /dev/null +++ b/app/Http/Controllers/Admin/ParkingDepartureReasonController.php @@ -0,0 +1,225 @@ +service = $service; + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + try { + $query = ParkingDepartureReason::query(); + + if ($request->has('reason')) { + $reason = $request->input('reason'); + if ($reason) { + $query->where('reason', 'like', "%{$reason}%"); + } + } + + if ($request->has('create_start_time') + && $request->has( + 'create_end_time' + ) + ) { + $create_start_time = $request->input('create_start_time'); + $create_end_time = $request->input('create_end_time'); + if ($create_start_time && $create_end_time) { + $query->whereBetween('created_at', [ + $create_start_time . ' 00:00:00', + $create_end_time . ' 23:59:59' + ]); + } + } + + // 分页 + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $total = $query->count(); + $items = $query->latest()->forPage($page, $perPage)->get()->each( + function ($item) { + $item['reason'] + = AdminTranslationService::getTranslationTypeName( + $item['id'], + 8, + $item['reason'] + ); + 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() + ); + } + } + + /** + * @param Request $request + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function store(Request $request): JsonResponse + { + try { + $this->saveValidator($request->all()); + $this->service->createModel($request->all()); + return $this->responseService->success( + null, + __('admin.save_succeeded') + ); + } catch (ValidationException|CustomException $e) { + throw $e; + } catch (Exception $e) { + return $this->responseService->systemError( + __('admin.operation_failed') . ':' + . $e->getMessage() + ); + } + } + + /** + * @param array $data + * @param int $id + * @return void + * @throws ValidationException + */ + protected function saveValidator(array $data, int $id = 0): void + { + $rules = [ + 'reason' => 'required' + ]; + $messages = [ + 'reason.required' => __( + 'validation.departure_management.n_empty' + ) + ]; + if ($id) { + $this->validateId($id, ParkingDepartureReason::class); + } + $validator = Validator::make($data, $rules, $messages); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + } + + /** + * @param string $id + * @return JsonResponse + */ + public function edit(string $id): JsonResponse + { + try { + $this->validateId($id, ParkingDepartureReason::class); + $data = [ + 'item' => ParkingDepartureReason::query()->find($id) + ]; + $Translation = AdminTranslationService::getTranslation( + $data['item']['id'], + 8 + ); + $data['item']['en_reason'] = $Translation['en'] ?? ''; + $data['item']['tw_reason'] = $Translation['zh_tw'] ?? ''; + return $this->responseService->success($data); + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.get_data_failed') . ':' . $e->getMessage() + ); + } + } + + /** + * @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->service->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( + __('admin.operation_failed') . ':' + . $e->getMessage() + ); + } + } + + /** + * @param string $id + * @return JsonResponse + * @throws CustomException + * @throws ValidationException + */ + public function destroy(string $id): JsonResponse + { + try { + $this->validateId($id, ParkingDepartureReason::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( + __('admin.operation_failed') . ':' + . $e->getMessage() + ); + } + } +} diff --git a/app/Models/ParkingDepartureReason.php b/app/Models/ParkingDepartureReason.php new file mode 100644 index 0000000..083bff2 --- /dev/null +++ b/app/Models/ParkingDepartureReason.php @@ -0,0 +1,31 @@ +findOrFail($data['id']); + $model->delete(); (new AdminTranslationService( new OperationLogService() ))->logService->logDeleted($model, 'translation.delete'); diff --git a/app/Services/ParkingDepartureReasonService.php b/app/Services/ParkingDepartureReasonService.php new file mode 100644 index 0000000..029fdbb --- /dev/null +++ b/app/Services/ParkingDepartureReasonService.php @@ -0,0 +1,138 @@ +where($existsWhere)->exists() + ) { + throw new Exception( + __('service.' . $this->menuTitle . '.reason_exists') + ); + } + + $model = ParkingDepartureReason::query()->create([ + 'reason' => $data['reason'], + 'remark' => $data['remark'] ?? '', + 'created_at' => get_datetime() + ]); + + $this->logService->logCreated( + $model, + $this->menuTitle . '.create' + ); + + AdminTranslationService::saveTranslation( + $data['reason'], + $data['en_reason'] ?? '', + $data['tw_reason'] ?? '', + $model->id, + 8 + ); + + DB::commit(); + return $model; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * @param array $data + * @param int $id + * @throws Exception + */ + public function updateModel(array $data, int $id) + { + try { + DB::beginTransaction(); + + // 验证 + $existsWhere = [ + ['reason', '=', $data['reason']], + ['id', '<>', $id] + ]; + if (ParkingDepartureReason::query()->where($existsWhere)->exists() + ) { + throw new Exception( + __('service.' . $this->menuTitle . '.reason_exists') + ); + } + + // 更新 + $model = ParkingDepartureReason::query()->findOrFail($id); + $oldValues = $model->toArray(); + + $model->update([ + 'reason' => $data['reason'], + 'remark' => $data['remark'] ?? '', + 'updated_at' => get_datetime() + ]); + + $this->logService->logUpdated( + $model, + $oldValues, + $this->menuTitle . '.update' + ); + + AdminTranslationService::saveTranslation( + $data['reason'], + $data['en_reason'] ?? '', + $data['tw_reason'] ?? '', + $id, + 8 + ); + + 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 = ParkingDepartureReason::query()->findOrFail($id); + + $this->logService->logDeleted($model, $this->menuTitle . '.delete'); + + $model->delete(); + + AdminTranslationService::syncDelete($id, 8); + + DB::commit(); + return true; + } catch (Exception $e) { + DB::rollBack(); + throw $e; + } + } +} diff --git a/database/migrations/2026_06_02_143449_create_parking_departure_reason_table.php b/database/migrations/2026_06_02_143449_create_parking_departure_reason_table.php new file mode 100644 index 0000000..cb3b87f --- /dev/null +++ b/database/migrations/2026_06_02_143449_create_parking_departure_reason_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('reason', 255)->comment('离场原因'); + $table->string('remark', 255)->default('')->comment('备注'); + $table->timestamps(); + $table->softDeletes(); + $table->innoDb(); + $table->comment('离职原因'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_guard_booth'); + } +}; diff --git a/resources/lang/en/log.php b/resources/lang/en/log.php index c83d15f..f91c13c 100644 --- a/resources/lang/en/log.php +++ b/resources/lang/en/log.php @@ -111,5 +111,10 @@ return [ 'create' => 'Create a booth', 'update' => 'Update the booth', 'delete' => 'Delete the booth' + ], + 'departure_management' => [ + 'create' => 'Create exit reason', + 'update' => 'Update the reason for departure', + 'delete' => 'Delete exit reason' ] ]; diff --git a/resources/lang/en/service.php b/resources/lang/en/service.php index 7d65c61..9f4c458 100644 --- a/resources/lang/en/service.php +++ b/resources/lang/en/service.php @@ -107,5 +107,8 @@ return [ ], 'guard_booth_management' => [ 'name_exists' => 'The booth name already exists' + ], + 'departure_management' => [ + 'reason_exists' => 'Reason for departure already exists' ] ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index b9b9cd5..204c6f3 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -146,5 +146,8 @@ return [ ], 'guard_booth_management' => [ 'n_empty' => 'The booth name cannot be empty', + ], + 'departure_management' => [ + 'n_empty' => 'Reason for departure cannot be empty', ] ]; diff --git a/resources/lang/zh-CN/log.php b/resources/lang/zh-CN/log.php index a912558..2049ab6 100644 --- a/resources/lang/zh-CN/log.php +++ b/resources/lang/zh-CN/log.php @@ -111,5 +111,10 @@ return [ 'create' => '创建岗亭', 'update' => '更新岗亭', 'delete' => '删除岗亭' + ], + 'departure_management' => [ + 'create' => '创建离场原因', + 'update' => '更新离场原因', + 'delete' => '删除离场原因' ] ]; diff --git a/resources/lang/zh-CN/service.php b/resources/lang/zh-CN/service.php index d88c82f..f174309 100644 --- a/resources/lang/zh-CN/service.php +++ b/resources/lang/zh-CN/service.php @@ -107,5 +107,8 @@ return [ ], 'guard_booth_management' => [ 'name_exists' => '岗亭名称已存在' + ], + 'departure_management' => [ + 'reason_exists' => '离场原因已存在' ] ]; diff --git a/resources/lang/zh-CN/validation.php b/resources/lang/zh-CN/validation.php index 63d9730..d084f54 100644 --- a/resources/lang/zh-CN/validation.php +++ b/resources/lang/zh-CN/validation.php @@ -146,5 +146,8 @@ return [ ], 'guard_booth_management' => [ 'n_empty' => '岗亭名称不能为空', + ], + 'departure_management' => [ + 'n_empty' => '离场原因不能为空', ] ]; diff --git a/resources/lang/zh-TW/log.php b/resources/lang/zh-TW/log.php index 1dca249..e7723de 100644 --- a/resources/lang/zh-TW/log.php +++ b/resources/lang/zh-TW/log.php @@ -111,5 +111,10 @@ return [ 'create' => '創建崗亭', 'update' => '更新崗亭', 'delete' => '删除崗亭' + ], + 'departure_management' => [ + 'create' => '創建離場原因', + 'update' => '更新離場原因', + 'delete' => '删除離場原因' ] ]; diff --git a/resources/lang/zh-TW/service.php b/resources/lang/zh-TW/service.php index abfbf65..7297a80 100644 --- a/resources/lang/zh-TW/service.php +++ b/resources/lang/zh-TW/service.php @@ -107,5 +107,8 @@ return [ ], 'guard_booth_management' => [ 'name_exists' => '崗亭名稱已存在' + ], + 'departure_management' => [ + 'reason_exists' => '離場原因已存在' ] ]; diff --git a/resources/lang/zh-TW/validation.php b/resources/lang/zh-TW/validation.php index 1774c08..61b823f 100644 --- a/resources/lang/zh-TW/validation.php +++ b/resources/lang/zh-TW/validation.php @@ -146,5 +146,8 @@ return [ ], 'guard_booth_management' => [ 'n_empty' => '崗亭名稱不能為空', + ], + 'departure_management' => [ + 'n_empty' => '離場原因不能為空', ] ]; diff --git a/routes/admin/api.php b/routes/admin/api.php index ea1c31b..94a3a4e 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -33,6 +33,7 @@ use App\Http\Controllers\Admin\ParkingAttendantController; use App\Http\Controllers\Admin\RegionalManagementController; use App\Http\Controllers\Admin\ParkingChannelController; use App\Http\Controllers\Admin\GuardBoothManagementController; +use App\Http\Controllers\Admin\ParkingDepartureReasonController; Route::group(['prefix' => 'admin'], function () { @@ -256,6 +257,13 @@ Route::group(['prefix' => 'admin'], function () { Route::put('/guardBoothManagement/{id}', [GuardBoothManagementController::class, 'update']); Route::delete('/guardBoothManagement/{id}', [GuardBoothManagementController::class, 'destroy']); Route::get('/guardBoothManagement/rule', [GuardBoothManagementController::class, 'rule']); + // 离职原因管理 + Route::get('/departureReasonManagement', [ParkingDepartureReasonController::class, 'index']); + Route::post('/departureReasonManagement', [ParkingDepartureReasonController::class, 'store']); + Route::get('/departureReasonManagement/edit/{id}', [ParkingDepartureReasonController::class, 'edit']); + Route::put('/departureReasonManagement/{id}', [ParkingDepartureReasonController::class, 'update']); + Route::delete('/departureReasonManagement/{id}', [ParkingDepartureReasonController::class, 'destroy']); + Route::get('/departureReasonManagement/rule', [ParkingDepartureReasonController::class, 'rule']); // 角色 Route::get('/roles', [RolesController::class, 'index']); Route::get('/roles/create', [RolesController::class, 'create']);