Skip to content

Commit

Permalink
Merge pull request #36 from dnd-side-project/feat/#34-find-member-exp…
Browse files Browse the repository at this point in the history
…ense

지출내역 조회 시 참여자 지출내역 포함 기능 추가
  • Loading branch information
sudhdkso authored Feb 7, 2025
2 parents 54a0baf + 4803c4d commit eb460d4
Show file tree
Hide file tree
Showing 27 changed files with 478 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;

import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.group.entity.Group;
import com.dnd.moddo.domain.memberExpense.dto.request.MemberExpenseRequest;
import com.fasterxml.jackson.annotation.JsonFormat;

Expand All @@ -16,7 +17,7 @@ public record ExpenseRequest(

) {

public Expense toEntity(Long groupId) {
return new Expense(groupId, amount(), content(), date());
public Expense toEntity(Group group, int maxOrder) {
return new Expense(group, amount(), content(), maxOrder, date());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.dnd.moddo.domain.expense.dto.request;

public record ExpenseUpdateOrderRequest(int newOrder, Long expenseId) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import java.util.List;

import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.group.entity.Group;

public record ExpensesRequest(
List<ExpenseRequest> expenses
) {
public List<Expense> toEntity(Long groupId) {
public List<Expense> toEntity(Group group, int maxOrder) {
return expenses.stream()
.map(e -> e.toEntity(groupId))
.map(e -> e.toEntity(group, maxOrder))
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
import com.dnd.moddo.domain.memberExpense.dto.response.MemberExpenseResponse;

public record ExpenseResponse(
Long id, Long amount, String content, LocalDate date,
Long id, Long amount, String content, int order, LocalDate date,
List<MemberExpenseResponse> memberExpenses
) {
public static ExpenseResponse of(Expense expense, List<MemberExpenseResponse> memberExpenses) {
return new ExpenseResponse(expense.getId(), expense.getAmount(), expense.getContent(), expense.getDate(),
return new ExpenseResponse(expense.getId(), expense.getAmount(), expense.getContent(), expense.getOrder(),
expense.getDate(),
memberExpenses);
}

public static ExpenseResponse of(Expense expense) {
return new ExpenseResponse(expense.getId(), expense.getAmount(), expense.getContent(), expense.getDate(), null);
return new ExpenseResponse(expense.getId(), expense.getAmount(), expense.getContent(), expense.getOrder(),
expense.getDate(), null);
}
}
25 changes: 20 additions & 5 deletions src/main/java/com/dnd/moddo/domain/expense/entity/Expense.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import java.time.LocalDate;
import java.util.List;

import com.dnd.moddo.domain.group.entity.Group;
import com.fasterxml.jackson.annotation.JsonFormat;

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 jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
Expand All @@ -24,31 +29,41 @@ public class Expense {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Long groupId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id")
private Group group;

private Long amount;

private String content;

@Column(name = "`order`")
private Integer order;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
private LocalDate date;

//TODO List 직렬화 @Convert 추가하기
private List<String> images;

public Expense(Long groupId, Long amount, String content, LocalDate date) {
this(groupId, amount, content, date, null);
public Expense(Group group, Long amount, String content, int order, LocalDate date) {
this(group, amount, content, order, date, null);
}

@Builder
public Expense(Long groupId, Long amount, String content, LocalDate date, List<String> images) {
this.groupId = groupId;
public Expense(Group group, Long amount, String content, int order, LocalDate date, List<String> images) {
this.group = group;
this.amount = amount;
this.content = content;
this.order = order;
this.date = date;
this.images = images;
}

public void updateOrder(int order) {
this.order = order;
}

public void update(Long amount, String content, LocalDate date) {
this.amount = amount;
this.content = content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.expense.exception.ExpenseNotFoundException;

public interface ExpenseRepository extends JpaRepository<Expense, Long> {
List<Expense> findByGroupId(Long groupId);

List<Expense> findByGroupIdOrderByOrderAsc(Long id);

@Query("select COALESCE(MAX(e.order),0) from Expense e where e.group.id = :groupId")
int findMaxOrderByGroupId(@Param("groupId") Long groupId);

default Expense getById(Long id) {
return findById(id)
.orElseThrow(() -> new ExpenseNotFoundException(id));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.expense.service.implementation.ExpenseCreator;
import com.dnd.moddo.domain.expense.service.implementation.ExpenseDeleter;
import com.dnd.moddo.domain.expense.service.implementation.ExpenseReader;
import com.dnd.moddo.domain.expense.service.implementation.ExpenseUpdater;
import com.dnd.moddo.domain.memberExpense.dto.response.MemberExpenseResponse;
import com.dnd.moddo.domain.memberExpense.service.CommandMemberExpenseService;
Expand All @@ -21,6 +22,7 @@
@Service
public class CommandExpenseService {
private final ExpenseCreator expenseCreator;
private final ExpenseReader expenseReader;
private final ExpenseUpdater expenseUpdater;
private final ExpenseDeleter expenseDeleter;
private final CommandMemberExpenseService commandMemberExpenseService;
Expand All @@ -34,7 +36,9 @@ public ExpensesResponse createExpenses(Long groupId, ExpensesRequest request) {
}

private ExpenseResponse createExpense(Long groupId, ExpenseRequest request) {
Expense expense = expenseCreator.create(groupId, request);
int maxOrder = expenseReader.findMaxOrderForGroup(groupId) + 1;
Expense expense = expenseCreator.create(groupId, maxOrder, request);

List<MemberExpenseResponse> memberExpenseResponses = commandMemberExpenseService.create(expense,
request.memberExpenses());
return ExpenseResponse.of(expense, memberExpenseResponses);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@
import com.dnd.moddo.domain.expense.dto.response.ExpensesResponse;
import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.expense.service.implementation.ExpenseReader;
import com.dnd.moddo.domain.memberExpense.service.QueryMemberExpenseService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
@RequiredArgsConstructor
public class QueryExpenseService {
private final ExpenseReader expenseReader;
private final QueryMemberExpenseService queryMemberExpenseService;

public ExpensesResponse findAllByGroupId(Long groupId) {
List<Expense> expenses = expenseReader.findAllByGroupId(groupId);
return ExpensesResponse.of(expenses);
return new ExpensesResponse(
expenses.stream()
.map(expense ->
ExpenseResponse.of(expense, queryMemberExpenseService.findAllByExpenseId(expense.getId()))
).toList()
);
}

public ExpenseResponse findOneByExpenseId(Long expenseId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import com.dnd.moddo.domain.expense.dto.request.ExpenseRequest;
import com.dnd.moddo.domain.expense.entity.Expense;
import com.dnd.moddo.domain.expense.repository.ExpenseRepository;
import com.dnd.moddo.domain.group.entity.Group;
import com.dnd.moddo.domain.group.repository.GroupRepository;
import com.dnd.moddo.domain.memberExpense.service.implementation.MemberExpenseValidator;

import lombok.RequiredArgsConstructor;

Expand All @@ -14,9 +17,16 @@
@Transactional
public class ExpenseCreator {
private final ExpenseRepository expenseRepository;
private final MemberExpenseValidator memberExpenseValidator;
private final GroupRepository groupRepository;

public Expense create(Long groupId, ExpenseRequest request) {
Expense expense = request.toEntity(groupId);
public Expense create(Long groupId, int maxOrder, ExpenseRequest request) {
Group group = groupRepository.getById(groupId);

memberExpenseValidator.validateMembersArePartOfGroup(groupId, request.memberExpenses());

Expense expense = request.toEntity(group, maxOrder);
return expenseRepository.save(expense);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ public class ExpenseReader {
private final ExpenseRepository expenseRepository;

public List<Expense> findAllByGroupId(Long groupId) {
return expenseRepository.findByGroupId(groupId);
return expenseRepository.findByGroupIdOrderByOrderAsc(groupId);
}

public Expense findOneByExpenseId(Long expenseId) {
return expenseRepository.getById(expenseId);
}

public int findMaxOrderForGroup(Long groupId) {
return expenseRepository.findMaxOrderByGroupId(groupId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dnd.moddo.domain.groupMember.exception;

import org.springframework.http.HttpStatus;

import com.dnd.moddo.global.exception.ModdoException;

public class InvalidGroupMemberException extends ModdoException {
public InvalidGroupMemberException(Long id) {
super(HttpStatus.BAD_REQUEST, "해당 모임에 속하지 않은 참여자가 포함되어 있습니다 (Member ID: " + id + ")");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.dnd.moddo.domain.groupMember.entity.GroupMember;
import com.dnd.moddo.domain.groupMember.exception.GroupMemberNotFoundException;
Expand All @@ -11,6 +13,9 @@ public interface GroupMemberRepository extends JpaRepository<GroupMember, Long>

List<GroupMember> findByGroupId(Long groupId);

@Query("select m.id from GroupMember m where m.group.id = :groupId")
List<Long> findGroupMemberIdsByGroupId(@Param("groupId") Long groupId);

default GroupMember getById(Long id) {
return findById(id)
.orElseThrow(() -> new GroupMemberNotFoundException(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public GroupMember findByGroupMemberId(Long groupMemberId) {
return groupMemberRepository.getById(groupMemberId);
}

public List<Long> findIdsByGroupId(Long groupId) {
return groupMemberRepository.findGroupMemberIdsByGroupId(groupId);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.dnd.moddo.domain.memberExpense.repotiroy;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.dnd.moddo.domain.memberExpense.entity.MemberExpense;

public interface MemberExpenseRepository extends JpaRepository<MemberExpense, Long> {
List<MemberExpense> findByExpenseId(Long expenseId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dnd.moddo.domain.memberExpense.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.dnd.moddo.domain.memberExpense.dto.response.MemberExpenseResponse;
import com.dnd.moddo.domain.memberExpense.entity.MemberExpense;
import com.dnd.moddo.domain.memberExpense.service.implementation.MemberExpenseReader;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class QueryMemberExpenseService {
private final MemberExpenseReader memberExpenseReader;

public List<MemberExpenseResponse> findAllByExpenseId(Long expenseId) {
List<MemberExpense> memberExpenses = memberExpenseReader.findAllByExpenseId(expenseId);
return memberExpenses.stream()
.map(MemberExpenseResponse::of)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
@RequiredArgsConstructor
@Transactional
public class MemberExpenseCreator {
private final MemberExpenseRepository memberExpenseRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dnd.moddo.domain.memberExpense.service.implementation;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dnd.moddo.domain.memberExpense.entity.MemberExpense;
import com.dnd.moddo.domain.memberExpense.repotiroy.MemberExpenseRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberExpenseReader {
private final MemberExpenseRepository memberExpenseRepository;

public List<MemberExpense> findAllByExpenseId(Long expenseId) {
return memberExpenseRepository.findByExpenseId(expenseId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.dnd.moddo.domain.memberExpense.service.implementation;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Component;

import com.dnd.moddo.domain.groupMember.exception.InvalidGroupMemberException;
import com.dnd.moddo.domain.groupMember.service.implementation.GroupMemberReader;
import com.dnd.moddo.domain.memberExpense.dto.request.MemberExpenseRequest;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class MemberExpenseValidator {
private final GroupMemberReader groupMemberReader;

public void validateMembersArePartOfGroup(Long groupId, List<MemberExpenseRequest> requests) {
Set<Long> validGroupMemberIds = new HashSet<>(groupMemberReader.findIdsByGroupId(groupId));
List<Long> requestedGroupMemberIds = requests.stream()
.map(MemberExpenseRequest::memberId)
.toList();

requestedGroupMemberIds.forEach(id -> {
if (!validGroupMemberIds.contains(id)) {
throw new InvalidGroupMemberException(id);
}
});

}
}
Loading

0 comments on commit eb460d4

Please sign in to comment.