-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from Central-MakeUs/dev
feat: 구매 인증 기능 구현
- Loading branch information
Showing
82 changed files
with
1,644 additions
and
323 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
### 토큰 재발급 요청 | ||
POST http://localhost:8080/api/v1/auth/refresh-access-token | ||
Content-Type: application/json | ||
|
||
{ | ||
"refreshToken": "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MDYwMDc0MzYsImV4cCI6MTcwNjI2NjYzNn0.7Cnv74WOMVBEKR6K33-EtTyB84DNMuA_FpD1OnduO399LA_tVhCK34V80z1f2dOS9U-CM9D3OqPGLYOmUWKjFQ" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.mm.api.config; | ||
|
||
import org.springframework.web.servlet.config.annotation.CorsRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
public class WebMvcConfig implements WebMvcConfigurer { | ||
@Override | ||
public void addCorsMappings(CorsRegistry registry) { | ||
registry.addMapping("/**") | ||
.allowedOrigins("/**") | ||
.allowedHeaders("*") | ||
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") | ||
.allowCredentials(true); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
api/src/main/java/com/mm/api/domain/auth/controller/AuthController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.mm.api.domain.auth.controller; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.mm.api.domain.auth.dto.request.RefreshTokenRequest; | ||
import com.mm.api.domain.auth.dto.response.TokenResponse; | ||
import com.mm.api.domain.auth.service.AuthService; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Tag(name = "회원 인증", description = "회원 인증 관련 API 입니다.") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class AuthController { | ||
private final AuthService authService; | ||
|
||
@Operation(summary = "oAuth 로그인을 합니다. 현재 provider는 kakao만 제공됩니다.") | ||
@GetMapping("/oauth2/authorization/{oauth2-provider}") | ||
public void login() { | ||
// oauth2 로그인 | ||
} | ||
|
||
@Operation(summary = "access token을 갱신합니다.") | ||
@PostMapping("/api/v1/auth/refresh-access-token") | ||
public ResponseEntity<TokenResponse> refreshAccessToken(@RequestBody RefreshTokenRequest request) { | ||
TokenResponse tokenResponse = authService.refreshAccessToken(request); | ||
return ResponseEntity.ok(tokenResponse); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
api/src/main/java/com/mm/api/domain/auth/dto/request/RefreshTokenRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.mm.api.domain.auth.dto.request; | ||
|
||
public record RefreshTokenRequest(String refreshToken) { | ||
} |
4 changes: 4 additions & 0 deletions
4
api/src/main/java/com/mm/api/domain/auth/dto/response/TokenResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.mm.api.domain.auth.dto.response; | ||
|
||
public record TokenResponse(String accessToken, String refreshToken) { | ||
} |
57 changes: 57 additions & 0 deletions
57
api/src/main/java/com/mm/api/domain/auth/service/AuthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package com.mm.api.domain.auth.service; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.stereotype.Service; | ||
|
||
import com.mm.api.domain.auth.dto.request.RefreshTokenRequest; | ||
import com.mm.api.domain.auth.dto.response.TokenResponse; | ||
import com.mm.api.exception.CustomException; | ||
import com.mm.api.exception.ErrorCode; | ||
import com.mm.coredomain.domain.Member; | ||
import com.mm.coredomain.repository.MemberRepository; | ||
import com.mm.coreinfraredis.repository.RedisRefreshTokenRepository; | ||
import com.mm.coresecurity.jwt.JwtTokenProvider; | ||
import com.mm.coresecurity.oauth.OAuth2UserDetails; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class AuthService { | ||
private final RedisRefreshTokenRepository redisRefreshTokenRepository; | ||
private final JwtTokenProvider jwtTokenProvider; | ||
private final MemberRepository memberRepository; | ||
|
||
public TokenResponse refreshAccessToken(RefreshTokenRequest request) { | ||
Long memberId = redisRefreshTokenRepository.findByRefreshToken(request.refreshToken()) | ||
.orElseThrow(() -> new CustomException(ErrorCode.REFRESH_TOKEN_EXPIRED)); | ||
|
||
Member member = memberRepository.findById(memberId) | ||
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); | ||
|
||
OAuth2UserDetails oauth2UserDetails = createOauth2UserDetails(member); | ||
|
||
String accessToken = jwtTokenProvider.generateAccessToken(oauth2UserDetails); | ||
String refreshToken = jwtTokenProvider.generateRefreshToken(); | ||
|
||
redisRefreshTokenRepository.save(refreshToken, memberId); | ||
|
||
return new TokenResponse(accessToken, refreshToken); | ||
} | ||
|
||
private OAuth2UserDetails createOauth2UserDetails(Member member) { | ||
List<SimpleGrantedAuthority> authorities = member.getGroups() | ||
.getGroupPermissions() | ||
.stream() | ||
.map(groupPermission -> new SimpleGrantedAuthority(groupPermission.getPermission().getName())) | ||
.toList(); | ||
|
||
return OAuth2UserDetails.builder() | ||
.id(member.getId()) | ||
.provider(member.getProvider()) | ||
.authorities(authorities) | ||
.build(); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
api/src/main/java/com/mm/api/domain/buy/controller/BuyController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.mm.api.domain.buy.controller; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PatchMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RequestPart; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import com.mm.api.domain.buy.dto.response.BuyResponse; | ||
import com.mm.api.domain.buy.service.BuyService; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Tag(name = "구매 인증", description = "구매 인증 관련 API 입니다.") | ||
@RestController | ||
@RequestMapping("/api/v1") | ||
@RequiredArgsConstructor | ||
public class BuyController { | ||
private final BuyService buyService; | ||
|
||
// 관리자만 | ||
@Operation(summary = "구매 인증을 페이지 단위로 가져옵니다.") | ||
@GetMapping("/buys") | ||
public ResponseEntity<?> getBuys(@RequestParam(required = false, defaultValue = "1") Integer page) { | ||
List<BuyResponse> responses = buyService.getBuys(page); | ||
return ResponseEntity.ok(responses); | ||
} | ||
|
||
@Operation(summary = "구매 인증 상태를 변경합니다.") | ||
@PatchMapping("/buys/{buyId}/refund-status") | ||
public ResponseEntity<?> updateBuyRefundStatus(@PathVariable Long buyId, | ||
@RequestParam String refundStatus) { | ||
BuyResponse response = buyService.updateBuyRefundStatus(buyId, refundStatus); | ||
return ResponseEntity.ok(response); | ||
} | ||
|
||
// 관리자 + 회원(자신만) | ||
@Operation(summary = "구매 인증을 삭제합니다.") | ||
@DeleteMapping("/buys/{buyId}") | ||
public ResponseEntity<?> deleteBuy(@PathVariable Long buyId) { | ||
buyService.deleteBuy(buyId); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
// 회원만 | ||
@Operation(summary = "구매 인증을 작성합니다.") | ||
@PostMapping("/buys/{memberId}/{itemId}") | ||
public ResponseEntity<?> postBuy(@PathVariable Long memberId, @PathVariable Long itemId, | ||
@RequestPart(value = "file", required = true) MultipartFile file) { | ||
BuyResponse buyResponse = buyService.postBuy(memberId, itemId, file); | ||
return ResponseEntity.ok(buyResponse); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
api/src/main/java/com/mm/api/domain/buy/dto/response/BuyResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.mm.api.domain.buy.dto.response; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import com.mm.coredomain.domain.Buy; | ||
import com.mm.coredomain.domain.RefundStatus; | ||
|
||
import lombok.Builder; | ||
|
||
@Builder | ||
public record BuyResponse(Long id, | ||
String redirectUrl, | ||
LocalDateTime uploadTime, | ||
Integer refund, | ||
RefundStatus refundStatus, | ||
String certImageUrl) { | ||
public static BuyResponse of(Buy buy) { | ||
return BuyResponse.builder() | ||
.id(buy.getId()) | ||
.redirectUrl(buy.getRedirectUrl()) | ||
.uploadTime(buy.getUploadTime()) | ||
.refund(buy.getRefund()) | ||
.refundStatus(buy.getRefundStatus()) | ||
.certImageUrl(buy.getCertImageUrl()) | ||
.build(); | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
api/src/main/java/com/mm/api/domain/buy/service/BuyService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package com.mm.api.domain.buy.service; | ||
|
||
import static com.mm.api.exception.ErrorCode.*; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import com.mm.api.domain.buy.dto.response.BuyResponse; | ||
import com.mm.api.exception.CustomException; | ||
import com.mm.api.exception.ErrorCode; | ||
import com.mm.coredomain.domain.Buy; | ||
import com.mm.coredomain.domain.Item; | ||
import com.mm.coredomain.domain.Member; | ||
import com.mm.coredomain.domain.RefundStatus; | ||
import com.mm.coredomain.repository.BuyRepository; | ||
import com.mm.coredomain.repository.ItemRepository; | ||
import com.mm.coredomain.repository.MemberRepository; | ||
import com.mm.coreinfraqdsl.repository.BuyCustomRepository; | ||
import com.mm.coreinfras3.util.S3Service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class BuyService { | ||
private final BuyRepository buyRepository; | ||
private final MemberRepository memberRepository; | ||
private final ItemRepository itemRepository; | ||
private final S3Service s3Service; | ||
private final BuyCustomRepository buyCustomRepository; | ||
|
||
public BuyResponse postBuy(Long memberId, Long itemId, MultipartFile file) { | ||
Member member = getMember(memberId); | ||
Item item = getItem(itemId); | ||
|
||
String url = s3Service.uploadFileToS3(file, memberId, itemId); | ||
|
||
Buy buy = Buy.builder() | ||
.member(member) | ||
.item(item) | ||
.redirectUrl(item.getRedirectUrl()) | ||
.refund(item.getRefund()) | ||
.refundStatus(RefundStatus.IN_PROGRESS) | ||
.uploadTime(LocalDateTime.now()) | ||
.certImageUrl(url) | ||
.build(); | ||
|
||
return BuyResponse.of(buyRepository.save(buy)); | ||
} | ||
|
||
public BuyResponse updateBuyRefundStatus(Long buyId, String refundStatus) { | ||
RefundStatus convertedRefundStatus = RefundStatus.of(refundStatus); | ||
Buy buy = getBuy(buyId); | ||
buy.updateRefundStatus(convertedRefundStatus); | ||
|
||
return BuyResponse.of(buy); | ||
} | ||
|
||
public void deleteBuy(Long buyId) { | ||
Buy buy = getBuy(buyId); | ||
buyRepository.delete(buy); | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public List<BuyResponse> getBuys(Integer page) { | ||
List<Buy> buys = buyCustomRepository.getBuysByPage(page); | ||
return buys.stream() | ||
.map(BuyResponse::of) | ||
.toList(); | ||
} | ||
|
||
private Buy getBuy(Long buyId) { | ||
return buyRepository.findById(buyId) | ||
.orElseThrow(() -> new CustomException(BUY_NOT_FOUND)); | ||
} | ||
|
||
private Item getItem(Long id) { | ||
return itemRepository.findById(id) | ||
.orElseThrow(() -> new CustomException(ITEM_NOT_FOUND)); | ||
} | ||
|
||
private Member getMember(Long memberId) { | ||
return memberRepository.findById(memberId) | ||
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); | ||
} | ||
} |
Oops, something went wrong.