first commit 2
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
/**
|
||||
* UI 리소스 관리 클래스
|
||||
* Singleton 패턴을 사용하여 인스턴스를 한 번만 생성하고,
|
||||
* 불러온 데이터를 캐시하여 DB 조회를 최소화합니다.
|
||||
*/
|
||||
class UiManager
|
||||
{
|
||||
private static $instance = null;
|
||||
private $resources = []; // 데이터를 캐시할 배열
|
||||
|
||||
// 외부에서 new 키워드로 인스턴스 생성을 막음
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* 클래스의 유일한 인스턴스를 반환합니다.
|
||||
* @return UiManager
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'LABEL' 타입의 UI 텍스트를 가져옵니다.
|
||||
* @param string $resource_code 리소스 코드
|
||||
* @param string $lang 언어 코드 (기본값: 'ko')
|
||||
* @return string 라벨 텍스트 (없으면 resource_code 반환)
|
||||
*/
|
||||
public function get_label($resource_code, $lang = 'ko')
|
||||
{
|
||||
// 캐시 확인
|
||||
if (isset($this->resources['labels'][$lang][$resource_code])) {
|
||||
return $this->resources['labels'][$lang][$resource_code];
|
||||
}
|
||||
|
||||
global $g5;
|
||||
$resource_code_escaped = sql_real_escape_string($resource_code);
|
||||
$lang_escaped = sql_real_escape_string($lang);
|
||||
|
||||
$sql = "SELECT B.cl_name
|
||||
FROM {$g5['ui_manager_table']} AS A
|
||||
LEFT JOIN {$g5['common_lang_table']} AS B
|
||||
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = '{$lang_escaped}')
|
||||
WHERE A.resource_code = '{$resource_code_escaped}' AND A.resource_type = 'LABEL'";
|
||||
$row = sql_fetch($sql);
|
||||
|
||||
$label_text = $row['cl_name'] ?? $resource_code;
|
||||
|
||||
// 결과 캐시
|
||||
$this->resources['labels'][$lang][$resource_code] = $label_text;
|
||||
|
||||
return $label_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'DATA' 타입의 옵션 목록을 배열로 가져옵니다.
|
||||
* @param string $resource_code 리소스 코드
|
||||
* @param string $lang 언어 코드 (기본값: 'ko')
|
||||
* @return array 옵션 목록 배열
|
||||
*/
|
||||
public function get_data($resource_code, $lang = 'ko')
|
||||
{
|
||||
// 캐시 확인
|
||||
if (isset($this->resources['data'][$lang][$resource_code])) {
|
||||
return $this->resources['data'][$lang][$resource_code];
|
||||
}
|
||||
|
||||
global $g5;
|
||||
$resource_code_escaped = sql_real_escape_string($resource_code);
|
||||
$lang_escaped = sql_real_escape_string($lang);
|
||||
|
||||
$sql_um = "SELECT um_id FROM {$g5['ui_manager_table']} WHERE resource_code = '{$resource_code_escaped}' AND resource_type = 'DATA'";
|
||||
$um_row = sql_fetch($sql_um);
|
||||
|
||||
if (!isset($um_row['um_id'])) {
|
||||
$this->resources['data'][$lang][$resource_code] = []; // 빈 결과도 캐시
|
||||
return [];
|
||||
}
|
||||
$um_id = $um_row['um_id'];
|
||||
|
||||
$sql = "SELECT A.fc_id, A.parent_id, A.fc_key, A.fc_order, B.cl_name
|
||||
FROM {$g5['form_category_table']} AS A
|
||||
LEFT JOIN {$g5['common_lang_table']} AS B
|
||||
ON (A.fc_id = B.target_id AND B.target_table = '{$g5['form_category_table']}' AND B.lang_code = '{$lang_escaped}')
|
||||
WHERE A.um_id = '{$um_id}' AND A.is_used = 1 AND A.is_deleted = 0
|
||||
ORDER BY A.fc_order, A.fc_id";
|
||||
|
||||
$result = sql_query($sql);
|
||||
$options = [];
|
||||
while ($row = sql_fetch_array($result)) {
|
||||
$options[] = $row;
|
||||
}
|
||||
|
||||
// 결과 캐시
|
||||
$this->resources['data'][$lang][$resource_code] = $options;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'DATA' 타입 리소스를 사용하여 HTML <select> 태그를 생성합니다.
|
||||
* @param string $resource_code 리소스 코드
|
||||
* @param string $select_name <select> 태그의 name 속성
|
||||
* @param string $selected_value 미리 선택될 옵션의 값(fc_key)
|
||||
* @param string $attributes <select> 태그에 추가할 HTML 속성 (e.g., 'id="my-id" class="my-class"')
|
||||
* @param string $lang 언어 코드 (기본값: 'ko')
|
||||
* @return string 생성된 HTML <select> 태그
|
||||
*/
|
||||
public function render_select($resource_code, $select_name, $selected_value = '', $attributes = '', $lang = 'ko')
|
||||
{
|
||||
$options = $this->get_data($resource_code, $lang);
|
||||
|
||||
if (empty($options)) {
|
||||
return "<select name=\"{$select_name}\" {$attributes}><option value=\"\">옵션 없음</option></select>";
|
||||
}
|
||||
|
||||
$html = "<select name=\"{$select_name}\" {$attributes}>";
|
||||
$html .= "<option value=\"\">선택</option>";
|
||||
foreach ($options as $option) {
|
||||
$selected = ($option['fc_key'] == $selected_value) ? ' selected' : '';
|
||||
$html .= "<option value=\"" . htmlspecialchars($option['fc_key']) . "\"{$selected}>" . htmlspecialchars($option['cl_name']) . "</option>";
|
||||
}
|
||||
$html .= "</select>";
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_'))
|
||||
exit;
|
||||
|
||||
/**
|
||||
* 주문 설정값을 가져옵니다.
|
||||
* @param string $key 설정 키
|
||||
* @param string $default 기본값
|
||||
* @return string 설정값
|
||||
*/
|
||||
function get_order_config($key, $default = '')
|
||||
{
|
||||
$result = sql_fetch("SELECT config_value FROM order_config WHERE config_key = '" . sql_real_escape_string($key) . "'");
|
||||
return $result ? $result['config_value'] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림 메시지를 처리합니다 (발송 모드에 따라 로그 또는 실제 발송)
|
||||
* @param string $template_key 템플릿 키
|
||||
* @param array $variables 치환할 변수들
|
||||
* @param string $type 'email' 또는 'sms'
|
||||
* @return bool 성공 여부
|
||||
*/
|
||||
function send_notification_alert($template_key, $variables = [], $type = 'email')
|
||||
{
|
||||
// 발송 모드 확인
|
||||
$notification_mode = get_order_config('notification_mode', 'log');
|
||||
|
||||
// 템플릿 조회
|
||||
$table = ($type == 'sms') ? 'order_sms_templates' : 'order_mail_templates';
|
||||
$template = sql_fetch("SELECT * FROM {$table} WHERE template_key = '" . sql_real_escape_string($template_key) . "'");
|
||||
|
||||
if (!$template) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 변수 치환
|
||||
$content = $template['content'];
|
||||
if ($type == 'email') {
|
||||
$subject = $template['subject'];
|
||||
foreach ($variables as $key => $value) {
|
||||
$subject = str_replace('{' . $key . '}', $value, $subject);
|
||||
$content = str_replace('{' . $key . '}', $value, $content);
|
||||
}
|
||||
$message = "제목: {$subject}\\n\\n내용: {$content}";
|
||||
} else {
|
||||
foreach ($variables as $key => $value) {
|
||||
$content = str_replace('{' . $key . '}', $value, $content);
|
||||
}
|
||||
$message = $content;
|
||||
}
|
||||
|
||||
if ($notification_mode == 'send') {
|
||||
// 실제 발송 모드
|
||||
if ($type == 'email') {
|
||||
// 실제 이메일 발송 (여기에 실제 발송 코드 구현)
|
||||
// mailer($to, $subject, $content, $from);
|
||||
error_log("[EMAIL SENT] {$template['template_name']}: {$message}");
|
||||
} else {
|
||||
// 실제 SMS 발송 (여기에 실제 발송 코드 구현)
|
||||
// sms_send($phone, $content);
|
||||
error_log("[SMS SENT] {$template['template_name']}: {$message}");
|
||||
}
|
||||
} else {
|
||||
// 로그 모드 - JavaScript 알림창으로 표시
|
||||
echo "<script>alert('[{$type}] {$template['template_name']}\\n\\n{$message}');</script>";
|
||||
error_log("[NOTIFICATION LOG] [{$type}] {$template['template_name']}: {$message}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 고객에게 알림을 발송합니다.
|
||||
* @param string $template_key 템플릿 키
|
||||
* @param array $customer_data 고객 데이터
|
||||
* @return bool 성공 여부
|
||||
*/
|
||||
function notify_customer($template_key, $customer_data)
|
||||
{
|
||||
// 이메일 발송
|
||||
$email_result = send_notification_alert($template_key, $customer_data, 'email');
|
||||
|
||||
// SMS 발송
|
||||
$sms_result = send_notification_alert($template_key, $customer_data, 'sms');
|
||||
|
||||
return $email_result && $sms_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 대리점에게 알림을 발송합니다.
|
||||
* @param string $template_key 템플릿 키
|
||||
* @param array $agent_data 대리점 데이터
|
||||
* @return bool 성공 여부
|
||||
*/
|
||||
function notify_agent($template_key, $agent_data)
|
||||
{
|
||||
// 이메일 발송
|
||||
$email_result = send_notification_alert($template_key, $agent_data, 'email');
|
||||
|
||||
// SMS 발송
|
||||
$sms_result = send_notification_alert($template_key, $agent_data, 'sms');
|
||||
|
||||
return $email_result && $sms_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자에게 알림을 발송합니다.
|
||||
* @param string $template_key 템플릿 키
|
||||
* @param array $admin_data 관리자 데이터
|
||||
* @return bool 성공 여부
|
||||
*/
|
||||
function notify_admin($template_key, $admin_data)
|
||||
{
|
||||
// 이메일 발송
|
||||
$email_result = send_notification_alert($template_key, $admin_data, 'email');
|
||||
|
||||
return $email_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적 상태 변경 시 알림을 발송합니다.
|
||||
* @param int $wr_id 게시물 ID
|
||||
* @param string $old_status 이전 상태
|
||||
* @param string $new_status 새 상태
|
||||
* @return bool 성공 여부
|
||||
*/
|
||||
function send_status_change_notification($wr_id, $old_status, $new_status)
|
||||
{
|
||||
// 게시물 정보 조회
|
||||
$post = sql_fetch("SELECT * FROM g5_write_order WHERE wr_id = '{$wr_id}'");
|
||||
if (!$post)
|
||||
return false;
|
||||
|
||||
// 상태별 알림 처리
|
||||
switch ($new_status) {
|
||||
case '작성완료':
|
||||
// 고객에게 완료 알림
|
||||
notify_customer('customer_request_complete', [
|
||||
'customer_name' => $post['wr_name'],
|
||||
'request_title' => $post['wr_subject'],
|
||||
'request_date' => date('Y-m-d', strtotime($post['wr_datetime']))
|
||||
]);
|
||||
|
||||
// 대리점들에게 새 견적 요청 알림
|
||||
$agents = sql_query("SELECT mb_id, mb_name FROM g5_member WHERE mb_level IN (5,6,7)");
|
||||
while ($agent = sql_fetch_array($agents)) {
|
||||
notify_agent('agent_new_request', [
|
||||
'agent_name' => $agent['mb_name'],
|
||||
'customer_name' => $post['wr_name'],
|
||||
'request_title' => $post['wr_subject'],
|
||||
'request_date' => date('Y-m-d', strtotime($post['wr_datetime'])),
|
||||
'write_url' => G5_BBS_URL . '/write.php?w=r&bo_table=order&wr_id=' . $wr_id
|
||||
]);
|
||||
}
|
||||
break;
|
||||
|
||||
case '견적채택':
|
||||
// 선택된 대리점과 고객에게 알림
|
||||
// (구체적인 로직은 견적 선택 시점에서 처리)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_'))
|
||||
exit;
|
||||
|
||||
// ❗ [핵심] 필요한 클래스와 라이브러리를 포함합니다.
|
||||
if (file_exists(G5_ADMIN_PATH . '/mail_manage/classes/MailSender.php')) {
|
||||
require_once(G5_ADMIN_PATH . '/mail_manage/classes/MailSender.php');
|
||||
}
|
||||
if (file_exists(G5_PLUGIN_PATH . '/sms5/sms5.lib.php')) {
|
||||
include_once(G5_PLUGIN_PATH . '/sms5/sms5.lib.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* 통합 메일/SMS 발송 클래스
|
||||
* 기존 mail_manage, sms_admin 시스템을 활용하여 발송 및 이력 기록
|
||||
*/
|
||||
class NotificationSender
|
||||
{
|
||||
private $g5;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
global $g5;
|
||||
$this->g5 = $g5;
|
||||
}
|
||||
|
||||
/**
|
||||
* ❗ [핵심 수정] 템플릿 기반 메인 발송 함수
|
||||
* @param array $params 발송 파라미터
|
||||
* @return array 발송 결과
|
||||
*/
|
||||
public function send($params)
|
||||
{
|
||||
// 파라미터 검증
|
||||
$validated = $this->validateParams($params);
|
||||
if (!$validated['success']) {
|
||||
return $validated;
|
||||
}
|
||||
|
||||
// 대상 회원 조회
|
||||
$members = $this->getTargetMembers($params);
|
||||
if (empty($members)) {
|
||||
return ['success' => false, 'message' => '발송 대상 회원이 없습니다.'];
|
||||
}
|
||||
|
||||
$results = [
|
||||
'success' => true,
|
||||
'total_targets' => count($members),
|
||||
'sms_success' => 0,
|
||||
'sms_fail' => 0,
|
||||
'email_success' => 0,
|
||||
'email_fail' => 0,
|
||||
'message' => ''
|
||||
];
|
||||
|
||||
// SMS 발송
|
||||
if (!empty($params['sms_template_key'])) {
|
||||
$sms_result = $this->sendSMS($members, $params['sms_template_key'], $params['vars'] ?? []);
|
||||
$results['sms_success'] = $sms_result['success'];
|
||||
$results['sms_fail'] = $sms_result['fail'];
|
||||
}
|
||||
|
||||
// 이메일 발송
|
||||
if (!empty($params['email_template_key'])) {
|
||||
$email_result = $this->sendEmail($members, $params['email_template_key'], $params['vars'] ?? []);
|
||||
$results['email_success'] = $email_result['success'];
|
||||
$results['email_fail'] = $email_result['fail'];
|
||||
}
|
||||
|
||||
$results['message'] = $this->generateResultMessage($results);
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* ❗ [핵심 수정] 템플릿 기반 파라미터 검증
|
||||
*/
|
||||
private function validateParams($params)
|
||||
{
|
||||
if (empty($params['target_type'])) {
|
||||
return ['success' => false, 'message' => "필수 파라미터 'target_type'이 누락되었습니다."];
|
||||
}
|
||||
if ($params['target_type'] === 'single' && empty($params['member_id'])) {
|
||||
return ['success' => false, 'message' => '단일 발송 시 회원 ID(member_id)가 필요합니다.'];
|
||||
}
|
||||
if ($params['target_type'] === 'bulk' && empty($params['member_levels'])) {
|
||||
return ['success' => false, 'message' => '대량 발송 시 회원 레벨(member_levels)이 필요합니다.'];
|
||||
}
|
||||
if (empty($params['sms_template_key']) && empty($params['email_template_key'])) {
|
||||
return ['success' => false, 'message' => 'SMS 또는 이메일 템플릿 키 중 하나는 반드시 필요합니다.'];
|
||||
}
|
||||
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* 대상 회원 조회
|
||||
*/
|
||||
private function getTargetMembers($params)
|
||||
{
|
||||
$members = [];
|
||||
$member_table = $this->g5['member_table'];
|
||||
|
||||
if ($params['target_type'] === 'single') {
|
||||
$sql = "SELECT mb_id, mb_name, mb_hp, mb_email, mb_sms, mb_mailling FROM `{$member_table}` WHERE mb_id = '" . sql_real_escape_string($params['member_id']) . "' AND mb_leave_date = '' AND mb_intercept_date = ''";
|
||||
} else {
|
||||
$levels = array_map('intval', $params['member_levels']);
|
||||
$level_condition = implode(',', $levels);
|
||||
$sql = "SELECT mb_id, mb_name, mb_hp, mb_email, mb_sms, mb_mailling FROM `{$member_table}` WHERE mb_level IN ({$level_condition}) AND mb_leave_date = '' AND mb_intercept_date = ''";
|
||||
}
|
||||
$this->write_debug_log("[SMS 발송 시작] sql '{$sql}'");
|
||||
$result = sql_query($sql);
|
||||
while ($row = sql_fetch_array($result)) {
|
||||
$members[] = $row;
|
||||
}
|
||||
return $members;
|
||||
}
|
||||
|
||||
/**
|
||||
* ❗ [핵심 수정] SMS 발송 (템플릿 및 변수 처리, 이력 기록 포함)
|
||||
*/
|
||||
private function sendSMS($members, $template_key, $common_vars)
|
||||
{
|
||||
$success = 0;
|
||||
$fail = 0;
|
||||
$notification_mode = get_order_config('notification_mode', 'log');
|
||||
$is_test_mode = ($notification_mode !== 'send');
|
||||
|
||||
$sizeof_members = count($members);
|
||||
$template = sql_fetch("SELECT * FROM `sms_templates` WHERE template_key = '" . sql_real_escape_string($template_key) . "'");
|
||||
if (!$template) {
|
||||
$this->write_debug_log("[SMS 발송 오류] 템플릿 '{$template_key}'을(를) 찾을 수 없습니다.");
|
||||
return ['success' => 0, 'fail' => count($members)];
|
||||
}
|
||||
$count= count($members);
|
||||
if ($is_test_mode) {
|
||||
// --- 개발 모드: 로그 파일에만 기록 ---
|
||||
foreach ($members as $member) {
|
||||
if ($member['mb_sms'] && !empty($member['mb_hp'])) {
|
||||
$personal_vars = array_merge($common_vars, ['이름' => $member['mb_name'], 'agent_name' => $member['mb_name'], 'dealer_name' => $member['mb_name']]);
|
||||
$personal_message = $template['content'];
|
||||
foreach ($personal_vars as $key => $value) {
|
||||
$personal_message = str_replace('{' . $key . '}', $value, $personal_message);
|
||||
}
|
||||
$this->write_debug_log("[SMS LOG] To: {$member['mb_hp']}, Content: {$personal_message}");
|
||||
$success++;
|
||||
} else {
|
||||
$empty = !empty($member['mb_hp']);
|
||||
$this->write_debug_log("[SMS 발송오류] 사용자 '{$member['mb_sms']}' '{$member['mb_hp']}' '$empty'");
|
||||
$fail++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// --- 실제 발송 모드: DB 기록 및 실제 발송 ---
|
||||
$sms_config = sql_fetch("SELECT * FROM {$this->g5['sms5_config_table']}");
|
||||
$send_phone = $sms_config['cf_phone'];
|
||||
|
||||
$wr_message = sql_real_escape_string($template['content']);
|
||||
$wr_reply = sql_real_escape_string($send_phone);
|
||||
sql_query("INSERT INTO {$this->g5['sms5_write_table']} (wr_message, wr_reply, wr_total, wr_datetime) VALUES ('{$wr_message}', '{$wr_reply}', '" . count($members) . "', '" . G5_TIME_YMDHIS . "')");
|
||||
$wr_no = sql_insert_id();
|
||||
|
||||
$SMS = null;
|
||||
if (class_exists('SMS5')) {
|
||||
$SMS = new SMS5;
|
||||
$SMS->SMS_con($sms_config['cf_sms_ip'], $sms_config['cf_sms_id'], $sms_config['cf_sms_pw'], $sms_config['cf_sms_port']);
|
||||
} else {
|
||||
$this->write_debug_log("[SMS 발송 오류] SMS5 클래스를 찾을 수 없습니다. 실제 발송을 건너뜁니다.");
|
||||
}
|
||||
|
||||
foreach ($members as $member) {
|
||||
if ($member['mb_sms'] && !empty($member['mb_hp'])) {
|
||||
$personal_vars = array_merge($common_vars, ['이름' => $member['mb_name'], 'agent_name' => $member['mb_name'], 'dealer_name' => $member['mb_name']]);
|
||||
$personal_message = $template['content'];
|
||||
foreach ($personal_vars as $key => $value) {
|
||||
$personal_message = str_replace('{' . $key . '}', $value, $personal_message);
|
||||
}
|
||||
|
||||
$result_code = 'Fail';
|
||||
$result_msg = 'SMS5 클래스가 없어 발송할 수 없습니다.';
|
||||
$hs_status = '0';
|
||||
|
||||
if ($SMS) {
|
||||
$SMS->Add($member['mb_hp'], $send_phone, '', $personal_message);
|
||||
$SMS->Send();
|
||||
$result_arr = $SMS->Result;
|
||||
$result_code = 'Fail';
|
||||
$result_msg = '서버로부터 응답이 없습니다.';
|
||||
|
||||
if(!empty($result_arr)){
|
||||
$result_parts = explode(':', $result_arr[0]);
|
||||
if(count($result_parts) > 1 && strpos($result_parts[1], 'Error') === false) {
|
||||
$result_code = 'Success';
|
||||
$result_msg = $result_parts[1];
|
||||
} else {
|
||||
$result_msg = $result_arr[0];
|
||||
}
|
||||
}
|
||||
$hs_status = ($result_code == 'Success') ? '1' : '0';
|
||||
$SMS->Init();
|
||||
}
|
||||
|
||||
sql_query("INSERT INTO {$this->g5['sms5_history_table']} (wr_no, mb_id, hs_name, hs_hp, hs_datetime, hs_status, hs_message) VALUES ('{$wr_no}', '{$member['mb_id']}', '{$member['mb_name']}', '{$member['mb_hp']}', '" . G5_TIME_YMDHIS . "', '{$hs_status}', '{$result_msg}')");
|
||||
|
||||
if ($hs_status == '1') $success++;
|
||||
else $fail++;
|
||||
} else {
|
||||
$fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['success' => $success, 'fail' => $fail];
|
||||
}
|
||||
|
||||
/**
|
||||
* ❗ [핵심 수정] 이메일 발송 (MailSender 클래스 활용)
|
||||
*/
|
||||
private function sendEmail($members, $template_key, $common_vars)
|
||||
{
|
||||
$success = 0;
|
||||
$fail = 0;
|
||||
|
||||
$notification_mode = get_order_config('notification_mode', 'log');
|
||||
$is_test_mode = ($notification_mode !== 'send');
|
||||
$sizeof_members = count($members);
|
||||
if ($is_test_mode) {
|
||||
// --- 개발 모드: 로그 파일에만 기록 ---
|
||||
$template = sql_fetch("SELECT * FROM `mail_templates` WHERE template_key = '" . sql_real_escape_string($template_key) . "'");
|
||||
if (!$template) {
|
||||
$this->write_debug_log("[EMAIL 발송 오류] 템플릿 '{$template_key}'을(를) 찾을 수 없습니다.");
|
||||
return ['success' => 0, 'fail' => count($members)];
|
||||
}
|
||||
|
||||
foreach ($members as $member) {
|
||||
if ($member['mb_mailling'] && !empty($member['mb_email'])) {
|
||||
$personal_vars = array_merge($common_vars, ['이름' => $member['mb_name'], 'agent_name' => $member['mb_name'], 'dealer_name' => $member['mb_name']]);
|
||||
$subject = $template['subject'];
|
||||
$content = $template['content'];
|
||||
foreach ($personal_vars as $key => $value) {
|
||||
$search = '{' . $key . '}';
|
||||
$subject = str_replace($search, $value, $subject);
|
||||
$content = str_replace($search, $value, $content);
|
||||
}
|
||||
$this->write_debug_log("[EMAIL LOG] To: {$member['mb_email']}, Subject: {$subject}, Content: {$content}");
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
$empty = !empty($member['mb_email']);
|
||||
$this->write_debug_log("[EMAIL 발송 오류] 사용자 '{$member['mb_sms']}' '{$member['mb_hp']}' '$empty'");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// --- 실제 발송 모드: MailSender 호출 ---
|
||||
if (!class_exists('MailSender')) {
|
||||
$this->write_debug_log("[EMAIL 발송 오류] MailSender 클래스를 찾을 수 없습니다.");
|
||||
return ['success' => 0, 'fail' => count($members)];
|
||||
}
|
||||
$mailSender = new MailSender();
|
||||
|
||||
foreach ($members as $member) {
|
||||
if ($member['mb_mailling'] && !empty($member['mb_email'])) {
|
||||
$personal_vars = array_merge($common_vars, ['이름' => $member['mb_name'], 'agent_name' => $member['mb_name'], 'dealer_name' => $member['mb_name']]);
|
||||
// MailSender의 send 메소드는 내부적으로 템플릿 조회 및 변수 치환을 모두 처리합니다.
|
||||
$send_result = $mailSender->send($template_key, $member['mb_email'], $personal_vars);
|
||||
if ($send_result) $success++;
|
||||
else $fail++;
|
||||
} else {
|
||||
$fail++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['success' => $success, 'fail' => $fail];
|
||||
}
|
||||
|
||||
/**
|
||||
* 결과 메시지 생성
|
||||
*/
|
||||
private function generateResultMessage($results)
|
||||
{
|
||||
$message = "발송이 완료되었습니다.\n\n";
|
||||
$message .= "전체 대상: " . $results['total_targets'] . "명\n\n";
|
||||
if (isset($results['sms_success'])) {
|
||||
$message .= "SMS 발송 결과: 성공 " . $results['sms_success'] . "건, 실패 " . $results['sms_fail'] . "건\n";
|
||||
}
|
||||
if (isset($results['email_success'])) {
|
||||
$message .= "이메일 발송 결과: 성공 " . $results['email_success'] . "건, 실패 " . $results['email_fail'] . "건\n";
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* ❗ [핵심 수정] 디버그 로그 기록 함수 (권한 문제 해결)
|
||||
*/
|
||||
private function write_debug_log($message)
|
||||
{
|
||||
$log_dir = G5_PATH . '/log';
|
||||
|
||||
// 1. 디렉토리 존재 여부 확인 및 생성
|
||||
if (!is_dir($log_dir)) {
|
||||
if (!@mkdir($log_dir, 0755, true) && !is_dir($log_dir)) {
|
||||
error_log("--- NotificationSender ERROR: 디버그 로그 디렉토리 생성 실패. '{$log_dir}' 경로를 확인하거나 수동으로 생성 후 웹서버 쓰기 권한을 부여해주세요.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 디렉토리 쓰기 권한 확인
|
||||
if (!is_writable($log_dir)) {
|
||||
error_log("--- NotificationSender ERROR: 디버그 로그 쓰기 오류. '{$log_dir}' 디렉토리에 쓰기 권한이 없습니다. 웹서버의 폴더 권한을 확인해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 로그 파일에 내용 기록
|
||||
$log_file = $log_dir . '/notification_debug.log';
|
||||
$log_message = date("[Y-m-d H:i:s]") . " " . $message . "\n";
|
||||
|
||||
if (file_put_contents($log_file, $log_message, FILE_APPEND | LOCK_EX) === false) {
|
||||
error_log("--- NotificationSender ERROR: 디버그 로그 파일 쓰기 실패. '{$log_file}' 파일에 내용을 쓸 수 없습니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_'))
|
||||
exit;
|
||||
|
||||
if (!function_exists('get_status_manager')) {
|
||||
/**
|
||||
* StatusManager 인스턴스 가져오기
|
||||
* @return StatusManager
|
||||
*/
|
||||
function get_status_manager()
|
||||
{
|
||||
static $status_manager = null;
|
||||
if ($status_manager === null) {
|
||||
if (!class_exists('StatusManager')) {
|
||||
require_once G5_PATH . '/adm/order_manage/classes/StatusManager.class.php';
|
||||
}
|
||||
$status_manager = new StatusManager();
|
||||
}
|
||||
return $status_manager;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 코드를 화면 표시용 이름으로 변환합니다.
|
||||
* @param string $status_code 실제 상태 코드
|
||||
* @return string 화면 표시용 이름
|
||||
*/
|
||||
function get_status_display_name($status_code)
|
||||
{
|
||||
$config_key = $status_code;
|
||||
$result = sql_fetch("SELECT config_value FROM order_config WHERE config_key = '" . sql_real_escape_string($config_key) . "'");
|
||||
return $result ? $result['config_value'] : $status_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 상태 코드와 표시명을 가져옵니다.
|
||||
* @return array 상태 코드 => 표시명 배열
|
||||
*/
|
||||
function get_all_status_names()
|
||||
{
|
||||
$statuses = [];
|
||||
$result = sql_query("SELECT config_key, config_value FROM order_config WHERE config_key LIKE 'status_%' ORDER BY config_key ASC");
|
||||
while ($row = sql_fetch_array($result)) {
|
||||
$status_code = str_replace('status_', '', $row['config_key']);
|
||||
$statuses[$status_code] = $row['config_value'];
|
||||
}
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 권한별 상태 전환 규칙을 가져옵니다.
|
||||
* @param string $user_role 사용자 역할 (customer, agent, admin)
|
||||
* @return array 상태 전환 규칙
|
||||
*/
|
||||
function get_status_transitions($user_role)
|
||||
{
|
||||
$transitions = [];
|
||||
|
||||
switch ($user_role) {
|
||||
case 'customer':
|
||||
$transitions = [
|
||||
'견적신청중' => ['작성완료'],
|
||||
'작성완료' => [], // 고객은 작성완료 후 변경 불가
|
||||
'견적채택' => ['입금예정'], // 견적 선택 시 자동 전환
|
||||
'입금예정' => [], // 관리자만 변경 가능
|
||||
'입금확인' => [],
|
||||
'다운로드' => []
|
||||
];
|
||||
break;
|
||||
|
||||
case 'agent':
|
||||
$transitions = [
|
||||
'견적제안' => ['견적채택', '견적취소'],
|
||||
'견적채택' => [],
|
||||
'견적취소' => []
|
||||
];
|
||||
break;
|
||||
|
||||
case 'admin':
|
||||
$transitions = [
|
||||
'견적신청중' => ['작성완료'],
|
||||
'작성완료' => ['견적신청중', '입금예정'], // 되돌리기 가능
|
||||
'견적채택' => ['입금예정'],
|
||||
'입금예정' => ['입금확인'],
|
||||
'입금확인' => ['다운로드'],
|
||||
'다운로드' => ['견적신청중'], // 루프백
|
||||
'견적제안' => ['견적채택', '견적취소'],
|
||||
'견적취소' => ['견적제안'] // 되돌리기 가능
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
return $transitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 전환이 유효한지 확인합니다.
|
||||
* @param string $current_status 현재 상태
|
||||
* @param string $new_status 새 상태
|
||||
* @param string $user_role 사용자 역할
|
||||
* @return bool 전환 가능 여부
|
||||
*/
|
||||
function is_valid_status_transition($current_status, $new_status, $user_role)
|
||||
{
|
||||
$transitions = get_status_transitions($user_role);
|
||||
|
||||
if (!isset($transitions[$current_status])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($new_status, $transitions[$current_status]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태별 CSS 클래스를 가져옵니다.
|
||||
* @param string $status_code 상태 코드
|
||||
* @return string CSS 클래스명
|
||||
*/
|
||||
function get_status_css_class($status_code)
|
||||
{
|
||||
$css_classes = [
|
||||
'견적신청중' => 'btn-request',
|
||||
'작성완료' => 'btn-offer',
|
||||
'견적채택' => 'btn-select',
|
||||
'입금예정' => 'btn-deposit-ready',
|
||||
'입금확인' => 'btn-deposit-done',
|
||||
'다운로드' => 'btn-excel',
|
||||
'견적제안' => 'btn-offer',
|
||||
'견적취소' => 'btn-cancel'
|
||||
];
|
||||
|
||||
return $css_classes[$status_code] ?? 'btn-request';
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
/**
|
||||
* UI 리소스 관리자 클래스 인스턴스를 반환하는 헬퍼 함수
|
||||
* @return UiManager
|
||||
*/
|
||||
function ui_manager() {
|
||||
// 클래스가 아직 로드되지 않았다면 인스턴스 생성
|
||||
if (!class_exists('UiManager')) {
|
||||
// UiManager 클래스 정의
|
||||
class UiManager
|
||||
{
|
||||
private static $instance = null;
|
||||
private $resources = []; // 데이터를 캐시할 배열
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function get_label($resource_code, $lang = 'ko')
|
||||
{
|
||||
if (isset($this->resources['labels'][$lang][$resource_code])) {
|
||||
return $this->resources['labels'][$lang][$resource_code];
|
||||
}
|
||||
|
||||
global $g5;
|
||||
$resource_code_escaped = sql_real_escape_string($resource_code);
|
||||
$lang_escaped = sql_real_escape_string($lang);
|
||||
|
||||
$sql = "SELECT B.cl_name
|
||||
FROM {$g5['ui_manager_table']} AS A
|
||||
LEFT JOIN {$g5['common_lang_table']} AS B
|
||||
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = '{$lang_escaped}')
|
||||
WHERE A.resource_code = '{$resource_code_escaped}' AND A.resource_type = 'LABEL'";
|
||||
$row = sql_fetch($sql);
|
||||
|
||||
$label_text = $row['cl_name'] ?? $resource_code;
|
||||
$this->resources['labels'][$lang][$resource_code] = $label_text;
|
||||
return $label_text;
|
||||
}
|
||||
|
||||
public function get_data($resource_code, $lang = 'ko')
|
||||
{
|
||||
if (isset($this->resources['data'][$lang][$resource_code])) {
|
||||
return $this->resources['data'][$lang][$resource_code];
|
||||
}
|
||||
|
||||
global $g5;
|
||||
$resource_code_escaped = sql_real_escape_string($resource_code);
|
||||
$lang_escaped = sql_real_escape_string($lang);
|
||||
|
||||
$sql_um = "SELECT um_id FROM {$g5['ui_manager_table']} WHERE resource_code = '{$resource_code_escaped}' AND resource_type = 'DATA'";
|
||||
$um_row = sql_fetch($sql_um);
|
||||
|
||||
if (!isset($um_row['um_id'])) {
|
||||
$this->resources['data'][$lang][$resource_code] = [];
|
||||
return [];
|
||||
}
|
||||
$um_id = $um_row['um_id'];
|
||||
|
||||
$sql = "SELECT A.fc_id, A.parent_id, A.fc_key, A.fc_order, B.cl_name
|
||||
FROM {$g5['form_category_table']} AS A
|
||||
LEFT JOIN {$g5['common_lang_table']} AS B
|
||||
ON (A.fc_id = B.target_id AND B.target_table = '{$g5['form_category_table']}' AND B.lang_code = '{$lang_escaped}')
|
||||
WHERE A.um_id = '{$um_id}' AND A.is_used = 1 AND A.is_deleted = 0
|
||||
ORDER BY A.fc_order, A.fc_id";
|
||||
|
||||
$result = sql_query($sql);
|
||||
$options = [];
|
||||
while ($row = sql_fetch_array($result)) {
|
||||
$options[] = $row;
|
||||
}
|
||||
|
||||
$this->resources['data'][$lang][$resource_code] = $options;
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function render_select($resource_code, $select_name, $selected_value = '', $attributes = '', $lang = 'ko')
|
||||
{
|
||||
$options = $this->get_data($resource_code, $lang);
|
||||
|
||||
if (empty($options)) {
|
||||
return "<select name=\"".htmlspecialchars($select_name)."\" {$attributes}><option value=\"\">옵션 없음</option></select>";
|
||||
}
|
||||
|
||||
$html = "<select name=\"".htmlspecialchars($select_name)."\" {$attributes}>";
|
||||
$html .= "<option value=\"\">선택</option>";
|
||||
foreach ($options as $option) {
|
||||
$selected = ($option['fc_key'] == $selected_value) ? ' selected' : '';
|
||||
$html .= "<option value=\"" . htmlspecialchars($option['fc_key']) . "\"{$selected}>" . htmlspecialchars($option['cl_name']) . "</option>";
|
||||
}
|
||||
$html .= "</select>";
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UiManager::getInstance();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user