승인 요청 처리 개선 및 UI/로직 추가:
- 승인정보 없는 요청 목록 노출 및 삭제 로직 추가 - 내부 결재 진행 중 취소 불가 안내 메시지 및 스타일 추가 - '승인대기' -> '승인정보 없음' 텍스트 수정
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.eactive.apim.portal.apps.app.controller;
|
package com.eactive.apim.portal.apps.app.controller;
|
||||||
|
|
||||||
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
import com.eactive.apim.portal.apprequest.entity.AppRequest;
|
||||||
|
import com.eactive.apim.portal.approval.statemachine.InvalidApprovalTransitionException;
|
||||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||||
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
import com.eactive.apim.portal.apps.apis.service.ApiService;
|
||||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||||
@@ -178,7 +179,6 @@ public class MyAppController {
|
|||||||
|
|
||||||
model.addAttribute("apiKey", apiKey);
|
model.addAttribute("apiKey", apiKey);
|
||||||
model.addAttribute("secretAvailable", secretAvailable);
|
model.addAttribute("secretAvailable", secretAvailable);
|
||||||
model.addAttribute("authType", "OAuth2");
|
|
||||||
|
|
||||||
return new ModelAndView(CREDENTIAL_DETAIL);
|
return new ModelAndView(CREDENTIAL_DETAIL);
|
||||||
}
|
}
|
||||||
@@ -217,9 +217,17 @@ public class MyAppController {
|
|||||||
appServiceFacade.cancelApiRequest(id, SecurityUtil.getPortalAuthenticatedUser().getPortalOrg());
|
appServiceFacade.cancelApiRequest(id, SecurityUtil.getPortalAuthenticatedUser().getPortalOrg());
|
||||||
result.put("success", true);
|
result.put("success", true);
|
||||||
result.put("message", "신청이 취소되었습니다.");
|
result.put("message", "신청이 취소되었습니다.");
|
||||||
} catch (Exception e) {
|
} catch (InvalidApprovalTransitionException e) {
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
result.put("message", "신청 취소 중 오류가 발생했습니다: " + e.getMessage());
|
result.put("message", "내부 결재가 진행 중이라 신청을 취소할 수 없습니다. 취소가 필요한 경우 관리자에게 문의해 주세요.");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
log.error("API Key 신청 취소 중 GW 차단 실패. id={}", id, e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("API Key 신청 취소 실패. id={}", id, e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "신청 취소 중 오류가 발생했습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ public class AppServiceFacade {
|
|||||||
private final ApiServiceHelper apiServiceHelper;
|
private final ApiServiceHelper apiServiceHelper;
|
||||||
private final FileService fileService;
|
private final FileService fileService;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final AdminGatewayClient adminGatewayClient;
|
||||||
|
|
||||||
public List<ClientDTO> getApikeyList(PortalOrg portalOrg) {
|
public List<ClientDTO> getApikeyList(PortalOrg portalOrg) {
|
||||||
|
|
||||||
@@ -68,9 +69,13 @@ public class AppServiceFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<AppRequest> getPendingApiKeyList(PortalOrg portalOrg) {
|
public List<AppRequest> getPendingApiKeyList(PortalOrg portalOrg) {
|
||||||
List<AppRequest> appRequests = appRequestRepository.findAllByOrgAndTypeIsInAndApproval_ApprovalStatusIn(portalOrg, Arrays.asList(AppRequestType.NEW, AppRequestType.MODIFY, AppRequestType.DELETE),
|
List<AppRequestType> types = Arrays.asList(AppRequestType.NEW, AppRequestType.MODIFY, AppRequestType.DELETE);
|
||||||
|
List<AppRequest> appRequests = appRequestRepository.findAllByOrgAndTypeIsInAndApproval_ApprovalStatusIn(portalOrg, types,
|
||||||
Arrays.asList(new ProcessingState(), new RequestedState()));
|
Arrays.asList(new ProcessingState(), new RequestedState()));
|
||||||
|
|
||||||
|
// 승인정보(approval) 없는 신청도 목록에 노출한다. (사용자가 직접 삭제 가능)
|
||||||
|
appRequests.addAll(appRequestRepository.findAllByOrgAndTypeIsInAndApprovalIsNull(portalOrg, types));
|
||||||
|
|
||||||
return appRequests;
|
return appRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +139,23 @@ public class AppServiceFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void cancelApiRequest(String id, PortalOrg portalOrg) {
|
public void cancelApiRequest(String id, PortalOrg portalOrg) {
|
||||||
appRequestRepository.findByIdAndOrg(id, portalOrg).ifPresent(approvalService::cancelAppApproval);
|
appRequestRepository.findByIdAndOrg(id, portalOrg).ifPresent(request -> {
|
||||||
|
if (request.getApproval() == null) {
|
||||||
|
// 승인정보 없는 신청은 결재 워크플로우가 없으므로 즉시 삭제.
|
||||||
|
// 단, GW에 클라이언트가 존재할 수 있으므로 차단(appstatus=0)+리로드를 먼저 수행하고
|
||||||
|
// 실패 시 삭제를 중단한다. (/api_key_delete 와 동일한 순서)
|
||||||
|
if (StringUtils.isNotBlank(request.getClientId())) {
|
||||||
|
try {
|
||||||
|
adminGatewayClient.blockClient(request.getClientId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("게이트웨이 차단 처리에 실패하여 삭제를 중단했습니다. 잠시 후 다시 시도해 주세요.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appRequestRepository.delete(request);
|
||||||
|
} else {
|
||||||
|
approvalService.cancelAppApproval(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -164,9 +185,15 @@ public class AppServiceFacade {
|
|||||||
Map<String, ApiServiceDTO> mainIconsMap = apiServiceHelper.getMainIconsFromServiceDtos(apiServices);
|
Map<String, ApiServiceDTO> mainIconsMap = apiServiceHelper.getMainIconsFromServiceDtos(apiServices);
|
||||||
|
|
||||||
for (String apiId : apiList) {
|
for (String apiId : apiList) {
|
||||||
ApiServiceDTO serviceDTO = mainIconsMap.get(apiId);
|
// 신청 이후 API 스펙/그룹이 삭제된 경우 null 가능
|
||||||
ApiSpecInfoDto spec = apiService.selectDetail(apiId);
|
ApiSpecInfoDto spec = apiService.selectDetail(apiId);
|
||||||
|
if (spec == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ApiServiceDTO serviceDTO = mainIconsMap.get(apiId);
|
||||||
|
if (serviceDTO != null) {
|
||||||
spec.setService(serviceDTO.getGroupName());
|
spec.setService(serviceDTO.getGroupName());
|
||||||
|
}
|
||||||
appRequest.getApiSpecList().add(spec);
|
appRequest.getApiSpecList().add(spec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+282
-3842
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1232,6 +1232,14 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 내부 결재 진행중: 신청 취소 불가 안내
|
||||||
|
.dt-cancel-notice {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
// Footer Buttons layout
|
// Footer Buttons layout
|
||||||
.dt-actions {
|
.dt-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -256,8 +256,8 @@
|
|||||||
<h3 class="app-card-title" th:text="${request.clientName}">앱 이름</h3>
|
<h3 class="app-card-title" th:text="${request.clientName}">앱 이름</h3>
|
||||||
<!-- Status Badge -->
|
<!-- Status Badge -->
|
||||||
<span class="app-card-badge status-pending"
|
<span class="app-card-badge status-pending"
|
||||||
th:text="${request.approval != null and request.approval.approvalStatus != null ? request.approval.approvalStatus.description : '승인대기'}">
|
th:text="${request.approval != null and request.approval.approvalStatus != null ? request.approval.approvalStatus.description : '승인정보 없음'}">
|
||||||
승인대기
|
승인정보 없음
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- App Description & Expected Completion Date -->
|
<!-- App Description & Expected Completion Date -->
|
||||||
|
|||||||
@@ -54,6 +54,9 @@
|
|||||||
대기중
|
대기중
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dt-app-status-row" th:if="${appRequest.approval == null}">
|
||||||
|
<span class="dt-status-badge status-pending">승인정보 없음</span>
|
||||||
|
</div>
|
||||||
<h3 class="dt-app-name" th:text="${appRequest.clientName}">앱 이름</h3>
|
<h3 class="dt-app-name" th:text="${appRequest.clientName}">앱 이름</h3>
|
||||||
<!-- App Description -->
|
<!-- App Description -->
|
||||||
<p class="dt-app-desc"
|
<p class="dt-app-desc"
|
||||||
@@ -200,12 +203,19 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 내부 결재 진행중: 취소 불가 안내 -->
|
||||||
|
<p class="dt-cancel-notice" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')"
|
||||||
|
th:if="${appRequest.approval != null and appRequest.approval.approvalStatus != null and
|
||||||
|
appRequest.approval.approvalStatus.toString() == 'PROCESSING'}">
|
||||||
|
내부 결재가 진행 중이라 신청을 취소할 수 없습니다. 취소가 필요한 경우 관리자에게 문의해 주세요.
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- Bottom Navigation Actions -->
|
<!-- Bottom Navigation Actions -->
|
||||||
<div class="dt-actions">
|
<div class="dt-actions">
|
||||||
<!-- Cancel Request Button (danger red) -->
|
<!-- Cancel Request Button (danger red) -->
|
||||||
<button type="button" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" class="dt-btn-red"
|
<button type="button" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" class="dt-btn-red"
|
||||||
th:if="${appRequest.approval != null and appRequest.approval.approvalStatus != null and
|
th:if="${appRequest.approval == null or (appRequest.approval.approvalStatus != null and
|
||||||
(appRequest.approval.approvalStatus.toString() == 'PENDING' or appRequest.approval.approvalStatus.toString() == 'REQUESTED')}"
|
(appRequest.approval.approvalStatus.toString() == 'PENDING' or appRequest.approval.approvalStatus.toString() == 'REQUESTED'))}"
|
||||||
th:data-request-id="${appRequest.id}" onclick="cancelRequestById(this)">
|
th:data-request-id="${appRequest.id}" onclick="cancelRequestById(this)">
|
||||||
신청 취소
|
신청 취소
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -51,16 +51,6 @@
|
|||||||
<!-- Credential Info Section Card -->
|
<!-- Credential Info Section Card -->
|
||||||
<div class="dt-info-container">
|
<div class="dt-info-container">
|
||||||
|
|
||||||
<!-- 인증 방식 -->
|
|
||||||
<div class="dt-row">
|
|
||||||
<div class="dt-col">
|
|
||||||
<label class="dt-label">인증 방식</label>
|
|
||||||
<div class="dt-input-group">
|
|
||||||
<div class="dt-input-box" th:text="${authType}">OAuth2</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Client ID -->
|
<!-- Client ID -->
|
||||||
<div class="dt-row">
|
<div class="dt-row">
|
||||||
<div class="dt-col">
|
<div class="dt-col">
|
||||||
|
|||||||
Reference in New Issue
Block a user