Files
dnssash/adm/consultant_manage/classes/ReservationManager.class.php
T
2026-06-11 18:47:38 +09:00

471 lines
17 KiB
PHP

<?php
/**
* 상담 예약 관리 클래스
*/
if (!defined('_GNUBOARD_'))
exit;
class ReservationManager
{
private $table_name = 'consultant_reservations';
/**
* 예약 목록 조회
*/
public function getReservationList($status = '', $date = '', $page = 1, $per_page = 20, $search_type = '', $search_keyword = '')
{
try {
$where_conditions = ["is_deleted = 0"];
// 상태 필터
if (!empty($status)) {
$where_conditions[] = "status = '" . sql_real_escape_string($status) . "'";
}
// 날짜 필터
if (!empty($date)) {
$where_conditions[] = "reservation_date = '" . sql_real_escape_string($date) . "'";
}
// 검색 조건
if (!empty($search_keyword) && !empty($search_type)) {
$search_keyword = sql_real_escape_string($search_keyword);
switch ($search_type) {
case 'customer_name':
$where_conditions[] = "customer_name LIKE '%{$search_keyword}%'";
break;
case 'customer_phone':
$where_conditions[] = "customer_phone LIKE '%{$search_keyword}%'";
break;
}
}
$where_clause = implode(' AND ', $where_conditions);
// 전체 개수 조회
$count_sql = "SELECT COUNT(*) as total FROM {$this->table_name} WHERE {$where_clause}";
$count_result = sql_fetch($count_sql);
$total = $count_result['total'];
// 페이징 계산
$offset = ($page - 1) * $per_page;
$total_pages = ceil($total / $per_page);
// 목록 조회
$sql = "SELECT * FROM {$this->table_name}
WHERE {$where_clause}
ORDER BY created_at DESC
LIMIT {$offset}, {$per_page}";
$reservations = [];
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$reservations[] = $row;
}
return [
'success' => true,
'data' => [
'reservations' => $reservations,
'pagination' => [
'current_page' => $page,
'per_page' => $per_page,
'total' => $total,
'total_pages' => $total_pages
]
]
];
} catch (Exception $e) {
consultant_log("예약 목록 조회 실패: " . $e->getMessage(), 'error');
return [
'success' => false,
'message' => '예약 목록 조회 중 오류가 발생했습니다.'
];
}
}
/**
* 예약 생성
*/
public function createReservation($data)
{
try {
// 필수 필드 검증
$required_fields = ['customer_name', 'customer_phone', 'reservation_date', 'reservation_time'];
foreach ($required_fields as $field) {
if (empty($data[$field])) {
throw new Exception("필수 필드가 누락되었습니다: {$field}");
}
}
// 중복 예약 확인
$check_sql = "SELECT COUNT(*) as count FROM {$this->table_name}
WHERE reservation_date = '" . sql_real_escape_string($data['reservation_date']) . "'
AND reservation_time = '" . sql_real_escape_string($data['reservation_time']) . "'
AND status IN ('payment_pending', 'reserved')
AND is_deleted = 0";
$check_result = sql_fetch($check_sql);
$current_count = $check_result['count'];
// 최대 인원 확인 (기본값 2명)
$max_persons = consultant_get_config('default_max_persons', 2);
if ($current_count >= $max_persons) {
throw new Exception('해당 시간대는 예약이 마감되었습니다.');
}
// 데이터 준비
$insert_data = [
'customer_name' => sql_real_escape_string($data['customer_name']),
'customer_phone' => sql_real_escape_string($data['customer_phone']),
'customer_email' => sql_real_escape_string($data['customer_email'] ?? ''),
'reservation_date' => sql_real_escape_string($data['reservation_date']),
'reservation_time' => sql_real_escape_string($data['reservation_time']),
'consultation_type' => sql_real_escape_string($data['consultation_type'] ?? 'onsite'),
'status' => 'payment_pending',
'payment_amount' => (int) ($data['payment_amount'] ?? consultant_get_config('consultation_fee', 50000)),
'payment_status' => 'pending',
'request_memo' => sql_real_escape_string($data['request_memo'] ?? ''),
'wr_id' => (int) ($data['wr_id'] ?? 0),
'temp_1' => sql_real_escape_string($data['temp_1'] ?? ''),
'created_at' => 'NOW()',
'updated_at' => 'NOW()'
];
// SQL 생성
$fields = implode(', ', array_keys($insert_data));
$values = "'" . implode("', '", array_values($insert_data)) . "'";
$values = str_replace("'NOW()'", "NOW()", $values); // NOW() 함수 처리
$sql = "INSERT INTO {$this->table_name} ({$fields}) VALUES ({$values})";
if (!sql_query($sql)) {
throw new Exception('데이터베이스 저장 실패: ' . sql_error());
}
$reservation_id = sql_insert_id();
// 알림 발송
$this->sendReservationNotification($reservation_id, 'created');
consultant_log("새 예약 생성: ID {$reservation_id}, 고객: {$data['customer_name']}");
return [
'success' => true,
'message' => '예약이 성공적으로 접수되었습니다.',
'data' => [
'reservation_id' => $reservation_id
]
];
} catch (Exception $e) {
consultant_log("예약 생성 실패: " . $e->getMessage(), 'error');
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 예약 상태 변경
*/
public function updateReservationStatus($reservation_id, $new_status, $admin_memo = '')
{
try {
// 예약 정보 조회
$reservation = $this->getReservationById($reservation_id);
if (!$reservation) {
throw new Exception('예약 정보를 찾을 수 없습니다.');
}
// 상태 변경 가능 여부 확인
$valid_transitions = [
'payment_pending' => ['reserved', 'cancelled'],
'reserved' => ['completed', 'cancelled'],
'completed' => [],
'cancelled' => []
];
$current_status = $reservation['status'];
if (!in_array($new_status, $valid_transitions[$current_status])) {
throw new Exception('현재 상태에서 해당 상태로 변경할 수 없습니다.');
}
// 상태 업데이트
$sql = "UPDATE {$this->table_name}
SET status = '" . sql_real_escape_string($new_status) . "',
admin_memo = '" . sql_real_escape_string($admin_memo) . "',
updated_at = NOW()
WHERE id = {$reservation_id}";
if (!sql_query($sql)) {
throw new Exception('상태 변경 실패: ' . sql_error());
}
// 알림 발송
$this->sendReservationNotification($reservation_id, 'status_changed', $new_status);
consultant_log("예약 상태 변경: ID {$reservation_id}, {$current_status} -> {$new_status}");
return [
'success' => true,
'message' => '예약 상태가 성공적으로 변경되었습니다.'
];
} catch (Exception $e) {
consultant_log("예약 상태 변경 실패: " . $e->getMessage(), 'error');
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 입금 확인
*/
public function confirmPayment($reservation_id, $admin_id)
{
try {
// 예약 정보 조회
$reservation = $this->getReservationById($reservation_id);
if (!$reservation) {
throw new Exception('예약 정보를 찾을 수 없습니다.');
}
if ($reservation['status'] !== 'payment_pending') {
throw new Exception('입금 대기 상태의 예약만 확인할 수 있습니다.');
}
// 입금 확인 처리
$sql = "UPDATE {$this->table_name}
SET status = 'reserved',
payment_status = 'paid',
payment_confirmed_at = NOW(),
payment_confirmed_by = '" . sql_real_escape_string($admin_id) . "',
updated_at = NOW()
WHERE id = {$reservation_id}";
if (!sql_query($sql)) {
throw new Exception('입금 확인 처리 실패: ' . sql_error());
}
// 알림 발송
$this->sendReservationNotification($reservation_id, 'payment_confirmed');
consultant_log("입금 확인: ID {$reservation_id}, 확인자: {$admin_id}");
return [
'success' => true,
'message' => '입금이 확인되어 예약이 확정되었습니다.'
];
} catch (Exception $e) {
consultant_log("입금 확인 실패: " . $e->getMessage(), 'error');
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 예약 취소
*/
public function cancelReservation($reservation_id, $reason)
{
try {
// 예약 정보 조회
$reservation = $this->getReservationById($reservation_id);
if (!$reservation) {
throw new Exception('예약 정보를 찾을 수 없습니다.');
}
if ($reservation['status'] === 'cancelled') {
throw new Exception('이미 취소된 예약입니다.');
}
if ($reservation['status'] === 'completed') {
throw new Exception('완료된 예약은 취소할 수 없습니다.');
}
// 예약 취소 처리
$sql = "UPDATE {$this->table_name}
SET status = 'cancelled',
admin_memo = '" . sql_real_escape_string($reason) . "',
updated_at = NOW()
WHERE id = {$reservation_id}";
if (!sql_query($sql)) {
throw new Exception('예약 취소 처리 실패: ' . sql_error());
}
// 알림 발송
$this->sendReservationNotification($reservation_id, 'cancelled', $reason);
consultant_log("예약 취소: ID {$reservation_id}, 사유: {$reason}");
return [
'success' => true,
'message' => '예약이 성공적으로 취소되었습니다.'
];
} catch (Exception $e) {
consultant_log("예약 취소 실패: " . $e->getMessage(), 'error');
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 예약 정보 조회
*/
public function getReservationById($reservation_id)
{
$sql = "SELECT * FROM {$this->table_name} WHERE id = {$reservation_id} AND is_deleted = 0";
return sql_fetch($sql);
}
/**
* 예약 알림 발송
*/
private function sendReservationNotification($reservation_id, $type, $extra_data = null)
{
try {
$reservation = $this->getReservationById($reservation_id);
if (!$reservation) {
return false;
}
// 알림 템플릿 키 결정
$template_keys = [
'created' => 'consultant_reservation_customer',
'payment_confirmed' => 'consultant_confirmed_customer',
'cancelled' => 'consultant_cancelled_customer',
'status_changed' => 'consultant_status_changed_customer'
];
$template_key = $template_keys[$type] ?? null;
if (!$template_key) {
return false;
}
// 템플릿 변수 준비
$variables = [
'customer_name' => $reservation['customer_name'],
'customer_phone' => $reservation['customer_phone'],
'customer_email' => $reservation['customer_email'],
'reservation_date' => $reservation['reservation_date'],
'reservation_time' => $reservation['reservation_time'],
'payment_amount' => number_format($reservation['payment_amount']),
'account_info' => consultant_get_config('account_info', ''),
'cancel_reason' => $extra_data ?? ''
];
// 이메일 발송
if (!empty($reservation['customer_email'])) {
$this->sendEmailNotification($reservation['customer_email'], $template_key, $variables);
}
// SMS 발송
if (!empty($reservation['customer_phone'])) {
$this->sendSmsNotification($reservation['customer_phone'], $template_key, $variables);
}
// 관리자 알림 (새 예약 시)
if ($type === 'created') {
$this->sendAdminNotification($reservation, $variables);
}
return true;
} catch (Exception $e) {
consultant_log("알림 발송 실패: " . $e->getMessage(), 'error');
return false;
}
}
/**
* 이메일 알림 발송
*/
private function sendEmailNotification($email, $template_key, $variables)
{
// 💡 [수정] consultant_send_notification 함수 사용으로 변경
return consultant_send_notification('email', $template_key, array_merge($variables, ['customer_email' => $email]));
}
/**
* SMS 알림 발송
*/
private function sendSmsNotification($phone, $template_key, $variables)
{
// 💡 [수정] consultant_send_notification 함수 사용으로 변경
return consultant_send_notification('sms', $template_key, array_merge($variables, ['customer_phone' => $phone]));
}
/**
* 관리자 알림 발송
*/
private function sendAdminNotification($reservation, $variables)
{
// 관리자 이메일 알림
$admin_template_key = 'consultant_reservation_admin';
// 관리자 이메일 주소 (설정에서 가져오거나 기본값 사용)
$admin_email = consultant_get_config('admin_email', get_admin_email());
if ($admin_email) {
// 💡 [수정] consultant_send_notification 함수 사용으로 변경
consultant_send_notification('email', $admin_template_key, array_merge($variables, ['customer_email' => $admin_email]));
}
}
/**
* 템플릿 변수 치환
*/
private function replaceTemplateVariables($template, $variables)
{
foreach ($variables as $key => $value) {
$template = str_replace('{' . $key . '}', $value, $template);
}
return $template;
}
/**
* 예약 통계 조회
*/
public function getReservationStats($start_date = null, $end_date = null)
{
try {
if (!$start_date)
$start_date = date('Y-m-01'); // 이번 달 첫날
if (!$end_date)
$end_date = date('Y-m-d'); // 오늘
$sql = "SELECT
COUNT(*) as total,
COUNT(CASE WHEN status = 'payment_pending' THEN 1 END) as pending,
COUNT(CASE WHEN status = 'reserved' THEN 1 END) as confirmed,
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) as cancelled,
SUM(CASE WHEN status = 'completed' THEN payment_amount ELSE 0 END) as total_revenue
FROM {$this->table_name}
WHERE reservation_date BETWEEN '{$start_date}' AND '{$end_date}'
AND is_deleted = 0";
return sql_fetch($sql);
} catch (Exception $e) {
consultant_log("통계 조회 실패: " . $e->getMessage(), 'error');
return null;
}
}
}