Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/refactor/convention' into deploy…
Browse files Browse the repository at this point in the history
…/production
  • Loading branch information
in-seo committed Jul 18, 2023
2 parents df5f42a + 39b2355 commit a6ef45f
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 22 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
![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)
<br/>
Expand All @@ -20,9 +17,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)
<br>

## 📚 사용 스택
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package net.sinzak.server.config;
package net.sinzak.server.common.redis;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
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;
Expand All @@ -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;

Expand All @@ -53,6 +55,16 @@ public CacheManager testCacheManager(RedisConnectionFactory cf){
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build();
}

@Bean
public RedisTemplate<String, Object> autoCompleteRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return redisTemplate;

}

private ObjectMapper objectMapper() {
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
.builder()
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/net/sinzak/server/common/redis/RedisService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package net.sinzak.server.common.redis;

import lombok.RequiredArgsConstructor;
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 {

private final RedisTemplate<String,String> redisTemplate;

private static final String REDIS_KEY_AUTO_COMPLETE ="autoComplete:";
private static final String COMPLETE_WORD ="+";
private static final Integer NUMBER_OF_WORD_TO_SHOW = 5;
public void addWordToRedis(String newWord){
String key = REDIS_KEY_AUTO_COMPLETE;
for(int i=0;i<newWord.length();i++){
String word = newWord.substring(i,i+1);
redisTemplate.opsForZSet().incrementScore(key,word,1);
key += word;
}
redisTemplate.opsForZSet().add(key,COMPLETE_WORD,0);
}

public List<String> getAutoCompleteWords(String prefix){
List<String> wordsToShow = new ArrayList<>();
findEndOfWord(prefix,wordsToShow);
return wordsToShow;
}
public void findEndOfWord(String prefix,List<String> wordsToShow){
List<String> temporaryWord = redisTemplate
.opsForZSet()
.reverseRangeWithScores(REDIS_KEY_AUTO_COMPLETE+prefix,0,NUMBER_OF_WORD_TO_SHOW)
.stream()
.map(ZSetOperations.TypedTuple::getValue)
.collect(Collectors.toList());
for(int i=0;i<temporaryWord.size();i++){
if(wordsToShow.size()>=NUMBER_OF_WORD_TO_SHOW) return;
if(temporaryWord.get(i).equals(COMPLETE_WORD)) wordsToShow.add(prefix);
if(!temporaryWord.get(i).equals(COMPLETE_WORD)) findEndOfWord(prefix+temporaryWord.get(i),wordsToShow);
}
}


// 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);
// }

}
20 changes: 20 additions & 0 deletions src/main/java/net/sinzak/server/common/redis/SearchNode.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,10 @@ public PageImpl<ShowForm> showMarketProduct(@RequestParam(required = false, defa
}
}


@ApiOperation(value = "검색어 자동완성")
@PostMapping("/products/auto-complete")
public List<String> autoCompleteSearch(@RequestParam String keyWord){
return productService.getCompleteWord(keyWord);
}
}
39 changes: 25 additions & 14 deletions src/main/java/net/sinzak/server/product/service/ProductService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
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;
import net.sinzak.server.user.domain.Role;
Expand All @@ -13,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.SinzakResponse;
import net.sinzak.server.product.dto.*;
import net.sinzak.server.product.repository.*;
import net.sinzak.server.user.domain.User;
Expand Down Expand Up @@ -54,6 +55,8 @@ public class ProductService implements PostService<Product,ProductPostDto,Produc
private final ProductQDSLRepositoryImpl QDSLRepository;
private final SearchHistoryRepository historyRepository;
private final AlarmService alarmService;
private final RedisService redisService;


private final int HistoryMaxCount = 10;
private final int HOME_OBJECTS = 10;
Expand Down Expand Up @@ -93,7 +96,7 @@ public JSONObject saveImageInS3AndProduct(List<MultipartFile> multipartFiles, Lo
return SinzakResponse.error(multipartFiles.indexOf(img)+"번째 이미지부터 저장 실패");
}
}
return SinzakResponse.success(true);
return SinzakResponse.success();
}


Expand Down Expand Up @@ -141,7 +144,7 @@ public JSONObject editPost(Long productId, ProductEditDto editDto){

product.editPost(editDto);
productRepository.save(product);
return SinzakResponse.success(true);
return SinzakResponse.success();
}

@Transactional(rollbackFor = {Exception.class})
Expand All @@ -153,7 +156,7 @@ public JSONObject deletePost(Long productId){
return SinzakResponse.error("글 작성자가 아닙니다.");
deleteImagesInPost(product);
product.setDeleted(true);
return SinzakResponse.success(true);
return SinzakResponse.success();
}

private void deleteImagesInPost(Product product) {
Expand Down Expand Up @@ -259,8 +262,8 @@ public JSONObject showHomeForUser(Long userId){
List<Product> list = QDSLRepository.findCountByCategoriesDesc(userCategories, HOME_OBJECTS);
obj.put("recommend", makeHomeShowForms(user.getProductLikesList(), list)); /** 추천목록 **/

List<Product> followingList = getFollowingList(user, productList, HOME_OBJECTS);
obj.put("following", makeHomeShowForms(user.getProductLikesList(),followingList)); /** 팔로잉 **/
List<Product> followings = getFollowingList(user, productList, HOME_OBJECTS);
obj.put("following", makeHomeShowForms(user.getProductLikesList(),followings)); /** 팔로잉 **/

return SinzakResponse.success(obj);
}
Expand All @@ -269,15 +272,15 @@ public JSONObject showHomeForUser(Long userId){
@Transactional(readOnly = true)
public JSONObject showHomeForGuest(){
JSONObject obj = new JSONObject();
List<Product> productList = productRepository.findAllProductNotDeleted();
List<Product> products = productRepository.findAllProductNotDeleted();

obj.put("new", makeHomeShowFormsForGuest(productList));
obj.put("new", makeHomeShowFormsForGuest(products));

List<Product> tradingList = getTradingList(productList, HOME_OBJECTS); /** Trading = 최신 목록 내림차순 중 채팅수가 1이상, 거래 완료되지 않은 것 **/
List<Product> 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 SinzakResponse.success(obj);
}
Expand Down Expand Up @@ -376,7 +379,7 @@ public JSONObject sell(@RequestBody SellDto dto){
ProductSell connect = ProductSell.createConnect(product, user);
productSellRepository.save(connect);
product.setComplete(true);
return SinzakResponse.success(true);
return SinzakResponse.success();
}

@Transactional
Expand All @@ -389,15 +392,18 @@ public JSONObject suggest(@RequestBody SuggestDto dto){
product.setTopPrice(dto.getPrice());
// alarmService.makeAlarm(product.getUser(),product.getThumbnail(),product.getId().toString(),Integer.toString(dto.getPrice()));
suggestRepository.save(connect);
return SinzakResponse.success(true);
return SinzakResponse.success();
}

@Transactional
public PageImpl<ShowForm> productsForUser(String keyword, List<String> categories, String align, boolean complete, Pageable pageable){
User user = userRepository.findByIdFetchHistoryAndLikesList(userUtils.getCurrentUserId()).orElseThrow(UserNotFoundException::new);
List<Report> userReports = reportRepository.findByUserId(user.getId());
if(!keyword.isEmpty())
if(!keyword.isEmpty()){
saveSearchHistory(keyword, user);
redisService.addWordToRedis(keyword);
}

Page<Product> productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable);
List<Product> products = new ArrayList<>(productList.getContent()); // Page의 content를 필터링 할 수 없어서 재생성.
log.error("{}", products.size());
Expand All @@ -422,6 +428,7 @@ private void saveSearchHistory(String keyword, User user) {

@Transactional(readOnly = true)
public PageImpl<ShowForm> productsForGuest(String keyword, List<String> categories, String align, boolean complete, Pageable pageable){
if(!keyword.isEmpty()) redisService.addWordToRedis(keyword);
Page<Product> productList = QDSLRepository.findAllByCompleteAndCategoriesAligned(complete, keyword, categories, align, pageable);
List<ShowForm> showList = makeShowFormsForGuest(productList);
return new PageImpl<>(showList, pageable, productList.getTotalElements());
Expand Down Expand Up @@ -481,4 +488,8 @@ private void removeBlockPost(List<Report> userReportList, List<Product> productL
}
}
}

public List<String> getCompleteWord(String keyWord) {
return redisService.getAutoCompleteWords(keyWord);
}
}

0 comments on commit a6ef45f

Please sign in to comment.