diff --git a/src/main/java/com/eactive/apim/portal/portalproperty/repository/PortalPropertyRepository.java b/src/main/java/com/eactive/apim/portal/portalproperty/repository/PortalPropertyRepository.java index 5a6546e..83a9dd6 100644 --- a/src/main/java/com/eactive/apim/portal/portalproperty/repository/PortalPropertyRepository.java +++ b/src/main/java/com/eactive/apim/portal/portalproperty/repository/PortalPropertyRepository.java @@ -4,8 +4,23 @@ import com.eactive.apim.portal.portalproperty.entity.PortalProperty; import com.eactive.apim.portal.portalproperty.entity.PortalPropertyId; import com.eactive.eai.data.jpa.BaseRepository; import com.eactive.eai.rms.data.EMSDataSource; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; @EMSDataSource public interface PortalPropertyRepository extends BaseRepository { + /** + * (PROPERTY_GROUP_NAME, PROPERTY_NAME) 조합이 2건 이상인 중복 키를 조회한다. + * DB에 유니크 제약을 걸지 않으므로 기동 시 무결성 점검용으로 사용한다. + * + * @return {@code Object[]{propertyGroupName, propertyName, count}} 목록 (중복 없으면 빈 리스트) + */ + @Query("SELECT p.id.propertyGroupName, p.id.propertyName, COUNT(p) " + + "FROM PortalProperty p " + + "GROUP BY p.id.propertyGroupName, p.id.propertyName " + + "HAVING COUNT(p) > 1") + List findDuplicatePropertyKeys(); + } diff --git a/src/main/java/com/eactive/apim/portal/qna/entity/Inquiry.java b/src/main/java/com/eactive/apim/portal/qna/entity/Inquiry.java index 527873e..2c59f2e 100644 --- a/src/main/java/com/eactive/apim/portal/qna/entity/Inquiry.java +++ b/src/main/java/com/eactive/apim/portal/qna/entity/Inquiry.java @@ -86,4 +86,23 @@ public class Inquiry extends Auditable { @Column(name = "ATTACH_FILE") private String attachFile; + + /** + * 공개범위 (전체공개/법인공개/비공개). 기본값 ORG. + * PTL_PROPERTY 기관 기본값이 상한(ceiling)이며 읽기/쓰기 시 clamp 된다. + */ + @Enumerated(EnumType.STRING) + @Column(name = "VISIBILITY", length = 16) + private VisibilityScope visibility = VisibilityScope.ORG; + + /** + * 조회수. sessionStorage 기반 dedup 후 POST 요청으로만 증가한다. + */ + @Column(name = "VIEW_COUNT") + private long viewCount = 0L; + + /** 조회수 1 증가. */ + public void increaseViewCount() { + this.viewCount++; + } } diff --git a/src/main/java/com/eactive/apim/portal/qna/entity/InquiryComment.java b/src/main/java/com/eactive/apim/portal/qna/entity/InquiryComment.java index d66f4a5..9ef7bbe 100644 --- a/src/main/java/com/eactive/apim/portal/qna/entity/InquiryComment.java +++ b/src/main/java/com/eactive/apim/portal/qna/entity/InquiryComment.java @@ -36,6 +36,18 @@ public class InquiryComment extends Auditable implements Serializable { @Column(name = "ADMIN_YN", length = 1) private String adminYn = "N"; + /** + * 댓글 공개범위. 개념상 공개(ALL)/비공개(PRIVATE) 2단계. + * 비공개 댓글은 작성자·같은 법인 corp-manager·관리자만 열람 가능. + */ + @Enumerated(EnumType.STRING) + @Column(name = "VISIBILITY", length = 16) + private VisibilityScope visibility = VisibilityScope.ALL; + + public boolean isPrivate() { + return this.visibility == VisibilityScope.PRIVATE; + } + public void markDeleted() { this.delYn = "Y"; } diff --git a/src/main/java/com/eactive/apim/portal/qna/entity/VisibilityScope.java b/src/main/java/com/eactive/apim/portal/qna/entity/VisibilityScope.java new file mode 100644 index 0000000..fa24673 --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/qna/entity/VisibilityScope.java @@ -0,0 +1,67 @@ +package com.eactive.apim.portal.qna.entity; + +/** + * 문의(Q&A) 게시물/댓글의 공개범위. + * + *

{@code width}는 공개 범위의 넓이 랭크로, 값이 클수록 더 넓게 공개된다. + * {@code PTL_PROPERTY}에 설정된 기관 기본값이 상한(ceiling)이 되며, 게시물이 상한보다 + * 넓게 설정될 수 없다({@link #clampTo}). property가 항상 우선한다.

+ * + * + */ +public enum VisibilityScope { + + ALL(3, "전체공개"), + ORG(2, "법인공개"), + PRIVATE(1, "비공개"); + + private final int width; + private final String label; + + VisibilityScope(int width, String label) { + this.width = width; + this.label = label; + } + + /** 공개 범위 넓이 랭크. 클수록 넓게 공개. */ + public int width() { + return width; + } + + /** 화면 표기용 한글 라벨. */ + public String label() { + return label; + } + + /** + * 문자열을 enum으로 안전 변환. null/빈값/미해당은 defaultScope 반환. + */ + public static VisibilityScope fromString(String value, VisibilityScope defaultScope) { + if (value == null || value.trim().isEmpty()) { + return defaultScope; + } + try { + return VisibilityScope.valueOf(value.trim().toUpperCase()); + } catch (IllegalArgumentException e) { + return defaultScope; + } + } + + /** + * 요청 공개범위를 상한으로 clamp 한다. 요청이 상한보다 넓으면 상한으로 낮춘다. + * (property가 항상 우선하므로, 저장값이 옛 상한이라 더 넓더라도 읽기 시 재-clamp 한다.) + */ + public static VisibilityScope clampTo(VisibilityScope requested, VisibilityScope ceiling) { + if (requested == null) { + return ceiling; + } + if (ceiling == null) { + return requested; + } + return requested.width() > ceiling.width() ? ceiling : requested; + } +}