Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9db70dc
[feat] 게시글 인기글(추천 임계치) 조회 API 추가
You-Hyuk Sep 19, 2026
25ea648
[feat] Notice 엔티티 및 마이그레이션 추가
You-Hyuk Sep 19, 2026
17321fd
[feat] 공지사항 공개 조회 API 추가
You-Hyuk Sep 19, 2026
08f653d
[feat] 관리자 공지사항 작성·수정·삭제 API 추가
You-Hyuk Sep 19, 2026
5ec48be
[feat] Report 엔티티 및 마이그레이션 추가
You-Hyuk Sep 19, 2026
5839843
[feat] 신고 생성 API 및 이벤트 발행 추가
You-Hyuk Sep 19, 2026
6a62380
[feat] 신고 접수 디스코드 알림 추가
You-Hyuk Sep 19, 2026
9901121
[feat] 게시글·댓글 관리자 강제 삭제 메서드 추가
You-Hyuk Sep 19, 2026
0333dc1
[feat] 관리자 신고 처리 API 추가
You-Hyuk Sep 19, 2026
9fc2e4d
[feat] 관리자 신고 조회 응답에 신고자 닉네임 추가
You-Hyuk Sep 19, 2026
694453f
[fix] PR #124 CodeRabbit 리뷰 반영 (공지 검증, 정렬 안정성, CD 환경변수)
You-Hyuk Sep 19, 2026
09201cd
[style] 테스트 파일 import 순서를 프로젝트 컨벤션에 맞게 정렬
You-Hyuk Sep 19, 2026
cdeafdf
[merge] #123 -> main
You-Hyuk Sep 20, 2026
96f3bbf
[feat] Rating 엔티티 및 기반 구조 추가
You-Hyuk Sep 20, 2026
e06c870
[feat] 별점 등록·수정·조회·취소 API 추가
You-Hyuk Sep 20, 2026
c928f23
[feat] 공연·릴리즈 응답에 평균 별점 필드 추가
You-Hyuk Sep 20, 2026
dae1adc
[style] be-review·simplify 반영 (포맷·중복 제거·보안 설정 보강)
You-Hyuk Sep 20, 2026
4bfb4c7
[feat] 공연 별점 등록에 ENDED 상태 검증 추가
You-Hyuk Sep 20, 2026
91024f7
[fix] 별점 등록 API에 CodeRabbit 리뷰 지적사항 반영
You-Hyuk Sep 21, 2026
761e9d3
[fix] 엔티티 멘션 검색 API 인증 누락 수정
You-Hyuk Sep 21, 2026
2b79362
[merge] #125 -> develop
You-Hyuk Sep 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,15 @@ jobs:
V_DISCORD_WEBHOOK_5XX_URL: ${{ secrets.DISCORD_WEBHOOK_5XX_URL }}
V_DISCORD_WEBHOOK_4XX_URL: ${{ secrets.DISCORD_WEBHOOK_4XX_URL }}
V_DISCORD_WEBHOOK_INQUIRY_URL: ${{ secrets.DISCORD_WEBHOOK_INQUIRY_URL }}
V_DISCORD_WEBHOOK_REPORT_URL: ${{ secrets.DISCORD_WEBHOOK_REPORT_URL }}
V_MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
V_MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 2222
envs: V_GHCR_TOKEN,V_GHCR_USER,V_DB_URL,V_DB_USERNAME,V_DB_PASSWORD,V_JWT_SECRET,V_GOOGLE_CLIENT_ID,V_GOOGLE_CLIENT_SECRET,V_KAKAO_CLIENT_ID,V_KAKAO_CLIENT_SECRET,V_CORS_ALLOWED_ORIGINS,V_OAUTH2_REDIRECT_BASE_URI,V_GOOGLE_REDIRECT_URI,V_KAKAO_REDIRECT_URI,V_DATA_PIPELINE_SECRET,V_DATA_PIPELINE_BASE_URL,V_REDIS_PASSWORD,V_DISCORD_WEBHOOK_5XX_URL,V_DISCORD_WEBHOOK_4XX_URL,V_DISCORD_WEBHOOK_INQUIRY_URL,V_MAIL_USERNAME,V_MAIL_PASSWORD
envs: V_GHCR_TOKEN,V_GHCR_USER,V_DB_URL,V_DB_USERNAME,V_DB_PASSWORD,V_JWT_SECRET,V_GOOGLE_CLIENT_ID,V_GOOGLE_CLIENT_SECRET,V_KAKAO_CLIENT_ID,V_KAKAO_CLIENT_SECRET,V_CORS_ALLOWED_ORIGINS,V_OAUTH2_REDIRECT_BASE_URI,V_GOOGLE_REDIRECT_URI,V_KAKAO_REDIRECT_URI,V_DATA_PIPELINE_SECRET,V_DATA_PIPELINE_BASE_URL,V_REDIS_PASSWORD,V_DISCORD_WEBHOOK_5XX_URL,V_DISCORD_WEBHOOK_4XX_URL,V_DISCORD_WEBHOOK_INQUIRY_URL,V_DISCORD_WEBHOOK_REPORT_URL,V_MAIL_USERNAME,V_MAIL_PASSWORD
script: |
echo "$V_GHCR_TOKEN" | docker login ghcr.io -u "$V_GHCR_USER" --password-stdin

Expand All @@ -100,6 +101,7 @@ jobs:
printf 'DISCORD_WEBHOOK_5XX_URL=%s\n' "$V_DISCORD_WEBHOOK_5XX_URL" >> ~/compose/be.env
printf 'DISCORD_WEBHOOK_4XX_URL=%s\n' "$V_DISCORD_WEBHOOK_4XX_URL" >> ~/compose/be.env
printf 'DISCORD_WEBHOOK_INQUIRY_URL=%s\n' "$V_DISCORD_WEBHOOK_INQUIRY_URL" >> ~/compose/be.env
printf 'DISCORD_WEBHOOK_REPORT_URL=%s\n' "$V_DISCORD_WEBHOOK_REPORT_URL" >> ~/compose/be.env
printf 'MAIL_USERNAME=%s\n' "$V_MAIL_USERNAME" >> ~/compose/be.env
printf 'MAIL_PASSWORD=%s\n' "$V_MAIL_PASSWORD" >> ~/compose/be.env

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,21 @@
import com.Coming.Backend.admin.dto.AdminInquiryDetailResponse;
import com.Coming.Backend.admin.dto.AdminInquiryListItemResponse;
import com.Coming.Backend.admin.dto.AdminInquiryStatusUpdateRequest;
import com.Coming.Backend.admin.dto.AdminNoticeCreateRequest;
import com.Coming.Backend.admin.dto.AdminNoticeCreateResponse;
import com.Coming.Backend.admin.dto.AdminNoticeDetailResponse;
import com.Coming.Backend.admin.dto.AdminNoticeListItemResponse;
import com.Coming.Backend.admin.dto.AdminNoticeUpdateRequest;
import com.Coming.Backend.admin.dto.AdminPendingConcertResponse;
import com.Coming.Backend.admin.dto.AdminReportDetailResponse;
import com.Coming.Backend.admin.dto.AdminReportListItemResponse;
import com.Coming.Backend.admin.dto.AdminReportStatusUpdateRequest;
import com.Coming.Backend.admin.service.AdminService;
import com.Coming.Backend.common.response.PageResponse;
import com.Coming.Backend.inquiry.entity.InquiryStatus;
import com.Coming.Backend.inquiry.entity.InquiryType;
import com.Coming.Backend.report.entity.ReportStatus;
import com.Coming.Backend.report.entity.ReportTargetType;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -35,6 +45,7 @@
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -106,6 +117,72 @@ public ResponseEntity<Void> updateInquiryStatus(
return ResponseEntity.ok().build();
}

@Operation(summary = "전체 공지사항 목록 조회")
@GetMapping("/notices")
public ResponseEntity<PageResponse<AdminNoticeListItemResponse>> getNotices(
@PageableDefault(size = 20) Pageable pageable) {
return ResponseEntity.ok(adminService.getNotices(pageable));
}

@Operation(summary = "공지사항 상세 조회 (비활성 포함)")
@ApiResponse(responseCode = "404", description = "NOTICE_NOT_FOUND")
@GetMapping("/notices/{id}")
public ResponseEntity<AdminNoticeDetailResponse> getAdminNotice(@PathVariable Long id) {
return ResponseEntity.ok(adminService.getAdminNotice(id));
}

@Operation(summary = "공지사항 작성")
@PostMapping("/notices")
public ResponseEntity<AdminNoticeCreateResponse> createNotice(
@AuthenticationPrincipal Long adminId,
@RequestBody @Valid AdminNoticeCreateRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(adminService.createNotice(adminId, request));
}

@Operation(summary = "공지사항 수정")
@ApiResponse(responseCode = "404", description = "NOTICE_NOT_FOUND")
@PatchMapping("/notices/{id}")
public ResponseEntity<Void> updateNotice(
@PathVariable Long id,
@RequestBody @Valid AdminNoticeUpdateRequest request) {
adminService.updateNotice(id, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "공지사항 삭제")
@ApiResponse(responseCode = "404", description = "NOTICE_NOT_FOUND")
@DeleteMapping("/notices/{id}")
public ResponseEntity<Void> deleteNotice(@PathVariable Long id) {
adminService.deleteNotice(id);
return ResponseEntity.noContent().build();
}

@Operation(summary = "전체 신고 목록 조회")
@GetMapping("/reports")
public ResponseEntity<PageResponse<AdminReportListItemResponse>> getReports(
@RequestParam(required = false) ReportTargetType targetType,
@RequestParam(required = false) ReportStatus status,
@PageableDefault(size = 20) Pageable pageable) {
return ResponseEntity.ok(adminService.getReports(targetType, status, pageable));
}

@Operation(summary = "신고 상세 조회")
@ApiResponse(responseCode = "404", description = "REPORT_NOT_FOUND")
@GetMapping("/reports/{id}")
public ResponseEntity<AdminReportDetailResponse> getReportDetail(@PathVariable Long id) {
return ResponseEntity.ok(adminService.getReportDetail(id));
}

@Operation(summary = "신고 처리 상태 변경 (deleteTarget=true 시 대상 게시글·댓글 강제 삭제)")
@ApiResponse(responseCode = "404", description = "REPORT_NOT_FOUND")
@PatchMapping("/reports/{id}/status")
public ResponseEntity<Void> updateReportStatus(
@PathVariable Long id,
@RequestBody @Valid AdminReportStatusUpdateRequest request) {
adminService.updateReportStatus(id, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "EXCLUDED 공연 목록 조회")
@GetMapping("/concerts/excluded")
public ResponseEntity<PageResponse<AdminExcludedConcertResponse>> getExcludedConcerts(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.Coming.Backend.admin.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record AdminNoticeCreateRequest(
@NotBlank @Size(max = 255) String title,
@NotBlank String content,
Boolean active
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.Coming.Backend.admin.dto;

public record AdminNoticeCreateResponse(Long noticeId) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.Coming.Backend.admin.dto;

import com.Coming.Backend.notice.entity.Notice;

import java.time.LocalDateTime;

public record AdminNoticeDetailResponse(
Long id,
String title,
String content,
boolean active,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
public static AdminNoticeDetailResponse of(Notice notice) {
return new AdminNoticeDetailResponse(
notice.getId(),
notice.getTitle(),
notice.getContent(),
notice.isActive(),
notice.getCreatedAt(),
notice.getUpdatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.Coming.Backend.admin.dto;

import com.Coming.Backend.notice.entity.Notice;

import java.time.LocalDateTime;

public record AdminNoticeListItemResponse(
Long id,
String title,
boolean active,
LocalDateTime createdAt
) {
public static AdminNoticeListItemResponse of(Notice notice) {
return new AdminNoticeListItemResponse(
notice.getId(),
notice.getTitle(),
notice.isActive(),
notice.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.Coming.Backend.admin.dto;

import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

public record AdminNoticeUpdateRequest(
@Pattern(regexp = "(?s).*\\S.*", message = "제목은 공백일 수 없습니다")
@Size(max = 255)
String title,

@Pattern(regexp = "(?s).*\\S.*", message = "내용은 공백일 수 없습니다")
String content,

Boolean active
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.Coming.Backend.admin.dto;

import com.Coming.Backend.report.entity.Report;
import com.Coming.Backend.report.entity.ReportReason;
import com.Coming.Backend.report.entity.ReportStatus;
import com.Coming.Backend.report.entity.ReportTargetType;

import java.time.LocalDateTime;

public record AdminReportDetailResponse(
Long id,
ReportTargetType targetType,
Long targetId,
ReportReason reason,
String detail,
ReportStatus status,
String adminNote,
Long reporterId,
String reporterNickname,
LocalDateTime createdAt
) {
public static AdminReportDetailResponse of(Report report, String reporterNickname) {
return new AdminReportDetailResponse(
report.getId(),
report.getTargetType(),
report.getTargetId(),
report.getReason(),
report.getDetail(),
report.getStatus(),
report.getAdminNote(),
report.getReporterId(),
reporterNickname,
report.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.Coming.Backend.admin.dto;

import com.Coming.Backend.report.entity.Report;
import com.Coming.Backend.report.entity.ReportReason;
import com.Coming.Backend.report.entity.ReportStatus;
import com.Coming.Backend.report.entity.ReportTargetType;

import java.time.LocalDateTime;

public record AdminReportListItemResponse(
Long id,
ReportTargetType targetType,
Long targetId,
ReportReason reason,
ReportStatus status,
Long reporterId,
String reporterNickname,
LocalDateTime createdAt
) {
public static AdminReportListItemResponse of(Report report, String reporterNickname) {
return new AdminReportListItemResponse(
report.getId(),
report.getTargetType(),
report.getTargetId(),
report.getReason(),
report.getStatus(),
report.getReporterId(),
reporterNickname,
report.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.Coming.Backend.admin.dto;

import com.Coming.Backend.report.entity.ReportStatus;
import jakarta.validation.constraints.NotNull;

public record AdminReportStatusUpdateRequest(
@NotNull ReportStatus status,
String adminNote,
Boolean deleteTarget
) {
}
Loading
Loading