개발자포탈 사용자/법인 삭제 기능 개선 및 완전삭제 기능 추가

This commit is contained in:
Rinjae
2026-03-05 15:01:27 +09:00
parent 2ff7562cd8
commit 0f28dd9ef8
13 changed files with 540 additions and 54 deletions
@@ -40,4 +40,10 @@ public class ApprovalService extends AbstractEMSDataSerivce<Approval, String, Ap
return repository.findAll(predicate, pageable);
}
public long countByRequesterId(String userId) {
return getJPAQueryFactory().selectFrom(QApproval.approval)
.where(QApproval.approval.requester.id.eq(userId))
.fetchCount();
}
}
@@ -43,4 +43,19 @@ public class PortalInquiryService extends AbstractEMSDataSerivce<Inquiry, String
public void deleteByIds(List<String> 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<Inquiry> inquiries = getJPAQueryFactory().selectFrom(QInquiry.inquiry)
.where(QInquiry.inquiry.inquirer.id.eq(userId))
.fetch();
if (!inquiries.isEmpty()) {
repository.deleteAll(inquiries);
}
}
}
@@ -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<PortalOrg, String,
// 기본 list 조회시엔 REMOVED 상태 아닌 데이터만 조회
if (StringUtils.isNotBlank(portalOrgUISearch.getSearchOrgStatus())){
predicate.and(qPortalOrg.orgStatus.eq(PortalOrgEnums.OrgStatus.valueOf(portalOrgUISearch.getSearchOrgStatus())));
} else {
} else if (!portalOrgUISearch.isIncludeRemoved()) {
predicate.and(qPortalOrg.orgStatus.ne(PortalOrgEnums.OrgStatus.REMOVED));
}
@@ -62,4 +63,10 @@ public class PortalOrgService extends AbstractEMSDataSerivce<PortalOrg, String,
repository.findAll(predicate).spliterator(), false)
.collect(Collectors.toList());
}
public long countAppRequestByOrgId(String orgId) {
return getJPAQueryFactory().selectFrom(QAppRequest.appRequest)
.where(QAppRequest.appRequest.org.id.eq(orgId))
.fetchCount();
}
}
@@ -16,5 +16,7 @@ public class PortalOrgUISearch {
private String searchEndYYYYMMDD;
private String searchStartDate;
private String searchEndDate;
private boolean includeRemoved;
}
@@ -4,6 +4,7 @@ import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
import com.eactive.apim.portal.portaluser.entity.QPortalUser;
import com.eactive.apim.portal.portaluser.repository.PortalUserRepository;
import com.eactive.apim.portal.template.entity.QMessageRequest;
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
@@ -26,7 +27,7 @@ public class PortalUserService extends AbstractEMSDataSerivce<PortalUser, String
if (StringUtils.isNotBlank(portalUserUISearch.getSearchUserStatus())) {
predicate.and(qPortalUser.userStatus.eq(PortalUserEnums.UserStatus.valueOf(portalUserUISearch.getSearchUserStatus())));
} else {
} else if (!portalUserUISearch.isIncludeRemoved()) {
predicate.and(qPortalUser.userStatus.ne(PortalUserEnums.UserStatus.REMOVED));
}
@@ -84,4 +85,16 @@ public class PortalUserService extends AbstractEMSDataSerivce<PortalUser, String
.orderBy(qPortalUser.createdDate.desc())
.fetchFirst();
}
public long countByOrgId(String orgId) {
return getJPAQueryFactory().selectFrom(QPortalUser.portalUser)
.where(QPortalUser.portalUser.portalOrg.id.eq(orgId))
.fetchCount();
}
public long countMessageRequestByUserId(String userId) {
return getJPAQueryFactory().selectFrom(QMessageRequest.messageRequest)
.where(QMessageRequest.messageRequest.user.id.eq(userId))
.fetchCount();
}
}
@@ -21,4 +21,6 @@ public class PortalUserUISearch {
private String searchStartDate;
private String searchEndDate;
private boolean includeRemoved;
}
@@ -24,6 +24,7 @@ import com.eactive.eai.rms.onl.apim.masking.MaskingUtils;
import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgManService;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserManService;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserUI;
import com.eactive.eai.rms.common.acl.user.ui.UserUI;
import com.eactive.eai.rms.onl.transaction.apim.ApiInterfaceService;
import com.eactive.eai.rms.onl.transaction.apim.mapping.ApiSpecUIMapper;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceUI;
@@ -61,29 +62,53 @@ public class PortalApprovalManService extends BaseService {
public Page<PortalApprovalUI> selectList(Pageable pageable, PortalApprovalUISearch portalApprovalUISearch) {
Page<Approval> 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<Approval> 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<>();
@@ -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<GridResponse<PortalOrgUI>> selectList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
PortalOrgUISearch portalOrgUISearch) {
portalOrgUISearch.setIncludeRemoved(!SystemUtil.isProdServer());
Page<PortalOrgUI> page = portalOrgManService.selectList(pageable, portalOrgUISearch);
return ResponseEntity.ok(new GridResponse<>(page));
}
@@ -69,6 +71,7 @@ public class PortalOrgManController extends BaseAnnotationController {
Map<String, Object> 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<Map<String, Object>> deleteMultiple(String ids) {
Map<String, Object> 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<Map<String, Object>> hardDelete(String id) {
Map<String, Object> 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<Map<String, Object>> hardDeleteMultiple(String ids) {
Map<String, Object> 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<String> 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<GridResponse<PortalOrgUI>> selectOrgList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
PortalOrgUISearch portalOrgUISearch) {
@@ -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<String> 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<String, Object> selectDetailWithUsers(String id) {
return getDetailWithUsers(id, true);
}
@@ -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<GridResponse<PortalUserUI>> selectList(@SortDefault(sort = "createdDate", direction = Sort.Direction.DESC) Pageable pageable,
PortalUserUISearch portalUserUISearch) {
portalUserUISearch.setIncludeRemoved(!SystemUtil.isProdServer());
Page<PortalUserUI> 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<Map<String, Object>> hardDelete(String id) {
Map<String, Object> 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<Map<String, Object>> hardDeleteMultiple(String ids) {
Map<String, Object> 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<String> 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<Object> checkDuplicateId(String loginId) {
String result = portalUserManService.checkDuplicateId(loginId) ? "success" : "";
@@ -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<PortalUserUI> 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<String> 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);