From 471f7acac75864b56f0149048b2aa68373abe061 Mon Sep 17 00:00:00 2001 From: wanghongjun <1445693971@qq.com> Date: Mon, 24 Aug 2026 15:48:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=81=9C=E8=BD=A6=E5=8E=86=E5=8F=B2=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Exports/UtilizationRateExport.php | 62 ++++++++ .../Admin/UtilizationRateController.php | 138 ++++++++++++++++++ app/Models/ParkingAccessRecord.php | 4 +- app/Models/ParkingUtilizationRate.php | 30 ++++ app/Services/UtilizationRateService.php | 91 ++++++++++++ ...create_parking_vip_access_record_table.php | 2 +- ..._create_parking_utilization_rate_table.php | 34 +++++ resources/lang/en/exports.php | 3 + resources/lang/zh-CN/exports.php | 3 + resources/lang/zh-TW/exports.php | 3 + routes/admin/api.php | 9 +- 11 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 app/Exports/UtilizationRateExport.php create mode 100644 app/Http/Controllers/Admin/UtilizationRateController.php create mode 100644 app/Models/ParkingUtilizationRate.php create mode 100644 app/Services/UtilizationRateService.php create mode 100644 database/migrations/2026_08_21_143449_create_parking_utilization_rate_table.php diff --git a/app/Exports/UtilizationRateExport.php b/app/Exports/UtilizationRateExport.php new file mode 100644 index 0000000..31b9bb8 --- /dev/null +++ b/app/Exports/UtilizationRateExport.php @@ -0,0 +1,62 @@ +parents = $parents; + $this->service = new UtilizationRateService( + new OperationLogService() + ); + } + + public function array(): array + { + $data = []; + $index = 1; + $query = $this->service->searchQuery($this->parents); + $list = $query->select()->get()->toArray(); + foreach ($list as $item) { + $item = $this->service->getItem($item); + $data[] = [ + 'index' => $index, + 'start_time' => $item['start_time'], + 'end_time' => $item['end_time'], + 'floor' => $item['floor'], + 'number' => $item['number'], + 'space_type' => $item['space_type'], + 'space_attr' => $item['space_attr'], + 'rate' => $item['rate'] + ]; + $index += 1; + } + return $data; + } + + /** + * @return array + */ + public function headings(): array + { + return [ + __('exports.global.index'), + __('exports.event_calendar.export3'), + __('exports.event_calendar.export5'), + __('exports.parking_space.floor'), + __('exports.parking_space.number'), + __('exports.parking_space.space_type'), + __('exports.parking_space.space_attr'), + __('exports.utilization_rate.rate'), + ]; + } +} diff --git a/app/Http/Controllers/Admin/UtilizationRateController.php b/app/Http/Controllers/Admin/UtilizationRateController.php new file mode 100644 index 0000000..270c81d --- /dev/null +++ b/app/Http/Controllers/Admin/UtilizationRateController.php @@ -0,0 +1,138 @@ +service = $service; + } + + public function index(Request $request): JsonResponse + { + try { + $query = $this->service->searchQuery($request->all()); + + // 分页 + $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) { + 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() + ); + } + } + + public function illustration(Request $request): JsonResponse + { + try { + $data = []; + $query = $this->service->searchQuery($request->all()); + $list = $query->select()->get()->toArray(); + $tempData = []; + foreach ($list as $item) { + $date_type = $item['date_type']; + $format = 'H:i'; + if ($date_type == 2) { + $format = 'm/d'; + } + $key = date($format, strtotime($item['end_time'])); + $tempData[$key][] = $item['rate']; + } + foreach ($tempData as $date => $value) { + $count = count($value); + $sum = 0; + foreach ($value as $val) { + $sum += $val; + } + $rate = 0; + if ($sum > 0) { + $rate = round($sum / $count, 2); + } + $data[] = [ + 'time' => $date, + 'rate' => $rate + ]; + } + return $this->responseService->success($data); + } catch (Exception $e) { + $m_prefix = __('exception.exception_handler.resource'); + return $this->responseService->systemError( + $m_prefix . ':' . $e->getMessage() + ); + } + } + + public function search(): JsonResponse + { + try { + $data = [ + 'floor_list' => AdminFloor::getData(), + '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() + ); + } + } + + /** + * @param Request $request + * @return BinaryFileResponse + */ + public function export(Request $request): BinaryFileResponse + { + return Excel::download( + new UtilizationRateExport($request->all()), + set_space_underline(__('exports.access_record.list')) + . get_datetime('date_') + . '.xlsx' + ); + } +} diff --git a/app/Models/ParkingAccessRecord.php b/app/Models/ParkingAccessRecord.php index 4fa6a24..21b3990 100644 --- a/app/Models/ParkingAccessRecord.php +++ b/app/Models/ParkingAccessRecord.php @@ -30,12 +30,12 @@ class ParkingAccessRecord extends Model public function getEnterTimeAttribute($value): string { - return get_datetime('datetime', $value); + return date('Y-m-d H:i:s', $value); } public function getLeaveTimeAttribute($value): string { - return $value ? get_datetime('datetime', $value) : ''; + return $value ? date('Y-m-d H:i:s', $value) : ''; } } diff --git a/app/Models/ParkingUtilizationRate.php b/app/Models/ParkingUtilizationRate.php new file mode 100644 index 0000000..2cbfdc0 --- /dev/null +++ b/app/Models/ParkingUtilizationRate.php @@ -0,0 +1,30 @@ +find( + $item['space_id'] + ); + if ($parkingSpace) { + $item['floor'] = AdminFloor::getName( + $parkingSpace['floor_id'] + ); + $item['number'] = $parkingSpace['number']; + $item['space_type'] = ParkingSpaceType::getName( + $parkingSpace['space_type_id'] + ); + $item['space_attr'] = ParkingSpaceAttributes::getAttr( + $parkingSpace['space_attr_id'] + ); + } + $item['rate'] = rtrim($item['rate'], ' ') . '%'; + $format = $item['date_type'] == 1 ? 'Y/m/d H:i' : 'Y/m/d'; + $item['start_time'] = date($format, strtotime($item['start_time'])); + $item['end_time'] = date($format, strtotime($item['end_time'])); + unset($item['space_id'], $item['date_type']); + return $item; + } + + /** + * @param $parents + * @return Builder + */ + public function searchQuery($parents): Builder + { + $query = ParkingUtilizationRate::query(); + $start_time = $parents['start_time'] ?? + get_datetime('date') . ' 00:00:00'; + $ent_time = $parents['end_time'] ?? + get_datetime('date') . ' 23:59:59'; + + $start_date = get_datetime('date', strtotime($start_time)); + $end_date = get_datetime('date', strtotime($ent_time)); + $date_type = $start_date == $end_date ? 1 : 2; + + $query->where('date_type', $date_type); + $query->where('start_time', '>=', $start_time); + $query->where('end_time', '<', $ent_time); + + $spaceIdsWhere = []; + if (isset($parents['floor_id']) && $parents['floor_id']) { + $spaceIdsWhere['floor_id'] = $parents['floor_id']; + } + if (isset($parents['number']) && $parents['number']) { + $spaceIdsWhere['number'] = $parents['number']; + } + + if (isset($parents['parking_space_type']) + && $parents['parking_space_type'] + ) { + $spaceIdsWhere['space_type_id'] = $parents['parking_space_type']; + } + + if ($spaceIdsWhere) { + $spaceIds = ParkingSpace::query()->where($spaceIdsWhere)->pluck( + 'id' + ); + if ($spaceIds) { + $query->whereIn('space_id', $spaceIds); + } else { + $query->where('id', 0); + } + } + return $query; + } +} diff --git a/database/migrations/2026_03_04_160517_create_parking_vip_access_record_table.php b/database/migrations/2026_03_04_160517_create_parking_vip_access_record_table.php index 0375e12..07eef63 100644 --- a/database/migrations/2026_03_04_160517_create_parking_vip_access_record_table.php +++ b/database/migrations/2026_03_04_160517_create_parking_vip_access_record_table.php @@ -29,6 +29,6 @@ return new class extends Migration */ public function down(): void { - Schema::dropIfExists('admin_vip_access_record'); + Schema::dropIfExists('parking_vip_access_record'); } }; diff --git a/database/migrations/2026_08_21_143449_create_parking_utilization_rate_table.php b/database/migrations/2026_08_21_143449_create_parking_utilization_rate_table.php new file mode 100644 index 0000000..ddabb1e --- /dev/null +++ b/database/migrations/2026_08_21_143449_create_parking_utilization_rate_table.php @@ -0,0 +1,34 @@ +id(); + $table->integer('space_id')->comment('车位id'); + $table->dateTime('start_time')->comment('开始时间'); + $table->dateTime('end_time')->comment('结束时间'); + $table->tinyInteger('date_type')->default(1)->comment('类型 1小时 2天'); + $table->float('rate', 5)->comment('使用率'); + $table->timestamp('created_at'); + $table->index('space_id', 'space_id'); + $table->comment('车位使用率'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('parking_utilization_rate'); + } +}; diff --git a/resources/lang/en/exports.php b/resources/lang/en/exports.php index e8614c7..3af9dbd 100644 --- a/resources/lang/en/exports.php +++ b/resources/lang/en/exports.php @@ -120,5 +120,8 @@ return [ 'leave_time' => 'Leave Time', 'duration' => 'Parking duration (mins)', 'status' => 'Vehicle status (entering/exiting)' + ], + 'utilization_rate' => [ + 'rate' => 'Usage Rate' ] ]; diff --git a/resources/lang/zh-CN/exports.php b/resources/lang/zh-CN/exports.php index 44ce69f..3a49d27 100644 --- a/resources/lang/zh-CN/exports.php +++ b/resources/lang/zh-CN/exports.php @@ -120,5 +120,8 @@ return [ 'leave_time' => '离开车位时间', 'duration' => '停车时长(mins)', 'status' => '车辆状态(入/出)' + ], + 'utilization_rate' => [ + 'rate' => '使用率' ] ]; diff --git a/resources/lang/zh-TW/exports.php b/resources/lang/zh-TW/exports.php index f0f5d4d..5a67f39 100644 --- a/resources/lang/zh-TW/exports.php +++ b/resources/lang/zh-TW/exports.php @@ -120,5 +120,8 @@ return [ 'leave_time' => '離開車位時間', 'duration' => '停車時長(mins)', 'status' => '車輛狀態(入/出)' + ], + 'utilization_rate' => [ + 'rate' => '使用率' ] ]; diff --git a/routes/admin/api.php b/routes/admin/api.php index 14d7665..30a8eb3 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -43,6 +43,7 @@ use App\Http\Controllers\Admin\ParkingSpaceRepairController; use App\Http\Controllers\Admin\ParkingGateControlController; use App\Http\Controllers\Admin\ProhibitedPassageController; use App\Http\Controllers\Admin\ParkingAccessRecordController; +use App\Http\Controllers\Admin\UtilizationRateController; Route::group(['prefix' => 'admin'], function () { @@ -251,11 +252,17 @@ Route::group(['prefix' => 'admin'], function () { Route::get('/parkingBehavior', [ParkingBehaviorController::class, 'index']); Route::get('/parkingBehavior/rule', [ParkingBehaviorController::class, 'rule']); Route::get('/parkingBehavior/export', [ParkingBehaviorController::class, 'export']); - // 停车行为 + // 停车历史 Route::get('/accessRecord', [ParkingAccessRecordController::class, 'index']); Route::get('/accessRecord/search', [ParkingAccessRecordController::class, 'search']); Route::get('/accessRecord/rule', [ParkingAccessRecordController::class, 'rule']); Route::get('/accessRecord/export', [ParkingAccessRecordController::class, 'export']); + // 车位使用率 + Route::get('/utilizationRate', [UtilizationRateController::class, 'index']); + Route::get('/utilizationRate/index', [UtilizationRateController::class, 'illustration']); + Route::get('/utilizationRate/search', [UtilizationRateController::class, 'search']); + Route::get('/utilizationRate/rule', [UtilizationRateController::class, 'rule']); + Route::get('/utilizationRate/export', [UtilizationRateController::class, 'export']); // 系统日志 Route::get('/operationLog/index', [OperationLogController::class, 'index']);