From 0f28dd9ef8c27eff13f9402e02a65edc0167ab40 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Thu, 5 Mar 2026 15:01:27 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=9C=EB=B0=9C=EC=9E=90=ED=8F=AC=ED=83=88?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90/=EB=B2=95=EC=9D=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=99=84=EC=A0=84=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsp/onl/apim/portalorg/portalOrgMan.jsp | 129 +++++++++++++++- .../jsp/onl/apim/portaluser/portalUserMan.jsp | 144 +++++++++++++----- .../onl/apim/approval/ApprovalService.java | 6 + .../portalinquiry/PortalInquiryService.java | 15 ++ .../onl/apim/portalorg/PortalOrgService.java | 9 +- .../onl/apim/portalorg/PortalOrgUISearch.java | 4 +- .../apim/portaluser/PortalUserService.java | 15 +- .../apim/portaluser/PortalUserUISearch.java | 2 + .../approval/PortalApprovalManService.java | 73 ++++++++- .../portalorg/PortalOrgManController.java | 86 +++++++++++ .../apim/portalorg/PortalOrgManService.java | 25 +++ .../portaluser/PortalUserManController.java | 60 ++++++++ .../apim/portaluser/PortalUserManService.java | 26 ++++ 13 files changed, 540 insertions(+), 54 deletions(-) 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 = ''; var url_view = ''; var select_approvalStatus = ""; var select_status = ""; -/* var select_orgType = ""; */ +var isProdServer = <%= isProdServer %>; +var gridRowDataMap = {}; function getGridSelectText(key, value, data) { if (!Array.isArray(data) || data.length === 0) { @@ -34,9 +37,104 @@ function getGridSelectText(key, value, data) { } function formatCompanyRegNo(text) { + if (!text) return ""; return text.replace(/(\d{3})(\d{2})(\d{5})/, "$1-$2-$3"); } +/* 선택된 법인 삭제 - 상태에 따라 Soft/Hard Delete 자동 분기 */ +function deleteSelectedOrgs() { + var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow'); + + if (selectedIds.length === 0) { + showAlert("삭제할 법인을 선택해주세요.", {type: 'warning'}); + return; + } + + // 선택된 항목을 REMOVED / 비REMOVED로 분류 + var softDeleteIds = []; + var hardDeleteIds = []; + for (var i = 0; i < selectedIds.length; i++) { + var rowRaw = gridRowDataMap[selectedIds[i]]; + if (rowRaw && rowRaw.orgStatus === 'REMOVED') { + hardDeleteIds.push(selectedIds[i]); + } else { + softDeleteIds.push(selectedIds[i]); + } + } + + // 비운영 환경이 아니면 Hard Delete 불가 + if (isProdServer && hardDeleteIds.length > 0) { + showAlert("운영 환경에서는 삭제(탈퇴) 상태인 항목을 완전삭제할 수 없습니다.", {type: 'error'}); + return; + } + + 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 { + confirmAndExecute(); + } + } + }); +} + function init(){ $.ajax({ type : "POST", @@ -110,6 +208,8 @@ function initializeGrid() { autowidth: true, viewrecords: true, rowList : eval('[${rmsDefaultRowList}]'), + multiselect: true, + multiboxonly: true, loadComplete:function (d){ var records = $(this).getGridParam('records'); var page = $(this).getGridParam('page'); @@ -119,12 +219,26 @@ function initializeGrid() { 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].orgStatus === '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); @@ -169,6 +283,10 @@ $(document).ready(function() { goNav(url2); }); + $("#btn_delete_selected").click(function(){ + deleteSelectedOrgs(); + }); + $("input[name^=search]").keydown(function(key){ if (key.keyCode == 13){ $("#btn_search").click(); @@ -194,10 +312,16 @@ $(document).ready(function() {
+
<%--법인정보관리--%>
<%= 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에서 영구 삭제됩니다. +
+ <% } %>
@@ -292,21 +372,6 @@ - <%-- - - - - - --%>
<%= localeMessage.getString("portalUser.approvalStatus") %> -
- -
-
<%= localeMessage.getString("portalUser.createOn")%> - - ~ - - -
@@ -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> deleteMultiple(String ids) { + Map resultMap = new HashMap<>(); + try { + if (StringUtils.isBlank(ids)) { + resultMap.put("status", "fail"); + resultMap.put("message", "삭제할 법인을 선택해주세요."); + return ResponseEntity.ok(resultMap); + } + + String[] idArray = ids.split(","); + for (String id : idArray) { + if (StringUtils.isNotBlank(id.trim())) { + portalOrgManService.removeOrg(id.trim()); + } + } + + resultMap.put("status", "success"); + resultMap.put("message", idArray.length + "개의 법인이 삭제되었습니다."); + } catch (Exception e) { + resultMap.put("status", "fail"); + resultMap.put("message", "삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return ResponseEntity.ok(resultMap); + } + + @PostMapping(value = "/onl/apim/portalorg/portalOrgMan.json", params = "cmd=HARD_DELETE") + public ResponseEntity> hardDelete(String id) { + Map resultMap = new HashMap<>(); + try { + portalOrgManService.hardDeleteOrg(id); + resultMap.put("status", "success"); + resultMap.put("message", "완전삭제가 완료되었습니다."); + } catch (BizException e) { + resultMap.put("status", "fail"); + resultMap.put("message", e.getMessage()); + } catch (Exception e) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return ResponseEntity.ok(resultMap); + } + + @PostMapping(value = "/onl/apim/portalorg/portalOrgMan.json", params = "cmd=HARD_DELETE_MULTIPLE") + public ResponseEntity> hardDeleteMultiple(String ids) { + Map resultMap = new HashMap<>(); + try { + if (StringUtils.isBlank(ids)) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제할 법인을 선택해주세요."); + return ResponseEntity.ok(resultMap); + } + + String[] idArray = ids.split(","); + int successCount = 0; + List failMessages = new java.util.ArrayList<>(); + + for (String id : idArray) { + if (StringUtils.isNotBlank(id.trim())) { + try { + portalOrgManService.hardDeleteOrg(id.trim()); + successCount++; + } catch (BizException e) { + failMessages.add(e.getMessage()); + } + } + } + + if (failMessages.isEmpty()) { + resultMap.put("status", "success"); + resultMap.put("message", successCount + "개의 법인이 완전삭제되었습니다."); + } else { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 실패: " + String.join(", ", failMessages) + + (successCount > 0 ? " (" + successCount + "건 성공)" : "")); + } + } catch (Exception e) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return ResponseEntity.ok(resultMap); + } + @PostMapping(value = "/onl/apim/portalorg/portalOrgMan.json", params = "cmd=POPUP_LIST") public ResponseEntity> selectOrgList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable, PortalOrgUISearch portalOrgUISearch) { diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManService.java index b13d60c..65d6db6 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/portalorg/PortalOrgManService.java @@ -8,6 +8,7 @@ import com.eactive.apim.portal.portalorg.entity.PortalOrg; import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus; import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus; import com.eactive.apim.portal.portaluser.entity.PortalUser; +import com.eactive.eai.common.util.SystemUtil; import com.eactive.eai.common.util.UUID; import com.eactive.eai.rms.common.base.BaseService; import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService; @@ -29,7 +30,9 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -134,6 +137,28 @@ public class PortalOrgManService extends BaseService { portalOrgService.save(portalOrg); } + public void hardDeleteOrg(String id) { + if (SystemUtil.isProdServer()) { + throw new BizException("운영 환경에서는 완전삭제를 수행할 수 없습니다."); + } + PortalOrg portalOrg = portalOrgService.getById(id); + if (portalOrg.getOrgStatus() != OrgStatus.REMOVED) { + throw new BizException("삭제(탈퇴) 상태인 법인만 완전삭제할 수 있습니다."); + } + + List dependencies = new ArrayList<>(); + long userCount = portalUserService.countByOrgId(id); + if (userCount > 0) dependencies.add("사용자 " + userCount + "건"); + long appRequestCount = portalOrgService.countAppRequestByOrgId(id); + if (appRequestCount > 0) dependencies.add("앱 신청 " + appRequestCount + "건"); + + if (!dependencies.isEmpty()) { + throw new BizException("의존 데이터가 존재하여 완전삭제할 수 없습니다: " + String.join(", ", dependencies)); + } + + portalOrgService.deleteById(id); + } + public Map selectDetailWithUsers(String id) { return getDetailWithUsers(id, true); } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java index 9ed9474..2669a8f 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManController.java @@ -4,6 +4,7 @@ import java.util.*; import java.util.stream.Collectors; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums; +import com.eactive.eai.common.util.SystemUtil; import com.eactive.eai.rms.onl.common.exception.BizException; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; @@ -43,6 +44,7 @@ public class PortalUserManController extends BaseAnnotationController { @PostMapping(value = "/onl/apim/portaluser/portalUserMan.json", params = "cmd=LIST") public ResponseEntity> selectList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable, PortalUserUISearch portalUserUISearch) { + portalUserUISearch.setIncludeRemoved(!SystemUtil.isProdServer()); Page page = portalUserManService.selectList(pageable, portalUserUISearch); return ResponseEntity.ok(new GridResponse<>(page)); } @@ -99,6 +101,7 @@ public class PortalUserManController extends BaseAnnotationController { resultMap.put("approvalStatusRows", approvalStatusList); resultMap.put("userStatusRows", userStatusList); resultMap.put("roleCodeRows", roleCodeList); + resultMap.put("isProdServer", SystemUtil.isProdServer()); return ResponseEntity.ok(resultMap); } @@ -146,6 +149,63 @@ public class PortalUserManController extends BaseAnnotationController { return ResponseEntity.ok(resultMap); } + @PostMapping(value = "/onl/apim/portaluser/portalUserMan.json", params = "cmd=HARD_DELETE") + public ResponseEntity> hardDelete(String id) { + Map resultMap = new HashMap<>(); + try { + portalUserManService.hardDeleteUser(id); + resultMap.put("status", "success"); + resultMap.put("message", "완전삭제가 완료되었습니다."); + } catch (BizException e) { + resultMap.put("status", "fail"); + resultMap.put("message", e.getMessage()); + } catch (Exception e) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return ResponseEntity.ok(resultMap); + } + + @PostMapping(value = "/onl/apim/portaluser/portalUserMan.json", params = "cmd=HARD_DELETE_MULTIPLE") + public ResponseEntity> hardDeleteMultiple(String ids) { + Map resultMap = new HashMap<>(); + try { + if (StringUtils.isBlank(ids)) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제할 사용자를 선택해주세요."); + return ResponseEntity.ok(resultMap); + } + + String[] idArray = ids.split(","); + int successCount = 0; + List failMessages = new ArrayList<>(); + + for (String id : idArray) { + if (StringUtils.isNotBlank(id.trim())) { + try { + portalUserManService.hardDeleteUser(id.trim()); + successCount++; + } catch (BizException e) { + failMessages.add(e.getMessage()); + } + } + } + + if (failMessages.isEmpty()) { + resultMap.put("status", "success"); + resultMap.put("message", successCount + "명의 사용자가 완전삭제되었습니다."); + } else { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 실패: " + String.join(", ", failMessages) + + (successCount > 0 ? " (" + successCount + "건 성공)" : "")); + } + } catch (Exception e) { + resultMap.put("status", "fail"); + resultMap.put("message", "완전삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return ResponseEntity.ok(resultMap); + } + @PostMapping(value = "/onl/apim/portaluser/portalUserMan.json", params = "cmd=CHECK_ID") public ResponseEntity checkDuplicateId(String loginId) { String result = portalUserManService.checkDuplicateId(loginId) ? "success" : ""; diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java index 028a040..75b9197 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/portaluser/PortalUserManService.java @@ -7,8 +7,11 @@ import com.eactive.apim.portal.portalorg.entity.PortalOrg; import com.eactive.apim.portal.portaluser.entity.PortalUser; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums; import com.eactive.apim.portal.portaluser.entity.PortalUserPrivacyAgreement; +import com.eactive.eai.common.util.SystemUtil; import com.eactive.eai.rms.common.base.BaseService; import com.eactive.eai.rms.common.spring.LocaleMessage; +import com.eactive.eai.rms.data.entity.onl.apim.approval.ApprovalService; +import com.eactive.eai.rms.data.entity.onl.apim.portalinquiry.PortalInquiryService; import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService; import com.eactive.eai.rms.data.entity.onl.apim.portaluser.PortalUserService; import com.eactive.eai.rms.data.entity.onl.apim.portaluser.PortalUserUISearch; @@ -27,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; @Service @@ -44,6 +48,8 @@ public class PortalUserManService extends BaseService { // private final PasswordEncoder passwordEncoder; private final PasswordEncoder kjbSafedbPasswordEncoder; private final PortalUserTermsService portalUserTermsService; + private final ApprovalService approvalService; + private final PortalInquiryService portalInquiryService; public Page selectList(Pageable pageable, PortalUserUISearch portalUserUISearch) { @@ -191,6 +197,26 @@ public class PortalUserManService extends BaseService { return !portalUserService.existsByLoginId(loginId); } + public void hardDeleteUser(String id) { + if (SystemUtil.isProdServer()) { + throw new BizException("운영 환경에서는 완전삭제를 수행할 수 없습니다."); + } + PortalUser portalUser = portalUserService.getById(id); + if (portalUser.getUserStatus() != PortalUserEnums.UserStatus.REMOVED) { + throw new BizException("삭제(탈퇴) 상태인 사용자만 완전삭제할 수 있습니다."); + } + + List dependencies = new ArrayList<>(); + long messageCount = portalUserService.countMessageRequestByUserId(id); + if (messageCount > 0) dependencies.add("메시지 " + messageCount + "건"); + + if (!dependencies.isEmpty()) { + throw new BizException("의존 데이터가 존재하여 완전삭제할 수 없습니다: " + String.join(", ", dependencies)); + } + + portalUserService.deleteById(id); + } + public void passwordReset(String id) { PortalUser portalUser = portalUserService.getById(id);