diff --git a/src/main/java/gg/agit/konect/domain/event/controller/EventApi.java b/src/main/java/gg/agit/konect/domain/event/controller/EventApi.java index 1a215b36..b0c648a3 100644 --- a/src/main/java/gg/agit/konect/domain/event/controller/EventApi.java +++ b/src/main/java/gg/agit/konect/domain/event/controller/EventApi.java @@ -6,6 +6,8 @@ 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.EventProgramsResponse; import gg.agit.konect.domain.event.enums.EventProgramType; import gg.agit.konect.global.auth.annotation.UserId; @@ -26,4 +28,20 @@ ResponseEntity getEventPrograms( @RequestParam(defaultValue = "20") @Min(1) Integer limit, @UserId Integer userId ); + + @Operation(summary = "행사 부스 목록을 조회한다.") + @GetMapping("/{eventId}/booths") + ResponseEntity 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 getEventBoothMap( + @PathVariable Integer eventId + ); } diff --git a/src/main/java/gg/agit/konect/domain/event/controller/EventController.java b/src/main/java/gg/agit/konect/domain/event/controller/EventController.java index ce07b41e..603ad860 100644 --- a/src/main/java/gg/agit/konect/domain/event/controller/EventController.java +++ b/src/main/java/gg/agit/konect/domain/event/controller/EventController.java @@ -4,6 +4,8 @@ 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.EventProgramsResponse; import gg.agit.konect.domain.event.enums.EventProgramType; import gg.agit.konect.domain.event.service.EventService; @@ -22,4 +24,15 @@ public ResponseEntity getEventPrograms(Integer eventId, E Integer userId) { return ResponseEntity.ok(eventService.getEventPrograms(eventId, type, page, limit, userId)); } + + @Override + public ResponseEntity getEventBooths(Integer eventId, String category, String keyword, + Integer page, Integer limit) { + return ResponseEntity.ok(eventService.getEventBooths(eventId, category, keyword, page, limit)); + } + + @Override + public ResponseEntity getEventBoothMap(Integer eventId) { + return ResponseEntity.ok(eventService.getEventBoothMap(eventId)); + } } diff --git a/src/main/java/gg/agit/konect/domain/event/dto/EventBoothSummaryResponse.java b/src/main/java/gg/agit/konect/domain/event/dto/EventBoothSummaryResponse.java new file mode 100644 index 00000000..95fb2b12 --- /dev/null +++ b/src/main/java/gg/agit/konect/domain/event/dto/EventBoothSummaryResponse.java @@ -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 +) { +} diff --git a/src/main/java/gg/agit/konect/domain/event/dto/EventBoothsResponse.java b/src/main/java/gg/agit/konect/domain/event/dto/EventBoothsResponse.java new file mode 100644 index 00000000..5be449ed --- /dev/null +++ b/src/main/java/gg/agit/konect/domain/event/dto/EventBoothsResponse.java @@ -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 booths +) { +} diff --git a/src/main/java/gg/agit/konect/domain/event/model/EventBooth.java b/src/main/java/gg/agit/konect/domain/event/model/EventBooth.java new file mode 100644 index 00000000..ba38b15b --- /dev/null +++ b/src/main/java/gg/agit/konect/domain/event/model/EventBooth.java @@ -0,0 +1,56 @@ +package gg.agit.konect.domain.event.model; + +import static jakarta.persistence.FetchType.LAZY; +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + +import gg.agit.konect.global.model.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "event_booth") +@NoArgsConstructor(access = PROTECTED) +public class EventBooth extends BaseEntity { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id", nullable = false, updatable = false, unique = true) + private Integer id; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "event_id", nullable = false, updatable = false) + private Event event; + + @Column(name = "name", nullable = false, length = 100) + private String name; + + @Column(name = "category", nullable = false, length = 50) + private String category; + + @Column(name = "description", columnDefinition = "TEXT") + private String description; + + @Column(name = "location_label", length = 100) + private String locationLabel; + + @Column(name = "zone", length = 50) + private String zone; + + @Column(name = "thumbnail_url", length = 255) + private String thumbnailUrl; + + @Column(name = "is_open", nullable = false) + private Boolean isOpen; + + @Column(name = "display_order", nullable = false) + private Integer displayOrder; +} diff --git a/src/main/java/gg/agit/konect/domain/event/repository/EventBoothRepository.java b/src/main/java/gg/agit/konect/domain/event/repository/EventBoothRepository.java new file mode 100644 index 00000000..63795ec4 --- /dev/null +++ b/src/main/java/gg/agit/konect/domain/event/repository/EventBoothRepository.java @@ -0,0 +1,14 @@ +package gg.agit.konect.domain.event.repository; + +import java.util.List; + +import org.springframework.data.repository.Repository; + +import gg.agit.konect.domain.event.model.EventBooth; + +public interface EventBoothRepository extends Repository { + + List findAllByEventIdOrderByDisplayOrderAscIdAsc(Integer eventId); + + int countByEventId(Integer eventId); +} diff --git a/src/main/java/gg/agit/konect/domain/event/service/EventService.java b/src/main/java/gg/agit/konect/domain/event/service/EventService.java index 5fc5cc0d..66584619 100644 --- a/src/main/java/gg/agit/konect/domain/event/service/EventService.java +++ b/src/main/java/gg/agit/konect/domain/event/service/EventService.java @@ -7,10 +7,14 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import gg.agit.konect.domain.event.dto.EventBoothSummaryResponse; +import gg.agit.konect.domain.event.dto.EventBoothsResponse; import gg.agit.konect.domain.event.dto.EventProgramSummaryResponse; import gg.agit.konect.domain.event.dto.EventProgramsResponse; import gg.agit.konect.domain.event.enums.EventProgramType; +import gg.agit.konect.domain.event.model.EventBooth; import gg.agit.konect.domain.event.model.EventProgram; +import gg.agit.konect.domain.event.repository.EventBoothRepository; import gg.agit.konect.domain.event.repository.EventProgramRepository; import gg.agit.konect.domain.event.repository.EventRepository; import gg.agit.konect.global.exception.CustomException; @@ -23,11 +27,11 @@ public class EventService { private final EventRepository eventRepository; private final EventProgramRepository eventProgramRepository; + private final EventBoothRepository eventBoothRepository; public EventProgramsResponse getEventPrograms(Integer eventId, EventProgramType type, Integer page, Integer limit, Integer userId) { - eventRepository.findById(eventId) - .orElseThrow(() -> CustomException.of(NOT_FOUND_EVENT)); + getEvent(eventId); List filteredPrograms = eventProgramRepository.findAllByEventIdOrderByDisplayOrderAscIdAsc( eventId).stream() @@ -49,6 +53,35 @@ public EventProgramsResponse getEventPrograms(Integer eventId, EventProgramType ); } + public EventBoothsResponse getEventBooths(Integer eventId, String category, String keyword, Integer page, + Integer limit) { + getEvent(eventId); + + List filteredBooths = eventBoothRepository.findAllByEventIdOrderByDisplayOrderAscIdAsc(eventId) + .stream() + .filter(booth -> category == null || category.isBlank() || booth.getCategory().equalsIgnoreCase(category)) + .filter(booth -> keyword == null || keyword.isBlank() || booth.getName().contains(keyword)) + .toList(); + + PagedResult pagedBooths = paginate(filteredBooths, page, limit); + List booths = pagedBooths.items().stream() + .map(this::toEventBoothSummaryResponse) + .toList(); + + return new EventBoothsResponse( + (long)pagedBooths.totalCount(), + booths.size(), + pagedBooths.totalPage(), + page, + booths + ); + } + + private void getEvent(Integer eventId) { + eventRepository.findById(eventId) + .orElseThrow(() -> CustomException.of(NOT_FOUND_EVENT)); + } + private PagedResult paginate(List items, Integer page, Integer limit) { int totalCount = items.size(); int fromIndex = Math.max((page - 1) * limit, 0); @@ -70,6 +103,18 @@ private EventProgramSummaryResponse toEventProgramSummaryResponse(EventProgram p ); } + private EventBoothSummaryResponse toEventBoothSummaryResponse(EventBooth booth) { + return new EventBoothSummaryResponse( + booth.getId(), + booth.getName(), + booth.getCategory(), + booth.getLocationLabel(), + booth.getZone(), + booth.getThumbnailUrl(), + Boolean.TRUE.equals(booth.getIsOpen()) + ); + } + private record PagedResult(List items, int totalCount, int totalPage) { } } diff --git a/src/main/resources/db/migration/V70__add_event_tables.sql b/src/main/resources/db/migration/V70__add_event_tables.sql index e9e35904..33d4e66d 100644 --- a/src/main/resources/db/migration/V70__add_event_tables.sql +++ b/src/main/resources/db/migration/V70__add_event_tables.sql @@ -28,3 +28,21 @@ CREATE TABLE IF NOT EXISTS event_program FOREIGN KEY (event_id) REFERENCES event (id) ON DELETE CASCADE ); + +CREATE TABLE IF NOT EXISTS event_booth +( + id INT AUTO_INCREMENT PRIMARY KEY, + event_id INT NOT NULL, + name VARCHAR(100) NOT NULL, + category VARCHAR(50) NOT NULL, + description TEXT, + location_label VARCHAR(100), + zone VARCHAR(50), + thumbnail_url VARCHAR(255), + is_open BOOLEAN NOT NULL DEFAULT true, + display_order INT NOT NULL DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, + + FOREIGN KEY (event_id) REFERENCES event (id) ON DELETE CASCADE +);