From 7003cf5eeefbfbd472dbbbbf423e4c7502b10586 Mon Sep 17 00:00:00 2001 From: Hyojin Ahn Date: Mon, 22 Jun 2026 08:41:28 -0400 Subject: [PATCH] =?UTF-8?q?v1.3.7=20-=20[API]=20Dashboard=20Express=20?= =?UTF-8?q?=EB=93=9C=EB=9D=BC=EC=9D=B4=EB=B2=84=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public_html/assets/api/v1/orders.php | 257 ++++++++++++++++++++++++++- 1 file changed, 255 insertions(+), 2 deletions(-) diff --git a/public_html/assets/api/v1/orders.php b/public_html/assets/api/v1/orders.php index 864e1de..4bfc7d7 100644 --- a/public_html/assets/api/v1/orders.php +++ b/public_html/assets/api/v1/orders.php @@ -26,6 +26,258 @@ validateApiKey(); $pdo = getDB(); +// ── Forecast Cache 분기 ─────────────────────── +if (($_GET['type'] ?? '') === 'forecast') { + + header('Content-Type: application/json'); + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); + + if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(204); exit; + } + + $pdo = getDB(); + $date = $_GET['date'] ?? date('Ymd'); + + $pdo->exec("CREATE TABLE IF NOT EXISTS tbl_forecast_cache ( + fc_date VARCHAR(8) NOT NULL PRIMARY KEY, + fc_data TEXT NOT NULL, + fc_updated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + )"); + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $body = json_decode(file_get_contents('php://input'), true); + if (!$body) { echo json_encode(['success' => false, 'error' => 'Invalid JSON']); exit; } + $st = $pdo->prepare(" + INSERT INTO tbl_forecast_cache (fc_date, fc_data) + VALUES (:d, :data) + ON DUPLICATE KEY UPDATE fc_data = VALUES(fc_data), fc_updated = NOW() + "); + $st->execute([':d' => $date, ':data' => json_encode($body)]); + echo json_encode(['success' => true, 'date' => $date]); + } else { + $st = $pdo->prepare("SELECT fc_data, fc_updated FROM tbl_forecast_cache WHERE fc_date = :d"); + $st->execute([':d' => $date]); + $row = $st->fetch(PDO::FETCH_ASSOC); + echo json_encode([ + 'success' => true, + 'date' => $date, + 'saved_at' => $row ? $row['fc_updated'] : null, + 'data' => $row ? json_decode($row['fc_data'], true) : null, + ]); + } + exit; +} + +// ── CS Dashboard 분기 ───────────────────────── +if (($_GET['type'] ?? '') === 'cs') { + + $dateFrom = $_GET['date_from'] ?? date('Ymd', strtotime('-30 days')); + $dateTo = $_GET['date_to'] ?? date('Ymd'); + $status = $_GET['status'] ?? 'all'; + $today = date('Ymd'); + + // 1. SUMMARY KPIs + $summary = []; + $st = $pdo->prepare("SELECT COUNT(*) FROM tbl_request WHERE r_status='A' AND SUBSTR(r_createddate,1,8) BETWEEN :f AND :t"); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $summary['total_pending'] = (int)$st->fetchColumn(); + + $st = $pdo->prepare("SELECT COUNT(*) FROM tbl_request WHERE SUBSTR(r_createddate,1,8) = :d"); + $st->execute([':d' => $today]); + $summary['today_new'] = (int)$st->fetchColumn(); + + $st = $pdo->prepare("SELECT COUNT(*) FROM tbl_request WHERE r_status='F' AND SUBSTR(r_createddate,1,8) = :d"); + $st->execute([':d' => $today]); + $summary['today_completed'] = (int)$st->fetchColumn(); + + $st = $pdo->prepare("SELECT MIN(r_requestdate) FROM tbl_request WHERE r_status='A' AND SUBSTR(r_createddate,1,8) BETWEEN :f AND :t"); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $oldest = $st->fetchColumn(); + $summary['oldest_pending_date'] = $oldest ?: null; + if ($oldest && strlen($oldest) === 8) { + $oDate = mktime(0,0,0,(int)substr($oldest,4,2),(int)substr($oldest,6,2),(int)substr($oldest,0,4)); + $summary['oldest_pending_days'] = (int)floor((time() - $oDate) / 86400); + } else { + $summary['oldest_pending_days'] = 0; + } + + $st = $pdo->prepare("SELECT COUNT(*) FROM tbl_request WHERE SUBSTR(r_createddate,1,8) BETWEEN :f AND :t"); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $summary['period_total'] = (int)$st->fetchColumn(); + + $st = $pdo->prepare("SELECT COUNT(*) FROM tbl_request WHERE r_status='F' AND SUBSTR(r_createddate,1,8) BETWEEN :f AND :t"); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $summary['period_completed'] = (int)$st->fetchColumn(); + + // 2a. ALL PENDING (날짜 필터 없음 — 전체 미처리) + $st = $pdo->query(" + SELECT r.r_uid AS id, r.r_requestdate AS request_date, r.r_accountno AS account, + r.r_name AS customer_name, r.r_status AS status, r.r_note AS note, + r.r_createddate AS created_at, + COALESCE(c.c_area, '') AS area, + COALESCE(c.c_city, '') AS city, + COALESCE(c.c_phone, '') AS phone, + COALESCE(m.m_initial, '') AS driver, + COALESCE(NULLIF(m2.m_initial,''), m2.m_firstname, '?') AS staff + FROM tbl_request r + LEFT JOIN tbl_customer c ON r.r_customeruid = c.c_uid + LEFT JOIN tbl_member m ON r.r_driveruid = m.m_uid + LEFT JOIN tbl_member m2 ON r.r_memberuid = m2.m_uid + WHERE r.r_status = 'A' + ORDER BY r.r_requestdate DESC, r.r_uid DESC + LIMIT 20000 + "); + $allPending = $st->fetchAll(PDO::FETCH_ASSOC); + foreach ($allPending as &$row) { + $d = $row['request_date']; + $row['request_date_fmt'] = strlen($d) === 8 + ? substr($d,0,4).'-'.substr($d,4,2).'-'.substr($d,6,2) : $d; + $oDate = mktime(0,0,0,(int)substr($d,4,2),(int)substr($d,6,2),(int)substr($d,0,4)); + $row['days_pending'] = (int)floor((time() - $oDate) / 86400); + } + unset($row); + + // 2b. PERIOD REQUESTS (기간 필터 — 전체 요청 탭용) + $where = ["SUBSTR(r.r_createddate,1,8) BETWEEN :date_from AND :date_to"]; + $params = [':date_from' => $dateFrom, ':date_to' => $dateTo]; + $sql = " + SELECT r.r_uid AS id, r.r_requestdate AS request_date, r.r_accountno AS account, + r.r_name AS customer_name, r.r_status AS status, r.r_note AS note, + r.r_createddate AS created_at, + COALESCE(c.c_area, '') AS area, + COALESCE(c.c_city, '') AS city, + COALESCE(c.c_phone, '') AS phone, + COALESCE(m.m_initial, '') AS driver, + COALESCE(NULLIF(m2.m_initial,''), m2.m_firstname, '?') AS staff + FROM tbl_request r + LEFT JOIN tbl_customer c ON r.r_customeruid = c.c_uid + LEFT JOIN tbl_member m ON r.r_driveruid = m.m_uid + LEFT JOIN tbl_member m2 ON r.r_memberuid = m2.m_uid + WHERE " . implode(" AND ", $where) . " + ORDER BY r.r_requestdate DESC, r.r_uid DESC + LIMIT 500 + "; + $st = $pdo->prepare($sql); + $st->execute($params); + $requests = $st->fetchAll(PDO::FETCH_ASSOC); + + foreach ($requests as &$row) { + $d = $row['request_date']; + $row['request_date_fmt'] = strlen($d) === 8 + ? substr($d,0,4).'-'.substr($d,4,2).'-'.substr($d,6,2) : $d; + if ($row['status'] === 'A' && strlen($d) === 8) { + $oDate = mktime(0,0,0,(int)substr($d,4,2),(int)substr($d,6,2),(int)substr($d,0,4)); + $row['days_pending'] = (int)floor((time() - $oDate) / 86400); + } else { + $row['days_pending'] = 0; + } + } + unset($row); + + // 3. STAFF BREAKDOWN (접수자별 통계) + $st = $pdo->prepare(" + SELECT + COALESCE(NULLIF(m2.m_initial,''), m2.m_firstname, '?') AS staff, + COUNT(*) AS total, + SUM(CASE WHEN r.r_status='A' THEN 1 ELSE 0 END) AS pending, + SUM(CASE WHEN r.r_status='F' THEN 1 ELSE 0 END) AS completed + FROM tbl_request r + LEFT JOIN tbl_member m2 ON r.r_memberuid = m2.m_uid + WHERE SUBSTR(r.r_createddate,1,8) BETWEEN :f AND :t + GROUP BY r.r_memberuid, m2.m_initial + ORDER BY total DESC + "); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $byStaff = $st->fetchAll(PDO::FETCH_ASSOC); + + // 4. DRIVER BREAKDOWN + $st = $pdo->prepare(" + SELECT COALESCE(m.m_initial, '?') AS driver, + COUNT(*) AS total, + SUM(CASE WHEN r.r_status='A' THEN 1 ELSE 0 END) AS pending, + SUM(CASE WHEN r.r_status='F' THEN 1 ELSE 0 END) AS completed + FROM tbl_request r + LEFT JOIN tbl_member m ON r.r_driveruid = m.m_uid + WHERE r.r_requestdate BETWEEN :f AND :t + GROUP BY r.r_driveruid, m.m_initial + ORDER BY pending DESC, total DESC + "); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $byDriver = $st->fetchAll(PDO::FETCH_ASSOC); + + // 4. NOTES (type A + D) + $st = $pdo->prepare(" + SELECT n.n_uid AS id, n.n_type AS type, n.n_note AS note, + n.n_createddate AS created_at, + COALESCE(c.c_name, '') AS customer_name, + COALESCE(c.c_accountno, '') AS account, + COALESCE(c.c_area, '') AS area, + COALESCE(c.c_city, '') AS city, + COALESCE(CONCAT(m.m_firstname,' ',m.m_lastname), '') AS author + FROM tbl_note n + LEFT JOIN tbl_customer c ON n.n_customeruid = c.c_uid + LEFT JOIN tbl_member m ON n.n_memberuid = m.m_uid + WHERE n.n_status = 'A' AND n.n_type IN ('A','D') + AND SUBSTR(n.n_createddate,1,8) BETWEEN :f AND :t + ORDER BY n.n_createddate DESC + LIMIT 300 + "); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $notes = $st->fetchAll(PDO::FETCH_ASSOC); + + foreach ($notes as &$n) { + $d = substr($n['created_at'], 0, 8); + $n['date_fmt'] = strlen($d) === 8 + ? substr($d,0,4).'-'.substr($d,4,2).'-'.substr($d,6,2) : $d; + } + unset($n); + + // 5. AREA BREAKDOWN + $st = $pdo->prepare(" + SELECT COALESCE(NULLIF(c.c_area,''), c.c_city, '기타') AS area, + COUNT(*) AS total + FROM tbl_request r + LEFT JOIN tbl_customer c ON r.r_customeruid = c.c_uid + WHERE SUBSTR(r.r_createddate,1,8) BETWEEN :f AND :t + GROUP BY area + ORDER BY total DESC + "); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $byArea = $st->fetchAll(PDO::FETCH_ASSOC); + + // 6. DAILY BREAKDOWN (접수일 기준) + $st = $pdo->prepare(" + SELECT SUBSTR(r_createddate,1,8) AS date, + COUNT(*) AS total, + SUM(CASE WHEN r_status='F' THEN 1 ELSE 0 END) AS completed, + SUM(CASE WHEN r_status='A' THEN 1 ELSE 0 END) AS pending + FROM tbl_request + WHERE SUBSTR(r_createddate,1,8) BETWEEN :f AND :t + GROUP BY SUBSTR(r_createddate,1,8) + ORDER BY SUBSTR(r_createddate,1,8) ASC + "); + $st->execute([':f' => $dateFrom, ':t' => $dateTo]); + $byDay = $st->fetchAll(PDO::FETCH_ASSOC); + + jsonResponse([ + 'success' => true, + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'summary' => $summary, + 'all_pending' => $allPending, + 'requests' => $requests, + 'by_driver' => $byDriver, + 'by_staff' => $byStaff, + 'by_area' => $byArea, + 'by_day' => $byDay, + 'notes' => $notes, + ]); + exit; +} +// ───────────────────────────────────────────── + // 날짜 파라미터 (DB 형식: YYYYMMDD varchar) $dateFrom = $_GET['date_from'] ?? date('Ymd'); $dateTo = $_GET['date_to'] ?? date('Ymd'); @@ -76,8 +328,9 @@ $sql = " h.h_comment AS comment FROM tbl_daily d - INNER JOIN tbl_member m - ON (d.d_druid = m.m_currentdriver) + INNER JOIN ( + SELECT DISTINCT m_currentdriver FROM tbl_member WHERE m_currentdriver IS NOT NULL + ) m ON (d.d_druid = m.m_currentdriver) LEFT JOIN tbl_memberhis h ON (d.d_druid = h.h_druid AND d.d_orderdate = h.h_date) WHERE d.d_orderdate BETWEEN :date_from AND :date_to