Skip to content

Commit

Permalink
Merge pull request #58 from ecolink-JOIN/meeting-delete
Browse files Browse the repository at this point in the history
[Feat] 회차 삭제 API 개발
  • Loading branch information
Moveuk authored Jan 1, 2025
2 parents e0f9947 + 04feab2 commit ed1cd6e
Show file tree
Hide file tree
Showing 21 changed files with 196 additions and 53 deletions.
3 changes: 3 additions & 0 deletions src/main/java/com/join/core/attendance/domain/Attendance.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -20,6 +22,7 @@ public class Attendance extends BaseTimeEntity {

@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "meeting_id", nullable = false)
private Meeting meeting;

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/join/core/common/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public enum ErrorCode {
DUPLICATE_APPLICATION(HttpStatus.BAD_REQUEST, "S-002", "이미 지원한 스터디입니다."),
APPLICATION_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "S-003", "주어진 식별자로 지원 정보를 찾을 수 없습니다."),

/**
* 회차 관련 오류
*/
MEETING_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "M-001", "주어진 식별자로 회차를 찾을 수 없습니다."),

/**
* 공지 관련 오류
*/
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/join/core/common/response/ApiResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public static <T> ApiResponse<T> created(T data) {
return new ApiResponse<>(data, 201, "Created");
}

public static ApiResponse<Void> noContent() {
return new ApiResponse<>(null, 204, "No Content");
}

}
3 changes: 3 additions & 0 deletions src/main/java/com/join/core/fine/domain/Fine.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import java.math.BigDecimal;
import java.time.LocalDate;
Expand Down Expand Up @@ -51,6 +53,7 @@ public class Fine extends BaseTimeEntity {

@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "meeting_id", nullable = false)
private Meeting meeting;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import com.join.core.auth.domain.UserPrincipal;
import com.join.core.common.response.ApiResponse;
import com.join.core.meeting.controller.specification.MeetingControllerSpecification;
import com.join.core.meeting.domain.MeetingAppendService;
import com.join.core.meeting.domain.MeetingDeleteService;
import com.join.core.meeting.domain.MeetingReadService;
import com.join.core.meeting.dto.request.MeetingAppendRequest;
import com.join.core.meeting.dto.response.MeetingResponse;
import com.join.core.meeting.service.MeetingAppendService;
import com.join.core.meeting.service.MeetingReadService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -20,30 +19,35 @@
@RequiredArgsConstructor
@RestController
@RequestMapping("${api.prefix}/study/{studyToken}/meetings")
public class MeetingController {
public class MeetingController implements MeetingControllerSpecification {

private final MeetingAppendService meetingAppendService;
private final MeetingReadService meetingReadService;
private final MeetingDeleteService meetingDeleteService;

@Tag(name = "${swagger.tag.meeting}")
@Operation(summary = "회차 추가 - 인증 필수",
description = "회차 추가 - 인증 필수",
security = {@SecurityRequirement(name = "session-token")})
@PreAuthorize("isAuthenticated()")
@PostMapping
public ApiResponse<Void> append(@AuthenticationPrincipal UserPrincipal principal,
@Valid @RequestBody MeetingAppendRequest request) {
@Valid @RequestBody MeetingAppendRequest request,
@PathVariable String studyToken
) {
Long avatarId = principal.getAvatarId();
meetingAppendService.appendMeetingToStudy(avatarId, request);
meetingAppendService.appendMeetingToStudy(avatarId, studyToken, request);
return ApiResponse.ok();
}

@Tag(name = "${swagger.tag.meeting}")
@Operation(summary = "회차 리스트 조회",
description = "회차 리스트 조회",
security = {@SecurityRequirement(name = "session-token")})
@GetMapping
public ApiResponse<List<MeetingResponse>> getMeetingDetails(@PathVariable String studyToken) {
return ApiResponse.ok(meetingReadService.getMeetings(studyToken));
}

@PreAuthorize("isAuthenticated()")
@DeleteMapping("/{meetingId}")
public ApiResponse<Void> delete(@AuthenticationPrincipal UserPrincipal principal,
@PathVariable String studyToken,
@PathVariable Long meetingId) {
Long avatarId = principal.getAvatarId();
meetingDeleteService.delete(avatarId, studyToken, meetingId);
return ApiResponse.noContent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.join.core.meeting.controller.specification;

import com.join.core.auth.domain.UserPrincipal;
import com.join.core.common.response.ApiResponse;
import com.join.core.meeting.dto.request.MeetingAppendRequest;
import com.join.core.meeting.dto.response.MeetingResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

public interface MeetingControllerSpecification {

@Tag(name = "${swagger.tag.meeting}")
@Operation(summary = "회차 추가 - 인증 필수",
description = "회차 추가 - 인증 필수",
security = {@SecurityRequirement(name = "session-token")})
ApiResponse<Void> append(@AuthenticationPrincipal UserPrincipal principal,
@Valid @RequestBody MeetingAppendRequest request,
@PathVariable String studyToken
);

@Tag(name = "${swagger.tag.meeting}")
@Operation(summary = "회차 리스트 조회",
description = "회차 리스트 조회",
security = {@SecurityRequirement(name = "session-token")})
ApiResponse<List<MeetingResponse>> getMeetingDetails(@PathVariable String studyToken);

@Tag(name = "${swagger.tag.meeting}")
@Operation(summary = "회차 삭제 - 인증 필수",
description = "회차 삭제 - 인증 필수",
security = {@SecurityRequirement(name = "session-token")})
ApiResponse<Void> delete(@AuthenticationPrincipal UserPrincipal principal,
@PathVariable String studyToken,
@PathVariable Long meetingId);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.join.core.meeting.service;
package com.join.core.meeting.domain;

import com.join.core.common.exception.ErrorCode;
import com.join.core.common.exception.impl.NoPermissionException;
import com.join.core.meeting.domain.Meeting;
import com.join.core.meeting.dto.request.MeetingAppendRequest;
import com.join.core.study.domain.Study;
import com.join.core.study.service.StudyReader;
Expand All @@ -24,9 +23,9 @@ public class MeetingAppendService {
private final MeetingReader meetingReader;

@Transactional
public void appendMeetingToStudy(Long avatarId, MeetingAppendRequest request) {
public void appendMeetingToStudy(Long avatarId, String studyToken, MeetingAppendRequest request) {

Study study = studyReader.getStudyByToken(request.getStudyToken());
Study study = studyReader.getStudyByToken(studyToken);

if (!study.getWriter().getId().equals(avatarId)) {
throw new NoPermissionException(ErrorCode.UNAUTHORIZED_ACCESS);
Expand All @@ -35,7 +34,7 @@ public void appendMeetingToStudy(Long avatarId, MeetingAppendRequest request) {

meetingStore.store(newMeeting);

List<Meeting> meetings = meetingReader.findByStudy(study);
List<Meeting> meetings = meetingReader.getMeetingsByStudy(study);
int meetingCount = meetings.size();

meetings.sort(Comparator.comparing(meeting ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.join.core.meeting.domain;

import com.join.core.common.exception.ErrorCode;
import com.join.core.common.exception.impl.NoPermissionException;
import com.join.core.study.domain.Study;
import com.join.core.study.service.StudyReader;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;

@Service
@RequiredArgsConstructor
public class MeetingDeleteService {

private final MeetingReader meetingReader;
private final StudyReader studyReader;
private final MeetingDeleter meetingDeleter;

@Transactional
public void delete(Long avatarId, String studyToken, Long meetingId) {

Study study = studyReader.getStudyByToken(studyToken);

if (!study.isWriter(avatarId)) {
throw new NoPermissionException(ErrorCode.UNAUTHORIZED_ACCESS);
}

Meeting meetingToDelete = meetingReader.getMeetingById(meetingId);

meetingDeleter.delete(meetingToDelete);

List<Meeting> remainingMeetings = meetingReader.getMeetingsByStudy(study)
.stream()
.filter(meeting -> !meeting.getId().equals(meetingId))
.sorted(Comparator.comparing(meeting ->
LocalDateTime.of(meeting.getStudyDate(), meeting.getStTime())))
.toList();

IntStream.range(0, remainingMeetings.size()).forEach(index -> {
Meeting meeting = remainingMeetings.get(index);
meeting.updateMeetingNo(index + 1);
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.join.core.meeting.domain;

public interface MeetingDeleter {
void delete(Meeting meeting);

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.join.core.meeting.service;
package com.join.core.meeting.domain;

import com.join.core.meeting.dto.response.MeetingResponse;
import com.join.core.study.domain.Study;
Expand All @@ -20,7 +20,7 @@ public class MeetingReadService {
public List<MeetingResponse> getMeetings(String studyToken) {
Study study = studyReader.getStudyByToken(studyToken);

return meetingReader.findByStudy(study)
return meetingReader.getMeetingsByStudy(study)
.stream()
.map(MeetingResponse::from)
.toList();
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/join/core/meeting/domain/MeetingReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.join.core.meeting.domain;

import com.join.core.study.domain.Study;

import java.util.List;

public interface MeetingReader {
List<Meeting> getMeetingsByStudy(Study study);
Meeting getMeetingById(Long meetingId);

}
6 changes: 6 additions & 0 deletions src/main/java/com/join/core/meeting/domain/MeetingStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.join.core.meeting.domain;

public interface MeetingStore {
void store(Meeting meeting);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.join.core.meeting.domain.Meeting;
import com.join.core.study.domain.Study;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -14,11 +13,6 @@
@Getter
@NoArgsConstructor
public class MeetingAppendRequest {

@Schema(description = "회차를 추가하고자 하는 스터디 토큰", example = "std_abc123")
@NotBlank
private String studyToken;

@Schema(description = "스터디 회차 날짜", example = "2024-09-01")
@NotNull
private LocalDate studyDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
@Getter
@AllArgsConstructor
public class MeetingResponse {
@Schema(description = "회차 id", example = "1")
private Long id;

@Schema(description = "회차 순서", example = "1")
private int meetingNo;

Expand All @@ -28,6 +31,6 @@ public class MeetingResponse {
private MeetingStatus status;

public static MeetingResponse from(Meeting meeting) {
return new MeetingResponse(meeting.getMeetingNo(), meeting.getStudyDate(), meeting.getStTime(), meeting.getEndTime(), meeting.getStatus());
return new MeetingResponse(meeting.getId(), meeting.getMeetingNo(), meeting.getStudyDate(), meeting.getStTime(), meeting.getEndTime(), meeting.getStatus());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.join.core.meeting.repository;

import com.join.core.meeting.domain.Meeting;
import com.join.core.meeting.domain.MeetingDeleter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class MeetingDeleterImpl implements MeetingDeleter {

private final MeetingRepository meetingRepository;

@Override
public void delete(Meeting meeting) {
meetingRepository.delete(meeting);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.join.core.meeting.repository;

import com.join.core.common.exception.ErrorCode;
import com.join.core.common.exception.impl.EntityNotFoundException;
import com.join.core.meeting.domain.Meeting;
import com.join.core.meeting.service.MeetingReader;
import com.join.core.meeting.domain.MeetingReader;
import com.join.core.study.domain.Study;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
Expand All @@ -15,7 +17,13 @@ public class MeetingReaderImpl implements MeetingReader {
private final MeetingRepository meetingRepository;

@Override
public List<Meeting> findByStudy(Study study) {
public List<Meeting> getMeetingsByStudy(Study study) {
return meetingRepository.findByStudy(study);
}

@Override
public Meeting getMeetingById(Long meetingId) {
return meetingRepository.findById(meetingId)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.MEETING_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.join.core.meeting.repository;

import com.join.core.meeting.domain.Meeting;
import com.join.core.meeting.service.MeetingStore;
import com.join.core.meeting.domain.MeetingStore;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand Down
11 changes: 0 additions & 11 deletions src/main/java/com/join/core/meeting/service/MeetingReader.java

This file was deleted.

8 changes: 0 additions & 8 deletions src/main/java/com/join/core/meeting/service/MeetingStore.java

This file was deleted.

Loading

0 comments on commit ed1cd6e

Please sign in to comment.