Browse Source

停车场占用率

master
wanghongjun 3 days ago
parent
commit
1ff72e0c9c
  1. 64
      app/Console/Commands/CreateParkingUtilizationRate.php
  2. 66
      app/Exports/OccupancyRateExport.php
  3. 149
      app/Http/Controllers/Admin/OccupancyRateController.php
  4. 5
      app/Models/AdminFloor.php
  5. 25
      app/Models/ParkingOccupancyRate.php
  6. 31
      app/Models/ParkingOccupancyRateInfo.php
  7. 125
      app/Services/ParkingOccupancyRateService.php
  8. 32
      database/migrations/2026_09_09_143449_create_parking_occupancy_rate_info_table.php
  9. 31
      database/migrations/2026_09_09_143449_create_parking_occupancy_rate_table.php
  10. 7
      routes/admin/api.php

64
app/Console/Commands/CreateParkingUtilizationRate.php

@ -4,7 +4,9 @@ namespace App\Console\Commands;
use App\Models\ParkingAccessRecord; use App\Models\ParkingAccessRecord;
use App\Models\ParkingSpace; use App\Models\ParkingSpace;
use App\Models\ParkingSpaceType;
use App\Models\ParkingUtilizationRate; use App\Models\ParkingUtilizationRate;
use App\Services\ParkingOccupancyRateService;
use Exception; use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -58,6 +60,24 @@ class CreateParkingUtilizationRate extends Command
} }
} }
} }
$ParkingSpaceTypeIds = ParkingSpaceType::query()->pluck('id');
foreach ($ParkingSpaceTypeIds as $space_type_id) {
$this->createOccupancyRate(
$start_time,
$end_time,
$space_type_id
);
if ($now_hours == '01') {
$this->createOccupancyRate(
$day_start_time,
$day_end_time,
$space_type_id,
2
);
}
}
DB::commit(); DB::commit();
} catch (Exception $e) { } catch (Exception $e) {
DB::rollBack(); DB::rollBack();
@ -141,4 +161,48 @@ class CreateParkingUtilizationRate extends Command
} }
return $rate * 100; return $rate * 100;
} }
private function createOccupancyRate(
$start_time,
$end_time,
$space_type_id,
$time_type = 1
) {
$start_times = strtotime($start_time);
$end_times = strtotime($end_time);
$list = ParkingSpace::query()->where(
'space_type_id',
$space_type_id
)->select(['floor_id', 'id'])->get()->toArray();
$floor_data = [];
foreach ($list as $item) {
$floor_data[$item['floor_id']][] = $item['id'];
}
foreach ($floor_data as $floor_id => $space_ids) {
$sum_count = count($space_ids);
$occupy_space_ids = ParkingAccessRecord::query()->whereIn(
'space_id',
$space_ids
)->where(
function ($query) use ($start_times, $end_times) {
$query->whereRaw(
" enter_time < {$end_times}"
.
" AND (leave_time IS NULL OR leave_time > {$start_times})"
);
}
)->pluck('space_id')->toArray();
$unique_space_ids = array_unique($occupy_space_ids);
$occupy_count = count($unique_space_ids);
ParkingOccupancyRateService::createData(
$space_type_id,
$floor_id,
$start_time,
$sum_count,
$occupy_count,
$time_type
);
}
}
} }

66
app/Exports/OccupancyRateExport.php

@ -0,0 +1,66 @@
<?php
namespace App\Exports;
use App\Models\ParkingSpaceType;
use App\Services\OperationLogService;
use App\Services\ParkingOccupancyRateService;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
class OccupancyRateExport implements FromArray, WithHeadings
{
public array $parents = [];
public ParkingOccupancyRateService $service;
public array $parkingSpaceType;
public function __construct($parents)
{
$this->parents = $parents;
$this->service = new ParkingOccupancyRateService(
new OperationLogService()
);
$this->parkingSpaceType = ParkingSpaceType::getData();
}
public function array(): array
{
$data = [];
$index = 1;
$parkingSpaceType = $this->parkingSpaceType;
$query = $this->service->queryList($this->parents);
$list = $query->select()->get()->toArray();
foreach ($list as $item) {
$item = $this->service->getItem($item, $parkingSpaceType);
$value = [
'index' => $index,
'time' => $item['time'],
'floor' => $item['floor']
];
foreach ($parkingSpaceType as $type) {
$type_id = $type['id'];
$key = 'type' . $type_id;
$value[$key] = $item[$key];
}
$data[] = $value;
$index += 1;
}
return $data;
}
/**
* @return array
*/
public function headings(): array
{
$header = [
__('exports.global.index'),
__('exports.occupancy_rate.time'),
__('exports.occupancy_rate.floor'),
];
foreach ($this->parkingSpaceType as $value) {
$header[] = $value['name'];
}
return $header;
}
}

149
app/Http/Controllers/Admin/OccupancyRateController.php

@ -0,0 +1,149 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Exports\OccupancyRateExport;
use App\Models\AdminFloor;
use App\Models\Parking;
use App\Models\ParkingOccupancyRate;
use App\Models\ParkingOccupancyRateInfo;
use App\Models\ParkingSpaceType;
use App\Services\ApiResponseService;
use App\Services\ParkingOccupancyRateService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class OccupancyRateController extends BaseController
{
protected string $menuUri = 'occupancyRate';
/**
* @var ParkingOccupancyRateService
*/
protected ParkingOccupancyRateService $service;
/**
* 构造函数
* @param ApiResponseService $responseService
* @param ParkingOccupancyRateService $service
*/
public function __construct(
ApiResponseService $responseService,
ParkingOccupancyRateService $service
) {
parent::__construct($responseService);
$this->service = $service;
}
public function index(Request $request): JsonResponse
{
try {
$query = $this->service->queryList($request->all());
// 分页
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$total = $query->count();
$items = $query->latest()->forPage($page, $perPage)->select()->get(
);
if ($items) {
$items = $items->toArray();
$parkingSpaceType = ParkingSpaceType::getData();
foreach ($items as &$item) {
$item = $this->service->getItem($item, $parkingSpaceType);
}
}
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 search(): JsonResponse
{
try {
$data = [
'space_type_list' => ParkingSpaceType::getData(),
'parking_list' => Parking::getData()
];
return $this->responseService->success($data);
} catch (Exception $e) {
$m_prefix = __('exception.exception_handler.resource');
return $this->responseService->systemError(
$m_prefix . ':' . $e->getMessage()
);
}
}
public function chart(Request $request): JsonResponse
{
try {
$query = $this->service->queryList($request->all());
$list = $query->groupBy(['time', 'time_type'])->select(
['time', 'time_type']
)->get();
$ParkingSpaceType = array_column(
ParkingSpaceType::getData(),
'name',
'id'
);
foreach ($list as &$item) {
$time = $item['time'];
$time_type = $item['time_type'];
if ($time_type == 1) {
$format = 'm-d H:i';
} else {
$format = 'Y-m-d';
}
$item['time'] = date($format, strtotime($time));
$ids = ParkingOccupancyRate::query()->where('time', $time)
->where('time_type', $time_type)->pluck('id');
$space_type_list = ParkingOccupancyRateInfo::query()->whereIn(
'occupancy_rate_id',
$ids
)->groupBy(['space_type_id'])->selectRaw(
'SUM(`occupy_count`) as sum_occupy_count, space_type_id'
)->get()->toArray();
foreach ($space_type_list as $key => $value) {
$space_type_list[$key]['space_type_name']
= $ParkingSpaceType[$value['space_type_id']];
unset($space_type_list[$key]['space_type_id']);
}
$item['space_type_list'] = $space_type_list;
unset($item['time_type'], $item['floor_id']);
}
return $this->responseService->success($list);
} 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 OccupancyRateExport($request->all()),
set_space_underline(__('exports.access_record.list'))
. get_datetime('date_')
. '.xlsx'
);
}
}

5
app/Models/AdminFloor.php

@ -72,4 +72,9 @@ class AdminFloor extends Model
{ {
return self::query()->where('id', $id)->value('building_floor') ?? ''; return self::query()->where('id', $id)->value('building_floor') ?? '';
} }
public static function getIdsParking($parking_id)
{
return self::query()->where('building_floor', $parking_id)->pluck('id');
}
} }

25
app/Models/ParkingOccupancyRate.php

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ParkingOccupancyRate extends Model
{
use HasFactory;
protected $table = 'parking_occupancy_rate';
protected $fillable
= [
'floor_id',
'time_type',
'time'
];
protected $hidden
= [
'created_at',
'updated_at'
];
}

31
app/Models/ParkingOccupancyRateInfo.php

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ParkingOccupancyRateInfo extends Model
{
use HasFactory;
protected $table = 'parking_occupancy_rate_info';
public $timestamps = false;
protected $fillable
= [
'occupancy_rate_id',
'space_type_id',
'sum_count',
'occupy_count'
];
public static function getTypeCount($occupancy_rate_id, $space_type_id)
{
return self::query()->where([
'occupancy_rate_id' => $occupancy_rate_id,
'space_type_id' => $space_type_id
])->first(['sum_count', 'occupy_count']);
}
}

125
app/Services/ParkingOccupancyRateService.php

@ -0,0 +1,125 @@
<?php
namespace App\Services;
use App\Models\AdminFloor;
use App\Models\ParkingOccupancyRate;
use App\Models\ParkingOccupancyRateInfo;
class ParkingOccupancyRateService extends BaseService
{
protected string $menuTitle = 'occupancy_rate';
public static function createData(
$space_type_id,
$floor_id,
$time,
$sum_count,
$occupy_count,
$time_type
) {
$where = [
'floor_id' => $floor_id,
'time' => $time,
'time_type' => $time_type
];
$id = ParkingOccupancyRate::query()->where($where)->value('id');
if (!$id) {
$data = [
'floor_id' => $floor_id,
'time' => $time,
'time_type' => $time_type,
'created_at' => date("Y-m-d H:i:s", time()),
'updated_at' => date("Y-m-d H:i:s", time())
];
$model = ParkingOccupancyRate::query()->create($data);
$id = $model->toArray()['id'];
}
$dataInfo = $whereInfo = [
'occupancy_rate_id' => $id,
'space_type_id' => $space_type_id
];
if (ParkingOccupancyRateInfo::query()->where($whereInfo)->exists()) {
return;
}
$dataInfo['sum_count'] = $sum_count;
$dataInfo['occupy_count'] = $occupy_count;
ParkingOccupancyRateInfo::query()->create($dataInfo);
}
public function queryList($param)
{
$query = ParkingOccupancyRate::query();
$floor_id = '';
if (isset($param['floor_id']) && !empty($param['floor_id'])) {
$floor_id = $param['floor_id'];
$query->where('floor_id', $param['floor_id']);
}
if (isset($param['parking_id']) && !empty($param['parking_id'])
&& !$floor_id
) {
$floor_ids = AdminFloor::getIdsParking($param['parking_id']);
if ($floor_ids) {
$query->whereIn('floor_id', $floor_ids);
}
}
$start_time = $param['start_date'] ?? date("Y-m-d 00:00:00");
$end_time = $param['end_date'] ?? date("Y-m-d 23:59:59");
$start_date = date('Y-m-d', strtotime($start_time));
$end_date = date('Y-m-d', strtotime($end_time));
$time_type = 2;
if ($start_date == $end_date) {
$time_type = 1;
}
$query->where('time_type', $time_type);
if ($start_time && strtotime($start_time)) {
$start_time = date("Y-m-d H:i:s", strtotime($start_time));
if ($start_time) {
$query->where('time', '>=', $start_time);
}
}
if ($end_time && strtotime($end_time)) {
$end_time = date("Y-m-d H:i:s", strtotime($end_time));
if ($end_time) {
$query->where('time', '<=', $end_time);
}
}
$query->orderBy('time', 'desc');
return $query;
}
public function getItem($item, $parkingSpaceType)
{
$item['floor'] = AdminFloor::getName($item['floor_id']);
if ($item['time_type'] == 1) {
$format = 'Y-m-d H:i';
} else {
$format = 'Y-m-d';
}
$item['time'] = date($format, strtotime($item['time']));
foreach ($parkingSpaceType as $spaceType) {
$type_id = $spaceType['id'];
$key = 'type' . $type_id;
$item[$key]['sum_count'] = 0;
$item[$key]['occupy_count'] = 0;
$res = ParkingOccupancyRateInfo::getTypeCount(
$item['id'],
$type_id
);
if ($res) {
$item[$key]['sum_count'] = $res['sum_count'];
$item[$key]['occupy_count'] = $res['occupy_count'];
}
}
unset($item['floor_id'], $item['time_type']);
return $item;
}
}

32
database/migrations/2026_09_09_143449_create_parking_occupancy_rate_info_table.php

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('parking_occupancy_rate', function (Blueprint $table) {
$table->id();
$table->integer('occupancy_rate_id')->comment('主表id');
$table->integer('space_type_id')->comment('车位类型id');
$table->integer('sum_count')->default(0)->comment('车位总数量');
$table->integer('occupy_count')->default(0)->comment('车位占用数量');
$table->innoDb();
$table->comment('停车位占用率详情');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('parking_occupancy_rate');
}
};

31
database/migrations/2026_09_09_143449_create_parking_occupancy_rate_table.php

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('parking_occupancy_rate', function (Blueprint $table) {
$table->id();
$table->integer('floor_id')->comment('楼层id');
$table->tinyInteger('time_type')->default(1)->comment('1小时 2天');
$table->dateTime('time')->comment('时间');
$table->innoDb();
$table->comment('停车位占用率');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('parking_occupancy_rate');
}
};

7
routes/admin/api.php

@ -45,6 +45,7 @@ use App\Http\Controllers\Admin\ProhibitedPassageController;
use App\Http\Controllers\Admin\ParkingAccessRecordController; use App\Http\Controllers\Admin\ParkingAccessRecordController;
use App\Http\Controllers\Admin\UtilizationRateController; use App\Http\Controllers\Admin\UtilizationRateController;
use App\Http\Controllers\Admin\ParkingManualCorrectionController; use App\Http\Controllers\Admin\ParkingManualCorrectionController;
use App\Http\Controllers\Admin\OccupancyRateController;
Route::group(['prefix' => 'admin'], function () { Route::group(['prefix' => 'admin'], function () {
// 认证相关接口 // 认证相关接口
@ -264,6 +265,12 @@ Route::group(['prefix' => 'admin'], function () {
Route::get('/utilizationRate/search', [UtilizationRateController::class, 'search']); Route::get('/utilizationRate/search', [UtilizationRateController::class, 'search']);
Route::get('/utilizationRate/rule', [UtilizationRateController::class, 'rule']); Route::get('/utilizationRate/rule', [UtilizationRateController::class, 'rule']);
Route::get('/utilizationRate/export', [UtilizationRateController::class, 'export']); Route::get('/utilizationRate/export', [UtilizationRateController::class, 'export']);
// 停车位占用率
Route::get('/occupancyRate', [OccupancyRateController::class, 'index']);
Route::get('/occupancyRate/index', [OccupancyRateController::class, 'chart']);
Route::get('/occupancyRate/search', [OccupancyRateController::class, 'search']);
Route::get('/occupancyRate/rule', [OccupancyRateController::class, 'rule']);
Route::get('/occupancyRate/export', [OccupancyRateController::class, 'export']);
// 车牌修正记录 // 车牌修正记录
Route::get('/manualCorrection', [ParkingManualCorrectionController::class, 'index']); Route::get('/manualCorrection', [ParkingManualCorrectionController::class, 'index']);
Route::get('/manualCorrection/rule', [ParkingManualCorrectionController::class, 'rule']); Route::get('/manualCorrection/rule', [ParkingManualCorrectionController::class, 'rule']);

Loading…
Cancel
Save