package kr.itn.itnhub.code; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; /** * 코드값을 기동 후 첫 조회에 한 번 읽어 메모리에 들고 있는다. 코드는 관리 화면에서 가끔 * 바뀔 뿐이라 매 요청 조회가 낭비이고, 단일 인스턴스 운영이라 분산 캐시는 필요 없다. * *
지연 로딩을 쓰는 이유는 Flyway 마이그레이션이 끝나기 전에 빈 초기화가 돌 수 있기 * 때문이다. 첫 조회 시점에는 마이그레이션이 반드시 끝나 있다.
*/ @Service public class CodeService { private static final Logger log = LoggerFactory.getLogger(CodeService.class); private final CodeMapper mapper; private final ObjectMapper json = new ObjectMapper(); /** 그룹ID -> 활성 코드(정렬순). 화면 선택지가 여기서 나간다. */ private volatile Map group(String groupId) {
ensureLoaded();
return activeByGroup.getOrDefault(groupId, List.of());
}
/** 비활성 코드도 찾는다 - 과거 데이터의 라벨을 그려야 하기 때문이다. */
public Optional find(String groupId, String code) {
ensureLoaded();
return Optional.ofNullable(allByGroup.getOrDefault(groupId, Map.of()).get(code));
}
/** attrs.scopes에 해당 자리가 든 활성 코드만. 공공누리 유형이 이걸 쓴다. */
public List scoped(String groupId, String scope) {
return group(groupId).stream().filter(code -> scopes(code).contains(scope)).toList();
}
@SuppressWarnings("unchecked")
private List scopes(Code code) {
Object raw = code.attrs().get(Codes.ATTR_SCOPES);
return raw instanceof List> list ? (List) list : Collections.emptyList();
}
/** 저장 요청 값이 이 그룹의 활성 코드인지. */
public boolean isValid(String groupId, String code) {
return group(groupId).stream().anyMatch(candidate -> candidate.code().equals(code));
}
/** 코드 관리 화면이 쓰는 그룹 메타. 캐시하지 않는다 - 호출 빈도가 낮다. */
public List groups() {
return mapper.findAllGroups();
}
}