380 lines
18 KiB
PHP
380 lines
18 KiB
PHP
<?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')) {
|
|
// 💡 [수정] 라이브러리 원본 파일(icode.lms.lib.php 등)의 Deprecated 경고를 숨기기 위해 에러 리포팅 임시 조정
|
|
// (PHP 8.0+에서 선택적 파라미터 선언 순서 문제로 인한 경고 회피)
|
|
$old_reporting_level = error_reporting();
|
|
error_reporting($old_reporting_level & ~E_DEPRECATED);
|
|
|
|
include_once(G5_PLUGIN_PATH . '/sms5/sms5.lib.php');
|
|
|
|
error_reporting($old_reporting_level);
|
|
}
|
|
|
|
/**
|
|
* 통합 메일/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 ($params['target_type'] === 'direct' && empty($params['receivers'])) {
|
|
return ['success' => false, 'message' => '직접 발송 시 수신자 정보(receivers)가 필요합니다.'];
|
|
}
|
|
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 = ''";
|
|
$this->write_debug_log("[SMS 발송 시작] sql '{$sql}'");
|
|
$result = sql_query($sql);
|
|
while ($row = sql_fetch_array($result)) {
|
|
$members[] = $row;
|
|
}
|
|
} elseif ($params['target_type'] === 'bulk') {
|
|
$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;
|
|
}
|
|
} elseif ($params['target_type'] === 'direct') {
|
|
// 직접 입력된 수신자 정보 사용
|
|
$members = $params['receivers'];
|
|
}
|
|
|
|
return $members;
|
|
}
|
|
|
|
/**
|
|
* ❗ [핵심 수정] SMS 발송 (템플릿 및 변수 처리, 이력 기록 포함)
|
|
*/
|
|
private function sendSMS($members, $template_key, $common_vars)
|
|
{
|
|
global $config; // config 전역 변수 사용
|
|
|
|
$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 `order_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';
|
|
$hs_code = '';
|
|
|
|
if ($SMS) {
|
|
// ❗ [수정] SMS/LMS 타입에 따라 함수 호출 분기 및 파라미터 순서 명시
|
|
if($config['cf_sms_type'] == 'LMS') {
|
|
// LMS: Add($strDest, $strCallBack, $strCaller, $strSubject, $strURL, $strData, $strDate, $nCount)
|
|
// $strDest는 배열로 전달
|
|
$SMS->Add(array($member['mb_hp']), $send_phone, '', '', '', $personal_message, '', 1);
|
|
} else {
|
|
// SMS: Add2($strDest, $strCallBack, $strCaller, $strURL, $strMessage, $strDate, $nCount)
|
|
// $strDest는 배열(구조체)로 전달
|
|
$dest = array(array('bk_hp' => $member['mb_hp'], 'bk_name' => $member['mb_name']));
|
|
$SMS->Add2($dest, $send_phone, '', '', $personal_message, '', 1);
|
|
}
|
|
|
|
$SMS->Send();
|
|
$result_arr = $SMS->Result;
|
|
$result_code = 'Fail';
|
|
$result_msg = '서버로부터 응답이 없습니다.';
|
|
|
|
if(!empty($result_arr)){
|
|
// 💡 [추가] 상세 에러 코드 처리 (sms_write_send.php 참고)
|
|
foreach ($result_arr as $result) {
|
|
list($phone, $code) = explode(":", $result);
|
|
if (substr($code, 0, 5) == "Error") {
|
|
$hs_code = substr($code, 6, 2);
|
|
switch ($hs_code) {
|
|
case '02': $result_msg = "형식이 잘못되어 전송이 실패하였습니다."; break;
|
|
case '23': $result_msg = "데이터를 다시 확인해 주시기바랍니다."; break;
|
|
case '97': $result_msg = "잔여코인이 부족합니다."; break;
|
|
case '98': $result_msg = "사용기간이 만료되었습니다."; break;
|
|
case '99': $result_msg = "인증 받지 못하였습니다. 계정을 다시 확인해 주세요."; break;
|
|
default: $result_msg = "알 수 없는 오류로 전송이 실패하였습니다."; break;
|
|
}
|
|
$result_code = 'Fail';
|
|
} else {
|
|
$hs_code = $code;
|
|
$result_msg = "전송했습니다.";
|
|
$result_code = 'Success';
|
|
}
|
|
}
|
|
}
|
|
$hs_status = ($result_code == 'Success') ? '1' : '0';
|
|
$SMS->Init();
|
|
}
|
|
|
|
$mb_id = isset($member['mb_id']) ? $member['mb_id'] : ''; // 비회원일 경우 mb_id 없음
|
|
sql_query("INSERT INTO {$this->g5['sms5_history_table']} (wr_no, mb_id, hs_name, hs_hp, hs_datetime, hs_status, hs_code, hs_message) VALUES ('{$wr_no}', '{$mb_id}', '{$member['mb_name']}', '{$member['mb_hp']}', '" . G5_TIME_YMDHIS . "', '{$hs_status}', '{$hs_code}', '{$result_msg}')");
|
|
|
|
if ($hs_status == '1') $success++;
|
|
else $fail++;
|
|
} else {
|
|
$fail++;
|
|
}
|
|
}
|
|
|
|
// 💡 [추가] 발송 완료 후 마스터 테이블 업데이트
|
|
sql_query("UPDATE {$this->g5['sms5_write_table']} SET wr_success = '{$success}', wr_failure = '{$fail}' WHERE wr_no = '{$wr_no}'");
|
|
}
|
|
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);
|
|
$template = sql_fetch("SELECT * FROM `order_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)];
|
|
}
|
|
if ($is_test_mode) {
|
|
// --- 개발 모드: 로그 파일에만 기록 ---
|
|
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']]);
|
|
$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);
|
|
}
|
|
if($mailSender->sendSimple($member['mb_email'], $subject,$content)){
|
|
$success++;
|
|
} else {
|
|
$fail++;
|
|
}
|
|
|
|
} else {
|
|
$fail++;
|
|
$empty = !empty($member['mb_email']);
|
|
$this->write_debug_log("[EMAIL 발송 오류] 사용자 '{$member['mb_sms']}' '{$member['mb_hp']}' '$empty'");
|
|
}
|
|
}
|
|
}
|
|
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}' 파일에 내용을 쓸 수 없습니다.");
|
|
}
|
|
}
|
|
} |