diff --git a/app/Http/Controllers/Admin/ParkingReservationController.php b/app/Http/Controllers/Admin/ParkingReservationController.php index f486296..66b757f 100644 --- a/app/Http/Controllers/Admin/ParkingReservationController.php +++ b/app/Http/Controllers/Admin/ParkingReservationController.php @@ -2,15 +2,19 @@ namespace App\Http\Controllers\Admin; -use App\Http\Controllers\Controller; use App\Models\AdminFloor; use App\Models\AdminFloorRegion; +use App\Models\EventCalendar; +use App\Models\Parking; +use App\Models\ParkingChannel; use App\Models\ParkingLicensePlate; +use App\Models\ParkingPattern; use App\Models\ParkingReservation; use App\Models\ParkingSpace; use App\Models\ParkingSpaceAttributes; use App\Models\ParkingSpaceType; use App\Services\ApiResponseService; +use App\Services\ParkingAdminUserService; use App\Services\ParkingReservationService; use Exception; use Illuminate\Http\JsonResponse; @@ -90,6 +94,7 @@ class ParkingReservationController extends BaseController if ($request->has('space_attr_id')) { $space_attr_id = $request->input('space_attr_id'); if (!empty($space_attr_id)) { + $query->where('space_attr_id', $space_attr_id); } } if ($request->has('status')) { @@ -98,20 +103,74 @@ class ParkingReservationController extends BaseController $query->where('status', $status); } } + if ($request->has('parking_id')) { + $parking_id = $request->input('parking_id'); + if (!empty($parking_id)) { + $query->where('parking_id', $parking_id); + } + } + + if ($request->has('member_type')) { + $member_type = $request->input('member_type'); + if (!empty($member_type)) { + $query->where('member_type', $member_type); + } + } + if ($request->has('customer_id')) { + $customer_id = $request->input('customer_id'); + if (!empty($customer_id)) { + $query->where('customer_id', $customer_id); + } + } + + if ($request->has('member_id')) { + $member_id = $request->input('member_id'); + if (!empty($member_id)) { + $query->where('member_id', $member_id); + } + } + + if ($request->has('start_date')) { + $start_date = $request->input('start_date'); + if (!empty($start_date)) { + $query->where('date', '>=', $start_date); + } + } + + if ($request->has('end_date')) { + $end_date = $request->input('end_date'); + if (!empty($end_date)) { + $query->where('date', '<=', $end_date); + } + } + + if ($request->has('reserve_id')) { + $reserve_id = $request->input('reserve_id'); + if (!empty($reserve_id)) { + $query->where('reserve_id', $reserve_id); + } + } // 分页 $page = $request->input('page', 1); $perPage = $request->input('per_page', 10); + $memberTypeArr = ParkingAdminUserService::$memberType; $total = $query->count(); $items = $query->latest()->forPage($page, $perPage)->get()->each( - function ($item) { + function ($item) use ($memberTypeArr) { + $item['member_type'] = $memberTypeArr[$item['member_type']] ?? ''; + $item['channel'] = ParkingChannel::getName($item['channel_id']); + $item['parking'] = Parking::getName($item['parking_id']); $item['status'] = $this->service->getStatusStr( $item['status'] ); $item['space_type'] = ParkingSpaceType::getName( $item['space_type_id'] ); + $item['space_attr'] = ParkingSpaceAttributes::getAttr( + $item['space_attr_id'] + ); $item['license_plate'] = ParkingLicensePlate::getNumber( $item['license_plate_id'] ); @@ -125,7 +184,10 @@ class ParkingReservationController extends BaseController $item['space_type_id'], $item['license_plate_id'], $item['floor_id'], - $item['floor_region_id'] + $item['floor_region_id'], + $item['space_attr_id'], + $item['channel_id'], + $item['parking_id'] ); return $item; } @@ -155,7 +217,9 @@ class ParkingReservationController extends BaseController 'space_attr_data' => ParkingSpaceAttributes::getData(), 'status_data' => get_select_data( $this->service->getStatusArr() - ) + ), + 'parking_list' => Parking::getData(), + 'member_type' => get_select_data(ParkingAdminUserService::$memberType) ]; return $this->responseService->success($data); } catch (Exception $e) { @@ -166,6 +230,58 @@ class ParkingReservationController extends BaseController } } + public function show(Request $request): JsonResponse + { + try { + $id = $request->input('id', 0); + $query = ParkingReservation::query(); + $query->where('id', $id); + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $memberTypeArr = ParkingAdminUserService::$memberType; + + $total = $query->count(); + $reservation_number = ''; + $items = $query->latest()->forPage($page, $perPage)->get()->each( + function ($item) use ($memberTypeArr, &$reservation_number) { + $item['member_type'] = $memberTypeArr[$item['member_type']] ?? ''; + $item['license_plate'] = ParkingLicensePlate::getNumber( + $item['license_plate_id'] + ); + $item['is_driver'] = $this->service->getIsDriver( + $item['is_driver'] + ); + $item['order_number'] = $item['reserve_id']; + $reservation_number = $item['reserve_id']; + unset( + $item['space_type_id'], + $item['license_plate_id'], + $item['floor_id'], + $item['floor_region_id'], + $item['space_attr_id'], + $item['channel_id'], + $item['parking_id'], + $item['status'] + ); + return $item; + } + ); + + return $this->responseService->success([ + 'items' => $items, + 'total' => $total, + 'page' => $page, + 'per_page' => $perPage, + 'last_page' => ceil($total / $perPage), + 'reservation_number' => $reservation_number + ]); + } catch (Exception $e) { + return $this->responseService->systemError( + __('exception.get_data_failed') . ':' . $e->getMessage() + ); + } + } + public function statistics(Request $request): JsonResponse { try { @@ -204,4 +320,50 @@ class ParkingReservationController extends BaseController } } + public function getDateStatistics(Request $request): JsonResponse + { + try { + $param = $request->all(); + $date = $param['date'] ?? date("Y-m-d", time()); + + $query = ParkingReservation::query(); + $query->where('date', $date); + if (isset($param['parking_id'])) { + $query->where('parking_id', $param['parking_id']); + } + $total = $query->count(); + + $w = date('w', strtotime($date)); + $week_str = __service("reservation.week{$w}"); + + $patternId = EventCalendar::getDatePatternId($date); + $space_set_name = ''; + if ($patternId) { + $space_set_name = ParkingPattern::getName($patternId); + } + $space_type_ids = $query->pluck('space_type_id')->toArray(); + $member_type_list = []; + if ($space_type_ids) { + $member_type_list = ParkingSpaceType::getData(); + $counts = array_count_values((array)$space_type_ids); + foreach ($member_type_list as &$item) { + $item['count'] = $counts[$item['id']] ?? 0; + unset($item['id']); + } + } + $data = [ + 'date_str' => $date, + 'space_set_name' => $space_set_name, + 'total' => $total, + 'week_str' => $week_str, + 'member_type_list' => $member_type_list + ]; + return $this->responseService->success($data); + } catch (Exception $e) { + $m_prefix = __('exception.exception_handler.resource'); + return $this->responseService->systemError( + $m_prefix . ':' . $e->getMessage() + ); + } + } } diff --git a/app/Models/EventCalendar.php b/app/Models/EventCalendar.php index 2a05fce..6d173c0 100644 --- a/app/Models/EventCalendar.php +++ b/app/Models/EventCalendar.php @@ -38,4 +38,15 @@ class EventCalendar extends Model { return date('Y-m-d H:i', strtotime($value)); } + + public static function getDatePatternId($date) + { + return self::query()->whereIn('status', [0, 1, 2])->where( + function ($query) use ($date) { + $query->whereRaw( + "'{$date}' BETWEEN start_time AND end_time" + ); + } + )->value('pattern_id'); + } } diff --git a/app/Models/ParkingReservation.php b/app/Models/ParkingReservation.php index 5bc4ec8..fef6ecc 100644 --- a/app/Models/ParkingReservation.php +++ b/app/Models/ParkingReservation.php @@ -15,19 +15,28 @@ class ParkingReservation extends Model = [ 'reserve_id', 'space_type_id', + 'space_attr_id', 'license_plate_type', 'license_plate_id', 'is_driver', 'floor_id', 'floor_region_id', 'status', - 'confirm_at' + 'confirm_at', + 'customer_id', + 'member_id', + 'channel_id', + 'member_type', + 'parking_id', + 'remark', + 'date', + 'operation', + 'confirm_status', + 'cancel_at', + 'send_at' ]; - protected $hidden - = [ - 'updated_at' - ]; + protected $hidden = []; public function getCreatedAtAttribute($value): string { diff --git a/app/Models/ParkingSpaceType.php b/app/Models/ParkingSpaceType.php index 75c0b5f..ce41e7a 100644 --- a/app/Models/ParkingSpaceType.php +++ b/app/Models/ParkingSpaceType.php @@ -37,9 +37,13 @@ class ParkingSpaceType extends Model return self::query()->where('id', $id)->value('name') ?? ''; } - public static function getData(): array + public static function getData($ids = []): array { - return self::query()->select(['id', 'name'])->get()->each( + $query = self::query(); + if ($ids) { + $query->whereIn('id', $ids); + } + return $query->select(['id', 'name'])->get()->each( function ($item) { $item['name'] = AdminTranslationService::getTranslationTypeName( diff --git a/database/migrations/2026_03_20_162930_create_parking_reservation_table.php b/database/migrations/2026_03_20_162930_create_parking_reservation_table.php index cfc23b6..83002cc 100644 --- a/database/migrations/2026_03_20_162930_create_parking_reservation_table.php +++ b/database/migrations/2026_03_20_162930_create_parking_reservation_table.php @@ -16,12 +16,24 @@ return new class extends Migration $table->string('reserve_id', 12)->comment('预定编号'); $table->integer('space_type_id')->comment('车位类型'); $table->integer('license_plate_type')->comment('车牌类型'); + $table->integer('space_attr_id')->comment('车位属性'); $table->integer('license_plate_id')->comment('车牌号码ID'); $table->tinyInteger('is_driver')->default(0)->comment('是否登记司机'); $table->integer('floor_id')->nullable()->comment('楼层ID'); $table->integer('floor_region_id')->nullable()->comment('楼层区域ID'); $table->tinyInteger('status')->default(0)->comment('状态 0待确定 1已确定 2已取消'); $table->timestamp('confirm_at')->nullable()->comment('确定时间'); + $table->string('member_id', 50)->default('')->comment('客户ID'); + $table->string('member_id', 50)->default('')->comment('会员ID'); + $table->integer('channel_id')->nullable()->comment('通道ID'); + $table->tinyInteger('member_type')->default(0)->comment('会员类型'); + $table->integer('parking_id')->nullable()->comment('停车场id'); + $table->string('remark', 255)->default('')->comment('备注'); + $table->date('date')->comment('预约日期'); + $table->string('varchar', 255)->default('')->comment('操作'); + $table->string('confirm_status', 255)->default('')->comment('预约状态'); + $table->timestamp('cancel_at')->nullable()->comment('取消时间'); + $table->timestamp('send_at')->nullable()->comment('预约发起时间'); $table->timestamps(); $table->comment('车位预定列表'); $table->innoDb(); diff --git a/database/seeders/AdminMenuSeeder.php b/database/seeders/AdminMenuSeeder.php index fe8848c..6b9e555 100644 --- a/database/seeders/AdminMenuSeeder.php +++ b/database/seeders/AdminMenuSeeder.php @@ -483,15 +483,15 @@ class AdminMenuSeeder extends Seeder ] ], 'parking_space_management' => [ - 'uri' => 'parkingManagement', + 'uri' => 'parkingSpaceManagement', 'page_uri' => '/parkingmanagement/parkingManagement', 'child' => [ - 'read_only' => 'parkingManagement.index', - 'add' => 'parkingManagement.store', - 'edit' => 'parkingManagement.update', - 'delete' => 'parkingManagement.destroy', - 'batch_update_attr' => 'parkingManagement.batchUpdateAttr', - 'batch_delete' => 'parkingManagement.batchDelete' + 'read_only' => 'parkingSpaceManagement.index', + 'add' => 'parkingSpaceManagement.store', + 'edit' => 'parkingSpaceManagement.update', + 'delete' => 'parkingSpaceManagement.destroy', + 'batch_update_attr' => 'parkingSpaceManagement.batchUpdateAttr', + 'batch_delete' => 'parkingSpaceManagement.batchDelete' ] ], ], diff --git a/resources/lang/en/service.php b/resources/lang/en/service.php index 3a9a266..7b5346f 100644 --- a/resources/lang/en/service.php +++ b/resources/lang/en/service.php @@ -50,7 +50,14 @@ return [ 'yes' => 'yes', 'no' => 'No', 'fuel' => 'fuel', - 'new_energy' => 'new energy' + 'new_energy' => 'new energy', + 'week0' => 'Sunday', + 'week1' => 'Monday', + 'week2' => 'Tuesday', + 'week3' => 'Wednesday', + 'week4' => 'Thursday', + 'week5' => 'Friday', + 'week6' => 'Saturday' ], 'parking_space' => [ 'vacant' => 'vacant', diff --git a/resources/lang/zh-CN/service.php b/resources/lang/zh-CN/service.php index b05753f..761d67a 100644 --- a/resources/lang/zh-CN/service.php +++ b/resources/lang/zh-CN/service.php @@ -50,7 +50,14 @@ return [ 'yes' => '是', 'no' => '否', 'fuel' => '燃油', - 'new_energy' => '新能源' + 'new_energy' => '新能源', + 'week0' => '星期日', + 'week1' => '星期一', + 'week2' => '星期二', + 'week3' => '星期三', + 'week4' => '星期四', + 'week5' => '星期五', + 'week6' => '星期六' ], 'parking_space' => [ 'vacant' => '空置', diff --git a/resources/lang/zh-TW/service.php b/resources/lang/zh-TW/service.php index 47d8e98..b8c489d 100644 --- a/resources/lang/zh-TW/service.php +++ b/resources/lang/zh-TW/service.php @@ -50,7 +50,14 @@ return [ 'yes' => '是', 'no' => '否', 'fuel' => '燃油', - 'new_energy' => '新能源' + 'new_energy' => '新能源', + 'week0' => '星期日', + 'week1' => '星期一', + 'week2' => '星期二', + 'week3' => '星期三', + 'week4' => '星期四', + 'week5' => '星期五', + 'week6' => '星期六' ], 'parking_space' => [ 'vacant' => '空置', diff --git a/routes/admin/api.php b/routes/admin/api.php index 057d80a..3f72bed 100644 --- a/routes/admin/api.php +++ b/routes/admin/api.php @@ -105,6 +105,9 @@ Route::group(['prefix' => 'admin'], function () { Route::get('/parkingReservation/search', [ParkingReservationController::class, 'search']); Route::get('/parkingReservation/statistics', [ParkingReservationController::class, 'statistics']); Route::get('/parkingReservation/rule', [ParkingReservationController::class, 'rule']); + Route::get('/parkingReservation/index', [ParkingReservationController::class, 'show']); + // 预约统计 + Route::get('/parkingReservationStatistics/index', [ParkingReservationController::class, 'getDateStatistics']); // 车位列表 Route::get('/parkingSpace', [ParkingSpaceController::class, 'index']); Route::get('/parkingSpace/search', [ParkingSpaceController::class, 'search']);