diff --git a/src/main/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApi.java b/src/main/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApi.java new file mode 100644 index 00000000..068c05c9 --- /dev/null +++ b/src/main/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApi.java @@ -0,0 +1,40 @@ +package com.stumeet.server.activity.adapter.in; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.stumeet.server.activity.application.port.in.ActivityUpdateUseCase; +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; +import com.stumeet.server.common.annotation.WebAdapter; +import com.stumeet.server.common.auth.model.LoginMember; +import com.stumeet.server.common.model.ApiResponse; +import com.stumeet.server.common.response.SuccessCode; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@WebAdapter +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class ActivityUpdateApi { + + private final ActivityUpdateUseCase activityUpdateUseCase; + + @PatchMapping("/studies/{studyId}/activities/{activityId}") + public ResponseEntity> update( + @AuthenticationPrincipal LoginMember loginMember, + @PathVariable Long studyId, + @PathVariable Long activityId, + @RequestBody @Valid ActivityUpdateCommand command + ) { + activityUpdateUseCase.update(loginMember.getId(), studyId, activityId, command); + + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessCode.UPDATE_SUCCESS)); + } +} diff --git a/src/main/java/com/stumeet/server/activity/adapter/out/mapper/ActivityPersistenceMapper.java b/src/main/java/com/stumeet/server/activity/adapter/out/mapper/ActivityPersistenceMapper.java index 72df7146..db920e34 100644 --- a/src/main/java/com/stumeet/server/activity/adapter/out/mapper/ActivityPersistenceMapper.java +++ b/src/main/java/com/stumeet/server/activity/adapter/out/mapper/ActivityPersistenceMapper.java @@ -5,7 +5,7 @@ import com.stumeet.server.activity.adapter.out.model.ActivityJpaEntity; import com.stumeet.server.activity.adapter.out.model.ActivityLinkedStudyJpaEntity; import com.stumeet.server.activity.adapter.out.model.ActivityMemberJpaEntity; -import com.stumeet.server.activity.application.service.model.ActivityCreateSource; +import com.stumeet.server.activity.application.service.model.ActivitySource; import com.stumeet.server.activity.domain.model.Activity; import com.stumeet.server.activity.domain.model.Meet; @@ -17,9 +17,9 @@ public class ActivityPersistenceMapper { public Activity toDomain(ActivityJpaEntity entity) { - ActivityCreateSource request = ActivityCreateSource.builder() + ActivitySource request = ActivitySource.builder() .id(entity.getId()) - .author(ActivityCreateSource.ActivityMemberCreateSource.builder() + .author(ActivitySource.ActivityMemberCreateSource.builder() .id(entity.getAuthor().getId()) .name(entity.getAuthor().getName()) .image(entity.getAuthor().getImage()) diff --git a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityImagePersistenceAdapter.java b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityImagePersistenceAdapter.java index 69858228..70e2817d 100644 --- a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityImagePersistenceAdapter.java +++ b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityImagePersistenceAdapter.java @@ -40,4 +40,10 @@ public void deleteAllByActivityId(Long studyId, Long activityId) { jpaActivityImageRepository.deleteAllByActivityId(activityId); EventPublisher.raise(new ActivityImageDeleteEvent(this, studyId, activityId)); } + + @Override + public void update(Long activityId, List activityImages) { + jpaActivityImageRepository.deleteAllByActivityId(activityId); + create(activityImages); + } } diff --git a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityParticipantPersistenceAdapter.java b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityParticipantPersistenceAdapter.java index 007324ba..8dff5617 100644 --- a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityParticipantPersistenceAdapter.java +++ b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityParticipantPersistenceAdapter.java @@ -37,4 +37,10 @@ public List findAllByActivityId(Long activityId) { public void deleteByActivityId(Long activityId) { jpaActivityParticipantRepository.deleteAllByActivityId(activityId); } + + @Override + public void update(Long activityId, List participants) { + deleteByActivityId(activityId); + create(participants); + } } diff --git a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityPersistenceAdapter.java b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityPersistenceAdapter.java index decc28f8..bbe1cea1 100644 --- a/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityPersistenceAdapter.java +++ b/src/main/java/com/stumeet/server/activity/adapter/out/persistence/ActivityPersistenceAdapter.java @@ -10,7 +10,7 @@ import com.stumeet.server.activity.adapter.out.mapper.ActivityPersistenceMapper; import com.stumeet.server.activity.adapter.out.model.ActivityJpaEntity; import com.stumeet.server.activity.application.port.out.ActivityAuthorValidationPort; -import com.stumeet.server.activity.application.port.out.ActivityCreatePort; +import com.stumeet.server.activity.application.port.out.ActivitySavePort; import com.stumeet.server.activity.application.port.out.ActivityDeletePort; import com.stumeet.server.activity.application.port.out.ActivityQueryPort; import com.stumeet.server.activity.domain.exception.NotExistsActivityException; @@ -22,54 +22,54 @@ @PersistenceAdapter @RequiredArgsConstructor -public class ActivityPersistenceAdapter implements ActivityCreatePort, ActivityQueryPort, ActivityAuthorValidationPort, ActivityDeletePort { - - private final JpaActivityRepository jpaActivityRepository; - private final ActivityPersistenceMapper activityPersistenceMapper; - - @Override - public Activity create(Activity activity) { - ActivityJpaEntity entity = activityPersistenceMapper.toEntity(activity); - - return activityPersistenceMapper.toDomain(jpaActivityRepository.save(entity)); - } - - @Override - public Activity getById(Long activityId) { - ActivityJpaEntity entity = jpaActivityRepository.findById(activityId) - .orElseThrow(() -> new NotExistsActivityException(activityId)); - - return activityPersistenceMapper.toDomain(entity); - } - - @Override - public Page getDetailPagesByCondition(Pageable pageable, Boolean isNotice, Long studyId, ActivityCategory category) { - Page entities = - jpaActivityRepository.findDetailPagesByCondition(pageable, isNotice, studyId, category); - - return activityPersistenceMapper.toDomainPages(entities); - } - - @Override - public Page getPaginatedBriefsByCondition(Pageable pageable, Boolean isNotice, Long memberId, - Long studyId, ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) { - return jpaActivityRepository.findBriefsByConditionWithPagination(pageable, isNotice, memberId, studyId, category, startDate, endDate); - } - - @Override - public List getBriefsByCondition(Boolean isNotice, Long memberId, Long studyId, - ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) { - return jpaActivityRepository.findBriefsByCondition(isNotice, memberId, studyId, category, startDate, endDate); - } - - @Override - public boolean isNotActivityAuthor(Long memberId, Long activityId) { - return jpaActivityRepository.findByIdAndAuthorId(activityId, memberId) - .isEmpty(); - } - - @Override - public void deleteById(Long activityId) { - jpaActivityRepository.deleteById(activityId); - } +public class ActivityPersistenceAdapter implements ActivitySavePort, ActivityQueryPort, ActivityAuthorValidationPort, ActivityDeletePort { + + private final JpaActivityRepository jpaActivityRepository; + private final ActivityPersistenceMapper activityPersistenceMapper; + + @Override + public Activity save(Activity activity) { + ActivityJpaEntity entity = activityPersistenceMapper.toEntity(activity); + + return activityPersistenceMapper.toDomain(jpaActivityRepository.save(entity)); + } + + @Override + public Activity getById(Long activityId) { + ActivityJpaEntity entity = jpaActivityRepository.findById(activityId) + .orElseThrow(() -> new NotExistsActivityException(activityId)); + + return activityPersistenceMapper.toDomain(entity); + } + + @Override + public Page getDetailPagesByCondition(Pageable pageable, Boolean isNotice, Long studyId, ActivityCategory category) { + Page entities = + jpaActivityRepository.findDetailPagesByCondition(pageable, isNotice, studyId, category); + + return activityPersistenceMapper.toDomainPages(entities); + } + + @Override + public Page getPaginatedBriefsByCondition(Pageable pageable, Boolean isNotice, Long memberId, + Long studyId, ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) { + return jpaActivityRepository.findBriefsByConditionWithPagination(pageable, isNotice, memberId, studyId, category, startDate, endDate); + } + + @Override + public List getBriefsByCondition(Boolean isNotice, Long memberId, Long studyId, + ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) { + return jpaActivityRepository.findBriefsByCondition(isNotice, memberId, studyId, category, startDate, endDate); + } + + @Override + public boolean isNotActivityAuthor(Long memberId, Long activityId) { + return jpaActivityRepository.findByIdAndAuthorId(activityId, memberId) + .isEmpty(); + } + + @Override + public void deleteById(Long activityId) { + jpaActivityRepository.deleteById(activityId); + } } diff --git a/src/main/java/com/stumeet/server/activity/application/port/in/ActivityUpdateUseCase.java b/src/main/java/com/stumeet/server/activity/application/port/in/ActivityUpdateUseCase.java new file mode 100644 index 00000000..31ccc15f --- /dev/null +++ b/src/main/java/com/stumeet/server/activity/application/port/in/ActivityUpdateUseCase.java @@ -0,0 +1,8 @@ +package com.stumeet.server.activity.application.port.in; + +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; + +public interface ActivityUpdateUseCase { + + void update(Long memberId, Long studyId, Long activityId, ActivityUpdateCommand command); +} diff --git a/src/main/java/com/stumeet/server/activity/application/port/in/command/ActivityUpdateCommand.java b/src/main/java/com/stumeet/server/activity/application/port/in/command/ActivityUpdateCommand.java new file mode 100644 index 00000000..c231f298 --- /dev/null +++ b/src/main/java/com/stumeet/server/activity/application/port/in/command/ActivityUpdateCommand.java @@ -0,0 +1,52 @@ +package com.stumeet.server.activity.application.port.in.command; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.format.annotation.DateTimeFormat; + +import com.stumeet.server.common.annotation.validator.NullOrNotBlank; + +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Builder; + +@Builder +public record ActivityUpdateCommand( + @NotBlank(message = "활동 카테고리를 입력해주세요") + String category, + + @NotBlank(message = "활동 제목을 입력해주세요") + @Size(max = 100, message = "활동 제목은 100자 이하여야 합니다") + String title, + + @NotBlank(message = "활동 내용을 입력해주세요") + @Size(max = 500, message = "활동 내용은 500자 이하여야 합니다") + String content, + + @NotNull(message = "이미지 리스트를 전달해주세요") + @Size(max = 5, message = "이미지는 5개 이하로 등록할 수 있습니다") + List images, + + boolean isNotice, + + @Nullable + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + LocalDateTime startDate, + + @Nullable + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + LocalDateTime endDate, + + @NullOrNotBlank + String location, + + @NullOrNotBlank + String link, + + @NotNull(message = "참여 멤버 리스트를 전달해주세요") + List participants +) { +} diff --git a/src/main/java/com/stumeet/server/activity/application/port/in/mapper/ActivityUseCaseMapper.java b/src/main/java/com/stumeet/server/activity/application/port/in/mapper/ActivityUseCaseMapper.java index 549520c5..b7d8f6c9 100644 --- a/src/main/java/com/stumeet/server/activity/application/port/in/mapper/ActivityUseCaseMapper.java +++ b/src/main/java/com/stumeet/server/activity/application/port/in/mapper/ActivityUseCaseMapper.java @@ -7,7 +7,8 @@ import com.stumeet.server.activity.adapter.in.response.ActivityParticipantSimpleResponse; import com.stumeet.server.activity.adapter.in.response.PageInfoResponse; import com.stumeet.server.activity.application.port.in.command.ActivityCreateCommand; -import com.stumeet.server.activity.application.service.model.ActivityCreateSource; +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; +import com.stumeet.server.activity.application.service.model.ActivitySource; import com.stumeet.server.activity.domain.model.Activity; import com.stumeet.server.activity.domain.model.ActivityCategory; @@ -23,77 +24,96 @@ @RequiredArgsConstructor public class ActivityUseCaseMapper { - private final ActivityParticipantUseCaseMapper activityParticipantUseCaseMapper; + private final ActivityParticipantUseCaseMapper activityParticipantUseCaseMapper; - public ActivityCreateSource toSource(Long studyId, ActivityCreateCommand command, Long id) { - return ActivityCreateSource.builder() - .id(null) - .studyId(studyId) - .author(ActivityCreateSource.ActivityMemberCreateSource.builder() - .id(id) - .build()) - .category(ActivityCategory.getByName(command.category())) - .title(command.title()) - .content(command.content()) - .location(command.location()) - .link(command.link()) - .startDate(command.startDate()) - .endDate(command.endDate()) - .isNotice(command.isNotice()) - .createdAt(LocalDateTime.now()) - .build(); - } + public ActivitySource toCreateSource(Long studyId, ActivityCreateCommand command, Long id) { + return ActivitySource.builder() + .id(null) + .studyId(studyId) + .author(ActivitySource.ActivityMemberCreateSource.builder() + .id(id) + .build()) + .category(ActivityCategory.getByName(command.category())) + .title(command.title()) + .content(command.content()) + .location(command.location()) + .link(command.link()) + .startDate(command.startDate()) + .endDate(command.endDate()) + .isNotice(command.isNotice()) + .createdAt(LocalDateTime.now()) + .build(); + } - public ActivityDetailResponse toDetailResponse( - Activity activity, - List activityImages, - ActivityParticipantSimpleResponse author, - List participants, - String status, - boolean isAuthor, - boolean isAdmin - ) { - return ActivityDetailResponse.builder() - .id(activity.getId()) - .category(activity.getCategory().name()) - .title(activity.getTitle()) - .content(activity.getContent()) - .imageUrl(activityImages) - .author(author) - .participants(participants) - .status(status) - .startDate(activity.getStartDate()) - .endDate(activity.getEndDate()) - .location(activity.getLocation()) - .createdAt(activity.getCreatedAt()) - .isAuthor(isAuthor) - .isAdmin(isAdmin) - .build(); - } + public ActivitySource toUpdateSource(Activity exist, ActivityUpdateCommand command) { + return ActivitySource.builder() + .id(exist.getId()) + .studyId(exist.getStudy().getId()) + .author(ActivitySource.ActivityMemberCreateSource.builder() + .id(exist.getAuthor().getId()) + .build()) + .category(ActivityCategory.getByName(command.category())) + .title(command.title()) + .content(command.content()) + .location(command.location()) + .link(command.link()) + .startDate(command.startDate()) + .endDate(command.endDate()) + .isNotice(command.isNotice()) + .createdAt(exist.getCreatedAt()) + .build(); + } - private ActivityListDetailedPageResponse toListDetailedPageResponse(Activity activity) { - return ActivityListDetailedPageResponse.builder() - .id(activity.getId()) - .category(activity.getCategory().name()) - .title(activity.getTitle()) - .content(activity.getContent()) - .startDate(activity.getStartDate()) - .endDate(activity.getEndDate()) - .location(activity.getLocation()) - .author(activityParticipantUseCaseMapper.toResponse(activity.getAuthor())) - .createdAt(activity.getCreatedAt()) - .build(); - } + public ActivityDetailResponse toDetailResponse( + Activity activity, + List activityImages, + ActivityParticipantSimpleResponse author, + List participants, + String status, + boolean isAuthor, + boolean isAdmin + ) { + return ActivityDetailResponse.builder() + .id(activity.getId()) + .category(activity.getCategory().name()) + .title(activity.getTitle()) + .content(activity.getContent()) + .imageUrl(activityImages) + .author(author) + .participants(participants) + .status(status) + .startDate(activity.getStartDate()) + .endDate(activity.getEndDate()) + .location(activity.getLocation()) + .createdAt(activity.getCreatedAt()) + .isAuthor(isAuthor) + .isAdmin(isAdmin) + .build(); + } - public ActivityListDetailedPageResponses toListDetailedPageResponses( - Page activities, - PageInfoResponse pageInfoResponse - ) { - return ActivityListDetailedPageResponses.builder() - .items(activities.stream() - .map(this::toListDetailedPageResponse) - .toList()) - .pageInfo(pageInfoResponse) - .build(); - } + private ActivityListDetailedPageResponse toListDetailedPageResponse(Activity activity) { + return ActivityListDetailedPageResponse.builder() + .id(activity.getId()) + .category(activity.getCategory().name()) + .title(activity.getTitle()) + .content(activity.getContent()) + .startDate(activity.getStartDate()) + .endDate(activity.getEndDate()) + .location(activity.getLocation()) + .author(activityParticipantUseCaseMapper.toResponse(activity.getAuthor())) + .createdAt(activity.getCreatedAt()) + .build(); + } + + public ActivityListDetailedPageResponses toListDetailedPageResponses( + Page activities, + PageInfoResponse pageInfoResponse + ) { + return ActivityListDetailedPageResponses.builder() + .items(activities.stream() + .map(this::toListDetailedPageResponse) + .toList()) + .pageInfo(pageInfoResponse) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityImageCommandPort.java b/src/main/java/com/stumeet/server/activity/application/port/out/ActivityImageCommandPort.java index 25220621..cbc0d9d2 100644 --- a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityImageCommandPort.java +++ b/src/main/java/com/stumeet/server/activity/application/port/out/ActivityImageCommandPort.java @@ -1,6 +1,12 @@ package com.stumeet.server.activity.application.port.out; +import java.util.List; + +import com.stumeet.server.activity.domain.model.ActivityImage; + public interface ActivityImageCommandPort { void deleteAllByActivityId(Long studyId, Long activityId); + + void update(Long activityId, List activityImages); } diff --git a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityParticipantCommandPort.java b/src/main/java/com/stumeet/server/activity/application/port/out/ActivityParticipantCommandPort.java index 1aa3d03f..a2a95403 100644 --- a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityParticipantCommandPort.java +++ b/src/main/java/com/stumeet/server/activity/application/port/out/ActivityParticipantCommandPort.java @@ -1,6 +1,12 @@ package com.stumeet.server.activity.application.port.out; +import java.util.List; + +import com.stumeet.server.activity.domain.model.ActivityParticipant; + public interface ActivityParticipantCommandPort { void deleteByActivityId(Long activityId); + + void update(Long activityId, List participants); } diff --git a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityCreatePort.java b/src/main/java/com/stumeet/server/activity/application/port/out/ActivitySavePort.java similarity index 60% rename from src/main/java/com/stumeet/server/activity/application/port/out/ActivityCreatePort.java rename to src/main/java/com/stumeet/server/activity/application/port/out/ActivitySavePort.java index 7f97f4e1..2d10bffb 100644 --- a/src/main/java/com/stumeet/server/activity/application/port/out/ActivityCreatePort.java +++ b/src/main/java/com/stumeet/server/activity/application/port/out/ActivitySavePort.java @@ -2,6 +2,6 @@ import com.stumeet.server.activity.domain.model.Activity; -public interface ActivityCreatePort { - Activity create(Activity activity); +public interface ActivitySavePort { + Activity save(Activity activity); } diff --git a/src/main/java/com/stumeet/server/activity/application/service/ActivityCreateService.java b/src/main/java/com/stumeet/server/activity/application/service/ActivityCreateService.java index 504496e1..5ffc1259 100644 --- a/src/main/java/com/stumeet/server/activity/application/service/ActivityCreateService.java +++ b/src/main/java/com/stumeet/server/activity/application/service/ActivityCreateService.java @@ -1,12 +1,12 @@ package com.stumeet.server.activity.application.service; import com.stumeet.server.activity.application.port.in.ActivityCreateUseCase; -import com.stumeet.server.activity.application.service.model.ActivityCreateSource; +import com.stumeet.server.activity.application.service.model.ActivitySource; import com.stumeet.server.activity.application.port.in.command.ActivityCreateCommand; import com.stumeet.server.activity.application.port.in.mapper.ActivityImageUseCaseMapper; import com.stumeet.server.activity.application.port.in.mapper.ActivityParticipantUseCaseMapper; import com.stumeet.server.activity.application.port.in.mapper.ActivityUseCaseMapper; -import com.stumeet.server.activity.application.port.out.ActivityCreatePort; +import com.stumeet.server.activity.application.port.out.ActivitySavePort; import com.stumeet.server.activity.application.port.out.ActivityImageCreatePort; import com.stumeet.server.activity.application.port.out.ActivityParticipantCreatePort; import com.stumeet.server.activity.domain.model.Activity; @@ -25,7 +25,7 @@ @RequiredArgsConstructor public class ActivityCreateService implements ActivityCreateUseCase { - private final ActivityCreatePort activityCreatePort; + private final ActivitySavePort activitySavePort; private final ActivityImageCreatePort activityImageCreatePort; private final ActivityParticipantCreatePort activityParticipantPort; @@ -34,21 +34,21 @@ public class ActivityCreateService implements ActivityCreateUseCase { private final ActivityUseCaseMapper activityUseCaseMapper; private final ActivityImageUseCaseMapper activityImageUseCaseMapper; - private final ActivityParticipantUseCaseMapper activityMemberUseCaseMapper; + private final ActivityParticipantUseCaseMapper activityParticipantUseCaseMapper; @Override public void create(Long studyId, ActivityCreateCommand command, Long memberId) { studyValidationUseCase.checkById(studyId); studyMemberValidationUseCase.checkStudyJoinMember(studyId, memberId); - ActivityCreateSource activitySource = activityUseCaseMapper.toSource(studyId, command, memberId); + ActivitySource activitySource = activityUseCaseMapper.toCreateSource(studyId, command, memberId); Activity activity = activitySource.category().create(activitySource); - Activity createdActivity = activityCreatePort.create(activity); + Activity createdActivity = activitySavePort.save(activity); + + List participants = activityParticipantUseCaseMapper.toDomains(command.participants(), createdActivity); + activityParticipantPort.create(participants); List images = activityImageUseCaseMapper.toDomains(command.images(), createdActivity); activityImageCreatePort.create(images); - - List participants = activityMemberUseCaseMapper.toDomains(command.participants(), createdActivity); - activityParticipantPort.create(participants); } } diff --git a/src/main/java/com/stumeet/server/activity/application/service/ActivityUpdateService.java b/src/main/java/com/stumeet/server/activity/application/service/ActivityUpdateService.java new file mode 100644 index 00000000..a77d358e --- /dev/null +++ b/src/main/java/com/stumeet/server/activity/application/service/ActivityUpdateService.java @@ -0,0 +1,59 @@ +package com.stumeet.server.activity.application.service; + +import java.util.List; + +import org.springframework.transaction.annotation.Transactional; + +import com.stumeet.server.activity.application.port.in.ActivityAuthorityValidationUseCase; +import com.stumeet.server.activity.application.port.in.ActivityUpdateUseCase; +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; +import com.stumeet.server.activity.application.port.in.mapper.ActivityImageUseCaseMapper; +import com.stumeet.server.activity.application.port.in.mapper.ActivityParticipantUseCaseMapper; +import com.stumeet.server.activity.application.port.in.mapper.ActivityUseCaseMapper; +import com.stumeet.server.activity.application.port.out.ActivityImageCommandPort; +import com.stumeet.server.activity.application.port.out.ActivityParticipantCommandPort; +import com.stumeet.server.activity.application.port.out.ActivitySavePort; +import com.stumeet.server.activity.application.port.out.ActivityQueryPort; +import com.stumeet.server.activity.application.service.model.ActivitySource; +import com.stumeet.server.activity.domain.model.Activity; +import com.stumeet.server.activity.domain.model.ActivityImage; +import com.stumeet.server.activity.domain.model.ActivityParticipant; +import com.stumeet.server.common.annotation.UseCase; +import com.stumeet.server.studymember.application.port.in.StudyMemberValidationUseCase; + +import lombok.RequiredArgsConstructor; + +@Transactional +@UseCase +@RequiredArgsConstructor +public class ActivityUpdateService implements ActivityUpdateUseCase { + + private final StudyMemberValidationUseCase studyMemberValidationUseCase; + private final ActivityAuthorityValidationUseCase activityAuthorityValidationUseCase; + + private final ActivityQueryPort activityQueryPort; + private final ActivitySavePort activitySavePort; + private final ActivityImageCommandPort activityImageCommandPort; + private final ActivityParticipantCommandPort activityParticipantCommandPort; + + private final ActivityUseCaseMapper activityUseCaseMapper; + private final ActivityImageUseCaseMapper activityImageUseCaseMapper; + private final ActivityParticipantUseCaseMapper activityParticipantUseCaseMapper; + + @Override + public void update(Long memberId, Long studyId, Long activityId, ActivityUpdateCommand command) { + studyMemberValidationUseCase.checkStudyJoinMember(studyId, memberId); + activityAuthorityValidationUseCase.checkDeleteAuthority(studyId, memberId, activityId); + + Activity exist = activityQueryPort.getById(activityId); + ActivitySource source = activityUseCaseMapper.toUpdateSource(exist, command); + Activity updated = exist.update(memberId, source); + activitySavePort.save(updated); + + List participants = activityParticipantUseCaseMapper.toDomains(command.participants(), updated); + activityParticipantCommandPort.update(activityId, participants); + + List images = activityImageUseCaseMapper.toDomains(command.images(), updated); + activityImageCommandPort.update(activityId, images); + } +} diff --git a/src/main/java/com/stumeet/server/activity/application/service/model/ActivityCreateSource.java b/src/main/java/com/stumeet/server/activity/application/service/model/ActivitySource.java similarity index 94% rename from src/main/java/com/stumeet/server/activity/application/service/model/ActivityCreateSource.java rename to src/main/java/com/stumeet/server/activity/application/service/model/ActivitySource.java index ce3d23f1..3a50f983 100644 --- a/src/main/java/com/stumeet/server/activity/application/service/model/ActivityCreateSource.java +++ b/src/main/java/com/stumeet/server/activity/application/service/model/ActivitySource.java @@ -6,7 +6,7 @@ import java.time.LocalDateTime; @Builder -public record ActivityCreateSource( +public record ActivitySource( Long id, Long studyId, ActivityMemberCreateSource author, diff --git a/src/main/java/com/stumeet/server/activity/domain/model/Activity.java b/src/main/java/com/stumeet/server/activity/domain/model/Activity.java index 2b6b052d..f293bdb9 100644 --- a/src/main/java/com/stumeet/server/activity/domain/model/Activity.java +++ b/src/main/java/com/stumeet/server/activity/domain/model/Activity.java @@ -1,14 +1,15 @@ package com.stumeet.server.activity.domain.model; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import java.time.LocalDateTime; +import java.util.Objects; -import org.springframework.cglib.core.Local; +import com.stumeet.server.activity.application.service.model.ActivitySource; +import com.stumeet.server.activity.domain.exception.ActivityManagementAccessDeniedException; +import com.stumeet.server.common.exception.model.BadRequestException; +import com.stumeet.server.common.response.ErrorCode; -@AllArgsConstructor(access = AccessLevel.PROTECTED) @Getter public abstract class Activity { @@ -36,7 +37,53 @@ public abstract class Activity { private LocalDateTime createdAt; + protected Activity(Long id, ActivityLinkedStudy study, ActivityMember author, ActivityCategory category, + String title, + String content, String link, String location, LocalDateTime startDate, LocalDateTime endDate, + boolean isNotice, + LocalDateTime createdAt) { + validatePeriod(category, startDate, endDate); + + this.id = id; + this.study = study; + this.author = author; + this.category = category; + this.title = title; + this.content = content; + this.link = link; + this.location = location; + this.startDate = startDate; + this.endDate = endDate; + this.isNotice = isNotice; + this.createdAt = createdAt; + } + + private void validatePeriod(ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) { + if (ActivityCategory.DEFAULT.equals(category)) { + return; + } + + if (startDate == null || endDate == null) { + throw new BadRequestException(ErrorCode.ACTIVITY_PERIOD_REQUIRED_EXCEPTION); + } + + if (startDate.isAfter(endDate)) { + throw new BadRequestException(ErrorCode.INVALID_PERIOD_EXCEPTION); + } + } + + public Activity update(Long memberId, ActivitySource source) { + validateAuthor(memberId); + return source.category().create(source); + } + + private void validateAuthor(Long memberId) { + if (!isAuthor(memberId)) { + throw new ActivityManagementAccessDeniedException(); + } + } + public boolean isAuthor(Long memberId) { - return author.getId() == memberId; + return Objects.equals(this.author.getId(), memberId); } } diff --git a/src/main/java/com/stumeet/server/activity/domain/model/ActivityCategory.java b/src/main/java/com/stumeet/server/activity/domain/model/ActivityCategory.java index 01000095..f087a78d 100644 --- a/src/main/java/com/stumeet/server/activity/domain/model/ActivityCategory.java +++ b/src/main/java/com/stumeet/server/activity/domain/model/ActivityCategory.java @@ -1,6 +1,6 @@ package com.stumeet.server.activity.domain.model; -import com.stumeet.server.activity.application.service.model.ActivityCreateSource; +import com.stumeet.server.activity.application.service.model.ActivitySource; import com.stumeet.server.activity.domain.exception.NotExistsActivityCategoryException; import lombok.Getter; @@ -13,7 +13,7 @@ public enum ActivityCategory { DEFAULT(DefaultStatus.NONE) { @Override - public Activity create(ActivityCreateSource source) { + public Activity create(ActivitySource source) { return Default.builder() .id(source.id()) .study(ActivityLinkedStudy.builder().id(source.studyId()).build()) @@ -35,7 +35,7 @@ public Activity create(ActivityCreateSource source) { MEET(MeetStatus.MEET_NOT_STARTED) { @Override - public Activity create(ActivityCreateSource source) { + public Activity create(ActivitySource source) { return Meet.builder() .id(source.id()) .study(ActivityLinkedStudy.builder().id(source.studyId()).build()) @@ -59,7 +59,7 @@ public Activity create(ActivityCreateSource source) { }, ASSIGNMENT(AssignmentStatus.ASSIGNMENT_NOT_STARTED) { @Override - public Activity create(ActivityCreateSource source) { + public Activity create(ActivitySource source) { return Assignment.builder() .id(source.id()) .study(ActivityLinkedStudy.builder().id(source.studyId()).build()) @@ -72,7 +72,7 @@ public Activity create(ActivityCreateSource source) { .category(source.category()) .title(source.title()) .content(source.content()) - .link(source.content()) + .link(source.link()) .startDate(source.startDate()) .endDate(source.endDate()) .isNotice(source.isNotice()) @@ -90,6 +90,5 @@ public static ActivityCategory getByName(String category) { .orElseThrow(() -> new NotExistsActivityCategoryException(category)); } - public abstract Activity create(ActivityCreateSource source); - + public abstract Activity create(ActivitySource source); } diff --git a/src/main/java/com/stumeet/server/activity/domain/model/ActivityPeriod.java b/src/main/java/com/stumeet/server/activity/domain/model/ActivityPeriod.java deleted file mode 100644 index af5d2d03..00000000 --- a/src/main/java/com/stumeet/server/activity/domain/model/ActivityPeriod.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.stumeet.server.activity.domain.model; - -import java.time.LocalDateTime; - -import com.stumeet.server.common.exception.model.BadRequestException; -import com.stumeet.server.common.response.ErrorCode; - -import lombok.Builder; -import lombok.Getter; - -@Getter -public class ActivityPeriod { - - private LocalDateTime startDate; - - private LocalDateTime endDate; - - @Builder - private ActivityPeriod(LocalDateTime startDate, LocalDateTime endDate) { - validate(startDate, endDate); - - this.startDate = startDate; - this.endDate = endDate; - } - - private void validate(LocalDateTime startDate, LocalDateTime endDate) { - validateNonNull(startDate, endDate); - validatePeriod(startDate, endDate); - } - - private void validateNonNull(LocalDateTime startDate, LocalDateTime endDate) { - if (startDate == null || endDate == null) { - throw new BadRequestException(ErrorCode.ACTIVITY_PERIOD_REQUIRED_EXCEPTION); - } - } - - private void validatePeriod(LocalDateTime startDate, LocalDateTime endDate) { - if (startDate.isAfter(endDate)) { - throw new BadRequestException(ErrorCode.INVALID_PERIOD_EXCEPTION); - } - } -} diff --git a/src/main/java/com/stumeet/server/activity/domain/model/Assignment.java b/src/main/java/com/stumeet/server/activity/domain/model/Assignment.java index 586d82ee..2592c6b4 100644 --- a/src/main/java/com/stumeet/server/activity/domain/model/Assignment.java +++ b/src/main/java/com/stumeet/server/activity/domain/model/Assignment.java @@ -6,15 +6,8 @@ public class Assignment extends Activity { - private ActivityPeriod period; - @Builder protected Assignment(Long id, ActivityLinkedStudy study, ActivityMember author, ActivityCategory category, String title, String content, boolean isNotice, LocalDateTime startDate, LocalDateTime endDate, String link, LocalDateTime createdAt) { super(id, study, author, category, title, content, link, null, startDate, endDate, isNotice, createdAt); - - this.period = ActivityPeriod.builder() - .startDate(startDate) - .endDate(endDate) - .build(); } } diff --git a/src/main/java/com/stumeet/server/activity/domain/model/Meet.java b/src/main/java/com/stumeet/server/activity/domain/model/Meet.java index e64ec857..c905ac30 100644 --- a/src/main/java/com/stumeet/server/activity/domain/model/Meet.java +++ b/src/main/java/com/stumeet/server/activity/domain/model/Meet.java @@ -11,18 +11,11 @@ @Getter public class Meet extends Activity { - private ActivityPeriod period; - @Builder protected Meet(Long id, ActivityLinkedStudy study, ActivityMember author, ActivityCategory category, String title, String content, String location, String link, LocalDateTime startDate, LocalDateTime endDate, boolean isNotice, LocalDateTime createdAt) { super(id, study, author, category, title, content, link, location, startDate, endDate, isNotice, createdAt); validateLocationNonNull(location); - - this.period = ActivityPeriod.builder() - .startDate(startDate) - .endDate(endDate) - .build(); } private void validateLocationNonNull(String location) { diff --git a/src/main/java/com/stumeet/server/common/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/stumeet/server/common/exception/handler/GlobalExceptionHandler.java index 4f3d4e23..e54f15e3 100644 --- a/src/main/java/com/stumeet/server/common/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/stumeet/server/common/exception/handler/GlobalExceptionHandler.java @@ -38,6 +38,7 @@ public class GlobalExceptionHandler { protected ResponseEntity handleSecurityViolationException(final SecurityViolationException e) { log.warn(SECURITY_LOG_MESSAGE, e.getClass().getSimpleName(), e.getMessage()); log.debug(e.getMessage(), e); + e.printStackTrace(); ErrorCode errorCode = e.getErrorCode(); ApiResponse response = ApiResponse.fail(errorCode); @@ -50,6 +51,7 @@ protected ResponseEntity handleSecurityViolationException(final Sec protected ResponseEntity handleBusinessException(final BusinessException e) { log.warn(WARN_LOG_MESSAGE, e.getClass().getSimpleName(), e.getMessage()); log.debug(e.getMessage(), e); + e.printStackTrace(); ErrorCode errorCode = e.getErrorCode(); ApiResponse response = ApiResponse.fail(errorCode); @@ -129,6 +131,7 @@ protected ResponseEntity handleMaxUploadSizeExceededException(final @ExceptionHandler(BadRequestException.class) protected ResponseEntity handleCustomBadRequestException(final BadRequestException e) { log.warn(ERROR_LOG_MESSAGE, e.getClass().getSimpleName(), e.getMessage()); + e.printStackTrace(); String message = String.format("%s %s", e.getErrorCode().getMessage(), e.getMessage()); ApiResponse response = ApiResponse.fail(e.getErrorCode().getHttpStatusCode(), message); diff --git a/src/test/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApiTest.java b/src/test/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApiTest.java new file mode 100644 index 00000000..ba987aa6 --- /dev/null +++ b/src/test/java/com/stumeet/server/activity/adapter/in/ActivityUpdateApiTest.java @@ -0,0 +1,211 @@ +package com.stumeet.server.activity.adapter.in; + +import static org.springframework.restdocs.headers.HeaderDocumentation.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; + +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; +import com.stumeet.server.common.auth.model.AuthenticationHeader; +import com.stumeet.server.helper.WithMockMember; +import com.stumeet.server.stub.ActivityStub; +import com.stumeet.server.stub.StudyStub; +import com.stumeet.server.stub.TokenStub; +import com.stumeet.server.template.ApiTest; + +class ActivityUpdateApiTest extends ApiTest { + + @Nested + @DisplayName("활동 수정 API") + class UpdateActivity { + + private final String path = "/api/v1/studies/{studyId}/activities/{activityId}"; + + @Test + @WithMockMember(id = 4L) + @DisplayName("[성공] 활동 수정에 성공한다.") + void success() throws Exception { + ActivityUpdateCommand command = ActivityStub.getActivityUpdateCommand(); + + mockMvc.perform(patch(path, StudyStub.getStudyId(), ActivityStub.getActivityId()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(command))) + .andExpect(status().isOk()) + .andDo(document("update-activity/success", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()) + .description("서버로부터 전달받은 액세스 토큰") + ), + requestFields( + fieldWithPath("category").description("활동 카테고리"), + fieldWithPath("title").description("활동 제목"), + fieldWithPath("content").description("활동 내용"), + fieldWithPath("images[]").description("활동 이미지 URL 리스트"), + fieldWithPath("isNotice").description("공지 여부"), + fieldWithPath("startDate").description("활동 시작 일시").optional(), + fieldWithPath("endDate").description("활동 종료 일시").optional(), + fieldWithPath("location").description("활동 장소").optional(), + fieldWithPath("link").description("링크").optional(), + fieldWithPath("participants").description("참여자 ID 리스트") + ), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지") + ))); + } + + @Test + @WithMockMember + @DisplayName("[실패] 작성자가 아닌 경우 수정에 실패한다.") + void fail_when_member_is_not_author() throws Exception { + ActivityUpdateCommand command = ActivityStub.getActivityUpdateCommand(); + + mockMvc.perform(patch(path, StudyStub.getStudyId(), ActivityStub.getActivityId()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(command))) + .andExpect(status().isForbidden()) + .andDo(document("update-activity/fail/not-author", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()) + .description("서버로부터 전달받은 액세스 토큰") + ), + requestFields( + fieldWithPath("category").description("활동 카테고리"), + fieldWithPath("title").description("활동 제목"), + fieldWithPath("content").description("활동 내용"), + fieldWithPath("images[]").description("활동 이미지 URL 리스트"), + fieldWithPath("isNotice").description("공지 여부"), + fieldWithPath("startDate").description("활동 시작 일시").optional(), + fieldWithPath("endDate").description("활동 종료 일시").optional(), + fieldWithPath("location").description("활동 장소").optional(), + fieldWithPath("link").description("링크").optional(), + fieldWithPath("participants").description("참여자 ID 리스트") + ), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지") + ))); + } + + @Test + @WithMockMember(id = 3L) + @DisplayName("[실패] 스터디 멤버가 아닌 경우 활동 수정에 실패한다.") + void fail_when_member_is_not_joined_member() throws Exception { + ActivityUpdateCommand command = ActivityStub.getActivityUpdateCommand(); + + mockMvc.perform(patch(path, StudyStub.getStudyId(), ActivityStub.getActivityId()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(command))) + .andExpect(status().isForbidden()) + .andDo(document("update-activity/fail/not-joined-member", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()) + .description("서버로부터 전달받은 액세스 토큰") + ), + requestFields( + fieldWithPath("category").description("활동 카테고리"), + fieldWithPath("title").description("활동 제목"), + fieldWithPath("content").description("활동 내용"), + fieldWithPath("images[]").description("활동 이미지 URL 리스트"), + fieldWithPath("isNotice").description("공지 여부"), + fieldWithPath("startDate").description("활동 시작 일시").optional(), + fieldWithPath("endDate").description("활동 종료 일시").optional(), + fieldWithPath("location").description("활동 장소").optional(), + fieldWithPath("link").description("링크").optional(), + fieldWithPath("participants").description("참여자 ID 리스트") + ), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지") + ))); + } + + @Test + @WithMockMember(id = 4L) + @DisplayName("[실패] 모임/과제 유형으로 수정하는 경우 활동 기간이 null이면 활동 수정에 실패한다.") + void fail_when_period_null() throws Exception { + ActivityUpdateCommand command = ActivityStub.getNullPeriodActivityUpdateCommand(); + + mockMvc.perform(patch(path, StudyStub.getStudyId(), ActivityStub.getActivityId()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(command))) + .andExpect(status().isBadRequest()) + .andDo(document("update-activity/fail/period-null", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()) + .description("서버로부터 전달받은 액세스 토큰") + ), + requestFields( + fieldWithPath("category").description("활동 카테고리"), + fieldWithPath("title").description("활동 제목"), + fieldWithPath("content").description("활동 내용"), + fieldWithPath("images[]").description("활동 이미지 URL 리스트"), + fieldWithPath("isNotice").description("공지 여부"), + fieldWithPath("startDate").description("활동 시작 일시").optional(), + fieldWithPath("endDate").description("활동 종료 일시").optional(), + fieldWithPath("location").description("활동 장소").optional(), + fieldWithPath("link").description("링크").optional(), + fieldWithPath("participants").description("참여자 ID 리스트") + ), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지") + ))); + } + + @Test + @WithMockMember(id = 4L) + @DisplayName("[실패] 모임/과제 유형으로 수정하는 경우 활동 기간이 유효하지 않으면 활동 수정에 실패한다.") + void fail_when_period_invalid() throws Exception { + ActivityUpdateCommand command = ActivityStub.getInvalidPeriodActivityUpdateCommand(); + + mockMvc.perform(patch(path, StudyStub.getStudyId(), ActivityStub.getActivityId()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(command))) + .andExpect(status().isBadRequest()) + .andDo(document("update-activity/fail/period-invalid", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()) + .description("서버로부터 전달받은 액세스 토큰") + ), + requestFields( + fieldWithPath("category").description("활동 카테고리"), + fieldWithPath("title").description("활동 제목"), + fieldWithPath("content").description("활동 내용"), + fieldWithPath("images[]").description("활동 이미지 URL 리스트"), + fieldWithPath("isNotice").description("공지 여부"), + fieldWithPath("startDate").description("활동 시작 일시").optional(), + fieldWithPath("endDate").description("활동 종료 일시").optional(), + fieldWithPath("location").description("활동 장소").optional(), + fieldWithPath("link").description("링크").optional(), + fieldWithPath("participants").description("참여자 ID 리스트") + ), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지") + ))); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/stumeet/server/activity/application/service/ActivityCreateServiceTest.java b/src/test/java/com/stumeet/server/activity/application/service/ActivityCreateServiceTest.java index 56ebce83..53247160 100644 --- a/src/test/java/com/stumeet/server/activity/application/service/ActivityCreateServiceTest.java +++ b/src/test/java/com/stumeet/server/activity/application/service/ActivityCreateServiceTest.java @@ -4,7 +4,7 @@ import com.stumeet.server.activity.application.port.in.mapper.ActivityImageUseCaseMapper; import com.stumeet.server.activity.application.port.in.mapper.ActivityParticipantUseCaseMapper; import com.stumeet.server.activity.application.port.in.mapper.ActivityUseCaseMapper; -import com.stumeet.server.activity.application.port.out.ActivityCreatePort; +import com.stumeet.server.activity.application.port.out.ActivitySavePort; import com.stumeet.server.activity.application.port.out.ActivityImageCreatePort; import com.stumeet.server.activity.application.port.out.ActivityParticipantCreatePort; import com.stumeet.server.activity.domain.model.Activity; @@ -14,7 +14,6 @@ import com.stumeet.server.study.application.port.in.StudyValidationUseCase; import com.stumeet.server.study.domain.exception.StudyNotExistsException; import com.stumeet.server.studymember.application.port.in.StudyMemberValidationUseCase; -import com.stumeet.server.studymember.domain.exception.NotStudyAdminException; import com.stumeet.server.studymember.domain.exception.StudyMemberNotJoinedException; import com.stumeet.server.template.UnitTest; import org.junit.jupiter.api.DisplayName; @@ -35,7 +34,7 @@ class ActivityCreateServiceTest extends UnitTest { private ActivityCreateService activityCreateService; @Mock - private ActivityCreatePort activityCreatePort; + private ActivitySavePort activitySavePort; @Mock private ActivityImageCreatePort activityImageCreatePort; @@ -71,9 +70,9 @@ void successTest() { ActivityCreateCommand request = ActivityStub.getDefaultActivityCreateCommand(); Activity activity = ActivityStub.getDefaultActivity(); - given(activityUseCaseMapper.toSource(any(), any(), any())) + given(activityUseCaseMapper.toCreateSource(any(), any(), any())) .willReturn(ActivityStub.getDefaultCreateSource()); - given(activityCreatePort.create(any())) + given(activitySavePort.save(any())) .willReturn(ActivityStub.getDefaultActivity()); given(activityImageUseCaseMapper.toDomains(any(), any())) .willReturn(ActivityStub.getActivityImages(activity)); @@ -82,7 +81,7 @@ void successTest() { activityCreateService.create(studyId, request, memberId); - then(activityCreatePort).should().create(any()); + then(activitySavePort).should().save(any()); then(activityImageCreatePort).should().create(any()); then(activityParticipantPort).should().create(any()); } diff --git a/src/test/java/com/stumeet/server/stub/ActivityStub.java b/src/test/java/com/stumeet/server/stub/ActivityStub.java index 6d78c4d3..52924ab6 100644 --- a/src/test/java/com/stumeet/server/stub/ActivityStub.java +++ b/src/test/java/com/stumeet/server/stub/ActivityStub.java @@ -1,7 +1,8 @@ package com.stumeet.server.stub; import com.stumeet.server.activity.adapter.in.response.*; -import com.stumeet.server.activity.application.service.model.ActivityCreateSource; +import com.stumeet.server.activity.application.port.in.command.ActivityUpdateCommand; +import com.stumeet.server.activity.application.service.model.ActivitySource; import com.stumeet.server.activity.application.port.in.command.ActivityCreateCommand; import com.stumeet.server.activity.domain.model.*; import com.stumeet.server.member.domain.Member; @@ -87,12 +88,54 @@ public static ActivityCreateCommand getMeetActivityCreateCommandPeriodInvalid() .build(); } - public static ActivityCreateSource getDefaultCreateSource() { + public static ActivityUpdateCommand getActivityUpdateCommand() { + return ActivityUpdateCommand.builder() + .category("MEET") + .title("title") + .content("content") + .images(List.of("https://example.com/image1.png", "https://example.com/image2.png", "https://example.com/image3.png")) + .location("서울") + .startDate(LocalDateTime.parse("2024-05-01T00:00:00")) + .endDate(LocalDateTime.parse("2024-05-02T00:00:00")) + .isNotice(true) + .participants(List.of(MemberStub.getMemberId())) + .build(); + } + + public static ActivityUpdateCommand getNullPeriodActivityUpdateCommand() { + return ActivityUpdateCommand.builder() + .category("MEET") + .title("title") + .content("content") + .images(List.of("https://example.com/image1.png", "https://example.com/image2.png", "https://example.com/image3.png")) + .location("서울") + .startDate(LocalDateTime.parse("2024-05-01T00:00:00")) + .endDate(null) + .isNotice(true) + .participants(List.of(MemberStub.getMemberId())) + .build(); + } + + public static ActivityUpdateCommand getInvalidPeriodActivityUpdateCommand() { + return ActivityUpdateCommand.builder() + .category("MEET") + .title("title") + .content("content") + .images(List.of("https://example.com/image1.png", "https://example.com/image2.png", "https://example.com/image3.png")) + .location("서울") + .startDate(LocalDateTime.parse("9999-12-31T00:00:00")) + .endDate(LocalDateTime.parse("2024-05-01T00:00:00")) + .isNotice(true) + .participants(List.of(MemberStub.getMemberId())) + .build(); + } + + public static ActivitySource getDefaultCreateSource() { Member member = MemberStub.getMember(); - return ActivityCreateSource.builder() + return ActivitySource.builder() .id(1L) .studyId(StudyStub.getStudyId()) - .author(ActivityCreateSource.ActivityMemberCreateSource.builder() + .author(ActivitySource.ActivityMemberCreateSource.builder() .id(member.getId()) .name(member.getName()) .image(member.getImage()) diff --git a/src/test/resources/db/setup.sql b/src/test/resources/db/setup.sql index 256cec16..1bbcc85f 100644 --- a/src/test/resources/db/setup.sql +++ b/src/test/resources/db/setup.sql @@ -44,8 +44,8 @@ INSERT INTO study_member (member_id, study_id, is_admin, is_sent_grape) VALUES ( -- [TABLE: activity] -- 스터디 1 (공지) 기본 활동 -INSERT INTO activity(id, study_id, author_id, category, title, content, is_notice, start_date, end_date, location) -VALUES (1, 1, 4, 'DEFAULT', 'title', 'content', true, '2024-04-01T00:00:00', '2050-05-01T00:00:00', null); +INSERT INTO activity(id, study_id, author_id, category, title, content, is_notice,location) +VALUES (1, 1, 4, 'DEFAULT', 'title', 'content', true, null); INSERT INTO activity_image (activity_id, image) VALUES (1, 'https://example.com/images/image1.png'); INSERT INTO activity_image (activity_id, image) VALUES (1, 'https://example.com/images/image2.png'); @@ -55,8 +55,8 @@ INSERT INTO activity_participant (activity_id, member_id, status) VALUES (1, 2, INSERT INTO activity_participant (activity_id, member_id, status) VALUES (1, 4, 'NONE'); -- 스터디 1 기본 활동 -INSERT INTO activity (id, study_id, author_id, category, title, content, is_notice, start_date, end_date, location) -VALUES (2, 1, 1, 'DEFAULT', 'title', 'content', false, '2024-04-08T00:00:00', '2050-05-01T00:00:00', null); +INSERT INTO activity (id, study_id, author_id, category, title, content, is_notice, location) +VALUES (2, 1, 1, 'DEFAULT', 'title', 'content', false, null); INSERT INTO activity_image (activity_id, image) VALUES (2, 'https://example.com/images/image1.png'); INSERT INTO activity_image (activity_id, image) VALUES (2, 'https://example.com/images/image2.png'); @@ -105,8 +105,8 @@ VALUES (7, 1, 1, 'ASSIGNMENT', 'title', 'content', false, '2024-04-08T00:00:00', INSERT INTO activity_participant (activity_id, member_id, status) VALUES (6, 2, 'PERFORMED'); -- 스터디 2 (공지) 기본 활동 -INSERT INTO activity(id, study_id, author_id, category, title, content, is_notice, start_date, end_date, location) -VALUES (8, 2, 1, 'DEFAULT', 'title', 'content', true, '2024-04-01T00:00:00', '2024-04-08T00:00:00', '성신여대 카페구월'); +INSERT INTO activity(id, study_id, author_id, category, title, content, is_notice, location) +VALUES (8, 2, 1, 'DEFAULT', 'title', 'content', true, '성신여대 카페구월'); INSERT INTO activity_image (activity_id, image) VALUES (8, 'https://example.com/images/image1.png'); INSERT INTO activity_image (activity_id, image) VALUES (8, 'https://example.com/images/image2.png');