krgm.so-manager-dev.com/app/Http/Controllers/Admin/InformationController.php
OU.ZAIKOU db681b219a
All checks were successful
Deploy main / deploy (push) Successful in 25s
【ダッシュボード】グラフ表示実装
2026-02-05 01:24:19 +09:00

319 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class InformationController extends Controller
{
public function list(Request $request)
{
// パラメータ
$period = $request->input('period', 'month'); // month | all
$type = $request->input('type', 'all'); // task(<99) | hard(>99) | all
$status = $request->input('status', 'untreated'); // untreated(=1) | inprogress(=2) | done(=3) | all
$q = DB::table('operator_que as oq')
->leftJoin('user as u', 'oq.user_id', '=', 'u.user_id')
->leftJoin('park as p', 'oq.park_id', '=', 'p.park_id')
// オペレータマスタ(テーブル・カラム名は環境に合わせて調整)
->leftJoin('ope as o', 'oq.operator_id', '=', 'o.ope_id')
->select(
'oq.que_id','oq.que_class','oq.user_id',
DB::raw('u.user_name as user_name'),
'oq.contract_id','oq.park_id',
DB::raw('p.park_name as park_name'),
'oq.que_comment','oq.que_status','oq.que_status_comment',
'oq.work_instructions','oq.created_at','oq.updated_at','oq.operator_id',
DB::raw('o.ope_name as operator_name')
);
// 期間: 登録日ベース最新1ヵ月 or 全期間)
if ($period === 'month') {
$q->where('oq.created_at', '>=', now()->subMonth());
}
// 種別: que_class
if ($type === 'task') {
$q->where('oq.que_class', '<', 99);
} elseif ($type === 'hard') {
$q->where('oq.que_class', '>', 99);
} // all は絞り込みなし
// ステータス: que_status
if ($status === 'untreated') {
$q->where('oq.que_status', 1);
} elseif ($status === 'inprogress') {
$q->where('oq.que_status', 2);
} elseif ($status === 'done') {
$q->where('oq.que_status', 3);
} // all は絞り込みなし
$jobs = $q->orderBy('oq.que_id')->paginate(20)->appends($request->query());
return view('admin.information.list', compact('jobs','period','type','status'));
}
// ダッシュボード表示
public function dashboard(Request $request)
{
// ダッシュボード統計情報を集計
// park_number テーブルから総容量を計算
// park_standard標準 + park_number割当+ park_limit制限値の合算
$totalCapacity = DB::table('park_number')
->selectRaw('
COALESCE(SUM(park_standard), 0) as std_sum,
COALESCE(SUM(park_number), 0) as num_sum,
COALESCE(SUM(park_limit), 0) as limit_sum
')
->first();
$totalCapacityValue = ($totalCapacity->std_sum ?? 0) +
($totalCapacity->num_sum ?? 0) +
($totalCapacity->limit_sum ?? 0);
// 予約待ち人数reserve テーブルから集計)
// 条件:有効(valid_flag=1) かつ契約化されていない(contract_id IS NULL)
// キャンセル除外reserve_cancel_flag が NULL または 0、かつ reserve_cancelday が NULL
$reserveQuery = DB::table('reserve')
->where('valid_flag', 1)
->whereNull('contract_id');
// キャンセルフラグの有無をチェック(列が存在するかどうか)
try {
$testResult = DB::table('reserve')
->select(DB::raw('1'))
->whereNotNull('reserve_cancel_flag')
->limit(1)
->first();
// 列が存在する場合、キャンセル除外条件を追加
$reserveQuery = $reserveQuery
->where(function ($q) {
$q->whereNull('reserve_cancel_flag')
->orWhere('reserve_cancel_flag', 0);
})
->whereNull('reserve_cancelday');
} catch (\Exception $e) {
// キャンセルフラグが未運用の場合は基本条件のみで計算
}
$totalWaiting = $reserveQuery->count();
// 使用中台数park_number の park_number が使用台数)
$totalUsed = DB::table('park_number')
->sum('park_number') ?? 0;
// 空き台数 = 総容量 - 使用中台数
$totalVacant = max(0, $totalCapacityValue - $totalUsed);
// 利用率計算(小数点以下切捨て)
$utilizationRate = $totalCapacityValue > 0
? (int) floor(($totalUsed / $totalCapacityValue) * 100)
: 0;
// 予約待ち率超過時のみ、超過なしは0%
// 超過判定:待機人数 > 空き台数
$totalWaitingRate = 0;
if ($totalCapacityValue > 0 && $totalWaiting > 0 && $totalWaiting > $totalVacant) {
// 超過分 / 総容量 * 100分母チェック付き
$totalWaitingRate = (int) floor((($totalWaiting - $totalVacant) / $totalCapacityValue) * 100);
}
$totalStats = [
'total_cities' => DB::table('city')->count(),
'total_parks' => DB::table('park')->count(),
'total_contracts' => DB::table('regular_contract')->count(),
'total_users' => DB::table('user')->count(),
'total_devices' => DB::table('device')->count(),
'today_queues' => DB::table('operator_que')
->whereDate('created_at', today())
->count(),
'total_waiting' => $totalWaiting,
'total_capacity' => $totalCapacityValue,
'total_utilization_rate' => $utilizationRate,
'total_vacant_number' => $totalVacant,
'total_waiting_rate' => $totalWaitingRate,
];
// 自治体別統計情報を作成
$cityStats = [];
$cities = DB::table('city')->get();
foreach ($cities as $city) {
// その自治体に属する駐輪場 ID を取得
$parkIds = DB::table('park')
->where('city_id', $city->city_id)
->pluck('park_id')
->toArray();
// ① 駐輪場数
$parksCount = count($parkIds);
// ② 総収容台数park_number テーブルの park_standard を合算)
$capacity = 0;
if (!empty($parkIds)) {
$capacityResult = DB::table('park_number')
->whereIn('park_id', $parkIds)
->sum('park_standard');
$capacity = $capacityResult ?? 0;
}
// ③ 契約台数contract_cancel_flag = 0 かつ有効期間内)
$contractsCount = 0;
if (!empty($parkIds)) {
$contractsCount = DB::table('regular_contract')
->whereIn('park_id', $parkIds)
->where('contract_cancel_flag', 0)
->where(function ($q) {
// 有効期間内:開始日 <= 今日 かつ 終了日 >= 今日
$q->where('contract_periods', '<=', now())
->where('contract_periode', '>=', now());
})
->count();
}
// ④ 利用率計算(小数点以下切捨て)
$utilizationRate = $capacity > 0
? (int) floor(($contractsCount / $capacity) * 100)
: 0;
// ⑤ 空き台数
$availableSpaces = max(0, $capacity - $contractsCount);
// ⑥ 予約待ち人数reserve テーブルで contract_id IS NULL かつ valid_flag = 1
$waitingCount = 0;
if (!empty($parkIds)) {
$waitingQuery = DB::table('reserve')
->whereIn('park_id', $parkIds)
->where('valid_flag', 1)
->whereNull('contract_id');
// キャンセルフラグの有無をチェック
try {
DB::table('reserve')
->select(DB::raw('1'))
->whereNotNull('reserve_cancel_flag')
->limit(1)
->first();
// 列が存在する場合、キャンセル除外条件を追加
$waitingQuery = $waitingQuery
->where(function ($q) {
$q->whereNull('reserve_cancel_flag')
->orWhere('reserve_cancel_flag', 0);
})
->whereNull('reserve_cancelday');
} catch (\Exception $e) {
// キャンセルフラグが未運用の場合は基本条件のみで計算
}
$waitingCount = $waitingQuery->count();
}
// ⑦ 利用者数(ユニークユーザー数)
$usersCount = 0;
if (!empty($parkIds)) {
$usersCount = DB::table('regular_contract')
->whereIn('park_id', $parkIds)
->distinct()
->count('user_id');
}
// 配列に追加
$cityStats[] = [
'city' => $city,
'parks_count' => $parksCount,
'contracts_count' => $contractsCount,
'users_count' => $usersCount,
'waiting_count' => $waitingCount,
'capacity' => $capacity,
'utilization_rate' => $utilizationRate,
'available_spaces' => $availableSpaces,
];
}
// グラフ用データ: city_name と utilization_rate, waiting_count のみ
$cityStatsChart = [];
foreach ($cities as $city) {
$parkIds = DB::table('park')
->where('city_id', $city->city_id)
->pluck('park_id')
->toArray();
// park_standard と park_number の合計
$parkNumberStats = DB::table('park_number')
->whereIn('park_id', $parkIds)
->selectRaw('COALESCE(SUM(park_standard), 0) as total_standard, COALESCE(SUM(park_number), 0) as total_number')
->first();
$parkStandard = $parkNumberStats->total_standard ?? 0;
$parkNumber = $parkNumberStats->total_number ?? 0;
// 利用率計算floor((park_number / park_standard) * 100)
$utilizationRate = $parkStandard > 0
? (int) floor(($parkNumber / $parkStandard) * 100)
: 0;
// 予約待ち人数
$waitingCount = 0;
if (!empty($parkIds)) {
$waitingQuery = DB::table('reserve')
->whereIn('park_id', $parkIds)
->where('valid_flag', 1)
->whereNull('contract_id');
try {
DB::table('reserve')
->select(DB::raw('1'))
->whereNotNull('reserve_cancel_flag')
->limit(1)
->first();
$waitingQuery = $waitingQuery
->where(function ($q) {
$q->whereNull('reserve_cancel_flag')
->orWhere('reserve_cancel_flag', 0);
})
->whereNull('reserve_cancelday');
} catch (\Exception $e) {
// キャンセルフラグが未運用
}
$waitingCount = $waitingQuery->count();
}
$cityStatsChart[] = [
'city_id' => $city->city_id,
'city_name' => $city->city_name,
'utilization_rate' => $utilizationRate,
'waiting_count' => $waitingCount,
];
}
return view('admin.information.dashboard', compact('totalStats', 'cityStats', 'cityStatsChart'));
}
// ステータス一括更新(着手=2 / 対応完了=3
public function updateStatus(Request $request)
{
$request->validate([
'ids' => 'required|array',
'action' => 'required|in:inprogress,done',
]);
$new = $request->action === 'inprogress' ? 2 : 3;
DB::table('operator_que')
->whereIn('que_id', $request->ids)
->update([
'que_status' => $new,
'updated_at' => now(),
]);
return back()->with('success', '選択したキューのステータスを更新しました。');
}
}