From 57d02d9735563d679d9f0efc953ea6ccb7e199c2 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Tue, 4 Feb 2025 04:15:48 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat=20:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20=ED=8F=BC=EC=A7=80=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/{TimeParser.java => TimeUtils.java} | 9 +++++- .../command/CreateActivityReportCommand.java | 6 ++-- .../command/UpdateActivityReportCommand.java | 6 ++-- .../domain/form/api/CentralFormApi.java | 18 +++++++++++ .../controller/CentralFormController.java | 12 +++++++ .../dto/response/FormListResponse.java | 31 +++++++++++++++++++ .../form/repository/FormRepository.java | 3 ++ .../service/FacadeCentralFormService.java | 4 +++ .../service/FacadeCentralFormServiceImpl.java | 19 +++++++++++- .../domain/form/service/FormService.java | 4 +++ .../form/service/GeneralFormService.java | 7 +++++ .../form/service/dto/query/FormListQuery.java | 26 ++++++++++++++++ 12 files changed, 137 insertions(+), 8 deletions(-) rename src/main/java/ddingdong/ddingdongBE/common/utils/{TimeParser.java => TimeUtils.java} (68%) create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormListResponse.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormListQuery.java diff --git a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeParser.java b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java similarity index 68% rename from src/main/java/ddingdong/ddingdongBE/common/utils/TimeParser.java rename to src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java index 5925449e..b5e3b57e 100644 --- a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeParser.java +++ b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java @@ -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"; @@ -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); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/CreateActivityReportCommand.java b/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/CreateActivityReportCommand.java index 30db903a..0d419fa2 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/CreateActivityReportCommand.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/CreateActivityReportCommand.java @@ -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; @@ -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(); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/UpdateActivityReportCommand.java b/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/UpdateActivityReportCommand.java index 436e3896..a7f65186 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/UpdateActivityReportCommand.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/activityreport/service/dto/command/UpdateActivityReportCommand.java @@ -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; @@ -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(); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java index b1f96759..e159299a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java @@ -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; @@ -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 getAllMyForm( + @AuthenticationPrincipal PrincipalDetails principalDetails + ); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java index 1d092019..fb66d53d 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java @@ -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; @@ -38,4 +41,13 @@ public void deleteForm(Long formId, PrincipalDetails principalDetails) { User user = principalDetails.getUser(); facadeCentralFormService.deleteForm(formId, user); } + + @Override + public List getAllMyForm(PrincipalDetails principalDetails) { + User user = principalDetails.getUser(); + List queries = facadeCentralFormService.getAllMyForm(user); + return queries.stream() + .map(FormListResponse::from) + .toList(); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormListResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormListResponse.java new file mode 100644 index 00000000..21efac08 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormListResponse.java @@ -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, + @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(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormRepository.java index 8ea4bdf5..908d4eb2 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormRepository.java @@ -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 { + List
findAllByClub(Club club); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java index c03fe600..af59fa8d 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java @@ -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 { @@ -11,4 +13,6 @@ public interface FacadeCentralFormService { void updateForm(UpdateFormCommand command); void deleteForm(Long formId, User user); + + List getAllMyForm(User user); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java index 3b05f7d7..3e9b72ec 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -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; @@ -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; @@ -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; @@ -59,6 +62,20 @@ public void deleteForm(Long formId, User user) { formService.delete(form); //테이블 생성 시 외래 키에 cascade 설정하여 formField 삭제도 자동으로 됨. } + @Override + public List getAllMyForm(User user) { + Club club = clubService.getByUserId(user.getId()); + List 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(); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java index 246b577b..96ca4ace 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java @@ -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 { @@ -9,4 +11,6 @@ public interface FormService { Form getById(Long formId); void delete(Form form); + + List getAllByClub(Club club); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java index fd30c51f..9f4e52ef 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java @@ -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; @@ -31,4 +33,9 @@ public Form getById(Long formId) { public void delete(Form form) { formRepository.delete(form); } + + @Override + public List getAllByClub(Club club) { + return formRepository.findAllByClub(club); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormListQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormListQuery.java new file mode 100644 index 00000000..750b4f82 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormListQuery.java @@ -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, + 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(); + } + +} From 880958e7d3ef05e2c22c76619840af0136151d1e Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Tue, 4 Feb 2025 04:34:38 +0900 Subject: [PATCH 02/14] =?UTF-8?q?test=20:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20=ED=8F=BC=EC=A7=80=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/TimeUtilsTest.java | 22 ++++++++++ .../FacadeCentralFormServiceImplTest.java | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/test/java/ddingdong/ddingdongBE/common/utils/TimeUtilsTest.java diff --git a/src/test/java/ddingdong/ddingdongBE/common/utils/TimeUtilsTest.java b/src/test/java/ddingdong/ddingdongBE/common/utils/TimeUtilsTest.java new file mode 100644 index 00000000..2ba709bc --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/common/utils/TimeUtilsTest.java @@ -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(); + } +} diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java index 52c5f8ff..71635db4 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java @@ -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; @@ -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 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"); + + } } From 285a67202c8cb5789f6289e59850b669f88f154a Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:58:00 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat=20:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20=ED=8F=BC=EC=A7=80=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/form/api/CentralFormApi.java | 11 +++ .../controller/CentralFormController.java | 8 ++ .../controller/dto/response/FormResponse.java | 73 +++++++++++++++++++ .../ddingdongBE/domain/form/entity/Form.java | 10 +++ .../form/repository/FormFieldRepository.java | 2 + .../service/FacadeCentralFormService.java | 3 + .../service/FacadeCentralFormServiceImpl.java | 8 ++ .../domain/form/service/FormFieldService.java | 1 - .../form/service/dto/query/FormQuery.java | 57 +++++++++++++++ 9 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormResponse.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormQuery.java diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java index e159299a..1355fb2f 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java @@ -4,6 +4,7 @@ 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.controller.dto.response.FormResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -70,4 +71,14 @@ void deleteForm( List getAllMyForm( @AuthenticationPrincipal PrincipalDetails principalDetails ); + + @Operation(summary = "동아리 지원 폼지 상세조회 API") + @ApiResponse(responseCode = "200", description = "동아리 지원 폼지 상세조회 성공", + content = @Content(schema = @Schema(implementation = FormResponse.class))) + @ResponseStatus(HttpStatus.OK) + @SecurityRequirement(name = "AccessToken") + @GetMapping("/my/forms/{formId}") + FormResponse getForm( + @PathVariable("formId") Long formId + ); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java index fb66d53d..9ad38720 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java @@ -5,8 +5,10 @@ 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.controller.dto.response.FormResponse; import ddingdong.ddingdongBE.domain.form.service.FacadeCentralFormService; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.util.List; import lombok.RequiredArgsConstructor; @@ -50,4 +52,10 @@ public List getAllMyForm(PrincipalDetails principalDetails) { .map(FormListResponse::from) .toList(); } + + @Override + public FormResponse getForm(Long formId) { + FormQuery query = facadeCentralFormService.getForm(formId); + return FormResponse.from(query); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormResponse.java new file mode 100644 index 00000000..1f40a48c --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormResponse.java @@ -0,0 +1,73 @@ +package ddingdong.ddingdongBE.domain.form.controller.dto.response; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery.FormFieldListQuery; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; +import java.util.List; +import lombok.Builder; + +@Builder +public record FormResponse( + @Schema(description = "폼지 제목", example = "카우 1기 폼지") + String title, + @Schema(description = "폼지 설명", example = "폼지 설명입니다") + String description, + @Schema(description = "폼지 시작일", example = "2001-01-01") + LocalDate startDate, + @Schema(description = "폼지 종료일", example = "2001-01-02") + LocalDate endDate, + @Schema(description = "면접 여부", example = "true") + boolean hasInterview, + @Schema(description = "생성한 섹션들", example = "[공통,서버,웹]") + List sections, + @ArraySchema(schema = @Schema(implementation = FormFieldListResponse.class)) + List formFields +) { + + @Builder + record FormFieldListResponse( + @Schema(description = "폼지 질문", example = "당신의 이름은?") + String question, + @Schema(description = "폼지 질문 유형", example = "CHECK_BOX") + FieldType type, + @Schema(description = "폼지 지문", example = "[지문1, 지문2]") + List options, + @Schema(description = "필수 여부", example = "true") + boolean required, + @Schema(description = "폼지 질문 순서", example = "1") + int order, + @Schema(description = "폼지 섹션", example = "공통") + String section + ) { + + public static FormFieldListResponse from(FormFieldListQuery formFieldListQuery) { + return FormFieldListResponse.builder() + .question(formFieldListQuery.question()) + .type(formFieldListQuery.type()) + .options(formFieldListQuery.options()) + .required(formFieldListQuery.required()) + .order(formFieldListQuery.order()) + .section(formFieldListQuery.section()) + .build(); + } + } + + public static FormResponse from(FormQuery formQuery) { + List responses = formQuery.formFields().stream() + .map(FormFieldListResponse::from) + .toList(); + + return FormResponse.builder() + .title(formQuery.title()) + .description(formQuery.description()) + .startDate(formQuery.startDate()) + .endDate(formQuery.endDate()) + .hasInterview(formQuery.hasInterview()) + .sections(formQuery.sections()) + .formFields(responses) + .build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java index 05ef3635..0c91edcf 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java @@ -1,8 +1,10 @@ package ddingdong.ddingdongBE.domain.form.entity; import ddingdong.ddingdongBE.common.BaseEntity; +import ddingdong.ddingdongBE.common.converter.StringListConverter; import ddingdong.ddingdongBE.domain.club.entity.Club; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -10,6 +12,7 @@ import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import java.time.LocalDate; +import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -38,6 +41,10 @@ public class Form extends BaseEntity { @Column(nullable = false) private boolean hasInterview; + @Column(nullable = false) + @Convert(converter = StringListConverter.class) + private List sections; + @ManyToOne(fetch = FetchType.LAZY) private Club club; @@ -48,6 +55,7 @@ private Form( LocalDate startDate, LocalDate endDate, boolean hasInterview, + List sections, Club club ) { this.title = title; @@ -55,6 +63,7 @@ private Form( this.startDate = startDate; this.endDate = endDate; this.hasInterview = hasInterview; + this.sections = sections; this.club = club; } @@ -63,6 +72,7 @@ public void update(Form updateForm) { this.description = updateForm.getDescription(); this.startDate = updateForm.getStartDate(); this.endDate = updateForm.getEndDate(); + this.sections = updateForm.getSections(); this.hasInterview = updateForm.isHasInterview(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java index 9a8db1e1..bab31349 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java @@ -8,4 +8,6 @@ public interface FormFieldRepository extends JpaRepository { List findAllByForm(Form form); + + List findAllByFormAndSection(Form form, String section); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java index af59fa8d..3556f14f 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java @@ -3,6 +3,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.query.FormListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.util.List; @@ -15,4 +16,6 @@ public interface FacadeCentralFormService { void deleteForm(Long formId, User user); List getAllMyForm(User user); + + FormQuery getForm(Long formId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java index 3e9b72ec..d06536fc 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -11,6 +11,7 @@ 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.form.service.dto.query.FormQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.time.LocalDate; import java.util.List; @@ -71,6 +72,13 @@ public List getAllMyForm(User user) { .toList(); } + @Override + public FormQuery getForm(Long formId) { + Form form = formService.getById(formId); + List formFields = formFieldService.findAllByForm(form); + return FormQuery.of(form, formFields); + } + private FormListQuery buildFormListQuery(Form form) { boolean isActive = TimeUtils.isDateInRange(LocalDate.now(), form.getStartDate(), form.getEndDate()); return FormListQuery.from(form, isActive); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java index 9fa1e86f..890d1924 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java @@ -3,7 +3,6 @@ import ddingdong.ddingdongBE.domain.form.entity.Form; import ddingdong.ddingdongBE.domain.form.entity.FormField; import java.util.List; -import java.util.Optional; public interface FormFieldService { diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormQuery.java new file mode 100644 index 00000000..00fe510c --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormQuery.java @@ -0,0 +1,57 @@ +package ddingdong.ddingdongBE.domain.form.service.dto.query; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import java.time.LocalDate; +import java.util.List; +import lombok.Builder; + +@Builder +public record FormQuery( + String title, + String description, + LocalDate startDate, + LocalDate endDate, + boolean hasInterview, + List sections, + List formFields +) { + + @Builder + public record FormFieldListQuery( + String question, + FieldType type, + List options, + boolean required, + int order, + String section + ) { + public static FormFieldListQuery from(FormField formField) { + return FormFieldListQuery.builder() + .question(formField.getQuestion()) + .type(formField.getFieldType()) + .options(formField.getOptions()) + .required(formField.isRequired()) + .order(formField.getFieldOrder()) + .section(formField.getSection()) + .build(); + } + } + + public static FormQuery of(Form form, List formFields) { + List formFieldListQueries = formFields.stream() + .map(FormFieldListQuery::from) + .toList(); + + return FormQuery.builder() + .title(form.getTitle()) + .description(form.getDescription()) + .startDate(form.getStartDate()) + .endDate(form.getEndDate()) + .hasInterview(form.isHasInterview()) + .sections(form.getSections()) + .formFields(formFieldListQueries) + .build(); + } +} From b773205b4a9b4ff00ac3bc08dc487798218af930 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:58:45 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat=20:=20=EB=AA=85=EC=84=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20sql=EB=AC=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/migration/V36__form_add_column_sections.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/main/resources/db/migration/V36__form_add_column_sections.sql diff --git a/src/main/resources/db/migration/V36__form_add_column_sections.sql b/src/main/resources/db/migration/V36__form_add_column_sections.sql new file mode 100644 index 00000000..6fcbfa2d --- /dev/null +++ b/src/main/resources/db/migration/V36__form_add_column_sections.sql @@ -0,0 +1 @@ +ALTER TABLE form ADD COLUMN sections TEXT NOT NULL DEFAULT '[]'; From e869e10c7d75e602ac5dd937912c87a16372fca2 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:03:25 +0900 Subject: [PATCH 05/14] =?UTF-8?q?test=20:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20=ED=8F=BC=EC=A7=80=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20test=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FacadeCentralFormServiceImplTest.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java index 71635db4..1c735ea6 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java @@ -17,12 +17,11 @@ 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.form.service.dto.query.FormQuery; import ddingdong.ddingdongBE.domain.user.entity.Role; import ddingdong.ddingdongBE.domain.user.entity.User; import ddingdong.ddingdongBE.domain.user.repository.UserRepository; -import jakarta.persistence.EntityManager; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -49,16 +48,8 @@ class FacadeCentralFormServiceImplTest extends TestContainerSupport { @Autowired private FormFieldRepository formFieldRepository; - @Autowired - private EntityManager entityManager; - private static final FixtureMonkey fixtureMonkey = FixtureMonkeyFactory.getNotNullBuilderIntrospectorMonkey(); - @BeforeEach - void setUp() { - entityManager.clear(); - } - @DisplayName("폼지와 폼지 질문을 생성할 수 있다.") @Test void createForm() { @@ -247,4 +238,26 @@ void getAllMyForm() { assertThat(queries.get(1).title()).isEqualTo("제목2"); } + + @DisplayName("동아리는 폼지를 상세조회 할 수 있다.") + @Test + void getForm() { + // given + Form form = fixtureMonkey.giveMeBuilder(Form.class) + .set("id", 1L) + .set("title", "제목1") + .set("club", null) + .sample(); + Form form2 = fixtureMonkey.giveMeBuilder(Form.class) + .set("id", 2L) + .set("title", "제목2") + .set("club", null) + .sample(); + formService.create(form); + formService.create(form2); + // when + FormQuery formQuery = facadeCentralFormService.getForm(1L); + // then + assertThat(formQuery.title()).isEqualTo("제목1"); + } } From 5092b4ca249ef58dfd6dbebf88d74549346e2236 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:34:11 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat=20:=20=ED=86=B5=EA=B3=84=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/CalculationUtils.java | 11 ++ .../ddingdongBE/common/utils/TimeUtils.java | 41 ++++--- .../domain/form/api/CentralFormApi.java | 17 ++- .../controller/CentralFormController.java | 8 ++ .../dto/response/FormStatisticsResponse.java | 105 ++++++++++++++++++ .../service/FacadeCentralFormService.java | 3 + .../service/FacadeCentralFormServiceImpl.java | 15 +++ .../form/service/FormStatisticService.java | 18 +++ .../service/FormStatisticServiceImpl.java | 100 +++++++++++++++++ .../dto/query/FormStatisticsQuery.java | 40 +++++++ .../form/service/vo/ApplicationRates.java | 50 +++++++++ .../domain/form/service/vo/HalfYear.java | 49 ++++++++ .../repository/FormAnswerRepository.java | 3 + .../repository/FormApplicationRepository.java | 31 ++++++ .../repository/dto/DepartmentInfo.java | 8 ++ 15 files changed, 475 insertions(+), 24 deletions(-) create mode 100644 src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/DepartmentInfo.java diff --git a/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java b/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java new file mode 100644 index 00000000..e3f52fe5 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java @@ -0,0 +1,11 @@ +package ddingdong.ddingdongBE.common.utils; + +public class CalculationUtils { + + public static int calculateRate(int count, int totalCount) { + if (totalCount == 0) { + return 0; + } + return (int) ((double) count / totalCount * 100); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java index b5e3b57e..292788ce 100644 --- a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java +++ b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java @@ -6,31 +6,36 @@ public class TimeUtils { - private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm"; + private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm"; - public static LocalDateTime parseToLocalDateTime(String dateString) { - if (dateString == null || dateString.isBlank()) { - return null; + public static LocalDateTime parseToLocalDateTime(String dateString) { + if (dateString == null || dateString.isBlank()) { + return null; + } + + return LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(DATE_FORMAT)); } - return LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(DATE_FORMAT)); - } + public static LocalDateTime processDate(String dateString, LocalDateTime currentDate) { + if (dateString == null) { + return null; + } - public static LocalDateTime processDate(String dateString, LocalDateTime currentDate) { - if (dateString == null) { - return null; - } + if (dateString.isBlank()) { + return null; + } - if (dateString.isBlank()) { - return null; + return parseToLocalDateTime(dateString); } - 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); + } - public static boolean isDateInRange(LocalDate nowDate, LocalDate startDate, LocalDate endDate) { - if (nowDate == null || startDate == null || endDate == null) { - return false; + public static boolean isFirstHalf(int month) { + return month >= 1 && month <= 6; } - return !nowDate.isBefore(startDate) && !nowDate.isAfter(endDate); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java index 1355fb2f..6db576aa 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java @@ -5,6 +5,7 @@ import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest; import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse; import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormResponse; +import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormStatisticsResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -68,9 +69,7 @@ void deleteForm( @ResponseStatus(HttpStatus.OK) @SecurityRequirement(name = "AccessToken") @GetMapping("/my/forms") - List getAllMyForm( - @AuthenticationPrincipal PrincipalDetails principalDetails - ); + List getAllMyForm(@AuthenticationPrincipal PrincipalDetails principalDetails); @Operation(summary = "동아리 지원 폼지 상세조회 API") @ApiResponse(responseCode = "200", description = "동아리 지원 폼지 상세조회 성공", @@ -78,7 +77,13 @@ List getAllMyForm( @ResponseStatus(HttpStatus.OK) @SecurityRequirement(name = "AccessToken") @GetMapping("/my/forms/{formId}") - FormResponse getForm( - @PathVariable("formId") Long formId - ); + FormResponse getForm(@PathVariable("formId") Long formId); + + @Operation(summary = "동아리 지원 폼지 통계 전체조회 API") + @ApiResponse(responseCode = "200", description = "동아리 지원 폼지 통계 전체조회 성공", + content = @Content(schema = @Schema(implementation = FormStatisticsResponse.class))) + @ResponseStatus(HttpStatus.OK) + @SecurityRequirement(name = "AccessToken") + @GetMapping("/my/forms/{formId}/statistics") + FormStatisticsResponse getFormStatistics(@PathVariable("formId") Long formId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java index 9ad38720..24443b1c 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java @@ -6,9 +6,11 @@ import ddingdong.ddingdongBE.domain.form.controller.dto.request.UpdateFormRequest; import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormListResponse; import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormResponse; +import ddingdong.ddingdongBE.domain.form.controller.dto.response.FormStatisticsResponse; import ddingdong.ddingdongBE.domain.form.service.FacadeCentralFormService; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.util.List; import lombok.RequiredArgsConstructor; @@ -58,4 +60,10 @@ public FormResponse getForm(Long formId) { FormQuery query = facadeCentralFormService.getForm(formId); return FormResponse.from(query); } + + @Override + public FormStatisticsResponse getFormStatistics(Long formId) { + FormStatisticsQuery query = facadeCentralFormService.getStatisticsByForm(formId); + return FormStatisticsResponse.from(query); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java new file mode 100644 index 00000000..eb917815 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java @@ -0,0 +1,105 @@ +package ddingdong.ddingdongBE.domain.form.controller.dto.response; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Builder; + +@Builder +public record FormStatisticsResponse( + @Schema(description = "총 지원자 수", example = "50") + int totalCount, + @ArraySchema(schema = @Schema(implementation = DepartmentRankResponse.class)) + List departmentRanks, + @ArraySchema(schema = @Schema(implementation = ApplicantRateResponse.class)) + List applicantRates, + @ArraySchema(schema = @Schema(implementation = FieldStatisticsListResponse.class)) + List fields +) { + + @Builder + record DepartmentRankResponse( + @Schema(description = "학과 내 경쟁 순위", example = "1") + int rank, + @Schema(description = "학과명", example = "융합소프트웨어학부") + String label, + @Schema(description = "해당 학과의 지원자 수", example = "50") + int count, + @Schema(description = "전체 지원자 수 대비 비율", example = "30") + int rate + ) { + + public static DepartmentRankResponse from(DepartmentRankQuery query) { + return DepartmentRankResponse.builder() + .rank(query.rank()) + .label(query.label()) + .count(query.count()) + .rate(query.rate()) + .build(); + } + } + + @Builder + record ApplicantRateResponse( + @Schema(description = "비교 년도 및 학기", example = "2025-1") + String label, + @Schema(description = "해당 년도 및 학기 총 지원자수", example = "40") + int count, + @Schema(description = "비교 대비 비율", example = "150") + int comparedToLastSemester + ) { + public static ApplicantRateResponse from(ApplicantRateQuery query) { + return ApplicantRateResponse.builder() + .label(query.label()) + .count(query.count()) + .comparedToLastSemester(query.comparedToLastSemester()) + .build(); + } + + } + + @Builder + record FieldStatisticsListResponse( + @Schema(description = "폼지 질문 id", example = "1") + Long id, + @Schema(description = "폼지 질문", example = "당신 이름은 무엇인가요?") + String question, + @Schema(description = "폼지 질문에 대해 총 작성 개수", example = "20") + int count, + @Schema(description = "폼지 질문 유형", example = "CHECK_BOX") + FieldType type + ) { + + public static FieldStatisticsListResponse from(FieldStatisticsListQuery query) { + return FieldStatisticsListResponse.builder() + .id(query.id()) + .question(query.question()) + .count(query.count()) + .type(query.fieldType()) + .build(); + } + } + + public static FormStatisticsResponse from(FormStatisticsQuery query) { + List departmentRankResponses = query.departmentRanks().stream() + .map(DepartmentRankResponse::from) + .toList(); + List applicantRateResponses = query.applicantRates().stream() + .map(ApplicantRateResponse::from) + .toList(); + List fieldStatisticsListResponses = query.fields().stream() + .map(FieldStatisticsListResponse::from) + .toList(); + return FormStatisticsResponse.builder() + .totalCount(query.totalCount()) + .departmentRanks(departmentRankResponses) + .applicantRates(applicantRateResponses) + .fields(fieldStatisticsListResponses) + .build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java index 3556f14f..55460ec9 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java @@ -4,6 +4,7 @@ import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.util.List; @@ -18,4 +19,6 @@ public interface FacadeCentralFormService { List getAllMyForm(User user); FormQuery getForm(Long formId); + + FormStatisticsQuery getStatisticsByForm(Long formId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java index d06536fc..4321e025 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -12,6 +12,10 @@ import ddingdong.ddingdongBE.domain.form.service.dto.command.UpdateFormCommand.UpdateFormFieldCommand; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.time.LocalDate; import java.util.List; @@ -28,6 +32,7 @@ public class FacadeCentralFormServiceImpl implements FacadeCentralFormService { private final FormService formService; private final FormFieldService formFieldService; private final ClubService clubService; + private final FormStatisticService formStatisTicService; @Transactional @Override @@ -79,6 +84,16 @@ public FormQuery getForm(Long formId) { return FormQuery.of(form, formFields); } + @Override + public FormStatisticsQuery getStatisticsByForm(Long formId) { + Form form = formService.getById(formId); + int totalCount = formStatisTicService.getTotalApplicationCountByForm(form); + List departmentRankQueries = formStatisTicService.createDepartmentRankByForm(form); + List applicantRateQueries = formStatisTicService.createApplicationRateByForm(form); + List fieldStatisticsListQueries = formStatisTicService.createFieldStatisticsListByForm(form); + return new FormStatisticsQuery(totalCount, departmentRankQueries, applicantRateQueries, fieldStatisticsListQueries); + } + private FormListQuery buildFormListQuery(Form form) { boolean isActive = TimeUtils.isDateInRange(LocalDate.now(), form.getStartDate(), form.getEndDate()); return FormListQuery.from(form, isActive); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java new file mode 100644 index 00000000..b0e329f9 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java @@ -0,0 +1,18 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import java.util.List; + +public interface FormStatisticService { + + int getTotalApplicationCountByForm(Form form); + + List createDepartmentRankByForm(Form form); + + List createApplicationRateByForm(Form form); + + List createFieldStatisticsListByForm(Form form); +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java new file mode 100644 index 00000000..d1af9332 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java @@ -0,0 +1,100 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.common.utils.CalculationUtils; +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.repository.FormFieldRepository; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.form.service.vo.ApplicationRates; +import ddingdong.ddingdongBE.domain.form.service.vo.ApplicationRates.ApplicationRate; +import ddingdong.ddingdongBE.domain.form.service.vo.HalfYear; +import ddingdong.ddingdongBE.domain.formapplication.repository.FormAnswerRepository; +import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FormStatisticServiceImpl implements FormStatisticService { + + private static final int APPLICATION_COMPARE_COUNT = 5; + private static final int FIRST_APPLICATION = 0; + private static final int NOT_EXIST_APPLICATION = 0; + private static final int DEFAULT_APPLICATION_RATE = 100; + + private final FormApplicationRepository formApplicationRepository; + private final FormFieldRepository formFieldRepository; + private final FormAnswerRepository formAnswerRepository; + + @Override + public int getTotalApplicationCountByForm(Form form) { + return formApplicationRepository.countByForm(form).intValue(); + } + + @Override + public List createDepartmentRankByForm(Form form) { + List departmentInfos = formApplicationRepository.findTopFiveDepartmentsByForm(form.getId()); + int totalCount = getTotalApplicationCountByForm(form); + + return IntStream.range(0, departmentInfos.size()) + .mapToObj(index -> { + DepartmentInfo departmentInfo = departmentInfos.get(index); + int rank = index + 1; + String department = departmentInfo.getDepartment(); + int count = parseToInt(departmentInfo.getCount()); + int rate = CalculationUtils.calculateRate(count, totalCount); + return new DepartmentRankQuery(rank, department, count, rate); + }) + .toList(); + } + + @Override + public List createApplicationRateByForm(Form form) { + List results = new ArrayList<>(); + + HalfYear halfYear = HalfYear.from(form.getEndDate()); + ApplicationRates applicationRates = new ApplicationRates(); + for (int count = 0; count < APPLICATION_COMPARE_COUNT; count++) { + int maxCount = parseToInt(formApplicationRepository.findMaxApplicationCountByDateRange( + halfYear.getHalfStartDate(), + halfYear.getHalfEndDate() + )); + if (maxCount == NOT_EXIST_APPLICATION) { + break; + } + int rate = (applicationRates.getPrevious() == null) ? DEFAULT_APPLICATION_RATE : + CalculationUtils.calculateRate(maxCount, applicationRates.getPrevious().getCount()); + applicationRates.add(halfYear.getLabel(), maxCount, rate); + halfYear.minusHalves(); + } + return applicationRates.getApplicationRates().stream() + .map(ApplicationRate::toQuery) + .toList(); + } + + @Override + public List createFieldStatisticsListByForm(Form form) { + List formFields = formFieldRepository.findAllByForm(form); + return formFields.stream() + .map(this::buildFieldStatisticsList) + .toList(); + } + + private FieldStatisticsListQuery buildFieldStatisticsList(FormField formField) { + int answerCount = formAnswerRepository.countByFormField(formField); + return new FieldStatisticsListQuery(formField.getId(), formField.getQuestion(), answerCount, + formField.getFieldType()); + } + + private int parseToInt(Integer count) { + return count == null ? 0 : count; + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java new file mode 100644 index 00000000..2bb9c097 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java @@ -0,0 +1,40 @@ +package ddingdong.ddingdongBE.domain.form.service.dto.query; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import java.util.List; +import lombok.Builder; + +@Builder +public record FormStatisticsQuery( + int totalCount, + List departmentRanks, + List applicantRates, + List fields +) { + + public record DepartmentRankQuery( + int rank, + String label, + int count, + int rate + ) { + } + + public record ApplicantRateQuery( + String label, + int count, + int comparedToLastSemester + ) { + + + } + + public record FieldStatisticsListQuery( + Long id, + String question, + int count, + FieldType fieldType + ) { + + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java new file mode 100644 index 00000000..9fdfe897 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java @@ -0,0 +1,50 @@ +package ddingdong.ddingdongBE.domain.form.service.vo; + +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import java.util.ArrayList; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ApplicationRates { + + @Getter + @Builder + public static class ApplicationRate { + private String label; + private int count; + private int comparedToLastSemester; + + public void updateComparedToLastSemester(int comparedToLastSemester) { + this.comparedToLastSemester = comparedToLastSemester; + } + + public ApplicantRateQuery toQuery() { + return new ApplicantRateQuery(label, count, comparedToLastSemester); + } + } + + private final List applicationRates = new ArrayList<>(); + private static final int DEFAULT_APPLICATION_RATE = 100; + + + public void add(String label, int count, int comparedToLastSemester) { + if (applicationRates.isEmpty()) { + applicationRates.add(new ApplicationRate(label, count, comparedToLastSemester)); + return; + } + ApplicationRate lastSemester = applicationRates.get(0); + lastSemester.updateComparedToLastSemester(comparedToLastSemester); + applicationRates.add(0, new ApplicationRate(label, count, DEFAULT_APPLICATION_RATE)); + } + + public ApplicationRate getPrevious() { + if (applicationRates.isEmpty()) { + return null; + } + return applicationRates.get(0); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java new file mode 100644 index 00000000..1a1256f7 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java @@ -0,0 +1,49 @@ +package ddingdong.ddingdongBE.domain.form.service.vo; + +import ddingdong.ddingdongBE.common.utils.TimeUtils; +import java.time.LocalDate; + +public class HalfYear { + + private int year; + private boolean isFirstHalf; + + private HalfYear(int year, boolean isFirstHalf) { + this.year = year; + this.isFirstHalf = isFirstHalf; + } + + public static HalfYear from(LocalDate date) { + return new HalfYear(date.getYear(), TimeUtils.isFirstHalf(date.getMonthValue())); + } + + public void minusHalves() { + if (isFirstHalf) { + year = year - 1; + isFirstHalf = false; + return; + } + isFirstHalf = true; + } + + public LocalDate getHalfStartDate() { + if(isFirstHalf) { + return LocalDate.of(year, 1, 1); + } + return LocalDate.of(year, 7, 1); + } + + public LocalDate getHalfEndDate() { + if(isFirstHalf) { + return LocalDate.of(year, 6, 30); + } + return LocalDate.of(year, 12, 31); + } + + public String getLabel() { + if (isFirstHalf) { + return year + "-1"; + } + return year + "-2"; + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormAnswerRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormAnswerRepository.java index cad0ce6c..3db574f0 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormAnswerRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormAnswerRepository.java @@ -1,7 +1,10 @@ package ddingdong.ddingdongBE.domain.formapplication.repository; +import ddingdong.ddingdongBE.domain.form.entity.FormField; import ddingdong.ddingdongBE.domain.formapplication.entity.FormAnswer; import org.springframework.data.jpa.repository.JpaRepository; public interface FormAnswerRepository extends JpaRepository { + + int countByFormField(FormField formField); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java index 131f0367..88ec6dea 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java @@ -1,11 +1,17 @@ package ddingdong.ddingdongBE.domain.formapplication.repository; +import ddingdong.ddingdongBE.domain.form.entity.Form; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo; +import java.time.LocalDate; +import java.util.List; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +@Repository public interface FormApplicationRepository extends JpaRepository { @Query(value = """ select * @@ -21,4 +27,29 @@ Slice findPageByFormIdOrderById( @Param("currentCursorId") Long currentCursorId ); + Long countByForm(Form form); + + @Query(value = """ + SELECT f.department, COUNT(f.id) AS count + FROM form_application f + WHERE f.form_id = :formId + GROUP BY f.department + ORDER BY count DESC + LIMIT 5 + """, nativeQuery = true) + List findTopFiveDepartmentsByForm(@Param("formId") Long formId); + + @Query(value = """ + SELECT COUNT(fa.id) AS count + FROM form_application fa + JOIN form f ON fa.form_id = f.id + WHERE f.end_date BETWEEN :startDate AND :endDate + GROUP BY f.id + ORDER BY count DESC + LIMIT 1 + """, nativeQuery = true) + Integer findMaxApplicationCountByDateRange( + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate + ); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/DepartmentInfo.java b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/DepartmentInfo.java new file mode 100644 index 00000000..1c29fae4 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/DepartmentInfo.java @@ -0,0 +1,8 @@ +package ddingdong.ddingdongBE.domain.formapplication.repository.dto; + +public interface DepartmentInfo { + + String getDepartment(); + + Integer getCount(); +} From 2ecdf49d8571fd6f53f9b9db99dd466f17c66ece Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:34:35 +0900 Subject: [PATCH 07/14] =?UTF-8?q?test=20:=20=ED=86=B5=EA=B3=84=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=EC=A1=B0=ED=9A=8C=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FormApplicationRepositoryTest.java | 136 ++++++++- .../service/FormStatisticServiceImplTest.java | 264 ++++++++++++++++++ 2 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java index bee1c0b0..d620df07 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java @@ -1,11 +1,14 @@ package ddingdong.ddingdongBE.domain.form.repository; +import static org.assertj.core.api.Assertions.assertThat; + import com.navercorp.fixturemonkey.FixtureMonkey; import ddingdong.ddingdongBE.common.support.DataJpaTestSupport; import ddingdong.ddingdongBE.common.support.FixtureMonkeyFactory; import ddingdong.ddingdongBE.domain.club.entity.Club; import ddingdong.ddingdongBE.domain.club.repository.ClubRepository; import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus; import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; @@ -13,18 +16,15 @@ import ddingdong.ddingdongBE.domain.user.entity.Role; import ddingdong.ddingdongBE.domain.user.entity.User; import ddingdong.ddingdongBE.domain.user.repository.UserRepository; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Slice; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - class FormApplicationRepositoryTest extends DataJpaTestSupport { @Autowired @@ -149,4 +149,128 @@ void findPageByFormIdOrderById() { assertThat(retrievedApplications.size()).isEqualTo(2); assertThat(retrievedApplications.get(0).getId()).isGreaterThan(retrievedApplications.get(1).getId()); } + + + @DisplayName("지원자 수 상위 5개 학과와 그 개수를 반환한다.") + @Test + void findTopFiveDepartmentsByForm_ShouldReturnTopFiveDepartments() throws InterruptedException { + // given + Form form = fixture.giveMeBuilder(Form.class) + .set("club", null) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(form); + FormApplication formApplication = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication2 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication3 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication4 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication5 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication6 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + + formApplicationRepository.saveAll( + List.of(formApplication, formApplication2, formApplication3, formApplication4, formApplication5, + formApplication6) + ); + // when + List topFive = formApplicationRepository.findTopFiveDepartmentsByForm(savedForm.getId()); + // then + + assertThat(topFive.size()).isEqualTo(3); + assertThat(topFive.get(0).getCount()).isEqualTo(3); + assertThat(topFive.get(0).getDepartment()).isEqualTo("학과3"); + assertThat(topFive.get(1).getCount()).isEqualTo(2); + assertThat(topFive.get(1).getDepartment()).isEqualTo("학과2"); + assertThat(topFive.get(2).getCount()).isEqualTo(1); + assertThat(topFive.get(2).getDepartment()).isEqualTo("학과1"); + } + + @DisplayName("해당 기간 내에 가장 지원자 수가 많았던 폼지의 지원자수를 반환한다.") + @Test + void findMaxApplicationCountByDateRange_ShouldReturnHighestCount() { + // given + Form form = fixture.giveMeBuilder(Form.class) + .set("id", 1L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 3, 1)) + .set("endDate", LocalDate.of(2020, 4, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(form); + FormApplication formApplication = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication2 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + + formApplicationRepository.saveAll(List.of(formApplication, formApplication2)); + + Form form2 = fixture.giveMeBuilder(Form.class) + .set("id", 2L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 1, 1)) + .set("endDate", LocalDate.of(2020, 2, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm2 = formRepository.save(form2); + FormApplication formApplication3 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm2) + .build(); + formApplicationRepository.save(formApplication3); + + LocalDate startDate = LocalDate.of(2020, 1, 1); + LocalDate endDate = LocalDate.of(2020, 6, 30); + // when + int count = formApplicationRepository.findMaxApplicationCountByDateRange(startDate, endDate); + // then + assertThat(count).isEqualTo(2); + } } diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java new file mode 100644 index 00000000..5753752f --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java @@ -0,0 +1,264 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.navercorp.fixturemonkey.FixtureMonkey; +import ddingdong.ddingdongBE.common.support.FixtureMonkeyFactory; +import ddingdong.ddingdongBE.common.support.TestContainerSupport; +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.repository.FormFieldRepository; +import ddingdong.ddingdongBE.domain.form.repository.FormRepository; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.formapplication.entity.FormAnswer; +import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; +import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus; +import ddingdong.ddingdongBE.domain.formapplication.repository.FormAnswerRepository; +import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class FormStatisticServiceImplTest extends TestContainerSupport { + + @Autowired + private FormApplicationRepository formApplicationRepository; + + @Autowired + private FormStatisticService formStatisticService; + + @Autowired + private FormRepository formRepository; + + @Autowired + private FormFieldRepository formFieldRepository; + + @Autowired + private FormAnswerRepository formAnswerRepository; + + private final FixtureMonkey fixture = FixtureMonkeyFactory.getNotNullBuilderIntrospectorMonkey(); + + @DisplayName("폼지에 지원한 총 지원자 수를 반환한다.") + @Test + void getTotalApplicationCountByForm() { + // given + Form form = fixture.giveMeBuilder(Form.class) + .set("club", null) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(form); + FormApplication formApplication = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication2 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication3 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + formApplicationRepository.saveAll(List.of(formApplication, formApplication2, formApplication3)); + // when + int count = formStatisticService.getTotalApplicationCountByForm(savedForm); + // then + assertThat(count).isEqualTo(3); + } + + @DisplayName("폼지 내 지원자 수가 높은 학과 순으로 관련 정보를 정해진 수만큼 반환한다.") + @Test + void createDepartmentRankByForm() { + // given + Form form = fixture.giveMeBuilder(Form.class) + .set("club", null) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(form); + FormApplication formApplication = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication2 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication3 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과2") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication4 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication5 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication6 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과3") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + + formApplicationRepository.saveAll( + List.of(formApplication, formApplication2, formApplication3, formApplication4, formApplication5, + formApplication6) + ); + // when + List departmentRanks = formStatisticService.createDepartmentRankByForm(savedForm); + // then + assertThat(departmentRanks.size()).isEqualTo(3); + assertThat(departmentRanks.get(0).rank()).isEqualTo(1); + assertThat(departmentRanks.get(0).label()).isEqualTo("학과3"); + assertThat(departmentRanks.get(0).count()).isEqualTo(3); + assertThat(departmentRanks.get(1).rank()).isEqualTo(2); + assertThat(departmentRanks.get(1).label()).isEqualTo("학과2"); + assertThat(departmentRanks.get(1).count()).isEqualTo(2); + assertThat(departmentRanks.get(2).rank()).isEqualTo(3); + assertThat(departmentRanks.get(2).label()).isEqualTo("학과1"); + assertThat(departmentRanks.get(2).count()).isEqualTo(1); + + } + + @DisplayName("전 반기에 대한 지원 비율 정보를 정해진 개수만큼 반환한다") + @Test + void createApplicationRateByForm() { + // given + Form formFirst = fixture.giveMeBuilder(Form.class) + .set("id", 1L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 2, 1)) + .set("endDate", LocalDate.of(2020, 3, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(formFirst); + FormApplication formApplication = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + FormApplication formApplication2 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm) + .build(); + + formApplicationRepository.saveAll(List.of(formApplication, formApplication2)); + + Form form2 = fixture.giveMeBuilder(Form.class) + .set("id", 2L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 7, 3)) + .set("endDate", LocalDate.of(2020, 8, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm2 = formRepository.save(form2); + FormApplication formApplication3 = FormApplication.builder() + .name("이름1") + .studentNumber("학번1") + .department("학과1") + .status(FormApplicationStatus.SUBMITTED) + .form(savedForm2) + .build(); + formApplicationRepository.save(formApplication3); + // when + List applicationRateQueries = formStatisticService.createApplicationRateByForm(savedForm2); + // then + + assertThat(applicationRateQueries.size()).isEqualTo(2); + assertThat(applicationRateQueries.get(0).label()).isEqualTo("2020-1"); + assertThat(applicationRateQueries.get(0).count()).isEqualTo(2); + assertThat(applicationRateQueries.get(0).comparedToLastSemester()).isEqualTo(100); + assertThat(applicationRateQueries.get(1).label()).isEqualTo("2020-2"); + assertThat(applicationRateQueries.get(1).count()).isEqualTo(1); + assertThat(applicationRateQueries.get(1).comparedToLastSemester()).isEqualTo(50); + + + } + + @DisplayName("폼지의 질문 정보") + @Test + void createFieldStatisticsListByForm() { + // given + Form formFirst = fixture.giveMeBuilder(Form.class) + .set("id", 1L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 2, 1)) + .set("endDate", LocalDate.of(2020, 3, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(formFirst); + FormField formField = FormField.builder() + .question("설문 질문") + .required(true) + .fieldOrder(1) + .section("기본 정보") + .options(List.of("옵션1", "옵션2", "옵션3")) + .fieldType(FieldType.TEXT) + .form(savedForm) + .build(); + FormField savedField = formFieldRepository.save(formField); + + FormAnswer answer = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + FormAnswer answer2 = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + FormAnswer answer3 = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + formAnswerRepository.saveAll(List.of(answer, answer2, answer3)); + // when + List fieldStatisticsList = formStatisticService.createFieldStatisticsListByForm( + savedForm); + // then + assertThat(fieldStatisticsList.size()).isEqualTo(1); + assertThat(fieldStatisticsList.get(0).count()).isEqualTo(3); + assertThat(fieldStatisticsList.get(0).question()).isEqualTo("설문 질문"); + } +} From 32577d967c9268347850eb7d186bf9a6509eee90 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:34:44 +0900 Subject: [PATCH 08/14] =?UTF-8?q?test=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/support/FixtureMonkeyFactory.java | 23 ++++++++++++++++--- .../common/support/TestContainerSupport.java | 2 -- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/test/java/ddingdong/ddingdongBE/common/support/FixtureMonkeyFactory.java b/src/test/java/ddingdong/ddingdongBE/common/support/FixtureMonkeyFactory.java index 69d96838..8b7c1008 100644 --- a/src/test/java/ddingdong/ddingdongBE/common/support/FixtureMonkeyFactory.java +++ b/src/test/java/ddingdong/ddingdongBE/common/support/FixtureMonkeyFactory.java @@ -1,7 +1,12 @@ package ddingdong.ddingdongBE.common.support; import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.generator.ArbitraryGeneratorContext; import com.navercorp.fixturemonkey.api.introspector.BuilderArbitraryIntrospector; +import com.navercorp.fixturemonkey.api.jqwik.JavaArbitraryResolver; +import com.navercorp.fixturemonkey.api.jqwik.JqwikPlugin; +import net.jqwik.api.Arbitrary; +import net.jqwik.api.arbitraries.StringArbitrary; public class FixtureMonkeyFactory { @@ -13,9 +18,21 @@ public static FixtureMonkey getBuilderIntrospectorMonkey() { public static FixtureMonkey getNotNullBuilderIntrospectorMonkey() { return FixtureMonkey.builder() - .objectIntrospector(BuilderArbitraryIntrospector.INSTANCE) - .defaultNotNull(true) - .build(); + .objectIntrospector(BuilderArbitraryIntrospector.INSTANCE) + .defaultNotNull(true) + .plugin( + new JqwikPlugin() + .javaArbitraryResolver(new JavaArbitraryResolver() { + @Override + public Arbitrary strings(StringArbitrary stringArbitrary, ArbitraryGeneratorContext context) { +// if (context.findAnnotation(MaxOfLength.class).isPresent()) { +// return stringArbitrary.ofMaxLength(10); +// } + return stringArbitrary; + } + }) + ) + .build(); } } diff --git a/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java b/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java index facbfa53..5db5d9c2 100644 --- a/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java +++ b/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java @@ -11,7 +11,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; -import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MySQLContainer; @@ -19,7 +18,6 @@ @NoArgsConstructor(access = PROTECTED) @ActiveProfiles("test") @Import(TestConfig.class) -@Transactional public abstract class TestContainerSupport { private static final String MYSQL_IMAGE = "mysql:8"; From b933a6c178608eab8c593820b2ad22744df78182 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:35:18 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix=20:=20flyway=20sql=EB=AC=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/migration/V36__form_add_column_sections.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V36__form_add_column_sections.sql b/src/main/resources/db/migration/V36__form_add_column_sections.sql index 6fcbfa2d..da2721a9 100644 --- a/src/main/resources/db/migration/V36__form_add_column_sections.sql +++ b/src/main/resources/db/migration/V36__form_add_column_sections.sql @@ -1 +1 @@ -ALTER TABLE form ADD COLUMN sections TEXT NOT NULL DEFAULT '[]'; +ALTER TABLE form ADD COLUMN sections VARCHAR(255) NOT NULL DEFAULT '[]'; From f8645c4e83d54504f4b913a11c51dae46b27331e Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:40:17 +0900 Subject: [PATCH 10/14] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=ED=95=84=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/form/service/FormStatisticServiceImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java index d1af9332..84359e5a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java @@ -26,7 +26,6 @@ public class FormStatisticServiceImpl implements FormStatisticService { private static final int APPLICATION_COMPARE_COUNT = 5; - private static final int FIRST_APPLICATION = 0; private static final int NOT_EXIST_APPLICATION = 0; private static final int DEFAULT_APPLICATION_RATE = 100; From 535e1fe35898f6a47b9775b113de96979399c401 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Sun, 9 Feb 2025 06:40:46 +0900 Subject: [PATCH 11/14] =?UTF-8?q?feat=20:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/CalculationUtils.java | 15 ++- .../ddingdongBE/common/utils/TimeUtils.java | 4 +- .../domain/form/api/CentralFormApi.java | 5 +- .../controller/CentralFormController.java | 8 +- .../dto/response/FormStatisticsResponse.java | 113 ++++++++++-------- .../form/repository/FormFieldRepository.java | 17 ++- .../form/repository/dto/FieldListInfo.java | 16 +++ .../service/FacadeCentralFormService.java | 2 +- .../service/FacadeCentralFormServiceImpl.java | 18 +-- .../form/service/FormStatisticService.java | 14 ++- .../service/FormStatisticServiceImpl.java | 93 +++++++------- .../dto/query/FormStatisticsQuery.java | 43 ++++--- .../form/service/vo/ApplicationRates.java | 50 -------- .../domain/form/service/vo/HalfYear.java | 49 -------- .../repository/FormApplicationRepository.java | 34 ++++-- .../repository/dto/RecentFormInfo.java | 9 ++ 16 files changed, 250 insertions(+), 240 deletions(-) create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/repository/dto/FieldListInfo.java delete mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java delete mode 100644 src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/RecentFormInfo.java diff --git a/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java b/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java index e3f52fe5..1fa48348 100644 --- a/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java +++ b/src/main/java/ddingdong/ddingdongBE/common/utils/CalculationUtils.java @@ -2,10 +2,19 @@ public class CalculationUtils { - public static int calculateRate(int count, int totalCount) { - if (totalCount == 0) { + public static int calculateRatio(int numerator, int denominator) { + if (denominator == 0) { return 0; } - return (int) ((double) count / totalCount * 100); + return (int) ((double) numerator / denominator * 100); + } + + public static int calculateDifference(int beforeCount, int count) { + return count - beforeCount; + } + + public static int calculateDifferenceRatio(int beforeCount, int count) { + int difference = calculateDifference(beforeCount, count); + return calculateRatio(difference, beforeCount); } } diff --git a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java index 292788ce..87750247 100644 --- a/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java +++ b/src/main/java/ddingdong/ddingdongBE/common/utils/TimeUtils.java @@ -35,7 +35,7 @@ public static boolean isDateInRange(LocalDate nowDate, LocalDate startDate, Loca return !nowDate.isBefore(startDate) && !nowDate.isAfter(endDate); } - public static boolean isFirstHalf(int month) { - return month >= 1 && month <= 6; + public static String getYearAndMonth(LocalDate date) { + return date.getYear() + "-" + date.getMonthValue(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java index 6db576aa..113b2723 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java @@ -85,5 +85,8 @@ void deleteForm( @ResponseStatus(HttpStatus.OK) @SecurityRequirement(name = "AccessToken") @GetMapping("/my/forms/{formId}/statistics") - FormStatisticsResponse getFormStatistics(@PathVariable("formId") Long formId); + FormStatisticsResponse getFormStatistics( + @PathVariable("formId") Long formId, + @AuthenticationPrincipal PrincipalDetails principalDetails + ); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java index 24443b1c..1915c473 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java @@ -62,8 +62,12 @@ public FormResponse getForm(Long formId) { } @Override - public FormStatisticsResponse getFormStatistics(Long formId) { - FormStatisticsQuery query = facadeCentralFormService.getStatisticsByForm(formId); + public FormStatisticsResponse getFormStatistics( + Long formId, + PrincipalDetails principalDetails + ) { + User user = principalDetails.getUser(); + FormStatisticsQuery query = facadeCentralFormService.getStatisticsByForm(user, formId); return FormStatisticsResponse.from(query); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java index eb917815..b164231d 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/response/FormStatisticsResponse.java @@ -2,9 +2,10 @@ import ddingdong.ddingdongBE.domain.form.entity.FieldType; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery.FieldStatisticsListQuery; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -14,16 +15,16 @@ public record FormStatisticsResponse( @Schema(description = "총 지원자 수", example = "50") int totalCount, - @ArraySchema(schema = @Schema(implementation = DepartmentRankResponse.class)) - List departmentRanks, - @ArraySchema(schema = @Schema(implementation = ApplicantRateResponse.class)) - List applicantRates, - @ArraySchema(schema = @Schema(implementation = FieldStatisticsListResponse.class)) - List fields + @ArraySchema(schema = @Schema(implementation = DepartmentStatisticResponse.class)) + List departmentStatistics, + @ArraySchema(schema = @Schema(implementation = ApplicantStatisticResponse.class)) + List applicantStatistics, + @Schema(description = "필드 통계 전체조회", implementation = FieldStatisticsResponse.class) + FieldStatisticsResponse fieldStatistics ) { @Builder - record DepartmentRankResponse( + record DepartmentStatisticResponse( @Schema(description = "학과 내 경쟁 순위", example = "1") int rank, @Schema(description = "학과명", example = "융합소프트웨어학부") @@ -31,75 +32,93 @@ record DepartmentRankResponse( @Schema(description = "해당 학과의 지원자 수", example = "50") int count, @Schema(description = "전체 지원자 수 대비 비율", example = "30") - int rate + int ratio ) { - public static DepartmentRankResponse from(DepartmentRankQuery query) { - return DepartmentRankResponse.builder() + public static DepartmentStatisticResponse from(DepartmentStatisticQuery query) { + return DepartmentStatisticResponse.builder() .rank(query.rank()) .label(query.label()) .count(query.count()) - .rate(query.rate()) + .ratio(query.ratio()) .build(); } } @Builder - record ApplicantRateResponse( - @Schema(description = "비교 년도 및 학기", example = "2025-1") + record ApplicantStatisticResponse( + @Schema(description = "비교 년도 및 월", example = "2025-1") String label, @Schema(description = "해당 년도 및 학기 총 지원자수", example = "40") int count, - @Schema(description = "비교 대비 비율", example = "150") - int comparedToLastSemester + @Schema(description = "전 폼지 대비 증감 값", example = "150") + CompareToBefore comparedToBefore ) { - public static ApplicantRateResponse from(ApplicantRateQuery query) { - return ApplicantRateResponse.builder() + record CompareToBefore( + @Schema(description = "증감율 %", example = "50") + int ratio, + @Schema(description = "증가수치 및 감소수치", example = "15") + int value + ) { + + } + public static ApplicantStatisticResponse from(ApplicantStatisticQuery query) { + return ApplicantStatisticResponse.builder() .label(query.label()) .count(query.count()) - .comparedToLastSemester(query.comparedToLastSemester()) + .comparedToBefore(new CompareToBefore(query.compareRatio(), query.compareValue())) .build(); } - } @Builder - record FieldStatisticsListResponse( - @Schema(description = "폼지 질문 id", example = "1") - Long id, - @Schema(description = "폼지 질문", example = "당신 이름은 무엇인가요?") - String question, - @Schema(description = "폼지 질문에 대해 총 작성 개수", example = "20") - int count, - @Schema(description = "폼지 질문 유형", example = "CHECK_BOX") - FieldType type + record FieldStatisticsResponse( + @Schema(description = "섹션종류", example = "[\"공통\"]") + List sections, + @ArraySchema(schema = @Schema(implementation = FieldStatisticsListResponse.class)) + List fields ) { + record FieldStatisticsListResponse( + @Schema(description = "폼지 질문 id", example = "1") + Long id, + @Schema(description = "폼지 질문", example = "당신 이름은 무엇인가요?") + String question, + @Schema(description = "폼지 질문에 대해 총 작성 개수", example = "20") + int count, + @Schema(description = "폼지 질문 유형", example = "CHECK_BOX") + FieldType type, + @Schema(description = "섹션", example = "공통") + String section + ) { + public static FieldStatisticsListResponse from(FieldStatisticsListQuery query) { + return new FieldStatisticsListResponse(query.id(), query.question(), query.count(), query.fieldType(), + query.section()); + } + } - public static FieldStatisticsListResponse from(FieldStatisticsListQuery query) { - return FieldStatisticsListResponse.builder() - .id(query.id()) - .question(query.question()) - .count(query.count()) - .type(query.fieldType()) + public static FieldStatisticsResponse from(FieldStatisticsQuery query) { + List fieldStatisticsListResponses = query.fieldStatisticsListQueries().stream() + .map(FieldStatisticsListResponse::from) + .toList(); + return FieldStatisticsResponse.builder() + .sections(query.sections()) + .fields(fieldStatisticsListResponses) .build(); } } public static FormStatisticsResponse from(FormStatisticsQuery query) { - List departmentRankResponses = query.departmentRanks().stream() - .map(DepartmentRankResponse::from) - .toList(); - List applicantRateResponses = query.applicantRates().stream() - .map(ApplicantRateResponse::from) + List departmentStatisticResponse = query.departmentStatisticQueries().stream() + .map(DepartmentStatisticResponse::from) .toList(); - List fieldStatisticsListResponses = query.fields().stream() - .map(FieldStatisticsListResponse::from) + List applicantStatisticResponse = query.applicantStatisticQueries().stream() + .map(ApplicantStatisticResponse::from) .toList(); return FormStatisticsResponse.builder() .totalCount(query.totalCount()) - .departmentRanks(departmentRankResponses) - .applicantRates(applicantRateResponses) - .fields(fieldStatisticsListResponses) + .departmentStatistics(departmentStatisticResponse) + .applicantStatistics(applicantStatisticResponse) + .fieldStatistics(FieldStatisticsResponse.from(query.fieldStatisticsQuery())) .build(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java index bab31349..dcce8133 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepository.java @@ -2,12 +2,27 @@ import ddingdong.ddingdongBE.domain.form.entity.Form; import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.repository.dto.FieldListInfo; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface FormFieldRepository extends JpaRepository { List findAllByForm(Form form); - List findAllByFormAndSection(Form form, String section); + @Query(value = """ + SELECT f.id AS id, f.question AS question, f.field_type AS type, f.section AS section, COUNT(fa.id) AS count + FROM ( + SELECT * + FROM form_field field + WHERE field.form_id = :formId + ) AS f + LEFT JOIN form_answer AS fa + ON fa.field_id = f.id + GROUP BY f.id + ORDER BY f.id + """, nativeQuery = true) + List findFieldWithAnswerCountByFormId(@Param("formId") Long formId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/repository/dto/FieldListInfo.java b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/dto/FieldListInfo.java new file mode 100644 index 00000000..8d07d082 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/repository/dto/FieldListInfo.java @@ -0,0 +1,16 @@ +package ddingdong.ddingdongBE.domain.form.repository.dto; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; + +public interface FieldListInfo { + + Long getId(); + + String getQuestion(); + + Integer getCount(); + + FieldType getType(); + + String getSection(); +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java index 55460ec9..f95020d5 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java @@ -20,5 +20,5 @@ public interface FacadeCentralFormService { FormQuery getForm(Long formId); - FormStatisticsQuery getStatisticsByForm(Long formId); + FormStatisticsQuery getStatisticsByForm(User user, Long formId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java index 4321e025..b6979e5f 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -13,9 +13,9 @@ import ddingdong.ddingdongBE.domain.form.service.dto.query.FormListQuery; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormQuery; import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery; import ddingdong.ddingdongBE.domain.user.entity.User; import java.time.LocalDate; import java.util.List; @@ -85,13 +85,15 @@ public FormQuery getForm(Long formId) { } @Override - public FormStatisticsQuery getStatisticsByForm(Long formId) { + public FormStatisticsQuery getStatisticsByForm(User user, Long formId) { + Club club = clubService.getByUserId(user.getId()); Form form = formService.getById(formId); int totalCount = formStatisTicService.getTotalApplicationCountByForm(form); - List departmentRankQueries = formStatisTicService.createDepartmentRankByForm(form); - List applicantRateQueries = formStatisTicService.createApplicationRateByForm(form); - List fieldStatisticsListQueries = formStatisTicService.createFieldStatisticsListByForm(form); - return new FormStatisticsQuery(totalCount, departmentRankQueries, applicantRateQueries, fieldStatisticsListQueries); + List departmentStatisticQueries = formStatisTicService.createDepartmentStatistics(totalCount, form); + List applicantStatisticQueries = formStatisTicService.createApplicationStatistics(club, form); + FieldStatisticsQuery fieldStatisticsQuery = formStatisTicService.createFieldStatisticsByForm(form); + + return new FormStatisticsQuery(totalCount, departmentStatisticQueries, applicantStatisticQueries, fieldStatisticsQuery); } private FormListQuery buildFormListQuery(Form form) { diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java index b0e329f9..21e7ad0c 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticService.java @@ -1,18 +1,20 @@ package ddingdong.ddingdongBE.domain.form.service; +import ddingdong.ddingdongBE.domain.club.entity.Club; import ddingdong.ddingdongBE.domain.form.entity.Form; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery; import java.util.List; public interface FormStatisticService { int getTotalApplicationCountByForm(Form form); - List createDepartmentRankByForm(Form form); + List createDepartmentStatistics(int totalCount, Form form); - List createApplicationRateByForm(Form form); + List createApplicationStatistics(Club club, Form form); + + FieldStatisticsQuery createFieldStatisticsByForm(Form form); - List createFieldStatisticsListByForm(Form form); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java index 84359e5a..a74837d0 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java @@ -1,19 +1,20 @@ package ddingdong.ddingdongBE.domain.form.service; import ddingdong.ddingdongBE.common.utils.CalculationUtils; +import ddingdong.ddingdongBE.common.utils.TimeUtils; +import ddingdong.ddingdongBE.domain.club.entity.Club; import ddingdong.ddingdongBE.domain.form.entity.Form; -import ddingdong.ddingdongBE.domain.form.entity.FormField; import ddingdong.ddingdongBE.domain.form.repository.FormFieldRepository; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; -import ddingdong.ddingdongBE.domain.form.service.vo.ApplicationRates; -import ddingdong.ddingdongBE.domain.form.service.vo.ApplicationRates.ApplicationRate; -import ddingdong.ddingdongBE.domain.form.service.vo.HalfYear; +import ddingdong.ddingdongBE.domain.form.repository.dto.FieldListInfo; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery.FieldStatisticsListQuery; import ddingdong.ddingdongBE.domain.formapplication.repository.FormAnswerRepository; import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo; -import java.util.ArrayList; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.RecentFormInfo; +import java.time.LocalDate; import java.util.List; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; @@ -25,8 +26,8 @@ @Transactional(readOnly = true) public class FormStatisticServiceImpl implements FormStatisticService { - private static final int APPLICATION_COMPARE_COUNT = 5; - private static final int NOT_EXIST_APPLICATION = 0; + private static final int DEPARTMENT_INFORMATION_SIZE = 5; + private static final int APPLICANT_RATIO_INFORMATION_SIZE = 3; private static final int DEFAULT_APPLICATION_RATE = 100; private final FormApplicationRepository formApplicationRepository; @@ -39,9 +40,11 @@ public int getTotalApplicationCountByForm(Form form) { } @Override - public List createDepartmentRankByForm(Form form) { - List departmentInfos = formApplicationRepository.findTopFiveDepartmentsByForm(form.getId()); - int totalCount = getTotalApplicationCountByForm(form); + public List createDepartmentStatistics(int totalCount, Form form) { + List departmentInfos = formApplicationRepository.findTopDepartmentsByFormId( + form.getId(), + DEPARTMENT_INFORMATION_SIZE + ); return IntStream.range(0, departmentInfos.size()) .mapToObj(index -> { @@ -49,48 +52,50 @@ public List createDepartmentRankByForm(Form form) { int rank = index + 1; String department = departmentInfo.getDepartment(); int count = parseToInt(departmentInfo.getCount()); - int rate = CalculationUtils.calculateRate(count, totalCount); - return new DepartmentRankQuery(rank, department, count, rate); + int ratio = CalculationUtils.calculateRatio(count, totalCount); + return new DepartmentStatisticQuery(rank, department, count, ratio); }) .toList(); } @Override - public List createApplicationRateByForm(Form form) { - List results = new ArrayList<>(); + public List createApplicationStatistics(Club club, Form form) { + LocalDate endDate = form.getEndDate(); + List recentForms = formApplicationRepository.findRecentFormByDateWithApplicationCount( + club.getId(), + endDate, + APPLICANT_RATIO_INFORMATION_SIZE + ); - HalfYear halfYear = HalfYear.from(form.getEndDate()); - ApplicationRates applicationRates = new ApplicationRates(); - for (int count = 0; count < APPLICATION_COMPARE_COUNT; count++) { - int maxCount = parseToInt(formApplicationRepository.findMaxApplicationCountByDateRange( - halfYear.getHalfStartDate(), - halfYear.getHalfEndDate() - )); - if (maxCount == NOT_EXIST_APPLICATION) { - break; - } - int rate = (applicationRates.getPrevious() == null) ? DEFAULT_APPLICATION_RATE : - CalculationUtils.calculateRate(maxCount, applicationRates.getPrevious().getCount()); - applicationRates.add(halfYear.getLabel(), maxCount, rate); - halfYear.minusHalves(); - } - return applicationRates.getApplicationRates().stream() - .map(ApplicationRate::toQuery) - .toList(); + return IntStream.range(0, recentForms.size()) + .mapToObj(index -> { + RecentFormInfo recentFormInfo = recentForms.get(index); + + String label = TimeUtils.getYearAndMonth(recentFormInfo.getDate()); + int count = parseToInt(recentFormInfo.getCount()); + if (index == 0) { + return new ApplicantStatisticQuery(label, count, 0, 0); + } + int beforeCount = parseToInt(recentForms.get(index - 1).getCount()); + int compareRatio = CalculationUtils.calculateDifferenceRatio(beforeCount, count); + int compareValue = CalculationUtils.calculateDifference(beforeCount, count); + + return new ApplicantStatisticQuery(label, count, compareRatio, compareValue); + }).toList(); } @Override - public List createFieldStatisticsListByForm(Form form) { - List formFields = formFieldRepository.findAllByForm(form); - return formFields.stream() - .map(this::buildFieldStatisticsList) - .toList(); + public FieldStatisticsQuery createFieldStatisticsByForm(Form form) { + List sections = form.getSections(); + List fieldListInfos = formFieldRepository.findFieldWithAnswerCountByFormId(form.getId()); + List fieldStatisticsListQueries = toFieldListQueries(fieldListInfos); + return new FieldStatisticsQuery(sections, fieldStatisticsListQueries); } - private FieldStatisticsListQuery buildFieldStatisticsList(FormField formField) { - int answerCount = formAnswerRepository.countByFormField(formField); - return new FieldStatisticsListQuery(formField.getId(), formField.getQuestion(), answerCount, - formField.getFieldType()); + private List toFieldListQueries(List fieldListInfos) { + return fieldListInfos.stream() + .map(FieldStatisticsListQuery::from) + .toList(); } private int parseToInt(Integer count) { diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java index 2bb9c097..353e0021 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/query/FormStatisticsQuery.java @@ -1,40 +1,55 @@ package ddingdong.ddingdongBE.domain.form.service.dto.query; import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.repository.dto.FieldListInfo; import java.util.List; import lombok.Builder; @Builder public record FormStatisticsQuery( int totalCount, - List departmentRanks, - List applicantRates, - List fields + List departmentStatisticQueries, + List applicantStatisticQueries, + FieldStatisticsQuery fieldStatisticsQuery ) { - public record DepartmentRankQuery( + public record DepartmentStatisticQuery( int rank, String label, int count, - int rate + int ratio ) { } - public record ApplicantRateQuery( + public record ApplicantStatisticQuery( String label, int count, - int comparedToLastSemester + int compareRatio, + int compareValue ) { - - } - public record FieldStatisticsListQuery( - Long id, - String question, - int count, - FieldType fieldType + public record FieldStatisticsQuery( + List sections, + List fieldStatisticsListQueries ) { + public record FieldStatisticsListQuery( + Long id, + String question, + int count, + FieldType fieldType, + String section + ) { + public static FieldStatisticsListQuery from(FieldListInfo fieldListInfo) { + return new FieldStatisticsListQuery( + fieldListInfo.getId(), + fieldListInfo.getQuestion(), + fieldListInfo.getCount(), + fieldListInfo.getType(), + fieldListInfo.getSection() + ); + } + } } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java deleted file mode 100644 index 9fdfe897..00000000 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/ApplicationRates.java +++ /dev/null @@ -1,50 +0,0 @@ -package ddingdong.ddingdongBE.domain.form.service.vo; - -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import java.util.ArrayList; -import java.util.List; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -public class ApplicationRates { - - @Getter - @Builder - public static class ApplicationRate { - private String label; - private int count; - private int comparedToLastSemester; - - public void updateComparedToLastSemester(int comparedToLastSemester) { - this.comparedToLastSemester = comparedToLastSemester; - } - - public ApplicantRateQuery toQuery() { - return new ApplicantRateQuery(label, count, comparedToLastSemester); - } - } - - private final List applicationRates = new ArrayList<>(); - private static final int DEFAULT_APPLICATION_RATE = 100; - - - public void add(String label, int count, int comparedToLastSemester) { - if (applicationRates.isEmpty()) { - applicationRates.add(new ApplicationRate(label, count, comparedToLastSemester)); - return; - } - ApplicationRate lastSemester = applicationRates.get(0); - lastSemester.updateComparedToLastSemester(comparedToLastSemester); - applicationRates.add(0, new ApplicationRate(label, count, DEFAULT_APPLICATION_RATE)); - } - - public ApplicationRate getPrevious() { - if (applicationRates.isEmpty()) { - return null; - } - return applicationRates.get(0); - } -} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java deleted file mode 100644 index 1a1256f7..00000000 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/vo/HalfYear.java +++ /dev/null @@ -1,49 +0,0 @@ -package ddingdong.ddingdongBE.domain.form.service.vo; - -import ddingdong.ddingdongBE.common.utils.TimeUtils; -import java.time.LocalDate; - -public class HalfYear { - - private int year; - private boolean isFirstHalf; - - private HalfYear(int year, boolean isFirstHalf) { - this.year = year; - this.isFirstHalf = isFirstHalf; - } - - public static HalfYear from(LocalDate date) { - return new HalfYear(date.getYear(), TimeUtils.isFirstHalf(date.getMonthValue())); - } - - public void minusHalves() { - if (isFirstHalf) { - year = year - 1; - isFirstHalf = false; - return; - } - isFirstHalf = true; - } - - public LocalDate getHalfStartDate() { - if(isFirstHalf) { - return LocalDate.of(year, 1, 1); - } - return LocalDate.of(year, 7, 1); - } - - public LocalDate getHalfEndDate() { - if(isFirstHalf) { - return LocalDate.of(year, 6, 30); - } - return LocalDate.of(year, 12, 31); - } - - public String getLabel() { - if (isFirstHalf) { - return year + "-1"; - } - return year + "-2"; - } -} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java index 88ec6dea..a086f90a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/FormApplicationRepository.java @@ -3,6 +3,7 @@ import ddingdong.ddingdongBE.domain.form.entity.Form; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; import ddingdong.ddingdongBE.domain.formapplication.repository.dto.DepartmentInfo; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.RecentFormInfo; import java.time.LocalDate; import java.util.List; import org.springframework.data.domain.Slice; @@ -35,21 +36,30 @@ SELECT f.department, COUNT(f.id) AS count WHERE f.form_id = :formId GROUP BY f.department ORDER BY count DESC - LIMIT 5 + LIMIT :size """, nativeQuery = true) - List findTopFiveDepartmentsByForm(@Param("formId") Long formId); + List findTopDepartmentsByFormId( + @Param("formId") Long formId, + @Param("size") int size + ); @Query(value = """ - SELECT COUNT(fa.id) AS count - FROM form_application fa - JOIN form f ON fa.form_id = f.id - WHERE f.end_date BETWEEN :startDate AND :endDate - GROUP BY f.id - ORDER BY count DESC - LIMIT 1 + SELECT recent_forms.start_date AS date, COUNT(fa.id) AS count + FROM ( + SELECT * + FROM form + WHERE club_id = :clubId + AND start_date <= :date + ORDER BY start_date + LIMIT :size + ) AS recent_forms + LEFT JOIN form_application fa + ON recent_forms.id = fa.form_id + GROUP BY recent_forms.id """, nativeQuery = true) - Integer findMaxApplicationCountByDateRange( - @Param("startDate") LocalDate startDate, - @Param("endDate") LocalDate endDate + List findRecentFormByDateWithApplicationCount( + @Param("clubId") Long clubId, + @Param("date") LocalDate date, + @Param("size") int size ); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/RecentFormInfo.java b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/RecentFormInfo.java new file mode 100644 index 00000000..fc024e81 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/formapplication/repository/dto/RecentFormInfo.java @@ -0,0 +1,9 @@ +package ddingdong.ddingdongBE.domain.formapplication.repository.dto; + +import java.time.LocalDate; + +public interface RecentFormInfo { + + LocalDate getDate(); + Integer getCount(); +} From d1d5cac77470735684f83a21138dc81874012175 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Sun, 9 Feb 2025 06:41:00 +0900 Subject: [PATCH 12/14] =?UTF-8?q?test=20:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=90=9C=20=EB=A1=9C=EC=A7=81=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/CalculationUtilsTest.java | 26 ++++++ .../FormApplicationRepositoryTest.java | 42 +++++++--- .../repository/FormFieldRepositoryTest.java | 81 +++++++++++++++++++ .../service/FormStatisticServiceImplTest.java | 56 ++++++++----- 4 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 src/test/java/ddingdong/ddingdongBE/common/utils/CalculationUtilsTest.java create mode 100644 src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepositoryTest.java diff --git a/src/test/java/ddingdong/ddingdongBE/common/utils/CalculationUtilsTest.java b/src/test/java/ddingdong/ddingdongBE/common/utils/CalculationUtilsTest.java new file mode 100644 index 00000000..2183ea7e --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/common/utils/CalculationUtilsTest.java @@ -0,0 +1,26 @@ +package ddingdong.ddingdongBE.common.utils; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CalculationUtilsTest { + + @DisplayName("beforeCount기준으로 beforeCount와 count의 증감율을 구한다.") + @Test + void calculateDifferenceRatio() { + // given + int beforeCount = 30; + int count = 45; + + int beforeCount2 = 45; + int count2 = 30; + // when + int result = CalculationUtils.calculateDifferenceRatio(beforeCount, count); + int result2 = CalculationUtils.calculateDifferenceRatio(beforeCount2, count2); + // then + assertThat(result).isEqualTo(50); + assertThat(result2).isEqualTo(-33); + } +} diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java index d620df07..7f8e5d33 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormApplicationRepositoryTest.java @@ -12,6 +12,7 @@ import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus; import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; +import ddingdong.ddingdongBE.domain.formapplication.repository.dto.RecentFormInfo; import ddingdong.ddingdongBE.domain.scorehistory.entity.Score; import ddingdong.ddingdongBE.domain.user.entity.Role; import ddingdong.ddingdongBE.domain.user.entity.User; @@ -208,7 +209,7 @@ void findTopFiveDepartmentsByForm_ShouldReturnTopFiveDepartments() throws Interr formApplication6) ); // when - List topFive = formApplicationRepository.findTopFiveDepartmentsByForm(savedForm.getId()); + List topFive = formApplicationRepository.findTopDepartmentsByFormId(savedForm.getId(),5); // then assertThat(topFive.size()).isEqualTo(3); @@ -220,13 +221,20 @@ void findTopFiveDepartmentsByForm_ShouldReturnTopFiveDepartments() throws Interr assertThat(topFive.get(2).getDepartment()).isEqualTo("학과1"); } - @DisplayName("해당 기간 내에 가장 지원자 수가 많았던 폼지의 지원자수를 반환한다.") + @DisplayName("주어진 날짜를 기준으로 주어진 개수만큼 최신 폼지의 시작일과 지원 수를 반환한다.") @Test - void findMaxApplicationCountByDateRange_ShouldReturnHighestCount() { + void findRecentFormByDateWithApplicationCount() { // given + Club club1 = fixture.giveMeBuilder(Club.class) + .set("id", 1L) + .set("user", null) + .set("score", Score.from(BigDecimal.ZERO)) + .set("clubMembers", null) + .sample(); + Club savedClub = clubRepository.save(club1); Form form = fixture.giveMeBuilder(Form.class) .set("id", 1L) - .set("club", null) + .set("club", savedClub) .set("startDate", LocalDate.of(2020, 3, 1)) .set("endDate", LocalDate.of(2020, 4, 1)) .set("sections", List.of("공통")) @@ -251,7 +259,7 @@ void findMaxApplicationCountByDateRange_ShouldReturnHighestCount() { Form form2 = fixture.giveMeBuilder(Form.class) .set("id", 2L) - .set("club", null) + .set("club", savedClub) .set("startDate", LocalDate.of(2020, 1, 1)) .set("endDate", LocalDate.of(2020, 2, 1)) .set("sections", List.of("공통")) @@ -266,11 +274,27 @@ void findMaxApplicationCountByDateRange_ShouldReturnHighestCount() { .build(); formApplicationRepository.save(formApplication3); - LocalDate startDate = LocalDate.of(2020, 1, 1); - LocalDate endDate = LocalDate.of(2020, 6, 30); + Form form3 = fixture.giveMeBuilder(Form.class) + .set("id", 1L) + .set("club", savedClub) + .set("startDate", LocalDate.of(2020, 5, 1)) + .set("endDate", LocalDate.of(2020, 6, 1)) + .set("sections", List.of("공통")) + .sample(); + formRepository.save(form3); // when - int count = formApplicationRepository.findMaxApplicationCountByDateRange(startDate, endDate); + List recentFormInfos = formApplicationRepository.findRecentFormByDateWithApplicationCount( + savedClub.getId(), + savedForm.getEndDate(), + 3 + ); // then - assertThat(count).isEqualTo(2); + assertThat(recentFormInfos.size()).isEqualTo(2); + assertThat(recentFormInfos.get(0).getCount()).isEqualTo(1); + assertThat(recentFormInfos.get(0).getDate()).isEqualTo(LocalDate.of(2020, 1, 1)); + assertThat(recentFormInfos.get(1).getCount()).isEqualTo(2); + assertThat(recentFormInfos.get(1).getDate()).isEqualTo(LocalDate.of(2020, 3, 1)); + + } } diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepositoryTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepositoryTest.java new file mode 100644 index 00000000..e9646e48 --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/repository/FormFieldRepositoryTest.java @@ -0,0 +1,81 @@ +package ddingdong.ddingdongBE.domain.form.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.navercorp.fixturemonkey.FixtureMonkey; +import ddingdong.ddingdongBE.common.support.DataJpaTestSupport; +import ddingdong.ddingdongBE.common.support.FixtureMonkeyFactory; +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.repository.dto.FieldListInfo; +import ddingdong.ddingdongBE.domain.formapplication.entity.FormAnswer; +import ddingdong.ddingdongBE.domain.formapplication.repository.FormAnswerRepository; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class FormFieldRepositoryTest extends DataJpaTestSupport { + + @Autowired + private FormRepository formRepository; + + @Autowired + private FormFieldRepository formFieldRepository; + + @Autowired + private FormAnswerRepository formAnswerRepository; + + private static final FixtureMonkey fixture = FixtureMonkeyFactory.getNotNullBuilderIntrospectorMonkey(); + + @DisplayName("원하는 필드 정보와 해당 필드의 답변 개수를 반환한다.") + @Test + void findFieldWithAnswerCountByFormId() { + // given + Form formFirst = fixture.giveMeBuilder(Form.class) + .set("id", 1L) + .set("club", null) + .set("startDate", LocalDate.of(2020, 2, 1)) + .set("endDate", LocalDate.of(2020, 3, 1)) + .set("sections", List.of("공통")) + .sample(); + Form savedForm = formRepository.save(formFirst); + FormField formField = FormField.builder() + .question("설문 질문") + .required(true) + .fieldOrder(1) + .section("기본 정보") + .options(List.of("옵션1", "옵션2", "옵션3")) + .fieldType(FieldType.TEXT) + .form(savedForm) + .build(); + FormField savedField = formFieldRepository.save(formField); + + FormAnswer answer = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + FormAnswer answer2 = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + FormAnswer answer3 = FormAnswer.builder() + .formApplication(null) + .value(null) + .formField(savedField) + .build(); + formAnswerRepository.saveAll(List.of(answer, answer2, answer3)); + + // when + List fieldListInfos = formFieldRepository.findFieldWithAnswerCountByFormId( + savedForm.getId()); + // then + assertThat(fieldListInfos.size()).isEqualTo(1); + assertThat(fieldListInfos.get(0).getQuestion()).isEqualTo(formField.getQuestion()); + assertThat(fieldListInfos.get(0).getCount()).isEqualTo(3); + } +} diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java index 5753752f..2dff65e1 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImplTest.java @@ -5,19 +5,23 @@ import com.navercorp.fixturemonkey.FixtureMonkey; import ddingdong.ddingdongBE.common.support.FixtureMonkeyFactory; import ddingdong.ddingdongBE.common.support.TestContainerSupport; +import ddingdong.ddingdongBE.domain.club.entity.Club; +import ddingdong.ddingdongBE.domain.club.repository.ClubRepository; import ddingdong.ddingdongBE.domain.form.entity.FieldType; import ddingdong.ddingdongBE.domain.form.entity.Form; import ddingdong.ddingdongBE.domain.form.entity.FormField; import ddingdong.ddingdongBE.domain.form.repository.FormFieldRepository; import ddingdong.ddingdongBE.domain.form.repository.FormRepository; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantRateQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentRankQuery; -import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsListQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.ApplicantStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.DepartmentStatisticQuery; +import ddingdong.ddingdongBE.domain.form.service.dto.query.FormStatisticsQuery.FieldStatisticsQuery; import ddingdong.ddingdongBE.domain.formapplication.entity.FormAnswer; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplication; import ddingdong.ddingdongBE.domain.formapplication.entity.FormApplicationStatus; import ddingdong.ddingdongBE.domain.formapplication.repository.FormAnswerRepository; import ddingdong.ddingdongBE.domain.formapplication.repository.FormApplicationRepository; +import ddingdong.ddingdongBE.domain.scorehistory.entity.Score; +import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -43,6 +47,9 @@ class FormStatisticServiceImplTest extends TestContainerSupport { @Autowired private FormAnswerRepository formAnswerRepository; + @Autowired + private ClubRepository clubRepository; + private final FixtureMonkey fixture = FixtureMonkeyFactory.getNotNullBuilderIntrospectorMonkey(); @DisplayName("폼지에 지원한 총 지원자 수를 반환한다.") @@ -138,8 +145,10 @@ void createDepartmentRankByForm() { List.of(formApplication, formApplication2, formApplication3, formApplication4, formApplication5, formApplication6) ); + int totalCount = formStatisticService.getTotalApplicationCountByForm(savedForm); // when - List departmentRanks = formStatisticService.createDepartmentRankByForm(savedForm); + List departmentRanks = formStatisticService.createDepartmentStatistics(totalCount, + savedForm); // then assertThat(departmentRanks.size()).isEqualTo(3); assertThat(departmentRanks.get(0).rank()).isEqualTo(1); @@ -154,13 +163,20 @@ void createDepartmentRankByForm() { } - @DisplayName("전 반기에 대한 지원 비율 정보를 정해진 개수만큼 반환한다") + @DisplayName("주어진 폼지와 이전 폼지의 비교 증감 정보를 정해진 개수만큼 반환한다") @Test void createApplicationRateByForm() { // given + Club club1 = fixture.giveMeBuilder(Club.class) + .set("id", 1L) + .set("user", null) + .set("score", Score.from(BigDecimal.ZERO)) + .set("clubMembers", null) + .sample(); + Club savedClub = clubRepository.save(club1); Form formFirst = fixture.giveMeBuilder(Form.class) .set("id", 1L) - .set("club", null) + .set("club", savedClub) .set("startDate", LocalDate.of(2020, 2, 1)) .set("endDate", LocalDate.of(2020, 3, 1)) .set("sections", List.of("공통")) @@ -185,7 +201,7 @@ void createApplicationRateByForm() { Form form2 = fixture.giveMeBuilder(Form.class) .set("id", 2L) - .set("club", null) + .set("club", savedClub) .set("startDate", LocalDate.of(2020, 7, 3)) .set("endDate", LocalDate.of(2020, 8, 1)) .set("sections", List.of("공통")) @@ -200,23 +216,27 @@ void createApplicationRateByForm() { .build(); formApplicationRepository.save(formApplication3); // when - List applicationRateQueries = formStatisticService.createApplicationRateByForm(savedForm2); + List applicationRateQueries = formStatisticService.createApplicationStatistics(savedClub, + savedForm2); // then assertThat(applicationRateQueries.size()).isEqualTo(2); - assertThat(applicationRateQueries.get(0).label()).isEqualTo("2020-1"); + assertThat(applicationRateQueries.get(0).label()).isEqualTo("2020-2"); assertThat(applicationRateQueries.get(0).count()).isEqualTo(2); - assertThat(applicationRateQueries.get(0).comparedToLastSemester()).isEqualTo(100); - assertThat(applicationRateQueries.get(1).label()).isEqualTo("2020-2"); + assertThat(applicationRateQueries.get(0).compareRatio()).isEqualTo(0); + assertThat(applicationRateQueries.get(0).compareValue()).isEqualTo(0); + + assertThat(applicationRateQueries.get(1).label()).isEqualTo("2020-7"); assertThat(applicationRateQueries.get(1).count()).isEqualTo(1); - assertThat(applicationRateQueries.get(1).comparedToLastSemester()).isEqualTo(50); + assertThat(applicationRateQueries.get(1).compareRatio()).isEqualTo(-50); + assertThat(applicationRateQueries.get(1).compareValue()).isEqualTo(-1); } - @DisplayName("폼지의 질문 정보") + @DisplayName("해당 폼지의 섹션 종류와 질문 정보 및 질문 답변 개수를 반환한다.") @Test - void createFieldStatisticsListByForm() { + void createFieldStatisticsByForm() { // given Form formFirst = fixture.giveMeBuilder(Form.class) .set("id", 1L) @@ -254,11 +274,11 @@ void createFieldStatisticsListByForm() { .build(); formAnswerRepository.saveAll(List.of(answer, answer2, answer3)); // when - List fieldStatisticsList = formStatisticService.createFieldStatisticsListByForm( + FieldStatisticsQuery fieldStatistics = formStatisticService.createFieldStatisticsByForm( savedForm); // then - assertThat(fieldStatisticsList.size()).isEqualTo(1); - assertThat(fieldStatisticsList.get(0).count()).isEqualTo(3); - assertThat(fieldStatisticsList.get(0).question()).isEqualTo("설문 질문"); + assertThat(fieldStatistics.fieldStatisticsListQueries().size()).isEqualTo(1); + assertThat(fieldStatistics.fieldStatisticsListQueries().get(0).count()).isEqualTo(3); + assertThat(fieldStatistics.fieldStatisticsListQueries().get(0).question()).isEqualTo("설문 질문"); } } From 604aede31a279959a8f935b8fdc36ee08af8be51 Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Sun, 9 Feb 2025 15:32:09 +0900 Subject: [PATCH 13/14] =?UTF-8?q?test=20:=20support=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90=20@Transactional=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddingdongBE/common/support/TestContainerSupport.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java b/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java index 5db5d9c2..facbfa53 100644 --- a/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java +++ b/src/test/java/ddingdong/ddingdongBE/common/support/TestContainerSupport.java @@ -11,6 +11,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; +import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MySQLContainer; @@ -18,6 +19,7 @@ @NoArgsConstructor(access = PROTECTED) @ActiveProfiles("test") @Import(TestConfig.class) +@Transactional public abstract class TestContainerSupport { private static final String MYSQL_IMAGE = "mysql:8"; From de574ee385bc37ada38f0ae3b1d834ec26c19bba Mon Sep 17 00:00:00 2001 From: KoSeonJe <127813439+KoSeonJe@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:46:27 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor=20:=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=A4=84=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../form/service/FacadeCentralFormServiceImpl.java | 10 +++++----- .../domain/form/service/FormStatisticServiceImpl.java | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java index b6979e5f..3167ed74 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -32,7 +32,7 @@ public class FacadeCentralFormServiceImpl implements FacadeCentralFormService { private final FormService formService; private final FormFieldService formFieldService; private final ClubService clubService; - private final FormStatisticService formStatisTicService; + private final FormStatisticService formStatisticService; @Transactional @Override @@ -88,10 +88,10 @@ public FormQuery getForm(Long formId) { public FormStatisticsQuery getStatisticsByForm(User user, Long formId) { Club club = clubService.getByUserId(user.getId()); Form form = formService.getById(formId); - int totalCount = formStatisTicService.getTotalApplicationCountByForm(form); - List departmentStatisticQueries = formStatisTicService.createDepartmentStatistics(totalCount, form); - List applicantStatisticQueries = formStatisTicService.createApplicationStatistics(club, form); - FieldStatisticsQuery fieldStatisticsQuery = formStatisTicService.createFieldStatisticsByForm(form); + int totalCount = formStatisticService.getTotalApplicationCountByForm(form); + List departmentStatisticQueries = formStatisticService.createDepartmentStatistics(totalCount, form); + List applicantStatisticQueries = formStatisticService.createApplicationStatistics(club, form); + FieldStatisticsQuery fieldStatisticsQuery = formStatisticService.createFieldStatisticsByForm(form); return new FormStatisticsQuery(totalCount, departmentStatisticQueries, applicantStatisticQueries, fieldStatisticsQuery); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java index a74837d0..3da6e28a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormStatisticServiceImpl.java @@ -81,7 +81,8 @@ public List createApplicationStatistics(Club club, Form int compareValue = CalculationUtils.calculateDifference(beforeCount, count); return new ApplicantStatisticQuery(label, count, compareRatio, compareValue); - }).toList(); + }) + .toList(); } @Override