261 lines
11 KiB
PHP
261 lines
11 KiB
PHP
<?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,
|
||
];
|
||
}
|
||
|
||
return view('admin.information.dashboard', compact('totalStats', 'cityStats'));
|
||
}
|
||
|
||
// ステータス一括更新(着手=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', '選択したキューのステータスを更新しました。');
|
||
}
|
||
} |