-
Notifications
You must be signed in to change notification settings - Fork 111
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
전남대 BE_김보민 5주차 과제 (2단계) #373
base: kbm05haruin30
Are you sure you want to change the base?
Changes from 25 commits
60fe159
6997d12
0640ac4
b23b8ae
cff38b6
fef7ea6
e6730f2
2b9840f
0795b1d
5ae4719
c16fa52
c02d103
01c1d33
29f1c77
16dd27f
96645ac
493847c
00c15b8
ea2c247
15f57aa
1312eff
60c8919
9c13918
c3ad5d1
5408450
d8bb7dd
c656659
27a49dd
c0f87b3
f5d5d15
09f1468
0527adc
3f27c40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package gift.controller; | ||
|
||
import gift.annotation.LoginMember; | ||
import gift.dto.OrderRequestDTO; | ||
import gift.dto.OrderResponseDTO; | ||
import gift.model.Member; | ||
import gift.service.KakaoService; | ||
import gift.service.OrderService; | ||
import jakarta.validation.Valid; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.ui.Model; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
|
||
@Controller | ||
@RequestMapping("/orders/{optionId}") | ||
public class OrderController { | ||
|
||
private final OrderService orderService; | ||
private final KakaoService kakaoService; | ||
|
||
public OrderController(OrderService orderService, KakaoService kakaoService) { | ||
this.orderService = orderService; | ||
this.kakaoService = kakaoService; | ||
} | ||
|
||
@GetMapping | ||
public String showOrderForm(@PathVariable("optionId") Long optionId, Model model) { | ||
OrderRequestDTO orderRequestDTO = new OrderRequestDTO(optionId, 1L, "임시 메시지", null); | ||
model.addAttribute("orderRequestDTO", orderRequestDTO); | ||
return "order_form"; | ||
} | ||
|
||
@PostMapping | ||
public String addOrder(@PathVariable("optionId") Long optionId, | ||
@RequestBody @Valid OrderRequestDTO orderRequestDTO, @LoginMember Member member) { | ||
if (member == null) { | ||
return "redirect:/members/login"; | ||
} | ||
OrderResponseDTO orderResponseDTO = orderService.createOrder(orderRequestDTO, | ||
member.getEmail()); | ||
String accessToken = orderRequestDTO.accessToken(); | ||
kakaoService.sendKakaoMessage(accessToken, orderResponseDTO); | ||
return "redirect:/admin/products"; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package gift.dto; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
public record KakaoAccountDTO( | ||
@JsonProperty("email") | ||
String email | ||
) { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package gift.dto; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
public record KakaoUserInfoDTO( | ||
@JsonProperty("id") | ||
Long id, | ||
@JsonProperty("kakao_account") | ||
KakaoAccountDTO kakaoAccountDTO | ||
) { | ||
|
||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package gift.dto; | ||
|
||
public record OrderRequestDTO( | ||
Long optionId, | ||
Long quantity, | ||
String message, | ||
String accessToken | ||
) { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package gift.dto; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
public record OrderResponseDTO( | ||
Long id, | ||
Long optionId, | ||
Long quantity, | ||
LocalDateTime orderDateTime, | ||
String message | ||
) { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package gift.model; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.Table; | ||
import java.time.LocalDateTime; | ||
|
||
@Entity | ||
@Table(name = "orders") | ||
public class Order { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne | ||
@JoinColumn(name = "option_id", nullable = false) | ||
private Option option; | ||
|
||
@Column(name = "quantity", nullable = false) | ||
private Long quantity; | ||
|
||
@Column(name = "order_date_time", nullable = false) | ||
private LocalDateTime orderDateTime; | ||
|
||
@Column(name = "message") | ||
private String message; | ||
|
||
@ManyToOne | ||
@JoinColumn(name = "member_id", nullable = false) | ||
private Member member; | ||
|
||
protected Order() { | ||
} | ||
|
||
public Order(Long id, Option option, Long quantity, LocalDateTime orderDateTime, String message, | ||
Member member) { | ||
this.id = id; | ||
this.option = option; | ||
this.quantity = quantity; | ||
this.orderDateTime = orderDateTime; | ||
this.message = message; | ||
this.member = member; | ||
} | ||
|
||
public Long getId() { | ||
return id; | ||
} | ||
|
||
public Option getOption() { | ||
return option; | ||
} | ||
|
||
public Long getQuantity() { | ||
return quantity; | ||
} | ||
|
||
public LocalDateTime getOrderDateTime() { | ||
return orderDateTime; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
public Member getMember() { | ||
return member; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package gift.repository; | ||
|
||
import gift.model.Order; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface OrderRepository extends JpaRepository<Order, Long> { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,19 @@ | |
|
||
import gift.config.KakaoProperties; | ||
import gift.dto.KakaoAccessTokenDTO; | ||
import gift.dto.KakaoUserInfoDTO; | ||
import gift.dto.MemberDTO; | ||
import gift.dto.OrderResponseDTO; | ||
import gift.model.Member; | ||
import gift.repository.MemberRepository; | ||
import gift.util.JwtUtil; | ||
import java.net.URI; | ||
import java.util.Random; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.web.client.RestClient; | ||
|
||
|
@@ -14,9 +23,14 @@ public class KakaoService { | |
|
||
private final KakaoProperties kakaoProperties; | ||
private final RestClient restClient = RestClient.builder().build(); | ||
private final MemberRepository memberRepository; | ||
private final JwtUtil jwtUtil; | ||
|
||
public KakaoService(KakaoProperties kakaoProperties) { | ||
public KakaoService(KakaoProperties kakaoProperties, MemberRepository memberRepository, | ||
JwtUtil jwtUtil) { | ||
this.kakaoProperties = kakaoProperties; | ||
this.memberRepository = memberRepository; | ||
this.jwtUtil = jwtUtil; | ||
} | ||
|
||
public String generateKakaoLoginUrl() { | ||
|
@@ -44,4 +58,72 @@ private LinkedMultiValueMap<String, String> createBody(String authorizationCode) | |
body.add("code", authorizationCode); | ||
return body; | ||
} | ||
|
||
public String getUserEmail(String accessToken) { | ||
String url = kakaoProperties.userInfoUrl(); | ||
ResponseEntity<KakaoUserInfoDTO> response = restClient.get() | ||
.uri(URI.create(url)) | ||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) | ||
.retrieve() | ||
.toEntity(KakaoUserInfoDTO.class); | ||
KakaoUserInfoDTO kakaoUserInfoDTO = response.getBody(); | ||
return kakaoUserInfoDTO.kakaoAccountDTO().email(); | ||
} | ||
|
||
@Transactional | ||
public Member saveKakaoUser(String email) { | ||
Member member = memberRepository.findByEmail(email); | ||
if (member == null) { | ||
String name = email.split("@")[0]; | ||
String password = generateRandomPassword(); | ||
MemberDTO memberDTO = new MemberDTO(name, email, password); | ||
member = new Member(null, memberDTO.name(), memberDTO.email(), memberDTO.password(), | ||
"user"); | ||
memberRepository.save(member); | ||
} | ||
return member; | ||
} | ||
|
||
public String generateToken(String email, String role) { | ||
String jwtToken = jwtUtil.generateToken(email, role); | ||
return jwtToken; | ||
} | ||
|
||
public void sendKakaoMessage(String accessToken, OrderResponseDTO orderResponseDTO) { | ||
String url = kakaoProperties.sendMessageUrl(); | ||
final LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | ||
body.add("template_object", TemplateObject(orderResponseDTO.id(), | ||
orderResponseDTO.optionId(), orderResponseDTO.quantity(), | ||
orderResponseDTO.orderDateTime().toString(), orderResponseDTO.message())); | ||
ResponseEntity<String> response = restClient.post() | ||
.uri(URI.create(url)) | ||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) | ||
.contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||
.body(body) | ||
.retrieve() | ||
.toEntity(String.class); | ||
} | ||
|
||
private String TemplateObject(Long id, Long optionId, Long quantity, String orderDateTime, | ||
String message) { | ||
return "{" | ||
+ "\"object_type\":\"text\"," | ||
+ "\"text\":\"주문 정보:\\n주문 ID: " + id + "\\n옵션 ID: " + optionId + "\\n수량: " + quantity | ||
+ "\\n주문 시간: " + orderDateTime + "\\n메시지: " + message + "\"," | ||
+ "\"link\":{\"web_url\":\"http://localhost:8080/admin/products\",\"mobile_web_url\":\"http://localhost:8080/admin/products\"}" | ||
+ "}"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 문자열로 다 입력하게 된다면, 관리하기 힘들지 않을까요? 전송할 내용이 많아진다면 문자열을 일일히 수정해야 될 것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확실히 전송할 내용이 많아지게 되면 문자열을 일일히 수정해야 한다는 불편한 점이 생길 것 같습니다. 하나의 TemplateObject DTO 클래스를 만들어 관리할 수 있도록 수정하겠습니다! |
||
} | ||
|
||
private String generateRandomPassword() { | ||
int leftLimit = 48; | ||
int rightLimit = 122; | ||
int targetStringLength = 20; | ||
Random random = new Random(); | ||
String generatedPassword = random.ints(leftLimit, rightLimit + 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 범위를 이렇게 잡은 이유가 어떻게 될까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 비밀 번호를 생성할 때 랜덤하게 만들고 싶었습니다. 그 중 영어 대문자, 소문자, 숫자를 사용해서 만들고 싶었습니다. 숫자 0은 아스키 코드에서 48이고 소문자 z는 아스키 코드에서 122이므로 범위를 위와 같이 잡았습니다. 비밀 번호의 길이는 몇 자가 적당할까 하다가 20자로 정했습니다. |
||
.filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) | ||
.limit(targetStringLength) | ||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) | ||
.toString(); | ||
return generatedPassword; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 작성하신 알고리즘의 원리가 궁금합니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. random을 사용하여 48에서 122까지의 난수를 생성합니다. StringBuilder에 관한 설명 |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
카카오톡 메시지 전송 로직에서 에러가 발생하게 된다면 정상 응답이 안내려갈 것 같아요. 주문은 정상적으로 완료되었으나, 카카오톡 전송 로직이 실패했다고 해서 사용자가 실패응답을 받는게 맞을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞지 않은 것 같습니다. 이것도 마찬가지로 예외가 발생하면 로그를 남기는 형식으로 처리해보도록 하겠습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음에는 model.addAttribute를 사용하여 "message"를 만들고 에러를 처리하려고 했었습니다. 하지만 생각대로 출력이 되지 않아서 로그를 남기는 형식으로 처리했습니다.