diff --git a/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java b/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java index 3d477b72..4c67a8cd 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java +++ b/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java @@ -28,6 +28,7 @@ public enum ErrorStatus { NOT_ACTIVE_GENERATION("활동 기수가 아닙니다."), NOT_TARGET_PART("지원 가능한 파트가 아닙니다."), NOT_FOUND_APPLY("신청상태가 아닌 모임입니다."), + ALREADY_PROCESSED_APPLY("이미 해당 상태로 처리된 신청 정보입니다."), /** * 401 UNAUTHORIZED diff --git a/main/src/main/java/org/sopt/makers/crew/main/entity/apply/Apply.java b/main/src/main/java/org/sopt/makers/crew/main/entity/apply/Apply.java index 92fe4795..08bc368b 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/entity/apply/Apply.java +++ b/main/src/main/java/org/sopt/makers/crew/main/entity/apply/Apply.java @@ -1,6 +1,7 @@ package org.sopt.makers.crew.main.entity.apply; import static jakarta.persistence.GenerationType.IDENTITY; +import static org.sopt.makers.crew.main.common.exception.ErrorStatus.*; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -17,6 +18,8 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; + +import org.sopt.makers.crew.main.common.exception.BadRequestException; import org.sopt.makers.crew.main.entity.apply.enums.ApplyStatusConverter; import org.sopt.makers.crew.main.entity.apply.enums.ApplyTypeConverter; import org.sopt.makers.crew.main.entity.apply.enums.EnApplyStatus; @@ -109,4 +112,10 @@ public Apply(EnApplyType type, Meeting meeting, Integer meetingId, User user, In public void updateApplyStatus(EnApplyStatus status) { this.status = status; } + + public void validateDuplicateUpdateApplyStatus(EnApplyStatus updatedApplyStatus){ + if(updatedApplyStatus.equals(this.getStatus())){ + throw new BadRequestException(ALREADY_PROCESSED_APPLY.getErrorCode()); + } + } } diff --git a/main/src/main/java/org/sopt/makers/crew/main/entity/meeting/Meeting.java b/main/src/main/java/org/sopt/makers/crew/main/entity/meeting/Meeting.java index 11dad9d3..c4aa4148 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/entity/meeting/Meeting.java +++ b/main/src/main/java/org/sopt/makers/crew/main/entity/meeting/Meeting.java @@ -27,15 +27,14 @@ import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Type; +import org.sopt.makers.crew.main.common.exception.BadRequestException; import org.sopt.makers.crew.main.common.exception.ForbiddenException; -import org.sopt.makers.crew.main.entity.apply.Apply; import org.sopt.makers.crew.main.entity.meeting.converter.MeetingCategoryConverter; import org.sopt.makers.crew.main.entity.meeting.enums.EnMeetingStatus; import org.sopt.makers.crew.main.entity.meeting.enums.MeetingCategory; import org.sopt.makers.crew.main.entity.meeting.enums.MeetingJoinablePart; import org.sopt.makers.crew.main.entity.meeting.vo.ImageUrlVO; import org.sopt.makers.crew.main.entity.user.User; -import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity @@ -80,7 +79,6 @@ public class Meeting { */ @Column(name = "imageURL", columnDefinition = "jsonb") @Type(JsonBinaryType.class) - //@JdbcTypeCode(SqlTypes.JSON) private List<ImageUrlVO> imageURL; /** @@ -231,6 +229,12 @@ public Boolean checkMeetingLeader(Integer userId) { return this.userId.equals(userId); } + public void validateCapacity(int approvedCount) { + if (approvedCount >= this.capacity) { + throw new BadRequestException(FULL_MEETING_CAPACITY.getErrorCode()); + } + } + public void updateMeeting(Meeting updateMeeting) { this.title = updateMeeting.getTitle(); diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java index 99d604db..328ad758 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java @@ -17,6 +17,7 @@ import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingGetAppliesQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingByOrgUserQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto; +import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingGetApplyListResponseDto; @@ -106,8 +107,11 @@ ResponseEntity<TempResponseDto<MeetingV2GetAllMeetingDto>> getMeetingsTemp(@Mode Principal principal); @Operation(summary = "모임 삭제", description = "모임 삭제합니다.") - ResponseEntity<Void> deleteMeeting(@PathVariable("meetingId") Integer meetingId, Principal principal); + ResponseEntity<Void> deleteMeeting(@PathVariable Integer meetingId, Principal principal); @Operation(summary = "모임 수정", description = "모임 내용을 수정합니다.") ResponseEntity<Void> updateMeeting(@PathVariable Integer meetingId, @RequestBody @Valid MeetingV2CreateMeetingBodyDto requestBody ,Principal principal); + + @Operation(summary = "모임 지원자 상태 변경", description = "모임 지원자의 지원 상태를 변경합니다.") + ResponseEntity<Void> updateApplyStatus(@PathVariable Integer meetingId, @RequestBody @Valid ApplyV2UpdateStatusBodyDto requestBody ,Principal principal); } diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java index 4a7660a9..35700230 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java @@ -13,6 +13,7 @@ import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingGetAppliesQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingByOrgUserQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto; +import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingGetApplyListResponseDto; @@ -119,7 +120,7 @@ public ResponseEntity<TempResponseDto<MeetingV2GetAllMeetingDto>> getMeetingsTem @Override @DeleteMapping("/{meetingId}") - public ResponseEntity<Void> deleteMeeting(@PathVariable("meetingId") Integer meetingId, Principal principal) { + public ResponseEntity<Void> deleteMeeting(@PathVariable Integer meetingId, Principal principal) { Integer userId = UserUtil.getUserId(principal); meetingV2Service.deleteMeeting(meetingId, userId); @@ -138,4 +139,17 @@ public ResponseEntity<Void> updateMeeting( return ResponseEntity.ok().build(); } + + @Override + @PutMapping("/{meetingId}/apply/status") + public ResponseEntity<Void> updateApplyStatus( + @PathVariable Integer meetingId, + @RequestBody @Valid ApplyV2UpdateStatusBodyDto requestBody, + Principal principal) { + + Integer userId = UserUtil.getUserId(principal); + meetingV2Service.updateApplyStatus(meetingId, requestBody, userId); + + return ResponseEntity.ok().build(); + } } diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/request/ApplyV2UpdateStatusBodyDto.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/request/ApplyV2UpdateStatusBodyDto.java new file mode 100644 index 00000000..3bf6e289 --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/request/ApplyV2UpdateStatusBodyDto.java @@ -0,0 +1,20 @@ +package org.sopt.makers.crew.main.meeting.v2.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Schema(description = "모임 지원자 상태 변경 request body dto") +public class ApplyV2UpdateStatusBodyDto { + @Schema(example = "1", description = "신청/지원 id") + @NotNull + private final Integer applyId; + + @Schema(example = "0", description = "0: 대기, 1: 승인, 2: 거절") + @NotNull + private final Integer status; + +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2Service.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2Service.java index c0883606..984d5cbb 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2Service.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2Service.java @@ -4,6 +4,7 @@ import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingGetAppliesQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingByOrgUserQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto; +import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingGetApplyListResponseDto; @@ -34,4 +35,6 @@ MeetingGetApplyListResponseDto findApplyList(MeetingGetAppliesQueryDto queryComm void deleteMeeting(Integer meetingId, Integer userId); void updateMeeting(Integer meetingId, MeetingV2CreateMeetingBodyDto requestBody, Integer userId); + + void updateApplyStatus(Integer meetingId, ApplyV2UpdateStatusBodyDto requestBody, Integer userId); } diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2ServiceImpl.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2ServiceImpl.java index ed15235e..61dc7d1a 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2ServiceImpl.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/service/MeetingV2ServiceImpl.java @@ -1,14 +1,7 @@ package org.sopt.makers.crew.main.meeting.v2.service; import static org.sopt.makers.crew.main.common.constant.CrewConst.ACTIVE_GENERATION; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.ALREADY_APPLIED_MEETING; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.FULL_MEETING_CAPACITY; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.MISSING_GENERATION_PART; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.NOT_ACTIVE_GENERATION; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.NOT_FOUND_APPLY; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.NOT_IN_APPLY_PERIOD; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.NOT_TARGET_PART; -import static org.sopt.makers.crew.main.common.exception.ErrorStatus.VALIDATION_EXCEPTION; +import static org.sopt.makers.crew.main.common.exception.ErrorStatus.*; import java.time.LocalDateTime; import java.util.ArrayList; @@ -49,6 +42,7 @@ import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingGetAppliesQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingByOrgUserQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto; +import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.ApplyInfoDto; @@ -279,6 +273,27 @@ public void updateMeeting(Integer meetingId, MeetingV2CreateMeetingBodyDto reque meeting.updateMeeting(updatedMeeting); } + @Override + @Transactional + public void updateApplyStatus(Integer meetingId, ApplyV2UpdateStatusBodyDto requestBody, Integer userId) { + Meeting meeting = meetingRepository.findByIdOrThrow(meetingId); + meeting.validateMeetingCreator(userId); + + Apply apply = applyRepository.findByIdOrThrow(requestBody.getApplyId()); + EnApplyStatus updatedApplyStatus = EnApplyStatus.ofValue(requestBody.getStatus()); + apply.validateDuplicateUpdateApplyStatus(updatedApplyStatus); + + if (updatedApplyStatus.equals(EnApplyStatus.APPROVE)) { + List<Apply> applies = applyRepository.findAllByMeetingIdAndStatus(meetingId, + EnApplyStatus.APPROVE); + + meeting.validateCapacity(applies.size()); + } + + apply.updateApplyStatus(updatedApplyStatus); + + } + private Boolean checkActivityStatus(Meeting meeting) { LocalDateTime now = LocalDateTime.now(); LocalDateTime mStartDate = meeting.getMStartDate(); @@ -312,9 +327,7 @@ private void validateMeetingCapacity(Meeting meeting, List<Apply> applies) { .filter(apply -> EnApplyStatus.APPROVE.equals(apply.getStatus())) .toList(); - if (approvedApplies.size() >= meeting.getCapacity()) { - throw new BadRequestException(FULL_MEETING_CAPACITY.getErrorCode()); - } + meeting.validateCapacity(approvedApplies.size()); } private void validateUserAlreadyApplied(Integer userId, List<Apply> applies) { diff --git a/server/src/meeting/v0/meeting-v0.controller.ts b/server/src/meeting/v0/meeting-v0.controller.ts index 3884f4d2..4704b7c9 100644 --- a/server/src/meeting/v0/meeting-v0.controller.ts +++ b/server/src/meeting/v0/meeting-v0.controller.ts @@ -47,6 +47,7 @@ export class MeetingV0Controller { @ApiOperation({ summary: '모임 지원자 상태 변경', description: '모임 지원자 상태 변경', + deprecated: true, }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'))