Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat : 상담원 회원가입, 로그인, 상담 완료 구현 및 Etag와 로컬 캐시 적용 #8

Merged
merged 19 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3512833
feat : 메모리에 banword 올리는 컨트롤러 구현
byeolhaha May 12, 2024
b30f0da
feat : 상담원의 상담 가능 상태를 변경하는 기능 구현 및 테스트 구현
byeolhaha May 12, 2024
92d430e
style : 파일 이동
byeolhaha May 13, 2024
cc0830b
feat : 상담원 상담 가능 상태 및 로그인, 회원가입 기능 구현
byeolhaha May 13, 2024
8d5a8ac
test : 상담원 상담 가능 상태 전환 서비스 테스트 구현
byeolhaha May 13, 2024
6180360
feat : 상담원 사원번호로 검색 쿼리 구현
byeolhaha May 13, 2024
045f695
refactor : 비밀번호 및 salt 필드 추가에 따른 수정
byeolhaha May 13, 2024
aee1916
feat : 상담원 상담 가능 상태 변화 및 로그인, 회원가입 관련 컨트롤러 구현
byeolhaha May 13, 2024
f3d0a46
test : 상담원 상담 가능 상태 변화 및 로그인, 회원가입 관련 rest docs test 구현
byeolhaha May 13, 2024
7d5e0f1
feat: 상담원 로그인 페이지 구현
byeolhaha May 13, 2024
49d2400
refactor : path variable로 fcid 받도록 수정
byeolhaha May 13, 2024
aea4c79
feat : 상담 완료 상태 api 연결
byeolhaha May 13, 2024
d242467
style : rest docs 추가
byeolhaha May 13, 2024
198eaef
test : 테스트 관련 Fixture 구현
byeolhaha May 13, 2024
f5b83a2
feat : 정적 자원 캐시 및 Etag 관련 설정
byeolhaha May 13, 2024
a46ea4c
feat : caffeine 로컬 캐시 설정
byeolhaha May 13, 2024
4932f61
refactor : 외국인 및 상담원 정보 조회 관련 로컬 캐시 적용
byeolhaha May 13, 2024
3a19827
chore : caffeine 캐시 환경 설정
byeolhaha May 13, 2024
bb61622
sylte : rest docs 추가
byeolhaha May 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
45 changes: 44 additions & 1 deletion src/docs/asciidoc/members.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
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[]
Original file line number Diff line number Diff line change
@@ -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<Void> uploadBanWord( ) {
banWordService.uploadBanWordsToMemory();

return ResponseEntity.status(HttpStatus.CREATED)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -17,7 +13,7 @@ public record ChatRoomPasswordCreateRequest(

public ChatRoomPasswordCreateParam toChatRoomPasswordCreateParam(Long chatRoomId) {
return new ChatRoomPasswordCreateParam(
ChatRoomPassword.of(chatRoomPassword),
PassWord.of(chatRoomPassword),
chatRoomId,
LocalDateTime.now()
);
Expand Down
18 changes: 2 additions & 16 deletions src/main/java/com/hellomeritz/chat/service/ChatService.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -20,7 +20,7 @@ public PasswordMatchRequest toPasswordMatchRequest(ChatRoomPasswordInfo chatRoom

public ChatRoomPasswordCreateParam toChatRoomPasswordCreateRequest() {
return new ChatRoomPasswordCreateParam(
ChatRoomPassword.of(chatRoomPassword),
PassWord.of(chatRoomPassword),
chatRoomId,
LocalDateTime.now()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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
);
Expand Down
39 changes: 34 additions & 5 deletions src/main/java/com/hellomeritz/global/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}

}
35 changes: 35 additions & 0 deletions src/main/java/com/hellomeritz/global/cache/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -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<CaffeineCache> 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<CaffeineCache> caffeineCaches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caffeineCaches);

return cacheManager;
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/hellomeritz/global/cache/CacheType.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.hellomeritz.member.global.encryption.dto;
package com.hellomeritz.global.encryption.dto;

import java.time.LocalDateTime;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.hellomeritz.member.global.encryption.dto;
package com.hellomeritz.global.encryption.dto;

public record EncryptionResponse(
String password,
Expand Down
Loading
Loading