Skip to content
Closed
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
11 changes: 11 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "fe-dev",
"runtimeExecutable": "npm",
"runtimeArgs": ["--prefix", "FE", "run", "dev"],
"port": 5173
}
]
}
2 changes: 2 additions & 0 deletions BE/src/main/java/com/min/edu/BeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class BeApplication {

public static void main(String[] args) {
Expand Down
71 changes: 71 additions & 0 deletions BE/src/main/java/com/min/edu/booth/domain/BoothRecruitment.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,75 @@ public class BoothRecruitment {

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

public static BoothRecruitment create(
Long eventId,
String title,
OffsetDateTime recruitmentStartAt,
OffsetDateTime recruitmentEndAt,
String participantTarget,
String qualification,
String selectionMethod,
OffsetDateTime expectedDecisionAt,
String contactName,
String contactEmail,
String contactPhone,
String notice,
OffsetDateTime now) {
return BoothRecruitment.builder()
.eventId(eventId)
.title(title)
.recruitmentStartAt(recruitmentStartAt)
.recruitmentEndAt(recruitmentEndAt)
.participantTarget(participantTarget)
.qualification(qualification)
.selectionMethod(selectionMethod)
.expectedDecisionAt(expectedDecisionAt)
.contactName(contactName)
.contactEmail(contactEmail)
.contactPhone(contactPhone)
.notice(notice)
.status(BoothRecruitmentStatus.BEFORE_OPEN)
.createdAt(now)
.updatedAt(now)
.build();
}

public void updateDetails(
String title,
OffsetDateTime recruitmentStartAt,
OffsetDateTime recruitmentEndAt,
String participantTarget,
String qualification,
String selectionMethod,
OffsetDateTime expectedDecisionAt,
String contactName,
String contactEmail,
String contactPhone,
String notice,
OffsetDateTime now) {
this.title = title;
this.recruitmentStartAt = recruitmentStartAt;
this.recruitmentEndAt = recruitmentEndAt;
this.participantTarget = participantTarget;
this.qualification = qualification;
this.selectionMethod = selectionMethod;
this.expectedDecisionAt = expectedDecisionAt;
this.contactName = contactName;
this.contactEmail = contactEmail;
this.contactPhone = contactPhone;
this.notice = notice;
this.updatedAt = now;
}

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

public void complete(OffsetDateTime now) {
this.status = BoothRecruitmentStatus.COMPLETED;
this.completedAt = now;
this.updatedAt = now;
}
}
12 changes: 12 additions & 0 deletions BE/src/main/java/com/min/edu/common/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
Expand Down Expand Up @@ -44,6 +45,17 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http,
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/reissue", "/auth/logout").permitAll()
.requestMatchers("/auth/me").authenticated()
.requestMatchers(HttpMethod.GET, "/booth-recruitments/**").permitAll()
.requestMatchers(HttpMethod.GET, "/events/*/booth-recruitment/management")
.authenticated()
.requestMatchers(
HttpMethod.POST,
"/events/*/booth-recruitment",
"/events/*/booth-recruitment/completion",
"/events/*/booth-recruitment/closure")
.authenticated()
.requestMatchers(HttpMethod.PATCH, "/events/*/booth-recruitment").authenticated()
.requestMatchers(HttpMethod.DELETE, "/events/*/booth-recruitment").authenticated()
.anyRequest().permitAll())
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public enum GlobalErrorCode {
EVENT_NOT_FOUND(HttpStatus.NOT_FOUND, "EVENT_404_001", "행사를 찾을 수 없습니다."),
ORDER_NUMBER_GENERATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "TICKET_500_001", "주문번호 생성에 실패했습니다."),
EXCHANGE_CODE_GENERATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "ADMISSION_500_001", "교환 코드 생성에 실패했습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_500", "서버 내부 오류가 발생했습니다.");
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_500", "서버 내부 오류가 발생했습니다."),
RECRUITMENT_PERIOD_INVALID(HttpStatus.BAD_REQUEST, "RECRUITMENT_400_001", "모집 종료일은 시작일 이후여야 합니다."),
RECRUITMENT_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "RECRUITMENT_400_002", "이미 모집 공고가 등록된 행사입니다."),
RECRUITMENT_STATUS_TRANSITION_INVALID(HttpStatus.BAD_REQUEST, "RECRUITMENT_400_003", "허용되지 않는 상태 변경입니다.");

private final HttpStatus status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.min.edu.event.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.min.edu.event.domain.EventMember;
import com.min.edu.event.domain.EventRole;

public interface EventMemberRepository extends JpaRepository<EventMember, Long> {
boolean existsByEventIdAndMemberIdAndEventRoleAndActiveTrue(
Long eventId,
Long memberId,
EventRole eventRole
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.min.edu.event.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.min.edu.event.domain.Event;

public interface EventRepository extends JpaRepository<Event, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.min.edu.recruitment.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.BoothRecruitmentStatus;
import com.min.edu.common.response.ApiResponse;
import com.min.edu.recruitment.dto.BoothRecruitmentCreateRequestDto;
import com.min.edu.recruitment.dto.BoothRecruitmentResponseDto;
import com.min.edu.recruitment.dto.BoothRecruitmentUpdateRequestDto;
import com.min.edu.recruitment.service.BoothRecruitmentService;

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

@RestController
@RequiredArgsConstructor
public class BoothRecruitmentController {

private final BoothRecruitmentService boothRecruitmentService;

// REC-API-001
@GetMapping("/booth-recruitments")
public ApiResponse<List<BoothRecruitmentResponseDto>> list(
@RequestParam(required = false) BoothRecruitmentStatus status) {
return ApiResponse.success(boothRecruitmentService.listPublic(status));
}

// REC-API-002
@GetMapping("/booth-recruitments/{recruitmentId}")
public ApiResponse<BoothRecruitmentResponseDto> getDetail(@PathVariable Long recruitmentId) {
return ApiResponse.success(boothRecruitmentService.getPublicDetail(recruitmentId));
}

// REC-API-003
@PostMapping("/events/{eventId}/booth-recruitment")
public ApiResponse<BoothRecruitmentResponseDto> create(
@PathVariable Long eventId,
@Valid @RequestBody BoothRecruitmentCreateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(
boothRecruitmentService.create(eventId, request, member)
);
}

// REC-API-004
@GetMapping("/events/{eventId}/booth-recruitment/management")
public ApiResponse<BoothRecruitmentResponseDto> getManagement(
@PathVariable Long eventId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothRecruitmentService.getManagement(eventId, member));
}

// REC-API-005
@PatchMapping("/events/{eventId}/booth-recruitment")
public ApiResponse<BoothRecruitmentResponseDto> update(
@PathVariable Long eventId,
@Valid @RequestBody BoothRecruitmentUpdateRequestDto request,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(
boothRecruitmentService.update(eventId, request, member)
);
}

@DeleteMapping("/events/{eventId}/booth-recruitment")
public ApiResponse<Void> delete(
@PathVariable Long eventId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
boothRecruitmentService.delete(eventId, member);
return ApiResponse.success();
}

// REC-API-006
@PostMapping("/events/{eventId}/booth-recruitment/completion")
public ApiResponse<BoothRecruitmentResponseDto> complete(
@PathVariable Long eventId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothRecruitmentService.complete(eventId, member));
}

// REC-API-007
@PostMapping("/events/{eventId}/booth-recruitment/closure")
public ApiResponse<BoothRecruitmentResponseDto> close(
@PathVariable Long eventId,
@AuthenticationPrincipal AuthenticatedMemberDto member) {
return ApiResponse.success(boothRecruitmentService.close(eventId, member));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.min.edu.recruitment.dto;

import java.time.OffsetDateTime;

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

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BoothRecruitmentCreateRequestDto {

@NotBlank
private String title;

@NotNull
private OffsetDateTime recruitmentStartAt;

@NotNull
private OffsetDateTime recruitmentEndAt;

@NotBlank
private String participantTarget;

private String qualification;

private String selectionMethod;

private OffsetDateTime expectedDecisionAt;

@NotBlank
private String contactName;

@NotBlank
@Email
private String contactEmail;

@NotBlank
private String contactPhone;

private String notice;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.min.edu.recruitment.dto;

import java.time.OffsetDateTime;

import com.min.edu.booth.domain.BoothRecruitment;
import com.min.edu.booth.domain.BoothRecruitmentStatus;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BoothRecruitmentResponseDto {

private Long id;
private Long eventId;
private String title;
private OffsetDateTime recruitmentStartAt;
private OffsetDateTime recruitmentEndAt;
private String participantTarget;
private String qualification;
private String selectionMethod;
private OffsetDateTime expectedDecisionAt;
private String contactName;
private String contactEmail;
private String contactPhone;
private String notice;
private BoothRecruitmentStatus status;
private OffsetDateTime completedAt;
private OffsetDateTime createdAt;
private OffsetDateTime updatedAt;

public static BoothRecruitmentResponseDto from(BoothRecruitment recruitment) {
return BoothRecruitmentResponseDto.builder()
.id(recruitment.getId())
.eventId(recruitment.getEventId())
.title(recruitment.getTitle())
.recruitmentStartAt(recruitment.getRecruitmentStartAt())
.recruitmentEndAt(recruitment.getRecruitmentEndAt())
.participantTarget(recruitment.getParticipantTarget())
.qualification(recruitment.getQualification())
.selectionMethod(recruitment.getSelectionMethod())
.expectedDecisionAt(recruitment.getExpectedDecisionAt())
.contactName(recruitment.getContactName())
.contactEmail(recruitment.getContactEmail())
.contactPhone(recruitment.getContactPhone())
.notice(recruitment.getNotice())
.status(recruitment.getStatus())
.completedAt(recruitment.getCompletedAt())
.createdAt(recruitment.getCreatedAt())
.updatedAt(recruitment.getUpdatedAt())
.build();
}
}
Loading