본문으로 건너뛰기

소스 변경 전체 보기

PHP 7 · GODO25
변경 파일 수: 40개
변경 파일 목록
총 40개
문자 발신번호 관리 프로세스 개선.
layer_manage_call_number.phpAdmin/member/sms_auto.phpAdmin/member/sms_send.phpAdmin/member/admin_log_list.phpAdmin/policy/manage_list.phpAdmin/policy/_manage_permission_manager_list.phpAdmin/policy/_manage_permission_menu_list.phpAdmin/policy/manage_permission.phpAdmin/policy/manage_register.phpAdmin/policy/manage_security.phpAdmin/policy/cart_remind_list.phpAdmin/promotion/comeback_coupon_list.phpAdmin/promotion/layer_message_representative_register_guide.phpAdmin/share/layer_sms_personal_info_access_log.phpAdmin/share/manager_certification_result.phpAdmin/share/AdminLogDAO.phpComponent/Admin/AdminMenu.phpComponent/Admin/CartRemind.phpComponent/CartRemind/CouponAdmin.phpComponent/Coupon/DBTableField.phpComponent/Database/ManagerCs.phpComponent/Member/Manager.phpComponent/Member/SmsAdmin.phpComponent/Sms/SmsAuto.phpComponent/Sms/SmsSender.phpComponent/Sms/LayerSmsAuthController.phpController/Admin/Base/LoginPsController.phpController/Admin/Base/SmsAutoController.phpController/Admin/Member/SmsPsController.phpController/Admin/Member/SmsSendController.phpController/Admin/Member/AdminLogListController.phpController/Admin/Policy/ManageListController.phpController/Admin/Policy/ManagePermissionController.phpController/Admin/Policy/ManagePsController.phpController/Admin/Policy/ManageRegisterController.phpController/Admin/Policy/ManageSecurityController.phpController/Admin/Policy/CartRemindListController.phpController/Admin/Promotion/ComebackCouponListController.phpController/Admin/Promotion/LayerExcelAuthController.phpController/Admin/Share/LayerManagePermissionController.phpController/Admin/Share/
변경 파일 코드
추가삭제
문자 발신번호 관리 프로세스 개선.
Admin/member/layer_manage_call_number.php+201
변경 전layer_manage_call_number.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
변경 후layer_manage_call_number.php
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<style>
/* 어드민 공용 CSS 가 라디오를 배경 이미지로 대체해 :disabled 의 grayscale 이 드러나지 않으므로 이 레이어에서만 비활성 표시를 덧붙인다 */
#layerManageCallNumber input[type="radio"]:disabled {
opacity: 0.4;
cursor: not-allowed;
}
</style>
<div id="layerManageCallNumber">
<table class="table table-rows table-fixed">
<colgroup>
<col class="width10p"/>
<col class="width30p"/>
<col class="width20p"/>
<col class="width10p"/>
<col class="width20p"/>
<col class="width10p"/>
</colgroup>
<thead>
<tr>
<th>선택</th>
<th>발신번호</th>
<th>관리메모</th>
<th>상태</th>
<th>승인일</th>
<th>관리</th>
</tr>
</thead>
<tbody>
<?php
if (!empty($smsCallNumberList)) {
foreach ($smsCallNumberList as $key => $smsCallNumber) {
?>
<tr class="text-center">
<td class="form-inline">
<?php if ($currentCallNumber !== '' && $currentCallNumber === $smsCallNumber->getCallNumber()) { ?>
사용 중
<?php } else { ?>
<input type="radio" name="selectNum" id="selectNum<?= $key ?>" value="<?= $smsCallNumber->getCallNumber() ?>" text="<?= gd_number_to_phone($smsCallNumber->getCallNumber()) ?>"<?= !$smsCallNumber->isApproved() ? ' disabled="disabled"' : '' ?> />
<?php } ?>
</td>
<td>
<label for="selectNum<?= $key ?>">
<span class="number text-darkblue bold"><?= gd_number_to_phone($smsCallNumber->getCallNumber()) ?></span>
</label>
</td>
<td><?= gd_htmlspecialchars($smsCallNumber->getAliasName()) ?></td>
<td><?= $smsCallNumber->getStatusName() ?: '-' ?></td>
<td><?= $smsCallNumber->getApprovedAt() ?? '-' ?></td>
<td>
<?php if ($smsCallNumber->isDeletable()) { ?>
<button type="button" class="btn btn-white btn-xs js-delete-call-number" data-call-number-no="<?= $smsCallNumber->getCallNumberNo() ?>" data-call-number="<?= $smsCallNumber->getCallNumber() ?>">삭제</button>
<?php } elseif ($smsCallNumber->hasRejectReason()) { ?>
<button type="button" class="btn btn-white btn-xs js-reject-reason" data-manage-status="<?= $smsCallNumber->getStatus() ?>" data-reject-reason="<?= gd_htmlspecialchars($smsCallNumber->getReason() ?? '') ?>">사유</button>
<?php } else { ?>
-
<?php } ?>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="6" class="no-data">등록된 발신번호가 없습니다.</td>
</tr>
<?php } ?>
</tbody>
</table>
 
<div class="notice-info">발신번호 등록·삭제는 발신관리책임자(최고운영자)만 가능합니다.</div>
<div class="notice-info">발신번호 회선은 최대 유선 발신번호 5회선, 무선 발신번호 3회선 까지만 등록 가능합니다.</div>
<div class="notice-info">등록한 발신번호는 통합회원이 보유한 다른 쇼핑몰 어드민에도 함께 등록됩니다.</div>
 
<div class="text-center"><?= $page->getPage('loadManageCallNumberLayer(\'PAGELINK\')') ?></div>
 
<div class="text-center">
<button type="button" class="btn btn-white btn-lg js-register-call-number">발신번호 등록</button>
<button type="button" class="btn btn-black btn-lg js-submit">발신번호 선택하기</button>
</div>
</div>
 
<script type="text/javascript">
<!--
// 이 fragment 는 레이어 페이지 이동마다 재주입되므로 top-level const/let 선언 시 재선언 SyntaxError 가 발생한다
var returnInputName = <?= json_encode($returnInput) ?>;
 
$(document).ready(function () {
// 발신번호 선택하기
$('#layerManageCallNumber .js-submit').click(function (e) {
var selectNum = $('#layerManageCallNumber input:radio[name=selectNum]:checked').val();
var selectNumText = $('#layerManageCallNumber input:radio[name=selectNum]:checked').attr('text');
if (typeof selectNum == 'undefined') {
alert('발신번호를 선택해 주세요.');
return;
}
saveCallNumber(selectNum, selectNumText);
});
 
// 발신번호 등록
$('#layerManageCallNumber .js-register-call-number').click(function (e) {
var isRepresentative = <?= !empty($isRepresentative) ? 'true' : 'false' ?>;
if (!isRepresentative) {
dialog_alert('발신관리책임자만 이용할 수 있습니다.<br/>발신관리책임자(최고운영자)에게 문의하시기 바랍니다.');
return;
}
 
SmsCallerHubLayer.openRegistration({
onCompleted: function () {
loadManageCallNumberLayer();
}
});
});
 
// 발신번호 삭제
$('#layerManageCallNumber .js-delete-call-number').click(function (e) {
var isRepresentative = <?= !empty($isRepresentative) ? 'true' : 'false' ?>;
 
if (!isRepresentative) {
dialog_alert('발신관리책임자만 이용할 수 있습니다.<br/>발신관리책임자(최고운영자)에게 문의하시기 바랍니다.');
return;
}
 
var callNumberNo = $(this).data('call-number-no');
var callNumber = String($(this).data('call-number'));
// 목록의 '사용 중' 표기와 같은 기준이어야 하므로 이 몰의 현재 발신번호와 비교한다
var inUse = callNumber === <?= json_encode($currentCallNumber) ?>;
var message = (inUse ? '사용 중인 발신번호입니다. 삭제하시겠습니까?' : '발신번호를 삭제하시겠습니까?')
+ '<br/><br/>발신번호 삭제 시, 다시 복구되지 않습니다.<br/>삭제 후 자동알림을 포함한 SMS가 발송되지 않습니다.';
 
dialog_confirm(message, function (result) {
if (result) {
var params = {
mode: 'deleteCallNumber',
callNumberNo: callNumberNo,
callNumber: callNumber
};
$.post('../member/layer_manage_call_number_ps.php', params, function (response) {
if (!response || response.success !== true) {
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.');
return;
}
dialog_alert('발신번호 삭제가 완료되었습니다.');
loadManageCallNumberLayer();
}).fail(function (xhr) {
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.');
});
}
}, '발신번호 삭제');
});
 
// 반려·차단 사유
$('#layerManageCallNumber .js-reject-reason').click(function (e) {
var reasonTitle = $(this).data('manage-status') === 'BLOCKED' ? '발신번호 차단 사유' : '발신번호 반려 사유';
dialog_alert($(this).data('reject-reason'), reasonTitle);
});
});
 
// 폼 저장 버튼이 없는 화면(SMS 발송)에서도 열리는 레이어라 선택 시점에 바로 저장한다
function saveCallNumber(callNumber, callNumberText) {
// 이 레이어는 member 외 화면(promotion 등)에서도 열리므로 상대경로를 쓰면 base 가 달라져 404 가 된다
$.post('/member/layer_manage_call_number_ps.php', {mode: 'setCallNumber', callNumber: callNumber}, function (response) {
if (!response || response.success !== true) {
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.');
return;
}
 
// 부모 폼이 smsCallNum 을 그대로 재저장하는 화면(자동알림 설정)이 있어 저장 직후 입력값까지 맞춘다
$('input[name="' + returnInputName + '"]').val(callNumber);
$('.smsCallNumText').html(callNumberText || callNumber);
$('.smsCallNumText').addClass('number text-darkblue bold');
// dialog_alert 는 [X]·ESC 로도 닫혀 콜백이 보장되지 않으므로 레이어를 먼저 닫는다
layer_close();
dialog_alert('선택한 발신번호가 저장 완료되었습니다.', '안내');
}).fail(function (xhr) {
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.');
});
}
 
function loadManageCallNumberLayer(pagelink) {
if (typeof pagelink == 'undefined') {
pagelink = '';
}
 
var parameters = {
'page': pagelink === '' ? 1 : parseInt(pagelink.replace('page=', ''), 10),
'returnInput': returnInputName
};
 
$.post('../member/layer_manage_call_number.php', parameters, function (data) {
$('#layerManageCallNumber').html(data);
}).fail(function (xhr) {
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.');
});
}
//-->
</script>
 
Admin/member/sms_auto.php+34-22
변경 전sms_auto.php
<!-- //@formatter:off -->
 
 
<input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/>
<input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/>
<input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/>
<tr>
<th>SMS 발신번호</th>
<td class="form-inline">
<?php if ($smsPreRegister === false) { ?>
 
 
 
 
 
 
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a>
<?php } ?>
</div>
<div class="notice-info">
발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" class="snote bold">자세히보기 ></a>
</div>
<?php } elseif ($smsPreRegister === 'reset') { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">기 설정된 SMS 발신번호는 사전 등록된 번호가 아닙니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
<?php } ?>
</div>
<?php } elseif ($smsPreRegister === 'empty') { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
</span>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
</div>
<?php } elseif ($smsPreRegister === true) { ?>
<input type="hidden" name="smsCallNum" value="<?php echo gd_isset($smsAutoData['smsCallNum']) ?>"/>
<span class="smsCallNumText number text-blue bold"><?php echo gd_number_to_phone($smsAutoData['smsCallNum']) ?></span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button>
<?php } ?>
<?php } ?>
<?php if ($smsPreRegister !== true) { ?>
<div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div>
<?php } ?>
</td>
</tr>
<tr>
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_sms_call_number_select.php', params, function (data) {
 
 
 
 
 
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
});
});
 
 
변경 후sms_auto.php
<!-- //@formatter:off -->
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/>
<input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/>
<input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/>
<tr>
<th>SMS 발신번호</th>
<td class="form-inline">
<?php if (!empty($smsAutoData['smsCallNum'])) { ?>
<input type="hidden" name="smsCallNum" value="<?= gd_isset($smsAutoData['smsCallNum']) ?>"/>
<span class="smsCallNumText number text-blue bold"><?= gd_number_to_phone($smsAutoData['smsCallNum']) ?></span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button>
<?php } ?>
<?php } elseif (!$hasRegisteredCallNumber) { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button>
<?php } ?>
</div>
<div class="notice-info">
발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" class="snote bold">자세히보기 ></a>
</div>
<?php } else { ?>
 
 
 
 
 
 
 
 
 
 
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
</span>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
</div>
 
 
 
 
 
 
<?php } ?>
 
<div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div>
 
</td>
</tr>
<tr>
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_manage_call_number.php', params, function (data) {
// 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다
if (typeof data === 'object' && data !== null) {
dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
return;
}
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
}).fail(function (xhr) {
var response = xhr.responseJSON;
if (response && response.data && response.data.canRegisterRepresentative) {
SmsCallerHubLayer.openRepresentativeRegisterGuide();
return;
}
if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) {
SmsCallerHubLayer.openMigration({
// 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다
onCompleted: function () {
$('.js-sms-call-number').click();
}
});
return;
}
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
});
});
 
 
Admin/member/sms_send.php+34-22
변경 전sms_send.php
 
 
<script type="text/javascript">
<!--
/**
// SMS 사전 등록 발신번호 선택하기
$('.js-sms-call-number').click(function (e) {
var params = {returnInput: 'smsCallNum'};
$.get('../member/layer_sms_call_number_select.php', params, function (data) {
 
 
 
 
 
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
});
});
 
<tr>
<th class="require">SMS 발신번호</th>
<td class="form-inline">
<?php if ($smsPreRegister === false) { ?>
 
 
 
 
 
 
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a>
<?php } ?>
</div>
<div class="notice-info">
발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank"
class="snote bold btn-link">자세히보기 ></a>
</div>
<?php } elseif ($smsPreRegister === 'reset') { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">기 설정된 SMS 발신번호는 사전 등록된 번호가 아닙니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
<?php } ?>
</div>
<?php } elseif ($smsPreRegister === 'empty') { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
</span>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
</div>
<?php } elseif ($smsPreRegister === true) { ?>
<input type="hidden" name="smsCallNum" value="<?php echo gd_isset($smsCallNum) ?>"/>
<span class="smsCallNumText number text-darkblue bold"><?php echo gd_number_to_phone($smsCallNum) ?></span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button>
<?php } ?>
<?php } ?>
<?php if ($smsPreRegister !== true) { ?>
<div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div>
<?php } ?>
</td>
</tr>
 
 
변경 후sms_send.php
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<script type="text/javascript">
<!--
/**
// SMS 사전 등록 발신번호 선택하기
$('.js-sms-call-number').click(function (e) {
var params = {returnInput: 'smsCallNum'};
$.get('../member/layer_manage_call_number.php', params, function (data) {
// 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다
if (typeof data === 'object' && data !== null) {
dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
return;
}
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
}).fail(function (xhr) {
var response = xhr.responseJSON;
if (response && response.data && response.data.canRegisterRepresentative) {
SmsCallerHubLayer.openRepresentativeRegisterGuide();
return;
}
if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) {
SmsCallerHubLayer.openMigration({
// 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다
onCompleted: function () {
$('.js-sms-call-number').click();
}
});
return;
}
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
});
});
 
<tr>
<th class="require">SMS 발신번호</th>
<td class="form-inline">
<?php if (!empty($smsCallNum)) { ?>
<input type="hidden" name="smsCallNum" value="<?= gd_isset($smsCallNum) ?>"/>
<span class="smsCallNumText number text-darkblue bold"><?= gd_number_to_phone($smsCallNum) ?></span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button>
<?php } ?>
<?php } elseif (!$hasRegisteredCallNumber) { ?>
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
<span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span>
</span>
<?php if (gd_is_provider() === false) { ?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button>
<?php } ?>
</div>
<div class="notice-info">
발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank"
class="snote bold btn-link">자세히보기 ></a>
</div>
<?php } else { ?>
 
 
 
 
 
 
 
 
 
 
<div>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText">
</span>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button>
</div>
 
 
 
 
 
 
<?php } ?>
 
<div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div>
 
</td>
</tr>
 
 
Admin/policy/admin_log_list.php+37-23
변경 전admin_log_list.php
 
<form name="frmSearch" id="frmSearch" action="admin_log_list.php" method="get" class="js-form-enter-submit">
<input type="hidden" name="view" value="<?=$view;?>">
 
 
<div class="search-detail-box">
<table class="table table-cols mgb15">
<tr>
<th class="width-md">운영자 아이디</th>
<td>
<div class="form-inline">
<?= gd_select_box('searchKind', 'searchKind', $searchKindASelectBox, null, gd_isset($req['searchKind']), null, null, 'form-control '); ?>
<input type="text" class="form-control width300" name="managerId" value="<?=$req['managerId']?>"/>
</div>
</td>
</tr>
<li role="presentation" <?=$view == 'adminAccess' ? 'class="active"' : ''?>>
<a href="../policy/admin_log_list.php?view=adminAccess<?=$queryString;?>">로그인, 인증기록</a>
</li>
 
 
 
</ul>
 
<div class="table-header">
</div>
</form>
 
 
 
 
 
<table class="table table-rows table-fixed">
<colgroup>
<col class="width15p">
<col class="width15p">
<col class="width15p">
<?php if ($view == 'default') { ?><col class="width15p"><?php } ?>
<?php if ($view == 'default' || $view == 'adminList') { ?><col ><?php } ?>
<col class="width15p">
</colgroup>
<thead>
<tr>
<th>접속일시</th>
<th>접속IP</th>
<th>운영자 아이디</th>
<?php if ($view == 'default') { ?><th>메뉴구분</th><?php } ?>
<?php if ($view == 'default' || $view == 'adminList') { ?><th>접속페이지<br>(개인정보관련)</th><?php } ?>
<th>수행업무</th>
</tr>
</thead>
<tbody>
<?php foreach($data['list'] as $row){
if(!$row['page']) {
$row['page'] = $row['baseUri'];
}
<td><?=$row['regDt']?></td>
<td><?=$row['ip']?></td>
<td><?=$row['managerId']?> <?=$row['deleteText']?></td>
<?php if ($view == 'default') { ?><td><?=$row['menu']?></td><?php } ?>
<?php if ($view == 'default' || $view == 'adminList') { ?><td><?=$row['page']?></td><?php } ?>
<td>
<?=$row['action']?>
<?php if ($row['displayDetailLogFl'] == 'y') { ?>
 
 
 
<button class="btn btn-white btn-sm js-detail-log" data-sno="<?=$row['sno']?>">상세</button>
<?php } ?>
 
</td>
</tr>
<?php }?>
</tbody>
</table>
<?php if ($view == 'default') { ?>
<div class="col-xs-12">
<div class="pull-right">
<button type="button" class="btn btn-white js-admin-log-excel">엑셀 다운로드 내역</button>
</button>
</div>
</div>
<?php } ?>
<div align="center"><?=$data['page']; ?></div>
 
<script type="text/javascript">
});
});
 
 
 
 
 
 
 
 
 
 
 
$(document).on('click', '.js-admin-log-excel', function (e) {
var addParam = {
"view": 'layer'
};
layer_add_info('admin_log_excel', addParam);
});
 
//검색어 변경 될 때 placeHolder 교체 및 검색 종류 변환 및 검색 종류 변환
var searchKeyword = $('#frmSearch input[name=managerId]');
var searchKind = $('#frmSearch #searchKind');
setKeywordPlaceholder(searchKeyword, searchKind);
searchKind.change(function (e) {
setKeywordPlaceholder(searchKeyword, searchKind);
});
});
</script>
 
변경 후admin_log_list.php
 
<form name="frmSearch" id="frmSearch" action="admin_log_list.php" method="get" class="js-form-enter-submit">
<input type="hidden" name="view" value="<?=$view;?>">
<?php // 튜닝 컨트롤러는 searchKind 를 강제하지 않아 화면 기본 검색을 전체일치로 유지하려 싣는다 — 쿼리스트링으로 덮을 수 있어 통제 수단은 아니다 ?>
<input type="hidden" name="searchKind" value="equalSearch">
<div class="search-detail-box">
<table class="table table-cols mgb15">
<tr>
<th class="width-md">운영자 아이디</th>
<td>
<div class="form-inline">
<input type="text" class="form-control width300" name="managerId" value="<?= gd_htmlspecialchars($req['managerId'] ?? '') ?>" placeholder="검색어 전체를 정확히 입력하세요."/>
 
</div>
</td>
</tr>
<li role="presentation" <?=$view == 'adminAccess' ? 'class="active"' : ''?>>
<a href="../policy/admin_log_list.php?view=adminAccess<?=$queryString;?>">로그인, 인증기록</a>
</li>
<li role="presentation" <?=$view == 'smsList' ? 'class="active"' : ''?>>
<a href="../policy/admin_log_list.php?view=smsList<?=$queryString;?>">SMS 관련 기록</a>
</li>
</ul>
 
<div class="table-header">
</div>
</form>
 
<?php if ($view == 'smsList' && !empty($data['apiErrorFl'])): ?>
<div class="notice-danger mgb0 mgl15 mgb15">SMS 관련 기록을 조회할 수 없습니다. 잠시 후 다시 시도해 주세요.</div>
<?php endif; ?>
 
<table class="table table-rows table-fixed">
<colgroup>
<col class="width15p">
<col class="width15p">
<col class="width15p">
<?php if ($displayMenuColumnFl): ?><col class="width15p"><?php endif; ?>
<?php if ($displayPageColumnFl): ?><col ><?php endif; ?>
<col class="width15p">
</colgroup>
<thead>
<tr>
<th><?= $view == 'smsList' ? '처리일시' : '접속일시' ?></th>
<th>접속IP</th>
<th>운영자 아이디</th>
<?php if ($displayMenuColumnFl): ?><th>메뉴구분</th><?php endif; ?>
<?php if ($displayPageColumnFl): ?><th>접속페이지<?= $displayPersonalInfoNoteFl ? '<br>(개인정보관련)' : '' ?></th><?php endif; ?>
<th>수행업무</th>
</tr>
</thead>
<tbody>
<?php foreach ($data['list'] as $row):
if(!$row['page']) {
$row['page'] = $row['baseUri'];
}
<td><?=$row['regDt']?></td>
<td><?=$row['ip']?></td>
<td><?=$row['managerId']?> <?=$row['deleteText']?></td>
<?php if ($displayMenuColumnFl): ?><td><?=$row['menu']?></td><?php endif; ?>
<?php if ($displayPageColumnFl): ?><td><?=$row['page']?></td><?php endif; ?>
<td>
<?=$row['action']?>
<?php if ($row['displayDetailLogFl'] == 'y'): ?>
<?php if ($view == 'smsList'): ?>
<button class="btn btn-white btn-sm js-sms-detail-log" data-log-id="<?=$row['logId']?>">상세</button>
<?php else: ?>
<button class="btn btn-white btn-sm js-detail-log" data-sno="<?=$row['sno']?>">상세</button>
<?php endif; ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if ($view == 'default'): ?>
<div class="col-xs-12">
<div class="pull-right">
<button type="button" class="btn btn-white js-admin-log-excel">엑셀 다운로드 내역</button>
</button>
</div>
</div>
<?php endif; ?>
<div align="center"><?=$data['page']; ?></div>
 
<script type="text/javascript">
});
});
 
// SMS 관련 기록 상세
$('.js-sms-detail-log').on('click', function () {
var addParam = {
'logId': $(this).data('log-id'),
'layerTitle': '개인정보접속기록'
};
 
layer_add_info('sms_personal_info_access_log', addParam);
});
 
$(document).on('click', '.js-admin-log-excel', function (e) {
var addParam = {
"view": 'layer'
};
layer_add_info('admin_log_excel', addParam);
});
 
 
 
 
 
 
 
 
});
</script>
 
Admin/policy/manage_list.php+14
변경 전manage_list.php
<input type="text" name="keyword" value="<?= $search['keyword']; ?>" class="form-control width-xl"/>
</td>
</tr>
 
 
 
 
 
 
 
 
 
 
<tbody class="js-search-detail">
<tr>
<th>장기 미로그인</th>
<th class="width10p">공급사 구분</th>
<th class="width30p">아이디/닉네임</th>
<th class="width7p">이름</th>
 
<th class="width7p">직원여부</th>
<th class="width10p">직원/부서/직급/직책</th>
<th class="width10p">연락처</th>
<td>
<div><?= $val['managerNm']; ?></div>
</td>
 
 
 
<td>
<?= $employeeList[$val['employeeFl']] ?>
</td>
 
변경 후manage_list.php
<input type="text" name="keyword" value="<?= $search['keyword']; ?>" class="form-control width-xl"/>
</td>
</tr>
<tr>
<th>SMS 권한</th>
<td colspan="3" class="form-inline">
<?php foreach ($smsRoleList as $smsRoleValue => $smsRoleName) { ?>
<label class="radio-inline">
<input type="radio" name="smsRole" value="<?= $smsRoleValue; ?>" <?= gd_isset($checked['smsRole'][$smsRoleValue]); ?> /> <?= $smsRoleName; ?>
</label>
<?php } ?>
</td>
</tr>
<tbody class="js-search-detail">
<tr>
<th>장기 미로그인</th>
<th class="width10p">공급사 구분</th>
<th class="width30p">아이디/닉네임</th>
<th class="width7p">이름</th>
<th class="width7p">SMS 권한</th>
<th class="width7p">직원여부</th>
<th class="width10p">직원/부서/직급/직책</th>
<th class="width10p">연락처</th>
<td>
<div><?= $val['managerNm']; ?></div>
</td>
<td>
<div><?= gd_isset($val['smsRoleName']); ?></div>
</td>
<td>
<?= $employeeList[$val['employeeFl']] ?>
</td>
 
Admin/policy/_manage_permission_manager_list.php+11-8
변경 전_manage_permission_manager_list.php
<table class="table table-rows table-fixed mgb0">
<thead>
<tr>
<th class="width10p center"><input type="checkbox" id="chk_all" class="js-checkall" data-target-name="manage_sno"/></th>
<th class="width10p center">번호</th>
<th class="width20p center">공급사 구분</th>
<th class="center">아이디/닉네임</th>
<th class="width20p center">이름</th>
<th class="width18p center">개별수정</th>
 
</tr>
</thead>
</table>
<div class="manager-list-body mgb15">
<table class="table table-rows table-fixed mgb0">
<colgroup>
<col class="width10p"/>
<col class="width10p"/>
<col class="width20p"/>
<col/>
<col class="width20p"/>
<col class="width18p"/>
 
</colgroup>
<tbody>
<?php
$superText = ($val['sno'] == 1) ? '<br/><span class="text-blue">(최고운영자)</span>' : '<br/><span class="text-blue">(대표운영자)</span>';
}
?>
<tr data-sno="<?= $val['sno']; ?>" data-issuper="<?= $val['isSuper']; ?>">
<td class="center">
<input type="checkbox" name="manage_sno[]" value="<?= $val['sno']; ?>" />
</td>
<?= $superText ?>
</td>
<td class="center"><div><?= $val['managerNm']; ?></div></td>
 
<td class="center"><input type="button" value="권한보기" class="btn btn-white btn-view-unit-permission"></td>
</tr>
<?php
} else {
?>
<tr>
<td class="center" colspan="6">검색된 정보가 없습니다.</td>
</tr>
<?php
}
 
변경 후_manage_permission_manager_list.php
<table class="table table-rows table-fixed mgb0">
<thead>
<tr>
<th class="width5p center"><input type="checkbox" id="chk_all" class="js-checkall" data-target-name="manage_sno"/></th>
<th class="width7p center">번호</th>
<th class="width20p center">공급사 구분</th>
<th class="center">아이디/닉네임</th>
<th class="width20p center">이름</th>
<th class="width15p center">SMS 권한</th>
<th class="width11p center">개별수정</th>
</tr>
</thead>
</table>
<div class="manager-list-body mgb15">
<table class="table table-rows table-fixed mgb0">
<colgroup>
<col class="width5p"/>
<col class="width7p"/>
<col class="width20p"/>
<col/>
<col class="width20p"/>
<col class="width15p"/>
<col class="width11p"/>
</colgroup>
<tbody>
<?php
$superText = ($val['sno'] == 1) ? '<br/><span class="text-blue">(최고운영자)</span>' : '<br/><span class="text-blue">(대표운영자)</span>';
}
?>
<tr data-sno="<?= $val['sno']; ?>" data-issuper="<?= $val['isSuper']; ?>" data-sms-role="<?= gd_isset($val['smsRole']); ?>" data-sms-permission-locked="<?= $val['smsPermissionLocked']; ?>">
<td class="center">
<input type="checkbox" name="manage_sno[]" value="<?= $val['sno']; ?>" />
</td>
<?= $superText ?>
</td>
<td class="center"><div><?= $val['managerNm']; ?></div></td>
<td class="center"><div><?= gd_isset($val['smsRoleName']); ?></div></td>
<td class="center"><input type="button" value="권한보기" class="btn btn-white btn-view-unit-permission"></td>
</tr>
<?php
} else {
?>
<tr>
<td class="center" colspan="7">검색된 정보가 없습니다.</td>
</tr>
<?php
}
 
Admin/policy/_manage_permission_menu_list.php+15-1
변경 전_manage_permission_menu_list.php
}
break;
}
 
 
 
 
 
 
 
 
 
 
 
default:
if ($adminMenuType == 'd' && $isSuper == 'y') { // 본사 최고운영자 수정 일 경우
$disabled['functionAuth'][$code] = 'disabled="disabled"';
break;
}
?>
<li class="dropdown-item"><label><input type="checkbox" name="functionAuth[<?= $code; ?>]" data-value="<?= $code; ?>" value="y" <?= gd_isset($disabled['functionAuth'][$code]); ?> <?= gd_isset($checked['functionAuth'][$code]['y']); ?> /> <?= $name; ?></label></li>
<?php
// 속도 높이기 위해 불필요해진 변수 unset
unset($disabled['functionAuth'][$code]);
?>
</table>
</div>
 
<div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;">
<div class="table-action-dropdown">
<div class="table-action mgt0 mgb15">
$('input:checkbox[name="manage_sno[]"]:checked').length > 0 ? $('.manage-btn').prop('disabled', false) : $('.manage-btn').prop('disabled', true);
}
});
 
 
}
});
 
 
변경 후_manage_permission_menu_list.php
}
break;
}
case 'messageSendSms':
if ($adminMenuType == 'd') {
if (!empty($isMessageApprover)) {
$disabled['functionAuth'][$code] = 'data-disabled-lock="lock" disabled="disabled"';
$checked['functionAuth'][$code]['y'] = 'checked="checked"';
} else {
$disabled['functionAuth'][$code] = 'data-disabled-lock="lock"';
}
break;
}
// 그 외에는 일반 기능권한과 동일 동작 (default 로 진행)
default:
if ($adminMenuType == 'd' && $isSuper == 'y') { // 본사 최고운영자 수정 일 경우
$disabled['functionAuth'][$code] = 'disabled="disabled"';
break;
}
?>
<li class="dropdown-item"><label><input type="checkbox" name="functionAuth[<?= $code; ?>]" data-value="<?= $code; ?>" value="y" data-origin-checked="<?= !empty($checked['functionAuth'][$code]['y']) ? 'y' : 'n'; ?>" <?= gd_isset($disabled['functionAuth'][$code]); ?> <?= gd_isset($checked['functionAuth'][$code]['y']); ?> /> <?= $name; ?></label></li>
<?php
// 속도 높이기 위해 불필요해진 변수 unset
unset($disabled['functionAuth'][$code]);
?>
</table>
</div>
<input type="hidden" id="messageSenderMeta" data-editor-approver="<?= !empty($isEditorMessageApprover) ? 'y' : 'n'; ?>" data-editor-representative="<?= !empty($isEditorMessageRepresentative) ? 'y' : 'n'; ?>" data-caller-blocked="<?= !empty($isCallerBlocked) ? 'y' : 'n'; ?>" data-sender-count="<?= (int)(gd_isset($messageSenderCount, 0)); ?>" data-sender-limit="<?= (int)(gd_isset($messageSenderLimit, 0)); ?>"/>
<div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;">
<div class="table-action-dropdown">
<div class="table-action mgt0 mgb15">
$('input:checkbox[name="manage_sno[]"]:checked').length > 0 ? $('.manage-btn').prop('disabled', false) : $('.manage-btn').prop('disabled', true);
}
});
} else if ($('#menuList input:checkbox[data-value="messageSendSms"]').length > 0) {
$('.access-control-reason').removeClass('display-none');
}
});
 
 
Admin/policy/manage_permission.php+62
변경 전manage_permission.php
</tr>
<?php } ?>
<?php } ?>
 
 
 
 
 
 
 
 
 
 
<tr>
<th>검색어</th>
<td class="form-inline">
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
form.target = 'ifrmProcess';
form.submit();
},
 
변경 후manage_permission.php
</tr>
<?php } ?>
<?php } ?>
<tr>
<th>SMS 권한</th>
<td colspan="3" class="form-inline">
<?php foreach ($smsRoleList as $smsRoleValue => $smsRoleName) { ?>
<label class="radio-inline">
<input type="radio" name="smsRole" value="<?= $smsRoleValue; ?>" <?= $smsRoleValue === 'all' ? 'checked="checked"' : ''; ?> /> <?= $smsRoleName; ?>
</label>
<?php } ?>
</td>
</tr>
<tr>
<th>검색어</th>
<td class="form-inline">
}
}
 
// 최고운영자와 부운영자 동시 선택 차단
var $smsCheckedRows = $('input:checkbox[name="manage_sno[]"]:checked').closest('tr');
if ($smsCheckedRows.length > 1 && $smsCheckedRows.filter('[data-issuper="y"]').length > 0) {
dialog_alert('최고운영자와 부운영자를 동시에 수정할 수 없습니다.', '정보');
return false;
}
 
// SMS 발송담당자(messageSendSms) 권한 변경 검증 — 다건 대상 합산
// 전체권한(permissionFl='s') 모드에선 disabled 라 저장 직렬화(:checked:not(:disabled))·서버 모두 미반영 → 검증 스킵
var $smsSetBox = $('#menuList input:checkbox[data-value="messageSendSms"]:not(:disabled)');
if ($smsSetBox.length > 0) {
var smsSetGranted = $smsSetBox.is(':checked');
var smsChanged = false, smsNewSenderCount = 0;
$smsCheckedRows.each(function () {
var smsRole = $(this).data('smsRole');
// 잠금 판정은 서버가 행마다 내려준다 — 미등록 최고운영자는 smsRole 이 비어 있어 역할값만으로는 걸러지지 않는다
if ($(this).data('smsPermissionLocked') === 'y') {
return; // 최고운영자/위임자는 messageSendSms 강제라 대상 아님
}
var nowSender = (smsRole === 'sender');
if (nowSender !== smsSetGranted) {
smsChanged = true;
}
if (smsSetGranted && !nowSender) {
smsNewSenderCount++;
}
});
if (smsChanged) {
var $smsMeta = $('#messageSenderMeta');
if ($smsMeta.data('editorApprover') !== 'y') {
dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고');
return false;
}
if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') {
dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고');
return false;
}
if (smsSetGranted) {
var smsSenderCount = parseInt($smsMeta.data('senderCount'), 10) || 0;
var smsSenderLimit = parseInt($smsMeta.data('senderLimit'), 10) || 0;
if (smsSenderCount + smsNewSenderCount > smsSenderLimit) {
dialog_alert('발송담당자는 최대 ' + smsSenderLimit + '명까지 지정 가능합니다.', '경고');
return false;
}
}
if ($.trim($('#accessControlReason').val()) === '') {
dialog_alert('발송담당자 권한 변경 사유를 작성해주세요.', '경고');
return false;
}
}
}
 
form.target = 'ifrmProcess';
form.submit();
},
 
Admin/policy/manage_register.php+210-9
변경 전manage_register.php
기본정보
</div>
 
 
 
 
 
 
 
 
 
 
<table class="table table-cols">
<colgroup>
<col class="width-md"/>
<tr>
<th class="require">이름</th>
<td class="form-inline">
<input type="text" name="managerNm" value="<?= gd_isset($data['managerNm']); ?>" class="form-control js-maxlength" maxlength="20"/>
 
 
 
</td>
<th>닉네임</th>
<td class="form-inline">
<tr>
<th>휴대폰번호</th>
<td class="form-inline" colspan="3">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
<label title="휴대폰 번호를 입력해 주세요!">
<input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/>
</label>
<div class="notice-info">
SMS발신번호 및 SMS인증번호 관리는 <a href="/member/sms_auto.php" target="_blank" class="btn-link">[회원 > SMS관리 > SMS 설정]</a> 에서 확인할 수 있습니다.
</div>
 
</td>
</tr>
 
<th>이메일</th>
<td class="form-inline" colspan="3">
<label title="이메일을 입력해 주세요!">
<input type="text" name="email[]" value="<?= gd_isset($data['email'][0]); ?>" class="form-control width-md"/>
@
<input type="text" id="email" name="email[]" value="<?= gd_isset($data['email'][1]); ?>" class="form-control width-md"/>
<?= gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1]); ?>
</label>
 
 
 
 
<button type="button" class="btn btn-gray btn-sm js-request-authemail">인증번호받기</button>
<span id="emailAuthSendBox" class="<?php if (gd_isset($data['isEmailAuth']) === 'y') { ?>display-none<?php } ?>">
인증번호 :
<a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a>
<?php } ?>
</div>
 
</td>
</tr>
 
<col/>
</colgroup>
<tbody>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
<tr>
<th>운영자 IP 접속제한</th>
<td>
<script type="text/javascript">
<!--
var isUseProvider = '<?=gd_use_provider()?>';
 
 
 
 
 
 
 
 
 
 
var manage_register = {
regex: {
auth_no: /\d{8}/
$(document).ready(function () {
$('.sms-point').text(manage_register.sms_point);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$.validator.addMethod('smsAuto', function(value){
if (value == 'n') {
return true;
onkeyup: false,
ignore: ':hidden',
submitHandler: function (form) {
 
 
 
 
 
if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우
if ($('input[name="permissionFl"]').length == 0) {
alert("메뉴 권한은 최소 1개 이상 '읽기' 또는 '읽기+쓰기'로 설정하셔야 합니다.");
});
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
form.target = 'ifrmProcess';
form.submit();
},
});
 
$('.js-request-authno').bind('click', function () {
if (manage_register.sms_point < 1) {
alert('SMS 포인트가 부족합니다. 인증번호 발송을 위해 SMS 포인트를 충전해주세요');
return false;
}
 
var cellPhoneFlag = true;
var $sms_auth_send_box = $('#smsAuthSendBox');
});
});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$('.js-request-authemail').bind('click', function () {
var emailFlag = true;
var $email_auth_send_box = $('#emailAuthSendBox');
$('input[name*=\'cellPhone\']').number_only();
 
if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우
 
 
 
 
 
 
 
$('input[name=scmFl][value=n]').bind('click', function () {
if ($('input[name="scmNo"]').length > 0) {
$('.btn-icon-delete').trigger('click');
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
function layer_register(typeStr, mode) {
var addParam = {
"mode": mode,
 
if (typeStr == 'scm') {
addParam['callFunc'] = 'setScmSelect';
$('input:radio[name=scmFl]:input[value=y]').prop("checked", true);
 
 
$('select[name="add_must_info_sel"]').html("<option>= 상품 필수 정보 선택 =</option>");
$('select[name="add_goods_info_sel"]').html("<option>= 추가 상품 그룹 정보 선택 =</option>");
}
}
 
function setScmSelect(data) {
 
 
 
//공급사 값 세팅
displayTemplate(data);
 
if ($('input[name="permissionSetupMode"]').length != 1) { // 운영자 권한 설정 방식이 아닌 경우(기존)
developmentPermissionToggle(data);
changePermissionLayout(data);
 
변경 후manage_register.php
기본정보
</div>
 
<?php if (!empty($superManagerCommerceGuide)): ?>
<div class="mgb10">
<div class="notice-info"><strong>최고운영자 정보(이름/휴대폰번호/이메일)는 통합회원 마이페이지에서 수정할 수 있습니다.</strong></div>
<div class="notice-info">보안 강화를 위해 계정당 1인 1계정 사용 환경으로 개선되어, 최고운영자 정보는 통합회원 정보와 항상 동일하게 유지됩니다.</div>
<div class="notice-info">통합회원 마이페이지에서 이름/휴대폰번호/이메일을 수정하시면, 최고운영자 정보에도 동일하게 반영됩니다. <a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합회원 정보 수정하러 가기 ></a></div>
<div class="notice-info">통합회원이 아닌 담당자가 최고운영자 계정을 이용하고 계셨다면, 부운영자 계정을 별도로 등록해 이용해 주세요.</div>
</div>
<?php endif; ?>
 
<table class="table table-cols">
<colgroup>
<col class="width-md"/>
<tr>
<th class="require">이름</th>
<td class="form-inline">
<input type="text" name="managerNm" value="<?= gd_isset($data['managerNm']); ?>" class="form-control js-maxlength" maxlength="20"<?= !empty($senderNameLocked) ? ' disabled="disabled"' : ''; ?>/>
<?php if (!empty($superManagerCommerceLinked)): ?>
<div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div>
<?php endif; ?>
</td>
<th>닉네임</th>
<td class="form-inline">
<tr>
<th>휴대폰번호</th>
<td class="form-inline" colspan="3">
<?php if (!empty($superManagerCommerceLinked)): ?>
<label title="휴대폰 번호를 입력해 주세요!">
<input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md" disabled="disabled"/>
</label>
<?php if ($selfCertification['enabled']): ?>
<button type="button" class="btn btn-gray btn-sm js-request-self-certification">본인인증</button>
<span class="my-sms-auth js-self-certification-state<?= $selfCertification['certified'] ? '' : ' text-red'; ?>">
<?= $selfCertification['certified'] ? '(인증완료)' : '(미인증)'; ?>
</span>
<?php endif; ?>
<div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div>
<?php if ($selfCertification['enabled']): ?>
<div class="notice-info">
인증이 완료된 휴대폰번호는 관리자페이지 보안로그인 및 화면보안접속 시 인증정보로 사용할 수 있습니다.
<?php if (!$isProvider): ?>
<a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php elseif ($selfCertification['enabled']): ?>
<label title="휴대폰 번호를 입력해 주세요!">
<input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"<?= !empty($senderCellPhoneLocked) ? ' disabled="disabled"' : ''; ?>/>
</label>
<button type="button" class="btn btn-gray btn-sm js-request-self-certification">본인인증</button>
<span class="my-sms-auth js-self-certification-state<?= $selfCertification['certified'] ? '' : ' text-red'; ?>">
<?= $selfCertification['certified'] ? '(인증완료)' : '(미인증)'; ?>
</span>
<div class="notice-info">
인증이 완료된 휴대폰번호는 관리자페이지 보안로그인 및 화면보안접속 시 인증정보로 사용할 수 있습니다.
<?php if (!$isProvider): ?>
<a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a>
<?php endif; ?>
</div>
<?php else: ?>
<label title="휴대폰 번호를 입력해 주세요!">
<input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/>
</label>
<div class="notice-info">
SMS발신번호 및 SMS인증번호 관리는 <a href="/member/sms_auto.php" target="_blank" class="btn-link">[회원 > SMS관리 > SMS 설정]</a> 에서 확인할 수 있습니다.
</div>
<?php endif; ?>
</td>
</tr>
 
<th>이메일</th>
<td class="form-inline" colspan="3">
<label title="이메일을 입력해 주세요!">
<input type="text" name="email[]" value="<?= gd_isset($data['email'][0]); ?>" class="form-control width-md"<?= !empty($superManagerCommerceLinked) ? ' disabled="disabled"' : ''; ?>/>
@
<input type="text" id="email" name="email[]" value="<?= gd_isset($data['email'][1]); ?>" class="form-control width-md"<?= !empty($superManagerCommerceLinked) ? ' disabled="disabled"' : ''; ?>/>
<?= gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1], null, !empty($superManagerCommerceLinked) ? 'disabled="disabled"' : null); ?>
</label>
 
<?php if (!empty($superManagerCommerceLinked)): ?>
<div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div>
<?php else: ?>
<button type="button" class="btn btn-gray btn-sm js-request-authemail">인증번호받기</button>
<span id="emailAuthSendBox" class="<?php if (gd_isset($data['isEmailAuth']) === 'y') { ?>display-none<?php } ?>">
인증번호 :
<a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a>
<?php } ?>
</div>
<?php endif; ?>
</td>
</tr>
 
<col/>
</colgroup>
<tbody>
<?php if (!empty($messageDelegate['visible'])): // SMS 발신 위임자 권한 (권한 설정 테이블 row, 지정/회수는 최고운영자만) ?>
<tr class="js-message-delegate-area">
<th>SMS 발신 위임자 권한 설정</th>
<td class="form-inline">
<input type="hidden" name="messageDelegateAction" id="messageDelegateAction" value="">
<?php if ($messageDelegate['assigned'] === true): ?>
<span class="js-message-delegate-done">권한 설정 완료</span>
<?php if ($messageDelegate['editable'] === true): ?>
<button type="button" class="btn btn-sm btn-gray js-message-delegate-release">위임자 권한 해제</button>
<?php endif; ?>
<?php elseif ($messageDelegate['editable'] === true): ?>
<button type="button" class="btn btn-sm btn-gray js-message-delegate-assign">위임자 권한 설정</button>
<?php else: ?>
<span class="notice-info">미지정</span>
<?php endif; ?>
<br>
<span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[설정 > 관리 정책 > 운영자 관리]</a> 페이지의 회원 추가설정 중 'SMS 발송' 권한을 설정해 주세요.</span>
</td>
</tr>
<?php if ($messageDelegate['assigned'] === false && $messageDelegate['editable'] === true): ?>
<tr class="js-message-delegate-set-reason-row" style="display:none;">
<th>SMS 발신 위임자 권한 설정 사유</th>
<td>
<input type="text" id="messageDelegateSetReason" class="form-control width-2xl" maxlength="50">
<span class="notice-info">SMS 발송 위임 권한을 설정하기 위한 명확한 사유를 입력해 주세요.</span>
</td>
</tr>
<?php endif; ?>
<?php if ($messageDelegate['assigned'] === true && $messageDelegate['editable'] === true): ?>
<tr class="js-message-delegate-release-reason-row" style="display:none;">
<th>SMS 발신 위임자 권한 해제 사유</th>
<td>
<input type="text" id="messageDelegateReleaseReason" class="form-control width-2xl" maxlength="50">
<span class="notice-info">SMS 발송 위임 권한을 해제하기 위한 명확한 사유를 입력해 주세요.</span>
</td>
</tr>
<?php endif; ?>
<?php endif; ?>
<tr>
<th>운영자 IP 접속제한</th>
<td>
<script type="text/javascript">
<!--
var isUseProvider = '<?=gd_use_provider()?>';
// 저장 게이트는 서버(ManagerEditPolicy::canEditAccount)와 같은 판정이라 휴대폰 인증 방식 분기와 분리한다
// 통합계정 연동된 최고운영자 화면은 3개 필드만 잠그고 나머지 항목 저장은 열어 둔다 (잠긴 필드는 저장 시 서버가 기존 값을 유지)
var isManagerInfoModifiable = <?= !empty($managerInfoModifiable) ? 'true' : 'false' ?>;
var isSelfCertificationTarget = <?= !empty($selfCertification['isSelf']) ? 'true' : 'false' ?>;
const delegateActionAssign = 'assign';
const delegateActionRelease = 'release';
const isDelegateCallerBlocked = <?= !empty($messageDelegate['callerBlocked']) ? 'true' : 'false' ?>;
const isDelegateBlockedByExisting = <?= !empty($messageDelegate['blockedByExisting']) ? 'true' : 'false' ?>;
const delegateCallerBlockedMessage = 'SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.';
const delegateBlockedByExistingMessage = '상점에는 최대 1명의 위임자만 설정할 수 있습니다.<br>이미 권한을 갖고 있는 운영자가 있어 권한을 설정할 수 없습니다.';
var manage_register = {
regex: {
auth_no: /\d{8}/
$(document).ready(function () {
$('.sms-point').text(manage_register.sms_point);
 
// SMS 발신 위임자 권한 — 버튼 클릭 시 사유 입력행 노출 + 처리 의도(action) 세팅
$('.js-message-delegate-assign').click(function () {
if (isDelegateCallerBlocked) {
dialog_alert(delegateCallerBlockedMessage, '경고');
return;
}
if (isDelegateBlockedByExisting) {
dialog_alert(delegateBlockedByExistingMessage, '안내');
return;
}
$('#messageDelegateAction').val(delegateActionAssign);
$('.js-message-delegate-set-reason-row').show();
$('#messageDelegateSetReason').focus();
});
$('.js-message-delegate-release').click(function () {
$('#messageDelegateAction').val(delegateActionRelease);
$('.js-message-delegate-release-reason-row').show();
$('#messageDelegateReleaseReason').focus();
});
 
$.validator.addMethod('smsAuto', function(value){
if (value == 'n') {
return true;
onkeyup: false,
ignore: ':hidden',
submitHandler: function (form) {
if (!isManagerInfoModifiable) {
// 1차(UI) 차단 — 서버 예외로 흐르면 1회성 CSRF 토큰만 소비되어 재시도 시 "잘못된 경로" 가 된다
alert('해당 계정 정보는 책임자 및 본인만 수정할 수 있습니다.');
return false;
}
if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우
if ($('input[name="permissionFl"]').length == 0) {
alert("메뉴 권한은 최소 1개 이상 '읽기' 또는 '읽기+쓰기'로 설정하셔야 합니다.");
});
}
 
// SMS 발신 위임자 권한 — 설정/해제 사유 필수 검증 + 전송값 세팅
var $mdAction = $('#messageDelegateAction');
if ($mdAction.length && $mdAction.val() !== '') {
if (isDelegateCallerBlocked) {
dialog_alert(delegateCallerBlockedMessage, '경고');
return false;
}
var mdReason = $mdAction.val() === delegateActionRelease
? $.trim($('#messageDelegateReleaseReason').val())
: $.trim($('#messageDelegateSetReason').val());
if (mdReason === '') {
dialog_alert($mdAction.val() === delegateActionRelease
? '발신관리위임자 권한 해제 사유를 작성해주세요.'
: '발신관리위임자 권한 설정 사유를 작성해주세요.', '경고');
return false;
}
// 위임자 사유를 권한 변경 사유(accessControlReason)로 제출 → 운영자 관리/수정 개인정보접속기록에 기록
var $mdReasonField = $('#frmManager input[name="accessControlReason"]');
if ($mdReasonField.length === 0) {
$mdReasonField = $('<input>', {type: 'hidden', name: 'accessControlReason'}).appendTo('#frmManager');
}
$mdReasonField.val(mdReason);
}
 
form.target = 'ifrmProcess';
form.submit();
},
});
 
$('.js-request-authno').bind('click', function () {
 
 
 
 
 
var cellPhoneFlag = true;
var $sms_auth_send_box = $('#smsAuthSendBox');
});
});
 
<?php if ($selfCertification['enabled']): ?>
$('.js-request-self-certification').on('click', function () {
// 본인인증은 등록된 계정 정보의 본인확인 수단이라 본인 계정에서만 진행한다
if (!isSelfCertificationTarget) {
alert('본인 계정의 정보만 수정할 수 있습니다.');
return;
}
if ($.trim($('input[name="cellPhone"]').val()) === '') {
alert('책임자에게 휴대폰 번호 등록을 먼저 요청해 주세요.');
return;
}
managerCertification.certify({
onComplete: function (success, reason) {
if (success) {
$('.js-self-certification-state').removeClass('text-red').text('(인증완료)');
alert('본인인증이 완료되었습니다.');
return;
}
if (reason === 'mismatch') {
dialog_alert('입력하신 정보가 등록된 계정 정보와 일치하지 않습니다.<br>다시 확인해 주세요.', '경고');
return;
}
if (reason === 'duplicateDi') {
dialog_alert('이미 등록된 SMS 관리자입니다.<br>SMS 관리자 권한은 한 사람이 중복으로 보유할 수 없습니다.', '경고');
return;
}
alert('본인인증에 실패했습니다. 인증 정보를 확인 후 다시 시도해 주세요.');
}
});
});
<?php endif; ?>
 
$('.js-request-authemail').bind('click', function () {
var emailFlag = true;
var $email_auth_send_box = $('#emailAuthSendBox');
$('input[name*=\'cellPhone\']').number_only();
 
if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우
$('input[name="scmFl"]').on('click', function () {
if ($(this).val() === 'n') {
scmFlConfirmed = 'n'; // 본사 직접 선택은 즉시 확정
}
toggleMessageDelegateAreaByScm();
});
toggleMessageDelegateAreaByScm(); // 초기 노출 상태
$('input[name=scmFl][value=n]').bind('click', function () {
if ($('input[name="scmNo"]').length > 0) {
$('.btn-icon-delete').trigger('click');
}
}
 
var scmFlConfirmed = $('input[name="scmFl"]:checked').val() || 'n'; // 확정된 공급사 구분 — 모달에서 공급사를 실제 선택(확인)해야만 'y'
 
// 공급사 구분에 따라 발신관리위임자 권한 설정 영역 노출/숨김 — 공급사 운영자에겐 미노출(서버 assertAssignableTarget 와 이중 방어)
function toggleMessageDelegateAreaByScm() {
var isHeadOffice = $('input[name="scmFl"]:checked').val() !== 'y';
$('.js-message-delegate-area').toggle(isHeadOffice);
if (isHeadOffice === false) {
$('#messageDelegateAction').val('');
$('.js-message-delegate-set-reason-row, .js-message-delegate-release-reason-row').hide();
}
}
 
function layer_register(typeStr, mode) {
var addParam = {
"mode": mode,
 
if (typeStr == 'scm') {
addParam['callFunc'] = 'setScmSelect';
// 라디오는 모달에서 공급사를 실제 선택(확인)해야 공급사로 변경 — 열기 시점엔 확정값 유지(미선택 닫기 시 자연히 본사 유지)
$('input[name="scmFl"][value="' + scmFlConfirmed + '"]').prop('checked', true);
toggleMessageDelegateAreaByScm();
$('select[name="add_must_info_sel"]').html("<option>= 상품 필수 정보 선택 =</option>");
$('select[name="add_goods_info_sel"]').html("<option>= 추가 상품 그룹 정보 선택 =</option>");
}
}
 
function setScmSelect(data) {
// 모달에서 공급사 확정 선택 시에만 라디오를 공급사로 변경
$('input[name="scmFl"][value="y"]').prop('checked', true);
scmFlConfirmed = 'y';
//공급사 값 세팅
displayTemplate(data);
toggleMessageDelegateAreaByScm();
if ($('input[name="permissionSetupMode"]').length != 1) { // 운영자 권한 설정 방식이 아닌 경우(기존)
developmentPermissionToggle(data);
changePermissionLayout(data);
 
Admin/policy/manage_security.php-7
변경 전manage_security.php
<p class="notice-info">
로그인한 운영자 정보에 인증정보(SMS, 이메일)가 없는 경우, 보안인증화면이 노출되지 않습니다.<br/>관리자 보안인증 설정을 사용 시, 운영자 정보에 인증정보를 등록해주세요. <a href="../policy/manage_list.php" target="_blank" class="btn-link">운영자 정보 수정하기 ></a>
</p>
<p class="notice-info">
인증번호 SMS는 잔여포인트가 있어야 발송됩니다. (잔여포인트 : <?php echo number_format($smsPoint, 1) ?>) <a href="../member/sms_charge.php" target="_blank" class="btn-link">SMS포인트 충전하기 ></a>
</p>
<p class="notice-info">
SMS잔여포인트가 없는 경우 자동으로 이메일이 인증수단으로 노출되며, 포인트 충전 이후 설정한 인증수단으로 노출됩니다.
</p>
<p class="notice-info">
이메일인증은 주문처리 및 프로모션 등으로 인해 이메일이 대량으로 발송될 경우 인증번호 전송이 지연될 수 있으므로 SMS인증을 사용하시길 권장합니다.
</p>
<td>
<input type="hidden" name="excel[smsAuth]" id="excelSmsAuthManager" value="manager" />
운영자 정보에 등록된 SMS로 관리자 보안 인증번호가 발송됩니다.
<p class="notice-info">운영자 인증번호 SMS는 잔여포인트가 있어야 발송됩니다. (잔여포인트 : <?= number_format($smsPoint) ?>) <a href="../member/sms_charge.php" target="_blank" class="btn-link">SMS포인트 충전하기 ></a></p>
<p class="notice-info">로그인한 운영자 정보에 SMS 인증정보(휴대폰번호)가 없는 경우, 엑셀 다운로드가 불가합니다.</p>
</td>
</tr>
 
변경 후manage_security.php
<p class="notice-info">
로그인한 운영자 정보에 인증정보(SMS, 이메일)가 없는 경우, 보안인증화면이 노출되지 않습니다.<br/>관리자 보안인증 설정을 사용 시, 운영자 정보에 인증정보를 등록해주세요. <a href="../policy/manage_list.php" target="_blank" class="btn-link">운영자 정보 수정하기 ></a>
</p>
 
 
 
 
 
 
<p class="notice-info">
이메일인증은 주문처리 및 프로모션 등으로 인해 이메일이 대량으로 발송될 경우 인증번호 전송이 지연될 수 있으므로 SMS인증을 사용하시길 권장합니다.
</p>
<td>
<input type="hidden" name="excel[smsAuth]" id="excelSmsAuthManager" value="manager" />
운영자 정보에 등록된 SMS로 관리자 보안 인증번호가 발송됩니다.
 
<p class="notice-info">로그인한 운영자 정보에 SMS 인증정보(휴대폰번호)가 없는 경우, 엑셀 다운로드가 불가합니다.</p>
</td>
</tr>
 
Admin/promotion/cart_remind_list.php+26-2
변경 전cart_remind_list.php
 
 
<div class="page-header js-affix">
<h3><?php echo end($naviMenu->location); ?>
</h3>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span>
<?php if (gd_is_provider() === false) {?>
<a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a>
<?php }?>
</div>
<div class="notice-info">
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_sms_call_number_select.php', params, function (data) {
 
 
 
 
 
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
});
});
 
 
변경 후cart_remind_list.php
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<div class="page-header js-affix">
<h3><?php echo end($naviMenu->location); ?>
</h3>
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span>
<?php if (gd_is_provider() === false) {?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button>
<?php }?>
</div>
<div class="notice-info">
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_manage_call_number.php', params, function (data) {
// 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다
if (typeof data === 'object' && data !== null) {
dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
return;
}
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
}).fail(function (xhr) {
var response = xhr.responseJSON;
if (response && response.data && response.data.canRegisterRepresentative) {
SmsCallerHubLayer.openRepresentativeRegisterGuide();
return;
}
if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) {
SmsCallerHubLayer.openMigration({
// 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다
onCompleted: function () {
$('.js-sms-call-number').click();
}
});
return;
}
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
});
});
 
 
Admin/promotion/comeback_coupon_list.php+26-2
변경 전comeback_coupon_list.php
 
 
<div class="page-header js-affix">
<h3><?php echo end($naviMenu->location); ?></h3>
<div class="btn-group">
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span>
<?php if (gd_is_provider() === false) {?>
<a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a>
<?php }?>
</div>
<div class="notice-info">
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_sms_call_number_select.php', params, function (data) {
 
 
 
 
 
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
});
});
 
 
변경 후comeback_coupon_list.php
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<div class="page-header js-affix">
<h3><?php echo end($naviMenu->location); ?></h3>
<div class="btn-group">
<input type="hidden" name="smsCallNum" value=""/>
<span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span>
<?php if (gd_is_provider() === false) {?>
<button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button>
<?php }?>
</div>
<div class="notice-info">
var params = {
returnInput: 'smsCallNum'
};
$.get('../member/layer_manage_call_number.php', params, function (data) {
// 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다
if (typeof data === 'object' && data !== null) {
dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
return;
}
BootstrapDialog.show({
title: 'SMS 발신번호 목록',
message: $(data),
closable: true
});
}).fail(function (xhr) {
var response = xhr.responseJSON;
if (response && response.data && response.data.canRegisterRepresentative) {
SmsCallerHubLayer.openRepresentativeRegisterGuide();
return;
}
if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) {
SmsCallerHubLayer.openMigration({
// 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다
onCompleted: function () {
$('.js-sms-call-number').click();
}
});
return;
}
// 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다
dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.');
});
});
 
 
Admin/share/layer_message_representative_register_guide.php+123
변경 전layer_message_representative_register_guide.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
변경 후layer_message_representative_register_guide.php
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script>
<script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/manager-certification.js') ?>"></script>
<link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-message-representative-register-guide.css') ?>"/>
<?php $guideAlt = '발신관리책임자 등록 안내 - 우리 쇼핑몰 번호, 도용되지 않게 지켜드려요. 발신관리책임자와 담당자 정보로 발신번호 사용을 관리해 다른 사람이 SMS를 보내지 못하게 막아줘요. 최고운영자만 등록 가능(통합회원과 최고운영자 정보가 동일해야 등록이 가능합니다), 등록은 한 번 1분이면 완료, 26년 10월 25일까지 등록 필수(미등록 시 SMS 관련 기능 이용이 제한됩니다). 공지사항에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다. 급증하는 불법스팸·스미싱 피해 예방을 위한 전기통신사업법 제22조의11 및 전송자격인증 기준 등에 관한 고시(방통위 고시 제2026-1호) 개정에 따른 조치입니다.'; ?>
<div id="popupNoticeRepresentativeRegisterGuide">
<div class="guide_overlay"></div>
<div class="guide_content">
<div class="guide_head">
<p class="tit">발신관리책임자 등록 안내</p>
</div>
<div class="guide_body">
<?php if (!empty($noticeUri)) { ?>
<a href="<?= $noticeUri ?>" target="_blank" class="guide_notice">
<img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/>
</a>
<?php } else { ?>
<img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/>
<?php } ?>
<div class="btn_guide_area">
<button type="button" class="js-representative-register">등록하기</button>
</div>
</div>
<div class="bot_gray_close">
<input type="checkbox" title="7일동안 보지 않기" onclick="adminPanelCookie('<?= $displayCookie ?>', <?= $displayCookieExpireDay ?>, this);$('#popupNoticeRepresentativeRegisterGuide').hide();"/>7일동안 보지 않기
<input type="checkbox" title="닫기" onclick="$('#popupNoticeRepresentativeRegisterGuide').hide();"/>닫기
</div>
</div>
</div>
<script>
(function () {
var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>;
var isSelfCertified = <?= json_encode($isSelfCertified) ?>;
 
$('.js-representative-register').on('click', function () {
$('#popupNoticeRepresentativeRegisterGuide').hide();
 
// 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다)
if (!isCommerceConnected) {
const message = '<b>발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.</b><br>통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.';
const commerceLoginUri = <?= json_encode($commerceLoginUri) ?>;
 
// 안내를 닫는 모든 경로에서 연동 페이지로 이어져야 한다
managerCertification.closableAlert(message, '안내', function () {
location.href = commerceLoginUri;
});
return;
}
 
// 본인인증 이력이 없으면 인증부터 진행한다
if (!isSelfCertified) {
managerCertification.certifyWithGuide({onSuccess: checkBlockAndRegister});
return;
}
 
// 이미 본인인증(솔루션 diValue 존재)한 운영자는 곧바로 등록 흐름을 탄다
checkBlockAndRegister();
});
 
// 인증을 마쳐도 차단된 책임자면 등록을 진행시키지 않는다
function checkBlockAndRegister() {
$.post('/share/message_representative_register_ps.php', {mode: 'checkBlock'}, function (response) {
if (!response || response.success !== true) {
showRequestFailed(response);
return;
}
 
if (response.data.blocked) {
dialog_alert(response.data.message, '안내');
return;
}
 
checkMigrationCaller();
}, 'json').fail(function () {
showRequestFailed();
});
}
 
// 통합회원 전환 전에 등록해 둔 기존 발신관리책임자가 있으면 이관 여부를 묻는다
function checkMigrationCaller() {
$.post('/share/message_representative_register_ps.php', {mode: 'checkMigration'}, function (response) {
if (!response || response.success !== true) {
showRequestFailed(response);
return;
}
 
if (response.data.exists) {
dialog_confirm(
'발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?<br>가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.',
function (confirmed) {
if (!confirmed) {
showRegisterCompleted();
return;
}
 
SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted});
},
'이전 발신번호 가져오기'
);
 
return;
}
 
showRegisterCompleted();
}, 'json').fail(function () {
showRequestFailed();
});
}
 
// 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다
function showRequestFailed(response) {
dialog_alert((response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', '안내');
}
 
// 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다
function showRegisterCompleted() {
dialog_alert(
"발신관리책임자 등록이 완료되었습니다.<br>부운영자와 함께 SMS 업무를 하려면 '발신관리위임자'로 설정하거나 'SMS 발송'권한을 부여해 주세요.",
'안내'
);
}
})();
</script>
 
Admin/share/layer_sms_personal_info_access_log.php+36
변경 전layer_sms_personal_info_access_log.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
변경 후layer_sms_personal_info_access_log.php
<?php
/*
* Copyright (C) 2026 NHN COMMERCE. - All Rights Reserved
*
* Unauthorized copying or redistribution of this file in source and binary forms via any medium
* is strictly prohibited.
*/
?>
<style>
.sms-access-log-layer{overflow-y:auto;max-width:600px;max-height:400px}
.sms-access-log-detail{margin:0;padding:0;list-style:none}
.sms-access-log-detail > li{padding:10px 0}
.sms-access-log-detail > li + li{border-top:1px solid #ddd}
.sms-access-log-detail__items{margin:5px 0 0;padding-left:14px;list-style:none}
.sms-access-log-detail__items > li{padding:2px 0}
</style>
<div class="sms-access-log-layer">
<?php if (!empty($errorMessage)): ?>
<p><?= gd_htmlspecialchars($errorMessage) ?></p>
<?php elseif (!empty($detailRows)): ?>
<ul class="sms-access-log-detail">
<?php foreach ($detailRows as $row): ?>
<li>
* <?= gd_htmlspecialchars($row['label']) ?><?php if (!empty($row['value'])): ?> : <?= gd_htmlspecialchars($row['value']) ?><?php endif; ?>
<?php if (!empty($row['items'])): ?>
<ul class="sms-access-log-detail__items">
<?php foreach ($row['items'] as $item): ?>
<li>&bull; <?= gd_htmlspecialchars($item['label']) ?> : <?= gd_htmlspecialchars($item['value']) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
 
Admin/share/manager_certification_result.php+11
변경 전manager_certification_result.php
 
 
 
 
 
 
 
 
 
 
 
 
변경 후manager_certification_result.php
<script type="text/javascript">
(function () {
var isCertificated = <?= $isCertificated ? 'true' : 'false' ?>;
var certKey = <?= json_encode($certKey) ?>;
 
if (window.opener && window.opener.managerCertification) {
window.opener.managerCertification.result(isCertificated, certKey);
}
window.close();
})();
</script>
 
Component/Admin/AdminLogDAO.php+21-1
변경 전AdminLogDAO.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\StringUtils;
 
 
/**
* Class AdminLogDAO
case 'register':
case 'modify':
$result['searchConditionMsg'] = '권한 변경 사유 : '. $data['data']['POST']['accessControlReason'] . '<br>';
 
 
 
 
 
 
 
 
$result['searchConditionMsg'] .= '처리 대상 : '. $data['data']['POST']['managerId'];
break;
case 'delete':
}
$data['searchData'] = implode(', ', $managerId);
}
 
 
 
 
 
 
 
 
 
 
// 암호화 적용 필드
$encryptTarget = [
'email',
$arrBind = [];
 
if ($request['managerId']) {
$where[] = "a.managerId LIKE concat('%',?,'%') ";
 
$this->_db->bind_param_push($arrBind, 's', $request['managerId']);
}
 
 
변경 후AdminLogDAO.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\StringUtils;
use Origin\Service\Member\ManagerEditPolicy;
 
/**
* Class AdminLogDAO
case 'register':
case 'modify':
$result['searchConditionMsg'] = '권한 변경 사유 : '. $data['data']['POST']['accessControlReason'] . '<br>';
// manage_ps 스냅샷의 휴대폰번호는 적재 시 암호화돼 있어 그대로 출력하면 암호문이 노출된다
// 적재값은 검증 전 요청 원문이고 상세 화면이 이 문자열을 DOM 으로 파싱하므로 출력 시 이스케이프한다
$changedCellPhone = '';
if (!empty($data['data']['isCellPhoneChanged'])) {
$changedCellPhone = gd_htmlspecialchars((string)\Encryptor::decryptJson($data['data']['POST']['cellPhone']));
}
// 항목을 통째로 빼면 기록을 읽는 쪽이 번호 미변경과 기록 누락을 구분할 수 없다
$result['searchConditionMsg'] .= '휴대폰번호 : ' . $changedCellPhone . '<br>';
$result['searchConditionMsg'] .= '처리 대상 : '. $data['data']['POST']['managerId'];
break;
case 'delete':
}
$data['searchData'] = implode(', ', $managerId);
}
$cellPhoneChangeMode = $data['POST']['mode'] ?? null;
$inputCellPhone = (string)($data['POST']['cellPhone'] ?? '');
// 뒤따르는 암호화 루프가 입력값을 암호문으로 덮으므로 판정은 원문이 남아 있는 이 시점에 끝내야 한다
if (in_array($cellPhoneChangeMode, ['register', 'modify'], true) && !empty($inputCellPhone)) {
/** @var ManagerEditPolicy $managerEditPolicy */
$managerEditPolicy = \App::getInstance(ManagerEditPolicy::class);
if ($managerEditPolicy->isCellPhoneChangeApplied((int)($data['POST']['sno'] ?? 0), $inputCellPhone)) {
$data['isCellPhoneChanged'] = true;
}
}
// 암호화 적용 필드
$encryptTarget = [
'email',
$arrBind = [];
 
if ($request['managerId']) {
// 개인정보 과다 노출 방지를 위해 부분포함이 아닌 전체일치로만 조회한다
$where[] = "a.managerId = ? ";
$this->_db->bind_param_push($arrBind, 's', $request['managerId']);
}
 
 
Component/Admin/AdminMenu.php+30-3
변경 전AdminMenu.php
'memberHack' => '회원탈퇴',
'memberExcelDown' => '엑셀다운로드',
'memberMaskingUseFl' => '개인정보조회 제한',
 
],
'godo00176' => [
'boardDelete' => '게시글 삭제',
$this->db->bind_param_push($this->arrBind, 's', $subMenu);
$this->db->bind_param_push($this->arrBind, 's', $lastMenu);
 
$this->db->strField = 't.adminMenuNo';
$join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as s ON s.adminMenuDepth = 2 AND f.adminMenuNo = s.adminMenuParentNo';
$join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as t ON t.adminMenuDepth = 3 AND s.adminMenuNo = t.adminMenuParentNo';
$this->db->strJoin = implode(' ', $join);
$this->db->strWhere = implode(' AND ', $this->arrWhere);
$query = $this->db->query_complete();
$strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_ADMIN_MENU . ' as f ' . implode(' ', $query);
$menuData = $this->db->query_fetch($strSQL, $this->arrBind, false);
unset($this->arrBind);
unset($this->arrWhere);
 
return $menuData['adminMenuNo'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
/**
 
변경 후AdminMenu.php
'memberHack' => '회원탈퇴',
'memberExcelDown' => '엑셀다운로드',
'memberMaskingUseFl' => '개인정보조회 제한',
'messageSendSms' => 'SMS 발송',
],
'godo00176' => [
'boardDelete' => '게시글 삭제',
$this->db->bind_param_push($this->arrBind, 's', $subMenu);
$this->db->bind_param_push($this->arrBind, 's', $lastMenu);
 
$this->db->strField = 't.adminMenuNo, t.adminMenuType';
$join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as s ON s.adminMenuDepth = 2 AND f.adminMenuNo = s.adminMenuParentNo';
$join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as t ON t.adminMenuDepth = 3 AND s.adminMenuNo = t.adminMenuParentNo';
$this->db->strJoin = implode(' ', $join);
$this->db->strWhere = implode(' AND ', $this->arrWhere);
$query = $this->db->query_complete();
$strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_ADMIN_MENU . ' as f ' . implode(' ', $query);
$menuList = $this->db->query_fetch($strSQL, $this->arrBind);
unset($this->arrBind);
unset($this->arrWhere);
 
$adminMenuType = gd_is_provider() && self::isProviderDirectory() ? 's' : 'd';
 
return $this->pickAdminMenuNoByAdminMenuType($menuList, $adminMenuType);
}
 
/**
* 본사(d)·공급사(s) 에 같은 3단 코드가 존재하는 메뉴는 조회 결과가 여러 행이 된다.
* 한쪽 유형만 등록된 메뉴가 0행으로 사라지지 않게 유형 조건은 SQL 에 넣지 않고 여기서 고른다.
*
* @param array $menuList [['adminMenuNo' => string, 'adminMenuType' => string], ...]
* @param string $adminMenuType 본사(d)/공급사(s)
* @return string
*/
protected function pickAdminMenuNoByAdminMenuType(array $menuList, string $adminMenuType): string
{
// 메뉴 행이 없으면 빈 menuCode 로 진행해 TypeError 500 대신 admin API 401 안내 경로를 타게 한다
if (empty($menuList)) {
return '';
}
 
foreach ($menuList as $menuData) {
if ($menuData['adminMenuType'] === $adminMenuType) {
return $menuData['adminMenuNo'];
}
}
 
return $menuList[0]['adminMenuNo'];
}
 
/**
 
Component/CartRemind/CartRemind.php+21
변경 전CartRemind.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\GodoUtils;
 
use Session;
 
class CartRemind
// 전송실패 수
$result['fail'] = 0;
 
 
 
 
 
 
 
 
 
 
foreach ($cartRemindSendData['member'] as $memKey => $memVal) {
// 장바구니 알림에 쿠폰 지급이 있다면 지급
if ($cartRemindSendData['coupon'] > 0) {
return $result;
}
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 장바구니 알림 삭제 (이미 발송 등록된 SMS 는 삭제 되지 않음)
*
 
변경 후CartRemind.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\GodoUtils;
use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType;
use Session;
 
class CartRemind
// 전송실패 수
$result['fail'] = 0;
 
// 개인정보접속기록 발행은 발송 지점(SmsAdmin::sendSms)에서 한다
// 튜닝 상점의 구 버전 복제본에는 없는 신규 메서드라 존재 확인 후 호출한다
if (method_exists($smsAdmin, 'setPublishPersonalInfoAccessLog') && method_exists($smsAdmin, 'setPersonalInfoAccessLogEventType')) {
$smsAdmin->setPublishPersonalInfoAccessLog(true);
$smsAdmin->setPersonalInfoAccessLogEventType($this->resolveAccessLogEventType($cartRemindSendData['type']));
} else {
\Logger::channel('sms')->warning('튜닝으로 인한 개인정보접속기록 발행 설정 미적용', ['missingMethod' => 'SmsAdmin::setPublishPersonalInfoAccessLog, SmsAdmin::setPersonalInfoAccessLogEventType']);
}
 
foreach ($cartRemindSendData['member'] as $memKey => $memVal) {
// 장바구니 알림에 쿠폰 지급이 있다면 지급
if ($cartRemindSendData['coupon'] > 0) {
return $result;
}
 
/**
* @param string $sendType 'auto' 면 크론 자동 발송, 그 외는 관리자 즉시발송
* @return string
*/
protected function resolveAccessLogEventType($sendType)
{
return $sendType === 'auto'
? SmsPersonalInfoAccessLogEventType::SMS_SEND_AUTO
: SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL;
}
 
/**
* 장바구니 알림 삭제 (이미 발송 등록된 SMS 는 삭제 되지 않음)
*
 
Component/Coupon/CouponAdmin.php+9
변경 전CouponAdmin.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\ProducerUtils;
use Globals;
 
use Request;
use Session;
use UserFilePath;
if ($getData['smsFl'] == 'y') {
$smsSendData['password'] = $request->post()->get('password');
$smsSendData['passwordCheckFl'] = $request->post()->get('passwordCheckFl');
 
 
 
 
 
 
 
 
$cnt = $smsAdmin->sendSms($smsSendData);
$logger->info(sprintf(__('SMS 발송이 완료되었습니다. (%1$s건 성공 / %2$s건 실패)'), $cnt['success'], $cnt['fail']));
}
 
변경 후CouponAdmin.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\ProducerUtils;
use Globals;
use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType;
use Request;
use Session;
use UserFilePath;
if ($getData['smsFl'] == 'y') {
$smsSendData['password'] = $request->post()->get('password');
$smsSendData['passwordCheckFl'] = $request->post()->get('passwordCheckFl');
// 개인정보접속기록 발행은 발송 지점(SmsAdmin::sendSms)에서 한다
// 튜닝 상점의 구 버전 복제본에는 없는 신규 메서드라 존재 확인 후 호출한다
if (method_exists($smsAdmin, 'setPublishPersonalInfoAccessLog') && method_exists($smsAdmin, 'setPersonalInfoAccessLogEventType')) {
$smsAdmin->setPublishPersonalInfoAccessLog(true);
$smsAdmin->setPersonalInfoAccessLogEventType(SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL);
} else {
\Logger::channel('sms')->warning('튜닝으로 인한 개인정보접속기록 발행 설정 미적용', ['missingMethod' => 'SmsAdmin::setPublishPersonalInfoAccessLog, SmsAdmin::setPersonalInfoAccessLogEventType']);
}
$cnt = $smsAdmin->sendSms($smsSendData);
$logger->info(sprintf(__('SMS 발송이 완료되었습니다. (%1$s건 성공 / %2$s건 실패)'), $cnt['success'], $cnt['fail']));
}
 
Component/Database/DBTableField.php+2
변경 전DBTableField.php
['val' => 'isOrderSearchMultiGrid', 'typ' => 's', 'def' => 'n'], //주문 멀티검색여부
['val' => 'ipManagerSecurityFl', 'typ' => 's', 'def' => 'n'], //관리자 ip접속제한 사용여부
['val' => 'commerceId', 'typ' => 's', 'def' => null], // 커머스 아이디
 
 
];
// @formatter:on
 
 
변경 후DBTableField.php
['val' => 'isOrderSearchMultiGrid', 'typ' => 's', 'def' => 'n'], //주문 멀티검색여부
['val' => 'ipManagerSecurityFl', 'typ' => 's', 'def' => 'n'], //관리자 ip접속제한 사용여부
['val' => 'commerceId', 'typ' => 's', 'def' => null], // 커머스 아이디
['val' => 'diValue', 'typ' => 's', 'def' => null], // 중복가입확인정보(DI)
['val' => 'messageRoleType', 'typ' => 's', 'def' => null], // 발신관리위임자 역할(위임자: delegate, 미지정: null)
];
// @formatter:on
 
 
Component/Member/ManagerCs.php+2
변경 전ManagerCs.php
}
unset($funcs);
 
 
 
if (!$isSuper) {
unset($result['goodsStockExceptView']); // 상품 상세 재고 수정 제외
unset($result['orderExcelDown']); // 주문/배송 엑셀 다운로드
 
변경 후ManagerCs.php
}
unset($funcs);
 
unset($result['messageSendSms']);
 
if (!$isSuper) {
unset($result['goodsStockExceptView']); // 상품 상세 재고 수정 제외
unset($result['orderExcelDown']); // 주문/배송 엑셀 다운로드
 
Component/Member/Manager.php+111-33
변경 전Manager.php
use Component\Member\Util\MemberUtil;
use Component\Policy\ManageSecurityPolicy;
use Component\Sms\Sms;
use Component\Sms\SmsMessage;
use Component\Sms\SmsSender;
use Component\Storage\Storage;
use Component\Validator\Validator;
use Encryptor;
use Request;
use Session;
use Origin\Service\Admin\Menu\AdminMenuFlagService;
 
 
 
 
 
 
 
/**
* Class Manager
$this->search['isSuper'] = $req['isSuper'];
$this->search['noVisitFl'] = gd_isset($req['noVisitFl'], 'n');
$this->search['searchKind'] = gd_isset($req['searchKind']);
 
 
$this->checked['employeeFl'][$this->search['employeeFl']] =
$this->checked['smsAutoReceive'][$this->search['smsAutoReceive']] =
$this->checked['scmFl'][$this->search['scmFl']] = 'checked = "checked"';
 
 
// 키워드 검색
if ($this->search['keyword']) {
$this->checked['smsAutoReceive']['all'] = 'checked';
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// 부서 검색
if ($this->search['departmentCd']) {
$this->arrWhere[] = 'departmentCd = ?';
$validator = new Validator();
 
$arrData['memo'] = strip_tags($arrData['memo']);//관리자 메모 XSS방지
 
 
 
if ($checkMode === 'modify') {
$managerData = $this->getManagerInfo($arrData['sno']);
if (empty($arrData['functionAuth']['goodsStockExceptView']) === false) {
$functionAuth['functionAuth']['goodsStockExceptView'] = $arrData['functionAuth']['goodsStockExceptView'];
}
 
 
 
 
 
 
 
 
 
}
$arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정
$arrData['functionAuth'] = json_encode($functionAuth, JSON_UNESCAPED_UNICODE); // 운영자 기능 권한 설정
$validator->add('permissionMenu', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 메뉴 접근 권한
$validator->add('functionAuth', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 기능 권한
$validator->add('isSmsAuth', 'yn'); // 휴대폰인증여부
 
 
$validator->add('isEmailAuth', 'yn'); // 이메일인증여부
$validator->add('memo', ''); // 메모
$validator->add('ipManagerSecurityFl', 'yn', true); // 운영자 ip 접속제한 설정여부
if (empty($arrData['functionAuth']['boardMaskingUseFl']) === false) {
$functionAuth['functionAuth']['boardMaskingUseFl'] = $arrData['functionAuth']['boardMaskingUseFl'];
}
 
 
 
 
 
 
 
 
 
}
$arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정
$arrData['writeEnabledMenu'] = json_encode($writeEnabledMenu, JSON_UNESCAPED_UNICODE); // 운영자 쓰기 권한 설정
$managersData[] = $data;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// 운영자 권한 저장
foreach ($managersData as $data) {
$arrManager = [];
foreach ($functionList as $lists) {
foreach ($lists as $code => $name) {
if ($code == 'goodsStockExceptView') continue;
 
$data['functionAuth'][$code] = 'y';
}
}
throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE);
}
$arrData['isSmsAuth'] = 'y';
 
 
 
 
 
 
 
 
} elseif ($arrData['isSmsAuth'] === 'y') {
unset($arrData['cellPhone']);
} else {
}
}
 
$strSql = "UPDATE " . DB_MANAGER . " SET isDelete = 'y' WHERE sno = ?";
 
 
$this->db->bind_query(
$strSql,
[
*/
public function sendSmsAuthNumber($managerInfo)
{
$smsPoint = Sms::getPoint();
if ($smsPoint >= 1) {
if (!$managerInfo['cellPhone']) {
throw new \Exception(__('관리자 보안을 위해 휴대폰 인증 로그인을 사용중입니다. 로그인하신 관리 아이디는 인증된 휴대폰번호가 없어 로그인할 수 없으니, 대표운영자에게 문의하시기 바랍니다.'));
}
 
// 인증번호 생성
$adminSecuritySmsAuthNumber = Otp::getOtp(8);
$contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber);
$receiver[]['cellPhone'] = $managerInfo['cellPhone'];
$smsSender = \App::load(SmsSender::class);
$smsSender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword());
$smsSender->setSmsPoint($smsPoint);
$smsSender->setMessage(new SmsMessage($contents));
$smsSender->setSmsType('user');
$smsSender->setMsgType('auth'); //인증용
$smsSender->setReceiver($receiver);
$smsSender->setLogData(['disableResend' => true]);
$smsSender->setContentsMask([$adminSecuritySmsAuthNumber]);
$smsResult = $smsSender->send();
$smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber;
if ($smsResult['success'] === 1) {
$smsAuth['message'] = 'OK';
} else {
$smsAuth['message'] = 'SMS Send Fail';
}
} else {
$smsAuth['message'] = 'SMS Point Fail';
}
 
 
 
 
 
 
 
 
return $smsAuth;
}
 
 
변경 후Manager.php
use Component\Member\Util\MemberUtil;
use Component\Policy\ManageSecurityPolicy;
use Component\Sms\Sms;
 
 
use Component\Storage\Storage;
use Component\Validator\Validator;
use Encryptor;
use Request;
use Session;
use Origin\Service\Admin\Menu\AdminMenuFlagService;
use Origin\Enum\Crm\Message\MessageOperatorRoleType;
use Origin\Service\Crm\Message\MessageOperatorRoleGuard;
use Origin\Service\Crm\Message\MessageOperatorRoleService;
use Origin\Service\Member\ManagerEditPolicy;
use Origin\Service\Member\ManagerService;
use Origin\Service\Sms\OccupancyAuth\OccupancyOtpSmsService;
 
/**
* Class Manager
$this->search['isSuper'] = $req['isSuper'];
$this->search['noVisitFl'] = gd_isset($req['noVisitFl'], 'n');
$this->search['searchKind'] = gd_isset($req['searchKind']);
$this->search['smsRole'] = gd_isset($req['smsRole'], 'all');
 
$this->checked['employeeFl'][$this->search['employeeFl']] = 'checked = "checked"';
$this->checked['smsAutoReceive'][$this->search['smsAutoReceive']] = 'checked = "checked"';
$this->checked['scmFl'][$this->search['scmFl']] = 'checked = "checked"';
$this->checked['smsRole'][$this->search['smsRole']] = 'checked="checked"';
 
// 키워드 검색
if ($this->search['keyword']) {
$this->checked['smsAutoReceive']['all'] = 'checked';
}
 
// SMS 권한 검색 (발신관리책임자=등록 완료 최고운영자 sno=1 / 발신관리위임자=messageRole / 발송담당자=functionAuth.messageSendSms 보유)
switch ($this->search['smsRole']) {
case MessageOperatorRoleType::REPRESENTATIVE:
// 통합회원 연동(commerceId)·본인인증(diValue) 미완료 최고운영자는 역할이 없어 검색 결과에서 빠진다
$this->arrWhere[] = "m.sno = ? AND IFNULL(m.commerceId, '') != '' AND IFNULL(m.diValue, '') != ''";
$this->db->bind_param_push($this->arrBind, 'i', ManagerService::SUPER_MANAGER_SNO);
break;
case MessageOperatorRoleType::DELEGATE:
$this->arrWhere[] = 'm.messageRoleType = ?';
$this->db->bind_param_push($this->arrBind, 's', MessageOperatorRoleType::DELEGATE);
break;
case MessageOperatorRoleType::SENDER:
// functionAuth JSON 저장 시 공백 유무가 경로마다 달라 JSON 경로로 판정
$this->arrWhere[] = "JSON_VALID(m.functionAuth) AND JSON_UNQUOTE(JSON_EXTRACT(m.functionAuth, '$.functionAuth.messageSendSms')) = ? AND m.sno != ? AND IFNULL(m.messageRoleType, '') != ?";
$this->db->bind_param_push($this->arrBind, 's', 'y');
$this->db->bind_param_push($this->arrBind, 'i', ManagerService::SUPER_MANAGER_SNO);
$this->db->bind_param_push($this->arrBind, 's', MessageOperatorRoleType::DELEGATE);
break;
}
 
// 부서 검색
if ($this->search['departmentCd']) {
$this->arrWhere[] = 'departmentCd = ?';
$validator = new Validator();
 
$arrData['memo'] = strip_tags($arrData['memo']);//관리자 메모 XSS방지
// 본인인증 결과(DI)는 인증 경로만 기록·파기한다 — 요청값이 그대로 실려 저장되지 않도록 여기서 걷어낸다
unset($arrData['diValue']);
 
if ($checkMode === 'modify') {
$managerData = $this->getManagerInfo($arrData['sno']);
if (empty($arrData['functionAuth']['goodsStockExceptView']) === false) {
$functionAuth['functionAuth']['goodsStockExceptView'] = $arrData['functionAuth']['goodsStockExceptView'];
}
if (!empty($arrData['functionAuth']['messageSendSms'])) {
$functionAuth['functionAuth']['messageSendSms'] = $arrData['functionAuth']['messageSendSms'];
}
}
// 최고운영자·위임자는 발송담당자-SMS 권한이 강제(체크박스 disabled 라 폼 미전송)되므로 저장 시 보존
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
if (!empty($arrData['sno']) && $messageOperatorRoleService->isSenderPermissionLocked((int)$arrData['sno'])) {
$functionAuth['functionAuth']['messageSendSms'] = 'y';
}
$arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정
$arrData['functionAuth'] = json_encode($functionAuth, JSON_UNESCAPED_UNICODE); // 운영자 기능 권한 설정
$validator->add('permissionMenu', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 메뉴 접근 권한
$validator->add('functionAuth', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 기능 권한
$validator->add('isSmsAuth', 'yn'); // 휴대폰인증여부
// 미등록 항목은 act() 의 garbage delete 로 사라져 파기가 저장에 반영되지 않는다
$validator->add('diValue', ''); // 중복가입확인정보(DI)
$validator->add('isEmailAuth', 'yn'); // 이메일인증여부
$validator->add('memo', ''); // 메모
$validator->add('ipManagerSecurityFl', 'yn', true); // 운영자 ip 접속제한 설정여부
if (empty($arrData['functionAuth']['boardMaskingUseFl']) === false) {
$functionAuth['functionAuth']['boardMaskingUseFl'] = $arrData['functionAuth']['boardMaskingUseFl'];
}
if (!empty($arrData['functionAuth']['messageSendSms'])) {
$functionAuth['functionAuth']['messageSendSms'] = $arrData['functionAuth']['messageSendSms'];
}
}
// 최고운영자·위임자는 발송담당자-SMS 권한이 강제(체크박스 disabled 라 폼 미전송)되므로 저장 시 보존
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
if (!empty($arrData['sno']) && $messageOperatorRoleService->isSenderPermissionLocked((int)$arrData['sno'])) {
$functionAuth['functionAuth']['messageSendSms'] = 'y';
}
$arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정
$arrData['writeEnabledMenu'] = json_encode($writeEnabledMenu, JSON_UNESCAPED_UNICODE); // 운영자 쓰기 권한 설정
$managersData[] = $data;
}
 
// SMS 발송담당자(messageSendSms) 권한 변경 검증 — 벌크 대상 합산 (클라이언트 submitHandler 와 이중 방어)
$smsRoleService = \App::getInstance(MessageOperatorRoleService::class);
$smsRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class);
$smsGranted = is_array($arrData['functionAuth']) && (($arrData['functionAuth'][MessageOperatorRoleService::SEND_SMS_FUNCTION_AUTH] ?? '') === 'y');
$smsChanged = false;
$smsNewSenderCount = 0;
foreach ($managersData as $smsTarget) {
// 최고운영자(sno=1)/위임자는 messageSendSms 강제(잠금)라 변경 대상 아님
if ($smsRoleService->isSenderPermissionLocked((int)$smsTarget['sno'])) {
continue;
}
$smsNowGranted = $smsRoleService->hasSenderPermission((int)$smsTarget['sno']);
// CS 계정은 발송담당자로 지정할 수 없음 (신규 부여 차단)
if ($smsGranted && $smsNowGranted === false && ($smsTarget['isSuper'] ?? 'n') === 'cs') {
throw new Exception(__('CS 계정은 발송담당자로 지정할 수 없습니다.'));
}
if ($smsNowGranted !== $smsGranted) {
$smsChanged = true;
}
if ($smsGranted && $smsNowGranted === false) {
$smsNewSenderCount++; // 신규로 발송담당자가 되는 대상
}
}
if ($smsChanged) {
if ($smsRoleGuard->isDelegateOrHigher() === false) {
throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.'));
}
if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) {
throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.'));
}
if ($smsGranted && ($smsRoleService->countSenders() + $smsNewSenderCount) > MessageOperatorRoleService::MAX_SENDER_COUNT) {
throw new Exception(__('발송담당자는 최대 %d명까지 지정 가능합니다.', MessageOperatorRoleService::MAX_SENDER_COUNT));
}
if (trim((string)($arrData['accessControlReason'] ?? '')) === '') {
throw new Exception(__('발송담당자 권한 변경 사유를 작성해주세요.'));
}
}
 
// 운영자 권한 저장
foreach ($managersData as $data) {
$arrManager = [];
foreach ($functionList as $lists) {
foreach ($lists as $code => $name) {
if ($code == 'goodsStockExceptView') continue;
if ($code == MessageOperatorRoleService::SEND_SMS_FUNCTION_AUTH) continue; // SMS 발송담당자는 통제 권한이라 신규 기본 부여 제외
$data['functionAuth'][$code] = 'y';
}
}
throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE);
}
$arrData['isSmsAuth'] = 'y';
/** @var ManagerEditPolicy $managerEditPolicy */
$managerEditPolicy = \App::getInstance(ManagerEditPolicy::class);
$targetSno = (int)($arrData['sno'] ?? 0);
$cellPhone = (string)($arrData['cellPhone'] ?? '');
if ($managerEditPolicy->isCertifiedCellPhoneReplaced($targetSno, $cellPhone)) {
// 점유인증으로 번호를 교체하면 기존 DI 는 이전 번호로 받은 인증 결과라 남겨 둘 수 없다
$arrData['diValue'] = null;
}
} elseif ($arrData['isSmsAuth'] === 'y') {
unset($arrData['cellPhone']);
} else {
}
}
 
// 삭제된 계정에 본인확인 식별자를 남겨 두지 않는다 — 점유인증 여부(isSmsAuth)는 번호 보유 사실이라 유지한다
// NULL 은 값이 아니라 키워드라 바인드로 표현할 수 없다 — bind_param 이 값마다 escape 를 걸어 빈 문자열이 된다
$strSql = "UPDATE " . DB_MANAGER . " SET isDelete = 'y', diValue = NULL WHERE sno = ?";
$this->db->bind_query(
$strSql,
[
*/
public function sendSmsAuthNumber($managerInfo)
{
if (!$managerInfo['cellPhone']) {
throw new \Exception(__('관리자 보안을 위해 휴대폰 인증 로그인을 사용중입니다. 로그인하신 관리 아이디는 인증된 휴대폰번호가 없어 로그인할 수 없으니, 대표운영자에게 문의하시기 바랍니다.'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
$adminSecuritySmsAuthNumber = Otp::getOtp(8);
$contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber);
$isSent = \App::getInstance(OccupancyOtpSmsService::class)->send($managerInfo['cellPhone'], $contents, $adminSecuritySmsAuthNumber);
 
$smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber;
$smsAuth['message'] = $isSent ? 'OK' : 'SMS Send Fail';
 
return $smsAuth;
}
 
 
Component/Sms/SmsAdmin.php+48-5
변경 전SmsAdmin.php
use Framework\Utility\StringUtils;
use Respect\Validation\Validator;
use Origin\Service\Member\CrmGroupService;
 
use Origin\Enum\ManagerTutorialType;
use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService;
 
 
/**
* SMS 관리자 관련 처리
class SmsAdmin
{
/** @var \Framework\Database\DBTool $db */
private $db;
/** @var SmsAutoCode $smsAutoCode */
protected $smsAutoCode;
protected $recall;
protected $fieldTypes;
 
 
 
 
 
 
const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg';
const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg';
}
}
 
// SMS 기본 정보 저장
$config['smsCallNum'] = $request['smsCallNum'];
$this->setConfig($config);
 
// 기 저장된 항목 삭제
unset($request['mode'], $request['smsCallNum']);
 
return $setData;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 관리자 SMS 개별/전체 발송
*
 
// 예약 일자 (발송시간)
$smsSendDate = date('Y-m-d H:i:s', $smsSendReserveDate);
 
 
// 발송 타입
$tranType = 'res_send';
} else {
// 발송시간
$smsSendDate = date('Y-m-d H:i:s', time());
 
 
// 발송 타입
$tranType = 'send';
 
// SMS 발송
$cnt = $transport->send();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// excel전송일경우 DB_SMS_EXCEL_LOG 테이블정보 삭제
if ((int) $cnt['success'] > 0 && $receiverData['receiverType'] === 'excel') {
 
변경 후SmsAdmin.php
use Framework\Utility\StringUtils;
use Respect\Validation\Validator;
use Origin\Service\Member\CrmGroupService;
use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType;
use Origin\Enum\ManagerTutorialType;
use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService;
use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService;
 
/**
* SMS 관리자 관련 처리
class SmsAdmin
{
/** @var \Framework\Database\DBTool $db */
protected $db;
/** @var SmsAutoCode $smsAutoCode */
protected $smsAutoCode;
protected $recall;
protected $fieldTypes;
// sendSms() 는 CartRemind·CouponAdmin 등 발행 대상이 아닌 경로에서도 호출되므로 수동발송 경로만 opt-in 으로 발행한다
protected $publishPersonalInfoAccessLog = false;
protected $personalInfoAccessLogEventType = SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL;
// CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다
protected static $personalInfoAccessLogPublishFailed = false;
 
const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg';
const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg';
}
}
 
 
 
 
 
// 기 저장된 항목 삭제
unset($request['mode'], $request['smsCallNum']);
 
return $setData;
}
 
/**
* @param bool $publishPersonalInfoAccessLog 수동 발송 개인정보접속기록 발행 여부
*/
public function setPublishPersonalInfoAccessLog(bool $publishPersonalInfoAccessLog)
{
$this->publishPersonalInfoAccessLog = $publishPersonalInfoAccessLog;
}
 
/**
* @param string $personalInfoAccessLogEventType 발행할 개인정보접속기록 이벤트 유형
*/
public function setPersonalInfoAccessLogEventType(string $personalInfoAccessLogEventType)
{
$this->personalInfoAccessLogEventType = $personalInfoAccessLogEventType;
}
 
/**
* 관리자 SMS 개별/전체 발송
*
 
// 예약 일자 (발송시간)
$smsSendDate = date('Y-m-d H:i:s', $smsSendReserveDate);
$reservedAt = $smsSendDate;
 
// 발송 타입
$tranType = 'res_send';
} else {
// 발송시간
$smsSendDate = date('Y-m-d H:i:s', time());
$reservedAt = null;
 
// 발송 타입
$tranType = 'send';
 
// SMS 발송
$cnt = $transport->send();
if ($this->publishPersonalInfoAccessLog && !self::$personalInfoAccessLogPublishFailed) {
// CLI(스케줄러) 세션은 SessionManager 가 아니라 서비스 생성부터 실패할 수 있어, 이미 끝난 발송 흐름을 끊지 않도록 잡는다
try {
/** @var SmsPersonalInfoAccessLogService $publishService */
$publishService = \App::getInstance(SmsPersonalInfoAccessLogService::class);
// 예약발송도 등록 API 호출이 이 요청 안에서 일어나므로 발행 payload 의 accessIp 는 예약 생성 시점 관리자 IP다
$publishService->publishSmsSendAccessLog(
$this->personalInfoAccessLogEventType,
array_column($receivers, 'cellPhone'),
$countReceivers,
$senderPhoneNumber,
$cnt,
$reservedAt
);
} catch (\Throwable $e) {
self::$personalInfoAccessLogPublishFailed = true;
\Logger::channel(SmsPersonalInfoAccessLogService::PRIVACY_LOG_CHANNEL)->error('[SMS 발송] 개인정보접속기록 발행 실패', [
'exception' => $e,
'eventType' => $this->personalInfoAccessLogEventType,
]);
}
}
 
// excel전송일경우 DB_SMS_EXCEL_LOG 테이블정보 삭제
if ((int) $cnt['success'] > 0 && $receiverData['receiverType'] === 'excel') {
 
Component/Sms/SmsAuto.php+44-1
변경 전SmsAuto.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\ComponentUtils;
use Framework\Utility\StringUtils;
 
 
use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService;
use SplObserver;
 
const KAKAO_ALRIM_CLOUD = 'cloud';
const KAKAO_ALRIM_BIZM = 'bizm';
const KAKAO_ALRIM_LUNA = 'luna'; // 블룸에이아이(구: 루나소프트)
 
 
 
 
/** @var \Framework\Database\DBTool $db */
protected $db;
/** @var \Bundle\Component\Sms\Sms $sms */
/** @var 비밀번호 확인 유무 */
protected $passwordCheckFl = true;
 
 
 
public function __construct(array $config = [])
{
if (isset($config['db']) && \is_object($config['db'])) {
if ($this->smsType == SmsAutoCode::MEMBER && $this->smsAutoCodeType == Code::PASS_AUTH){
$transport->setMsgType('auth'); //인증용
}
$result[] = $transport->send();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
unset($receiverData);
return $this->smsAutoCodeType !== Code::COUPON_MANUAL;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 자동발송 내용에 치환될 데이터를 치환하는 함수
*
 
변경 후SmsAuto.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\ComponentUtils;
use Framework\Utility\StringUtils;
use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType;
use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService;
use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService;
use SplObserver;
 
const KAKAO_ALRIM_CLOUD = 'cloud';
const KAKAO_ALRIM_BIZM = 'bizm';
const KAKAO_ALRIM_LUNA = 'luna'; // 블룸에이아이(구: 루나소프트)
 
// CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다
protected static $personalInfoAccessLogPublishFailed = false;
 
/** @var \Framework\Database\DBTool $db */
protected $db;
/** @var \Bundle\Component\Sms\Sms $sms */
/** @var 비밀번호 확인 유무 */
protected $passwordCheckFl = true;
 
protected $msgType;
 
public function __construct(array $config = [])
{
if (isset($config['db']) && \is_object($config['db'])) {
if ($this->smsType == SmsAutoCode::MEMBER && $this->smsAutoCodeType == Code::PASS_AUTH){
$transport->setMsgType('auth'); //인증용
}
$smsSendResult = $transport->send();
$smsConfig = gd_policy('sms.config');
// CLI(스케줄러) 세션은 SessionManager 가 아니라 서비스 생성부터 실패할 수 있어, 이미 끝난 발송 흐름을 끊지 않도록 잡는다
if (!self::$personalInfoAccessLogPublishFailed) {
$eventType = $this->resolveAccessLogEventType();
try {
/** @var SmsPersonalInfoAccessLogService $publishService */
$publishService = \App::getInstance(SmsPersonalInfoAccessLogService::class);
$publishService->publishSmsSendAccessLog(
$eventType,
array_column($receiverData, 'cellPhone'),
count($receiverData),
$smsConfig['smsCallNum'] ?? '',
$smsSendResult
);
} catch (\Throwable $e) {
self::$personalInfoAccessLogPublishFailed = true;
\Logger::channel(SmsPersonalInfoAccessLogService::PRIVACY_LOG_CHANNEL)->error('[SMS 자동발송] 개인정보접속기록 발행 실패', [
'exception' => $e,
'eventType' => $eventType,
]);
}
}
$result[] = $smsSendResult;
}
 
unset($receiverData);
return $this->smsAutoCodeType !== Code::COUPON_MANUAL;
}
 
/**
* 수동 쿠폰 발급 안내만 관리자 조작 발송이라 수동 기록으로 남는다
*
* @return string
*/
protected function resolveAccessLogEventType()
{
return $this->isAutoSend()
? SmsPersonalInfoAccessLogEventType::SMS_SEND_AUTO
: SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL;
}
 
/**
* 자동발송 내용에 치환될 데이터를 치환하는 함수
*
 
Component/Sms/SmsSender.php+30-9
변경 전SmsSender.php
protected $logData;
/** @var int sms 포인트 */
protected $smsPoint;
 
 
/** @var int sms 전송내역 일련번호 */
protected $smsLogSno;
/** @var array sms api 호출 시 사용될 정보 */
*/
protected function validatePoint()
{
 
 
 
if ($this->smsPoint < $this->getSendPoint() * $this->message->count()) {
$logger = \App::getInstance('logger');
$logger->info(sprintf('current sms point[%d], send count[%d], message count[%d]', $this->smsPoint, $this->getSendPoint(), $this->message->count()));
$defaultReplaceCode = array_fill_keys($this->message->getReplaceCodes(), '');
$replaceCode = array_merge($defaultReplaceCode, $replaceCode);
$replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode);
$hasPoint = $this->smsPoint > 1;
if ($this->message->exceedSmsLength($replaceContents)) { // 치환 후 sms 길이를 초과하는 경우 lms 로 변환
$this->message = $this->message->toLmsMessage();
$replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode);
$hasPoint = $this->smsPoint > Sms::LMS_POINT;
if ($this->message->exceedLmsLength($replaceContents)) {
$logger->error('Message [' . $replaceContents . '] is exceed lms length');
$this->saveSendResultByFail($this->sendSms['hp'], '1002');
$this->sendSms['msg'] = $message;
$this->sendSms['memberNo'] = $item['memNo'];
$res = $this->callApi();
$this->smsPoint -= ($this->_isLms() ? Sms::LMS_POINT : 1);
 
 
$this->callApiPostProcess($res, 1);
} else {
$this->saveSendResultByFail($this->sendSms['hp'], '1001');
}
}
$logger->info(__METHOD__ . ', end point[' . $this->smsPoint . ']');
Sms::saveSmsPoint($this->smsPoint);
 
 
}
/**
if ($this->getReceiverGroup() > 1) {
$receiverCount = count(explode(',', $this->sendSms['hp']));
}
$this->smsPoint -= ($this->getSendPoint($receiverCount) * $this->message->count());
Sms::saveSmsPoint($this->smsPoint);
 
 
}
/**
$this->callApiByLargePostProcess($res, $hpCount);
// 그룹을 2개 이상 발송할 경우 sms포인트가 중복 차감 처리 되어 해당 부분 수신인으로 카운팅 처리
$this->smsPoint -= ($this->getSendPoint() * $this->message->count());
Sms::saveSmsPoint($this->smsPoint);
 
 
}
/**
$this->smsPoint = $smsPoint;
}
 
 
 
 
 
 
 
 
/**
* @param array $contentsMask
*/
return '[제목없음]';
}
return $subject;
}
/**
 
변경 후SmsSender.php
protected $logData;
/** @var int sms 포인트 */
protected $smsPoint;
protected $usePoint = true;
/** @var int sms 전송내역 일련번호 */
protected $smsLogSno;
/** @var array sms api 호출 시 사용될 정보 */
*/
protected function validatePoint()
{
if ($this->usePoint === false) {
return;
}
if ($this->smsPoint < $this->getSendPoint() * $this->message->count()) {
$logger = \App::getInstance('logger');
$logger->info(sprintf('current sms point[%d], send count[%d], message count[%d]', $this->smsPoint, $this->getSendPoint(), $this->message->count()));
$defaultReplaceCode = array_fill_keys($this->message->getReplaceCodes(), '');
$replaceCode = array_merge($defaultReplaceCode, $replaceCode);
$replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode);
$hasPoint = $this->usePoint === false || $this->smsPoint > 1;
if ($this->message->exceedSmsLength($replaceContents)) { // 치환 후 sms 길이를 초과하는 경우 lms 로 변환
$this->message = $this->message->toLmsMessage();
$replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode);
$hasPoint = $this->usePoint === false || $this->smsPoint > Sms::LMS_POINT;
if ($this->message->exceedLmsLength($replaceContents)) {
$logger->error('Message [' . $replaceContents . '] is exceed lms length');
$this->saveSendResultByFail($this->sendSms['hp'], '1002');
$this->sendSms['msg'] = $message;
$this->sendSms['memberNo'] = $item['memNo'];
$res = $this->callApi();
if ($this->usePoint) {
$this->smsPoint -= ($this->_isLms() ? Sms::LMS_POINT : 1);
}
$this->callApiPostProcess($res, 1);
} else {
$this->saveSendResultByFail($this->sendSms['hp'], '1001');
}
}
$logger->info(__METHOD__ . ', end point[' . $this->smsPoint . ']');
if ($this->usePoint) {
Sms::saveSmsPoint($this->smsPoint);
}
}
/**
if ($this->getReceiverGroup() > 1) {
$receiverCount = count(explode(',', $this->sendSms['hp']));
}
if ($this->usePoint) {
$this->smsPoint -= ($this->getSendPoint($receiverCount) * $this->message->count());
Sms::saveSmsPoint($this->smsPoint);
}
}
/**
$this->callApiByLargePostProcess($res, $hpCount);
// 그룹을 2개 이상 발송할 경우 sms포인트가 중복 차감 처리 되어 해당 부분 수신인으로 카운팅 처리
if ($this->usePoint) {
$this->smsPoint -= ($this->getSendPoint() * $this->message->count());
Sms::saveSmsPoint($this->smsPoint);
}
}
/**
$this->smsPoint = $smsPoint;
}
/**
* @param bool $usePoint
*/
public function setUsePoint($usePoint)
{
$this->usePoint = (bool)$usePoint;
}
/**
* @param array $contentsMask
*/
return '[제목없음]';
}
return SmsUtil::cutSubject($subject);
}
/**
 
Controller/Admin/Base/LayerSmsAuthController.php+4-41
변경 전LayerSmsAuthController.php
 
use Component\Member\Manager;
use Component\Policy\ManageSecurityPolicy;
use Component\Sms\Exception\PasswordException;
use Component\Sms\Sms;
use Component\Sms\SmsMessage;
use Component\Sms\SmsSender;
use Component\Validator\Validator;
use Framework\Debug\Exception\AlertReloadException;
 
$isEmailAuth = $manager['isEmailAuth'] === 'y' && Validator::required($manager['email']);
$isCsAuth = array_key_exists('csSno', $manager) && $manager['csSno'] > 0;
$isInvalidPassword = false;
$smsPoint = Sms::getPoint(true);
$hasSmsPoint = ($smsPoint >= 1);
 
$email = $cellPhone = '';
 
$securitySelect = \App::load(ManageSecurityPolicy::class)->getLoginSecuritySelect();
 
if (array_key_exists('smsReSend', $securitySelect) && !$hasSmsPoint) {
try {
$sender = \App::load(SmsSender::class);
$sender->setIsThrowPasswordException(true);
$sender->setMessage(new SmsMessage('SMS 인증번호 검증'));
$receiver = [
[
'memNo' => '0',
'cellPhone' => '00000000000',
],
];
$sender->setReceiver($receiver);
$sender->setSmsType('guest');
$sender->setLogData(['disableResend' => true]);
$sender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword());
$sender->setSmsPoint(1);
$sender->send();
} catch (PasswordException $e) {
if (array_key_exists('emailSend', $securitySelect)) {
unset($securitySelect['smsReSend']);
} else {
$isInvalidPassword = true;
}
}
}
 
$countSelect = \count($securitySelect);
 
if ($isCsAuth) {
$emptyAuthMethod = true;
$securitySelect = ['emailSend' => '이메일'];
} elseif ($countSelect === 2) {
if ($isSmsAuth && !$isEmailAuth && !$hasSmsPoint) {
unset($securitySelect['smsReSend']);
$emptySmsPoint = true;
} elseif (!$isSmsAuth && !$isEmailAuth) {
$securitySelect = [];
} elseif ($isSmsAuth && !$isEmailAuth) {
unset($securitySelect['emailSend']);
} elseif ($isEmailAuth && (!$isSmsAuth || !$hasSmsPoint)) {
unset($securitySelect['smsReSend']);
}
} elseif (array_key_exists('smsReSend', $securitySelect) && $countSelect === 1) {
if (!$hasSmsPoint) {
$emptySmsPoint = true;
} elseif (!$isSmsAuth && $hasSmsPoint) {
unset($securitySelect['smsReSend']);
}
} elseif (array_key_exists('emailSend', $securitySelect) && $countSelect === 1 && !$isEmailAuth) {
unset($securitySelect['emailSend']);
}
 
if ($isSmsAuth && $hasSmsPoint) {
$phoneArr = explode('-', $manager['cellPhone']);
$phoneLen = \strlen($phoneArr[1]);
$s = '';
 
변경 후LayerSmsAuthController.php
 
use Component\Member\Manager;
use Component\Policy\ManageSecurityPolicy;
 
 
 
 
use Component\Validator\Validator;
use Framework\Debug\Exception\AlertReloadException;
 
$isEmailAuth = $manager['isEmailAuth'] === 'y' && Validator::required($manager['email']);
$isCsAuth = array_key_exists('csSno', $manager) && $manager['csSno'] > 0;
$isInvalidPassword = false;
 
 
 
$email = $cellPhone = '';
 
$securitySelect = \App::load(ManageSecurityPolicy::class)->getLoginSecuritySelect();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$countSelect = \count($securitySelect);
 
if ($isCsAuth) {
$emptyAuthMethod = true;
$securitySelect = ['emailSend' => '이메일'];
} elseif ($countSelect === 2) {
if (!$isSmsAuth && !$isEmailAuth) {
 
 
 
$securitySelect = [];
} elseif ($isSmsAuth && !$isEmailAuth) {
unset($securitySelect['emailSend']);
} elseif ($isEmailAuth && !$isSmsAuth) {
unset($securitySelect['smsReSend']);
}
} elseif (array_key_exists('smsReSend', $securitySelect) && $countSelect === 1) {
if (!$isSmsAuth) {
 
 
unset($securitySelect['smsReSend']);
}
} elseif (array_key_exists('emailSend', $securitySelect) && $countSelect === 1 && !$isEmailAuth) {
unset($securitySelect['emailSend']);
}
 
if ($isSmsAuth) {
$phoneArr = explode('-', $manager['cellPhone']);
$phoneLen = \strlen($phoneArr[1]);
$s = '';
 
Controller/Admin/Base/LoginPsController.php-11
변경 전LoginPsController.php
use Component\Member\Manager;
use Component\Member\ManagerCs;
use Component\Policy\ManageSecurityPolicy;
use Component\Sms\Exception\PasswordException;
use Component\Sms\SmsSender;
use Component\Validator\Validator;
use Exception;
use Framework\Debug\Exception\AlertRedirectException;
// 임시 세션 정보
$tmpManager = $session->get(Manager::SESSION_TEMP_MANAGER);
$manager = \App::load(Manager::class);
$smsSender = \App::load(SmsSender::class);
$smsSender->setIsThrowPasswordException(true);
$smsAuth = $manager->sendSmsAuthNumber($tmpManager);
$tmpManager['smsAuthNumber'] = $smsAuth['smsAuthNumber'];
$tmpManager['isSmsLogin'] = $smsAuth['message'];
throw $e;
} catch (LayerNotReloadException $e) {
throw $e;
} catch (PasswordException $e) {
$this->json(
[
'error' => 5,
'message' => $e->getMessage(),
]
);
} catch (Exception $e) {
$adminLog->setAdminLog();
$logger->warning($e->getMessage() . ', ' . $e->getFile() . ', ' . $e->getLine(), $e->getTrace());
 
변경 후LoginPsController.php
use Component\Member\Manager;
use Component\Member\ManagerCs;
use Component\Policy\ManageSecurityPolicy;
 
 
use Component\Validator\Validator;
use Exception;
use Framework\Debug\Exception\AlertRedirectException;
// 임시 세션 정보
$tmpManager = $session->get(Manager::SESSION_TEMP_MANAGER);
$manager = \App::load(Manager::class);
 
 
$smsAuth = $manager->sendSmsAuthNumber($tmpManager);
$tmpManager['smsAuthNumber'] = $smsAuth['smsAuthNumber'];
$tmpManager['isSmsLogin'] = $smsAuth['message'];
throw $e;
} catch (LayerNotReloadException $e) {
throw $e;
 
 
 
 
 
 
 
} catch (Exception $e) {
$adminLog->setAdminLog();
$logger->warning($e->getMessage() . ', ' . $e->getFile() . ', ' . $e->getLine(), $e->getTrace());
 
Controller/Admin/Member/SmsAutoController.php+7-5
변경 전SmsAutoController.php
use Component\Sms\Sms;
use Framework\Utility\ComponentUtils;
use Framework\Utility\ConfigUrlUtils;
 
 
/**
* SMS 자동 발송 설정
$request = \App::getInstance('request');
// --- 메뉴 설정
$this->callMenu('member', 'sms', 'auto');
 
 
 
$smsAdmin = \App::load('Component\\Sms\\SmsAdmin');
$smsAutoData = $smsAdmin->getSmsAutoData();
 
// SMS 발신번호 사전 등록 번호 정보
$godoSms = \App::load('Component\\Godo\\GodoSmsServerApi');
$smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']);
 
$checked = $smsAutoData['checked'];
 
$this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime());
$this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData));
$this->setData('checked', gd_isset($checked));
$this->setData('smsPreRegister', $smsPreRegister);
$this->setData('policy', ComponentUtils::getPolicy('sms.sms080'));
$this->setData('memberJoin', ComponentUtils::getPolicy('member.join'));
$this->setData('useApprovalFlag', ($memberJoin['appUseFl'] != 'n' || $memberJoin['under14Fl'] == 'y'));
$gLicense = $globals->get('gLicense');
$this->setData('mallSettingDate', $gLicense['sdate']);
 
$this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro'));
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
$this->setData('commerceSmsRefusalIntroUrl', ConfigUrlUtils::getNhnCommerceEchostUrl('sms-refusal-intro'));
}
 
변경 후SmsAutoController.php
use Component\Sms\Sms;
use Framework\Utility\ComponentUtils;
use Framework\Utility\ConfigUrlUtils;
use Origin\Service\Crm\Message\SmsManageCallNumberService;
 
/**
* SMS 자동 발송 설정
$request = \App::getInstance('request');
// --- 메뉴 설정
$this->callMenu('member', 'sms', 'auto');
// 인터셉터의 menuCode 보완은 postHandle 이라 index() 안의 admin API 호출보다 늦다
$this->setMenuCode('member', 'sms', 'auto');
 
$smsAdmin = \App::load('Component\\Sms\\SmsAdmin');
$smsAutoData = $smsAdmin->getSmsAutoData();
 
/** @var SmsManageCallNumberService $smsManageCallNumberService */
$smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class);
$hasRegisteredCallNumber = $smsManageCallNumberService->hasRegisteredCallNumber();
 
$checked = $smsAutoData['checked'];
 
$this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime());
$this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData));
$this->setData('checked', gd_isset($checked));
$this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber);
$this->setData('policy', ComponentUtils::getPolicy('sms.sms080'));
$this->setData('memberJoin', ComponentUtils::getPolicy('member.join'));
$this->setData('useApprovalFlag', ($memberJoin['appUseFl'] != 'n' || $memberJoin['under14Fl'] == 'y'));
$gLicense = $globals->get('gLicense');
$this->setData('mallSettingDate', $gLicense['sdate']);
 
 
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
$this->setData('commerceSmsRefusalIntroUrl', ConfigUrlUtils::getNhnCommerceEchostUrl('sms-refusal-intro'));
}
 
Controller/Admin/Member/SmsPsController.php-205
변경 전SmsPsController.php
<?php
 
/**
* This is commercial software, only users who have purchased a valid license
* and accept to the terms of the License Agreement can install and use this
* program.
*
* Do not edit or add to this file if you wish to upgrade Godomall5 to newer
* versions in the future.
*
* @copyright ⓒ 2016, NHN godo: Corp.
* @link http://www.godo.co.kr
*/
 
namespace Bundle\Controller\Admin\Member;
 
use Component\Godo\GodoSmsServerApi;
use Component\Sms\Exception\PasswordException;
use Component\Sms\SmsAdmin;
use Framework\Debug\Exception\LayerException;
use Framework\Debug\Exception\LayerNotReloadException;
use Origin\Service\Admin\Member\ManagerTutorial\HelperPopupExposureAvailabilityService;
use Origin\Enum\ManagerTutorialType;
 
/**
* SMS 관련 처리
*
* @author Shin Donggyu <artherot@godo.co.kr>
*/
class SmsPsController extends \Controller\Admin\Controller
{
 
/**
* index
*
* @throws LayerException
*/
public function index()
{
// --- 모듈 호출
$request = \App::getInstance('request');
$logger = \App::getInstance('logger');
$postData = $request->post()->toArray();
$mode = $request->post()->get('mode');
 
switch (gd_isset($mode)) {
case 'smsAuto': // SMS 자동발송 설정
try {
$smsAdmin = \App::load(SmsAdmin::class);
$smsAdmin->saveSmsAuto($postData);
 
// 운영 도우미 팝업 노출
$helperPopupExposureAvailabilityService = \App::getInstance(HelperPopupExposureAvailabilityService::class);
$getTutorialUrl = $helperPopupExposureAvailabilityService->getTutorialUrl(ManagerTutorialType::SMS_REGISTER);
 
if (!empty($getTutorialUrl)) {
$this->layer(__('저장이 완료되었습니다.'), 'parent.tutorial_layer("' . $getTutorialUrl . '");');
}
$this->layer(__('저장이 완료되었습니다.'));
} catch (\Exception $e) {
$item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : '');
throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item);
}
break;
 
case 'smsCallNumSave': // SMS 발신번호 저장
try {
$smsAdmin = \App::load(SmsAdmin::class);
$result = $smsAdmin->saveCallNum($postData['smsCallNum']);
if ($result === true) {
echo 'OK';
} else {
echo 'FAIL';
}
} catch (\Exception $e) {
echo 'FAIL';
}
break;
 
case 'contentsRegister': // SMS 내용 등록
case 'contentsModify': // SMS 내용 수정
try {
$smsAdmin = \App::load(SmsAdmin::class);
$smsAdmin->saveContentsData($postData);
$this->layer(__('저장이 완료되었습니다.'));
} catch (\Exception $e) {
$item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : '');
throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item);
}
break;
 
case 'contentsDelete': // SMS 내용 수정
try {
$smsAdmin = \App::load(SmsAdmin::class);
$smsAdmin->deleteContentsData($request->post()->get('sno'));
$this->layer(__('저장이 완료되었습니다.'));
} catch (\Exception $e) {
$item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : '');
throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item);
}
break;
 
case 'smsSend': // 발송
try {
$smsAdmin = \App::load(SmsAdmin::class);
$cnt = $smsAdmin->sendSms($postData);
$info = ' (' . $cnt['success'] . __('건 성공') . ' / ' . $cnt['fail'] . __('건 실패') . ')';
$logger->info($info, $postData);
if ((int) $cnt['success'] > 0) {
echo "<script> parent.BootstrapDialog.closeAll(); </script>";
$this->layer(__('SMS 발송 요청이 완료되었습니다. 자세한 내용은 SMS 발송 내역 보기 확인 바랍니다.'), 'parent.location.href="' . $request->getReferer() . '"');
} else {
throw new \Exception('SEND_FAIL');
}
} catch (PasswordException $e) {
echo "<script> parent.BootstrapDialog.closeAll(); </script>";
throw new LayerNotReloadException($e->getMessage(), null, null, $e->getScript(), 4000);
} catch (\Exception $e) {
echo "<script> parent.BootstrapDialog.closeAll(); </script>";
if ($e->getMessage() == 'SEND_FAIL') {
throw new LayerNotReloadException(__('SMS 발송 요청이 실패했습니다.'), null, null, null, 4000);
} else {
$item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : '');
throw new LayerNotReloadException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item, null, null, null, 4000);
}
}
break;
case 'cancel_all_member'://예약문자 전체회원 취소
try {
// --- SMS 모듈
$godoSms = new GodoSmsServerApi();
$componentSmsLog = \App::load('Component\\Sms\\SmsLog');
$smsLog = $componentSmsLog->getSmsLog('*', $postData['smsLogSno']);
if ($smsLog['replaceCodeType'] == 'none') {
// SMS 결과 수신 처리
$godoSms->smsReserveChange($postData['smsLogSno'], 'cancelAll', [], true);
$js = 'parent.hide_process();';
$js .= 'top.dialog_alert(\'전체 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});';
$this->js($js);
} else {
// SMS 결과 수신 처리
if ($godoSms->smsSendListReserveChange($postData['smsLogSno'], 'cancelAll', [], true)) {
//SMS 발송 내역 상세보기
$js = 'parent.hide_process();';
$js .= 'top.dialog_alert(\'전체 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});';
$this->js($js);
} else {
throw new LayerException('통신이 정상적이지 않습니다.\r\n잠시 후 다시 시도해 주세요.', null, null, null, 3000);
}
}
} catch (\Exception $e) {
throw new LayerException($e->getMessage(), null, null, null, 3000);
}
break;
case 'cancel_select_member'://예약문자 특정회원 취소
case 'cancel_search_member'://예약문자 검색회원 취소
try {
// --- SMS 모듈
$godoSms = new GodoSmsServerApi();
$componentSmsLog = \App::load('Component\\Sms\\SmsLog');
$smsLog = $componentSmsLog->getSmsLog('*', $postData['smsLogSno']);
 
$smeModeText = '선택';
if($mode == 'cancel_search_member'){
if($postData['smsKey'] == 'receiverCellPhone') {
$postData['smsKeyword'] = str_replace('-', '', $postData['smsKeyword']);
}
$postData['noPaging'] = 'y';
unset($postData['sno']);
$getData = $componentSmsLog->getSmsSendList($postData);
$postData['sno'] = array();
foreach($getData['data'] as $smsSendItem){
if($smsSendItem['sendCheckFl'] != 'c'){
$postData['sno'][] = $smsSendItem['sno'];
}
}
$smeModeText = '검색';
}
 
if ($smsLog['replaceCodeType'] == 'none') {
// SMS 결과 수신 처리
$godoSms->smsReserveChange($postData['smsLogSno'], 'cancel', $postData['sno'], true);
$js = 'parent.hide_process();';
$js .= 'top.dialog_alert(\''.$smeModeText.'회원 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});';
$this->js($js);
} else {
// SMS 결과 수신 처리
if ($godoSms->smsSendListReserveChange($postData['smsLogSno'], 'cancel', $postData['sno'], true)) {
$js = 'parent.hide_process();';
$js .= 'top.dialog_alert(\''.$smeModeText.'회원 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});';
$this->js($js);
} else {
echo "<script> parent.hide_process(); </script>";
throw new LayerException('통신이 정상적이지 않습니다.\r\n잠시 후 다시 시도해 주세요.', null, null, null, 3000);
}
}
} catch (LayerException $e) {
throw new LayerException($e->getMessage(), null, null, null, 3000);
}
break;
}
 
exit();
}
}
 
변경 후SmsPsController.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Controller/Admin/Member/SmsSendController.php+9-8
변경 전SmsSendController.php
namespace Bundle\Controller\Admin\Member;
 
use Component\Code\Code;
use Component\Godo\GodoSmsServerApi;
use Component\Member\Group\Util;
use Component\Member\MemberDAO;
use Component\Sms\Sms;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\GodoUtils;
use Framework\Utility\StringUtils;
use Origin\Service\Member\CrmGroupService;
 
/**
* SMS 개별/전체 발송
*/
if (gd_is_provider()) { // 공급사관리
$this->callMenu('order', 'order', 'smsSend');
 
 
} else {
$this->callMenu('member', 'sms', 'send');
 
}
$logger = \App::getInstance('logger');
$request = \App::getInstance('request');
$popupMode = false;
$receiverMode = 'select';
 
// SMS 발신번호 사전 등록 번호 정보
$smsCallNum = $smsAdmin->getSmsCallNum();
$godoSms = new GodoSmsServerApi();
$smsPreRegister = $godoSms->checkSmsCallNumber($smsCallNum);
$memberGroupNames = Util::getGroupName();
 
if ($request->get()->has('opener')) {
$this->setData('smsForbidTime', Sms::SMS_FORBID_TIME);
$this->setData('lmsPoint', Sms::LMS_POINT);
$this->setData('smsCallNum', $smsCallNum);
$this->setData('smsPreRegister', $smsPreRegister);
if ($this->getData('receiverCount') < 1) {
$this->setData('receiverCount', count($receiverData));
}
);
$this->setData('excelUploadExposing', $excelUploadExposing);
}
$this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro'));
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
 
 
변경 후SmsSendController.php
namespace Bundle\Controller\Admin\Member;
 
use Component\Code\Code;
 
use Component\Member\Group\Util;
use Component\Member\MemberDAO;
use Component\Sms\Sms;
use Framework\Utility\DateTimeUtils;
use Framework\Utility\GodoUtils;
use Framework\Utility\StringUtils;
use Origin\Service\Crm\Message\SmsManageCallNumberService;
 
/**
* SMS 개별/전체 발송
*/
if (gd_is_provider()) { // 공급사관리
$this->callMenu('order', 'order', 'smsSend');
// 인터셉터의 menuCode 보완은 postHandle 이라 index() 안의 admin API 호출보다 늦다
$this->setMenuCode('order', 'order', 'smsSend');
} else {
$this->callMenu('member', 'sms', 'send');
$this->setMenuCode('member', 'sms', 'send');
}
$logger = \App::getInstance('logger');
$request = \App::getInstance('request');
$popupMode = false;
$receiverMode = 'select';
 
/** @var SmsManageCallNumberService $smsManageCallNumberService */
$smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class);
$smsCallNum = $smsManageCallNumberService->getCallNumber();
$hasRegisteredCallNumber = $smsManageCallNumberService->hasRegisteredCallNumber();
$memberGroupNames = Util::getGroupName();
 
if ($request->get()->has('opener')) {
$this->setData('smsForbidTime', Sms::SMS_FORBID_TIME);
$this->setData('lmsPoint', Sms::LMS_POINT);
$this->setData('smsCallNum', $smsCallNum);
$this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber);
if ($this->getData('receiverCount') < 1) {
$this->setData('receiverCount', count($receiverData));
}
);
$this->setData('excelUploadExposing', $excelUploadExposing);
}
 
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
 
 
Controller/Admin/Policy/AdminLogListController.php+46-11
변경 전AdminLogListController.php
namespace Bundle\Controller\Admin\Policy;
 
 
use Bundle\Component\Member\Member;
use Component\Admin\AdminLogDAO;
 
 
class AdminLogListController extends \Controller\Admin\Controller
{
 
 
 
 
 
 
public function index()
{
$this->callMenu('policy', 'management', 'adminLogList');
$adminLog = new AdminLogDAO();
 
$request = \Request::get()->toArray();
gd_isset($request['view'], 'default');
 
 
 
 
$request['searchDate'][0] = $request['searchDate'][0] ? $request['searchDate'][0] : date('Y-m-d', strtotime('-6 day'));
$request['searchDate'][1] = $request['searchDate'][1] ? $request['searchDate'][1] : date('Y-m-d');
 
// 페이지 구분
$request['adminAccessPage'] = 'adminInfo';
if ($request['view'] == 'adminAccess') {
$request['adminAccessFl'] = true;
}
 
 
 
 
 
 
 
 
$fields = ['sno','regDt', 'ip','managerId','menu','page','action','baseUri','data'];
$data = $adminLog->getList($request,$fields,\Request::get()->get('pageNum',20));
 
 
// 정규식 패턴 view 파라미터 제거
$pattern = '/view=[^&]+$|view=[^&]+&/';
$this->setData('data',$data);
$this->setData('view', $request['view']);
$this->setData('queryString', $queryString);
$this->setData('searchKindASelectBox', Member::getSearchKindASelectBox());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
}
 
변경 후AdminLogListController.php
namespace Bundle\Controller\Admin\Policy;
 
 
 
use Component\Admin\AdminLogDAO;
use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogListDataService;
 
class AdminLogListController extends \Controller\Admin\Controller
{
const VIEW_DEFAULT = 'default';
const VIEW_ADMIN_LIST = 'adminList';
const VIEW_ADMIN_ACCESS = 'adminAccess';
const VIEW_SMS_LIST = 'smsList';
const SEARCH_KIND_EQUAL_SEARCH = 'equalSearch';
 
public function index()
{
$this->callMenu('policy', 'management', 'adminLogList');
// notification-admin 은 공통 헤더의 menuCode 로 관리자 인증을 하므로 API 조회 전에 채워둔다
$this->setMenuCode('policy', 'management', 'adminLogList');
$request = \Request::get()->toArray();
gd_isset($request['view'], self::VIEW_DEFAULT);
 
// 개인정보 과다 조회를 막기 위해 이 화면은 부분포함 검색을 제공하지 않는다
$request['searchKind'] = self::SEARCH_KIND_EQUAL_SEARCH;
 
$request['searchDate'][0] = $request['searchDate'][0] ? $request['searchDate'][0] : date('Y-m-d', strtotime('-6 day'));
$request['searchDate'][1] = $request['searchDate'][1] ? $request['searchDate'][1] : date('Y-m-d');
 
$pageNum = \Request::get()->get('pageNum', 20);
 
if ($request['view'] == self::VIEW_SMS_LIST) {
$data = $this->getSmsPersonalInfoAccessLogData($request, (int)$pageNum);
} else {
$adminLog = new AdminLogDAO();
 
// 페이지 구분
$request['adminAccessPage'] = 'adminInfo';
if ($request['view'] == self::VIEW_ADMIN_ACCESS) {
$request['adminAccessFl'] = true;
}
 
$fields = ['sno', 'regDt', 'ip', 'managerId', 'menu', 'page', 'action', 'baseUri', 'data'];
$data = $adminLog->getList($request, $fields, $pageNum);
}
 
// 정규식 패턴 view 파라미터 제거
$pattern = '/view=[^&]+$|view=[^&]+&/';
$this->setData('data',$data);
$this->setData('view', $request['view']);
$this->setData('queryString', $queryString);
$this->setData('displayMenuColumnFl', $request['view'] == self::VIEW_DEFAULT);
$this->setData('displayPageColumnFl', in_array($request['view'], [self::VIEW_DEFAULT, self::VIEW_ADMIN_LIST, self::VIEW_SMS_LIST], true));
$this->setData('displayPersonalInfoNoteFl', $request['view'] != self::VIEW_SMS_LIST);
}
 
/**
* SMS 관련 개인정보 접속기록 목록 조회
*
* @param array $request
* @param int $pageNum
* @return array list, count, page, request, apiErrorFl
*/
protected function getSmsPersonalInfoAccessLogData(array $request, int $pageNum): array
{
/** @var SmsPersonalInfoAccessLogListDataService $smsPersonalInfoAccessLogListDataService */
$smsPersonalInfoAccessLogListDataService = \App::getInstance(SmsPersonalInfoAccessLogListDataService::class);
 
return $smsPersonalInfoAccessLogListDataService->getSmsPersonalInfoAccessLogData($request, $pageNum);
}
}
 
Controller/Admin/Policy/ManageListController.php+14
변경 전ManageListController.php
use Component\Member\ManagerCs;
use Component\Page\Page;
use Component\Scm\Scm;
 
 
use Framework\Debug\Exception\LayerException;
use Framework\Utility\GodoUtils;
use Framework\Utility\StringUtils;
$component = \App::load(Manager::class);
$getValue = $request->get()->toArray();
$getData = $component->getManagerList($getValue);
 
 
 
 
 
 
 
 
$page = \App::load(Page::class); // 페이지 재설정
$department = gd_code('02001'); // 부서
$position = gd_code('02002'); // 직급
$smsAutoReceiveKind = $component->smsAutoReceiveKind;
$smsAutoReceiveKind = array_merge(['all' => __('전체')], ['n' => __('SMS 수신안함')], $smsAutoReceiveKind);
 
 
 
 
// 장기 미로그인 운영자 안내
$dataSecurity = gd_policy('manage.security');
gd_isset($dataSecurity['noVisitAlarmFl'], 'n');
$this->setData('position', $position);
$this->setData('duty', $duty);
$this->setData('smsAutoReceiveKind', $smsAutoReceiveKind);
 
$this->setData('scmList', $scmList);
$this->addCss(['layer.css']);
}
 
변경 후ManageListController.php
use Component\Member\ManagerCs;
use Component\Page\Page;
use Component\Scm\Scm;
use Origin\Enum\Crm\Message\MessageOperatorRoleType;
use Origin\Service\Crm\Message\MessageOperatorRoleService;
use Framework\Debug\Exception\LayerException;
use Framework\Utility\GodoUtils;
use Framework\Utility\StringUtils;
$component = \App::load(Manager::class);
$getValue = $request->get()->toArray();
$getData = $component->getManagerList($getValue);
// SMS 권한 컬럼 라벨 (조회된 행 데이터만으로 판정 — 추가 쿼리 없음)
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
if (is_array($getData['data'])) {
foreach ($getData['data'] as $messageRoleRowKey => $messageRoleRow) {
$getData['data'][$messageRoleRowKey]['smsRoleName'] = MessageOperatorRoleType::getName($messageOperatorRoleService->extractRole($messageRoleRow));
}
}
$page = \App::load(Page::class); // 페이지 재설정
$department = gd_code('02001'); // 부서
$position = gd_code('02002'); // 직급
$smsAutoReceiveKind = $component->smsAutoReceiveKind;
$smsAutoReceiveKind = array_merge(['all' => __('전체')], ['n' => __('SMS 수신안함')], $smsAutoReceiveKind);
 
// SMS 권한 검색 라디오 목록 ('all'=전체 는 필터 미적용 UI 옵션이라 역할 목록 앞에 붙임)
$smsRoleList = ['all' => __('전체')] + MessageOperatorRoleType::availableForSearch();
 
// 장기 미로그인 운영자 안내
$dataSecurity = gd_policy('manage.security');
gd_isset($dataSecurity['noVisitAlarmFl'], 'n');
$this->setData('position', $position);
$this->setData('duty', $duty);
$this->setData('smsAutoReceiveKind', $smsAutoReceiveKind);
$this->setData('smsRoleList', $smsRoleList);
$this->setData('scmList', $scmList);
$this->addCss(['layer.css']);
}
 
Controller/Admin/Policy/ManagePermissionController.php+5
변경 전ManagePermissionController.php
 
namespace Bundle\Controller\Admin\Policy;
 
 
 
//use Globals;
//use Request;
//use Session;
$this->addScript([
'managePermission.js?'.time(), // 운영자 권한 설정 JS
]);
 
 
 
}
 
public function post()
 
변경 후ManagePermissionController.php
 
namespace Bundle\Controller\Admin\Policy;
 
use Origin\Enum\Crm\Message\MessageOperatorRoleType;
 
//use Globals;
//use Request;
//use Session;
$this->addScript([
'managePermission.js?'.time(), // 운영자 권한 설정 JS
]);
 
// SMS 권한 검색 필터 목록 ('all'=전체 + 역할)
$this->setData('smsRoleList', ['all' => __('전체')] + MessageOperatorRoleType::availableForSearch());
}
 
public function post()
 
Controller/Admin/Policy/ManagePsController.php+138-37
변경 전ManagePsController.php
use Component\Policy\ManageSecurityPolicy;
use Component\Policy\Policy;
use Component\Scm\ScmAdmin;
use Component\Sms\Sms;
use Component\Sms\SmsMessage;
use Component\Sms\SmsSender;
use Exception;
use Framework\Debug\Exception\LayerNotReloadException;
use Framework\Security\Otp;
use Framework\Utility\ComponentUtils;
use Framework\Utility\StringUtils;
use Framework\Security\Token;
 
 
 
 
 
 
/**
* 운영자 등록/수정 처리 페이지
$request = \App::getInstance('request');
$session = \App::getInstance('session');
$_managerClass = \App::load(Manager::class);
 
// 각 모드에 따른 처리
switch ($request->request()->get('mode')) {
case 'modify': // --- 관리자 등록 / 수정
if (gd_is_provider()) {
$managerData = $_managerClass->getManagerData($request->post()->get('sno'));
$this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()');
}
case 'register':
 
try {
if ((DEFAULT_CODE_SCMNO === (int)$request->post()->get('sno')) && (DEFAULT_CODE_SCMNO !== (int)$session->get('manager.sno'))) {
$this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()');
}
// 데모인 경우 패스워드 수정 금지
$demo = \App::load(GodoDemoApi::class);
if ($demo->checkDomain() === true && $request->request()->get('mode') === 'modify') {
$request->post()->del('isModManagerPw');
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
if ($request->post()->get('permissionSetupMode') == 'managePermission' && $this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) {
$postData = $request->post()->toArray();
if (method_exists($_managerClass, 'setManagerWorkDebugPermissionFl')) {
$_managerClass->saveManagerWritePermissionData($postData, $managerSno);
$_managerClass->saveManagerIp($postData,$managerSno);
} else {
$_managerClass->saveManagerData($request->post()->toArray(), $request->files()->toArray());
}
 
 
 
 
 
 
 
 
$this->layer(__('저장이 완료되었습니다.') . '<br/><b class="notice-danger notice-info">'.__('개인정보보호법에 따라 개인정보처리시스템에 접속하는') . '<br>'.__('개인정보취급자(운영자)인 경우에는 접속보안(접속가능 IP)을 설정하시기 바랍니다.'). '</b>', 'parent.location.replace("manage_list.php")');
} catch (Exception $e) {
throw new LayerNotReloadException($e->getMessage(), $e->getCode(), $e, null, 3000);
} else { // 기존 운영자 권한 불러오기
$mode = 'modify';
}
 
 
// 운영자 정보
if ($request->get()->get('sno') == '') {
$this->setData('disabled', $disabled);
$this->setData('changeType', $request->get()->get('type'));
$this->setData('isAccessControlReason', $request->get()->get('isAccessControlReason'));
 
 
$this->setData('sno', $request->get()->get('sno'));
 
 
 
 
 
 
 
 
 
 
 
break;
 
// --- 운영자 권한 설정> 운영자 검색
gd_isset($getValue['pageNum'], 40);
 
$getData = $_managerClass->getManagerList($getValue);
 
 
 
 
 
 
 
 
 
 
 
$page = \App::load('\\Component\\Page\\Page', $getValue['page']); // 페이지 재설정
 
// --- 관리자 디자인 템플릿
 
// --- 운영자 권한 설정 저장
case 'setManagePermission':
 
 
 
try {
if ($this->getData('managerPermissionMethodExist') !== true || $this->getData('adminMenuPermissionMethodExist') !== true) {
throw new \RuntimeException(__('정상적으로 서비스가 제공되지 않습니다. 잠시 후 다시 시도해주세요.<br>문제가 지속될 경우 1:1문의 게시판에 문의해주세요.'));
}
$_managerClass->saveManagersPermission($request->post()->toArray());
 
 
 
 
 
 
 
 
 
 
 
 
$this->layerNotReload(__('저장이 완료되었습니다.'), 'parent.save_after_manger();');
} catch (Exception $e) {
throw new LayerNotReloadException($e->getMessage(), null, null, null, 5000);
 
case 'authSms':
$session->del(Member::SESSION_USER_CERTIFICATION);
$smsPoint = Sms::getPoint();
if ($smsPoint < 1) {
$this->json(
[
'error' => 100,
'message' => __('SMS 포인트가 부족합니다. 인증번호 발송을 위해 SMS 포인트를 충전해주세요'),
]
);
}
// 인증번호 생성
$userCertificationSession['certificationCode'] = Otp::getOtp(8);
$cellPhone = $request->post()->get('cellPhone');
$receiver[] = ['cellPhone' => $cellPhone];
$logData['reserve']['dbTable'] = $userCertificationSession;
 
$msg = sprintf(__('인증번호는 [%s]입니다. 정확히 입력해주세요.'), $userCertificationSession['certificationCode']);
$smsSender = \App::load(SmsSender::class);
$smsSender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword());
$smsSender->setSmsPoint($smsPoint);
$smsSender->setMessage(new SmsMessage($msg));
$smsSender->setSmsType('user');
$smsSender->setMsgType('auth'); //인증용
$smsSender->setReceiver($receiver);
$smsSender->setLogData(['disableResend' => true]);
$smsSender->setContentsMask([$userCertificationSession['certificationCode']]);
$smsResult[] = $smsSender->send();
if ($smsResult[0]['success'] === 1) {
$userCertificationSession['authType'] = 'authSms';
$certData = [
'certNo' => $userCertificationSession['certificationCode'],
]
);
}
$exitMessage = '등록된 SMS 발신번호가 없어 인증번호를 SMS로 발송할 수 없습니다.';
$exitMessage .= ' 발신번호 사전등록제에 따라 SMS를 발송하려면 발신번호가 먼저 등록되어 있어야 합니다.';
$exitMessage .= '<br />고도몰 홈페이지의 마이페이지>SMS발신번호 등록/관리에서 발신번호 등록 후';
$exitMessage .= ' 관리자의 회원>SMS 관리>자동 SMS 설정에서 발신번호를 설정할 수 있습니다.';
$this->json(
[
'error' => 200,
'message' => __($exitMessage),
]
);
break;
break;
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
변경 후ManagePsController.php
use Component\Policy\ManageSecurityPolicy;
use Component\Policy\Policy;
use Component\Scm\ScmAdmin;
 
 
 
use Exception;
use Framework\Debug\Exception\LayerNotReloadException;
use Framework\Security\Otp;
use Framework\Utility\ComponentUtils;
use Framework\Utility\StringUtils;
use Framework\Security\Token;
use Origin\Enum\Crm\Message\MessageOperatorRoleType;
use Origin\Service\Crm\Message\MessageOperatorRoleGuard;
use Origin\Service\Crm\Message\MessageOperatorRoleService;
use Origin\Service\Member\ManagerEditPolicy;
use Origin\Service\Sms\OccupancyAuth\OccupancyOtpSmsService;
 
/**
* 운영자 등록/수정 처리 페이지
$request = \App::getInstance('request');
$session = \App::getInstance('session');
$_managerClass = \App::load(Manager::class);
$mode = $request->request()->get('mode');
// 각 모드에 따른 처리
switch ($mode) {
case 'modify': // --- 관리자 등록 / 수정
if (gd_is_provider()) {
$managerData = $_managerClass->getManagerData($request->post()->get('sno'));
$this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()');
}
case 'register':
$this->setManageMenuCode($mode);
try {
if ((DEFAULT_CODE_SCMNO === (int)$request->post()->get('sno')) && (DEFAULT_CODE_SCMNO !== (int)$session->get('manager.sno'))) {
$this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()');
}
// 데모인 경우 패스워드 수정 금지
$demo = \App::load(GodoDemoApi::class);
if ($demo->checkDomain() === true && $mode === 'modify') {
$request->post()->del('isModManagerPw');
}
// SMS 발신관리위임자 지정/회수 — 버튼 선택(설정/해제) + 사유 입력 (책임자만). 저장 전엔 검증만, 실제 지정/회수(functionAuth messageSendSms 반영 포함)는 저장 후 Service 로 일원화.
/** @var MessageOperatorRoleService $messageRoleService */
$messageRoleService = \App::getInstance(MessageOperatorRoleService::class);
 
// 위임자/발송담당자 계정 정보는 책임자(최고운영자)와 본인만 수정 가능 — 메뉴 쓰기 권한보다 우선
if ($mode === 'modify') {
$editTargetSno = (int)$request->post()->get('sno');
/** @var ManagerEditPolicy $managerEditPolicy */
$managerEditPolicy = \App::getInstance(ManagerEditPolicy::class);
if (!$managerEditPolicy->canEditAccount($editTargetSno)) {
throw new Exception(__('해당 계정 정보는 책임자 및 본인만 수정할 수 있습니다.'));
}
// 화면 잠금으로 값이 빠진 요청과 잠금 우회로 값이 실린 요청을 같은 방식으로 처리한다
$isSenderNameLocked = $managerEditPolicy->isSenderNameLocked($editTargetSno);
$isSenderCellPhoneLocked = $managerEditPolicy->isSenderCellPhoneLocked($editTargetSno);
if ($isSenderNameLocked || $isSenderCellPhoneLocked) {
$senderRoleInfo = $_managerClass->getManagerInfo($editTargetSno);
if ($isSenderNameLocked) {
$request->post()->set('managerNm', $senderRoleInfo['managerNm']);
}
if ($isSenderCellPhoneLocked) {
$request->post()->set('cellPhone', $senderRoleInfo['cellPhone']);
}
}
if ($managerEditPolicy->isRepresentativeProfileLocked($editTargetSno)) {
$superManagerInfo = $_managerClass->getManagerInfo($editTargetSno);
$request->post()->set('managerNm', $superManagerInfo['managerNm']);
$request->post()->set('cellPhone', $superManagerInfo['cellPhone']);
$request->post()->set('email', $superManagerInfo['email']);
}
}
 
$targetSno = (int)$request->post()->get('sno');
$messageDelegateAction = (string)$request->post()->get('messageDelegateAction');
$accessControlReason = trim((string)$request->post()->get('accessControlReason')); // 사유는 accessControlReason 으로 제출 → 운영자 관리/수정 개인정보접속기록에 남음
$postFunctionAuth = $request->post()->get('functionAuth');
// 권한 검증 실패는 [확인] 버튼으로 닫는 얼럿이어야 해 자동 닫힘 레이어를 쓰는 공통 catch 를 타지 않고 여기서 직접 출력한다
try {
$messageRoleService->assertRoleChangeAllowed(
$targetSno,
$messageDelegateAction,
$accessControlReason,
$request->post()->get('permissionSetupMode') === 'managePermission',
is_array($postFunctionAuth) ? $postFunctionAuth : []
);
if ($mode === 'register') {
$messageRoleService->assertAssignableNewAccount(
(string)$request->post()->get('scmNo'),
(string)$request->post()->get('isSuper'),
$messageDelegateAction
);
}
} catch (Exception $e) {
$this->js('parent.dialog_alert("' . addslashes(__($e->getMessage())) . '","' . __('경고') . '" ,{isReload:false});');
}
 
// 발송담당자 권한 전이 발행용 스냅샷 — 저장 후 상태와 비교해 실제로 바뀐 대상만 발행한다
$senderPermissionSnapshot = $messageRoleService->collectSenderPermissions([$targetSno]);
 
if ($request->post()->get('permissionSetupMode') == 'managePermission' && $this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) {
$postData = $request->post()->toArray();
if (method_exists($_managerClass, 'setManagerWorkDebugPermissionFl')) {
$_managerClass->saveManagerWritePermissionData($postData, $managerSno);
$_managerClass->saveManagerIp($postData,$managerSno);
} else {
$managerSno = $_managerClass->saveManagerData($request->post()->toArray(), $request->files()->toArray());
}
 
// 저장 후 반영은 Service 로 일원화 — 전이 발행과 위임자 지정/회수 순서가 여기에 고정돼 있다. 등록은 저장 후 확정된 sno.
$messageRoleService->applyRoleChange(
$targetSno > 0 ? $targetSno : (int)$managerSno,
$messageDelegateAction,
$accessControlReason,
$senderPermissionSnapshot
);
$this->layer(__('저장이 완료되었습니다.') . '<br/><b class="notice-danger notice-info">'.__('개인정보보호법에 따라 개인정보처리시스템에 접속하는') . '<br>'.__('개인정보취급자(운영자)인 경우에는 접속보안(접속가능 IP)을 설정하시기 바랍니다.'). '</b>', 'parent.location.replace("manage_list.php")');
} catch (Exception $e) {
throw new LayerNotReloadException($e->getMessage(), $e->getCode(), $e, null, 3000);
} else { // 기존 운영자 권한 불러오기
$mode = 'modify';
}
$this->setManageMenuCode($mode);
 
// 운영자 정보
if ($request->get()->get('sno') == '') {
$this->setData('disabled', $disabled);
$this->setData('changeType', $request->get()->get('type'));
$this->setData('isAccessControlReason', $request->get()->get('isAccessControlReason'));
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
$this->setData('sno', $request->get()->get('sno'));
// 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (뷰에서 사용)
$this->setData('isMessageApprover', $messageOperatorRoleService->isSenderPermissionLocked((int)$request->get()->get('sno')));
$this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno')));
/** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */
$messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class);
$isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative();
$this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative);
// 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다
$this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative());
$this->setData('messageSenderCount', $messageOperatorRoleService->countSenders());
$this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT);
break;
 
// --- 운영자 권한 설정> 운영자 검색
gd_isset($getValue['pageNum'], 40);
 
$getData = $_managerClass->getManagerList($getValue);
// SMS 권한 컬럼 라벨 (조회된 행 데이터만으로 판정 — 추가 쿼리 없음)
/** @var MessageOperatorRoleService $messageSmsRoleService */
$messageSmsRoleService = \App::getInstance(MessageOperatorRoleService::class);
if (is_array($getData['data'])) {
foreach ($getData['data'] as $smsRoleRowKey => $smsRoleRow) {
$smsRoleValue = $messageSmsRoleService->extractRole($smsRoleRow);
$getData['data'][$smsRoleRowKey]['smsRoleName'] = MessageOperatorRoleType::getName($smsRoleValue);
$getData['data'][$smsRoleRowKey]['smsRole'] = (string)$smsRoleValue;
$getData['data'][$smsRoleRowKey]['smsPermissionLocked'] = $messageSmsRoleService->isSenderPermissionLockedByRow($smsRoleRow) ? 'y' : 'n';
}
}
$page = \App::load('\\Component\\Page\\Page', $getValue['page']); // 페이지 재설정
 
// --- 관리자 디자인 템플릿
 
// --- 운영자 권한 설정 저장
case 'setManagePermission':
// 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는다
// — PS 요청은 인터셉터가 채워주지 않으므로 실제 화면 메뉴로 직접 지정한다
$this->setMenuCode('policy', 'management', 'managePermission');
try {
if ($this->getData('managerPermissionMethodExist') !== true || $this->getData('adminMenuPermissionMethodExist') !== true) {
throw new \RuntimeException(__('정상적으로 서비스가 제공되지 않습니다. 잠시 후 다시 시도해주세요.<br>문제가 지속될 경우 1:1문의 게시판에 문의해주세요.'));
}
$permissionPostData = $request->post()->toArray();
/** @var MessageOperatorRoleService $senderRoleService */
$senderRoleService = \App::getInstance(MessageOperatorRoleService::class);
// 벌크 저장은 여러 운영자의 발송담당자 권한을 한 번에 바꾸므로 저장 전 상태를 모아 전이 대상만 1건으로 발행한다
$senderPermissionTargetSnos = is_array($permissionPostData['manage_sno'] ?? null) ? $permissionPostData['manage_sno'] : [];
$senderPermissionSnapshot = $senderRoleService->collectSenderPermissions($senderPermissionTargetSnos);
$_managerClass->saveManagersPermission($permissionPostData);
$revokedSenderSnos = $senderRoleService->publishSenderPermissionTransition(
$senderPermissionTargetSnos,
$senderPermissionSnapshot,
trim((string)($permissionPostData['accessControlReason'] ?? ''))
);
$senderRoleService->destroyCertificationIdentifiers($revokedSenderSnos);
$this->layerNotReload(__('저장이 완료되었습니다.'), 'parent.save_after_manger();');
} catch (Exception $e) {
throw new LayerNotReloadException($e->getMessage(), null, null, null, 5000);
 
case 'authSms':
$session->del(Member::SESSION_USER_CERTIFICATION);
 
 
 
 
 
 
 
 
 
 
$userCertificationSession['certificationCode'] = Otp::getOtp(8);
$cellPhone = $request->post()->get('cellPhone');
$isSent = \App::getInstance(OccupancyOtpSmsService::class)
->sendCertificationCode($cellPhone, $userCertificationSession['certificationCode']);
if ($isSent) {
 
 
 
 
 
 
 
 
 
 
 
 
$userCertificationSession['authType'] = 'authSms';
$certData = [
'certNo' => $userCertificationSession['certificationCode'],
]
);
}
 
 
 
 
$this->json(
[
'error' => 200,
'message' => __('인증번호 SMS 발송에 실패했습니다. 잠시 후 다시 시도해주세요.'),
]
);
break;
break;
}
}
 
/**
* 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는데
* PS 요청은 인터셉터가 채워주지 않으므로 운영자 등록·수정 화면 메뉴로 직접 지정한다
*/
protected function setManageMenuCode(string $mode)
{
switch ($mode) {
case 'modify':
$this->setMenuCode('policy', 'management', 'modify');
break;
default:
$this->setMenuCode('policy', 'management', 'register');
break;
}
}
}
 
Controller/Admin/Policy/ManageRegisterController.php+81
변경 전ManageRegisterController.php
use Framework\Debug\Exception\AlertBackException;
use Framework\Security\Token;
use Framework\StaticProxy\Proxy\App;
 
 
 
 
use Origin\Util\Oauth\Commerce\CommerceLoginUtils;
use Globals;
use Request;
 
if ($mode === 'register') {
$this->callMenu('policy', 'management', 'register');
 
 
 
} elseif ($isModify) {
$this->callMenu('policy', 'management', 'modify');
 
}
 
// 메일도메인
// 최고운영자 커머스 통합회원 로그인 연동 패치일 이후 설치 상점인지
$isMallInstalledAfterPatch = CommerceLoginUtils::isMallInstalledAfterPatch();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// --- 관리자 디자인 템플릿
$this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch);
$this->setData('isgPlusGift', $isgPlusGift);
 
변경 후ManageRegisterController.php
use Framework\Debug\Exception\AlertBackException;
use Framework\Security\Token;
use Framework\StaticProxy\Proxy\App;
use Origin\Service\Crm\Message\MessageOperatorRoleGuard;
use Origin\Service\Crm\Message\MessageOperatorRoleService;
use Origin\Service\Member\ManagerEditPolicy;
use Origin\Service\Member\ManagerService;
use Origin\Util\Oauth\Commerce\CommerceLoginUtils;
use Globals;
use Request;
 
if ($mode === 'register') {
$this->callMenu('policy', 'management', 'register');
// 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는데
// 인터셉터는 발송 게이트 메뉴만 채워주므로 여기서 직접 지정한다
$this->setMenuCode('policy', 'management', 'register');
} elseif ($isModify) {
$this->callMenu('policy', 'management', 'modify');
$this->setMenuCode('policy', 'management', 'modify');
}
 
// 메일도메인
// 최고운영자 커머스 통합회원 로그인 연동 패치일 이후 설치 상점인지
$isMallInstalledAfterPatch = CommerceLoginUtils::isMallInstalledAfterPatch();
 
// SMS 발신 위임자 권한 (권한 설정 섹션 row + [위임자 권한 설정]/[위임자 권한 해제] 버튼 + 사유)
// 규칙(뷰어 공급사·대상 적격성·역할)은 Origin(Guard/Service)에 위임 — 튜닝돼도 규칙 유지. 컨트롤러는 뷰데이터 조립만.
/** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */
$messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class);
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
/** @var ManagerEditPolicy $managerEditPolicy */
$managerEditPolicy = \App::getInstance(ManagerEditPolicy::class);
 
$messageDelegate = ['visible' => false, 'editable' => false, 'assigned' => false];
$managerNo = (int)$data['data']['sno'];
$scmNo = (int)$data['data']['scmNo'];
// 등록(통합회원 연동 + 본인인증)을 마치지 않은 최고운영자는 지정/회수 주체가 아니므로 UI 자체를 내린다 — 저장측 차단만으로는 안내 없는 실패가 된다
$isRegisteredRepresentative = $messageOperatorRoleGuard->isRepresentative()
&& $messageOperatorRoleGuard->isRepresentativeConnected();
// 노출 대상: 발신관리책임자만 + 뷰어가 공급사 아님 + (등록이거나, 수정 대상이 본사 일반운영자)
if ($isRegisteredRepresentative
&& $messageOperatorRoleGuard->isProviderContext() === false
&& (!$isModify || $messageOperatorRoleService->isEligibleDelegateTarget($managerNo, $scmNo))) {
$delegate = $messageOperatorRoleService->getDelegate();
$assigned = $isModify && $delegate !== null && $delegate->getSno() === $managerNo;
$messageDelegate = [
'visible' => true,
'editable' => $isRegisteredRepresentative, // 지정/회수는 발신관리책임자만
'assigned' => $assigned,
'blockedByExisting' => $delegate !== null && $delegate->getSno() !== $managerNo, // 다른 운영자가 이미 위임자 → 설정 클릭 시 안내 모달
// 노출 대상일 때만 조회 — 차단 여부는 외부 API 호출이라 비노출 화면에서 불필요한 호출을 만들지 않는다
'callerBlocked' => $messageOperatorRoleService->isBlockedRepresentative(),
];
}
$this->setData('messageDelegate', $messageDelegate);
 
// 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다
// 본인인증은 본인 계정에서만 성립하므로 책임자·제3자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다
$isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo;
// 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다
$isRestrictedEditTarget = $isModify
&& ($managerNo === ManagerService::SUPER_MANAGER_SNO
|| $messageOperatorRoleService->hasSenderPermission($managerNo));
// 최고운영자의 이름·휴대폰번호·이메일은 통합회원 정보 동기화 또는 발신관리책임자 등록 이후 어드민 입력이 기준이 아니다 — 그때부터 화면 입력을 잠그고 통합 마이페이지 안내를 노출한다
$isRepresentativeSelf = $isModify && $managerEditPolicy->isRepresentativeProfileLocked($managerNo);
$this->setData('superManagerCommerceLinked', $isRepresentativeSelf);
 
$selfCertification = ['enabled' => false];
// 최고운영자의 본인인증 제공 기준은 잠금과 달라(동기화 후에는 미제공) 정책 판정을 따로 받는다 — 등록 전에는 점유인증으로 정보를 맞춘다
$isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO
? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo)
: $isRestrictedEditTarget;
if ($isSelfTarget && $isSelfCertificationProvided) {
$selfCertification = [
'enabled' => true,
'certified' => $messageOperatorRoleGuard->isConnected($managerNo),
'isSelf' => $isSelfTarget,
];
$this->addScript([
'manager-certification.js?' . time(),
]);
}
$this->setData('selfCertification', $selfCertification);
// 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다
// 저장측 throw 는 발송 역할 계정만 막으므로 최고운영자 대상은 이 화면 판정이 유일한 차단이다
$this->setData(
'managerInfoModifiable',
!$isRestrictedEditTarget || $messageOperatorRoleGuard->isRepresentative() || $isSelfTarget
);
// 동기화 전에만 통합회원 연동 안내를 노출한다 — 동기화 후에는 잠긴 필드 하단 안내가 대신한다
$this->setData('superManagerCommerceGuide', $isModify && $managerEditPolicy->isRepresentativeProfileUnsynced($managerNo));
 
// 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다
// 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다
$this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf);
$this->setData('senderCellPhoneLocked', ($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf);
 
// --- 관리자 디자인 템플릿
$this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch);
$this->setData('isgPlusGift', $isgPlusGift);
 
Controller/Admin/Policy/ManageSecurityController.php+1-2
변경 전ManageSecurityController.php
 
namespace Bundle\Controller\Admin\Policy;
 
use Component\Sms\Sms;
use Exception;
use Framework\Debug\Exception\LayerException;
use Framework\Utility\StringUtils;
}
gd_isset(asort($dataSecurity['countryAccessBlocking']), '');
 
if ($superMangerInfo['data']['isSmsAuth'] == 'y' && Sms::getPoint() > 0) {
$dataSecurity['superCellPhoneFl'] = true;
} else {
$dataSecurity['superCellPhoneFl'] = false;
 
변경 후ManageSecurityController.php
 
namespace Bundle\Controller\Admin\Policy;
 
 
use Exception;
use Framework\Debug\Exception\LayerException;
use Framework\Utility\StringUtils;
}
gd_isset(asort($dataSecurity['countryAccessBlocking']), '');
 
if ($superMangerInfo['data']['isSmsAuth'] == 'y') {
$dataSecurity['superCellPhoneFl'] = true;
} else {
$dataSecurity['superCellPhoneFl'] = false;
 
Controller/Admin/Promotion/CartRemindListController.php+9-9
변경 전CartRemindListController.php
namespace Bundle\Controller\Admin\Promotion;
 
use Component\CartRemind\CartRemind;
use Component\Godo\GodoSmsServerApi;
use Component\Sms\Sms;
use Component\Sms\SmsAdmin;
use Framework\Utility\ConfigUrlUtils;
 
 
use Request;
 
/**
// --- 메뉴 설정
$this->callMenu('promotion', 'cartRemind', 'cartRemindList');
 
$smsAdmin = new SmsAdmin();
$smsAutoData = $smsAdmin->getSmsAutoData();
 
 
// SMS 발신번호 사전 등록 번호 정보
$godoSms = new GodoSmsServerApi();
$smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']);
 
$searchData = Request::get()->all();
$cartRemind = new CartRemind();
 
$this->setData('smsAutoList', Sms::SMS_AUTO_RECEIVE_LIST);
$this->setData('smsAutoOrderPeriod', Sms::SMS_AUTO_ORDER_PERIOD);
$this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData));
 
$this->setData('smsPreRegister', $smsPreRegister);
$this->setData('cartRemindData', gd_isset($getData['data']));
$this->setData('convertArrData', gd_isset($convertArrData));
$this->setData('page', $page);
$this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro'));
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
}
 
변경 후CartRemindListController.php
namespace Bundle\Controller\Admin\Promotion;
 
use Component\CartRemind\CartRemind;
 
use Component\Sms\Sms;
 
use Framework\Utility\ConfigUrlUtils;
use Origin\Enum\Crm\Message\LegacyCallNumberStatus;
use Origin\Service\Crm\Message\SmsManageCallNumberService;
use Request;
 
/**
// --- 메뉴 설정
$this->callMenu('promotion', 'cartRemind', 'cartRemindList');
 
/** @var SmsManageCallNumberService $smsManageCallNumberService */
$smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class);
$smsCallNum = $smsManageCallNumberService->getCallNumber();
 
// 스킨이 === 로 4분기하고 else 가 없어, 키를 빼면 발신번호 영역이 통째로 렌더되지 않는다
$smsPreRegister = LegacyCallNumberStatus::toLegacyValue($smsManageCallNumberService->getLegacyCallNumberStatus());
 
 
$searchData = Request::get()->all();
$cartRemind = new CartRemind();
 
$this->setData('smsAutoList', Sms::SMS_AUTO_RECEIVE_LIST);
$this->setData('smsAutoOrderPeriod', Sms::SMS_AUTO_ORDER_PERIOD);
// 스킨은 smsAutoData['smsCallNum'] 만 참조하므로 배열 계약만 유지하고 자동발송 전건 조회는 하지 않는다
$this->setData('smsAutoData', gd_htmlspecialchars(['smsCallNum' => $smsCallNum]));
$this->setData('smsPreRegister', $smsPreRegister);
$this->setData('cartRemindData', gd_isset($getData['data']));
$this->setData('convertArrData', gd_isset($convertArrData));
$this->setData('page', $page);
 
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
}
 
Controller/Admin/Promotion/ComebackCouponListController.php+9-9
변경 전ComebackCouponListController.php
namespace Bundle\Controller\Admin\Promotion;
 
use Exception;
use Component\Godo\GodoSmsServerApi;
use Component\Sms\SmsAdmin;
use Framework\Debug\Exception\LayerException;
use Framework\Utility\ConfigUrlUtils;
 
 
use Request;
 
class ComebackCouponListController extends \Controller\Admin\Controller
 
// --- 모듈 호출
try {
$smsAdmin = new SmsAdmin();
$smsAutoData = $smsAdmin->getSmsAutoData();
 
 
// SMS 발신번호 사전 등록 번호 정보
$godoSms = new GodoSmsServerApi();
$smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']);
 
// 리스트 정보가져오기
$couponAdmin = \App::load('\\Component\\Coupon\\CouponAdmin');
}
 
// --- 관리자 디자인 템플릿
$this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData));
 
$this->setData('smsPreRegister', $smsPreRegister);
$this->setData('data', gd_isset($getData['data']));
$this->setData('list', gd_isset($getData['list']));
$this->setData('page', $page);
$this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro'));
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
}
 
변경 후ComebackCouponListController.php
namespace Bundle\Controller\Admin\Promotion;
 
use Exception;
 
 
use Framework\Debug\Exception\LayerException;
use Framework\Utility\ConfigUrlUtils;
use Origin\Enum\Crm\Message\LegacyCallNumberStatus;
use Origin\Service\Crm\Message\SmsManageCallNumberService;
use Request;
 
class ComebackCouponListController extends \Controller\Admin\Controller
 
// --- 모듈 호출
try {
/** @var SmsManageCallNumberService $smsManageCallNumberService */
$smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class);
$smsCallNum = $smsManageCallNumberService->getCallNumber();
 
// 스킨이 === 로 4분기하고 else 가 없어, 키를 빼면 발신번호 영역이 통째로 렌더되지 않는다
$smsPreRegister = LegacyCallNumberStatus::toLegacyValue($smsManageCallNumberService->getLegacyCallNumberStatus());
 
 
// 리스트 정보가져오기
$couponAdmin = \App::load('\\Component\\Coupon\\CouponAdmin');
}
 
// --- 관리자 디자인 템플릿
// 스킨은 smsAutoData['smsCallNum'] 만 참조하므로 배열 계약만 유지하고 자동발송 전건 조회는 하지 않는다
$this->setData('smsAutoData', gd_htmlspecialchars(['smsCallNum' => $smsCallNum]));
$this->setData('smsPreRegister', $smsPreRegister);
$this->setData('data', gd_isset($getData['data']));
$this->setData('list', gd_isset($getData['list']));
$this->setData('page', $page);
 
$this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main'));
}
}
 
Controller/Admin/Share/LayerExcelAuthController.php+9-19
변경 전LayerExcelAuthController.php
namespace Bundle\Controller\Admin\Share;
use Component\Sms\Sms;
use Framework\Utility\StringUtils;
/**
];
$isExcelAuthSms = ($auth['requireExcelAuthSms']) && $manager['isSmsAuth'] == 'y' && $manager['cellPhone'];
$isExcelAuthEmail = $auth['requireExcelAuthEmail'] && $manager['isEmailAuth'] == 'y' && $manager['email'];
$hasSmsPoint = (Sms::getPoint() >= 1);
$getValue = \Request::get()->all();
if ($isExcelAuthSms) {
if ($hasSmsPoint) {
$securitySelect['authSms'] = '휴대폰';
$phoneArr = explode('-', $manager['cellPhone']);
$phoneLen = strlen($phoneArr[1]);
$s = '';
for ($i = 1; $i <= $phoneLen; $i++) {
$s .= '*';
}
$phoneArr[1] = $s;
$phoneArr[2] = '**' . substr($phoneArr[2], 2, 2);
$authInformation['cellPhone'] = implode('-', $phoneArr);
} else {
if ($isExcelAuthEmail) {
$this->setData('message', __('잔여 SMS포인트가 부족하여 이메일로 인증을 진행합니다.'));
} else {
$this->setData('superAlertMessage', __('SMS 잔여 포인트가 부족하여 SMS 인증을 할 수 없습니다. 포인트를 충전해주시기 바랍니다.'));
}
}
 
 
 
}
if ($auth['requireExcelAuthSmsGodo']) {
$securitySelect['authSmsGodo'] = '고도회원 휴대폰번호';
 
변경 후LayerExcelAuthController.php
namespace Bundle\Controller\Admin\Share;
 
use Framework\Utility\StringUtils;
/**
];
$isExcelAuthSms = ($auth['requireExcelAuthSms']) && $manager['isSmsAuth'] == 'y' && $manager['cellPhone'];
$isExcelAuthEmail = $auth['requireExcelAuthEmail'] && $manager['isEmailAuth'] == 'y' && $manager['email'];
 
$getValue = \Request::get()->all();
if ($isExcelAuthSms) {
$securitySelect['authSms'] = '휴대폰';
$phoneArr = explode('-', $manager['cellPhone']);
$phoneLen = strlen($phoneArr[1]);
$s = '';
for ($i = 1; $i <= $phoneLen; $i++) {
$s .= '*';
 
 
 
 
 
 
 
 
 
 
 
}
$phoneArr[1] = $s;
$phoneArr[2] = '**' . substr($phoneArr[2], 2, 2);
$authInformation['cellPhone'] = implode('-', $phoneArr);
}
if ($auth['requireExcelAuthSmsGodo']) {
$securitySelect['authSmsGodo'] = '고도회원 휴대폰번호';
 
Controller/Admin/Share/LayerManagePermissionController.php+21
변경 전LayerManagePermissionController.php
use Framework\Debug\Exception\LayerException;
use Component\Admin\AdminMenu;
use Component\Member\Manager;
 
 
use Globals;
 
/**
$request = \App::getInstance('request');
$session = \App::getInstance('session');
 
 
 
 
 
$_managerClass = new Manager();
 
// 실행 조건 검증
 
$checked['permissionFl'][$existingPermission['permissionFl']] = 'checked="checked"';
 
 
 
 
 
 
 
 
 
$this->getView()->setDefine('layout', 'layout_layer.php');
$this->getView()->setPageName('share/layer_manage_permission.php');
 
$this->setData('checked', $checked);
$this->setData('disabled', $disabled);
$this->setData('sno', $request->post()->get('sno'));
 
 
 
 
 
 
 
$this->setData('scmFl', $scmFl);
$this->setData('scmNo', $scmNo);
$this->setData('viewMemuDepth', $viewMemuDepth);
 
변경 후LayerManagePermissionController.php
use Framework\Debug\Exception\LayerException;
use Component\Admin\AdminMenu;
use Component\Member\Manager;
use Origin\Service\Crm\Message\MessageOperatorRoleGuard;
use Origin\Service\Crm\Message\MessageOperatorRoleService;
use Globals;
 
/**
$request = \App::getInstance('request');
$session = \App::getInstance('session');
 
// 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는다
// — 레이어 요청은 인터셉터가 채워주지 않으므로 실제 화면 메뉴로 직접 지정한다
$this->setMenuCode('policy', 'management', $request->post()->get('mode') === 'register' ? 'register' : 'modify');
 
$_managerClass = new Manager();
 
// 실행 조건 검증
 
$checked['permissionFl'][$existingPermission['permissionFl']] = 'checked="checked"';
 
// 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (레이어 최초 렌더에도 반영)
/** @var MessageOperatorRoleService $messageOperatorRoleService */
$messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class);
$isMessageApprover = $messageOperatorRoleService->isSenderPermissionLocked((int)$request->post()->get('sno'));
/** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */
$messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class);
$isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative();
 
$this->getView()->setDefine('layout', 'layout_layer.php');
$this->getView()->setPageName('share/layer_manage_permission.php');
 
$this->setData('checked', $checked);
$this->setData('disabled', $disabled);
$this->setData('sno', $request->post()->get('sno'));
$this->setData('isMessageApprover', $isMessageApprover);
$this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno')));
$this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative);
// 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다
$this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative());
$this->setData('messageSenderCount', $messageOperatorRoleService->countSenders());
$this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT);
$this->setData('scmFl', $scmFl);
$this->setData('scmNo', $scmNo);
$this->setData('viewMemuDepth', $viewMemuDepth);