Skip to content

[DDING-88] 동아리 지원 폼지 전체 조회 API 구현 #237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package ddingdong.ddingdongBE.common.utils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class TimeParser {
public class TimeUtils {

private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm";

Expand All @@ -26,4 +27,10 @@ public static LocalDateTime processDate(String dateString, LocalDateTime current

return parseToLocalDateTime(dateString);
}

public static boolean isDateInRange(LocalDate nowDate, LocalDate startDate, LocalDate endDate) {
if (nowDate == null || startDate == null || endDate == null) {
return false;
}
return !nowDate.isBefore(startDate) && !nowDate.isAfter(endDate); }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ddingdong.ddingdongBE.domain.activityreport.service.dto.command;

import ddingdong.ddingdongBE.common.utils.TimeParser;
import ddingdong.ddingdongBE.common.utils.TimeUtils;
import ddingdong.ddingdongBE.domain.activityreport.domain.ActivityReport;
import ddingdong.ddingdongBE.domain.activityreport.domain.Participant;
import ddingdong.ddingdongBE.domain.club.entity.Club;
Expand All @@ -23,8 +23,8 @@ public ActivityReport toEntity(Club club) {
.term(term)
.content(content)
.place(place)
.startDate(TimeParser.parseToLocalDateTime(startDate))
.endDate(TimeParser.parseToLocalDateTime(endDate))
.startDate(TimeUtils.parseToLocalDateTime(startDate))
.endDate(TimeUtils.parseToLocalDateTime(endDate))
.participants(participants)
.club(club)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ddingdong.ddingdongBE.domain.activityreport.service.dto.command;

import ddingdong.ddingdongBE.common.utils.TimeParser;
import ddingdong.ddingdongBE.common.utils.TimeUtils;
import ddingdong.ddingdongBE.domain.activityreport.domain.ActivityReport;
import ddingdong.ddingdongBE.domain.activityreport.domain.Participant;
import java.time.LocalDateTime;
Expand All @@ -22,8 +22,8 @@ public ActivityReport toEntity() {
return ActivityReport.builder()
.content(content)
.place(place)
.startDate(TimeParser.processDate(startDate, LocalDateTime.now()))
.endDate(TimeParser.processDate(endDate, LocalDateTime.now()))
.startDate(TimeUtils.processDate(startDate, LocalDateTime.now()))
.endDate(TimeUtils.processDate(endDate, LocalDateTime.now()))
.participants(participants)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
Expand Down Expand Up @@ -52,4 +58,16 @@ void deleteForm(
@PathVariable("formId") Long formId,
@AuthenticationPrincipal PrincipalDetails principalDetails
);

@Operation(summary = "동아리 지원 폼지 전체조회 API")
@ApiResponse(responseCode = "200", description = "동아리 지원 폼지 전체조회 성공",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = FormListResponse.class))
))
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
@GetMapping("/my/forms")
List<FormListResponse> getAllMyForm(
@AuthenticationPrincipal PrincipalDetails principalDetails
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import ddingdong.ddingdongBE.domain.form.api.CentralFormApi;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest;
import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse;
import ddingdong.ddingdongBE.domain.form.service.FacadeCentralFormService;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import ddingdong.ddingdongBE.domain.user.entity.User;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;

Expand Down Expand Up @@ -38,4 +41,13 @@ public void deleteForm(Long formId, PrincipalDetails principalDetails) {
User user = principalDetails.getUser();
facadeCentralFormService.deleteForm(formId, user);
}

@Override
public List<FormListResponse> getAllMyForm(PrincipalDetails principalDetails) {
User user = principalDetails.getUser();
List<FormListQuery> queries = facadeCentralFormService.getAllMyForm(user);
return queries.stream()
.map(FormListResponse::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ddingdong.ddingdongBE.domain.form.controller.dto.response;

import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import lombok.Builder;

@Builder
public record FormListResponse(
@Schema(description = "지원 폼지 ID", example = "1")
Long formId,
@Schema(description = "지원 폼지 제목", example = "폼지 제목입니다.")
String title,
@Schema(description = "지원 폼지 시작일", example = "2001-01-01")
LocalDate startDate,
@Schema(description = "지원 폼지 종료일", example = "2001-01-02")
LocalDate endData,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

필드명 오타를 수정해주세요.

endDataendDate의 오타로 보입니다. 일관성을 위해 수정이 필요합니다.

다음과 같이 수정해주세요:

-        LocalDate endData,
+        LocalDate endDate,

그리고 from 메서드에서도:

-                .endData(query.endData())
+                .endDate(query.endDate())

Also applies to: 27-27

@Schema(description = "활성화 여부", example = "true")
boolean isActive
) {

public static FormListResponse from(FormListQuery query) {
return FormListResponse.builder()
.formId(query.formId())
.title(query.title())
.startDate(query.startDate())
.endData(query.endData())
.isActive(query.isActive())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package ddingdong.ddingdongBE.domain.form.repository;

import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.form.entity.Form;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FormRepository extends JpaRepository<Form, Long> {

List<Form> findAllByClub(Club club);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import ddingdong.ddingdongBE.domain.user.entity.User;
import java.util.List;

public interface FacadeCentralFormService {

Expand All @@ -11,4 +13,6 @@ public interface FacadeCentralFormService {
void updateForm(UpdateFormCommand command);

void deleteForm(Long formId, User user);

List<FormListQuery> getAllMyForm(User user);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ddingdong.ddingdongBE.domain.form.service;

import ddingdong.ddingdongBE.common.exception.AuthenticationException.NonHaveAuthority;
import ddingdong.ddingdongBE.common.utils.TimeUtils;
import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.club.service.ClubService;
import ddingdong.ddingdongBE.domain.form.entity.Form;
Expand All @@ -9,7 +10,9 @@
import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand.CreateFormFieldCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand.UpdateFormFieldCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import ddingdong.ddingdongBE.domain.user.entity.User;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
Expand All @@ -19,7 +22,7 @@
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class FacadeCentralFormServiceImpl implements FacadeCentralFormService{
public class FacadeCentralFormServiceImpl implements FacadeCentralFormService {

private final FormService formService;
private final FormFieldService formFieldService;
Expand Down Expand Up @@ -59,6 +62,20 @@ public void deleteForm(Long formId, User user) {
formService.delete(form); //테이블 생성 시 외래 키에 cascade 설정하여 formField 삭제도 자동으로 됨.
}

@Override
public List<FormListQuery> getAllMyForm(User user) {
Club club = clubService.getByUserId(user.getId());
List<Form> forms = formService.getAllByClub(club);
return forms.stream()
.map(this::buildFormListQuery)
.toList();
}

private FormListQuery buildFormListQuery(Form form) {
boolean isActive = TimeUtils.isDateInRange(LocalDate.now(), form.getStartDate(), form.getEndDate());
return FormListQuery.from(form, isActive);
}

private void validateEqualsClub(Club club, Form form) {
if (!Objects.equals(club.getId(), form.getClub().getId())) {
throw new NonHaveAuthority();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ddingdong.ddingdongBE.domain.form.service;

import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.form.entity.Form;
import java.util.List;

public interface FormService {

Expand All @@ -9,4 +11,6 @@ public interface FormService {
Form getById(Long formId);

void delete(Form form);

List<Form> getAllByClub(Club club);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package ddingdong.ddingdongBE.domain.form.service;

import ddingdong.ddingdongBE.common.exception.PersistenceException.ResourceNotFound;
import ddingdong.ddingdongBE.domain.club.entity.Club;
import ddingdong.ddingdongBE.domain.form.entity.Form;
import ddingdong.ddingdongBE.domain.form.repository.FormRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -31,4 +33,9 @@ public Form getById(Long formId) {
public void delete(Form form) {
formRepository.delete(form);
}

@Override
public List<Form> getAllByClub(Club club) {
return formRepository.findAllByClub(club);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ddingdong.ddingdongBE.domain.form.service.dto.query;

import ddingdong.ddingdongBE.domain.form.entity.Form;
import java.time.LocalDate;
import lombok.Builder;

@Builder
public record FormListQuery(
Long formId,
String title,
LocalDate startDate,
LocalDate endData,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

필드명 오타를 수정해주세요.

endDataendDate의 오타로 보입니다. 이는 혼란을 야기할 수 있으므로 수정이 필요합니다.

다음과 같이 수정해주세요:

-        LocalDate endData,
+        LocalDate endDate,

이에 따라 팩토리 메서드도 수정이 필요합니다:

-                .endData(form.getEndDate())
+                .endDate(form.getEndDate())

Committable suggestion skipped: line range outside the PR's diff.

boolean isActive
) {

public static FormListQuery from(Form form, boolean isActive) {
return FormListQuery.builder()
.formId(form.getId())
.title(form.getTitle())
.startDate(form.getStartDate())
.endData(form.getEndDate())
.isActive(isActive)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ddingdong.ddingdongBE.common.utils;

import java.time.LocalDate;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class TimeUtilsTest {

@DisplayName("현재 날짜가 기간 내 포함된다면 true를 반환한다.")
@Test
void isDateInRange() {
// given
LocalDate now = LocalDate.now();
LocalDate startDate = now.minusDays(1);
LocalDate endDate = now.plusDays(1);
// when
boolean isActive = TimeUtils.isDateInRange(now, startDate, endDate);
// then
Assertions.assertThat(isActive).isTrue();
}
Comment on lines +10 to +21
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

경계값 테스트 케이스 추가가 필요합니다.

현재 구현은 기본적인 케이스만 테스트하고 있습니다. 다음과 같은 경계값 테스트 케이스를 추가하면 좋을 것 같습니다:

  1. 현재 날짜가 시작일과 같은 경우
  2. 현재 날짜가 종료일과 같은 경우
  3. 시작일이 null인 경우
  4. 종료일이 null인 경우
  5. 현재 날짜가 범위를 벗어난 경우

다음과 같이 테스트 메소드를 추가해보세요:

@DisplayName("현재 날짜가 시작일과 같다면 true를 반환한다.")
@Test
void isDateInRange_WhenCurrentDateEqualsStartDate() {
    // given
    LocalDate now = LocalDate.now();
    // when
    boolean isActive = TimeUtils.isDateInRange(now, now, now.plusDays(1));
    // then
    Assertions.assertThat(isActive).isTrue();
}

@DisplayName("현재 날짜가 종료일과 같다면 true를 반환한다.")
@Test
void isDateInRange_WhenCurrentDateEqualsEndDate() {
    // given
    LocalDate now = LocalDate.now();
    // when
    boolean isActive = TimeUtils.isDateInRange(now, now.minusDays(1), now);
    // then
    Assertions.assertThat(isActive).isTrue();
}

@DisplayName("시작일이 null이면 false를 반환한다.")
@Test
void isDateInRange_WhenStartDateIsNull() {
    // given
    LocalDate now = LocalDate.now();
    // when
    boolean isActive = TimeUtils.isDateInRange(now, null, now.plusDays(1));
    // then
    Assertions.assertThat(isActive).isFalse();
}

@DisplayName("종료일이 null이면 false를 반환한다.")
@Test
void isDateInRange_WhenEndDateIsNull() {
    // given
    LocalDate now = LocalDate.now();
    // when
    boolean isActive = TimeUtils.isDateInRange(now, now.minusDays(1), null);
    // then
    Assertions.assertThat(isActive).isFalse();
}

@DisplayName("현재 날짜가 범위를 벗어나면 false를 반환한다.")
@Test
void isDateInRange_WhenCurrentDateIsOutOfRange() {
    // given
    LocalDate now = LocalDate.now();
    LocalDate startDate = now.plusDays(1);
    LocalDate endDate = now.plusDays(2);
    // when
    boolean isActive = TimeUtils.isDateInRange(now, startDate, endDate);
    // then
    Assertions.assertThat(isActive).isFalse();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand.UpdateFormFieldCommand;
import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery;
import ddingdong.ddingdongBE.domain.user.entity.Role;
import ddingdong.ddingdongBE.domain.user.entity.User;
import ddingdong.ddingdongBE.domain.user.repository.UserRepository;
Expand Down Expand Up @@ -207,4 +208,43 @@ void validateEqualsClub() {
facadeCentralFormService.deleteForm(savedForm.getId(), user2);
});
}

@DisplayName("동아리는 자신의 폼지를 전부 조회할 수 있다.")
@Test
void getAllMyForm() {
// given
User user = fixtureMonkey.giveMeBuilder(User.class)
.set("id", 1L)
.set("Role", Role.CLUB)
.set("deletedAt", null)
.sample();
User savedUser = userRepository.save(user);
Club club = fixtureMonkey.giveMeBuilder(Club.class)
.set("id", 1L)
.set("user", savedUser)
.set("score", null)
.set("clubMembers", null)
.set("deletedAt", null)
.sample();
clubRepository.save(club);
Form form = fixtureMonkey.giveMeBuilder(Form.class)
.set("title", "제목1")
.set("club", club)
.sample();
Form form2 = fixtureMonkey.giveMeBuilder(Form.class)
.set("title", "제목2")
.set("club", club)
.sample();
formService.create(form);
formService.create(form2);

// when
List<FormListQuery> queries = facadeCentralFormService.getAllMyForm(savedUser);
// then
assertThat(queries).isNotEmpty();
assertThat(queries.size()).isEqualTo(2);
assertThat(queries.get(0).title()).isEqualTo("제목1");
assertThat(queries.get(1).title()).isEqualTo("제목2");

}
}