변경 파일 목록
총 53개
관리자 동시 접속 보안 강화.
duplicate_login.phpAdmin/base/AdminLogDAO.phpComponent/Admin/ManagerCs.phpComponent/Member/Manager.phpComponent/Member/IndexController.phpController/Admin/Base/상품 리스트 및 상품 가격 관리 정렬 기능 개선.
DBTableField.phpComponent/Database/GoodsAdmin.phpComponent/Goods/GoodsBatchPriceController.phpController/Admin/Goods/GoodsPsController.phpController/Admin/Goods/고도몰 쇼핑몰 보안 강화
Board.phpComponent/Board/PollDetailViewController.phpController/Admin/Promotion/PollResultController.phpController/Front/Service/report.htmldata/skin/mobile/moment/board/report.htmldata/skin/mobile/mygreek/board/report.htmldata/skin/mobile/story_g/board/게시판 이미지 업로드 지원 확대.
Board.phpComponent/Board/EditorUpload.phpComponent/File/ArticlePsController.phpController/Admin/Board/BoardPsController.phpController/Front/Board/BoardPsController.phpController/Mobile/Board/해외구매대행 전자상거래 업체부호 설정 기능 추가
base_info.phpAdmin/policy/BuyerInform.phpComponent/Agreement/DBTableField.phpComponent/Database/Mall.phpComponent/Mall/Policy.phpComponent/Policy/수기주문에서 회원 장바구니 상품 2개 이상 추가 안정성 개선
OrderPsController.phpController/Admin/Order/DB 데드락 자동 복구 및 발송/장바구니 교착 완화
Cart.phpComponent/Cart/MailLog.phpComponent/Mail/CartPsController.phpController/Front/Order/비회원 주문 취소 복원 처리 개선
ReOrderCalculation.phpComponent/Order/관리자 주문 상세보기 성능 개선
order_view.phpAdmin/order/OrderAdminNew.phpComponent/Order/OrderAdmin.phpComponent/Order/OrderDeliveryOriginalRepository.phpRepository/Order/OrderHandleRepository.phpRepository/Order/사은품 주문 보안 강화.
Gift.phpComponent/Gift/GiftSelectCntValidator.phpComponent/Gift/OrderNew.phpComponent/Order/Order.phpComponent/Order/카카오 아이디 로그인 설정 페이지 헤더 표시 개선.
kakao_login_config.phpAdmin/member/네이버 아이디 로그인 보안 강화
AdultController.phpController/Front/Intro/MemberController.phpController/Front/Intro/LoginController.phpController/Front/Member/NaverLoginController.phpController/Front/Member/Naver/AdultController.phpController/Mobile/Intro/MemberController.phpController/Mobile/Intro/회원등급 평가방법 설정의 '주문금액' 표기를 '실결제금액'으로 변경
member_group_appraisal_config.phpAdmin/member/변경 파일 코드
추가삭제
변경 전duplicate_login.php
변경 후duplicate_login.php
| <link rel="stylesheet" href="<?= PATH_ADMIN_GD_SHARE ?>css/duplicate-login.css"/> | |
| <form id="frmDuplicateLogin" action="login_ps.php" method="post"> | |
| <input type="hidden" name="mode" value="concurrentLoginConfirm"/> | |
| <input type="hidden" name="returnUrl" value=""/> | |
| <div class="duplicate-login-backdrop"> | |
| <div class="duplicate-login-dialog"> | |
| <a href="/base/login_ps.php?mode=logout" class="close duplicate-login-close" aria-label="닫기">×</a> | |
| <h2 class="duplicate-login-title">고객님의 계정이 이미 사용중입니다.</h2> | |
| <div class="duplicate-login-content"> | |
| <p class="duplicate-login-desc"> | |
| • 고객님의 계정이 다른 IP에서 로그인되었습니다. 아래 버튼을 선택하여 로그인을 진행해주세요.<br> | |
| • 안전하게 이용하려면 이전 로그인을 종료하고 새로 로그인해주세요. | |
| </p> | |
| <table class="duplicate-login-ip-table"> | |
| <colgroup> | |
| <col width="50%"> | |
| <col width="50%"> | |
| </colgroup> | |
| <tbody> | |
| <tr> | |
| <th>현재 로그인한 IP</th> | |
| <td><?= gd_htmlspecialchars($currentLoginIp) ?></td> | |
| </tr> | |
| <tr> | |
| <th>이전 로그인한 IP</th> | |
| <td><?= gd_htmlspecialchars($lastLoginIp) ?></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <div class="duplicate-login-options"> | |
| <label class="duplicate-login-option"> | |
| <input type="radio" name="keepExisting" value="y" checked/> | |
| 이전 로그인을 그대로 유지한 상태에서 로그인 (동시 로그인 허용) | |
| </label> | |
| <label class="duplicate-login-option"> | |
| <input type="radio" name="keepExisting" value="n"/> | |
| 이전 로그인은 종료하고, 새로 로그인 | |
| </label> | |
| </div> | |
| </div> | |
| <div class="duplicate-login-actions"> | |
| <a href="/base/login_ps.php?mode=logout" class="btn btn-white">취소</a> | |
| <input type="submit" value="확인" class="btn btn-black"/> | |
| </div> | |
| </div> | |
| </div> | |
| </form> | |
| <script> | |
| (() => { | |
| const form = document.getElementById('frmDuplicateLogin'); | |
| if (!form) { return; } | |
| form.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const checked = form.querySelector('input[name="keepExisting"]:checked'); | |
| const keepExisting = checked?.value ?? 'n'; | |
| const standalone = keepExisting !== 'y'; | |
| fetch('/share/duplicate_login_ps.php', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Accept': 'application/json', | |
| 'X-Requested-With': 'XMLHttpRequest', | |
| }, | |
| body: JSON.stringify({ standalone: standalone }), | |
| }).then((response) => { | |
| if (!response.ok) { | |
| throw new Error('request failed'); | |
| } | |
| location.reload(); | |
| }).catch(() => { | |
| alert('요청하신 작업을 완료하지 못했습니다.\n잠시 후 다시 시도해 주세요.'); | |
| }); | |
| }); | |
| })(); | |
| </script> | |
변경 전AdminLogDAO.php
| const LOGIN = 'login'; | |
| const LOGOUT = 'logout'; | |
| const ALLOWED_MODES = ['authEmail', 'authSms', 'register', 'modify', 'delete', 'setManagePermission', 'getManagerListLayout']; | |
| $page = '주문 내역 삭제'; | |
| } | |
| $logType = $pageData['type'] ?? AdminLogDAO::LOG_TYPE_P2Y; | |
| // 개인정보접속기록 다운로드로그 5년보관 | |
| if ($page == 'adminLogExcel') { | |
변경 후AdminLogDAO.php
| const LOGIN = 'login'; | |
| const LOGOUT = 'logout'; | |
| const LOGOUT_SESSION_END = 'logoutSessionEnd'; | |
| const ALLOWED_MODES = ['authEmail', 'authSms', 'register', 'modify', 'delete', 'setManagePermission', 'getManagerListLayout']; | |
| $page = '주문 내역 삭제'; | |
| } | |
| if ($mode === self::LOGOUT_SESSION_END) { | |
| $page = 'adminInfo'; | |
| $action = '로그아웃(세션 종료)'; | |
| } | |
| $logType = $pageData['type'] ?? AdminLogDAO::LOG_TYPE_P2Y; | |
| // 개인정보접속기록 다운로드로그 5년보관 | |
| if ($page == 'adminLogExcel') { | |
변경 전ManagerCs.php
| use Framework\Debug\Exception\DatabaseException; | |
| use Framework\Security\Digester; | |
| use Framework\Security\Otp; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\StringUtils; | |
| use Framework\Utility\GodoUtils; | |
| /** | |
| * Class ManagerCs | |
| * @package Bundle\Component\Member | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $logger = \App::getInstance('logger'); | |
| $this->db->query_reset(); | |
| $data['lastLoginIp'] = $request->getRemoteAddress(); | |
| $data['sessionLimitTime'] = $sessionLimitTime; | |
| $session->del(self::SESSION_TEMP_MANAGER); | |
| $session->set(self::SESSION_MANAGER_LOGIN, $data); | |
| } | |
| /** | |
변경 후ManagerCs.php
| use Framework\Debug\Exception\DatabaseException; | |
| use Framework\Security\Digester; | |
| use Framework\Security\Otp; | |
| use Framework\SimpleCache\SimpleCache; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\StringUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Origin\Service\Storage\ManagerDuplicateLoginService; | |
| /** | |
| * Class ManagerCs | |
| * @package Bundle\Component\Member | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $logger = \App::getInstance('logger'); | |
| $duplicateLoginService = new ManagerDuplicateLoginService( | |
| \App::getInstance('application'), | |
| \App::getInstance('session'), | |
| SimpleCache::init(SimpleCache::REDIS) | |
| ); | |
| $this->db->query_reset(); | |
| $data['lastLoginIp'] = $request->getRemoteAddress(); | |
| $data['sessionLimitTime'] = $sessionLimitTime; | |
| $session->del(self::SESSION_TEMP_MANAGER); | |
| $session->set(self::SESSION_MANAGER_LOGIN, $data); | |
| $duplicateLoginService->addCurrentLoginToList($data['sno']); | |
| } | |
| /** | |
변경 전Manager.php
| namespace Bundle\Component\Member; | |
| use Component\Admin\AdminMenu; | |
| use Component\Database\DBTableField; | |
| use Component\Member\Exception\LoginException; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| use Framework\Security\Digester; | |
| use Framework\Security\Otp; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Admin\Menu\AdminMenuFlagService; | |
| use Origin\Service\Member\ManagerLogoutService; | |
| use Password; | |
| use Request; | |
| use Session; | |
| */ | |
| private $_storage; | |
| /** | |
| * 생성자 | |
| */ | |
| $this->smsAutoReceiveKind[$newKey] = $val; | |
| } | |
| unset($tmp, $newKey); | |
| } | |
| /** | |
| // SMS 자동발송 수신여부 | |
| $arrTmp = [ | |
| 'smsAutoOrder', | |
| 'smsAutoMember', | |
| 'smsAutoPromotion', | |
| 'smsAutoBoard', | |
| 'smsAutoAdmin', | |
| ]; | |
| $arrSms = []; | |
| if ($arrData['smsAutoFl'] === 'y') { | |
| if ((int) $data['scmNo'] > DEFAULT_CODE_SCMNO && GodoUtils::isPlusShop(PLUSSHOP_CODE_SCM) === false) { | |
| throw new \RuntimeException(__('아래 "플러스샵 > 공급사관리" 링크로 이동하셔서 설치하신 후 시도해주세요.<br><a href="http://plus.godo.co.kr/goods/view.gd?idx=6" target="_blank">>공급사 관리 설치하기</a>')); | |
| } | |
| $adminLog = \App::load('Component\\Admin\\AdminLogDAO'); | |
| // 동일 IP 로그인 연속 시도에 의한 접속제한 처리 | |
| $this->setManagerOldMenuAccessChange(); // 운영자 접속 권한 - 세부 권한으로 마이그레이션 | |
| $this->setManagerFunctionAuthChange(); // 운영자 기능 권한 - 기능 권한으로 마이그레이션 | |
| $this->setManagerScmFunctionAuthChange(); // 공급사 기능 권한 - 기능 권한으로 마이그레이션 | |
| // 관리자 로그인 성공 로그 | |
| $request->request()->set('adminLoginFl', true); | |
| $adminMenuFlagService->updateAdminMenuFlagsInSession(); | |
| } | |
| /** | |
| * setManagerOldMenuAccessChange | |
| * 운영자 세부 접속 권한 마이그레이션 | |
| } | |
| } | |
| /** | |
| * 관리자 로그아웃 | |
| */ | |
| public function managerLogout() | |
| { | |
| $cookie = \App::getInstance('cookie'); | |
| // 관리자 로그아웃 로그 작성 | |
| $adminLog = \App::load('Component\\Admin\\AdminLogDAO'); | |
| $adminLog->setAdminLog($adminLog::LOGOUT); | |
| // 세션을 없애기전에 세션 쿠키를 지웁니다. | |
| if ($cookie->has($session->getName())) { | |
| // 관리자 통합회원 로그아웃 | |
| /** @var ManagerLogoutService $managerLogoutService */ | |
| $managerLogoutService = \App::getInstance(ManagerLogoutService::class); | |
| $managerLogoutService->logout(); | |
| // 관리자만 로그아웃하는 경우 세션 클리어 | |
| if (MemberUtil::isLogin()) { | |
| } | |
| if ($result > 0 && $type != 'reset') { | |
| Manager::managerLogout(); | |
| } | |
| } | |
| unset($arrBind); | |
| return gd_htmlspecialchars_stripslashes($data); | |
| } | |
| } | |
변경 후Manager.php
| namespace Bundle\Component\Member; | |
| use Component\Admin\AdminLogDAO; | |
| use Component\Admin\AdminMenu; | |
| use Component\Database\DBTableField; | |
| use Component\Member\Exception\LoginException; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| use Framework\Security\Digester; | |
| use Framework\Security\Otp; | |
| use Framework\SimpleCache\SimpleCache; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Admin\Menu\AdminMenuFlagService; | |
| use Origin\Service\Member\ManagerLogoutService; | |
| use Origin\Service\Storage\ManagerDuplicateLoginService; | |
| use Password; | |
| use Request; | |
| use Session; | |
| */ | |
| private $_storage; | |
| private ManagerDuplicateLoginService $managerLoginStorage; | |
| /** @var bool 중복 로그인으로 인한 (강제) 로그아웃 여부 */ | |
| private bool $shouldDuplicateLogout = false; | |
| /** | |
| * 생성자 | |
| */ | |
| $this->smsAutoReceiveKind[$newKey] = $val; | |
| } | |
| unset($tmp, $newKey); | |
| $this->managerLoginStorage = new ManagerDuplicateLoginService( | |
| \App::getInstance('application'), | |
| \App::getInstance('session'), | |
| SimpleCache::init(SimpleCache::REDIS) | |
| ); | |
| } | |
| /** | |
| // SMS 자동발송 수신여부 | |
| $arrTmp = [ | |
| 'smsAutoOrder', | |
| 'smsAutoRegular', | |
| 'smsAutoMember', | |
| 'smsAutoPromotion', | |
| 'smsAutoBoard', | |
| 'smsAutoAdmin', | |
| 'smsAutoPresent', | |
| ]; | |
| $arrSms = []; | |
| if ($arrData['smsAutoFl'] === 'y') { | |
| if ((int) $data['scmNo'] > DEFAULT_CODE_SCMNO && GodoUtils::isPlusShop(PLUSSHOP_CODE_SCM) === false) { | |
| throw new \RuntimeException(__('아래 "플러스샵 > 공급사관리" 링크로 이동하셔서 설치하신 후 시도해주세요.<br><a href="http://plus.godo.co.kr/goods/view.gd?idx=6" target="_blank">>공급사 관리 설치하기</a>')); | |
| } | |
| $adminLog = \App::load('Component\\Admin\\AdminLogDAO'); | |
| // 동일 IP 로그인 연속 시도에 의한 접속제한 처리 | |
| $this->setManagerOldMenuAccessChange(); // 운영자 접속 권한 - 세부 권한으로 마이그레이션 | |
| $this->setManagerFunctionAuthChange(); // 운영자 기능 권한 - 기능 권한으로 마이그레이션 | |
| $this->setManagerScmFunctionAuthChange(); // 공급사 기능 권한 - 기능 권한으로 마이그레이션 | |
| $this->setDuplicateLoginList($data['sno']); // 중복 로그인 감지용 로그인 세션 목록에 현재 세션 등록 | |
| // 관리자 로그인 성공 로그 | |
| $request->request()->set('adminLoginFl', true); | |
| $adminMenuFlagService->updateAdminMenuFlagsInSession(); | |
| } | |
| public function setDuplicateLoginList(int $sno): void | |
| { | |
| $this->managerLoginStorage->addCurrentLoginToList($sno); | |
| } | |
| /** | |
| * setManagerOldMenuAccessChange | |
| * 운영자 세부 접속 권한 마이그레이션 | |
| } | |
| } | |
| /** | |
| * 중복 로그인으로 인한 (강제) 로그아웃 여부 주입. | |
| * managerLogout() 을 무인자로 유지하기 위해 파라미터 대신 setter 로 받는다. | |
| * | |
| * @param bool $shouldDuplicateLogout true 면 es_adminLog 에 세션 종료 action 으로 기록 | |
| */ | |
| public function setShouldDuplicateLogout(bool $shouldDuplicateLogout): void | |
| { | |
| $this->shouldDuplicateLogout = $shouldDuplicateLogout; | |
| } | |
| /** | |
| * 관리자 로그아웃 | |
| * 중복 로그인 여부는 setShouldDuplicateLogout() 로 주입한다. | |
| */ | |
| public function managerLogout() | |
| { | |
| $cookie = \App::getInstance('cookie'); | |
| // 관리자 로그아웃 로그 작성 | |
| /** @var AdminLogDAO $adminLog */ | |
| $adminLog = \App::load('Component\\Admin\\AdminLogDAO'); | |
| $adminLog->setAdminLog($this->shouldDuplicateLogout ? $adminLog::LOGOUT_SESSION_END : $adminLog::LOGOUT); | |
| // 세션을 없애기전에 세션 쿠키를 지웁니다. | |
| if ($cookie->has($session->getName())) { | |
| // 관리자 통합회원 로그아웃 | |
| /** @var ManagerLogoutService $managerLogoutService */ | |
| $managerLogoutService = \App::getInstance(ManagerLogoutService::class); | |
| $managerLogoutService->logout($this->shouldDuplicateLogout); | |
| $this->managerLoginStorage->deleteCurrentLoginFromList(); | |
| // 관리자만 로그아웃하는 경우 세션 클리어 | |
| if (MemberUtil::isLogin()) { | |
| } | |
| if ($result > 0 && $type != 'reset') { | |
| $this->managerLogout(); | |
| } | |
| } | |
| unset($arrBind); | |
| return gd_htmlspecialchars_stripslashes($data); | |
| } | |
| public function isCurrentLoginRegistered(): bool | |
| { | |
| return $this->managerLoginStorage->isCurrentLoginRegistered(); | |
| } | |
| public function isDuplicateLogin(): bool | |
| { | |
| return $this->managerLoginStorage->isDuplicateLatestLogin(); | |
| } | |
| public function getLastLoginIp(): ?string | |
| { | |
| return $this->managerLoginStorage->getPreviousLoginIp(); | |
| } | |
| public function allLogoutWithoutMe(): void | |
| { | |
| $this->managerLoginStorage->deleteLoginAllWithoutMe(); | |
| } | |
| } | |
변경 전IndexController.php
| use Component\Member\Manager; | |
| use Core\Base\View\Alert; | |
| use Framework\Enum\ExternalUrl; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\License\LicenseRenewService; | |
| $mainSettingPolicy = \App::load('Component\\Policy\\MainSettingPolicy'); | |
| $sessionByManager = $session->get(Manager::SESSION_MANAGER_LOGIN); | |
| // 메인 통계의 권한 체크 | |
| $adminMenu = \App::load('Component\\Admin\\AdminMenu'); | |
| if ($this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) { | |
| $this->getView()->setPageName('base/index.php'); | |
| } | |
| } | |
변경 후IndexController.php
| use Component\Member\Manager; | |
| use Core\Base\View\Alert; | |
| use Framework\Enum\ExternalUrl; | |
| use Framework\Http\Session\SessionManager; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\License\LicenseRenewService; | |
| $mainSettingPolicy = \App::load('Component\\Policy\\MainSettingPolicy'); | |
| $sessionByManager = $session->get(Manager::SESSION_MANAGER_LOGIN); | |
| $this->checkDuplicateLogin($session); | |
| // 메인 통계의 권한 체크 | |
| $adminMenu = \App::load('Component\\Admin\\AdminMenu'); | |
| if ($this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) { | |
| $this->getView()->setPageName('base/index.php'); | |
| } | |
| public function checkDuplicateLogin(SessionManager $session) | |
| { | |
| $manager = \App::getInstance(\Manager::class); | |
| $request = \App::getInstance('request'); | |
| if ( | |
| $manager->isDuplicateLogin() && | |
| !$session->get('manager.allowDuplicateLogin') | |
| ) { | |
| $this->getView()->setDefine('layout', 'layout_blank_noiframe.php'); | |
| $this->getView()->setPageName('base/duplicate_login.php'); | |
| $this->getView()->setData('lastLoginIp',$manager->getLastLoginIp()); | |
| $this->getView()->setData('currentLoginIp', $request->getRemoteAddress()); | |
| echo $this->getView()->render(); | |
| exit; | |
| } | |
| } | |
| } | |
변경 전DBTableField.php
| ['val' => 'naverFl', 'typ' => 's', 'def' => 'y', 'name' => '네이버쇼핑여부','batch'=>'y'], // 네이버수입및제작여부 | |
| ['val' => 'daumFl', 'typ' => 's', 'def' => 'y', 'name' => '다음쇼핑하우연동여부', 'batch'=>'y'], // 다음쇼핑하우연동여부 | |
| ['val' => 'paycoFl', 'typ' => 's', 'def' => 'y', 'name' => '페이코쇼핑여부','batch'=>'y'], // 페이코수입및제작여부 | |
| ['val' => 'modDt', 'typ' => 's', 'def' => null], // 수정일 | |
| ['val' => 'regDt', 'typ' => 's', 'def' => null], // 등록일 | |
| ]; | |
| // @formatter:on | |
변경 후DBTableField.php
| ['val' => 'naverFl', 'typ' => 's', 'def' => 'y', 'name' => '네이버쇼핑여부','batch'=>'y'], // 네이버수입및제작여부 | |
| ['val' => 'daumFl', 'typ' => 's', 'def' => 'y', 'name' => '다음쇼핑하우연동여부', 'batch'=>'y'], // 다음쇼핑하우연동여부 | |
| ['val' => 'paycoFl', 'typ' => 's', 'def' => 'y', 'name' => '페이코쇼핑여부','batch'=>'y'], // 페이코수입및제작여부 | |
| ['val' => 'modDt', 'typ' => 's', 'def' => null, 'batch'=>'y'], // 수정일 | |
| ['val' => 'regDt', 'typ' => 's', 'def' => null], // 등록일 | |
| ]; | |
| // @formatter:on | |
변경 전GoodsAdmin.php
| const EXCEL_FL = 'R'; | |
| /** | |
| * 생성자 | |
| */ | |
| $this->search['sortList'] = array( | |
| 'g.goodsNo desc' => __('등록일 ↓'), | |
| 'g.goodsNo asc' => __('등록일 ↑'), | |
| 'g.goodsNm asc' => __('상품명 ↓'), | |
| 'g.goodsNm desc' => __('상품명 ↑'), | |
| 'g.goodsPrice asc' => __('판매가 ↓'), | |
| $getValue = Request::get()->toArray(); | |
| gd_isset($getValue['delFl'], 'n'); | |
| // --- 정렬 설정 | |
| if ($getValue['delFl'] == 'y') { | |
| $sort = gd_isset($getValue['sort'], 'delDt desc , g.goodsNo desc'); | |
| if ($this->goodsDivisionFl) { | |
| $join[] = ' LEFT JOIN ' . DB_GOODS . ' as goods ON g.goodsNo = goods.goodsNo '; | |
| } | |
| } else { | |
| $sort = gd_isset($getValue['sort'], 'g.goodsNo desc'); | |
| } | |
| $this->setSearchGoods($getValue); | |
| //상품 헤택 모듈 | |
| $getValue = Request::get()->toArray(); | |
| gd_isset($getValue['delFl'], 'n'); | |
| $sort = gd_isset($getValue['sort'], 'g.goodsNo desc'); | |
| $changeGoodsTableFl = false; | |
| if ($mode == 'restock') { | |
| if(empty($arrGoodsNo)) return true; | |
| $updateField = []; | |
| foreach(DBTableField::tableGoodsSearch() as $k => $v) { | |
| if($v['batch'] =='y') $updateField[] = 'gs.'.$v['val'].'='.'g.'.$v['val']; | |
| $this->setSearchGoods($getValue); | |
| if($getValue['goodsNo'] && is_array($getValue['goodsNo'])) { | |
| $this->arrWhere[] = 'goodsNo IN (' . gd_implode(',', $getValue['goodsNo']) . ')'; | |
| } | |
| $strSQL = ' SELECT COUNT(g.goodsNo) AS cnt FROM ' . $this->goodsTable . ' as g WHERE ' . gd_implode(' AND ', $this->arrWhere); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $goodsNo); | |
| $this->db->setModDtUse(true); | |
| $this->db->set_update_db(DB_GOODS, 'cateCd = ?', 'goodsNo = ?', $arrBind['bind']); | |
| unset($getLink); | |
| } | |
| } | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $goodsNo); | |
| $this->db->setModDtUse(true); | |
| $return = $this->db->set_update_db(DB_GOODS, $arrBind['param'], 'goodsNo = ?', $arrBind['bind']); | |
| unset($arrBind, $goodsData); | |
| return $return; | |
| } | |
| unset($arrBind); | |
| } | |
| return $arrData['applyFl']; | |
| } | |
변경 후GoodsAdmin.php
| const EXCEL_FL = 'R'; | |
| // 정렬 SQL 인젝션 방지용 화이트리스트 (관리자 상품 리스트 정렬 옵션) | |
| protected const SORT_ALLOW_LIST = [ | |
| 'g.goodsNo desc', 'g.goodsNo asc', | |
| 'g.modDt desc', 'g.modDt asc', | |
| 'g.goodsNm asc', 'g.goodsNm desc', | |
| 'g.goodsPrice asc', 'g.goodsPrice desc', | |
| 'companyNm asc', 'companyNm desc', | |
| 'g.makerNm asc', 'g.makerNm desc', | |
| 'g.orderGoodsCnt desc', | |
| 'g.hitCnt desc', | |
| 'orderRate desc', | |
| 'g.cartCnt desc', | |
| 'g.wishCnt desc', | |
| 'g.reviewCnt desc', | |
| 'delDt desc, g.goodsNo desc', | |
| 'delDt asc', | |
| ]; | |
| // 정렬 SQL 인젝션 방지용 화이트리스트 (관리자 상품 일괄처리 리스트 - getAdminListBatch) | |
| protected const SORT_ALLOW_LIST_BATCH = [ | |
| 'g.goodsNo desc', 'g.goodsNo asc', | |
| 'g.modDt desc', 'g.modDt asc', | |
| 'goodsNm asc', 'goodsNm desc', | |
| 'companyNm asc', 'companyNm desc', | |
| 'goodsPrice asc', 'goodsPrice desc', | |
| 'fixedPrice asc', 'fixedPrice desc', | |
| 'costPrice asc', 'costPrice desc', | |
| ]; | |
| /** | |
| * 생성자 | |
| */ | |
| $this->search['sortList'] = array( | |
| 'g.goodsNo desc' => __('등록일 ↓'), | |
| 'g.goodsNo asc' => __('등록일 ↑'), | |
| 'g.modDt desc' => __('수정일 ↓'), | |
| 'g.modDt asc' => __('수정일 ↑'), | |
| 'g.goodsNm asc' => __('상품명 ↓'), | |
| 'g.goodsNm desc' => __('상품명 ↑'), | |
| 'g.goodsPrice asc' => __('판매가 ↓'), | |
| $getValue = Request::get()->toArray(); | |
| gd_isset($getValue['delFl'], 'n'); | |
| // --- 검색 설정 보조 JOIN | |
| if ($getValue['delFl'] == 'y') { | |
| if ($this->goodsDivisionFl) { | |
| $join[] = ' LEFT JOIN ' . DB_GOODS . ' as goods ON g.goodsNo = goods.goodsNo '; | |
| } | |
| } | |
| // --- 정렬 설정 (SQL 인젝션 방지 - 사용자 입력은 화이트리스트만 허용) | |
| if (isset($getValue['sort']) && in_array($getValue['sort'], self::SORT_ALLOW_LIST, true)) { | |
| $sort = $getValue['sort']; | |
| } else { | |
| $sort = ($getValue['delFl'] == 'y') ? 'delDt desc, g.goodsNo desc' : 'g.goodsNo desc'; | |
| } | |
| // 수정일 정렬 시 NULL 처리 - 등록일로 대체, 페이징 안정화 보조정렬 | |
| if (preg_match('/^g\.modDt\s+(asc|desc)$/i', $sort, $m)) { | |
| $sort = "COALESCE(g.modDt, g.regDt) {$m[1]}, g.goodsNo desc"; | |
| } | |
| $this->setSearchGoods($getValue); | |
| //상품 헤택 모듈 | |
| $getValue = Request::get()->toArray(); | |
| gd_isset($getValue['delFl'], 'n'); | |
| // 정렬 설정 (SQL 인젝션 방지 - 사용자 입력은 화이트리스트만 허용) | |
| if (isset($getValue['sort']) && in_array($getValue['sort'], self::SORT_ALLOW_LIST_BATCH, true)) { | |
| $sort = $getValue['sort']; | |
| } else { | |
| $sort = 'g.goodsNo desc'; | |
| } | |
| $changeGoodsTableFl = false; | |
| if ($mode == 'restock') { | |
| if(empty($arrGoodsNo)) return true; | |
| // goodsNo 는 정수 전용 식별자 → IN 절 SQL 인젝션 방어 (sink 지점에서 일괄 정수화) | |
| $arrGoodsNo = array_map('intval', $arrGoodsNo); | |
| $updateField = []; | |
| foreach(DBTableField::tableGoodsSearch() as $k => $v) { | |
| if($v['batch'] =='y') $updateField[] = 'gs.'.$v['val'].'='.'g.'.$v['val']; | |
| $this->setSearchGoods($getValue); | |
| if($getValue['goodsNo'] && is_array($getValue['goodsNo'])) { | |
| $this->arrWhere[] = 'goodsNo IN (' . gd_implode(',', array_map('intval', $getValue['goodsNo'])) . ')'; | |
| } | |
| $strSQL = ' SELECT COUNT(g.goodsNo) AS cnt FROM ' . $this->goodsTable . ' as g WHERE ' . gd_implode(' AND ', $this->arrWhere); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $goodsNo); | |
| $this->db->setModDtUse(true); | |
| $this->db->set_update_db(DB_GOODS, 'cateCd = ?', 'goodsNo = ?', $arrBind['bind']); | |
| // es_goodsSearch 동기화 | |
| if ($this->goodsDivisionFl) { | |
| $this->updateGoodsSearch([$goodsNo]); | |
| } | |
| unset($getLink); | |
| } | |
| } | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $goodsNo); | |
| $this->db->setModDtUse(true); | |
| $return = $this->db->set_update_db(DB_GOODS, $arrBind['param'], 'goodsNo = ?', $arrBind['bind']); | |
| // es_goodsSearch 동기화 | |
| if ($this->goodsDivisionFl) { | |
| $this->updateGoodsSearch([$goodsNo]); | |
| } | |
| unset($arrBind, $goodsData); | |
| return $return; | |
| } | |
| unset($arrBind); | |
| } | |
| // es_goodsSearch 동기화 | |
| if ($this->goodsDivisionFl) { | |
| $this->updateGoodsSearch($arrGoodsNo, $getData['modDtUse']); | |
| } | |
| return $arrData['applyFl']; | |
| } | |
변경 전GoodsBatchPriceController.php
| //정렬 재정의 | |
| $getData['search']['sortList'] = array( | |
| 'g.goodsNo desc' => sprintf(__('등록일 %1$s'), '↓'), | |
| 'g.goodsNo asc' => sprintf(__('등록일 %1$s'), '↑'), | |
| 'goodsNm asc' => sprintf(__('상품명 %1$s'), '↓'), | |
| 'goodsNm desc' => sprintf(__('상품명 %1$s'), '↑'), | |
| 'companyNm asc' => sprintf(__('공급사 %1$s'), '↓'), | |
| 'companyNm desc' => sprintf(__('공급사 %1$s'), '↑'), | |
| 'fixedPrice asc' => sprintf(__('정가 %1$s'), '↓'), | |
| 'fixedPrice desc' => sprintf(__('정가 %1$s'), '↑'), | |
| 'costPrice asc' => sprintf(__('매입가 %1$s'), '↓'), | |
| 'costPrice desc' => sprintf(__('매입가 %1$s'), '↑'), | |
| 'goodsPrice asc' => sprintf(__('판매가 %1$s'), '↓'), | |
| 'goodsPrice desc' => sprintf(__('판매가 %1$s'), '↑'), | |
| ); | |
| $this->setData('goods', $goods); | |
변경 후GoodsBatchPriceController.php
| //정렬 재정의 | |
| $getData['search']['sortList'] = array( | |
| 'g.goodsNo desc' => __('등록일 ↓'), | |
| 'g.goodsNo asc' => __('등록일 ↑'), | |
| 'g.modDt desc' => __('수정일 ↓'), | |
| 'g.modDt asc' => __('수정일 ↑'), | |
| 'goodsNm asc' => __('상품명 ↓'), | |
| 'goodsNm desc' => __('상품명 ↑'), | |
| 'companyNm asc' => __('공급사 ↓'), | |
| 'companyNm desc' => __('공급사 ↑'), | |
| 'fixedPrice asc' => __('정가 ↓'), | |
| 'fixedPrice desc' => __('정가 ↑'), | |
| 'costPrice asc' => __('매입가 ↓'), | |
| 'costPrice desc' => __('매입가 ↑'), | |
| 'goodsPrice asc' => __('판매가 ↓'), | |
| 'goodsPrice desc' => __('판매가 ↑'), | |
| ); | |
| $this->setData('goods', $goods); | |
변경 전GoodsPsController.php
| // 상품리스트 메모 저장하기(그리드) | |
| case 'goods_list_memo' : | |
| try { | |
| if ($goods->getGoodsListAdminMemo($postValue['goodsNo'], $postValue['adminMemo'], 'update')) { | |
| $this->layer(__('정상적으로 저장 되었습니다.')); | |
| } | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage()); | |
| } | |
| break; | |
| // 상품리스트 선택 조회항목 삭제하기 | |
| case 'goods_list_add_grid_del' : | |
| try { | |
| // 조회항목 | |
| if ($goods->goodsListDisplayEachDelete($postValue)) { | |
| echo true; | |
| } | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage()); | |
| } | |
| break; | |
변경 후GoodsPsController.php
| // 상품리스트 메모 저장하기(그리드) | |
| case 'goods_list_memo' : | |
| try { | |
| \DB::begin_tran(); | |
| $result = $goods->getGoodsListAdminMemo($postValue['goodsNo'], $postValue['adminMemo'], 'update'); | |
| \DB::commit(); | |
| if ($result) { | |
| $this->layer(__('정상적으로 저장 되었습니다.')); | |
| } | |
| } catch (Exception $e) { | |
| \DB::rollback(); | |
| throw new LayerNotReloadException($e->getMessage()); | |
| } | |
| break; | |
| // 상품리스트 선택 조회항목 삭제하기 | |
| case 'goods_list_add_grid_del' : | |
| try { | |
| \DB::begin_tran(); | |
| // 조회항목 | |
| $result = $goods->goodsListDisplayEachDelete($postValue); | |
| \DB::commit(); | |
| if ($result) { | |
| echo true; | |
| } | |
| } catch (Exception $e) { | |
| \DB::rollback(); | |
| throw new LayerNotReloadException($e->getMessage()); | |
| } | |
| break; | |
변경 전Board.php
| protected function xssClean($data) | |
| { | |
| $onevents = 'onabort|onactivate|onafterprint|onafterupdate|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditfocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror|onerrorupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizeend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload|ontoggle|onpageshow|onrepeat|onprogress|onbegin|onpointerdown|onauxclick|onpointerenter|onend|onsearch'; | |
| $data = preg_replace('#([\x00-\x20"\'/])(?:'.$onevents.'|xmlns)[^>]*+>#iu', '$1>', $data); | |
| // Remove javascript: and vbscript: protocols | |
변경 후Board.php
| protected function xssClean($data) | |
| { | |
| // 이벤트 핸들러 목록은 StringUtils 단일소스 사용 (목록 드리프트 방지) | |
| $onevents = StringUtils::getXssEventHandlers(); | |
| $data = preg_replace('#([\x00-\x20"\'/])(?:'.$onevents.'|xmlns)[^>]*+>#iu', '$1>', $data); | |
| // Remove javascript: and vbscript: protocols | |
변경 전PollDetailViewController.php
| use Component\Promotion\Poll; | |
| use Component\Page\Page; | |
| use Exception; | |
| use Request; | |
| /** | |
| if (is_array($decData) === true) { | |
| foreach ($decData as $k => $v) { | |
| if ($k == $getValue['detail']) { | |
| $resData[] = $v; | |
| } | |
| } | |
| } | |
변경 후PollDetailViewController.php
| use Component\Promotion\Poll; | |
| use Component\Page\Page; | |
| use Exception; | |
| use Framework\Utility\StringUtils; | |
| use Request; | |
| /** | |
| if (is_array($decData) === true) { | |
| foreach ($decData as $k => $v) { | |
| if ($k == $getValue['detail']) { | |
| $resData[] = StringUtils::htmlSpecialChars($v); | |
| } | |
| } | |
| } | |
변경 전PollResultController.php
| use Component\Promotion\Poll; | |
| use Framework\Debug\Exception\AlertBackException; | |
| use Request; | |
| class PollResultController extends \Controller\Front\Controller | |
| foreach ($item['itemAnswerType'] as $key => $val) { | |
| if (empty(isset($result[$key])) === false) { | |
| if ($val == 'sub') { | |
| if (gd_count($sortData[$key]) < 20 && $result[$key]) $sortData[$key][] = $result[$key]; | |
| } else { | |
| foreach ($item['itemAnswer'][$key] as $_key => $_value) { | |
| if ($item['itemResponseType'][$key] == 'radio') { | |
변경 후PollResultController.php
| use Component\Promotion\Poll; | |
| use Framework\Debug\Exception\AlertBackException; | |
| use Framework\Utility\StringUtils; | |
| use Request; | |
| class PollResultController extends \Controller\Front\Controller | |
| foreach ($item['itemAnswerType'] as $key => $val) { | |
| if (empty(isset($result[$key])) === false) { | |
| if ($val == 'sub') { | |
| if (gd_count($sortData[$key]) < 20 && $result[$key]) { | |
| $sortData[$key][] = StringUtils::htmlSpecialChars($result[$key]); | |
| } | |
| } else { | |
| foreach ($item['itemAnswer'][$key] as $_key => $_value) { | |
| if ($item['itemResponseType'][$key] == 'radio') { | |
변경 전report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterId" value="{=memo.writerId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerId = $('#frmMemberReportWrite').find('input[name=writerId]').val(); | |
| var memoWriterId = $('#frmMemberReportWrite').find('input[name=memoWriterId]').val(); | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| writerId = memoWriterId; | |
| } | |
| if (!writerId) { // 피신고자가 비회원인 경우 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 후report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerMemNo = $('#frmMemberReportWrite').find('input[name=writerMemNo]').val(); | |
| var memoWriterMemNo = $('#frmMemberReportWrite').find('input[name=memoWriterMemNo]').val(); | |
| var targetMemNo = writerMemNo; // 기본: 게시글 작성자 회원번호 | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| targetMemNo = memoWriterMemNo; // 댓글 작성자 회원번호 | |
| } | |
| if (!targetMemNo || targetMemNo <= 0) { // 피신고자가 비회원인 경우 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 전report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterId" value="{=memo.writerId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerId = $('#frmMemberReportWrite').find('input[name=writerId]').val(); | |
| var memoWriterId = $('#frmMemberReportWrite').find('input[name=memoWriterId]').val(); | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| writerId = memoWriterId; | |
| } | |
| if (!writerId) { // 피신고자가 비회원인 경우 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 후report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerMemNo = $('#frmMemberReportWrite').find('input[name=writerMemNo]').val(); | |
| var memoWriterMemNo = $('#frmMemberReportWrite').find('input[name=memoWriterMemNo]').val(); | |
| var targetMemNo = writerMemNo; // 기본: 게시글 작성자 회원번호 | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| targetMemNo = memoWriterMemNo; // 댓글 작성자 회원번호 | |
| } | |
| if (!targetMemNo || targetMemNo <= 0) { // 피신고자가 비회원인 경우 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 전report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterId" value="{=memo.writerId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerId = $('#frmMemberReportWrite').find('input[name=writerId]').val(); | |
| var memoWriterId = $('#frmMemberReportWrite').find('input[name=memoWriterId]').val(); | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| writerId = memoWriterId; | |
| } | |
| if (!writerId) { // 피신고자가 비회원인 경우 회원 신고 및 차단 불가 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 후report.html
| <input type="hidden" name="bdId" value="{=req.bdId}"/> | |
| <input type="hidden" name="bdSno" value="{=req.bdSno}"/> | |
| <input type="hidden" name="memId" value="{=memId}"/> | |
| <input type="hidden" name="memoWriterMemNo" value="{=memo.memNo}"/> | |
| <input type="hidden" name="writerMemNo" value="{=writerMemNo}"/> | |
| <input type="hidden" name="writerId" value="{=writerId}"/> | |
| // 회원 신고차단탭 노출 | |
| function memberReportOn() { | |
| var memoSno = $('#frmMemberReportWrite').find('input[name=memoSno]').val() | |
| var writerMemNo = $('#frmMemberReportWrite').find('input[name=writerMemNo]').val(); | |
| var memoWriterMemNo = $('#frmMemberReportWrite').find('input[name=memoWriterMemNo]').val(); | |
| var targetMemNo = writerMemNo; // 기본: 게시글 작성자 회원번호 | |
| if (memoSno > 0) { // 댓글을 작성한 경우 | |
| targetMemNo = memoWriterMemNo; // 댓글 작성자 회원번호 | |
| } | |
| if (!targetMemNo || targetMemNo <= 0) { // 피신고자가 비회원인 경우 회원 신고 및 차단 불가 | |
| alert("회원 신고 및 차단이 불가한 회원입니다."); | |
| } else { | |
| if ($('#frmWrite').find('#reportMemo').val()) { | |
변경 전Board.php
| } | |
| foreach ($saveFileNmArr as $index => $saveFileNm) { | |
| $newSaveFileNm = substr(md5(microtime()), 0, 16) . rand(100, 999); | |
| $binaryData = file_get_contents($saveFileNm); | |
| $result = $imageUploadService->uploadBinaryImageToRealPath($binaryData, $movePath, $newSaveFileNm); | |
| $dbTable = self::getBoardTableName($this->req['moveBdId']); | |
| } | |
| $uploadFileNm[$i] = $file_array[$i]['name']; | |
| $saveFileNm[$i] = substr(md5(microtime()), 0, 16); | |
| $listImageWidth = $this->cfg['bdListImgWidth'] ?? 500; | |
| //이미지 아니면 섬네일 생성안됨; | |
| } | |
| $uploadFileNm = $fileData['name']; | |
| $saveFileNm = 'tmp_' . substr(md5(microtime()), 0, 16); | |
| if ($this->imageResize === true) { | |
| $result = $this->storage->upload($fileData['tmp_name'], $this->cfg['bdUploadPath'] . $saveFileNm, ['width' => $reSizeImgWidth, 'height' => $reSizeImgHeight]); | |
변경 후Board.php
| } | |
| foreach ($saveFileNmArr as $index => $saveFileNm) { | |
| $newSaveFileNm = ImageUploadService::generateSaveFileName($uploadFileNmArr[$index] ?? '', 16, true); | |
| $binaryData = file_get_contents($saveFileNm); | |
| $result = $imageUploadService->uploadBinaryImageToRealPath($binaryData, $movePath, $newSaveFileNm); | |
| $dbTable = self::getBoardTableName($this->req['moveBdId']); | |
| } | |
| $uploadFileNm[$i] = $file_array[$i]['name']; | |
| $saveFileNm[$i] = ImageUploadService::generateSaveFileName($uploadFileNm[$i]); | |
| $listImageWidth = $this->cfg['bdListImgWidth'] ?? 500; | |
| //이미지 아니면 섬네일 생성안됨; | |
| } | |
| $uploadFileNm = $fileData['name']; | |
| $saveFileNm = 'tmp_' . ImageUploadService::generateSaveFileName($uploadFileNm); | |
| if ($this->imageResize === true) { | |
| $result = $this->storage->upload($fileData['tmp_name'], $this->cfg['bdUploadPath'] . $saveFileNm, ['width' => $reSizeImgWidth, 'height' => $reSizeImgHeight]); | |
변경 전EditorUpload.php
| $result['result'] = false; | |
| try { | |
| if (file_exists($filePath)) { | |
| $saveFileName = substr(md5(microtime()), 0, 16) . rand(100, 999); | |
| $file = fopen($filePath, 'rb'); | |
| $size = filesize($filePath); | |
| $binary_data = fread($file, $size); | |
변경 후EditorUpload.php
| $result['result'] = false; | |
| try { | |
| if (file_exists($filePath)) { | |
| $saveFileName = ImageUploadService::generateSaveFileName($filePath, 16, true); | |
| $file = fopen($filePath, 'rb'); | |
| $size = filesize($filePath); | |
| $binary_data = fread($file, $size); | |
변경 전ArticlePsController.php
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| $boardConfig = new BoardConfig($req['bdId']); | |
| $result = (new ImageUploadService())->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| $boardAct = new ArticleActAdmin($req); | |
| $result = $boardAct->uploadAjax($fileData); | |
변경 후ArticlePsController.php
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| $boardConfig = new BoardConfig($req['bdId']); | |
| $imageUploadService = new ImageUploadService(); | |
| // 확장자 검증 (저장소 변경 경로의 uploadAjax 와 동일하게 차단) | |
| if ($imageUploadService->isAllowUploadExtention($fileData['name']) === false) { | |
| throw new AlertBackException('업로드 할 수 없는 파일입니다.'); | |
| } | |
| $result = $imageUploadService->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| $boardAct = new ArticleActAdmin($req); | |
| $result = $boardAct->uploadAjax($fileData); | |
변경 전BoardPsController.php
| // 기본 저장소 인지 확인 | |
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| // 기본 저장소일 경우 OBS Upload 진행 | |
| $boardConfig = new BoardConfig($req['bdId']); | |
| $result = (new ImageUploadService())->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| // 저장소 변경일 경우 해당 저장소에 Upload 진행 | |
| $boardAct = new BoardAct($req); | |
변경 후BoardPsController.php
| // 기본 저장소 인지 확인 | |
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| // 기본 저장소일 경우 OBS Upload 진행 | |
| $imageUploadService = new ImageUploadService(); | |
| // 확장자 검증 (저장소 변경 경로의 uploadAjax 와 동일하게 차단) | |
| if ($imageUploadService->isAllowUploadExtention($fileData['name']) === false) { | |
| throw new AlertBackException('업로드 할 수 없는 파일입니다.'); | |
| } | |
| $result = $imageUploadService->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| // 저장소 변경일 경우 해당 저장소에 Upload 진행 | |
| $boardAct = new BoardAct($req); | |
변경 전BoardPsController.php
| // 기본 저장소 인지 확인 | |
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| // 기본 저장소일 경우 OBS Upload 진행 | |
| $boardConfig = new BoardConfig($req['bdId']); | |
| $result = (new ImageUploadService())->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| // 저장소 변경일 경우 해당 저장소에 Upload 진행 | |
| $boardAct = new BoardAct($req); | |
변경 후BoardPsController.php
| // 기본 저장소 인지 확인 | |
| if (Board::isDefaultUploadStorage($req['bdId'])) { | |
| // 기본 저장소일 경우 OBS Upload 진행 | |
| $imageUploadService = new ImageUploadService(); | |
| // 확장자 검증 (저장소 변경 경로의 uploadAjax 와 동일하게 차단) | |
| if ($imageUploadService->isAllowUploadExtention($fileData['name']) === false) { | |
| throw new AlertBackException('업로드 할 수 없는 파일입니다.'); | |
| } | |
| $result = $imageUploadService->uploadImage($fileData, '/temp', true, $boardConfig->cfg['bdUploadMaxSize']); | |
| } else { | |
| // 저장소 변경일 경우 해당 저장소에 Upload 진행 | |
| $boardAct = new BoardAct($req); | |
변경 전base_info.php
| <form id="frmBase" action="base_ps.php" method="post" enctype="multipart/form-data"> | |
| <input type="hidden" name="mode" value="base_info"/> | |
| <input type="hidden" name="mallFaviconTmp" value="<?php echo $data['mallFavicon']; ?>"/> | |
| <input type="hidden" name="stampImageTmp" value="<?php echo $data['stampImage'];?>" /> | |
| <input type="hidden" name="receiptImageTmp" value="<?php echo $data['receiptImage'];?>" /> | |
| <input type="hidden" name="mallSno" value="<?php echo $mallSno;?>" /> | |
| <input type="hidden" name="mallFl" value="<?php echo $mallFl;?>" /> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| <small>쇼핑몰의 기본적인 정보를 변경하실 수 있습니다.</small> | |
| </h3> | |
| <input type="submit" value="저장" class="btn btn-red"/> | |
| <?php if ($mallCnt > 1) { ?> | |
| <ul class="multi-skin-nav nav nav-tabs" style="margin-bottom:20px;"> | |
| <?php foreach ($mallList as $key => $mall) { ?> | |
| <li role="presentation" class="js-popover <?php echo $mallSno == $mall['sno'] ? 'active' : 'passive'; ?>" data-html="true" data-content="<?php echo $mall['mallName']; ?>" data-placement="top"> | |
| <a href="./base_info.php?mallSno=<?php echo $mall['sno']; ?>"> | |
| <span class="flag flag-16 flag-<?php echo $mall['domainFl']?>"></span> | |
| <span class="mall-name"><?php echo $mall['mallName']; ?></span> | |
| </a> | |
| </li> | |
| <?php } ?> | |
| <tr> | |
| <th>쇼핑몰명</th> | |
| <td class="form-inline" <?php if ($mallSno > 1) { ?>colspan="3"<?php } ?>> | |
| <input type="text" name="mallNm" value="<?php echo $data['mallNm']; ?>" class="form-control width-lg"/> | |
| </td> | |
| <th class="<?php if ($mallSno > 1) { ?>display-none<?php } ?>">쇼핑몰영문명</th> | |
| <td class="form-inline <?php if ($mallSno > 1) { ?>display-none<?php } ?>"> | |
| <input type="text" name="mallNmEng" value="<?php echo $data['mallNmEng']; ?>" <?php echo $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>상단타이틀</th> | |
| <td class="form-inline"> | |
| <input type="text" name="mallTitle" value="<?php echo $data['mallTitle']; ?>" class="form-control width-lg"/> | |
| </td> | |
| <th>파비콘</th> | |
| <td class="form-inline"> | |
| <input type="file" name="mallFavicon" <?php echo $disabled; ?> class="form-control"/> | |
| <span> | |
| <?php | |
| if (empty($data['mallFavicon']) === false) { | |
| <th class="require">쇼핑몰 도메인</th> | |
| <td colspan="3" class="form-inline"> | |
| <span class="font-eng">http://</span> | |
| <input type="text" name="mallDomain" value="<?php echo $data['mallDomain']; ?>" <?php echo $readonly . $disabled; ?> class="form-control width-2xl"/> | |
| <input type="button" value="도메인 신청하기" onclick="moveToExternalDomainUrl('intro');" class="btn btn-gray btn-sm"> | |
| <?php if ($mallFl == 'kr'): ?> | |
| <input type="button" value="도메인 연결/관리" onclick="moveToExternalDomainUrl('manage');" class="btn btn-gray btn-sm"> | |
| <?php else: ?> | |
| <input type="button" value="도메인 연결/관리" onclick="window.location.href='<?php echo $domainUrlList['manage']; ?>'" class="btn btn-gray btn-sm"> | |
| <?php endif; ?> | |
| <div class="notice-info"> | |
| 입력 시 쇼핑몰 및 관리자 화면에 도메인 정보가 노출됩니다. <br /> | |
| 실제 쇼핑몰 접속 도메인의 추가 및 변경은 [도메인 연결/관리] 버튼을 통해서 가능합니다. <a class="btn-link" href="<?php echo $domainUrlList['guide']; ?>" target="_blank">도움말 바로가기></a> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="require">대표카테고리</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="hidden" name="mallCategory" value="<?php echo $data['mallCategory']; ?>" <?php echo $readonly . $disabled; ?>/> | |
| <span id="mallCategory"><?php echo $data['mallCategory']; ?></span> | |
| <input type="button" onclick="mall_categoty();" value="대표카테고리 선택" <?php echo $disabled; ?> class="btn btn-gray btn-sm"/> | |
| </td> | |
| </tr> | |
| </table> | |
| <tr> | |
| <th>상호(회사명)</th> | |
| <td class="form-inline"> | |
| <input type="text" name="companyNm" value="<?php echo $data['companyNm']; ?>" class="form-control width-md"/> | |
| </td> | |
| <th>사업자등록번호</th> | |
| <td class="form-inline"> | |
| <div> | |
| <input type="text" name="businessNo[]" value="<?php echo $data['businessNo'][0]; ?>" <?php echo $readonly . $disabled; ?> maxlength="3" class="form-control js-number-only width-3xs"/> - | |
| <input type="text" name="businessNo[]" value="<?php echo $data['businessNo'][1]; ?>" <?php echo $readonly . $disabled; ?> maxlength="2" class="form-control js-number-only width-3xs"/> - | |
| <input type="text" name="businessNo[]" value="<?php echo $data['businessNo'][2]; ?>" <?php echo $readonly . $disabled; ?> maxlength="5" class="form-control js-number-only width-3xs"/> | |
| </div> | |
| <div class="notice-info"> | |
| 인터넷 쇼핑몰 운영자는 전자상거래법에 의해 사업자정보 공개페이지를 쇼핑몰 홈페이지 초기 화면에 연결해야 합니다. | |
| <tr> | |
| <th>대표자명</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="text" name="ceoNm" value="<?php echo $data['ceoNm']; ?>" class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>업태</th> | |
| <td class="form-inline"> | |
| <input type="text" name="service" value="<?php echo $data['service']; ?>" <?php echo $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| <th>종목</th> | |
| <td class="form-inline"> | |
| <input type="text" name="item" value="<?php echo $data['item']; ?>" <?php echo $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <?php if (empty($mallSno) || $mallSno == DEFAULT_MALL_NUMBER) { ?> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="emailDefaultFl" value="default" onclick="default_toggle_email_info('email', true);" <?php echo gd_isset($checked['email']['default']); ?> /> 기본 이메일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="emailDefaultFl" value="manual" onclick="default_toggle_email_info('email', false);" <?php echo gd_isset($checked['email']['manual']); ?> /> 직접 입력 | |
| </label> | |
| </div><br> | |
| <?php } ?> | |
| <input type="text" id="emailId" name="email[]" value="<?php echo $data['email'][0]; ?>" <?php echo gd_isset($deactivated['email']) . $disabled; ?> class="form-control width-lg"/> @ | |
| <input type="text" id="email" name="email[]" value="<?php echo $data['email'][1]; ?>" <?php echo gd_isset($deactivated['email']) . $disabled; ?> class="form-control width-md js-email-domain"/> | |
| <div id="email_domain_y" class="display-inline-block"> | |
| <?php echo gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1], null, $disabled, 'email_domain'); ?> | |
| </div> | |
| <div id="email_domain_n"></div> | |
| <li>• 몰 생성 시 기본 이메일 주소로 설정됩니다.</li> | |
| <li>• 25년 2월부터 네이버(naver) 이메일을 사용할 경우 메일 발송이 불가하오니 기본 이메일 사용을 권장합니다.</li> | |
| <li>• 포탈(gmail, hanmail 등) 도메인으로 사용할 경우, 고객의 메일 수신이 차단될 수 있습니다. | |
| <a class="btn-link" href="<?php echo $domainUrlList['guide_mail_cert']; ?>" target="_blank">메일 레코드 등록 가이드 보기 ></a> | |
| </li> | |
| </ul> | |
| </div> | |
| <th>사업장 주소</th> | |
| <td colspan="3"> | |
| <div class="form-inline mgb5"> | |
| <input type="text" name="zonecode" value="<?php echo $data['zonecode']; ?>" maxlength="5" class="form-control width-2xs"/> | |
| <input type="hidden" name="zipcode" value="<?php echo $data['zipcode']; ?>"/> | |
| <span id="zipcodeText" class="number <?php if (strlen($data['zipcode']) != 7) { echo 'display-none'; } ?>">(<?php echo $data['zipcode']; ?>)</span> | |
| <input type="button" onclick="postcode_search('zonecode', 'address', 'zipcode');" value="우편번호찾기" class="btn btn-gray btn-sm"/> | |
| </div> | |
| <div class="form-inline"> | |
| <input type="text" name="address" value="<?php echo $data['address']; ?>" class="form-control width-2xl"/> | |
| <input type="text" name="addressSub" value="<?php echo $data['addressSub']; ?>" class="form-control width-2xl"/> | |
| </div> | |
| </td> | |
| </tr> | |
| <td colspan="3"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="unstoringFl" value="same" onclick="display_toggle('unstoringFl','same');" <?php echo gd_isset($checked['unstoringFl']['same']); ?> />사업장 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="unstoringFl" value="new" onclick="display_toggle('unstoringFl','new');" <?php echo gd_isset($checked['unstoringFl']['new']); ?> />주소 등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="button" value="출고지 관리" class="btn btn-black btn-sm btn-unstoring" onclick="address_register('출고지', 'unstoringFl', 'unstoringFl_new', 'unstoringTable');"/> | |
| <div class="form-inline mgt10 mgb5 unstoringTable <?= empty($data['unstoringInfo']) === false ? 'active' : '' ?>"> | |
| <?php foreach ($data['unstoringInfo'] as $k => $v) { ?> | |
| <?php if ($v['addressFl'] == 'unstoring') { ?> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringNo]" value="<?php echo $data['unstoringInfo'][$k]['sno'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringZonecode]" value="<?php echo $data['unstoringInfo'][$k]['unstoringZonecode'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringZipcode]" value="<?php echo $data['unstoringInfo'][$k]['unstoringZipcode'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringAddress]" value="<?php echo $data['unstoringInfo'][$k]['unstoringAddress'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringAddressSub]" value="<?php echo $data['unstoringInfo'][$k]['unstoringAddressSub'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][mainFl]" value="<?php echo $data['unstoringInfo'][$k]['mainFl'] ?>"/> | |
| <table class="table table-cols" value="<?= $v['sno'] ?>"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="same" onclick="display_toggle('returnFl','same');" <?php echo gd_isset($checked['returnFl']['same']); ?> />사업장 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="unstoring" onclick="display_toggle('returnFl','same');" <?php echo gd_isset($checked['returnFl']['unstoring']); ?> />출고지 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="new" onclick="display_toggle('returnFl','new');" <?php echo gd_isset($checked['returnFl']['new']); ?> />주소 등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="button" value="반품/교환지 관리" class="btn btn-black btn-sm btn-return" onclick="address_register('반품/교환지', 'returnFl', 'returnFl_new', 'returnTable');"/> | |
| <div class="form-inline mgt10 mgb5 returnTable <?= empty($data['returnInfo']) === false ? 'active' : '' ?>"> | |
| <?php foreach ($data['returnInfo'] as $k => $v) { ?> | |
| <?php if ($v['addressFl'] == 'return') { ?> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnNo]" value="<?php echo $data['returnInfo'][$k]['sno'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnZonecode]" value="<?php echo $data['returnInfo'][$k]['unstoringZonecode'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnZipcode]" value="<?php echo $data['returnInfo'][$k]['unstoringZipcode'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnAddress]" value="<?php echo $data['returnInfo'][$k]['unstoringAddress'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnAddressSub]" value="<?php echo $data['returnInfo'][$k]['unstoringAddressSub'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][mainFl]" value="<?php echo $data['returnInfo'][$k]['mainFl'] ?>"/> | |
| <table class="table table-cols" value="<?= $v['sno'] ?>"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr> | |
| <th>대표전화</th> | |
| <td class="form-inline"> | |
| <input type="text" name="phone" value="<?php echo $data['phone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </td> | |
| <th>팩스번호</th> | |
| <td class="form-inline"> | |
| <input type="text" name="fax" value="<?php echo $data['fax']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>통신판매신고번호</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="text" name="onlineOrderSerial" value="<?php echo $data['onlineOrderSerial']; ?>" class="form-control width-sm"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>인감 이미지 등록</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="file" name="stampImage" <?php echo $disabled; ?> class="form-control"/> | |
| <span> | |
| <?php | |
| if (empty($data['stampImage']) === false) { | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="default" onclick="display_toggle('receiptFl','default');" <?php echo gd_isset($checked['receiptFl']['default']); ?> />현금영수증 가맹점 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="liability" onclick="display_toggle('receiptFl','liability');" <?php echo gd_isset($checked['receiptFl']['liability']); ?> />현금영수증 의무발행 가맹점 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="upload" onclick="display_toggle('receiptFl','upload');" <?php echo gd_isset($checked['receiptFl']['upload']); ?> />노출 이미지 직접등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="n" onclick="display_toggle('receiptFl','n');" <?php echo gd_isset($checked['receiptFl']['n']); ?> />노출 안함 | |
| </label> | |
| </div> | |
| <div id="receiptFl_default" class="mgt10"> | |
| <img src="/admin/gd_share/img/logo_cash_receipt_liability.png" alt="현금영수증 의무발행 가맹점"> | |
| </div> | |
| <div id="receiptFl_upload" class="mgt10"> | |
| <input type="file" name="receiptImageFile" <?php echo $disabled; ?> class="form-control"/> | |
| <div> | |
| <?php | |
| if (empty($data['receiptImage']) === false) { | |
| echo gd_select_box('centerPhoneHead', 'centerPhoneHead', $countryAddress, null, $data['centerPhoneHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerPhone" value="<?php echo $data['centerPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </div> | |
| <div> | |
| <?php | |
| echo gd_select_box('centerSubPhoneHead', 'centerSubPhoneHead', $countryAddress, null, $data['centerSubPhoneHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerSubPhone" value="<?php echo $data['centerSubPhone']; ?>" maxlength="15" class="form-control js-number-only width-md"/> | |
| </div> | |
| </td> | |
| <th>팩스번호</th> | |
| echo gd_select_box('centerFaxHead', 'centerFaxHead', $countryAddress, null, $data['centerFaxHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerFax" value="<?php echo $data['centerFax']; ?>" maxlength="15" class="form-control js-number-only width-md"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="centerEmailDefaultFl" value="default" onclick="default_toggle_email_info('centerEmail', true);" <?php echo gd_isset($checked['centerEmail']['default']); ?> /> 기본 이메일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="centerEmailDefaultFl" value="manual" onclick="default_toggle_email_info('centerEmail', false);" <?php echo gd_isset($checked['centerEmail']['manual']); ?> /> 직접 입력 | |
| </label> | |
| </div><br> | |
| <input type="text" id="centerEmailId" name="centerEmail[]" value="<?php echo $data['centerEmail'][0]; ?>" class="form-control width-lg" <?php echo gd_isset($deactivated['centerEmail']); ?>/> @ | |
| <input type="text" id="centerEmail" name="centerEmail[]" value="<?php echo $data['centerEmail'][1]; ?>" class="form-control width-md js-email-domain" <?php echo gd_isset($deactivated['centerEmail']); ?> /> | |
| <div id="centerEmail_domain_y" class="display-inline-block"> | |
| <?php echo gd_select_box('center_email_domain', null, $emailDomain, null, $data['centerEmail'][1], null, null, 'email_domain'); ?> | |
| </div> | |
| <div id="centerEmail_domain_n"></div> | |
| </td> | |
| <tr> | |
| <th>운영시간</th> | |
| <td colspan="3" class="form-inline"> | |
| <textarea name="centerHours" rows="4" class="form-control width-2xl"><?php echo $data['centerHours']; ?></textarea> | |
| </td> | |
| </tr> | |
| </table> | |
| <tr> | |
| <th>회사소개 내용</th> | |
| <td class="form-inline"> | |
| <textarea name="company" id="editor" class="form-control width100p height400"><?php echo $data['company']; ?></textarea> | |
| </td> | |
| </tr> | |
| </table> | |
| <script type="text/javascript"> | |
| <!-- | |
| var unstoringNo = <?php echo json_encode($data['unstoringInfo']) ?>; | |
| var returnNo = <?php echo json_encode($data['returnInfo']) ?>; | |
| var $mallName = '<?= $mallName ?>'; | |
| var $mallFl = '<?php echo $mallFl; ?>'; | |
| var unstoringFl = '<?= empty($data['unstoringInfo']) ?>'; | |
| var returnFl = '<?= empty($data['returnInfo']) ?>'; | |
| var isOauthSuperManagerLoggedIn = <?= $isOauthSuperManagerLoggedIn ? 'true' : 'false'; ?>; | |
| } | |
| }); | |
| display_toggle('unstoringFl','<?php echo gd_isset($data['unstoringFl']);?>'); | |
| display_toggle('returnFl','<?php echo gd_isset($data['returnFl']);?>'); | |
| display_toggle('robotsTxt','<?php echo gd_isset($data['robotsFl']);?>'); | |
| display_toggle('receiptFl','<?php echo gd_isset($data['receiptFl']);?>'); | |
| display_toggle_inline('email_domain', '<?php echo gd_isset($data['emailFl']);?>'); | |
| display_toggle_inline('centerEmail_domain', '<?php echo gd_isset($data['centerEmailFl']);?>'); | |
| $('.email_domain').change(function () { | |
| var val = $(this).val() == 'self' ? '' : $(this).val(); | |
| 'unstoringNo' : addressNo, | |
| 'mallName' : $mallName, | |
| 'mallFl' : $mallFl, | |
| 'unstoringFl' : '<?php echo gd_isset($data['unstoringFl']);?>', | |
| 'returnFl' : '<?php echo gd_isset($data['returnFl']);?>', | |
| 'mallSno' : '<?php echo $mallSno;?>' | |
| }; | |
| $.ajax({ | |
변경 후base_info.php
| <form id="frmBase" action="base_ps.php" method="post" enctype="multipart/form-data"> | |
| <input type="hidden" name="mode" value="base_info"/> | |
| <input type="hidden" name="mallFaviconTmp" value="<?= $data['mallFavicon']; ?>"/> | |
| <input type="hidden" name="stampImageTmp" value="<?= $data['stampImage'];?>" /> | |
| <input type="hidden" name="receiptImageTmp" value="<?= $data['receiptImage'];?>" /> | |
| <input type="hidden" name="mallSno" value="<?= $mallSno;?>" /> | |
| <input type="hidden" name="mallFl" value="<?= $mallFl;?>" /> | |
| <div class="page-header js-affix"> | |
| <h3><?= end($naviMenu->location); ?> | |
| <small>쇼핑몰의 기본적인 정보를 변경하실 수 있습니다.</small> | |
| </h3> | |
| <input type="submit" value="저장" class="btn btn-red"/> | |
| <?php if ($mallCnt > 1) { ?> | |
| <ul class="multi-skin-nav nav nav-tabs" style="margin-bottom:20px;"> | |
| <?php foreach ($mallList as $key => $mall) { ?> | |
| <li role="presentation" class="js-popover <?= $mallSno == $mall['sno'] ? 'active' : 'passive'; ?>" data-html="true" data-content="<?= $mall['mallName']; ?>" data-placement="top"> | |
| <a href="./base_info.php?mallSno=<?= $mall['sno']; ?>"> | |
| <span class="flag flag-16 flag-<?= $mall['domainFl']?>"></span> | |
| <span class="mall-name"><?= $mall['mallName']; ?></span> | |
| </a> | |
| </li> | |
| <?php } ?> | |
| <tr> | |
| <th>쇼핑몰명</th> | |
| <td class="form-inline" <?php if ($mallSno > 1) { ?>colspan="3"<?php } ?>> | |
| <input type="text" name="mallNm" value="<?= $data['mallNm']; ?>" class="form-control width-lg"/> | |
| </td> | |
| <th class="<?php if ($mallSno > 1) { ?>display-none<?php } ?>">쇼핑몰영문명</th> | |
| <td class="form-inline <?php if ($mallSno > 1) { ?>display-none<?php } ?>"> | |
| <input type="text" name="mallNmEng" value="<?= $data['mallNmEng']; ?>" <?= $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>상단타이틀</th> | |
| <td class="form-inline"> | |
| <input type="text" name="mallTitle" value="<?= $data['mallTitle']; ?>" class="form-control width-lg"/> | |
| </td> | |
| <th>파비콘</th> | |
| <td class="form-inline"> | |
| <input type="file" name="mallFavicon" <?= $disabled; ?> class="form-control"/> | |
| <span> | |
| <?php | |
| if (empty($data['mallFavicon']) === false) { | |
| <th class="require">쇼핑몰 도메인</th> | |
| <td colspan="3" class="form-inline"> | |
| <span class="font-eng">http://</span> | |
| <input type="text" name="mallDomain" value="<?= $data['mallDomain']; ?>" <?= $readonly . $disabled; ?> class="form-control width-2xl"/> | |
| <input type="button" value="도메인 신청하기" onclick="moveToExternalDomainUrl('intro');" class="btn btn-gray btn-sm"> | |
| <?php if ($mallFl == 'kr'): ?> | |
| <input type="button" value="도메인 연결/관리" onclick="moveToExternalDomainUrl('manage');" class="btn btn-gray btn-sm"> | |
| <?php else: ?> | |
| <input type="button" value="도메인 연결/관리" onclick="window.location.href='<?= $domainUrlList['manage']; ?>'" class="btn btn-gray btn-sm"> | |
| <?php endif; ?> | |
| <div class="notice-info"> | |
| 입력 시 쇼핑몰 및 관리자 화면에 도메인 정보가 노출됩니다. <br /> | |
| 실제 쇼핑몰 접속 도메인의 추가 및 변경은 [도메인 연결/관리] 버튼을 통해서 가능합니다. <a class="btn-link" href="<?= $domainUrlList['guide']; ?>" target="_blank">도움말 바로가기></a> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th class="require">대표카테고리</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="hidden" name="mallCategory" value="<?= $data['mallCategory']; ?>" <?= $readonly . $disabled; ?>/> | |
| <span id="mallCategory"><?= $data['mallCategory']; ?></span> | |
| <input type="button" onclick="mall_categoty();" value="대표카테고리 선택" <?= $disabled; ?> class="btn btn-gray btn-sm"/> | |
| </td> | |
| </tr> | |
| </table> | |
| <tr> | |
| <th>상호(회사명)</th> | |
| <td class="form-inline"> | |
| <input type="text" name="companyNm" value="<?= $data['companyNm']; ?>" class="form-control width-md"/> | |
| </td> | |
| <th>사업자등록번호</th> | |
| <td class="form-inline"> | |
| <div> | |
| <input type="text" name="businessNo[]" value="<?= $data['businessNo'][0]; ?>" <?= $readonly . $disabled; ?> maxlength="3" class="form-control js-number-only width-3xs"/> - | |
| <input type="text" name="businessNo[]" value="<?= $data['businessNo'][1]; ?>" <?= $readonly . $disabled; ?> maxlength="2" class="form-control js-number-only width-3xs"/> - | |
| <input type="text" name="businessNo[]" value="<?= $data['businessNo'][2]; ?>" <?= $readonly . $disabled; ?> maxlength="5" class="form-control js-number-only width-3xs"/> | |
| </div> | |
| <div class="notice-info"> | |
| 인터넷 쇼핑몰 운영자는 전자상거래법에 의해 사업자정보 공개페이지를 쇼핑몰 홈페이지 초기 화면에 연결해야 합니다. | |
| <tr> | |
| <th>대표자명</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="text" name="ceoNm" value="<?= $data['ceoNm']; ?>" class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>업태</th> | |
| <td class="form-inline"> | |
| <input type="text" name="service" value="<?= $data['service']; ?>" <?= $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| <th>종목</th> | |
| <td class="form-inline"> | |
| <input type="text" name="item" value="<?= $data['item']; ?>" <?= $readonly . $disabled; ?> class="form-control width-lg"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <?php if (empty($mallSno) || $mallSno == DEFAULT_MALL_NUMBER) { ?> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="emailDefaultFl" value="default" onclick="default_toggle_email_info('email', true);" <?= gd_isset($checked['email']['default']); ?> /> 기본 이메일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="emailDefaultFl" value="manual" onclick="default_toggle_email_info('email', false);" <?= gd_isset($checked['email']['manual']); ?> /> 직접 입력 | |
| </label> | |
| </div><br> | |
| <?php } ?> | |
| <input type="text" id="emailId" name="email[]" value="<?= $data['email'][0]; ?>" <?= gd_isset($deactivated['email']) . $disabled; ?> class="form-control width-lg"/> @ | |
| <input type="text" id="email" name="email[]" value="<?= $data['email'][1]; ?>" <?= gd_isset($deactivated['email']) . $disabled; ?> class="form-control width-md js-email-domain"/> | |
| <div id="email_domain_y" class="display-inline-block"> | |
| <?= gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1], null, $disabled, 'email_domain'); ?> | |
| </div> | |
| <div id="email_domain_n"></div> | |
| <li>• 몰 생성 시 기본 이메일 주소로 설정됩니다.</li> | |
| <li>• 25년 2월부터 네이버(naver) 이메일을 사용할 경우 메일 발송이 불가하오니 기본 이메일 사용을 권장합니다.</li> | |
| <li>• 포탈(gmail, hanmail 등) 도메인으로 사용할 경우, 고객의 메일 수신이 차단될 수 있습니다. | |
| <a class="btn-link" href="<?= $domainUrlList['guide_mail_cert']; ?>" target="_blank">메일 레코드 등록 가이드 보기 ></a> | |
| </li> | |
| </ul> | |
| </div> | |
| <th>사업장 주소</th> | |
| <td colspan="3"> | |
| <div class="form-inline mgb5"> | |
| <input type="text" name="zonecode" value="<?= $data['zonecode']; ?>" maxlength="5" class="form-control width-2xs"/> | |
| <input type="hidden" name="zipcode" value="<?= $data['zipcode']; ?>"/> | |
| <span id="zipcodeText" class="number <?php if (strlen($data['zipcode']) != 7) { echo 'display-none'; } ?>">(<?= $data['zipcode']; ?>)</span> | |
| <input type="button" onclick="postcode_search('zonecode', 'address', 'zipcode');" value="우편번호찾기" class="btn btn-gray btn-sm"/> | |
| </div> | |
| <div class="form-inline"> | |
| <input type="text" name="address" value="<?= $data['address']; ?>" class="form-control width-2xl"/> | |
| <input type="text" name="addressSub" value="<?= $data['addressSub']; ?>" class="form-control width-2xl"/> | |
| </div> | |
| </td> | |
| </tr> | |
| <td colspan="3"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="unstoringFl" value="same" onclick="display_toggle('unstoringFl','same');" <?= gd_isset($checked['unstoringFl']['same']); ?> />사업장 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="unstoringFl" value="new" onclick="display_toggle('unstoringFl','new');" <?= gd_isset($checked['unstoringFl']['new']); ?> />주소 등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="button" value="출고지 관리" class="btn btn-black btn-sm btn-unstoring" onclick="address_register('출고지', 'unstoringFl', 'unstoringFl_new', 'unstoringTable');"/> | |
| <div class="form-inline mgt10 mgb5 unstoringTable <?= empty($data['unstoringInfo']) === false ? 'active' : '' ?>"> | |
| <?php foreach ($data['unstoringInfo'] as $k => $v) { ?> | |
| <?php if ($v['addressFl'] == 'unstoring') { ?> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringNo]" value="<?= $data['unstoringInfo'][$k]['sno'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringZonecode]" value="<?= $data['unstoringInfo'][$k]['unstoringZonecode'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringZipcode]" value="<?= $data['unstoringInfo'][$k]['unstoringZipcode'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringAddress]" value="<?= $data['unstoringInfo'][$k]['unstoringAddress'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][unstoringAddressSub]" value="<?= $data['unstoringInfo'][$k]['unstoringAddressSub'] ?>"/> | |
| <input type="hidden" name="unstoringInfo[<?= $k ?>][mainFl]" value="<?= $data['unstoringInfo'][$k]['mainFl'] ?>"/> | |
| <table class="table table-cols" value="<?= $v['sno'] ?>"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="same" onclick="display_toggle('returnFl','same');" <?= gd_isset($checked['returnFl']['same']); ?> />사업장 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="unstoring" onclick="display_toggle('returnFl','same');" <?= gd_isset($checked['returnFl']['unstoring']); ?> />출고지 주소와 동일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="returnFl" value="new" onclick="display_toggle('returnFl','new');" <?= gd_isset($checked['returnFl']['new']); ?> />주소 등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="button" value="반품/교환지 관리" class="btn btn-black btn-sm btn-return" onclick="address_register('반품/교환지', 'returnFl', 'returnFl_new', 'returnTable');"/> | |
| <div class="form-inline mgt10 mgb5 returnTable <?= empty($data['returnInfo']) === false ? 'active' : '' ?>"> | |
| <?php foreach ($data['returnInfo'] as $k => $v) { ?> | |
| <?php if ($v['addressFl'] == 'return') { ?> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnNo]" value="<?= $data['returnInfo'][$k]['sno'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnZonecode]" value="<?= $data['returnInfo'][$k]['unstoringZonecode'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnZipcode]" value="<?= $data['returnInfo'][$k]['unstoringZipcode'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnAddress]" value="<?= $data['returnInfo'][$k]['unstoringAddress'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][returnAddressSub]" value="<?= $data['returnInfo'][$k]['unstoringAddressSub'] ?>"/> | |
| <input type="hidden" name="returnInfo[<?= $k ?>][mainFl]" value="<?= $data['returnInfo'][$k]['mainFl'] ?>"/> | |
| <table class="table table-cols" value="<?= $v['sno'] ?>"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr> | |
| <th>대표전화</th> | |
| <td class="form-inline"> | |
| <input type="text" name="phone" value="<?= $data['phone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </td> | |
| <th>팩스번호</th> | |
| <td class="form-inline"> | |
| <input type="text" name="fax" value="<?= $data['fax']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>통신판매신고번호</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="text" name="onlineOrderSerial" value="<?= $data['onlineOrderSerial']; ?>" class="form-control width-sm"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>전자상거래업자 부호</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="text" name="ecommerceBusinessCode" value="<?= $data['ecommerceBusinessCode']; ?>" <?= $readonly . $disabled; ?> maxlength="30" class="form-control width-sm"/> | |
| <div class="notice-info"> | |
| 전자상거래업자 부호는 관세청 UNI-PASS 에서 신청/발급받을 수 있습니다. | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>인감 이미지 등록</th> | |
| <td colspan="3" class="form-inline"> | |
| <input type="file" name="stampImage" <?= $disabled; ?> class="form-control"/> | |
| <span> | |
| <?php | |
| if (empty($data['stampImage']) === false) { | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="default" onclick="display_toggle('receiptFl','default');" <?= gd_isset($checked['receiptFl']['default']); ?> />현금영수증 가맹점 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="liability" onclick="display_toggle('receiptFl','liability');" <?= gd_isset($checked['receiptFl']['liability']); ?> />현금영수증 의무발행 가맹점 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="upload" onclick="display_toggle('receiptFl','upload');" <?= gd_isset($checked['receiptFl']['upload']); ?> />노출 이미지 직접등록 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="receiptFl" value="n" onclick="display_toggle('receiptFl','n');" <?= gd_isset($checked['receiptFl']['n']); ?> />노출 안함 | |
| </label> | |
| </div> | |
| <div id="receiptFl_default" class="mgt10"> | |
| <img src="/admin/gd_share/img/logo_cash_receipt_liability.png" alt="현금영수증 의무발행 가맹점"> | |
| </div> | |
| <div id="receiptFl_upload" class="mgt10"> | |
| <input type="file" name="receiptImageFile" <?= $disabled; ?> class="form-control"/> | |
| <div> | |
| <?php | |
| if (empty($data['receiptImage']) === false) { | |
| echo gd_select_box('centerPhoneHead', 'centerPhoneHead', $countryAddress, null, $data['centerPhoneHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerPhone" value="<?= $data['centerPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </div> | |
| <div> | |
| <?php | |
| echo gd_select_box('centerSubPhoneHead', 'centerSubPhoneHead', $countryAddress, null, $data['centerSubPhoneHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerSubPhone" value="<?= $data['centerSubPhone']; ?>" maxlength="15" class="form-control js-number-only width-md"/> | |
| </div> | |
| </td> | |
| <th>팩스번호</th> | |
| echo gd_select_box('centerFaxHead', 'centerFaxHead', $countryAddress, null, $data['centerFaxHead'], '==선택==', null, 'form-control'); | |
| } | |
| ?> | |
| <input type="text" name="centerFax" value="<?= $data['centerFax']; ?>" maxlength="15" class="form-control js-number-only width-md"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td colspan="3" class="form-inline"> | |
| <div class="form-inline"> | |
| <label class="radio-inline"> | |
| <input type="radio" name="centerEmailDefaultFl" value="default" onclick="default_toggle_email_info('centerEmail', true);" <?= gd_isset($checked['centerEmail']['default']); ?> /> 기본 이메일 | |
| </label> | |
| <label class="radio-inline"> | |
| <input type="radio" name="centerEmailDefaultFl" value="manual" onclick="default_toggle_email_info('centerEmail', false);" <?= gd_isset($checked['centerEmail']['manual']); ?> /> 직접 입력 | |
| </label> | |
| </div><br> | |
| <input type="text" id="centerEmailId" name="centerEmail[]" value="<?= $data['centerEmail'][0]; ?>" class="form-control width-lg" <?= gd_isset($deactivated['centerEmail']); ?>/> @ | |
| <input type="text" id="centerEmail" name="centerEmail[]" value="<?= $data['centerEmail'][1]; ?>" class="form-control width-md js-email-domain" <?= gd_isset($deactivated['centerEmail']); ?> /> | |
| <div id="centerEmail_domain_y" class="display-inline-block"> | |
| <?= gd_select_box('center_email_domain', null, $emailDomain, null, $data['centerEmail'][1], null, null, 'email_domain'); ?> | |
| </div> | |
| <div id="centerEmail_domain_n"></div> | |
| </td> | |
| <tr> | |
| <th>운영시간</th> | |
| <td colspan="3" class="form-inline"> | |
| <textarea name="centerHours" rows="4" class="form-control width-2xl"><?= $data['centerHours']; ?></textarea> | |
| </td> | |
| </tr> | |
| </table> | |
| <tr> | |
| <th>회사소개 내용</th> | |
| <td class="form-inline"> | |
| <textarea name="company" id="editor" class="form-control width100p height400"><?= $data['company']; ?></textarea> | |
| </td> | |
| </tr> | |
| </table> | |
| <script type="text/javascript"> | |
| <!-- | |
| var unstoringNo = <?= json_encode($data['unstoringInfo']) ?>; | |
| var returnNo = <?= json_encode($data['returnInfo']) ?>; | |
| var $mallName = '<?= $mallName ?>'; | |
| var $mallFl = '<?= $mallFl; ?>'; | |
| var unstoringFl = '<?= empty($data['unstoringInfo']) ?>'; | |
| var returnFl = '<?= empty($data['returnInfo']) ?>'; | |
| var isOauthSuperManagerLoggedIn = <?= $isOauthSuperManagerLoggedIn ? 'true' : 'false'; ?>; | |
| } | |
| }); | |
| display_toggle('unstoringFl','<?= gd_isset($data['unstoringFl']);?>'); | |
| display_toggle('returnFl','<?= gd_isset($data['returnFl']);?>'); | |
| display_toggle('robotsTxt','<?= gd_isset($data['robotsFl']);?>'); | |
| display_toggle('receiptFl','<?= gd_isset($data['receiptFl']);?>'); | |
| display_toggle_inline('email_domain', '<?= gd_isset($data['emailFl']);?>'); | |
| display_toggle_inline('centerEmail_domain', '<?= gd_isset($data['centerEmailFl']);?>'); | |
| $('.email_domain').change(function () { | |
| var val = $(this).val() == 'self' ? '' : $(this).val(); | |
| 'unstoringNo' : addressNo, | |
| 'mallName' : $mallName, | |
| 'mallFl' : $mallFl, | |
| 'unstoringFl' : '<?= gd_isset($data['unstoringFl']);?>', | |
| 'returnFl' : '<?= gd_isset($data['returnFl']);?>', | |
| 'mallSno' : '<?= $mallSno;?>' | |
| }; | |
| $.ajax({ | |
변경 전BuyerInform.php
| // 저장할 필드 | |
| //@formatter:off | |
| $arrSaveData = ['companyNm', 'businessNo', 'service', 'item', 'email', 'zonecode', 'zipcode', 'address', 'addressSub', 'phone', 'fax', 'onlineOrderSerial', 'unstoringZonecode', 'unstoringZipcode', 'unstoringAddress', 'unstoringAddressSub', 'returnZonecode', 'returnZipcode', 'returnAddress', 'returnAddressSub',]; | |
| //@formatter:on | |
| // 데이터 조합 | |
변경 후BuyerInform.php
| // 저장할 필드 | |
| //@formatter:off | |
| $arrSaveData = ['companyNm', 'businessNo', 'service', 'item', 'email', 'zonecode', 'zipcode', 'address', 'addressSub', 'phone', 'fax', 'onlineOrderSerial', 'ecommerceBusinessCode', 'unstoringZonecode', 'unstoringZipcode', 'unstoringAddress', 'unstoringAddressSub', 'returnZonecode', 'returnZipcode', 'returnAddress', 'returnAddressSub',]; | |
| //@formatter:on | |
| // 데이터 조합 | |
변경 전DBTableField.php
| ['val' => 'businessNm', 'typ' => 's', 'def' => null, 'name' => '상호명'], // 상호명 | |
| ['val' => 'businessLicenseImage', 'typ' => 's', 'def' => null, 'name' => '사업자등록증이미지'], // 사업자등록증이미지 | |
| ['val' => 'onlineOrderSerial', 'typ' => 's', 'def' => null, 'name' => '통신판매 신고번호'], // 통신 판매 신고 번호 | |
| ['val' => 'service', 'typ' => 's', 'def' => null, 'name' => '업태'], // 업태 | |
| ['val' => 'item', 'typ' => 's', 'def' => null, 'name' => '종목'], // 종목 | |
| ['val' => 'ceoNm', 'typ' => 's', 'def' => null, 'name' => '대표자'], // 대표자 | |
변경 후DBTableField.php
| ['val' => 'businessNm', 'typ' => 's', 'def' => null, 'name' => '상호명'], // 상호명 | |
| ['val' => 'businessLicenseImage', 'typ' => 's', 'def' => null, 'name' => '사업자등록증이미지'], // 사업자등록증이미지 | |
| ['val' => 'onlineOrderSerial', 'typ' => 's', 'def' => null, 'name' => '통신판매 신고번호'], // 통신 판매 신고 번호 | |
| ['val' => 'ecommerceBusinessCode', 'typ' => 's', 'def' => null, 'name' => '전자상거래업체부호'], // 전자상거래업체부호 | |
| ['val' => 'service', 'typ' => 's', 'def' => null, 'name' => '업태'], // 업태 | |
| ['val' => 'item', 'typ' => 's', 'def' => null, 'name' => '종목'], // 종목 | |
| ['val' => 'ceoNm', 'typ' => 's', 'def' => null, 'name' => '대표자'], // 대표자 | |
변경 전Mall.php
| 'item', | |
| 'email', | |
| 'stampImage', | |
| ]; | |
| /** 설정 공통 사용 **/ | |
| const GLOBAL_MALL_SHARE = [ | |
변경 후Mall.php
| 'item', | |
| 'email', | |
| 'stampImage', | |
| 'ecommerceBusinessCode', | |
| ]; | |
| /** 설정 공통 사용 **/ | |
| const GLOBAL_MALL_SHARE = [ | |
변경 전Policy.php
| 'phone', | |
| 'fax', | |
| 'onlineOrderSerial', | |
| 'unstoringNo', | |
| 'unstoringZonecode', | |
| 'unstoringZipcode', | |
변경 후Policy.php
| 'phone', | |
| 'fax', | |
| 'onlineOrderSerial', | |
| 'ecommerceBusinessCode', | |
| 'unstoringNo', | |
| 'unstoringZonecode', | |
| 'unstoringZipcode', | |
변경 전GoodsAdmin.php
| // 상품이미지 > obs 카프카 컨슈머 처리 start (상품복사) | |
| if($arrData['applyGoodsCopy'] && gd_in_array($arrData['imageStorage'], self::DEFAULT_STORAGE)) { | |
| $copiedGoodsData = ['goodsNo' => $arrData['applyNo'], 'newGoodsNo' => $arrData['goodsNo'], 'convertedType' => 'copy']; | |
| $kafka = new ProducerUtils(); | |
| $result = $kafka->send($kafka::TOPIC_CONVERTED_GOODS_IMAGE, $kafka->makeData($copiedGoodsData, 'cgi'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, $copiedGoodsData]); | |
| if ($result['result'] == 'success') { | |
| // originGoodsNo, newGoodsNo obsConvertFl = N 업데이트 필요 | |
| $goodsObsConvertStatus->updateObsConvertFl($arrData['applyNo']); | |
| $goodsObsConvertStatus->updateObsConvertFl($arrData['goodsNo']); | |
| } | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| if (!empty($goodsImageMigrationLog)) { | |
| $goodsImageMigrationLog['convertedType'] = 'manage'; // 상품이미지 등록,수정,삭제 | |
| $kafka = new ProducerUtils(); | |
| $result = $kafka->send($kafka::TOPIC_CONVERTED_GOODS_IMAGE, $kafka->makeData($goodsImageMigrationLog, 'cgi'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, $goodsImageMigrationLog]); | |
| if ($result['result'] == 'success') { | |
| // goodsNo obsConvertFl = N 업데이트 필요 | |
| $goodsObsConvertStatus->updateObsConvertFl($arrData['goodsNo'], $goodsImageMigrationLog); | |
| } | |
| } | |
| } | |
| // 상품아이콘 > obs 카프카 컨슈머 처리 end | |
| // URL 이미지는 OBS 변환 대상 아님 → 'y' 고정 (legacy 'n' 잠금 해제 포함) | |
| if (gd_in_array($arrData['imageStorage'], self::DEFAULT_STORAGE)) { | |
| if ($arrData['applyGoodsCopy']) { | |
| $goodsObsConvertStatus->setObsConvertFlagForUrlImages($arrData['applyNo']); | |
| if ($extraArrData['callBy'] == OPEN_API) { | |
| $goodsImageMigrationLog['convertedType'] = 'manage'; // 상품이미지 등록,수정,삭제 | |
| $kafka = new ProducerUtils(); | |
| $result = $kafka->send($kafka::TOPIC_CONVERTED_GOODS_IMAGE_BY_OPEN_API, $kafka->makeData($goodsImageMigrationLog, 'cgi'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, 'openAPI', $goodsImageMigrationLog]); | |
| if ($result['result'] == 'success') { | |
| // goodsNo obsConvertFl = N 업데이트 필요 | |
| $goodsObsConvertStatus->updateObsConvertFl($this->goodsNo, $goodsImageMigrationLog); | |
| } | |
| } | |
| // URL 이미지는 OBS 변환 대상 아님 → 'y' 고정 (legacy 'n' 잠금 해제 포함) | |
| $goodsObsConvertStatus->setObsConvertFlagForUrlImages($this->goodsNo); | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| return $fbImageInfo; | |
| } | |
| /** | |
| * 상품 복사 | |
| * | |
| $addLogData = $goodsNo . ' -> ' . $newGoodsNo . ' 상품 복사' . chr(10); | |
| LogHandler::wholeLog('goods', null, 'copy', $newGoodsNo, $data['goodsNm'], $addLogData); | |
| // 상품이미지 > obs 카프카 컨슈머 처리 start | |
| if(gd_in_array($data['imageStorage'], self::DEFAULT_STORAGE)) { | |
| $copiedGoodsData = ['goodsNo' => $goodsNo, 'newGoodsNo' => $newGoodsNo, 'convertedType' => 'copy']; | |
| $kafka = new ProducerUtils(); | |
| $result = $kafka->send($kafka::TOPIC_CONVERTED_GOODS_IMAGE, $kafka->makeData($copiedGoodsData, 'cgi'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, $copiedGoodsData]); | |
| if ($result['result'] == 'success') { | |
| // originGoodsNo, newGoodsNo obsConvertFl = N 업데이트 필요 | |
| $goodsObsConvertStatus = \App::load('\\Component\\Goods\\GoodsObsConvertStatus'); | |
| $goodsObsConvertStatus->updateObsConvertFl($goodsNo); | |
| $goodsObsConvertStatus->updateObsConvertFl($newGoodsNo); | |
| } | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| // URL 이미지는 OBS 변환 대상 아님 → 'y' 고정 (legacy 'n' 잠금 해제 포함) | |
| if (gd_in_array($data['imageStorage'], self::DEFAULT_STORAGE)) { | |
| $goodsObsConvertStatusForUrl = \App::load('\\Component\\Goods\\GoodsObsConvertStatus'); | |
| $goodsObsConvertStatusForUrl->setObsConvertFlagForUrlImages($goodsNo); | |
| $goodsObsConvertStatusForUrl->setObsConvertFlagForUrlImages($newGoodsNo); | |
| } | |
| // --- 이미지 복사 처리 | |
| if($data['imageStorage'] !='url') { | |
| if ($data['imageStorage'] == 'obs') { | |
| $this->db->commit(); | |
| // 에디터 이미지 복사 토픽 발행 | |
| $kafka = new ProducerUtils(); | |
| $contentsInfo = ['contentsKey' => $newGoodsNo, 'tableName' => DB_GOODS, 'orgContentsKey' => $goodsNo, 'orgTableName' => DB_GOODS]; | |
| $result = $kafka->send($kafka::TOPIC_COPIED_EDITOR_IMAGE, $kafka->makeData($contentsInfo, 'cpei'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, $contentsInfo]); | |
| return $newGoodsNo; | |
| } catch (\Exception $e) { | |
변경 후GoodsAdmin.php
| // 상품이미지 > obs 카프카 컨슈머 처리 start (상품복사) | |
| if($arrData['applyGoodsCopy'] && gd_in_array($arrData['imageStorage'], self::DEFAULT_STORAGE)) { | |
| // 신규 상품을 변환 대기(obsConvertFl='n')로 표시 | |
| $goodsObsConvertStatus->updateObsConvertFl($arrData['goodsNo']); | |
| $copiedGoodsData = ['goodsNo' => $arrData['applyNo'], 'newGoodsNo' => $arrData['goodsNo'], 'convertedType' => 'copy']; | |
| $this->publishObsConvertTopic($goodsObsConvertStatus, ProducerUtils::TOPIC_CONVERTED_GOODS_IMAGE, $copiedGoodsData, $arrData['goodsNo'], '상품복사 OBS 토픽 발행 실패 - obsConvertFl 해제(fail-open)'); | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| if (!empty($goodsImageMigrationLog)) { | |
| $goodsImageMigrationLog['convertedType'] = 'manage'; // 상품이미지 등록,수정,삭제 | |
| // 상품 이미지를 변환 대기(obsConvertFl='n')로 표시 | |
| $goodsObsConvertStatus->updateObsConvertFl($arrData['goodsNo'], $goodsImageMigrationLog); | |
| $this->publishObsConvertTopic($goodsObsConvertStatus, ProducerUtils::TOPIC_CONVERTED_GOODS_IMAGE, $goodsImageMigrationLog, $arrData['goodsNo'], '상품이미지 OBS 토픽 발행 실패 - obsConvertFl 해제(fail-open)'); | |
| } | |
| } | |
| // 상품아이콘 > obs 카프카 컨슈머 처리 end | |
| // URL 이미지는 변환 대상이 아니므로 'y'로 고정 | |
| if (gd_in_array($arrData['imageStorage'], self::DEFAULT_STORAGE)) { | |
| if ($arrData['applyGoodsCopy']) { | |
| $goodsObsConvertStatus->setObsConvertFlagForUrlImages($arrData['applyNo']); | |
| if ($extraArrData['callBy'] == OPEN_API) { | |
| $goodsImageMigrationLog['convertedType'] = 'manage'; // 상품이미지 등록,수정,삭제 | |
| // 상품 이미지를 변환 대기(obsConvertFl='n')로 표시 | |
| $goodsObsConvertStatus->updateObsConvertFl($this->goodsNo, $goodsImageMigrationLog); | |
| $this->publishObsConvertTopic($goodsObsConvertStatus, ProducerUtils::TOPIC_CONVERTED_GOODS_IMAGE_BY_OPEN_API, $goodsImageMigrationLog, $this->goodsNo, '상품이미지 OBS 토픽 발행 실패(openAPI) - obsConvertFl 해제(fail-open)', ['openAPI']); | |
| } | |
| // URL 이미지는 변환 대상이 아니므로 'y'로 고정 | |
| $goodsObsConvertStatus->setObsConvertFlagForUrlImages($this->goodsNo); | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| return $fbImageInfo; | |
| } | |
| /** | |
| * OBS 이미지 변환 토픽 발행 + 발행 실패 시 fail-open 처리 | |
| * | |
| * 발행에 실패(예외 발생 또는 비성공 응답)하면 obsConvertFl을 'y'로 되돌려 | |
| * 이미지가 '변환 대기(편집 잠금)' 상태로 고착되는 것을 방지한다. | |
| * 잠금(updateObsConvertFl) 자체는 발행 대상/시점이 달라 호출부에서 수행한다. | |
| * | |
| * @param \Component\Goods\GoodsObsConvertStatus $goodsObsConvertStatus 상태 처리 컴포넌트 | |
| * @param string $topic 발행 토픽 | |
| * @param array $data 발행 데이터(makeData 입력) | |
| * @param mixed $unlockGoodsNo 발행 실패 시 잠금 해제할 상품번호 | |
| * @param string $failMessage 발행 실패 시 남길 로그 메시지 | |
| * @param array $infoLogContext info 로그에 추가로 남길 항목(예: ['openAPI']) | |
| * @return void | |
| */ | |
| private function publishObsConvertTopic($goodsObsConvertStatus, string $topic, array $data, $unlockGoodsNo, string $failMessage, array $infoLogContext = []): void | |
| { | |
| $obsPublishOk = false; | |
| $obsPublishError = ''; | |
| try { | |
| $kafka = new ProducerUtils(); | |
| $result = $kafka->send($topic, $kafka->makeData($data, 'cgi'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge([$result], $infoLogContext, [$data])); | |
| $obsPublishOk = (isset($result['result']) && $result['result'] === 'success'); | |
| } catch (\Throwable $obsPublishException) { | |
| $obsPublishError = $obsPublishException->getMessage(); | |
| } | |
| if (!$obsPublishOk) { | |
| // 발행 실패 → obsConvertFl 'y'로 재변환 | |
| // 미변환 시, 이미지 변경 불가능 상태로 고착화됨 | |
| $goodsObsConvertStatus->unlockObsConvertFl($unlockGoodsNo); | |
| \Logger::channel('kafka')->warning($failMessage, [$unlockGoodsNo, $obsPublishError]); | |
| } | |
| } | |
| /** | |
| * 상품 복사 | |
| * | |
| $addLogData = $goodsNo . ' -> ' . $newGoodsNo . ' 상품 복사' . chr(10); | |
| LogHandler::wholeLog('goods', null, 'copy', $newGoodsNo, $data['goodsNm'], $addLogData); | |
| // --- 이미지 복사 처리 | |
| if($data['imageStorage'] !='url') { | |
| if ($data['imageStorage'] == 'obs') { | |
| $this->db->commit(); | |
| try { | |
| if(gd_in_array($data['imageStorage'], self::DEFAULT_STORAGE)) { | |
| // 신규 상품을 변환 대기(obsConvertFl='n')로 표시 | |
| $goodsObsConvertStatus = \App::load('\\Component\\Goods\\GoodsObsConvertStatus'); | |
| $goodsObsConvertStatus->updateObsConvertFl($newGoodsNo); | |
| $copiedGoodsData = ['goodsNo' => $goodsNo, 'newGoodsNo' => $newGoodsNo, 'convertedType' => 'copy']; | |
| $this->publishObsConvertTopic($goodsObsConvertStatus, ProducerUtils::TOPIC_CONVERTED_GOODS_IMAGE, $copiedGoodsData, $newGoodsNo, '상품복사 OBS 토픽 발행 실패 - obsConvertFl 해제(fail-open)'); | |
| } | |
| // 상품이미지 > obs 카프카 컨슈머 처리 end | |
| // URL 이미지는 변환 대상이 아니므로 'y'로 고정 | |
| if (gd_in_array($data['imageStorage'], self::DEFAULT_STORAGE)) { | |
| $goodsObsConvertStatusForUrl = \App::load('\\Component\\Goods\\GoodsObsConvertStatus'); | |
| $goodsObsConvertStatusForUrl->setObsConvertFlagForUrlImages($newGoodsNo); | |
| } | |
| // 에디터 이미지 복사 토픽 발행 | |
| $kafka = new ProducerUtils(); | |
| $contentsInfo = ['contentsKey' => $newGoodsNo, 'tableName' => DB_GOODS, 'orgContentsKey' => $goodsNo, 'orgTableName' => DB_GOODS]; | |
| $result = $kafka->send($kafka::TOPIC_COPIED_EDITOR_IMAGE, $kafka->makeData($contentsInfo, 'cpei'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', [$result, $contentsInfo]); | |
| } catch (\Throwable $postCommitException) { | |
| // 이미지 로컬 복사는 성공. 토픽 처리 실패만 기록하고 넘어간다. | |
| \Logger::channel('kafka')->warning('상품복사 commit 이후 부가처리 실패', [$postCommitException->getMessage(), $newGoodsNo]); | |
| } | |
| return $newGoodsNo; | |
| } catch (\Exception $e) { | |
변경 전GoodsObsConvertStatus.php
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| $this->db->bind_query($strSQL, $arrBind); | |
| } | |
| } | |
변경 후GoodsObsConvertStatus.php
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| $this->db->bind_query($strSQL, $arrBind); | |
| } | |
| /** | |
| * 상품번호를 기준으로 상품 이미지의 변환 대기 표시(obsConvertFl='n')를 풀어줌('y'). | |
| */ | |
| public function unlockObsConvertFl(int $goodsNo): void | |
| { | |
| // 상품이미지 | |
| $arrBind = []; | |
| $strSQL = 'UPDATE ' . DB_GOODS_IMAGE . ' SET obsConvertFl = ? WHERE goodsNo = ? AND imageName NOT LIKE "http%" '; | |
| $this->db->bind_param_push($arrBind, 's', 'y'); | |
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| $this->db->bind_query($strSQL, $arrBind); | |
| // 상품옵션이미지 | |
| $arrBind = []; | |
| $strSQL = 'UPDATE ' . DB_GOODS_OPTION_ICON . ' SET obsConvertFl = ? WHERE goodsNo = ? AND goodsImage NOT LIKE "http%" '; | |
| $this->db->bind_param_push($arrBind, 's', 'y'); | |
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| $this->db->bind_query($strSQL, $arrBind); | |
| } | |
| } | |
변경 전OrderPsController.php
| $postValue = Request::post()->toArray(); | |
| try { | |
| //회원의 장바구니 로드 | |
| $cart = new CartAdmin($postValue['memNo'], true); | |
| $cartSaveInfo = array(); | |
| foreach($postValue['cartSno'] as $key => $cartIdx){ | |
| $cartInfo = array(); | |
| $cartInfo = $cart->getCartGoodsData($cartIdx); | |
| if(gd_count($cartInfo) > 0){ | |
| } | |
| } | |
| } | |
| } | |
| //order write cart 에 저장 | |
| $cart = null; | |
| unset($cart); | |
| if(gd_count($cartSaveInfo) > 0){ | |
| $cart = new CartAdmin($postValue['memNo']); | |
| $resultCartSno = $cart->saveInfoMemberCartAdmin($cartSaveInfo); | |
변경 후OrderPsController.php
| $postValue = Request::post()->toArray(); | |
| try { | |
| $cartSaveInfo = array(); | |
| foreach($postValue['cartSno'] as $key => $cartIdx){ | |
| $cart = new CartAdmin($postValue['memNo'], true); | |
| $cartInfo = array(); | |
| $cartInfo = $cart->getCartGoodsData($cartIdx); | |
| if(gd_count($cartInfo) > 0){ | |
| } | |
| } | |
| } | |
| $cart = null; | |
| unset($cart); | |
| } | |
| //order write cart 에 저장 | |
| if(gd_count($cartSaveInfo) > 0){ | |
| $cart = new CartAdmin($postValue['memNo']); | |
| $resultCartSno = $cart->saveInfoMemberCartAdmin($cartSaveInfo); | |
변경 전Cart.php
| return false; | |
| } | |
| $arrBind = []; | |
| foreach ($getData as $cartSno) { | |
| // 장바구니 삭제 전 쿠폰 장바구니사용 상태 되돌리기 | |
변경 후Cart.php
| return false; | |
| } | |
| // [데드락 회피] sno IN 다건 삭제 시 PK 잠금 순서를 오름차순으로 통일 | |
| // 삭제 대상 집합은 동일하며(동작 불변), 동시 트랜잭션 간 PK 획득 순서만 일치시켜 교착 순환을 제거 | |
| sort($getData, SORT_NUMERIC); | |
| $arrBind = []; | |
| foreach ($getData as $cartSno) { | |
| // 장바구니 삭제 전 쿠폰 장바구니사용 상태 되돌리기 | |
변경 전MailLog.php
| protected $arrWhere; | |
| /** @var int es_mailLog sno */ | |
| private $_sno; | |
| /** @var SimpleStorage */ | |
| private $requestStorage; | |
| $arrBind = $this->db->get_binding(DBTableField::tableMailSendList(), $bindArray, 'insert'); | |
| $this->db->set_insert_db(DB_MAIL_SEND_LIST, $arrBind['param'], $arrBind['bind'], 'y'); | |
| } | |
| /** | |
| */ | |
| public function updateSendList($email) | |
| { | |
| $this->db->bind_param_push($arrBind, 's', $this->_sno); | |
| $this->db->bind_param_push($arrBind, 's', $email); | |
| $this->db->set_update_db(DB_MAIL_SEND_LIST, 'acceptCheckFl = \'y\'', 'mailLogSno = ? AND receiverEmail = ? AND acceptCheckFl = \'n\' LIMIT 1', $arrBind); | |
변경 후MailLog.php
| protected $arrWhere; | |
| /** @var int es_mailLog sno */ | |
| private $_sno; | |
| /** @var int|null 직전 insertMailSendList 로 INSERT 한 es_mailSendList 의 PK(sno). updateSendList PK 단건 갱신용 */ | |
| private $sendListSno = null; | |
| /** @var SimpleStorage */ | |
| private $requestStorage; | |
| $arrBind = $this->db->get_binding(DBTableField::tableMailSendList(), $bindArray, 'insert'); | |
| $this->db->set_insert_db(DB_MAIL_SEND_LIST, $arrBind['param'], $arrBind['bind'], 'y'); | |
| // 방금 INSERT 한 발송내역 PK 를 보관 → updateSendList 에서 PK 단건 갱신(데드락 회피)에 사용 | |
| $this->sendListSno = (int) $this->db->insert_id(); | |
| } | |
| /** | |
| */ | |
| public function updateSendList($email) | |
| { | |
| // [데드락 회피] 직전 insertMailSendList 로 확보한 PK(sno)가 있으면 PK 단건 UPDATE | |
| // 보조인덱스(ix_es_mailSendList_05) 스캔을 제거해 잠금 순서를 PRIMARY 기준으로 통일 | |
| // acceptCheckFl='n' 가드를 유지해 멱등성을 보장(기존 동작과 동일하게 미접수 건만 갱신) | |
| // 사용 후 즉시 초기화하여, INSERT 가 선행되지 않은 호출은 아래 기존 로직으로 처리 | |
| if (!empty($this->sendListSno)) { | |
| $sendListSno = $this->sendListSno; | |
| $this->sendListSno = null; | |
| $arrBind = []; | |
| $this->db->bind_param_push($arrBind, 'i', $sendListSno); | |
| $this->db->set_update_db(DB_MAIL_SEND_LIST, 'acceptCheckFl = \'y\'', 'sno = ? AND acceptCheckFl = \'n\'', $arrBind); | |
| return; | |
| } | |
| // (fallback) PK 미확보 호출 경로 — 기존 동작 100% 유지 | |
| $arrBind = []; | |
| $this->db->bind_param_push($arrBind, 's', $this->_sno); | |
| $this->db->bind_param_push($arrBind, 's', $email); | |
| $this->db->set_update_db(DB_MAIL_SEND_LIST, 'acceptCheckFl = \'y\'', 'mailLogSno = ? AND receiverEmail = ? AND acceptCheckFl = \'n\' LIMIT 1', $arrBind); | |
변경 전CartPsController.php
| } | |
| // 처리별 이동 경로 | |
| if (gd_isset($postValue['cartMode']) == 'd') { | |
| $goodsList = gd_implode(',', $addedGoods); | |
| $goodsListQuote = gd_implode(',', $addedGoodsQuote); | |
| //장바구니에서 보이지 않도록 조치 시작 | |
| $this->db = \App::load('DB'); | |
| $where = 'sno IN (\''.str_replace(',', '\',\'', $goodsList.'\')'); | |
| $arrUpdate['directCart'] = 'y'; | |
| $arrBind = $this->db->get_binding(DBTableField::tableCart(), $arrUpdate, 'update', gd_array_keys($arrUpdate)); | |
| $this->db->set_update_db(DB_CART, $arrBind['param'], $where, $arrBind['bind']); | |
| //장바구니에서 보이지 않도록 조치 종료 | |
| $goodsListQuote = '['.$goodsListQuote.']'; | |
변경 후CartPsController.php
| } | |
| // 처리별 이동 경로 | |
| if (gd_isset($postValue['cartMode']) == 'd') { | |
| $goodsListQuote = implode(',', $addedGoodsQuote); | |
| //장바구니에서 보이지 않도록 조치 시작 | |
| $this->db = \App::load('DB'); | |
| // [데드락 회피] UPDATE WHERE 의 sno IN 잠금 순서만 오름차순 통일(대상 행 동일) | |
| // URL(cartIdx) 노출 순서에는 영향을 주지 않도록 정렬 복사본을 별도 생성 | |
| $sortedAddedGoods = $addedGoods; | |
| sort($sortedAddedGoods, SORT_NUMERIC); | |
| if (!empty($sortedAddedGoods)) { | |
| $arrUpdate['directCart'] = 'y'; | |
| $arrBind = $this->db->get_binding(DBTableField::tableCart(), $arrUpdate, 'update', gd_array_keys($arrUpdate)); | |
| // sno IN 값은 바인딩으로 처리(SQL 인젝션 방지) | |
| $where = 'sno IN (' . implode(', ', array_fill(0, count($sortedAddedGoods), '?')) . ')'; | |
| foreach ($sortedAddedGoods as $sno) { | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $sno); | |
| } | |
| $this->db->set_update_db(DB_CART, $arrBind['param'], $where, $arrBind['bind']); | |
| } | |
| //장바구니에서 보이지 않도록 조치 종료 | |
| $goodsListQuote = '['.$goodsListQuote.']'; | |
변경 전Board.php
| } else { | |
| $extraData['arrGoodsNo'][] = $extraData['goodsNoText']; | |
| } | |
| foreach ($extraData['arrGoodsNo'] as $key => $goodsNo) { | |
| $extraData['arrGoodsData'][$key] = $goods->getGoodsInfo($goodsNo); | |
| } | |
| } | |
| if ($extraData['orderGoodsNoText']) { | |
| $extraData['arrOrderGoodsNo'][] = $extraData['orderGoodsNoText']; | |
| } | |
| $order = new Order(); | |
| foreach ($extraData['arrOrderGoodsNo'] as $key => $orderGoodsNo) { | |
| $isAddGoods = substr($orderGoodsNo, 0, 1) == 'A' ? true : false; | |
| if ($isAddGoods) { | |
| $orderGoodsNo = substr($orderGoodsNo, 1); | |
| } | |
| $orderGoodsData = $order->getOrderGoods(null, $orderGoodsNo, null, null, null)[0]; | |
| $extraData['arrOrderGoodsData'][$key] = $orderGoodsData; | |
| $_arrOrderGoodsData = $extraData['arrOrderGoodsData'][$key]; | |
| $goodsNo = $_arrOrderGoodsData['goodsNo']; | |
| $extraData['arrOrderGoodsData'][$key]['goodsNo'] = $orderGoodsData['parentGoodsNo']; | |
| $extraData['arrOrderGoodsData'][$key]['totalGoodsPrice'] = $_arrOrderGoodsData['goodsCnt'] * ($addGoodsData['goodsPrice'] + $_arrOrderGoodsData['optionPrice'] + $_arrOrderGoodsData['optionTextPrice']); | |
| } else { | |
| $goodsImage = $goods->getGoodsImage($goodsNo, 'main'); | |
| $goodsInfo = $goods->getGoodsInfo($goodsNo); | |
| $goodsImageSrc = SkinUtils::imageViewStorageConfig($goodsImage[0]['goodsImageStorage'] == 'obs' ? $goodsImage[0]['imageUrl'] : $goodsImage[0]['imageName'], $goodsInfo['imagePath'], $goodsInfo['imageStorage'], 100, 'goods')[0]; | |
| $extraData['arrOrderGoodsData'][$key]['totalGoodsPrice'] = $_arrOrderGoodsData['goodsCnt'] * ($_arrOrderGoodsData['goodsPrice'] + $_arrOrderGoodsData['optionPrice'] + $_arrOrderGoodsData['optionTextPrice']); | |
| } | |
변경 후Board.php
| } else { | |
| $extraData['arrGoodsNo'][] = $extraData['goodsNoText']; | |
| } | |
| // N+1 쿼리 패턴 개선: 연관상품 개별 getGoodsInfo() 반복 -> IN절 배치 1회 | |
| // (user/module Goods 오버라이드가 배치 메서드 미상속 시 fatal 방지 가드) | |
| if (method_exists($goods, 'getGoodsInfoByNos')) { | |
| $batchGoodsData = $goods->getGoodsInfoByNos($extraData['arrGoodsNo']); | |
| foreach ($extraData['arrGoodsNo'] as $key => $goodsNo) { | |
| $extraData['arrGoodsData'][$key] = $batchGoodsData[$goodsNo] ?? []; | |
| } | |
| } else { | |
| foreach ($extraData['arrGoodsNo'] as $key => $goodsNo) { | |
| $extraData['arrGoodsData'][$key] = $goods->getGoodsInfo($goodsNo); | |
| } | |
| } | |
| } | |
| if ($extraData['orderGoodsNoText']) { | |
| $extraData['arrOrderGoodsNo'][] = $extraData['orderGoodsNoText']; | |
| } | |
| $order = new Order(); | |
| // N+1 쿼리 패턴 개선: 주문상품/이미지/정보 개별 반복 조회 -> 루프 진입 전 IN절 배치 선조회 | |
| // add-goods 의 getDataAddGoods 만 per-item 유지. user/module Goods 미상속 시 기존 per-item 폴백 | |
| $useBatch = method_exists($goods, 'getGoodsInfoByNos'); | |
| $orderGoodsMap = $goodsInfoMap = $goodsImageMap = []; | |
| if ($useBatch) { | |
| $snoList = []; | |
| foreach ($extraData['arrOrderGoodsNo'] as $orderGoodsNo) { | |
| $snoList[] = (substr($orderGoodsNo, 0, 1) == 'A') ? substr($orderGoodsNo, 1) : $orderGoodsNo; | |
| } | |
| $orderGoodsList = $order->getOrderGoods(null, $snoList, null, null, null); | |
| if (is_array($orderGoodsList)) { | |
| foreach ($orderGoodsList as $orderGoodsRow) { | |
| $orderGoodsMap[$orderGoodsRow['sno']] = $orderGoodsRow; | |
| } | |
| } | |
| $normalGoodsNos = []; | |
| foreach ($extraData['arrOrderGoodsNo'] as $orderGoodsNo) { | |
| if (substr($orderGoodsNo, 0, 1) == 'A') { | |
| continue; | |
| } | |
| if (isset($orderGoodsMap[$orderGoodsNo]['goodsNo'])) { | |
| $normalGoodsNos[] = $orderGoodsMap[$orderGoodsNo]['goodsNo']; | |
| } | |
| } | |
| $goodsInfoMap = $goods->getGoodsInfoByNos($normalGoodsNos); | |
| $goodsImageMap = $goods->getGoodsImageByNos($normalGoodsNos, 'main'); | |
| } | |
| foreach ($extraData['arrOrderGoodsNo'] as $key => $orderGoodsNo) { | |
| $isAddGoods = substr($orderGoodsNo, 0, 1) == 'A' ? true : false; | |
| if ($isAddGoods) { | |
| $orderGoodsNo = substr($orderGoodsNo, 1); | |
| } | |
| $orderGoodsData = $useBatch | |
| ? ($orderGoodsMap[$orderGoodsNo] ?? []) | |
| : ($order->getOrderGoods(null, $orderGoodsNo, null, null, null)[0] ?? []); | |
| $extraData['arrOrderGoodsData'][$key] = $orderGoodsData; | |
| $_arrOrderGoodsData = $extraData['arrOrderGoodsData'][$key]; | |
| $goodsNo = $_arrOrderGoodsData['goodsNo']; | |
| $extraData['arrOrderGoodsData'][$key]['goodsNo'] = $orderGoodsData['parentGoodsNo']; | |
| $extraData['arrOrderGoodsData'][$key]['totalGoodsPrice'] = $_arrOrderGoodsData['goodsCnt'] * ($addGoodsData['goodsPrice'] + $_arrOrderGoodsData['optionPrice'] + $_arrOrderGoodsData['optionTextPrice']); | |
| } else { | |
| $goodsImage = $useBatch ? ($goodsImageMap[$goodsNo] ?? false) : $goods->getGoodsImage($goodsNo, 'main'); | |
| $goodsInfo = $useBatch ? ($goodsInfoMap[$goodsNo] ?? []) : $goods->getGoodsInfo($goodsNo); | |
| $goodsImageSrc = SkinUtils::imageViewStorageConfig($goodsImage[0]['goodsImageStorage'] == 'obs' ? $goodsImage[0]['imageUrl'] : $goodsImage[0]['imageName'], $goodsInfo['imagePath'], $goodsInfo['imageStorage'], 100, 'goods')[0]; | |
| $extraData['arrOrderGoodsData'][$key]['totalGoodsPrice'] = $_arrOrderGoodsData['goodsCnt'] * ($_arrOrderGoodsData['goodsPrice'] + $_arrOrderGoodsData['optionPrice'] + $_arrOrderGoodsData['optionTextPrice']); | |
| } | |
변경 전Goods.php
| return $infoDataContent; | |
| } | |
| } | |
변경 후Goods.php
| return $infoDataContent; | |
| } | |
| /** | |
| * 상품정보 배치 조회 — getGoodsInfo() 의 IN절 배치판 (게시판 상세보기 N+1 쿼리 패턴 개선) | |
| * | |
| * 단건 getGoodsInfo() 를 goodsNo 건수만큼 반복 호출하던 구간을 IN 절 1회 조회로 대체한다. | |
| * | |
| * 게시판 상세보기 배치 전용 내부 헬퍼. 호출부(Board::getExtraData)가 method_exists 가드로 미구현(튜닝 fork) 환경을 단건 경로로 폴백하므로 외부 튜닝 확장점이 아니다. 시그니처 안정 유지를 권장한다. | |
| * | |
| * @param array $arrGoodsNo 상품번호 배열 | |
| * @param string|null $goodsField 출력 필드 (기본 null = 단건 getGoodsInfo 와 동일 전체 조회). goodsNo 컬럼은 결과 맵의 키이므로 부분 필드 지정 시에도 반드시 포함할 것 | |
| * @return array goodsNo 를 키로 하는 상품정보 맵 [goodsNo => row] | |
| */ | |
| public function getGoodsInfoByNos(array $arrGoodsNo, $goodsField = null): array | |
| { | |
| $arrGoodsNo = array_values(array_unique(array_filter($arrGoodsNo))); | |
| if (empty($arrGoodsNo)) { | |
| return []; | |
| } | |
| $arrBind = null; | |
| $bindQuery = []; | |
| foreach ($arrGoodsNo as $goodsNo) { | |
| $bindQuery[] = '?'; | |
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| } | |
| $inClause = ' g.goodsNo IN (' . implode(',', $bindQuery) . ') '; | |
| if ($this->db->strWhere) { | |
| $this->db->strWhere = $inClause . ' AND ' . $this->db->strWhere; | |
| } else { | |
| $this->db->strWhere = $inClause; | |
| } | |
| if ($goodsField) { | |
| $this->db->strField = $this->db->strField ? $goodsField . ', ' . $this->db->strField : $goodsField; | |
| } | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_GOODS . ' g ' . implode(' ', $query); | |
| $getData = $this->db->secondary()->query_fetch($strSQL, $arrBind); | |
| $goodsInfoMap = []; | |
| foreach ((array) $getData as $row) { | |
| $row = gd_htmlspecialchars_stripslashes($row); | |
| $goodsInfoMap[$row['goodsNo']] = $row; | |
| } | |
| return $goodsInfoMap; | |
| } | |
| /** | |
| * 상품이미지 배치 조회 — getGoodsImage() 의 IN절 배치판 (게시판 상세보기 N+1 쿼리 패턴 개선) | |
| * | |
| * 게시판 상세보기 배치 전용 내부 헬퍼. 호출부(Board::getExtraData)가 method_exists 가드로 미구현(튜닝 fork) 환경을 단건 경로로 폴백하므로 외부 튜닝 확장점이 아니다. 시그니처 안정 유지를 권장한다. | |
| * | |
| * @param array $arrGoodsNo 상품번호 배열 | |
| * @param string $imageKind 이미지 종류 (기본 'main') | |
| * @return array goodsNo 를 키로 하는 이미지행 맵 [goodsNo => [row, ...]] | |
| */ | |
| public function getGoodsImageByNos(array $arrGoodsNo, $imageKind = 'main'): array | |
| { | |
| $arrGoodsNo = array_values(array_unique(array_filter($arrGoodsNo))); | |
| if (empty($arrGoodsNo)) { | |
| return []; | |
| } | |
| $arrBind = null; | |
| $bindQuery = []; | |
| foreach ($arrGoodsNo as $goodsNo) { | |
| $bindQuery[] = '?'; | |
| $this->db->bind_param_push($arrBind, 'i', $goodsNo); | |
| } | |
| $this->db->bind_param_push($arrBind, 's', $imageKind); | |
| // 단건 getGoodsImage 는 goodsNo 를 SELECT 에서 제외하나, 배치는 매핑 키로 필요 → goodsNo 추가 | |
| $arrField = DBTableField::setTableField('tableGoodsImage', null, 'goodsNo'); | |
| $strSQL = "SELECT goodsNo, sno, " . implode(', ', $arrField) . " FROM " . DB_GOODS_IMAGE | |
| . " WHERE goodsNo IN (" . implode(',', $bindQuery) . ") AND imageKind = ? " | |
| . " ORDER BY imageKind ASC, imageNo ASC"; | |
| $getData = $this->db->secondary()->query_fetch($strSQL, $arrBind); | |
| $goodsImageMap = []; | |
| foreach ((array) $getData as $row) { | |
| $row = gd_htmlspecialchars_stripslashes($row); | |
| $goodsImageMap[$row['goodsNo']][] = $row; | |
| } | |
| return $goodsImageMap; | |
| } | |
| } | |
변경 전ReOrderCalculation.php
| private function precheckRestoreAddPayment(string $orderNo, string $claimStatus): array | |
| { | |
| $original = $this->getOrderOriginalDepositMileageData($orderNo, $claimStatus); | |
| $current = $this->getOrderDepositMileageData($orderNo); | |
| // precheck 는 setOrderRestore 진입 직후(member 초기화 이전)에 호출되므로 직접 로드한다. | |
| if (!is_object($this->member)) { | |
| $this->member = \App::load('\\Component\\Member\\Member'); | |
| } | |
| $memInfo = []; | |
| if (is_array($original) && !empty($original['memNo'])) { | |
| $memInfo = $this->member->getMemberInfo($original['memNo']); | |
| } | |
| $exceptMsg = []; | |
| $checks = [ | |
| ['field' => 'useDeposit', 'hold' => 'deposit', 'msg' => '고객이 보유한 예치금 부족'], | |
| ['field' => 'useMileage', 'hold' => 'mileage', 'msg' => '고객이 보유한 마일리지 부족'], | |
| ]; | |
| foreach ($checks as $check) { | |
| $diff = gd_isset($current[$check['field']], 0) - gd_isset($original[$check['field']], 0); | |
| if ($diff === 0) { | |
| continue; | |
| } | |
| if (empty($memInfo) || (gd_isset($memInfo[$check['hold']], 0) - abs($diff) < 0)) { | |
| $exceptMsg[] = $check['msg']; | |
| } | |
| } | |
변경 후ReOrderCalculation.php
| private function precheckRestoreAddPayment(string $orderNo, string $claimStatus): array | |
| { | |
| $original = $this->getOrderOriginalDepositMileageData($orderNo, $claimStatus); | |
| $exceptMsg = []; | |
| if (is_array($original) && !empty($original['memNo'])) { | |
| $current = $this->getOrderDepositMileageData($orderNo); | |
| // precheck 는 setOrderRestore 진입 직후(member 초기화 이전)에 호출되므로 직접 로드한다. | |
| if (!is_object($this->member)) { | |
| $this->member = \App::load('\\Component\\Member\\Member'); | |
| } | |
| $memInfo = $this->member->getMemberInfo($original['memNo']); | |
| $checks = [ | |
| ['field' => 'useDeposit', 'hold' => 'deposit', 'msg' => '고객이 보유한 예치금 부족'], | |
| ['field' => 'useMileage', 'hold' => 'mileage', 'msg' => '고객이 보유한 마일리지 부족'], | |
| ]; | |
| foreach ($checks as $check) { | |
| $diff = ($current[$check['field']] ?? 0) - ($original[$check['field']] ?? 0); | |
| if ($diff === 0) { | |
| continue; | |
| } | |
| if (empty($memInfo) || (($memInfo[$check['hold']] ?? 0) - abs($diff) < 0)) { | |
| $exceptMsg[] = $check['msg']; | |
| } | |
| } | |
| } | |
변경 전order_view.php
| if ($('#tabOrderStatus .nav-tabs li').hasClass('active')) { | |
| $('#tabOrderStatus .nav-tabs li.active a').trigger('show.bs.tab'); | |
| } | |
| if ($('#tabClaimStatus .nav-tabs li').hasClass('active')) { | |
| $('#tabClaimStatus .nav-tabs li.active a').trigger('show.bs.tab'); | |
| } | |
| // 일괄 처리 모드의 체크 | |
변경 후order_view.php
| if ($('#tabOrderStatus .nav-tabs li').hasClass('active')) { | |
| $('#tabOrderStatus .nav-tabs li.active a').trigger('show.bs.tab'); | |
| } | |
| // 클레임 내역은 데이터가 있을 때만 초기 로드 (불필요한 inc_claim_view → getOrderView 호출 방지) | |
| // 데이터가 없으면 초기 AJAX를 생략하고, 탭 클릭 시 기존 show.bs.tab 핸들러로 지연 로드된다. | |
| var hasClaimData = false; | |
| $('#tabClaimStatus .nav-tabs a strong').each(function () { | |
| if (parseInt($(this).text().replace(/[^0-9]/g, ''), 10) > 0) { | |
| hasClaimData = true; | |
| return false; | |
| } | |
| }); | |
| if ($('#tabClaimStatus .nav-tabs li').hasClass('active')) { | |
| var $activeClaimPane = $('#tabClaimStatus .tab-pane.active'); | |
| if (hasClaimData || $activeClaimPane.attr('id') !== 'tab-claim-cancel') { | |
| // 클레임 데이터가 있거나, 활성 탭이 취소탭이 아닌 경우(공급사/교환·반품·환불 등) → 기존대로 AJAX 로드 | |
| $('#tabClaimStatus .nav-tabs li.active a').trigger('show.bs.tab'); | |
| } else { | |
| // 클레임이 전혀 없고 활성 탭이 취소탭인 가장 흔한 케이스 → AJAX 없이 동일한 '취소정보가 없습니다' 빈 상태를 렌더 | |
| $('#tabClaimStatus .tab-content').removeClass('loading'); | |
| $activeClaimPane.html( | |
| '<table class="table table-rows">' | |
| + '<thead><tr>' | |
| + '<th class="width3p">번호</th>' | |
| + '<th class="width5p">처리상태</th>' | |
| + '<th class="width8p">공급사</th>' | |
| + '<th class="width-3xs">이미지</th>' | |
| + '<th>상품</th>' | |
| + '<th class="width5p">취소수량</th>' | |
| + '<th class="width8p">취소사유</th>' | |
| + '<th class="width10p">상세사유</th>' | |
| + '</tr></thead>' | |
| + '<tbody><tr><td class="no-data" colspan="25">취소정보가 없습니다.</td></tr></tbody>' | |
| + '</table>' | |
| ); | |
| } | |
| } | |
| // 일괄 처리 모드의 체크 | |
변경 전OrderAdminNew.php
| use Origin\Enum\Order\OrderStatusGroup; | |
| use Origin\Service\Statistics\Analytics\Order\OrderCompleteAnalyticsCollectorService; | |
| use Origin\Service\WebHook\OrderWebHookService; | |
| use Request; | |
| use Session; | |
| use Component\Page\Page; | |
| $deliveryUniqueKey = ''; | |
| $aCancelDeliveryKey = array(); // 품목 하나가 부분으로 취소되서 품목수가 나뉘어진경우 취소배송비 중복계산이 안되도록 체크하기위해서 orderDeliverySno로 키값 저장&체크 todo 취소부분 환불과 맞추면 다 삭제될 내용 | |
| // 전체 정책별 배송비와 지역별 배송비의 총합 구하기 | |
| foreach ($getData['goods'] as $sKey => $sVal) { | |
| foreach ($sVal as $dKey => $dVal) { | |
| // $dVal = array_shift($dVal); | |
| foreach ($dVal as $gKey => $gVal) { | |
| $realDeliveryTaxSum = gd_array_sum([ | |
| $gVal['realTaxSupplyDeliveryCharge'], | |
| $gVal['realTaxVatDeliveryCharge'], | |
| $gVal['realTaxFreeDeliveryCharge'], | |
| ]); | |
| // 전체 배송비 계산 (주문상품추가/주문타상품교환으로 같은 배송비 조건(deliverySno)이더라도 주문배송비(orderDeliverySno)가 추가된다 | |
| $deliveryKeyCheck = $gVal['deliverySno'] . '-' . $gVal['orderDeliverySno']; | |
| if ($deliveryKeyCheck == $deliveryUniqueKey) { | |
| if (gd_in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (!gd_in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 | |
| $aHandleInfo = $this->getOrderHandleInfo($gVal['handleSno']); | |
| $aDeliveryOriginalInfo = $this->getOrderDeliveryOriginal($gVal['orderDeliverySno']); | |
| $getData['dashBoardPrice']['cancelPrice'] += $aHandleInfo['refundDeliveryCharge'] - ($aDeliveryOriginalInfo['divisionMemberDeliveryDcPrice'] - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| } | |
| } else { | |
| // 주문상세내 배송비 금액 합산 | |
| if (!gd_in_array($gVal['orderStatus'], ['o1', 'c1', 'c2', 'c3', 'c4'])) { | |
| $getData['dashBoardPrice']['settlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['o1'])) { | |
| $getData['dashBoardPrice']['dueSettlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (!gd_in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 | |
| $aHandleInfo = $this->getOrderHandleInfo($gVal['handleSno']); | |
| $aDeliveryOriginalInfo = $this->getOrderDeliveryOriginal($gVal['orderDeliverySno']); | |
| $getData['dashBoardPrice']['cancelPrice'] += $aHandleInfo['refundDeliveryCharge'] - ($aDeliveryOriginalInfo['divisionMemberDeliveryDcPrice'] - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['r1', 'r2'])) { | |
| $getData['dashBoardPrice']['dueRefundPrice'] += $realDeliveryTaxSum; | |
| } | |
| $getData['totalDeliveryPolicyCharge'] += $gVal['deliveryPolicyCharge']; | |
| // 배송무게 계산 | |
| $getData['totalDeliveryWeights'] = json_decode($gVal['deliveryWeightInfo'], true); | |
| } | |
| $deliveryUniqueKey = $deliveryKeyCheck; | |
| // 해당 주문의 배송비es_orderDelivery.sno에 대한 배송비 세율정보 | |
| } | |
| } | |
| if (Manager::isProvider()) { | |
| $getData = $this->getProviderTotalPrice($getData); | |
| } | |
변경 후OrderAdminNew.php
| use Origin\Enum\Order\OrderStatusGroup; | |
| use Origin\Service\Statistics\Analytics\Order\OrderCompleteAnalyticsCollectorService; | |
| use Origin\Service\WebHook\OrderWebHookService; | |
| use Repository\Order\OrderDeliveryOriginalRepository; | |
| use Repository\Order\OrderHandleRepository; | |
| use Request; | |
| use Session; | |
| use Component\Page\Page; | |
| $deliveryUniqueKey = ''; | |
| $aCancelDeliveryKey = array(); // 품목 하나가 부분으로 취소되서 품목수가 나뉘어진경우 취소배송비 중복계산이 안되도록 체크하기위해서 orderDeliverySno로 키값 저장&체크 todo 취소부분 환불과 맞추면 다 삭제될 내용 | |
| // 전체 정책별 배송비와 지역별 배송비의 총합 구하기 (DB 불필요 합산은 단일 순회로 처리, 취소행/sno는 수집만) | |
| $cancelRows = []; | |
| $handleSnoList = $deliveryOriginalSnoList = []; | |
| foreach ($getData['goods'] as $sKey => $sVal) { | |
| foreach ($sVal as $dKey => $dVal) { | |
| foreach ($dVal as $gKey => $gVal) { | |
| $realDeliveryTaxSum = array_sum([ | |
| $gVal['realTaxSupplyDeliveryCharge'], | |
| $gVal['realTaxVatDeliveryCharge'], | |
| $gVal['realTaxFreeDeliveryCharge'], | |
| ]); | |
| // 전체 배송비 계산 (주문상품추가/주문타상품교환으로 같은 배송비 조건(deliverySno)이더라도 주문배송비(orderDeliverySno)가 추가된다 | |
| $deliveryKeyCheck = $gVal['deliverySno'] . '-' . $gVal['orderDeliverySno']; | |
| if ($deliveryKeyCheck != $deliveryUniqueKey) { | |
| // 주문상세내 배송비 금액 합산 | |
| if (!in_array($gVal['orderStatus'], ['o1', 'c1', 'c2', 'c3', 'c4'])) { | |
| $getData['dashBoardPrice']['settlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (in_array($gVal['orderStatus'], ['o1'])) { | |
| $getData['dashBoardPrice']['dueSettlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (in_array($gVal['orderStatus'], ['r1', 'r2'])) { | |
| $getData['dashBoardPrice']['dueRefundPrice'] += $realDeliveryTaxSum; | |
| } | |
| $getData['totalDeliveryPolicyCharge'] += $gVal['deliveryPolicyCharge']; | |
| // 배송무게 계산 | |
| $getData['totalDeliveryWeights'] = json_decode($gVal['deliveryWeightInfo'], true); | |
| } | |
| // 취소행은 기존에 배송키 분기와 무관하게 동일 처리됐으므로 모아두고, N+1 제거 위해 일괄조회 후 후처리 | |
| if (in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $cancelRows[] = $gVal; | |
| if ($gVal['handleSno'] > 0) { | |
| $handleSnoList[] = $gVal['handleSno']; | |
| } | |
| if ($gVal['orderDeliverySno'] > 0) { | |
| $deliveryOriginalSnoList[] = $gVal['orderDeliverySno']; | |
| } | |
| } | |
| $deliveryUniqueKey = $deliveryKeyCheck; | |
| // 해당 주문의 배송비es_orderDelivery.sno에 대한 배송비 세율정보 | |
| } | |
| } | |
| // 취소상태 배송행의 handleSno/orderDeliverySno 를 일괄 1회씩 조회 (N+1 제거) | |
| $handleInfoMap = \App::getInstance(OrderHandleRepository::class) | |
| ->findMapBySnos(array_values(array_unique($handleSnoList))); | |
| $deliveryOriginalMap = \App::getInstance(OrderDeliveryOriginalRepository::class) | |
| ->findMapBySnos(array_values(array_unique($deliveryOriginalSnoList))); | |
| // 취소 배송비 합산 (동일 orderDeliverySno 키는 1회만 계산) | |
| foreach ($cancelRows as $gVal) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| continue; | |
| } | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 (일괄조회 맵에서 룩업) | |
| $refundDeliveryCharge = $handleInfoMap[$gVal['handleSno']]['refundDeliveryCharge'] ?? 0; | |
| $originalDcPrice = $deliveryOriginalMap[$gVal['orderDeliverySno']]['divisionMemberDeliveryDcPrice'] ?? 0; | |
| $getData['dashBoardPrice']['cancelPrice'] += $refundDeliveryCharge - ($originalDcPrice - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| if (Manager::isProvider()) { | |
| $getData = $this->getProviderTotalPrice($getData); | |
| } | |
변경 전OrderAdmin.php
| use Origin\Enum\Order\OrderStatusGroup; | |
| use Origin\Service\Statistics\Analytics\Order\OrderCompleteAnalyticsCollectorService; | |
| use Origin\Service\WebHook\OrderWebHookService; | |
| use Request; | |
| use Session; | |
| use Vendor\Spreadsheet\Excel\Reader as SpreadsheetExcelReader; | |
| $deliveryUniqueKey = ''; | |
| $aCancelDeliveryKey = array(); // 품목 하나가 부분으로 취소되서 품목수가 나뉘어진경우 취소배송비 중복계산이 안되도록 체크하기위해서 orderDeliverySno로 키값 저장&체크 todo 취소부분 환불과 맞추면 다 삭제될 내용 | |
| // 전체 정책별 배송비와 지역별 배송비의 총합 구하기 | |
| foreach ($getData['goods'] as $sKey => $sVal) { | |
| foreach ($sVal as $dKey => $dVal) { | |
| // $dVal = array_shift($dVal); | |
| foreach ($dVal as $gKey => $gVal) { | |
| $realDeliveryTaxSum = gd_array_sum([ | |
| $gVal['realTaxSupplyDeliveryCharge'], | |
| $gVal['realTaxVatDeliveryCharge'], | |
| $gVal['realTaxFreeDeliveryCharge'], | |
| ]); | |
| // 전체 배송비 계산 (주문상품추가/주문타상품교환으로 같은 배송비 조건(deliverySno)이더라도 주문배송비(orderDeliverySno)가 추가된다 | |
| $deliveryKeyCheck = $gVal['deliverySno'] . '-' . $gVal['orderDeliverySno']; | |
| if ($deliveryKeyCheck == $deliveryUniqueKey) { | |
| if (gd_in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (!gd_in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 | |
| $aHandleInfo = $this->getOrderHandleInfo($gVal['handleSno']); | |
| $aDeliveryOriginalInfo = $this->getOrderDeliveryOriginal($gVal['orderDeliverySno']); | |
| $getData['dashBoardPrice']['cancelPrice'] += $aHandleInfo['refundDeliveryCharge'] - ($aDeliveryOriginalInfo['divisionMemberDeliveryDcPrice'] - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| } | |
| } else { | |
| // 주문상세내 배송비 금액 합산 | |
| if (!gd_in_array($gVal['orderStatus'], ['o1', 'c1', 'c2', 'c3', 'c4'])) { | |
| $getData['dashBoardPrice']['settlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['o1'])) { | |
| $getData['dashBoardPrice']['dueSettlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (!gd_in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 | |
| $aHandleInfo = $this->getOrderHandleInfo($gVal['handleSno']); | |
| $aDeliveryOriginalInfo = $this->getOrderDeliveryOriginal($gVal['orderDeliverySno']); | |
| $getData['dashBoardPrice']['cancelPrice'] += $aHandleInfo['refundDeliveryCharge'] - ($aDeliveryOriginalInfo['divisionMemberDeliveryDcPrice'] - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| } | |
| if (gd_in_array($gVal['orderStatus'], ['r1', 'r2'])) { | |
| $getData['dashBoardPrice']['dueRefundPrice'] += $realDeliveryTaxSum; | |
| } | |
| $getData['totalDeliveryPolicyCharge'] += $gVal['deliveryPolicyCharge']; | |
| // 배송무게 계산 | |
| $getData['totalDeliveryWeights'] = json_decode($gVal['deliveryWeightInfo'], true); | |
| } | |
| $deliveryUniqueKey = $deliveryKeyCheck; | |
| // 해당 주문의 배송비es_orderDelivery.sno에 대한 배송비 세율정보 | |
| } | |
| } | |
| if (Manager::isProvider()) { | |
| $getData = $this->getProviderTotalPrice($getData); | |
| } | |
변경 후OrderAdmin.php
| use Origin\Enum\Order\OrderStatusGroup; | |
| use Origin\Service\Statistics\Analytics\Order\OrderCompleteAnalyticsCollectorService; | |
| use Origin\Service\WebHook\OrderWebHookService; | |
| use Repository\Order\OrderDeliveryOriginalRepository; | |
| use Repository\Order\OrderHandleRepository; | |
| use Request; | |
| use Session; | |
| use Vendor\Spreadsheet\Excel\Reader as SpreadsheetExcelReader; | |
| $deliveryUniqueKey = ''; | |
| $aCancelDeliveryKey = array(); // 품목 하나가 부분으로 취소되서 품목수가 나뉘어진경우 취소배송비 중복계산이 안되도록 체크하기위해서 orderDeliverySno로 키값 저장&체크 todo 취소부분 환불과 맞추면 다 삭제될 내용 | |
| // 전체 정책별 배송비와 지역별 배송비의 총합 구하기 (DB 불필요 합산은 단일 순회로 처리, 취소행/sno는 수집만) | |
| $cancelRows = []; | |
| $handleSnoList = $deliveryOriginalSnoList = []; | |
| foreach ($getData['goods'] as $sKey => $sVal) { | |
| foreach ($sVal as $dKey => $dVal) { | |
| foreach ($dVal as $gKey => $gVal) { | |
| $realDeliveryTaxSum = array_sum([ | |
| $gVal['realTaxSupplyDeliveryCharge'], | |
| $gVal['realTaxVatDeliveryCharge'], | |
| $gVal['realTaxFreeDeliveryCharge'], | |
| ]); | |
| // 전체 배송비 계산 (주문상품추가/주문타상품교환으로 같은 배송비 조건(deliverySno)이더라도 주문배송비(orderDeliverySno)가 추가된다 | |
| $deliveryKeyCheck = $gVal['deliverySno'] . '-' . $gVal['orderDeliverySno']; | |
| if ($deliveryKeyCheck != $deliveryUniqueKey) { | |
| // 주문상세내 배송비 금액 합산 | |
| if (!in_array($gVal['orderStatus'], ['o1', 'c1', 'c2', 'c3', 'c4'])) { | |
| $getData['dashBoardPrice']['settlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (in_array($gVal['orderStatus'], ['o1'])) { | |
| $getData['dashBoardPrice']['dueSettlePrice'] += $realDeliveryTaxSum; | |
| } | |
| if (in_array($gVal['orderStatus'], ['r1', 'r2'])) { | |
| $getData['dashBoardPrice']['dueRefundPrice'] += $realDeliveryTaxSum; | |
| } | |
| $getData['totalDeliveryPolicyCharge'] += $gVal['deliveryPolicyCharge']; | |
| // 배송무게 계산 | |
| $getData['totalDeliveryWeights'] = json_decode($gVal['deliveryWeightInfo'], true); | |
| } | |
| // 취소행은 기존에 배송키 분기와 무관하게 동일 처리됐으므로 모아두고, N+1 제거 위해 일괄조회 후 후처리 | |
| if (in_array($gVal['orderStatus'], ['c1', 'c2', 'c3', 'c4'])) { | |
| $cancelRows[] = $gVal; | |
| if ($gVal['handleSno'] > 0) { | |
| $handleSnoList[] = $gVal['handleSno']; | |
| } | |
| if ($gVal['orderDeliverySno'] > 0) { | |
| $deliveryOriginalSnoList[] = $gVal['orderDeliverySno']; | |
| } | |
| } | |
| $deliveryUniqueKey = $deliveryKeyCheck; | |
| // 해당 주문의 배송비es_orderDelivery.sno에 대한 배송비 세율정보 | |
| } | |
| } | |
| // 취소상태 배송행의 handleSno/orderDeliverySno 를 일괄 1회씩 조회 (N+1 제거) | |
| $handleInfoMap = \App::getInstance(OrderHandleRepository::class) | |
| ->findMapBySnos(array_values(array_unique($handleSnoList))); | |
| $deliveryOriginalMap = \App::getInstance(OrderDeliveryOriginalRepository::class) | |
| ->findMapBySnos(array_values(array_unique($deliveryOriginalSnoList))); | |
| // 취소 배송비 합산 (동일 orderDeliverySno 키는 1회만 계산) | |
| foreach ($cancelRows as $gVal) { | |
| $tempCancelDeliveryKey = substr($gVal['orderStatus'], 0, 1) . '-' . $gVal['orderDeliverySno']; | |
| if (in_array($tempCancelDeliveryKey, $aCancelDeliveryKey)) { | |
| continue; | |
| } | |
| // $gVal['handleSno'] 값으로 orderHandle에 기록된 취소 배송비 값 가져와서 계산 (일괄조회 맵에서 룩업) | |
| $refundDeliveryCharge = $handleInfoMap[$gVal['handleSno']]['refundDeliveryCharge'] ?? 0; | |
| $originalDcPrice = $deliveryOriginalMap[$gVal['orderDeliverySno']]['divisionMemberDeliveryDcPrice'] ?? 0; | |
| $getData['dashBoardPrice']['cancelPrice'] += $refundDeliveryCharge - ($originalDcPrice - $gVal['divisionMemberDeliveryDcPrice']); | |
| $aCancelDeliveryKey[] = $tempCancelDeliveryKey; | |
| } | |
| if (Manager::isProvider()) { | |
| $getData = $this->getProviderTotalPrice($getData); | |
| } | |
변경 전OrderDeliveryOriginalRepository.php
변경 후OrderDeliveryOriginalRepository.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\Repository\Order; | |
| use Origin\Model\Order\OrderDeliveryOriginal; | |
| class OrderDeliveryOriginalRepository | |
| { | |
| /** | |
| * sno 목록으로 orderDeliveryOriginal 정보를 일괄 조회해 [sno => row] 맵으로 반환 (주문상세 N+1 제거용) | |
| * | |
| * @param array $snos orderDeliveryOriginal.sno 목록 | |
| * @return array sno 를 키로 하는 orderDeliveryOriginal row 맵 | |
| */ | |
| public function findMapBySnos(array $snos): array | |
| { | |
| if (empty($snos)) { | |
| return []; | |
| } | |
| return OrderDeliveryOriginal::query() | |
| ->whereIn('sno', $snos) | |
| ->get() | |
| ->keyBy('sno') | |
| ->map(fn ($model) => $model->toArray()) | |
| ->all(); | |
| } | |
| } | |
변경 전OrderHandleRepository.php
변경 후OrderHandleRepository.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\Repository\Order; | |
| use Origin\Model\Order\OrderHandle; | |
| class OrderHandleRepository | |
| { | |
| /** | |
| * sno 목록으로 orderHandle 정보를 일괄 조회해 [sno => row] 맵으로 반환 (주문상세 N+1 제거용) | |
| * | |
| * @param array $snos orderHandle.sno 목록 | |
| * @return array sno 를 키로 하는 orderHandle row 맵 | |
| */ | |
| public function findMapBySnos(array $snos): array | |
| { | |
| if (empty($snos)) { | |
| return []; | |
| } | |
| return OrderHandle::query() | |
| ->whereIn('sno', $snos) | |
| ->get() | |
| ->keyBy('sno') | |
| ->map(fn ($model) => $model->toArray()) | |
| ->all(); | |
| } | |
| } | |
변경 전Gift.php
| } | |
| return $isStrip ? gd_htmlspecialchars_stripslashes($setData) : $setData; | |
| } | |
| } | |
변경 후Gift.php
| } | |
| return $isStrip ? gd_htmlspecialchars_stripslashes($setData) : $setData; | |
| } | |
| /** | |
| * 주문 사은품 데이터 검증 및 정규화 (변조 방지) | |
| * | |
| * 주문서에서 전달된 사은품 hidden 필드(giveCnt/selectCnt/giftNo)는 개발자도구 등으로 | |
| * 변조가 가능하므로, 서버에서 재조회한 유효 사은품 정보($giftInfo)를 기준으로 검증·정규화 | |
| * - giveCnt(지급수량) : 지급조건의 값으로 강제 정규화 (변조 무력화, 주문은 정상 생성) | |
| * - selectCnt(선택수량) : 실제 선택한 사은품 개수가 지급조건과 다르면 차단 (전체=0 은 제외) | |
| * - 지급조건에 포함되지 않은 사은품(giftNo)은 제거 | |
| * | |
| * @param array $submittedGift 주문서에서 전달된 사은품 데이터 [presentSno][index] => [...] | |
| * @param array $giftInfo getGiftPresentOrder() 로 조회한 유효 사은품 정보 | |
| * @return array 정규화된 사은품 데이터 | |
| * @throws \Exception | |
| */ | |
| public function validateAndNormalizeOrderGift(array $submittedGift, array $giftInfo): array | |
| { | |
| if (empty($submittedGift) || empty($giftInfo)) { | |
| return $submittedGift; | |
| } | |
| foreach ($submittedGift as $presentSno => $giftData) { | |
| // 유효하지 않은 사은품 정책은 기존 저장 로직에서 스킵되므로 건드리지 않음 | |
| if (!array_key_exists($presentSno, $giftInfo) || empty($giftInfo[$presentSno]['gift'])) { | |
| continue; | |
| } | |
| // 권위 데이터 집계 : giftNo => 지급조건(row), 지급조건별 giveCnt/selectCnt | |
| $authGiveCnt = []; // giftNo => giveCnt | |
| $giftNoToRow = []; // giftNo => rowKey | |
| $rowSelectCnt = []; // rowKey => selectCnt | |
| foreach ($giftInfo[$presentSno]['gift'] as $rowKey => $row) { | |
| $rowSelectCnt[$rowKey] = (int) ($row['selectCnt'] ?? 0); | |
| if (empty($row['multiGiftNo']) || !is_array($row['multiGiftNo'])) { | |
| continue; | |
| } | |
| foreach ($row['multiGiftNo'] as $item) { | |
| if (!isset($item['giftNo'])) { | |
| continue; | |
| } | |
| $authGiveCnt[$item['giftNo']] = (int) ($row['giveCnt'] ?? 0); | |
| $giftNoToRow[$item['giftNo']] = $rowKey; | |
| } | |
| } | |
| // 제출된 사은품 정규화 + 지급조건별 선택 개수/제출 선택수량 집계 | |
| $selectedCntByRow = []; | |
| $submittedSelectCntByRow = []; | |
| foreach ($giftData as $index => $gift) { | |
| // 선택되지 않은 항목(giftNo 없음)은 통과 (기존 로직이 저장하지 않음) | |
| if (!isset($gift['giftNo']) || $gift['giftNo'] === '') { | |
| continue; | |
| } | |
| // 지급조건에 포함되지 않은 사은품(변조)은 제거 | |
| if (!isset($authGiveCnt[$gift['giftNo']])) { | |
| \Logger::channel('order')->info(__METHOD__ . ' 지급조건에 없는 사은품 제거', [$presentSno, $gift['giftNo']]); | |
| unset($submittedGift[$presentSno][$index]); | |
| continue; | |
| } | |
| // giveCnt(지급수량) 정규화 - POST 변조 무력화 | |
| $submittedGift[$presentSno][$index]['giveCnt'] = $authGiveCnt[$gift['giftNo']]; | |
| $rowKey = $giftNoToRow[$gift['giftNo']]; | |
| $selectedCntByRow[$rowKey] = ($selectedCntByRow[$rowKey] ?? 0) + 1; | |
| // 제출된 선택수량(변조 탐지용) - 같은 지급조건 항목은 동일 값 | |
| if (isset($gift['selectCnt'])) { | |
| $submittedSelectCntByRow[$rowKey] = (int) $gift['selectCnt']; | |
| } | |
| } | |
| // 선택수량(selectCnt) 검증 | |
| foreach ($rowSelectCnt as $rowKey => $selectCnt) { | |
| $selectedCnt = (int) ($selectedCntByRow[$rowKey] ?? 0); | |
| $submittedSelectCnt = $submittedSelectCntByRow[$rowKey] ?? null; | |
| $message = GiftSelectCntValidator::resolveViolationMessage($selectCnt, $selectedCnt, $submittedSelectCnt); | |
| if ($message !== null) { | |
| \Logger::channel('order')->warning(__METHOD__ . ' 사은품 선택수량 검증 실패', [ | |
| 'presentSno' => $presentSno, | |
| 'rowKey' => $rowKey, | |
| 'authSelectCnt' => $selectCnt, | |
| 'selectedCnt' => $selectedCnt, | |
| 'submittedSelectCnt' => $submittedSelectCnt, | |
| ]); | |
| throw new \Exception($message); | |
| } | |
| } | |
| } | |
| return $submittedGift; | |
| } | |
| } | |
변경 전GiftSelectCntValidator.php
변경 후GiftSelectCntValidator.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\Component\Gift; | |
| /** | |
| * 사은품 선택수량(selectCnt) 검증 | |
| * | |
| * DB/세션/로거 등 부수효과 없이 정수 입력만으로 위반 여부를 판정 | |
| * 일반주문/정기결제 등 각 호출부는 이 메시지를 받아 흐름에 맞는 예외를 던진다 | |
| */ | |
| class GiftSelectCntValidator | |
| { | |
| /** | |
| * 선택수량 위반 메시지를 반환한다. 위반이 없으면 null. | |
| * | |
| * - 지급조건 선택수량(전체=0)은 검증 제외 | |
| * - 미선택(0개)은 검증 제외 (사은품 거절 허용) | |
| * - 실제 선택 개수 또는 제출된 선택수량(변조)이 지급조건과 다르면 위반 | |
| * | |
| * @param int $authSelectCnt 서버(지급조건)의 선택수량. 0이면 전체 지급 | |
| * @param int $selectedCnt 실제 선택(체크)된 사은품 개수 | |
| * @param int|null $submittedSelectCnt 주문서에서 제출된 선택수량 값(변조 탐지용). null이면 미사용 | |
| * @return string|null 위반 메시지 또는 null | |
| */ | |
| public static function resolveViolationMessage(int $authSelectCnt, int $selectedCnt, ?int $submittedSelectCnt = null): ?string | |
| { | |
| // 전체(0) 또는 미선택은 검증 제외 (전체 지급 / 사은품 거절 허용) | |
| if ($authSelectCnt <= 0 || $selectedCnt <= 0) { | |
| return null; | |
| } | |
| // 제출된 선택수량이 없으면 실제 선택 개수로 비교 | |
| $submittedSelectCnt ??= $selectedCnt; | |
| return match (true) { | |
| // 최대 초과 : 실제 선택 개수 또는 제출된 선택수량(변조)이 지급조건보다 큰 경우 | |
| $selectedCnt > $authSelectCnt || $submittedSelectCnt > $authSelectCnt | |
| => sprintf(__('사은품은 최대 %s개만 선택하실 수 있습니다.'), $authSelectCnt), | |
| // 최소 미달 : 실제 선택 개수 또는 제출된 선택수량(변조)이 지급조건보다 작은 경우 | |
| $selectedCnt < $authSelectCnt || $submittedSelectCnt < $authSelectCnt | |
| => sprintf(__('사은품은 최소 %s개 이상 선택하셔야 합니다.'), $authSelectCnt), | |
| default => null, | |
| }; | |
| } | |
| } | |
변경 전OrderNew.php
| $giftInfo = $giftPresent->getGiftPresentOrder($orderInfo['giftForData'], 0, false, false); // 수기 주문 케이스는 이 구간을 거치지 않음 | |
| \Logger::channel('goods')->info(__METHOD__ . ' getGiftPresentOrder() result :', $giftInfo); | |
| if (gd_count($giftInfo) > 0) { | |
| foreach ($orderInfo['gift'] as $presentSno => $giftData) { | |
| // 주문시 사용된 사은품 정보가 유효하지 않으면 삭제 | |
변경 후OrderNew.php
| $giftInfo = $giftPresent->getGiftPresentOrder($orderInfo['giftForData'], 0, false, false); // 수기 주문 케이스는 이 구간을 거치지 않음 | |
| \Logger::channel('goods')->info(__METHOD__ . ' getGiftPresentOrder() result :', $giftInfo); | |
| // 사은품 지급/선택 수량 검증 및 정규화 (giveCnt 변조 무력화 / selectCnt 위반 시 차단은 프론트 주문만, 관리자 수기주문 제외) | |
| // 존재 여부 가드 - 미존재 시 fatal 대신 검증 미적용 + 경고 로그 | |
| if (method_exists($giftPresent, 'validateAndNormalizeOrderGift')) { | |
| $orderInfo['gift'] = $giftPresent->validateAndNormalizeOrderGift($orderInfo['gift'], $giftInfo); | |
| } else { | |
| \Logger::channel('order')->warning(__METHOD__ . ' validateAndNormalizeOrderGift 미존재(Gift.php 튜닝본) - 사은품 수량 변조 검증 미적용', ['orderNo' => $this->orderNo]); | |
| } | |
| if (gd_count($giftInfo) > 0) { | |
| foreach ($orderInfo['gift'] as $presentSno => $giftData) { | |
| // 주문시 사용된 사은품 정보가 유효하지 않으면 삭제 | |
변경 전Order.php
| $giftInfo = $giftPresent->getGiftPresentOrder($orderInfo['giftForData'], $groupSno, $isWrite, false); | |
| \Logger::channel('goods')->info(__METHOD__ . ' getGiftPresentOrder() result :', $giftInfo); | |
| if (gd_count($giftInfo) > 0) { | |
| foreach ($orderInfo['gift'] as $presentSno => $giftData) { | |
| // 주문시 사용된 사은품 정보가 유효하지 않으면 삭제 | |
변경 후Order.php
| $giftInfo = $giftPresent->getGiftPresentOrder($orderInfo['giftForData'], $groupSno, $isWrite, false); | |
| \Logger::channel('goods')->info(__METHOD__ . ' getGiftPresentOrder() result :', $giftInfo); | |
| // 사은품 지급/선택 수량 검증 및 정규화 (giveCnt 변조 무력화 / selectCnt 위반 시 차단) | |
| // 프론트/회원 주문($isWrite === false)만 검증한다. 관리자 수기주문($isWrite === true)은 | |
| // selectCnt 강제(차단) 대상이 아니므로 제외한다. | |
| if ($isWrite === false) { | |
| // 존재 여부 가드 - 미존재 시 fatal 대신 검증 미적용 + 경고 로그 | |
| if (method_exists($giftPresent, 'validateAndNormalizeOrderGift')) { | |
| $orderInfo['gift'] = $giftPresent->validateAndNormalizeOrderGift($orderInfo['gift'], $giftInfo); | |
| } else { | |
| \Logger::channel('order')->warning(__METHOD__ . ' validateAndNormalizeOrderGift 미존재(Gift.php 튜닝본) - 사은품 수량 변조 검증 미적용', ['orderNo' => $this->orderNo]); | |
| } | |
| } | |
| if (gd_count($giftInfo) > 0) { | |
| foreach ($orderInfo['gift'] as $presentSno => $giftData) { | |
| // 주문시 사용된 사은품 정보가 유효하지 않으면 삭제 | |
변경 전kakao_login_config.php
| <div class="notice-info"> | |
| 카카오 아이디 로그인에 사용한 REST API 키와 ADMIN 키는 <a href="https://developers.kakao.com/" target="_blank" class="notice-ref btn-link">카카오 디벨로퍼스 > 내 애플리케이션 > 앱 설정 > 앱 키</a>에서 확인하실 수 있습니다. | |
| </div> | |
| <?php elseif($isAvailableOnlyKakaoSync === false) : ?> | |
| <form id="form" name="form" action="kakao_login_config_ps.php" method="post"> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <input type="submit" value="저장" class="btn btn-red" /> | |
| </div> | |
| <div class="table-title gd-help-manual"> | |
변경 후kakao_login_config.php
| <div class="notice-info"> | |
| 카카오 아이디 로그인에 사용한 REST API 키와 ADMIN 키는 <a href="https://developers.kakao.com/" target="_blank" class="notice-ref btn-link">카카오 디벨로퍼스 > 내 애플리케이션 > 앱 설정 > 앱 키</a>에서 확인하실 수 있습니다. | |
| </div> | |
| <?php else : ?> | |
| <form id="form" name="form" action="kakao_login_config_ps.php" method="post"> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <?php if (!$isAvailableOnlyKakaoSync) : ?> | |
| <input type="submit" value="저장" class="btn btn-red" /> | |
| <?php endif; ?> | |
| </div> | |
| <div class="table-title gd-help-manual"> | |
변경 전OrderNew.php
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq']]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', $result); | |
| } | |
| unset($arrOrderGoodsSno['mq']); | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', $result); | |
| } | |
| unset($mqData); | |
변경 후OrderNew.php
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| $request = \App::getInstance('request'); | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq'], 'reqUri' => $request->getRequestUri()]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| $request = \App::getInstance('request'); | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData, 'reqUri' => $request->getRequestUri()]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq']]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| unset($arrOrderGoodsSno['mq']); | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| unset($mqData); | |
변경 전Order.php
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq']]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', $result); | |
| } | |
| unset($arrOrderGoodsSno['mq']); | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', $result); | |
| } | |
| unset($mqData); | |
변경 후Order.php
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| $request = \App::getInstance('request'); | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq'], 'reqUri' => $request->getRequestUri()]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| $request = \App::getInstance('request'); | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData, 'reqUri' => $request->getRequestUri()]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($arrOrderGoodsSno['mq'])) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $this->orderNo, 'orderGoodsNos' => $arrOrderGoodsSno['mq']]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($arrOrderGoodsSno['mq'], 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $this->orderNo], is_array($result) ? $result : [])); | |
| } | |
| unset($arrOrderGoodsSno['mq']); | |
| $kafka = new ProducerUtils(); | |
| // 주문카운트 대상이 있을 때만 발행 (없으면 null 메시지 방지) | |
| if (!empty($mqData)) { | |
| \Logger::channel('kafka')->info('process sendMQ - payload :', ['topic' => $kafka::TOPIC_PRODUCT_ORDER_COUNT, 'orderNo' => $orderNo, 'orderGoodsNos' => $mqData]); | |
| $result = $kafka->send($kafka::TOPIC_PRODUCT_ORDER_COUNT, $kafka->makeData($mqData, 'p'), $kafka::MODE_RESULT_CALLLBACK, true); | |
| \Logger::channel('kafka')->info('process sendMQ - return :', array_merge(['orderNo' => $orderNo], is_array($result) ? $result : [])); | |
| } | |
| unset($mqData); | |
변경 전AdultController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| $this->setData('naverUrl', $naver->getLoginURL($returnUrl)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 후AdultController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| // redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), 동적값은 state로 이관 | |
| $naverRedirectUri = $request->getDomainUrl() . '/member/naver/naver_login.php'; | |
| $naverState = $naver->createState([ | |
| 'referer' => $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH), | |
| 'returnUrl' => $returnUrl, | |
| 'naverType' => '', | |
| ]); | |
| $this->setData('naverUrl', $naver->getLoginURL($naverRedirectUri, $naverState)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 전MemberController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| $this->setData('naverUrl', $naver->getLoginURL($returnUrl)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
| $siteLink = new SiteLink(); | |
변경 후MemberController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| // redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), 동적값은 state로 이관 | |
| $naverRedirectUri = $request->getDomainUrl() . '/member/naver/naver_login.php'; | |
| $naverState = $naver->createState([ | |
| 'referer' => $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH), | |
| 'returnUrl' => $returnUrl, | |
| 'naverType' => '', | |
| ]); | |
| $this->setData('naverUrl', $naver->getLoginURL($naverRedirectUri, $naverState)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
| $siteLink = new SiteLink(); | |
변경 전LoginController.php
| if ($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| $this->setData('naverUrl', $naver->getLoginURL($returnUrl)); | |
| } | |
| $kakaoPolicy = \App::getInstance(KakaoLoginPolicy::class); | |
변경 후LoginController.php
| if ($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| // redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), 동적값은 state로 이관 | |
| $naverRedirectUri = $request->getDomainUrl() . '/member/naver/naver_login.php'; | |
| $naverState = $naver->createState([ | |
| 'referer' => $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH), | |
| 'returnUrl' => $returnUrl, | |
| 'naverType' => '', | |
| ]); | |
| $this->setData('naverUrl', $naver->getLoginURL($naverRedirectUri, $naverState)); | |
| } | |
| $kakaoPolicy = \App::getInstance(KakaoLoginPolicy::class); | |
변경 전NaverLoginController.php
| $loginReturnURL = MemberUtil::getLoginReturnURL(); | |
| } | |
| $js = " | |
| if (typeof(window.top.layerSearchArea) == 'object') { | |
| parent.location.href='" . $loginReturnURL . "'; | |
| } else if (window.opener === null) { | |
| location.href='" . $loginReturnURL . "'; | |
| } else { | |
| opener.location.href='" . $loginReturnURL . "'; | |
| self.close(); | |
| } | |
| "; | |
변경 후NaverLoginController.php
| $loginReturnURL = MemberUtil::getLoginReturnURL(); | |
| } | |
| // location.href JS 문자열 컨텍스트 인젝션 방지: URL을 JSON 리터럴로 인코딩 | |
| $loginReturnUrlJs = json_encode((string) $loginReturnURL, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | |
| $js = " | |
| if (typeof(window.top.layerSearchArea) == 'object') { | |
| parent.location.href=" . $loginReturnUrlJs . "; | |
| } else if (window.opener === null) { | |
| location.href=" . $loginReturnUrlJs . "; | |
| } else { | |
| opener.location.href=" . $loginReturnUrlJs . "; | |
| self.close(); | |
| } | |
| "; | |
변경 전AdultController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| $this->setData('naverUrl', $naver->getLoginURL($returnUrl)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 후AdultController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| // redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), 동적값은 state로 이관 | |
| $naverRedirectUri = $request->getDomainUrl() . '/member/naver/naver_login.php'; | |
| $naverState = $naver->createState([ | |
| 'referer' => $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH), | |
| 'returnUrl' => $returnUrl, | |
| 'naverType' => '', | |
| ]); | |
| $this->setData('naverUrl', $naver->getLoginURL($naverRedirectUri, $naverState)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 전MemberController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| $this->setData('naverUrl', $naver->getLoginURL($returnUrl)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 후MemberController.php
| if($useNaver) { | |
| $naver = new GodoNaverServerApi(); | |
| $scripts[] = 'gd_naver.js'; | |
| // redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), 동적값은 state로 이관 | |
| $naverRedirectUri = $request->getDomainUrl() . '/member/naver/naver_login.php'; | |
| $naverState = $naver->createState([ | |
| 'referer' => $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH), | |
| 'returnUrl' => $returnUrl, | |
| 'naverType' => '', | |
| ]); | |
| $this->setData('naverUrl', $naver->getLoginURL($naverRedirectUri, $naverState)); | |
| } | |
| $this->setData('useNaverLogin', $useNaver); | |
변경 전member_group_appraisal_config.php
| </label> | |
| </th> | |
| <td colspan="3"> | |
| 주문금액, 상품주문건수, 주문상품후기횟수를 종합하여 평가하는 방법입니다. 회원등급별 평가기준을 입력하세요. | |
| <div class="notice-info">수동 평가 선택 시 회원등급이 자동으로 평가되지 않고 <a href="#" class="js-btn-appraisal">[회원등급 수동평가]</a>를 눌러 회원등급을 평가합니다.</div> | |
| </td> | |
| </tr> | |
| <th> | |
| <label class="checkbox-inline"> | |
| <input type="checkbox" name="appraisalPointOrderPriceFl" value="y" <?= gd_isset($checked['appraisalPointOrderPriceFl']['y']); ?> data-target-class="terms_p01"/> | |
| 주문금액 | |
| </label> | |
| </th> | |
| <td> | |
| ?> | |
| <label class="checkbox-inline"> | |
| <input type="checkbox" name="<?= $chknm1 ?>" value="y" <?= $checked['apprFigureOrderPriceFl'][$val['sno']]['y'] ?> data-target-class="<?= $checkClass1 ?>"/> | |
| 주문금액 | |
| </label> | |
| <br/> | |
| <label class="checkbox-inline"> | |
| var gd_group_list = { | |
| layerRule: null, | |
| message: { | |
| figure: "현재 설정된 평가방법은 실적수치제이며, 회원등급별 평가기준에 따라 주문금액, 상품주문건수, 주문상품후기횟수를 종합하여 회원등급이 평가됩니다. 회원등급 평가를 진행하시겠습니까?", | |
| point: "현재 설정된 평가방법은 실적점수제이며, 주문금액, 상품주문건수, 주문상품후기횟수, 로그인횟수를 점수로 환산하여 회원등급이 평가됩니다. 회원등급 평가를 진행하시겠습니까?" | |
| } | |
| }; | |
변경 후member_group_appraisal_config.php
| </label> | |
| </th> | |
| <td colspan="3"> | |
| 실결제금액, 상품주문건수, 주문상품후기횟수를 종합하여 평가하는 방법입니다. 회원등급별 평가기준을 입력하세요. | |
| <div class="notice-info">수동 평가 선택 시 회원등급이 자동으로 평가되지 않고 <a href="#" class="js-btn-appraisal">[회원등급 수동평가]</a>를 눌러 회원등급을 평가합니다.</div> | |
| </td> | |
| </tr> | |
| <th> | |
| <label class="checkbox-inline"> | |
| <input type="checkbox" name="appraisalPointOrderPriceFl" value="y" <?= gd_isset($checked['appraisalPointOrderPriceFl']['y']); ?> data-target-class="terms_p01"/> | |
| 실결제금액 | |
| </label> | |
| </th> | |
| <td> | |
| ?> | |
| <label class="checkbox-inline"> | |
| <input type="checkbox" name="<?= $chknm1 ?>" value="y" <?= $checked['apprFigureOrderPriceFl'][$val['sno']]['y'] ?> data-target-class="<?= $checkClass1 ?>"/> | |
| 실결제금액 | |
| </label> | |
| <br/> | |
| <label class="checkbox-inline"> | |
| var gd_group_list = { | |
| layerRule: null, | |
| message: { | |
| figure: "현재 설정된 평가방법은 실적수치제이며, 회원등급별 평가기준에 따라 실결제금액, 상품주문건수, 주문상품후기횟수를 종합하여 회원등급이 평가됩니다. 회원등급 평가를 진행하시겠습니까?", | |
| point: "현재 설정된 평가방법은 실적점수제이며, 실결제금액, 상품주문건수, 주문상품후기횟수, 로그인횟수를 점수로 환산하여 회원등급이 평가됩니다. 회원등급 평가를 진행하시겠습니까?" | |
| } | |
| }; | |