Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
899633a
feat: 모집 OPEN 상태에서는 종료일만 수정 가능하도록 제한, 자동 상태전환 테스트 추가
cfu344-png Aug 4, 2026
4a10176
feat: 부스 관리 API 구현 (목록·상세·등록·일괄등록·수정·상태변경·삭제)
cfu344-png Aug 4, 2026
1bdf969
Merge branch 'dev' into feature/booth-management
cfu344-png Aug 4, 2026
bc12bc5
fix: 스케줄러 테스트가 REQUIRES_NEW 트랜잭션 경계 때문에 항상 실패하던 문제 수정
cfu344-png Aug 4, 2026
8c41fc0
feat: 배정 부스 소개 수정, QR 발급/조회, 부스 검색 API 구현
cfu344-png Aug 4, 2026
61cff6e
fix: 부스 API 인증 규칙 누락으로 미인증 요청 시 500 발생하던 문제 수정
cfu344-png Aug 4, 2026
e7661a8
feat: 부스 관리 프론트엔드 구현 (WBS-186~190), QR 발급 멱등화
cfu344-png Aug 4, 2026
e5891da
feat: 모집 공고 상세에 부스 목록 공개 및 스펙 팝업 표시
cfu344-png Aug 4, 2026
43c3887
Merge remote-tracking branch 'origin/dev' into feature/booth-management
cfu344-png Aug 4, 2026
bd26b91
Merge branch 'dev' into feature/booth-management
cfu344-png Aug 4, 2026
9556d9c
Merge remote-tracking branch 'origin/dev' into feature/booth-management
cfu344-png Aug 4, 2026
8bf7fec
feat: 부스/모집 관리 화면을 실제 행사 선택과 연동
cfu344-png Aug 4, 2026
29d58e3
fix: CodeRabbit 리뷰 지적사항 반영 (검증, 동시성, 보안, 성능)
cfu344-png Aug 4, 2026
3a522f7
fix: 부스 더 보기에도 행사 전환 요청 무효화 가드 적용
cfu344-png Aug 4, 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
147 changes: 147 additions & 0 deletions BE/src/main/java/com/min/edu/booth/controller/BoothController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.min.edu.booth.controller;

import java.util.List;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.min.edu.auth.dto.AuthenticatedMemberDto;
import com.min.edu.booth.domain.BoothStatus;
import com.min.edu.booth.dto.BoothBulkCreateRequestDto;
import com.min.edu.booth.dto.BoothCreateRequestDto;
import com.min.edu.booth.dto.BoothIntroUpdateRequestDto;
import com.min.edu.booth.dto.BoothPageResponse;
import com.min.edu.booth.dto.BoothPublicPageResponse;
import com.min.edu.booth.dto.BoothQrResponseDto;
import com.min.edu.booth.dto.BoothResponseDto;
import com.min.edu.booth.dto.BoothStatusUpdateRequestDto;
import com.min.edu.booth.dto.BoothUpdateRequestDto;
import com.min.edu.booth.service.BoothService;
import com.min.edu.common.response.ApiResponse;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class BoothController {

private final BoothService boothService;

// 모집 공고 상세 화면 등 비로그인 사용자에게 부스 목록을 공개하기 위한 API
@GetMapping("/events/{eventId}/booths/public")
public ApiResponse<BoothPublicPageResponse> listPublic(
@PathVariable Long eventId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return ApiResponse.success(boothService.listPublic(eventId, page, size));
}

// WBS-067, WBS-077
@GetMapping("/events/{eventId}/booths")
public ApiResponse<BoothPageResponse> list(
@PathVariable Long eventId,
@RequestParam(required = false) BoothStatus status,
@RequestParam(required = false) String floorName,
@RequestParam(required = false) String zoneName,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(
boothService.list(eventId, status, floorName, zoneName, keyword, page, size, member)
);
}

// WBS-068
@GetMapping("/events/{eventId}/booths/{boothId}")
public ApiResponse<BoothResponseDto> getDetail(
@PathVariable Long eventId,
@PathVariable Long boothId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.getDetail(eventId, boothId, member));
}

// WBS-069
@PostMapping("/events/{eventId}/booths")
public ApiResponse<BoothResponseDto> create(
@PathVariable Long eventId,
@Valid @RequestBody BoothCreateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.create(eventId, request, member));
}

// WBS-070
@PostMapping("/events/{eventId}/booths/bulk")
public ApiResponse<List<BoothResponseDto>> createBulk(
@PathVariable Long eventId,
@Valid @RequestBody BoothBulkCreateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.createBulk(eventId, request, member));
}

// WBS-071
@PatchMapping("/events/{eventId}/booths/{boothId}")
public ApiResponse<BoothResponseDto> update(
@PathVariable Long eventId,
@PathVariable Long boothId,
@Valid @RequestBody BoothUpdateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.update(eventId, boothId, request, member));
}

// WBS-072
@PatchMapping("/events/{eventId}/booths/{boothId}/status")
public ApiResponse<BoothResponseDto> updateStatus(
@PathVariable Long eventId,
@PathVariable Long boothId,
@Valid @RequestBody BoothStatusUpdateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.updateStatus(eventId, boothId, request, member));
}

// WBS-073
@DeleteMapping("/events/{eventId}/booths/{boothId}")
public ApiResponse<Void> delete(
@PathVariable Long eventId,
@PathVariable Long boothId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
boothService.delete(eventId, boothId, member);
return ApiResponse.success();
}

// WBS-074
@PatchMapping("/events/{eventId}/booths/{boothId}/intro")
public ApiResponse<BoothResponseDto> updateIntro(
@PathVariable Long eventId,
@PathVariable Long boothId,
@Valid @RequestBody BoothIntroUpdateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.updateIntro(eventId, boothId, request, member));
}

// WBS-075
@PostMapping("/events/{eventId}/booths/{boothId}/qr")
public ApiResponse<BoothQrResponseDto> issueQr(
@PathVariable Long eventId,
@PathVariable Long boothId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.issueQr(eventId, boothId, member));
}

// WBS-076
@GetMapping("/events/{eventId}/booths/{boothId}/qr")
public ApiResponse<BoothQrResponseDto> getQr(
@PathVariable Long eventId,
@PathVariable Long boothId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothService.getQr(eventId, boothId, member));
}
}
98 changes: 98 additions & 0 deletions BE/src/main/java/com/min/edu/booth/domain/Booth.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,102 @@ public class Booth {

@Column(name = "updated_at", nullable = false)
private OffsetDateTime updatedAt;

public static Booth create(
Long eventId,
String boothCode,
String boothType,
String floorName,
String zoneName,
String locationDescription,
BigDecimal widthMeter,
BigDecimal depthMeter,
BigDecimal areaSqm,
String basicEquipment,
boolean electricityAvailable,
boolean waterAvailable,
boolean drainageAvailable,
boolean internetAvailable,
BigDecimal price,
OffsetDateTime now) {
return Booth.builder()
.eventId(eventId)
.boothCode(boothCode)
.boothType(boothType)
.floorName(floorName)
.zoneName(zoneName)
.locationDescription(locationDescription)
.widthMeter(widthMeter)
.depthMeter(depthMeter)
.areaSqm(areaSqm)
.basicEquipment(basicEquipment)
.electricityAvailable(electricityAvailable)
.waterAvailable(waterAvailable)
.drainageAvailable(drainageAvailable)
.internetAvailable(internetAvailable)
.price(price)
.status(BoothStatus.AVAILABLE)
.createdAt(now)
.updatedAt(now)
.build();
}

public void updateDetails(
String boothCode,
String boothType,
String floorName,
String zoneName,
String locationDescription,
BigDecimal widthMeter,
BigDecimal depthMeter,
BigDecimal areaSqm,
String basicEquipment,
boolean electricityAvailable,
boolean waterAvailable,
boolean drainageAvailable,
boolean internetAvailable,
BigDecimal price,
OffsetDateTime now) {
this.boothCode = boothCode;
this.boothType = boothType;
this.floorName = floorName;
this.zoneName = zoneName;
this.locationDescription = locationDescription;
this.widthMeter = widthMeter;
this.depthMeter = depthMeter;
this.areaSqm = areaSqm;
this.basicEquipment = basicEquipment;
this.electricityAvailable = electricityAvailable;
this.waterAvailable = waterAvailable;
this.drainageAvailable = drainageAvailable;
this.internetAvailable = internetAvailable;
this.price = price;
this.updatedAt = now;
}

public void changeStatus(BoothStatus status, OffsetDateTime now) {
this.status = status;
this.updatedAt = now;
}

public void updateIntro(
String displayName,
String shortIntro,
String description,
String exhibitionContent,
Long representativeFileId,
OffsetDateTime now) {
this.displayName = displayName;
this.shortIntro = shortIntro;
this.description = description;
this.exhibitionContent = exhibitionContent;
this.representativeFileId = representativeFileId;
this.updatedAt = now;
}

public void issueQrToken(String qrToken, OffsetDateTime now) {
this.qrToken = qrToken;
this.qrIssuedAt = now;
this.updatedAt = now;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public void updateDetails(
this.updatedAt = now;
}

public void updateEndAt(OffsetDateTime recruitmentEndAt, OffsetDateTime now) {
this.recruitmentEndAt = recruitmentEndAt;
this.updatedAt = now;
}

public void changeStatus(BoothRecruitmentStatus newStatus, OffsetDateTime now) {
this.status = newStatus;
this.updatedAt = now;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.min.edu.booth.dto;

import java.math.BigDecimal;
import java.util.List;

import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BoothBulkCreateRequestDto {

@NotEmpty
private List<@NotBlank String> boothCodes;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

@NotBlank
private String boothType;

private String floorName;

private String zoneName;

private String locationDescription;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal widthMeter;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal depthMeter;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal areaSqm;

private List<String> basicEquipment;

private boolean electricityAvailable;

private boolean waterAvailable;

private boolean drainageAvailable;

private boolean internetAvailable;

@NotNull
@DecimalMin(value = "0", inclusive = true)
private BigDecimal price;
}
51 changes: 51 additions & 0 deletions BE/src/main/java/com/min/edu/booth/dto/BoothCreateRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.min.edu.booth.dto;

import java.math.BigDecimal;
import java.util.List;

import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BoothCreateRequestDto {

@NotBlank
private String boothCode;

@NotBlank
private String boothType;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

private String floorName;

private String zoneName;

private String locationDescription;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal widthMeter;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal depthMeter;

@DecimalMin(value = "0", inclusive = true)
private BigDecimal areaSqm;

private List<String> basicEquipment;

private boolean electricityAvailable;

private boolean waterAvailable;

private boolean drainageAvailable;

private boolean internetAvailable;

@NotNull
@DecimalMin(value = "0", inclusive = true)
private BigDecimal price;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.min.edu.booth.dto;

import jakarta.validation.constraints.Size;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BoothIntroUpdateRequestDto {

@Size(max = 150)
private String displayName;

@Size(max = 300)
private String shortIntro;

private String description;

private String exhibitionContent;

private Long representativeFileId;
}
Loading