- 삭제된 사용자/법인 포함 승인 요청 삭제 기능 추가
eapim-admin CI / build (push) Has been cancelled

- 삭제 버튼 관련 JSP/JS 수정 및 승인 상태별 버튼 표시 개선
- 초기 패스워드 설정 접미사 적용 및 강제 변경 유도 로직 추가
This commit is contained in:
Rinjae
2026-07-28 15:37:58 +09:00
parent 775d69eb53
commit 2d414dc31b
5 changed files with 166 additions and 46 deletions
@@ -45,6 +45,10 @@
padding: 2px 5px;
border-radius: 3px;
}
.cssbtn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
</style>
<script language="javascript">
<%
@@ -176,6 +180,29 @@
}
});
$("#btn_user_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(e.responseText);
}
});
});
$('#btn_user_redeploy').click(function() {
if (confirm("재배포 하시겠습니까?")) {
$.ajax({
@@ -243,53 +270,83 @@
// Display Portal User details
var portalUser = data.portalUser;
$("#loginId").text(portalUser.loginId);
$("#userName").text(portalUser.userName);
$("#mobileNumber").text(portalUser.mobileNumber);
$("#userStatusDescription").text(portalUser.userStatusDescription);
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
var portalOrg = portalUser ? portalUser.portalOrgUIs : null;
// 삭제된 대상 사용자/법인 판별
var userDeleted = !portalUser
|| portalUser.userName === '반려된 사용자'
|| portalUser.userName === '삭제된 사용자';
var orgDeleted = !portalOrg
|| portalOrg.orgStatus === 'REMOVED'
|| portalOrg.orgName === '삭제된법인';
var deletedTarget = userDeleted || orgDeleted;
if (portalUser) {
$("#loginId").text(portalUser.loginId);
$("#userName").text(portalUser.userName);
$("#mobileNumber").text(portalUser.mobileNumber);
$("#userStatusDescription").text(portalUser.userStatusDescription);
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
} else {
$("#loginId, #userName, #mobileNumber, #userStatusDescription, #roleCodeDescription").text("삭제된 사용자");
}
var isCurrentApprover = data.approvers.some(approver =>
approver.approverStatus === 'CURRENT' &&
approver.user && approver.user.USERID === loginUser
);
if (isCurrentApprover) {
document.getElementById("btn_user_approve").style.display = "";
document.getElementById("btn_user_reject").style.display = "";
} else {
document.getElementById("btn_user_approve").style.display = "none";
document.getElementById("btn_user_reject").style.display = "none";
}
// 삭제된 승인자(USERID 없음)로 진행 불가한 건: 요청됨/진행중 상태면 승인자가 아니어도 강제 반려 허용
var hasDeletedApprover = data.approvers.some(approver =>
!approver.user || !approver.user.USERID
);
var isProgressStatus = data.approvalStatus.code === 'REQUESTED' || data.approvalStatus.code === 'PROCESSING';
document.getElementById("btn_user_force_reject").style.display =
(hasDeletedApprover && isProgressStatus && !isCurrentApprover) ? "" : "none";
if (deletedTarget) {
// 삭제된 사용자/법인 포함 → 승인·반려 불가. 버튼은 비활성(disable) 표기, 삭제 버튼 노출
$("#btn_user_approve, #btn_user_reject").show().prop("disabled", true);
document.getElementById("btn_user_force_reject").style.display = "none";
document.getElementById("btn_user_delete").style.display = "";
} else {
$("#btn_user_approve, #btn_user_reject").prop("disabled", false);
document.getElementById("btn_user_delete").style.display = "none";
if (isCurrentApprover) {
document.getElementById("btn_user_approve").style.display = "";
document.getElementById("btn_user_reject").style.display = "";
} else {
document.getElementById("btn_user_approve").style.display = "none";
document.getElementById("btn_user_reject").style.display = "none";
}
// 삭제된 승인자(USERID 없음)로 진행 불가한 건: 요청됨/진행중 상태면 승인자가 아니어도 강제 반려 허용
var hasDeletedApprover = data.approvers.some(approver =>
!approver.user || !approver.user.USERID
);
var isProgressStatus = data.approvalStatus.code === 'REQUESTED' || data.approvalStatus.code === 'PROCESSING';
document.getElementById("btn_user_force_reject").style.display =
(hasDeletedApprover && isProgressStatus && !isCurrentApprover) ? "" : "none";
}
if (data.approvalStatus.description === '배포실패'){
document.getElementById("btn_user_redeploy").style.display = "";
}
// Display Portal Org details
var portalOrg = portalUser.portalOrgUIs;
$("#orgName").text(portalOrg.orgName);
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
$("#serviceName").text(portalOrg.serviceName);
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
if (portalOrg) {
$("#orgName").text(portalOrg.orgName);
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
$("#serviceName").text(portalOrg.serviceName);
$("#corpRegNo").text(formatCorpRegNo(portalOrg.corpRegNo));
$("#compRegNo").text(formatCompanyRegNo(portalOrg.compRegNo));
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
$("#orgCode").text(portalOrg.orgCode);
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
$("#scPhoneNumber").text(portalOrg.scPhoneNumber); // 고객센터 전화번호
$("#orgPhoneNumber").text(portalOrg.orgPhoneNumber); // 회사 전화번호
$("#ceoName").text(portalOrg.ceoName); // 대표자 성명
$("#orgIndustryType").text(portalOrg.orgIndustryType || ""); // 업종
$("#orgAddr").text(portalOrg.orgAddr); // 소재지
$("#orgSectors").text(portalOrg.orgSectors || ""); // 업태
$("#orgCode").text(portalOrg.orgCode);
$("#ipWhitelist").text(portalOrg.ipWhitelist || "");
} else {
$("#orgName").text("삭제된법인");
$("#orgStatusDescription, #serviceName, #corpRegNo, #compRegNo, #scPhoneNumber, #orgPhoneNumber, #ceoName, #orgIndustryType, #orgAddr, #orgSectors, #orgCode, #ipWhitelist").text("");
}
if (portalOrg.compRegFile) {
if (portalOrg && portalOrg.compRegFile) {
$("#fileName").text(portalOrg.fileName);
if (portalOrg.fileName) {
$("#fileWrapper").show().off('click').on('click', function() {
@@ -329,15 +386,18 @@
// 사용자 정보 업데이트
var portalUser = data.portalUser;
$("#loginId").text(portalUser.loginId);
$("#userName").text(portalUser.userName);
$("#mobileNumber").text(portalUser.mobileNumber);
$("#userStatusDescription").text(portalUser.userStatusDescription);
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
if (portalUser) {
$("#loginId").text(portalUser.loginId);
$("#userName").text(portalUser.userName);
$("#mobileNumber").text(portalUser.mobileNumber);
$("#userStatusDescription").text(portalUser.userStatusDescription);
$("#roleCodeDescription").text(portalUser.roleCodeDescription);
} else {
$("#loginId, #userName, #mobileNumber, #userStatusDescription, #roleCodeDescription").text("삭제된 사용자");
}
// 법인 정보 업데이트
var portalOrg = portalUser.portalOrgUIs;
updateOrgInfo(portalOrg);
updateOrgInfo(portalUser ? portalUser.portalOrgUIs : null);
// 마스킹 상태에 따라 데이터를 다르게 표시
var isMasked = $("#btn_unmask").attr("status") === "DETAIL";
@@ -379,6 +439,11 @@
// 법인 정보 업데이트 함수
function updateOrgInfo(portalOrg) {
if (!portalOrg) {
$("#orgName").text("삭제된법인");
$("#orgStatusDescription, #serviceName, #corpRegNo, #compRegNo, #scPhoneNumber, #orgPhoneNumber, #ceoName, #orgIndustryType, #orgAddr, #orgSectors, #orgCode, #ipWhitelist").text("");
return;
}
$("#orgName").text(portalOrg.orgName);
$("#orgStatusDescription").text(portalOrg.orgStatusDescription);
$("#serviceName").text(portalOrg.serviceName);
@@ -522,6 +587,7 @@
<button type="button" class="cssbtn" style="display: none;" id="btn_user_approve" level="W"><i class="material-icons">check</i>승인</button>
<button type="button" class="cssbtn" style="display: none;" id="btn_user_reject" level="W"><i class="material-icons">close</i>반려</button>
<button type="button" class="cssbtn" style="display: none;" id="btn_user_force_reject" level="W"><i class="material-icons">block</i>강제 반려</button>
<button type="button" class="cssbtn" style="display: none;" id="btn_user_delete" level="W"><i class="material-icons">delete</i>삭제</button>
<button type="button" class="cssbtn" style="display: none;" id="btn_user_redeploy" level="W"><i class="material-icons">close</i>재처리</button>
<button type="button" class="cssbtn" id="btn_close" level="R"><i class="material-icons">cancel</i> <%= localeMessage.getString("button.close") %>
</button>
@@ -285,7 +285,7 @@
}
const fullLoginId = id + "@" + domain;
const emailPattern = /^[A-Za-z0-9._-]*@[A-Za-z0-9][-A-Za-z0-9.]+\.[A-Za-z]{2,}$/;
const emailPattern = /^[A-Za-z0-9._%+-]*@[A-Za-z0-9][-A-Za-z0-9.]+\.[A-Za-z]{2,}$/;
if (!emailPattern.test(fullLoginId)) {
alert(fullLoginId + " 은 올바른 이메일 형식이 아닙니다.");
return;
@@ -317,7 +317,7 @@
}
function validateEmailId(input) {
input.value = input.value.replace(/[^A-Za-z0-9._-]/g, '');
input.value = input.value.replace(/[^A-Za-z0-9._%+-]/g, '');
}
function validateDomainInput(input) {
@@ -531,8 +531,8 @@
}
);
} else {
// 신규 등록: 사유 불필요
if (!confirm("<%= localeMessage.getString("common.checkSave")%>")) return;
// 신규 등록: 사유 불필요. 초기 패스워드는 비밀번호 초기화와 동일(이메일ID + subfix)
if (!confirm("가입자를 등록하시겠습니까?\n초기 패스워드는 '이메일ID${passwordInitSubfix}' 입니다.")) return;
submitUser("INSERT", null);
}
});
@@ -86,6 +86,12 @@ public class PortalApprovalManController extends BaseAnnotationController {
return ResponseEntity.ok().build();
}
@PostMapping(value = "/onl/apim/approval/portalApprovalMan.json", params = "cmd=DELETE")
public ResponseEntity<Void> delete(String id) {
portalApprovalManService.deleteApprovalRequest(id);
return ResponseEntity.ok().build();
}
@PostMapping(value = "/onl/apim/approval/portalApprovalMan.json", params = "cmd=REDEPLOY")
public ResponseEntity<Void> redeploy(String id) {
portalApprovalManService.redeploy(id);
@@ -7,6 +7,8 @@ import com.eactive.apim.portal.approval.entity.Approval;
import com.eactive.apim.portal.approval.entity.ApprovalType;
import com.eactive.apim.portal.approval.entity.ApproverStatus;
import com.eactive.apim.portal.approval.statemachine.*;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.apim.portal.portaluser.repository.PortalUserRepository;
@@ -432,6 +434,45 @@ public class PortalApprovalManService extends BaseService {
}
}
/**
* 삭제된 대상 사용자 또는 삭제된 법인이 포함되어 승인·반려가 불가능한 승인 요청을 물리 삭제한다.
* 사용자(PortalUser)/법인(PortalOrg) row 는 건드리지 않고 PTL_APPROVER/PTL_APPROVAL row 만 제거한다.
* 임의 삭제 방지를 위해 실제로 삭제된 사용자/법인이 포함된 USER 유형 요청만 허용한다.
*/
public void deleteApprovalRequest(String id) {
Approval approval = approvalService.findById(id)
.orElseThrow(() -> new BizException("승인 정보를 찾을 수 없습니다. ID: " + id));
if (!approval.getApprovalType().equals(ApprovalType.USER)) {
throw new BizException("법인사용자 승인 요청만 삭제할 수 있습니다.");
}
Optional<PortalUser> optUser = portalUserRepository.findById(approval.getTargetId());
boolean userDeleted = !optUser.isPresent()
|| "반려된 사용자".equals(optUser.get().getUserName())
|| "삭제된 사용자".equals(optUser.get().getUserName());
boolean orgDeleted = false;
if (optUser.isPresent()) {
PortalOrg org = optUser.get().getPortalOrg();
orgDeleted = org == null
|| OrgStatus.REMOVED.equals(org.getOrgStatus())
|| "삭제된법인".equals(org.getOrgName());
}
if (!(userDeleted || orgDeleted)) {
throw new BizException("삭제된 사용자 또는 법인이 포함된 요청만 삭제할 수 있습니다.");
}
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 target user/org present)", id);
}
public void sendEvent(String id, ApprovalEvent event, Map<String, Object> options) {
approvalService.findById(id).ifPresent(approval -> {
options.put("authorizer", portalApprovalAuthorizer);
@@ -190,7 +190,14 @@ public class PortalUserManService extends BaseService {
}
PortalUser portalUser = portalUserUIMapper.toEntity(portalUserUI);
portalUser.setPasswordHash(kjbSafedbPasswordEncoder.encode("!" + portalUserUI.getLoginId()));
// 최초 아이디 생성 시 초기 패스워드(loginId + subfix)로 세팅 (비밀번호 초기화와 동일)
String subfix = getPasswordInitSubfix();
portalUser.setPasswordHash(kjbSafedbPasswordEncoder.encode(portalUserUI.getLoginId() + subfix));
// 비밀번호 변경일 초기화 (초기 비밀번호 강제 변경 유도)
portalUser.setPasswordChangeDate(null);
portalUser.setLoginFailureCount(0);
portalUser.setAccountLockYn("N");
portalUser.setEmailAddr(portalUserUI.getLoginId());