변경 파일 목록
총 160개
SMS 발송 보안 강화
auto_send_config_contents.phpAdmin/crm/auto_send_config/auto_send_config_preview.phpAdmin/crm/auto_send_config/auto_send_config_recipient_section.phpAdmin/crm/auto_send_config/layer_auto_send_modify_kakao_template.phpAdmin/crm/auto_send_config/auto_send_config.phpAdmin/crm/auto_send.phpAdmin/crm/crm_recipe_send.phpAdmin/crm/layer_message_template_approval_history.phpAdmin/crm/layer_mobile_message_performance_detail.phpAdmin/crm/layer_mobile_message_performance_summary.phpAdmin/crm/layer_save_as_template.phpAdmin/crm/layer_update_kakao_template.phpAdmin/crm/layer_manage_sms_verification.phpAdmin/crm/message_config/message_config.phpAdmin/crm/message_template_result.phpAdmin/crm/message_template/message_template_search.phpAdmin/crm/message_template/message_template.phpAdmin/crm/basic_config.phpAdmin/crm/message_template_register/message_template_register.phpAdmin/crm/template_contents_sms.phpAdmin/crm/message_template_register/layer_kakao_friendtalk_content_setting.phpAdmin/crm/mobile_send/layer_mobile_send_save_sms_template.phpAdmin/crm/mobile_send/layer_mobile_send_sms_template.phpAdmin/crm/mobile_send/layer_recipient_setting.phpAdmin/crm/mobile_send/layer_send_method_setting.phpAdmin/crm/mobile_send/layer_sms_content_setting.phpAdmin/crm/mobile_send/mobile_send.phpAdmin/crm/sms_auto.phpAdmin/member/sms_send.phpAdmin/member/_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_message_representative_send_restrict.phpAdmin/share/layer_occupancy_auth.phpAdmin/share/layer_sms_contents.phpAdmin/share/DBTableField.phpComponent/Database/Manager.phpComponent/Member/SmsAdmin.phpComponent/Sms/SmsAuto.phpComponent/Sms/AutoSendConfigController.phpController/Admin/Crm/LayerAutoSendModifyKakaoTemplateController.phpController/Admin/Crm/AutoSendConfig/LayerAutoSendSaveTemplateController.phpController/Admin/Crm/AutoSendConfig/LayerAutoSendTemplateContentController.phpController/Admin/Crm/AutoSendConfig/LayerAutoSendTemplateController.phpController/Admin/Crm/AutoSendConfig/AutoSendConfigPsController.phpController/Admin/Crm/AutoSendController.phpController/Admin/Crm/LayerListController.phpController/Admin/Crm/AutoSend/LayerListRecommendController.phpController/Admin/Crm/AutoSend/LayerListSearchController.phpController/Admin/Crm/AutoSend/AutoSendPsController.phpController/Admin/Crm/CrmRecipeClickLogPsController.phpController/Admin/Crm/CrmRecipeSendController.phpController/Admin/Crm/LayerKakaoTemplateCommentController.phpController/Admin/Crm/LayerMobileMessagePerformanceDetailController.phpController/Admin/Crm/LayerMobileMessagePerformanceSummaryController.phpController/Admin/Crm/LayerMyAppSendGuideController.phpController/Admin/Crm/LayerPreviewAutoSendController.phpController/Admin/Crm/LayerPreviewMobileSendController.phpController/Admin/Crm/LayerRejectRecipientMemberInfoController.phpController/Admin/Crm/LayerSaveAsTemplateController.phpController/Admin/Crm/LayerSelectCouponController.phpController/Admin/Crm/LayerSelectCouponResultController.phpController/Admin/Crm/LayerSelectMemberGroupsController.phpController/Admin/Crm/LayerSelectMemberGroupsResultController.phpController/Admin/Crm/LayerTemplateContentsController.phpController/Admin/Crm/LayerUpdateKaKaoTemplateController.phpController/Admin/Crm/LayerViewMessageLinkTypeController.phpController/Admin/Crm/MessageConfigController.phpController/Admin/Crm/LayerBizmKakaoTalkChannelController.phpController/Admin/Crm/MessageConfig/LayerBizmKakaoTalkChannelPsController.phpController/Admin/Crm/MessageConfig/LayerChargeMessagePointsHistoryController.phpController/Admin/Crm/MessageConfig/LayerChargeMessagePointsHistoryResultController.phpController/Admin/Crm/MessageConfig/LayerCloudKakaoTalkChannelController.phpController/Admin/Crm/MessageConfig/LayerCloudKakaoTalkChannelPsController.phpController/Admin/Crm/MessageConfig/LayerLunaKakaoTalkChannelController.phpController/Admin/Crm/MessageConfig/LayerLunaKakaoTalkChannelPsController.phpController/Admin/Crm/MessageConfig/LayerManageSmsVerificationController.phpController/Admin/Crm/MessageConfig/LayerManageSmsVerificationPsController.phpController/Admin/Crm/MessageConfig/LayerMessagePointController.phpController/Admin/Crm/MessageConfig/LayerRegisterKakaoTalkChannelController.phpController/Admin/Crm/MessageConfig/LayerRegisterKakaoTalkChannelPsController.phpController/Admin/Crm/MessageConfig/LayerRejectNumberListController.phpController/Admin/Crm/MessageConfig/LayerRejectNumberListPsController.phpController/Admin/Crm/MessageConfig/LayerRejectNumberListResultController.phpController/Admin/Crm/MessageConfig/LayerSmsVerificationController.phpController/Admin/Crm/MessageConfig/MessageConfigPsController.phpController/Admin/Crm/MessageTemplateController.phpController/Admin/Crm/MessageTemplatePsController.phpController/Admin/Crm/MessageTemplateRegisterController.phpController/Admin/Crm/MessageTemplateRegisterPsController.phpController/Admin/Crm/MobileHistoryController.phpController/Admin/Crm/MobileHistoryListController.phpController/Admin/Crm/LayerBizmKakaoAlimTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerCloudKakaoAlimTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerKakaoAlimTalkContentsController.phpController/Admin/Crm/MobileMessageHistory/LayerKakaoAlimTalkHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerKakaoFriendTalkContentsController.phpController/Admin/Crm/MobileMessageHistory/LayerKakaoFriendTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerKakaoFriendTalkHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerLunaKakaoAlimTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberBizmKakaoAlimTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberCloudKakaoAlimTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberKakaoAlimTalkHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberKakaoFriendTalkHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberKakaoFriendTalkHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberSmsHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMemberSmsHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryDetailController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryDetailResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryExcelDownloadController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryExcelRequestController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryExcelRequestPsController.phpController/Admin/Crm/MobileMessageHistory/LayerMessageHistoryExcelRequestResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMyappPushContentsController.phpController/Admin/Crm/MobileMessageHistory/LayerMyappPushHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerMyappPushHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/LayerSmsContentsController.phpController/Admin/Crm/MobileMessageHistory/LayerSmsHistoryResultController.phpController/Admin/Crm/MobileMessageHistory/LayerSmsHistoryResultPsController.phpController/Admin/Crm/MobileMessageHistory/LayerSmsHistorySearchController.phpController/Admin/Crm/MobileMessageHistory/MobileSendController.phpController/Admin/Crm/LayerKakaoAlimtalkContentSettingController.phpController/Admin/Crm/MobileSend/LayerKakaoFriendtalkContentSettingController.phpController/Admin/Crm/MobileSend/LayerMessagePointController.phpController/Admin/Crm/MobileSend/LayerMobileSendMyappPushTemplateController.phpController/Admin/Crm/MobileSend/LayerMobileSendMyappPushTemplateResultController.phpController/Admin/Crm/MobileSend/LayerMobileSendSaveMyappPushTemplateController.phpController/Admin/Crm/MobileSend/LayerMobileSendSaveSmsTemplateController.phpController/Admin/Crm/MobileSend/LayerMobileSendSmsTemplateController.phpController/Admin/Crm/MobileSend/LayerMobileSendSmsTemplateResultController.phpController/Admin/Crm/MobileSend/LayerMyappPushContentSettingController.phpController/Admin/Crm/MobileSend/LayerPreviewMessageController.phpController/Admin/Crm/MobileSend/LayerRecipeSendTimeSettingController.phpController/Admin/Crm/MobileSend/LayerRecipientSettingController.phpController/Admin/Crm/MobileSend/LayerRecipientSettingPsController.phpController/Admin/Crm/MobileSend/LayerSendMethodSettingController.phpController/Admin/Crm/MobileSend/LayerSendTimeSettingController.phpController/Admin/Crm/MobileSend/LayerSmsContentSettingController.phpController/Admin/Crm/MobileSend/MobileSendPsController.phpController/Admin/Crm/MyappPushTemplatePsController.phpController/Admin/Crm/Myapp/MyappPushTemplatePsController.phpController/Admin/Crm/MyappPush/PopupChargeMessagePointsController.phpController/Admin/Crm/PopupSelectMembersController.phpController/Admin/Crm/LayerSelectedMembersResultController.phpController/Admin/Crm/PopupSelectMembers/LayerSelectMembersResultController.phpController/Admin/Crm/PopupSelectMembers/SmsTemplatePsController.phpController/Admin/Crm/Sms/SmsAutoController.phpController/Admin/Member/SmsSendController.phpController/Admin/Member/SmsSendPsController.phpController/Admin/Member/ManagePsController.phpController/Admin/Policy/ManageRegisterController.phpController/Admin/Policy/CartRemindPsController.phpController/Admin/Promotion/CouponPsController.phpController/Admin/Promotion/LayerManagePermissionController.phpController/Admin/Share/LayerSmsContentsController.phpController/Admin/Share/LayerSmsPasswordPsController.phpController/Admin/Share/변경 파일 코드
추가삭제
변경 전auto_send_config_contents.php
| <?php include __DIR__ . '/auto_send_config_recipient_checkboxes.php'; ?> | |
| <?php foreach ($autoSendConfigsResponse as $recipient => $autoSendConfig): ?> | |
| <?php include __DIR__ . '/auto_send_config_recipient_section.php'; ?> | |
| <?php endforeach; ?> | |
변경 후auto_send_config_contents.php
| <?php include __DIR__ . '/auto_send_config_recipient_checkboxes.php'; ?> | |
| <script type="application/json" class="sms-templates-data"><?= json_encode($smsTemplates, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?></script> | |
| <?php foreach ($autoSendConfigsResponse as $recipient => $autoSendConfig): ?> | |
| <?php include __DIR__ . '/auto_send_config_recipient_section.php'; ?> | |
| <?php endforeach; ?> | |
변경 전auto_send_config_preview.php
| <div class="swiper swiper-initialized swiper-horizontal"> | |
| <div class="swiper-wrapper"> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button is-active" data-preview-tab="sms">SMS</button> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button" data-preview-tab="kakao">카카오 알림톡</button> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button" data-preview-tab="app">마이앱(앱푸시)</button> | |
| </div> | |
| </div> | |
| </div> | |
변경 후auto_send_config_preview.php
| <div class="swiper swiper-initialized swiper-horizontal"> | |
| <div class="swiper-wrapper"> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button is-active" data-preview-tab="kakao">카카오 알림톡</button> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button" data-preview-tab="app">마이앱(앱푸시)</button> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button" data-preview-tab="sms">SMS</button> | |
| </div> | |
| </div> | |
| </div> | |
변경 전auto_send_config_recipient_section.php
| // MYAPP_PUSH는 member인 경우에만 표시 | |
| $filteredChannels = array_filter($channels, fn($channel) => $channel->name !== 'MYAPP_PUSH' || in_array($recipient, $myappSupportRecipients)); | |
| $isSingleChannel = count($filteredChannels) === 1; | |
| foreach ($filteredChannels as $channel) { | |
| if ($isSingleChannel) { | |
| $isChecked = true; | |
| $isDisabled = true; | |
| </span> | |
| <span class="ncua-checkbox-field__text"><?= $channel->getTitle() ?></span> | |
| </label> | |
| <?php } ?> | |
| </div> | |
| <div class="channel-hint" hidden> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">발송수단을 선택해 주세요.</span> | |
| <div class="ncua-horizontal-tab ncua-horizontal-tab--panel"> | |
| <div class="swiper swiper-initialized swiper-horizontal"> | |
| <div class="swiper-wrapper"> | |
| <?php foreach ($filteredChannels as $channel) { ?> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button is-active" data-tab-target="<?= $channel->name ?>"><?= $channel->getTitle() ?></button> | |
| </div> | |
| <?php } ?> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <div class="ncua-card__body-block-section-wrap"> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs tooltip-align" data-tooltip-seq="009">내용 입력</div> | |
| <?php /* 제목은 본문이 LMS(90bytes 초과)일 때만 활성 — 토글·카운트는 JS(initMessageModules)가 담당 */ ?> | |
| <div class="message-title-field"> | |
| <div class="ncua-input ncua-input--xs is-disabled"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input name="recipients[<?= $recipient ?>][SMS][templateTitle]" type="text" | |
| value="<?= htmlspecialchars($autoSendConfig['SMS']['templateTitle'] ?? '', ENT_QUOTES) ?>" | |
| placeholder="제목을 입력해 주세요." data-sms-title-input disabled /> | |
| </div> | |
| </div> | |
| </div> | |
| <span class="message-title-count"> | |
| <span data-sms-title-current>0</span>/40 | |
| </span> | |
| </div> | |
| <div id="sms-message-input-container-<?= $recipient ?>" | |
| data-initial-value="<?= htmlspecialchars($autoSendConfig['SMS']['templateContents'] ?? '', ENT_QUOTES) ?>"> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div id="sms-chip-selector-container-<?= $recipient ?>"></div> | |
| </section> | |
| </div> | |
| <div class="ncua-card__body-block-section-actions"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary" onclick="load_as_template('<?= $recipient ?>', 'SMS')">템플릿 불러오기</button> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray file-btn" onclick="save_as_template('<?= $recipient ?>', 'SMS')">템플릿으로 저장하기</button> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <?php if (!empty($autoSendConfig['KAKAO_ALRIM_TALK'])): ?> | |
| <!-- 카카오 알림톡 --> | |
| <div class="tab-content-item" data-tab="KAKAO_ALRIM_TALK" style="display: none;"> | |
| <script type="application/json" class="kakao-templates-data"><?= json_encode($kakaoTemplates, JSON_UNESCAPED_UNICODE) ?></script> | |
| <?php if (!$kakaoCrmUseFlag): ?> | |
| <div class="no-ready-send-type"> | |
| <p class="ncua-caution-text"> | |
변경 후auto_send_config_recipient_section.php
| // MYAPP_PUSH는 member인 경우에만 표시 | |
| $filteredChannels = array_filter($channels, fn($channel) => $channel->name !== 'MYAPP_PUSH' || in_array($recipient, $myappSupportRecipients)); | |
| $isSingleChannel = count($filteredChannels) === 1; | |
| foreach ($filteredChannels as $channel): | |
| if ($isSingleChannel) { | |
| $isChecked = true; | |
| $isDisabled = true; | |
| </span> | |
| <span class="ncua-checkbox-field__text"><?= $channel->getTitle() ?></span> | |
| </label> | |
| <?php endforeach; ?> | |
| </div> | |
| <div class="channel-hint" hidden> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">발송수단을 선택해 주세요.</span> | |
| <div class="ncua-horizontal-tab ncua-horizontal-tab--panel"> | |
| <div class="swiper swiper-initialized swiper-horizontal"> | |
| <div class="swiper-wrapper"> | |
| <?php foreach ($filteredChannels as $channel): ?> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <button type="button" class="ncua-tab-button is-active" data-tab-target="<?= $channel->name ?>"><?= $channel->getTitle() ?></button> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <?php | |
| $smsSelection = $smsTemplateSelections[$recipient]; | |
| $smsCurrentCategory = $smsSelection['category']; | |
| $smsCurrentType = $smsSelection['templateType']; | |
| $smsSelectedSno = $smsSelection['selectedSno']; | |
| $smsVisibleTemplates = $smsSelection['visibleTemplates']; | |
| ?> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs tooltip-align" data-tooltip-seq="009">내용 입력</div> | |
| <div class="ncua-border-content-layout" <?= $isSingleChannel ? 'data-sms-template-restrict-target' : '' ?>> | |
| <div class="ncua-border-content"> | |
| <div class="ncua-border-content-title required">카테고리</div> | |
| <div class="ncua-border-content-form ncua-flex ncua-flex-wrap ncua-flex-gap"> | |
| <?php foreach ($smsTemplateCategories as $smsCategory): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" class="sms-template-category-radio" name="recipients[<?= $recipient ?>][SMS][category]" value="<?= $smsCategory['value'] ?>" data-recipient="<?= $recipient ?>" <?= $smsCurrentCategory === $smsCategory['value'] ? 'checked' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text"><?= $smsCategory['name'] ?></span> | |
| </label> | |
| <?php endforeach; ?> | |
| </div> | |
| </div> | |
| <div class="ncua-border-content"> | |
| <div class="ncua-border-content-title required">템플릿 명</div> | |
| <div class="ncua-flex ncua-flex-column ncua-gap-8"> | |
| <div class="ncua-border-content-form ncua-flex ncua-flex-gap"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" class="sms-template-type-radio" name="recipients[<?= $recipient ?>][SMS][templateType]" value="godomall" data-recipient="<?= $recipient ?>" <?= $smsCurrentType === 'godomall' ? 'checked' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">고도몰 템플릿</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" class="sms-template-type-radio" name="recipients[<?= $recipient ?>][SMS][templateType]" value="user" data-recipient="<?= $recipient ?>" <?= $smsCurrentType === 'user' ? 'checked' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">사용자 템플릿</span> | |
| </label> | |
| </div> | |
| <div class="ncua-border-content-form ncua-flex ncua-gap-4"> | |
| <input type="hidden" class="sms-selected-template-godomall" data-recipient="<?= $recipient ?>" value="<?= $smsCurrentType === 'godomall' && $smsSelectedSno > 0 ? $smsSelectedSno : '' ?>" /> | |
| <input type="hidden" class="sms-selected-template-user" data-recipient="<?= $recipient ?>" value="<?= $smsCurrentType === 'user' && $smsSelectedSno > 0 ? $smsSelectedSno : '' ?>" /> | |
| <input type="hidden" class="sms-template-cleared" name="recipients[<?= $recipient ?>][SMS][templateCleared]" data-recipient="<?= $recipient ?>" value="n" /> | |
| <div class="ncua-select ncua-select--xs sms-template-select"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag sms-template-dropdown" name="recipients[<?= $recipient ?>][SMS][sourceTemplateSno]" data-recipient="<?= $recipient ?>"> | |
| <option value="">템플릿을 선택하세요</option> | |
| <?php foreach ($smsVisibleTemplates as $smsTemplate): ?> | |
| <option value="<?= $smsTemplate['sno'] ?>" <?= $smsTemplate['sno'] === $smsSelectedSno ? 'selected' : '' ?>><?= htmlspecialchars($smsTemplate['name'], ENT_QUOTES) ?></option> | |
| <?php endforeach; ?> | |
| <?php if ($smsVisibleTemplates === []): ?> | |
| <option value="" disabled>사용할 수 있는 템플릿이 없습니다.</option> | |
| <?php endif; ?> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs sms-template-title"> | |
| <textarea class="ncua-input__textarea" rows="1" readonly><?= htmlspecialchars($smsSelection['selectedLmsTitle'], ENT_QUOTES) ?></textarea> | |
| </div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs sms-template-preview"> | |
| <textarea class="ncua-input__textarea" readonly><?= htmlspecialchars($smsSelection['selectedContents'], ENT_QUOTES) ?></textarea> | |
| </div> | |
| <div class="ncua-accordion ncua-accordion--gray notice-accordion"> | |
| <div class="ncua-accordion__summary">SMS/LMS 발송 안내</div> | |
| <ul class="ncua-accordion__content"> | |
| <li class="ncua-notice-info">승인된 템플릿만 자동 알림으로 발송할 수 있습니다.</li> | |
| <li class="ncua-notice-info">SMS/LMS 템플릿 등록·관리·승인은 <a class="ncua-link" href="./message_template.php?sendMethod=sms" target="_blank">템플릿 관리</a>에서 가능합니다.</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <?php if (!empty($autoSendConfig['KAKAO_ALRIM_TALK'])): ?> | |
| <!-- 카카오 알림톡 --> | |
| <div class="tab-content-item" data-tab="KAKAO_ALRIM_TALK" style="display: none;"> | |
| <script type="application/json" class="kakao-templates-data"><?= json_encode($kakaoTemplates, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?></script> | |
| <?php if (!$kakaoCrmUseFlag): ?> | |
| <div class="no-ready-send-type"> | |
| <p class="ncua-caution-text"> | |
변경 전layer_auto_send_modify_kakao_template.php
| <div class="modal-dialog__content layer_update_kakao_template"> | |
| <div class="ncua-table ncua-table--vertical"> | |
| <table> | |
| <tbody> | |
| <tr> | |
| <th class="ncua-required"><div>구분</div></th> | |
| <td> | |
| <div class="ncua-flex-gap"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">주문/배송</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">정기결제(배송)</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">선물하기</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">회원</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">게시판</span> | |
| </label> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="ncua-required"><div>템플릿 카테고리</div></th> | |
| <td> | |
| <div class="ncua-flex-gap"> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag"> | |
| <option value="">대분류</option> | |
| <option value="1">Option 1</option> | |
| <option value="2">Option 2</option> | |
| <option value="3">Option 3</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag" disabled> | |
| <option value="">중분류</option> | |
| <option value="1">Option 1</option> | |
| <option value="2">Option 2</option> | |
| <option value="3">Option 3</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="ncua-required"><div>템플릿 제목</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs "> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs ncua-input-width-520"> | |
| <input maxlength="10" type="text" placeholder="제목을 입력하세요" class="kakao-template-title" data-charcount-key="kakaoTemplateTitle" /></div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoTemplateTitle"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/10</span> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">템플릿 제목을 입력해 주세요.</span> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th><div>보안 템플릿 설정</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" checked="" name="demo-switch" /> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="demo-switch" /> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="content-border-box"> | |
| <div class="template_type"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">메시지형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">강조표기형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">이미지형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">아이템리스트형</span> | |
| </label> | |
| </div> | |
| <div class="template_content ncua-card__body-block-section-wrap"> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs">본문 입력</div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea kakao-template-textarea-content" placeholder="템플릿 본문을 작성해 주세요." maxlength="1000"></textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <div class="ncua-hint-text">본문을 입력해 주세요.</div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /1000 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs" data-tooltip-seq="008">사용 가능한 변수</div> | |
| <div class="chip-selector"> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class=" ncua-select__tag"> | |
| <option value="1">회원</option> | |
| <option value="2">게시판</option> | |
| <option value="3">통계</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="chip-button-wrap"> | |
| <button class="chip-button"><span class="chip-button-text">{rc_mallNm} 쇼핑몰 명/상점명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{memId} 회원 아이디</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{memNm} 회원명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{sleepScheduleDt} 휴면회원 전환 예정 일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_scheduleDt} 일반 회원 전환 일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{smsAgreementDt} SMS 수신동의일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{mailAgreementDt}메일 수신동의일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{mileage} 보유한 마일리지</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{deposit}보유한 예치금</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{groupNm} 회원등급</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_mileage} 보유한 마일리지</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_deposit}보유한 예치금</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_certificationCode} 인증코드</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{wriNm} 작성자</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{orderNo} 주문번호</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{orderName} 주문명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{settlePrice} 결제금액</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{bankAccount} 입금계좌번호</span></button> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title-wrap has-checkbox"> | |
| <span class="ncua-card__body-title--xs">부가 정보 문구</span> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" /> | |
| </span> | |
| <span class="ncua-checkbox-field__text">추가</span> | |
| </label> | |
| </div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea kakao-template-textarea-add-info" placeholder="템플릿 본문을 작성해 주세요." maxlength="200"></textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <div class="ncua-hint-text destructive">본문을 입력해 주세요.</div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /200 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section channel_add_message"> | |
| <div class="ncua-card__body-title-wrap has-checkbox"> | |
| <span class="ncua-card__body-title--xs">채널 추가 문구(고정 내용)</span> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" /> | |
| </span> | |
| <span class="ncua-checkbox-field__text">추가</span> | |
| </label> | |
| </div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea" maxlength="36" readonly>채널 추가하고 이 채널의 광고와 마케팅을 카카오톡으로 받기</textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <div class="ncua-hint-text"></div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /36 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title-wrap"> | |
| <span class="ncua-card__body-title--xs">버튼</span> | |
| <div> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray auto-send-icon auto-send-icon--plus"> | |
| <span class="ncua-btn__label">페이지 링크 추가</span> | |
| </button> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray auto-send-icon auto-send-icon--plus"> | |
| <span class="ncua-btn__label">배송 조회 추가</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="ncua-table ncua-table--horizontal"> | |
| <table> | |
| <colgroup> | |
| <col width="79px"> | |
| <col width="auto"> | |
| <col width="auto"> | |
| <col width="88px"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><div class="ncua-align-center">타입</div></th> | |
| <th class="ncua-required"><div>버튼명</div></th> | |
| <th class="ncua-required"><div>URL</div></th> | |
| <th><div class="ncua-align-center">관리</div></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td> | |
| <div class="ncua-align-center">페이지 링크</div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs ncua-input--full-width-with-count"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="20" type="text" placeholder="버튼명을 입력해주세요" class="kakao-button-page-link" data-charcount-key="kakaoButtonPageLink" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoButtonPageLink"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/20</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">버튼명을 입력해 주세요.</span> | |
| </div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input type="text" placeholder="버튼명을 입력해주세요" /> | |
| </div> | |
| </div> | |
| </div> | |
| <span class="ncua-hint-text ncua-input__hint-text">외부 링크 입력 시 캠페인 통계가 집계되지 않습니다.</span> | |
| </div> | |
| </div> | |
| </td> | |
| <td> | |
| <div class="ncua-align-center"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray auto-send-icon auto-send-icon--minus"> | |
| <span class="ncua-btn__label">삭제</span> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="ncua-table ncua-table--horizontal"> | |
| <table> | |
| <colgroup> | |
| <col width="79px"> | |
| <col width="auto"> | |
| <col width="auto"> | |
| <col width="88px"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><div class="ncua-align-center">타입</div></th> | |
| <th class="ncua-required"><div>버튼명</div></th> | |
| <th class="ncua-required"><div>URL</div></th> | |
| <th><div class="ncua-align-center">관리</div></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td> | |
| <div class="ncua-align-center">배송 조회</div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs ncua-input--full-width-with-count"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="20" type="text" placeholder="버튼명을 입력해주세요" class="kakao-button-delivery-search" data-charcount-key="kakaoButtonDeliverySearch" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoButtonDeliverySearch"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/20</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">버튼명을 입력해 주세요.</span> | |
| </div> | |
| </td> | |
| <td> | |
| <div> | |
| <p>본문에 택배사명과 송장번호가 포함되면 버튼이 카카오 배송조회 페이지로 연결됩니다. 단, 미지원 택배사는 조회되지 않습니다.</p> | |
| </div> | |
| </td> | |
| <td> | |
| <div class="ncua-align-center"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray minus-icon"> | |
| <span class="ncua-btn__label">삭제</span> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-dialog__footer"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray js-layer-close"> | |
| <span class="ncua-btn__label">취소</span> | |
| </button> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-layer-close"> | |
| <span class="ncua-btn__label">저장</span> | |
| </button> | |
| </div> | |
변경 후layer_auto_send_modify_kakao_template.php
변경 전auto_send_config.php
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/ncds-jquery-validator.js')?>"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/unsaved-changes-guard.js')?>"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/scroll-sticky.js')?>"></script> | |
| <!-- CRM Preview --> | |
| <script src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/godo-ui-module/crm-preview/crm-preview-loader.js')?>" data-exclude="friendtalk"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/godo-ui-module/chip-selector/chip-selector.js')?>"></script> | |
| <script type="text/javascript"> | |
| const AUTO_SEND_CODE = '<?= $code ?>'; | |
| const USE_JOIN_POLICY = <?= $useJoinPolicy ? 'true' : 'false' ?>; | |
| /** | |
| * 뒤로가기 버튼 핸들러 | |
| // 카카오 알림톡 구분(카테고리) 변경 시 템플릿 드롭다운 업데이트 | |
| initKakaoCategoryChange(); | |
| // 스크롤 시 미리보기 섹션 전환 초기화 | |
| initPreviewScrollSync(); | |
| const recipient = section.dataset.targetType; | |
| if (tabInfo.channel === 'SMS') { | |
| const input = messageInputInstances[recipient]?.SMS; | |
| const smsContent = input ? normalizeNewlines(input.getValue()) : ''; | |
| const smsTitleInput = section.querySelector('input[data-sms-title-input]'); | |
| const smsTitle = smsTitleInput && !smsTitleInput.disabled ? smsTitleInput.value.replace(/\{[^}]*\}/g, '') : ''; | |
| this.setPreview(section, { title: smsTitle, content: smsContent || '메시지 내용을 입력하세요.' }); | |
| } else if (tabInfo.channel === 'KAKAO_ALRIM_TALK') { | |
| const tabContent = section.querySelector('[data-tab="KAKAO_ALRIM_TALK"]'); | |
| if (!tabContent) return; | |
| messageInputInstances[recipient] = {}; | |
| chipSelectorInstances[recipient] = {}; | |
| // SMS MessageInput | |
| const smsContainer = document.getElementById(`sms-message-input-container-${recipient}`); | |
| if (smsContainer) { | |
| const smsInitialValue = normalizeNewlines(smsContainer.dataset.initialValue || ''); | |
| const smsInput = GodoUIModule.render({ | |
| type: 'MessageInput', | |
| target: `#sms-message-input-container-${recipient}`, | |
| name: `recipients[${recipient}][SMS][templateContent]`, | |
| maxLength: 2000, | |
| useBytes: true, | |
| hintText: 'SMS 건당 1포인트 차감', | |
| initialValue: smsInitialValue, | |
| allowEmoji: false, | |
| onInput: (input) => { | |
| const normalized = normalizeNewlines(input); | |
| if (normalized !== input && smsInput.textarea) { | |
| const pos = Math.max(0, smsInput.textarea.selectionStart - (input.length - normalized.length)); | |
| smsInput.textarea.value = normalized; | |
| smsInput.textarea.setSelectionRange(pos, pos); | |
| smsInput.updateCount(); | |
| } | |
| PreviewFacade.setPreview(smsContainer.closest('[data-target-type]'), { content: normalized }); | |
| // SMS/LMS 포인트 힌트 업데이트 | |
| const byteLength = smsInput.calculateLength(normalized); | |
| smsInput.setHint(byteLength > 90 ? 'LMS 건당 3포인트 차감' : 'SMS 건당 1포인트 차감'); | |
| // 본문 기준 SMS/LMS 판단 — LMS 일 때만 제목 입력 활성화 | |
| setSmsTitleEnabled(byteLength > 90); | |
| // 빈 내용일 때 안내문구 숨김 | |
| if (normalized === '') { | |
| const caution = document.querySelector(`#sms-chip-selector-container-${recipient} [data-caution-template]`); | |
| if (caution) caution.style.display = 'none'; | |
| crmPreview.setContent('메시지 내용을 입력하세요.'); | |
| } | |
| } | |
| }); | |
| messageInputInstances[recipient].SMS = smsInput; | |
| // ===== SMS/LMS 제목 — 본문이 LMS(90bytes 초과)일 때만 활성 (모바일 발송과 동일 규칙) ===== | |
| const SMS_TITLE_MAX_BYTES = 40; | |
| const sectionEl = smsContainer.closest('[data-target-type]'); | |
| const smsTitleInput = sectionEl.querySelector('input[data-sms-title-input]'); | |
| const smsTitleCounter = sectionEl.querySelector('[data-sms-title-current]'); | |
| // 미리보기 — LMS 상태에서 값이 있을 때만 노출, 제목은 변수 미지원이라 치환자는 공란으로 표기 | |
| const updateSmsTitlePreview = () => { | |
| if (!smsTitleInput) return; | |
| const value = !smsTitleInput.disabled ? smsTitleInput.value.replace(/\{[^}]*\}/g, '') : ''; | |
| PreviewFacade.setPreview(sectionEl, { title: value }); | |
| }; | |
| // 제목 입력 — 이모지 제거, 40bytes 초과분 자동 삭제 (byte 기준은 본문 MessageInput 과 동일) | |
| const handleSmsTitleInput = () => { | |
| if (!smsTitleInput) return; | |
| let value = smsInput.removeEmoji(smsTitleInput.value).value; | |
| value = smsInput.truncateBytes(value, SMS_TITLE_MAX_BYTES); | |
| if (smsTitleInput.value !== value) { | |
| smsTitleInput.value = value; | |
| } | |
| if (smsTitleCounter) smsTitleCounter.textContent = smsInput.getByteLength(value); | |
| updateSmsTitlePreview(); | |
| }; | |
| // SMS 로 돌아오면 값은 유지한 채 비활성 | |
| const setSmsTitleEnabled = (enabled) => { | |
| if (!smsTitleInput) return; | |
| smsTitleInput.disabled = !enabled; | |
| smsTitleInput.closest('.ncua-input').classList.toggle('is-disabled', !enabled); | |
| updateSmsTitlePreview(); | |
| }; | |
| if (smsTitleInput) { | |
| smsTitleInput.addEventListener('input', handleSmsTitleInput); | |
| // 초기 상태: 저장된 본문 byte 기준 활성 여부 + 저장된 제목 카운트 반영 | |
| setSmsTitleEnabled(smsInput.calculateLength(smsInitialValue) > 90); | |
| handleSmsTitleInput(); | |
| } | |
| // 초기 힌트 상태 설정 | |
| const hintEl = smsContainer.querySelector('[data-message-hint]'); | |
| if (hintEl) hintEl.classList.add('destructive'); | |
| if (smsInitialValue) { | |
| const byteLength = smsInput.calculateLength(smsInitialValue); | |
| smsInput.setHint(byteLength > 90 ? 'LMS 건당 3포인트 차감' : 'SMS 건당 1포인트 차감'); | |
| } | |
| } | |
| // SMS ChipSelector | |
| const smsChipContainer = document.getElementById(`sms-chip-selector-container-${recipient}`); | |
| if (smsChipContainer) { | |
| chipSelectorInstances[recipient].SMS = GodoUIModule.render({ | |
| type: 'ChipSelector', | |
| target: `#sms-chip-selector-container-${recipient}`, | |
| title: '사용 가능한 변수', | |
| tooltipSeq: '008', | |
| cautionHTML: cautionHTML, | |
| categories: channelChipSelectorCategories['SMS'] || [], | |
| onSelect: (variable) => { | |
| messageInputInstances[recipient]?.SMS?.insertText(variable); | |
| } | |
| }); | |
| } | |
| // MYAPP MessageInput | |
| const myappContainer = document.getElementById(`myapp-message-input-container-${recipient}`); | |
| if (myappContainer) { | |
| let isSubmitting = false; | |
| const submitForm = (form) => { | |
| if (isSubmitting) return; | |
| isSubmitting = true; | |
| const formData = new FormData(form); | |
| if (!section) continue; | |
| const smsCheckbox = section.querySelector('input[type="checkbox"][name$="[SMS][isEnabled]"]:checked'); | |
| if (!smsCheckbox) continue; | |
| const input = messageInputInstances[recipient]?.SMS; | |
| if (input && input.getValue().trim() === '') return section; | |
| } | |
| return null; | |
| }; | |
| if (section.dataset[dataAttr] !== 'true') continue; | |
| const channelCheckbox = section.querySelector(`input[type="checkbox"][name$="[${channel}][isEnabled]"]:checked`); | |
| if (!channelCheckbox) continue; | |
| const input = messageInputInstances[recipient]?.SMS; | |
| if (input && input.getValue().trim() === '') return true; | |
| } | |
| } | |
| return false; | |
| if (!section) return; | |
| const channel = tabButton.dataset.tabTarget; | |
| // 모든 탭 숨기고 버튼 비활성화 | |
| section.querySelectorAll('.tab-content-item').forEach(item => item.style.display = 'none'); | |
| tabButtons.forEach((button) => { | |
| button.addEventListener('click', (event) => { | |
| const targetTab = event.currentTarget.getAttribute('data-preview-tab'); | |
| tabButtons.forEach(btn => btn.classList.remove('is-active')); | |
| event.currentTarget.classList.add('is-active'); | |
| }; | |
| // 카카오 템플릿 드롭다운 업데이트 공통 함수 | |
| const updateKakaoTemplateDropdown = (tabContent, recipient, kakaoTemplates, selectedCategory, selectedType, shouldResetSelection = false) => { | |
| const dropdown = tabContent.querySelector('.kakao-template-dropdown'); | |
| if (!dropdown) return; | |
| return selectedType === 'godomall' ? isGodomall : !isGodomall; | |
| }); | |
| const currentHiddenInput = tabContent.querySelector(`.kakao-selected-template-${selectedType}[data-recipient="${recipient}"]`); | |
| const savedTemplateCode = shouldResetSelection ? '' : (currentHiddenInput ? currentHiddenInput.value : ''); | |
| filteredTemplates.forEach((template) => { | |
| const selectedCategory = event.target.value; | |
| const section = event.target.closest('[data-target-type]'); | |
| if (!section) return; | |
| const recipient = section.dataset.targetType; | |
| const tabContent = section.querySelector('[data-tab="KAKAO_ALRIM_TALK"]'); | |
| if (!tabContent) return; | |
| } | |
| // 구분 변경 시 템플릿 선택 초기화 (shouldResetSelection = true) | |
| updateKakaoTemplateDropdown(tabContent, recipient, kakaoTemplates, selectedCategory, selectedType, true); | |
| }); | |
| }); | |
| if (!dropdown) return; | |
| // 직전 선택 템플릿 저장용 hidden input | |
| const previousHiddenInput = tabContent.querySelector(`.kakao-selected-template-${previousType}[data-recipient="${recipient}"]`); | |
| // 현재 선택된 템플릿을 이전 유형의 hidden input에 저장 | |
| if (previousHiddenInput && dropdown.value) { | |
| } | |
| // 템플릿 타입 변경 시에는 저장된 값 유지 시도 | |
| updateKakaoTemplateDropdown(tabContent, recipient, kakaoTemplates, selectedCategory, selectedType, false); | |
| }); | |
| }); | |
| const currentType = checkedRadio ? checkedRadio.value : 'godomall'; | |
| // 현재 유형의 hidden input에 선택값 저장 | |
| const currentHiddenInput = tabContent.querySelector(`.kakao-selected-template-${currentType}[data-recipient="${recipient}"]`); | |
| if (currentHiddenInput) { | |
| currentHiddenInput.value = selectedCode; | |
| } | |
| }); | |
| }; | |
| let isLoadingTemplate = false; | |
| const load_as_template = (recipient, channel) => { | |
| } | |
| // 템플릿 불러오기 후 안내문구 표시 | |
| const containerSelector = channel === 'SMS' | |
| ? `#sms-chip-selector-container-${recipient}` | |
| : `#myapp-chip-selector-container-${recipient}`; | |
| const cautionEl = document.querySelector(`${containerSelector} [data-caution-template]`); | |
| if (cautionEl) cautionEl.style.display = ''; | |
| }; | |
변경 후auto_send_config.php
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/ncds-jquery-validator.js')?>"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/unsaved-changes-guard.js')?>"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/scroll-sticky.js')?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.useNcdsDialog(); | |
| SmsSendGate.applyRestrictOnAjax('<?= gd_isset($smsSendRestrictType, '') ?>', { | |
| tooltip: '<?= gd_isset($smsSendRestrictTooltip, '') ?>', | |
| selectors: ['input[type="checkbox"][name$="[SMS][isEnabled]"]'] | |
| }); | |
| SmsSendGate.bindClickGate({ | |
| message: '<?= gd_isset($smsSendRestrictAlertMessage, '') ?>', | |
| subMessage: '<?= gd_isset($smsSendRestrictAlertSubMessage, '') ?>', | |
| selectors: ['[data-sms-template-restrict-target]'] | |
| }); | |
| }); | |
| </script> | |
| <!-- CRM Preview --> | |
| <script src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/godo-ui-module/crm-preview/crm-preview-loader.js')?>" data-exclude="friendtalk"></script> | |
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/godo-ui-module/chip-selector/chip-selector.js')?>"></script> | |
| <script type="text/javascript"> | |
| const AUTO_SEND_CODE = '<?= $code ?>'; | |
| const USE_JOIN_POLICY = <?= $useJoinPolicy ? 'true' : 'false' ?>; | |
| const IS_SMS_TAB_ACCESSIBLE = <?= json_encode($isSmsTabAccessible) ?>; | |
| const SMS_NO_ROLE_MESSAGE = '<?= gd_htmlspecialchars_stripslashes($smsNoRoleMessage) ?>'; | |
| const SMS_NO_ROLE_SUB_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsNoRoleSubMessage, '')) ?>'; | |
| const SMS_RESTRICT_ALERT_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertMessage, '')) ?>'; | |
| const SMS_RESTRICT_ALERT_SUB_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertSubMessage, '')) ?>'; | |
| // 탭은 노출 유지가 스펙이라 렌더 차단 대신 클릭 진입점에서 막는다 | |
| const blockSmsTabWithoutRole = () => { | |
| if (SMS_RESTRICT_ALERT_MESSAGE) { | |
| NCDSAlert({message: SMS_RESTRICT_ALERT_MESSAGE, subMessage: SMS_RESTRICT_ALERT_SUB_MESSAGE, iconType: 'error'}); | |
| return true; | |
| } | |
| if (IS_SMS_TAB_ACCESSIBLE) return false; | |
| NCDSAlert({message: SMS_NO_ROLE_MESSAGE, subMessage: SMS_NO_ROLE_SUB_MESSAGE, iconType: 'error'}); | |
| return true; | |
| }; | |
| /** | |
| * 뒤로가기 버튼 핸들러 | |
| // 카카오 알림톡 구분(카테고리) 변경 시 템플릿 드롭다운 업데이트 | |
| initKakaoCategoryChange(); | |
| initSmsCategoryChange(); | |
| // 스크롤 시 미리보기 섹션 전환 초기화 | |
| initPreviewScrollSync(); | |
| const recipient = section.dataset.targetType; | |
| if (tabInfo.channel === 'SMS') { | |
| const tabContent = section.querySelector('[data-tab="SMS"]'); | |
| if (!tabContent) return; | |
| const dropdown = tabContent.querySelector('.sms-template-dropdown'); | |
| const template = findSmsTemplateBySno(tabContent, dropdown?.value); | |
| const smsContent = template ? normalizeNewlines(template.contents) : ''; | |
| this.setPreview(section, { title: getSmsLmsTitle(template), content: smsContent || '메시지 내용을 입력하세요.' }); | |
| } else if (tabInfo.channel === 'KAKAO_ALRIM_TALK') { | |
| const tabContent = section.querySelector('[data-tab="KAKAO_ALRIM_TALK"]'); | |
| if (!tabContent) return; | |
| messageInputInstances[recipient] = {}; | |
| chipSelectorInstances[recipient] = {}; | |
| // MYAPP MessageInput | |
| const myappContainer = document.getElementById(`myapp-message-input-container-${recipient}`); | |
| if (myappContainer) { | |
| let isSubmitting = false; | |
| const submitForm = async (form) => { | |
| if (isSubmitting) return; | |
| isSubmitting = true; | |
| const formData = new FormData(form); | |
| if (!section) continue; | |
| const smsCheckbox = section.querySelector('input[type="checkbox"][name$="[SMS][isEnabled]"]:checked'); | |
| if (!smsCheckbox) continue; | |
| const dropdown = section.querySelector('.sms-template-dropdown'); | |
| if (dropdown && dropdown.value === '') return section; | |
| } | |
| return null; | |
| }; | |
| if (section.dataset[dataAttr] !== 'true') continue; | |
| const channelCheckbox = section.querySelector(`input[type="checkbox"][name$="[${channel}][isEnabled]"]:checked`); | |
| if (!channelCheckbox) continue; | |
| const dropdown = section.querySelector('.sms-template-dropdown'); | |
| if (dropdown && dropdown.value === '') return true; | |
| } | |
| } | |
| return false; | |
| if (!section) return; | |
| const channel = tabButton.dataset.tabTarget; | |
| if (channel === 'SMS' && blockSmsTabWithoutRole()) return; | |
| // 모든 탭 숨기고 버튼 비활성화 | |
| section.querySelectorAll('.tab-content-item').forEach(item => item.style.display = 'none'); | |
| tabButtons.forEach((button) => { | |
| button.addEventListener('click', (event) => { | |
| const targetTab = event.currentTarget.getAttribute('data-preview-tab'); | |
| if (targetTab === 'sms' && blockSmsTabWithoutRole()) return; | |
| tabButtons.forEach(btn => btn.classList.remove('is-active')); | |
| event.currentTarget.classList.add('is-active'); | |
| }; | |
| // 카카오 템플릿 드롭다운 업데이트 공통 함수 | |
| const updateKakaoTemplateDropdown = (tabContent, kakaoTemplates, selectedCategory, selectedType, shouldResetSelection = false) => { | |
| const dropdown = tabContent.querySelector('.kakao-template-dropdown'); | |
| if (!dropdown) return; | |
| return selectedType === 'godomall' ? isGodomall : !isGodomall; | |
| }); | |
| const currentHiddenInput = tabContent.querySelector(`.kakao-selected-template-${selectedType}`); | |
| const savedTemplateCode = shouldResetSelection ? '' : (currentHiddenInput ? currentHiddenInput.value : ''); | |
| filteredTemplates.forEach((template) => { | |
| const selectedCategory = event.target.value; | |
| const section = event.target.closest('[data-target-type]'); | |
| if (!section) return; | |
| const tabContent = section.querySelector('[data-tab="KAKAO_ALRIM_TALK"]'); | |
| if (!tabContent) return; | |
| } | |
| // 구분 변경 시 템플릿 선택 초기화 (shouldResetSelection = true) | |
| updateKakaoTemplateDropdown(tabContent, kakaoTemplates, selectedCategory, selectedType, true); | |
| }); | |
| }); | |
| if (!dropdown) return; | |
| // 직전 선택 템플릿 저장용 hidden input | |
| const previousHiddenInput = tabContent.querySelector(`.kakao-selected-template-${previousType}`); | |
| // 현재 선택된 템플릿을 이전 유형의 hidden input에 저장 | |
| if (previousHiddenInput && dropdown.value) { | |
| } | |
| // 템플릿 타입 변경 시에는 저장된 값 유지 시도 | |
| updateKakaoTemplateDropdown(tabContent, kakaoTemplates, selectedCategory, selectedType, false); | |
| }); | |
| }); | |
| const currentType = checkedRadio ? checkedRadio.value : 'godomall'; | |
| // 현재 유형의 hidden input에 선택값 저장 | |
| const currentHiddenInput = tabContent.querySelector(`.kakao-selected-template-${currentType}`); | |
| if (currentHiddenInput) { | |
| currentHiddenInput.value = selectedCode; | |
| } | |
| }); | |
| }; | |
| let smsTemplatesCache; | |
| const parseSmsTemplates = () => { | |
| if (smsTemplatesCache !== undefined) return smsTemplatesCache; | |
| const dataScript = document.querySelector('.sms-templates-data'); | |
| if (!dataScript) { | |
| smsTemplatesCache = null; | |
| return smsTemplatesCache; | |
| } | |
| try { | |
| smsTemplatesCache = JSON.parse(dataScript.textContent); | |
| } catch (e) { | |
| console.error('SMS 템플릿 데이터 파싱 오류:', e); | |
| smsTemplatesCache = null; | |
| } | |
| return smsTemplatesCache; | |
| }; | |
| const getSmsLmsTitle = (template) => template?.lmsTitle || ''; | |
| const setSmsTemplatePreview = (tabContent, template) => { | |
| const lmsTitle = getSmsLmsTitle(template); | |
| const titleTextarea = tabContent.querySelector('.sms-template-title textarea'); | |
| if (titleTextarea) titleTextarea.value = lmsTitle; | |
| const textarea = tabContent.querySelector('.sms-template-preview textarea'); | |
| const contents = template ? normalizeNewlines(template.contents) : ''; | |
| if (textarea) textarea.value = contents; | |
| PreviewFacade.setPreview(tabContent.closest('[data-target-type]'), { | |
| title: lmsTitle, | |
| content: contents || '메시지 내용을 입력하세요.' | |
| }); | |
| }; | |
| const getSmsSelectedType = (tabContent) => { | |
| const checked = tabContent.querySelector('.sms-template-type-radio:checked'); | |
| return checked ? checked.value : 'godomall'; | |
| }; | |
| const getSmsSelectedCategory = (tabContent) => { | |
| const checked = tabContent.querySelector('.sms-template-category-radio:checked'); | |
| return checked ? checked.value : ''; | |
| }; | |
| const findSmsTemplateBySno = (tabContent, sno) => { | |
| const smsTemplates = parseSmsTemplates(); | |
| if (!smsTemplates || !sno) return null; | |
| for (const templates of Object.values(smsTemplates)) { | |
| const found = templates.find(t => String(t.sno) === String(sno)); | |
| if (found) return found; | |
| } | |
| return null; | |
| }; | |
| const setSmsTemplateCleared = (tabContent, value) => { | |
| const clearedInput = tabContent.querySelector('.sms-template-cleared'); | |
| if (clearedInput) { | |
| clearedInput.value = value; | |
| } | |
| }; | |
| const updateSmsTemplateDropdown = (tabContent, selectedCategory, selectedType, shouldResetSelection = false) => { | |
| const dropdown = tabContent.querySelector('.sms-template-dropdown'); | |
| const smsTemplates = parseSmsTemplates(); | |
| if (!dropdown || !smsTemplates) return; | |
| dropdown.innerHTML = '<option value="">템플릿을 선택하세요</option>'; | |
| const filteredTemplates = (smsTemplates[selectedCategory] || []) | |
| .filter(template => (selectedType === 'godomall') === Boolean(template.isProvided)); | |
| const hiddenInput = tabContent.querySelector(`.sms-selected-template-${selectedType}`); | |
| const initialSno = hiddenInput?.dataset.initialSno || ''; | |
| const restoredSno = shouldResetSelection && initialSno && filteredTemplates.some(template => String(template.sno) === initialSno) | |
| ? initialSno | |
| : ''; | |
| const savedSno = shouldResetSelection ? restoredSno : (hiddenInput ? hiddenInput.value : ''); | |
| filteredTemplates.forEach(template => { | |
| const option = document.createElement('option'); | |
| option.value = template.sno; | |
| option.textContent = template.name; | |
| if (savedSno && String(template.sno) === String(savedSno)) { | |
| option.selected = true; | |
| } | |
| dropdown.appendChild(option); | |
| }); | |
| if (filteredTemplates.length === 0) { | |
| const emptyOption = document.createElement('option'); | |
| emptyOption.value = ''; | |
| emptyOption.disabled = true; | |
| emptyOption.textContent = '사용할 수 있는 템플릿이 없습니다.'; | |
| dropdown.appendChild(emptyOption); | |
| } | |
| if (shouldResetSelection) { | |
| if (hiddenInput) { | |
| hiddenInput.value = savedSno; | |
| } | |
| setSmsTemplateCleared(tabContent, savedSno ? 'n' : 'y'); | |
| } | |
| const selectedTemplate = savedSno | |
| ? filteredTemplates.find(template => String(template.sno) === String(savedSno)) | |
| : null; | |
| setSmsTemplatePreview(tabContent, selectedTemplate || null); | |
| }; | |
| const initSmsCategoryChange = () => { | |
| document.querySelectorAll('.sms-selected-template-godomall, .sms-selected-template-user').forEach(hiddenInput => { | |
| hiddenInput.dataset.initialSno = hiddenInput.value; | |
| }); | |
| document.querySelectorAll('.sms-template-category-radio').forEach(radio => { | |
| radio.addEventListener('change', event => { | |
| const recipient = event.target.dataset.recipient; | |
| const tabContent = document.querySelector(`[data-target-type="${recipient}"] [data-tab="SMS"]`); | |
| if (!tabContent) return; | |
| updateSmsTemplateDropdown(tabContent, event.target.value, getSmsSelectedType(tabContent), true); | |
| }); | |
| }); | |
| document.querySelectorAll('.sms-template-type-radio').forEach(radio => { | |
| radio.addEventListener('change', event => { | |
| const selectedType = event.target.value; | |
| const recipient = event.target.dataset.recipient; | |
| const tabContent = document.querySelector(`[data-target-type="${recipient}"] [data-tab="SMS"]`); | |
| if (!tabContent) return; | |
| const dropdown = tabContent.querySelector('.sms-template-dropdown'); | |
| const previousType = selectedType === 'godomall' ? 'user' : 'godomall'; | |
| const previousHiddenInput = tabContent.querySelector(`.sms-selected-template-${previousType}`); | |
| if (previousHiddenInput && dropdown?.value) { | |
| previousHiddenInput.value = dropdown.value; | |
| } | |
| updateSmsTemplateDropdown(tabContent, getSmsSelectedCategory(tabContent), selectedType, false); | |
| setSmsTemplateCleared(tabContent, dropdown?.value ? 'n' : 'y'); | |
| }); | |
| }); | |
| document.querySelectorAll('.sms-template-dropdown').forEach(dropdown => { | |
| dropdown.addEventListener('change', event => { | |
| const recipient = event.target.dataset.recipient; | |
| const tabContent = document.querySelector(`[data-target-type="${recipient}"] [data-tab="SMS"]`); | |
| if (!tabContent) return; | |
| const selectedType = getSmsSelectedType(tabContent); | |
| const hiddenInput = tabContent.querySelector(`.sms-selected-template-${selectedType}`); | |
| if (hiddenInput) { | |
| hiddenInput.value = event.target.value; | |
| hiddenInput.dataset.initialSno = event.target.value; | |
| } | |
| setSmsTemplateCleared(tabContent, event.target.value ? 'n' : 'y'); | |
| setSmsTemplatePreview(tabContent, findSmsTemplateBySno(tabContent, event.target.value)); | |
| }); | |
| }); | |
| }; | |
| let isLoadingTemplate = false; | |
| const load_as_template = (recipient, channel) => { | |
| } | |
| // 템플릿 불러오기 후 안내문구 표시 | |
| const cautionEl = document.querySelector(`#myapp-chip-selector-container-${recipient} [data-caution-template]`); | |
| if (cautionEl) cautionEl.style.display = ''; | |
| }; | |
변경 전auto_send.php
| const useJoinPolicy = <?= $useJoinPolicy ? 'true' : 'false' ?>; | |
| const MEMBER_JOIN_APPROVE_CODE = 'APPROVAL'; | |
| const FORCE_SEND_CODE = 'PRESENT'; | |
| document.addEventListener('DOMContentLoaded', () => { | |
| onClickAutoSendSearch(); | |
| // 저장 버튼 클릭 | |
| document.querySelector('.auto-send-save-button').addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const changedCodes = getChangedAutoSendCodes(); | |
| const loadingModal = window.spinnerModal({ message: '저장 중...' }); | |
| updateInitialValues(changedCodes); | |
| window.unsavedGuard?.reset(); | |
| NCDSToast({message: '저장되었습니다.', color: 'success'}); | |
| } else { | |
| NCDSToast({message: '일시적인 오류가 발생하였습니다. </br>다시 시도해 주세요.', color: 'error'}); | |
| } | |
변경 후auto_send.php
| const useJoinPolicy = <?= $useJoinPolicy ? 'true' : 'false' ?>; | |
| const MEMBER_JOIN_APPROVE_CODE = 'APPROVAL'; | |
| const FORCE_SEND_CODE = 'PRESENT'; | |
| const IS_SMS_SEND_ACCESSIBLE = <?= json_encode($isSmsSendAccessible) ?>; | |
| const SMS_NO_ROLE_MESSAGE = '<?= gd_htmlspecialchars_stripslashes($smsNoRoleMessage) ?>'; | |
| const SMS_NO_ROLE_SUB_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsNoRoleSubMessage, '')) ?>'; | |
| const SMS_RESTRICT_ALERT_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertMessage, '')) ?>'; | |
| const SMS_RESTRICT_ALERT_SUB_MESSAGE = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertSubMessage, '')) ?>'; | |
| // 저장 버튼은 노출 유지가 스펙이라 렌더 차단 대신 클릭 진입점에서 막는다 | |
| const blockSaveWithoutSmsRole = () => { | |
| if (SMS_RESTRICT_ALERT_MESSAGE) { | |
| NCDSAlert({message: SMS_RESTRICT_ALERT_MESSAGE, subMessage: SMS_RESTRICT_ALERT_SUB_MESSAGE, iconType: 'error'}); | |
| return true; | |
| } | |
| if (IS_SMS_SEND_ACCESSIBLE) return false; | |
| NCDSAlert({message: SMS_NO_ROLE_MESSAGE, subMessage: SMS_NO_ROLE_SUB_MESSAGE, iconType: 'error'}); | |
| return true; | |
| }; | |
| document.addEventListener('DOMContentLoaded', () => { | |
| onClickAutoSendSearch(); | |
| // 저장 버튼 클릭 | |
| document.querySelector('.auto-send-save-button').addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| if (blockSaveWithoutSmsRole()) return; | |
| const changedCodes = getChangedAutoSendCodes(); | |
| const loadingModal = window.spinnerModal({ message: '저장 중...' }); | |
| updateInitialValues(changedCodes); | |
| window.unsavedGuard?.reset(); | |
| NCDSToast({message: '저장되었습니다.', color: 'success'}); | |
| } else if (data.restricted) { | |
| NCDSAlert({message: data.message, subMessage: data.subMessage || '', iconType: 'error'}); | |
| } else { | |
| NCDSToast({message: '일시적인 오류가 발생하였습니다. </br>다시 시도해 주세요.', color: 'error'}); | |
| } | |
변경 전crm_recipe_send.php
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/jquery/jquery.multi_select_box.js') ?>"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/mobile_message_replace_code.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/message_send_common.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_status.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-checkbox-group/ncds-checkbox-group.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-datepicker-factory/ncds-datepicker-factory.js') ?>"></script> | |
| }; | |
| } | |
| function validateHasMessagePoint() { | |
| return messagePoint > 0; | |
| } | |
| function validateMessagePoint() { | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked')?.value ?? 'SMS'; | |
| const expectCount = getExpectTargetCount(); | |
| let requiredPoint; | |
| switch (sendMethod) { | |
| case 'SMS': | |
| const messageLength = window.messageInput?.calculateLength(window.messageInput.getValue()) ?? 0; | |
| requiredPoint = messageLength > 90 ? lmsPointEach * expectCount : smsPointEach * expectCount; | |
| break; | |
| case 'ALIMTALK': | |
| requiredPoint = kakaoAlrimtalkPointEach * expectCount; | |
| break; | |
| case 'FRIENDTALK': | |
| requiredPoint = kakaoFriendtalkPointEach * expectCount; | |
| break; | |
| default: | |
| requiredPoint = 1; | |
| } | |
| return messagePoint >= requiredPoint; | |
| } | |
| function generateSendSchedulePayload() { | |
| const sendCycle = document.querySelector('#sendCycle').value; | |
| const [repeatStartDate, repeatEndDate] = [...document.querySelectorAll('input[name="repeatDate[]"]')].map(el => el.value); | |
| $button.prop('disabled', false); | |
| }; | |
| if (!messageConfig.hasCallNumber) { | |
| return fail('발신번호가 없어 발송할 수 없습니다.', '설정에서 발신번호 등록 후 발송 시도를 해주세요.'); | |
| } | |
| if (!validateHasMessagePoint()) { | |
| return fail('메시지 포인트를 충전해 주세요'); | |
| } | |
| if (!validateMessagePoint()) { | |
| return fail('포인트가 부족하여 발송할 수 없습니다.', '발송 대상 인원보다 포인트가 적어 발송이 불가합니다. <br /> 충전 후 다시 시도해주세요.'); | |
| } | |
| const result = validateSendMethodContent(); | |
| if (!result.success) { | |
| } | |
| // 대체 메시지 미작성 시 본문 치환코드 변환해 자동 세팅 | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked').value; | |
| const mainContents = window.messageInput?.getValue() ?? ''; | |
| if (enableAlternative(sendMethod) && (window.alternativeMessageInput?.getValue() ?? '') === '') { | |
| const allKeys = [ | |
변경 후crm_recipe_send.php
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/jquery/jquery.multi_select_box.js') ?>"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/mobile_message_replace_code.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/message_send_common.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.useNcdsDialog(); | |
| SmsSendGate.applyRestrictOnAjax('<?= gd_isset($smsSendRestrictType, '') ?>', { | |
| tooltip: '<?= gd_isset($smsSendRestrictTooltip, '') ?>' | |
| }); | |
| SmsSendGate.bindClickGate({ | |
| message: '<?= gd_isset($smsSendRestrictAlertMessage, '') ?>', | |
| subMessage: '<?= gd_isset($smsSendRestrictAlertSubMessage, '') ?>', | |
| selectors: ['[data-send-tab-type="ALTERNATIVE"]', '[data-preview-message-type="ALTERNATIVE"]'] | |
| }); | |
| }); | |
| </script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_status.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-checkbox-group/ncds-checkbox-group.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-datepicker-factory/ncds-datepicker-factory.js') ?>"></script> | |
| }; | |
| } | |
| function generateSendSchedulePayload() { | |
| const sendCycle = document.querySelector('#sendCycle').value; | |
| const [repeatStartDate, repeatEndDate] = [...document.querySelectorAll('input[name="repeatDate[]"]')].map(el => el.value); | |
| $button.prop('disabled', false); | |
| }; | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked').value; | |
| const smsIncluded = sendMethod === 'SMS' || enableAlternative(sendMethod); | |
| try { | |
| await SmsSendGate.ensure({ | |
| requiredPoint: getRequiredPoint(), | |
| smsIncluded | |
| }); | |
| } catch (e) { | |
| $button.prop('disabled', false); | |
| return; | |
| } | |
| const result = validateSendMethodContent(); | |
| if (!result.success) { | |
| } | |
| // 대체 메시지 미작성 시 본문 치환코드 변환해 자동 세팅 | |
| const mainContents = window.messageInput?.getValue() ?? ''; | |
| if (enableAlternative(sendMethod) && (window.alternativeMessageInput?.getValue() ?? '') === '') { | |
| const allKeys = [ | |
변경 전layer_message_template_approval_history.php
변경 후layer_message_template_approval_history.php
| <div class="modal-dialog__content"> | |
| <div class="ncua-table ncua-table--horizontal"> | |
| <table> | |
| <colgroup> | |
| <col width="60px" /> | |
| <col width="180px" /> | |
| <col /> | |
| <col /> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><div class="ncua-align-center">번호</div></th> | |
| <th><div class="ncua-align-center">처리 일시</div></th> | |
| <th><div class="ncua-align-center">처리 내용</div></th> | |
| <th><div class="ncua-align-center">처리자</div></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php if (empty($histories)): ?> | |
| <tr> | |
| <td colspan="4" height="50" class="no-data"><div>조회된 승인 이력이 없습니다.</div></td> | |
| </tr> | |
| <?php else: ?> | |
| <?php foreach ($histories as $history): ?> | |
| <tr> | |
| <td><div class="ncua-align-center"><?= gd_htmlspecialchars($history['number']) ?></div></td> | |
| <td><div class="ncua-align-center"><?= gd_htmlspecialchars($history['processedAt']) ?></div></td> | |
| <td><div><?= gd_htmlspecialchars($history['eventName']) ?></div></td> | |
| <td><div><?= gd_htmlspecialchars($history['processedBy']) ?></div></td> | |
| </tr> | |
| <?php endforeach; ?> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
변경 전layer_mobile_message_performance_detail.php
| <div> | |
| 발송 성과 상세 contents | |
| </div> | |
| <script type="text/javascript"> | |
| </script> | |
변경 후layer_mobile_message_performance_detail.php
변경 전layer_mobile_message_performance_summary.php
| <div> | |
| 항목별 집계 기준 contents | |
| </div> | |
| <script type="text/javascript"> | |
| </script> | |
변경 후layer_mobile_message_performance_summary.php
변경 전layer_save_as_template.php
| <script defer src="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/charcount.js')?>"></script> | |
| <link rel="stylesheet" href="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/css/crm/layer-save-as-template.css')?>"> | |
| <div class="modal-dialog__content layer-save-as-template"> | |
| <div class="ncua-table ncua-table--vertical"> | |
| <table> | |
| <tbody> | |
| <tr> | |
| <th><div>카테고리</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag"> | |
| <option value="">카테고리 선택</option> | |
| <option value="1">Option 1</option> | |
| <option value="2">Option 2</option> | |
| <option value="3">Option 3</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th><div>제목</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs ncua-input--full-width-with-count"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="10" type="text" placeholder="제목을 입력하세요" class="layer-template-title" data-charcount-key="layerTemplateTitle" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="layerTemplateTitle"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/10</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="modal-dialog__footer"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-layer-close"> | |
| <span class="ncua-btn__label">저장</span> | |
| </button> | |
| </div> | |
| <script type="text/javascript"> | |
| const charCountManager = createCharCountManager({ | |
| targetClasses: ['ncua-title'], | |
| }); | |
| charCountManager.init(); | |
| </script> | |
변경 후layer_save_as_template.php
변경 전layer_update_kakao_template.php
| <div class="modal-dialog__content layer_update_kakao_template"> | |
| <div class="ncua-table ncua-table--vertical"> | |
| <table> | |
| <tbody> | |
| <tr> | |
| <th class="ncua-required"><div>구분</div></th> | |
| <td> | |
| <div class="ncua-flex-gap"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">주문/배송</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">정기결제(배송)</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">선물하기</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">회원</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">게시판</span> | |
| </label> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="ncua-required"><div>템플릿 카테고리</div></th> | |
| <td> | |
| <div class="ncua-flex-gap"> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag"> | |
| <option value="">대분류</option> | |
| <option value="1">Option 1</option> | |
| <option value="2">Option 2</option> | |
| <option value="3">Option 3</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class="ncua-select__tag" disabled> | |
| <option value="">중분류</option> | |
| <option value="1">Option 1</option> | |
| <option value="2">Option 2</option> | |
| <option value="3">Option 3</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="ncua-required"><div>템플릿 제목</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs "> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs ncua-input-width-520"> | |
| <input maxlength="10" type="text" placeholder="제목을 입력하세요" class="kakao-template-title" data-charcount-key="kakaoTemplateTitle" /></div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoTemplateTitle"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/10</span> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">템플릿 제목을 입력해 주세요.</span> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th><div>보안 템플릿 설정</div></th> | |
| <td> | |
| <div> | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" checked="" name="demo-switch" /> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="demo-switch" /> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="content-border-box"> | |
| <div class="template_type"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">메시지형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">강조표기형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">이미지형</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="templateType" /> | |
| </span> | |
| <span class="ncua-radio-field__text">아이템리스트형</span> | |
| </label> | |
| </div> | |
| <div class="template_content ncua-card__body-block-section-wrap"> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs">본문 입력</div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea kakao-template-textarea-content" placeholder="템플릿 본문을 작성해 주세요." maxlength="1000"></textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <div class="ncua-hint-text">본문을 입력해 주세요.</div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /1000 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title--xs" data-tooltip-seq="008">사용 가능한 변수</div> | |
| <div class="chip-selector"> | |
| <div class="ncua-select ncua-select--xs"> | |
| <div class="ncua-select__content"> | |
| <select class=" ncua-select__tag"> | |
| <option value="1">회원</option> | |
| <option value="2">게시판</option> | |
| <option value="3">통계</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="chip-button-wrap"> | |
| <button class="chip-button"><span class="chip-button-text">{rc_mallNm} 쇼핑몰 명/상점명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{memId} 회원 아이디</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{memNm} 회원명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{sleepScheduleDt} 휴면회원 전환 예정 일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_scheduleDt} 일반 회원 전환 일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{smsAgreementDt} SMS 수신동의일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{mailAgreementDt}메일 수신동의일</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{mileage} 보유한 마일리지</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{deposit}보유한 예치금</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{groupNm} 회원등급</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_mileage} 보유한 마일리지</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_deposit}보유한 예치금</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{rc_certificationCode} 인증코드</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{wriNm} 작성자</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{orderNo} 주문번호</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{orderName} 주문명</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{settlePrice} 결제금액</span></button> | |
| <button class="chip-button"><span class="chip-button-text">{bankAccount} 입금계좌번호</span></button> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title-wrap has-checkbox"> | |
| <span class="ncua-card__body-title--xs">부가 정보 문구</span> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" /> | |
| </span> | |
| <span class="ncua-checkbox-field__text">추가</span> | |
| </label> | |
| </div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea kakao-template-textarea-add-info" placeholder="템플릿 본문을 작성해 주세요." maxlength="200"></textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <div class="ncua-hint-text destructive">본문을 입력해 주세요.</div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /200 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section channel_add_message"> | |
| <div class="ncua-card__body-title-wrap has-checkbox"> | |
| <span class="ncua-card__body-title--xs">채널 추가 문구(고정 내용)</span> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" /> | |
| </span> | |
| <span class="ncua-checkbox-field__text">추가</span> | |
| </label> | |
| </div> | |
| <div class="ncua-input ncua-input--textarea ncua-input--textarea--xs"> | |
| <textarea class="ncua-input__textarea" maxlength="36" readonly>채널 추가하고 이 채널의 광고와 마케팅을 카카오톡으로 받기</textarea> | |
| <div class="ncua-input__text-count-wrap"> | |
| <div class="ncua-hint-text"></div> | |
| <div class="ncua-input__text-count"> | |
| <span class="ncua-input__text-count-text"> | |
| <span class="ncua-input__text-count-text-count">0</span> | |
| /36 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="ncua-card__body-block-section"> | |
| <div class="ncua-card__body-title-wrap"> | |
| <span class="ncua-card__body-title--xs">버튼</span> | |
| <div> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray auto-send-icon auto-send-icon--plus"> | |
| <span class="ncua-btn__label">페이지 링크 추가</span> | |
| </button> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray auto-send-icon auto-send-icon--plus"> | |
| <span class="ncua-btn__label">배송 조회 추가</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="ncua-table ncua-table--horizontal"> | |
| <table> | |
| <colgroup> | |
| <col width="79px"> | |
| <col width="auto"> | |
| <col width="auto"> | |
| <col width="88px"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><div class="ncua-align-center">타입</div></th> | |
| <th class="ncua-required"><div>버튼명</div></th> | |
| <th class="ncua-required"><div>URL</div></th> | |
| <th><div class="ncua-align-center">관리</div></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td> | |
| <div class="ncua-align-center">페이지 링크</div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs ncua-input--full-width-with-count"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="20" type="text" placeholder="버튼명을 입력해주세요" class="kakao-button-page-link" data-charcount-key="kakaoButtonPageLink" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoButtonPageLink"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/20</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">버튼명을 입력해 주세요.</span> | |
| </div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input type="text" placeholder="버튼명을 입력해주세요" /> | |
| </div> | |
| </div> | |
| </div> | |
| <span class="ncua-hint-text ncua-input__hint-text">외부 링크 입력 시 캠페인 통계가 집계되지 않습니다.</span> | |
| </div> | |
| </div> | |
| </td> | |
| <td> | |
| <div class="ncua-align-center"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray auto-send-icon auto-send-icon--minus"> | |
| <span class="ncua-btn__label">삭제</span> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="ncua-table ncua-table--horizontal"> | |
| <table> | |
| <colgroup> | |
| <col width="79px"> | |
| <col width="auto"> | |
| <col width="auto"> | |
| <col width="88px"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><div class="ncua-align-center">타입</div></th> | |
| <th class="ncua-required"><div>버튼명</div></th> | |
| <th class="ncua-required"><div>URL</div></th> | |
| <th><div class="ncua-align-center">관리</div></th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td> | |
| <div class="ncua-align-center">배송 조회</div> | |
| </td> | |
| <td> | |
| <div> | |
| <div class="ncua-input ncua-input--xs ncua-input--full-width-with-count"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="20" type="text" placeholder="버튼명을 입력해주세요" class="kakao-button-delivery-search" data-charcount-key="kakaoButtonDeliverySearch" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="kakaoButtonDeliverySearch"> | |
| <output class="ncua-input__field-text-count-current">0</output> | |
| <span>/20</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NOTE [저장] 유효성 검사 : 내용을 입력하지 않은 경우 --> | |
| <span class="ncua-hint-text destructive ncua-input__hint-text">버튼명을 입력해 주세요.</span> | |
| </div> | |
| </td> | |
| <td> | |
| <div> | |
| <p>본문에 택배사명과 송장번호가 포함되면 버튼이 카카오 배송조회 페이지로 연결됩니다. 단, 미지원 택배사는 조회되지 않습니다.</p> | |
| </div> | |
| </td> | |
| <td> | |
| <div class="ncua-align-center"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray minus-icon"> | |
| <span class="ncua-btn__label">삭제</span> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-dialog__footer"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray js-layer-close"> | |
| <span class="ncua-btn__label">취소</span> | |
| </button> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-layer-close"> | |
| <span class="ncua-btn__label">저장</span> | |
| </button> | |
| </div> | |
변경 후layer_update_kakao_template.php
변경 전layer_manage_sms_verification.php
| if (validatorInstance) validatorInstance.showErrors({ smsPassword: '10~16자의 영대/소문자, 숫자, 특수문자 중 2가지 이상의 조합을 사용해 주세요.' }); | |
| } | |
| } | |
| }); | |
| } | |
변경 후layer_manage_sms_verification.php
| if (validatorInstance) validatorInstance.showErrors({ smsPassword: '10~16자의 영대/소문자, 숫자, 특수문자 중 2가지 이상의 조합을 사용해 주세요.' }); | |
| } | |
| } | |
| }).fail(function (xhr) { | |
| NCDSAlert({ message: xhr.responseJSON?.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.', subMessage: xhr.responseJSON?.subMessage || '', iconType: 'error' }); | |
| }); | |
| } | |
변경 전message_config.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/switch.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/unsaved-changes-guard.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/ncds-jquery-validator.js') ?>"></script> | |
| <article class="ncua-content message-config-wrapper"> | |
| <form id="frmMessageConfig" action="message_config_ps.php" method="post"> | |
| <input type="hidden" name="mode" value="save"/> | |
| <td> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($smsAutoSendOverTypes as $smsAutoSendOverType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="smsAutoConfig[smsAutoSendOver]" value="<?= $smsAutoSendOverType->value ?>" <?= $smsAutoConfig->getSmsAutoSendOver() === $smsAutoSendOverType ? 'checked' : '' ?>/> | |
| </span> | |
| <td> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoFriendTalkConfig[smsAlternativeSendFlag]" <?= $kakaoFriendTalkConfig->getSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoFriendTalkConfig[smsAlternativeSendFlag]" <?= $kakaoFriendTalkConfig->getSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center kakao-alim-talk-manual-sms-alternative-send-setting <?= $kakaoAlimTalkConfig->getSender()->value === 'kakaoAlrim' ? 'display-none' : '' ?>"> | |
| 모바일 메시지 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoAlimTalkConfig[manualSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getManualSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoAlimTalkConfig[manualSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getManualSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 자동 알림 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoAlimTalkConfig[autoSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoAlimTalkConfig[autoSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="017">대체발송 처리 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($autoSmsAlternativeSendTypes as $autoSmsAlternativeSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoAlimTalkConfig[autoSmsAlternativeSendType]" value="<?= $autoSmsAlternativeSendType->name ?>" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendType() === $autoSmsAlternativeSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="014">90 byte 초과 시 메시지 전송 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($failedMessageSendTypes as $failedMessageSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoAlimTalkConfig[failedMessageSendType]" value="<?= $failedMessageSendType->name ?>" <?= $kakaoAlimTalkConfig->getFailedMessageSendType() === $failedMessageSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 모바일 메시지 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="myappPushConfig[manualSmsAlternativeSendFlag]" <?= $myappPushConfig->getManualSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="myappPushConfig[manualSmsAlternativeSendFlag]" <?= $myappPushConfig->getManualSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 자동 알림 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active"> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="myappPushConfig[autoSmsAlternativeSendFlag]" <?= $myappPushConfig->getAutoSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive"> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="myappPushConfig[autoSmsAlternativeSendFlag]" <?= $myappPushConfig->getAutoSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="013">대체발송 처리 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($autoSmsAlternativeSendTypes as $autoSmsAlternativeSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="myappPushConfig[autoSmsAlternativeSendType]" value="<?= $autoSmsAlternativeSendType->name ?>" <?= $myappPushConfig->getAutoSmsAlternativeSendType() === $autoSmsAlternativeSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="014">90 byte 초과 시 메시지 전송 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($failedMessageSendTypes as $failedMessageSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="myappPushConfig[failedMessageSendType]" value="<?= $failedMessageSendType->name ?>" <?= $myappPushConfig->getFailedMessageSendType() == $failedMessageSendType ? 'checked' : '' ?>/> | |
| </span> | |
변경 후message_config.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/switch.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/unsaved-changes-guard.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/ncds-jquery-validator.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.bindClickGate({ | |
| message: '<?= gd_isset($smsSendRestrictAlertMessage, '') ?>', | |
| subMessage: '<?= gd_isset($smsSendRestrictAlertSubMessage, '') ?>', | |
| selectors: <?= json_encode(array_values(array_filter([ | |
| '[data-sms-click-gate]', | |
| gd_isset($smsCallNumberManageRestricted, false) ? '#manageCallNumber' : null, | |
| ]))) ?> | |
| }); | |
| }); | |
| </script> | |
| <article class="ncua-content message-config-wrapper"> | |
| <form id="frmMessageConfig" action="message_config_ps.php" method="post"> | |
| <input type="hidden" name="mode" value="save"/> | |
| <td> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($smsAutoSendOverTypes as $smsAutoSendOverType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-sms-click-gate> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="smsAutoConfig[smsAutoSendOver]" value="<?= $smsAutoSendOverType->value ?>" <?= $smsAutoConfig->getSmsAutoSendOver() === $smsAutoSendOverType ? 'checked' : '' ?>/> | |
| </span> | |
| <td> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoFriendTalkConfig[smsAlternativeSendFlag]" <?= $kakaoFriendTalkConfig->getSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoFriendTalkConfig[smsAlternativeSendFlag]" <?= $kakaoFriendTalkConfig->getSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center kakao-alim-talk-manual-sms-alternative-send-setting <?= $kakaoAlimTalkConfig->getSender()->value === 'kakaoAlrim' ? 'display-none' : '' ?>"> | |
| 모바일 메시지 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoAlimTalkConfig[manualSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getManualSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoAlimTalkConfig[manualSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getManualSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 자동 알림 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="kakaoAlimTalkConfig[autoSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="kakaoAlimTalkConfig[autoSmsAlternativeSendFlag]" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="017">대체발송 처리 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($autoSmsAlternativeSendTypes as $autoSmsAlternativeSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-sms-click-gate> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoAlimTalkConfig[autoSmsAlternativeSendType]" value="<?= $autoSmsAlternativeSendType->name ?>" <?= $kakaoAlimTalkConfig->getAutoSmsAlternativeSendType() === $autoSmsAlternativeSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="014">90 byte 초과 시 메시지 전송 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($failedMessageSendTypes as $failedMessageSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-sms-click-gate> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="kakaoAlimTalkConfig[failedMessageSendType]" value="<?= $failedMessageSendType->name ?>" <?= $kakaoAlimTalkConfig->getFailedMessageSendType() === $failedMessageSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 모바일 메시지 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="myappPushConfig[manualSmsAlternativeSendFlag]" <?= $myappPushConfig->getManualSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="myappPushConfig[manualSmsAlternativeSendFlag]" <?= $myappPushConfig->getManualSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-gap-8 ncua-align-items-center"> | |
| 자동 알림 발송 | |
| <div class="ncua-switch ncua-switch--xs"> | |
| <label class="ncua-switch__option ncua-switch__option--left ncua-switch__option--active" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--left" type="radio" value="y" name="myappPushConfig[autoSmsAlternativeSendFlag]" <?= $myappPushConfig->getAutoSmsAlternativeSendFlag() === 'y' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용함</span> | |
| </label> | |
| <label class="ncua-switch__option ncua-switch__option--right ncua-switch__option--inactive" data-sms-click-gate> | |
| <input class="ncua-switch__radio ncua-switch__radio--right" type="radio" value="n" name="myappPushConfig[autoSmsAlternativeSendFlag]" <?= $myappPushConfig->getAutoSmsAlternativeSendFlag() === 'n' ? 'checked' : '' ?>/> | |
| <span class="ncua-switch__label">사용안함</span> | |
| </label> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="013">대체발송 처리 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($autoSmsAlternativeSendTypes as $autoSmsAlternativeSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-sms-click-gate> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="myappPushConfig[autoSmsAlternativeSendType]" value="<?= $autoSmsAlternativeSendType->name ?>" <?= $myappPushConfig->getAutoSmsAlternativeSendType() === $autoSmsAlternativeSendType ? 'checked' : '' ?>/> | |
| </span> | |
| <div class="ncua-flex ncua-align-items-center" data-tooltip-seq="014">90 byte 초과 시 메시지 전송 방법</div> | |
| <div class="ncua-radio-group ncua-flex ncua-flex-gap"> | |
| <?php foreach ($failedMessageSendTypes as $failedMessageSendType): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-sms-click-gate> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="myappPushConfig[failedMessageSendType]" value="<?= $failedMessageSendType->name ?>" <?= $myappPushConfig->getFailedMessageSendType() == $failedMessageSendType ? 'checked' : '' ?>/> | |
| </span> | |
변경 전message_template_result.php
| <td><div> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" name="templateCodeList[]" value="<?= $result->getTemplateCode() ?>" <?= ( $result->getBasicFl() === 'y') ? 'disabled' : '' ?> /> | |
| </span> | |
| </label> | |
| </div></td> | |
| <div class="ncua-search-result__actions"> | |
| <div class="ncua-search-result__actions-button"> | |
| <div class="ncua-search-result__actions-text"> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--destructive ncua-select-delete js-btn-delete">선택 삭제</button> | |
| </div> | |
| </div> | |
| <col> | |
| <col width="120px"> | |
| <col> | |
| <col> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <col> | |
| <col> | |
| <col> | |
| <?php endif; ?> | |
| <col width="180px"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| </div> | |
| </th> | |
| <th rowspan="2"><div data-tooltip-seq="007">템플릿코드</div></th> | |
| <th rowspan="2"><div data-tooltip-seq="008"><?= $sendMethod === 'kakao' ? '구분' : '카테고리' ?></div></th> | |
| <th rowspan="2"><div data-tooltip-seq="009">템플릿명</div></th> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <th colspan="2"><div data-tooltip-seq="010">알림톡 검수</div></th> | |
| <?php endif ?> | |
| <th rowspan="2"><div data-tooltip-seq="011">등록일</div></th> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <th rowspan="2"><div data-tooltip-seq="012">사용 중인 자동 알림</div></th> | |
| <?php endif ?> | |
| <th rowspan="2"><div data-tooltip-seq="013">관리</div></th> | |
| </tr> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <tr> | |
| <th><div data-tooltip-seq="014">검수 상태</div></th> | |
| <th><div data-tooltip-seq="015">검수 답변</div></th> | |
| <?php endif ?> | |
| </thead> | |
| <tbody> | |
| <?php if ( | |
| ($sendMethod === 'kakao' && ( | |
| ($search->getProvider() === 'bizm' && $config->getBizmFl() === 'n') || | |
| ($search->getProvider() === 'cloud' && $config->getCloudFl() === 'n') || | |
| ($config->getKakaoUseFl() === 'n') | |
| )) || | |
| ($sendMethod === 'myapp' && $config->getMyappUseFl() === 'n') | |
| ) :?> | |
| <tr> | |
| <td colspan="<?= ($sendMethod === 'kakao') ? '9' : '6' ?>" height="50" class="no-data"> | |
| <div class="message-config-info">이 발송수단은 현재 '사용 안함'으로 설정되어 있습니다.<br>템플릿 등록 및 메시지를 발송하려면 <a href="./message_config.php" class="ncua-link">메시지 설정</a>에서 사용을 활성화해 주세요.</div> | |
| </td> | |
| </tr> | |
| <?php elseif (empty($results)):?> | |
| <tr> | |
| <td colspan="<?= ($sendMethod === 'kakao') ? '9' : '6' ?>" height="50" class="no-data"><div>검색된 정보가 없습니다.</div></td> | |
| </tr> | |
| <?php else: ?> | |
| <?php foreach ($results as $result) : ?> | |
| <tr> | |
| <td><div> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <button type="button" class="ncua-left-align message-list-content-btn" onclick="check_template_contents('<?= $result->getTemplateCode() ?>', '<?= $sendMethod ?>', '<?= $search->getProvider() ?>')"><?= $result->getTemplateName() ?></button> | |
| </div> | |
| </td> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <td><div><?= $result->getTemplateStatus() ?></div></td> | |
| <?php if ($result->getTemplateStatus() === '반려') :?> | |
| <td><div><button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray" onclick="check_kakao_template_comment('<?= $search->getProvider() ?>','<?= $result->getTemplateCode() ?>')">답변 확인</button></div></td> | |
| <?php else: ?> | |
| <td><div>-</div></td> | |
| <?php endif; ?> | |
| <?php endif; ?> | |
| <td><div><?= date('Y. m. d', strtotime($result->getRegDt())) . ' ' . ((date('A', strtotime($result->getRegDt())) === 'AM' ? '오전' : '오후') . ' ' . date('h:i:s', strtotime($result->getRegDt()))) ?></div></td> | |
| <?php if ($sendMethod === 'kakao') :?> | |
| <?php if ($result->getAutoFl() === 'y') :?> | |
| <td> | |
| <div class="ncua-custom-tooltip"> | |
| <div data-tooltip-type="auto-notification-count"> | |
| <div class="ncua-left-align message-list-content-btn" ><?= $result->getAutoName() ?></div> | |
| <div class="ncua-custom-tooltip__content"> | |
| <?php foreach ($result->getAutoList() as $auto): ?> | |
| <p><?= $auto ?></p> | |
| <?php endforeach; ?> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| <?php else : ?> | |
| <td><div><?= $result->getAutoName() ?></div></td> | |
| <?php endif; ?> | |
| <?php endif; ?> | |
| <td><div class="ncua-flex-gap ncua-gap-4"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray js-btn-copy" value='<?= $result->getTemplateCode() ?>'>복제</button> | |
| <?php | |
| $canModify = $sendMethod !== 'kakao' || in_array($result->getTemplateStatus(), ['등록', '반려']); | |
| ?> | |
| <?php if ($canModify): ?> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray js-btn-modify" value='<?= $result->getTemplateCode() ?>'>수정</button> | |
| <?php endif; ?> | |
| </div></td> | |
| </tr> | |
| <?php endforeach ?> | |
| <?php endif; ?> | |
변경 후message_template_result.php
| <td><div> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| <input type="checkbox" name="templateCodeList[]" value="<?= $result->getTemplateCode() ?>" <?= $result->isSelectable() ? '' : 'disabled' ?> /> | |
| </span> | |
| </label> | |
| </div></td> | |
| <div class="ncua-search-result__actions"> | |
| <div class="ncua-search-result__actions-button"> | |
| <div class="ncua-search-result__actions-text"> | |
| <?php if ($isSms): ?> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray js-btn-approve">선택 승인</button> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary-gray js-btn-reject">선택 반려</button> | |
| <?php endif; ?> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--destructive ncua-select-delete js-btn-delete">선택 삭제</button> | |
| </div> | |
| </div> | |
| <col> | |
| <col width="120px"> | |
| <col> | |
| <?php if ($isKakao): ?> | |
| <col> | |
| <col> | |
| <?php endif; ?> | |
| <col> | |
| <col> | |
| <?php if ($isSms): ?> | |
| <col width="100px"> | |
| <?php endif; ?> | |
| <col width="180px"> | |
| <?php if ($isSms): ?> | |
| <col width="120px"> | |
| <?php endif; ?> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| </div> | |
| </th> | |
| <th rowspan="2"><div data-tooltip-seq="007">템플릿코드</div></th> | |
| <th rowspan="2"><div data-tooltip-seq="008"><?= $isKakao ? '구분' : '카테고리' ?></div></th> | |
| <th rowspan="2"><div data-tooltip-seq="009">템플릿명</div></th> | |
| <?php if ($isKakao): ?> | |
| <th colspan="2"><div data-tooltip-seq="010">알림톡 검수</div></th> | |
| <?php endif ?> | |
| <th rowspan="2"><div data-tooltip-seq="011">등록일</div></th> | |
| <th rowspan="2"><div data-tooltip-seq="012">사용 중인 자동 알림</div></th> | |
| <?php if ($isSms): ?> | |
| <th rowspan="2"><div data-tooltip-seq="014">검수 상태</div></th> | |
| <?php endif ?> | |
| <th rowspan="2"><div data-tooltip-seq="013">관리</div></th> | |
| <?php if ($isSms): ?> | |
| <th rowspan="2"><div data-tooltip-seq="016">승인 이력</div></th> | |
| <?php endif ?> | |
| </tr> | |
| <?php if ($isKakao): ?> | |
| <tr> | |
| <th><div data-tooltip-seq="014">검수 상태</div></th> | |
| <th><div data-tooltip-seq="015">검수 답변</div></th> | |
| <?php endif ?> | |
| </thead> | |
| <tbody> | |
| <?php $columnCount = 7 + ($isKakao ? 2 : 0) + ($isSms ? 2 : 0); ?> | |
| <?php if ($sendMethodDisabled): ?> | |
| <tr> | |
| <td colspan="<?= $columnCount ?>" height="50" class="no-data"> | |
| <div class="message-config-info">이 발송수단은 현재 '사용 안함'으로 설정되어 있습니다.<br>템플릿 등록 및 메시지를 발송하려면 <a href="./message_config.php" class="ncua-link">메시지 설정</a>에서 사용을 활성화해 주세요.</div> | |
| </td> | |
| </tr> | |
| <?php elseif (empty($results)): ?> | |
| <tr> | |
| <td colspan="<?= $columnCount ?>" height="50" class="no-data"><div>검색된 정보가 없습니다.</div></td> | |
| </tr> | |
| <?php else: ?> | |
| <?php foreach ($results as $result): ?> | |
| <tr> | |
| <td><div> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <button type="button" class="ncua-left-align message-list-content-btn" onclick="check_template_contents('<?= $result->getTemplateCode() ?>', '<?= $sendMethod ?>', '<?= $search->getProvider() ?>')"><?= $result->getTemplateName() ?></button> | |
| </div> | |
| </td> | |
| <?php if ($isKakao): ?> | |
| <td><div><?= $result->getTemplateStatus() ?></div></td> | |
| <?php if ($result->isRejected()): ?> | |
| <td><div><button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray" onclick="check_kakao_template_comment('<?= $search->getProvider() ?>','<?= $result->getTemplateCode() ?>')">답변 확인</button></div></td> | |
| <?php else: ?> | |
| <td><div>-</div></td> | |
| <?php endif; ?> | |
| <?php endif; ?> | |
| <td><div><?= date('Y. m. d', strtotime($result->getRegDt())) . ' ' . ((date('A', strtotime($result->getRegDt())) === 'AM' ? '오전' : '오후') . ' ' . date('h:i:s', strtotime($result->getRegDt()))) ?></div></td> | |
| <?php if ($result->getAutoFl() === 'y'): ?> | |
| <td><div> | |
| <button type="button" class="ncua-btn ncua-btn--xs has-underline ncua-btn--text-gray ncua-message-auto-alam"> | |
| <span class="ncua-btn__label"><?= $result->getAutoName() ?></span> | |
| <span class="ncua-tooltip-panel ncua-tooltip__bg ncua-tooltip__bg--white ncua-tooltip__bg--bottom"> | |
| <?php foreach ($result->getAutoList() as $auto): ?> | |
| <span class="ncua-tooltip__content"><?= $auto ?></span> | |
| <?php endforeach; ?> | |
| </span> | |
| </button> | |
| </div></td> | |
| <?php else : ?> | |
| <td><div><?= $result->getAutoName() ?></div></td> | |
| <?php endif; ?> | |
| <?php if ($isSms): ?> | |
| <td><div><?= $result->getTemplateStatus() ?></div></td> | |
| <?php endif; ?> | |
| <td><div class="ncua-flex-gap ncua-gap-4"> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray js-btn-copy" value='<?= $result->getTemplateCode() ?>'>복제</button> | |
| <?php if ($result->isModifiable()): ?> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray js-btn-modify" value='<?= $result->getTemplateCode() ?>'>수정</button> | |
| <?php endif; ?> | |
| </div></td> | |
| <?php if ($isSms): ?> | |
| <td> | |
| <div> | |
| <button type="button" class="ncua-btn ncua-btn--xxs ncua-btn--secondary-gray js-btn-detail" value="<?= $result->getTemplateCode() ?>">상세</button> | |
| </div> | |
| </td> | |
| <?php endif; ?> | |
| </tr> | |
| <?php endforeach ?> | |
| <?php endif; ?> | |
변경 전message_template_search.php
| <col> | |
| </colgroup> | |
| <tbody> | |
| <?php if ($sendMethod === 'kakao') : ?> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="001">제공사</div> | |
| </span> | |
| <span class="ncua-radio-field__text">고도몰</span> | |
| </label> | |
| <?php if ($isNewMall === false) : ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="provider" | |
| </div> | |
| </td> | |
| </tr> | |
| <?php | |
| $categories = [ | |
| ['value' => 'all', 'name' => '전체'], | |
| ]; | |
| if ($sendMethod === 'sms') { | |
| $categories = array_merge($categories, $searchCategory->getSmsCategory()); | |
| } elseif ($sendMethod === 'myapp') { | |
| $categories = array_merge($categories, $searchCategory->getMyappCategory()); | |
| } elseif ($search->getProvider() === 'cloud') { | |
| $categories = array_merge($categories, $searchCategory->getCloudCategory()); | |
| } elseif ($search->getProvider() === 'bizm') { | |
| $categories = array_merge($categories, $searchCategory->getBizmCategory()); | |
| } | |
| ?> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="003"><?= $sendMethod === 'kakao' ? '구분' : '카테고리' ?></div> | |
| </th> | |
| <td colspan="3"> | |
| <div class="ncua-flex-gap js-trigger-type"> | |
| <?php foreach ($categories as $cat): ?> | |
| <?php | |
| $checked = in_array($cat['value'], $search->getCategory()) ? 'checked' : ''; | |
| ?> | |
| </div> | |
| </td> | |
| </tr> | |
| <?php if ($sendMethod === 'kakao') : ?> | |
| <?php | |
| $statusOptions = [ | |
| ['value' => 'all', 'label' => '전체'], | |
| ]; | |
| if ($search->getProvider() === 'cloud') { | |
| $statusOptions = array_merge($statusOptions, $searchStatus->getCloudStatus()); | |
| } elseif ($search->getProvider() === 'bizm') { | |
| $statusOptions = array_merge($statusOptions, $searchStatus->getBizmStatus()); | |
| } | |
| ?> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="004">검수 상태</div> | |
| </th> | |
| <td> | |
| <div class="ncua-flex-gap js-trigger-status"> | |
| <?php foreach ($statusOptions as $option): ?> | |
| <?php if (!isset($option['provider']) || $search->getProvider() === $option['provider']): ?> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| </table> | |
| </div> | |
| <div class="ncua-flex ncua-justify-between ncua-align-items-start search-footer-container"> | |
| <?php if ($sendMethod === 'kakao') : ?> | |
| <p class="ncua-notice-info">블룸에이아이의 템플릿은 <a class="ncua-link" href="https://bizmsg.blumn.ai/template" | |
| target="_blank">블룸에이아이 파트너스 센터</a>에서 확인 가능합니다.</p> | |
| <?php else : ?> | |
변경 후message_template_search.php
| <col> | |
| </colgroup> | |
| <tbody> | |
| <?php if ($isKakao): ?> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="001">제공사</div> | |
| </span> | |
| <span class="ncua-radio-field__text">고도몰</span> | |
| </label> | |
| <?php if ($isNewMall === false): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="provider" | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="003"><?= $isKakao ? '구분' : '카테고리' ?></div> | |
| </th> | |
| <td colspan="3"> | |
| <div class="ncua-flex-gap js-trigger-type"> | |
| <?php foreach ($searchCategories as $cat): ?> | |
| <?php | |
| $checked = in_array($cat['value'], $search->getCategory()) ? 'checked' : ''; | |
| ?> | |
| </div> | |
| </td> | |
| </tr> | |
| <?php if ($isKakao || $isSms): ?> | |
| <tr> | |
| <th> | |
| <div data-tooltip-seq="004">검수 상태</div> | |
| </th> | |
| <td> | |
| <div class="ncua-flex-gap js-trigger-status"> | |
| <?php foreach ($searchStatusOptions as $option): ?> | |
| <?php if (!isset($option['provider']) || $search->getProvider() === $option['provider']): ?> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"> | |
| </table> | |
| </div> | |
| <div class="ncua-flex ncua-justify-between ncua-align-items-start search-footer-container"> | |
| <?php if ($isKakao): ?> | |
| <p class="ncua-notice-info">블룸에이아이의 템플릿은 <a class="ncua-link" href="https://bizmsg.blumn.ai/template" | |
| target="_blank">블룸에이아이 파트너스 센터</a>에서 확인 가능합니다.</p> | |
| <?php else : ?> | |
변경 전message_template.php
| if (hasBasic) { | |
| NCDSAlert({ | |
| message: '고도몰에서 제공하는 기본 템플릿은 수정할 수 없습니다.<br>복제 후 내용을 수정해 신규 템플릿으로 등록해 주세요.', | |
| iconType: 'error' | |
| }); | |
| return false; | |
| <div class="swiper ncua-horizontal-tab ncua-horizontal-tab--md ncua-horizontal-tab--underline-fill"> | |
| <div class="swiper-wrapper ncua-gap-8"> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="redirectWithParams('sendMethod','sms',false)" | |
| class="ncua-tab-button <?= ($sendMethod === 'sms') ? ' is-active' : '' ?>" data-html="true" | |
| data-content="SMS" data-placement="top">SMS</a> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="redirectWithParams('sendMethod','kakao',false)" | |
| class="ncua-tab-button <?= ($sendMethod === 'kakao') ? ' is-active' : '' ?>" data-html="true" | |
| data-content="카카오 알림톡" data-placement="top">카카오 알림톡</a> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="redirectWithParams('sendMethod','myapp',false)" | |
| class="ncua-tab-button <?= ($sendMethod === 'myapp') ? ' is-active' : '' ?>" data-html="true" | |
| data-content="마이앱(앱푸시)" data-placement="top">마이앱(앱푸시)</a> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </section> | |
| </article> | |
| <script type="text/javascript"> | |
| const start = new Date('1970-01-01'); | |
| const end = new Date(); // 오늘 날짜 | |
| NCDSAlert({ message, iconType: 'error' }); | |
| }); | |
| // 템플릿 코드별 autoFl 매핑 | |
| const templateAutoMap = { | |
| <?php foreach ($results as $item): ?> | |
| '<?= $item->getTemplateCode() ?>': '<?= $item->getAutoFl() ?>', | |
| <?php endforeach; ?> | |
| }; | |
| const templateBasicMap = { | |
| <?php foreach ($results as $item): ?> | |
| '<?= $item->getTemplateCode() ?>': '<?= $item->getBasicFl() ?>', | |
| <?php endforeach; ?> | |
| }; | |
| const searchData = JSON.parse('<?= json_encode($search->toArray(), JSON_UNESCAPED_UNICODE) ?>'); | |
| } | |
| function redirectWithParams(key, value, isAll = true) { | |
| const form = document.createElement('form'); | |
| form.method = 'POST'; | |
| form.action = ''; | |
| form.appendChild(createHiddenInput('page', 1)); | |
| form.appendChild(createHiddenInput(key, value)); | |
| document.body.appendChild(form); | |
| form.submit(); | |
| } | |
| form.submit(); | |
| } | |
| // 페이지 이동 | |
| function loadByPage(page) { | |
| const [key, value] = page.split('='); | |
| // 수정 버튼 (기본 템플릿 수정 불가 안내) | |
| if (target.classList.contains('js-btn-modify')) { | |
| const templateCode = target.value; | |
| const hasBasic = templateBasicMap[templateCode] === 'y'; | |
| if (hasBasic) { | |
| NCDSAlert({ | |
| return false; | |
| } | |
| const data = { | |
| mode: 'modify', | |
| sendMethod: '<?= $sendMethod ?>', | |
| } | |
| }); | |
| // 선택삭제 유효성 검사 | |
| $('#frmList').validate({ | |
| ignore: ':hidden', dialog: false, submitHandler: function (form) { | |
| const selectedCodes = Array.from(form.querySelectorAll('input[name="templateCodeList[]"]:checked')) | |
| .map(input => input.value); | |
| // 자동알림 사용 중인 템플릿 체크 | |
| const hasAutoAlrim = selectedCodes.some(code => { | |
| return templateAutoMap[code] === 'y'; | |
| }); | |
| if (hasAutoAlrim) { | |
| NCDSAlert({ | |
| message: '자동알림에 사용 중인 템플릿은 삭제할 수 없습니다.', | |
| iconType: 'error' | |
| }); | |
| return false; | |
| const data = { | |
| mode: mode, | |
| sendMethod: '<?= $sendMethod ?>', | |
| templateCodeList: selectedCodes, | |
| provider: '<?= $search->getProvider() ?>' | |
| } | |
| $.post('./message_template_ps.php', data, function (response) { | |
| if (response.success) { | |
| NCDSToast({message: '템플릿이 삭제되었습니다.', color: 'success'}); | |
| setTimeout(() => { | |
| redirectWithParams('page', 1, true); | |
| }, | |
| messages: { | |
| 'templateCode[]': { | |
| required: '선택하신 글이 없습니다.' | |
| }, | |
| }, | |
| const btnDelete = document.querySelector('.js-btn-delete'); | |
| if (btnDelete) { | |
| btnDelete.addEventListener('click', handleDeleteClick); | |
| } | |
| const code = '251219001'; | |
| // COS 가이드 초기화 | |
| if (window.GodoCosGuide) { | |
변경 후message_template.php
| if (hasBasic) { | |
| NCDSAlert({ | |
| message: '<?= $basicNotModifiableMessage ?>', | |
| iconType: 'error' | |
| }); | |
| return false; | |
| <div class="swiper ncua-horizontal-tab ncua-horizontal-tab--md ncua-horizontal-tab--underline-fill"> | |
| <div class="swiper-wrapper ncua-gap-8"> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="redirectWithParams('sendMethod','kakao',false)" | |
| class="ncua-tab-button <?= $isKakao ? ' is-active' : '' ?>" data-html="true" | |
| data-content="카카오 알림톡" data-placement="top">카카오 알림톡</a> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="redirectWithParams('sendMethod','myapp',false)" | |
| class="ncua-tab-button <?= $isMyapp ? ' is-active' : '' ?>" data-html="true" | |
| data-content="마이앱(앱푸시)" data-placement="top">마이앱(앱푸시)</a> | |
| </div> | |
| <div class="swiper-slide ncua-horizontal-tab__item"> | |
| <a href="javascript:" onclick="selectSmsTab()" | |
| class="ncua-tab-button <?= $isSms ? ' is-active' : '' ?>" data-html="true" | |
| data-content="SMS" data-placement="top">SMS</a> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </section> | |
| </article> | |
| <script type="text/javascript"> | |
| const start = new Date('1970-01-01'); | |
| const end = new Date(); // 오늘 날짜 | |
| NCDSAlert({ message, iconType: 'error' }); | |
| }); | |
| const templateMap = <?= json_encode($templateMap) ?>; | |
| const templateApprovalStatus = <?= json_encode($approvalStatusValues) ?>; | |
| const hasSmsApprovalPermission = <?= $hasSmsApprovalPermission ? 'true' : 'false' ?>; | |
| const searchData = JSON.parse('<?= json_encode($search->toArray(), JSON_UNESCAPED_UNICODE) ?>'); | |
| } | |
| function selectSmsTab() { | |
| const restrictAlertMessage = '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertMessage, '')) ?>'; | |
| if (restrictAlertMessage) { | |
| NCDSAlert({ | |
| message: restrictAlertMessage, | |
| subMessage: '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsSendRestrictAlertSubMessage, '')) ?>', | |
| iconType: 'error' | |
| }); | |
| return; | |
| } | |
| if (<?= json_encode($isSmsTabVisible) ?>) { | |
| redirectWithParams('sendMethod', 'sms', false, {smsTabReturnMethod: '<?= $sendMethod ?>'}); | |
| return; | |
| } | |
| NCDSAlert({ | |
| message: '<?= gd_htmlspecialchars_stripslashes($smsNoRoleMessage) ?>', | |
| subMessage: '<?= gd_htmlspecialchars_stripslashes(gd_isset($smsNoRoleSubMessage, '')) ?>', | |
| iconType: 'error' | |
| }); | |
| } | |
| function redirectWithParams(key, value, isAll = true, extraParams = {}) { | |
| const form = document.createElement('form'); | |
| form.method = 'POST'; | |
| form.action = ''; | |
| form.appendChild(createHiddenInput('page', 1)); | |
| form.appendChild(createHiddenInput(key, value)); | |
| Object.entries(extraParams).forEach(([k, v]) => form.appendChild(createHiddenInput(k, v))); | |
| document.body.appendChild(form); | |
| form.submit(); | |
| } | |
| form.submit(); | |
| } | |
| // 점유인증 완료 후 복귀 — POST 로만 유지되는 현재 탭·검색조건을 재요청한다 | |
| window.occupancyAuthReload = function () { | |
| redirectWithParams('sendMethod', '<?= gd_isset($requestedSendMethod, $sendMethod) ?>', true); | |
| }; | |
| // 페이지 이동 | |
| function loadByPage(page) { | |
| const [key, value] = page.split('='); | |
| // 수정 버튼 (기본 템플릿 수정 불가 안내) | |
| if (target.classList.contains('js-btn-modify')) { | |
| const templateCode = target.value; | |
| const hasBasic = templateMap[templateCode]?.basicFl === 'y'; | |
| if (hasBasic) { | |
| NCDSAlert({ | |
| return false; | |
| } | |
| if (templateMap[templateCode]?.approvalStatus === templateApprovalStatus.PENDING) { | |
| NCDSAlert({ | |
| message: '<?= $notModifiableMessage ?>', | |
| iconType: 'info' | |
| }); | |
| return false; | |
| } | |
| if ('<?= $sendMethod ?>' === 'sms' && templateMap[templateCode]?.usedFl === 'y') { | |
| NCDSAlert({ | |
| message: '<?= $linkedNotModifiableMessage ?>', | |
| iconType: 'info' | |
| }); | |
| return false; | |
| } | |
| const data = { | |
| mode: 'modify', | |
| sendMethod: '<?= $sendMethod ?>', | |
| } | |
| }); | |
| $('.js-btn-detail').on('click', function () { | |
| $.post('./layer_message_template_approval_history.php', {templateCode: this.value}, function (data) { | |
| ncds_layer_popup({ | |
| message: data, | |
| title: 'SMS 발송 템플릿 승인 이력', | |
| }); | |
| }); | |
| }); | |
| // 선택삭제 유효성 검사 | |
| $('#frmList').validate({ | |
| ignore: ':hidden', dialog: false, submitHandler: function (form) { | |
| const selectedCodes = Array.from(form.querySelectorAll('input[name="templateCodeList[]"]:checked')) | |
| .map(input => input.value); | |
| const deletableCodes = selectedCodes.filter(code => templateMap[code]?.usedFl !== 'y'); | |
| const usedCount = selectedCodes.length - deletableCodes.length; | |
| if (deletableCodes.length === 0) { | |
| NCDSAlert({ | |
| message: '<?= $usedNotDeletableMessage ?>', | |
| iconType: 'error' | |
| }); | |
| return false; | |
| const data = { | |
| mode: mode, | |
| sendMethod: '<?= $sendMethod ?>', | |
| templateCodeList: deletableCodes, | |
| provider: '<?= $search->getProvider() ?>' | |
| } | |
| $.post('./message_template_ps.php', data, function (response) { | |
| if (response.success) { | |
| const excludedCount = usedCount + (response.mappedCount || 0); | |
| if (excludedCount > 0) { | |
| NCDSAlert({ | |
| message: `자동 알림 발송 목록 ${excludedCount}건 제외 ${response.deletedCount}건의 템플릿이 삭제되었습니다.`, | |
| iconType: 'warning', | |
| onClose: () => redirectWithParams('page', 1, true) | |
| }); | |
| return; | |
| } | |
| NCDSToast({message: response.message, color: 'success'}); | |
| setTimeout(() => { | |
| redirectWithParams('page', 1, true); | |
| }, | |
| messages: { | |
| 'templateCode[]': { | |
| required: '선택하신 템플릿이 없습니다.' | |
| }, | |
| }, | |
| const btnDelete = document.querySelector('.js-btn-delete'); | |
| if (btnDelete) { | |
| btnDelete.addEventListener('click', handleDeleteClick); | |
| const syncDeleteButton = () => { | |
| btnDelete.disabled = document.querySelectorAll('input[name="templateCodeList[]"]:checked').length === 0; | |
| }; | |
| // 전체선택 체크박스는 공용 스크립트가 각 행을 갱신한 뒤 change 를 올리므로 위임으로 함께 받는다 | |
| document.addEventListener('change', (e) => { | |
| if (e.target.matches('input[name="templateCodeList[]"], .ncua-checkall')) { | |
| syncDeleteButton(); | |
| } | |
| }); | |
| syncDeleteButton(); | |
| } | |
| const handleApprovalClick = (mode) => { | |
| if (!hasSmsApprovalPermission) { | |
| NCDSAlert({message: '<?= $smsNoApprovalPermissionMessage ?>', iconType: 'error'}); | |
| return; | |
| } | |
| const actionName = mode === 'approve' ? '승인' : '반려'; | |
| const selectedCodes = Array.from(document.querySelectorAll('input[name="templateCodeList[]"]:checked')) | |
| .map(input => input.value); | |
| if (selectedCodes.length === 0) { | |
| NCDSAlert({message: '선택하신 템플릿이 없습니다.', iconType: 'error'}); | |
| return; | |
| } | |
| const pendingCodes = selectedCodes.filter(code => templateMap[code]?.approvalStatus === templateApprovalStatus.PENDING); | |
| if (pendingCodes.length === 0) { | |
| if (selectedCodes.length === 1) { | |
| NCDSAlert({ | |
| message: '이미 승인 또는 반려 처리가 된 신청 건입니다.<br>페이지 새로고침 후 확인해 주세요.', | |
| iconType: 'error', | |
| callback: () => location.reload() | |
| }); | |
| return; | |
| } | |
| NCDSAlert({ | |
| message: `선택한 템플릿 중 ${actionName} 가능한 템플릿이 없습니다. 검수중 상태의 템플릿만 처리할 수 있습니다.`, | |
| iconType: 'error' | |
| }); | |
| return; | |
| } | |
| const approvedCount = selectedCodes.filter(code => templateMap[code]?.approvalStatus === templateApprovalStatus.APPROVED).length; | |
| const rejectedCount = selectedCodes.filter(code => templateMap[code]?.approvalStatus === templateApprovalStatus.REJECTED).length; | |
| const tailMessage = mode === 'approve' | |
| ? '승인한 템플릿은 승인 취소가 불가합니다.' | |
| : '반려한 템플릿은 수정 후 재검수가 가능합니다.'; | |
| NCDSConfirm({ | |
| message: `선택한 템플릿을 ${actionName}하시겠습니까?`, | |
| subMessage: `선택한 ${selectedCodes.length}건 중 검수중 ${pendingCodes.length}건을 ${actionName}합니다. (승인 ${approvedCount}건·반려 ${rejectedCount}건 제외). ${tailMessage}`, | |
| callback: function (result) { | |
| if (!result) return false; | |
| const data = { | |
| mode: mode, | |
| sendMethod: '<?= $sendMethod ?>', | |
| templateCodeList: pendingCodes, | |
| provider: '<?= $search->getProvider() ?>' | |
| }; | |
| $.post('./message_template_ps.php', data, function (response) { | |
| if (response.noPermission) { | |
| NCDSAlert({ | |
| message: response.message, | |
| iconType: 'error', | |
| callback: () => location.reload() | |
| }); | |
| return; | |
| } | |
| if (!response.success) { | |
| NCDSAlert({ | |
| message: response.message || `${actionName} 처리에 실패했습니다.`, | |
| iconType: 'error' | |
| }); | |
| return; | |
| } | |
| NCDSToast({ | |
| message: response.message, | |
| color: 'success' | |
| }); | |
| setTimeout(() => { | |
| redirectWithParams('page', 1, true); | |
| }, 3000); | |
| }, 'json'); | |
| }, | |
| }); | |
| }; | |
| document.querySelector('.js-btn-approve')?.addEventListener('click', () => handleApprovalClick('approve')); | |
| document.querySelector('.js-btn-reject')?.addEventListener('click', () => handleApprovalClick('reject')); | |
| const code = '251219001'; | |
| // COS 가이드 초기화 | |
| if (window.GodoCosGuide) { | |
변경 전basic_config.php
| <th><div>발송수단</div></th> | |
| <td> | |
| <div class="ncua-flex ncua-flex-gap ncua-custom-tooltip <?= in_array($request->getMode(), ['copy', 'modify']) ? 'is-edit-mode' : '' ?>"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-tooltip-type="disabled-send-method"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="sms" <?= ($request->getSendMethod() === 'sms') ? 'checked="checked"' : '' ?> <?= in_array($request->getMode(), ['copy', 'modify']) ? 'disabled' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">SMS/LMS</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-tooltip-type="disabled-send-method"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="kakao" <?= ($request->getSendMethod() === 'kakao') ? 'checked="checked"' : '' ?> <?= (in_array($request->getMode(), ['copy', 'modify']) || $config->getKakaoUseFl() === 'n' || ($config->getCloudFl() === 'n' && $config->getBizmFl() === 'n')) ? 'disabled' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">마이앱 (앱푸시)</span> | |
| </label> | |
| </div> | |
| </td> | |
| </tr> | |
| $templateTitleMaxLengthAttr = ''; | |
| } elseif ($request->getSendMethod() === 'kakao' && $request->getProvider() === 'bizm') { | |
| $templateTitleMaxLengthAttr = 'maxlength="30"'; | |
| } else { | |
| $templateTitleMaxLengthAttr = 'maxlength="10"'; | |
| } | |
| <output class="ncua-input__field-text-count-current"><?= mb_strlen($templateTitleValue) ?></output><span class="js-template-title-max-length"><?= $templateTitleMaxLength ? '/' . $templateTitleMaxLength : '' ?></span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
변경 후basic_config.php
| <th><div>발송수단</div></th> | |
| <td> | |
| <div class="ncua-flex ncua-flex-gap ncua-custom-tooltip <?= in_array($request->getMode(), ['copy', 'modify']) ? 'is-edit-mode' : '' ?>"> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-tooltip-type="disabled-send-method"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="kakao" <?= ($request->getSendMethod() === 'kakao') ? 'checked="checked"' : '' ?> <?= (in_array($request->getMode(), ['copy', 'modify']) || $config->getKakaoUseFl() === 'n' || ($config->getCloudFl() === 'n' && $config->getBizmFl() === 'n')) ? 'disabled' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">마이앱 (앱푸시)</span> | |
| </label> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text" data-tooltip-type="disabled-send-method" data-tooltip-text="<?= gd_htmlspecialchars(gd_isset($smsSendMethodRestrictTooltip, '')) ?>"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="sms" <?= ($request->getSendMethod() === 'sms') ? 'checked="checked"' : '' ?> <?= (in_array($request->getMode(), ['copy', 'modify']) || gd_isset($smsSendMethodRestricted, false)) ? 'disabled' : '' ?> /> | |
| </span> | |
| <span class="ncua-radio-field__text">SMS/LMS</span> | |
| </label> | |
| </div> | |
| </td> | |
| </tr> | |
| $templateTitleMaxLengthAttr = ''; | |
| } elseif ($request->getSendMethod() === 'kakao' && $request->getProvider() === 'bizm') { | |
| $templateTitleMaxLengthAttr = 'maxlength="30"'; | |
| } elseif (mb_strlen($templateTitleValue) > 10) { | |
| $templateTitleMaxLengthAttr = ''; | |
| } else { | |
| $templateTitleMaxLengthAttr = 'maxlength="10"'; | |
| } | |
| <output class="ncua-input__field-text-count-current"><?= mb_strlen($templateTitleValue) ?></output><span class="js-template-title-max-length"><?= $templateTitleMaxLength ? '/' . $templateTitleMaxLength : '' ?></span> | |
| </div> | |
| </div> | |
| <span class="ncua-hint-text ncua-input__hint-text">템플릿 제목은 템플릿을 구분하기 위한 항목이며, 실제 발송되는 내용에는 포함되지 않습니다.</span> | |
| </div> | |
| </div> | |
| </td> | |
변경 전message_template_register.php
| const sendMethodRadios = document.querySelectorAll('input[name="sendMethod"]'); | |
| sendMethodRadios.forEach((radio) => { | |
| radio.addEventListener('change', (event) => { | |
| const selectedValue = event.target.value; | |
| // 이전 발송 수단의 필드 값 저장 | |
| if (previousSendMethod) { | |
| saveFieldValues(previousSendMethod); | |
| if (selectedValue === 'sms') { | |
| updateSmsMessageHint(smsMessageInput.getValue()); | |
| } | |
| }); | |
| }); | |
| /** | |
| * 제공사 라디오 버튼 변경 시 조건부 표시 업데이트 | |
| // 제목 입력 — 이모지 제거, 40bytes 초과분 자동 삭제 (byte 기준은 본문 MessageInput 과 동일) | |
| const handleSmsTitleInput = () => { | |
| const input = getSmsTitleInput(); | |
| let value = smsMessageInput.removeEmoji(input.value).value; | |
| value = smsMessageInput.truncateBytes(value, SMS_TITLE_MAX_BYTES); | |
| if (input.value !== value) { | |
| input.value = value; | |
| } | |
| document.querySelector('[data-sms-title-current]').textContent = smsMessageInput.getByteLength(value); | |
| updateSmsTitlePreview(); | |
| getSmsTitleInput().addEventListener('input', handleSmsTitleInput); | |
| handleSmsTitleInput(); | |
| const smsVariableSelector = GodoUIModule.render({ | |
| type: 'ChipSelector', | |
| target: '#sms-variable-selector-container', | |
| return true; | |
| }; | |
| /** | |
| * 검증 에러 표시 (공통) | |
| */ | |
| }, | |
| }, | |
| invalidHandler: function (event, validator) { | |
| const templateTypeRadio = document.querySelector('input[name="templateType"]:checked'); | |
| // IMAGE 타입: 상단 이미지 미등록 시 highlight + alert (ITEM_LIST는 submitHandler에서 별도 검증) | |
변경 후message_template_register.php
| const sendMethodRadios = document.querySelectorAll('input[name="sendMethod"]'); | |
| sendMethodRadios.forEach((radio) => { | |
| radio.addEventListener('change', (event) => { | |
| switchSendMethod(event.target.value); | |
| }); | |
| }); | |
| const switchSendMethod = (selectedValue) => { | |
| // 이전 발송 수단의 필드 값 저장 | |
| if (previousSendMethod) { | |
| saveFieldValues(previousSendMethod); | |
| if (selectedValue === 'sms') { | |
| updateSmsMessageHint(smsMessageInput.getValue()); | |
| } | |
| }; | |
| /** | |
| * 제공사 라디오 버튼 변경 시 조건부 표시 업데이트 | |
| // 제목 입력 — 이모지 제거, 40bytes 초과분 자동 삭제 (byte 기준은 본문 MessageInput 과 동일) | |
| const handleSmsTitleInput = () => { | |
| const input = getSmsTitleInput(); | |
| const stripped = smsMessageInput.removeEmoji(input.value, input.selectionStart); | |
| const value = smsMessageInput.truncateBytes(stripped.value, SMS_TITLE_MAX_BYTES); | |
| if (input.value !== value) { | |
| const caret = Math.min(stripped.cursorPos, value.length); | |
| input.value = value; | |
| input.setSelectionRange(caret, caret); | |
| } | |
| document.querySelector('[data-sms-title-current]').textContent = smsMessageInput.getByteLength(value); | |
| updateSmsTitlePreview(); | |
| getSmsTitleInput().addEventListener('input', handleSmsTitleInput); | |
| handleSmsTitleInput(); | |
| const templateTitleInput = document.querySelector('.js-template-title'); | |
| if (templateTitleInput) { | |
| templateTitleInput.addEventListener('input', () => { | |
| const stripped = smsMessageInput.removeEmoji(templateTitleInput.value, templateTitleInput.selectionStart); | |
| if (templateTitleInput.value === stripped.value) return; | |
| templateTitleInput.value = stripped.value; | |
| templateTitleInput.setSelectionRange(stripped.cursorPos, stripped.cursorPos); | |
| templateTitleInput.dispatchEvent(new Event('keyup', {bubbles: true})); | |
| }); | |
| } | |
| const smsVariableSelector = GodoUIModule.render({ | |
| type: 'ChipSelector', | |
| target: '#sms-variable-selector-container', | |
| return true; | |
| }; | |
| const scrollToTopmostInvalidField = (validator) => { | |
| const fields = (validator.errorList || []) | |
| .map(error => error.element) | |
| .filter(element => element && element.offsetParent !== null); | |
| if (fields.length < 2) { | |
| return; | |
| } | |
| const topmost = fields.reduce((top, element) => ( | |
| element.getBoundingClientRect().top < top.getBoundingClientRect().top ? element : top | |
| )); | |
| topmost.scrollIntoView({behavior: 'smooth', block: 'center'}); | |
| }; | |
| /** | |
| * 검증 에러 표시 (공통) | |
| */ | |
| }, | |
| }, | |
| invalidHandler: function (event, validator) { | |
| scrollToTopmostInvalidField(validator); | |
| const templateTypeRadio = document.querySelector('input[name="templateType"]:checked'); | |
| // IMAGE 타입: 상단 이미지 미등록 시 highlight + alert (ITEM_LIST는 submitHandler에서 별도 검증) | |
변경 전template_contents_sms.php
| <ul> | |
| <li>URL 삽입 시 [단축 URL] 앱을 이용하시면 짧고 간단하게 URL을 사용하실 수 있습니다. <a href="https://apps.godo.co.kr/apps/1519" target="_blank" class="ncua-link-underline">바로가기</a></li> | |
| <li>제목은 LMS 발송 시에만 적용되는 선택 입력 항목으로, 미입력 시에는 ‘제목 없음’으로 발송되며 SMS 발송 시에는 포함되지 않습니다.</li> | |
| </ul> | |
| </div> | |
| </details> | |
변경 후template_contents_sms.php
| <ul> | |
| <li>URL 삽입 시 [단축 URL] 앱을 이용하시면 짧고 간단하게 URL을 사용하실 수 있습니다. <a href="https://apps.godo.co.kr/apps/1519" target="_blank" class="ncua-link-underline">바로가기</a></li> | |
| <li>제목은 LMS 발송 시에만 적용되는 선택 입력 항목으로, 미입력 시에는 ‘제목 없음’으로 발송되며 SMS 발송 시에는 포함되지 않습니다.</li> | |
| <li>등록/수정한 템플릿은 승인 후 발송 가능합니다.</li> | |
| </ul> | |
| </div> | |
| </details> | |
변경 전layer_kakao_friendtalk_content_setting.php
| kakaoFriendtalkTabSection.querySelector('input[name="friendtalkCampaignImageLink"]').value = carousel.imageLink; | |
| // 메시지 (message) | |
| window.messageInput.setValue(carousel.message); | |
| <?php if ($isRecipeContext): ?> | |
| // 레시피: 슬라이드 전환은 setValue 라 onInput 미발생 → 포인트 힌트·안내문구 직접 반영 | |
| window.messageInput.setHint(carousel.message ? friendtalkUnitPointHint(getCampaignMessageType()) : null); | |
| MobileMessageReplaceCode.updateExpireCodeGuide('#replaceCodeContainer', carousel.message); | |
| <?php endif; ?> | |
| } | |
| function renderMessageInput(campaignMessageType) { | |
| window.messageInput.clear(); | |
| window.messageInput.setHint(null); | |
| window.preview.reset(); | |
| kakaoFriendtalkButtonSection.querySelectorAll('.ncua-table').forEach(t => t.remove()); | |
| kakaoFriendtalkButtonSection.querySelector('.empty-coupon-data').classList.remove('display-none'); | |
| window.preview.reset(); | |
| checkTable(); | |
| // 아이템 리스트 테이블 row 수 초기화 | |
| } | |
| function updateDefaultPreview(campaignType) { | |
| switch (campaignType) { | |
| case 'TEXT': | |
| setDefaultPreview(['TEXT']); | |
변경 후layer_kakao_friendtalk_content_setting.php
| kakaoFriendtalkTabSection.querySelector('input[name="friendtalkCampaignImageLink"]').value = carousel.imageLink; | |
| // 메시지 (message) | |
| window.messageInput?.setValue(carousel.message); | |
| <?php if ($isRecipeContext): ?> | |
| // 레시피: 슬라이드 전환은 setValue 라 onInput 미발생 → 포인트 힌트·안내문구 직접 반영 | |
| window.messageInput?.setHint(carousel.message ? friendtalkUnitPointHint(getCampaignMessageType()) : null); | |
| MobileMessageReplaceCode.updateExpireCodeGuide('#replaceCodeContainer', carousel.message); | |
| <?php endif; ?> | |
| } | |
| function renderMessageInput(campaignMessageType) { | |
| if (!window.messageInput || !window.preview) return; | |
| window.messageInput.clear(); | |
| window.messageInput.setHint(null); | |
| window.preview.reset(); | |
| kakaoFriendtalkButtonSection.querySelectorAll('.ncua-table').forEach(t => t.remove()); | |
| kakaoFriendtalkButtonSection.querySelector('.empty-coupon-data').classList.remove('display-none'); | |
| window.preview?.reset(); | |
| checkTable(); | |
| // 아이템 리스트 테이블 row 수 초기화 | |
| } | |
| function updateDefaultPreview(campaignType) { | |
| if (!window.preview) return; | |
| switch (campaignType) { | |
| case 'TEXT': | |
| setDefaultPreview(['TEXT']); | |
변경 전layer_mobile_send_save_sms_template.php
| <link rel="stylesheet" href="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/css/crm/layer-save-as-template.css')?>"> | |
| <div class="modal-dialog__content layer-save-as-template"> | |
| <input type="hidden" id="templateContents" value="<?= htmlspecialchars($contents, ENT_QUOTES) ?>" /> | |
| <input type="hidden" id="templateMessageTitle" value="<?= htmlspecialchars($messageTitle ?? '', ENT_QUOTES) ?>" /> | |
| <div class="ncua-table ncua-table--vertical"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="10" type="text" id="templateSubject" placeholder="제목을 입력하세요" class="layer-template-title" data-charcount-key="layerTemplateTitle" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="layerTemplateTitle"> | |
변경 후layer_mobile_send_save_sms_template.php
| <link rel="stylesheet" href="<?=gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/css/crm/layer-save-as-template.css')?>"> | |
| <div class="modal-dialog__content layer-save-as-template"> | |
| <ul> | |
| <li class="ncua-notice-info">작성한 내용을 저장할 카테고리와 템플릿명을 입력하여 주시기 바랍니다.</li> | |
| <li class="ncua-notice-info">저장한 템플릿은 검수중 상태로 등록되며, 승인 후 발송 가능합니다.</li> | |
| </ul> | |
| <input type="hidden" id="templateContents" value="<?= htmlspecialchars($contents, ENT_QUOTES) ?>" /> | |
| <input type="hidden" id="templateMessageTitle" value="<?= htmlspecialchars($messageTitle ?? '', ENT_QUOTES) ?>" /> | |
| <div class="ncua-table ncua-table--vertical"> | |
| <div class="ncua-input__content-wrap"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input maxlength="10" type="text" id="templateSubject" placeholder="템플릿명을 입력하여 주세요." class="layer-template-title" data-charcount-key="layerTemplateTitle" /> | |
| </div> | |
| </div> | |
| <div class="ncua-input__field-text-count" data-charcount-text="layerTemplateTitle"> | |
변경 전layer_mobile_send_sms_template.php
| <div class="modal-dialog__content layer-load-template js-sms-template-section"> | |
| <ul> | |
| <li class="ncua-caution-text">치환코드는 지정된 페이지에서만 사용하실 수 있습니다.</li> | |
| <li class="ncua-notice-info">예) 주문 관련 치환코드는 주문리스트에서 SMS 발송 시에만 사용하실 수 있습니다.</li> | |
| <li class="ncua-notice-info">중복 선택은 불가합니다.</li> | |
변경 후layer_mobile_send_sms_template.php
| <div class="modal-dialog__content layer-load-template js-sms-template-section"> | |
| <ul> | |
| <li class="ncua-notice-info">템플릿 작성 및 수정은 템플릿 관리에서 가능합니다. <a href="./message_template.php?sendMethod=sms" target="_blank" class="ncua-link">바로가기</a></li> | |
| <li class="ncua-caution-text">치환코드는 지정된 페이지에서만 사용하실 수 있습니다.</li> | |
| <li class="ncua-notice-info">예) 주문 관련 치환코드는 주문리스트에서 SMS 발송 시에만 사용하실 수 있습니다.</li> | |
| <li class="ncua-notice-info">중복 선택은 불가합니다.</li> | |
변경 전layer_recipient_setting.php
| function updateFriendtalkCampainUnitPoint() { | |
| if (document.querySelector('input[name="sendMethod"]:checked')?.value === 'FRIENDTALK') { | |
| const campaignMessageType = document.querySelector('input[name="campaignMessageType"]:checked')?.value; | |
| updateCampaignUnitPoint(campaignMessageType); | |
| } | |
변경 후layer_recipient_setting.php
| function updateFriendtalkCampainUnitPoint() { | |
| if (document.querySelector('input[name="sendMethod"]:checked')?.value === 'FRIENDTALK') { | |
| if (typeof updateCampaignUnitPoint !== 'function') return; | |
| const campaignMessageType = document.querySelector('input[name="campaignMessageType"]:checked')?.value; | |
| updateCampaignUnitPoint(campaignMessageType); | |
| } | |
변경 전layer_send_method_setting.php
| <?php foreach ($sendMethods as $sendMethod): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="<?= $sendMethod->name ?>" <?= $sendMethod->name === 'SMS' ? 'checked="checked"' : ''?> /> | |
| </span> | |
| <span> | |
| <span class="ncua-radio-field__text"><?= $sendMethod->value ?></span> | |
| } | |
| } | |
| const validateResult = validateSendMethodUseFlag(this.value); | |
| if (!validateResult.result) { | |
| e.preventDefault(); | |
| NCDSAlert({ | |
| message: validateResult.message, | |
| subMessage: validateResult.subMessage, | |
| iconType: 'error' | |
| }); | |
| return; | |
| }); | |
| } | |
| function validateSendMethodUseFlag(sendMethod) { | |
| switch (sendMethod) { | |
| case 'FRIENDTALK': | |
| if (messageConfig.kakaoFriendTalk.useFlag === 'y') { | |
| return { result: true }; | |
| } else { | |
| return { | |
| result: false, | |
| message: '발송 수단이 준비되지 않았습니다.', | |
| subMessage: '메시지를 발송하려면 발송 수단 인증 및 설치가 필요합니다.<br />통합 설정 페이지에서 먼저 완료해 주세요.' | |
| }; | |
| } | |
| case 'ALIMTALK': | |
| if (messageConfig.kakaoAlimTalk.useFlag === 'y') { | |
| if (messageConfig.kakaoAlimTalk.sender !== 'kakaoAlrimCloud') { | |
| return { | |
| result: false, | |
| message: '비즈엠/블룸에이아이 발송 불가 안내', | |
| subMessage: '비즈엠, 블룸에이아이 채널은 알림톡으로 메시지를 발송할 수 없습니다.<br />CRM > 메시지 > 메시지 설정에서 제공사를 고도몰로 변경해 주세요.' | |
| }; | |
| } | |
| return {result: true}; | |
| } else { | |
| return { | |
| result: false, | |
| message: '발송 수단이 준비되지 않았습니다.', | |
| subMessage: '메시지를 발송하려면 발송 수단 인증 및 설치가 필요합니다.<br />통합 설정 페이지에서 먼저 완료해 주세요.' | |
| }; | |
| } | |
| case 'MYAPP': | |
| if (messageConfig.myappPush.useFlag === 'y') { | |
| return { result: true }; | |
| } else { | |
| return { | |
| result: false, | |
| message: '발송 수단이 준비되지 않았습니다.', | |
| subMessage: '메시지를 발송하려면 발송 수단 인증 및 설치가 필요합니다.<br />통합 설정 페이지에서 먼저 완료해 주세요.' | |
| }; | |
| } | |
| default: | |
| return { result: true }; | |
| } | |
| } | |
| function initializeData() { | |
| } | |
| function loadSendMethodContentSettingLayer(sendMethod) { | |
| const urlMap = { | |
| SMS: './mobile_send/layer_sms_content_setting.php', | |
| FRIENDTALK: './mobile_send/layer_kakao_friendtalk_content_setting.php', | |
| }); | |
| } | |
| function getActivatedSendTabType() { | |
| return document.querySelector('.ncua-tab-button[data-send-tab-type].is-active')?.dataset.sendTabType ?? null; | |
| } | |
| // 제목 byte 규칙 적용(이모지 제거 포함) 후 최종 값 반환 | |
| function applySmsTitleByteRule(titleInput, messageInputModule) { | |
| let value = messageInputModule.removeEmoji(titleInput.value).value; | |
| const isOver = messageInputModule.getByteLength(value) > SMS_TITLE_MAX_BYTES; | |
| // addAdText 가 (광고) 삽입 시에만 세우는 1회성 플래그 (사용자 타이핑과 구분) | |
| const adInserted = titleInput.dataset.titleAdInsert === '1'; | |
| const base = titleInput.dataset.titleOverBase; | |
| let caret = null; | |
| delete titleInput.dataset.titleAdInsert; | |
| // C4-2 — 절삭하지 않는다. 초과 상태에서는 늘리는 입력만 되돌리고 삭제는 허용 | |
| if (!adInserted && base !== undefined && messageInputModule.getByteLength(value) > messageInputModule.getByteLength(base)) { | |
| // 되돌린 만큼 커서를 입력 직전 위치로 복원 | |
| caret = Math.max(0, (titleInput.selectionStart ?? value.length) - (value.length - base.length)); | |
| value = base; | |
| } | |
| titleInput.dataset.titleOverBase = value; | |
| if (titleInput.value !== value) { | |
| titleInput.value = value; | |
| if (caret !== null) { | |
| titleInput.setSelectionRange(caret, caret); | |
| } | |
| } | |
| return value; | |
| * 대체메시지 사용 가능여부 검증 | |
| */ | |
| function enableAlternative(activatedSendMethod) { | |
| switch (activatedSendMethod) { | |
| case 'SMS': | |
| return false; | |
| case 'FRIENDTALK': | |
| if (messageConfig.kakaoFriendTalk.useAlternative !== 'y') { | |
| return false; | |
| } | |
| const campaignMessageType = document.querySelector('input[name="campaignMessageType"]:checked')?.value; | |
| if (['CAROUSEL_FEED', 'WIDE_ITEM_LIST'].includes(campaignMessageType)) { | |
| return false; | |
| } | |
| break; | |
| case 'ALIMTALK': | |
| if (messageConfig.kakaoAlimTalk.useAlternative !== 'y') { | |
| return false; | |
| } | |
| break; | |
| case 'MYAPP': | |
| if (messageConfig.myappPush.useAlternative !== 'y') { | |
| return false; | |
| } | |
| const recipientType = document.querySelector('input[name="recipientType"]:checked')?.value ?? 'ALL' | |
| const isOnlyAgreed = document.querySelector('input[name="isOnlyAgreed"]')?.checked; | |
| const myappSendCondition = document.querySelector('input[name="myappSendCondition"]:checked').value; | |
| if (recipientType === 'ALL' && !isOnlyAgreed && myappSendCondition === 'APP_INSTALLED') { | |
| return false; | |
| } | |
| break; | |
| } | |
| return true; | |
| } | |
| function initializeSendMethodSettingSectionElement() { | |
| document.querySelector('input[name="sendMethod"]:checked')?.dispatchEvent(new Event('change')); | |
| } | |
| function isEditing() { | |
변경 후layer_send_method_setting.php
| <?php foreach ($sendMethods as $sendMethod): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input name="sendMethod" type="radio" value="<?= $sendMethod->name ?>" <?= $sendMethod->name === 'FRIENDTALK' ? 'checked="checked"' : ''?> /> | |
| </span> | |
| <span> | |
| <span class="ncua-radio-field__text"><?= $sendMethod->value ?></span> | |
| } | |
| } | |
| const restriction = getSendMethodRestriction(this.value); | |
| if (restriction?.reason === 'ALIMTALK_SENDER') { | |
| e.preventDefault(); | |
| NCDSAlert({ | |
| ...buildSendMethodRestrictionText(restriction), | |
| iconType: 'error' | |
| }); | |
| return; | |
| }); | |
| } | |
| function getSendMethodRestriction(sendMethod) { | |
| return messageConfig.sendMethods?.[sendMethod]?.restriction ?? null; | |
| } | |
| function initializeData() { | |
| } | |
| function loadSendMethodContentSettingLayer(sendMethod) { | |
| const restriction = getSendMethodRestriction(sendMethod); | |
| if (restriction) { | |
| renderSendMethodRestrictionNotice(restriction); | |
| return; | |
| } | |
| const urlMap = { | |
| SMS: './mobile_send/layer_sms_content_setting.php', | |
| FRIENDTALK: './mobile_send/layer_kakao_friendtalk_content_setting.php', | |
| }); | |
| } | |
| function renderSendMethodRestrictionNotice(restriction) { | |
| window.preview?.reset(); | |
| window.alternativePreview?.reset(); | |
| const { message, subMessage } = buildSendMethodRestrictionText(restriction); | |
| $('#layerSendMethodContent').html(` | |
| <div class="no-ready-send-type"> | |
| <p class="ncua-caution-text">${message} ${subMessage}</p> | |
| </div> | |
| `); | |
| } | |
| function getActivatedSendTabType() { | |
| return document.querySelector('.ncua-tab-button[data-send-tab-type].is-active')?.dataset.sendTabType ?? null; | |
| } | |
| // 제목 byte 규칙 적용(이모지 제거 포함) 후 최종 값 반환 | |
| function applySmsTitleByteRule(titleInput, messageInputModule) { | |
| const stripped = messageInputModule.removeEmoji(titleInput.value, titleInput.selectionStart ?? titleInput.value.length); | |
| let value = stripped.value; | |
| const isOver = messageInputModule.getByteLength(value) > SMS_TITLE_MAX_BYTES; | |
| // addAdText 가 (광고) 삽입 시에만 세우는 1회성 플래그 (사용자 타이핑과 구분) | |
| const adInserted = titleInput.dataset.titleAdInsert === '1'; | |
| const base = titleInput.dataset.titleOverBase; | |
| let caret = value === titleInput.value ? null : stripped.cursorPos; | |
| delete titleInput.dataset.titleAdInsert; | |
| // C4-2 — 절삭하지 않는다. 초과 상태에서는 늘리는 입력만 되돌리고 삭제는 허용 | |
| if (!adInserted && base !== undefined && messageInputModule.getByteLength(value) > messageInputModule.getByteLength(base)) { | |
| // 되돌린 만큼 커서를 입력 직전 위치로 복원 | |
| caret = Math.max(0, stripped.cursorPos - (value.length - base.length)); | |
| value = base; | |
| } | |
| titleInput.dataset.titleOverBase = value; | |
| if (titleInput.value !== value) { | |
| titleInput.value = value; | |
| if (caret !== null) { | |
| const caretPos = Math.min(caret, value.length); | |
| titleInput.setSelectionRange(caretPos, caretPos); | |
| } | |
| } | |
| return value; | |
| * 대체메시지 사용 가능여부 검증 | |
| */ | |
| function enableAlternative(activatedSendMethod) { | |
| if (messageConfig.isSmsManager !== true) { | |
| return false; | |
| } | |
| if (messageConfig.sendMethods?.[activatedSendMethod]?.useAlternative !== 'y') { | |
| return false; | |
| } | |
| if (activatedSendMethod === 'FRIENDTALK') { | |
| const campaignMessageType = document.querySelector('input[name="campaignMessageType"]:checked')?.value; | |
| return !['CAROUSEL_FEED', 'WIDE_ITEM_LIST'].includes(campaignMessageType); | |
| } | |
| if (activatedSendMethod === 'MYAPP') { | |
| const recipientType = document.querySelector('input[name="recipientType"]:checked')?.value ?? 'ALL' | |
| const isOnlyAgreed = document.querySelector('input[name="isOnlyAgreed"]')?.checked; | |
| const myappSendCondition = document.querySelector('input[name="myappSendCondition"]:checked').value; | |
| return !(recipientType === 'ALL' && !isOnlyAgreed && myappSendCondition === 'APP_INSTALLED'); | |
| } | |
| return true; | |
| } | |
| function initializeSendMethodSettingSectionElement() { | |
| sendMethodSettingSection.querySelector('input[name="sendMethod"]:checked') | |
| ?.dispatchEvent(new Event('change')); | |
| } | |
| function isEditing() { | |
변경 전layer_sms_content_setting.php
| <li>SMS 작성 시 90byte를 초과하면 LMS로 자동 전환되어 발송됩니다.</li> | |
| <li>변수에 실제 데이터가 적용된 최종 메시지가 90byte를 초과하는 경우에도 LMS로 전환될 수 있습니다.</li> | |
| <li>본문 내용이 LMS인 경우에만 제목 입력이 가능하며, 제목 영역에는 직접 (광고) 문구를 입력하지 않도록 하고 광고 삽입이 필요한 경우 광고성 문구 추가 기능을 사용해야 합니다.</li> | |
| </ul> | |
| </div> | |
| </details> | |
변경 후layer_sms_content_setting.php
| <li>SMS 작성 시 90byte를 초과하면 LMS로 자동 전환되어 발송됩니다.</li> | |
| <li>변수에 실제 데이터가 적용된 최종 메시지가 90byte를 초과하는 경우에도 LMS로 전환될 수 있습니다.</li> | |
| <li>본문 내용이 LMS인 경우에만 제목 입력이 가능하며, 제목 영역에는 직접 (광고) 문구를 입력하지 않도록 하고 광고 삽입이 필요한 경우 광고성 문구 추가 기능을 사용해야 합니다.</li> | |
| <li>SMS/LMS 템플릿 등록·관리·승인은 <a class="ncua-link" href="./message_template.php?sendMethod=sms" target="_blank">템플릿 관리</a>에서 가능합니다.</li> | |
| <li>SMS/LMS 템플릿은 승인 상태에서만 발송이 가능합니다. (검수중·반려 상태의 템플릿은 발송이 제한됩니다)</li> | |
| </ul> | |
| </div> | |
| </details> | |
변경 전mobile_send.php
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_description.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/mobile_message_replace_code.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/message_send_common.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_status.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-checkbox-group/ncds-checkbox-group.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-datepicker-factory/ncds-datepicker-factory.js') ?>"></script> | |
| const lmsPointEach = Number(<?= $lmsPointEach ?>); | |
| const kakaoAlrimtalkPointEach = Number(<?= $kakaoAlrimTalkPointEach ?>); | |
| const kakaoFriendtalkPointEach = Number(<?= $kakaoFriendTalkPointEach ?>); | |
| const messageConfig = JSON.parse('<?= json_encode($messageConfig, JSON_UNESCAPED_UNICODE) ?>'); | |
| $(document).ready(function () { | |
| commonLogic(); | |
| refreshOverSmsTitleState(); | |
| } | |
| // 발신번호 검증 | |
| if (!messageConfig.hasCallNumber) { | |
| NCDSAlert({message: '발신번호가 없어 발송할 수 없습니다.', subMessage: '설정에서 발신번호 등록 후 발송 시도를 해주세요.', iconType: 'error'}); | |
| $button.prop('disabled', false); | |
| return; | |
| } | |
| // 포인트 0인 경우 | |
| if (!validateHasMessagePoint()) { | |
| NCDSAlert({ | |
| message: '메시지 포인트를 충전해 주세요', | |
| iconType: 'error' | |
| }); | |
| $button.prop('disabled', false); | |
| return; | |
| } | |
| // 발송인원 X 수단별 포인트 검증 | |
| if (!validateMessagePoint()) { | |
| NCDSAlert({ | |
| message: '포인트가 부족하여 발송할 수 없습니다.', | |
| subMessage: '발송 대상 인원보다 포인트가 적어 발송이 불가합니다. <br /> 충전 후 다시 시도해주세요.', | |
| iconType: 'error' | |
| }); | |
| $button.prop('disabled', false); | |
| return; | |
| } | |
| break; | |
| } | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked').value; | |
| const mainContents = sendMethod === 'ALIMTALK' | |
| ? (document.querySelector('textarea[name="messageContent"]')?.value ?? '') | |
| : (window.messageInput?.getValue() ?? ''); | |
| }); | |
| } | |
| function validateHasMessagePoint() { | |
| return messagePoint > 0; | |
| } | |
| function validateMessagePoint() { | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked')?.value ?? "SMS"; | |
| const expectCount = getExpectTargetCount(); | |
| let requiredPoint; | |
| switch (sendMethod) { | |
| case 'SMS': | |
| const messageLength = window.messageInput.calculateLength(window.messageInput.getValue()); | |
| requiredPoint = messageLength > 90 ? lmsPointEach * expectCount : smsPointEach * expectCount; | |
| break; | |
| case 'ALIMTALK': | |
| requiredPoint = kakaoAlrimtalkPointEach * expectCount; | |
| break; | |
| case 'FRIENDTALK': | |
| requiredPoint = kakaoFriendtalkPointEach * expectCount; | |
| break; | |
| default: | |
| requiredPoint = 1; | |
| } | |
| return messagePoint >= requiredPoint; | |
| } | |
| function getExpectTargetCount() { | |
| const recipientType = document.querySelector('[name="recipientType"]:checked')?.value | |
| if (recipientType == null || recipientType === '') { | |
변경 후mobile_send.php
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_description.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/mobile_message_replace_code.js"></script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/message_send_common.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.useNcdsDialog(); | |
| SmsSendGate.applyRestrictOnAjax('<?= gd_isset($smsSendRestrictType, '') ?>', { | |
| tooltip: '<?= gd_isset($smsSendRestrictTooltip, '') ?>' | |
| }); | |
| SmsSendGate.bindClickGate({ | |
| message: '<?= gd_isset($smsSendRestrictAlertMessage, '') ?>', | |
| subMessage: '<?= gd_isset($smsSendRestrictAlertSubMessage, '') ?>', | |
| selectors: ['[data-send-tab-type="ALTERNATIVE"]', '[data-preview-message-type="ALTERNATIVE"]'] | |
| }); | |
| }); | |
| </script> | |
| <script type="text/javascript" src="<?= PATH_ADMIN_GD_SHARE ?>script/crm/crm_group_status.js"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-checkbox-group/ncds-checkbox-group.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/ncds-datepicker-factory/ncds-datepicker-factory.js') ?>"></script> | |
| const lmsPointEach = Number(<?= $lmsPointEach ?>); | |
| const kakaoAlrimtalkPointEach = Number(<?= $kakaoAlrimTalkPointEach ?>); | |
| const kakaoFriendtalkPointEach = Number(<?= $kakaoFriendTalkPointEach ?>); | |
| const messageConfig = <?= json_encode($messageConfig, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>; | |
| $(document).ready(function () { | |
| commonLogic(); | |
| refreshOverSmsTitleState(); | |
| } | |
| const sendMethod = document.querySelector('input[name="sendMethod"]:checked').value; | |
| const smsIncluded = sendMethod === 'SMS' || enableAlternative(sendMethod); | |
| try { | |
| await SmsSendGate.ensure({ | |
| requiredPoint: getRequiredPoint(), | |
| smsIncluded | |
| }); | |
| } catch (e) { | |
| $button.prop('disabled', false); | |
| return; | |
| } | |
| break; | |
| } | |
| const mainContents = sendMethod === 'ALIMTALK' | |
| ? (document.querySelector('textarea[name="messageContent"]')?.value ?? '') | |
| : (window.messageInput?.getValue() ?? ''); | |
| }); | |
| } | |
| function getExpectTargetCount() { | |
| const recipientType = document.querySelector('[name="recipientType"]:checked')?.value | |
| if (recipientType == null || recipientType === '') { | |
변경 전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']; ?>"/> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][recipientMode]" value="<?php echo $typeData['recipientMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][recipientSno]" value="<?php echo $typeData['recipientSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['recipientMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['recipientContents'] ?></textarea> | |
| </td> | |
| <?php } ?> | |
| <td class="form-inline"> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberMode]" value="<?php echo $typeData['memberMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberSno]" value="<?php echo $typeData['memberSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['memberMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['memberContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="text-center text-blue">운영자 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminMode]" value="<?php echo $typeData['adminMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminSno]" value="<?php echo $typeData['adminSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['adminMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminContents]" rows="4" | |
| class="form-control js-sms-auto-contents <?php if ($sKey == 'admin') { ?> width100p <?php } ?>"><?= $typeData['adminContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="text-center text-orange-red">회원 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['providerMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['providerContents'] ?></textarea> | |
| <?php } else { ?> | |
| <?php if ($sKey == 'admin') { ?> | |
| <div class="text-center text-orange-red">본사 전용</div> | |
| while (smsAutoStringToByte(text) > <?= $lmsSubjectLimit; ?>) { text = text.substring(0, text.length - 1); } | |
| input.val(text); | |
| }); | |
| $('.btn-change-password').click(function () { | |
| $.get('../share/layer_sms_password.php?mode=change', function () { | |
| BootstrapDialog.closeAll(); | |
변경 후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> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-emoji.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']; ?>"/> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][recipientMode]" value="<?php echo $typeData['recipientMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][recipientSno]" value="<?php echo $typeData['recipientSno']; ?>"/> | |
| <?php if (isset($typeData['recipientLinkedContents'])) { ?> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientMessageTitle]" value="<?= gd_isset($typeData['recipientMessageTitle']) ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientContents]" value="<?= $typeData['recipientContents'] ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" class="form-control js-sms-auto-subject" value="<?= $typeData['recipientLinkedMessageTitle'] ?>" readonly/> | |
| </div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents" readonly><?= $typeData['recipientLinkedContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['recipientMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][recipientContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['recipientContents'] ?></textarea> | |
| <?php } ?> | |
| </td> | |
| <?php } ?> | |
| <td class="form-inline"> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberMode]" value="<?php echo $typeData['memberMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberSno]" value="<?php echo $typeData['memberSno']; ?>"/> | |
| <?php if (isset($typeData['memberLinkedContents'])) { ?> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberMessageTitle]" value="<?= gd_isset($typeData['memberMessageTitle']) ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberContents]" value="<?= $typeData['memberContents'] ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" class="form-control js-sms-auto-subject" value="<?= $typeData['memberLinkedMessageTitle'] ?>" readonly/> | |
| </div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents" readonly><?= $typeData['memberLinkedContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['memberMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['memberContents'] ?></textarea> | |
| <?php } ?> | |
| <?php } else { ?> | |
| <div class="text-center text-blue">운영자 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminMode]" value="<?php echo $typeData['adminMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminSno]" value="<?php echo $typeData['adminSno']; ?>"/> | |
| <?php if (isset($typeData['adminLinkedContents'])) { ?> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminMessageTitle]" value="<?= gd_isset($typeData['adminMessageTitle']) ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminContents]" value="<?= $typeData['adminContents'] ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" class="form-control js-sms-auto-subject" value="<?= $typeData['adminLinkedMessageTitle'] ?>" readonly/> | |
| </div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents" readonly><?= $typeData['adminLinkedContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['adminMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminContents]" rows="4" | |
| class="form-control js-sms-auto-contents <?php if ($sKey == 'admin') { ?> width100p <?php } ?>"><?= $typeData['adminContents'] ?></textarea> | |
| <?php } ?> | |
| <?php } else { ?> | |
| <div class="text-center text-orange-red">회원 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <?php if (isset($typeData['providerLinkedContents'])) { ?> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerMessageTitle]" value="<?= gd_isset($typeData['providerMessageTitle']) ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerContents]" value="<?= $typeData['providerContents'] ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" class="form-control js-sms-auto-subject" value="<?= $typeData['providerLinkedMessageTitle'] ?>" readonly/> | |
| </div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents" readonly><?= $typeData['providerLinkedContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['providerMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['providerContents'] ?></textarea> | |
| <?php } ?> | |
| <?php } else { ?> | |
| <?php if ($sKey == 'admin') { ?> | |
| <div class="text-center text-orange-red">본사 전용</div> | |
| while (smsAutoStringToByte(text) > <?= $lmsSubjectLimit; ?>) { text = text.substring(0, text.length - 1); } | |
| input.val(text); | |
| }); | |
| smsEmoji.bind('#frmSmsAuto textarea'); | |
| $('.btn-change-password').click(function () { | |
| $.get('../share/layer_sms_password.php?mode=change', function () { | |
| BootstrapDialog.closeAll(); | |
변경 전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"> | |
| <!-- | |
| /** | |
| $('input[name=receiverCnt]').val(receiverCount * pointCnt); | |
| $('input[name=agreeCnt]').val(agreeCount * pointCnt); | |
| $('input[name=rejectCnt]').val(rejectCount * pointCnt); | |
| } | |
| $(document).ready(function () { | |
| var $formSendSms = gd_sms_send.get_form(); | |
| // 폼 체크 | |
| $formSendSms.validate({ | |
| dialog: false, | |
| <input type="hidden" name="receiverCnt" value="" /> | |
| <input type="hidden" name="agreeCnt" value="" /> | |
| <input type="hidden" name="rejectCnt" value="<?=$countSmsFlN?>" /> | |
| <input type="hidden" name="sendFl" value="sms" /> | |
| <input type="hidden" name="rejectSend" value="n" /> | |
| <input type="hidden" name="receiverList" value="" /> | |
변경 후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" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.applyRestrict('<?= gd_isset($smsSendRestrictType, '') ?>', { | |
| tooltip: '<?= gd_isset($smsSendRestrictTooltip, '') ?>', | |
| selectors: ['#formSendSms input[type="submit"]', '#formSendSms textarea[name="smsContents"]'] | |
| }); | |
| }); | |
| </script> | |
| <script type="text/javascript"> | |
| <!-- | |
| /** | |
| $('input[name=receiverCnt]').val(receiverCount * pointCnt); | |
| $('input[name=agreeCnt]').val(agreeCount * pointCnt); | |
| $('input[name=rejectCnt]').val(rejectCount * pointCnt); | |
| $('input[name=sendTargetCnt]').val(receiverCount); | |
| } | |
| $(document).ready(function () { | |
| var $formSendSms = gd_sms_send.get_form(); | |
| // 점유인증이 입력값 유효성보다 앞이라 submitHandler 로 옮기면 안 된다 | |
| $formSendSms.on('click', 'input[type="submit"]', function (event) { | |
| event.preventDefault(); | |
| // 발송 건수는 유효성 검증 단계에서 채워지는데 게이트가 그보다 앞이라 여기서 먼저 세운다 | |
| setReceiverCount(); | |
| SmsSendGate.ensure({ | |
| targetCount: parseInt($('input[name=sendTargetCnt]').val()) || 0 | |
| }).then(function () { | |
| $formSendSms.submit(); | |
| }).catch(function () {}); | |
| }); | |
| // 폼 체크 | |
| $formSendSms.validate({ | |
| dialog: false, | |
| <input type="hidden" name="receiverCnt" value="" /> | |
| <input type="hidden" name="agreeCnt" value="" /> | |
| <input type="hidden" name="rejectCnt" value="<?=$countSmsFlN?>" /> | |
| <input type="hidden" name="sendTargetCnt" value="" /> | |
| <input type="hidden" name="sendFl" value="sms" /> | |
| <input type="hidden" name="rejectSend" value="n" /> | |
| <input type="hidden" name="receiverList" value="" /> | |
변경 전_manage_permission_menu_list.php
| ?> | |
| </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"> | |
변경 후_manage_permission_menu_list.php
| ?> | |
| </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-caller-uncertified-delegate="<?= !empty($isCallerUncertifiedDelegate) ? '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"> | |
변경 전manage_permission.php
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') { | |
| dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고'); | |
| return false; | |
변경 후manage_permission.php
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('callerUncertifiedDelegate') === 'y') { | |
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>본인인증을 완료해야 발신관리위임자 역할을 수행할 수 있습니다.<br>운영자 수정 페이지에서 본인인증을 먼저 완료하시기 바랍니다.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') { | |
| dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고'); | |
| return false; | |
변경 전manage_register.php
| <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="notice-info">미지정</span> | |
| <?php endif; ?> | |
| <br> | |
| <span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[설정 > 관리 정책 > 운영자 관리]</a> 페이지의 CRM 추가설정 중 '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> | |
| <?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> | |
| }); | |
| }); | |
| $('.js-check-authno').click(function () { | |
| var cell_phone_auth_no = $('input[name=cellPhoneAuthNo]').val(); | |
| if (!manage_register.regex.auth_no.test(cell_phone_auth_no)) { | |
변경 후manage_register.php
| <tbody> | |
| <?php if (!empty($messageDelegate['visible'])): // SMS 발신 위임자 권한 (권한 설정 테이블 row, 지정/회수는 최고운영자만) ?> | |
| <tr class="js-message-delegate-area"> | |
| <th>발신관리위임자 권한 설정</th> | |
| <td class="form-inline"> | |
| <input type="hidden" name="messageDelegateAction" id="messageDelegateAction" value=""> | |
| <?php if ($messageDelegate['assigned'] === true): ?> | |
| <span class="notice-info">미지정</span> | |
| <?php endif; ?> | |
| <br> | |
| <span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[설정 > 관리 정책 > 운영자 권한 설정]</a> 페이지의 CRM 추가설정 중 'SMS 발송' 권한을 설정해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php if ($messageDelegate['assigned'] === false && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-set-reason-row" style="display:none;"> | |
| <th>발신관리위임자 권한 설정 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateSetReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 설정하기 위한 명확한 사유를 입력해 주세요.</span> | |
| <?php endif; ?> | |
| <?php if ($messageDelegate['assigned'] === true && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-release-reason-row" style="display:none;"> | |
| <th>발신관리위임자 권한 해제 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateReleaseReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 해제하기 위한 명확한 사유를 입력해 주세요.</span> | |
| }); | |
| }); | |
| $('input[name="cellPhone"]').on('input', function () { | |
| $('.my-sms-auth').text('(미인증)'); | |
| }); | |
| $('.js-check-authno').click(function () { | |
| var cell_phone_auth_no = $('input[name=cellPhoneAuthNo]').val(); | |
| if (!manage_register.regex.auth_no.test(cell_phone_auth_no)) { | |
변경 전manage_security.php
| 화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br> | |
| - 설정 > 관리정책 > 운영자관리/운영자등록<br> | |
| - 설정 > 관리정책 > 운영보안설정<br> | |
| - 설정 > 결제정책 > 무통장입금은행관리<br> | |
| - CRM > 메시지 발송 > 모바일 메시지 발송 | |
| </p> | |
| </div> | |
| </td> | |
변경 후manage_security.php
| 화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br> | |
| - 설정 > 관리정책 > 운영자관리/운영자등록<br> | |
| - 설정 > 관리정책 > 운영보안설정<br> | |
| - 설정 > 결제정책 > 무통장입금은행관리 | |
| </p> | |
| </div> | |
| </td> | |
변경 전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> | |
| menual_send(); | |
| } | |
| function menual_send() { | |
| var params = { | |
| mode: 'sendCartRemindManual', | |
| cartRemindNo: cartRemindNo, | |
변경 후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> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| menual_send(); | |
| } | |
| function menual_send() { | |
| if (window.__cartRemindGatePassed !== true) { | |
| SmsSendGate.ensure().then(function () { | |
| window.__cartRemindGatePassed = true; | |
| menual_send(); | |
| window.__cartRemindGatePassed = false; | |
| }).catch(function () {}); | |
| return; | |
| } | |
| var params = { | |
| mode: 'sendCartRemindManual', | |
| cartRemindNo: cartRemindNo, | |
변경 전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"> | |
| } | |
| }); | |
| function sendAction(sno, couponNo) { | |
| var params = { | |
| mode : 'ajaxComebackCouponActionCount', | |
| var smsCount = $('#sms-point').val(); | |
| var smsFl = $('#dataInfo' + sno).attr('data-smsFl'); | |
| $.ajax({ | |
| method: "POST", | |
| cache: false, | |
변경 후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> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/crm/sms-send-gate.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <div class="btn-group"> | |
| } | |
| }); | |
| var comebackCouponGatePassed = false; | |
| function sendAction(sno, couponNo) { | |
| var params = { | |
| mode : 'ajaxComebackCouponActionCount', | |
| var smsCount = $('#sms-point').val(); | |
| var smsFl = $('#dataInfo' + sno).attr('data-smsFl'); | |
| if (smsFl === 'y' && comebackCouponGatePassed !== true) { | |
| SmsSendGate.ensure().then(function () { | |
| comebackCouponGatePassed = true; | |
| sendAction(sno, couponNo); | |
| comebackCouponGatePassed = false; | |
| }).catch(function () {}); | |
| return false; | |
| } | |
| $.ajax({ | |
| method: "POST", | |
| cache: false, | |
변경 전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 . 'ncds/css/share/layer-message-representative-register-guide.css') ?>"> | |
| <div class="modal-dialog__content ncua-sms-representative-guide"> | |
| <div class="ncua-sms-representative-guide-hero"> | |
| <p class="ncua-sms-representative-guide-hero-title"><b>우리 쇼핑몰 번호, 도용되지 않게 지켜드려요</b></p> | |
| <p class="ncua-sms-representative-guide-hero-desc">발신관리책임자와 담당자 정보로 발신번호 사용을 관리해<br/>다른 사람이 SMS를 보내지 못하게 막아줘요.</p> | |
| </div> | |
| <ul class="ncua-sms-representative-guide-list"> | |
| <li class="ncua-flex"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24" stroke="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 6 9 17l-5-5"></path></svg>등록은 한 번, 1분이면 완료</li> | |
| <li class="ncua-flex"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24" stroke="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 6 9 17l-5-5"></path></svg> | |
| <span class="ncua-sms-representative-guide-data"> | |
| 등록 시, 최고운영자 정보가 <em class="ncua-sms-representative-guide-emphasis">통합회원 정보로 자동 변경</em> | |
| <span>등록 이후 이름·휴대폰번호·이메일은 <a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" rel="noopener noreferrer">통합회원 마이페이지</a>에서만 수정할 수 있어요.</span> | |
| </span> | |
| </li> | |
| <li class="ncua-flex"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24" stroke="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 6 9 17l-5-5"></path></svg> | |
| <span class="ncua-sms-representative-guide-data"> | |
| 26년 10월 25일까지 등록 필수 | |
| <span>미등록 시 SMS 관련 기능 이용이 제한됩니다.</span> | |
| </span> | |
| </li> | |
| </ul> | |
| <ul class="ncua-sms-representative-guide-caution"> | |
| <li>※ <?= !empty($noticeUri) ? '<a href="' . $noticeUri . '" target="_blank">공지사항</a>' : '공지사항' ?>에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다.</li> | |
| <li>※ 급증하는 불법스팸·스미싱 피해 예방을 위한 전기통신사업법 제22조의11 및 전송자격인증 기준 등에 관한 고시(방통위 고시 제2026-1호) 개정에 따른 조치입니다.</li> | |
| </ul> | |
| </div> | |
| <div class="modal-dialog__footer"> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"><input type="checkbox" class="js-hide-representative-guide" /></span> | |
| <span class="ncua-checkbox-field__text">7일동안 보지 않기</span> | |
| </label> | |
| <div> | |
| <button type="button" class="ncua-btn ncua-btn--md ncua-btn--secondary-gray" onclick="layer_close();"><span class="ncua-btn__label">나중에 하기</span></button> | |
| <button type="button" class="ncua-btn ncua-btn--md ncua-btn--primary js-representative-register">등록하기</button> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| // 안내 레이어 쿠키 규약 — 'false' 저장이 노출 억제를 뜻한다 (layer_legalRequirements.php 와 동일) | |
| var displayCookie = <?= json_encode($displayCookie) ?>; | |
| var expireDay = <?= json_encode($displayCookieExpireDay) ?>; | |
| var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>; | |
| var isSelfCertified = <?= json_encode($isSelfCertified) ?>; | |
| var isCommerceLoginRequired = <?= json_encode($isCommerceLoginRequired) ?>; | |
| $('.js-hide-representative-guide').on('change', function () { | |
| if (this.checked) { | |
| $.cookie(displayCookie, 'false', {expires: expireDay, path: '/'}); | |
| return; | |
| } | |
| $.removeCookie(displayCookie, {path: '/'}); | |
| }); | |
| $('.js-representative-register').on('click', function () { | |
| layer_close(); | |
| // 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다) | |
| if (!isCommerceConnected) { | |
| NCDSAlert({ | |
| message: '발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.', | |
| subMessage: '통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.', | |
| iconType: 'warning', | |
| callback: function () { | |
| location.href = <?= json_encode($commerceLoginUri) ?>; | |
| } | |
| }); | |
| return; | |
| } | |
| // 이미 본인인증한 운영자도 일반 로그인이면 등록을 진행시키지 않는다 — 로그인 방식은 인증 이력과 무관하게 판정한다 | |
| if (isCommerceLoginRequired) { | |
| managerCertification.alertCommerceLoginRequired(); | |
| 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) { | |
| NCDSAlert({message: response.data.message, subMessage: response.data.subMessage, iconType: 'error'}); | |
| 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) { | |
| NCDSConfirm({ | |
| message: '발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?', | |
| subMessage: '가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.', | |
| callback: function (confirmed) { | |
| if (!confirmed) { | |
| showRegisterCompleted(); | |
| return; | |
| } | |
| SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted}); | |
| } | |
| }); | |
| return; | |
| } | |
| showRegisterCompleted(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다 | |
| function showRequestFailed(response) { | |
| NCDSAlert({message: (response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', iconType: 'error'}); | |
| } | |
| // 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다 | |
| function showRegisterCompleted() { | |
| NCDSAlert({ | |
| message: '발신관리책임자 등록이 완료되었습니다.', | |
| subMessage: "최고운영자 정보(이름·휴대폰번호·이메일)는<br>이제 통합회원 마이페이지에서만 수정할 수 있습니다.<br><br>부운영자와 함께 SMS 업무를 하려면<br>'발신관리위임자'로 설정하거나 'SMS 발송'권한을 부여해 주세요.", | |
| iconType: 'success' | |
| }); | |
| } | |
| })(); | |
| </script> | |
변경 후layer_message_representative_register_guide.php
변경 전layer_message_representative_send_restrict.php
변경 후layer_message_representative_send_restrict.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 . 'ncds/css/share/layer-message-representative-send-restrict.css') ?>"> | |
| <div class="modal-dialog__content ncua-sms-period-expire"> | |
| <p class="ncua-sms-period-expire-caution"><em>SMS 메뉴 접근과 발송 기능 이용이 제한된 상태입니다.</em></p> | |
| <ul> | |
| <li class="ncua-notice-info">발신관리책임자 등록을 완료하면 즉시 다시 이용할 수 있습니다.</li> | |
| <li class="ncua-notice-info">발신관리책임자 등록은 최고운영자만 할 수 있으며,<br>등록되면 최고운영자 정보가 <a class="ncua-btn--tertiary has-underline" href="https://accounts.godo.co.kr/mypage/acc" target="_blank" rel="noopener noreferrer"><strong>통합회원 정보</strong></a><strong>로 자동 변경</strong>됩니다.</li> | |
| <li class="ncua-notice-info"><?= !empty($guideUri) ? '<a class="ncua-btn--tertiary has-underline" href="' . gd_htmlspecialchars($guideUri) . '" target="_blank">관련 가이드</a>' : '관련 가이드' ?>에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다.</li> | |
| </ul> | |
| <p class="ncua-sms-period-expire-info">※ 「전기통신사업법」 제22조의11 및 「전송자격인증 기준 등에 관한 고시」(방통위 고시 제2026-1호) 2.1 계정관리 기준에 따른 안내입니다.</p> | |
| </div> | |
| <div class="modal-dialog__footer ncua-justify-between"> | |
| <label class="ncua-checkbox-field ncua-checkbox-field--xs has-text"> | |
| <span class="ncua-checkbox-input ncua-checkbox-input--xs ncua-checkbox-field__input"><input type="checkbox" class="js-hide-representative-guide" /></span> | |
| <span class="ncua-checkbox-field__text">7일동안 보지 않기</span> | |
| </label> | |
| <div> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray" onclick="layer_close();"><span class="ncua-btn__label">다음에 등록하기</span></button> | |
| <?php if (!empty($isRegisterAvailable)) { ?> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-representative-register"><span class="ncua-btn__label">발신관리책임자 등록</span></button> | |
| <?php } ?> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| // 안내 레이어 쿠키 규약 — 'false' 저장이 노출 억제를 뜻한다 (layer_legalRequirements.php 와 동일) | |
| var displayCookie = <?= json_encode($displayCookie) ?>; | |
| var expireDay = <?= json_encode($displayCookieExpireDay) ?>; | |
| var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>; | |
| var isSelfCertified = <?= json_encode($isSelfCertified) ?>; | |
| var isCommerceLoginRequired = <?= json_encode($isCommerceLoginRequired) ?>; | |
| $('.js-hide-representative-guide').on('change', function () { | |
| if (this.checked) { | |
| $.cookie(displayCookie, 'false', {expires: expireDay, path: '/'}); | |
| return; | |
| } | |
| $.removeCookie(displayCookie, {path: '/'}); | |
| }); | |
| $('.js-representative-register').on('click', function () { | |
| layer_close(); | |
| // 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다) | |
| if (!isCommerceConnected) { | |
| NCDSAlert({ | |
| message: '발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.', | |
| subMessage: '통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.', | |
| iconType: 'warning', | |
| callback: function () { | |
| location.href = <?= json_encode($commerceLoginUri) ?>; | |
| } | |
| }); | |
| return; | |
| } | |
| // 이미 본인인증한 운영자도 일반 로그인이면 등록을 진행시키지 않는다 — 로그인 방식은 인증 이력과 무관하게 판정한다 | |
| if (isCommerceLoginRequired) { | |
| managerCertification.alertCommerceLoginRequired(); | |
| 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) { | |
| NCDSAlert({message: response.data.message, subMessage: response.data.subMessage, iconType: 'error'}); | |
| 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) { | |
| NCDSConfirm({ | |
| message: '발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?', | |
| subMessage: '가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.', | |
| callback: function (confirmed) { | |
| if (!confirmed) { | |
| showRegisterCompleted(); | |
| return; | |
| } | |
| SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted}); | |
| } | |
| }); | |
| return; | |
| } | |
| showRegisterCompleted(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다 | |
| function showRequestFailed(response) { | |
| NCDSAlert({message: (response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', iconType: 'error'}); | |
| } | |
| // 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다 | |
| function showRegisterCompleted() { | |
| managerCertification.registerCompletedAlert(function () { window.location.reload(); }); | |
| } | |
| })(); | |
| </script> | |
변경 전layer_occupancy_auth.php
변경 후layer_occupancy_auth.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. | |
| */ | |
| ?> | |
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-occupancy-auth.css') ?>"> | |
| <script type="text/javascript"> | |
| (function () { | |
| if (BootstrapDialog.occupancyAuthGuarded) { | |
| return; | |
| } | |
| BootstrapDialog.occupancyAuthGuarded = true; | |
| BootstrapDialog.closeAll = function () { | |
| $.each(BootstrapDialog.dialogs, function (id, instance) { | |
| var cssClass = instance.getCssClass ? String(instance.getCssClass() || '') : ''; | |
| if (cssClass.indexOf('occupancy-auth-dialog') === -1) { | |
| instance.close(); | |
| } | |
| }); | |
| }; | |
| }()); | |
| </script> | |
| <?php if (!empty($isLocked)): ?> | |
| <div class="ncua-sms-auth-modal"> | |
| <p class="ncua-sms-auth-caution js-occupancy-auth-caution"><strong><?= $lockedMessage ?></strong><br>잠금 해제를 위해서는 본인확인 절차를 진행해야 합니다.</p> | |
| <div class="text-center mgt20"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray js-layer-close"><span class="ncua-btn__label">닫기</span></button> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-unlock-occupancy-auth"><span class="ncua-btn__label">본인 인증</span></button> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| (function () { | |
| var AUTH_CONTEXT = <?= json_encode($authContext, JSON_UNESCAPED_UNICODE) ?>; | |
| $('.js-unlock-occupancy-auth').on('click', function () { | |
| $.getScript('<?= PATH_ADMIN_GD_SHARE ?>script/manager-certification.js').done(function () { | |
| managerCertification.certify({ | |
| onComplete: function (success, reason) { | |
| if (success === false) { | |
| if (reason === 'mismatch') { | |
| // 불일치는 잠금 모달을 유지한 채 안내 문구만 바꿔 재인증을 유도한다 | |
| $('.js-occupancy-auth-caution').html('<strong>인증하신 정보가 등록된 운영자의 정보와 일치하지 않습니다. 다시 인증해 주세요.</strong>'); | |
| return; | |
| } | |
| NCDSAlert({message: '본인인증에 실패했습니다. 인증 정보를 확인 후 다시 시도해 주세요.', iconType: 'error'}); | |
| return; | |
| } | |
| // 발송 화면에서는 리로드하면 작성 중인 발송 내용이 사라지므로 게이트만 다시 태운다 | |
| var reloadsAfterAuth = (AUTH_CONTEXT !== 'send' || typeof SmsSendGate === 'undefined'); | |
| if (reloadsAfterAuth && typeof window.occupancyAuthCompleted === 'function') { | |
| window.occupancyAuthCompleted(); | |
| $('.js-layer-close').trigger('click'); | |
| return; | |
| } | |
| $('.js-layer-close').trigger('click'); | |
| if (reloadsAfterAuth) { | |
| location.reload(); | |
| return; | |
| } | |
| SmsSendGate.ensure({}).catch(function () {}); | |
| } | |
| }); | |
| }); | |
| }); | |
| })(); | |
| </script> | |
| <?php else: ?> | |
| <div class="ncua-sms-auth-modal"> | |
| <details class="ncua-accordion ncua-accordion--blue" open> | |
| <summary>안내사항</summary> | |
| <ul> | |
| <li class="ncua-notice-info">「전기통신사업법」 제22조의11 및 전송자격인증 기준(방송통신위원회 고시)에 따라 계정 탈취 및 무단 사용을 방지하기 위해 <?= gd_htmlspecialchars($authNotice) ?> 적용됩니다. 등록된 인증수단으로 본인 확인을 완료한 후 이용해 주세요.</li> | |
| </ul> | |
| </details> | |
| <div class="ncua-table ncua-table--vertical mgt20"> | |
| <table> | |
| <tbody> | |
| <?php if (!empty($hasAuthTarget)): ?> | |
| <tr> | |
| <th><div>인증수단</div></th> | |
| <td> | |
| <div class="ncua-flex-gap"> | |
| <?php foreach ($authTypeStates as $authType => $selectable): ?> | |
| <label class="ncua-radio-field ncua-radio-field--xs has-text"> | |
| <span class="ncua-radio-input ncua-radio-input--xs ncua-radio-field__input"> | |
| <input type="radio" name="occupancyAuthType" value="<?= gd_htmlspecialchars($authType) ?>" <?= gd_isset($checked['occupancyAuthType'][$authType]); ?> <?= gd_isset($disabled['occupancyAuthType'][$authType]); ?> /> | |
| </span> | |
| <span><span class="ncua-radio-field__text"><?= gd_htmlspecialchars(gd_isset($authTypeLabels[$authType], $authType)) ?></span></span> | |
| </label> | |
| <?php endforeach; ?> | |
| </div> | |
| <p class="ncua-radio-field__support-text mgl5">운영자 관리 페이지에서 설정한 인증수단 정보로 인증번호가 발송됩니다.</p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th><div>인증정보</div></th> | |
| <td> | |
| <div class="ncua-flex ncua-flex-column ncua-align-items-start"> | |
| <div class="js-otp-info-line"> | |
| <span class="js-auth-target-display"></span> | |
| <button type="button" class="ncua-btn ncua-btn--xs ncua-btn--secondary js-request-occupancy-otp"><span class="ncua-btn__label">인증번호 발송</span></button> | |
| <span class="ncua-caution-text js-otp-status"></span> | |
| <span class="ncua-caution-text js-resend-notice"></span> | |
| </div> | |
| <div class="js-otp-code-row mgt10" style="display:none;"> | |
| <div class="ncua-input ncua-input--xs ncua-input-width-320 js-otp-input"> | |
| <div class="ncua-input__content"> | |
| <div class="ncua-input__field ncua-input__field--xs"> | |
| <input type="text" name="occupancyAuthCode" id="occupancyAuthCode" maxlength="6" placeholder="인증번호를 입력해주세요." autocomplete="off"> | |
| <div class="ncua-input__icon-wrap ncua-input__right-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="none" class="ncua-input__destructive-icon"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 16v-4m0-4h.01M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10"></path></svg> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <p class="ncua-caution-text js-otp-error mgt5" style="display:none;"><em></em></p> | |
| </div> | |
| </td> | |
| </tr> | |
| <?php else: ?> | |
| <tr> | |
| <th><div>인증수단</div></th> | |
| <td><span class="ncua-caution-text js-no-target"><em><?= nl2br(gd_htmlspecialchars($noAuthTargetMessage)) ?></em></span></td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="text-center mgt20"> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--secondary-gray js-layer-close"><span class="ncua-btn__label">닫기</span></button> | |
| <?php if (!empty($hasAuthTarget)): ?> | |
| <button type="button" class="ncua-btn ncua-btn--sm ncua-btn--primary js-complete-occupancy-auth"><span class="ncua-btn__label">인증 완료</span></button> | |
| <?php endif; ?> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| (function () { | |
| var PS_URL = '../share/occupancy_auth_ps.php'; | |
| var LAYER_URL = '../share/layer_occupancy_auth.php'; | |
| var CODE_INPUT = '#occupancyAuthCode'; | |
| var CODE_PATTERN = /^[0-9]{1,6}$/; | |
| var AUTH_CONTEXT = <?= json_encode($authContext, JSON_UNESCAPED_UNICODE) ?>; | |
| var popupSession = <?= (int)$popupSessionSeconds ?>; | |
| var otpTtl = <?= (int)$otpTtlSeconds ?>; | |
| var resendInterval = <?= (int)$resendIntervalSeconds ?>; | |
| var authTargets = <?= json_encode($authTypeTargets, JSON_UNESCAPED_UNICODE) ?>; | |
| var otpTimer = null; | |
| var restRemain = 0; | |
| var sessionHandle = null; | |
| var resendHandle = null; | |
| var resendRemain = 0; | |
| var lastSentAt = 0; | |
| function selectedType() { | |
| return $('input[name="occupancyAuthType"]:checked').val() || 'SMS'; | |
| } | |
| function updateAuthTargetDisplay() { | |
| $('.js-auth-target-display').text(authTargets[selectedType()] || ''); | |
| } | |
| function nowSec() { | |
| return Math.floor(Date.now() / 1000); | |
| } | |
| function pad2(value) { | |
| return value < 10 ? '0' + value : '' + value; | |
| } | |
| function renderStatus() { | |
| $('.js-otp-status').text(pad2(Math.floor(restRemain / 60)) + ':' + pad2(restRemain % 60)); | |
| } | |
| function renderResendCoolDown() { | |
| $('.js-resend-notice').text(resendInterval + '초 후에 다시 시도해주세요.'); | |
| } | |
| function clearResendCoolDown() { | |
| resendRemain = 0; | |
| if (resendHandle) { | |
| clearInterval(resendHandle); | |
| resendHandle = null; | |
| } | |
| $('.js-resend-notice').text(''); | |
| } | |
| function startResendCoolDown() { | |
| clearResendCoolDown(); | |
| resendRemain = resendInterval - (nowSec() - lastSentAt); | |
| if (resendRemain <= 0) { | |
| return; | |
| } | |
| renderResendCoolDown(); | |
| resendHandle = setInterval(tickResendCoolDown, 1000); | |
| } | |
| function tickResendCoolDown() { | |
| resendRemain -= 1; | |
| if (resendRemain <= 0) { | |
| clearResendCoolDown(); | |
| return; | |
| } | |
| renderResendCoolDown(); | |
| } | |
| function tickCountDown() { | |
| restRemain -= 1; | |
| if (restRemain <= 0) { | |
| restRemain = 0; | |
| stopOtpTimer(); | |
| $('.js-otp-status').text('00:00'); | |
| return; | |
| } | |
| renderStatus(); | |
| } | |
| function startOtpTimer() { | |
| stopOtpTimer(); | |
| restRemain = otpTtl; | |
| renderStatus(); | |
| otpTimer = setInterval(tickCountDown, 1000); | |
| } | |
| function stopOtpTimer() { | |
| if (otpTimer) { | |
| clearInterval(otpTimer); | |
| otpTimer = null; | |
| } | |
| } | |
| function stopTimers() { | |
| stopOtpTimer(); | |
| clearResendCoolDown(); | |
| if (sessionHandle) { | |
| clearTimeout(sessionHandle); | |
| sessionHandle = null; | |
| } | |
| } | |
| function startPopupSession() { | |
| if (sessionHandle) { | |
| clearTimeout(sessionHandle); | |
| } | |
| sessionHandle = setTimeout(function () { | |
| stopTimers(); | |
| if (typeof window.occupancyAuthMarkExpired === 'function') { | |
| window.occupancyAuthMarkExpired(); | |
| } | |
| // callback 은 [확인] 에서만 실행돼 백드롭 클릭으로 닫으면 만료된 인증 모달이 남는다 | |
| NCDSAlert({message: '인증시간이 만료되었습니다. 다시 시도해주세요.', iconType: 'error', onClose: closeLayer}); | |
| }, popupSession * 1000); | |
| } | |
| function clearError() { | |
| $('.js-otp-input').removeClass('destructive'); | |
| $('.js-otp-error').hide().find('em').text(''); | |
| } | |
| function showError(message) { | |
| $('.js-otp-input').addClass('destructive'); | |
| $('.js-otp-error').find('em').text(message); | |
| $('.js-otp-error').show(); | |
| } | |
| function showSendFailure(message) { | |
| $('.js-otp-error').find('em').text(message); | |
| $('.js-otp-error').show(); | |
| } | |
| function showResendRequired(message) { | |
| stopOtpTimer(); | |
| restRemain = 0; | |
| $('.js-otp-status').text('00:00'); | |
| showError(message); | |
| } | |
| function showLockedLayer() { | |
| stopTimers(); | |
| var $layerBody = $('.js-otp-error').closest('.bootstrap-dialog-message'); | |
| if ($layerBody.length === 0) { | |
| closeLayer(); | |
| return; | |
| } | |
| $.ajax({ | |
| url: LAYER_URL, | |
| data: {authContext: AUTH_CONTEXT}, | |
| cache: false, | |
| dataType: 'html' | |
| }).done(function (lockedHtml) { | |
| $layerBody.html(lockedHtml); | |
| }).fail(closeLayer); | |
| } | |
| function resetToInitial() { | |
| stopOtpTimer(); | |
| restRemain = 0; | |
| lastSentAt = 0; | |
| clearResendCoolDown(); | |
| $('.js-otp-code-row').hide(); | |
| clearError(); | |
| $(CODE_INPUT).val(''); | |
| $('.js-otp-status').text(''); | |
| $('.js-request-occupancy-otp').removeClass('ncua-btn--secondary-gray').addClass('ncua-btn--secondary'); | |
| $('.js-request-occupancy-otp .ncua-btn__label').text('인증번호 발송'); | |
| } | |
| function requestOtp() { | |
| if (lastSentAt > 0 && nowSec() - lastSentAt < resendInterval) { | |
| startResendCoolDown(); | |
| return; | |
| } | |
| clearError(); | |
| $.post(PS_URL, {mode: 'sendOtp', authType: selectedType(), authContext: AUTH_CONTEXT}, function (data) { | |
| if (data.success === false) { | |
| if ((data.data || {}).locked) { | |
| showLockedLayer(); | |
| return; | |
| } | |
| if ((data.data || {}).sendBlocked) { | |
| NCDSAlert({message: data.message, iconType: 'error'}); | |
| return; | |
| } | |
| showSendFailure(data.message); | |
| return; | |
| } | |
| lastSentAt = nowSec(); | |
| clearError(); | |
| $('.js-otp-code-row').show(); | |
| $(CODE_INPUT).val('').focus(); | |
| $('.js-request-occupancy-otp').removeClass('ncua-btn--secondary').addClass('ncua-btn--secondary-gray'); | |
| $('.js-request-occupancy-otp .ncua-btn__label').text('재전송'); | |
| startOtpTimer(); | |
| }, 'json'); | |
| } | |
| function completeAuth() { | |
| var code = $(CODE_INPUT).val(); | |
| if (code === '') { | |
| showError('인증번호를 입력해 주세요.'); | |
| $(CODE_INPUT).focus(); | |
| return; | |
| } | |
| if (CODE_PATTERN.test(code) === false) { | |
| showError('인증번호가 일치하지 않습니다.'); | |
| $(CODE_INPUT).focus(); | |
| return; | |
| } | |
| $.post(PS_URL, {mode: 'verifyOtp', code: code, authContext: AUTH_CONTEXT}, function (data) { | |
| if (data.success) { | |
| if (typeof window.occupancyAuthCompleted === 'function') { | |
| window.occupancyAuthCompleted(); | |
| } | |
| closeLayer(); | |
| return; | |
| } | |
| var result = data.data || {}; | |
| if (result.expired) { | |
| stopTimers(); | |
| NCDSAlert({message: data.message, iconType: 'error', onClose: closeLayer}); | |
| return; | |
| } | |
| if (result.locked) { | |
| showLockedLayer(); | |
| return; | |
| } | |
| if (result.resendRequired) { | |
| showResendRequired(data.message); | |
| return; | |
| } | |
| showError(data.message); | |
| $(CODE_INPUT).focus(); | |
| }, 'json'); | |
| } | |
| function closeLayer() { | |
| stopTimers(); | |
| $('.js-layer-close').trigger('click'); | |
| } | |
| $('.js-layer-close').on('click', stopTimers); | |
| $('.js-request-occupancy-otp').on('click', requestOtp); | |
| $('.js-complete-occupancy-auth').on('click', completeAuth); | |
| $(CODE_INPUT).on('input', clearError); | |
| $('input[name="occupancyAuthType"]').on('change', function () { | |
| updateAuthTargetDisplay(); | |
| resetToInitial(); | |
| }); | |
| updateAuthTargetDisplay(); | |
| if (window.occupancyAuthCleanup) { | |
| window.occupancyAuthCleanup(); | |
| } | |
| startPopupSession(); | |
| window.occupancyAuthCleanup = stopTimers; | |
| })(); | |
| </script> | |
| <?php endif; ?> | |
변경 전layer_sms_contents.php
| <div> | |
| <div class="table-title gd-help-manual">SMS / LMS 문구 등록</div> | |
| <table class="table table-cols"> | |
| <tr class="text-center"> | |
| <td><input name="sno[]" type="checkbox" value="<?php echo $val['sno']; ?>" /></td> | |
| <td class="font-num"><?php echo number_format($page->idx--); ?></td> | |
| <td><?php echo $arrGroup[$val['smsAutoCode']]; ?></td> | |
| <td class="smsContents-<?php echo $val['sno']; ?>"><?php echo $val['contents']; ?></td> | |
| <td><input type="button" value="선택" class="btn btn-sm btn-black js-sms-contents-select" data-sno="<?php echo $val['sno']; ?>" /></span></td> | |
| </tr> | |
| // 글자수 체크 | |
| $('textarea[name=smsContentsText]').keyup(getSmsContentsLength).change(getSmsContentsLength); | |
| // SMS 문구 등록 | |
| $('.js-sms-contents-register').click(function (e) { | |
| var smsContentsGroup = $('select[name=\'smsContentsGroup\']').val(); | |
| var smsContentsText = $('textarea[name=\'smsContentsText\']').val(); | |
| if (smsContentsGroup == '' || smsContentsGroup == '0') { | |
| alert('그룹을 선택해 주세요.'); | |
| return; | |
| } | |
| if (smsContentsText == '') { | |
| alert('내용을 작성해 주세요.'); | |
| return; | |
| } | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| alert('등록시 오류가 발생하였습니다.'); | |
| } | |
| }); | |
| }); | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| alert('삭제시 오류가 발생하였습니다.'); | |
| } | |
| }); | |
| } | |
변경 후layer_sms_contents.php
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-emoji.js') ?>"></script> | |
| <div> | |
| <div class="table-title gd-help-manual">SMS / LMS 문구 등록</div> | |
| <table class="table table-cols"> | |
| <tr class="text-center"> | |
| <td><input name="sno[]" type="checkbox" value="<?php echo $val['sno']; ?>" /></td> | |
| <td class="font-num"><?php echo number_format($page->idx--); ?></td> | |
| <td><?= $arrGroup[$val['smsAutoCode']] ?? '' ?></td> | |
| <td class="smsContents-<?php echo $val['sno']; ?>"><?php echo $val['contents']; ?></td> | |
| <td><input type="button" value="선택" class="btn btn-sm btn-black js-sms-contents-select" data-sno="<?php echo $val['sno']; ?>" /></span></td> | |
| </tr> | |
| // 글자수 체크 | |
| $('textarea[name=smsContentsText]').keyup(getSmsContentsLength).change(getSmsContentsLength); | |
| smsEmoji.bind('textarea[name=smsContentsText]', getSmsContentsLength); | |
| // SMS 문구 등록 | |
| $('.js-sms-contents-register').click(function (e) { | |
| var smsContentsGroup = $('select[name=\'smsContentsGroup\']').val(); | |
| var smsContentsText = $('textarea[name=\'smsContentsText\']').val(); | |
| if (smsContentsGroup == '' || smsContentsGroup == '0') { | |
| NCDSAlert({message: '그룹을 선택해 주세요.'}); | |
| return; | |
| } | |
| if (smsContentsText == '') { | |
| NCDSAlert({message: '내용을 작성해 주세요.'}); | |
| return; | |
| } | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| NCDSAlert({message: data && data != 'ERROR' ? data : '등록시 오류가 발생하였습니다.', iconType: 'error'}); | |
| } | |
| }); | |
| }); | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| // 자동알림 사용 건만 제외하고 삭제된 경우에도 문구가 오므로 닫을 때 목록을 다시 읽는다 | |
| NCDSAlert({ | |
| message: data && data != 'ERROR' ? data : '삭제시 오류가 발생하였습니다.', | |
| iconType: 'error', | |
| onClose: function () { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
변경 전DBTableField.php
| // @formatter:off | |
| $arrField = [ | |
| ['val' => 'sno', 'typ' => 'i', 'def' => null], // 일련번호 | |
| ['val' => 'smsType', 'typ' => 's', 'def' => 'user'], // SMS 전송타입 (order, member, promotion, user) | |
| ['val' => 'smsAutoCode', 'typ' => 's', 'def' => null], // SMS 자동발송 코드 | |
| ['val' => 'smsAutoType', 'typ' => 's', 'def' => null], // SMS 자동발송 수신 종류 (member, admin, provider) | |
변경 후DBTableField.php
| // @formatter:off | |
| $arrField = [ | |
| ['val' => 'sno', 'typ' => 'i', 'def' => null], // 일련번호 | |
| ['val' => 'templateCode', 'typ' => 's', 'def' => null], | |
| ['val' => 'templateCategoryCode', 'typ' => 's', 'def' => null], | |
| ['val' => 'referenceSmsContentNo', 'typ' => 'i', 'def' => null], | |
| ['val' => 'approvalStatus', 'typ' => 's', 'def' => 'PENDING'], | |
| ['val' => 'autoConversionFl', 'typ' => 's', 'def' => 'n'], | |
| ['val' => 'smsType', 'typ' => 's', 'def' => 'user'], // SMS 전송타입 (order, member, promotion, user) | |
| ['val' => 'smsAutoCode', 'typ' => 's', 'def' => null], // SMS 자동발송 코드 | |
| ['val' => 'smsAutoType', 'typ' => 's', 'def' => null], // SMS 자동발송 수신 종류 (member, admin, provider) | |
변경 전Manager.php
| if ($sessionManager['managerId'] === $arrData['managerId']) { | |
| $sessionManager['isSmsAuth'] = $arrData['isSmsAuth']; | |
| $sessionManager['isEmailAuth'] = $arrData['isEmailAuth']; | |
| $sessionManager['email'] = $arrData['email']; | |
| $sessionManager['cellPhone'] = $arrData['cellPhone']; | |
| $session->set(self::SESSION_MANAGER_LOGIN, $sessionManager); | |
| } | |
| if ($smsRoleGuard->isDelegateOrHigher() === false) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.')); | |
| } | |
| if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) { | |
| throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.')); | |
| } | |
| */ | |
| protected function setAuthenticationInfo(&$arrData, $smsAuth, $emailAuth) | |
| { | |
| if ($smsAuth['isAdminSmsAuth'] === true) { | |
| if (str_replace('-', '', $smsAuth['authCellPhone']) !== str_replace('-', '', $arrData['cellPhone'])) { | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| $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'; | |
변경 후Manager.php
| if ($sessionManager['managerId'] === $arrData['managerId']) { | |
| $sessionManager['isSmsAuth'] = $arrData['isSmsAuth']; | |
| $sessionManager['isEmailAuth'] = $arrData['isEmailAuth']; | |
| if (array_key_exists('email', $arrData)) { | |
| $sessionManager['email'] = $arrData['email']; | |
| } | |
| if (array_key_exists('cellPhone', $arrData)) { | |
| $sessionManager['cellPhone'] = $arrData['cellPhone']; | |
| } | |
| $session->set(self::SESSION_MANAGER_LOGIN, $sessionManager); | |
| } | |
| if ($smsRoleGuard->isDelegateOrHigher() === false) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.')); | |
| } | |
| if ($smsRoleService->isUncertifiedDelegateActor()) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>본인인증을 완료해야 발신관리위임자 역할을 수행할 수 있습니다.<br>운영자 수정 페이지에서 본인인증을 먼저 완료하시기 바랍니다.')); | |
| } | |
| if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) { | |
| throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.')); | |
| } | |
| */ | |
| protected function setAuthenticationInfo(&$arrData, $smsAuth, $emailAuth) | |
| { | |
| $isAuthCellPhoneReplaced = $smsAuth['isAdminSmsAuth'] === true | |
| && str_replace('-', '', $smsAuth['authCellPhone']) !== str_replace('-', '', $arrData['cellPhone']); | |
| // 수정 저장에서 점유인증 뒤 번호를 다시 고치면 미인증으로 보고 기존 번호를 유지한다 | |
| if ($isAuthCellPhoneReplaced && ($arrData['mode'] ?? '') === 'modify') { | |
| $smsAuth['isAdminSmsAuth'] = false; | |
| } | |
| if ($smsAuth['isAdminSmsAuth'] === true) { | |
| if ($isAuthCellPhoneReplaced) { | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| $adminSecuritySmsAuthNumber = Otp::getOtp(8); | |
| $contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber); | |
| $isSent = \App::getInstance(OccupancyOtpSmsService::class)->send($managerInfo['cellPhone'], $contents); | |
| $smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber; | |
| $smsAuth['message'] = $isSent ? 'OK' : 'SMS Send Fail'; | |
변경 전SmsAdmin.php
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Respect\Validation\Validator; | |
| use Origin\Service\Member\CrmGroupService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| public function __construct($config = []) | |
| { | |
| // 기존 DBTool 파라미터를 보장하기 위한 로직 | |
| */ | |
| public function saveSmsAuto($request) | |
| { | |
| // 디비에 저장될 항목을 정리 (insert , update) | |
| $smsAutoReceiveKind = Sms::SMS_AUTO_RECEIVE_LIST; | |
| $smsAutoType = [ | |
| 'member', | |
| 'admin', | |
| 'provider', | |
| 'board', | |
| 'recipient', | |
| ]; | |
| foreach ($smsAutoReceiveKind as $smsType => $val) { | |
| foreach ($request[$smsType] as $autoCode => $autoData) { | |
| foreach ($smsAutoType as $autoType) { | |
| if (isset($autoData[$autoType . 'Mode'])) { | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['sno'] = $autoData[$autoType . 'Sno']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['subject'] = $autoData['subject']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['contents'] = $autoData[$autoType . 'Contents']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['messageTitle'] = gd_isset($autoData[$autoType . 'MessageTitle'], ''); | |
| // $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['smsType'] = $smsType; | |
| unset($request[$smsType][$autoCode][$autoType . 'Mode']); | |
| unset($request[$smsType][$autoCode][$autoType . 'Sno']); | |
| unset($request[$smsType][$autoCode]['subject']); | |
| unset($request[$smsType][$autoCode][$autoType . 'Contents']); | |
| unset($request[$smsType][$autoCode][$autoType . 'MessageTitle']); | |
| } | |
| } | |
| } | |
| } | |
| // 디비 정보 저장 | |
| foreach ($dbData as $saveMode => $tmpData1) { | |
| foreach ($tmpData1 as $smsType => $tmpData2) { | |
| foreach ($tmpData2 as $smsAutoCode => $tmpData3) { | |
| foreach ($tmpData3 as $smsAutoType => $saveData) { | |
| $arrBind = []; | |
| // 저장 방법 | |
| if ($saveMode === 'insert') { | |
| $saveData['groupNm'] = __('자동발송'); | |
| $saveData['smsAutoCode'] = $smsAutoCode; | |
| $saveData['smsAutoType'] = $smsAutoType; | |
| $saveData['smsType'] = $smsType; | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $saveData, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| } elseif ($saveMode === 'update') { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $saveData, 'update', gd_array_keys($saveData)); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $saveData['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno=?', $arrBind['bind']); | |
| } | |
| unset($arrBind); | |
| } | |
| } | |
| } | |
| } | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| } else { | |
| $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\', \'regular\', \'present\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL'; | |
| } | |
| $this->db->strOrder = 'smsType ASC'; | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . array_shift($query) . ' ' . gd_implode(' ', $query); | |
| $getData = gd_htmlspecialchars_stripslashes($this->db->query_fetch($strSQL, null, false)); | |
| foreach ($getData as $data) { | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['contents'] = $data['contents']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['messageTitle'] = $data['messageTitle']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['sno'] = $data['sno']; | |
| } | |
| unset($getData); | |
| // SMS 자동발송 설정값 불러오기 | |
| $smsAutoType[$keyType][$key][$sendType . 'Title'] = $messageTitle; // CRM 뷰 호환(messageTitle 일원화) | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'update'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = $setData[$keyType][$val['code']][$sendType]['sno']; | |
| } else { | |
| $smsAutoType[$keyType][$key][$sendType . 'Contents'] = $val[$sendType . 'Contents']; | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'Title'] = ''; // CRM 뷰 호환 | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'insert'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = ''; | |
| } | |
| } | |
| // 기본 검색 | |
| $arrWhere[] = 'smsType = ?'; | |
| $this->db->bind_param_push($arrBind, 's', $smsType); | |
| // 키워드 검색 | |
| if ($search['key'] && $search['keyword']) { | |
| } | |
| $postValue['smsType'] = 'user'; | |
| StringUtils::strIsSet($postValue['subject'], ''); | |
| if (gd_array_key_exists('sno', $postValue)) { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'update'); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $postValue['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno = ?', $arrBind['bind']); | |
| } else { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| } | |
| return true; | |
변경 후SmsAdmin.php
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Respect\Validation\Validator; | |
| use Origin\Service\Member\CrmGroupService; | |
| use Origin\Enum\Crm\Message\SmsTemplateApprovalStatus; | |
| use Origin\Repository\Member\Sms\SmsContentsRepository; | |
| use Origin\Service\Crm\Message\SmsTemplateService; | |
| use Origin\Service\Sms\AutoSend\SaveAutoSendContentsService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| const SMS_AUTO_TYPES = [ | |
| 'member', | |
| 'admin', | |
| 'provider', | |
| 'board', | |
| 'recipient', | |
| ]; | |
| public function __construct($config = []) | |
| { | |
| // 기존 DBTool 파라미터를 보장하기 위한 로직 | |
| */ | |
| public function saveSmsAuto($request) | |
| { | |
| /** @var SaveAutoSendContentsService $saveAutoSendContentsService */ | |
| $saveAutoSendContentsService = \App::getInstance(SaveAutoSendContentsService::class); | |
| $request = $saveAutoSendContentsService->saveAutoSendContents( | |
| $request, | |
| array_keys(Sms::SMS_AUTO_RECEIVE_LIST), | |
| self::SMS_AUTO_TYPES | |
| ); | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| } else { | |
| $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\', \'regular\', \'present\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL'; | |
| } | |
| $this->db->strOrder = 'smsType ASC, sno ASC'; | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . array_shift($query) . ' ' . gd_implode(' ', $query); | |
| $getData = gd_htmlspecialchars_stripslashes($this->db->query_fetch($strSQL, null, false)); | |
| foreach ($getData as $data) { | |
| if ((int)($data['referenceSmsContentNo'] ?? 0) === SmsContentsRepository::CLEARED_REFERENCE_SMS_CONTENT_NO) { | |
| $data['contents'] = ''; | |
| $data['messageTitle'] = ''; | |
| } | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['contents'] = $data['contents']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['messageTitle'] = $data['messageTitle']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['sno'] = $data['sno']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['templateCategoryCode'] = $data['templateCategoryCode']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['referenceSmsContentNo'] = $data['referenceSmsContentNo'] ?? null; | |
| } | |
| unset($getData); | |
| // SMS 자동발송 설정값 불러오기 | |
| $smsAutoType[$keyType][$key][$sendType . 'Title'] = $messageTitle; // CRM 뷰 호환(messageTitle 일원화) | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'update'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = $setData[$keyType][$val['code']][$sendType]['sno']; | |
| $smsAutoType[$keyType][$key][$sendType . 'TemplateCategoryCode'] = gd_isset($setData[$keyType][$val['code']][$sendType]['templateCategoryCode'], ''); | |
| $smsAutoType[$keyType][$key][$sendType . 'ReferenceSmsContentNo'] = gd_isset($setData[$keyType][$val['code']][$sendType]['referenceSmsContentNo'], ''); | |
| } else { | |
| $smsAutoType[$keyType][$key][$sendType . 'Contents'] = $val[$sendType . 'Contents']; | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'Title'] = ''; // CRM 뷰 호환 | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'insert'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'TemplateCategoryCode'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'ReferenceSmsContentNo'] = ''; | |
| } | |
| } | |
| // 기본 검색 | |
| $arrWhere[] = 'smsType = ?'; | |
| $this->db->bind_param_push($arrBind, 's', $smsType); | |
| $arrWhere[] = 'approvalStatus = ?'; | |
| $this->db->bind_param_push($arrBind, 's', SmsTemplateApprovalStatus::APPROVED->value); | |
| // 키워드 검색 | |
| if ($search['key'] && $search['keyword']) { | |
| } | |
| $postValue['smsType'] = 'user'; | |
| StringUtils::strIsSet($postValue['subject'], ''); | |
| /** @var SmsTemplateService $smsTemplateService */ | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $postValue = $smsTemplateService->normalizeSendText( | |
| $postValue, | |
| gd_array_key_exists('sno', $postValue) ? (int)$postValue['sno'] : null | |
| ); | |
| $contents = $postValue['contents']; | |
| if (gd_array_key_exists('sno', $postValue)) { | |
| $smsTemplateService->assertSavable((int)$postValue['sno']); | |
| $modifyResult = $smsTemplateService->resolveModifyResult( | |
| (int)$postValue['sno'], | |
| $contents, | |
| isset($postValue['subject']) ? (string)$postValue['subject'] : null, | |
| isset($postValue['messageTitle']) ? (string)$postValue['messageTitle'] : null | |
| ); | |
| if ($modifyResult['status'] !== null) { | |
| $postValue['approvalStatus'] = $modifyResult['status']; | |
| } | |
| if ($modifyResult['contentsChanged'] === false) { | |
| unset($postValue['contents']); | |
| } | |
| if (!$modifyResult['messageTitleChanged']) { | |
| unset($postValue['messageTitle']); | |
| } | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'update', gd_array_keys($postValue)); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $postValue['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno = ?', $arrBind['bind']); | |
| if ($modifyResult['modified']) { | |
| $smsTemplateService->writeModifiedHistory((int)$postValue['sno']); | |
| } | |
| } else { | |
| $postValue['approvalStatus'] = SmsTemplateApprovalStatus::PENDING->value; | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| $smsTemplateService->issueTemplateCodeWithHistory((int)$this->db->insert_id()); | |
| } | |
| return true; | |
변경 전SmsAuto.php
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Enum\Crm\Message\FailedMessageSendType; | |
| use Origin\Enum\Crm\Message\SmsAutoSendOverType; | |
| use Origin\Service\AutoSend\AutoSendMyappService; | |
| use Origin\Service\AutoSend\AutoSendTemplateVariableReplacer; | |
| use Origin\Service\AutoSend\FindAutoSendConfigService; | |
| { | |
| $logger = $this->logger; | |
| $binds = []; | |
| $this->db->strField = 'smsType, smsAutoType, contents, subject, messageTitle'; | |
| $this->db->strJoin = DB_SMS_CONTENTS; | |
| $this->db->strWhere = 'smsType= ? AND smsAutoCode = ? AND smsAutoType IN (\'' . gd_implode('\', \'', $wheres) . '\') AND contents != \'\' AND contents IS NOT NULL'; | |
| $this->db->bind_param_push($binds, 's', $this->smsType); | |
| $this->db->bind_param_push($binds, 's', $this->smsAutoCodeType); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . array_shift($query) . ' ' . gd_implode(' ', $query); | |
| $getData = $this->db->query_fetch($strSQL, $binds); | |
| throw new \Exception(__('자동발송 내용을 찾을 수 없습니다.')); | |
| } | |
| $getData = StringUtils::htmlSpecialCharsStripSlashes($getData); | |
| return $getData; | |
| } | |
| /** | |
변경 후SmsAuto.php
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Enum\Crm\Message\FailedMessageSendType; | |
| use Origin\Enum\Crm\Message\SmsAutoSendOverType; | |
| use Origin\Enum\Crm\Message\SmsTemplateApprovalStatus; | |
| use Origin\Service\AutoSend\AutoSendMyappService; | |
| use Origin\Service\AutoSend\AutoSendTemplateVariableReplacer; | |
| use Origin\Service\AutoSend\FindAutoSendConfigService; | |
| { | |
| $logger = $this->logger; | |
| $binds = []; | |
| $approvedStatus = SmsTemplateApprovalStatus::APPROVED->value; | |
| $latestApprovedOf = static fn(string $column): string => 'SELECT stah.' . $column | |
| . ' FROM ' . DB_SMS_TEMPLATE_APPROVAL_HISTORY . ' stah' | |
| . ' WHERE stah.smsContentNo = rsc.sno' | |
| . ' AND stah.approvalStatus = ?' | |
| . ' ORDER BY stah.historyNo DESC LIMIT 1'; | |
| $latestApproved = $latestApprovedOf('templateContent'); | |
| $latestApprovedTitle = $latestApprovedOf('messageTitle'); | |
| $isApproved = 'rsc.approvalStatus = ?'; | |
| $field = []; | |
| $field[] = 'sc.smsType'; | |
| $field[] = 'sc.smsAutoType'; | |
| $field[] = 'IF(' . $isApproved . ', rsc.contents, (' . $latestApproved . ')) AS contents'; | |
| $field[] = 'sc.subject'; | |
| $field[] = 'IF(' . $isApproved . ', rsc.messageTitle, COALESCE((' . $latestApprovedTitle . '), rsc.messageTitle)) AS messageTitle'; | |
| $where = []; | |
| $where[] = 'sc.smsType = ?'; | |
| $where[] = 'sc.smsAutoCode = ?'; | |
| $where[] = 'sc.smsAutoType IN (\'' . gd_implode('\', \'', $wheres) . '\')'; | |
| $where[] = 'rsc.contents != \'\''; | |
| $where[] = 'rsc.contents IS NOT NULL'; | |
| $where[] = '(' . $isApproved . ' OR (' . $latestApproved . ') IS NOT NULL)'; | |
| $this->db->strField = gd_implode(', ', $field); | |
| $this->db->strJoin = DB_SMS_CONTENTS . ' sc INNER JOIN ' . DB_SMS_CONTENTS . ' rsc ON rsc.sno = COALESCE(sc.referenceSmsContentNo, sc.sno)'; | |
| $this->db->strWhere = gd_implode(' AND ', $where); | |
| $this->db->strOrder = 'sc.sno ASC'; | |
| // query_complete() 가 SELECT → FROM → WHERE 순으로 조립하므로 바인드도 그 순서로 밀어 넣는다 | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $this->smsType); | |
| $this->db->bind_param_push($binds, 's', $this->smsAutoCodeType); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . array_shift($query) . ' ' . gd_implode(' ', $query); | |
| $getData = $this->db->query_fetch($strSQL, $binds); | |
| throw new \Exception(__('자동발송 내용을 찾을 수 없습니다.')); | |
| } | |
| $getData = StringUtils::htmlSpecialCharsStripSlashes($getData); | |
| $latestByAutoType = []; | |
| foreach ($getData as $row) { | |
| $latestByAutoType[$row['smsAutoType']] = $row; | |
| } | |
| return array_values($latestByAutoType); | |
| } | |
| /** | |
변경 전AutoSendConfigController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use App; | |
| use Bundle\Component\Sms\Code; | |
| use Bundle\Component\Sms\SmsAutoCode; | |
| use Bundle\Controller\Admin\Controller; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| use GuzzleHttp\Exception\ConnectException; | |
| use Origin\Enum\AutoSend\AutoSendChannelPolicy; | |
| use Origin\Enum\AutoSend\AutoSendRecipient; | |
| use Origin\Enum\AutoSend\AutoSendRecipientPolicy; | |
| use Origin\Enum\AutoSend\AutoSendOptionVisibilityPolicy; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Enum\AutoSend\AutoSendChannel; | |
| use Origin\Enum\AutoSend\AutoSendTemplateVariableCatalog; | |
| use Origin\Service\AutoSend\FindAutoSendCodeConfigService; | |
| use Origin\Service\AutoSend\FindAutoSendConfigService; | |
| use Origin\Service\Crm\Message\MyappPushTemplateService; | |
| use Origin\Service\Crm\Message\SmsPasswordService; | |
| use Request; | |
| class AutoSendConfigController extends Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $code = Request::get()->get('code'); | |
| $isBlock = false; | |
| $blockMode = null; | |
| $validAutoSendSms = false; | |
| if (!AutoSendSupport::canSendIfClaim($code)) { | |
| $isBlock = true; | |
| $blockMode = 'claim'; | |
| } | |
| try { | |
| $validAutoSendSms = $this->validAutoSendSms(); | |
| } catch (ConnectException) { | |
| $isBlock = true; | |
| $blockMode = 'smsConnect'; | |
| } | |
| $this->applyBlockPage(); | |
| if ($isBlock) { | |
| $this->setData('blockMode', $blockMode); | |
| } else { | |
| /** @var SmsAutoCode $smsAutoCode */ | |
| $smsAutoCode = \App::load('\\Component\\Sms\\SmsAutoCode'); | |
| $groupCode = $smsAutoCode->getByCode($code); | |
| $recipients = AutoSendRecipientPolicy::getCodesWithRecipientsWithBoard()[$code] ?? []; | |
| $channels = AutoSendChannelPolicy::getCodesWithChannelsWithBoard()[$code] ?? []; | |
| /** @var FindAutoSendCodeConfigService $codeConfigService */ | |
| $codeConfigService = App::getInstance(FindAutoSendCodeConfigService::class); | |
| [$options, $autoSendConfigsResponse, $channelCodeConfigDto] = $codeConfigService->createRecipientConfigByCode($code); | |
| /** @var FindAutoSendConfigService $configService */ | |
| $configService = App::getInstance(FindAutoSendConfigService::class); | |
| $crmMyappConfig = $configService->getCrmMyappConfig(); | |
| $kakaoCrmConfig = $configService->getCrmKakaoConfig(); | |
| $autoSendOptionVisibility = AutoSendOptionVisibilityPolicy::getOptionVisibilityByCode($code) | |
| ?? AutoSendOptionVisibilityPolicy::getDefaultBoardVisibility($code); | |
| $checkedRecipients = $this->collectGroupsHavingAutoSendEnabled($autoSendConfigsResponse); | |
| $shouldChangeKakaoTemplate = in_array($kakaoCrmConfig['sender'] ?? '', ['kakaoAlrim', 'kakaoAlrimCloud'], true); | |
| $kakaoVendorLabel = match ($kakaoCrmConfig['sender'] ?? '') { | |
| 'kakaoAlrimCloud' => '클라우드', | |
| 'kakaoAlrim' => '비즈엠', | |
| 'kakaoAlrimLuna' => '블룸에이아이', | |
| default => '', | |
| }; | |
| /** @var MyappPushTemplateService $myappPushTemplateService */ | |
| $myappPushTemplateService = App::getInstance(MyappPushTemplateService::class); | |
| $this->callMenu('crm', 'messageSend', 'autoSendConfig'); | |
| $this->setData('mode', 'save'); | |
| $this->setData('code', $code); | |
| $this->setData('groupCode', $groupCode); | |
| $this->setData('displayOptions', $autoSendOptionVisibility->bindToDisplay()); | |
| $this->setData('options', $options); | |
| $this->setData('defaultRecipient', AutoSendRecipientPolicy::getDefaultRecipient($code)); | |
| $this->setData('recipientTitles', $this->buildRecipientTitles($recipients)); | |
| $this->setData('forceCheckRecipients', $this->getForceCheckRecipients($code)); | |
| $this->setData('recipients', $recipients); | |
| $this->setData('channels', $channels); | |
| $this->setData('useJoinPolicy', AutoSendSupport::useJoinPolicy()); | |
| $this->setData('myappSupportRecipients', ['member']); | |
| $this->setData('kakaoCrmUseFlag', ($kakaoCrmConfig['useFlag'] ?? '') === 'y'); | |
| $this->setData('myappCrmUseFlag', ($crmMyappConfig['useFlag'] ?? '') === 'y'); | |
| $this->setData('isKakaoAlternativeBySms', ($kakaoCrmConfig['autoSmsAlternativeSendFlag'] ?? '') === 'y' && ($kakaoCrmConfig['autoSmsAlternativeSendType'] ?? '') === 'SMS'); | |
| $this->setData('isMyappAlternativeBySms', ($crmMyappConfig['autoSmsAlternativeSendFlag'] ?? '') === 'y' && ($crmMyappConfig['autoSmsAlternativeSendType'] ?? '') === 'SMS'); | |
| $this->setData('validAutoSendSms', $validAutoSendSms); | |
| $this->setData('checkedRecipients', $checkedRecipients); | |
| $this->setData('autoSendConfigsResponse', $autoSendConfigsResponse); | |
| $this->setData('channelTemplateVariables', [ | |
| AutoSendChannel::SMS->name => AutoSendTemplateVariableCatalog::groupCodeVariablesByCategoryAndChannel($code, AutoSendChannel::SMS, true), | |
| AutoSendChannel::KAKAO_ALRIM_TALK->name => AutoSendTemplateVariableCatalog::groupCodeVariablesByCategoryAndChannel($code, AutoSendChannel::KAKAO_ALRIM_TALK, true), | |
| AutoSendChannel::MYAPP_PUSH->name => AutoSendTemplateVariableCatalog::groupCodeVariablesByCategoryAndChannel($code, AutoSendChannel::MYAPP_PUSH, true), | |
| ]); | |
| $this->setData('groupedTemplateAllVariables', AutoSendTemplateVariableCatalog::groupVariablesByCategory(true)); | |
| $this->setData('shouldChangeKakaoTemplate', $shouldChangeKakaoTemplate); | |
| $this->setData('kakaoTemplates', $channelCodeConfigDto->kakaoTemplates); | |
| $this->setData('kakaoVendorLabel', $kakaoVendorLabel); | |
| $this->setData('myappDomain', $myappPushTemplateService->getMyappDomainUrl()); | |
| $this->getView()->setDefine('autoSendConfigOptions', 'crm/auto_send_config/auto_send_config_options.php'); | |
| $this->getView()->setDefine('autoSendConfigContents', 'crm/auto_send_config/auto_send_config_contents.php'); | |
| $this->getView()->setDefine('autoSendConfigPreview', 'crm/auto_send_config/auto_send_config_preview.php'); | |
| } | |
| } | |
| private function buildRecipientTitles(array $recipients): array | |
| { | |
| $titles = []; | |
| foreach ($recipients as $recipient) { | |
| $titles[strtolower($recipient->name)] = $recipient->getTitle(); | |
| } | |
| return $titles; | |
| } | |
| /** | |
| * @throws ConnectException | |
| */ | |
| private function validAutoSendSms(): bool | |
| { | |
| try { | |
| /** @var SmsPasswordService $smsPasswordService */ | |
| $smsPasswordService = App::getInstance(SmsPasswordService::class); | |
| $smsPassword = $smsPasswordService->getSmsPassword(true); | |
| if (empty($smsPassword)) { | |
| return false; | |
| } | |
| if (empty(gd_policy('sms.config')['smsCallNum'])) { | |
| return false; | |
| } | |
| return $smsPasswordService->verifySmsPassword($smsPassword); | |
| } catch (ConnectException $e) { | |
| throw $e; | |
| } catch (\Exception) { | |
| return false; | |
| } | |
| } | |
| private function collectGroupsHavingAutoSendEnabled(array $autoSendConfigsResponse): array | |
| { | |
| $result = []; | |
| foreach ($autoSendConfigsResponse as $groupKey => $channels) { | |
| foreach ($channels as $channel) { | |
| if (($channel['isAutoSend'] ?? null) === 'y') { | |
| $result[] = $groupKey; | |
| break; | |
| } | |
| } | |
| } | |
| return $result; | |
| } | |
| /** | |
| * 정책상 무조건 수신 대상 설정에 체크 상태로 있어야 하는 수신자 | |
| * @param string $code | |
| * @return array | |
| */ | |
| public function getForceCheckRecipients(mixed $code): array | |
| { | |
| $forceCheckedRecipients = []; | |
| if ($code == Code::PRESENT) { | |
| $forceCheckedRecipients[] = AutoSendRecipient::RECIPIENT; | |
| } | |
| return $forceCheckedRecipients; | |
| } | |
| /** | |
| * @return void | |
| */ | |
| public function applyBlockPage(): void | |
| { | |
| $this->getView()->setPageName('crm/auto_send_config.php'); | |
| $this->getView()->setDefine('autoSendConfigBlock', 'crm/auto_send_config/auto_send_config_block.php'); | |
| } | |
| } | |
변경 후AutoSendConfigController.php
변경 전LayerAutoSendModifyKakaoTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSendConfig; | |
| use App; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudMessageTemplateService; | |
| use Request; | |
| class LayerAutoSendModifyKakaoTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $templateCode = $request['templateCode'] ?? ''; | |
| // todo test | |
| $templateCode = 'custom_order_0011'; | |
| $this->setData('template', $template); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerAutoSendModifyKakaoTemplateController.php
변경 전LayerAutoSendSaveTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSendConfig; | |
| use Exception; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| use Request; | |
| class LayerAutoSendSaveTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $contents = $request['contents'] ?? ''; | |
| $title = $request['title'] ?? ''; | |
| $channel = $request['channel']; | |
| switch ($channel) { | |
| case 'MYAPP_PUSH': | |
| $categories = MyappPushTemplateCategory::getFindOptions(); | |
| break; | |
| case 'SMS': | |
| $categories = SmsTemplateCategory::getFindOptions(); | |
| break; | |
| default: | |
| throw new Exception("지원하지 않는 채널"); | |
| } | |
| $url = $request['url'] ?? ''; | |
| $image = $request['image'] ?? ''; | |
| $this->setData('channel', $channel); | |
| $this->setData('categories', $categories); | |
| $this->setData('contents', $contents); | |
| $this->setData('title', $title); | |
| $this->setData('url', $url); | |
| $this->setData('image', $image); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setPageName('crm/auto_send_config/layer_auto_send_save_template.php'); | |
| } | |
| } | |
변경 후LayerAutoSendSaveTemplateController.php
변경 전LayerAutoSendTemplateContentController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSendConfig; | |
| use Bundle\Component\Page\Page; | |
| use Exception; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| use Origin\Repository\Member\Myapp\MyappPushRepository; | |
| use Origin\Repository\Member\Sms\SmsContentsRepository; | |
| use Request; | |
| class LayerAutoSendTemplateContentController extends \Controller\Admin\Controller | |
| { | |
| private const PER_PAGE = 10; | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $channel = $request['channel']; | |
| $categoryValue = $request['category'] ?? ''; | |
| $keyword = $request['keyword'] ?? ''; | |
| $page = max(1, (int)($request['page'] ?? 1)); | |
| $offset = ($page - 1) * self::PER_PAGE; | |
| switch ($channel) { | |
| case 'MYAPP_PUSH': | |
| $category = MyappPushTemplateCategory::tryFrom($categoryValue); | |
| $repository = new MyappPushRepository(); | |
| $totalCount = $repository->searchCount($category, $keyword); | |
| $templates = array_map(function ($template) { | |
| return [ | |
| 'sno' => $template['pushSno'], | |
| 'subject' => $template['templateName'] ?? '', | |
| 'contents' => $template['pushContent'], | |
| 'title' => $template['pushSubject'] ?? '', | |
| 'myappImage' => $template['pushImage'], | |
| 'url' => $template['pushUrl'] ?? '', | |
| ]; | |
| }, $repository->searchPaginated($category, $keyword, self::PER_PAGE, $offset)); | |
| $categories = MyappPushTemplateCategory::getFindOptions(); | |
| break; | |
| case 'SMS': | |
| $category = SmsTemplateCategory::tryFrom($categoryValue); | |
| $repository = new SmsContentsRepository(); | |
| $totalCount = $repository->searchCount($category, $keyword); | |
| $templates = $repository->searchPaginated($category, $keyword, self::PER_PAGE, $offset); | |
| $categories = SmsTemplateCategory::getFindOptions(); | |
| break; | |
| default: | |
| throw new Exception(); | |
| } | |
| $page = new Page($page, $totalCount, $totalCount, self::PER_PAGE); | |
| $templates = array_map(function ($template) { | |
| $template['subject'] = nl2br(gd_htmlspecialchars_stripslashes($template['subject'])); | |
| $template['contents'] = gd_htmlspecialchars_stripslashes($template['contents']); | |
| return $template; | |
| }, $templates); | |
| $this->setData('channel', $channel); | |
| $this->setData('templates', $templates); | |
| $this->setData('categories', $categories); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setPageName('crm/auto_send_config/layer_auto_send_template_content.php'); | |
| } | |
| } | |
변경 후LayerAutoSendTemplateContentController.php
변경 전LayerAutoSendTemplateController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSendConfig; | |
| use Request; | |
| class LayerAutoSendTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $channel = $request['channel'] ?? 'SMS'; | |
| $this->setData('channel', $channel); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setPageName('crm/auto_send_config/layer_auto_send_template.php'); | |
| } | |
| } | |
변경 후LayerAutoSendTemplateController.php
변경 전AutoSendConfigPsController.php
| <?php | |
| namespace Bundle\Controller\Admin\Crm; | |
| use App; | |
| use Bundle\Component\Sms\SmsAutoCode; | |
| use Exception; | |
| use Origin\DTO\AutoSend\AutoSendOptionCodeConfigDto; | |
| use Origin\Enum\AutoSend\AutoSendChannel; | |
| use Origin\Enum\AutoSend\AutoSendChannelPolicy; | |
| use Origin\Enum\AutoSend\AutoSendRecipient; | |
| use Origin\Enum\AutoSend\AutoSendRecipientPolicy; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Service\AutoSend\FindAutoSendConfigService; | |
| use Origin\Service\AutoSend\Request\UpdateAutoSendConfigRequest; | |
| use Origin\Service\AutoSend\SaveAutoSendConfigService; | |
| use Origin\Service\AutoSend\SaveAutoSendTemplateService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSyncService; | |
| use Request; | |
| class AutoSendConfigPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $files = Request::files()->toArray(); | |
| $mode = $request['mode'] ?? ''; // todo throw | |
| $code = $request['code']; | |
| $options = $request['options'] ?? []; | |
| try { | |
| switch ($mode) { | |
| case 'save': | |
| $configOptions = AutoSendOptionCodeConfigDto::createCodeConfigByOption($code, $options); | |
| /** @var SmsAutoCode $smsAutoCode */ | |
| $smsAutoCode = App::load('Component\\Sms\\SmsAutoCode'); | |
| $smsAutoCode->getCodes(); | |
| /** @var SaveAutoSendConfigService $saveAutoSendConfigService */ | |
| $saveAutoSendConfigService = App::getInstance(SaveAutoSendConfigService::class); | |
| $channels = AutoSendChannelPolicy::getCodesWithChannelsWithBoard()[$code] ?? []; | |
| /** @var SaveAutoSendTemplateService $saveAutoSendTemplateService */ | |
| $saveAutoSendTemplateService = App::getInstance(SaveAutoSendTemplateService::class); | |
| $recipientFiles = $files['recipients'] ?? []; | |
| $flagMap = []; | |
| $templateItems = []; | |
| foreach ($request['recipients'] as $requestRecipientName => $requestRecipientConfig) { | |
| $autoSendRecipient = AutoSendRecipient::fromName(strtoupper($requestRecipientName)); | |
| foreach ($channels as $channel) { | |
| $channelConfig = $requestRecipientConfig[$channel->name] ?? []; | |
| if (!empty($channelConfig)) { | |
| $imageMode = $channelConfig['imageMode'] ?? null; | |
| if ($imageMode === 'FILE') { | |
| $uploadedFile = $this->extractUploadedFile($recipientFiles, $requestRecipientName, $channel->name); | |
| if ($uploadedFile !== null) { | |
| $channelConfig['uploadedImage'] = $uploadedFile; | |
| } | |
| } | |
| $flagMap[] = [$channel, $requestRecipientName, $channelConfig['isEnabled'] ?? 'n']; | |
| $templateItems[] = [ | |
| 'channel' => $channel, | |
| 'recipient' => $autoSendRecipient, | |
| 'config' => $channelConfig, | |
| ]; | |
| } | |
| } | |
| } | |
| $insertedSnoMap = \DB::transaction(function () use ($saveAutoSendConfigService, $code, $flagMap, $configOptions, $saveAutoSendTemplateService, $templateItems) { | |
| $saveAutoSendConfigService->updateAutoSendConfigBatch($code, $flagMap, $configOptions); | |
| $insertedSnoMap = $saveAutoSendTemplateService->updateTemplatesBatch($code, $templateItems); | |
| /** @var FindAutoSendConfigService $findAutoSendConfigService */ | |
| $findAutoSendConfigService = App::getInstance(FindAutoSendConfigService::class); | |
| if ($findAutoSendConfigService->getActiveCrmKakaoAlrimSender() == 'kakaoAlrimCloud') { | |
| /** @var KakaoAlrimCloudSyncService $kakaoAlrimCloudSyncService */ | |
| $kakaoAlrimCloudSyncService = App::getInstance(KakaoAlrimCloudSyncService::class); | |
| $kakaoAlrimCloudSyncService->syncMessageTemplate(); | |
| } | |
| return $insertedSnoMap; | |
| }); | |
| $this->json(['result'=> true, 'message' => '', 'insertedSnoMap' => $insertedSnoMap]); | |
| break; | |
| default: | |
| break; | |
| } | |
| } catch (Exception $exception) { | |
| $this->json(['result'=> false, 'message' => $exception->getMessage()]); | |
| } | |
| } | |
| /** | |
| * $_FILES 중첩 배열에서 수신자/채널별 업로드 파일 정보 추출 | |
| * | |
| * @param array $recipientFiles $_FILES['recipients'] | |
| * @param string $recipientName 수신자 (member, admin, provider) | |
| * @param string $channelName 채널 (SMS, KAKAO_ALRIM_TALK, MYAPP_PUSH) | |
| * @return array|null ['name', 'type', 'tmp_name', 'error', 'size'] 또는 null | |
| */ | |
| private function extractUploadedFile(array $recipientFiles, string $recipientName, string $channelName): ?array | |
| { | |
| $tmpName = $recipientFiles['tmp_name'][$recipientName][$channelName]['image'] ?? null; | |
| $error = $recipientFiles['error'][$recipientName][$channelName]['image'] ?? UPLOAD_ERR_NO_FILE; | |
| $size = $recipientFiles['size'][$recipientName][$channelName]['image'] ?? 0; | |
| if (empty($tmpName) || (int)$error === UPLOAD_ERR_NO_FILE || (int)$size === 0) { | |
| return null; | |
| } | |
| return [ | |
| 'name' => $recipientFiles['name'][$recipientName][$channelName]['image'] ?? '', | |
| 'type' => $recipientFiles['type'][$recipientName][$channelName]['image'] ?? '', | |
| 'tmp_name' => $tmpName, | |
| 'error' => $error, | |
| 'size' => $recipientFiles['size'][$recipientName][$channelName]['image'] ?? 0, | |
| ]; | |
| } | |
| } | |
변경 후AutoSendConfigPsController.php
변경 전AutoSendController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Bundle\Component\Sms\Code; | |
| use Bundle\Component\Sms\Sms; | |
| use Bundle\Component\Sms\SmsUtil; | |
| use Component\Admin\AdminMenu; | |
| use Origin\Enum\AutoSend\AutoSendRecipient; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Service\AutoSend\SearchAutoSendListService; | |
| use Origin\Service\AutoSend\SearchAutoSendsRequest; | |
| use Request; | |
| class AutoSendController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $request = Request::get()->toArray(); | |
| $this->callMenu('crm', 'messageSend', 'autoSend'); | |
| $smsPoint = (float) Sms::getPoint(); | |
| $sendableMethodCountByPoint = [ | |
| 'sms' => SmsUtil::getAvailableCount('sms', $smsPoint), | |
| 'lms' => SmsUtil::getAvailableCount('lms', $smsPoint), | |
| 'kakaoAlrimTalk' => SmsUtil::getAvailableCount('kakaoAlrimTalk', $smsPoint), | |
| 'kakaoFriendTalk' => SmsUtil::getAvailableCount('kakaoFriendTalk', $smsPoint), | |
| ]; | |
| Sms::saveSmsPoint(); | |
| /** @var SearchAutoSendListService $findAutoSendService */ | |
| $findAutoSendService = \App::getInstance(SearchAutoSendListService::class); | |
| $response = $findAutoSendService->search(SearchAutoSendsRequest::fromArray($request)); | |
| $this->setData('shouldExpandRecommend', AutoSendSupport::shouldExpandRecommend()); | |
| $this->setData('newIconCodes', AutoSendSupport::getNewIconCodes()); | |
| $this->setData('useJoinPolicy', AutoSendSupport::useJoinPolicy()); | |
| $this->setData('autoSends', $response['autoSends']); | |
| $this->setData('recommendAutoSends', $response['recommendAutoSends']); | |
| $this->setData('pageCounts', $response['pageCounts']); | |
| $this->setData('smsPoint', $smsPoint); | |
| $this->setData('sendableMethodCountByPoint', $sendableMethodCountByPoint); | |
| $this->setData('recipients', AutoSendRecipient::cases()); | |
| $this->setData('request', $request); | |
| $this->getView()->setPageName('crm/auto_send.php'); | |
| $this->getView()->setDefine('messagePoint', 'crm/auto_send/layer_list_message_point.php'); | |
| $this->getView()->setDefine('autoRecommend', 'crm/auto_send/layer_list_recommend.php'); | |
| $this->getView()->setDefine('autoListSearch', 'crm/auto_send/layer_list_search.php'); | |
| $this->getView()->setDefine('autoList', 'crm/auto_send/layer_list.php'); | |
| } | |
| } | |
변경 후AutoSendController.php
변경 전LayerListController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSend; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Service\AutoSend\SearchAutoSendListService; | |
| use Origin\Service\AutoSend\SearchAutoSendsRequest; | |
| use Request; | |
| class LayerListController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = SearchAutoSendsRequest::fromArray(Request::post()->toArray()); | |
| /** @var SearchAutoSendListService $findAutoSendService */ | |
| $findAutoSendService = \App::getInstance(SearchAutoSendListService::class); | |
| $response = $findAutoSendService->search($request); | |
| $this->setData('newIconCodes', AutoSendSupport::getNewIconCodes()); | |
| $this->setData('autoSends', $response['autoSends']); | |
| $this->setData('pageCounts', $response['pageCounts']); | |
| $this->getView()->setPageName('crm/auto_send/layer_list.php'); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerListController.php
변경 전LayerListRecommendController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSend; | |
| use Origin\Service\AutoSend\SearchAutoSendListService; | |
| class LayerListRecommendController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setPageName('crm/auto_send/layer_list_recommend.php'); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerListRecommendController.php
변경 전LayerListSearchController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\AutoSend; | |
| class LayerListSearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setPageName('crm/auto_send/layer_list_search.php'); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerListSearchController.php
변경 전AutoSendPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Exception; | |
| use Framework\Http\Response; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Service\AutoSend\Request\UpdateAutoSendConfigRequest; | |
| use Origin\Service\AutoSend\SaveAutoSendConfigService; | |
| use Request; | |
| class AutoSendPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() { | |
| $request = Request::post()->toArray(); | |
| try { | |
| switch ($request['mode']) { | |
| case 'updateEnabledFlags': | |
| if (!empty($request['codes'])) { | |
| /** @var SaveAutoSendConfigService $saveAutoSendConfigService */ | |
| $saveAutoSendConfigService = \App::getInstance(SaveAutoSendConfigService::class); | |
| $saveAutoSendConfigService->updateAutoSendConfig( | |
| new UpdateAutoSendConfigRequest( | |
| codesWithFlag: $request['codes'], | |
| ) | |
| ); | |
| } | |
| $this->json(['result'=> true, 'message' => '']); | |
| break; | |
| case 'saveRecommendCollapseState': | |
| $collapsed = ($request['collapsed'] ?? '') === 'true'; | |
| AutoSendSupport::saveRecommendCollapseState($collapsed); | |
| $this->json(['result' => true, 'message' => '']); | |
| break; | |
| default: | |
| break; | |
| } | |
| } catch (Exception $exception) { | |
| // todo logging | |
| $this->json(['result'=> false, 'message' => $exception->getMessage()]); | |
| } | |
| } | |
| } | |
변경 후AutoSendPsController.php
변경 전CrmRecipeClickLogPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Http\Request; | |
| use Origin\Service\Crm\CrmRecipeClickLogCollectorService; | |
| class CrmRecipeClickLogPsController extends \Controller\Admin\Controller | |
| { | |
| private const ALLOWED_ACTIONS = [ | |
| CrmRecipeClickLogCollectorService::ACTION_SEND, | |
| CrmRecipeClickLogCollectorService::ACTION_CLOSE, | |
| ]; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'crm', 'crmRecipe'); | |
| $this->setMenuCode('crm', 'crm', 'crmRecipe'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $action = $postData['action'] ?? ''; | |
| if (!in_array($action, self::ALLOWED_ACTIONS, true)) { | |
| $this->json(['success' => false]); | |
| return; | |
| } | |
| /** @var CrmRecipeClickLogCollectorService $clickLogCollector */ | |
| $clickLogCollector = \App::getInstance(CrmRecipeClickLogCollectorService::class); | |
| $clickLogCollector->collect([ | |
| 'action' => $action, | |
| 'recipeType' => $postData['recipeType'] ?? '', | |
| 'currentPage' => $postData['currentPage'] ?? '', | |
| ]); | |
| $this->json(['success' => true]); | |
| } | |
| } | |
변경 후CrmRecipeClickLogPsController.php
변경 전CrmRecipeSendController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Sms\Sms; | |
| use Component\Validator\Validator; | |
| use Framework\Debug\Exception\AlertBackException; | |
| use Origin\DTO\ApiClient\Commerce\Crm\TargetsRepeatRuleStatusRequestDTO; | |
| use Origin\Enum\ApiClient\Crm\RecipeType; | |
| use Origin\Service\ApiClient\Commerce\Crm\CrmAdminApiClientService; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| class CrmRecipeSendController extends \Controller\Admin\Controller | |
| { | |
| protected $messageLogger; | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'crm', 'crmRecipe'); | |
| $this->setMenuCode('crm', 'crm', 'crmRecipe'); | |
| $this->messageLogger = \Logger::channel('mobileMessage'); | |
| $recipeType = (string) \Request::get()->get('recipeType', ''); | |
| if (!empty($recipeType) && RecipeType::tryFromSlug($recipeType) === null) { | |
| throw new AlertBackException(__('잘못된 접근입니다.')); | |
| } | |
| $crmGroupNoRaw = \Request::get()->get('no', ''); | |
| if (Validator::number($crmGroupNoRaw, 1) === false) { | |
| throw new AlertBackException(__('잘못된 접근입니다.')); | |
| } | |
| $crmGroupNo = (int) $crmGroupNoRaw; | |
| /** @var CrmAdminApiClientService $crmAdminApiClientService */ | |
| $crmAdminApiClientService = \App::getInstance(CrmAdminApiClientService::class); | |
| $crmGroup = $crmGroupNo > 0 | |
| ? $this->fetchSelectedCrmGroup($crmAdminApiClientService, $crmGroupNo) | |
| : []; | |
| $recipeDefault = []; | |
| if (!empty($recipeType)) { | |
| try { | |
| $recipeDefault = $crmAdminApiClientService->getRecipeDefault($recipeType)->toRecursiveArray(); | |
| } catch (\Throwable $e) { | |
| $this->messageLogger->error('CRM 레시피 기본값 처리 실패', [$e->getMessage(), $recipeType, __METHOD__]); | |
| $recipeDefault = []; | |
| } | |
| } | |
| $recipeName = $recipeDefault['recipeTitle'] ?? '레시피'; | |
| $conditionGuideView = $this->buildConditionGuideView($recipeDefault['conditionGuide'] ?? []); | |
| Sms::saveSmsPoint(); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $hasCallNumber = $smsManageCallNumberService->hasUsableCallNumber(); | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $messageConfig = $messageConfigService->buildSendableMessageConfig($hasCallNumber); | |
| $this->setData('messageConfig', $messageConfig); | |
| $this->setData('smsPointEach', Sms::SMS_POINT); | |
| $this->setData('lmsPointEach', Sms::LMS_POINT); | |
| $this->setData('kakaoAlrimTalkPointEach', Sms::KAKAO_POINT); | |
| $this->setData('kakaoFriendTalkPointEach', Sms::KAKAO_FRIEND_TALK_POINT); | |
| $this->setData('recipeType', $recipeType); | |
| $this->setData('crmGroup', $crmGroup); | |
| $this->setData('recipeName', $recipeName); | |
| $this->setData('recipeDefault', $recipeDefault); | |
| $this->setData('conditionGuideView', $conditionGuideView); | |
| // 어드민 헤더·네비 없는 blank 레이아웃 (드로어용) | |
| $this->getView()->setDefine('layout', 'layout_blank_noiframe.php'); | |
| $this->getView()->setDefine('layoutHeader', 'header_no_breadcrumb.php'); | |
| $this->getView()->setPageName('crm/crm_recipe_send.php'); | |
| $this->setData('adminBodyClass', trim($this->getData('adminBodyClass') . ' ncds')); | |
| } | |
| protected function fetchSelectedCrmGroup(CrmAdminApiClientService $crmAdminApiClientService, int $crmGroupNo): array | |
| { | |
| try { | |
| $crmGroup = $crmAdminApiClientService | |
| ->getNotificationTargetsRepeatRuleStatus(new TargetsRepeatRuleStatusRequestDTO(), $crmGroupNo) | |
| ->toRecursiveArray(); | |
| } catch (\Throwable $e) { | |
| $this->messageLogger->error('CRM 그룹 상태 조회 실패', [$e->getMessage(), $crmGroupNo, __METHOD__]); | |
| return []; | |
| } | |
| if (empty($crmGroup)) { | |
| return []; | |
| } | |
| // status API 응답의 no 는 내부 레코드 PK라 crmGroupNo 로 덮어씀 | |
| $crmGroup['no'] = $crmGroupNo; | |
| // status 응답엔 title 이 없어 규칙 단건 조회로 보강 | |
| try { | |
| $crmGroup['title'] = $crmAdminApiClientService | |
| ->getNotificationTargetsRepeatRule($crmGroupNo) | |
| ->getTitle(); | |
| } catch (\Throwable $e) { | |
| $this->messageLogger->error('CRM 그룹명 조회 실패', [$e->getMessage(), $crmGroupNo, __METHOD__]); | |
| } | |
| return $crmGroup; | |
| } | |
| protected function buildConditionGuideView(array $conditionGuide): array | |
| { | |
| $hasConditionGuide = !empty($conditionGuide['selectionGuide']) | |
| && !empty($conditionGuide['selectableValues']) | |
| && is_array($conditionGuide['selectableValues']); | |
| if (!$hasConditionGuide) { | |
| return ['hasConditionGuide' => false]; | |
| } | |
| $selectableValues = $conditionGuide['selectableValues']; | |
| $currentValue = $conditionGuide['value'] ?? $selectableValues[0]; | |
| $guideParts = explode('{no}', $conditionGuide['selectionGuide'], 2); | |
| // 미리보기 문구는 JS(updateConditionPreview)가 단독 계산·렌더 — 날짜 규칙 단일화 | |
| return [ | |
| 'hasConditionGuide' => true, | |
| 'selectableValues' => $selectableValues, | |
| 'currentValue' => $currentValue, | |
| 'guideParts' => $guideParts, | |
| 'productSelectableCount' => (int) ($conditionGuide['productSelectableCount'] ?? 0), | |
| ]; | |
| } | |
| } | |
변경 후CrmRecipeSendController.php
변경 전LayerKakaoTemplateCommentController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\StaticProxy\Proxy\Logger; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudMessageTemplateService; | |
| use Bundle\Component\Member\KakaoAlrim; | |
| class LayerKakaoTemplateCommentController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| $provider = $requestData['provider'] ?? ""; | |
| $templateCode = $requestData['templateCode'] ?? ""; | |
| if ($provider === 'bizm'){ | |
| $kakao = new KakaoAlrim; | |
| $messageTemplateComments = $kakao->getTemplateComment($templateCode); | |
| $comments = $messageTemplateComments['comments'] ?? []; | |
| $comment = !empty($comments) ? end($comments)->content ?? '' : ''; | |
| $this->setData('comment', $comment); | |
| } else{ | |
| /** @var KakaoAlrimCloudMessageTemplateService $service */ | |
| $service = \App::getInstance(KakaoAlrimCloudMessageTemplateService::class); | |
| $messageTemplateComments = $service->getMessageTemplateComments($templateCode); | |
| $this->setData('comment', $messageTemplateComments); | |
| } | |
| } | |
| } | |
변경 후LayerKakaoTemplateCommentController.php
변경 전LayerMobileMessagePerformanceDetailController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerMobileMessagePerformanceDetailController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileMessagePerformanceDetailController.php
변경 전LayerMobileMessagePerformanceSummaryController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerMobileMessagePerformanceSummaryController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileMessagePerformanceSummaryController.php
변경 전LayerMyAppSendGuideController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerMyAppSendGuideController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMyAppSendGuideController.php
변경 전LayerPreviewAutoSendController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use App; | |
| use Bundle\Component\Sms\SmsAutoCode; | |
| use Origin\Enum\AutoSend\AutoSendChannel; | |
| use Origin\Enum\AutoSend\AutoSendChannelPolicy; | |
| use Origin\Enum\AutoSend\AutoSendRecipient; | |
| use Origin\Enum\AutoSend\AutoSendRecipientPolicy; | |
| use Origin\Enum\AutoSend\AutoSendSupport; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| use Origin\Repository\Member\Myapp\MyappPushRepository; | |
| use Origin\Repository\Member\Sms\SmsContentsRepository; | |
| use Origin\Service\AutoSend\FindAutoSendCodeConfigService; | |
| use Origin\Service\AutoSend\FindAutoSendConfigService; | |
| use Origin\Service\AutoSend\FindAutoSendTemplateService; | |
| use Request; | |
| class LayerPreviewAutoSendController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::get()->toArray(); | |
| $code = $request['code']; | |
| $supportChannels = AutoSendChannelPolicy::getCodesWithChannelsWithBoard()[$code] ?? []; | |
| // 수신대상 라벨 (key => label) | |
| $recipientLabels = []; | |
| foreach (AutoSendRecipient::cases() as $recipient) { | |
| $recipientLabels[strtolower($recipient->name)] = $recipient->getTitle(); | |
| } | |
| // 발송 수단 라벨 (key => label) | |
| $channelLabels = []; | |
| foreach ($supportChannels as $channel) { | |
| $channelLabels[$channel->name] = $channel->getShortTitle(); | |
| } | |
| /** @var FindAutoSendCodeConfigService $codeConfigService */ | |
| $codeConfigService = App::getInstance(FindAutoSendCodeConfigService::class); | |
| [$_, $autoSendConfigsResponse, $channelCodeConfigDto] = $codeConfigService->createRecipientConfigByCode($code, false); | |
| // kakaoTemplates를 templateCode 기준 flat map으로 변환 | |
| $kakaoTemplateMap = []; | |
| foreach ($channelCodeConfigDto->kakaoTemplates as $categoryTemplates) { | |
| foreach ($categoryTemplates as $template) { | |
| if (!empty($template['templateCode'])) { | |
| $kakaoTemplateMap[$template['templateCode']] = $template; | |
| } | |
| } | |
| } | |
| $this->setData('allRecipients', $this->getSortedAllRecipients()); | |
| $this->setData('recipientLabels', $recipientLabels); | |
| $this->setData('channelLabels', $channelLabels); | |
| $this->setData('templates', $autoSendConfigsResponse); | |
| $this->setData('kakaoTemplateMap', $kakaoTemplateMap); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| private function getAutoSendMeta($code) | |
| { | |
| /** @var SmsAutoCode $smsAutoCode */ | |
| $smsAutoCode = App::load('Component\\Sms\\SmsAutoCode'); | |
| $groupKey = $smsAutoCode->getGroupByCode($code); | |
| $recipients = AutoSendRecipientPolicy::getCodesWithRecipientsWithBoard()[$code] ?? []; | |
| $channels = AutoSendChannelPolicy::getCodesWithChannelsWithBoard()[$code] ?? []; | |
| return [$groupKey, $recipients, $channels]; | |
| } | |
| /** | |
| * 전체 수신 대상들 반환 (탭 순서용) | |
| * 순서 정책: 회원 - 수령자 - 본사 운영자 - 공급사 | |
| * | |
| * @return array | |
| */ | |
| public function getSortedAllRecipients(): array | |
| { | |
| return array_map( | |
| fn(AutoSendRecipient $r) => strtolower($r->name), | |
| [ | |
| AutoSendRecipient::MEMBER, | |
| AutoSendRecipient::RECIPIENT, | |
| AutoSendRecipient::ADMIN, | |
| AutoSendRecipient::PROVIDER, | |
| ] | |
| ); | |
| } | |
| } | |
변경 후LayerPreviewAutoSendController.php
변경 전LayerPreviewMobileSendController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\StaticProxy\Proxy\UserFilePath; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| use Request; | |
| use Logger; | |
| use Respect\Validation\Exceptions\ValidationException; | |
| use Respect\Validation\Validator; | |
| class LayerPreviewMobileSendController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $postValue = Request::post()->toArray(); | |
| // 요청 검증 | |
| Validator::key('sendMethod', Validator::in(['SMS', 'FRIENDTALK', 'ALIMTALK', 'MYAPP'])) | |
| ->setTemplate('유효하지 않은 요청입니다.') | |
| ->assert($postValue); | |
| Validator::key('viewType', Validator::in(['layer', 'side'])) | |
| ->setTemplate('유효하지 않은 요청입니다.') | |
| ->assert($postValue); | |
| Validator::key('payload', Validator::stringType()->notEmpty()) | |
| ->setTemplate('유효하지 않은 요청입니다.') | |
| ->assert($postValue); | |
| $payload = json_decode($postValue['payload'], true); | |
| $sendMethod = $postValue['sendMethod']; | |
| // 공통 데이터 | |
| $this->setData('sendMethod', $sendMethod); | |
| $this->setData('payloadJson', $postValue['payload']); | |
| $useAlternative = !empty($payload['enableAlternative']) ? 'y' : 'n'; | |
| $this->setData('useAlternative', $useAlternative); | |
| $previewContents = ""; | |
| $alternativePreviewContents = 'crm/preview_mobile_send/layer_preview_mobile_send_alternative.php'; | |
| switch ($sendMethod) { | |
| case 'SMS': | |
| $smsData = $payload['smsLmsRequest'] ?? []; | |
| $this->setData('mainTitle', $smsData['title'] ?? ''); | |
| $this->setData('mainContents', $smsData['content'] ?? ''); | |
| $this->setData('showAdWarningText', 'n'); | |
| $previewContents = 'crm/preview_mobile_send/layer_preview_mobile_send_sms.php'; | |
| break; | |
| case 'FRIENDTALK': | |
| $friendTalkData = $payload['kakaoFriendtalkRequest'] ?? []; | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| $showAdWarningText = 'n'; | |
| if ($kakaoFriendTalkConfig->getSmsAlternativeSendFlag() === 'y' && !$friendTalkData['alternativeInfo']['isAdText'] && $friendTalkData['type'] !== 'WIDE_ITEM_LIST' && $friendTalkData['type'] !== 'CAROUSEL_FEED') { | |
| $showAdWarningText = 'y'; | |
| } | |
| $this->setData('friendtalk', $friendTalkData); | |
| $this->setData('alternativeContents', $friendTalkData['alternativeInfo']['content'] ?? ''); | |
| // 대체 제목 미입력 시 메인 메시지 타이틀(친구톡 헤더)이 있으면 카피하여 표기 (미리보기 전용) | |
| $alternativeTitle = $friendTalkData['alternativeInfo']['title'] ?? ''; | |
| if ($alternativeTitle === '' || $alternativeTitle === null) { | |
| $alternativeTitle = $friendTalkData['header'] ?? ''; | |
| } | |
| $this->setData('alternativeTitle', $alternativeTitle); | |
| $this->setData('showAdWarningText', $showAdWarningText); | |
| $previewContents = 'crm/preview_mobile_send/layer_preview_mobile_send_kakao_friendtalk.php'; | |
| break; | |
| case 'ALIMTALK': | |
| $alimTalkData = $payload['kakaoAlimtalkRequest'] ?? []; | |
| $previewMeta = json_decode($postValue['previewMeta'] ?? '{}', true); | |
| $hasAddChannelButton = !empty(array_filter( | |
| $previewMeta['templateButtons'] ?? [], | |
| fn($button) => ($button['type'] ?? '') === 'ADD_CHANNEL' | |
| )); | |
| $this->setData('template', $previewMeta); | |
| $this->setData('hasAddChannelButton', $hasAddChannelButton); | |
| $this->setData('alternativeContents', $alimTalkData['alternativeInfo']['content'] ?? ''); | |
| $this->setData('alternativeTitle', $alimTalkData['alternativeInfo']['title'] ?? ''); | |
| $this->setData('showAdWarningText', 'n'); | |
| $previewContents = 'crm/preview_mobile_send/layer_preview_mobile_send_kakao_alimtalk.php'; | |
| break; | |
| case 'MYAPP': | |
| $myappPushData = $payload['myappRequest'] ?? []; | |
| /** @var MyappPushSettingService $myappPushSettingService */ | |
| $myappPushSettingService = \App::getInstance(MyappPushSettingService::class); | |
| $myappPushConfig = $myappPushSettingService->getMyappPushConfig(); | |
| $showAdWarningText = 'n'; | |
| if ($myappPushData['notificationType'] === 'AD' && $myappPushConfig->getManualSmsAlternativeSendFlag() === 'y' && !$myappPushData['alternativeInfo']['isAdText']) { | |
| $showAdWarningText = 'y'; | |
| } | |
| $this->setData('myappPayload', $myappPushData); | |
| $this->setData('showAdWarningText', $showAdWarningText); | |
| $this->setData('alternativeContents', $payload['myappRequest']['alternativeInfo']['content'] ?? ''); | |
| // 대체 제목 미입력 시 메인 메시지 타이틀(푸시 제목)이 있으면 카피하여 표기 (미리보기 전용) | |
| $alternativeTitle = $payload['myappRequest']['alternativeInfo']['title'] ?? ''; | |
| if ($alternativeTitle === '' || $alternativeTitle === null) { | |
| $alternativeTitle = $myappPushData['pushSubject'] ?? ''; | |
| } | |
| $this->setData('alternativeTitle', $alternativeTitle); | |
| $previewContents = 'crm/preview_mobile_send/layer_preview_mobile_send_myapp_push.php'; | |
| break; | |
| } | |
| if($postValue['viewType'] === 'side') { | |
| ob_start(); | |
| $paths = explode('/', $previewContents); | |
| include UserFilePath::adminSkin(...$paths); | |
| if($out = ob_get_clean()) { | |
| echo $out; | |
| } | |
| exit; | |
| } else { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setDefine('previewContents', $previewContents); | |
| $this->getView()->setDefine('alternativePreviewContents', $alternativePreviewContents); | |
| } | |
| } catch (ValidationException $e) { | |
| Logger::channel('mobileMessage')->warning(__CLASS__ . ' 모바일 메시지 전송 처리 검증 실패 : ', [ | |
| 'error' => $e->getMainMessage(), | |
| 'postValue' => $postValue ?? [] | |
| ]); | |
| $this->alert(__('잘못된 요청입니다 발송전 미리보기 로드에 실패하였습니다. 잠시후 다시 시도해주세요.')); | |
| } catch (\Throwable $e) { | |
| Logger::channel('mobileMessage')->warning(__CLASS__ . ' 모바일 메시지 전송 처리 중 에러 발생 : ', [ | |
| 'error' => $e->getMessage(), | |
| 'postValue' => $postValue ?? [] | |
| ]); | |
| $this->alert(__('발송전 미리보기 로드에 실패하였습니다. 잠시후 다시 시도해주세요.')); | |
| } | |
| } | |
| } | |
변경 후LayerPreviewMobileSendController.php
변경 전LayerRejectRecipientMemberInfoController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Request; | |
| class LayerRejectRecipientMemberInfoController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $postValue = Request::post()->toArray(); | |
| $this->setData('totalCount', $postValue['totalCount']); | |
| $this->setData('rejectedCount', $postValue['rejectedCount']); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerRejectRecipientMemberInfoController.php
변경 전LayerSaveAsTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerSaveAsTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSaveAsTemplateController.php
변경 전LayerSelectCouponController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerSelectCouponController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSelectCouponController.php
변경 전LayerSelectCouponResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Coupon\CouponAdmin; | |
| class LayerSelectCouponResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = \App::getInstance('request'); | |
| $getParams = $request->get()->all(); | |
| /** @var \Bundle\Component\Coupon\CouponAdmin $couponAdmin */ | |
| $couponAdmin = \App::load('\\Component\\Coupon\\CouponAdmin'); | |
| $useTypeArray = []; | |
| if (!empty($getParams['couponUseTypeList'])) { | |
| $useTypeArray = explode(',', $getParams['couponUseTypeList']); | |
| $useTypeArray = array_filter($useTypeArray); // 빈 문자열 제거 | |
| } | |
| $deviceTypeArray = []; | |
| if (!empty($getParams['couponDeviceTypeList'])) { | |
| $deviceTypeArray = explode(',', $getParams['couponDeviceTypeList']); | |
| $deviceTypeArray = array_filter($deviceTypeArray); // 빈 문자열 제거 | |
| $deviceTypeArray = array_map('strtolower', $deviceTypeArray); // 소문자로 변환 | |
| } | |
| $couponAdminList = $couponAdmin->getCouponAdminList(addUseTypeWhereIn: $useTypeArray, addDeviceTypeWhereIn: $deviceTypeArray); | |
| $couponData = []; | |
| foreach ($couponAdminList['data'] as $index => $item) { | |
| $couponData[$item['couponNo']] = $item; | |
| } | |
| $page = \App::load('\\Component\\Page\\Page'); | |
| $searchCount = $page->recode['total']; | |
| $this->setData('page', $page); | |
| $this->setData('couponData', $couponData); | |
| $this->setData('couponSearchCount', $searchCount); | |
| $this->setData('requestData', $getParams); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSelectCouponResultController.php
변경 전LayerSelectMemberGroupsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Page\Page; | |
| use Component\Member\MemberGroup; | |
| use Framework\StaticProxy\Proxy\UserFilePath; | |
| use Framework\Utility\StringUtils; | |
| class LayerSelectMemberGroupsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSelectMemberGroupsController.php
변경 전LayerSelectMemberGroupsResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Page\Page; | |
| use Component\Member\MemberGroup; | |
| use Framework\StaticProxy\Proxy\UserFilePath; | |
| use Framework\Utility\StringUtils; | |
| class LayerSelectMemberGroupsResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = \App::getInstance('request'); | |
| $getParams = $request->get()->all(); | |
| /* @var \Bundle\Component\Member\MemberGroup $memberGroupComponent */ | |
| $memberGroupComponent = \App::load(MemberGroup::class); | |
| $groupData = $memberGroupComponent->getGroupListSearch(); | |
| $groups = $groupData['data'] ?? []; | |
| // getGroupListSearch 내부 페이징 처리 포함 | |
| $page = \App::load('\\Component\\Page\\Page'); | |
| $searchCount = $page->recode['total']; | |
| $totalCount = $memberGroupComponent->getAllGroupCount()[0]['cnt']; | |
| $this->setData('search', StringUtils::strIsSet($groupData['search'])); | |
| $this->setData('groups', $groups); | |
| $this->setData('groupSearchCount', $searchCount); | |
| $this->setData('groupTotalCount', $totalCount); | |
| $this->setData('page', $page); | |
| $this->setData('requestData', $getParams); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSelectMemberGroupsResultController.php
변경 전LayerTemplateContentsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateResultDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateSearchDTO; | |
| use Origin\Service\Crm\Message\MessageTemplateFindService; | |
| class LayerTemplateContentsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $request = \App::getInstance('request'); | |
| $searchDto = new MessageTemplateSearchDTO($request->request()->toArray()); | |
| /** @var MessageTemplateFindService $service */ | |
| $service = \App::getInstance(MessageTemplateFindService::class); | |
| $responseDto = $service->getMessageTemplate($searchDto); | |
| $this->getView()->setDefine('layerTemplateContentsSms', 'crm/layer_template_contents_sms.php'); | |
| $this->getView()->setDefine('layerTemplateContentsKakao', 'crm/layer_template_contents_kakao.php'); | |
| $this->getView()->setDefine('layerTemplateContentsMyapp', 'crm/layer_template_contents_myapp.php'); | |
| $this->setData('request', $searchDto); | |
| $this->setData('response', $responseDto); | |
| } | |
| } | |
변경 후LayerTemplateContentsController.php
변경 전LayerUpdateKaKaoTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerUpdateKaKaoTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerUpdateKaKaoTemplateController.php
변경 전LayerViewMessageLinkTypeController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| class LayerViewMessageLinkTypeController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerViewMessageLinkTypeController.php
변경 전MessageConfigController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Utility\GodoUtils; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappStatusResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessagePointConfigResponseDTO; | |
| use Origin\Enum\Crm\Message\AutoSmsAlternativeSendType; | |
| use Origin\Enum\Crm\Message\FailedMessageSendType; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Enum\Crm\Message\LinkPlatformType; | |
| use Origin\Enum\Crm\Message\SmsAutoSendOverType; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\MessagePointConfigService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| use Origin\Service\Crm\Message\SmsSettingService; | |
| use Origin\Service\MyApp\MyappSettingService; | |
| use Origin\Traits\Member\KakaoAlrim\KakaoAlrimCloudAvailable; | |
| class MessageConfigController extends \Controller\Admin\Controller | |
| { | |
| use KakaoAlrimCloudAvailable; | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| // 메뉴 설정 | |
| $this->callMenu('crm', 'messageConfig', 'messageConfig'); | |
| $this->setMenuCode('crm', 'messageConfig', 'messageConfig'); | |
| try { | |
| /** @var MessagePointConfigService $messagePointConfigService */ | |
| $messagePointConfigService = \App::getInstance(MessagePointConfigService::class); | |
| $messagePointConfig = $messagePointConfigService->getMessagePointConfig(); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $messagePointConfig = new MessagePointConfigResponseDTO(null); | |
| } | |
| /** @var SmsSettingService $smsSettingService */ | |
| $smsSettingService = \App::getInstance(SmsSettingService::class); | |
| $smsAutoConfig = $smsSettingService->getSmsAutoConfig(); | |
| $smsRejectPolicy = $smsSettingService->getSmsRejectConfig(); | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| /** @var MyappPushSettingService $myappPushSettingService */ | |
| $myappPushSettingService = \App::getInstance(MyappPushSettingService::class); | |
| $myappPushConfig = $myappPushSettingService->getMyappPushConfig(); | |
| try { | |
| /** @var MyappSettingService $myappSettingService */ | |
| $myappSettingService = \App::getInstance(MyappSettingService::class); | |
| $myappStatus = $myappSettingService->getMyappStatus(); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('myapp')->error($e->getMessage()); | |
| $myappStatus = new MyappStatusResponseDTO(['isInstalled' => false, 'isReleased' => false]); | |
| } | |
| $isKakaoAlrimAvailable = !$this->isNewMall(); | |
| $isKakaoAlrimLunaInstalled = GodoUtils::isPlusShop(PLUSSHOP_CODE_KAKAOALRIMLUNA); | |
| $this->setData('messagePointConfig', $messagePointConfig); | |
| $this->setData('smsRejectPolicy', $smsRejectPolicy); | |
| $this->setData('smsAutoConfig', $smsAutoConfig); | |
| $this->setData('kakaoFriendTalkConfig', $kakaoFriendTalkConfig); | |
| $this->setData('isKakaoAlrimAvailable', $isKakaoAlrimAvailable); | |
| $this->setData('isKakaoAlrimLunaInstalled', $isKakaoAlrimLunaInstalled); | |
| $this->setData('kakaoAlimTalkConfig', $kakaoAlimTalkConfig); | |
| $this->setData('isMyappInstalled', $myappStatus->isInstalled()); | |
| $this->setData('isMyappReleased', $myappStatus->isReleased()); | |
| $this->setData('myappPushConfig', $myappPushConfig); | |
| $this->setData('linkPlatformTypes', LinkPlatformType::cases()); | |
| $this->setData('kakaoAlimTalkSenders', KakaoAlimTalkSender::cases()); | |
| $this->setData('autoSmsAlternativeSendTypes', AutoSmsAlternativeSendType::cases()); | |
| $this->setData('failedMessageSendTypes', FailedMessageSendType::cases()); | |
| $this->setData('smsAutoSendOverTypes', SmsAutoSendOverType::cases()); | |
| $this->getView()->setPageName('crm/message_config.php'); | |
| } | |
| } | |
변경 후MessageConfigController.php
변경 전LayerBizmKakaoTalkChannelController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Member\KakaoAlrim; | |
| class LayerBizmKakaoTalkChannelController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $kakaoAlrimBizmConfig = gd_policy('kakaoAlrim.config'); | |
| gd_isset($kakaoAlrimBizmConfig['plusId'], ''); | |
| gd_isset($kakaoAlrimBizmConfig['kakaoKey'], ''); | |
| gd_isset($kakaoAlrimBizmConfig['phoneNumber'], ''); | |
| gd_isset($kakaoAlrimBizmConfig['useFlag'], 'n'); | |
| gd_isset($kakaoAlrimBizmConfig['approvalFl'], 'n'); | |
| $oKakao = new KakaoAlrim; | |
| $senderProfileLookup = $oKakao->senderProfileLookup(); | |
| $kakaoAlrimBizmConfig['status'] = $senderProfileLookup->senderProfileStatus; | |
| // PG 설정 불러오기 | |
| $pgConfig = gd_pgs(); | |
| $isConnectedPg = ($pgConfig['pgAutoSetting'] === 'y' || $pgConfig['pgApprovalSetting'] === 'y') ? 'y' : 'n'; | |
| $this->setData('isConnectedPg', $isConnectedPg); | |
| $this->setData('kakaoAlrimBizmConfig', $kakaoAlrimBizmConfig); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerBizmKakaoTalkChannelController.php
변경 전LayerBizmKakaoTalkChannelPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Member\KakaoAlrim; | |
| use Bundle\Component\Policy\Policy; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| class LayerBizmKakaoTalkChannelPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'delete': | |
| try { | |
| $oKakao = new KakaoAlrim; | |
| $result = $oKakao->deleteKakaoKey(); | |
| if ($result['result'] !== 'success') { | |
| $this->json(['success' => false, 'message' => '프로필키 삭제에 실패하였습니다.', 'data' => ['type' => 'fail_delete_kakao_key']]); | |
| } | |
| /** @var Policy $policy */ | |
| $policy = \App::load('\\Component\\Policy\\Policy'); | |
| $result = $policy->setValue('kakaoAlrim.config', []); | |
| if (!$result) { | |
| $this->json(['success' => false, 'message' => '설정 업데이트에 실패하였습니다.', 'data' => ['type' => 'fail_update_policy']]); | |
| break; | |
| } | |
| $oKakao->deleteAllTemplate(); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| // 비즈엠 알림톡 사용안함 처리 | |
| if ($kakaoAlimTalkConfig->getSender() === KakaoAlimTalkSender::BIZM && $kakaoAlimTalkConfig->getUseFlag() === 'y') { | |
| $kakaoAlimTalkConfig->setUseFlag('n'); | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $messageConfigService->save(null, null, null, null, $kakaoAlimTalkConfig, null); | |
| } | |
| $this->json(['success' => true, 'message' => '프로필키 삭제에 성공하였습니다.']); | |
| break; | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()]); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerBizmKakaoTalkChannelPsController.php
변경 전LayerChargeMessagePointsHistoryController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| class LayerChargeMessagePointsHistoryController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerChargeMessagePointsHistoryController.php
변경 전LayerChargeMessagePointsHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Framework\Http\Request; | |
| use Origin\DTO\Member\Sms\SmsPointChargeDTO; | |
| use Origin\Service\Member\Sms\SmsPointChargeService; | |
| class LayerChargeMessagePointsHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageConfig', 'chargeMessagePoints'); | |
| $naviMenu = $this->getData('naviMenu'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->post()->toArray(); | |
| /** @var SmsPointChargeService $smsPointChargeService */ | |
| $smsPointChargeService = \App::getInstance(SmsPointChargeService::class); | |
| $smsPointChargeListResponseDTO = $smsPointChargeService->getSmsPointChargeList( | |
| $naviMenu->lno['2'], | |
| $requestData['page'] ?? SmsPointChargeService::DEFAULT_PAGE, | |
| $requestData['pageSize'] ?? SmsPointChargeService::DEFAULT_CRM_PAGE_SIZE | |
| ); | |
| $smsPointChargeList = array_map(function(SmsPointChargeDTO $dto) { | |
| $item = $dto->toArray(); | |
| $item['payTypeName'] = $dto->getPayTypeName(); // 원하는 값으로 변경 | |
| return $item; | |
| }, $smsPointChargeListResponseDTO->getSmsPointChargeList()); | |
| $page = $smsPointChargeListResponseDTO->getPage(); | |
| $this->setData('smsPointChargeList', $smsPointChargeList); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerChargeMessagePointsHistoryResultController.php
변경 전LayerCloudKakaoTalkChannelController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSettingService; | |
| class LayerCloudKakaoTalkChannelController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var KakaoAlrimCloudSettingService $kakaoAlrimCloudSettingService */ | |
| $kakaoAlrimCloudSettingService = \App::getInstance(KakaoAlrimCloudSettingService::class); | |
| $kakaoAlrimCloudConfig = $kakaoAlrimCloudSettingService->getKakaoAlrimCloudData(); | |
| // PG 설정 불러오기 | |
| $pgConfig = gd_pgs(); | |
| $isConnectedPg = ($pgConfig['pgAutoSetting'] === 'y' || $pgConfig['pgApprovalSetting'] === 'y') ? 'y' : 'n'; | |
| $this->setData('isConnectedPg', $isConnectedPg); | |
| $this->setData('kakaoAlrimCloudConfig', $kakaoAlrimCloudConfig); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerCloudKakaoTalkChannelController.php
변경 전LayerCloudKakaoTalkChannelPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudPsService; | |
| class LayerCloudKakaoTalkChannelPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'delete': | |
| try { | |
| /** @var KakaoAlrimCloudPsService $service */ | |
| $service = \App::getInstance(KakaoAlrimCloudPsService::class); | |
| $result = $service->deletePlusId($postData); | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $needConfigUpdate = false; | |
| // 친구톡 사용안함 처리 | |
| if ($kakaoFriendTalkConfig->getUseFlag() === 'y') { | |
| $kakaoFriendTalkConfig->setUseFlag('n'); | |
| $needConfigUpdate = true; | |
| } | |
| // 클라우드 알림톡 사용안함 처리 | |
| if ($kakaoAlimTalkConfig->getSender() === KakaoAlimTalkSender::NHN_CLOUD && $kakaoAlimTalkConfig->getUseFlag() === 'y') { | |
| $kakaoAlimTalkConfig->setUseFlag('n'); | |
| $needConfigUpdate = true; | |
| } | |
| if ($needConfigUpdate) { | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $messageConfigService->save(null, null, null, $kakaoFriendTalkConfig, $kakaoAlimTalkConfig, null); | |
| } | |
| $this->json(['success' => $result]); | |
| break; | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()]); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerCloudKakaoTalkChannelPsController.php
변경 전LayerLunaKakaoTalkChannelController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Framework\Enum\ExternalUrl; | |
| class LayerLunaKakaoTalkChannelController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $kakaoAlrimLunaConfig = gd_policy('kakaoAlrimLuna.config'); | |
| gd_isset($kakaoAlrimLunaConfig['useFlag'], 'n'); | |
| gd_isset($kakaoAlrimLunaConfig['lunaCliendId'], ''); | |
| gd_isset($kakaoAlrimLunaConfig['lunaClientKey'], ''); | |
| $this->setData('kakaoAlrimLunaConfig', $kakaoAlrimLunaConfig); | |
| $this->setData('lunaKakaoRequestUrl', ExternalUrl::GODO_KAKAO_API->getUrl('/lunaKakaoRequest.php')); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerLunaKakaoTalkChannelController.php
변경 전LayerLunaKakaoTalkChannelPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Member\KakaoAlrimLuna; | |
| use Bundle\Component\Policy\Policy; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| class LayerLunaKakaoTalkChannelPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'generateSendData': | |
| try { | |
| unset($postData['mode']); | |
| $oKakao = new KakaoAlrimLuna; | |
| $encodedContent = $oKakao->sendLunaId($postData); | |
| $this->json(['success' => true, 'data' => $encodedContent]); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('kakao')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()]); | |
| } | |
| break; | |
| case 'logout': | |
| try { | |
| $oKakao = new KakaoAlrimLuna; | |
| $deleteResult = $oKakao->deleteLunaKey(); | |
| if ($deleteResult['result'] !== 'success') { | |
| $this->json(['success' => false, 'message' => '처리중에 오류가 발생하여 실패되었습니다.']); | |
| } | |
| $updateData = ['lunaKeyDel' => 'y']; | |
| /** @var Policy $policy */ | |
| $policy = \App::load('\\Component\\Policy\\Policy'); | |
| $result = $policy->saveKakaoAlrimLunaConfig($updateData); | |
| if ($result) { | |
| $tmpkakaoAutoSet = gd_policy('kakaoAlrimLuna.kakaoAuto'); | |
| $tmpLunaSet = gd_array_merge($tmpkakaoAutoSet, array('useFlag' => 'n')); | |
| gd_set_policy('kakaoAlrimLuna.kakaoAuto', $tmpLunaSet); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| // 루나 알림톡 사용안함 처리 | |
| if ($kakaoAlimTalkConfig->getSender() === KakaoAlimTalkSender::BLUMN_AI && $kakaoAlimTalkConfig->getUseFlag() === 'y') { | |
| $kakaoAlimTalkConfig->setUseFlag('n'); | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $messageConfigService->save(null, null, null, null, $kakaoAlimTalkConfig, null); | |
| } | |
| } | |
| $this->json(['success' => $result]); | |
| break; | |
| } catch (\Throwable $e) { | |
| \Logger::channel('kakao')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()]); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerLunaKakaoTalkChannelPsController.php
변경 전LayerManageSmsVerificationController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Framework\Http\Request; | |
| use Origin\Service\Crm\Message\SmsPasswordService; | |
| class LayerManageSmsVerificationController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $displayInfo = [ | |
| 'title' => 'SMS 인증번호', | |
| 'useCaptcha' => false, | |
| 'retry' => $session->get('captchaRetry', 1), | |
| ]; | |
| if ($request->request()->get('mode', 'input') === 'change') { | |
| $displayInfo['useCaptcha'] = true; | |
| } | |
| $restrictedKeywords = SmsPasswordService::RESTRICTED_KEYWORDS; | |
| $this->setData('displayInfo', $displayInfo); | |
| $this->setData('restrictedKeywords', $restrictedKeywords); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerManageSmsVerificationController.php
변경 전LayerManageSmsVerificationPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Captcha\Captcha; | |
| use Bundle\Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Framework\Http\Request; | |
| use Framework\Security\Encryptor; | |
| use Framework\Utility\ComponentUtils; | |
| class LayerManageSmsVerificationPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $isChangePassword = $request->post()->get('mode', 'changePassword') === 'changePassword'; | |
| if ($isChangePassword) { | |
| $captchaNumber = $request->post()->get('captcha'); | |
| $captcha = new Captcha(); | |
| $result = $captcha->verify($captchaNumber, 1); | |
| if ($result['code'] !== '0000') { | |
| $this->json(['success' => false, 'message' => '자동등록 방지문자가 맞지 않습니다.', 'data' => ['type' => 'invalid_captcha']]); | |
| } | |
| } | |
| $smsPassword = $request->post()->get('password'); | |
| /** @var \Bundle\Component\Sms\SmsSender $smsSender */ | |
| $smsSender = \App::load(SmsSender::class); | |
| try { | |
| $smsSender->validPassword($smsPassword, !$isChangePassword); | |
| /** @var Encryptor $encryptor */ | |
| $encryptor = \App::getInstance('encryptor'); | |
| $policy = ComponentUtils::getPolicy('sms.config'); | |
| if ($isChangePassword) { | |
| $policy['authentication'] = [ | |
| 'failLog' => [], | |
| 'failCnt' => 0, | |
| 'password' => $encryptor->encrypt($smsPassword), | |
| ]; | |
| } else { | |
| $policy['authentication']['failLog'] = []; | |
| $policy['authentication']['failCnt'] = 0; | |
| } | |
| ComponentUtils::setPolicy('sms.config', $policy); | |
| $this->json(['success' => true, 'message' => '메시지 인증번호 등록이 완료되었습니다.']); | |
| } catch (PasswordException $e) { | |
| $message = $e->getMessage(); | |
| if ($isChangePassword) { | |
| $message = '메시지 인증번호는 영문대문자/영문소문자/숫자/특수문자 중 2가지 이상 조합, 10~16자리로만 설정할 수 있습니다.'; | |
| $message .= '<br/>[마이페이지 > 쇼핑몰 관리]에서 메시지 인증번호를 수정 후 다시 시도해주세요.'; | |
| } | |
| $this->json(['success' => false, 'message' => $message, 'data' => ['type' => 'invalid_format']]); | |
| } | |
| } | |
| } | |
변경 후LayerManageSmsVerificationPsController.php
변경 전LayerMessagePointController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Component\Sms\SmsUtil; | |
| use Component\Sms\Sms; | |
| use Origin\Service\Member\Sms\SmsPointChargeService; | |
| class LayerMessagePointController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| // 메뉴 설정 | |
| $this->callMenu('crm', 'messageConfig', 'messageConfig'); | |
| $naviMenu = $this->getData('naviMenu'); | |
| // 현재 SMS 포인트 싱크 | |
| Sms::saveSmsPoint(); | |
| // 포인트 충전 내역 | |
| /** @var SmsPointChargeService $smsPointChargeService */ | |
| $smsPointChargeService = \App::getInstance(SmsPointChargeService::class); | |
| $existsChargeHistory = $smsPointChargeService->existsSmsPointChargeHistory($naviMenu->lno['2']); | |
| if ($existsChargeHistory) { | |
| // 현재 메시지 포인트 | |
| $nowSmsPoint = (float) Sms::getPoint(); | |
| $availableCount = [ | |
| 'sms' => SmsUtil::getAvailableCount('sms', $nowSmsPoint), | |
| 'lms' => SmsUtil::getAvailableCount('lms', $nowSmsPoint), | |
| 'kakaoAlrimTalk' => SmsUtil::getAvailableCount('kakaoAlrimTalk', $nowSmsPoint), | |
| 'kakaoFriendTalk' => SmsUtil::getAvailableCount('kakaoFriendTalk', $nowSmsPoint), | |
| ]; | |
| $this->setData('availableCount', $availableCount); | |
| } | |
| // 포인트 충전내역 존재 여부 set | |
| $this->setData('existsChargeHistory', $existsChargeHistory); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMessagePointController.php
변경 전LayerRegisterKakaoTalkChannelController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudRegistService; | |
| class LayerRegisterKakaoTalkChannelController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $sender = $request->post()->get('sender'); | |
| try { | |
| /** @var KakaoAlrimCloudRegistService $service */ | |
| $service = \App::getInstance(KakaoAlrimCloudRegistService::class); | |
| [$firstLevelCategories, $secondLevelCategories, $thirdLevelCategories] = $service->getCategoryInfo(); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| $this->setData('sender', $sender); | |
| $this->setData('firstLevelCategories', gd_isset($firstLevelCategories)); | |
| $this->setData('secondLevelCategories', gd_isset($secondLevelCategories)); | |
| $this->setData('thirdLevelCategories', gd_isset($thirdLevelCategories)); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerRegisterKakaoTalkChannelController.php
변경 전LayerRegisterKakaoTalkChannelPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Member\KakaoAlrim; | |
| use Bundle\Component\Policy\Policy; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use GuzzleHttp\Exception\GuzzleException; | |
| use Origin\DTO\Member\KakaoAlrim\KakaoAlirmCloudRegisterDto; | |
| use Origin\DTO\Member\KakaoAlrim\KakaoAlirmGetTokenDto; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudRegistService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSyncService; | |
| use Origin\Util\RequestValidation; | |
| class LayerRegisterKakaoTalkChannelPsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * @throws AlertCloseException | |
| * @throws GuzzleException | |
| */ | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| $mode = $requestData['mode']; | |
| switch ($mode) { | |
| case 'getToken': | |
| try { | |
| switch ($requestData['sender']) { | |
| case 'kakaoAlrimCloud': | |
| RequestValidation::validate($requestData, ['plusId', 'phoneNumber', 'category']); | |
| $dto = new KakaoAlirmGetTokenDto($requestData); | |
| /** @var KakaoAlrimCloudRegistService $service */ | |
| $service = \App::getInstance(KakaoAlrimCloudRegistService::class); | |
| $result = $service->getToken($dto); | |
| $this->json(['success' => $result, 'message' => $result ? null : '인증 번호 발송 실패']); | |
| break; | |
| case 'kakaoAlrim': | |
| $bizmRequestData = [ | |
| 'yellowId' => $requestData['plusId'], | |
| 'phoneNumber' => $requestData['phoneNumber'] | |
| ]; | |
| $kakaoAlrim = new KakaoAlrim(); | |
| $result = $kakaoAlrim->getToken($bizmRequestData); | |
| if (!empty($result['code']) && $result['code'] == 'success') { | |
| $this->json(['success' => true, 'message' => '인증 번호 발송 성공']); | |
| } else { | |
| $this->json(['success' => false, 'message' => $result['message'] ?? '인증 번호 발송 실패']); | |
| } | |
| break; | |
| } | |
| } catch (\Throwable $e) { | |
| \Logger::channel('kakao')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'register': | |
| try { | |
| RequestValidation::validate($requestData, ['sender', 'plusId', 'token', 'phoneNumber']); | |
| $requestData['approvalFl'] = 'y'; | |
| switch ($requestData['sender']) { | |
| case 'kakaoAlrimCloud': | |
| $registerDto = new KakaoAlirmCloudRegisterDto($requestData); | |
| /** @var KakaoAlrimCloudRegistService $registerService */ | |
| $registerService = \App::getInstance(KakaoAlrimCloudRegistService::class); | |
| $result = $registerService->register($registerDto); | |
| /** @var KakaoAlrimCloudSyncService $kakaoAlrimCloudSyncService */ | |
| $kakaoAlrimCloudSyncService = \App::getInstance(KakaoAlrimCloudSyncService::class); | |
| $kakaoAlrimCloudSyncService->syncMessageTemplate(); | |
| if ($result) { | |
| $this->json([ | |
| 'success' => true, | |
| 'message' => '카카오톡 채널이 등록되었습니다.', | |
| 'data' => [ | |
| 'alertMessage' => [ | |
| 'message' => '카카오톡 채널이 등록되었습니다.<br>메시지 설정 화면에서 등록된 채널 정보를 확인할 수 있습니다.' | |
| ], | |
| 'requestData' => $requestData | |
| ] | |
| ]); | |
| } else { | |
| \Logger::channel('kakao')->warning('카카오톡 채널 등록에 실패했습니다.', [$requestData['sender']]); | |
| throw new \Exception("카카오톡 채널 등록에 실패했습니다."); | |
| } | |
| break; | |
| case 'kakaoAlrim': | |
| $kakaoAlrim = new KakaoAlrim(); | |
| $profile = $kakaoAlrim->getKakaoKey($requestData); | |
| if ($profile['result'] == 'success') { | |
| $requestData['approvalFl'] = 'y'; | |
| $requestData['approvalDt'] = date('Y-m-d'); | |
| $requestData['approvalId'] = \Session::get('manager.managerId'); | |
| $requestData['kakaoKey'] = $profile['data']; | |
| /** @var Policy $policy */ | |
| $policy = \App::load('\\Component\\Policy\\Policy'); | |
| $result = $policy->saveKakaoAlrimConfig($requestData); | |
| if($result) { | |
| $kakaoAlrim->saveKakaoAuto($requestData); | |
| $this->json([ | |
| 'success' => true, | |
| 'message' => '카카오톡 채널이 등록되었습니다.', | |
| 'data' => [ | |
| 'alertMessage' => [ | |
| 'message' => '카카오톡 채널이 등록되었습니다.<br>메시지 설정 화면에서 등록된 채널 정보를 확인할 수 있습니다.' | |
| ], | |
| 'requestData' => $requestData | |
| ] | |
| ]); | |
| } else { | |
| \Logger::channel('kakao')->warning('카카오톡 채널 등록에 실패하였습니다.', [$requestData['sender']]); | |
| throw new \Exception("카카오톡 채널 등록에 실패했습니다."); | |
| } | |
| } | |
| break; | |
| } | |
| } catch (\Throwable $e) { | |
| \Logger::channel('kakao')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json([ | |
| 'success' => false, | |
| 'message' => $e->getMessage(), | |
| 'data' => [ | |
| 'alertMessage' => [ | |
| 'message' => '카카오톡 채널 등록에 실패했습니다.', | |
| 'subMessage' => '카카오 채널의 관리자 설정 또는 비즈니스 인증 상태를 확인 후 다시 시도해 주세요.' | |
| ] | |
| ] | |
| ]); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerRegisterKakaoTalkChannelPsController.php
변경 전LayerRejectNumberListController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| class LayerRejectNumberListController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerRejectNumberListController.php
변경 전LayerRejectNumberListPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Sms\Sms080; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| class LayerRejectNumberListPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request(); | |
| $mode = $requestData->get('mode'); | |
| switch ($mode) { | |
| case 'checkActivation': | |
| $policy = ComponentUtils::getPolicy('sms.sms080'); | |
| StringUtils::strIsSet($policy['status'], ''); | |
| $isOpened = $policy['status'] == 'O'; | |
| $this->json(['success' => $isOpened]); | |
| break; | |
| case 'manualSync': | |
| try { | |
| /** @var Sms080 $sms080 */ | |
| $sms080 = \App::load('Component\\Sms\\Sms080'); | |
| $sms080->syncListByManual(); | |
| $this->json(['success' => true, 'message' => "동기화가 완료되었습니다."]); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('sms')->warning($e->getMessage(), [__METHOD__, $e->getTraceAsString()]); | |
| $this->json(['success' => false, 'message' => "회원 정보와 동기화 중 오류가 발생했습니다."]); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerRejectNumberListPsController.php
변경 전LayerRejectNumberListResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Bundle\Component\Page\Page; | |
| use Bundle\Component\Sms\Sms080DAO; | |
| use Framework\Http\Request; | |
| class LayerRejectNumberListResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageConfig', 'messageConfig'); | |
| $naviMenu = $this->getData('naviMenu'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post(); | |
| $page = $postData->get('page', 1); | |
| $pageSize = $postData->get('pageSize', 10); | |
| $keyword = str_replace('-', '', $postData->get('keyword', '')); | |
| /** @var Sms080DAO $dao */ | |
| $dao = \App::load('Component\\Sms\\Sms080DAO'); | |
| $sms080RejectList = $dao->selectList(['offset' => $page, 'limit' => $pageSize, 'keyword' => $keyword]); | |
| $totalCount = $dao->countList(['keyword' => $keyword]); | |
| $amountCount = $dao->countList([]); | |
| $page = new Page($page, $totalCount, $amountCount, $pageSize); | |
| $this->setData('sms080RejectList', $sms080RejectList); | |
| $this->setData('keyword', $keyword); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerRejectNumberListResultController.php
변경 전LayerSmsVerificationController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MessageConfig; | |
| use Origin\Service\Crm\Message\SmsPasswordService; | |
| class LayerSmsVerificationController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageConfig', 'messageConfig'); | |
| $this->setMenuCode('crm', 'messageConfig', 'messageConfig'); | |
| try { | |
| /** @var SmsPasswordService $smsPasswordService */ | |
| $smsPasswordService = \App::getInstance(SmsPasswordService::class); | |
| $smsPassword = $smsPasswordService->getSmsPassword(true); | |
| $isVerified = $smsPasswordService->verifySmsPassword($smsPassword); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $isVerified = false; | |
| } | |
| $this->setData('smsPassword', $smsPassword); | |
| $this->setData('isVerified', $isVerified); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSmsVerificationController.php
변경 전MessageConfigPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Http\Request; | |
| use Origin\DTO\Crm\Message\InsufficMessagePointAlertConfigDTO; | |
| use Origin\DTO\Crm\Message\KakaoAlimTalkConfigDTO; | |
| use Origin\DTO\Crm\Message\KakaoFriendTalkConfigDTO; | |
| use Origin\DTO\Crm\Message\MessagePointConfigDTO; | |
| use Origin\DTO\Crm\Message\MyappPushConfigDTO; | |
| use Origin\DTO\Crm\Message\SmsAutoConfigDTO; | |
| use Origin\DTO\Crm\Message\SmsRejectConfigDTO; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| class MessageConfigPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageConfig', 'messageConfig'); | |
| $this->setMenuCode('crm', 'messageConfig', 'messageConfig'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $mode = $request->request()->get('mode'); | |
| switch ($mode) { | |
| case 'save': | |
| $postValue = $request->post()->toArray(); | |
| try { | |
| $messagePointConfig = new MessagePointConfigDTO($postValue['messagePointConfig']); | |
| $smsAutoConfig = new SmsAutoConfigDTO($postValue['smsAutoConfig']); | |
| $smsRejectConfig = $postValue['smsRejectUse'] ? new SmsRejectConfigDTO(['use' => $postValue['smsRejectUse']]) : null; | |
| $kakaoFriendTalkConfig = new KakaoFriendTalkConfigDTO($postValue['kakaoFriendTalkConfig']); | |
| $kakaoAlimTalkConfig = new KakaoAlimTalkConfigDTO($postValue['kakaoAlimTalkConfig']); | |
| $myappPushConfig = new MyappPushConfigDTO($postValue['myappPushConfig']); | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $messageConfigService->save( | |
| $messagePointConfig, | |
| $smsAutoConfig, | |
| $smsRejectConfig, | |
| $kakaoFriendTalkConfig, | |
| $kakaoAlimTalkConfig, | |
| $myappPushConfig | |
| ); | |
| $this->json(['success' => true, 'message' => '메시지 설정이 저장되었습니다.']); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->warning($e->getMessage(), ['requestData' => $postValue]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()]); | |
| } | |
| break; | |
| default: | |
| throw new \InvalidArgumentException('잘못된 접근입니다.'); | |
| } | |
| } | |
| } | |
변경 후MessageConfigPsController.php
변경 전MessageTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Bundle\Component\Page\Page; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateCategoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateKakaoStatusDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateListSearchDTO; | |
| use Origin\Service\Crm\Message\MessageTemplateFindService; | |
| use Origin\Traits\Member\KakaoAlrim\KakaoAlrimCloudAvailable; | |
| class MessageTemplateController extends \Controller\Admin\Controller | |
| { | |
| use KakaoAlrimCloudAvailable; | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'messageSend', 'messageTemplate'); | |
| $this->getView()->setPageName('crm/message_template.php'); | |
| $this->getView()->setDefine('messageTemplateSearch', 'crm/message_template/message_template_search.php'); | |
| $this->getView()->setDefine('messageTemplateResult', 'crm/message_template/message_template_result.php'); | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| /** @var MessageTemplateFindService $messageHistoryService */ | |
| $service = \App::getInstance(MessageTemplateFindService::class); | |
| $configDto = $service->getUseFlagDto(); | |
| $searchDto = new MessageTemplateListSearchDTO($requestData); | |
| $results = $service->getMessageTemplateList($searchDto); | |
| $isNewMall = $this->isNewMall(); | |
| $this->setData('sendMethod', $searchDto->getSendMethod()); | |
| $this->setData('search', $searchDto); | |
| $this->setData('config', $configDto); | |
| $this->setData('results', $results['result']); | |
| $this->setData('page', $results['page']); | |
| $this->setData('searchCategory', new MessageTemplateCategoryDTO()); | |
| $this->setData('searchStatus', new MessageTemplateKakaoStatusDTO()); | |
| $this->setData('isNewMall', $isNewMall); | |
| } | |
| } | |
변경 후MessageTemplateController.php
변경 전MessageTemplatePsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Http\Request; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateCopyRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateMyappDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateRemoveRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateSmsDTO; | |
| use Origin\Service\Crm\Message\MessageTemplateFindService; | |
| use Origin\Service\Crm\Message\MessageTemplateRegisterService; | |
| use Origin\Service\Crm\Message\MessageTemplateRemoveService; | |
| class MessageTemplatePsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| $mode = $requestData['mode'] ?? ''; | |
| switch ($mode) { | |
| case 'delete': | |
| $requestDto = new MessageTemplateRemoveRequestDTO($requestData); | |
| /** @var MessageTemplateRemoveService $service */ | |
| $service = \App::getInstance(MessageTemplateRemoveService::class); | |
| $result = $service->deleteTemplate($requestDto); | |
| break; | |
| case 'copy': | |
| $requestDto = new MessageTemplateCopyRequestDTO($requestData); | |
| /** @var MessageTemplateRegisterService $service */ | |
| $service = \App::getInstance(MessageTemplateRegisterService::class); | |
| $result = $service->copyTemplate($requestDto); | |
| break; | |
| default: | |
| $result = ['success' => false, 'message' => '잘못된 요청입니다.']; | |
| break; | |
| } | |
| $this->json($result); | |
| } | |
| } | |
변경 후MessageTemplatePsController.php
변경 전MessageTemplateRegisterController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Http\Request; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateCategoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateKakaoStatusDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateRegisterRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateResultDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateSearchDTO; | |
| use Origin\Service\Crm\Message\MessageTemplateFindService; | |
| use Origin\Service\Crm\Message\MyappPushTemplateService; | |
| use Origin\Traits\Member\KakaoAlrim\KakaoAlrimCloudAvailable; | |
| use function JmesPath\search; | |
| class MessageTemplateRegisterController extends \Controller\Admin\Controller | |
| { | |
| use KakaoAlrimCloudAvailable; | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| /** @var MessageTemplateFindService $messageHistoryService */ | |
| $service = \App::getInstance(MessageTemplateFindService::class); | |
| $configDto = $service->getUseFlagDto(); | |
| /** @var MessageTemplateRegisterRequestDTO $messageTemplateRegisterRequestDTO */ | |
| $requestDto = new MessageTemplateRegisterRequestDTO($requestData); | |
| $searchDto = new MessageTemplateSearchDTO($requestData); | |
| $this->getView()->setPageName('crm/message_template_register.php'); | |
| $this->getView()->setDefine('basicConfig', 'crm/message_template_register/basic_config.php'); | |
| $this->getView()->setDefine('templateContentsSms', 'crm/message_template_register/template_contents_sms.php'); | |
| $this->getView()->setDefine('templateContentsKakao', 'crm/message_template_register/template_contents_kakao.php'); | |
| $this->getView()->setDefine('templateContentsMyapp', 'crm/message_template_register/template_contents_myapp.php'); | |
| $this->getView()->setDefine('previewMessage', 'crm/message_template_register/preview_message.php'); | |
| switch ($requestDto->getMode()) { | |
| case 'register': | |
| $requestDto = $service->changeRegisterPormat($requestDto,$configDto); | |
| $this->callMenu('crm', 'messageSend', 'messageTemplateRegister'); | |
| $responseDto = new MessageTemplateResultDTO(); | |
| break; | |
| case 'copy': | |
| $this->callMenu('crm', 'messageSend', 'messageTemplateRegister'); | |
| $responseDto = $service->getMessageTemplate($searchDto); | |
| break; | |
| case 'modify': | |
| $this->callMenu('crm', 'messageSend', 'messageTemplateModify'); | |
| $responseDto = $service->getMessageTemplate($searchDto); | |
| break; | |
| default: | |
| $responseDto = new MessageTemplateResultDTO(); | |
| } | |
| $isNewMall = $this->isNewMall(); | |
| $myappService = \App::getInstance(MyappPushTemplateService::class); | |
| $this->getView()->setData('myappUrl', $myappService->getMyappDomainUrl()); | |
| $this->getView()->setData('config', $configDto); | |
| $this->getView()->setData('request', $requestDto); | |
| $this->getView()->setData('response', $responseDto); | |
| $this->setData('category', new MessageTemplateCategoryDTO()); | |
| $this->setData('status', new MessageTemplateKakaoStatusDTO()); | |
| $this->setData('isNewMall', $isNewMall); | |
| } | |
| } | |
변경 후MessageTemplateRegisterController.php
변경 전MessageTemplateRegisterPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateKakaoBizmDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateKakaoCloudDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateMyappDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateRegisterRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateSmsDTO; | |
| use Origin\Service\Crm\Message\MessageTemplateModifyService; | |
| use Origin\Service\Crm\Message\MessageTemplateRegisterService; | |
| class MessageTemplateRegisterPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| $filesData = $request->files()->toArray(); | |
| $requestData['mode'] = ($requestData['mode'] === 'copy') ? 'register' : $requestData['mode']; | |
| $requestDto = new MessageTemplateRegisterRequestDTO($requestData); | |
| switch ($requestDto->getMode()){ | |
| case 'register': | |
| /** @var MessageTemplateRegisterService $service */ | |
| $service = \App::getInstance(MessageTemplateRegisterService::class); | |
| $result = $service->saveTemplate($requestDto, $requestData, $filesData); | |
| break; | |
| case 'modify': | |
| /** @var MessageTemplateModifyService $service */ | |
| $service = \App::getInstance(MessageTemplateModifyService::class); | |
| $result = $service->updateTemplate($requestDto, $requestData, $filesData); | |
| break; | |
| default: | |
| $result = ['success' => false, 'message' => '잘못된 모드입니다.']; | |
| break; | |
| } | |
| $this->json($result); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => '등록, 수정에 실패하였습니다']); | |
| } | |
| } | |
| } | |
변경 후MessageTemplateRegisterPsController.php
변경 전MobileHistoryController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use App; | |
| use Component\Sms\Sms; | |
| use Component\Sms\SmsUtil; | |
| use DateTime; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistorySummaryRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistorySummaryResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Crm\CampaignCountsRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Crm\CampaignCountsResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageHistorySummaryResponseDTO; | |
| use Origin\Enum\ApiClient\Crm\CampaignSendType; | |
| use Origin\Enum\ApiClient\Crm\CampaignStatusType; | |
| use Origin\Service\AutoSend\ActiveAutoSendService; | |
| use Origin\Service\Crm\Message\CrmNotificationCampaignService; | |
| use Origin\Service\Crm\Message\MessageHistoryService; | |
| use Origin\Service\Crm\Message\MyappPushHistoryService; | |
| class MobileHistoryController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistory'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistory'); | |
| try { | |
| /** @var MessageHistoryService $messageHistoryService */ | |
| $messageHistoryService = \App::getInstance(MessageHistoryService::class); | |
| $messageHistorySummaryResponseDTO = $messageHistoryService->getMessageHistorySummary(); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $messageHistorySummaryResponseDTO = new MessageHistorySummaryResponseDTO(null); | |
| } | |
| try { | |
| $requestDTO = (new MyappPushHistorySummaryRequestDTO()) | |
| ->setStartDate(new DateTime('-7 days')) | |
| ->setEndDate(new DateTime('-1 days')); | |
| /** @var MyappPushHistoryService $myappPushHistoryService */ | |
| $myappPushHistoryService = \App::getInstance(MyappPushHistoryService::class); | |
| $myappPushHistorySummaryResponseDTO = $myappPushHistoryService->getMyappPushHistorySummary($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $myappPushHistorySummaryResponseDTO = new MyappPushHistorySummaryResponseDTO(null); | |
| } | |
| try { | |
| $requestDTO = (new CampaignCountsRequestDTO()) | |
| ->setSendType([CampaignSendType::SCHEDULED, CampaignSendType::REPEAT]) | |
| ->setStatusType([CampaignStatusType::WAITING, CampaignStatusType::PROCESSING]); | |
| /** @var CrmNotificationCampaignService $crmNotificationCampaignService */ | |
| $crmNotificationCampaignService = \App::getInstance(CrmNotificationCampaignService::class); | |
| $crmNotificationCampaignCountsResponseDTO = $crmNotificationCampaignService->getNotificationCampaignCounts($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $crmNotificationCampaignCountsResponseDTO = new CampaignCountsResponseDTO(null); | |
| } | |
| // 현재 SMS 포인트 싱크 | |
| Sms::saveSmsPoint(); | |
| // 현재 메시지 포인트 | |
| $nowSmsPoint = (float) Sms::getPoint(); | |
| $availableCount = [ | |
| 'sms' => SmsUtil::getAvailableCount('sms', $nowSmsPoint), | |
| 'lms' => SmsUtil::getAvailableCount('lms', $nowSmsPoint), | |
| 'kakaoAlrimTalk' => SmsUtil::getAvailableCount('kakaoAlrimTalk', $nowSmsPoint), | |
| 'kakaoFriendTalk' => SmsUtil::getAvailableCount('kakaoFriendTalk', $nowSmsPoint), | |
| ]; | |
| /** @var ActiveAutoSendService $activeAutoSendService */ | |
| $activeAutoSendService = \App::getInstance(ActiveAutoSendService::class); | |
| $autoSendCounts = [ | |
| 'active' => $activeAutoSendService->count(), | |
| ]; | |
| $this->setData('availableCount', $availableCount); | |
| $this->setData('messageCountLast7Days', $messageHistorySummaryResponseDTO->getMessageCountLast7Days()); | |
| $this->setData('friendtalkCountLast7Days', $messageHistorySummaryResponseDTO->getFriendtalkCountLast7Days()); | |
| $this->setData('alimtalkCountLast7Days', $messageHistorySummaryResponseDTO->getAlimtalkCountLast7Days()); | |
| $this->setData('myappPushCountLast7Days', $myappPushHistorySummaryResponseDTO->getTotalPushMessageCount()); | |
| $this->setData('pointUsageLast7Days', $messageHistorySummaryResponseDTO->getPointUsageLast7Days()); | |
| $this->setData('pointUsageLast28Days', $messageHistorySummaryResponseDTO->getPointUsageLast28Days()); | |
| $this->setData('crmNotificationCampaignCounts', $crmNotificationCampaignCountsResponseDTO->getStatusCount()); | |
| $this->setData('autoSendCounts', $autoSendCounts); | |
| $this->setData('scheduledSendWaitingUrl', './scheduled_send.php?statusType=WAITING'); | |
| $this->setData('scheduledSendRepeatUrl', './scheduled_send.php?formType=REPEAT&statusType=PROCESSING&periodType=1YEAR&createdAtFrom=' . date('Y-m-d', strtotime('-1 year')) . '&createdAtTo=' . date('Y-m-d')); | |
| $this->setData('autoSendUrl', './auto_send.php?shouldAutoSend=y'); | |
| $this->getView()->setPageName('crm/mobile_history.php'); | |
| } | |
| } | |
변경 후MobileHistoryController.php
변경 전MobileHistoryListController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Http\Request; | |
| class MobileHistoryListController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $getData = $request->get()->toArray(); | |
| $this->setData('sendMethod', $getData['sendMethod']); | |
| $this->getView()->setPageName('crm/mobile_history_list.php'); | |
| } | |
| } | |
변경 후MobileHistoryListController.php
변경 전LayerBizmKakaoAlimTalkHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\BizmKakaoAlimTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\BizmKakaoAlimTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\BizmKakaoAlimTalkHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\BizmKakaoAlimTalkHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| class LayerBizmKakaoAlimTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new BizmKakaoAlimTalkHistorySearchFormDTO($postData); | |
| $searchFromArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFromArray); | |
| $requestDTO = new BizmKakaoAlimTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isEnabled = $kakaoAlimTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoAlimTalkHistoryService $kakaoAlimTalkHistoryService */ | |
| $kakaoAlimTalkHistoryService = \App::getInstance(KakaoAlimTalkHistoryService::class); | |
| $bizmKakaoAlimTalkHistoryListResponseDTO = $kakaoAlimTalkHistoryService->getBizmKakaoAlimTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $bizmKakaoAlimTalkHistoryListResponseDTO = new BizmKakaoAlimTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $bizmKakaoAlimTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormDTO->toRecursiveArray()); | |
| $this->setData('excelSearchQuery', $requestDTO->toRecursiveArray()); | |
| $this->setData('excelMenu', ExcelGenerateMenu::ADMIN_BIZM_ALIMTALK_SEND_RESULT); | |
| $this->setData('sort', KakaoAlimTalkHistoryService::SORT_LIST); | |
| $this->setData('bizmKakaoAlimTalkHistoryList', $this->sanitizeHistoryList($bizmKakaoAlimTalkHistoryListResponseDTO->getBizmKakaoAlimTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sort = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sort[1] === 'desc') ? 'true' : 'false'; | |
| unset($preparedData['triggerTypes']); // bizm 에서는 사용하지 않음 | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoAlimTalkSendStatus::availableForSearch()); | |
| if (!empty($preparedData['sendStatuses']) && !array_diff($preparedData['sendStatuses'], $allSendStatuses) && !array_diff($allSendStatuses, $preparedData['sendStatuses'])) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| return $preparedData; | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param BizmKakaoAlimTalkHistoryDTO[] $historyList | |
| * @return BizmKakaoAlimTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| } | |
변경 후LayerBizmKakaoAlimTalkHistoryResultController.php
변경 전LayerCloudKakaoAlimTalkHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\CloudKakaoAlimTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\CloudKakaoAlimTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\CloudKakaoAlimTalkHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\CloudKakaoAlimTalkHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkTriggerType; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| class LayerCloudKakaoAlimTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new CloudKakaoAlimTalkHistorySearchFormDTO($postData); | |
| $searchFromArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFromArray); | |
| $requestDTO = new CloudKakaoAlimTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isEnabled = $kakaoAlimTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoAlimTalkHistoryService $kakaoAlimTalkHistoryService */ | |
| $kakaoAlimTalkHistoryService = \App::getInstance(KakaoAlimTalkHistoryService::class); | |
| $cloudKakaoAlimTalkHistoryListResponseDTO = $kakaoAlimTalkHistoryService->getCloudKakaoAlimTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $cloudKakaoAlimTalkHistoryListResponseDTO = new CloudKakaoAlimTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $cloudKakaoAlimTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFromArray); | |
| $this->setData('excelSearchQuery', $requestDTO->toRecursiveArray()); | |
| $this->setData('excelMenu', ExcelGenerateMenu::ADMIN_ALIMTALK_SEND_RESULT); | |
| $this->setData('sort', KakaoAlimTalkHistoryService::SORT_LIST); | |
| $this->setData('cloudKakaoAlimTalkHistoryList', $this->sanitizeHistoryList($cloudKakaoAlimTalkHistoryListResponseDTO->getCloudKakaoAlimTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param CloudKakaoAlimTalkHistoryDTO[] $historyList | |
| * @return CloudKakaoAlimTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sort = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sort[1] === 'desc') ? 'true' : 'false'; | |
| $allTriggerTypes = array_map(fn($e) => $e->name, KakaoAlimTalkTriggerType::availableForSearch()); | |
| if (!empty($preparedData['triggerTypes']) && !array_diff($preparedData['triggerTypes'], $allTriggerTypes) && !array_diff($allTriggerTypes, $preparedData['triggerTypes'])) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoAlimTalkSendStatus::availableForSearch()); | |
| if (!empty($preparedData['sendStatuses']) && !array_diff($preparedData['sendStatuses'], $allSendStatuses) && !array_diff($allSendStatuses, $preparedData['sendStatuses'])) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerCloudKakaoAlimTalkHistoryResultController.php
변경 전LayerKakaoAlimTalkContentsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\KakaoAlimTalkHistoryContentRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\KakaoAlimTalkTemplateButtonDTO; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkTemplateButtonType; | |
| use Origin\Enum\ApiClient\Notification\NotificationSendType; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkHistoryService; | |
| class LayerKakaoAlimTalkContentsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $sendGroupKey = $request->post()->get('sendGroupKey'); | |
| try { | |
| $requestDTO = (new KakaoAlimTalkHistoryContentRequestDTO(['type' => NotificationSendType::ALIMTALK->name])); | |
| /** @var KakaoAlimTalkHistoryService $kakaoAlimTalkHistoryService */ | |
| $kakaoAlimTalkHistoryService = \App::getInstance(KakaoAlimTalkHistoryService::class); | |
| $kakaoAlimTalkTalkHistoryContentDTO = $kakaoAlimTalkHistoryService->getKakaoAlimTalkHistoryContent($sendGroupKey, $requestDTO); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| $templateButtons = $kakaoAlimTalkTalkHistoryContentDTO->getTemplateButtons(); | |
| $hasAddChannelButton = !empty(array_filter($templateButtons ?? [], fn($button) => $button->getType() === KakaoAlimTalkTemplateButtonType::ADD_CHANNEL)); | |
| $this->sortButtons($templateButtons); | |
| $previewMessage = null; | |
| if ($kakaoAlimTalkTalkHistoryContentDTO->isPendingOrRequestFailed()) { | |
| $previewMessage = '발송 중이거나 발송 요청 단계에서 실패한 메시지는 본문 내용만 제공됩니다.'; | |
| } elseif (!$kakaoAlimTalkTalkHistoryContentDTO->isTemplateExists() || !empty($kakaoAlimTalkTalkHistoryContentDTO->getTemplateImageUrl())) { | |
| $previewMessage = '상단 이미지는 현재 템플릿 기준으로 제공되며, 템플릿이 삭제된 경우 이미지를 표시할 수 없습니다.'; | |
| } | |
| $this->setData('kakaoAlimTalkTalkHistoryContentDTO', $kakaoAlimTalkTalkHistoryContentDTO); | |
| $this->setData('templateContent', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateContent()); | |
| $this->setData('templateExtra', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateExtra()); | |
| $this->setData('hasAddChannelButton', $hasAddChannelButton); | |
| $this->setData('templateButtons', $templateButtons); | |
| $this->setData('templateTitle', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateTitle()); | |
| $this->setData('templateSubTitle', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateSubTitle()); | |
| $this->setData('templateItemHighlight', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateItemHighlight()); | |
| $this->setData('templateItems', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateItems()); | |
| $this->setData('templateImageUrl', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateImageUrl()); | |
| $this->setData('isTemplateExists', $kakaoAlimTalkTalkHistoryContentDTO->isTemplateExists()); | |
| $this->setData('previewMessage', $previewMessage); | |
| $this->setData('isPendingOrRequestFailed', $kakaoAlimTalkTalkHistoryContentDTO->isPendingOrRequestFailed()); | |
| $this->setData('templateHeader', $kakaoAlimTalkTalkHistoryContentDTO->getTemplateHeader()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| /** | |
| * 버튼 유형에 따라 분리 | |
| * | |
| * @param KakaoAlimTalkTemplateButtonDTO[] $templateButtons | |
| * @return void | |
| */ | |
| protected function sortButtons(?array &$templateButtons): void | |
| { | |
| if (empty($templateButtons)) { | |
| return; | |
| } | |
| usort($templateButtons, function ($a, $b) { | |
| return ($a->getOrder() ?? 0) <=> ($b->getOrder() ?? 0); | |
| }); | |
| } | |
| } | |
변경 후LayerKakaoAlimTalkContentsController.php
변경 전LayerKakaoAlimTalkHistorySearchController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Enum\ExternalUrl; | |
| use Framework\Utility\GodoUtils; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkResultSearchType; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkTriggerType; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Traits\Member\KakaoAlrim\KakaoAlrimCloudAvailable; | |
| class LayerKakaoAlimTalkHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| use KakaoAlrimCloudAvailable; | |
| public function index() | |
| { | |
| $triggerTypes = KakaoAlimTalkTriggerType::availableForSearch(); | |
| $sendStatuses = KakaoAlimTalkSendStatus::availableForSearch(); | |
| $searchType = KakaoAlimTalkResultSearchType::generateSelectBoxData(); | |
| $isKakaoAlrimAvailable = !$this->isNewMall(); | |
| $isKakaoAlrimLunaInstalled = GodoUtils::isPlusShop(PLUSSHOP_CODE_KAKAOALRIMLUNA); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isSelectedBizm = $kakaoAlimTalkConfig->getSender() === KakaoAlimTalkSender::BIZM; | |
| $this->setData('searchType', $searchType); | |
| $this->setData('isSelectedBizm', $isSelectedBizm); | |
| $this->setData('triggerTypes', $triggerTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->setData('isKakaoAlrimAvailable', $isKakaoAlrimAvailable); | |
| $this->setData('isKakaoAlrimLunaInstalled', $isKakaoAlrimLunaInstalled); | |
| $this->setData('blumnAiStatisticsUrl', ExternalUrl::BIZMSG_BLUMN_AI->getUrl('/statistics-alimtalk')); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerKakaoAlimTalkHistorySearchController.php
변경 전LayerKakaoFriendTalkContentsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkType; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkHistoryService; | |
| class LayerKakaoFriendTalkContentsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $sendHistoryNo = $request->post()->get('sendHistoryNo'); | |
| try { | |
| /** @var KakaoFriendTalkHistoryService $kakaoFriendTalkHistoryService */ | |
| $kakaoFriendTalkHistoryService = \App::getInstance(KakaoFriendTalkHistoryService::class); | |
| $kakaoFriendTalkHistoryContentDTO = $kakaoFriendTalkHistoryService->getKakaoFriendTalkHistoryContent($sendHistoryNo); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| \Logger::channel('mobileMessage')->info('content', $kakaoFriendTalkHistoryContentDTO->getContent()); | |
| $this->setData('type', $kakaoFriendTalkHistoryContentDTO->getType()); | |
| $this->setData('isText', $kakaoFriendTalkHistoryContentDTO->getType() === KakaoFriendTalkType::TEXT); | |
| $this->setData('isCarousel', $kakaoFriendTalkHistoryContentDTO->getType() === KakaoFriendTalkType::CAROUSEL); | |
| $this->setData('isWideItemList', $kakaoFriendTalkHistoryContentDTO->getType() === KakaoFriendTalkType::WIDE_ITEM_LIST); | |
| $this->setData('wideTypes', KakaoFriendTalkType::wideTypes()); | |
| $this->setData('availableImageTypes', KakaoFriendTalkType::availableImageTypes()); | |
| $this->setData('singleButtonTypes', KakaoFriendTalkType::singleButtonTypes()); | |
| $this->setData('doubleButtonTypes', KakaoFriendTalkType::doubleButtonTypes()); | |
| $this->setData('header', $kakaoFriendTalkHistoryContentDTO->getHeader()); | |
| $this->setData('imageUrl', $kakaoFriendTalkHistoryContentDTO->getImageUrl()); | |
| $this->setData('imageLink', $kakaoFriendTalkHistoryContentDTO->getImageLink()); | |
| $this->setData('items', $kakaoFriendTalkHistoryContentDTO->getItems()); | |
| $this->setData('content', $kakaoFriendTalkHistoryContentDTO->getContent()); | |
| $this->setData('buttons', $kakaoFriendTalkHistoryContentDTO->getButtons()); | |
| $this->setData('coupon', $kakaoFriendTalkHistoryContentDTO->getCoupon()); | |
| $this->setData('carousels', $kakaoFriendTalkHistoryContentDTO->getCarousels()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerKakaoFriendTalkContentsController.php
변경 전LayerKakaoFriendTalkHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Origin\DTO\ApiClient\Commerce\Apps\KakaoFriendTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\KakaoFriendTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\KakaoFriendTalkHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\KakaoFriendTalkHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultDirection; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultSortType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkScheduleType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkSendStatus; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| class LayerKakaoFriendTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new KakaoFriendTalkHistorySearchFormDTO($postData); | |
| $searchFormArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFormArray); | |
| $requestDTO = new KakaoFriendTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| $isEnabled = $kakaoFriendTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoFriendTalkHistoryService $kakaoFriendTalkHistoryService */ | |
| $kakaoFriendTalkHistoryService = \App::getInstance(KakaoFriendTalkHistoryService::class); | |
| $kakaoFriendTalkHistoryListResponseDTO = $kakaoFriendTalkHistoryService->getKakaoFriendTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $kakaoFriendTalkHistoryListResponseDTO = new KakaoFriendTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $kakaoFriendTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormArray); | |
| $this->setData('excelSearchQuery', $requestDTO->toRecursiveArray()); | |
| $this->setData('excelMenu', ExcelGenerateMenu::ADMIN_FRIENDTALK_SEND_RESULT); | |
| $this->setData('sort', KakaoFriendTalkHistoryService::generateSortList()); | |
| $this->setData('kakaoFriendTalkHistoryList', $this->sanitizeHistoryList($kakaoFriendTalkHistoryListResponseDTO->getKakaoFriendTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param KakaoFriendTalkHistoryDTO[] $historyList | |
| * @return KakaoFriendTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendGroupKey = $dto->getSendGroupKey(); | |
| if (!empty($sendGroupKey)) { | |
| $dto->setSendGroupKey(htmlspecialchars($sendGroupKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $campaignName = $dto->getCampaignName(); | |
| if (!empty($campaignName)) { | |
| $dto->setCampaignName(htmlspecialchars($campaignName, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $snapshot = $dto->getSnapshot(); | |
| if (!empty($snapshot)) { | |
| $dto->setSnapshot(htmlspecialchars($snapshot, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| $sort = explode(' ', $preparedData['sort'] ?? (KakaoFriendTalkResultSortType::CREATED_DATE_TIME->name . ' ' . KakaoFriendTalkResultDirection::DESC->name)); | |
| $preparedData['sortType'] = (KakaoFriendTalkResultSortType::tryFromName($sort[0]) ?? KakaoFriendTalkResultSortType::CREATED_DATE_TIME)->name; | |
| $preparedData['direction'] = (KakaoFriendTalkResultDirection::tryFromName(strtoupper($sort[1])) ?? KakaoFriendTalkResultDirection::DESC)->name; | |
| $allScheduleTypes = array_map(fn($e) => $e->name, KakaoFriendTalkScheduleType::cases()); | |
| if (!empty($preparedData['scheduleTypes']) && !array_diff($preparedData['scheduleTypes'], $allScheduleTypes) && !array_diff($allScheduleTypes, $preparedData['scheduleTypes'])) { | |
| unset($preparedData['scheduleTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoFriendTalkSendStatus::availableForSearch()); | |
| if (!empty($preparedData['sendResultStatuses']) && !array_diff($preparedData['sendResultStatuses'], $allSendStatuses) && !array_diff($allSendStatuses, $preparedData['sendResultStatuses'])) { | |
| unset($preparedData['sendResultStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerKakaoFriendTalkHistoryResultController.php
변경 전LayerKakaoFriendTalkHistorySearchController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultSearchType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkScheduleType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkSendStatus; | |
| class LayerKakaoFriendTalkHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $scheduleTypes = KakaoFriendTalkScheduleType::cases(); | |
| $sendResultStatuses = KakaoFriendTalkSendStatus::availableForSearch(); | |
| $searchType = KakaoFriendTalkResultSearchType::generateSelectBoxData(); | |
| $this->setData('searchType', $searchType); | |
| $this->setData('scheduleTypes', $scheduleTypes); | |
| $this->setData('sendResultStatuses', $sendResultStatuses); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerKakaoFriendTalkHistorySearchController.php
변경 전LayerLunaKakaoAlimTalkHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| class LayerLunaKakaoAlimTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $naviMenu = $this->getData('naviMenu'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $requestData = $request->request()->toArray(); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerLunaKakaoAlimTalkHistoryResultController.php
변경 전LayerMemberBizmKakaoAlimTalkHistoryResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberBizmKakaoAlimTalkHistorySearchFormDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberBizmKakaoAlimTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberBizmKakaoAlimTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberBizmKakaoAlimTalkHistoryListResponseDTO; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| class LayerMemberBizmKakaoAlimTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new MemberBizmKakaoAlimTalkHistorySearchFormDTO($postData); | |
| $searchFromArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFromArray); | |
| $requestDTO = new MemberBizmKakaoAlimTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isEnabled = $kakaoAlimTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoAlimTalkHistoryService $kakaoAlimTalkHistoryService */ | |
| $kakaoAlimTalkHistoryService = \App::getInstance(KakaoAlimTalkHistoryService::class); | |
| $bizmKakaoAlimTalkHistoryListResponseDTO = $kakaoAlimTalkHistoryService->getMemberBizmKakaoAlimTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $bizmKakaoAlimTalkHistoryListResponseDTO = new MemberBizmKakaoAlimTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $bizmKakaoAlimTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormDTO->toRecursiveArray()); | |
| $this->setData('sort', KakaoAlimTalkHistoryService::SORT_LIST); | |
| $this->setData('bizmKakaoAlimTalkHistoryList', $this->sanitizeHistoryList($bizmKakaoAlimTalkHistoryListResponseDTO->getBizmKakaoAlimTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $throwable) { | |
| $this->json(['success' => false, 'message' => $throwable->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sort = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sort[1] === 'desc') ? 'true' : 'false'; | |
| unset($preparedData['triggerTypes']); // bizm 에서는 사용하지 않음 | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoAlimTalkSendStatus::availableForMemberHistorySearch()); | |
| if ( | |
| !empty($preparedData['sendStatuses']) && | |
| !array_diff($preparedData['sendStatuses'], $allSendStatuses) && | |
| !array_diff($allSendStatuses, $preparedData['sendStatuses']) | |
| ) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| return $preparedData; | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param MemberBizmKakaoAlimTalkHistoryDTO[] $historyList | |
| * @return MemberBizmKakaoAlimTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| } | |
변경 후LayerMemberBizmKakaoAlimTalkHistoryResultController.php
변경 전LayerMemberCloudKakaoAlimTalkHistoryResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberCloudKakaoAlimTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberCloudKakaoAlimTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberCloudKakaoAlimTalkHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberCloudKakaoAlimTalkHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkTriggerType; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| class LayerMemberCloudKakaoAlimTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new MemberCloudKakaoAlimTalkHistorySearchFormDTO($postData); | |
| $searchFromArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFromArray); | |
| $requestDTO = new MemberCloudKakaoAlimTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isEnabled = $kakaoAlimTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoAlimTalkHistoryService $kakaoAlimTalkHistoryService */ | |
| $kakaoAlimTalkHistoryService = \App::getInstance(KakaoAlimTalkHistoryService::class); | |
| $cloudKakaoAlimTalkHistoryListResponseDTO = $kakaoAlimTalkHistoryService->getMemberCloudKakaoAlimTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $cloudKakaoAlimTalkHistoryListResponseDTO = new MemberCloudKakaoAlimTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $cloudKakaoAlimTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFromArray); | |
| $this->setData('sort', KakaoAlimTalkHistoryService::SORT_LIST); | |
| $this->setData('cloudKakaoAlimTalkHistoryList', $this->sanitizeHistoryList($cloudKakaoAlimTalkHistoryListResponseDTO->getMemberCloudKakaoAlimTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $throwable) { | |
| $this->json(['success' => false, 'message' => $throwable->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param MemberCloudKakaoAlimTalkHistoryDTO[] $historyList | |
| * @return MemberCloudKakaoAlimTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sort = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sort[1] === 'desc') ? 'true' : 'false'; | |
| $allTriggerTypes = array_map(fn($e) => $e->name, KakaoAlimTalkTriggerType::availableForSearch()); | |
| if (!empty($preparedData['triggerTypes']) && !array_diff($preparedData['triggerTypes'], $allTriggerTypes) && !array_diff($allTriggerTypes, $preparedData['triggerTypes'])) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoAlimTalkSendStatus::availableForMemberHistorySearch()); | |
| if ( | |
| !empty($preparedData['sendStatuses']) && | |
| !array_diff($preparedData['sendStatuses'], $allSendStatuses) && | |
| !array_diff($allSendStatuses, $preparedData['sendStatuses']) | |
| ) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerMemberCloudKakaoAlimTalkHistoryResultController.php
변경 전LayerMemberKakaoAlimTalkHistorySearchController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Enum\ExternalUrl; | |
| use Framework\Http\Request; | |
| use Framework\Utility\GodoUtils; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkResultSearchType; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkSendStatus; | |
| use Origin\Enum\ApiClient\Notification\KakaoAlimTalkTriggerType; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkSender; | |
| use Origin\Service\Crm\Message\KakaoAlimTalkSettingService; | |
| use Origin\Traits\Member\KakaoAlrim\KakaoAlrimCloudAvailable; | |
| class LayerMemberKakaoAlimTalkHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| use KakaoAlrimCloudAvailable; | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $memberNo = filter_var($postData['memberNo'] ?? 0, FILTER_VALIDATE_INT) ?: 0; | |
| $triggerTypes = KakaoAlimTalkTriggerType::availableForSearch(); | |
| $sendStatuses = KakaoAlimTalkSendStatus::availableForMemberHistorySearch(); | |
| $searchType = KakaoAlimTalkResultSearchType::generateSelectBoxData(); | |
| $isKakaoAlrimAvailable = !$this->isNewMall(); | |
| $isKakaoAlrimLunaInstalled = GodoUtils::isPlusShop(PLUSSHOP_CODE_KAKAOALRIMLUNA); | |
| /** @var KakaoAlimTalkSettingService $kakaoAlimTalkSettingService */ | |
| $kakaoAlimTalkSettingService = \App::getInstance(KakaoAlimTalkSettingService::class); | |
| $kakaoAlimTalkConfig = $kakaoAlimTalkSettingService->getKakaoAlimTalkConfig(); | |
| $isSelectedBizm = $kakaoAlimTalkConfig->getSender() === KakaoAlimTalkSender::BIZM; | |
| $this->setData('memberNo', $memberNo); | |
| $this->setData('searchType', $searchType); | |
| $this->setData('isSelectedBizm', $isSelectedBizm); | |
| $this->setData('triggerTypes', $triggerTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->setData('isKakaoAlrimAvailable', $isKakaoAlrimAvailable); | |
| $this->setData('isKakaoAlrimLunaInstalled', $isKakaoAlrimLunaInstalled); | |
| $this->setData('blumnAiStatisticsUrl', ExternalUrl::BIZMSG_BLUMN_AI->getUrl('/statistics-alimtalk')); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMemberKakaoAlimTalkHistorySearchController.php
변경 전LayerMemberKakaoFriendTalkHistoryResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MemberKakaoFriendTalkHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MemberKakaoFriendTalkHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MemberKakaoFriendTalkHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MemberKakaoFriendTalkHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultDirection; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultSortType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkScheduleType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkSendStatus; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkHistoryService; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| class LayerMemberKakaoFriendTalkHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new MemberKakaoFriendTalkHistorySearchFormDTO($postData); | |
| $searchFormArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFormArray); | |
| $requestDTO = new MemberKakaoFriendTalkHistoryListRequestDTO($preparedData); | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| $isEnabled = $kakaoFriendTalkConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var KakaoFriendTalkHistoryService $kakaoFriendTalkHistoryService */ | |
| $kakaoFriendTalkHistoryService = \App::getInstance(KakaoFriendTalkHistoryService::class); | |
| $kakaoFriendTalkHistoryListResponseDTO = $kakaoFriendTalkHistoryService->getMemberKakaoFriendTalkHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $kakaoFriendTalkHistoryListResponseDTO = new MemberKakaoFriendTalkHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $kakaoFriendTalkHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormArray); | |
| $this->setData('sort', KakaoFriendTalkHistoryService::generateSortList()); | |
| $this->setData('kakaoFriendTalkHistoryList', $this->sanitizeHistoryList($kakaoFriendTalkHistoryListResponseDTO->getKakaoFriendTalkHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $throwable) { | |
| $this->json(['success' => false, 'message' => $throwable->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param MemberKakaoFriendTalkHistoryDTO[] $historyList | |
| * @return MemberKakaoFriendTalkHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendGroupKey = $dto->getSendGroupKey(); | |
| if (!empty($sendGroupKey)) { | |
| $dto->setSendGroupKey(htmlspecialchars($sendGroupKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $campaignName = $dto->getCampaignName(); | |
| if (!empty($campaignName)) { | |
| $dto->setCampaignName(htmlspecialchars($campaignName, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // 정렬 기준 추가를 대비하여 분리 | |
| $sortType = explode(' ', $preparedData['sort'] ?? (KakaoFriendTalkResultSortType::CREATED_DATE_TIME->name . ' ' . KakaoFriendTalkResultDirection::DESC->name)); | |
| $preparedData['sortDescending'] = ($sortType[1] === KakaoFriendTalkResultDirection::DESC->name) ? 'true' : 'false'; | |
| $allScheduleTypes = array_map(fn($e) => $e->name, KakaoFriendTalkScheduleType::cases()); | |
| if ( | |
| !empty($preparedData['triggerTypes']) && | |
| !array_diff($preparedData['triggerTypes'], $allScheduleTypes) && | |
| !array_diff($allScheduleTypes, $preparedData['triggerTypes']) | |
| ) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, KakaoFriendTalkSendStatus::availableForMemberHistorySearch()); | |
| if ( | |
| !empty($preparedData['sendStatuses']) && | |
| !array_diff($preparedData['sendStatuses'], $allSendStatuses) && | |
| !array_diff($allSendStatuses, $preparedData['sendStatuses']) | |
| ) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerMemberKakaoFriendTalkHistoryResultController.php
변경 전LayerMemberKakaoFriendTalkHistorySearchController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkResultSearchType; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkSendStatus; | |
| use Origin\Enum\ApiClient\Apps\KakaoFriendTalkTriggerType; | |
| class LayerMemberKakaoFriendTalkHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $memberNo = filter_var($postData['memberNo'] ?? 0, FILTER_VALIDATE_INT) ?: 0; | |
| $triggerTypes = KakaoFriendTalkTriggerType::cases(); | |
| $sendStatuses = KakaoFriendTalkSendStatus::availableForMemberHistorySearch(); | |
| $searchType = KakaoFriendTalkResultSearchType::generateSelectBoxData(); | |
| $this->setData('memberNo', $memberNo); | |
| $this->setData('searchType', $searchType); | |
| $this->setData('triggerTypes', $triggerTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMemberKakaoFriendTalkHistorySearchController.php
변경 전LayerMemberSmsHistoryResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberSmsHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberSmsHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberSmsHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MemberSmsHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Notification\SmsSendStatus; | |
| use Origin\Enum\ApiClient\Notification\SmsTriggerType; | |
| use Origin\Service\Crm\Message\SmsHistoryService; | |
| class LayerMemberSmsHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new MemberSmsHistorySearchFormDTO($postData); | |
| $searchFormArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFormArray); | |
| $requestDTO = new MemberSmsHistoryListRequestDTO($preparedData); | |
| try { | |
| /** @var SmsHistoryService $smsHistoryService */ | |
| $smsHistoryService = \App::getInstance(SmsHistoryService::class); | |
| $smsHistoryListResponseDTO = $smsHistoryService->getMemberSmsHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO]); | |
| $smsHistoryListResponseDTO = new MemberSmsHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $smsHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormArray); | |
| $this->setData('sortType', SmsHistoryService::SORT_LIST); | |
| $this->setData('smsHistoryList', $this->sanitizeHistoryList($smsHistoryListResponseDTO->getMemberSmsHistoryList())); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $throwable) { | |
| $this->json(['success' => false, 'message' => $throwable->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param MemberSmsHistoryDTO[] $historyList | |
| * @return MemberSmsHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $senderName = $dto->getSenderName(); | |
| if (!empty($senderName)) { | |
| $dto->setSenderName(htmlspecialchars($senderName, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $from = $dto->getFrom(); | |
| if (!empty($from)) { | |
| $dto->setFrom(htmlspecialchars($from, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sortType = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sortType[1] === 'desc') ? 'true' : 'false'; | |
| $allTriggerTypes = array_map(fn($e) => $e->name, SmsTriggerType::cases()); | |
| if (!empty($preparedData['triggerTypes']) && !array_diff($preparedData['triggerTypes'], $allTriggerTypes) && !array_diff($allTriggerTypes, $preparedData['triggerTypes'])) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, SmsSendStatus::availableForMemberHistorySearch()); | |
| if ( | |
| !empty($preparedData['sendStatuses']) && | |
| !array_diff($preparedData['sendStatuses'], $allSendStatuses) && | |
| !array_diff($allSendStatuses, $preparedData['sendStatuses']) | |
| ) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['smsType'])) { | |
| unset($preparedData['smsType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerMemberSmsHistoryResultController.php
변경 전LayerMemberSmsHistorySearchController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Origin\Enum\ApiClient\Notification\SmsResultSearchType; | |
| use Origin\Enum\ApiClient\Notification\SmsSendStatus; | |
| use Origin\Enum\ApiClient\Notification\SmsTriggerType; | |
| use Origin\Enum\ApiClient\Notification\SmsType; | |
| class LayerMemberSmsHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $triggerTypes = SmsTriggerType::cases(); | |
| $sendStatuses = SmsSendStatus::availableForMemberHistorySearch(); | |
| $smsTypes = SmsType::cases(); | |
| $memberNo = filter_var($postData['memberNo'] ?? 0, FILTER_VALIDATE_INT) ?: 0; | |
| $searchType = SmsResultSearchType::generateSelectBoxData((bool) $memberNo); | |
| $this->setData('memberNo', $memberNo); | |
| $this->setData('triggerTypes', $triggerTypes); | |
| $this->setData('smsTypes', $smsTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->setData('searchType', $searchType); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMemberSmsHistorySearchController.php
변경 전LayerMessageHistoryDetailController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Origin\Enum\ApiClient\Notification\MessageResultType; | |
| use Origin\Enum\Crm\Message\SendMethod; | |
| class LayerMessageHistoryDetailController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $sendGroupKey = $postData['sendGroupKey']; | |
| $type = $postData['type']; | |
| $this->setData('sendGroupKey', $sendGroupKey); | |
| $this->setData('type', $type); | |
| if ($type === SendMethod::SMS->name) { | |
| $this->setData('messageResultTypes', MessageResultType::cases()); | |
| } else { | |
| $this->setData('messageResultTypes', MessageResultType::availableForSearch()); | |
| } | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMessageHistoryDetailController.php
변경 전LayerMessageHistoryDetailResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageHistoryDetailRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageHistoryDetailResponseDTO; | |
| use Origin\Enum\ApiClient\Crm\WorkingType; | |
| use Origin\Enum\ApiClient\Notification\MessageAccessLocationType; | |
| use Origin\Service\Crm\Message\MessageHistoryService; | |
| class LayerMessageHistoryDetailResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $requestDTO = (new MessageHistoryDetailRequestDTO($postData)) | |
| ->setAccessLocation(MessageAccessLocationType::HISTORY) | |
| ->setWorkingType(WorkingType::tryFromName($postData['workingType'] ?? null) ?? WorkingType::VIEW); | |
| try { | |
| /** @var MessageHistoryService $messageHistoryService */ | |
| $messageHistoryService = \App::getInstance(MessageHistoryService::class); | |
| $messageHistoryDetailResponseDTO = $messageHistoryService->getMessageHistoryDetail($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO]); | |
| $messageHistoryDetailResponseDTO = new MessageHistoryDetailResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $messageHistoryDetailResponseDTO->getPage(); | |
| $this->setData('postData', $postData); | |
| $this->setData('messageHistoryDetailList', $messageHistoryDetailResponseDTO->getMessageHistoryDetailList()); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| } | |
변경 후LayerMessageHistoryDetailResultController.php
변경 전LayerMessageHistoryExcelDownloadController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Session; | |
| class LayerMessageHistoryExcelDownloadController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| if (Session::get('manager.functionAuthState') == 'check' && Session::get('manager.functionAuth.messageExcelDown') != 'y') { | |
| $this->json(['success' => false, 'message' => '권한이 없습니다.<br>권한은 대표운영자에게 문의하시기 바랍니다.'], Response::HTTP_FORBIDDEN); | |
| } | |
| // 대응되는 es_code itemCd를 이용해 사유들 가져오기 | |
| $excelDownloadReasonList = ExcelGenerateMenu::fromName($request->post()->get('menu'))->getCode(); | |
| $this->setData('menu', $excelDownloadReasonList); | |
| $this->setData('excelNo', $postData['excelNo']); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMessageHistoryExcelDownloadController.php
변경 전LayerMessageHistoryExcelRequestController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\Enum\ApiClient\Excel\ExcelDownloadRange; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Session; | |
| class LayerMessageHistoryExcelRequestController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post(); | |
| $menu = ExcelGenerateMenu::fromName($postData->get('type')); | |
| $selectedSendKeys = $postData->get('selectedSendKeys', []); | |
| $searchQuery = $postData->get('searchQuery', []); | |
| if (Session::get('manager.functionAuthState') == 'check' && Session::get('manager.functionAuth.messageExcelDown') != 'y') { | |
| $this->json(['success' => false, 'message' => '권한이 없습니다.<br>권한은 대표운영자에게 문의하시기 바랍니다.'], Response::HTTP_FORBIDDEN); | |
| } | |
| $this->setData('menu', $menu); | |
| $this->setData('searchQuery', $searchQuery); | |
| $this->setData('selectedSendKeys', $selectedSendKeys); | |
| $this->setData('excelRequestDownloadRanges', ExcelDownloadRange::cases()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMessageHistoryExcelRequestController.php
변경 전LayerMessageHistoryExcelRequestPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\Enum\ApiClient\Excel\ExcelDownloadRange; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Origin\Service\Admin\Excel\CommerceExcelService; | |
| use Respect\Validation\Exceptions\ValidationException; | |
| use Respect\Validation\Validator; | |
| class LayerMessageHistoryExcelRequestPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'generate': | |
| try { | |
| Validator::arrayVal() | |
| ->key('menu', Validator::stringType()->notEmpty()->in(array_map(fn($case) => $case->name, ExcelGenerateMenu::cases()))) | |
| ->key('downloadRange', Validator::stringType()->notEmpty()->in(array_map(fn($case) => $case->name, ExcelDownloadRange::cases()))) | |
| ->key('excelPassword', Validator::stringType()->notEmpty()) | |
| ->assert($postData); | |
| /** @var CommerceExcelService $commerceExcelService */ | |
| $commerceExcelService = \App::getInstance(CommerceExcelService::class); | |
| $requestDTO = $commerceExcelService->prepareCommerceExcelGenerateRequestDTO($postData); | |
| $commerceExcelService->requestExcelGenerate($requestDTO); | |
| $this->json(['success' => true, 'message' => '다운로드할 엑셀 파일 생성이 완료되었습니다.<br />파일을 선택하여 다운로드하시기 바랍니다.'], Response::HTTP_OK); | |
| } catch (ValidationException $e) { | |
| \Logger::channel('excel')->error("엑셀 생성 요청 파라미터가 잘못 되었습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_BAD_REQUEST); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('excel')->error("엑셀 생성 요청에 실패 하였습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'download': | |
| try { | |
| Validator::arrayVal() | |
| ->key('reason', Validator::stringType()->notEmpty()) | |
| ->key('excelNo', Validator::stringType()->notEmpty()) | |
| ->assert($postData); | |
| $naviMenu = $this->getData('naviMenu'); | |
| /** @var CommerceExcelService $commerceExcelService */ | |
| $commerceExcelService = \App::getInstance(CommerceExcelService::class); | |
| $requestDTO = $commerceExcelService->prepareCommerceExcelDownloadRequestDTO($postData['reason'], $naviMenu->location[0], $naviMenu->location[2]); | |
| $commerceExcelService->downloadExcel($requestDTO, $postData['excelNo']); | |
| } catch (ValidationException $e) { | |
| \Logger::channel('excel')->error("엑셀 다운로드 파라미터가 잘못 되었습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_BAD_REQUEST); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('excel')->error("엑셀 다운로드에 실패 하였습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| default: | |
| throw new \InvalidArgumentException('잘못된 접근입니다.'); | |
| } | |
| } | |
| } | |
변경 후LayerMessageHistoryExcelRequestPsController.php
변경 전LayerMessageHistoryExcelRequestResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\Excel\CommerceExcelGenerateHistoryListResponseDTO; | |
| use Origin\DTO\Excel\CommerceExcelGenerateHistoryRequestDTO; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateStatus; | |
| use Origin\Service\Admin\Excel\CommerceExcelService; | |
| class LayerMessageHistoryExcelRequestResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $requestData = [ | |
| 'excelMenu' => $postData['menu'], | |
| 'page' => $postData['page'] ?? CommerceExcelGenerateHistoryRequestDTO::DEFAULT_PAGE, | |
| 'pageSize' => CommerceExcelGenerateHistoryRequestDTO::DEFAULT_PAGE_SIZE | |
| ]; | |
| $requestDTO = new CommerceExcelGenerateHistoryRequestDTO($requestData); | |
| try { | |
| /** @var CommerceExcelService $commerceExcelService */ | |
| $commerceExcelService = \App::getInstance(CommerceExcelService::class); | |
| $excelGenerateHistoryListResponseDTO = $commerceExcelService->getExcelGenerateHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('excel')->error($e->getMessage(), ['requestDTO' => $requestDTO]); | |
| $excelGenerateHistoryListResponseDTO = new CommerceExcelGenerateHistoryListResponseDTO( | |
| ['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize() | |
| ); | |
| } | |
| $page = $excelGenerateHistoryListResponseDTO->getPage(); | |
| $this->setData('postData', $postData); | |
| $this->setData('excelGenerateHistoryList', $excelGenerateHistoryListResponseDTO->getExcelGenerateHistoryList()); | |
| $this->setData('downloadableStatuses', ExcelGenerateStatus::getDownloadableStatuses()); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| } | |
변경 후LayerMessageHistoryExcelRequestResultController.php
변경 전LayerMyappPushContentsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Origin\Enum\ApiClient\Apps\MyappPushNotificationType; | |
| use Origin\Service\Crm\Message\MyappPushHistoryService; | |
| class LayerMyappPushContentsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $pushNo = $request->post()->get('pushNo'); | |
| try { | |
| /** @var MyappPushHistoryService $myappPushHistoryService */ | |
| $myappPushHistoryService = \App::getInstance(MyappPushHistoryService::class); | |
| $myappPushHistoryContentDTO = $myappPushHistoryService->getMyappPushHistoryContent($pushNo); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['pushNo' => $pushNo]); | |
| exit; | |
| } | |
| $this->setData('title', $myappPushHistoryContentDTO->getTitle()); | |
| $this->setData('content', $myappPushHistoryContentDTO->getContent()); | |
| $this->setData('unsubscribeGuide', $myappPushHistoryContentDTO->getUnsubscribeGuide()); | |
| $this->setData('imageUrl', $myappPushHistoryContentDTO->getImageUrl()); | |
| $this->setData('pushUrl', $myappPushHistoryContentDTO->getPushUrl()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMyappPushContentsController.php
변경 전LayerMyappPushHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Apps\MyappPushHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Apps\MyappPushResultDirection; | |
| use Origin\Enum\ApiClient\Apps\MyappPushResultSortType; | |
| use Origin\Enum\ApiClient\Apps\MyappPushScheduleType; | |
| use Origin\Enum\ApiClient\Apps\MyappPushSendStatus; | |
| use Origin\Service\Crm\Message\MyappPushHistoryService; | |
| use Origin\Service\Crm\Message\MyappPushSettingService; | |
| class LayerMyappPushHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new MyappPushHistorySearchFormDTO($postData); | |
| $searchFormArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFormArray); | |
| $requestDTO = new MyappPushHistoryListRequestDTO($preparedData); | |
| /** @var MyappPushSettingService $myappPushSettingService */ | |
| $myappPushSettingService = \App::getInstance(MyappPushSettingService::class); | |
| $myappPushConfig = $myappPushSettingService->getMyappPushConfig(); | |
| $isEnabled = $myappPushConfig->getUseFlag() === 'y'; | |
| try { | |
| /** @var MyappPushHistoryService $myappPushHistoryService */ | |
| $myappPushHistoryService = \App::getInstance(MyappPushHistoryService::class); | |
| $myappPushHistoryListResponseDTO = $myappPushHistoryService->getMyappPushHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $myappPushHistoryListResponseDTO = new MyappPushHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $myappPushHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormArray); | |
| $this->setData('sort', MyappPushHistoryService::generateSortList()); | |
| $this->setData('myappPushHistoryList', $this->sanitizeHistoryList($myappPushHistoryListResponseDTO->getMyappPushHistoryList())); | |
| $this->setData('isEnabled', $isEnabled); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param MyappPushHistoryDTO[] $historyList | |
| * @return MyappPushHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $title = $dto->getTitle(); | |
| if (!empty($title)) { | |
| $dto->setTitle(htmlspecialchars($title, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $failReason = $dto->getFailReason(); | |
| if (!empty($failReason)) { | |
| $dto->setFailReason(htmlspecialchars($failReason, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| /** | |
| * DTO 생성을 위한 요청 데이터 전처리 | |
| * | |
| * @param array $postData | |
| * @return array | |
| */ | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| $sort = explode(' ', $preparedData['sort'] ?? (MyappPushResultSortType::SEND_DATE_TIME->name . ' ' . MyappPushResultDirection::DESC->name)); | |
| $preparedData['sortType'] = (MyappPushResultSortType::tryFromName($sort[0]) ?? MyappPushResultSortType::SEND_DATE_TIME)->name; | |
| $preparedData['direction'] = (MyappPushResultDirection::tryFromName(strtoupper($sort[1])) ?? MyappPushResultDirection::DESC)->name; | |
| $allScheduleTypes = array_map(fn($e) => $e->name, MyappPushScheduleType::cases()); | |
| if (!empty($preparedData['scheduleTypes']) && !array_diff($preparedData['scheduleTypes'], $allScheduleTypes) && !array_diff($allScheduleTypes, $preparedData['scheduleTypes'])) { | |
| unset($preparedData['scheduleTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, MyappPushSendStatus::availableForSearch()); | |
| if (!empty($preparedData['sendStatuses']) && !array_diff($preparedData['sendStatuses'], $allSendStatuses) && !array_diff($allSendStatuses, $preparedData['sendStatuses'])) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['searchType'])) { | |
| unset($preparedData['searchType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerMyappPushHistoryResultController.php
변경 전LayerMyappPushHistorySearchController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Origin\Enum\ApiClient\Apps\MyappPushScheduleType; | |
| use Origin\Enum\ApiClient\Apps\MyappPushSendStatus; | |
| use Origin\Enum\ApiClient\Notification\MyappPushResultSearchType; | |
| class LayerMyappPushHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $scheduleTypes = MyappPushScheduleType::cases(); | |
| $sendStatuses = MyappPushSendStatus::availableForSearch(); | |
| $searchType = MyappPushResultSearchType::generateSelectBoxData(); | |
| $this->setData('searchType', $searchType); | |
| $this->setData('scheduleTypes', $scheduleTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMyappPushHistorySearchController.php
변경 전LayerSmsContentsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\SmsHistoryContentRequestDTO; | |
| use Origin\Enum\ApiClient\Notification\NotificationSendType; | |
| use Origin\Service\Crm\Message\SmsHistoryService; | |
| class LayerSmsContentsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $sendGroupKey = $request->post()->get('sendGroupKey'); | |
| try { | |
| $requestDTO = (new SmsHistoryContentRequestDTO(['type' => NotificationSendType::SMS->name])); | |
| /** @var SmsHistoryService $smsHistoryService */ | |
| $smsHistoryService = \App::getInstance(SmsHistoryService::class); | |
| $smsHistoryContentDTO = $smsHistoryService->getSmsHistoryContent($sendGroupKey, $requestDTO); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| $this->setData('title', $smsHistoryContentDTO->getTitle()); | |
| $this->setData('content', $smsHistoryContentDTO->getContent()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSmsContentsController.php
변경 전LayerSmsHistoryResultController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\SmsHistoryDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\SmsHistoryListRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\SmsHistoryListResponseDTO; | |
| use Origin\DTO\ApiClient\Commerce\Notification\SmsHistorySearchFormDTO; | |
| use Origin\Enum\ApiClient\Excel\ExcelGenerateMenu; | |
| use Origin\Enum\ApiClient\Notification\SmsSendStatus; | |
| use Origin\Enum\ApiClient\Notification\SmsTriggerType; | |
| use Origin\Service\Crm\Message\SmsHistoryService; | |
| class LayerSmsHistoryResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $searchFormDTO = new SmsHistorySearchFormDTO($postData); | |
| $searchFormArray = $searchFormDTO->toRecursiveArray(); | |
| $preparedData = $this->prepareRequestDataForDto($searchFormArray); | |
| $requestDTO = new SmsHistoryListRequestDTO($preparedData); | |
| try { | |
| /** @var SmsHistoryService $smsHistoryService */ | |
| $smsHistoryService = \App::getInstance(SmsHistoryService::class); | |
| $smsHistoryListResponseDTO = $smsHistoryService->getSmsHistoryList($requestDTO); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO]); | |
| $smsHistoryListResponseDTO = new SmsHistoryListResponseDTO(['totalCount' => 0, 'contents' => []], $requestDTO->getPage(), $requestDTO->getPageSize()); | |
| } | |
| $page = $smsHistoryListResponseDTO->getPage(); | |
| $this->setData('searchFormData', $searchFormArray); | |
| $this->setData('excelSearchQuery', $requestDTO->toRecursiveArray()); | |
| $this->setData('excelMenu', ExcelGenerateMenu::ADMIN_SMS_SEND_RESULT); | |
| $this->setData('sortType', SmsHistoryService::SORT_LIST); | |
| $this->setData('smsHistoryList', $this->sanitizeHistoryList($smsHistoryListResponseDTO->getSmsHistoryList())); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| /** | |
| * DTO 리스트의 문자열 필드에 htmlspecialchars 처리 | |
| * | |
| * @param SmsHistoryDTO[] $historyList | |
| * @return SmsHistoryDTO[] | |
| */ | |
| private function sanitizeHistoryList(array $historyList): array | |
| { | |
| foreach ($historyList as $dto) { | |
| $sendKey = $dto->getSendKey(); | |
| if (!empty($sendKey)) { | |
| $dto->setSendKey(htmlspecialchars($sendKey, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $senderName = $dto->getSenderName(); | |
| if (!empty($senderName)) { | |
| $dto->setSenderName(htmlspecialchars($senderName, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $from = $dto->getFrom(); | |
| if (!empty($from)) { | |
| $dto->setFrom(htmlspecialchars($from, ENT_QUOTES, 'UTF-8')); | |
| } | |
| $content = $dto->getContent(); | |
| if (!empty($content)) { | |
| $dto->setContent(htmlspecialchars($content, ENT_QUOTES, 'UTF-8')); | |
| } | |
| } | |
| return $historyList; | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sortType = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sortType[1] === 'desc') ? 'true' : 'false'; | |
| $allTriggerTypes = array_map(fn($e) => $e->name, SmsTriggerType::cases()); | |
| if (!empty($preparedData['triggerTypes']) && !array_diff($preparedData['triggerTypes'], $allTriggerTypes) && !array_diff($allTriggerTypes, $preparedData['triggerTypes'])) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| $allSendStatuses = array_map(fn($e) => $e->name, SmsSendStatus::availableForSearch()); | |
| if (!empty($preparedData['sendStatuses']) && !array_diff($preparedData['sendStatuses'], $allSendStatuses) && !array_diff($allSendStatuses, $preparedData['sendStatuses'])) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['smsType'])) { | |
| unset($preparedData['smsType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerSmsHistoryResultController.php
변경 전LayerSmsHistoryResultPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Notification\RemoveReservedSmsSendRequestDTO; | |
| use Origin\Service\Crm\Message\SmsHistoryService; | |
| use Respect\Validation\Exceptions\ValidationException; | |
| use Respect\Validation\Validator; | |
| class LayerSmsHistoryResultPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageHistory', 'mobileHistoryList'); | |
| $this->setMenuCode('crm', 'messageHistory', 'mobileHistoryList'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'cancel': | |
| try { | |
| Validator::arrayVal() | |
| ->key('sendGroupKey', Validator::stringType()->notEmpty()) | |
| ->key('type', Validator::stringType()->notEmpty()) | |
| ->key('cancelMode', Validator::stringType()->notEmpty()) | |
| ->when( | |
| Validator::key('cancelMode', Validator::equals('SELECT')), | |
| Validator::key('ids', Validator::stringType()->notEmpty()), | |
| Validator::alwaysValid() | |
| ) | |
| ->assert($postData); | |
| $requestDTO = new RemoveReservedSmsSendRequestDTO($postData); | |
| /** @var SmsHistoryService $smsHistoryService */ | |
| $smsHistoryService = \App::getInstance(SmsHistoryService::class); | |
| $smsHistoryService->removeReservedSmsSend($postData['sendGroupKey'], $requestDTO); | |
| $this->json(['success' => true, 'message' => '예약 발송이 취소되었습니다.'], Response::HTTP_OK); | |
| } catch (ValidationException $e) { | |
| \Logger::channel('mobileMessage')->error("예약 취소 요청 파라미터가 잘못 되었습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage(), 'data' => $postData], Response::HTTP_BAD_REQUEST); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error("예약 취소 요청에 실패 하였습니다.", [$e->getMessage(), __METHOD__]); | |
| $this->json(['success' => false, 'message' => $e->getMessage(), 'data' => $postData], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| default: | |
| $this->json(['success' => false, 'message' => '잘못된 접근입니다.'], Response::HTTP_BAD_REQUEST); | |
| } | |
| } | |
| } | |
변경 후LayerSmsHistoryResultPsController.php
변경 전LayerSmsHistorySearchController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileMessageHistory; | |
| use Origin\Enum\ApiClient\Notification\SmsResultSearchType; | |
| use Origin\Enum\ApiClient\Notification\SmsSendStatus; | |
| use Origin\Enum\ApiClient\Notification\SmsTriggerType; | |
| use Origin\Enum\ApiClient\Notification\SmsType; | |
| class LayerSmsHistorySearchController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $triggerTypes = SmsTriggerType::cases(); | |
| $sendStatuses = SmsSendStatus::availableForSearch(); | |
| $smsTypes = SmsType::cases(); | |
| $searchType = SmsResultSearchType::generateSelectBoxData(); | |
| $this->setData('triggerTypes', $triggerTypes); | |
| $this->setData('smsTypes', $smsTypes); | |
| $this->setData('sendStatuses', $sendStatuses); | |
| $this->setData('searchType', $searchType); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSmsHistorySearchController.php
변경 전MobileSendController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Sms\Sms; | |
| use Origin\Service\Crm\Message\MessageConfigService; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| class MobileSendController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| // 현재 SMS 포인트 싱크 | |
| Sms::saveSmsPoint(); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $hasCallNumber = $smsManageCallNumberService->hasUsableCallNumber(); | |
| /** @var MessageConfigService $messageConfigService */ | |
| $messageConfigService = \App::getInstance(MessageConfigService::class); | |
| $this->setData('messageConfig', $messageConfigService->buildSendableMessageConfig($hasCallNumber)); | |
| $this->setData('smsPointEach', Sms::SMS_POINT); | |
| $this->setData('lmsPointEach', Sms::LMS_POINT); | |
| $this->setData('kakaoAlrimTalkPointEach', Sms::KAKAO_POINT); | |
| $this->setData('kakaoFriendTalkPointEach', Sms::KAKAO_FRIEND_TALK_POINT); | |
| $this->getView()->setPageName('crm/mobile_send.php'); | |
| } | |
| } | |
변경 후MobileSendController.php
변경 전LayerKakaoAlimtalkContentSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\Crm\Message\KakaoAlimTalkTemplateType; | |
| use Origin\Service\Crm\Message\Sms080RejectService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSettingService; | |
| class LayerKakaoAlimtalkContentSettingController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /* @var KakaoAlrimCloudSettingService $kakaoAlimCloudsSettingService*/ | |
| $kakaoAlimCloudsSettingService = \App::getInstance(KakaoAlrimCloudSettingService::class); | |
| $kakaoAlimTalkTemplateTypes = KakaoAlimTalkTemplateType::cases(); | |
| $allowedKeys = array_map(fn($type) => strtolower($type->name), $kakaoAlimTalkTemplateTypes); | |
| $kakaoAlimtalkTemplates = array_filter( | |
| $kakaoAlimCloudsSettingService->getTemplates(), | |
| fn($key) => in_array($key, $allowedKeys), | |
| ARRAY_FILTER_USE_KEY | |
| ); | |
| /** @var Sms080RejectService $sms080RejectService */ | |
| $sms080RejectService = \App::getInstance(Sms080RejectService::class); | |
| $is080RejectAvailable = $sms080RejectService->isAvailable(); | |
| $this->setData('is080RejectAvailable', $is080RejectAvailable); | |
| $this->setData('kakaoAlimTalkTemplateTypes', $kakaoAlimTalkTemplateTypes); | |
| $this->setData('kakaoAlimtalkTemplates', $kakaoAlimtalkTemplates); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerKakaoAlimtalkContentSettingController.php
변경 전LayerKakaoFriendtalkContentSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Bundle\Util\Crm\RecipeContextResolverTrait; | |
| use Origin\Enum\Crm\Message\CampaignMessageType; | |
| use Origin\Service\Crm\Message\KakaoFriendTalkSettingService; | |
| use Origin\Service\Crm\Message\Sms080RejectService; | |
| class LayerKakaoFriendtalkContentSettingController extends \Controller\Admin\Controller | |
| { | |
| use RecipeContextResolverTrait; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var KakaoFriendTalkSettingService $kakaoFriendTalkSettingService */ | |
| $kakaoFriendTalkSettingService = \App::getInstance(KakaoFriendTalkSettingService::class); | |
| $kakaoFriendTalkConfig = $kakaoFriendTalkSettingService->getKakaoFriendTalkConfig(); | |
| /** @var Sms080RejectService $sms080RejectService */ | |
| $sms080RejectService = \App::getInstance(Sms080RejectService::class); | |
| $is080RejectAvailable = $sms080RejectService->isAvailable(); | |
| $campaignMessageTypeUnitPoint = []; | |
| foreach (CampaignMessageType::cases() as $type) { | |
| $campaignMessageTypeUnitPoint[$type->name] = $type->unitPoint(); | |
| } | |
| $recipeContext = $this->resolveRecipeContext(); | |
| $isRecipeContext = $this->hasRecipeContext(); | |
| $defaultCampaignRequest = $recipeContext['defaultCampaignRequest'] ?? []; | |
| $campaignName = $recipeContext['recipeTitle'] ?? ''; | |
| $friendtalkRequest = $defaultCampaignRequest['kakaoFriendtalkRequest'] ?? []; | |
| $initialTextContent = $friendtalkRequest['text']['content'] ?? ''; | |
| $initialAlternativeMessage = $defaultCampaignRequest['smsLmsRequest']['content'] ?? ''; | |
| $initialAlternativeTitle = $defaultCampaignRequest['smsLmsRequest']['title'] ?? ''; | |
| $this->setData('is080RejectAvailable', $is080RejectAvailable); | |
| $this->setData('campaignMessageTypes', CampaignMessageType::cases()); | |
| $this->setData('campaignMessageTypeUnitPoints', $campaignMessageTypeUnitPoint); | |
| $this->setData('useSmsAlternativeSendFlag', $kakaoFriendTalkConfig->getSmsAlternativeSendFlag()); | |
| $this->setData('linkPlatformType', $kakaoFriendTalkConfig->getLinkPlatformType()->name); | |
| $this->setData('isRecipeContext', $isRecipeContext); | |
| $this->setData('campaignName', $campaignName); | |
| $this->setData('friendtalkRequest', $friendtalkRequest); | |
| $this->setData('initialTextContent', $initialTextContent); | |
| $this->setData('initialAlternativeMessage', $initialAlternativeMessage); | |
| $this->setData('initialAlternativeTitle', $initialAlternativeTitle); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerKakaoFriendtalkContentSettingController.php
변경 전LayerMessagePointController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Component\Sms\SmsUtil; | |
| use Component\Sms\Sms; | |
| class LayerMessagePointController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| // 현재 SMS 포인트 싱크 | |
| Sms::saveSmsPoint(); | |
| // 현재 메시지 포인트 | |
| $nowSmsPoint = (float) Sms::getPoint(); | |
| $availableCount = [ | |
| 'sms' => SmsUtil::getAvailableCount('sms', $nowSmsPoint), | |
| 'lms' => SmsUtil::getAvailableCount('lms', $nowSmsPoint), | |
| 'kakaoAlrimTalk' => SmsUtil::getAvailableCount('kakaoAlrimTalk', $nowSmsPoint), | |
| 'kakaoFriendTalk' => SmsUtil::getAvailableCount('kakaoFriendTalk', $nowSmsPoint), | |
| ]; | |
| $this->setData('availableCount', $availableCount); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMessagePointController.php
변경 전LayerMobileSendMyappPushTemplateController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| class LayerMobileSendMyappPushTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $categories = MyappPushTemplateCategory::getFindOptions(); | |
| $this->setData('categories', $categories); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileSendMyappPushTemplateController.php
변경 전LayerMobileSendMyappPushTemplateResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Component\Page\Page; | |
| use Framework\StaticProxy\Proxy\UserFilePath; | |
| use Origin\Enum\Sms\SmsContentsType; | |
| use Origin\Repository\Member\Myapp\MyappPushRepository; | |
| use Origin\Repository\Member\Sms\SmsContentsRepository; | |
| use Request; | |
| class LayerMobileSendMyappPushTemplateResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = \App::getInstance('request'); | |
| $getParams = $request->get()->all(); | |
| $pageNum = 10; | |
| $page = $getParams['page'] ?? 1; | |
| $pushType = $getParams['pushType'] ?? ''; | |
| $keyword = $getParams['keyword'] ?? ''; | |
| $searchParams = ['keyword' => $keyword, 'templateFl'=> 'y', 'pushType' => $pushType]; | |
| $repository = \App::getInstance(MyappPushRepository::class); | |
| $templates = $repository->findAllWithPagination($searchParams, $pageNum, ($page - 1) * $pageNum); | |
| $totalCount = $repository->countAll($searchParams); | |
| /** @var \Bundle\Component\Page\Page $pageComponent */ | |
| $pageComponent = \App::load('\\Component\\Page\\Page', $page); | |
| $pageComponent->page['list'] = $pageNum; | |
| $pageComponent->recode['amount'] = $totalCount; | |
| $pageComponent->recode['total'] = $totalCount; | |
| $pageComponent->setPage(); | |
| $pageComponent->setUrl(\Request::getQueryString()); | |
| $this->setData('templates', $templates); | |
| $this->setData('page', $pageComponent); | |
| $this->setData('requestData', $getParams); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileSendMyappPushTemplateResultController.php
변경 전LayerMobileSendSaveMyappPushTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Request; | |
| class LayerMobileSendSaveMyappPushTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $pushSubject = $request['pushSubject'] ?? ''; | |
| $pushContent = $request['pushContent'] ?? ''; | |
| $pushImage = $request['pushImage'] ?? ''; | |
| $pushUrl = $request['pushUrl'] ?? ''; | |
| $pushWithdraw = $request['pushWithdraw'] ?? ''; | |
| $categories = MyappPushTemplateCategory::getFindOptions(); | |
| $this->setData('categories', $categories); | |
| $this->setData('pushSubject', $pushSubject); | |
| $this->setData('pushContent', $pushContent); | |
| $this->setData('pushImage', $pushImage); | |
| $this->setData('pushUrl', $pushUrl); | |
| $this->setData('pushWithdraw', $pushWithdraw); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileSendSaveMyappPushTemplateController.php
변경 전LayerMobileSendSaveSmsTemplateController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| use Request; | |
| class LayerMobileSendSaveSmsTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $contents = $request['contents'] ?? ''; | |
| $messageTitle = $request['messageTitle'] ?? ''; | |
| $categories = SmsTemplateCategory::getFindOptions(); | |
| $this->setData('categories', $categories); | |
| $this->setData('contents', $contents); | |
| $this->setData('messageTitle', $messageTitle); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileSendSaveSmsTemplateController.php
변경 전LayerMobileSendSmsTemplateController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| class LayerMobileSendSmsTemplateController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $categories = SmsTemplateCategory::getFindOptions(); | |
| $this->setData('categories', $categories); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMobileSendSmsTemplateController.php
변경 전LayerMobileSendSmsTemplateResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Component\Page\Page; | |
| use Framework\Http\Response; | |
| use Origin\DTO\Crm\Message\SmsTemplateSearchDTO; | |
| use Origin\Service\Crm\Message\SmsTemplateService; | |
| class LayerMobileSendSmsTemplateResultController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| try { | |
| $request = \App::getInstance('request'); | |
| $getParams = $request->get()->all(); | |
| /** @var SmsTemplateService $smsTemplateService */ | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $dto = new SmsTemplateSearchDTO($getParams); | |
| $result = $smsTemplateService->getSmsTemplateList($dto); | |
| $templates = $result['templates']; | |
| $totalCount = $result['totalCount']; | |
| $pageNum = $result['pageNum']; | |
| $page = $result['page']; | |
| /** @var \Bundle\Component\Page\Page $pageComponent */ | |
| $pageComponent = \App::load('\\Component\\Page\\Page', $page); | |
| $pageComponent->page['list'] = $pageNum; | |
| $pageComponent->recode['amount'] = $totalCount; | |
| $pageComponent->recode['total'] = $totalCount; | |
| $pageComponent->setPage(); | |
| $pageComponent->setUrl(\Request::getQueryString()); | |
| $this->setData('templates', $templates); | |
| $this->setData('page', $pageComponent); | |
| $this->setData('requestData', $getParams); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } | |
| } | |
변경 후LayerMobileSendSmsTemplateResultController.php
변경 전LayerMyappPushContentSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Bundle\Util\Crm\RecipeContextResolverTrait; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\Crm\Message\MyappPushNotificationType; | |
| use Origin\Enum\Crm\Message\MyappPushSendCondition; | |
| use Origin\Enum\Crm\Message\MyappPushSendPlatform; | |
| use Origin\Service\Crm\Message\MyappPushTemplateService; | |
| use Origin\Service\Crm\Message\Sms080RejectService; | |
| class LayerMyappPushContentSettingController extends \Controller\Admin\Controller | |
| { | |
| use RecipeContextResolverTrait; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var Sms080RejectService $sms080RejectService */ | |
| $sms080RejectService = \App::getInstance(Sms080RejectService::class); | |
| $is080RejectAvailable = $sms080RejectService->isAvailable(); | |
| /** @var MyappPushTemplateService $myappPushTemplateService */ | |
| $myappPushTemplateService = \App::getInstance(MyappPushTemplateService::class); | |
| $myappPushUrl = $myappPushTemplateService->getMyappDomainUrl(); | |
| $recipeContext = $this->resolveRecipeContext(); | |
| $isRecipeContext = $this->hasRecipeContext(); | |
| $myappRequest = $recipeContext['defaultCampaignRequest']['myappRequest'] ?? []; | |
| $initialTitle = $myappRequest['title'] ?? ''; | |
| $initialMessage = $myappRequest['content'] ?? ''; | |
| $initialAlternativeMessage = $recipeContext['defaultCampaignRequest']['smsLmsRequest']['content'] ?? ''; | |
| $initialAlternativeTitle = $recipeContext['defaultCampaignRequest']['smsLmsRequest']['title'] ?? ''; | |
| $this->setData('is080RejectAvailable', $is080RejectAvailable); | |
| $this->setData('myappPushSendConditions', MyappPushSendCondition::cases()); | |
| $this->setData('myappPushNotificationTypes', MyappPushNotificationType::cases()); | |
| $this->setData('myappPushSendPlatforms', MyappPushSendPlatform::cases()); | |
| $this->setData('myappPushUrl', $myappPushUrl); | |
| $this->setData('isRecipeContext', $isRecipeContext); | |
| $this->setData('initialTitle', $initialTitle); | |
| $this->setData('initialMessage', $initialMessage); | |
| $this->setData('initialAlternativeMessage', $initialAlternativeMessage); | |
| $this->setData('initialAlternativeTitle', $initialAlternativeTitle); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerMyappPushContentSettingController.php
변경 전LayerPreviewMessageController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| class LayerPreviewMessageController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerPreviewMessageController.php
변경 전LayerRecipeSendTimeSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Bundle\Util\Crm\RecipeContextResolverTrait; | |
| use Origin\Enum\Common\WeekDay; | |
| use Origin\Enum\Crm\Message\MessageRepeatCycle; | |
| class LayerRecipeSendTimeSettingController extends \Controller\Admin\Controller | |
| { | |
| use RecipeContextResolverTrait; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| $sendSchedule = $this->resolveRecipeContext()['sendSchedule'] ?? []; | |
| $sendTimeView = $this->buildSendTimeView($sendSchedule); | |
| $this->setData('messageRepeatCycles', MessageRepeatCycle::cases()); | |
| $this->setData('sendTimeView', $sendTimeView); | |
| $this->setData('weekDays', WeekDay::cases()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| protected function buildSendTimeView(array $sendSchedule): array | |
| { | |
| $selectedRepeatType = $sendSchedule['repeatType'] ?? 'WEEKLY'; | |
| $repeatConfig = $sendSchedule['repeatConfig'] ?? []; | |
| $sendTime = $repeatConfig['sendTime'] ?? ''; | |
| return [ | |
| 'selectedRepeatType' => $selectedRepeatType, | |
| 'selectedRepeatHour' => $sendTime !== '' ? substr($sendTime, 0, 2) : '', | |
| 'selectedRepeatMinutes' => $sendTime !== '' ? substr($sendTime, 3, 2) : '', | |
| 'selectedRepeatDays' => $repeatConfig['repeatDays'] ?? [], | |
| ]; | |
| } | |
| } | |
변경 후LayerRecipeSendTimeSettingController.php
변경 전LayerRecipientSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Component\Sms\SmsUtil; | |
| use Component\Sms\Sms; | |
| use Framework\Http\Request; | |
| use Origin\Enum\Crm\Message\RecipientType; | |
| class LayerRecipientSettingController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $selectedCrmGroup = $request->post()->get('selectedCrmGroup'); | |
| // PG 설정 불러오기 | |
| $pgConfig = gd_pgs(); | |
| $isConnectedPg = ($pgConfig['pgAutoSetting'] === 'y' || $pgConfig['pgApprovalSetting'] === 'y') ? 'y' : 'n'; | |
| $this->setData('recipientTypes', RecipientType::cases()); | |
| $this->setData('isConnectedPg', $isConnectedPg); | |
| $this->setData('selectedCrmGroup', $selectedCrmGroup); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerRecipientSettingController.php
변경 전LayerRecipientSettingPsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Component\Crm\MobileMessage; | |
| use Bundle\Component\Excel\Enum\ExcelSampleType; | |
| use Bundle\Component\Excel\ExcelSample; | |
| use Bundle\Component\Excel\ExcelSmsConvert; | |
| use Bundle\Component\Sms\SmsExcelLog; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use Framework\Http\Response; | |
| use Origin\DTO\ApiClient\Commerce\Crm\TargetsRepeatRulesRequestDTO; | |
| use Origin\DTO\ApiClient\Commerce\Crm\TargetsRepeatRuleStatusRequestDTO; | |
| use Origin\Service\ApiClient\Commerce\Crm\CrmAdminApiClientService; | |
| class LayerRecipientSettingPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $mode = $request->request()->get('mode'); | |
| switch ($mode) { | |
| case 'getCountAll': | |
| case 'getCountMembers': | |
| try { | |
| $getData = $request->get()->toArray(); | |
| $memberNos = json_decode($getData['selectedMemberNos']); | |
| /* @var MobileMessage $mobileMessage*/ | |
| $mobileMessage = \App::getInstance(MobileMessage::class); | |
| $counts = $mobileMessage->getReceiverCounts($memberNos, null); | |
| $this->json(['success' => true, 'data' => $counts], Response::HTTP_OK); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'getCountGroupMembers': | |
| try { | |
| $getData = $request->get()->toArray(); | |
| $groupSnos = json_decode($getData['selectedMemberGroups']); | |
| /* @var MobileMessage $mobileMessage*/ | |
| $mobileMessage = \App::getInstance(MobileMessage::class); | |
| $counts = $mobileMessage->getReceiverCounts(null, $groupSnos); | |
| $this->json(['success' => true, 'data' => $counts], Response::HTTP_OK); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage()); | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'getCrmGroupStatus': | |
| try { | |
| $getData = $request->get()->toArray(); | |
| $requestDTO = new TargetsRepeatRuleStatusRequestDTO($getData); | |
| /** @var $crmAdminApiClient CrmAdminApiClientService */ | |
| $crmAdminApiClient = \App::getInstance(CrmAdminApiClientService::class); | |
| $targetsRepeatRuleStatusResponseDTO = $crmAdminApiClient->getNotificationTargetsRepeatRuleStatus($requestDTO, $getData['crmGroupNo']); | |
| $this->json([ | |
| 'success' => true, | |
| 'message' => 'CRN 그룹 개수 조회 성공', | |
| 'data' => $targetsRepeatRuleStatusResponseDTO->toRecursiveArray() | |
| ]); | |
| } catch (\Throwable $e) { | |
| \Logger::channel('mobileMessage')->error($e->getMessage(), ['requestDTO' => $requestDTO->toArray()]); | |
| $this->json(['success' => false], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'downloadExcelSample': | |
| (new ExcelSample(ExcelSampleType::SMS))->outputSample(); | |
| break; | |
| case 'uploadExcelTarget': | |
| $requestFiles = $request->files()->toArray(); | |
| $excelFile = $requestFiles['excel'] ?? null; | |
| if (!$excelFile) { | |
| $this->json(['success' => false, 'message' => '엑셀 파일이 없습니다.'], Response::HTTP_BAD_REQUEST); | |
| break; | |
| } | |
| set_time_limit(RUN_TIME_LIMIT); | |
| /* @var ExcelSmsConvert $excelSmsConvert */ | |
| $excelSmsConvert = \App::load('Component\\Excel\\ExcelSmsConvert'); | |
| /* @var SmsExcelLog $smsExcelLog */ | |
| $smsExcelLog = \App::load('Component\\Sms\\SmsExcelLog'); | |
| try { | |
| $excelSmsConvert->upload(); | |
| $uploadKey = $excelSmsConvert->getUploadKey(); | |
| $counts = $smsExcelLog->countValidationLogByUploadKey($uploadKey); | |
| $this->json(['success' => true, 'data' => ['uploadedExcelKey' => $uploadKey, 'successTargetCount' => $counts]], Response::HTTP_OK); | |
| } catch (\Throwable $e) { | |
| Logger::channel('mobileMessage')->error(__CLASS__ . ' 엑셀 업로드 처리 중 에러 발생 : ', ['error' => $e->getMessage()]); | |
| $this->json(['success' => false, 'message' => '엑셀 파일 처리에 실패했습니다.'], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후LayerRecipientSettingPsController.php
변경 전LayerSendMethodSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Bundle\Util\Crm\RecipeContextResolverTrait; | |
| use Origin\Enum\Crm\Message\SendMethod; | |
| use Origin\Service\Crm\Message\Sms080RejectService; | |
| class LayerSendMethodSettingController extends \Controller\Admin\Controller | |
| { | |
| use RecipeContextResolverTrait; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var Sms080RejectService $sms080RejectService */ | |
| $sms080RejectService = \App::getInstance(Sms080RejectService::class); | |
| $smsRejectPolicy = $sms080RejectService->getConfig(); | |
| $smsRejectText = ($smsRejectPolicy['rejectNumber'] != '' && $smsRejectPolicy['status'] == 'O' && $smsRejectPolicy['use'] == 'y') ? "수신거부 {$smsRejectPolicy['rejectNumber']}" : ''; | |
| $recipeContext = $this->resolveRecipeContext(); | |
| $isRecipeContext = $this->hasRecipeContext(); | |
| $messageSendGuide = $recipeContext['guideMessages']['messageSendGuide'] ?? ''; | |
| $this->setData('sendMethods', SendMethod::cases()); | |
| $this->setData('smsRejectText', $smsRejectText); | |
| $this->setData('isRecipeContext', $isRecipeContext); | |
| $this->setData('messageSendGuide', $messageSendGuide); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSendMethodSettingController.php
변경 전LayerSendTimeSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Origin\Enum\Common\WeekDay; | |
| use Origin\Enum\Crm\Message\MessageRepeatCycle; | |
| use Origin\Enum\Crm\Message\MessageSendType; | |
| class LayerSendTimeSettingController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| $this->setData('messageSendTypes', MessageSendType::cases()); | |
| $this->setData('messageRepeatCycles', MessageRepeatCycle::cases()); | |
| $this->setData('weekDays', WeekDay::cases()); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSendTimeSettingController.php
변경 전LayerSmsContentSettingController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MobileSend; | |
| use Bundle\Util\Crm\RecipeContextResolverTrait; | |
| use Origin\Service\Crm\Message\Sms080RejectService; | |
| class LayerSmsContentSettingController extends \Controller\Admin\Controller | |
| { | |
| use RecipeContextResolverTrait; | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var Sms080RejectService $sms080RejectService */ | |
| $sms080RejectService = \App::getInstance(Sms080RejectService::class); | |
| $is080RejectAvailable = $sms080RejectService->isAvailable(); | |
| $recipeContext = $this->resolveRecipeContext(); | |
| $isRecipeContext = $this->hasRecipeContext(); | |
| $initialMessage = $recipeContext['defaultCampaignRequest']['smsLmsRequest']['content'] ?? ''; | |
| $initialTitle = $recipeContext['defaultCampaignRequest']['smsLmsRequest']['title'] ?? ''; | |
| $this->setData('is080RejectAvailable', $is080RejectAvailable); | |
| $this->setData('isRecipeContext', $isRecipeContext); | |
| $this->setData('initialMessage', $initialMessage); | |
| $this->setData('initialTitle', $initialTitle); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSmsContentSettingController.php
변경 전MobileSendPsController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Framework\Debug\Exception\AlertCloseException; | |
| use Framework\Http\Request; | |
| use GuzzleHttp\Exception\RequestException; | |
| use Origin\Enum\ApiClient\Crm\RecipeType; | |
| use Origin\Service\Crm\Message\MessageSendService; | |
| use Origin\Service\Crm\Message\SmsPasswordService; | |
| use Component\Crm\MobileMessage; | |
| use Framework\Http\Response; | |
| class MobileSendPsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| $this->setMenuCode('crm', 'messageSend', 'mobileSend'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $postData['mode']; | |
| switch ($mode) { | |
| case 'send': | |
| try { | |
| $requestData = json_decode($postData['payload'], true); | |
| // CRM 레시피 발송은 레시피 메뉴 권한으로 인가 | |
| if (!empty($requestData['recipeType'])) { | |
| if (RecipeType::tryFromSlug($requestData['recipeType']) === null) { | |
| throw new AlertCloseException(__('잘못된 접근입니다.')); | |
| } | |
| $this->setMenuCode('crm', 'crm', 'crmRecipe'); | |
| } | |
| // 수신대상 - 엑셀업로드, 업로드된 정보로 수신자 설정 | |
| if (($requestData['targetInfo']['targetType'] ?? null) === 'EXCEL') { | |
| /* @var \Bundle\Component\Crm\MobileMessage $mobileMessage*/ | |
| $mobileMessage = \App::getInstance(MobileMessage::class); | |
| $requestData = $mobileMessage->setExcelTargets($requestData); | |
| } | |
| /* @var MessageSendService $messageSendService*/ | |
| $messageSendService = \App::getInstance(MessageSendService::class); | |
| $messageSendService->send($requestData); | |
| $this->json(['success' => true]); | |
| } catch (RequestException $e) { | |
| if ($e->hasResponse()) { | |
| $response = $e->getResponse(); | |
| if ($response->getStatusCode() === Response::HTTP_BAD_REQUEST) { | |
| $contents = json_decode($response->getBody()->getContents(), true); | |
| $this->json(['success' => false, 'message' => $contents['message']], Response::HTTP_BAD_REQUEST); | |
| } | |
| } | |
| throw $e; | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| case 'validateSendPassword': | |
| try { | |
| /** @var SmsPasswordService $smsPasswordService */ | |
| $smsPasswordService = \App::getInstance(SmsPasswordService::class); | |
| $smsPassword = $smsPasswordService->getSmsPassword(true); | |
| if ($postData['sendPassword'] === $smsPassword) { | |
| $this->json(['success' => true]); | |
| } else { | |
| $this->json(['success' => false, 'message' => '설정 메뉴에 저장된 메시지 인증번호와 일치하지 않습니다.', 'data' => ['subMessage' => '설정 페이지에서 메시지 인증번호를 먼저 확인해주세요.']]); | |
| } | |
| } catch (\Throwable $e) { | |
| $this->json(['success' => false, 'message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| break; | |
| default: | |
| throw new AlertCloseException(__("잘못된 접근입니다.")); | |
| } | |
| } | |
| } | |
변경 후MobileSendPsController.php
변경 전MyappPushTemplatePsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\Myapp; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateMyappDTO; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Origin\Repository\Member\Myapp\MyappPushRepository; | |
| use Origin\Service\Crm\Message\MyappPushTemplateService; | |
| use Request; | |
| class MyappPushTemplatePsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $mode = $request['mode'] ?? ''; | |
| if ($mode === 'save') { | |
| $this->save($request); | |
| } else { | |
| $this->json(['result' => true, 'message' => '잘못된 요청입니다.']); | |
| } | |
| } | |
| private function save(array $request): void | |
| { | |
| $categoryValue = $request['category'] ?? ''; | |
| $subject = $request['subject'] ?? ''; | |
| $contents = $request['contents'] ?? ''; | |
| $title = $request['title'] ?? ''; | |
| $url = $request['url'] ?? ''; | |
| $image = $request['image'] ?? ''; | |
| $category = MyappPushTemplateCategory::tryFrom($categoryValue); | |
| if ($category === null) { | |
| $this->json(['success' => false, 'message' => '카테고리를 선택해주세요.']); | |
| } | |
| if (empty($subject)) { | |
| $this->json(['success' => false, 'message' => '제목을 입력해주세요.']); | |
| } | |
| if (empty($contents)) { | |
| $this->json(['success' => false, 'message' => '내용을 입력해주세요.']); | |
| } | |
| try { | |
| $repository = \App::getInstance(MyappPushRepository::class); | |
| $sno = $repository->saveByGodomallTemplate($category, $subject, $contents, $title, $url, $image); | |
| // 파일 업로드된 이미지가 있으면 서버에 저장 | |
| $files = Request::files()->toArray(); | |
| if (!empty($files['imageFile']['tmp_name']) && (int)$files['imageFile']['error'] === UPLOAD_ERR_OK) { | |
| /** @var MyappPushTemplateService $templateService */ | |
| $templateService = \App::getInstance(MyappPushTemplateService::class); | |
| $files['imageFile']['pushImage'] = $files['imageFile']['tmp_name']; | |
| $templateService->processAndSaveImage( | |
| $sno, | |
| new MessageTemplateMyappDTO(['pushSno' => $sno]), | |
| $files['imageFile'] | |
| ); | |
| } | |
| $this->json(['success' => true, 'message' => '템플릿이 저장되었습니다.']); | |
| } catch (\Exception $e) { | |
| $this->json(['success' => false, 'message' => '저장에 실패했습니다.']); | |
| } | |
| } | |
| } | |
변경 후MyappPushTemplatePsController.php
변경 전MyappPushTemplatePsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\MyappPush; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateMyappDTO; | |
| use Origin\Enum\Crm\Message\MyappPushTemplateCategory; | |
| use Origin\Service\Crm\Message\MyappPushTemplateService; | |
| use Request; | |
| class MyappPushTemplatePsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $mode = $request['mode'] ?? ''; | |
| $service = \App::getInstance(MyappPushTemplateService::class); | |
| switch ($mode) { | |
| case 'save': | |
| $categoryValue = $request['category'] ?? ''; | |
| $category = MyappPushTemplateCategory::tryFrom($categoryValue); | |
| if ($category === null) { | |
| $this->json(['success' => false, 'message' => '카테고리를 선택해주세요.']); | |
| return; | |
| } | |
| $templateName = trim($request['templateName'] ?? ''); | |
| if ($templateName === '') { | |
| $this->json(['success' => false, 'message' => '템플릿명을 입력해주세요.']); | |
| return; | |
| } | |
| $pushContent = trim($request['pushContent'] ?? ''); | |
| if ($pushContent === '') { | |
| $this->json(['success' => false, 'message' => '푸시 내용을 입력해주세요.']); | |
| return; | |
| } | |
| $pushSubject = trim($request['pushSubject'] ?? ''); | |
| $dto = new MessageTemplateMyappDTO([ | |
| 'pushType' => $category->value, | |
| 'templateName' => $templateName, | |
| 'pushSubject' => $pushSubject, | |
| 'pushContent' => $pushContent, | |
| 'pushImage' => $request['pushImage'], | |
| 'pushUrl' => $request['pushUrl'], | |
| 'pushWithdraw' => $request['pushWithdraw'], | |
| ]); | |
| $pushSno = $service->saveMyappTemplate($dto); | |
| if ($pushSno == 0) { | |
| $this->json(['success' => false, 'message' => '템플릿 저장에 실패하였습니다.']); | |
| } else { | |
| $this->json(['success' => true, 'data' => ['sno' => $pushSno], 'message' => '템플릿이 저장되었습니다.']); | |
| } | |
| return; | |
| default: | |
| $this->json(['error' => 'Invalid mode', 'data' => []]); | |
| return; | |
| } | |
| } | |
| } | |
변경 후MyappPushTemplatePsController.php
변경 전PopupChargeMessagePointsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Bundle\Component\Member\Manager; | |
| use Bundle\Component\Sms\SmsUtil; | |
| use Bundle\Component\Sms\SmsAdmin; | |
| use Bundle\Component\Sms\Sms; | |
| use Core\Base\Interceptor\AdminCertification; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| class PopupChargeMessagePointsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $globals = \App::getInstance('globals'); | |
| $this->callMenu('crm', 'messageConfig', 'chargeMessagePoints'); | |
| if ($request->get()->get('popupMode', '') === 'yes') { | |
| $this->getView()->setDefine('layout', 'layout_blank.php'); | |
| } elseif ($session->has(Manager::SESSION_TEMP_MANAGER) | |
| && $session->has(AdminCertification::SESSION_TEMP_CERTIFICATION)) { | |
| if (strpos($request->getReferer(), '/base/login') === false) { | |
| $session->del(Manager::SESSION_TEMP_MANAGER); | |
| $session->del(AdminCertification::SESSION_TEMP_CERTIFICATION); | |
| throw new AlertRedirectException('잘못된 경로로 접근되었습니다.', 403, null, '/base/login.php', 'top'); | |
| } | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->setData('guestCharge', true); | |
| } | |
| $shopNo = $globals->get('gLicense.godosno'); | |
| // SMS 포인트 Sync | |
| Sms::saveSmsPoint(); | |
| // 현재 메시지 포인트 | |
| $nowSmsPoint = (float) Sms::getPoint(); | |
| $availableCount = [ | |
| 'sms' => SmsUtil::getAvailableCount('sms', $nowSmsPoint), | |
| 'lms' => SmsUtil::getAvailableCount('lms', $nowSmsPoint), | |
| 'kakaoAlrimTalk' => SmsUtil::getAvailableCount('kakaoAlrimTalk', $nowSmsPoint), | |
| 'kakaoFriendTalk' => SmsUtil::getAvailableCount('kakaoFriendTalk', $nowSmsPoint), | |
| ]; | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $webConfig = \App::getSystemConfig('internal')['commerce-web']; | |
| $messagePointPayUrl = $webConfig['url'] . '/' . $webConfig['path']['message-point-pay-popup']; | |
| $this->setData('lmsPoint', Sms::LMS_POINT); | |
| $this->setData('kakaoPoint', Sms::KAKAO_POINT); | |
| $this->setData('kakaoFriendTalkPoint', Sms::KAKAO_FRIEND_TALK_POINT); | |
| $this->setData('kakaoFriendTalkImagePoint', Sms::KAKAO_FRIEND_TALK_IMAGE_POINT ?? 2.2); | |
| $this->setData('kakaoFriendTalkWideItemPoint', Sms::KAKAO_FRIEND_TALK_WIDE_ITEM_POINT ?? 2.7); | |
| $this->setData('availableCount', $availableCount); | |
| $this->setData('smsPriceList', $smsAdmin->getSmsPriceList()); | |
| $this->setData('shopSno', $shopNo); | |
| $this->setData('messagePointPayUrl', $messagePointPayUrl); | |
| $this->getView()->setDefine('layout', 'layout_blank.php'); | |
| } | |
| } | |
변경 후PopupChargeMessagePointsController.php
변경 전PopupSelectMembersController.php
| <?php | |
| /* | |
| * Copyright (C) 2025 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm; | |
| use Component\Member\Group\Util; | |
| use Component\Member\Member; | |
| use Component\Member\Util\MemberUtil; | |
| use Component\Page\Page; | |
| use Framework\Http\Request; | |
| class PopupSelectMembersController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->setNcdsSubBodyClass(); | |
| $this->callMenu('crm', 'messageSend', 'mobileSend'); | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $this->setDefaultRequestParams($request); | |
| $postData = $request->post()->toArray(); | |
| /* @var \Bundle\Component\Member\Member $memberComponent */ | |
| $memberComponent = \App::load(Member::class); | |
| $groups = Util::getGroupName(); | |
| $checked = MemberUtil::checkedByMemberListSearch($postData); | |
| $selected = MemberUtil::selectedByMemberListSearch($postData); | |
| $this->setData('combineSearch', $memberComponent->getCombineSearchSelectBox()); | |
| $this->setData('searchKind', $memberComponent->getSearchKindASelectBox()); | |
| $this->setData('groups', $groups); | |
| $this->setData('search', $postData); | |
| $this->setData('checked', $checked); | |
| $this->setData('selected', $selected); | |
| // 필요 | |
| $this->setData('sendMode', $getParams['sendMode']); | |
| $this->getView()->setDefine('layout', 'layout_blank.php'); | |
| $this->getView()->setPageName('crm/popup_select_members.php'); | |
| } | |
| private function setDefaultRequestParams($request): void | |
| { | |
| $post = $request->post(); | |
| if (!$post->has('mallSno')) { | |
| $post->set('mallSno', ''); | |
| } | |
| if (!$post->get('sendMode', '') != 'mail') { | |
| $post->set('mallSno', DEFAULT_MALL_NUMBER); | |
| } | |
| if (!$post->has('maillingFl')) { | |
| $post->set('maillingFl', 'y'); | |
| } | |
| if (!$post->has('smsFl')) { | |
| $post->set('smsFl', 'y'); | |
| } | |
| $post->set('sendMode', $request->get()->get('sendMode')); | |
| } | |
| } | |
변경 후PopupSelectMembersController.php
변경 전LayerSelectedMembersResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\PopupSelectMembers; | |
| use Bundle\Component\Page\Page; | |
| use Component\Member\Group\Util; | |
| use Component\Member\Member; | |
| use Framework\Http\Request; | |
| use Origin\Enum\ApiClient\Notification\SmsSendStatus; | |
| use Origin\Enum\ApiClient\Notification\SmsTriggerType; | |
| class LayerSelectedMembersResultController extends \Controller\Admin\Controller | |
| { | |
| const SORT_LIST = [ | |
| 'entryDt desc' => '회원가입일 내림차순', | |
| 'entryDt asc' => '회원가입일 오름차순', | |
| 'lastLoginDt desc' => '최종로그인 내림차순', | |
| 'lastLoginDt asc' => '최종로그인 오름차순', | |
| 'loginCnt desc' => '방문횟수 내림차순', | |
| 'loginCnt asc' => '방문횟수 오름차순', | |
| 'memNm desc' => '이름 내림차순', | |
| 'memNm asc' => '이름 오름차순', | |
| 'memId desc' => '아이디 내림차순', | |
| 'memId asc' => '아이디 오름차순', | |
| 'mileage desc' => '마일리지 내림차순', | |
| 'mileage asc' => '마일리지 오름차순', | |
| 'saleAmt desc' => '주문금액 내림차순', | |
| 'saleAmt asc' => '주문금액 오름차순', | |
| ]; | |
| const DEFAULT_PAGE = 1; | |
| const DEFAULT_PER_PAGE = 10; | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $postData['sendMode'] = $request->request()->get('sendMode'); | |
| /* @var \Bundle\Component\Member\Member $memberComponent */ | |
| $memberComponent = \App::load(Member::class); | |
| $members = []; | |
| $searchCount = 0; | |
| if (!empty($postData['selectedMemberNos'])) { | |
| $getParams = [ | |
| 'sendMode' => $postData['sendMode'], | |
| 'chk' => $postData['selectedMemberNos'], | |
| 'sort' => $postData['sort'], | |
| 'mallSno' => $postData['mallSno'] ?? '', | |
| ]; | |
| $page = $postData['page'] ?? 1; | |
| $pageSize = $postData['pageSize'] ?? self::DEFAULT_PER_PAGE; | |
| $members = $memberComponent->listsWithCoupon($getParams, $page, $pageSize); | |
| $searchCount = $memberComponent->foundRowsByListsWithCoupon($getParams); | |
| } | |
| $page = new Page($postData['page'] ?? self::DEFAULT_PAGE, $searchCount, $searchCount, $postData['pageSize'] ?? self::DEFAULT_PER_PAGE); | |
| $sysIcons = [ | |
| 'payco' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/payco_logo.png', 'name' => '페이코'], | |
| 'facebook' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/facebook_logo.png', 'name' => '페이스북'], | |
| 'naver' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/naver_logo.png', 'name' => '네이버'], | |
| 'kakao' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/kakao_logo.png', 'name' => '카카오'], | |
| 'wonder' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/wemakeprice_logo.png', 'name' => '위메프'], | |
| 'apple' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/apple_logo.png', 'name' => '애플'], | |
| 'google' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/google_logo.png', 'name' => '구글'], | |
| ]; | |
| $this->setData('selectedFormData', $postData); | |
| $this->setData('snsIcons', $sysIcons); | |
| $this->setData('sortType', self::SORT_LIST); | |
| $this->setData('members', $members); | |
| $this->setData('groups', Util::getGroupName()); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| private function prepareRequestDataForDto(array $postData): array | |
| { | |
| $preparedData = $postData; | |
| // TODO 정렬 기준 추가를 대비하여 분리 | |
| $sortType = explode(' ', $preparedData['sort'] ?? 'reservedAt desc'); | |
| $preparedData['sortDescending'] = ($sortType[1] === 'desc') ? 'true' : 'false'; | |
| if (!empty($preparedData['triggerTypes']) && empty(array_diff(array_map(fn($e) => $e->name, SmsTriggerType::cases()), $preparedData['triggerTypes']))) { | |
| unset($preparedData['triggerTypes']); | |
| } | |
| if (!empty($preparedData['sendStatuses']) && empty(array_diff(array_map(fn($e) => $e->name, SmsSendStatus::availableForSearch()), $preparedData['sendStatuses']))) { | |
| unset($preparedData['sendStatuses']); | |
| } | |
| if (empty($preparedData['keyword'])) { | |
| unset($preparedData['keyword']); | |
| } | |
| if (empty($preparedData['smsType'])) { | |
| unset($preparedData['smsType']); | |
| } | |
| return $preparedData; | |
| } | |
| } | |
변경 후LayerSelectedMembersResultController.php
변경 전LayerSelectMembersResultController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\PopupSelectMembers; | |
| use Bundle\Component\Page\Page; | |
| use Component\Member\Group\Util; | |
| use Component\Member\Member; | |
| use Framework\Http\Request; | |
| class LayerSelectMembersResultController extends \Controller\Admin\Controller | |
| { | |
| const SORT_LIST = [ | |
| 'entryDt desc' => '회원가입일 내림차순', | |
| 'entryDt asc' => '회원가입일 오름차순', | |
| 'lastLoginDt desc' => '최종로그인 내림차순', | |
| 'lastLoginDt asc' => '최종로그인 오름차순', | |
| 'loginCnt desc' => '방문횟수 내림차순', | |
| 'loginCnt asc' => '방문횟수 오름차순', | |
| 'memNm desc' => '이름 내림차순', | |
| 'memNm asc' => '이름 오름차순', | |
| 'memId desc' => '아이디 내림차순', | |
| 'memId asc' => '아이디 오름차순', | |
| 'mileage desc' => '마일리지 내림차순', | |
| 'mileage asc' => '마일리지 오름차순', | |
| 'saleAmt desc' => '주문금액 내림차순', | |
| 'saleAmt asc' => '주문금액 오름차순', | |
| ]; | |
| const DEFAULT_PAGE = 1; | |
| const DEFAULT_PER_PAGE = 10; | |
| public function index() | |
| { | |
| /** @var Request $request */ | |
| $request = \App::getInstance('request'); | |
| $postData = $request->post()->toArray(); | |
| $postData['sendMode'] = $request->request()->get('sendMode'); | |
| if (in_array($postData['key'], ['company', 'busiNo'])) $postData['searchKind'] = 'fullLikeSearch'; | |
| /* @var \Bundle\Component\Member\Member $memberComponent */ | |
| $memberComponent = \App::load(Member::class); | |
| $members = $memberComponent->listsWithCoupon($postData, $postData['page'] ?? self::DEFAULT_PAGE, $postData['pageSize'] ?? self::DEFAULT_PER_PAGE); | |
| $searchCount = $memberComponent->foundRowsByListsWithCoupon($postData); | |
| $totalCount = $memberComponent->foundRowsByListsWithCoupon(['sendMode' => $postData['sendMode'], 'mallSno' => $postData['mallSno']]); // sendMode만 포함하여 전체 회원수 조회 | |
| $page = new Page($postData['page'] ?? self::DEFAULT_PAGE, $searchCount, $totalCount, $postData['pageSize'] ?? self::DEFAULT_PER_PAGE); | |
| $sysIcons = [ | |
| 'payco' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/payco_logo.png', 'name' => '페이코'], | |
| 'facebook' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/facebook_logo.png', 'name' => '페이스북'], | |
| 'naver' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/naver_logo.png', 'name' => '네이버'], | |
| 'kakao' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/kakao_logo.png', 'name' => '카카오'], | |
| 'wonder' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/wemakeprice_logo.png', 'name' => '위메프'], | |
| 'apple' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/apple_logo.png', 'name' => '애플'], | |
| 'google' => ['icon' => PATH_ADMIN_GD_SHARE . 'ncds/image/google_logo.png', 'name' => '구글'], | |
| ]; | |
| $this->setData('searchFormData', $postData); | |
| $this->setData('snsIcons', $sysIcons); | |
| $this->setData('sortType', self::SORT_LIST); | |
| $this->setData('members', $members); | |
| $this->setData('groups', Util::getGroupName()); | |
| $this->setData('page', $page); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| } | |
| } | |
변경 후LayerSelectMembersResultController.php
변경 전SmsTemplatePsController.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. | |
| */ | |
| namespace Bundle\Controller\Admin\Crm\Sms; | |
| use Origin\DTO\ApiClient\Commerce\Notification\MessageTemplateSmsDTO; | |
| use Origin\Enum\Crm\Message\SmsTemplateCategory; | |
| use Origin\Repository\Member\Sms\SmsContentsRepository; | |
| use Origin\Service\Crm\Message\SmsTemplateService; | |
| use Request; | |
| class SmsTemplatePsController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $request = Request::post()->toArray(); | |
| $mode = $request['mode'] ?? ''; | |
| $service = \App::getInstance(SmsTemplateService::class); | |
| switch ($mode) { | |
| case 'save': | |
| $categoryValue = $request['category'] ?? ''; | |
| $category = SmsTemplateCategory::tryFrom($categoryValue); | |
| if ($category === null) { | |
| $this->json(['success' => false, 'message' => '카테고리를 선택해주세요.']); | |
| return; | |
| } | |
| $subject = trim($request['subject'] ?? ''); | |
| if ($subject === '') { | |
| $this->json(['success' => false, 'message' => '제목을 입력해주세요.']); | |
| return; | |
| } | |
| $contents = trim($request['contents'] ?? ''); | |
| if ($contents === '') { | |
| $this->json(['success' => false, 'message' => '내용을 입력해주세요.']); | |
| return; | |
| } | |
| $templateDto = MessageTemplateSmsDTO::fromRegisterRequest([ | |
| 'templateCategory' => $category->value, | |
| 'templateTitle' => $subject, | |
| 'templateMessageContent' => $contents, | |
| 'templateMessageTitle' => trim($request['messageTitle'] ?? ''), | |
| ]); | |
| $sno = $service->setSmsContents($templateDto); | |
| if ($sno == 0) { | |
| $this->json(['success' => false, 'message' => '템플릿 저장에 실패하였습니다.']); | |
| } else { | |
| $this->json(['success' => true, 'data' => ['sno' => $sno], 'message' => '템플릿이 저장되었습니다.']); | |
| } | |
| return; | |
| default: | |
| $this->json(['error' => 'Invalid mode', 'data' => []]); | |
| return; | |
| } | |
| } | |
| } | |
변경 후SmsTemplatePsController.php
변경 전SmsAutoController.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
변경 후SmsAutoController.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Origin\Service\Crm\Message\SmsTemplateService; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| /** @var SmsTemplateService $smsTemplateService */ | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $smsAutoData = $smsTemplateService->attachLinkedAutoSendContents($smsAutoData, array_keys(Sms::SMS_AUTO_RECEIVE_LIST), \Bundle\Component\Sms\SmsAdmin::SMS_AUTO_TYPES); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
변경 전SmsSendController.php
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Origin\Service\Member\CrmGroupService; | |
| $getSmsContentsByGoodsRestock = function () { | |
| $db = \App::getInstance('DB'); | |
| $db->strField = '*'; | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode LIKE CONCAT(\'01007501\', \'%\')'; | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . gd_implode(' ', $query); | |
| $resultData = $db->query_fetch($strSQL); | |
변경 후SmsSendController.php
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\Crm\Message\SmsTemplateApprovalStatus; | |
| use Origin\Enum\Crm\Message\LegacyCallNumberStatus; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Origin\Service\Member\CrmGroupService; | |
| $getSmsContentsByGoodsRestock = function () { | |
| $db = \App::getInstance('DB'); | |
| $db->strField = '*'; | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode LIKE CONCAT(\'01007501\', \'%\')' | |
| . ' AND approvalStatus = \'' . SmsTemplateApprovalStatus::APPROVED->value . '\''; | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . gd_implode(' ', $query); | |
| $resultData = $db->query_fetch($strSQL); | |
변경 전SmsSendPsController.php
| use Bundle\Component\Excel\Enum\ExcelSampleType; | |
| use Bundle\Component\Excel\ExcelSample; | |
| use Framework\Utility\StringUtils; | |
| /** | |
| * Class SmsSendPsController | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ?, \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . gd_implode(' ', $query); | |
| $data = $db->query_fetch($strSQL, $binds, false); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ? , \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| try { | |
| $isUpdate = $request->post()->has('sno'); | |
| $smsAdmin->validateSmsContents($request->post(), $isUpdate); | |
| if ($smsAdmin->saveSmsContentsData($request->post()->all())) { | |
| case 'deleteSmsContents': // 선택 발송내역 삭제 | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| if ($smsAdmin->deleteSmsContentsData($request->post()->get('sno', []))) { | |
| $this->json( | |
| [ | |
| 'result' => 'OK', | |
| 'message' => '삭제되었습니다.', | |
| ] | |
| ); | |
| } else { | |
변경 후SmsSendPsController.php
| use Bundle\Component\Excel\Enum\ExcelSampleType; | |
| use Bundle\Component\Excel\ExcelSample; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\Crm\Message\SmsTemplateApprovalStatus; | |
| use Origin\Service\Crm\Message\SmsTemplateService; | |
| /** | |
| * Class SmsSendPsController | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED->value); | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ?, \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED->value); | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . gd_implode(' ', $query); | |
| $data = $db->query_fetch($strSQL, $binds, false); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED->value); | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ? , \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| try { | |
| $restrictMessage = \App::getInstance(SmsTemplateService::class)->getWriteRestrictMessage(); | |
| if ($restrictMessage !== null) { | |
| $this->json(['result' => 'FAIL', 'message' => $restrictMessage,]); | |
| break; | |
| } | |
| $isUpdate = $request->post()->has('sno'); | |
| $smsAdmin->validateSmsContents($request->post(), $isUpdate); | |
| if ($smsAdmin->saveSmsContentsData($request->post()->all())) { | |
| case 'deleteSmsContents': // 선택 발송내역 삭제 | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $restrictMessage = $smsTemplateService->getWriteRestrictMessage(); | |
| if ($restrictMessage !== null) { | |
| $this->json(['result' => 'FAIL', 'message' => $restrictMessage,]); | |
| break; | |
| } | |
| $snoList = $request->post()->get('sno', []); | |
| $excluded = $smsTemplateService->excludeUsedByAutoSend(is_array($snoList) ? $snoList : []); | |
| $deletableSnoList = $smsTemplateService->filterLegacyDeletableSnoList($excluded['deletableSnoList']); | |
| if (empty($deletableSnoList) && $excluded['mappedCount'] > 0) { | |
| $this->json(['result' => 'FAIL', 'message' => SmsTemplateService::SMS_USED_NOT_DELETABLE_MESSAGE,]); | |
| break; | |
| } | |
| if ($smsAdmin->deleteSmsContentsData($deletableSnoList)) { | |
| $this->json( | |
| [ | |
| 'result' => 'OK', | |
| 'message' => $excluded['mappedCount'] > 0 | |
| ? sprintf(SmsTemplateService::SMS_PARTIAL_DELETED_MESSAGE, $excluded['mappedCount'], count($deletableSnoList)) | |
| : '삭제되었습니다.', | |
| 'mappedCount' => $excluded['mappedCount'], | |
| ] | |
| ); | |
| } else { | |
변경 전ManagePsController.php
| $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; | |
변경 후ManagePsController.php
| $this->setData('sno', $request->get()->get('sno')); | |
| // 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (뷰에서 사용) | |
| $this->setData('isMessageApprover', $messageOperatorRoleService->isSenderPermissionLocked((int)$request->get()->get('sno'))); | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative(); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleGuard->isDelegateOrHigher()); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| // 위임자 아님 안내가 우선이라 위임자 이상일 때만 판정한다 — isUncertifiedDelegateActor 는 위임자가 아닌 운영자도 DI 가 없으면 true 다 | |
| $this->setData('isCallerUncertifiedDelegate', $messageOperatorRoleGuard->isDelegateOrHigher() && $messageOperatorRoleService->isUncertifiedDelegateActor()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| break; | |
변경 전ManageRegisterController.php
| $this->setData('messageDelegate', $messageDelegate); | |
| // 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다 | |
| // 본인인증은 본인 계정에서만 성립하므로 책임자·제3자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다 | |
| $isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo; | |
| // 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다 | |
| $isRestrictedEditTarget = $isModify | |
| $isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO | |
| ? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo) | |
| : $isRestrictedEditTarget; | |
| if ($isSelfTarget && $isSelfCertificationProvided) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| $this->addScript([ | |
| 'manager-certification.js?' . time(), | |
| ]); | |
| } | |
| $this->setData('selfCertification', $selfCertification); | |
| // 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다 | |
| // 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다 | |
| // 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다 | |
| $this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf); | |
| $this->setData('senderCellPhoneLocked', ($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
변경 후ManageRegisterController.php
| $this->setData('messageDelegate', $messageDelegate); | |
| // 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다 | |
| // 본인인증은 본인 계정에서만 성립하므로 책임자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다 | |
| $isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo; | |
| // 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다 | |
| $isRestrictedEditTarget = $isModify | |
| $isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO | |
| ? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo) | |
| : $isRestrictedEditTarget; | |
| // 책임자도 본인도 아닌 운영자에게도 발송 역할 계정의 인증 방식은 본인인증으로 노출한다 — 인증 스크립트는 싣지 않고 클릭은 본인 아님 안내로 끝난다 | |
| $isOtherEditorOfSenderRole = $isModify | |
| && $managerNo !== ManagerService::SUPER_MANAGER_SNO | |
| && $isRestrictedEditTarget | |
| && !$isSelfTarget | |
| && !$messageOperatorRoleGuard->isRepresentative(); | |
| if ($isSelfTarget && $isSelfCertificationProvided) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| $this->addScript([ | |
| 'manager-certification.js?' . time(), | |
| ]); | |
| } elseif ($isOtherEditorOfSenderRole) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| 'certified' => $messageOperatorRoleGuard->isConnected($managerNo), | |
| 'isSelf' => false, | |
| ]; | |
| } | |
| $this->setData('selfCertification', $selfCertification); | |
| // 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다 | |
| // 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다 | |
| // 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다 | |
| // 그 외 운영자 화면의 휴대폰 입력은 기획상 활성으로 노출한다 — 저장은 화면 1차 차단과 저장측 되돌림이 막는다 | |
| $this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf); | |
| $this->setData( | |
| 'senderCellPhoneLocked', | |
| !$isOtherEditorOfSenderRole | |
| && (($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf) | |
| ); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
변경 전CartRemindPsController.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\Promotion; | |
| use Component\CartRemind\CartRemind; | |
| use Component\Sms\Exception\PasswordException; | |
| use Framework\Debug\Exception; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Message; | |
| use Request; | |
| class CartRemindPsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * 장바구니 알림 처리 | |
| * [관리자 모드] 장바구니 알림 처리 | |
| * | |
| * @author su | |
| */ | |
| public function index() | |
| { | |
| try { | |
| $cartRemind = new CartRemind(); | |
| switch (Request::post()->get('mode')) { | |
| case 'insertCartRemind': | |
| case 'modifyCartRemind': | |
| // 모듈 호출 | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemind($postValue); | |
| $this->layer(__('저장이 완료되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| case 'modifyCartRemindMessage': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemindSendMessage($postValue); | |
| $this->layer(__('수정되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| case 'sendCartRemindManual': | |
| $postValue = Request::post()->toArray(); | |
| try { | |
| $result = $cartRemind->sendCartRemind($postValue); | |
| } catch (PasswordException $e) { | |
| $result['msg'] = $e->getMessage(); | |
| } | |
| echo json_encode($result); | |
| exit; | |
| break; | |
| case 'setCartRemindAutoState': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemindAutoState($postValue); | |
| $result['code'] = 99; | |
| $result['msg'] = __('변경되었습니다.'); | |
| echo json_encode($result); | |
| exit; | |
| break; | |
| case 'deleteCartRemind': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->deleteCartRemind($postValue); | |
| $this->layer(__('삭제되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| } | |
| } catch (\Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage()); //새로고침안됨 | |
| } | |
| } | |
| } | |
변경 후CartRemindPsController.php
변경 전CouponPsController.php
| use Framework\File\FileValidator; | |
| use Origin\DTO\ApiClient\Commerce\Crm\TargetsRepeatRuleLatestResultsRequestDTO; | |
| use Origin\Service\ApiClient\Commerce\Crm\CrmAdminApiClientService; | |
| use Request; | |
| class CouponPsController extends \Controller\Admin\Controller | |
| break; | |
| case 'sendComebackCoupon': | |
| $aSno = Request::post()->get('chkCoupon'); | |
| $couponAdmin->sendComebackCoupon($aSno[0]); | |
| $this->layer(__('컴백쿠폰 발송이 완료되었습니다.'), 'top.location.href="comeback_coupon_list.php";'); | |
| break; | |
변경 후CouponPsController.php
| use Framework\File\FileValidator; | |
| use Origin\DTO\ApiClient\Commerce\Crm\TargetsRepeatRuleLatestResultsRequestDTO; | |
| use Origin\Service\ApiClient\Commerce\Crm\CrmAdminApiClientService; | |
| use Origin\Service\Sms\OccupancyAuth\SendGateService; | |
| use Request; | |
| class CouponPsController extends \Controller\Admin\Controller | |
| break; | |
| case 'sendComebackCoupon': | |
| $aSno = Request::post()->get('chkCoupon'); | |
| $comebackCouponInfo = $couponAdmin->getComebackCouponInfo($aSno[0], 'smsFl'); | |
| if (($comebackCouponInfo['smsFl'] ?? '') === 'y') { | |
| /** @var SendGateService $sendGateService */ | |
| $sendGateService = \App::getInstance(SendGateService::class); | |
| $sendGateService->assertSendable(); | |
| $sendGateService->assertCallNumberUsable(); | |
| } | |
| $couponAdmin->sendComebackCoupon($aSno[0]); | |
| $this->layer(__('컴백쿠폰 발송이 완료되었습니다.'), 'top.location.href="comeback_coupon_list.php";'); | |
| break; | |
변경 전LayerManagePermissionController.php
| $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); | |
변경 후LayerManagePermissionController.php
| $this->setData('disabled', $disabled); | |
| $this->setData('sno', $request->post()->get('sno')); | |
| $this->setData('isMessageApprover', $isMessageApprover); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleGuard->isDelegateOrHigher()); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| // 위임자 아님 안내가 우선이라 위임자 이상일 때만 판정한다 — isUncertifiedDelegateActor 는 위임자가 아닌 운영자도 DI 가 없으면 true 다 | |
| $this->setData('isCallerUncertifiedDelegate', $messageOperatorRoleGuard->isDelegateOrHigher() && $messageOperatorRoleService->isUncertifiedDelegateActor()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| $this->setData('scmFl', $scmFl); | |
변경 전LayerSmsContentsController.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\Share; | |
| use Component\Sms\SmsAdmin; | |
| use Request; | |
| /** | |
| * SMS 문구 선택하기 | |
| * | |
| * @author Shin Donggyu <artherot@godo.co.kr> | |
| */ | |
| class LayerSmsContentsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * index | |
| * | |
| */ | |
| public function index() | |
| { | |
| // --- SMS 모듈 | |
| $smsAdmin = new SmsAdmin(); | |
| // mode 가 있는 경우 | |
| if (Request::post()->has('mode')) { | |
| // _POST 데이터 | |
| $postValue = Request::post()->toArray(); | |
| switch (Request::post()->get('mode')) { | |
| // 등록 / 수정 | |
| case 'registerSmsContents': | |
| case 'modifySmsContents': | |
| $result = $smsAdmin->saveSmsContentsData($postValue); | |
| if ($result === true) { | |
| echo 'OK'; | |
| } else { | |
| echo 'ERROR'; | |
| } | |
| break; | |
| // 선택 삭제 | |
| case 'deleteSmsContents': | |
| $result = $smsAdmin->deleteSmsContentsData(Request::post()->get('delSno')); | |
| if ($result === true) { | |
| echo 'OK'; | |
| } else { | |
| echo 'ERROR'; | |
| } | |
| break; | |
| } | |
| exit(); | |
| } | |
| // SMS 문구 그룹 | |
| $smsContentsGroup = gd_code('01007'); | |
| $smsContentsGroup = gd_array_merge(['0' => '문구 선택'], $smsContentsGroup); | |
| // SMS 문구 리스트 | |
| $smsContentsList = $smsAdmin->getSmsContentsList('user'); | |
| // _GET Data | |
| $getValue = Request::get()->toArray(); | |
| // page Url | |
| $pageUrl = '../' . Request::getDirectoryUri() . '/' . Request::getFileUri(); | |
| //--- 관리자 디자인 템플릿 | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setDefine('layoutContent', Request::getDirectoryUri() . '/' . Request::getFileUri()); | |
| $this->setData('layerFormID', $getValue['layerFormID']); | |
| $this->setData('parentFormID', $getValue['parentFormID']); | |
| $this->setData('dataFormID', $getValue['dataFormID']); | |
| $this->setData('dataInputNm', $getValue['dataInputNm']); | |
| $this->setData('mode', gd_isset($getValue['mode'], 'search')); | |
| $this->setData('disabled', gd_isset($getValue['disabled'], '')); | |
| $this->setData('callFunc', gd_isset($getValue['callFunc'], '')); | |
| $this->setData('pageUrl', $pageUrl); | |
| $this->setData('smsContentsGroup', $smsContentsGroup); | |
| $this->setData($smsContentsList); | |
| } | |
| } | |
변경 후LayerSmsContentsController.php
변경 전LayerSmsPasswordPsController.php
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Framework\Utility\ComponentUtils; | |
| class LayerSmsPasswordPsController extends \Controller\Admin\Controller | |
| { | |
| $request = \App::getInstance('request'); | |
| $isChangePassword = $request->post()->get('mode', 'changePassword') === 'changePassword'; | |
| if ($isChangePassword) { | |
| $captchaNumber = $request->post()->get('captcha'); | |
| $captcha = new Captcha(); | |
| $result = $captcha->verify($captchaNumber, 1); | |
| ); | |
| } | |
| } | |
| } | |
변경 후LayerSmsPasswordPsController.php
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Framework\Utility\ComponentUtils; | |
| use Origin\Enum\Crm\Message\MessageSendMenuAccessVerdict; | |
| use Origin\Enum\Crm\Message\SmsSendRestrictType; | |
| use Origin\Service\Crm\Message\MessageSendMenuAccessService; | |
| class LayerSmsPasswordPsController extends \Controller\Admin\Controller | |
| { | |
| $request = \App::getInstance('request'); | |
| $isChangePassword = $request->post()->get('mode', 'changePassword') === 'changePassword'; | |
| if ($isChangePassword) { | |
| $restrictMessage = $this->getSmsRestrictMessage(); | |
| if ($restrictMessage !== null) { | |
| $this->json(['error' => 1, 'message' => $restrictMessage]); | |
| } | |
| $captchaNumber = $request->post()->get('captcha'); | |
| $captcha = new Captcha(); | |
| $result = $captcha->verify($captchaNumber, 1); | |
| ); | |
| } | |
| } | |
| /** | |
| * SMS 인증번호 변경은 SMS 관리자만 가능하다. | |
| * ps 는 인터셉터 게이트를 타지 않아 화면 제한을 우회한 직접 호출이 가능하므로 조작 시점에 다시 본다. | |
| * 판정축은 진입 게이트와 동일(역할·차단·IP·진입 점유인증). 책임자 미연동은 등록 안내 플로우라 차단하지 않는다. | |
| */ | |
| protected function getSmsRestrictMessage(): ?string | |
| { | |
| /** @var MessageSendMenuAccessService $menuAccessService */ | |
| $menuAccessService = \App::getInstance(MessageSendMenuAccessService::class); | |
| $verdict = $menuAccessService->evaluateEntry((int)\App::getInstance('session')->get('manager.sno')); | |
| $writableVerdicts = [ | |
| null, | |
| MessageSendMenuAccessVerdict::NOT_CONNECTED_REPRESENTATIVE, | |
| MessageSendMenuAccessVerdict::NOT_CONNECTED_MANAGER, | |
| ]; | |
| if (in_array($verdict, $writableVerdicts, true)) { | |
| return null; | |
| } | |
| $restrictType = SmsSendRestrictType::fromVerdict($verdict); | |
| return $restrictType->getClickAlertMessage() . ' ' . $restrictType->getClickAlertSubMessage(); | |
| } | |
| } | |