Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
75 changes: 75 additions & 0 deletions src/main/java/gg/agit/konect/domain/event/controller/EventApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package gg.agit.konect.domain.event.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import gg.agit.konect.domain.event.dto.EventBoothMapResponse;
import gg.agit.konect.domain.event.dto.EventBoothsResponse;
import gg.agit.konect.domain.event.dto.EventContentsResponse;
import gg.agit.konect.domain.event.dto.EventHomeResponse;
import gg.agit.konect.domain.event.dto.EventMiniEventsResponse;
import gg.agit.konect.domain.event.dto.EventProgramsResponse;
import gg.agit.konect.domain.event.enums.EventProgramType;
import gg.agit.konect.global.auth.annotation.UserId;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Min;

@Tag(name = "(Normal) Event: 행사", description = "행사 API")
@RequestMapping("/events")
public interface EventApi {

@Operation(summary = "행사 홈 정보를 조회한다.")
@GetMapping("/{eventId}/home")
ResponseEntity<EventHomeResponse> getEventHome(
@PathVariable Integer eventId,
@UserId Integer userId
);

@Operation(summary = "행사 프로그램 목록을 조회한다.")
@GetMapping("/{eventId}/programs")
ResponseEntity<EventProgramsResponse> getEventPrograms(
@PathVariable Integer eventId,
@RequestParam(defaultValue = "ALL") EventProgramType type,
@RequestParam(defaultValue = "1") @Min(1) Integer page,
@RequestParam(defaultValue = "20") @Min(1) Integer limit,
@UserId Integer userId
);

@Operation(summary = "행사 부스 목록을 조회한다.")
@GetMapping("/{eventId}/booths")
ResponseEntity<EventBoothsResponse> getEventBooths(
@PathVariable Integer eventId,
@RequestParam(required = false) String category,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") @Min(1) Integer page,
@RequestParam(defaultValue = "20") @Min(1) Integer limit
);

@Operation(summary = "행사 부스 맵을 조회한다.")
@GetMapping("/{eventId}/booth-map")
ResponseEntity<EventBoothMapResponse> getEventBoothMap(
@PathVariable Integer eventId
);

@Operation(summary = "행사 미니 이벤트 목록을 조회한다.")
@GetMapping("/{eventId}/mini-events")
ResponseEntity<EventMiniEventsResponse> getEventMiniEvents(
@PathVariable Integer eventId,
@RequestParam(defaultValue = "1") @Min(1) Integer page,
@RequestParam(defaultValue = "20") @Min(1) Integer limit,
@UserId Integer userId
);

@Operation(summary = "행사 콘텐츠 목록을 조회한다.")
@GetMapping("/{eventId}/contents")
ResponseEntity<EventContentsResponse> getEventContents(
@PathVariable Integer eventId,
@RequestParam(required = false) String category,
@RequestParam(defaultValue = "1") @Min(1) Integer page,
@RequestParam(defaultValue = "20") @Min(1) Integer limit
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package gg.agit.konect.domain.event.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import gg.agit.konect.domain.event.dto.EventBoothMapResponse;
import gg.agit.konect.domain.event.dto.EventBoothsResponse;
import gg.agit.konect.domain.event.dto.EventContentsResponse;
import gg.agit.konect.domain.event.dto.EventHomeResponse;
import gg.agit.konect.domain.event.dto.EventMiniEventsResponse;
import gg.agit.konect.domain.event.dto.EventProgramsResponse;
import gg.agit.konect.domain.event.enums.EventProgramType;
import gg.agit.konect.domain.event.service.EventService;
import lombok.RequiredArgsConstructor;

@RestController
@Validated
@RequiredArgsConstructor
public class EventController implements EventApi {

private final EventService eventService;

@Override
public ResponseEntity<EventHomeResponse> getEventHome(Integer eventId, Integer userId) {
return ResponseEntity.ok(eventService.getEventHome(eventId, userId));
}

@Override
public ResponseEntity<EventProgramsResponse> getEventPrograms(Integer eventId, EventProgramType type, Integer page,
Integer limit,
Integer userId) {
return ResponseEntity.ok(eventService.getEventPrograms(eventId, type, page, limit, userId));
}

@Override
public ResponseEntity<EventBoothsResponse> getEventBooths(Integer eventId, String category, String keyword,
Integer page, Integer limit) {
return ResponseEntity.ok(eventService.getEventBooths(eventId, category, keyword, page, limit));
}

@Override
public ResponseEntity<EventBoothMapResponse> getEventBoothMap(Integer eventId) {
return ResponseEntity.ok(eventService.getEventBoothMap(eventId));
}

@Override
public ResponseEntity<EventMiniEventsResponse> getEventMiniEvents(Integer eventId, Integer page, Integer limit,
Integer userId) {
return ResponseEntity.ok(eventService.getEventMiniEvents(eventId, page, limit, userId));
}

@Override
public ResponseEntity<EventContentsResponse> getEventContents(Integer eventId, String category, Integer page,
Integer limit) {
return ResponseEntity.ok(eventService.getEventContents(eventId, category, page, limit));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gg.agit.konect.domain.event.dto;

import java.util.List;

public record EventBoothMapResponse(
String mapImageUrl,
List<ZoneResponse> zones,
List<BoothMapItemResponse> booths
) {

public record ZoneResponse(
String code,
String label
) {
}

public record BoothMapItemResponse(
Integer boothId,
String name,
String zone,
Integer x,
Integer y,
Integer width,
Integer height,
String status
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

public record EventBoothSummaryResponse(
Integer boothId,
String name,
String category,
String locationLabel,
String zone,
String thumbnailUrl,
boolean open
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

import java.util.List;

public record EventBoothsResponse(
Long totalCount,
Integer currentCount,
Integer totalPage,
Integer currentPage,
List<EventBoothSummaryResponse> booths
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package gg.agit.konect.domain.event.dto;

import java.time.LocalDateTime;

public record EventContentSummaryResponse(
Integer contentId,
String title,
String thumbnailUrl,
String type,
String summary,
LocalDateTime publishedAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

import java.util.List;

public record EventContentsResponse(
Long totalCount,
Integer currentCount,
Integer totalPage,
Integer currentPage,
List<EventContentSummaryResponse> contents
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package gg.agit.konect.domain.event.dto;

import java.time.LocalDateTime;

public record EventHomeResponse(
Integer eventId,
String title,
String subtitle,
String posterImageUrl,
LocalDateTime startAt,
LocalDateTime endAt,
String notice,
Summary summary,
UserStatus userStatus
) {

public record Summary(
Integer programCount,
Integer boothCount,
Integer eventCount,
Integer contentCount
) {
}

public record UserStatus(
Integer point,
Integer participatedEventCount
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

public record EventMiniEventSummaryResponse(
Integer miniEventId,
String title,
String thumbnailUrl,
String description,
String reward,
String status,
boolean joined
Comment on lines +3 to +10
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

[LEVEL: medium] Line 10의 joined 값이 현재 구현에서 상수로 고정되어 응답 의미가 깨집니다.
문제: src/main/java/gg/agit/konect/domain/event/service/EventService.java Line 241-251에서 EventMiniEventSummaryResponse.joined에 항상 false를 주입해 DTO가 사용자 참여 상태를 표현하지 못합니다.
영향: 실제 참여 사용자도 미참여로 내려가 버튼 상태/UX가 잘못될 수 있습니다.
제안: 참여 여부를 사용자 기준으로 조회해 채우거나, 기능 미지원 단계라면 joined를 nullable로 변경(또는 필드 제거)해 오해를 방지하세요. As per coding guidelines, "확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/gg/agit/konect/domain/event/dto/EventMiniEventSummaryResponse.java`
around lines 3 - 10, EventMiniEventSummaryResponse.joined is currently hardcoded
to false in EventService (the DTO population around the block that constructs
EventMiniEventSummaryResponse), which breaks semantics; update the code that
builds EventMiniEventSummaryResponse in EventService to compute the real
participation state for the current user (e.g., call your participant lookup
like EventParticipantRepository.existsByUserIdAndMiniEventId(userId,
miniEventId) or a service wrapper) and pass that boolean into the record, or if
real-time participation cannot be supported yet, change the DTO signature
(EventMiniEventSummaryResponse) to use Boolean joined (nullable) or remove the
field to avoid misleading clients. Ensure you reference
EventMiniEventSummaryResponse and the method in EventService that maps/creates
the DTO so the change is applied where the DTO is instantiated.

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

import java.util.List;

public record EventMiniEventsResponse(
Long totalCount,
Integer currentCount,
Integer totalPage,
Integer currentPage,
List<EventMiniEventSummaryResponse> miniEvents
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.event.dto;

public record EventProgramSummaryResponse(
Integer programId,
String title,
String description,
String thumbnailUrl,
Integer rewardPoint,
String status,
boolean participated
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gg.agit.konect.domain.event.dto;

import java.util.List;

import gg.agit.konect.domain.event.enums.EventProgramType;

public record EventProgramsResponse(
Long totalCount,
Integer currentCount,
Integer totalPage,
Integer currentPage,
EventProgramType type,
List<EventProgramSummaryResponse> programs
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.agit.konect.domain.event.enums;

public enum EventBoothMapItemStatus {
OPEN,
CLOSED,
HIDDEN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.agit.konect.domain.event.enums;

public enum EventContentType {
ARTICLE,
IMAGE,
VIDEO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.agit.konect.domain.event.enums;

public enum EventProgramType {
ALL,
POINT,
RESONANCE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.agit.konect.domain.event.enums;

public enum EventProgressStatus {
UPCOMING,
ONGOING,
ENDED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.agit.konect.domain.event.enums;

public enum EventStatus {
DRAFT,
PUBLISHED,
ENDED
}
52 changes: 52 additions & 0 deletions src/main/java/gg/agit/konect/domain/event/model/Event.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gg.agit.konect.domain.event.model;

import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;

import java.time.LocalDateTime;

import gg.agit.konect.domain.event.enums.EventStatus;
import gg.agit.konect.global.model.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@Table(name = "event")
@NoArgsConstructor(access = PROTECTED)
public class Event extends BaseEntity {

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", nullable = false, updatable = false, unique = true)
private Integer id;

@Column(name = "title", nullable = false, length = 100)
private String title;

@Column(name = "subtitle", length = 255)
private String subtitle;

@Column(name = "poster_image_url", length = 255)
private String posterImageUrl;

@Column(name = "notice", columnDefinition = "TEXT")
private String notice;
Comment on lines +40 to +41
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

[LEVEL: high]
문제: Line [40-41]의 notice 필드는 엔티티에만 존재하고, src/main/resources/db/migration/V70__add_event_tables.sql Line [1-13]의 event 테이블에는 해당 컬럼이 없어 스키마 불일치입니다.
영향: 이벤트 조회/저장 시 notice가 포함된 SQL이 실행되면 운영 DB에서 Unknown column 'notice'로 500 오류가 발생할 수 있습니다(마이그레이션 직후 바로 재현 가능).
제안: Flyway 마이그레이션으로 event.notice를 추가하거나 엔티티에서 필드를 제거해 스키마와 모델을 반드시 일치시켜 주세요; As per coding guidelines "보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/gg/agit/konect/domain/event/model/Event.java` around lines 40 -
41, Event 엔티티의 private String notice 필드(Event.java)와 Flyway 마이그레이션
V70__add_event_tables.sql의 event 테이블 스키마가 불일치합니다; 해결하려면 둘 중 하나를 선택해 일치시켜 주세요:
(1) 영구 해결로 V70 또는 새로운 Flyway 마이그레이션에 event.notice 컬럼(예: TEXT 타입) 추가하거나, (2) 모델
우선이라면 Event 클래스에서 notice 필드와 관련 게터/세터/사용처를 제거하여 엔티티를 스키마에 맞추세요; 변경 후 마이그레이션을
실행하고 관련 저장/조회 테스트 및 통합 시나리오를 검증해 오류(Unknown column) 재발생이 없는지 확인하세요.


@Column(name = "start_at", nullable = false)
private LocalDateTime startAt;

@Column(name = "end_at", nullable = false)
private LocalDateTime endAt;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 20)
private EventStatus status;
}
Loading
Loading