diff --git a/WebContent/jsp/onl/apim/portalorg/portalOrgMan.jsp b/WebContent/jsp/onl/apim/portalorg/portalOrgMan.jsp
index 1549585..0fdca7e 100644
--- a/WebContent/jsp/onl/apim/portalorg/portalOrgMan.jsp
+++ b/WebContent/jsp/onl/apim/portalorg/portalOrgMan.jsp
@@ -1,5 +1,6 @@
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ page import="java.io.*"%>
+<%@ page import="com.eactive.eai.common.util.SystemUtil"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
@@ -8,6 +9,7 @@
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
+ boolean isProdServer = SystemUtil.isProdServer();
%>
@@ -20,7 +22,8 @@ var url = '
+
<%--법인정보관리--%>
<%= localeMessage.getString("portalOrg.title") %>
+ <% if (!isProdServer) { %>
+
+ [비운영 환경] 삭제된(탈퇴) 데이터가 포함되어 표시됩니다. 탈퇴 상태 항목 선택 시 DB에서 영구 삭제됩니다.
+
+ <% } %>
@@ -235,4 +359,3 @@ $(document).ready(function() {
-
diff --git a/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp b/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
index 7976913..95ddbbb 100644
--- a/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
+++ b/WebContent/jsp/onl/apim/portaluser/portalUserMan.jsp
@@ -1,5 +1,6 @@
<%@ page language="java" contentType="text/html; charset=utf-8" %>
<%@ page import="java.io.*" %>
+<%@ page import="com.eactive.eai.common.util.SystemUtil"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
@@ -8,6 +9,7 @@
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
+ boolean isProdServer = SystemUtil.isProdServer();
%>
@@ -22,41 +24,101 @@
var url_view = '';
var select_approvalStatus = "";
var select_userStatus = "";
+ var isProdServer = <%= isProdServer %>;
- /* 선택된 사용자 일괄 삭제 */
+ // 원본 행 데이터를 ID로 조회하기 위한 맵
+ var gridRowDataMap = {};
+
+ /* 선택된 사용자 삭제 - 상태에 따라 Soft/Hard Delete 자동 분기 */
function deleteSelectedUsers() {
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
if (selectedIds.length === 0) {
- alert("삭제할 사용자를 선택해주세요.");
+ showAlert("삭제할 사용자를 선택해주세요.", {type: 'warning'});
return;
}
- if (!confirm("선택된 " + selectedIds.length + "명의 사용자를 삭제하시겠습니까?\n이 작업은 되돌릴 수 없습니다.")) {
+ // 선택된 항목을 REMOVED / 비REMOVED로 분류
+ var softDeleteIds = [];
+ var hardDeleteIds = [];
+ for (var i = 0; i < selectedIds.length; i++) {
+ var rowRaw = gridRowDataMap[selectedIds[i]];
+ if (rowRaw && rowRaw.userStatus === 'REMOVED') {
+ hardDeleteIds.push(selectedIds[i]);
+ } else {
+ softDeleteIds.push(selectedIds[i]);
+ }
+ }
+
+ // 비운영 환경이 아니면 Hard Delete 불가
+ if (isProdServer && hardDeleteIds.length > 0) {
+ showAlert("운영 환경에서는 삭제(탈퇴) 상태인 항목을 완전삭제할 수 없습니다.", {type: 'error'});
return;
}
- $.ajax({
- type: "POST",
- url: url,
- dataType: "json",
- data: {
- cmd: 'DELETE_MULTIPLE',
- ids: selectedIds.join(',')
- },
- success: function (data) {
- if (data.status === "success") {
- alert(data.message || "선택된 사용자가 삭제되었습니다.");
- $("#grid").trigger("reloadGrid");
- // 체크박스 초기화
- $("#grid").jqGrid('clearGridData', true);
- $("#grid").trigger("reloadGrid");
+ var messages = [];
+ if (softDeleteIds.length > 0) messages.push(softDeleteIds.length + "명 삭제(탈퇴 처리)");
+ if (hardDeleteIds.length > 0) messages.push(hardDeleteIds.length + "명 완전삭제(DB 영구 삭제)");
+
+ var confirmMsg = "선택된 사용자: " + messages.join(", ") + "\n진행하시겠습니까?";
+ var confirmType = hardDeleteIds.length > 0 ? 'error' : 'warning';
+
+ showConfirm(confirmMsg, {
+ type: confirmType,
+ onConfirm: function() {
+ var confirmAndExecute = function() {
+ var completed = 0;
+ var total = (softDeleteIds.length > 0 ? 1 : 0) + (hardDeleteIds.length > 0 ? 1 : 0);
+ var resultMessages = [];
+ var hasError = false;
+
+ function checkDone() {
+ completed++;
+ if (completed >= total) {
+ var finalType = hasError ? 'error' : 'success';
+ showAlert(resultMessages.join("\n"), {type: finalType, onClose: function() {
+ $("#grid").trigger("reloadGrid");
+ }});
+ }
+ }
+
+ if (softDeleteIds.length > 0) {
+ $.ajax({
+ type: "POST", url: url, dataType: "json",
+ data: { cmd: 'DELETE_MULTIPLE', ids: softDeleteIds.join(',') },
+ success: function(data) {
+ resultMessages.push(data.message);
+ if (data.status !== "success") hasError = true;
+ checkDone();
+ },
+ error: function(e) { hasError = true; resultMessages.push("삭제 오류: " + e.responseText); checkDone(); }
+ });
+ }
+
+ if (hardDeleteIds.length > 0) {
+ $.ajax({
+ type: "POST", url: url, dataType: "json",
+ data: { cmd: 'HARD_DELETE_MULTIPLE', ids: hardDeleteIds.join(',') },
+ success: function(data) {
+ resultMessages.push(data.message);
+ if (data.status !== "success") hasError = true;
+ checkDone();
+ },
+ error: function(e) { hasError = true; resultMessages.push("완전삭제 오류: " + e.responseText); checkDone(); }
+ });
+ }
+ };
+
+ // Hard Delete 포함 시 2차 확인
+ if (hardDeleteIds.length > 0) {
+ showConfirm("최종 확인: 완전삭제는 되돌릴 수 없습니다. 정말로 실행하시겠습니까?", {
+ type: 'error',
+ title: '최종 확인',
+ onConfirm: confirmAndExecute
+ });
} else {
- alert(data.message || "삭제 중 오류가 발생했습니다.");
+ confirmAndExecute();
}
- },
- error: function (e) {
- alert("오류: " + e.responseText);
}
});
}
@@ -168,12 +230,26 @@
for (var i = 0; i < rows.length; i++) {
var number = ((page - 1) * rowNum + i) + 1;
- // var reverseNumber = records - ((page - 1) * rowNum + i);
$(this).setCell(rows[i], 'rowNum', number);
}
for (var i = 0; i < colModel.length; i++) {
$(this).setColProp(colModel[i].name, {sortable: false});
}
+
+ // 원본 행 데이터 맵 구성 및 REMOVED 행 스타일링
+ if (d && d.rows) {
+ gridRowDataMap = {};
+ for (var j = 0; j < d.rows.length; j++) {
+ gridRowDataMap[d.rows[j].id] = d.rows[j];
+ if (!isProdServer && d.rows[j].userStatus === 'REMOVED') {
+ $('#grid tr#' + $.jgrid.jqID(d.rows[j].id)).css({
+ 'background-color': '#f8d7da',
+ 'text-decoration': 'line-through',
+ 'opacity': '0.7'
+ });
+ }
+ }
+ }
},
ondblClickRow: function (rowId) {
var rowData = $(this).getRowData(rowId);
@@ -198,7 +274,6 @@
init();
$("#btn_search").click(function () {
- //var searchMobileNumber = $("#searchMobileNumber").val().trim();
var postData = getSearchForJqgrid("cmd", "LIST");
$("#grid").setGridParam({url: url, postData: postData, page: 1}).trigger("reloadGrid");
@@ -253,6 +328,11 @@
<%= localeMessage.getString("portalUser.title") %>
+ <% if (!isProdServer) { %>
+
+ [비운영 환경] 삭제된(탈퇴) 데이터가 포함되어 표시됩니다. 탈퇴 상태 항목 선택 시 DB에서 영구 삭제됩니다.
+
+ <% } %>
@@ -317,4 +382,3 @@
-
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/approval/ApprovalService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/approval/ApprovalService.java
index 80e5b72..4a956ae 100644
--- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/approval/ApprovalService.java
+++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/approval/ApprovalService.java
@@ -40,4 +40,10 @@ public class ApprovalService extends AbstractEMSDataSerivce ids) {
repository.deleteAllById(ids);
}
+
+ public long countByInquirerId(String userId) {
+ return getJPAQueryFactory().selectFrom(QInquiry.inquiry)
+ .where(QInquiry.inquiry.inquirer.id.eq(userId))
+ .fetchCount();
+ }
+
+ public void deleteByInquirerId(String userId) {
+ List inquiries = getJPAQueryFactory().selectFrom(QInquiry.inquiry)
+ .where(QInquiry.inquiry.inquirer.id.eq(userId))
+ .fetch();
+ if (!inquiries.isEmpty()) {
+ repository.deleteAll(inquiries);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portalorg/PortalOrgService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portalorg/PortalOrgService.java
index 60fe515..6ed2e0a 100644
--- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portalorg/PortalOrgService.java
+++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/portalorg/PortalOrgService.java
@@ -1,5 +1,6 @@
package com.eactive.eai.rms.data.entity.onl.apim.portalorg;
+import com.eactive.apim.portal.apprequest.entity.QAppRequest;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums;
import com.eactive.apim.portal.portalorg.entity.QPortalOrg;
@@ -27,7 +28,7 @@ public class PortalOrgService extends AbstractEMSDataSerivce selectList(Pageable pageable, PortalApprovalUISearch portalApprovalUISearch) {
Page approval = approvalService.findAll(pageable, portalApprovalUISearch);
- return approval.map(entity -> getDetailWithMaskOption(entity.getId(), false));
+ return approval.map(entity -> {
+ try {
+ return getDetailWithMaskOption(entity.getId(), false);
+ } catch (Exception e) {
+ logger.error("승인 상세 조회 실패 - ID: {}, 오류: {}", entity.getId(), e.getMessage());
+ return null;
+ }
+ });
}
/**
* 마스킹된 상세 정보 조회
*/
public PortalApprovalUI selectDetail(String id) {
- return getDetailWithMaskOption(id, false);
+ PortalApprovalUI result = getDetailWithMaskOption(id, false);
+ if (result == null) {
+ throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
+ }
+ return result;
}
/**
* 마스킹 해제된 상세 정보 조회
*/
public PortalApprovalUI selectDetailUnmask(String id) {
- return getDetailWithMaskOption(id, false);
+ PortalApprovalUI result = getDetailWithMaskOption(id, false);
+ if (result == null) {
+ throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
+ }
+ return result;
}
private PortalApprovalUI getDetailWithMaskOption(String id, boolean isMasked) {
- Approval approval = approvalService.getById(id);
+ Optional optApproval = approvalService.findById(id);
+ if (!optApproval.isPresent()) {
+ return null;
+ }
+ Approval approval = optApproval.get();
if (approval.getApprovalType().equals(ApprovalType.USER)) {
PortalUserApprovalUI userApprovalUI = portalUserApprovalUIMapper.toVo(approval);
+ // 삭제된 요청자 처리
+ if (userApprovalUI.getRequester() == null) {
+ userApprovalUI.setRequester(createDeletedUserPlaceholder());
+ }
+
// 요청자 정보 마스킹 처리
if (userApprovalUI.getRequester() != null && isMasked) {
userApprovalUI.getRequester().setUserName(
@@ -115,12 +140,23 @@ public class PortalApprovalManService extends BaseService {
}
userApprovalUI.setPortalUser(userUI);
});
- userApprovalUI.getApprovers().forEach(approvers -> approvers.getUser().setUserName(MaskingUtils.maskName(approvers.getUser().getUserName())));
+ userApprovalUI.getApprovers().forEach(approver -> {
+ if (approver.getUser() == null) {
+ approver.setUser(createDeletedApproverPlaceholder());
+ } else {
+ approver.getUser().setUserName(MaskingUtils.maskName(approver.getUser().getUserName()));
+ }
+ });
return userApprovalUI;
} else if (approval.getApprovalType().equals(ApprovalType.APP)) {
PortalAppApprovalUI appApprovalUI = portalAppApprovalUIMapper.toVo(approval);
+ // 삭제된 요청자 처리
+ if (appApprovalUI.getRequester() == null) {
+ appApprovalUI.setRequester(createDeletedUserPlaceholder());
+ }
+
// 요청자 정보 마스킹 처리
if (appApprovalUI.getRequester() != null && isMasked) {
appApprovalUI.getRequester().setUserName(
@@ -173,9 +209,13 @@ public class PortalApprovalManService extends BaseService {
appApprovalUI.setApprovalDetail(appRequest.getReason());
appApprovalUI.setClientName(appRequest.getClientName());
- appApprovalUI.getApprovers().forEach(approverList ->
- approverList.getUser().setUserName(MaskingUtils.maskName(approverList.getUser().getUserName()))
- );
+ appApprovalUI.getApprovers().forEach(approver -> {
+ if (approver.getUser() == null) {
+ approver.setUser(createDeletedApproverPlaceholder());
+ } else {
+ approver.getUser().setUserName(MaskingUtils.maskName(approver.getUser().getUserName()));
+ }
+ });
}
return appApprovalUI;
@@ -238,6 +278,23 @@ public class PortalApprovalManService extends BaseService {
// appApprovalUI.setClientName(appRequest.getClientName());
// }
+ private PortalUserUI createDeletedUserPlaceholder() {
+ PortalUserUI placeholder = new PortalUserUI();
+ placeholder.setUserName("삭제된 사용자");
+ placeholder.setLoginId("");
+ placeholder.setEmailAddr("");
+ placeholder.setMobileNumber("");
+ placeholder.setPhoneNumber("");
+ return placeholder;
+ }
+
+ private UserUI createDeletedApproverPlaceholder() {
+ UserUI placeholder = new UserUI();
+ placeholder.setUserName("삭제된 사용자");
+ placeholder.setUserId("");
+ return placeholder;
+ }
+
public void approve(String id, String approverId) {
Map options = new HashMap<>();
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManController.java b/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManController.java
index 2783e36..3a37e5f 100644
--- a/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManController.java
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManController.java
@@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import com.eactive.eai.common.util.SystemUtil;
import com.eactive.eai.rms.onl.common.exception.BizException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -50,6 +51,7 @@ public class PortalOrgManController extends BaseAnnotationController {
@PostMapping(value = "/onl/apim/portalorg/portalOrgMan.json", params = "cmd=LIST")
public ResponseEntity> selectList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
PortalOrgUISearch portalOrgUISearch) {
+ portalOrgUISearch.setIncludeRemoved(!SystemUtil.isProdServer());
Page page = portalOrgManService.selectList(pageable, portalOrgUISearch);
return ResponseEntity.ok(new GridResponse<>(page));
}
@@ -69,6 +71,7 @@ public class PortalOrgManController extends BaseAnnotationController {
Map resultMap = new HashMap<>();
resultMap.put("approvalStatusRows", approvalStatusList);
resultMap.put("orgStatusRows", orgStatusList);
+ resultMap.put("isProdServer", SystemUtil.isProdServer());
return ResponseEntity.ok(resultMap);
}
@@ -101,6 +104,89 @@ public class PortalOrgManController extends BaseAnnotationController {
return ResponseEntity.ok().build();
}
+ @PostMapping(value = "/onl/apim/portalorg/portalOrgMan.json", params = "cmd=DELETE_MULTIPLE")
+ public ResponseEntity