Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import run.backend.domain.event.dto.request.EventInfoRequest;
import run.backend.domain.event.dto.response.EventDetailResponse;
import run.backend.domain.event.service.EventService;
import run.backend.domain.member.entity.Member;
import run.backend.global.annotation.member.Login;
Expand All @@ -25,7 +27,7 @@ public class EventController {
private final EventService eventService;

@PostMapping
@PreAuthorize("hasRole('MANAGER') or hasRole('LEADER')")
// @PreAuthorize("hasRole('MANAGER') or hasRole('LEADER')")
@Operation(summary = "일정 생성", description = "러닝 일정을 생성합니다. LEADER 또는 MANAGER 권한이 필요합니다.")
public CommonResponse<Void> createEvent(
@RequestBody EventInfoRequest eventInfoRequest,
Expand All @@ -37,15 +39,35 @@ public CommonResponse<Void> createEvent(
}

@PatchMapping("/{eventId}")
@PreAuthorize("hasRole('MANAGER') or hasRole('LEADER')")
// @PreAuthorize("hasRole('MANAGER') or hasRole('LEADER')")
@Operation(summary = "일정 수정", description = "러닝 일정을 수정합니다. LEADER 또는 MANAGER 권한이 필요합니다.")
public CommonResponse<Void> updateEvent(
@PathVariable Long eventId,
@RequestBody EventInfoRequest eventUpdateRequest,
@Login Member member
@RequestBody EventInfoRequest eventUpdateRequest
) {

eventService.updateEvent(eventId, eventUpdateRequest, member);
eventService.updateEvent(eventId, eventUpdateRequest);
return new CommonResponse<>("러닝 일정 수정 성공");
}

@GetMapping("/{eventId}")
@Operation(summary = "일정 상세 조회", description = "러닝 일정을 조회합니다.")
public CommonResponse<EventDetailResponse> getEventDetail(@PathVariable Long eventId) {
EventDetailResponse response = eventService.getEventDetail(eventId);
return new CommonResponse<>("러닝 일정 상세 조회 성공", response);
}

@PostMapping("/{eventId}/join-requests")
@Operation(summary = "러닝 참여 요청", description = "러닝 참여를 요청합니다")
public CommonResponse<Void> joinEvent(@PathVariable Long eventId, @Login Member member) {
eventService.joinEvent(eventId, member);
return new CommonResponse<>("러닝 참여 요청 완료");
}

@DeleteMapping("/{eventId}/join-requests")
@Operation(summary = "러닝 참여 요청 취소", description = "러닝 참여 요청을 취소합니다")
public CommonResponse<Void> cancelJoinEvent(@PathVariable Long eventId, @Login Member member) {
eventService.cancelJoinEvent(eventId, member);
return new CommonResponse<>("러닝 참여 요청 취소 완료");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package run.backend.domain.event.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import run.backend.domain.event.enums.EventStatus;

import java.time.LocalDateTime;
import java.util.List;

public record EventDetailResponse(
String title,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
LocalDateTime startDateTime,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
LocalDateTime endDateTime,
String startLocation,
EventStatus status,
String distanceKm,
String runningTime,
Long runningLeaderId,
List<ParticipantDto> participants
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package run.backend.domain.event.dto.response;

public record ParticipantDto(
Long id,
String name,
String image
) {
}
56 changes: 51 additions & 5 deletions src/main/java/run/backend/domain/event/entity/Event.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package run.backend.domain.event.entity;

import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
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.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.time.LocalTime;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import run.backend.domain.crew.entity.Crew;
import run.backend.domain.event.enums.EventStatus;
import run.backend.domain.member.entity.Member;
import run.backend.domain.record.entity.CrewRecord;
import run.backend.global.common.BaseEntity;

import java.time.LocalDate;
import java.time.LocalTime;


@Entity
@Getter
Expand Down Expand Up @@ -54,6 +63,8 @@ public class Event extends BaseEntity {
@JoinColumn(name = "running_captain")
private Member member;

private EventStatus status;

@Builder
public Event(
String title,
Expand All @@ -70,21 +81,38 @@ public Event(
this.startTime = startTime;
this.endTime = endTime;
this.place = place;
this.expectedParticipants = 0L;
this.expectedParticipants = 1L;
this.actualParticipants = 0L;
this.crew = crew;
this.record = record;
this.member = member;
this.status = EventStatus.BEFORE;
}

public void incrementExpectedParticipants() {
this.expectedParticipants++;
}

public void decrementExpectedParticipants() {
if (this.expectedParticipants > 0) {
this.expectedParticipants--;
}
}

public void incrementActualParticipants() {
this.actualParticipants++;
}

public void decrementActualParticipants() {
if (this.actualParticipants > 0) {
this.actualParticipants--;
}
}

public void complete() {
this.status = EventStatus.COMPLETED;
}

public void updateEvent(
String title,
LocalDate date,
Expand Down Expand Up @@ -112,4 +140,22 @@ public void updateEvent(
this.member = runningCaptain;
}
}

public String getDistanceKm() {
if (record != null && record.getDistance() != null) {
return record.getDistance().toString();
}
return "0";
}

public String getRunningTime() {
if (record != null && record.getDurationTime() != null) {
long totalSeconds = record.getDurationTime();
long hours = totalSeconds / 3600;
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
return "00:00:00";
}
}
10 changes: 8 additions & 2 deletions src/main/java/run/backend/domain/event/entity/JoinEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import run.backend.domain.member.entity.Member;
import run.backend.global.common.BaseEntity;

import java.time.LocalDateTime;

@Entity
@Getter
@Table(name = "join_events")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class JoinEvent {
public class JoinEvent extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -36,6 +39,9 @@ public JoinEvent(
this.isRunning = false;
this.member = member;
this.event = event;
this.event.incrementExpectedParticipants();
}

public void softDelete() {
this.setDeletedAt(LocalDateTime.now());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,8 @@ public void updatePeriodicEvent(
this.member = runningCaptain;
}
}

public void softDelete() {
this.setDeletedAt(java.time.LocalDateTime.now());
}
}
6 changes: 6 additions & 0 deletions src/main/java/run/backend/domain/event/enums/EventStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package run.backend.domain.event.enums;

public enum EventStatus {
BEFORE,
COMPLETED;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
public enum EventErrorCode implements ErrorCode {

RUNNING_CAPTAIN_NOT_CREW_MEMBER(6001, "러닝캡이 크루원이 아닙니다."),
EVENT_NOT_FOUND(6002, "일정을 찾을 수 없습니다.");
EVENT_NOT_FOUND(6002, "일정을 찾을 수 없습니다."),
ALREADY_JOINED_EVENT(6003, "이미 참여 요청이 되어있습니다."),
JOIN_EVENT_NOT_FOUND(6004, "참여 요청이 존재하지 않습니다.");

private final int errorCode;
private final String errorMessage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,17 @@ public EventNotFound() {
super(EventErrorCode.EVENT_NOT_FOUND);
}
}

public static class AlreadyJoinedEvent extends EventException {
public AlreadyJoinedEvent() {
super(EventErrorCode.ALREADY_JOINED_EVENT);
}
}

public static class JoinEventNotFound extends EventException {
public JoinEventNotFound() {
super(EventErrorCode.JOIN_EVENT_NOT_FOUND);
}
}

}
22 changes: 22 additions & 0 deletions src/main/java/run/backend/domain/event/mapper/EventMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
import run.backend.domain.crew.dto.response.EventProfileResponse;
import run.backend.domain.crew.entity.Crew;
import run.backend.domain.event.dto.request.EventInfoRequest;
import run.backend.domain.event.dto.response.EventDetailResponse;
import run.backend.domain.event.dto.response.ParticipantDto;
import run.backend.domain.event.entity.Event;
import run.backend.domain.event.entity.JoinEvent;
import run.backend.domain.event.entity.PeriodicEvent;
import run.backend.domain.event.enums.EventStatus;
import run.backend.domain.member.entity.Member;

@Mapper(
Expand All @@ -35,6 +38,25 @@ public interface EventMapper {

List<EventProfileResponse> toEventProfileList(List<Event> events);

@Mapping(target = "startDateTime", expression = "java(java.time.LocalDateTime.of(event.getDate(), event.getStartTime()))")
@Mapping(target = "endDateTime", expression = "java(java.time.LocalDateTime.of(event.getDate(), event.getEndTime()))")
@Mapping(target = "startLocation", source = "event.place")
@Mapping(target = "runningLeaderId", source = "event.member.id")
@Mapping(target = "distanceKm", source = "event.distanceKm")
@Mapping(target = "runningTime", source = "event.runningTime")
EventDetailResponse toEventDetailResponse(Event event, EventStatus status, List<ParticipantDto> participants);

@Mapping(target = "id", source = "member.id")
@Mapping(target = "name", source = "member.nickname")
@Mapping(target = "image", source = "member.profileImage")
ParticipantDto toParticipantDto(JoinEvent joinEvent);

default List<ParticipantDto> toParticipantDtoList(List<JoinEvent> joinEvents) {
return joinEvents.stream()
.map(this::toParticipantDto)
.toList();
}
Comment on lines +41 to +58
Copy link
Contributor

Choose a reason for hiding this comment

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

근데 이거 어떤거 Mapping으로 하고, 어떤건 default로 한 기준이 궁금합니다!
저는 모든 필드에 Mapping 다 할거면 그냥 default로 빌더 써서 했고, responseDto 만드는 거에 대해서만 map-struct 사용했는데 동현님은 어떤 기준으로 한건지 궁금합니다..


default EventInfoRequest toEventInfoRequest(EventInfoRequest updateRequest, Event event) {
return new EventInfoRequest(
updateRequest.title() != null ? updateRequest.title() : event.getTitle(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package run.backend.domain.event.repository;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import run.backend.domain.event.entity.Event;
import run.backend.domain.event.entity.JoinEvent;
import run.backend.domain.member.entity.Member;

import java.util.Optional;

public interface JoinEventRepository extends JpaRepository<JoinEvent, Long> {

void deleteByEventAndMember(Event event, Member member);

@Query("SELECT j FROM JoinEvent j WHERE j.event = :event AND j.member = :member AND j.deletedAt IS NULL")
Optional<JoinEvent> findByEventAndMember(@Param("event") Event event, @Param("member") Member member);

@Query("SELECT j FROM JoinEvent j WHERE j.event = :event AND j.deletedAt IS NULL")
List<JoinEvent> findByEventAndNotDeleted(@Param("event") Event event);

@Query("SELECT j FROM JoinEvent j WHERE j.event = :event AND j.isRunning = true AND j.deletedAt IS NULL")
List<JoinEvent> findActualParticipantsByEvent(@Param("event") Event event);

boolean existsByEventAndMemberAndDeletedAtIsNull(Event event, Member member);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public interface PeriodicEventRepository extends JpaRepository<PeriodicEvent, Lo
AND pe.title = :title
AND pe.startTime = :startTime
AND pe.endTime = :endTime
AND pe.deletedAt IS NULL
""")
Optional<PeriodicEvent> findByCrewAndTitleAndTime(
@Param("crew") Crew crew,
Expand Down
Loading