diff --git a/app/Exports/ParkingManualCorrectionExport.php b/app/Exports/ParkingManualCorrectionExport.php new file mode 100644 index 0000000..f385ad1 --- /dev/null +++ b/app/Exports/ParkingManualCorrectionExport.php @@ -0,0 +1,62 @@ +parents = $parents; + $this->service = new ParkingManualCorrectionService( + new OperationLogService() + ); + } + + public function array(): array + { + $data = []; + $index = 1; + $query = $this->service->indexQuery($this->parents); + $query->select()->get()->each( + function ($item) use (&$data, &$index) { + $oldItem = $this->service->getItem($item); + $data[] = [ + 'id' => $index, + 'enter_time' => $oldItem['enter_time'], + 'space_number' => $oldItem['space_number'], + 'old_license_place' => $oldItem['old_license_place'], + 'updated_at' => $oldItem['update_time'], + 'username' => $oldItem['username'], + 'new_license_place' => $oldItem['new_license_place'] + ]; + $index += 1; + } + ); + return $data; + } + + /** + * @return array + */ + public function headings(): array + { + return [ + __('exports.global.index'), + __('exports.parking_space.enter_time'), + __('exports.parking_space.space_number'), + __('exports.parking_space.old_license_place'), + __('exports.parking_space.updated_at'), + __('exports.parking_space.username'), + __('exports.parking_space.new_license_place') + ]; + } +} diff --git a/app/Http/Controllers/Admin/ParkingManualCorrectionController.php b/app/Http/Controllers/Admin/ParkingManualCorrectionController.php new file mode 100644 index 0000000..91b2983 --- /dev/null +++ b/app/Http/Controllers/Admin/ParkingManualCorrectionController.php @@ -0,0 +1,81 @@ +service = $service; + } + + /** + * Display a listing of the resource. + */ + public function index(Request $request): JsonResponse + { + try { + $query = $this->service->indexQuery($request->all()); + + // 分页 + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $total = $query->count(); + $_this = $this; + $items = $query->latest()->forPage($page, $perPage)->get()->each( + function ($item) use ($_this) { + return $_this->service->getItem($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 BinaryFileResponse + */ + public function export(Request $request): BinaryFileResponse + { + return Excel::download( + new ParkingManualCorrectionExport($request->all()), + set_space_underline(__('exports.manual_correction.list')) + . get_datetime('date_') + . '.xlsx' + ); + } +} diff --git a/app/Models/ParkingManualCorrection.php b/app/Models/ParkingManualCorrection.php new file mode 100644 index 0000000..37d1530 --- /dev/null +++ b/app/Models/ParkingManualCorrection.php @@ -0,0 +1,35 @@ +isMainlandChinaPlate($plate)) { // 中国大陆车牌校验(禁止 I、O) - $pattern - = '/^[\x{4e00}-\x{9fa5}][A-Z](?:[A-HJ-NP-Z0-9]{6}|(?:[A-HJ-NP-Z0-9][0-9]{4}|[0-9]{5}))$/u'; + $pattern = '/^[\x{4e00}-\x{9fa5}][A-Z](?:[A-HJ-NP-Z0-9]{6}|(?:[A-HJ-NP-Z0-9][0-9]{4}|[A-HJ-NP-Z0-9]{5}))$/u'; if (!preg_match($pattern, $plate)) { return __validation('parking_space.zh_plate'); } diff --git a/app/Services/ParkingManualCorrectionService.php b/app/Services/ParkingManualCorrectionService.php new file mode 100644 index 0000000..2fa34b0 --- /dev/null +++ b/app/Services/ParkingManualCorrectionService.php @@ -0,0 +1,121 @@ + $data['enter_time'], + 'space_id' => $data['space_id'], + 'old_license_id' => $data['old_license_id'], + 'admin_user_id' => $user_id, + 'new_license_id' => $data['new_license_id'], + 'created_at' => date("Y-m-d H:i:s", time()) + ]; + $model = ParkingManualCorrection::query()->create($data); + $this->logService->logCreated($model, $this->menuTitle . '.create'); + $data = $model->toArray(); + return $data['id'] ?? 0; + } + + public function indexQuery($param) + { + $query = ParkingManualCorrection::query(); + + if (isset($param['space_number']) && !empty($param['space_number'])) { + $space_ids = ParkingSpace::getId($param['space_number']); + if ($space_ids) { + $query->whereIn('space_id', $space_ids); + } else { + $query->where('id', 0); + } + } + + if (isset($param['old_license_place']) + && !empty($param['old_license_place']) + ) { + $old_id_arr = ParkingLicensePlate::getId( + $param['old_license_place'] + ); + $old_id_arr ? $query->whereIn( + 'old_license_id', + $old_id_arr + ) : $query->where('id', 0); + } + + if (isset($param['start_date']) + && !empty($param['start_date']) + && strtotime($param['start_date']) + ) { + $start_date = date( + "Y-m-d 00:00:00", + strtotime($param['start_date']) + ); + if ($start_date) { + $query->where('created_at', '>=', $start_date); + } + } + + if (isset($param['end_date']) + && !empty($param['end_date']) + && strtotime($param['end_date']) + ) { + $end_date = date( + "Y-m-d 00:00:00", + strtotime($param['end_date']) + ); + if ($end_date) { + $query->where('created_at', '<', $end_date); + } + } + + if (isset($param['username']) && !empty($param['username'])) { + $ids = AdminUsers::getIds($param['username']); + if ($ids) { + $query->whereIn('admin_user_id', $ids); + } else { + $query->where('id', 0); + } + } + + if (isset($param['new_license_place']) + && !empty($param['new_license_place']) + ) { + $new_id_arr = ParkingLicensePlate::getId( + $param['new_license_place'] + ); + $new_id_arr ? $query->whereIn( + 'new_license_id', + $new_id_arr + ) : $query->where('id', 0); + } + + $query->orderBy('id', 'desc'); + + return $query; + } + + public function getItem($item) + { + $item['space_number'] = ParkingSpace::getNumber($item['space_id']); + $item['old_license_place'] = ParkingLicensePlate::getNumber( + $item['old_license_id'] + ); + $item['new_license_place'] = ParkingLicensePlate::getNumber( + $item['new_license_id'] + ); + $item['username'] = AdminUsers::getUsername($item['admin_user_id']); + $item['update_time'] = $item['created_at']; + unset($item['space_id'], $item['old_license_id'], $item['new_license_id'], $item['admin_user_id'], $item['created_at']); + return $item; + } +} diff --git a/app/Services/ParkingSpaceService.php b/app/Services/ParkingSpaceService.php index 6c52285..8a7f9e2 100644 --- a/app/Services/ParkingSpaceService.php +++ b/app/Services/ParkingSpaceService.php @@ -362,6 +362,17 @@ class ParkingSpaceService extends BaseService $oldValues['space_attr_id'], 1 ); + if ($number_id && $oldValues['license_plate_id']) { + $manualService = new ParkingManualCorrectionService( + $this->logService + ); + $manualService->createData([ + 'enter_time' => $oldValues['berthing_time'], + 'space_id' => $oldValues['id'], + 'old_license_id' => $oldValues['license_plate_id'], + 'new_license_id' => $number_id + ], $this->adminUserId); + } } else if ($status == 4) { // 占用无车牌 $update['license_plate_id'] = 0; diff --git a/database/migrations/2026_09_09_143449_create_parking_manual_correction_table.php b/database/migrations/2026_09_09_143449_create_parking_manual_correction_table.php new file mode 100644 index 0000000..0dc151b --- /dev/null +++ b/database/migrations/2026_09_09_143449_create_parking_manual_correction_table.php @@ -0,0 +1,33 @@ +id(); + $table->dateTime('enter_time')->comment('进入车位时间'); + $table->integer('space_id')->comment('车位id'); + $table->integer('old_license_id')->comment('原本车牌号码'); + $table->integer('admin_user_id')->comment('操作员'); + $table->integer('new_license_id')->comment('现时车牌号码'); + $table->index('space_id', 'space_id'); + $table->comment('车位车牌修正记录'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_manual_correction'); + } +}; diff --git a/resources/lang/en/exports.php b/resources/lang/en/exports.php index ee8e180..4fd96df 100644 --- a/resources/lang/en/exports.php +++ b/resources/lang/en/exports.php @@ -125,5 +125,14 @@ return [ ], 'utilization_rate' => [ 'rate' => 'Usage Rate' + ], + 'manual_correction' => [ + 'list' => 'License Plate Correction Record', + 'enter_time' => 'Entry Time', + 'space_number' => 'Parking Space Number', + 'old_license_place' => 'Original License Plate Number', + 'updated_at' => 'Modification Time', + 'username' => 'Operator', + 'new_license_place' => 'Current License Plate Number', ] ]; diff --git a/resources/lang/en/log.php b/resources/lang/en/log.php index 6135120..1d59b75 100644 --- a/resources/lang/en/log.php +++ b/resources/lang/en/log.php @@ -157,5 +157,8 @@ return [ 'access_record' => [ 'create' => 'Set up vehicle entry and exit for parking spaces.', 'update' => 'Update vehicle entry and exit records for parking spaces.' + ], + 'manual_correction' => [ + 'create' => 'Create license plate correction record' ] ]; diff --git a/resources/lang/zh-CN/exports.php b/resources/lang/zh-CN/exports.php index e408985..b7a5f94 100644 --- a/resources/lang/zh-CN/exports.php +++ b/resources/lang/zh-CN/exports.php @@ -125,5 +125,14 @@ return [ ], 'utilization_rate' => [ 'rate' => '使用率' + ], + 'manual_correction' => [ + 'list' => '车牌修正记录', + 'enter_time' => '进入车位时间', + 'space_number' => '车位号码', + 'old_license_place' => '原本车牌号码', + 'updated_at' => '修改时间', + 'username' => '操作员', + 'new_license_place' => '现时车牌号码', ] ]; diff --git a/resources/lang/zh-CN/log.php b/resources/lang/zh-CN/log.php index 5848249..432ab50 100644 --- a/resources/lang/zh-CN/log.php +++ b/resources/lang/zh-CN/log.php @@ -157,5 +157,8 @@ return [ 'access_record' => [ 'create' => '创建车位车辆进出', 'update' => '更新车位车辆进出' + ], + 'manual_correction' => [ + 'create' => '创建车牌修正记录' ] ]; diff --git a/resources/lang/zh-TW/exports.php b/resources/lang/zh-TW/exports.php index aaebeda..dedb581 100644 --- a/resources/lang/zh-TW/exports.php +++ b/resources/lang/zh-TW/exports.php @@ -125,5 +125,14 @@ return [ ], 'utilization_rate' => [ 'rate' => '使用率' + ], + 'manual_correction' => [ + 'list' => '車牌修正記錄', + 'enter_time' => '進入車位時間', + 'space_number' => '車位號碼', + 'old_license_place' => '原本車牌號碼', + 'updated_at' => '修改時間', + 'username' => '操作員', + 'new_license_place' => '現時車牌號碼', ] ]; diff --git a/resources/lang/zh-TW/log.php b/resources/lang/zh-TW/log.php index 5af147e..72c113c 100644 --- a/resources/lang/zh-TW/log.php +++ b/resources/lang/zh-TW/log.php @@ -157,5 +157,8 @@ return [ 'access_record' => [ 'create' => '創建車位車輛進出', 'update' => '更新車位車輛進出' + ], + 'manual_correction' => [ + 'create' => '創建車牌修正記錄' ] ]; diff --git a/routes/admin/api.php b/routes/admin/api.php index ea1a3c7..8682cef 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -44,7 +44,7 @@ use App\Http\Controllers\Admin\ParkingGateControlController; use App\Http\Controllers\Admin\ProhibitedPassageController; use App\Http\Controllers\Admin\ParkingAccessRecordController; use App\Http\Controllers\Admin\UtilizationRateController; - +use App\Http\Controllers\Admin\ParkingManualCorrectionController; Route::group(['prefix' => 'admin'], function () { // 认证相关接口 @@ -264,6 +264,10 @@ Route::group(['prefix' => 'admin'], function () { Route::get('/utilizationRate/search', [UtilizationRateController::class, 'search']); Route::get('/utilizationRate/rule', [UtilizationRateController::class, 'rule']); Route::get('/utilizationRate/export', [UtilizationRateController::class, 'export']); + // 车牌修正记录 + Route::get('/manualCorrection', [ParkingManualCorrectionController::class, 'index']); + Route::get('/manualCorrection/rule', [ParkingManualCorrectionController::class, 'rule']); + Route::get('/manualCorrection/export', [ParkingManualCorrectionController::class, 'export']); // 系统日志 Route::get('/operationLog/index', [OperationLogController::class, 'index']);