From a63a7097ff43aa8ce06a96c37cfe10b771b87b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= Date: Tue, 19 Nov 2024 00:02:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=8C=80=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 결제 기능 요구사항 반영 * feat: Team 도메인 수정 및 일급 컬렉션화 (#3) * feat: 팀 생성 기능 구현 (#4) * feat: Team 도메인 수정 및 일급 컬렉션화 * feat: 팀 생성 기능 구현 --- .../payment/application/KakaopayService.java | 171 +++++++++--------- .../application/PaymentProcessingService.java | 22 +-- .../payment/application/PaymentService.java | 8 +- .../domain/team/application/TeamService.java | 40 ++++ .../jangburich/domain/team/domain/Team.java | 21 ++- .../domain/team/domain/TeamLeader.java | 4 +- .../domain/team/domain/UserTeam.java | 46 +++++ .../domain/repository/UserTeamRepository.java | 7 + .../team/dto/request/RegisterTeamRequest.java | 10 +- .../team/presentation/TeamController.java | 5 +- .../jangburich/domain/user/domain/User.java | 8 - .../utils/parser/AuthenticationParser.java | 4 +- 12 files changed, 219 insertions(+), 127 deletions(-) create mode 100644 src/main/java/com/jangburich/domain/team/domain/UserTeam.java create mode 100644 src/main/java/com/jangburich/domain/team/domain/repository/UserTeamRepository.java diff --git a/src/main/java/com/jangburich/domain/payment/application/KakaopayService.java b/src/main/java/com/jangburich/domain/payment/application/KakaopayService.java index db4bad0..d3c7e81 100644 --- a/src/main/java/com/jangburich/domain/payment/application/KakaopayService.java +++ b/src/main/java/com/jangburich/domain/payment/application/KakaopayService.java @@ -1,16 +1,5 @@ package com.jangburich.domain.payment.application; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; - import com.jangburich.domain.payment.domain.PaymentChargeStatus; import com.jangburich.domain.payment.domain.TeamChargeHistory; import com.jangburich.domain.payment.domain.repository.TeamChargeHistoryRepository; @@ -20,114 +9,116 @@ import com.jangburich.domain.payment.exception.TeamNotFoundException; import com.jangburich.domain.team.domain.Team; import com.jangburich.domain.team.domain.repository.TeamRepository; - +import java.util.HashMap; +import java.util.Map; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; @Service @RequiredArgsConstructor public class KakaopayService implements PaymentService { - private final TeamChargeHistoryRepository teamChargeHistoryRepository; - @Value("${kakaopay.secretKey}") - private String secretKey; - - @Value("${kakaopay.approve-url}") - private String approvalUrl; - - @Value("${kakaopay.cancel-url}") - private String cancelUrl; + private final TeamChargeHistoryRepository teamChargeHistoryRepository; + @Value("${kakaopay.secretKey}") + private String secretKey; - @Value("${kakaopay.fail-url}") - private String failUrl; + @Value("${kakaopay.approve-url}") + private String approvalUrl; - private final TeamRepository teamRepository; + @Value("${kakaopay.cancel-url}") + private String cancelUrl; - private ResponseEntity readyResponseResponseEntity; - private String userId; + @Value("${kakaopay.fail-url}") + private String failUrl; - @Override - public String getType() { - return "kakao"; - } + private final TeamRepository teamRepository; - @Transactional - @Override - public ReadyResponse payReady(Long userId, PayRequest payRequest) { - this.userId = String.valueOf(userId); + @Override + public String getType() { + return "kakao"; + } - Map parameters = new HashMap<>(); + @Transactional + @Override + public ReadyResponse payReady(String userId, PayRequest payRequest) { + Map parameters = new HashMap<>(); - parameters.put("cid", "TC0ONETIME"); // 가맹점 코드(테스트용) - parameters.put("partner_order_id", "1234567890"); // 주문번호 - parameters.put("partner_user_id", String.valueOf(userId)); // 회원 아이디 - parameters.put("item_name", "ITEM_NAME"); // 상품명 - parameters.put("quantity", "1"); // 상품 수량 - parameters.put("total_amount", payRequest.totalAmount()); // 상품 총액 - parameters.put("tax_free_amount", "0"); // 상품 비과세 금액 - parameters.put("approval_url", approvalUrl); // 결제 성공 시 URL - parameters.put("cancel_url", cancelUrl); // 결제 취소 시 URL - parameters.put("fail_url", failUrl); // 결제 실패 시 URL + parameters.put("cid", "TC0ONETIME"); // 가맹점 코드(테스트용) + parameters.put("partner_order_id", "1234567890"); // 주문번호 + parameters.put("partner_user_id", String.valueOf(userId)); // 회원 아이디 + parameters.put("item_name", "ITEM_NAME"); // 상품명 + parameters.put("quantity", "1"); // 상품 수량 + parameters.put("total_amount", payRequest.totalAmount()); // 상품 총액 + parameters.put("tax_free_amount", "0"); // 상품 비과세 금액 + parameters.put("approval_url", approvalUrl); // 결제 성공 시 URL + parameters.put("cancel_url", cancelUrl); // 결제 취소 시 URL + parameters.put("fail_url", failUrl); // 결제 실패 시 URL - HttpEntity> requestEntity = new HttpEntity<>(parameters, this.getHeaders()); + HttpEntity> requestEntity = new HttpEntity<>(parameters, this.getHeaders()); - RestTemplate template = new RestTemplate(); - String url = "https://open-api.kakaopay.com/online/v1/payment/ready"; + RestTemplate template = new RestTemplate(); + String url = "https://open-api.kakaopay.com/online/v1/payment/ready"; - readyResponseResponseEntity = template.postForEntity(url, requestEntity, - ReadyResponse.class); + ResponseEntity readyResponseResponseEntity = template.postForEntity(url, requestEntity, + ReadyResponse.class); - Team team = teamRepository.findById(payRequest.teamId()) - .orElseThrow(() -> new TeamNotFoundException()); + Team team = teamRepository.findById(payRequest.teamId()) + .orElseThrow(() -> new TeamNotFoundException()); - TeamChargeHistory teamChargeHistory = TeamChargeHistory.builder() - .transactionId(readyResponseResponseEntity.getBody().tid()) - .paymentAmount(Integer.valueOf(payRequest.totalAmount())) - .paymentChargeStatus(PaymentChargeStatus.PENDING) - .team(team) - .build(); + TeamChargeHistory teamChargeHistory = TeamChargeHistory.builder() + .transactionId(readyResponseResponseEntity.getBody().tid()) + .paymentAmount(Integer.valueOf(payRequest.totalAmount())) + .paymentChargeStatus(PaymentChargeStatus.PENDING) + .team(team) + .build(); - teamChargeHistoryRepository.save(teamChargeHistory); + teamChargeHistoryRepository.save(teamChargeHistory); - return readyResponseResponseEntity.getBody(); - } + return readyResponseResponseEntity.getBody(); + } - @Transactional - @Override - public ApproveResponse payApprove(String pgToken) { - Map parameters = new HashMap<>(); - parameters.put("cid", "TC0ONETIME"); // 가맹점 코드(테스트용) - parameters.put("tid", readyResponseResponseEntity.getBody().tid()); // 결제 고유번호 - parameters.put("partner_order_id", "1234567890"); // 주문번호 - parameters.put("partner_user_id", String.valueOf(userId)); // 회원 아이디 - parameters.put("pg_token", pgToken); // 결제승인 요청을 인증하는 토큰 + @Transactional + @Override + public ApproveResponse payApprove(String userId, String tid, String pgToken) { + Map parameters = new HashMap<>(); + parameters.put("cid", "TC0ONETIME"); // 가맹점 코드(테스트용) + parameters.put("tid", tid); // 결제 고유번호 + parameters.put("partner_order_id", "1234567890"); // 주문번호 + parameters.put("partner_user_id", String.valueOf(userId)); // 회원 아이디 + parameters.put("pg_token", pgToken); // 결제승인 요청을 인증하는 토큰 - HttpEntity> requestEntity = new HttpEntity<>(parameters, this.getHeaders()); + HttpEntity> requestEntity = new HttpEntity<>(parameters, this.getHeaders()); - RestTemplate template = new RestTemplate(); + RestTemplate template = new RestTemplate(); - String url = "https://open-api.kakaopay.com/online/v1/payment/approve"; - ApproveResponse approveResponse = template.postForObject(url, requestEntity, ApproveResponse.class); + String url = "https://open-api.kakaopay.com/online/v1/payment/approve"; + ApproveResponse approveResponse = template.postForObject(url, requestEntity, ApproveResponse.class); - TeamChargeHistory teamChargeHistory = teamChargeHistoryRepository.findByTransactionId( - readyResponseResponseEntity.getBody().tid()) - .orElseThrow(() -> new NullPointerException()); + TeamChargeHistory teamChargeHistory = teamChargeHistoryRepository.findByTransactionId(tid) + .orElseThrow(() -> new NullPointerException()); - teamChargeHistory.completePaymentChargeStatus(); + teamChargeHistory.completePaymentChargeStatus(); - Team team = teamRepository.findById(teamChargeHistory.getTeam().getId()) - .orElseThrow(() -> new TeamNotFoundException()); + Team team = teamRepository.findById(teamChargeHistory.getTeam().getId()) + .orElseThrow(() -> new TeamNotFoundException()); - team.updatePoint(teamChargeHistory.getPaymentAmount()); + team.updatePoint(teamChargeHistory.getPaymentAmount()); - return approveResponse; - } + return approveResponse; + } - private HttpHeaders getHeaders() { - HttpHeaders headers = new HttpHeaders(); - String auth = "SECRET_KEY " + secretKey; - headers.set("Authorization", auth); - headers.set("Content-type", "application/json"); + private HttpHeaders getHeaders() { + HttpHeaders headers = new HttpHeaders(); + String auth = "SECRET_KEY " + secretKey; + headers.set("Authorization", auth); + headers.set("Content-type", "application/json"); - return headers; - } + return headers; + } } \ No newline at end of file diff --git a/src/main/java/com/jangburich/domain/payment/application/PaymentProcessingService.java b/src/main/java/com/jangburich/domain/payment/application/PaymentProcessingService.java index c7429b1..6f6186a 100644 --- a/src/main/java/com/jangburich/domain/payment/application/PaymentProcessingService.java +++ b/src/main/java/com/jangburich/domain/payment/application/PaymentProcessingService.java @@ -1,27 +1,25 @@ package com.jangburich.domain.payment.application; -import org.springframework.stereotype.Service; - import com.jangburich.domain.payment.application.strategy.PaymentServiceStrategy; import com.jangburich.domain.payment.dto.request.PayRequest; import com.jangburich.domain.payment.dto.response.ApproveResponse; import com.jangburich.domain.payment.dto.response.ReadyResponse; - import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class PaymentProcessingService { - private final PaymentServiceStrategy paymentServiceStrategy; + private final PaymentServiceStrategy paymentServiceStrategy; - public ReadyResponse processPayment(Long userId, PayRequest payRequest) { - PaymentService paymentService = paymentServiceStrategy.getPaymentService(payRequest.paymentType()); - return paymentService.payReady(userId, payRequest); - } + public ReadyResponse processPayment(String userId, PayRequest payRequest) { + PaymentService paymentService = paymentServiceStrategy.getPaymentService(payRequest.paymentType()); + return paymentService.payReady(userId, payRequest); + } - public ApproveResponse processSuccess(String pgToken) { - PaymentService paymentService = paymentServiceStrategy.getPaymentService("kakao"); - return paymentService.payApprove(pgToken); - } + public ApproveResponse processSuccess(String userId, String tid, String pgToken) { + PaymentService paymentService = paymentServiceStrategy.getPaymentService("kakao"); + return paymentService.payApprove(userId, tid, pgToken); + } } diff --git a/src/main/java/com/jangburich/domain/payment/application/PaymentService.java b/src/main/java/com/jangburich/domain/payment/application/PaymentService.java index 48eddef..743361c 100644 --- a/src/main/java/com/jangburich/domain/payment/application/PaymentService.java +++ b/src/main/java/com/jangburich/domain/payment/application/PaymentService.java @@ -5,9 +5,7 @@ import com.jangburich.domain.payment.dto.response.ReadyResponse; public interface PaymentService { - String getType(); - - ReadyResponse payReady(Long userId, PayRequest payRequest); - - ApproveResponse payApprove(String pgToken); + String getType(); + ReadyResponse payReady(String userId, PayRequest payRequest); + ApproveResponse payApprove(String userId, String tid, String pgToken); } \ No newline at end of file diff --git a/src/main/java/com/jangburich/domain/team/application/TeamService.java b/src/main/java/com/jangburich/domain/team/application/TeamService.java index af234d1..28374e5 100644 --- a/src/main/java/com/jangburich/domain/team/application/TeamService.java +++ b/src/main/java/com/jangburich/domain/team/application/TeamService.java @@ -1,5 +1,15 @@ package com.jangburich.domain.team.application; +import com.jangburich.domain.team.domain.Team; +import com.jangburich.domain.team.domain.TeamLeader; +import com.jangburich.domain.team.domain.TeamType; +import com.jangburich.domain.team.domain.UserTeam; +import com.jangburich.domain.team.domain.repository.TeamRepository; +import com.jangburich.domain.team.domain.repository.UserTeamRepository; +import com.jangburich.domain.team.dto.request.RegisterTeamRequest; +import com.jangburich.domain.user.domain.User; +import com.jangburich.domain.user.domain.repository.UserRepository; +import com.jangburich.global.payload.Message; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,5 +19,35 @@ @RequiredArgsConstructor public class TeamService { + private static final int ZERO = 0; + private final TeamRepository teamRepository; + private final UserRepository userRepository; + private final UserTeamRepository userTeamRepository; + + @Transactional + public Message registerTeam(String userId, RegisterTeamRequest registerTeamRequest) { + User user = userRepository.findByProviderId(userId) + .orElseThrow(() -> new NullPointerException()); + + Team team = Team.builder() + .name(registerTeamRequest.teamName()) + .description(registerTeamRequest.description()) + .teamLeader(new TeamLeader(user.getUserId(), registerTeamRequest.teamLeaderAccountNumber(), + registerTeamRequest.bankName())) + .point(ZERO) + .memberLimit(registerTeamRequest.memberLimit()) + .teamType(TeamType.valueOf(registerTeamRequest.teamType())) + .build(); + + System.out.println("222"); + teamRepository.save(team); + + UserTeam userTeam = UserTeam.of(user, team); + userTeamRepository.save(userTeam); + + return Message.builder() + .message("팀 생성이 완료되었습니다.") + .build(); + } } diff --git a/src/main/java/com/jangburich/domain/team/domain/Team.java b/src/main/java/com/jangburich/domain/team/domain/Team.java index 621a694..8c3806e 100644 --- a/src/main/java/com/jangburich/domain/team/domain/Team.java +++ b/src/main/java/com/jangburich/domain/team/domain/Team.java @@ -3,19 +3,16 @@ import jakarta.persistence.Embedded; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; -import java.util.HashSet; -import java.util.Set; import com.jangburich.domain.common.BaseEntity; -import com.jangburich.domain.user.domain.User; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.ManyToMany; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -51,10 +48,20 @@ public class Team extends BaseEntity { @Column(name = "team_type") private TeamType teamType; - @ManyToMany(mappedBy = "teams") - private Set users = new HashSet<>(); - public void updatePoint(Integer point) { this.point += point; } + + + @Builder + public Team(String name, String description, String secretCode, TeamLeader teamLeader, Integer point, + Integer memberLimit, TeamType teamType) { + this.name = name; + this.description = description; + this.secretCode = secretCode; + this.teamLeader = teamLeader; + this.point = point; + this.memberLimit = memberLimit; + this.teamType = teamType; + } } diff --git a/src/main/java/com/jangburich/domain/team/domain/TeamLeader.java b/src/main/java/com/jangburich/domain/team/domain/TeamLeader.java index 1bdba9c..bc3357f 100644 --- a/src/main/java/com/jangburich/domain/team/domain/TeamLeader.java +++ b/src/main/java/com/jangburich/domain/team/domain/TeamLeader.java @@ -11,10 +11,12 @@ public class TeamLeader { private Long user_id; private String accountNumber; + private String bankName; - public TeamLeader(Long user_id, String accountNumber) { + public TeamLeader(Long user_id, String accountNumber, String bankName) { this.user_id = user_id; this.accountNumber = accountNumber; + this.bankName = bankName; } public boolean isSameLeader(Long userId) { diff --git a/src/main/java/com/jangburich/domain/team/domain/UserTeam.java b/src/main/java/com/jangburich/domain/team/domain/UserTeam.java new file mode 100644 index 0000000..de746a6 --- /dev/null +++ b/src/main/java/com/jangburich/domain/team/domain/UserTeam.java @@ -0,0 +1,46 @@ +package com.jangburich.domain.team.domain; + +import com.jangburich.domain.common.BaseEntity; +import com.jangburich.domain.user.domain.User; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class UserTeam extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", updatable = false) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "team_id") + private Team team; + + public UserTeam(User user, Team team) { + this.user = user; + this.team = team; + } + + public static UserTeam of(User user, Team team) { + if (user == null || team == null) { + throw new IllegalArgumentException("유저와 팀은 null이 될 수 없습니다."); + } + return new UserTeam(user, team); + } +} diff --git a/src/main/java/com/jangburich/domain/team/domain/repository/UserTeamRepository.java b/src/main/java/com/jangburich/domain/team/domain/repository/UserTeamRepository.java new file mode 100644 index 0000000..e7060be --- /dev/null +++ b/src/main/java/com/jangburich/domain/team/domain/repository/UserTeamRepository.java @@ -0,0 +1,7 @@ +package com.jangburich.domain.team.domain.repository; + +import com.jangburich.domain.team.domain.UserTeam; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserTeamRepository extends JpaRepository { +} diff --git a/src/main/java/com/jangburich/domain/team/dto/request/RegisterTeamRequest.java b/src/main/java/com/jangburich/domain/team/dto/request/RegisterTeamRequest.java index 993d7c4..a0f9eda 100644 --- a/src/main/java/com/jangburich/domain/team/dto/request/RegisterTeamRequest.java +++ b/src/main/java/com/jangburich/domain/team/dto/request/RegisterTeamRequest.java @@ -1,4 +1,12 @@ package com.jangburich.domain.team.dto.request; -public record RegisterTeamRequest() { +public record RegisterTeamRequest( + String teamType, + String teamName, + String description, + String secretCode, + String teamLeaderAccountNumber, + String bankName, + int memberLimit +) { } diff --git a/src/main/java/com/jangburich/domain/team/presentation/TeamController.java b/src/main/java/com/jangburich/domain/team/presentation/TeamController.java index 5a51616..ecc88e8 100644 --- a/src/main/java/com/jangburich/domain/team/presentation/TeamController.java +++ b/src/main/java/com/jangburich/domain/team/presentation/TeamController.java @@ -4,6 +4,8 @@ import com.jangburich.domain.team.dto.request.RegisterTeamRequest; import com.jangburich.global.payload.Message; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; @@ -20,11 +22,12 @@ public class TeamController { private final TeamService teamService; + @Operation(summary = "팀 생성", description = "팀을 생성한다. 팀 리더는 생성자") @PostMapping public ResponseCustom registerTeam( Authentication authentication, @RequestBody RegisterTeamRequest registerTeamRequest ) { - return ResponseCustom.OK(); + return ResponseCustom.OK(teamService.registerTeam(AuthenticationParser.parseUserId(authentication), registerTeamRequest)); } } diff --git a/src/main/java/com/jangburich/domain/user/domain/User.java b/src/main/java/com/jangburich/domain/user/domain/User.java index 55eacb6..20ddfbf 100644 --- a/src/main/java/com/jangburich/domain/user/domain/User.java +++ b/src/main/java/com/jangburich/domain/user/domain/User.java @@ -56,14 +56,6 @@ public class User extends BaseEntity { private String role; - @ManyToMany - @JoinTable( - name = "user_team", - joinColumns = @JoinColumn(name = "user_id"), - inverseJoinColumns = @JoinColumn(name = "team_id") - ) - private Set teams = new HashSet<>(); - public static User create(String userId, String nickname, String image, String role) { User newUser = new User(); newUser.setProviderId(userId); diff --git a/src/main/java/com/jangburich/utils/parser/AuthenticationParser.java b/src/main/java/com/jangburich/utils/parser/AuthenticationParser.java index b607042..e5949c9 100644 --- a/src/main/java/com/jangburich/utils/parser/AuthenticationParser.java +++ b/src/main/java/com/jangburich/utils/parser/AuthenticationParser.java @@ -6,8 +6,8 @@ public class AuthenticationParser { private AuthenticationParser(){} - public static Long parseUserId(Authentication authentication) { + public static String parseUserId(Authentication authentication) { CustomOAuthUser principal = (CustomOAuthUser) authentication.getPrincipal(); - return Long.parseLong(principal.getUserId()); + return principal.getUserId(); } }