diff --git a/src/main/java/org/example/tackit/domain/entity/ColorChip.java b/src/main/java/org/example/tackit/domain/entity/ColorChip.java new file mode 100644 index 0000000..bdce6af --- /dev/null +++ b/src/main/java/org/example/tackit/domain/entity/ColorChip.java @@ -0,0 +1,35 @@ +package org.example.tackit.domain.entity; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ColorChip { + BLUE("blue"), + GRAY("gray"), + PINK("pink"), + ORANGE("orange"), + GREEN("green"); + + private final String colorName; + + // 프론트엔드 -> 백엔드 역직렬화 용 + @JsonCreator + public static ColorChip from(String value) { + for (ColorChip color : ColorChip.values()) { + if (color.getColorName().equalsIgnoreCase(value)) { + return color; + } + } + throw new IllegalArgumentException("지원하지 않는 컬러칩 색상입니다: " + value); + } + + // 백엔드 -> 프론트엔드 직렬화 용 + @JsonValue + public String getColorName() { + return colorName; + } +} \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/entity/Event.java b/src/main/java/org/example/tackit/domain/entity/Event.java index d756916..f6d10bc 100644 --- a/src/main/java/org/example/tackit/domain/entity/Event.java +++ b/src/main/java/org/example/tackit/domain/entity/Event.java @@ -1,6 +1,21 @@ package org.example.tackit.domain.entity; -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -9,84 +24,95 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "event") -public class Event{ - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "event_id") - private Long id; - - @Column(nullable = false) - private String title; - - @Column(nullable = false) - private LocalDateTime startsAt; - - @Column(nullable = false) - private LocalDateTime endsAt; - - @CreatedDate - private LocalDateTime createdAt; - - @LastModifiedDate - private LocalDateTime updatedAt; - - @Column(columnDefinition = "TEXT") - private String description; - - @Column(name = "color_chip", nullable = false) - private String colorChip; - - @Enumerated(EnumType.STRING) - @Column(name = "event_scope", nullable = false) - private EventScope eventScope; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "org_id", nullable = false) - private Organization organization; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member creator; - - @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) - private List participants = new ArrayList<>(); - - @Builder - public Event(String title, LocalDateTime startsAt, LocalDateTime endsAt, LocalDateTime createdAt, LocalDateTime updatedAt, - String description, String colorChip, EventScope eventScope, - Organization organization, Member creator) { - this.title = title; - this.startsAt = startsAt; - this.endsAt = endsAt; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - this.description = description; - this.colorChip = colorChip; - this.eventScope = eventScope; - this.organization = organization; - this.creator = creator; +public class Event { + + @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) + private final List participants = new ArrayList<>(); + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "event_id") + private Long id; + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private LocalDateTime startsAt; + + @Column(nullable = false) + private LocalDateTime endsAt; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + @Column(columnDefinition = "TEXT") + private String description; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 10) + private ColorChip colorChip; + + @Enumerated(EnumType.STRING) + @Column(name = "event_scope", nullable = false) + private EventScope eventScope; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "org_id", nullable = false) + private Organization organization; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member creator; + + @Builder + public Event(String title, LocalDateTime startsAt, LocalDateTime endsAt, LocalDateTime createdAt, + LocalDateTime updatedAt, + String description, ColorChip colorChip, EventScope eventScope, + Organization organization, Member creator) { + this.title = title; + this.startsAt = startsAt; + this.endsAt = endsAt; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.description = description; + this.colorChip = colorChip; + this.eventScope = eventScope; + this.organization = organization; + this.creator = creator; + } + + public void update(String title, LocalDateTime startsAt, LocalDateTime endsAt, + String description, ColorChip colorChip, EventScope eventScope) { + if (title != null) { + this.title = title; } - - public void update(String title, LocalDateTime startsAt, LocalDateTime endsAt, - String description, String colorChip, EventScope eventScope) { - if (title != null) this.title = title; - if (startsAt != null) this.startsAt = startsAt; - if (endsAt != null) this.endsAt = endsAt; - if (description != null) this.description = description; - if (colorChip != null) this.colorChip = colorChip; - if (eventScope != null) this.eventScope = eventScope; + if (startsAt != null) { + this.startsAt = startsAt; } - - // 참여자 목록 초기화 (참여자 수정 시 사용) - public void clearParticipants() { - this.participants.clear(); + if (endsAt != null) { + this.endsAt = endsAt; + } + if (description != null) { + this.description = description; } + if (colorChip != null) { + this.colorChip = colorChip; + } + if (eventScope != null) { + this.eventScope = eventScope; + } + } + + // 참여자 목록 초기화 (참여자 수정 시 사용) + public void clearParticipants() { + this.participants.clear(); + } } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/event/dto/EventCreateReqDto.java b/src/main/java/org/example/tackit/domain/event/dto/EventCreateReqDto.java index 8e948b6..c4cf46f 100644 --- a/src/main/java/org/example/tackit/domain/event/dto/EventCreateReqDto.java +++ b/src/main/java/org/example/tackit/domain/event/dto/EventCreateReqDto.java @@ -2,11 +2,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import lombok.*; -import org.example.tackit.domain.entity.EventScope; - import java.time.LocalDateTime; import java.util.List; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.example.tackit.domain.entity.ColorChip; +import org.example.tackit.domain.entity.EventScope; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -14,26 +18,26 @@ @Builder public class EventCreateReqDto { - @NotNull(message = "조직 ID는 필수입니다.") - private Long orgId; + @NotNull(message = "조직 ID는 필수입니다.") + private Long orgId; - @NotBlank(message = "일정 제목은 필수입니다.") - private String title; + @NotBlank(message = "일정 제목은 필수입니다.") + private String title; - @NotNull(message = "시작 시간은 필수입니다.") - private LocalDateTime startsAt; + @NotNull(message = "시작 시간은 필수입니다.") + private LocalDateTime startsAt; - @NotNull(message = "종료 시간은 필수입니다.") - private LocalDateTime endsAt; + @NotNull(message = "종료 시간은 필수입니다.") + private LocalDateTime endsAt; - private String description; + private String description; - @NotNull(message = "참여자 범위(eventScope)는 필수입니다.") - private EventScope eventScope; + @NotNull(message = "참여자 범위(eventScope)는 필수입니다.") + private EventScope eventScope; - @NotNull(message = "참여자 목록 필드는 필수입니다.") - private List participants; + @NotNull(message = "참여자 목록 필드는 필수입니다.") + private List participants; - @NotBlank(message = "색상 코드는 필수입니다.") - private String colorChip; + @NotNull(message = "색상 코드는 필수입니다.") + private ColorChip colorChip; } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/event/dto/EventDetailResDto.java b/src/main/java/org/example/tackit/domain/event/dto/EventDetailResDto.java index bcc3f96..b61ad42 100644 --- a/src/main/java/org/example/tackit/domain/event/dto/EventDetailResDto.java +++ b/src/main/java/org/example/tackit/domain/event/dto/EventDetailResDto.java @@ -6,6 +6,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.example.tackit.domain.entity.ColorChip; import org.example.tackit.domain.member.dto.SimpleMemberProfileDto; @Getter @@ -19,6 +20,6 @@ public class EventDetailResDto { private LocalDateTime startsAt; private LocalDateTime endsAt; private String description; - private String colorChip; + private ColorChip colorChip; private List participants; } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/event/dto/EventSimpleResDto.java b/src/main/java/org/example/tackit/domain/event/dto/EventSimpleResDto.java index 750cb62..f9c19b5 100644 --- a/src/main/java/org/example/tackit/domain/event/dto/EventSimpleResDto.java +++ b/src/main/java/org/example/tackit/domain/event/dto/EventSimpleResDto.java @@ -1,20 +1,21 @@ package org.example.tackit.domain.event.dto; +import java.time.LocalDateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; +import org.example.tackit.domain.entity.ColorChip; @Getter @NoArgsConstructor @AllArgsConstructor @Builder public class EventSimpleResDto { - private Long eventId; - private String title; - private LocalDateTime startsAt; - private LocalDateTime endsAt; - private String colorChip; + + private Long eventId; + private String title; + private LocalDateTime startsAt; + private LocalDateTime endsAt; + private ColorChip colorChip; } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/event/dto/EventUpdateReqDto.java b/src/main/java/org/example/tackit/domain/event/dto/EventUpdateReqDto.java index df14164..4a60e60 100644 --- a/src/main/java/org/example/tackit/domain/event/dto/EventUpdateReqDto.java +++ b/src/main/java/org/example/tackit/domain/event/dto/EventUpdateReqDto.java @@ -1,21 +1,26 @@ package org.example.tackit.domain.event.dto; -import lombok.*; -import org.example.tackit.domain.entity.EventScope; - import java.time.LocalDateTime; import java.util.List; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.example.tackit.domain.entity.ColorChip; +import org.example.tackit.domain.entity.EventScope; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Builder public class EventUpdateReqDto { - private String title; - private LocalDateTime startsAt; - private LocalDateTime endsAt; - private String description; - private EventScope eventScope; - private List participants; - private String colorChip; + + private String title; + private LocalDateTime startsAt; + private LocalDateTime endsAt; + private String description; + private EventScope eventScope; + private List participants; + private ColorChip colorChip; } \ No newline at end of file diff --git a/src/main/java/org/example/tackit/domain/event/service/EventService.java b/src/main/java/org/example/tackit/domain/event/service/EventService.java index 8320bd5..2d32f96 100644 --- a/src/main/java/org/example/tackit/domain/event/service/EventService.java +++ b/src/main/java/org/example/tackit/domain/event/service/EventService.java @@ -9,11 +9,12 @@ import org.example.tackit.domain.entity.Event; import org.example.tackit.domain.entity.EventParticipant; import org.example.tackit.domain.entity.Member; -import org.example.tackit.domain.entity.MemberRole; import org.example.tackit.domain.entity.Org.MemberOrg; -import org.example.tackit.domain.entity.Org.OrgStatus; import org.example.tackit.domain.entity.Org.Organization; -import org.example.tackit.domain.event.dto.*; +import org.example.tackit.domain.event.dto.EventCreateReqDto; +import org.example.tackit.domain.event.dto.EventDetailResDto; +import org.example.tackit.domain.event.dto.EventSimpleResDto; +import org.example.tackit.domain.event.dto.EventUpdateReqDto; import org.example.tackit.domain.event.repository.EventRepository; import org.example.tackit.domain.member.component.MemberOrgValidator; import org.example.tackit.domain.member.dto.SimpleMemberProfileDto; @@ -27,196 +28,173 @@ @Transactional(readOnly = true) public class EventService { - private final EventRepository eventRepository; - private final OrganizationRepository organizationRepository; - private final MemberOrgRepository memberOrgRepository; - private final MemberOrgValidator memberOrgValidator; - - // 일정 생성 - @Transactional - public Long createEvent(EventCreateReqDto reqDto, Long requesterId) { - MemberOrg memberOrg = validateExecutive(reqDto.getOrgId(), requesterId); - - Member creator = memberOrg.getMember(); - - Organization organization = organizationRepository.findById(reqDto.getOrgId()) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 조직입니다.")); - - Event event = Event.builder() - .organization(organization) - .creator(creator) - .title(reqDto.getTitle()) - .startsAt(reqDto.getStartsAt()) - .endsAt(reqDto.getEndsAt()) - .description(reqDto.getDescription()) - .colorChip(reqDto.getColorChip()) - .eventScope(reqDto.getEventScope()) - .build(); - - // 참여자 추가 - addParticipants(event, reqDto.getParticipants()); - - return eventRepository.save(event).getId(); + private final EventRepository eventRepository; + private final OrganizationRepository organizationRepository; + private final MemberOrgRepository memberOrgRepository; + private final MemberOrgValidator memberOrgValidator; + + // 일정 생성 + @Transactional + public Long createEvent(EventCreateReqDto reqDto, Long requesterMemberOrgId) { + MemberOrg memberOrg = memberOrgValidator.validateExecutive(reqDto.getOrgId(), + requesterMemberOrgId); + + Member creator = memberOrg.getMember(); + + Organization organization = organizationRepository.findById(reqDto.getOrgId()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 조직입니다.")); + + Event event = Event.builder() + .organization(organization) + .creator(creator) + .title(reqDto.getTitle()) + .startsAt(reqDto.getStartsAt()) + .endsAt(reqDto.getEndsAt()) + .description(reqDto.getDescription()) + .colorChip(reqDto.getColorChip()) + .eventScope(reqDto.getEventScope()) + .build(); + + // 참여자 추가 + addParticipants(event, reqDto.getParticipants()); + + return eventRepository.save(event).getId(); + } + + // 일정 수정 + @Transactional + public void updateEvent(Long eventId, EventUpdateReqDto reqDto, Long requesterMemberOrgId) { + Event event = findEventOrThrow(eventId); + + memberOrgValidator.validateExecutive(event.getOrganization().getId(), requesterMemberOrgId); + + event.update( + reqDto.getTitle(), + reqDto.getStartsAt(), + reqDto.getEndsAt(), + reqDto.getDescription(), + reqDto.getColorChip(), + reqDto.getEventScope() + ); + + // 참여자 목록 수정 (기존의 데이터 전부 삭제 후 다시 추가) + if (reqDto.getParticipants() != null) { + event.clearParticipants(); + addParticipants(event, reqDto.getParticipants()); } - - // 일정 수정 - @Transactional - public void updateEvent(Long eventId, EventUpdateReqDto reqDto, Long requesterId) { - Event event = findEventOrThrow(eventId); - - validateExecutive(event.getOrganization().getId(), requesterId); - - event.update( - reqDto.getTitle(), - reqDto.getStartsAt(), - reqDto.getEndsAt(), - reqDto.getDescription(), - reqDto.getColorChip(), - reqDto.getEventScope() - ); - - // 참여자 목록 수정 (기존의 데이터 전부 삭제 후 다시 추가) - if (reqDto.getParticipants() != null) { - event.clearParticipants(); - addParticipants(event, reqDto.getParticipants()); - } - } - - // 일정 삭제 - @Transactional - public void deleteEvent(Long eventId, Long requesterId) { - Event event = findEventOrThrow(eventId); - - validateExecutive(event.getOrganization().getId(), requesterId); - - eventRepository.delete(event); - } - - // 월간 일정 조회 - public List getMonthlyEvents(Long orgId, int year, int month, Long requesterId) { - validateMembership(orgId, requesterId); - - YearMonth yearMonth = YearMonth.of(year, month); - LocalDateTime startDateTime = yearMonth.atDay(1).atStartOfDay(); - LocalDateTime endDateTime = yearMonth.atEndOfMonth().atTime(LocalTime.MAX); - - List events = eventRepository.findAllByOrganizationIdAndDateRange(orgId, startDateTime, endDateTime); - - return events.stream() - .map(event -> EventSimpleResDto.builder() - .eventId(event.getId()) - .title(event.getTitle()) - .startsAt(event.getStartsAt()) - .endsAt(event.getEndsAt()) - .colorChip(event.getColorChip()) - .build()) - .collect(Collectors.toList()); + } + + // 일정 삭제 + @Transactional + public void deleteEvent(Long eventId, Long requesterMemberOrgId) { + Event event = findEventOrThrow(eventId); + + memberOrgValidator.validateExecutive(event.getOrganization().getId(), requesterMemberOrgId); + + eventRepository.delete(event); + } + + // 월간 일정 조회 + public List getMonthlyEvents(Long orgId, int year, int month, + Long requesterMemberOrgId) { + memberOrgValidator.validateActiveMembership(orgId, requesterMemberOrgId); + + YearMonth yearMonth = YearMonth.of(year, month); + LocalDateTime startDateTime = yearMonth.atDay(1).atStartOfDay(); + LocalDateTime endDateTime = yearMonth.atEndOfMonth().atTime(LocalTime.MAX); + + List events = eventRepository.findAllByOrganizationIdAndDateRange(orgId, startDateTime, + endDateTime); + + return events.stream() + .map(event -> EventSimpleResDto.builder() + .eventId(event.getId()) + .title(event.getTitle()) + .startsAt(event.getStartsAt()) + .endsAt(event.getEndsAt()) + .colorChip(event.getColorChip()) + .build()) + .collect(Collectors.toList()); + } + + // 일정 상세 조회 + public EventDetailResDto getEventDetail(Long eventId, Long requesterMemberOrgId) { + Event event = findEventOrThrow(eventId); + + memberOrgValidator.validateActiveMembership(event.getOrganization().getId(), + requesterMemberOrgId); + + List participantDtos = event.getParticipants().stream() + .map(ep -> SimpleMemberProfileDto.builder() + .orgMemberId(ep.getMemberOrg().getId()) + .profileImageUrl(ep.getMemberOrg().getProfileImageUrl()) + .nickname(ep.getMemberOrg().getNickname()) + .build()) + .collect(Collectors.toList()); + + return EventDetailResDto.builder() + .eventId(event.getId()) + .title(event.getTitle()) + .startsAt(event.getStartsAt()) + .endsAt(event.getEndsAt()) + .description(event.getDescription()) + .colorChip(event.getColorChip()) + .participants(participantDtos) + .build(); + } + + // 다가오는 일정 조회 + public List getUpcomingEvents(Long orgId, Long requesterMemberOrgId) { + memberOrgValidator.validateActiveMembership(orgId, requesterMemberOrgId); + + List events = eventRepository.findByOrganizationIdAndStartsAtAfterOrderByStartsAtAsc( + orgId, + LocalDateTime.now() + ); + + return events.stream() + .map(event -> EventSimpleResDto.builder() + .eventId(event.getId()) + .title(event.getTitle()) + .startsAt(event.getStartsAt()) + .endsAt(event.getEndsAt()) + .colorChip(event.getColorChip()) + .build()) + .collect(Collectors.toList()); + } + + // 이벤트 참가자 추가 메서드 + private void addParticipants(Event event, List memberOrgIds) { + if (memberOrgIds == null || memberOrgIds.isEmpty()) { + return; } - // 일정 상세 조회 - public EventDetailResDto getEventDetail(Long eventId, Long requesterMemberOrgId) { - Event event = findEventOrThrow(eventId); - - memberOrgValidator.validateActiveMembership(event.getOrganization().getId(), - requesterMemberOrgId); - - List participantDtos = event.getParticipants().stream() - .map(ep -> SimpleMemberProfileDto.builder() - .orgMemberId(ep.getMemberOrg().getId()) - .profileImageUrl(ep.getMemberOrg().getProfileImageUrl()) - .nickname(ep.getMemberOrg().getNickname()) - .build()) - .collect(Collectors.toList()); - - return EventDetailResDto.builder() - .eventId(event.getId()) - .title(event.getTitle()) - .startsAt(event.getStartsAt()) - .endsAt(event.getEndsAt()) - .description(event.getDescription()) - .colorChip(event.getColorChip()) - .participants(participantDtos) - .build(); - } - - // 다가오는 일정 조회 - public List getUpcomingEvents(Long orgId, Long requesterId) { - validateMembership(orgId, requesterId); - - List events = eventRepository.findByOrganizationIdAndStartsAtAfterOrderByStartsAtAsc( - orgId, - LocalDateTime.now() - ); - - return events.stream() - .map(event -> EventSimpleResDto.builder() - .eventId(event.getId()) - .title(event.getTitle()) - .startsAt(event.getStartsAt()) - .endsAt(event.getEndsAt()) - .colorChip(event.getColorChip()) - .build()) - .collect(Collectors.toList()); - } - - // 이벤트 참가자 추가 메서드 - private void addParticipants(Event event, List memberOrgIds) { - if (memberOrgIds == null || memberOrgIds.isEmpty()) return; - - List memberOrgs = memberOrgRepository.findAllById(memberOrgIds); - - // 개수 검증 - if (memberOrgs.size() != memberOrgIds.size()) { - throw new IllegalArgumentException("존재하지 않는 부원 ID가 포함되어 있습니다."); - } - - for (MemberOrg memberOrg : memberOrgs) { - // 소속 동아리 일치 여부 검증 - if (!memberOrg.getOrganization().getId().equals(event.getOrganization().getId())) { - throw new IllegalArgumentException("해당 동아리의 소속 부원이 아닙니다."); - } - - // 참여자 생성 - EventParticipant participant = EventParticipant.builder() - .event(event) - .memberOrg(memberOrg) - .build(); - - participant.assignEvent(event); - } - } - - // 이벤트 존재 확인 메서드 - private Event findEventOrThrow(Long eventId) { - return eventRepository.findById(eventId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 일정입니다.")); - } + List memberOrgs = memberOrgRepository.findAllById(memberOrgIds); - // 활동 중인 멤버(운영진 포함)인지 확인하는 메서드 - private void validateMembership(Long orgId, Long memberId) { - boolean isActiveMember = memberOrgRepository.existsByMemberIdAndOrganizationIdAndOrgStatus( - memberId, - orgId, - OrgStatus.ACTIVE - ); - - if (!isActiveMember) { - throw new IllegalArgumentException("해당 조직의 활동 중인 회원만 접근할 수 있습니다."); - } + // 개수 검증 + if (memberOrgs.size() != memberOrgIds.size()) { + throw new IllegalArgumentException("존재하지 않는 부원 ID가 포함되어 있습니다."); } - // 활동 중인 운영진인지 확인하는 메서드 - private MemberOrg validateExecutive(Long orgId, Long memberId) { - MemberOrg memberOrg = memberOrgRepository.findByMemberIdAndOrganizationIdAndOrgStatus( - memberId, - orgId, - OrgStatus.ACTIVE - ).orElseThrow(() -> new IllegalArgumentException("해당 조직의 활동 중인 회원만 접근할 수 있습니다.")); + for (MemberOrg memberOrg : memberOrgs) { + // 소속 동아리 일치 여부 검증 + if (!memberOrg.getOrganization().getId().equals(event.getOrganization().getId())) { + throw new IllegalArgumentException("해당 동아리의 소속 부원이 아닙니다."); + } - if (memberOrg.getMemberRole() != MemberRole.EXECUTIVE) { - throw new IllegalArgumentException("해당 조직의 운영진만 일정을 관리할 수 있습니다."); - } + // 참여자 생성 + EventParticipant participant = EventParticipant.builder() + .event(event) + .memberOrg(memberOrg) + .build(); - return memberOrg; + participant.assignEvent(event); } -} + } + + // 이벤트 존재 확인 메서드 + private Event findEventOrThrow(Long eventId) { + return eventRepository.findById(eventId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 일정입니다.")); + } +} \ No newline at end of file