From 2cdb08a26f9e4350faddecbd0648e56a7b3bd210 Mon Sep 17 00:00:00 2001 From: "Rinjae(gf63)" Date: Tue, 15 Sep 2026 13:42:27 +0900 Subject: [PATCH] =?UTF-8?q?-=20=EC=95=BD=EA=B4=80=20=EB=8F=99=EC=9D=98?= =?UTF-8?q?=EC=84=9C=20=EB=85=B8=EC=B6=9C=20=EB=B0=8F=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=20=EB=B0=A9=EC=8B=9D=20=EA=B4=80=EB=A6=AC=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20'=EC=A0=84=EC=B2=B4=20=EB=8F=99?= =?UTF-8?q?=EC=9D=98'=20=EB=B0=8F=20=EC=95=BD=EA=B4=80=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=EB=B3=84=20=ED=99=9C=EC=84=B1=ED=99=94=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B0=98=EC=98=81=20-=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=ED=83=80=EC=9E=85=20=EB=B0=8F=20=EC=95=BD=EA=B4=80?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC=EC=84=B1=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AgreementTypeConfigService.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/main/java/com/eactive/apim/portal/agreements/service/AgreementTypeConfigService.java diff --git a/src/main/java/com/eactive/apim/portal/agreements/service/AgreementTypeConfigService.java b/src/main/java/com/eactive/apim/portal/agreements/service/AgreementTypeConfigService.java new file mode 100644 index 0000000..cdbe9db --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/agreements/service/AgreementTypeConfigService.java @@ -0,0 +1,146 @@ +package com.eactive.apim.portal.agreements.service; + +import com.eactive.apim.portal.agreements.entity.AgreementType; +import com.eactive.apim.portal.portalproperty.service.PortalPropertyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 약관 종류 운영 설정 조회. PTL_PROPERTY(그룹 {@code Portal})로 관리하며 admin·portal 이 같은 값을 본다. + * + * + * + * + * + * + * + *
PTL_PROPERTY (group = Portal)
{@code portal.terms.enabled-types}시스템에서 사용할 약관 종류. 쉼표 구분, 순서 무의미
{@code portal.terms.display-types}약관 페이지에 노출할 약관 종류. 쉼표 구분, 기재 순서가 노출 순서
+ * + *

값은 관리 콘솔의 '약관 목록 > 약관 종류 관리' 화면에서 설정한다. 프로퍼티가 없으면 + * {@link #DEFAULT_TYPES} 를 사용하며 조회만으로 행을 만들지는 않는다 + * (PTL_PROPERTY 는 (그룹, 이름) 유니크 제약이 없어 동시 요청이 중복 행을 만들 수 있다). + * 알 수 없는 코드는 무시하므로 enum 항목이 빠져도 화면이 깨지지 않는다. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AgreementTypeConfigService { + + public static final String PROPERTY_GROUP = "Portal"; + public static final String ENABLED_TYPES_KEY = "portal.terms.enabled-types"; + public static final String DISPLAY_TYPES_KEY = "portal.terms.display-types"; + public static final String AGREE_ALL_ENABLED_KEY = "portal.terms.agree-all-enabled"; + + /** '전체 동의' 기본값 — 미설정 시 항목별로 끝까지 읽고 개별 동의하는 방식 */ + public static final boolean DEFAULT_AGREE_ALL_ENABLED = false; + + /** 프로퍼티 미설정 시 기본값 — 현재 운영중인 약관 종류 */ + public static final List DEFAULT_TYPES = Collections.unmodifiableList(Arrays.asList( + AgreementType.TERMS_OF_USE, + AgreementType.PRIVACY_COLLECT, + AgreementType.NOTIFICATION_CONSENT)); + + private final PortalPropertyService portalPropertyService; + + /** 시스템에서 사용하는 약관 종류 */ + public Set getEnabledTypes() { + Set enabled = EnumSet.noneOf(AgreementType.class); + enabled.addAll(readTypes(readGroupProperties(), ENABLED_TYPES_KEY)); + return enabled; + } + + /** 약관 페이지에 노출할 약관 종류(노출 순서대로). 사용하지 않는 종류는 제외된다. */ + public List getDisplayTypes() { + Map properties = readGroupProperties(); + List enabled = readTypes(properties, ENABLED_TYPES_KEY); + + List display = new ArrayList<>(); + for (AgreementType type : readTypes(properties, DISPLAY_TYPES_KEY)) { + if (enabled.contains(type)) { + display.add(type); + } + } + return display; + } + + /** 시스템에서 사용하는 약관 종류인지 여부 */ + public boolean isEnabled(AgreementType type) { + return getEnabledTypes().contains(type); + } + + /** 약관 페이지에 노출(배치)된 약관 종류인지 여부. 사용하지 않는 종류는 항상 {@code false}. */ + public boolean isDisplayed(AgreementType type) { + return getDisplayTypes().contains(type); + } + + /** + * 회원가입 약관 동의 화면에서 '전체 동의'를 허용할지 여부. + * + *

+ */ + public boolean isAgreeAllEnabled() { + return parseBoolean(readGroupProperties().get(AGREE_ALL_ENABLED_KEY)); + } + + /** {@code true/false} 외에 레거시 {@code Y/N}, {@code 1/0} 표기도 허용한다. */ + private boolean parseBoolean(String value) { + if (value == null || value.trim().isEmpty()) { + return DEFAULT_AGREE_ALL_ENABLED; + } + String trimmed = value.trim(); + if ("true".equalsIgnoreCase(trimmed) || "Y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) { + return true; + } + if ("false".equalsIgnoreCase(trimmed) || "N".equalsIgnoreCase(trimmed) || "0".equals(trimmed)) { + return false; + } + log.warn("PTL_PROPERTY {}/{} 값을 해석할 수 없어 기본값({})을 사용합니다. : {}", + PROPERTY_GROUP, AGREE_ALL_ENABLED_KEY, DEFAULT_AGREE_ALL_ENABLED, trimmed); + return DEFAULT_AGREE_ALL_ENABLED; + } + + /** + * {@code Portal} 그룹 프로퍼티 전체를 읽는다. + * + *

단건 조회({@code findById})는 중복 행에서 예외가 나므로 그룹 단위로 읽는다. + */ + private Map readGroupProperties() { + return portalPropertyService.getPortalPropertiesAsMap(PROPERTY_GROUP); + } + + /** 프로퍼티 값을 약관 종류 목록으로 변환. 미설정이면 기본값, 알 수 없는 코드는 무시한다. */ + private List readTypes(Map properties, String propertyName) { + String value = properties.get(propertyName); + if (value == null) { + return new ArrayList<>(DEFAULT_TYPES); + } + + List types = new ArrayList<>(); + for (String code : value.split(",")) { + String trimmed = code.trim(); + if (trimmed.isEmpty()) { + continue; + } + try { + types.add(AgreementType.valueOf(trimmed)); + } catch (IllegalArgumentException e) { + log.warn("PTL_PROPERTY {}/{} 에 알 수 없는 약관 종류 코드가 있어 무시합니다. : {}", + PROPERTY_GROUP, propertyName, trimmed); + } + } + return new ArrayList<>(new LinkedHashSet<>(types)); + } +}