Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a7e48f3
docs: 운동 API에 부모임장 권한 범위 명시
Dimo-2562 Mar 2, 2026
ea8bd21
fix: 운동 수정 및 운동 삭제 API는 부모임장도 사용 가능하도록 변경
Dimo-2562 Mar 2, 2026
fa2052e
test: 운동 생성 단위 테스트 추가
Dimo-2562 Mar 2, 2026
5e4eca3
test: ExerciseCommandService createExercise 단위 테스트 추가
Dimo-2562 Mar 2, 2026
e6f929e
fix: status도 정상적으로 반환하기 위해 ResponseEntity로 응답을 감싸도록 변경
Dimo-2562 Mar 2, 2026
5d72c00
test: createExercise 통합 테스트 추가
Dimo-2562 Mar 2, 2026
0cd88af
test: ExerciseLifecycleService deleteExercise 단위 테스트 추가
Dimo-2562 Mar 2, 2026
a5bfa6c
test: ExerciseCommandService deleteExercise 단위 테스트 추가
Dimo-2562 Mar 2, 2026
c0a987e
test: 운동 삭제 통합 테스트 추가
Dimo-2562 Mar 2, 2026
25dae49
test: ExerciseLifecycleService 운동 수정 단위 테스트 추가
Dimo-2562 Mar 2, 2026
47efaf4
test: ExerciseCommandService updateExercise 단위 테스트 추가
Dimo-2562 Mar 2, 2026
e982b28
test: 운동 수정 통합 테스트 추가
Dimo-2562 Mar 2, 2026
06c4a30
test: 특정 참여자 운동 취소 단위 테스트 추가
Dimo-2562 Mar 2, 2026
f7e0c44
test: 특정 참여자 운동 취소 Command Service 단위 테스트 추가
Dimo-2562 Mar 2, 2026
5875b06
test: 특정 참여자 운동 취소 통합 테스트 추가
Dimo-2562 Mar 2, 2026
92d4d6c
fix: Builder 어노테이션의 기본값 설정을 위해 Builder.Default 어노테이션 추가
Dimo-2562 Mar 2, 2026
13d5b62
test: MemberFixture에 nickname도 설정하도록 변경
Dimo-2562 Mar 2, 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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import umc.cockple.demo.domain.exercise.dto.*;
Expand Down Expand Up @@ -38,11 +39,11 @@ public class ExerciseController {

@PostMapping("/parties/{partyId}/exercises")
@Operation(summary = "운동 생성",
description = "모임 내에서 새로운 운동을 생성합니다. 모임장만 생성 가능합니다.")
description = "모임 내에서 새로운 운동을 생성합니다. 모임장과 부모임장만 생성 가능합니다.")
@ApiResponse(responseCode = "201", description = "운동 생성 성공")
@ApiResponse(responseCode = "400", description = "입력값 오류")
@ApiResponse(responseCode = "403", description = "권한 없음")
public BaseResponse<ExerciseCreateDTO.Response> createExercise(
public ResponseEntity<BaseResponse<ExerciseCreateDTO.Response>> createExercise(
@PathVariable Long partyId,
@Valid @RequestBody ExerciseCreateDTO.Request request
) {
Expand All @@ -51,34 +52,34 @@ public BaseResponse<ExerciseCreateDTO.Response> createExercise(
ExerciseCreateDTO.Response response = exerciseCommandService.createExercise(
partyId, memberId, request);

return BaseResponse.success(CommonSuccessCode.CREATED, response);
return BaseResponse.of(CommonSuccessCode.CREATED, response);
}

@DeleteMapping("/exercises/{exerciseId}")
@Operation(summary = "운동 삭제",
description = "모임장이 운동을 삭제합니다. 삭제된 운동의 모든 참여자와 게스트도 함께 삭제됩니다.")
description = "모임장 또는 부모임장이 운동을 삭제합니다. 삭제된 운동의 모든 참여자와 게스트도 함께 삭제됩니다.")
@ApiResponse(responseCode = "200", description = "운동 삭제 성공")
@ApiResponse(responseCode = "403", description = "권한 없음 (모임장이 아님)")
@ApiResponse(responseCode = "404", description = "운동을 찾을 수 없음")
public BaseResponse<ExerciseDeleteDTO.Response> deleteExercise(
public ResponseEntity<BaseResponse<ExerciseDeleteDTO.Response>> deleteExercise(
@PathVariable Long exerciseId
) {
Long memberId = SecurityUtil.getCurrentMemberId();

ExerciseDeleteDTO.Response response = exerciseCommandService.deleteExercise(
exerciseId, memberId);

return BaseResponse.success(CommonSuccessCode.OK, response);
return BaseResponse.of(CommonSuccessCode.OK, response);
}

@PatchMapping("/exercises/{exerciseId}")
@Operation(summary = "운동 수정",
description = "모임장이 생성한 운동의 정보를 수정합니다. 이미 시작된 운동은 수정할 수 없습니다.")
description = "모임장 또는 부모임장이 생성한 운동의 정보를 수정합니다. 이미 시작된 운동은 수정할 수 없습니다.")
@ApiResponse(responseCode = "200", description = "운동 수정 성공")
@ApiResponse(responseCode = "400", description = "입력값 오류 또는 비즈니스 룰 위반")
@ApiResponse(responseCode = "403", description = "권한 없음 (모임장이 아님)")
@ApiResponse(responseCode = "404", description = "존재하지 않는 운동")
public BaseResponse<ExerciseUpdateDTO.Response> updateExercise(
public ResponseEntity<BaseResponse<ExerciseUpdateDTO.Response>> updateExercise(
@PathVariable Long exerciseId,
@Valid @RequestBody ExerciseUpdateDTO.Request request
) {
Expand All @@ -87,7 +88,7 @@ public BaseResponse<ExerciseUpdateDTO.Response> updateExercise(
ExerciseUpdateDTO.Response response = exerciseCommandService.updateExercise(
exerciseId, memberId, request);

return BaseResponse.success(CommonSuccessCode.OK, response);
return BaseResponse.of(CommonSuccessCode.OK, response);
}

@PostMapping("/exercises/{exerciseId}/participants")
Expand Down Expand Up @@ -131,7 +132,7 @@ public BaseResponse<ExerciseCancelDTO.Response> cancelParticipation(
@ApiResponse(responseCode = "400", description = "취소할 수 없는 상태 (이미 시작됨, 참여하지 않음 등)")
@ApiResponse(responseCode = "403", description = "권한 없음 (매니저가 아님)")
@ApiResponse(responseCode = "404", description = "운동 또는 참여 기록을 찾을 수 없음")
public BaseResponse<ExerciseCancelDTO.Response> cancelParticipationByManager(
public ResponseEntity<BaseResponse<ExerciseCancelDTO.Response>> cancelParticipationByManager(
@PathVariable Long exerciseId,
@PathVariable Long participantId,
@Valid @RequestBody ExerciseCancelDTO.ByManagerRequest request
Expand All @@ -141,7 +142,7 @@ public BaseResponse<ExerciseCancelDTO.Response> cancelParticipationByManager(
ExerciseCancelDTO.Response response = exerciseCommandService.cancelParticipationByManager(
exerciseId, participantId, memberId, request);

return BaseResponse.success(CommonSuccessCode.OK, response);
return BaseResponse.of(CommonSuccessCode.OK, response);
}

@PostMapping("/exercises/{exerciseId}/guests")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ public void validateCancelGuestParticipationByManager(Guest guest, Exercise exer
}

public void validateDeleteExercise(Exercise exercise, Long memberId) {
validateManagerPermission(memberId, exercise.getParty());
validateSubManagerPermission(memberId, exercise.getParty());
}

public void validateUpdateExercise(Exercise exercise, Member member, ExerciseUpdateDTO.Request request) {
validateManagerPermission(member.getId(), exercise.getParty());
validateSubManagerPermission(member.getId(), exercise.getParty());
validateAlreadyStarted(exercise, ExerciseErrorCode.EXERCISE_ALREADY_STARTED_UPDATE);
validateUpdateTime(request, exercise);
}
Expand All @@ -87,15 +87,6 @@ private void validatePartyIsActive(Party party) {
}
}

private void validateManagerPermission(Long memberId, Party party) {
boolean isOwner = party.getOwnerId().equals(memberId);
boolean isManager = memberPartyRepository.existsByPartyIdAndMemberIdAndRole(
party.getId(), memberId, Role.party_MANAGER);

if (!isOwner && !isManager)
throw new ExerciseException(ExerciseErrorCode.INSUFFICIENT_PERMISSION);
}

private void validateSubManagerPermission(Long memberId, Party party) {
boolean isOwner = party.getOwnerId().equals(memberId);
boolean isManager = memberPartyRepository.existsByPartyIdAndMemberIdAndRole(
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/umc/cockple/demo/domain/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,36 +58,46 @@ public class Member extends BaseEntity {


@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<Contest> contests = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<Notification> notifications = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<MemberKeyword> keywords = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<MemberAddr> addresses = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
Copy link
Contributor

Choose a reason for hiding this comment

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

감사합니다

private List<MemberParty> memberParties = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<MemberExercise> memberExercises = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<ExerciseBookmark> exerciseBookmarks = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
@Builder.Default
private List<PartyBookmark> partyBookmarks = new ArrayList<>();

@OneToOne(mappedBy = "member", cascade = CascadeType.ALL)
private ProfileImg profileImg;

@OneToMany(mappedBy = "sender")
@Builder.Default
private List<ChatMessage> chatMessages = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ChatRoomMember> chatRoomMembers = new ArrayList<>();


Expand Down
20 changes: 20 additions & 0 deletions src/main/java/umc/cockple/demo/global/response/BaseResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.ResponseEntity;
import umc.cockple.demo.global.response.code.BaseCode;
import umc.cockple.demo.global.response.code.BaseErrorCode;
import umc.cockple.demo.global.response.code.status.CommonSuccessCode;
Expand Down Expand Up @@ -84,4 +85,23 @@ public static <T> BaseResponse<T> error(BaseErrorCode errorCode, String customMe
.build();
}

public static <T> ResponseEntity<BaseResponse<T>> of(BaseCode code) {
return of(code, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

아 이렇게 한 번 감싼거구나 !! 좋아요

}

public static <T> ResponseEntity<BaseResponse<T>> of(BaseCode code, T data) {
ReasonDTO reason = code.getReason();

BaseResponse<T> body = BaseResponse.<T>builder()
.isSuccess(reason.getHttpStatus().is2xxSuccessful())
.code(reason.getCode())
.message(reason.getMessage())
.data(data)
.build();

return ResponseEntity
.status(reason.getHttpStatus())
.body(body);
}

}
Loading