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

[BE] 참여자 개별 지출 금액 수정 및 조회 기능 구현 #378

Merged
merged 7 commits into from
Aug 19, 2024
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
93 changes: 93 additions & 0 deletions server/src/docs/asciidoc/billActionDetail.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
== 지출 상세

=== 지출 상세 조회

operation::findBillActionDetailsTest[snippets="path-parameters,http-request,http-response,request-cookies"]

==== [.red]#Exceptions#

[source,json,options="nowrap"]
----
[
{
"code": "EVENT_NOT_FOUND",
"message": "존재하지 않는 행사입니다."
},
{
"code": "BILL_ACTION_NOT_FOUND",
"message": "존재하지 않는 지출 액션입니다."
},
{
"code": "BILL_ACTION_DETAIL_NOT_FOUND",
"message": "존재하지 않는 참여자 지출입니다."
},
{
"code": "TOKEN_NOT_FOUND",
"message": "토큰이 존재하지 않습니다."
},
{
"code": "TOKEN_EXPIRED",
"message": "만료된 토큰입니다."
},
{
"code": "TOKEN_INVALID",
"message": "유효하지 않은 토큰입니다."
},
{
"code": "FORBIDDEN",
"message": "접근할 수 없는 행사입니다."
}
]
----

=== 지출 상세 수정

operation::updateBillActionDetailsTest[snippets="path-parameters,http-request,request-body,request-fields,http-response,request-cookies"]

==== [.red]#Exceptions#

[source,json,options="nowrap"]
----
[
{
"code": "REQUEST_EMPTY",
"message": "멤버 이름은 공백일 수 없습니다."
},
{
"code": "REQUEST_EMPTY",
"message": "지출 금액은 공백일 수 없습니다."
},
{
"code": "EVENT_NOT_FOUND",
"message": "존재하지 않는 행사입니다."
},
{
"code": "BILL_ACTION_NOT_FOUND",
"message": "존재하지 않는 지출 액션입니다."
},
{
"code": "BILL_ACTION_DETAIL_NOT_FOUND",
"message": "존재하지 않는 참여자 지출입니다."
},
{
"code": "BILL_ACTION_PRICE_NOT_MATCHED",
"message": "지출 총액이 일치하지 않습니다."
},
{
"code": "TOKEN_NOT_FOUND",
"message": "토큰이 존재하지 않습니다."
},
{
"code": "TOKEN_EXPIRED",
"message": "만료된 토큰입니다."
},
{
"code": "TOKEN_INVALID",
"message": "유효하지 않은 토큰입니다."
},
{
"code": "FORBIDDEN",
"message": "접근할 수 없는 행사입니다."
}
]
----
17 changes: 7 additions & 10 deletions server/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
:source-highlighter: highlightjs
:hardbreaks:
:toc: left
:doctype: book
:icons: font
:toc-title: 전체 API 목록
:toclevels: 2
:sectlinks:
ifndef::snippets[]
:snippets: ../../build/generated-snippets
endif::[]
= 행동대장
:source-highlighter: highlightjs :hardbreaks:
:toc: left :doctype: book :icons: font :toc-title: 전체 API 목록 :toclevels: 2 :sectlinks:
:sectnums:
:sectnumlevels: 2

= 행동대장

include::{docdir}/event.adoc[]
include::{docdir}/memberBillReport.adoc[]
include::{docdir}/memberAction.adoc[]
include::{docdir}/billAction.adoc[]

include::{docdir}/billActionDetail.adoc[]
Original file line number Diff line number Diff line change
@@ -1,12 +1,78 @@
package server.haengdong.application;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import server.haengdong.application.request.BillActionDetailUpdateAppRequest;
import server.haengdong.application.request.BillActionDetailsUpdateAppRequest;
import server.haengdong.application.response.BillActionDetailsAppResponse;
import server.haengdong.domain.action.BillAction;
import server.haengdong.domain.action.BillActionDetail;
import server.haengdong.domain.action.BillActionDetailRepository;
import server.haengdong.domain.action.BillActionRepository;
import server.haengdong.domain.event.Event;
import server.haengdong.exception.HaengdongErrorCode;
import server.haengdong.exception.HaengdongException;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class BillActionDetailService {

private final BillActionDetailRepository billActionDetailRepository;
private final BillActionRepository billActionRepository;

public BillActionDetailsAppResponse findBillActionDetails(String token, Long actionId) {
BillAction billAction = billActionRepository.findByAction_Id(actionId)
.orElseThrow(() -> new HaengdongException(HaengdongErrorCode.BILL_ACTION_NOT_FOUND));
validateToken(token, billAction);

List<BillActionDetail> billActionDetails = billActionDetailRepository.findAllByBillAction(billAction);

return BillActionDetailsAppResponse.of(billActionDetails);
}

@Transactional
public void updateBillActionDetails(String token, Long actionId, BillActionDetailsUpdateAppRequest request) {
BillAction billAction = billActionRepository.findByAction_Id(actionId)
.orElseThrow(() -> new HaengdongException(HaengdongErrorCode.BILL_ACTION_NOT_FOUND));

List<BillActionDetailUpdateAppRequest> billActionDetailUpdateAppRequests = request.billActionDetailUpdateAppRequests();

validateToken(token, billAction);
validateTotalPrice(billActionDetailUpdateAppRequests, billAction);

List<BillActionDetail> billActionDetails = billActionDetailRepository.findAllByBillAction(billAction);

for (BillActionDetailUpdateAppRequest updateRequest : billActionDetailUpdateAppRequests) {
BillActionDetail detailToUpdate = billActionDetails.stream()
.filter(detail -> detail.isSameName(updateRequest.name()))
.findFirst()
.orElseThrow(() -> new HaengdongException(HaengdongErrorCode.BILL_ACTION_DETAIL_NOT_FOUND));

detailToUpdate.updatePrice(updateRequest.price());
}
}

private void validateToken(String token, BillAction billAction) {
Event event = billAction.getEvent();
if (event.isTokenMismatch(token)) {
throw new HaengdongException(HaengdongErrorCode.BILL_ACTION_NOT_FOUND);
}
}

private void validateTotalPrice(List<BillActionDetailUpdateAppRequest> billActionDetailUpdateAppRequests,
BillAction billAction) {
Long requestsPriceSum = calculateUpdatePriceSum(billActionDetailUpdateAppRequests);
if (!billAction.isSamePrice(requestsPriceSum)) {
throw new HaengdongException(HaengdongErrorCode.BILL_ACTION_PRICE_NOT_MATCHED);
}
Comment on lines +67 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c: 프론트에서 주는 값을 Long으로 받고 있어서, 데이터 주고받는 과정에서 발생한 오차로도 예외가 발생할 텐데 어떻게 처리하면 좋을지 같이 얘기해 보면 좋을 것 같아요.

}

private Long calculateUpdatePriceSum(List<BillActionDetailUpdateAppRequest> billActionDetailUpdateAppRequests) {
return billActionDetailUpdateAppRequests.stream()
.map(BillActionDetailUpdateAppRequest::price)
.reduce(0L, Long::sum);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void updateBillAction(String token, Long actionId, BillActionUpdateAppReq

private void resetBillActionDetail(BillAction billAction, Long updatePrice) {
if (billAction.getPrice() != updatePrice) {
List<BillActionDetail> billActionDetails = billActionDetailRepository.findByBillAction(billAction);
List<BillActionDetail> billActionDetails = billActionDetailRepository.findAllByBillAction(billAction);
int memberCount = billActionDetails.size();
if (memberCount != 0) {
Long eachPrice = updatePrice / memberCount;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package server.haengdong.application.request;

public record BillActionDetailUpdateAppRequest(
String name,
Long price
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package server.haengdong.application.request;

import java.util.List;

public record BillActionDetailsUpdateAppRequest(
List<BillActionDetailUpdateAppRequest> billActionDetailUpdateAppRequests
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package server.haengdong.application.response;

import server.haengdong.domain.action.BillActionDetail;

public record BillActionDetailAppResponse(
String name,
Long price
) {

public static BillActionDetailAppResponse of(BillActionDetail billActionDetail) {
return new BillActionDetailAppResponse(billActionDetail.getMemberName(), billActionDetail.getPrice());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package server.haengdong.application.response;

import java.util.List;
import java.util.stream.Collectors;
import server.haengdong.domain.action.BillActionDetail;

public record BillActionDetailsAppResponse(List<BillActionDetailAppResponse> billActionDetailAppResponses) {

public static BillActionDetailsAppResponse of(List<BillActionDetail> billActionDetails) {
return billActionDetails.stream()
.map(BillActionDetailAppResponse::of)
.collect(Collectors.collectingAndThen(Collectors.toList(), BillActionDetailsAppResponse::new));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public void update(String title, Long price) {
this.price = price;
}

public boolean isSamePrice(Long price) {
return this.price.equals(price);
}

public Long getSequence() {
return action.getSequence();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ public BillActionDetail(String memberName, Long price) {
this.price = price;
}

public void setBillAction(BillAction billAction) {
public BillActionDetail(BillAction billAction, String memberName, Long price) {
this.billAction = billAction;
this.memberName = memberName;
this.price = price;
}

public boolean hasMemberName(String memberName) {
return this.memberName.equals(memberName);
}

public BillActionDetail(BillAction billAction, String memberName, Long price) {
public BillActionDetail(Long id, BillAction billAction, String memberName, Long price) {
this.id = id;
this.billAction = billAction;
this.memberName = memberName;
this.price = price;
Expand All @@ -48,4 +47,16 @@ public BillActionDetail(BillAction billAction, String memberName, Long price) {
public void updatePrice(Long price) {
this.price = price;
}

public boolean hasMemberName(String memberName) {
return this.memberName.equals(memberName);
}

public boolean isSameName(String memberName) {
return this.memberName.equals(memberName);
}

public void setBillAction(BillAction billAction) {
this.billAction = billAction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import server.haengdong.domain.event.Event;

@Repository
public interface BillActionDetailRepository extends JpaRepository<BillActionDetail, Long> {

List<BillActionDetail> findByBillAction(BillAction billAction);
@Query("""
select bd
from BillActionDetail bd
where bd.billAction = :billAction
""")
Comment on lines +12 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c: 확인해 보니까 @Query를 적용하지 않았을 때랑 실행되는 쿼리가 동일한 것 같은데 따로 작성해 주신 이유가 있나요?

Pasted Graphic 2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query creation이 편하긴 해도 작성한 메서드의 쿼리가 어떻게 나가는지 직관적으로 알 수 있어서 좋다고 생각합니다.

List<BillActionDetail> findAllByBillAction(BillAction billAction);

void deleteAllByBillAction(BillAction billAction);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public enum HaengdongErrorCode {
BillAction.MIN_TITLE_LENGTH,
BillAction.MAX_TITLE_LENGTH)),
BILL_ACTION_PRICE_INVALID(String.format("지출 금액은 %,d 이하의 자연수여야 합니다.", BillAction.MAX_PRICE)),
BILL_ACTION_DETAIL_NOT_FOUND("존재하지 않는 참여자 지출입니다."),
BILL_ACTION_PRICE_NOT_MATCHED("지출 총액이 일치하지 않습니다."),
Comment on lines +38 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

노션 문서도 최신화 해주셨네요 👍


/* Authentication */

Expand All @@ -55,7 +57,8 @@ public enum HaengdongErrorCode {
MESSAGE_NOT_READABLE("읽을 수 없는 요청입니다."),
REQUEST_METHOD_NOT_SUPPORTED("지원하지 않는 요청 메서드입니다."),
NO_RESOURCE_REQUEST("존재하지 않는 자원입니다."),
INTERNAL_SERVER_ERROR("서버 내부에서 에러가 발생했습니다.");
INTERNAL_SERVER_ERROR("서버 내부에서 에러가 발생했습니다."),
;

private final String message;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
package server.haengdong.presentation;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import server.haengdong.application.BillActionDetailService;
import server.haengdong.application.response.BillActionDetailsAppResponse;
import server.haengdong.presentation.request.BillActionDetailsUpdateRequest;
import server.haengdong.presentation.response.BillActionDetailsResponse;

@RequiredArgsConstructor
@RestController
public class BillActionDetailController {

private final BillActionDetailService billActionDetailService;

@GetMapping("/api/events/{eventId}/bill-actions/{actionId}/fixed")
public ResponseEntity<BillActionDetailsResponse> findBillActionDetails(
@PathVariable("eventId") String token,
@PathVariable("actionId") Long actionId
) {
BillActionDetailsAppResponse appResponse = billActionDetailService.findBillActionDetails(token, actionId);

return ResponseEntity.ok(BillActionDetailsResponse.of(appResponse));
}

@PutMapping("/api/events/{eventId}/bill-actions/{actionId}/fixed")
public ResponseEntity<Void> updateBillActionDetails(
@PathVariable("eventId") String token,
@PathVariable("actionId") Long actionId,
@Valid @RequestBody BillActionDetailsUpdateRequest request
) {
billActionDetailService.updateBillActionDetails(token, actionId, request.toAppRequest());

return ResponseEntity.ok().build();
}
}
Loading
Loading