Skip to content

Commit

Permalink
Tht server 221 문의하기 Api (#243)
Browse files Browse the repository at this point in the history
* chore : github actions 워크플로우 구문 수정

* chore : 워크플로우 필터링 구문 수정

* docs : 문서 작성

* docs : 오탈자 수정

* feat : 문의하기 기능 추가

* fix : 오탈자 수정 및 응답값 변경
  • Loading branch information
thalals authored Jul 24, 2024
1 parent 97c3d28 commit 4902526
Show file tree
Hide file tree
Showing 15 changed files with 336 additions and 5 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ name: THT API SERVER
run-name: ${{ github.actor }} is learning GitHub Actions

on:
issues:
types:
- labeled
pull_request:
branches:
- main
paths:
- 'tht-apis/**'
push:
branches:
- main
paths:
- 'tht-apis/**'

env:
AWS_CODE_DEPLOY_APPLICATION: THT-ApiServer-GitAction
AWS_CODE_DEPLOY_GROUP: THT-ApiServer-GitAction-SingleServer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.NOT_FOUND;

import com.tht.domain.entity.inquiry.UserInquiryException;
import com.tht.domain.exception.AligoException;
import com.tht.domain.exception.EntityStateException;
import com.tht.enums.EnumStateNotFoundException;
Expand Down Expand Up @@ -158,4 +159,13 @@ public ResponseEntity<ErrorResponse> handlerException(final DailyFallingExceptio
);
}

@ExceptionHandler
public ResponseEntity<ErrorResponse> handlerException(final UserInquiryException e,
final HttpServletRequest request) {

return ResponseEntity.badRequest().body(
ErrorResponse.of(e.getHttpStatus(), e.getMessage(), request)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tht.thtapis.facade.setting;

import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;

public record InquiryRequest(
@NotEmpty(message = "userEmail 을 입력해주세요") String userEmail,
@NotEmpty(message = "contents 을 입력해주세요") String contents,
@AssertTrue(message = "정보제공에 동의해주세요") boolean isEmailAgree
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.tht.thtapis.facade.setting;

import com.tht.domain.entity.inquiry.InquiryService;
import com.tht.thtapis.facade.Facade;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@Facade
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class SettingFacade {

private final InquiryService inquiryService;

@Transactional
public void inquiry(final String userUuid, final InquiryRequest request) {
inquiryService.receiptInquiry(userUuid, request.contents(), request.userEmail(), request.isEmailAgree());
}
}
28 changes: 28 additions & 0 deletions tht-apis/src/main/java/com/tht/thtapis/ui/SettingController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tht.thtapis.ui;

import com.tht.domain.entity.user.User;
import com.tht.thtapis.facade.setting.InquiryRequest;
import com.tht.thtapis.facade.setting.SettingFacade;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RestController;

@RestController
@RequiredArgsConstructor
public class SettingController {

private final SettingFacade settingFacade;

@PostMapping("/user/inquiry")
public ResponseEntity<Object> inquiry(@AuthenticationPrincipal User user,
@RequestBody @Valid InquiryRequest request) {

settingFacade.inquiry(user.getUserUuid(), request);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.tht.thtapis.documentation;

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.epages.restdocs.apispec.Schema;
import com.tht.thtapis.controller.config.ControllerTestConfig;
import com.tht.thtapis.controller.config.WithCustomMockUser;
import com.tht.thtapis.facade.setting.InquiryRequest;
import com.tht.thtapis.facade.setting.SettingFacade;
import com.tht.thtapis.fixture.setting.InquiryRequestFixture;
import com.tht.thtapis.security.SecurityConst;
import com.tht.thtapis.ui.SettingController;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;

import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.mockito.Mockito.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(SettingController.class)
class SettingDocumentation extends ControllerTestConfig {

@MockBean
SettingFacade settingFacade;

@Test
@WithCustomMockUser
@DisplayName("문의하기 api docs")
void docsInquiryApi() throws Exception {

InquiryRequest request = InquiryRequestFixture.make();
String requestBody = ControllerTestConfig.objectMapper.writeValueAsString(request);

doNothing().when(settingFacade).inquiry(anyString(), any());

final ResultActions resultActions = mockMvc.perform(post("/user/inquiry")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(SecurityConst.AUTH_HEADER_NAME, "Bearer {ACCESS_TOKEN}")
.content(requestBody)
).andDo(
document("문의하기 api docs",
resource(
ResourceSnippetParameters.builder()
.tag("설정")
.description("유저 문의하기 api")
.requestFields(
fieldWithPath("userEmail").description("문의 답장 받을 이메일"),
fieldWithPath("contents").description("유저 디바이스 키"),
fieldWithPath("isEmailAgree").description("메일 정보동의 제공 여부")
)
.requestSchema(Schema.schema("InquiryRequest"))
.build()
))
);

resultActions.andExpect(status().isCreated());

}
}


Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void userLogoutDocs() throws Exception {
.accept(MediaType.APPLICATION_JSON)
.header(SecurityConst.AUTH_HEADER_NAME, "Bearer {ACCESS_TOKEN}")
).andDo(
document("로으가웃 api docs",
document("로그아웃 api docs",
resource(
ResourceSnippetParameters.builder()
.tag("유저 - 로그아웃")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.tht.thtapis.fixture.setting;

import com.tht.thtapis.facade.setting.InquiryRequest;

public class InquiryRequestFixture {

public static InquiryRequest make() {
return new InquiryRequest(
"email@email.com",
"문의하기내용입니다 ~~ 아아아!!",
true
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.tht.domain.entity.inquiry;

import org.springframework.data.jpa.repository.JpaRepository;

public interface InquiryRepository extends JpaRepository<UserInquiry, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.tht.domain.entity.inquiry;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class InquiryService {

private final InquiryRepository repository;

@Transactional
public void receiptInquiry(final String userUuid, final String contents, final String userEmail, final boolean isEmailAgree) {
final UserInquiry userInquiry = UserInquiry.createInquiry(userUuid, contents, userEmail, isEmailAgree);
repository.save(userInquiry);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.tht.domain.entity.inquiry;

import com.tht.domain.entity.Auditable;
import com.tht.enums.inquiry.InquiryStatus;
import com.tht.enums.inquiry.InquiryStatusConverter;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Entity
@ToString
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor
@DynamicUpdate
public class UserInquiry extends Auditable {

@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idx;

@Column
private String userUuid;

@Column
private String contents;

@Column
private String userEmail;

@Column
private Boolean isEmailAgree;

@Column
@Convert(converter = InquiryStatusConverter.class)
private InquiryStatus inquiryStatus;

@Builder
private UserInquiry(final String userUuid, final String contents, final String userEmail,
final Boolean isEmailAgree, final InquiryStatus inquiryStatus) {

this.userUuid = userUuid;
this.contents = contents;
this.userEmail = userEmail;
this.isEmailAgree = isEmailAgree;
this.inquiryStatus = inquiryStatus;
}

public static UserInquiry createInquiry(final String userUuid, final String contents, final String userEmail, final boolean isEmailAgree) {

if (!isEmailAgree) {
throw UserInquiryException.isFalseEmailAgree();
}

return UserInquiry.builder()
.userUuid(userUuid)
.contents(contents)
.userEmail(userEmail)
.isEmailAgree(isEmailAgree)
.inquiryStatus(InquiryStatus.RECEPTION)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.tht.domain.entity.inquiry;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserInquiryException extends RuntimeException {

private HttpStatus httpStatus;

public UserInquiryException(final String message) {
super(message);
httpStatus = HttpStatus.BAD_REQUEST;
}

public static UserInquiryException isFalseEmailAgree() {
return new UserInquiryException("이메일 정보제공에 동의해주세요");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.tht.enums.agreement.AgreementCategory;
import com.tht.enums.dailyfalling.DailyFallingType;
import com.tht.enums.inquiry.InquiryStatus;
import com.tht.enums.user.UserFrequency;
import com.tht.enums.user.UserReligion;
import lombok.AccessLevel;
Expand Down Expand Up @@ -53,7 +54,11 @@ public static EnumStateNotFoundException ofAgreement(final String agreementCateg
return new EnumStateNotFoundException(String.format("%s는 유효하지 않은 약관동의 카테고리 타입입니다. (ex) %s", agreementCategoryName, getEnumValueList(AgreementCategory.class)));
}

public static EnumStateNotFoundException ofDailyFallingType(String type) {
public static EnumStateNotFoundException ofDailyFallingType(final String type) {
return new EnumStateNotFoundException(String.format("%s는 유효하지 않은 약관동의 카테고리 타입입니다. (ex) %s", type, getEnumValueList(DailyFallingType.class)));
}

public static EnumStateNotFoundException ofInquiryStatus(final String inquiryName) {
return new EnumStateNotFoundException(String.format("%s는 유효하지 문의 진행 상황 타입입니다. (ex) %s", inquiryName, getEnumValueList(InquiryStatus.class)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.tht.enums.inquiry;

import com.tht.enums.EnumModel;
import com.tht.enums.EnumStateNotFoundException;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;

@RequiredArgsConstructor
public enum InquiryStatus implements EnumModel {

RECEPTION("reception"),
CHECK("check"),
COMPLETED("completed");

private final String value;

public static InquiryStatus toConverter(final String dbData) {
return Arrays.stream(InquiryStatus.values())
.filter(inquiryStatus -> inquiryStatus.name().equals(dbData))
.findAny()
.orElseThrow(() -> EnumStateNotFoundException.ofInquiryStatus(dbData));
}

@Override
public String getKey() {
return name();
}

@Override
public String getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.tht.enums.inquiry;

import jakarta.persistence.AttributeConverter;

import java.util.Objects;

public class InquiryStatusConverter implements AttributeConverter<InquiryStatus, String> {

@Override
public String convertToDatabaseColumn(InquiryStatus inquiryStatus) {
if (Objects.isNull(inquiryStatus)) {
throw new NullPointerException("Enum Converting String - DailyFallingType is null");
}

return inquiryStatus.getKey();
}

@Override
public InquiryStatus convertToEntityAttribute(String dbData) {
return InquiryStatus.toConverter(dbData);
}
}

0 comments on commit 4902526

Please sign in to comment.