diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java new file mode 100644 index 00000000..0c2dbf26 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/api/CentralFormApi.java @@ -0,0 +1,29 @@ +package ddingdong.ddingdongBE.domain.form.api; + +import ddingdong.ddingdongBE.auth.PrincipalDetails; +import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest; +import io.swagger.v3.oas.annotations.Operation; +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 org.springframework.http.HttpStatus; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Tag(name = "Form - Club", description = "Form API") +@RequestMapping("/server/central") +public interface CentralFormApi { + + @Operation(summary = "동아리 지원 폼지 생성 API") + @ApiResponse(responseCode = "201", description = "동아리 지원 폼지 생성 성공") + @ResponseStatus(HttpStatus.CREATED) + @SecurityRequirement(name = "AccessToken") + @PostMapping("/my/forms") + void createForm( + @RequestBody CreateFormRequest createFormRequest, + @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 new file mode 100644 index 00000000..19a20e79 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/CentralFormController.java @@ -0,0 +1,25 @@ +package ddingdong.ddingdongBE.domain.form.controller; + +import ddingdong.ddingdongBE.auth.PrincipalDetails; +import ddingdong.ddingdongBE.domain.form.api.CentralFormApi; +import ddingdong.ddingdongBE.domain.form.controller.dto.request.CreateFormRequest; +import ddingdong.ddingdongBE.domain.form.service.FacadeCentralFormService; +import ddingdong.ddingdongBE.domain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class CentralFormController implements CentralFormApi { + + private final FacadeCentralFormService facadeCentralFormService; + + @Override + public void createForm( + CreateFormRequest createFormRequest, + PrincipalDetails principalDetails + ) { + User user = principalDetails.getUser(); + facadeCentralFormService.createForm(createFormRequest.toCommand(user)); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/request/CreateFormRequest.java b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/request/CreateFormRequest.java new file mode 100644 index 00000000..339f6141 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/controller/dto/request/CreateFormRequest.java @@ -0,0 +1,90 @@ +package ddingdong.ddingdongBE.domain.form.controller.dto.request; + +import ddingdong.ddingdongBE.domain.form.entity.FieldType; +import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand; +import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand.CreateFormFieldCommand; +import ddingdong.ddingdongBE.domain.user.entity.User; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import java.util.List; + + +public record CreateFormRequest( + @Schema(description = "폼지 제목", example = "폼지제목입니다") + @NotNull(message = "폼지 제목은 null이 될 수 없습니다.") + String title, + + @Schema(description = "폼지 설명", example = "우리 동아리는 띵동입니다.") + String description, + + @Schema(description = "폼지 시작일자", example = "2001-01-01") + @NotNull(message = "폼지 시작일자는 null이 될 수 없습니다.") + LocalDate startDate, + + @Schema(description = "폼지 종료일자", example = "2001-01-02") + @NotNull(message = "폼지 종료일자는 null이 될 수 없습니다.") + LocalDate endDate, + + @Schema(description = "면접여부", example = "true") + @NotNull(message = "면접여부는 null이 될 수 없습니다.") + boolean hasInterview, + + @ArraySchema(schema = @Schema(implementation = CreateFormFieldRequest.class)) + List formFields +) { + + record CreateFormFieldRequest( + @Schema(description = "폼지 질문", example = "우리 동아리 들어올겁니까?") + @NotNull(message = "질문는 null이 될 수 없습니다.") + String question, + + @Schema(description = "질문 종류", example = "CHECK_BOX") + @NotNull(message = "질문 종류는 null이 될 수 없습니다.") + FieldType type, + + @Schema(description = "질문의 선택리스트", example = "[지문1이다., 지문2이다., 지문3이다.]") + List options, + + @Schema(description = "필수여부", example = "true") + @NotNull(message = "필수여부는 null이 될 수 없습니다.") + boolean required, + + @Schema(description = "질문 순서", example = "1") + @NotNull(message = "질문 순서는 null이 될 수 없습니다.") + int order, + + @Schema(description = "질문 섹션 종류", example = "공통") + @NotNull(message = "질문 섹션종류는 null이 될 수 없습니다.") + String section + ) { + + public CreateFormFieldCommand toCommand() { + return CreateFormFieldCommand.builder() + .question(question) + .type(type) + .options(options) + .required(required) + .order(order) + .section(section) + .build(); + } + } + + public CreateFormCommand toCommand(User user) { + List createFormFieldCommands = formFields.stream() + .map(CreateFormFieldRequest::toCommand) + .toList(); + return CreateFormCommand.builder() + .user(user) + .title(title) + .description(description) + .startDate(startDate) + .endDate(endDate) + .hasInterview(hasInterview) + .formFieldCommands(createFormFieldCommands) + .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 new file mode 100644 index 00000000..3ed8153e --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormService.java @@ -0,0 +1,8 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand; + +public interface FacadeCentralFormService { + + void createForm(CreateFormCommand command); +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java new file mode 100644 index 00000000..36c623f7 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImpl.java @@ -0,0 +1,39 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.club.entity.Club; +import ddingdong.ddingdongBE.domain.club.service.ClubService; +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand; +import ddingdong.ddingdongBE.domain.form.service.dto.command.CreateFormCommand.CreateFormFieldCommand; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FacadeCentralFormServiceImpl implements FacadeCentralFormService{ + + private final FormService formService; + private final FormFieldService formFieldService; + private final ClubService clubService; + + @Transactional + @Override + public void createForm(CreateFormCommand createFormCommand) { + Club club = clubService.getByUserId(createFormCommand.user().getId()); + Form form = createFormCommand.toEntity(club); + Form savedForm = formService.create(form); + + List formFields = toFormFields(savedForm, createFormCommand.formFieldCommands()); + formFieldService.createAll(formFields); + } + + private List toFormFields(Form savedForm, List createFormFieldCommands) { + return createFormFieldCommands.stream() + .map(formFieldCommand -> formFieldCommand.toEntity(savedForm)) + .toList(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java new file mode 100644 index 00000000..72c4346e --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormFieldService.java @@ -0,0 +1,9 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import java.util.List; + +public interface FormFieldService { + + void createAll(List formFields); +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java new file mode 100644 index 00000000..059f43d7 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/FormService.java @@ -0,0 +1,8 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.entity.Form; + +public interface FormService { + + Form create(Form form); +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormFieldService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormFieldService.java new file mode 100644 index 00000000..2c0c1237 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormFieldService.java @@ -0,0 +1,22 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.entity.FormField; +import ddingdong.ddingdongBE.domain.form.repository.FormFieldRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GeneralFormFieldService implements FormFieldService { + + private final FormFieldRepository formFieldRepository; + + @Transactional + @Override + public void createAll(List formFields) { + formFieldRepository.saveAll(formFields); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java new file mode 100644 index 00000000..922608ef --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/GeneralFormService.java @@ -0,0 +1,21 @@ +package ddingdong.ddingdongBE.domain.form.service; + +import ddingdong.ddingdongBE.domain.form.entity.Form; +import ddingdong.ddingdongBE.domain.form.repository.FormRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GeneralFormService implements FormService{ + + private final FormRepository formRepository; + + @Transactional + @Override + public Form create(Form form) { + return formRepository.save(form); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/command/CreateFormCommand.java b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/command/CreateFormCommand.java new file mode 100644 index 00000000..e17f7f2c --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/service/dto/command/CreateFormCommand.java @@ -0,0 +1,56 @@ +package ddingdong.ddingdongBE.domain.form.service.dto.command; + +import ddingdong.ddingdongBE.domain.club.entity.Club; +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.user.entity.User; +import java.time.LocalDate; +import java.util.List; +import lombok.Builder; + +@Builder +public record CreateFormCommand( + User user, + String title, + String description, + LocalDate startDate, + LocalDate endDate, + boolean hasInterview, + List formFieldCommands +) { + + @Builder + public record CreateFormFieldCommand( + String question, + FieldType type, + List options, + boolean required, + int order, + String section + ) { + + public FormField toEntity(Form savedForm) { + return FormField.builder() + .question(question) + .fieldType(type) + .options(options) + .required(required) + .fieldOrder(order) + .section(section) + .form(savedForm) + .build(); + } + } + + public Form toEntity(Club club) { + return Form.builder() + .title(title) + .description(description) + .startDate(startDate) + .endDate(endDate) + .hasInterview(hasInterview) + .club(club) + .build(); + } +} diff --git a/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java new file mode 100644 index 00000000..fc50d37e --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/domain/form/service/FacadeCentralFormServiceImplTest.java @@ -0,0 +1,77 @@ +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.club.entity.Club; +import ddingdong.ddingdongBE.domain.club.repository.ClubRepository; +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.command.CreateFormCommand; +import ddingdong.ddingdongBE.domain.user.entity.Role; +import ddingdong.ddingdongBE.domain.user.entity.User; +import ddingdong.ddingdongBE.domain.user.repository.UserRepository; +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 FacadeCentralFormServiceImplTest extends TestContainerSupport { + + @Autowired + private FormService formService; + + @Autowired + private FormRepository formRepository; + + @Autowired + private FacadeCentralFormService facadeCentralFormService; + + @Autowired + private ClubRepository clubRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private FormFieldRepository formFieldRepository; + + private static final FixtureMonkey fixtureMonkey = FixtureMonkeyFactory.getNotNullBuilderIntrospectorMonkey(); + + @DisplayName("폼지와 폼지 질문을 생성할 수 있다.") + @Test + void createForm() { + // 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); + CreateFormCommand createFormCommand = fixtureMonkey.giveMeBuilder(CreateFormCommand.class) + .set("user", savedUser) + .sample(); + // when + facadeCentralFormService.createForm(createFormCommand); + // then + List
form = formRepository.findAll(); + List formFields = formFieldRepository.findAll(); + + assertThat(form).isNotEmpty(); + assertThat(formFields).isNotEmpty(); + } +}