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

Feature/favorite 찜하기 기능 구현 #85

Merged
merged 5 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading