376 lines
12 KiB
PHP
376 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* 월별 스케줄 자동 생성 엔진
|
|
*/
|
|
|
|
if (!defined('_GNUBOARD_'))
|
|
exit;
|
|
|
|
class ScheduleGenerator
|
|
{
|
|
|
|
/**
|
|
* 특정 월의 전체 스케줄 생성
|
|
*/
|
|
public function generateMonth($year, $month)
|
|
{
|
|
try {
|
|
// 기본 설정 조회
|
|
$basic_settings = $this->getBasicSettings();
|
|
|
|
// 요일별 설정 조회
|
|
$weekly_settings = $this->getWeeklySettings();
|
|
|
|
// 해당 월의 모든 날짜 생성
|
|
$dates = $this->getMonthDates($year, $month);
|
|
|
|
// 기존 스케줄 삭제 (자동 생성된 것만)
|
|
$this->clearAutoGeneratedSchedule($year, $month);
|
|
|
|
$generated_count = 0;
|
|
|
|
foreach ($dates as $date) {
|
|
$day_of_week = date('w', strtotime($date)); // 0=일요일, 1=월요일, ...
|
|
$day_name = $this->getDayName($day_of_week);
|
|
|
|
// 해당 요일의 설정 확인
|
|
if (isset($weekly_settings[$day_name]) && $weekly_settings[$day_name]['enabled'] == '1') {
|
|
// 운영일인 경우 스케줄 생성
|
|
$slots_created = $this->generateDay($date, $weekly_settings[$day_name], $basic_settings);
|
|
$generated_count += $slots_created;
|
|
} else {
|
|
// 휴무일인 경우 휴무 표시
|
|
$this->createHolidaySlot($date, '휴무일');
|
|
}
|
|
}
|
|
|
|
consultant_log("월별 스케줄 생성 완료: {$year}-{$month}, 생성된 슬롯: {$generated_count}개");
|
|
return $generated_count;
|
|
|
|
} catch (Exception $e) {
|
|
consultant_log("월별 스케줄 생성 실패: " . $e->getMessage(), 'error');
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 특정 날짜의 스케줄 생성
|
|
*/
|
|
public function generateDay($date, $day_settings, $basic_settings)
|
|
{
|
|
$slots_created = 0;
|
|
|
|
try {
|
|
$start_time = $day_settings['start'];
|
|
$end_time = $day_settings['end'];
|
|
$lunch_start = $day_settings['lunch_start'];
|
|
$lunch_end = $day_settings['lunch_end'];
|
|
|
|
$slot_duration = (int) $basic_settings['consultation_duration'];
|
|
$max_persons = (int) $basic_settings['max_persons_per_slot'];
|
|
|
|
// 시간 슬롯 생성
|
|
$current_time = strtotime($start_time);
|
|
$end_timestamp = strtotime($end_time);
|
|
|
|
while ($current_time < $end_timestamp) {
|
|
$slot_start = date('H:i', $current_time);
|
|
$slot_end = date('H:i', $current_time + ($slot_duration * 60));
|
|
|
|
// 종료시간이 운영시간을 넘지 않도록 체크
|
|
if (strtotime($slot_end) > $end_timestamp) {
|
|
break;
|
|
}
|
|
|
|
// 점심시간 체크
|
|
$is_lunch_time = $this->isLunchTime($slot_start, $slot_end, $lunch_start, $lunch_end);
|
|
|
|
if ($is_lunch_time) {
|
|
// 점심시간 슬롯 생성
|
|
$this->createTimeSlot($date, $slot_start, $slot_end, $slot_duration, 0, 0, 'lunch_time');
|
|
} else {
|
|
// 일반 상담 슬롯 생성
|
|
$this->createTimeSlot($date, $slot_start, $slot_end, $slot_duration, $max_persons, 1, 'auto_generated');
|
|
}
|
|
|
|
$slots_created++;
|
|
$current_time += ($slot_duration * 60);
|
|
}
|
|
|
|
return $slots_created;
|
|
|
|
} catch (Exception $e) {
|
|
consultant_log("일별 스케줄 생성 실패 ({$date}): " . $e->getMessage(), 'error');
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 시간 슬롯 생성
|
|
*/
|
|
private function createTimeSlot($date, $start_time, $end_time, $duration, $max_persons, $is_available, $type)
|
|
{
|
|
$sql = "INSERT INTO consultant_schedule
|
|
(specific_date, start_time, end_time, time_slot, max_persons, is_available, temp_1, created_at)
|
|
VALUES
|
|
('{$date}', '{$start_time}', '{$end_time}', {$duration}, {$max_persons}, {$is_available}, '{$type}', NOW())";
|
|
|
|
return sql_query($sql);
|
|
}
|
|
|
|
/**
|
|
* 휴무일 슬롯 생성
|
|
*/
|
|
private function createHolidaySlot($date, $reason = '휴무일')
|
|
{
|
|
$sql = "INSERT INTO consultant_schedule
|
|
(specific_date, start_time, end_time, time_slot, max_persons, is_available, temp_1, temp_2, created_at)
|
|
VALUES
|
|
('{$date}', '00:00', '23:59', 0, 0, 0, 'holiday', '{$reason}', NOW())";
|
|
|
|
return sql_query($sql);
|
|
}
|
|
|
|
/**
|
|
* 점심시간 여부 확인
|
|
*/
|
|
private function isLunchTime($slot_start, $slot_end, $lunch_start, $lunch_end)
|
|
{
|
|
if (empty($lunch_start) || empty($lunch_end)) {
|
|
return false;
|
|
}
|
|
|
|
$slot_start_time = strtotime($slot_start);
|
|
$slot_end_time = strtotime($slot_end);
|
|
$lunch_start_time = strtotime($lunch_start);
|
|
$lunch_end_time = strtotime($lunch_end);
|
|
|
|
// 슬롯이 점심시간과 겹치는지 확인
|
|
return ($slot_start_time >= $lunch_start_time && $slot_start_time < $lunch_end_time) ||
|
|
($slot_end_time > $lunch_start_time && $slot_end_time <= $lunch_end_time) ||
|
|
($slot_start_time <= $lunch_start_time && $slot_end_time >= $lunch_end_time);
|
|
}
|
|
|
|
/**
|
|
* 해당 월의 모든 날짜 배열 생성
|
|
*/
|
|
private function getMonthDates($year, $month)
|
|
{
|
|
$dates = [];
|
|
$days_in_month = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
|
|
|
for ($day = 1; $day <= $days_in_month; $day++) {
|
|
$dates[] = sprintf('%04d-%02d-%02d', $year, $month, $day);
|
|
}
|
|
|
|
return $dates;
|
|
}
|
|
|
|
/**
|
|
* 요일 숫자를 요일명으로 변환
|
|
*/
|
|
private function getDayName($day_of_week)
|
|
{
|
|
$day_names = [
|
|
0 => 'sunday',
|
|
1 => 'monday',
|
|
2 => 'tuesday',
|
|
3 => 'wednesday',
|
|
4 => 'thursday',
|
|
5 => 'friday',
|
|
6 => 'saturday'
|
|
];
|
|
|
|
return $day_names[$day_of_week] ?? 'sunday';
|
|
}
|
|
|
|
/**
|
|
* 기본 설정 조회
|
|
*/
|
|
private function getBasicSettings()
|
|
{
|
|
return [
|
|
'consultation_duration' => consultant_get_config('consultation_duration', '60'),
|
|
'max_persons_per_slot' => consultant_get_config('max_persons_per_slot', '2'),
|
|
'consultation_fee' => consultant_get_config('consultation_fee', '50000')
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 요일별 설정 조회
|
|
*/
|
|
private function getWeeklySettings()
|
|
{
|
|
$days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
|
$settings = [];
|
|
|
|
foreach ($days as $day) {
|
|
$settings[$day] = [
|
|
'enabled' => consultant_get_config($day . '_enabled', $day == 'saturday' || $day == 'sunday' ? '0' : '1'),
|
|
'start' => consultant_get_config($day . '_start', '09:00'),
|
|
'end' => consultant_get_config($day . '_end', '18:00'),
|
|
'lunch_start' => consultant_get_config($day . '_lunch_start', '12:00'),
|
|
'lunch_end' => consultant_get_config($day . '_lunch_end', '13:00')
|
|
];
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* 자동 생성된 스케줄 삭제
|
|
*/
|
|
private function clearAutoGeneratedSchedule($year, $month)
|
|
{
|
|
$start_date = sprintf('%04d-%02d-01', $year, $month);
|
|
$end_date = sprintf('%04d-%02d-%02d', $year, $month, cal_days_in_month(CAL_GREGORIAN, $month, $year));
|
|
|
|
// 기존 예약이 없는 자동 생성 스케줄만 삭제
|
|
$sql = "DELETE cs FROM consultant_schedule cs
|
|
LEFT JOIN consultant_reservations cr ON (
|
|
cs.specific_date = cr.reservation_date
|
|
AND cs.start_time = cr.reservation_time
|
|
AND cr.is_deleted = 0
|
|
)
|
|
WHERE cs.specific_date >= '{$start_date}'
|
|
AND cs.specific_date <= '{$end_date}'
|
|
AND cs.temp_1 IN ('auto_generated', 'lunch_time', 'holiday')
|
|
AND cr.id IS NULL";
|
|
|
|
return sql_query($sql);
|
|
}
|
|
|
|
/**
|
|
* 기존 예약 보호 - 예약이 있는 시간대 확인
|
|
*/
|
|
public function getExistingReservations($year, $month)
|
|
{
|
|
$start_date = sprintf('%04d-%02d-01', $year, $month);
|
|
$end_date = sprintf('%04d-%02d-%02d', $year, $month, cal_days_in_month(CAL_GREGORIAN, $month, $year));
|
|
|
|
$sql = "SELECT reservation_date, reservation_time, COUNT(*) as count
|
|
FROM consultant_reservations
|
|
WHERE reservation_date >= '{$start_date}'
|
|
AND reservation_date <= '{$end_date}'
|
|
AND is_deleted = 0
|
|
GROUP BY reservation_date, reservation_time";
|
|
|
|
$result = sql_query($sql);
|
|
$reservations = [];
|
|
|
|
if ($result) {
|
|
while ($row = sql_fetch_array($result)) {
|
|
$key = $row['reservation_date'] . '_' . $row['reservation_time'];
|
|
$reservations[$key] = $row['count'];
|
|
}
|
|
}
|
|
|
|
return $reservations;
|
|
}
|
|
|
|
/**
|
|
* 다음 달 스케줄 자동 생성 (크론잡용)
|
|
*/
|
|
public function generateNextMonth()
|
|
{
|
|
$next_month = date('Y-m', strtotime('+1 month'));
|
|
list($year, $month) = explode('-', $next_month);
|
|
|
|
return $this->generateMonth((int) $year, (int) $month);
|
|
}
|
|
|
|
/**
|
|
* 스케줄 생성 상태 확인
|
|
*/
|
|
public function checkScheduleStatus($year, $month)
|
|
{
|
|
$start_date = sprintf('%04d-%02d-01', $year, $month);
|
|
$end_date = sprintf('%04d-%02d-%02d', $year, $month, cal_days_in_month(CAL_GREGORIAN, $month, $year));
|
|
|
|
$sql = "SELECT
|
|
COUNT(*) as total_slots,
|
|
COUNT(CASE WHEN temp_1 = 'auto_generated' THEN 1 END) as auto_slots,
|
|
COUNT(CASE WHEN temp_1 = 'lunch_time' THEN 1 END) as lunch_slots,
|
|
COUNT(CASE WHEN temp_1 = 'holiday' THEN 1 END) as holiday_slots,
|
|
COUNT(CASE WHEN temp_1 = 'admin_blocked' THEN 1 END) as blocked_slots
|
|
FROM consultant_schedule
|
|
WHERE specific_date >= '{$start_date}'
|
|
AND specific_date <= '{$end_date}'";
|
|
|
|
return sql_fetch($sql);
|
|
}
|
|
|
|
/**
|
|
* 설정 변경 시 영향받는 예약 확인
|
|
*/
|
|
public function checkSettingConflicts($year, $month)
|
|
{
|
|
$existing_reservations = $this->getExistingReservations($year, $month);
|
|
$conflicts = [];
|
|
|
|
// 새로운 설정으로 생성될 스케줄과 기존 예약 비교
|
|
$weekly_settings = $this->getWeeklySettings();
|
|
$dates = $this->getMonthDates($year, $month);
|
|
|
|
foreach ($dates as $date) {
|
|
$day_of_week = date('w', strtotime($date));
|
|
$day_name = $this->getDayName($day_of_week);
|
|
|
|
// 휴무일로 변경되었는데 예약이 있는 경우
|
|
if (!isset($weekly_settings[$day_name]) || $weekly_settings[$day_name]['enabled'] != '1') {
|
|
foreach ($existing_reservations as $key => $count) {
|
|
if (strpos($key, $date) === 0) {
|
|
$conflicts[] = [
|
|
'date' => $date,
|
|
'type' => 'holiday_conflict',
|
|
'message' => "{$date}는 휴무일로 설정되었지만 {$count}건의 예약이 있습니다."
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $conflicts;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 스케줄 생성 헬퍼 함수들
|
|
*/
|
|
|
|
/**
|
|
* 월별 스케줄 생성 실행
|
|
*/
|
|
function generate_monthly_schedule($year, $month)
|
|
{
|
|
$generator = new ScheduleGenerator();
|
|
return $generator->generateMonth($year, $month);
|
|
}
|
|
|
|
/**
|
|
* 다음 달 스케줄 자동 생성
|
|
*/
|
|
function auto_generate_next_month_schedule()
|
|
{
|
|
$generator = new ScheduleGenerator();
|
|
return $generator->generateNextMonth();
|
|
}
|
|
|
|
/**
|
|
* 스케줄 생성 상태 확인
|
|
*/
|
|
function get_schedule_generation_status($year, $month)
|
|
{
|
|
$generator = new ScheduleGenerator();
|
|
return $generator->checkScheduleStatus($year, $month);
|
|
}
|
|
|
|
/**
|
|
* 설정 변경 영향 확인
|
|
*/
|
|
function check_schedule_setting_conflicts($year, $month)
|
|
{
|
|
$generator = new ScheduleGenerator();
|
|
return $generator->checkSettingConflicts($year, $month);
|
|
}
|
|
?>
|