Skip to content

Commit

Permalink
Merge pull request #85 from YAPP-Github/feature/favorite
Browse files Browse the repository at this point in the history
Feature/favorite 찜하기 기능 구현
  • Loading branch information
sooyoungh authored Oct 8, 2023
2 parents 025a6c2 + 88d1e75 commit f03b547
Show file tree
Hide file tree
Showing 64 changed files with 540 additions and 214 deletions.
11 changes: 0 additions & 11 deletions src/main/java/com/pyonsnalcolor/auth/oauth/OAuthClient.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ public BasicBatchServiceTemplate(BasicProductRepository basicProductRepository)
public void execute() {
List<T> allProducts = getAllProducts();
List<T> newProducts = getNewProducts(allProducts);
List<T> eventExpiredProducts = getEventExpiredProducts(allProducts);
updateExpiredProducts(allProducts);
sendAlarms(newProducts);
saveProducts(newProducts);
deleteProducts(eventExpiredProducts);
}

protected abstract List<T> getAllProducts();
Expand All @@ -33,7 +32,7 @@ public void execute() {
* @param allProducts
* @return
*/
protected List<T> getEventExpiredProducts(List<T> allProducts) {
protected List<T> updateExpiredProducts(List<T> allProducts) {
return Collections.emptyList();
}

Expand All @@ -48,12 +47,4 @@ private final void sendAlarms(List<T> baseProducts) {
private final void saveProducts(List<T> baseProducts) {
basicProductRepository.saveAll(baseProducts);
}

/**
* event 상품에 대해서만 구현체 작성 필요
*
* @param baseProducts
*/
protected void deleteProducts(List<T> baseProducts) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,48 @@ protected <T extends BaseProduct> List<T> getNewProducts(List<T> allProducts) {
List<T> alreadyExistProducts = basicProductRepository.findAll();
List<T> newProducts = allProducts.stream()
.filter(product -> !alreadyExistProducts.contains(product))
.peek(product -> log.info("새로운 행사 상품이 저장됩니다. {}", product))
.peek(product -> log.info("새로운 행사 상품이 저장됩니다. {}", product.getName()))
.collect(Collectors.toList());
return newProducts;
}

@Override
protected List<BaseEventProduct> getEventExpiredProducts(List<BaseEventProduct> allProducts) {
protected List<BaseEventProduct> updateExpiredProducts(List<BaseEventProduct> allProducts) {
if(allProducts.isEmpty()) {
return Collections.emptyList();
}

List<BaseEventProduct> alreadyExistEventProducts = basicProductRepository.findAll();
List<BaseEventProduct> expiredEventProducts = alreadyExistEventProducts.stream()
.peek(product -> log.info("지난 행사 상품이 삭제됩니다. {}", product))
.filter(product -> !alreadyExistEventProducts.contains(product))
.collect(Collectors.toList());

return expiredEventProducts;
}
updateEventTypeOfEventProductsIfExpired(expiredEventProducts);
updateEventTypeOfPbProductsIfExpired(expiredEventProducts);

@Override
protected void deleteProducts(List<BaseEventProduct> expiredEventProducts) {
updateEventTypeOfAllProductsIfExpired(expiredEventProducts);
basicProductRepository.deleteAll(expiredEventProducts);
return expiredEventProducts;
}

private void updateEventTypeOfAllProductsIfExpired(List<BaseEventProduct> expiredEventProducts) {
expiredEventProducts
.forEach(this::updateEventTypeIfEventExpired);
protected void updateEventTypeOfEventProductsIfExpired(List<BaseEventProduct> expiredEventProducts) {
expiredEventProducts.stream()
.forEach(e -> {
e.setIsEventExpiredTrue();
e.updateEventType(EventType.NONE);
basicProductRepository.save(e);
}
);
}

private void updateEventTypeIfEventExpired(BaseEventProduct baseEventProduct) {
pbProductRepository.findAll().stream()
.filter(basePbProduct -> basePbProduct.equals(baseEventProduct))
.findFirst()
.ifPresent(basePbProduct -> {
basePbProduct.updateEventType(EventType.NONE);
log.info("PB 상품 {}의 행사가 종료되어 행사 정보를 삭제합니다.", basePbProduct);
});
private void updateEventTypeOfPbProductsIfExpired(List<BaseEventProduct> expiredEventProducts) {
expiredEventProducts.forEach(baseEventProduct -> {
pbProductRepository.findAll().stream()
.filter(basePbProduct -> basePbProduct.equals(baseEventProduct))
.findFirst()
.ifPresent(basePbProduct -> {
log.info("PB 상품 {}의 행사가 종료되어 행사 정보를 삭제합니다.", basePbProduct.getName());
basePbProduct.updateEventType(EventType.NONE);
pbProductRepository.save(basePbProduct);
});
});
}
}
7 changes: 3 additions & 4 deletions src/main/java/com/pyonsnalcolor/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.pyonsnalcolor.config;

import com.pyonsnalcolor.auth.security.AuthUserDetailsService;
import com.pyonsnalcolor.auth.security.JwtAuthenticationFilter;
import com.pyonsnalcolor.member.security.AuthUserDetailsService;
import com.pyonsnalcolor.member.security.JwtAuthenticationFilter;
import com.pyonsnalcolor.handler.JwtAccessDeniedHandler;
import com.pyonsnalcolor.handler.JwtAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -51,8 +51,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**", "/products/**",
"/promotions/**", "/fcm/**", "/manage/**").permitAll()
.antMatchers("/auth/**", "/promotions/**", "/fcm/**", "/manage/**").permitAll()
.antMatchers("/member/**").hasRole("USER")
.anyRequest().authenticated()
.and()
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/pyonsnalcolor/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.pyonsnalcolor.config;

import com.pyonsnalcolor.auth.AuthParameterResolver;
import com.pyonsnalcolor.member.AuthParameterResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ public enum CommonErrorCode implements ErrorCode {
SERVER_UNAVAILABLE(SERVICE_UNAVAILABLE, "서버에 오류가 발생하였습니다."),
INVALID_PARAMETER(BAD_REQUEST, "입력값이 형식에 맞지 않습니다."),
INVALID_FILTER_CODE(BAD_REQUEST, "필터 코드값이 유효하지 않습니다."),
NOT_FOUND_ERROR(NOT_FOUND, "입력값이 형식에 맞지 않습니다.");
NOT_FOUND_ERROR(NOT_FOUND, "입력값이 형식에 맞지 않습니다."),

INVALID_PRODUCT_TYPE(BAD_REQUEST, "해당 상품 유형에 상품 id가 존재하지 않습니다."),
UNMATCHED_PRODUCT_MEMBER(BAD_REQUEST, "찜한 상품 id와 사용자 정보가 일치하지 않습니다."),
FAVORITE_PRODUCT_ALREADY_EXIST(BAD_REQUEST, "해당하는 찜한 상품이 이미 저장되어 있습니다."),
FAVORITE_PRODUCT_NOT_EXIST(BAD_REQUEST, "삭제할 상품이 사용자의 찜한 상품에 존재하지 않습니다.");

private final HttpStatus httpStatus;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.pyonsnalcolor.auth;
package com.pyonsnalcolor.member;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.pyonsnalcolor.auth;
package com.pyonsnalcolor.member;

import com.pyonsnalcolor.auth.security.AuthUserDetails;
import com.pyonsnalcolor.member.security.AuthUserDetails;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.pyonsnalcolor.auth;
package com.pyonsnalcolor.member;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.pyonsnalcolor.auth.controller;
package com.pyonsnalcolor.member.controller;

import com.pyonsnalcolor.auth.MemberRepository;
import com.pyonsnalcolor.auth.dto.*;
import com.pyonsnalcolor.auth.enumtype.OAuthType;
import com.pyonsnalcolor.auth.service.MemberService;
import com.pyonsnalcolor.member.repository.MemberRepository;
import com.pyonsnalcolor.member.dto.*;
import com.pyonsnalcolor.member.enumtype.OAuthType;
import com.pyonsnalcolor.member.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -20,7 +20,7 @@
@RequestMapping("/auth")
public class AuthController {

private final MemberService memberService;
private final AuthService authService;
private final MemberRepository memberRepository;

@Operation(summary = "OAuth 인증", description = "Token으로 이메일 정보를 얻어 회원가입/재로그인합니다.")
Expand All @@ -29,7 +29,7 @@ public class AuthController {
public ResponseEntity<LoginResponseDto> login(
@RequestBody LoginRequestDto loginRequestDto
) {
LoginResponseDto loginResponseDto = memberService.oAuthLogin(loginRequestDto);
LoginResponseDto loginResponseDto = authService.oAuthLogin(loginRequestDto);
return new ResponseEntity(loginResponseDto, HttpStatus.OK);
}

Expand All @@ -39,7 +39,7 @@ public ResponseEntity<LoginResponseDto> login(
public ResponseEntity<JoinStatusResponseDto> getJoinStatus(
@RequestBody LoginRequestDto loginRequestDto
) {
JoinStatusResponseDto joinStatusResponseDto = memberService.getJoinStatus(loginRequestDto);
JoinStatusResponseDto joinStatusResponseDto = authService.getJoinStatus(loginRequestDto);
return new ResponseEntity(joinStatusResponseDto, HttpStatus.OK);
}

Expand All @@ -49,7 +49,7 @@ public ResponseEntity<JoinStatusResponseDto> getJoinStatus(
public ResponseEntity<LoginResponseDto> reissueAccessToken(
@RequestBody TokenDto tokenRequestDto
) {
LoginResponseDto tokenResponseDto = memberService.reissueAccessToken(tokenRequestDto);
LoginResponseDto tokenResponseDto = authService.reissueAccessToken(tokenRequestDto);
return new ResponseEntity(tokenResponseDto, HttpStatus.OK);
}

Expand All @@ -59,7 +59,7 @@ public ResponseEntity<LoginResponseDto> testLogin(
@RequestParam OAuthType oAuthType,
@RequestParam String email
) {
LoginResponseDto loginResponseDto = memberService.join(oAuthType, email);
LoginResponseDto loginResponseDto = authService.join(oAuthType, email);
return new ResponseEntity(loginResponseDto, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.pyonsnalcolor.member.controller;

import com.pyonsnalcolor.member.AuthMemberId;
import com.pyonsnalcolor.member.dto.FavoriteRequestDto;
import com.pyonsnalcolor.member.service.MemberService;
import com.pyonsnalcolor.product.ProductFactory;
import com.pyonsnalcolor.product.dto.ProductResponseDto;
import com.pyonsnalcolor.product.enumtype.ProductType;
import com.pyonsnalcolor.product.service.ProductService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@Tag(name = "찜하기 관련 api")
@RestController
@RequiredArgsConstructor
@RequestMapping("/favorites")
public class FavoriteController {

private final MemberService memberService;
private final ProductFactory productFactory;

@Operation(summary = "찜한 상품 조회", description = "찜한 PB 혹은 행사 상품을 조회합니다.")
@GetMapping
public ResponseEntity<Slice<ProductResponseDto>> getFavorites(
@RequestParam int pageNumber,
@RequestParam int pageSize,
@RequestParam ProductType productType,
@Parameter(hidden = true) @AuthMemberId Long memberId
) {
Pageable pageable = PageRequest.of(pageNumber, pageSize);
Slice<String> productIds = memberService.getProductIdsOfFavorite(pageable, memberId, productType);
ProductService productService = productFactory.getService(productType);
Slice<ProductResponseDto> results = productService.getProductsOfFavoriteByIds(productIds);
return new ResponseEntity(results, HttpStatus.OK);
}

@Operation(summary = "찜하기 등록", description = "상품을 찜하기에 등록합니다.")
@PostMapping
public ResponseEntity<Void> createFavorite(
@Parameter(hidden = true) @AuthMemberId Long memberId,
@RequestBody FavoriteRequestDto favoriteRequestDto
) {
memberService.createFavorite(memberId, favoriteRequestDto);
return new ResponseEntity(HttpStatus.OK);
}

@Operation(summary = "찜하기 취소", description = "상품을 찜하기에서 취소합니다.")
@DeleteMapping
public ResponseEntity<Void> deleteFavorite(
@Parameter(hidden = true) @AuthMemberId Long memberId,
@RequestBody FavoriteRequestDto favoriteRequestDto
) {
memberService.deleteFavorite(memberId, favoriteRequestDto);
return new ResponseEntity(HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.pyonsnalcolor.auth.controller;
package com.pyonsnalcolor.member.controller;

import com.pyonsnalcolor.auth.AuthMemberId;
import com.pyonsnalcolor.auth.dto.MemberInfoResponseDto;
import com.pyonsnalcolor.auth.dto.NicknameRequestDto;
import com.pyonsnalcolor.auth.dto.TokenDto;
import com.pyonsnalcolor.auth.service.MemberService;
import com.pyonsnalcolor.member.AuthMemberId;
import com.pyonsnalcolor.member.dto.MemberInfoResponseDto;
import com.pyonsnalcolor.member.dto.NicknameRequestDto;
import com.pyonsnalcolor.member.dto.TokenDto;
import com.pyonsnalcolor.member.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -21,13 +21,13 @@
@RequestMapping("/member")
public class MemberController {

private final MemberService memberService;
private final AuthService authService;

@Operation(summary = "로그아웃", description = "사용자의 JWT 토큰을 무효화합니다.")
@Parameter(name = "tokenDto", description = "JWT 로그인 토큰")
@DeleteMapping("/logout")
public ResponseEntity logout(@RequestBody TokenDto tokenDto) {
memberService.logout(tokenDto);
authService.logout(tokenDto);
return new ResponseEntity(HttpStatus.OK);
}

Expand All @@ -36,7 +36,7 @@ public ResponseEntity logout(@RequestBody TokenDto tokenDto) {
public ResponseEntity<MemberInfoResponseDto> getMemberInfo(
@Parameter(hidden = true) @AuthMemberId Long memberId
) {
MemberInfoResponseDto memberInfoResponseDto = memberService.getMemberInfo(memberId);
MemberInfoResponseDto memberInfoResponseDto = authService.getMemberInfo(memberId);
return new ResponseEntity(memberInfoResponseDto, HttpStatus.OK);
}

Expand All @@ -47,7 +47,7 @@ public ResponseEntity<TokenDto> updateNickname(
@RequestBody @Valid NicknameRequestDto nicknameRequestDto,
@Parameter(hidden = true) @AuthMemberId Long memberId
) {
memberService.updateNickname(memberId, nicknameRequestDto);
authService.updateNickname(memberId, nicknameRequestDto);
return new ResponseEntity(HttpStatus.OK);
}

Expand All @@ -56,7 +56,7 @@ public ResponseEntity<TokenDto> updateNickname(
public ResponseEntity<TokenDto> withdraw(
@Parameter(hidden = true) @AuthMemberId Long memberId
) {
memberService.withdraw(memberId);
authService.withdraw(memberId);
return new ResponseEntity(HttpStatus.OK);
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/pyonsnalcolor/member/dto/FavoriteRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.pyonsnalcolor.member.dto;

import com.pyonsnalcolor.product.enumtype.ProductType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Schema(description = "상품 찜하기 등록 DTO")
@Getter
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class FavoriteRequestDto {

private String productId;

private String productType;

public ProductType getProductType() {
return ProductType.valueOf(productType.toUpperCase());
}
}
Loading

0 comments on commit f03b547

Please sign in to comment.