삭제 요청 처리 기능 추가 및 오류 메시지 처리 강화

- 승인 요청 삭제 버튼 추가 및 확인 경고문 삽입
- 삭제 가능 여부 검증 로직 추가 및 네이티브 쿼리 대체 처리
- 오류 메시지 파싱 로직 및 상세 조회 실패 시 UI 처리 개선
This commit is contained in:
Rinjae
2026-08-24 13:16:39 +09:00
parent a6e88f2281
commit 0f36718534
3 changed files with 132 additions and 14 deletions
@@ -176,11 +176,52 @@
} }
}); });
$("#btn_app_delete").click(function () {
if (!confirm("이 승인 요청을 삭제하시겠습니까?\n삭제 후에는 복구할 수 없습니다.")) {
return;
}
$.ajax({
type: "POST",
url: url,
data: {cmd: "DELETE", id: key},
success: function (args) {
alert("삭제되었습니다.");
if (window.dialogArguments && window.dialogArguments.self) {
window.dialogArguments.self.location.reload();
} else if (window.opener) {
window.opener.location.reload();
}
window.close();
},
error: function (e) {
alert(approvalErrorMessage(e));
}
});
});
$("#btn_close").click(function () { $("#btn_close").click(function () {
window.close(); window.close();
}); });
}); });
function approvalErrorMessage(e) {
try {
var json = JSON.parse(e.responseText);
return json.errorMsg || e.responseText;
} catch (ex) {
return e.responseText;
}
}
// 요청자(PTL_USER) row 가 삭제되면 상세 조회 자체가 불가능하다. 이 경우 삭제만 가능하도록 노출한다.
function showLoadFailure(message) {
$("#approvalSubject").text("상세 정보를 조회할 수 없는 승인 요청");
$("#approvalDetail").text(message);
$("#btn_modify, #btn_app_approve, #btn_app_reject, #btn_app_force_reject, #btn_app_redeploy").hide();
$("#gwActionSection, #gwActionResult").hide();
document.getElementById("btn_app_delete").style.display = "";
}
var currentRequestType = null; // 현재 신청 유형 (DELETE 승인 시 GW 처리방식 선택 노출용) var currentRequestType = null; // 현재 신청 유형 (DELETE 승인 시 GW 처리방식 선택 노출용)
// 값이 있을 때만 행을 노출한다. (해지 신청처럼 해당 항목이 없는 유형에서 빈 행이 남지 않도록) // 값이 있을 때만 행을 노출한다. (해지 신청처럼 해당 항목이 없는 유형에서 빈 행이 남지 않도록)
@@ -344,7 +385,9 @@
$("#apisTable tbody").html(apisHtml); $("#apisTable tbody").html(apisHtml);
}, },
error: function (e) { error: function (e) {
alert(e.responseText); var message = approvalErrorMessage(e);
alert(message);
showLoadFailure(message);
} }
}); });
} }
@@ -454,6 +497,9 @@
<button type="button" class="cssbtn" style="display: none;" id="btn_app_force_reject" level="W"><i <button type="button" class="cssbtn" style="display: none;" id="btn_app_force_reject" level="W"><i
class="material-icons">block</i>강제 반려 class="material-icons">block</i>강제 반려
</button> </button>
<button type="button" class="cssbtn" style="display: none;" id="btn_app_delete" level="W"><i
class="material-icons">delete</i>삭제
</button>
<button type="button" class="cssbtn" style="display: none;" id="btn_app_redeploy" level="W"><i <button type="button" class="cssbtn" style="display: none;" id="btn_app_redeploy" level="W"><i
class="material-icons">close</i>재처리 class="material-icons">close</i>재처리
</button> </button>
@@ -181,7 +181,7 @@
}); });
$("#btn_user_delete").click(function() { $("#btn_user_delete").click(function() {
if (!confirm("삭제된 사용자 또는 법인이 포함되어 승인·반려할 수 없는 요청입니다.\n이 승인 요청을 삭제하시겠습니까?")) { if (!confirm("삭제된 요청자·사용자 또는 법인이 포함되어 승인·반려할 수 없는 요청입니다.\n이 승인 요청을 삭제하시겠습니까?\n삭제 후에는 복구할 수 없습니다.")) {
return; return;
} }
$.ajax({ $.ajax({
@@ -198,7 +198,7 @@
window.close(); window.close();
}, },
error: function(e) { error: function(e) {
alert(e.responseText); alert(approvalErrorMessage(e));
} }
}); });
}); });
@@ -362,10 +362,31 @@
} }
}, },
error: function(e) { error: function(e) {
alert(e.responseText); var message = approvalErrorMessage(e);
alert(message);
showLoadFailure(message);
} }
}); });
} }
function approvalErrorMessage(e) {
try {
var json = JSON.parse(e.responseText);
return json.errorMsg || e.responseText;
} catch (ex) {
return e.responseText;
}
}
// 요청자(PTL_USER) row 가 삭제되면 상세 조회 자체가 불가능하다. 이 경우 삭제만 가능하도록 노출한다.
function showLoadFailure(message) {
$("#approvalSubject").text("상세 정보를 조회할 수 없는 승인 요청");
$("#approvalDetail").text(message);
$("#approvalDetailRow").show();
$("#btn_unmask").hide();
$("#btn_user_approve, #btn_user_reject, #btn_user_force_reject, #btn_user_redeploy").hide();
document.getElementById("btn_user_delete").style.display = "";
}
function updateDetailView(data) { function updateDetailView(data) {
$("#approvalType").text(data.approvalType === 'USER' ? "법인사용자" : "API 사용"); $("#approvalType").text(data.approvalType === 'USER' ? "법인사용자" : "API 사용");
$("#approvalStatus").text(data.approvalStatus.description); $("#approvalStatus").text(data.approvalStatus.description);
@@ -468,8 +468,40 @@ public class PortalApprovalManService extends BaseService {
* 임의 삭제 방지를 위해 실제로 삭제된 사용자/법인이 포함된 USER 유형 요청만 허용한다. * 임의 삭제 방지를 위해 실제로 삭제된 사용자/법인이 포함된 USER 유형 요청만 허용한다.
*/ */
public void deleteApprovalRequest(String id) { public void deleteApprovalRequest(String id) {
Approval approval = approvalService.findById(id) Approval approval = null;
.orElseThrow(() -> new BizException("승인 정보를 찾을 수 없습니다. ID: " + id)); try {
approval = approvalService.findById(id).orElse(null);
} catch (Exception e) {
// 요청자/승인자 row 가 사라진 건은 엔티티 hydration 자체가 실패한다. 네이티브 조회로 우회한다.
logger.warn("Approval {} 엔티티 로딩 실패. 네이티브 조회로 대체합니다. 원인: {}", id, e.getMessage());
}
if (approval != null) {
assertDeletable(approval);
} else {
// Approval.requester 는 @JoinColumn(nullable=false) + @NotFound(IGNORE) 조합이라
// 요청자(PTL_USER) row 가 삭제되면 findById 가 빈 결과를 반환한다(조회·승인·반려 모두 불가).
assertDeletableByNativeQuery(id);
}
entityManager.createNativeQuery("DELETE FROM PTL_APPROVER WHERE APPROVAL_ID = :id")
.setParameter("id", id)
.executeUpdate();
int affected = entityManager.createNativeQuery("DELETE FROM PTL_APPROVAL WHERE ID = :id")
.setParameter("id", id)
.executeUpdate();
if (affected == 0) {
throw new BizException("승인 정보를 찾을 수 없습니다. ID: " + id);
}
logger.info("Approval {} deleted (deleted requester/target user/org present)", id);
}
/** 엔티티 로딩이 가능한 건: 요청자 삭제이거나, 대상 사용자·법인이 삭제된 법인사용자 요청만 삭제 허용 */
private void assertDeletable(Approval approval) {
boolean requesterDeleted = approval.getRequester() == null;
if (requesterDeleted) {
return;
}
if (!approval.getApprovalType().equals(ApprovalType.USER)) { if (!approval.getApprovalType().equals(ApprovalType.USER)) {
throw new BizException("법인사용자 승인 요청만 삭제할 수 있습니다."); throw new BizException("법인사용자 승인 요청만 삭제할 수 있습니다.");
} }
@@ -486,19 +518,38 @@ public class PortalApprovalManService extends BaseService {
|| "삭제된법인".equals(org.getOrgName()); || "삭제된법인".equals(org.getOrgName());
} }
if (!(userDeleted || orgDeleted)) { if (!(userDeleted || orgDeleted)) {
throw new BizException("삭제된 사용자 또는 법인이 포함된 요청만 삭제할 수 있습니다."); throw new BizException("삭제된 요청자·사용자 또는 법인이 포함된 요청만 삭제할 수 있습니다.");
} }
}
entityManager.createNativeQuery("DELETE FROM PTL_APPROVER WHERE APPROVAL_ID = :id") /** 엔티티 로딩이 불가능한 건: PTL_APPROVAL 원본 row 를 직접 읽어 요청자/대상 사용자 삭제 여부를 확인한다. */
@SuppressWarnings("unchecked")
private void assertDeletableByNativeQuery(String id) {
List<Object[]> rows = entityManager.createNativeQuery(
"SELECT APPROVAL_TYPE, TARGET_ID, REQUESTER_ID FROM PTL_APPROVAL WHERE ID = :id")
.setParameter("id", id) .setParameter("id", id)
.executeUpdate(); .getResultList();
int affected = entityManager.createNativeQuery("DELETE FROM PTL_APPROVAL WHERE ID = :id") if (rows.isEmpty()) {
.setParameter("id", id)
.executeUpdate();
if (affected == 0) {
throw new BizException("승인 정보를 찾을 수 없습니다. ID: " + id); throw new BizException("승인 정보를 찾을 수 없습니다. ID: " + id);
} }
logger.info("Approval {} deleted (deleted target user/org present)", id); Object[] row = rows.get(0);
String approvalType = row[0] == null ? null : String.valueOf(row[0]);
String targetId = row[1] == null ? null : String.valueOf(row[1]);
String requesterId = row[2] == null ? null : String.valueOf(row[2]);
boolean requesterDeleted = requesterId == null || !existsPortalUser(requesterId);
boolean targetUserDeleted = ApprovalType.USER.name().equals(approvalType)
&& (targetId == null || !existsPortalUser(targetId));
if (!(requesterDeleted || targetUserDeleted)) {
throw new BizException("삭제된 요청자·사용자가 포함된 요청만 삭제할 수 있습니다.");
}
}
private boolean existsPortalUser(String userId) {
Number count = (Number) entityManager.createNativeQuery("SELECT COUNT(*) FROM PTL_USER WHERE ID = :id")
.setParameter("id", userId)
.getSingleResult();
return count != null && count.intValue() > 0;
} }
public void sendEvent(String id, ApprovalEvent event, Map<String, Object> options) { public void sendEvent(String id, ApprovalEvent event, Map<String, Object> options) {