From 351283308bcb0d44b97801c907f1c0dd656ed2ad Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Sun, 12 May 2024 23:57:14 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat=20:=20=EB=A9=94=EB=AA=A8=EB=A6=AC?= =?UTF-8?q?=EC=97=90=20banword=20=EC=98=AC=EB=A6=AC=EB=8A=94=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/BanWordController.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/hellomeritz/chat/controller/BanWordController.java diff --git a/src/main/java/com/hellomeritz/chat/controller/BanWordController.java b/src/main/java/com/hellomeritz/chat/controller/BanWordController.java new file mode 100644 index 0000000..feb61c5 --- /dev/null +++ b/src/main/java/com/hellomeritz/chat/controller/BanWordController.java @@ -0,0 +1,26 @@ +package com.hellomeritz.chat.controller; + +import com.hellomeritz.chat.service.BanWordService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping("/banwords") +public class BanWordController { + + private final BanWordService banWordService; + + public BanWordController(BanWordService banWordService) { + this.banWordService = banWordService; + } + + @PatchMapping + public ResponseEntity uploadBanWord( ) { + banWordService.uploadBanWordsToMemory(); + + return ResponseEntity.status(HttpStatus.CREATED) + .build(); + } + +} From b30f0dae7f906d29ea67d2f53cd3b7af867419e1 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 00:15:53 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat=20:=20=EC=83=81=EB=8B=B4=EC=9B=90?= =?UTF-8?q?=EC=9D=98=20=EC=83=81=EB=8B=B4=20=EA=B0=80=EB=8A=A5=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20=EB=B3=80=EA=B2=BD=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsultantController.java | 21 ++++++++++++++++--- .../member/service/MemberService.java | 7 +++++++ .../FinancialConsultantChangeStateParam.java | 11 ++++++++++ .../ConsultantControllerDocsTest.java | 19 +++++++++++++++++ .../member/service/MemberServiceTest.java | 19 ++++++++++++++++- 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/hellomeritz/member/service/dto/param/FinancialConsultantChangeStateParam.java diff --git a/src/main/java/com/hellomeritz/member/controller/ConsultantController.java b/src/main/java/com/hellomeritz/member/controller/ConsultantController.java index 465b87b..fc55237 100644 --- a/src/main/java/com/hellomeritz/member/controller/ConsultantController.java +++ b/src/main/java/com/hellomeritz/member/controller/ConsultantController.java @@ -2,12 +2,13 @@ import com.hellomeritz.member.controller.dto.response.ConsultantMatchResponse; import com.hellomeritz.member.service.MemberService; +import com.hellomeritz.member.service.dto.param.FinancialConsultantChangeStateParam; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RequestMapping("/consultants") @RestController @@ -27,4 +28,18 @@ public ResponseEntity matchConsultant(){ .body(ConsultantMatchResponse.to(memberService.matchConsultant())); } + @PutMapping( + path = "/{fcId}" + ) + public ResponseEntity endConsultation( + @Positive (message = "fcId는 양수여야 합니다.") + @NotNull (message = "fcId는 null일 수 없습니다.") + @PathVariable Long fcId + ) { + memberService.endConsultation(FinancialConsultantChangeStateParam.to(fcId)); + + return ResponseEntity.status(HttpStatus.OK) + .build(); + } + } diff --git a/src/main/java/com/hellomeritz/member/service/MemberService.java b/src/main/java/com/hellomeritz/member/service/MemberService.java index c58d121..8da5f76 100644 --- a/src/main/java/com/hellomeritz/member/service/MemberService.java +++ b/src/main/java/com/hellomeritz/member/service/MemberService.java @@ -81,4 +81,11 @@ private FinancialConsultant selectConsultant(List consultan int randomIndex = random.nextInt(consultants.size()); return consultants.get(randomIndex); } + + @Transactional + public void endConsultation(FinancialConsultantChangeStateParam param) { + FinancialConsultant financialConsultant = financialConsultantRepository.getFinancialConsultant(param.fcId()); + financialConsultant.endConsulting(); + } + } diff --git a/src/main/java/com/hellomeritz/member/service/dto/param/FinancialConsultantChangeStateParam.java b/src/main/java/com/hellomeritz/member/service/dto/param/FinancialConsultantChangeStateParam.java new file mode 100644 index 0000000..94c4a2f --- /dev/null +++ b/src/main/java/com/hellomeritz/member/service/dto/param/FinancialConsultantChangeStateParam.java @@ -0,0 +1,11 @@ +package com.hellomeritz.member.service.dto.param; + +public record FinancialConsultantChangeStateParam( + long fcId +) { + public static FinancialConsultantChangeStateParam to( + Long fcId + ) { + return new FinancialConsultantChangeStateParam(fcId); + } +} diff --git a/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java b/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java index 8875967..5e828cc 100644 --- a/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java +++ b/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java @@ -18,8 +18,11 @@ import static org.mockito.Mockito.mock; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -48,4 +51,20 @@ void checkUserIsFc() throws Exception { ) )); } + + @DisplayName("상담이 끝나면 상담원의 상태를 가능 상태로 변경하는 API") + @Test + void endConsultation() throws Exception { + mockMvc.perform(put("/consultants/{fcId}",1L) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8)) + .andDo(print()) + .andExpect(status().isOk()) + .andDo(document("end-consultation-and-change-available", + pathParameters( + parameterWithName("fcId").description("상담원의 ID") + ) + )); + } + } diff --git a/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java b/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java index 45a6e7b..f001c64 100644 --- a/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java +++ b/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java @@ -2,16 +2,17 @@ import com.hellomeritz.global.FinancialConsultantFixture; import com.hellomeritz.global.ForeignFixture; +import com.hellomeritz.member.domain.ConsultationState; import com.hellomeritz.member.domain.FinancialConsultant; import com.hellomeritz.member.domain.Foreigner; import com.hellomeritz.member.repository.fc.FinancialConsultantRepository; import com.hellomeritz.member.repository.foreign.ForeignRepository; +import com.hellomeritz.member.service.dto.param.FinancialConsultantChangeStateParam; import com.hellomeritz.member.service.dto.param.FinancialConsultantInfoGetParam; import com.hellomeritz.member.service.dto.param.ForeignerInfoGetParam; import com.hellomeritz.member.service.dto.result.ConsultantMatchResult; import com.hellomeritz.member.service.dto.result.FcInfoResult; import com.hellomeritz.member.service.dto.result.ForeignerInfoResult; -import jakarta.persistence.EntityNotFoundException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -92,4 +93,20 @@ void matchConsultant_unavailable_throwException() { , ()->memberService.matchConsultant() ); } + + @DisplayName("상담원은 상담이 끝나면 본인의 상태를 UNAVAILABLE로 바꿀 수 있다.") + @Test + void changeConsultationState() { + // given + FinancialConsultant financialConsultant = financialConsultantRepository.save(FinancialConsultantFixture.financialConsultantNotVailable()); + + // when + memberService.endConsultation(new FinancialConsultantChangeStateParam(financialConsultant.getFinancialConsultantId())); + FinancialConsultant changedFinancialConsultant = financialConsultantRepository.getFinancialConsultant(financialConsultant.getFinancialConsultantId()); + + // then + assertThat(changedFinancialConsultant.getConsultationState()).isEqualTo(ConsultationState.AVAILABLE.name()); + + + } } From 92d430e870ff71802bdd928df780110108ab0d57 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 11:51:56 +0900 Subject: [PATCH 03/19] =?UTF-8?q?style=20:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatRoomPasswordCreateRequest.java | 8 ++--- .../hellomeritz/chat/service/ChatService.java | 18 ++-------- .../dto/param/ChatRoomPasswordCheckParam.java | 6 ++-- .../param/ChatRoomPasswordCreateParam.java | 8 ++--- .../encryption/PassWord.java} | 34 +++++++++---------- .../global/encryption/PasswordEncoder.java | 11 +++--- .../encryption/dto/EncryptionRequest.java | 2 +- .../encryption/dto/EncryptionResponse.java | 2 +- .../encryption/dto/PasswordMatchRequest.java | 2 +- .../global/encryption/dto/SaltRequest.java | 2 +- 10 files changed, 37 insertions(+), 56 deletions(-) rename src/main/java/com/hellomeritz/{chat/domain/ChatRoomPassword.java => global/encryption/PassWord.java} (63%) rename src/main/java/com/hellomeritz/{member => }/global/encryption/PasswordEncoder.java (86%) rename src/main/java/com/hellomeritz/{member => }/global/encryption/dto/EncryptionRequest.java (88%) rename src/main/java/com/hellomeritz/{member => }/global/encryption/dto/EncryptionResponse.java (80%) rename src/main/java/com/hellomeritz/{member => }/global/encryption/dto/PasswordMatchRequest.java (71%) rename src/main/java/com/hellomeritz/{member => }/global/encryption/dto/SaltRequest.java (78%) diff --git a/src/main/java/com/hellomeritz/chat/controller/dto/request/ChatRoomPasswordCreateRequest.java b/src/main/java/com/hellomeritz/chat/controller/dto/request/ChatRoomPasswordCreateRequest.java index 7a34feb..2bb4916 100644 --- a/src/main/java/com/hellomeritz/chat/controller/dto/request/ChatRoomPasswordCreateRequest.java +++ b/src/main/java/com/hellomeritz/chat/controller/dto/request/ChatRoomPasswordCreateRequest.java @@ -1,12 +1,8 @@ package com.hellomeritz.chat.controller.dto.request; -import com.hellomeritz.chat.domain.ChatRoomPassword; -import com.hellomeritz.chat.service.dto.param.ChatRoomCreateParam; +import com.hellomeritz.global.encryption.PassWord; import com.hellomeritz.chat.service.dto.param.ChatRoomPasswordCreateParam; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; -import org.springframework.web.bind.annotation.PathVariable; import java.time.LocalDateTime; @@ -17,7 +13,7 @@ public record ChatRoomPasswordCreateRequest( public ChatRoomPasswordCreateParam toChatRoomPasswordCreateParam(Long chatRoomId) { return new ChatRoomPasswordCreateParam( - ChatRoomPassword.of(chatRoomPassword), + PassWord.of(chatRoomPassword), chatRoomId, LocalDateTime.now() ); diff --git a/src/main/java/com/hellomeritz/chat/service/ChatService.java b/src/main/java/com/hellomeritz/chat/service/ChatService.java index 1ed4c29..ecbcab5 100644 --- a/src/main/java/com/hellomeritz/chat/service/ChatService.java +++ b/src/main/java/com/hellomeritz/chat/service/ChatService.java @@ -1,18 +1,6 @@ package com.hellomeritz.chat.service; -import com.hellomeritz.chat.domain.ChatMessage; import com.hellomeritz.chat.domain.ChatRoom; -import com.hellomeritz.chat.global.stt.SttManager; -import com.hellomeritz.chat.global.stt.SttManagerHandler; -import com.hellomeritz.chat.global.stt.SttProvider; -import com.hellomeritz.chat.global.stt.dto.SttResponse; -import com.hellomeritz.chat.global.translator.TranslateProvider; -import com.hellomeritz.chat.global.translator.TranslationResponse; -import com.hellomeritz.chat.global.translator.Translator; -import com.hellomeritz.chat.global.translator.TranslatorHandler; -import com.hellomeritz.chat.global.uploader.AudioUploadResponse; -import com.hellomeritz.chat.global.uploader.AudioUploader; -import com.hellomeritz.chat.repository.chatentry.ChatRoomEntryLocalRepository; import com.hellomeritz.chat.repository.chatentry.ChatRoomEntryRepository; import com.hellomeritz.chat.repository.chatmessage.ChatMessageRepository; import com.hellomeritz.chat.repository.chatmessage.dto.ChatMessageGetRepositoryResponses; @@ -26,13 +14,11 @@ import com.hellomeritz.chat.repository.chatsession.ChatSessionRepository; import com.hellomeritz.chat.service.dto.param.*; import com.hellomeritz.chat.service.dto.result.*; -import com.hellomeritz.global.CircuitBreakerBot; import com.hellomeritz.member.global.IpSensor; -import com.hellomeritz.member.global.encryption.PasswordEncoder; -import com.hellomeritz.member.global.encryption.dto.EncryptionResponse; +import com.hellomeritz.global.encryption.PasswordEncoder; +import com.hellomeritz.global.encryption.dto.EncryptionResponse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCheckParam.java b/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCheckParam.java index 32dbebb..6ee60ea 100644 --- a/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCheckParam.java +++ b/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCheckParam.java @@ -1,8 +1,8 @@ package com.hellomeritz.chat.service.dto.param; -import com.hellomeritz.chat.domain.ChatRoomPassword; +import com.hellomeritz.global.encryption.PassWord; import com.hellomeritz.chat.repository.chatroom.dto.ChatRoomPasswordInfo; -import com.hellomeritz.member.global.encryption.dto.PasswordMatchRequest; +import com.hellomeritz.global.encryption.dto.PasswordMatchRequest; import java.time.LocalDateTime; @@ -20,7 +20,7 @@ public PasswordMatchRequest toPasswordMatchRequest(ChatRoomPasswordInfo chatRoom public ChatRoomPasswordCreateParam toChatRoomPasswordCreateRequest() { return new ChatRoomPasswordCreateParam( - ChatRoomPassword.of(chatRoomPassword), + PassWord.of(chatRoomPassword), chatRoomId, LocalDateTime.now() ); diff --git a/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCreateParam.java b/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCreateParam.java index 6b2537c..9d6c6fa 100644 --- a/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCreateParam.java +++ b/src/main/java/com/hellomeritz/chat/service/dto/param/ChatRoomPasswordCreateParam.java @@ -1,18 +1,18 @@ package com.hellomeritz.chat.service.dto.param; -import com.hellomeritz.chat.domain.ChatRoomPassword; -import com.hellomeritz.member.global.encryption.dto.EncryptionRequest; +import com.hellomeritz.global.encryption.PassWord; +import com.hellomeritz.global.encryption.dto.EncryptionRequest; import java.time.LocalDateTime; public record ChatRoomPasswordCreateParam( - ChatRoomPassword chatRoomPassWord, + PassWord chatRoomPassWord, long chatRoomId, LocalDateTime enterChatRoomDateTime ) { public EncryptionRequest toEncryptionRequest(String ipAddress) { return new EncryptionRequest( - chatRoomPassWord.getChatRoomPassword(), + chatRoomPassWord.getPassword(), ipAddress, enterChatRoomDateTime ); diff --git a/src/main/java/com/hellomeritz/chat/domain/ChatRoomPassword.java b/src/main/java/com/hellomeritz/global/encryption/PassWord.java similarity index 63% rename from src/main/java/com/hellomeritz/chat/domain/ChatRoomPassword.java rename to src/main/java/com/hellomeritz/global/encryption/PassWord.java index ced94a4..d10c245 100644 --- a/src/main/java/com/hellomeritz/chat/domain/ChatRoomPassword.java +++ b/src/main/java/com/hellomeritz/global/encryption/PassWord.java @@ -1,47 +1,47 @@ -package com.hellomeritz.chat.domain; +package com.hellomeritz.global.encryption; import lombok.Getter; import org.springframework.util.Assert; @Getter -public class ChatRoomPassword { +public class PassWord { private static final int MIN_PASSWORD_LENGTH = 10; - private String chatRoomPassword; + private String password; - private ChatRoomPassword() { + private PassWord() { } - private ChatRoomPassword( - String chatRoomPassword + private PassWord( + String password ) { - this.chatRoomPassword = chatRoomPassword; + this.password = password; } - public static ChatRoomPassword of( - String chatRoomPassword + public static PassWord of( + String password ) { - Assert.hasLength(chatRoomPassword, "비밀번호는 빈값이나 null 일 수 없습니다."); - checkLength(chatRoomPassword); - isContainEngAndSpecialCharAndDigit(chatRoomPassword); + Assert.hasLength(password, "비밀번호는 빈값이나 null 일 수 없습니다."); + checkLength(password); + isContainEngAndSpecialCharAndDigit(password); - return new ChatRoomPassword(chatRoomPassword); + return new PassWord(password); } - private static void checkLength(String chatRoomPassword) { - int passwordLength = chatRoomPassword.length(); + private static void checkLength(String password) { + int passwordLength = password.length(); if (passwordLength < MIN_PASSWORD_LENGTH) { throw new IllegalArgumentException(String.format("password의 자릿수가 %d를 넘지 않습니다. " + "현재 자릿수는 %d 입니다.", MIN_PASSWORD_LENGTH, passwordLength)); } } - private static void isContainEngAndSpecialCharAndDigit(String chatRoomPassword) { + private static void isContainEngAndSpecialCharAndDigit(String password) { boolean hasEnglish = false; boolean hasSpecialCharacter = false; boolean hasDigit = false; - for (char ch : chatRoomPassword.toCharArray()) { + for (char ch : password.toCharArray()) { if (Character.isLetter(ch)) { hasEnglish = true; } else if (Character.isDigit(ch)) { diff --git a/src/main/java/com/hellomeritz/member/global/encryption/PasswordEncoder.java b/src/main/java/com/hellomeritz/global/encryption/PasswordEncoder.java similarity index 86% rename from src/main/java/com/hellomeritz/member/global/encryption/PasswordEncoder.java rename to src/main/java/com/hellomeritz/global/encryption/PasswordEncoder.java index ee4dedc..100f63e 100644 --- a/src/main/java/com/hellomeritz/member/global/encryption/PasswordEncoder.java +++ b/src/main/java/com/hellomeritz/global/encryption/PasswordEncoder.java @@ -1,10 +1,9 @@ -package com.hellomeritz.member.global.encryption; +package com.hellomeritz.global.encryption; -import com.hellomeritz.member.global.encryption.dto.EncryptionRequest; -import com.hellomeritz.member.global.encryption.dto.EncryptionResponse; -import com.hellomeritz.member.global.encryption.dto.PasswordMatchRequest; -import com.hellomeritz.member.global.encryption.dto.SaltRequest; -import org.springframework.beans.factory.annotation.Value; +import com.hellomeritz.global.encryption.dto.EncryptionResponse; +import com.hellomeritz.global.encryption.dto.SaltRequest; +import com.hellomeritz.global.encryption.dto.EncryptionRequest; +import com.hellomeritz.global.encryption.dto.PasswordMatchRequest; import org.springframework.stereotype.Component; import javax.crypto.SecretKeyFactory; diff --git a/src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionRequest.java b/src/main/java/com/hellomeritz/global/encryption/dto/EncryptionRequest.java similarity index 88% rename from src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionRequest.java rename to src/main/java/com/hellomeritz/global/encryption/dto/EncryptionRequest.java index 4d01c7f..89d0ebb 100644 --- a/src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionRequest.java +++ b/src/main/java/com/hellomeritz/global/encryption/dto/EncryptionRequest.java @@ -1,4 +1,4 @@ -package com.hellomeritz.member.global.encryption.dto; +package com.hellomeritz.global.encryption.dto; import java.time.LocalDateTime; diff --git a/src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionResponse.java b/src/main/java/com/hellomeritz/global/encryption/dto/EncryptionResponse.java similarity index 80% rename from src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionResponse.java rename to src/main/java/com/hellomeritz/global/encryption/dto/EncryptionResponse.java index f692b1e..a8fe15f 100644 --- a/src/main/java/com/hellomeritz/member/global/encryption/dto/EncryptionResponse.java +++ b/src/main/java/com/hellomeritz/global/encryption/dto/EncryptionResponse.java @@ -1,4 +1,4 @@ -package com.hellomeritz.member.global.encryption.dto; +package com.hellomeritz.global.encryption.dto; public record EncryptionResponse( String password, diff --git a/src/main/java/com/hellomeritz/member/global/encryption/dto/PasswordMatchRequest.java b/src/main/java/com/hellomeritz/global/encryption/dto/PasswordMatchRequest.java similarity index 71% rename from src/main/java/com/hellomeritz/member/global/encryption/dto/PasswordMatchRequest.java rename to src/main/java/com/hellomeritz/global/encryption/dto/PasswordMatchRequest.java index c37b4ce..7c6423c 100644 --- a/src/main/java/com/hellomeritz/member/global/encryption/dto/PasswordMatchRequest.java +++ b/src/main/java/com/hellomeritz/global/encryption/dto/PasswordMatchRequest.java @@ -1,4 +1,4 @@ -package com.hellomeritz.member.global.encryption.dto; +package com.hellomeritz.global.encryption.dto; public record PasswordMatchRequest( char[] inputPassword, diff --git a/src/main/java/com/hellomeritz/member/global/encryption/dto/SaltRequest.java b/src/main/java/com/hellomeritz/global/encryption/dto/SaltRequest.java similarity index 78% rename from src/main/java/com/hellomeritz/member/global/encryption/dto/SaltRequest.java rename to src/main/java/com/hellomeritz/global/encryption/dto/SaltRequest.java index ddf1d8b..3b96d0a 100644 --- a/src/main/java/com/hellomeritz/member/global/encryption/dto/SaltRequest.java +++ b/src/main/java/com/hellomeritz/global/encryption/dto/SaltRequest.java @@ -1,4 +1,4 @@ -package com.hellomeritz.member.global.encryption.dto; +package com.hellomeritz.global.encryption.dto; public record SaltRequest( String ipAddress, From cc0830b07fff64437bcce29e4e6ce881a9ea3c20 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 11:53:09 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat=20:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EC=83=81=EB=8B=B4=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberService.java | 40 ++++++++++++++++++- .../dto/param/ConsultantLoginParam.java | 29 ++++++++++++++ .../dto/param/ConsultantSignUpParam.java | 37 +++++++++++++++++ .../dto/result/ConsultantLoginResult.java | 16 ++++++++ .../dto/result/ConsultantSignUpResult.java | 9 +++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hellomeritz/member/service/dto/param/ConsultantLoginParam.java create mode 100644 src/main/java/com/hellomeritz/member/service/dto/param/ConsultantSignUpParam.java create mode 100644 src/main/java/com/hellomeritz/member/service/dto/result/ConsultantLoginResult.java create mode 100644 src/main/java/com/hellomeritz/member/service/dto/result/ConsultantSignUpResult.java diff --git a/src/main/java/com/hellomeritz/member/service/MemberService.java b/src/main/java/com/hellomeritz/member/service/MemberService.java index 8da5f76..f81f216 100644 --- a/src/main/java/com/hellomeritz/member/service/MemberService.java +++ b/src/main/java/com/hellomeritz/member/service/MemberService.java @@ -3,6 +3,8 @@ import com.hellomeritz.member.domain.FinancialConsultant; import com.hellomeritz.member.domain.Foreigner; import com.hellomeritz.member.global.IpSensor; +import com.hellomeritz.global.encryption.PasswordEncoder; +import com.hellomeritz.global.encryption.dto.EncryptionResponse; import com.hellomeritz.member.global.sms.SmsManager; import com.hellomeritz.member.repository.fc.FinancialConsultantRepository; import com.hellomeritz.member.repository.foreign.ForeignRepository; @@ -24,11 +26,14 @@ public class MemberService { private final IpSensor ipSensor; private final SmsManager smsManager; - public MemberService(ForeignRepository foreignRepository, FinancialConsultantRepository financialConsultantRepository, IpSensor ipSensor, SmsManager smsManager) { + private final PasswordEncoder passwordEncoder; + + public MemberService(ForeignRepository foreignRepository, FinancialConsultantRepository financialConsultantRepository, IpSensor ipSensor, SmsManager smsManager, PasswordEncoder passwordEncoder) { this.foreignRepository = foreignRepository; this.financialConsultantRepository = financialConsultantRepository; this.ipSensor = ipSensor; this.smsManager = smsManager; + this.passwordEncoder = passwordEncoder; } @Transactional @@ -82,6 +87,39 @@ private FinancialConsultant selectConsultant(List consultan return consultants.get(randomIndex); } + @Transactional + public ConsultantSignUpResult signUp(ConsultantSignUpParam param) { + String clientIp = ipSensor.getClientIP(); + EncryptionResponse encryptionResponse + = passwordEncoder.encrypt(param.toEncryptionRequest(clientIp)); + + FinancialConsultant savedFinancialConsultant + = financialConsultantRepository.save(param.toFinancialConsultant(encryptionResponse)); + + return ConsultantSignUpResult.to(savedFinancialConsultant.getFinancialConsultantId()); + } + + @Transactional + public ConsultantLoginResult login(ConsultantLoginParam param) { + FinancialConsultant financialConsultant + = financialConsultantRepository.getByEmployeeNumber(param.employeeNumber()); + boolean isAuthorized = passwordEncoder.matchPassword(param.toPasswordMatchRequest(financialConsultant)); + + if (isAuthorized) { + createConsultantPassword(param, financialConsultant); + } + return ConsultantLoginResult.to(isAuthorized, financialConsultant.getFinancialConsultantId()); + } + + public void createConsultantPassword(ConsultantLoginParam param, FinancialConsultant financialConsultant) { + String clientIp = ipSensor.getClientIP(); + EncryptionResponse encryptionResponse + = passwordEncoder.encrypt(param.toEncryptionRequest(clientIp)); + + financialConsultant.changePassword(encryptionResponse.password()); + financialConsultant.changeSalt(encryptionResponse.salt()); + } + @Transactional public void endConsultation(FinancialConsultantChangeStateParam param) { FinancialConsultant financialConsultant = financialConsultantRepository.getFinancialConsultant(param.fcId()); diff --git a/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantLoginParam.java b/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantLoginParam.java new file mode 100644 index 0000000..351eca4 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantLoginParam.java @@ -0,0 +1,29 @@ +package com.hellomeritz.member.service.dto.param; + +import com.hellomeritz.member.domain.FinancialConsultant; +import com.hellomeritz.global.encryption.dto.EncryptionRequest; +import com.hellomeritz.global.encryption.dto.PasswordMatchRequest; + +import java.time.LocalDateTime; + +public record ConsultantLoginParam( + String employeeNumber, + String consultantPassword +) { + public PasswordMatchRequest toPasswordMatchRequest(FinancialConsultant financialConsultant) { + return new PasswordMatchRequest( + consultantPassword.toCharArray(), + financialConsultant.getConsultantPassword(), + financialConsultant.getSalt() + ); + } + + public EncryptionRequest toEncryptionRequest(String clientIp) { + return new EncryptionRequest( + consultantPassword, + clientIp, + LocalDateTime.now() + ); + } + +} diff --git a/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantSignUpParam.java b/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantSignUpParam.java new file mode 100644 index 0000000..3001d8e --- /dev/null +++ b/src/main/java/com/hellomeritz/member/service/dto/param/ConsultantSignUpParam.java @@ -0,0 +1,37 @@ +package com.hellomeritz.member.service.dto.param; + +import com.hellomeritz.global.encryption.PassWord; +import com.hellomeritz.member.domain.FinancialConsultant; +import com.hellomeritz.global.encryption.dto.EncryptionRequest; +import com.hellomeritz.global.encryption.dto.EncryptionResponse; + +import java.time.LocalDateTime; + +public record ConsultantSignUpParam( + String employeeNumber, + PassWord consultantPassword, + String phoneNumber, + String name, + String profileUrl, + String introduceMessage +) { + public FinancialConsultant toFinancialConsultant(EncryptionResponse response) { + return FinancialConsultant.of( + employeeNumber, + response.password(), + response.salt(), + phoneNumber, + name, + profileUrl, + introduceMessage + ); + } + + public EncryptionRequest toEncryptionRequest(String clientIp) { + return new EncryptionRequest( + consultantPassword.getPassword(), + clientIp, + LocalDateTime.now() + ); + } +} diff --git a/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantLoginResult.java b/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantLoginResult.java new file mode 100644 index 0000000..c2cce24 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantLoginResult.java @@ -0,0 +1,16 @@ +package com.hellomeritz.member.service.dto.result; + +public record ConsultantLoginResult( + boolean isMyConsultant, + long fcId +) { + public static ConsultantLoginResult to( + Boolean isMyConsultant, + Long fcId + ){ + return new ConsultantLoginResult( + isMyConsultant, + fcId + ); + } +} diff --git a/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantSignUpResult.java b/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantSignUpResult.java new file mode 100644 index 0000000..21a11b0 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/service/dto/result/ConsultantSignUpResult.java @@ -0,0 +1,9 @@ +package com.hellomeritz.member.service.dto.result; + +public record ConsultantSignUpResult( + Long fcId +) { + public static ConsultantSignUpResult to(Long fcId) { + return new ConsultantSignUpResult(fcId); + } +} From 8d5a8ac75a7fadbcdb4d657b1682e467ea196c18 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 11:54:02 +0900 Subject: [PATCH 05/19] =?UTF-8?q?test=20:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EC=83=81=EB=8B=B4=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hellomeritz/member/service/MemberServiceTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java b/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java index f001c64..712267b 100644 --- a/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java +++ b/src/test/java/com/hellomeritz/member/service/MemberServiceTest.java @@ -106,7 +106,6 @@ void changeConsultationState() { // then assertThat(changedFinancialConsultant.getConsultationState()).isEqualTo(ConsultationState.AVAILABLE.name()); - - } + } From 618036016a93765f21518bcb6b0ff533b9f95674 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 11:57:41 +0900 Subject: [PATCH 06/19] =?UTF-8?q?feat=20:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EC=82=AC=EC=9B=90=EB=B2=88=ED=98=B8=EB=A1=9C=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=BF=BC=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/fc/FinancialConsultantJapRepository.java | 4 ++++ .../member/repository/fc/FinancialConsultantRepository.java | 2 ++ .../repository/fc/FinancialConsultantRepositoryImpl.java | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantJapRepository.java b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantJapRepository.java index a4cb79b..96f30ca 100644 --- a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantJapRepository.java +++ b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantJapRepository.java @@ -5,8 +5,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface FinancialConsultantJapRepository extends JpaRepository { @@ -14,4 +16,6 @@ public interface FinancialConsultantJapRepository extends JpaRepository findFinancialConsultant(); + Optional findByEmployeeNumber (String employeeNumber); + } diff --git a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepository.java b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepository.java index a36a0cf..0fdb52d 100644 --- a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepository.java +++ b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepository.java @@ -14,4 +14,6 @@ public interface FinancialConsultantRepository { List getFinancialConsultantWithAvailable(); + FinancialConsultant getByEmployeeNumber (String employeeNumber); + } diff --git a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepositoryImpl.java b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepositoryImpl.java index 3b6efb0..0371412 100644 --- a/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepositoryImpl.java +++ b/src/main/java/com/hellomeritz/member/repository/fc/FinancialConsultantRepositoryImpl.java @@ -34,4 +34,10 @@ public List getFinancialConsultantWithAvailable() { return financialConsultants; } + @Override + public FinancialConsultant getByEmployeeNumber(String employeeNumber) { + return financialConsultantJapRepository.findByEmployeeNumber(employeeNumber) + .orElseThrow(()-> new EntityNotFoundException("해당하는 설계사는 존재하지 않습니다.")); + } + } From 045f6959242bf28c708fe7df4e80e5fb6761330c Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:01:37 +0900 Subject: [PATCH 07/19] =?UTF-8?q?refactor=20:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B0=8F=20salt=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/domain/FinancialConsultant.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/main/java/com/hellomeritz/member/domain/FinancialConsultant.java b/src/main/java/com/hellomeritz/member/domain/FinancialConsultant.java index 9503094..9feff29 100644 --- a/src/main/java/com/hellomeritz/member/domain/FinancialConsultant.java +++ b/src/main/java/com/hellomeritz/member/domain/FinancialConsultant.java @@ -1,7 +1,9 @@ package com.hellomeritz.member.domain; +import com.hellomeritz.global.encryption.PassWord; import jakarta.persistence.*; import lombok.Getter; +import org.springframework.util.Assert; @Table(name = "financial_consultant") @Getter @@ -28,6 +30,15 @@ public class FinancialConsultant { @Column(name = "consultation_state") private String consultationState; + @Column(name = "consultant_password") + private String consultantPassword; + + @Column(name = "salt") + private String salt; + + @Column(name = "employee_number") + private String employeeNumber; + protected FinancialConsultant() { } @@ -37,6 +48,11 @@ private FinancialConsultant( String profileUrl, String introduceMessage ) { + Assert.hasLength(phoneNumber, "phoneNumber는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(name, "name는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(profileUrl, "profileUrl는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(introduceMessage, "introduceMessage는 null이거나 빈값일 수 없습니다."); + this.phoneNumber = phoneNumber; this.name = name; this.profileUrl = profileUrl; @@ -58,6 +74,53 @@ public static FinancialConsultant of( ); } + private FinancialConsultant( + String employeeNumber, + String consultantPassword, + String salt, + String phoneNumber, + String name, + String profileUrl, + String introduceMessage + ) { + Assert.hasLength(employeeNumber, "employeeNumber는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(consultantPassword, "consultantPassword는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(salt, "salt는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(phoneNumber, "phoneNumber는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(name, "name는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(profileUrl, "profileUrl는 null이거나 빈값일 수 없습니다."); + Assert.hasLength(introduceMessage, "introduceMessage는 null이거나 빈값일 수 없습니다."); + + this.employeeNumber = employeeNumber; + this.consultantPassword = consultantPassword; + this.salt = salt; + this.phoneNumber = phoneNumber; + this.name = name; + this.profileUrl = profileUrl; + this.introduceMessage = introduceMessage; + this.consultationState = ConsultationState.AVAILABLE.name(); + } + + public static FinancialConsultant of( + String employeeNumber, + String consultantPassword, + String salt, + String phoneNumber, + String name, + String profileUrl, + String introduceMessage + ) { + return new FinancialConsultant( + employeeNumber, + consultantPassword, + salt, + phoneNumber, + name, + profileUrl, + introduceMessage + ); + } + public void startConsulting() { this.consultationState = ConsultationState.UNAVAILABLE.name(); } @@ -66,4 +129,11 @@ public void endConsulting() { this.consultationState = ConsultationState.AVAILABLE.name(); } + public void changePassword(String consultantPassword) { + this.consultantPassword = consultantPassword; + } + + public void changeSalt(String salt) { + this.salt = salt; + } } From aee191648e8d51aecccbcdc212e084b5f7627dea Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:02:15 +0900 Subject: [PATCH 08/19] =?UTF-8?q?feat=20:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EC=83=81=EB=8B=B4=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=94=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsultantController.java | 39 +++++++++++++++++-- .../dto/request/ConsultantLoginRequest.java | 20 ++++++++++ .../dto/request/ConsultantSignUpRequest.java | 37 ++++++++++++++++++ .../dto/response/ConsultantLoginResponse.java | 16 ++++++++ .../response/ConsultantSignUpResponse.java | 11 ++++++ 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantLoginRequest.java create mode 100644 src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantSignUpRequest.java create mode 100644 src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantLoginResponse.java create mode 100644 src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantSignUpResponse.java diff --git a/src/main/java/com/hellomeritz/member/controller/ConsultantController.java b/src/main/java/com/hellomeritz/member/controller/ConsultantController.java index fc55237..71ecdc4 100644 --- a/src/main/java/com/hellomeritz/member/controller/ConsultantController.java +++ b/src/main/java/com/hellomeritz/member/controller/ConsultantController.java @@ -1,8 +1,15 @@ package com.hellomeritz.member.controller; +import com.hellomeritz.member.controller.dto.request.ConsultantLoginRequest; +import com.hellomeritz.member.controller.dto.request.ConsultantSignUpRequest; +import com.hellomeritz.member.controller.dto.response.ConsultantLoginResponse; import com.hellomeritz.member.controller.dto.response.ConsultantMatchResponse; +import com.hellomeritz.member.controller.dto.response.ConsultantSignUpResponse; import com.hellomeritz.member.service.MemberService; +import com.hellomeritz.member.service.dto.param.ConsultantLoginParam; import com.hellomeritz.member.service.dto.param.FinancialConsultantChangeStateParam; +import com.hellomeritz.member.service.dto.result.ConsultantSignUpResult; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import org.springframework.http.HttpStatus; @@ -23,7 +30,7 @@ public ConsultantController(MemberService memberService) { @PatchMapping( produces = MediaType.APPLICATION_JSON_VALUE ) - public ResponseEntity matchConsultant(){ + public ResponseEntity matchConsultant() { return ResponseEntity.status(HttpStatus.OK) .body(ConsultantMatchResponse.to(memberService.matchConsultant())); } @@ -32,8 +39,8 @@ public ResponseEntity matchConsultant(){ path = "/{fcId}" ) public ResponseEntity endConsultation( - @Positive (message = "fcId는 양수여야 합니다.") - @NotNull (message = "fcId는 null일 수 없습니다.") + @Positive(message = "fcId는 양수여야 합니다.") + @NotNull(message = "fcId는 null일 수 없습니다.") @PathVariable Long fcId ) { memberService.endConsultation(FinancialConsultantChangeStateParam.to(fcId)); @@ -42,4 +49,30 @@ public ResponseEntity endConsultation( .build(); } + @PostMapping( + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity signUp( + @Valid @RequestBody ConsultantSignUpRequest request + ) { + ConsultantSignUpResult consultantSignUpResult = memberService.signUp(request.toConsultantSignUpParam()); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(ConsultantSignUpResponse.to(consultantSignUpResult)); + } + + @PatchMapping( + path = "/passwords", + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity login( + @Valid @RequestBody ConsultantLoginRequest request + ) { + + return ResponseEntity.status(HttpStatus.OK) + .body(ConsultantLoginResponse.to(memberService.login(request.toConsultantLoginParam()))); + } + } diff --git a/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantLoginRequest.java b/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantLoginRequest.java new file mode 100644 index 0000000..59d4c33 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantLoginRequest.java @@ -0,0 +1,20 @@ +package com.hellomeritz.member.controller.dto.request; + +import com.hellomeritz.member.service.dto.param.ConsultantLoginParam; +import jakarta.validation.constraints.NotBlank; + +public record ConsultantLoginRequest( + + @NotBlank(message = "consultantPassword는 null이거나 빈값일 수 없습니다.") + String consultantPassword, + + @NotBlank(message = "employNumber인 사원번호는 null이거나 빈값일 수 없습니다.") + String employeeNumber +) { + public ConsultantLoginParam toConsultantLoginParam() { + return new ConsultantLoginParam( + employeeNumber, + consultantPassword + ); + } +} diff --git a/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantSignUpRequest.java b/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantSignUpRequest.java new file mode 100644 index 0000000..bed7603 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/controller/dto/request/ConsultantSignUpRequest.java @@ -0,0 +1,37 @@ +package com.hellomeritz.member.controller.dto.request; + +import com.hellomeritz.global.encryption.PassWord; +import com.hellomeritz.member.service.dto.param.ConsultantSignUpParam; +import jakarta.validation.constraints.NotBlank; + +public record ConsultantSignUpRequest( + + @NotBlank(message = "employNumber인 사원번호는 null이거나 빈값일 수 없습니다.") + String employeeNumber, + + @NotBlank(message = "consultantPassword는 null이거나 빈값일 수 없습니다.") + String consultantPassword, + + @NotBlank(message = "phoneNumber는 null이거나 빈값일 수 없습니다.") + String phoneNumber, + + @NotBlank(message = "name호는 null이거나 빈값일 수 없습니다.") + String name, + + @NotBlank(message = "profileUrl는 null이거나 빈값일 수 없습니다.") + String profileUrl, + + @NotBlank(message = "introduceMessage는 null이거나 빈값일 수 없습니다.") + String introduceMessage +) { + public ConsultantSignUpParam toConsultantSignUpParam() { + return new ConsultantSignUpParam( + employeeNumber, + PassWord.of(consultantPassword), + phoneNumber, + name, + profileUrl, + introduceMessage + ); + } +} diff --git a/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantLoginResponse.java b/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantLoginResponse.java new file mode 100644 index 0000000..113a0b5 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantLoginResponse.java @@ -0,0 +1,16 @@ +package com.hellomeritz.member.controller.dto.response; + +import com.hellomeritz.member.service.dto.result.ConsultantLoginResult; + +public record ConsultantLoginResponse( + boolean isMyConsultant, + long fcId +) { + + public static ConsultantLoginResponse to(ConsultantLoginResult result) { + return new ConsultantLoginResponse( + result.isMyConsultant(), + result.fcId() + ); + } +} diff --git a/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantSignUpResponse.java b/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantSignUpResponse.java new file mode 100644 index 0000000..4f72067 --- /dev/null +++ b/src/main/java/com/hellomeritz/member/controller/dto/response/ConsultantSignUpResponse.java @@ -0,0 +1,11 @@ +package com.hellomeritz.member.controller.dto.response; + +import com.hellomeritz.member.service.dto.result.ConsultantSignUpResult; + +public record ConsultantSignUpResponse( + long fcId +) { + public static ConsultantSignUpResponse to(ConsultantSignUpResult result) { + return new ConsultantSignUpResponse(result.fcId()); + } +} From f3d0a460545602adfe996164343a8adc9bac6ca8 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:03:03 +0900 Subject: [PATCH 09/19] =?UTF-8?q?test=20:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EC=83=81=EB=8B=B4=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=94=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?rest=20docs=20test=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConsultantControllerDocsTest.java | 63 +++++++++++++++++-- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java b/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java index 5e828cc..ede157e 100644 --- a/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java +++ b/src/test/java/com/hellomeritz/member/controller/ConsultantControllerDocsTest.java @@ -4,7 +4,9 @@ import com.hellomeritz.global.ForeignFixture; import com.hellomeritz.global.RestDocsSupport; import com.hellomeritz.member.service.MemberService; +import com.hellomeritz.member.service.dto.result.ConsultantLoginResult; import com.hellomeritz.member.service.dto.result.ConsultantMatchResult; +import com.hellomeritz.member.service.dto.result.ConsultantSignUpResult; import com.hellomeritz.member.service.dto.result.UserCheckIsFcResult; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,10 +19,8 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -29,6 +29,7 @@ class ConsultantControllerDocsTest extends RestDocsSupport { MemberService memberService = mock(MemberService.class); + @Override protected Object initController() { return new ConsultantController(memberService); @@ -55,7 +56,7 @@ void checkUserIsFc() throws Exception { @DisplayName("상담이 끝나면 상담원의 상태를 가능 상태로 변경하는 API") @Test void endConsultation() throws Exception { - mockMvc.perform(put("/consultants/{fcId}",1L) + mockMvc.perform(put("/consultants/{fcId}", 1L) .contentType(MediaType.APPLICATION_JSON) .characterEncoding(StandardCharsets.UTF_8)) .andDo(print()) @@ -67,4 +68,56 @@ void endConsultation() throws Exception { )); } + @DisplayName("상담원 회원가입 API") + @Test + void signUp() throws Exception { + ConsultantSignUpResult result = FinancialConsultantFixture.consultantSignUpResult(); + given(memberService.signUp(any())).willReturn(result); + + mockMvc.perform(post("/consultants") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8) + .content(objectMapper.writeValueAsString(FinancialConsultantFixture.consultantSignUpRequest()))) + .andDo(print()) + .andExpect(status().isCreated()) + .andDo(document("sign-up-consultant", + requestFields( + fieldWithPath("employeeNumber").type(JsonFieldType.STRING).description("설계사 사원번호"), + fieldWithPath("consultantPassword").type(JsonFieldType.STRING).description("설계사 비밀번호"), + fieldWithPath("phoneNumber").type(JsonFieldType.STRING).description("설계사 전화번호"), + fieldWithPath("name").type(JsonFieldType.STRING).description("이름"), + fieldWithPath("profileUrl").type(JsonFieldType.STRING).description("프로필 url"), + fieldWithPath("introduceMessage").type(JsonFieldType.STRING).description("소개 메세지") + + ), + responseFields( + fieldWithPath("fcId").type(JsonFieldType.NUMBER).description("회원 가입된 상담원의 ID") + ) + )); + } + + @DisplayName("상담원 로그인 API") + @Test + void login() throws Exception { + ConsultantLoginResult result = FinancialConsultantFixture.consultantLoginResult(); + given(memberService.login(any())).willReturn(result); + + mockMvc.perform(patch("/consultants/passwords") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8) + .content(objectMapper.writeValueAsString(FinancialConsultantFixture.consultantLoginRequest()))) + .andDo(print()) + .andExpect(status().isOk()) + .andDo(document("login-consultant", + requestFields( + fieldWithPath("employeeNumber").type(JsonFieldType.STRING).description("설계사 사원번호"), + fieldWithPath("consultantPassword").type(JsonFieldType.STRING).description("설계사 비밀번호") + ), + responseFields( + fieldWithPath("isMyConsultant").type(JsonFieldType.BOOLEAN).description("로그인한 사람이 우리 상담원인가?"), + fieldWithPath("fcId").type(JsonFieldType.NUMBER).description("설계사 ID") + ) + )); + } + } From 7d5e0f18a262c639b2420318ca8fbbf75bf13144 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:04:01 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20=EC=83=81=EB=8B=B4=EC=9B=90=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/static/js/consultant-login.js | 33 +++++++++++ .../static/styles/consultant-login.css | 56 +++++++++++++++++++ .../static/templates/consultant-login.html | 27 +++++++++ 3 files changed, 116 insertions(+) create mode 100644 src/main/resources/static/js/consultant-login.js create mode 100644 src/main/resources/static/styles/consultant-login.css create mode 100644 src/main/resources/static/templates/consultant-login.html diff --git a/src/main/resources/static/js/consultant-login.js b/src/main/resources/static/js/consultant-login.js new file mode 100644 index 0000000..754e8c7 --- /dev/null +++ b/src/main/resources/static/js/consultant-login.js @@ -0,0 +1,33 @@ +document.getElementById('loginForm').addEventListener('submit', function(event) { + event.preventDefault(); + + var employeeNumber = document.getElementById('employeeNumber').value; + var password = document.getElementById('password').value; + + var url = '/consultants/passwords'; + var requestBody = { + employeeNumber: employeeNumber, + consultantPassword : password + }; + + fetch(url, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }) + .then(response => response.json()) + .then(data => { + if (data.isMyConsultant === true) { + window.location.href = `/templates/consultant-chatrooms.html?fcId=${data.fcId}`; + } else { + // 로그인 실패 알림창 띄우기 + alert('로그인에 실패했습니다. 사용자 이름과 비밀번호를 확인해주세요.'); + } + }) + .catch(error => { + console.error('Error:', error); + alert('로그인에 실패했습니다. 사용자 이름과 비밀번호를 확인해주세요.'); + }); +}); \ No newline at end of file diff --git a/src/main/resources/static/styles/consultant-login.css b/src/main/resources/static/styles/consultant-login.css new file mode 100644 index 0000000..0d38dfe --- /dev/null +++ b/src/main/resources/static/styles/consultant-login.css @@ -0,0 +1,56 @@ +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 0; +} + +.container { + max-width: 400px; + margin: 100px auto; + padding: 20px; + background-color: #fff; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +form { + text-align: center; +} + +input[type="text"], +input[type="password"] { + width: 100%; + padding: 10px; + margin: 8px 0; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + width: 100%; + background-color: #4caf50; + color: white; + padding: 14px 20px; + margin: 8px 0; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; +} + +button:hover { + background-color: #45a049; +} + +.input-group { + margin-bottom: 16px; + text-align: left; +} + +.input-group label { + display: block; + margin-bottom: 6px; + font-weight: bold; +} \ No newline at end of file diff --git a/src/main/resources/static/templates/consultant-login.html b/src/main/resources/static/templates/consultant-login.html new file mode 100644 index 0000000..18d8fe7 --- /dev/null +++ b/src/main/resources/static/templates/consultant-login.html @@ -0,0 +1,27 @@ + + + + + + 상담원 로그인 + + + +
+
+

상담원 로그인

+
+ + +
+
+ + +
+ +
+
+ + + + \ No newline at end of file From 49d2400ab10ebe0357692f89c7046d5c4ee258ea Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:04:54 +0900 Subject: [PATCH 11/19] =?UTF-8?q?refactor=20:=20path=20variable=EB=A1=9C?= =?UTF-8?q?=20fcid=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/js/consultant-chatrooms.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/resources/static/js/consultant-chatrooms.js b/src/main/resources/static/js/consultant-chatrooms.js index f1a4d6b..6343153 100644 --- a/src/main/resources/static/js/consultant-chatrooms.js +++ b/src/main/resources/static/js/consultant-chatrooms.js @@ -1,8 +1,16 @@ let userId = 1; +function getUserIdFromUrl() { + var queryString = window.location.search; + var urlParams = new URLSearchParams(queryString); + return urlParams.get('fcId'); +} + + document.addEventListener("DOMContentLoaded", function() { getChatRooms(); getConsultantInfo(); + userId = getUserIdFromUrl(); }); async function getChatRooms() { From aea4c791752d118655794aca4db54129d7e54bb3 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:05:39 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat=20:=20=EC=83=81=EB=8B=B4=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=83=81=ED=83=9C=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/static/js/consultant-chat.js | 38 ++++++++++++++++++- src/main/resources/static/styles/chat.css | 17 +++++++++ .../static/templates/consultant.html | 3 ++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/js/consultant-chat.js b/src/main/resources/static/js/consultant-chat.js index 20167ae..32454ba 100644 --- a/src/main/resources/static/js/consultant-chat.js +++ b/src/main/resources/static/js/consultant-chat.js @@ -247,4 +247,40 @@ async function leaveChatRoom() { console.error('There was a problem with the fetch operation:', error); throw error; } -} \ No newline at end of file +} + +function completeConsultation() { + var url = `/consultants/${consultantId}`; + + // PUT 요청 보내기 + fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({}) + }) + .then(response => { + endMessages(); + }) + .catch(error => { + console.error('Error:', error); + alert('상담 완료 요청을 처리하는 동안 오류가 발생했습니다. 다시 시도해주세요.'); + }); +} + +function endMessages() { + const messageList = document.getElementById('message-list'); + + const originMessageElement = document.createElement('div'); + originMessageElement.className = 'message sent'; + + const originContentElement = document.createElement('div'); + originContentElement.className = 'message-content'; + originContentElement.textContent = '상담이 완료되었습니다.✅ 다른 궁금한 사항이 있다면 언제든 다시 물아봐주세요'; + + originMessageElement.appendChild(originContentElement); + + messageList.appendChild(originMessageElement); + +} diff --git a/src/main/resources/static/styles/chat.css b/src/main/resources/static/styles/chat.css index ff3a1bb..3bcac5b 100644 --- a/src/main/resources/static/styles/chat.css +++ b/src/main/resources/static/styles/chat.css @@ -253,3 +253,20 @@ button[onclick="sendMessage()"]:hover { #submitPassword:hover { background-color: #45a049; } + +/*상담완료 버튼*/ +.complete-button { + width: calc(100% - 20px); + margin: 10px; + padding: 10px; + background-color: #ff6347; /* 버튼 배경색 */ + color: white; /* 버튼 텍스트 색상 */ + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s; +} + +.complete-button:hover { + background-color: #d6342b; /* 마우스 호버 시 배경색 */ +} \ No newline at end of file diff --git a/src/main/resources/static/templates/consultant.html b/src/main/resources/static/templates/consultant.html index 50a8a59..af2d983 100644 --- a/src/main/resources/static/templates/consultant.html +++ b/src/main/resources/static/templates/consultant.html @@ -36,8 +36,11 @@
+ + + \ No newline at end of file From d242467c2fa9594d0e18e523768b347f01b9efd0 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:06:57 +0900 Subject: [PATCH 13/19] =?UTF-8?q?style=20:=20rest=20docs=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/members.adoc | 45 +++- src/main/resources/static/docs/chats.html | 2 +- src/main/resources/static/docs/index.html | 267 +++++++++++++++++++- src/main/resources/static/docs/members.html | 249 +++++++++++++++++- 4 files changed, 559 insertions(+), 4 deletions(-) diff --git a/src/docs/asciidoc/members.adoc b/src/docs/asciidoc/members.adoc index b53de5b..d4f27b7 100644 --- a/src/docs/asciidoc/members.adoc +++ b/src/docs/asciidoc/members.adoc @@ -85,4 +85,47 @@ include::{snippets}/match-consultant/http-request.adoc[] === Response (Success) include::{snippets}/match-consultant/http-response.adoc[] -include::{snippets}/match-consultant/response-fields.adoc[] \ No newline at end of file +include::{snippets}/match-consultant/response-fields.adoc[] + + +== 상담원 상담 가능 상태로 바꿔주는 API +상담이 끝나면 상담원의 상담 가능 상태를 +UNAVAILABLE에서 AVAILABLE로 바꿔줍니다. + +=== Request + +include::{snippets}/end-consultation-and-change-available/http-request.adoc[] +include::{snippets}/end-consultation-and-change-available/path-parameters.adoc[] + +=== Response (Success) + +include::{snippets}/end-consultation-and-change-available/http-response.adoc[] + +== 상담원 회원가입 API + +상담원 회원가입 API +사원 번호는 미리 받은 상태입니다. + +=== Request + +include::{snippets}/sign-up-consultant/http-request.adoc[] +include::{snippets}/sign-up-consultant/request-fields.adoc[] + +=== Response (Success) + +include::{snippets}/sign-up-consultant/http-response.adoc[] +include::{snippets}/sign-up-consultant/response-fields.adoc[] + +== 상담원 로그인 API + +상담원 로그인 API + +=== Request + +include::{snippets}/login-consultant/http-request.adoc[] +include::{snippets}/login-consultant/request-fields.adoc[] + +=== Response (Success) + +include::{snippets}/login-consultant/http-response.adoc[] +include::{snippets}/login-consultant/response-fields.adoc[] \ No newline at end of file diff --git a/src/main/resources/static/docs/chats.html b/src/main/resources/static/docs/chats.html index 0454ad5..a8cfd4b 100644 --- a/src/main/resources/static/docs/chats.html +++ b/src/main/resources/static/docs/chats.html @@ -758,7 +758,7 @@

Response (Success)

{ "textBySpeech" : "Hello my name is byeol", - "createdAt" : "2024-05-12T20:06:47.074224800", + "createdAt" : "2024-05-13T12:03:49.480445200", "sttProvider" : "WHISPER" } diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 721120c..0f8c617 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -551,6 +551,24 @@

HELLO-MERITZ API Document

  • Response (Success)
  • +
  • 상담원 상담 가능 상태로 바꿔주는 API + +
  • +
  • 상담원 회원가입 API + +
  • +
  • 상담원 로그인 API + +
  • @@ -873,7 +891,7 @@

    Respons { "textBySpeech" : "Hello my name is byeol", - "createdAt" : "2024-05-12T20:06:47.074224800", + "createdAt" : "2024-05-13T12:03:49.480445200", "sttProvider" : "WHISPER" } @@ -1924,6 +1942,253 @@

    Respo +
    +

    상담원 상담 가능 상태로 바꿔주는 API

    +
    +
    +

    상담이 끝나면 상담원의 상담 가능 상태를 +UNAVAILABLE에서 AVAILABLE로 바꿔줍니다.

    +
    +
    +

    Request

    +
    +
    +
    PUT /consultants/1 HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Host: localhost:8080
    +
    +
    + + ++++ + + + + + + + + + + + + +
    Table 11. /consultants/{fcId}
    ParameterDescription

    fcId

    상담원의 ID

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 200 OK
    +
    +
    +
    +
    +
    +
    +

    상담원 회원가입 API

    +
    +
    +

    상담원 회원가입 API +사원 번호는 미리 받은 상태입니다.

    +
    +
    +

    Request

    +
    +
    +
    POST /consultants HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 251
    +Host: localhost:8080
    +
    +{
    +  "employeeNumber" : "123401343",
    +  "consultantPassword" : "123456789TWE!@",
    +  "phoneNumber" : "01012345678",
    +  "name" : "KIM BYEOL",
    +  "profileUrl" : "https://gcp//meritz-profile/mypicture",
    +  "introduceMessage" : "hello, my name is byeol"
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    employeeNumber

    String

    설계사 사원번호

    consultantPassword

    String

    설계사 비밀번호

    phoneNumber

    String

    설계사 전화번호

    name

    String

    이름

    profileUrl

    String

    프로필 url

    introduceMessage

    String

    소개 메세지

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 201 Created
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 18
    +
    +{
    +  "fcId" : 1
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    fcId

    Number

    회원 가입된 상담원의 ID

    +
    +
    +
    +
    +

    상담원 로그인 API

    +
    +
    +

    상담원 로그인 API

    +
    +
    +

    Request

    +
    +
    +
    PATCH /consultants/passwords HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 81
    +Host: localhost:8080
    +
    +{
    +  "consultantPassword" : "123401343",
    +  "employeeNumber" : "12345678TWE!@"
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    employeeNumber

    String

    설계사 사원번호

    consultantPassword

    String

    설계사 비밀번호

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 200 OK
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 46
    +
    +{
    +  "isMyConsultant" : true,
    +  "fcId" : 1
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    isMyConsultant

    Boolean

    로그인한 사람이 우리 상담원인가?

    fcId

    Number

    설계사 ID

    +
    +
    +
    +
    +

    상담원 상담 가능 상태로 바꿔주는 API

    +
    +
    +

    상담이 끝나면 상담원의 상담 가능 상태를 +UNAVAILABLE에서 AVAILABLE로 바꿔줍니다.

    +
    +
    +

    Request

    +
    +
    +
    PUT /consultants/1 HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Host: localhost:8080
    +
    +
    + + ++++ + + + + + + + + + + + + +
    Table 5. /consultants/{fcId}
    ParameterDescription

    fcId

    상담원의 ID

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 200 OK
    +
    +
    +
    +
    +
    +
    +

    상담원 회원가입 API

    +
    +
    +

    상담원 회원가입 API +사원 번호는 미리 받은 상태입니다.

    +
    +
    +

    Request

    +
    +
    +
    POST /consultants HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 251
    +Host: localhost:8080
    +
    +{
    +  "employeeNumber" : "123401343",
    +  "consultantPassword" : "123456789TWE!@",
    +  "phoneNumber" : "01012345678",
    +  "name" : "KIM BYEOL",
    +  "profileUrl" : "https://gcp//meritz-profile/mypicture",
    +  "introduceMessage" : "hello, my name is byeol"
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    employeeNumber

    String

    설계사 사원번호

    consultantPassword

    String

    설계사 비밀번호

    phoneNumber

    String

    설계사 전화번호

    name

    String

    이름

    profileUrl

    String

    프로필 url

    introduceMessage

    String

    소개 메세지

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 201 Created
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 18
    +
    +{
    +  "fcId" : 1
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    fcId

    Number

    회원 가입된 상담원의 ID

    +
    +
    +
    +
    +

    상담원 로그인 API

    +
    +
    +

    상담원 로그인 API

    +
    +
    +

    Request

    +
    +
    +
    PATCH /consultants/passwords HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 81
    +Host: localhost:8080
    +
    +{
    +  "consultantPassword" : "123401343",
    +  "employeeNumber" : "12345678TWE!@"
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    employeeNumber

    String

    설계사 사원번호

    consultantPassword

    String

    설계사 비밀번호

    +
    +
    +

    Response (Success)

    +
    +
    +
    HTTP/1.1 200 OK
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 46
    +
    +{
    +  "isMyConsultant" : true,
    +  "fcId" : 1
    +}
    +
    +
    + +++++ + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    isMyConsultant

    Boolean

    로그인한 사람이 우리 상담원인가?

    fcId

    Number

    설계사 ID

    +
    +
    +
    From 198eaefc6e9001a543d10bf77f08112fd52d8e24 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 12:07:23 +0900 Subject: [PATCH 14/19] =?UTF-8?q?test=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20Fixture=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hellomeritz/global/ChatFixture.java | 4 +-- .../global/FinancialConsultantFixture.java | 31 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/hellomeritz/global/ChatFixture.java b/src/test/java/com/hellomeritz/global/ChatFixture.java index 6fe75fa..9d3a5ec 100644 --- a/src/test/java/com/hellomeritz/global/ChatFixture.java +++ b/src/test/java/com/hellomeritz/global/ChatFixture.java @@ -5,7 +5,7 @@ import com.hellomeritz.chat.domain.ChatMessage; import com.hellomeritz.chat.domain.ChatMessageType; import com.hellomeritz.chat.domain.ChatRoom; -import com.hellomeritz.chat.domain.ChatRoomPassword; +import com.hellomeritz.global.encryption.PassWord; import com.hellomeritz.chat.global.SourceLanguage; import com.hellomeritz.chat.global.TargetLanguage; import com.hellomeritz.chat.global.stt.SttProvider; @@ -250,7 +250,7 @@ public static ChatRoomPasswordCreateParam chatRoomPasswordCreateRequest( long chatRoomId ) { return new ChatRoomPasswordCreateParam( - ChatRoomPassword.of("30303TWSA!!!"), + PassWord.of("30303TWSA!!!"), chatRoomId, LocalDateTime.now() ); diff --git a/src/test/java/com/hellomeritz/global/FinancialConsultantFixture.java b/src/test/java/com/hellomeritz/global/FinancialConsultantFixture.java index c19b9ed..9878719 100644 --- a/src/test/java/com/hellomeritz/global/FinancialConsultantFixture.java +++ b/src/test/java/com/hellomeritz/global/FinancialConsultantFixture.java @@ -1,7 +1,11 @@ package com.hellomeritz.global; +import com.hellomeritz.member.controller.dto.request.ConsultantLoginRequest; +import com.hellomeritz.member.controller.dto.request.ConsultantSignUpRequest; import com.hellomeritz.member.domain.FinancialConsultant; +import com.hellomeritz.member.service.dto.result.ConsultantLoginResult; import com.hellomeritz.member.service.dto.result.ConsultantMatchResult; +import com.hellomeritz.member.service.dto.result.ConsultantSignUpResult; import com.hellomeritz.member.service.dto.result.FcInfoResult; public class FinancialConsultantFixture { @@ -20,7 +24,7 @@ public static FinancialConsultant financialConsultant() { } public static FinancialConsultant financialConsultantNotVailable() { - FinancialConsultant financialConsultant = FinancialConsultant.of( + FinancialConsultant financialConsultant = FinancialConsultant.of( "010-1234-6789", "박지호", "https://gcp//meritz-profile/mypicture", @@ -44,4 +48,29 @@ public static FcInfoResult fcInfoResult() { public static ConsultantMatchResult consultantMatchResult() { return new ConsultantMatchResult(1L); } + + public static ConsultantSignUpResult consultantSignUpResult() { + return new ConsultantSignUpResult(1L); + } + + public static ConsultantSignUpRequest consultantSignUpRequest() { + return new ConsultantSignUpRequest( + "123401343", + "123456789TWE!@", + "01012345678", + "KIM BYEOL", + "https://gcp//meritz-profile/mypicture", + "hello, my name is byeol"); + } + + public static ConsultantLoginResult consultantLoginResult() { + return new ConsultantLoginResult(true, 1L); + } + + public static ConsultantLoginRequest consultantLoginRequest() { + return new ConsultantLoginRequest( + "123401343", + "12345678TWE!@"); + } + } From f5b83a2a35fb7e32d16e5e1e91180f9851710ec0 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 13:26:14 +0900 Subject: [PATCH 15/19] =?UTF-8?q?feat=20:=20=EC=A0=95=EC=A0=81=20=EC=9E=90?= =?UTF-8?q?=EC=9B=90=20=EC=BA=90=EC=8B=9C=20=EB=B0=8F=20Etag=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hellomeritz/global/WebConfig.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hellomeritz/global/WebConfig.java b/src/main/java/com/hellomeritz/global/WebConfig.java index a7f4319..78a15d7 100644 --- a/src/main/java/com/hellomeritz/global/WebConfig.java +++ b/src/main/java/com/hellomeritz/global/WebConfig.java @@ -2,8 +2,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.hellomeritz.member.global.IpSensor; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.CacheControl; +import org.springframework.web.filter.ShallowEtagHeaderFilter; +import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; @@ -23,15 +29,38 @@ public WebConfig(ObjectMapper objectMapper) { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOriginPatterns("*") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") - .maxAge(3600); + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") + .maxAge(3600); } @Override - public void addInterceptors(InterceptorRegistry registry){ + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/static/**") + .addResourceLocations("classpath:/static/") + .setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS).mustRevalidate()); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new IpSensor(objectMapper)) - .addPathPatterns("/users/*"); + .addPathPatterns("/users/*"); + registry.addInterceptor(new HandlerInterceptor() { + @Override + @PostConstruct + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + if (request.getMethod().equals("GET")) { + response.setHeader("Cache-Control", "no-cache"); + } + return HandlerInterceptor.super.preHandle(request, response, handler); + } + }); + } + + @Bean + public ShallowEtagHeaderFilter shallowEtagHeaderFilter() { + return new ShallowEtagHeaderFilter(); } } From a46ea4cddd3f5fe207b5d87990acc83a2f725a9d Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 13:27:09 +0900 Subject: [PATCH 16/19] =?UTF-8?q?feat=20:=20caffeine=20=EB=A1=9C=EC=BB=AC?= =?UTF-8?q?=20=EC=BA=90=EC=8B=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hellomeritz/global/cache/CacheConfig.java | 35 +++++++++++++++++++ .../hellomeritz/global/cache/CacheType.java | 19 ++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/com/hellomeritz/global/cache/CacheConfig.java create mode 100644 src/main/java/com/hellomeritz/global/cache/CacheType.java diff --git a/src/main/java/com/hellomeritz/global/cache/CacheConfig.java b/src/main/java/com/hellomeritz/global/cache/CacheConfig.java new file mode 100644 index 0000000..9a34a42 --- /dev/null +++ b/src/main/java/com/hellomeritz/global/cache/CacheConfig.java @@ -0,0 +1,35 @@ +package com.hellomeritz.global.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableCaching +public class CacheConfig { + + @Bean + public List caffeineCaches() { + return Arrays.stream(CacheType.values()) + .map(cache -> new CaffeineCache(cache.getCacheName(), Caffeine.newBuilder().recordStats() + .expireAfterWrite(cache.getExpiredHourAfterWrite(), TimeUnit.HOURS) + .maximumSize(cache.getMaximumSize()) + .build())) + .toList(); + } + @Bean + public CacheManager cacheManager(List caffeineCaches) { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + cacheManager.setCaches(caffeineCaches); + + return cacheManager; + } +} diff --git a/src/main/java/com/hellomeritz/global/cache/CacheType.java b/src/main/java/com/hellomeritz/global/cache/CacheType.java new file mode 100644 index 0000000..58b334d --- /dev/null +++ b/src/main/java/com/hellomeritz/global/cache/CacheType.java @@ -0,0 +1,19 @@ +package com.hellomeritz.global.cache; + +import lombok.Getter; + +@Getter +public enum CacheType { + FOREIGNER_PROFILE("foreigner", 1, 10000), + CONSULTANT_PROFILE("consultant", 3, 1000); + + private final String cacheName; + private final int expiredHourAfterWrite; + private final int maximumSize; + + CacheType(String cacheName, int expiredHourAfterWrite, int maximumSize) { + this.cacheName = cacheName; + this.expiredHourAfterWrite = expiredHourAfterWrite; + this.maximumSize = maximumSize; + } +} From 4932f610ff7cd350962d07d62acb9e9b3e42f021 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 13:27:47 +0900 Subject: [PATCH 17/19] =?UTF-8?q?refactor=20:=20=EC=99=B8=EA=B5=AD?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=8F=20=EC=83=81=EB=8B=B4=EC=9B=90=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=BB=AC=20=EC=BA=90=EC=8B=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hellomeritz/member/service/MemberService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/hellomeritz/member/service/MemberService.java b/src/main/java/com/hellomeritz/member/service/MemberService.java index f81f216..71b3594 100644 --- a/src/main/java/com/hellomeritz/member/service/MemberService.java +++ b/src/main/java/com/hellomeritz/member/service/MemberService.java @@ -1,5 +1,6 @@ package com.hellomeritz.member.service; +import com.hellomeritz.global.cache.CacheType; import com.hellomeritz.member.domain.FinancialConsultant; import com.hellomeritz.member.domain.Foreigner; import com.hellomeritz.member.global.IpSensor; @@ -10,6 +11,7 @@ import com.hellomeritz.member.repository.foreign.ForeignRepository; import com.hellomeritz.member.service.dto.param.*; import com.hellomeritz.member.service.dto.result.*; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -61,12 +63,14 @@ public void notifyForeignerArrival(AlarmToFcParam param) { smsManager.sendAlarmMessage(param.t0SmsSendRequest(financialConsultant.getPhoneNumber())); } + @Cacheable(cacheNames = "consultant", key = "#param.userId()") public FcInfoResult getFinancialConsultantInfo(FinancialConsultantInfoGetParam param) { FinancialConsultant financialConsultant = financialConsultantRepository.getFinancialConsultant(param.userId()); return FcInfoResult.of(financialConsultant); } + @Cacheable(cacheNames = "foreigner", key = "#param.userId()") public ForeignerInfoResult getForeignerInfo(ForeignerInfoGetParam param) { Foreigner foreigner = foreignRepository.getById(param.userId()); return ForeignerInfoResult.of(foreigner); From 3a19827a78fdac7a2280e6183cea90b106b11b3f Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 13:28:21 +0900 Subject: [PATCH 18/19] =?UTF-8?q?chore=20:=20caffeine=20=EC=BA=90=EC=8B=9C?= =?UTF-8?q?=20=ED=99=98=EA=B2=BD=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index c3f55ad..8297632 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,10 @@ dependencies { // Aho-corasick algorithm implementation 'org.ahocorasick:ahocorasick:0.6.3' + // caffeine 캐시 + implementation 'org.springframework.boot:spring-boot-starter-cache' + implementation 'com.github.ben-manes.caffeine:caffeine' + } ext { set('springCloudVersion', "2022.0.3") From bb61622f294096543e930f7ee7d0e45e1b8c4747 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Mon, 13 May 2024 13:28:43 +0900 Subject: [PATCH 19/19] =?UTF-8?q?sylte=20:=20rest=20docs=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/docs/chats.html | 2 +- src/main/resources/static/docs/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/static/docs/chats.html b/src/main/resources/static/docs/chats.html index a8cfd4b..31f165e 100644 --- a/src/main/resources/static/docs/chats.html +++ b/src/main/resources/static/docs/chats.html @@ -758,7 +758,7 @@

    Response (Success)

    { "textBySpeech" : "Hello my name is byeol", - "createdAt" : "2024-05-13T12:03:49.480445200", + "createdAt" : "2024-05-13T12:50:01.507708700", "sttProvider" : "WHISPER" } diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 0f8c617..dde5349 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -891,7 +891,7 @@

    Respons { "textBySpeech" : "Hello my name is byeol", - "createdAt" : "2024-05-13T12:03:49.480445200", + "createdAt" : "2024-05-13T12:50:01.507708700", "sttProvider" : "WHISPER" }