인덱스 API 통계 부분 실 데이터 적용
This commit is contained in:
@@ -1,33 +1,22 @@
|
||||
package com.eactive.apim.portal.apps.main.controller;
|
||||
|
||||
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
|
||||
import com.eactive.apim.portal.apps.apis.service.ApiSearchFacade;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiGroupSearch;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceApiDTO;
|
||||
import com.eactive.apim.portal.apps.apiservice.dto.ApiServiceDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.service.PortalNoticeFacade;
|
||||
import com.eactive.apim.portal.apps.main.dto.IndexStatisticsDTO;
|
||||
import com.eactive.apim.portal.apps.main.service.IndexStatisticsService;
|
||||
import com.eactive.apim.portal.apps.main.service.MainApiFacade;
|
||||
import com.eactive.apim.portal.common.util.SecurityUtil;
|
||||
|
||||
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class IndexController {
|
||||
|
||||
private final ApiSearchFacade apiSearchFacade;
|
||||
private final MainApiFacade mainApiFacade;
|
||||
private final PortalPropertyService portalPropertyService;
|
||||
private final IndexStatisticsService indexStatisticsService;
|
||||
|
||||
/**
|
||||
* 메인 페이지 API는 Portal Property 에 main.service.list 에 등록된 그룹을 기준으로 API를 조회함.
|
||||
@@ -45,29 +34,23 @@ public class IndexController {
|
||||
List<ApiServiceDTO> services = mainApiFacade.getOpenApiServices();
|
||||
List<String> hashTags = mainApiFacade.getHashTags();
|
||||
|
||||
ApiGroupSearch search = new ApiGroupSearch();
|
||||
Map<String, Object> searchResult = apiSearchFacade.searchApis(search);
|
||||
// 인덱스 페이지 하단 통계 조회 (캐싱 적용)
|
||||
IndexStatisticsDTO statistics = indexStatisticsService.getStatistics();
|
||||
|
||||
int selectedApiCount = (int) searchResult.get("selectedApiCount");
|
||||
|
||||
int serviceCount = ((List<?>) searchResult.get("services")).size();
|
||||
|
||||
// 승인된 기업 건수
|
||||
int approvedOrgCount = mainApiFacade.getApprovedOrgCount();
|
||||
|
||||
addAttributesToModel(model, services, hashTags, serviceCount, approvedOrgCount, selectedApiCount);
|
||||
addAttributesToModel(model, services, hashTags, statistics);
|
||||
|
||||
return "apps/main/index";
|
||||
}
|
||||
|
||||
private void addAttributesToModel(Model model, List<ApiServiceDTO> apiServices, List<String> hashTags, int serviceCount, int approvedOrgCount, int selectedApiCount) {
|
||||
private void addAttributesToModel(Model model, List<ApiServiceDTO> apiServices, List<String> hashTags, IndexStatisticsDTO statistics) {
|
||||
|
||||
model.addAttribute("services", apiServices);
|
||||
model.addAttribute("hashtags", hashTags);
|
||||
|
||||
model.addAttribute("serviceCount", serviceCount);
|
||||
model.addAttribute("approvedOrgCount", approvedOrgCount);
|
||||
model.addAttribute("selectedApiCount", selectedApiCount);
|
||||
// 인덱스 페이지 하단 통계
|
||||
model.addAttribute("approvedOrgCount", statistics.getApprovedOrgCount());
|
||||
model.addAttribute("serviceCount", statistics.getActiveAppCount());
|
||||
model.addAttribute("totalApiCount", statistics.getTotalApiCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.eactive.apim.portal.apps.main.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 인덱스 페이지 하단 통계 DTO
|
||||
* - API 활용 기업: 법인으로 등록된 수의 합계 (정상 상태)
|
||||
* - 서비스 이용 수: 전체 법인이 생성한 앱의 합계 (이용 가능 상태)
|
||||
* - API 이용 건수: 전체 법인이 생성한 앱의 API 수의 합계 (이용 가능 상태)
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IndexStatisticsDTO {
|
||||
|
||||
/**
|
||||
* API 활용 기업 수
|
||||
* 법인으로 등록되고 정상 상태인 기업의 수
|
||||
*/
|
||||
private int approvedOrgCount;
|
||||
|
||||
/**
|
||||
* 서비스 이용 수
|
||||
* 정상 상태 법인이 생성한 앱 중 이용 가능 상태인 앱의 수
|
||||
*/
|
||||
private int activeAppCount;
|
||||
|
||||
/**
|
||||
* API 이용 건수
|
||||
* 정상 상태 법인이 생성한 이용 가능 앱에 연결된 API의 총 수
|
||||
*/
|
||||
private int totalApiCount;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.eactive.apim.portal.apps.main.service;
|
||||
|
||||
import com.eactive.apim.portal.app.entity.Credential;
|
||||
import com.eactive.apim.portal.app.repository.CredentialRepository;
|
||||
import com.eactive.apim.portal.apps.main.dto.IndexStatisticsDTO;
|
||||
import com.eactive.apim.portal.apps.user.repository.PortalOrgRepository;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 인덱스 페이지 통계 서비스
|
||||
* 메모리 캐싱을 통해 DB 조회를 최소화 (1시간 TTL)
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IndexStatisticsService {
|
||||
|
||||
private static final long CACHE_TTL_HOURS = 1;
|
||||
private static final String APP_STATUS_ACTIVE = "1";
|
||||
|
||||
private final PortalOrgRepository portalOrgRepository;
|
||||
private final CredentialRepository credentialRepository;
|
||||
|
||||
// 캐시된 통계 데이터
|
||||
private volatile IndexStatisticsDTO cachedStatistics;
|
||||
// 마지막 집계 시간
|
||||
private volatile LocalDateTime lastAggregatedTime;
|
||||
|
||||
/**
|
||||
* 인덱스 페이지 통계 조회
|
||||
* 캐시가 없거나 1시간이 경과한 경우 재집계
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public IndexStatisticsDTO getStatistics() {
|
||||
if (shouldRefreshCache()) {
|
||||
synchronized (this) {
|
||||
// Double-check locking
|
||||
if (shouldRefreshCache()) {
|
||||
refreshStatistics();
|
||||
}
|
||||
}
|
||||
}
|
||||
return cachedStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 갱신이 필요한지 확인
|
||||
*/
|
||||
private boolean shouldRefreshCache() {
|
||||
if (cachedStatistics == null || lastAggregatedTime == null) {
|
||||
return true;
|
||||
}
|
||||
return LocalDateTime.now().isAfter(lastAggregatedTime.plusHours(CACHE_TTL_HOURS));
|
||||
}
|
||||
|
||||
/**
|
||||
* 통계 재집계
|
||||
*/
|
||||
private void refreshStatistics() {
|
||||
log.info("인덱스 페이지 통계 집계 시작");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// 1. API 활용 기업 수: 정상 상태이고 승인 완료된 기관 수
|
||||
int approvedOrgCount = (int) portalOrgRepository.countByOrgStatusAndApprovalStatus(
|
||||
OrgStatus.ACTIVE, ApprovalStatus.COMPLETED);
|
||||
|
||||
// 2. 정상 상태 기관의 ID 목록 조회
|
||||
List<String> activeOrgIds = portalOrgRepository.findIdsByOrgStatusAndApprovalStatus(
|
||||
OrgStatus.ACTIVE, ApprovalStatus.COMPLETED);
|
||||
|
||||
int activeAppCount = 0;
|
||||
int totalApiCount = 0;
|
||||
|
||||
if (!activeOrgIds.isEmpty()) {
|
||||
// 3. 서비스 이용 수: 정상 기관의 이용 가능 앱 수
|
||||
activeAppCount = (int) credentialRepository.countActiveAppsByOrgIds(activeOrgIds);
|
||||
|
||||
// 4. API 이용 건수: 정상 기관의 이용 가능 앱에 연결된 API 수
|
||||
List<Credential> activeApps = credentialRepository.findActiveAppsByOrgIds(activeOrgIds);
|
||||
totalApiCount = activeApps.stream()
|
||||
.mapToInt(credential -> credential.getApiList() != null ? credential.getApiList().size() : 0)
|
||||
.sum();
|
||||
}
|
||||
|
||||
cachedStatistics = IndexStatisticsDTO.builder()
|
||||
.approvedOrgCount(approvedOrgCount)
|
||||
.activeAppCount(activeAppCount)
|
||||
.totalApiCount(totalApiCount)
|
||||
.build();
|
||||
|
||||
lastAggregatedTime = LocalDateTime.now();
|
||||
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
log.info("인덱스 페이지 통계 집계 완료 - 소요시간: {}ms, 기업: {}, 앱: {}, API: {}",
|
||||
elapsed, approvedOrgCount, activeAppCount, totalApiCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("인덱스 페이지 통계 집계 실패", e);
|
||||
// 기존 캐시가 없는 경우 기본값 설정
|
||||
if (cachedStatistics == null) {
|
||||
cachedStatistics = IndexStatisticsDTO.builder()
|
||||
.approvedOrgCount(0)
|
||||
.activeAppCount(0)
|
||||
.totalApiCount(0)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 강제 갱신 (관리자용)
|
||||
*/
|
||||
public void forceRefresh() {
|
||||
synchronized (this) {
|
||||
lastAggregatedTime = null;
|
||||
refreshStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 만료 시간 조회
|
||||
*/
|
||||
public LocalDateTime getCacheExpireTime() {
|
||||
if (lastAggregatedTime == null) {
|
||||
return null;
|
||||
}
|
||||
return lastAggregatedTime.plusHours(CACHE_TTL_HOURS);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,10 @@ import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
@EMSDataSource
|
||||
public interface PortalOrgRepository extends BaseRepository<PortalOrg, String> {
|
||||
@@ -20,5 +23,13 @@ public interface PortalOrgRepository extends BaseRepository<PortalOrg, String> {
|
||||
long countByOrgStatusAndApprovalStatus(PortalOrgEnums.OrgStatus status, PortalOrgEnums.ApprovalStatus approvalStatus);
|
||||
|
||||
Optional<PortalOrg> findById(String orgId);
|
||||
|
||||
/**
|
||||
* 정상 상태이고 승인 완료된 기관의 ID 목록 조회
|
||||
*/
|
||||
@Query("SELECT o.id FROM PortalOrg o WHERE o.orgStatus = :status AND o.approvalStatus = :approvalStatus")
|
||||
List<String> findIdsByOrgStatusAndApprovalStatus(
|
||||
@Param("status") PortalOrgEnums.OrgStatus status,
|
||||
@Param("approvalStatus") PortalOrgEnums.ApprovalStatus approvalStatus);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user