From d7b77d836625c1c8bf03d186ebe9cd82e18ed95c Mon Sep 17 00:00:00 2001 From: Inseo Song Date: Thu, 15 Jun 2023 11:43:33 +0900 Subject: [PATCH 1/9] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 15bfede2..0e1cfa3c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![mainbanner2](https://user-images.githubusercontent.com/94730032/227761473-096957f6-9e00-4445-ac17-9d8b506c120b.png) +![image](https://github.com/SINZAK/sinzak-backend/assets/94730032/c5fdd3ab-9487-4e51-a75a-26cd09b6393f)![mainbanner2](https://user-images.githubusercontent.com/94730032/227761473-096957f6-9e00-4445-ac17-9d8b506c120b.png) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SINZAK_sinzak-backend&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SINZAK_sinzak-backend) @@ -20,9 +20,8 @@ ## ✨ 서비스 관련 - [PC 버전](https://sinzak.net) -- [안드로이드 출시 링크](https://play.google.com/store/apps/details?id=io.sinzak.android&pli=1) -- [서비스 소개 노션]() - +- [AOS](https://play.google.com/store/apps/details?id=io.sinzak.android&pli=1) +- [IOS](https://apps.apple.com/kr/app/%EC%8B%A0%EC%9E%91/id6449455462)
## 📚 사용 스택 From cb9e14804c0944fb656202ba4d837344c9984e7b Mon Sep 17 00:00:00 2001 From: Inseo Song Date: Thu, 15 Jun 2023 11:44:59 +0900 Subject: [PATCH 2/9] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 0e1cfa3c..4a140e58 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -![image](https://github.com/SINZAK/sinzak-backend/assets/94730032/c5fdd3ab-9487-4e51-a75a-26cd09b6393f)![mainbanner2](https://user-images.githubusercontent.com/94730032/227761473-096957f6-9e00-4445-ac17-9d8b506c120b.png) - - [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SINZAK_sinzak-backend&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SINZAK_sinzak-backend) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=SINZAK_sinzak-backend&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=SINZAK_sinzak-backend)
From 665c2ba95907173dcacc3acf416bb8eeedf7e9c3 Mon Sep 17 00:00:00 2001 From: starwook Date: Thu, 15 Jun 2023 23:55:22 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=99=84=EC=84=B1=EC=9A=A9=20cacheManager=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/sinzak/server/config/RedisCacheConfig.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/sinzak/server/config/RedisCacheConfig.java b/src/main/java/net/sinzak/server/config/RedisCacheConfig.java index 9a3f3dad..36b5fd78 100644 --- a/src/main/java/net/sinzak/server/config/RedisCacheConfig.java +++ b/src/main/java/net/sinzak/server/config/RedisCacheConfig.java @@ -54,6 +54,15 @@ public CacheManager testCacheManager(RedisConnectionFactory cf){ return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build(); } + @Bean + public CacheManager searchManager(RedisConnectionFactory cf){ + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))//Serializer로 저장해야함 + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper()))) + .entryTtl(Duration.ZERO); //ttl 무한 설정 + return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build(); + } + private ObjectMapper objectMapper() { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator .builder() From 96c84d4d927a98ddd3d5b5a516b96abda91b0e2e Mon Sep 17 00:00:00 2001 From: starwook Date: Fri, 16 Jun 2023 13:49:52 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5(Trie=EA=B5=AC=EC=A1=B0,=20Redis=20SOrted=20set=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sinzak/server/common/RedisServcie.java | 47 +++++++++++++++++++ ...RedisCacheConfig.java => RedisConfig.java} | 16 ++++--- .../product/controller/ProductController.java | 6 +++ .../product/service/ProductService.java | 10 +++- 4 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/sinzak/server/common/RedisServcie.java rename src/main/java/net/sinzak/server/config/{RedisCacheConfig.java => RedisConfig.java} (83%) diff --git a/src/main/java/net/sinzak/server/common/RedisServcie.java b/src/main/java/net/sinzak/server/common/RedisServcie.java new file mode 100644 index 00000000..10f04c10 --- /dev/null +++ b/src/main/java/net/sinzak/server/common/RedisServcie.java @@ -0,0 +1,47 @@ +package net.sinzak.server.common; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class RedisServcie { + + @Resource(name="redisTemplate") + private final RedisTemplate redisTemplate; + private static final String REDIS_AUTO_COMPLETE ="autoComplete"; + private static final Integer SUB_WORD_SCORE =0; + private static final Integer NEW_SEARCH_WORD_SCORE =1; + public void addWordToRedis(String newWord){ + for(int i=0;i getAutoCompleteWords(String prefix){ +// Set allWords = redisTemplate +// .opsForZSet() +// .rangeByLex() +// . +// +// } + + +} diff --git a/src/main/java/net/sinzak/server/config/RedisCacheConfig.java b/src/main/java/net/sinzak/server/config/RedisConfig.java similarity index 83% rename from src/main/java/net/sinzak/server/config/RedisCacheConfig.java rename to src/main/java/net/sinzak/server/config/RedisConfig.java index 36b5fd78..2ad4a9ca 100644 --- a/src/main/java/net/sinzak/server/config/RedisCacheConfig.java +++ b/src/main/java/net/sinzak/server/config/RedisConfig.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.swagger.models.auth.In; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; @@ -22,13 +23,14 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; @Slf4j @Configuration -public class RedisCacheConfig { +public class RedisConfig { @Value("${spring.redis.host}") private String host; @@ -55,12 +57,12 @@ public CacheManager testCacheManager(RedisConnectionFactory cf){ } @Bean - public CacheManager searchManager(RedisConnectionFactory cf){ - RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() - .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))//Serializer로 저장해야함 - .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper()))) - .entryTtl(Duration.ZERO); //ttl 무한 설정 - return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build(); + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); + return redisTemplate; } private ObjectMapper objectMapper() { diff --git a/src/main/java/net/sinzak/server/product/controller/ProductController.java b/src/main/java/net/sinzak/server/product/controller/ProductController.java index 8d43365e..a2943ce5 100644 --- a/src/main/java/net/sinzak/server/product/controller/ProductController.java +++ b/src/main/java/net/sinzak/server/product/controller/ProductController.java @@ -162,4 +162,10 @@ public PageImpl showMarketProduct(@RequestParam(required=false, defaul } } + +// @ApiOperation(value = "검색어 자동완성") +// @PostMapping("/products/auto-complete") +// public List autoCompleteSearch(@RequestParam String keyWord){ +// return productService.autoCompleteWord(keyWord); +// } } diff --git a/src/main/java/net/sinzak/server/product/service/ProductService.java b/src/main/java/net/sinzak/server/product/service/ProductService.java index 26f30b8a..ab6a5e2b 100644 --- a/src/main/java/net/sinzak/server/product/service/ProductService.java +++ b/src/main/java/net/sinzak/server/product/service/ProductService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import net.sinzak.server.alarm.service.AlarmService; import net.sinzak.server.common.PostService; +import net.sinzak.server.common.RedisServcie; import net.sinzak.server.common.UserUtils; import net.sinzak.server.user.domain.Report; import net.sinzak.server.user.domain.Role; @@ -54,6 +55,8 @@ public class ProductService implements PostService productListForUser(String keyword, List categories, String align, boolean complete, Pageable pageable){ User user = userRepository.findByIdFetchHistoryAndLikesList(userUtils.getCurrentUserId()).orElseThrow(UserNotFoundException::new); List userReports = reportRepository.findByUserId(user.getId()); - if(!keyword.isEmpty()) + if(!keyword.isEmpty()){ saveSearchHistory(keyword, user); + redisServcie.addWordToRedis(keyword); + } + Page productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable); List products = new ArrayList<>(productList.getContent()); // Page의 content를 필터링 할 수 없어서 재생성. log.error("{}", products.size()); @@ -422,6 +428,7 @@ private void saveSearchHistory(String keyword, User user) { @Transactional(readOnly = true) public PageImpl productListForGuest(String keyword, List categories, String align, boolean complete, Pageable pageable){ + if(!keyword.isEmpty()) redisServcie.addWordToRedis(keyword); Page productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable); List showList = makeShowFormsForGuest(productList); return new PageImpl<>(showList, pageable, productList.getTotalElements()); @@ -481,4 +488,5 @@ private void removeBlockPost(List userReportList, List productL } } } + } From 22f5d1964df7136a86c29c093e0d4357dd62f987 Mon Sep 17 00:00:00 2001 From: starwook Date: Fri, 16 Jun 2023 18:46:29 +0900 Subject: [PATCH 5/9] =?UTF-8?q?refactor:=20=EC=9E=90=EC=8B=9D=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5(trie=20=EA=B5=AC=EC=A1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sinzak/server/common/RedisServcie.java | 47 -------------- .../{config => common/redis}/RedisConfig.java | 7 +- .../server/common/redis/RedisService.java | 64 +++++++++++++++++++ .../server/common/redis/SearchNode.java | 20 ++++++ .../product/service/ProductService.java | 8 +-- 5 files changed, 92 insertions(+), 54 deletions(-) delete mode 100644 src/main/java/net/sinzak/server/common/RedisServcie.java rename src/main/java/net/sinzak/server/{config => common/redis}/RedisConfig.java (94%) create mode 100644 src/main/java/net/sinzak/server/common/redis/RedisService.java create mode 100644 src/main/java/net/sinzak/server/common/redis/SearchNode.java diff --git a/src/main/java/net/sinzak/server/common/RedisServcie.java b/src/main/java/net/sinzak/server/common/RedisServcie.java deleted file mode 100644 index 10f04c10..00000000 --- a/src/main/java/net/sinzak/server/common/RedisServcie.java +++ /dev/null @@ -1,47 +0,0 @@ -package net.sinzak.server.common; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Set; - -@Service -@RequiredArgsConstructor -public class RedisServcie { - - @Resource(name="redisTemplate") - private final RedisTemplate redisTemplate; - private static final String REDIS_AUTO_COMPLETE ="autoComplete"; - private static final Integer SUB_WORD_SCORE =0; - private static final Integer NEW_SEARCH_WORD_SCORE =1; - public void addWordToRedis(String newWord){ - for(int i=0;i getAutoCompleteWords(String prefix){ -// Set allWords = redisTemplate -// .opsForZSet() -// .rangeByLex() -// . -// -// } - - -} diff --git a/src/main/java/net/sinzak/server/config/RedisConfig.java b/src/main/java/net/sinzak/server/common/redis/RedisConfig.java similarity index 94% rename from src/main/java/net/sinzak/server/config/RedisConfig.java rename to src/main/java/net/sinzak/server/common/redis/RedisConfig.java index 2ad4a9ca..6315317d 100644 --- a/src/main/java/net/sinzak/server/config/RedisConfig.java +++ b/src/main/java/net/sinzak/server/common/redis/RedisConfig.java @@ -1,4 +1,4 @@ -package net.sinzak.server.config; +package net.sinzak.server.common.redis; import com.fasterxml.jackson.databind.ObjectMapper; @@ -57,12 +57,13 @@ public CacheManager testCacheManager(RedisConnectionFactory cf){ } @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { - RedisTemplate redisTemplate = new RedisTemplate<>(); + public RedisTemplate autoCompleteRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); return redisTemplate; + } private ObjectMapper objectMapper() { diff --git a/src/main/java/net/sinzak/server/common/redis/RedisService.java b/src/main/java/net/sinzak/server/common/redis/RedisService.java new file mode 100644 index 00000000..bb079ff9 --- /dev/null +++ b/src/main/java/net/sinzak/server/common/redis/RedisService.java @@ -0,0 +1,64 @@ +package net.sinzak.server.common.redis; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class RedisService { + + @Resource(name= "autoCompleteRedisTemplate") + private final RedisTemplate redisTemplate; + private static final String REDIS_AUTO_COMPLETE ="autoComplete"; + private static final Integer SUB_WORD_SCORE =0; + private static final Integer NEW_SEARCH_WORD_SCORE =1; + public void addWordToRedis(String newWord){ + SearchNode parentNode = SearchNode.builder() + .children(null) + .score(0) + .value(newWord.substring(0,1)) + .build(); + SearchNode firstNode = parentNode; + for(int i=1;i getAutoCompleteWords(String prefix){ +// Set allWords = redisTemplate +// .opsForZSet() +// .rangeByLex() +// . +// +// } + + +} diff --git a/src/main/java/net/sinzak/server/common/redis/SearchNode.java b/src/main/java/net/sinzak/server/common/redis/SearchNode.java new file mode 100644 index 00000000..65079080 --- /dev/null +++ b/src/main/java/net/sinzak/server/common/redis/SearchNode.java @@ -0,0 +1,20 @@ +package net.sinzak.server.common.redis; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Setter +@Getter +public class SearchNode { + private String value; + private SearchNode children; + private int score; + + public SearchNode(String value, SearchNode children, int score) { + this.value = value; + this.children = children; + this.score = score; + } +} diff --git a/src/main/java/net/sinzak/server/product/service/ProductService.java b/src/main/java/net/sinzak/server/product/service/ProductService.java index ab6a5e2b..7a818e44 100644 --- a/src/main/java/net/sinzak/server/product/service/ProductService.java +++ b/src/main/java/net/sinzak/server/product/service/ProductService.java @@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j; import net.sinzak.server.alarm.service.AlarmService; import net.sinzak.server.common.PostService; -import net.sinzak.server.common.RedisServcie; +import net.sinzak.server.common.redis.RedisService; import net.sinzak.server.common.UserUtils; import net.sinzak.server.user.domain.Report; import net.sinzak.server.user.domain.Role; @@ -55,7 +55,7 @@ public class ProductService implements PostService productListForUser(String keyword, List catego List userReports = reportRepository.findByUserId(user.getId()); if(!keyword.isEmpty()){ saveSearchHistory(keyword, user); - redisServcie.addWordToRedis(keyword); + redisService.addWordToRedis(keyword); } Page productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable); @@ -428,7 +428,7 @@ private void saveSearchHistory(String keyword, User user) { @Transactional(readOnly = true) public PageImpl productListForGuest(String keyword, List categories, String align, boolean complete, Pageable pageable){ - if(!keyword.isEmpty()) redisServcie.addWordToRedis(keyword); + if(!keyword.isEmpty()) redisService.addWordToRedis(keyword); Page productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable); List showList = makeShowFormsForGuest(productList); return new PageImpl<>(showList, pageable, productList.getTotalElements()); From a388a9cbcc5593c5bed8cf69b9fd61d8729688cc Mon Sep 17 00:00:00 2001 From: starwook Date: Sun, 18 Jun 2023 18:56:55 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=EB=8B=A8=EC=96=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20Trie=EA=B5=AC=EC=A1=B0,=20=EC=99=84=EC=84=B1?= =?UTF-8?q?=EB=90=9C=20=EB=8B=A8=EC=96=B4=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=9E=AC=EA=B7=80=ED=95=A8=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/common/redis/RedisService.java | 92 +++++++++++-------- .../product/controller/ProductController.java | 10 +- .../product/service/ProductService.java | 3 + 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/main/java/net/sinzak/server/common/redis/RedisService.java b/src/main/java/net/sinzak/server/common/redis/RedisService.java index bb079ff9..c567bcc7 100644 --- a/src/main/java/net/sinzak/server/common/redis/RedisService.java +++ b/src/main/java/net/sinzak/server/common/redis/RedisService.java @@ -1,64 +1,78 @@ package net.sinzak.server.common.redis; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class RedisService { - @Resource(name= "autoCompleteRedisTemplate") - private final RedisTemplate redisTemplate; - private static final String REDIS_AUTO_COMPLETE ="autoComplete"; +// @Resource(name= "autoCompleteRedisTemplate") + private final RedisTemplate redisTemplate; + + private static final String REDIS_KEY_AUTO_COMPLETE ="autoComplete:"; + private static final String COMPLETE_WORD ="+"; + private static final Integer NUMBER_OF_WORD_TO_BRING =6; + private static final Integer NUMBER_OF_WORD_TO_SHOW = 5; private static final Integer SUB_WORD_SCORE =0; private static final Integer NEW_SEARCH_WORD_SCORE =1; public void addWordToRedis(String newWord){ - SearchNode parentNode = SearchNode.builder() - .children(null) - .score(0) - .value(newWord.substring(0,1)) - .build(); - SearchNode firstNode = parentNode; - for(int i=1;i getAutoCompleteWords(String prefix){ + List wordsToShow = new ArrayList<>(); + findEndOfWord(prefix,wordsToShow); + return wordsToShow; + } + public void findEndOfWord(String prefix,List wordsToShow){ + List temporaryWord = redisTemplate + .opsForZSet() + .reverseRangeWithScores(REDIS_KEY_AUTO_COMPLETE+prefix,0,NUMBER_OF_WORD_TO_BRING) + .stream() + .map(ZSetOperations.TypedTuple::getValue) + .collect(Collectors.toList()); + for(int i=0;iNUMBER_OF_WORD_TO_SHOW) return; + if(temporaryWord.get(i).equals("+")) wordsToShow.add(prefix); + if(!temporaryWord.get(i).equals("+")) findEndOfWord(prefix+temporaryWord.get(i),wordsToShow); } - redisTemplate.opsForValue().set(REDIS_AUTO_COMPLETE,firstNode); -// for(int i=0;i getAutoCompleteWords(String prefix){ -// Set allWords = redisTemplate -// .opsForZSet() -// .rangeByLex() -// . + + // private void addWordsThroughNode(String newWord) { +// SearchNode parentNode = SearchNode.builder() +// .children(null) +// .score(0) +// .value(newWord.substring(0,1)) +// .build(); +// SearchNode firstNode = parentNode; +// for(int i = 1; i< newWord.length(); i++){ +// SearchNode childNode = SearchNode.builder() +// .children(null) +// .score(0) +// .value(newWord.substring(i,i+1)) +// .build(); +// parentNode.setChildren(childNode); +// parentNode = childNode; // +// } +// redisTemplate.opsForValue().set(REDIS_KEY_AUTO_COMPLETE,firstNode); // } - } diff --git a/src/main/java/net/sinzak/server/product/controller/ProductController.java b/src/main/java/net/sinzak/server/product/controller/ProductController.java index a2943ce5..5c11e5bb 100644 --- a/src/main/java/net/sinzak/server/product/controller/ProductController.java +++ b/src/main/java/net/sinzak/server/product/controller/ProductController.java @@ -163,9 +163,9 @@ public PageImpl showMarketProduct(@RequestParam(required=false, defaul } -// @ApiOperation(value = "검색어 자동완성") -// @PostMapping("/products/auto-complete") -// public List autoCompleteSearch(@RequestParam String keyWord){ -// return productService.autoCompleteWord(keyWord); -// } + @ApiOperation(value = "검색어 자동완성") + @PostMapping("/products/auto-complete") + public List autoCompleteSearch(@RequestParam String keyWord){ + return productService.getCompleteWord(keyWord); + } } diff --git a/src/main/java/net/sinzak/server/product/service/ProductService.java b/src/main/java/net/sinzak/server/product/service/ProductService.java index 7a818e44..9fe3fe21 100644 --- a/src/main/java/net/sinzak/server/product/service/ProductService.java +++ b/src/main/java/net/sinzak/server/product/service/ProductService.java @@ -489,4 +489,7 @@ private void removeBlockPost(List userReportList, List productL } } + public List getCompleteWord(String keyWord) { + return redisService.getAutoCompleteWords(keyWord); + } } From 8f03b30f9fa7d5b005b0d638a02aaa1707e9df27 Mon Sep 17 00:00:00 2001 From: starwook Date: Sun, 18 Jun 2023 19:30:37 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=EC=88=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sinzak/server/common/redis/RedisService.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/sinzak/server/common/redis/RedisService.java b/src/main/java/net/sinzak/server/common/redis/RedisService.java index c567bcc7..8b3854db 100644 --- a/src/main/java/net/sinzak/server/common/redis/RedisService.java +++ b/src/main/java/net/sinzak/server/common/redis/RedisService.java @@ -15,20 +15,16 @@ @RequiredArgsConstructor public class RedisService { -// @Resource(name= "autoCompleteRedisTemplate") private final RedisTemplate redisTemplate; private static final String REDIS_KEY_AUTO_COMPLETE ="autoComplete:"; private static final String COMPLETE_WORD ="+"; - private static final Integer NUMBER_OF_WORD_TO_BRING =6; private static final Integer NUMBER_OF_WORD_TO_SHOW = 5; - private static final Integer SUB_WORD_SCORE =0; - private static final Integer NEW_SEARCH_WORD_SCORE =1; public void addWordToRedis(String newWord){ String key = REDIS_KEY_AUTO_COMPLETE; for(int i=0;i getAutoCompleteWords(String prefix){ public void findEndOfWord(String prefix,List wordsToShow){ List temporaryWord = redisTemplate .opsForZSet() - .reverseRangeWithScores(REDIS_KEY_AUTO_COMPLETE+prefix,0,NUMBER_OF_WORD_TO_BRING) + .reverseRangeWithScores(REDIS_KEY_AUTO_COMPLETE+prefix,0,NUMBER_OF_WORD_TO_SHOW) .stream() .map(ZSetOperations.TypedTuple::getValue) .collect(Collectors.toList()); for(int i=0;iNUMBER_OF_WORD_TO_SHOW) return; + if(wordsToShow.size()>=NUMBER_OF_WORD_TO_SHOW) return; if(temporaryWord.get(i).equals("+")) wordsToShow.add(prefix); if(!temporaryWord.get(i).equals("+")) findEndOfWord(prefix+temporaryWord.get(i),wordsToShow); } - } From 355fc19804eeb40d4605628c2a03e3a3a12a8cf4 Mon Sep 17 00:00:00 2001 From: starwook Date: Sun, 18 Jun 2023 19:33:00 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=EC=88=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/sinzak/server/common/redis/RedisService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/sinzak/server/common/redis/RedisService.java b/src/main/java/net/sinzak/server/common/redis/RedisService.java index 8b3854db..ba95f401 100644 --- a/src/main/java/net/sinzak/server/common/redis/RedisService.java +++ b/src/main/java/net/sinzak/server/common/redis/RedisService.java @@ -44,8 +44,8 @@ public void findEndOfWord(String prefix,List wordsToShow){ .collect(Collectors.toList()); for(int i=0;i=NUMBER_OF_WORD_TO_SHOW) return; - if(temporaryWord.get(i).equals("+")) wordsToShow.add(prefix); - if(!temporaryWord.get(i).equals("+")) findEndOfWord(prefix+temporaryWord.get(i),wordsToShow); + if(temporaryWord.get(i).equals(COMPLETE_WORD)) wordsToShow.add(prefix); + if(!temporaryWord.get(i).equals(COMPLETE_WORD)) findEndOfWord(prefix+temporaryWord.get(i),wordsToShow); } } From 39b2355bde5fc9190e4483f15e4a298b51662b0c Mon Sep 17 00:00:00 2001 From: inseo Date: Mon, 17 Jul 2023 22:17:36 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20redis=20=EC=9E=90=EB=8F=99=EC=99=84?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/service/ProductService.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/sinzak/server/product/service/ProductService.java b/src/main/java/net/sinzak/server/product/service/ProductService.java index 9fe3fe21..3721f946 100644 --- a/src/main/java/net/sinzak/server/product/service/ProductService.java +++ b/src/main/java/net/sinzak/server/product/service/ProductService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import net.sinzak.server.alarm.service.AlarmService; import net.sinzak.server.common.PostService; +import net.sinzak.server.common.SinzakResponse; import net.sinzak.server.common.redis.RedisService; import net.sinzak.server.common.UserUtils; import net.sinzak.server.user.domain.Report; @@ -14,7 +15,6 @@ import net.sinzak.server.common.error.PostNotFoundException; import net.sinzak.server.image.S3Service; import net.sinzak.server.product.domain.*; -import net.sinzak.server.common.PropertyUtil; import net.sinzak.server.product.dto.*; import net.sinzak.server.product.repository.*; import net.sinzak.server.user.domain.User; @@ -78,25 +78,25 @@ public JSONObject makePost(@Valid ProductPostDto buildDto){ // 글 생성 .build(); product.setUser(user); Long productId = productRepository.save(product).getId();// 미리 저장해야 이미지도 저장가능.. - return PropertyUtil.response(productId); + return SinzakResponse.success(productId); } public JSONObject saveImageInS3AndProduct(List multipartFiles, Long id) { Product product = productRepository.findById(id).orElseThrow(PostNotFoundException::new); if(multipartFiles.size() == 0) - return PropertyUtil.responseMessage("사진 1개이상 첨부해주세요."); + return SinzakResponse.error("사진 1개이상 첨부해주세요."); if(!userUtils.getCurrentUserId().equals(product.getUser().getId())) - return PropertyUtil.responseMessage("잘못된 접근입니다."); + return SinzakResponse.error("잘못된 접근입니다."); for (MultipartFile img : multipartFiles) { try{ String url = uploadImageAndSetThumbnail(multipartFiles, product, img); saveImageUrl(product, url); } catch (Exception e){ - return PropertyUtil.responseMessage(multipartFiles.indexOf(img)+"번째 이미지부터 저장 실패"); + return SinzakResponse.error(multipartFiles.indexOf(img)+"번째 이미지부터 저장 실패"); } } - return PropertyUtil.response(true); + return SinzakResponse.success(); } @@ -117,13 +117,13 @@ private void saveImageUrl(Product product, String url) { public JSONObject deleteImage(Long productId, String url){ Product product = productRepository.findByIdFetchImages(productId).orElseThrow(PostNotFoundException::new); if(!userUtils.getCurrentUserId().equals(product.getUser().getId())) - return PropertyUtil.responseMessage("해당 작품의 작가가 아닙니다."); + return SinzakResponse.error("해당 작품의 작가가 아닙니다."); if(product.getImages().size()==1) - return PropertyUtil.responseMessage("최소 1개 이상의 이미지를 보유해야 합니다."); + return SinzakResponse.error("최소 1개 이상의 이미지를 보유해야 합니다."); for (ProductImage image : product.getImages()) { if(image.getImageUrl().equals(product.getThumbnail())) - return PropertyUtil.responseMessage("썸네일은 삭제 불가능합니다."); + return SinzakResponse.error("썸네일은 삭제 불가능합니다."); if(image.getImageUrl().equals(url)){ imageRepository.delete(image); product.getImages().remove(image); @@ -131,7 +131,7 @@ public JSONObject deleteImage(Long productId, String url){ } } s3Service.deleteImage(url); - return PropertyUtil.response(productId); + return SinzakResponse.success(productId); } @Transactional(rollbackFor = {Exception.class}) @@ -140,11 +140,11 @@ public JSONObject editPost(Long productId, ProductEditDto editDto){ User user = userUtils.getCurrentUser(); Product product = productRepository.findById(productId).orElseThrow(PostNotFoundException::new); if(!user.getId().equals(product.getUser().getId()) && user.getRole() != Role.ADMIN) - return PropertyUtil.responseMessage("글 작성자가 아닙니다."); + return SinzakResponse.error("글 작성자가 아닙니다."); product.editPost(editDto); productRepository.save(product); - return PropertyUtil.response(true); + return SinzakResponse.success(); } @Transactional(rollbackFor = {Exception.class}) @@ -153,10 +153,10 @@ public JSONObject deletePost(Long productId){ User user = userUtils.getCurrentUser(); Product product = productRepository.findByIdFetchChatRooms(productId).orElseThrow(PostNotFoundException::new); if(!user.getId().equals(product.getUser().getId()) && user.getRole() != Role.ADMIN) - return PropertyUtil.responseMessage("글 작성자가 아닙니다."); + return SinzakResponse.error("글 작성자가 아닙니다."); deleteImagesInPost(product); product.setDeleted(true); - return PropertyUtil.response(true); + return SinzakResponse.success(); } private void deleteImagesInPost(Product product) { @@ -182,14 +182,14 @@ public JSONObject showDetailForUser(Long currentUserId, Long id){ // 글 상 detailForm.setMyPost(); boolean isLike = checkIsLikes(user.getProductLikesList(), product); - boolean isWish = checkIsWish(user, product.getProductWishList()); /**최적화를 위해 상품의 찜목록과 비교함. (대체적으로 해당 상품 찜개수 < 유저의 찜개수) **/ + boolean isWish = checkIsWish(user, product.getProductWishes()); /**최적화를 위해 상품의 찜목록과 비교함. (대체적으로 해당 상품 찜개수 < 유저의 찜개수) **/ boolean isFollowing = false; if(!product.getUser().isDelete()) isFollowing = checkIsFollowing(user.getFollowings(), product); detailForm.setUserAction(isLike, isWish, isFollowing); product.addViews(); - return PropertyUtil.response(detailForm); + return SinzakResponse.success(detailForm); } @Transactional @@ -205,7 +205,7 @@ public JSONObject showDetailForGuest(Long id){ // 비회원 글 보기 } detailForm.setUserAction(false,false,false); product.addViews(); - return PropertyUtil.response(detailForm); + return SinzakResponse.success(detailForm); } private DetailProductForm makeProductDetailForm(Product product, List images) { @@ -262,27 +262,27 @@ public JSONObject showHomeForUser(Long userId){ List list = QDSLRepository.findCountByCategoriesDesc(userCategories, HOME_OBJECTS); obj.put("recommend", makeHomeShowForms(user.getProductLikesList(), list)); /** 추천목록 **/ - List followingList = getFollowingList(user, productList, HOME_OBJECTS); - obj.put("following", makeHomeShowForms(user.getProductLikesList(),followingList)); /** 팔로잉 **/ + List followings = getFollowingList(user, productList, HOME_OBJECTS); + obj.put("following", makeHomeShowForms(user.getProductLikesList(),followings)); /** 팔로잉 **/ - return PropertyUtil.response(obj); + return SinzakResponse.success(obj); } // @Cacheable(value ="home_guest", cacheManager ="testCacheManager") @Transactional(readOnly = true) public JSONObject showHomeForGuest(){ JSONObject obj = new JSONObject(); - List productList = productRepository.findAllProductNotDeleted(); + List products = productRepository.findAllProductNotDeleted(); - obj.put("new", makeHomeShowFormsForGuest(productList)); + obj.put("new", makeHomeShowFormsForGuest(products)); - List tradingList = getTradingList(productList, HOME_OBJECTS); /** Trading = 최신 목록 내림차순 중 채팅수가 1이상, 거래 완료되지 않은 것 **/ + List tradingList = getTradingList(products, HOME_OBJECTS); /** Trading = 최신 목록 내림차순 중 채팅수가 1이상, 거래 완료되지 않은 것 **/ obj.put("trading", makeHomeShowFormsForGuest(tradingList)); - productList.sort((o1, o2) -> o2.getLikesCnt() - o1.getLikesCnt()); /** hot : 좋아요 순 정렬!! **/ - obj.put("hot", makeHomeShowFormsForGuest(productList)); + products.sort((o1, o2) -> o2.getLikesCnt() - o1.getLikesCnt()); /** hot : 좋아요 순 정렬!! **/ + obj.put("hot", makeHomeShowFormsForGuest(products)); - return PropertyUtil.response(obj); + return SinzakResponse.success(obj); } private List getTradingList(List productList, int limit) { @@ -305,7 +305,7 @@ private List getFollowingList(User user, List productList, int public JSONObject wish(@RequestBody ActionForm form){ JSONObject obj = new JSONObject(); User user = userRepository.findByIdFetchProductWishList(userUtils.getCurrentUserId()).orElseThrow(UserNotFoundException::new); // 작품 찜까지 페치 조인 - List wishList = user.getProductWishList(); //wishList == 유저의 찜 리스트 + List wishList = user.getProductWishes(); //wishList == 유저의 찜 리스트 boolean success = false; boolean isWish=false; Product product = productRepository.findById(form.getId()).orElseThrow(PostNotFoundException::new); @@ -375,28 +375,28 @@ public JSONObject sell(@RequestBody SellDto dto){ User user = userRepository.findByIdFetchProductSellList(userUtils.getCurrentUserId()).orElseThrow(UserNotFoundException::new); Product product = productRepository.findById(dto.getPostId()).orElseThrow(PostNotFoundException::new); if(product.isComplete()) - return PropertyUtil.responseMessage("이미 판매완료된 작품입니다."); + return SinzakResponse.error("이미 판매완료된 작품입니다."); ProductSell connect = ProductSell.createConnect(product, user); productSellRepository.save(connect); product.setComplete(true); - return PropertyUtil.response(true); + return SinzakResponse.success(); } @Transactional public JSONObject suggest(@RequestBody SuggestDto dto){ User user = userUtils.getCurrentUser(); if(suggestRepository.findByUserIdAndProductId(user.getId(),dto.getId()).isPresent()) - return PropertyUtil.responseMessage("이미 제안을 하신 작품입니다."); + return SinzakResponse.error("이미 제안을 하신 작품입니다."); Product product = productRepository.findByIdFetchUser(dto.getId()).orElseThrow(); ProductSuggest connect = ProductSuggest.createConnect(product, user); product.setTopPrice(dto.getPrice()); // alarmService.makeAlarm(product.getUser(),product.getThumbnail(),product.getId().toString(),Integer.toString(dto.getPrice())); suggestRepository.save(connect); - return PropertyUtil.response(true); + return SinzakResponse.success(); } @Transactional - public PageImpl productListForUser(String keyword, List categories, String align, boolean complete, Pageable pageable){ + public PageImpl productsForUser(String keyword, List categories, String align, boolean complete, Pageable pageable){ User user = userRepository.findByIdFetchHistoryAndLikesList(userUtils.getCurrentUserId()).orElseThrow(UserNotFoundException::new); List userReports = reportRepository.findByUserId(user.getId()); if(!keyword.isEmpty()){ @@ -415,7 +415,7 @@ public PageImpl productListForUser(String keyword, List catego private void saveSearchHistory(String keyword, User user) { - List historyList = new ArrayList<>(user.getHistoryList()); + List historyList = new ArrayList<>(user.getHistories()); if(historyList.size() >= HistoryMaxCount) historyRepository.delete(historyList.get(0)); for (SearchHistory history : historyList) { @@ -427,7 +427,7 @@ private void saveSearchHistory(String keyword, User user) { } @Transactional(readOnly = true) - public PageImpl productListForGuest(String keyword, List categories, String align, boolean complete, Pageable pageable){ + public PageImpl productsForGuest(String keyword, List categories, String align, boolean complete, Pageable pageable){ if(!keyword.isEmpty()) redisService.addWordToRedis(keyword); Page productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable); List showList = makeShowFormsForGuest(productList);