package com.mydreamjob.review; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import java.util.Optional; @Repository public interface JobReviewRepository extends JpaRepository { Optional findByJobIdAndStudentUserId(Long jobId, Long studentUserId); Page findByJobIdAndStatusOrderByCreatedAtDesc(Long jobId, String status, Pageable pageable); /** 公开列表:仅 PUBLISHED 且审核状态为 AUTO_APPROVED/APPROVED */ Page findByJobIdAndStatusAndModerationStatusInOrderByCreatedAtDesc( Long jobId, String status, Collection moderationStatuses, Pageable pageable); boolean existsByJobIdAndStudentUserId(Long jobId, Long studentUserId); List findByJobIdAndStatus(Long jobId, String status); /** 公开汇总:仅 PUBLISHED 且审核通过(AUTO_APPROVED/APPROVED) */ List findByJobIdAndStatusAndModerationStatusIn( Long jobId, String status, Collection moderationStatuses); // ── 防刷规则查询 ────────────────────────────────────────── long countByStudentUserIdAndCreatedAtAfter(Long studentUserId, LocalDateTime after); boolean existsByJobIdAndContentHashAndCreatedAtAfter(Long jobId, String contentHash, LocalDateTime after); long countByIpHashAndCreatedAtAfter(String ipHash, LocalDateTime after); long countByUaHashAndCreatedAtAfter(String uaHash, LocalDateTime after); /** 公开列表(含筛选):PUBLISHED 且审核通过;支持评分档/时间范围/未回复筛选 */ @Query("SELECT r FROM JobReview r" + " WHERE r.jobId = :jobId AND r.status = :status" + " AND r.moderationStatus IN :moderationStatuses" + " AND (:ratingType IS NULL OR" + " (:ratingType = 'GOOD' AND r.rating = 5)" + " OR (:ratingType = 'MEDIUM' AND r.rating IN (3,4))" + " OR (:ratingType = 'BAD' AND r.rating IN (1,2)))" + " AND (:createdFrom IS NULL OR r.createdAt >= :createdFrom)" + " AND (:createdTo IS NULL OR r.createdAt <= :createdTo)" + " AND (:unreplied = false OR NOT EXISTS" + " (SELECT 1 FROM CompanyReviewReply rr" + " WHERE rr.reviewId = r.id AND rr.moderationStatus = 'APPROVED'))" + " ORDER BY r.createdAt DESC") Page findPublicReviews(@Param("jobId") Long jobId, @Param("status") String status, @Param("moderationStatuses") Collection moderationStatuses, @Param("ratingType") String ratingType, @Param("createdFrom") LocalDateTime createdFrom, @Param("createdTo") LocalDateTime createdTo, @Param("unreplied") boolean unreplied, Pageable pageable); /** 管理端待审列表:PENDING,支持 jobId / riskLevel / 时间范围筛选 */ @Query("SELECT r FROM JobReview r WHERE r.status = 'PUBLISHED' AND r.moderationStatus = 'PENDING'" + " AND (:jobId IS NULL OR r.jobId = :jobId)" + " AND (:riskLevel IS NULL OR r.riskLevel = :riskLevel)" + " AND (:createdFrom IS NULL OR r.createdAt >= :createdFrom)" + " AND (:createdTo IS NULL OR r.createdAt <= :createdTo)") Page findPendingModeration(@Param("jobId") Long jobId, @Param("riskLevel") String riskLevel, @Param("createdFrom") LocalDateTime createdFrom, @Param("createdTo") LocalDateTime createdTo, Pageable pageable); }