본문으로 건너뛰기

처리 완료된 클레임의 상태값 보호 강화

PHP 8 · GODO25
변경 파일 수: 7개
변경 파일 목록
7
변경 파일 코드
추가삭제
Component/Order/OrderAdminNew.php+9
변경 전OrderAdminNew.php
return false;
}
 
 
 
 
 
 
 
 
 
 
$partCompleteData = [];
foreach ($arrData['statusCheck'] as $val) {
$bundleData = [];
 
변경 후OrderAdminNew.php
return false;
}
 
// [#6126] 관리자 수동 승인/거절 시, 이미 처리된 신청 건 재처리 차단 (stale 화면/동시성 대응)
// 자동 환불 등 자동 처리(autoProceess) 흐름은 자체 선행 조건을 따르므로 가드 대상에서 제외한다.
if ($autoProceess !== true) {
$processGuard = \App::getInstance(\Component\Order\UserHandleProcessGuard::class);
if ($processGuard->hasAlreadyProcessed($arrData['statusCheck'] ?? [])) {
throw new AlertBackException(__('이미 승인 또는 거절 처리가 된 신청 건입니다. 페이지 새로고침 후 확인해 주세요.'));
}
}
 
$partCompleteData = [];
foreach ($arrData['statusCheck'] as $val) {
$bundleData = [];
 
Component/Order/OrderAdmin.php+9
변경 전OrderAdmin.php
return false;
}
 
 
 
 
 
 
 
 
 
 
$partCompleteData = [];
foreach ($arrData['statusCheck'] as $val) {
$bundleData = [];
 
변경 후OrderAdmin.php
return false;
}
 
// [#6126] 관리자 수동 승인/거절 시, 이미 처리된 신청 건 재처리 차단 (stale 화면/동시성 대응)
// 자동 환불 등 자동 처리(autoProceess) 흐름은 자체 선행 조건을 따르므로 가드 대상에서 제외한다.
if ($autoProceess !== true) {
$processGuard = \App::getInstance(\Component\Order\UserHandleProcessGuard::class);
if ($processGuard->hasAlreadyProcessed($arrData['statusCheck'] ?? [])) {
throw new AlertBackException(__('이미 승인 또는 거절 처리가 된 신청 건입니다. 페이지 새로고침 후 확인해 주세요.'));
}
}
 
$partCompleteData = [];
foreach ($arrData['statusCheck'] as $val) {
$bundleData = [];
 
Component/Order/ReOrderCalculation.php+8
변경 전ReOrderCalculation.php
use Component\Cart\CartAdmin;
use Component\Delivery\OverseasDelivery;
use Exception;
 
use Framework\Utility\ArrayUtils;
use Framework\Utility\NumberUtils;
use Framework\Utility\StringUtils;
public function setUserSameExchangeOrderGoods($postData)
{
$claimStatus = 'e';
 
 
 
 
 
 
 
$order = App::load(\Component\Order\Order::class);
$returnOrderGoodsSno = $addOrderGoodsSnoArr = [];
$changeStatus = $this->setAddExchangeOrderGoodsStatus(0);
 
변경 후ReOrderCalculation.php
use Component\Cart\CartAdmin;
use Component\Delivery\OverseasDelivery;
use Exception;
use Framework\Debug\Exception\AlertBackException;
use Framework\Utility\ArrayUtils;
use Framework\Utility\NumberUtils;
use Framework\Utility\StringUtils;
public function setUserSameExchangeOrderGoods($postData)
{
$claimStatus = 'e';
 
// [#6126] 이미 승인/거절 처리된 신청 건 재처리 차단 (stale 화면/동시성 대응)
$processGuard = App::getInstance(\Component\Order\UserHandleProcessGuard::class);
if ($processGuard->hasAlreadyProcessed($postData['statusCheck'] ?? [])) {
throw new AlertBackException(__('이미 승인 또는 거절 처리가 된 신청 건입니다. 페이지 새로고침 후 확인해 주세요.'));
}
 
$order = App::load(\Component\Order\Order::class);
$returnOrderGoodsSno = $addOrderGoodsSnoArr = [];
$changeStatus = $this->setAddExchangeOrderGoodsStatus(0);
 
Component/Order/UserHandleProcessGuard.php+71
변경 전UserHandleProcessGuard.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
변경 후UserHandleProcessGuard.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\Order;
 
use Repository\Order\OrderUserHandleRepository;
 
/**
* 고객 클레임(교환/반품/환불) 신청 건의 승인/거절 처리 전 상태 검증 가드
*
* 이미 승인(userHandleFl='y') 또는 거절(userHandleFl='n') 처리된 신청 건에 대해
* 중복 승인/거절이 발생하지 않도록, 처리 직전 현재 상태를 재확인한다.
*
* 목록 화면 로드 이후 다른 관리자/자동환불 등으로 상태가 바뀐 stale 상황에서
* 승인/거절을 시도하면, 환불 완료 등 이미 종결된 건이 다시 신청 상태로 되돌아가
* 환불 재시도가 불가해지는 문제(고객센터 문의 인입)를 방지한다.
*
* @package Bundle\Component\Order
*/
class UserHandleProcessGuard
{
public function __construct(
private readonly OrderUserHandleRepository $orderUserHandleRepository
) {}
 
/**
* statusCheck 항목들 중 이미 승인/거절 처리된(userHandleFl != 'r') 클레임이 있는지 여부
*
* @param array $statusCheckList 체크박스 value 목록
* (orderNo||orderGoodsSno||userHandleSno||cnt||originCnt)
* @return bool 하나라도 이미 처리된 건이 있으면 true
*/
public function hasAlreadyProcessed(array $statusCheckList): bool
{
$userHandleSnoList = $this->extractUserHandleSnoList($statusCheckList);
if (empty($userHandleSnoList)) {
return false;
}
 
return $this->orderUserHandleRepository->existsAlreadyProcessed($userHandleSnoList);
}
 
/**
* statusCheck value 목록에서 클레임 신청 sno(es_orderUserHandle.sno) 목록을 추출한다.
*
* statusCheck value 포맷: orderNo || orderGoodsSno || userHandleSno || cnt || originCnt
* (INT_DIVISION 구분, layout_order_goods_list.php 의 checkBoxCd 생성 규칙과 동일)
*
* @param array $statusCheckList
* @return int[]
*/
public function extractUserHandleSnoList(array $statusCheckList): array
{
$snoList = [];
foreach ($statusCheckList as $statusCheckValue) {
// 포맷: orderNo || orderGoodsSno || userHandleSno || cnt || originCnt (index 2 = userHandleSno)
$columns = explode(INT_DIVISION, (string) $statusCheckValue);
if (empty($columns[2])) {
continue;
}
$snoList[] = (int) $columns[2];
}
 
return array_values(array_unique(array_filter($snoList)));
}
}
 
Controller/Admin/Order/LayerUserAcceptController.php+11
변경 전LayerUserAcceptController.php
*/
namespace Bundle\Controller\Admin\Order;
 
 
use Exception;
use Request;
 
try {
// POST 리퀘스트
$postValue = Request::post()->toArray();
 
 
 
 
 
 
 
 
 
 
$this->setData('data', $postValue);
 
// 타이틀 설정
 
변경 후LayerUserAcceptController.php
*/
namespace Bundle\Controller\Admin\Order;
 
use App;
use Exception;
use Request;
 
try {
// POST 리퀘스트
$postValue = Request::post()->toArray();
 
// [#6126] 이미 승인/거절 처리된 신청 건이 포함된 경우 모달을 띄우지 않고 알럿 처리
$processGuard = App::getInstance(\Component\Order\UserHandleProcessGuard::class);
if ($processGuard->hasAlreadyProcessed($postValue['statusCheck'] ?? [])) {
$this->json([
'code' => 0,
'message' => __('이미 승인 또는 거절 처리가 된 신청 건입니다.') . "\n" . __('페이지 새로고침 후 확인해 주세요.'),
]);
}
 
$this->setData('data', $postValue);
 
// 타이틀 설정
 
Controller/Admin/Order/LayerUserRejectController.php+11
변경 전LayerUserRejectController.php
*/
namespace Bundle\Controller\Admin\Order;
 
 
use Request;
use Exception;
 
try {
// POST 리퀘스트
$postValue = Request::post()->toArray();
 
 
 
 
 
 
 
 
 
 
$this->setData('data', $postValue);
 
// 타이틀 설정
 
변경 후LayerUserRejectController.php
*/
namespace Bundle\Controller\Admin\Order;
 
use App;
use Request;
use Exception;
 
try {
// POST 리퀘스트
$postValue = Request::post()->toArray();
 
// [#6126] 이미 승인/거절 처리된 신청 건이 포함된 경우 모달을 띄우지 않고 알럿 처리
$processGuard = App::getInstance(\Component\Order\UserHandleProcessGuard::class);
if ($processGuard->hasAlreadyProcessed($postValue['statusCheck'] ?? [])) {
$this->json([
'code' => 0,
'message' => __('이미 승인 또는 거절 처리가 된 신청 건입니다.') . "\n" . __('페이지 새로고침 후 확인해 주세요.'),
]);
}
 
$this->setData('data', $postValue);
 
// 타이틀 설정
 
Repository/Order/OrderUserHandleRepository.php+17
변경 전OrderUserHandleRepository.php
->get()
->toArray();
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
변경 후OrderUserHandleRepository.php
->get()
->toArray();
}
 
/**
* 주어진 클레임 신청 sno 목록 중 이미 승인/거절 처리된(userHandleFl != 'r') 건이 존재하는지 확인
*
* 승인/거절 처리 직전 현재 상태를 재확인하여, stale 화면(목록 로드 이후 다른 관리자/
* 자동환불 등으로 상태가 변경된 경우)에서의 중복 처리를 차단하기 위해 사용한다.
*
* @param int[] $userHandleSnoList es_orderUserHandle.sno 목록
* @return bool 하나라도 신청(미처리, 'r') 이 아닌 건이 있으면 true
*/
public function existsAlreadyProcessed(array $userHandleSnoList): bool
{
return OrderUserHandle::query()
->whereIn('sno', array_map('intval', $userHandleSnoList))
->where('userHandleFl', '!=', UserHandleFl::REQUEST->value)
->exists();
}
}