From 1d95bd272751afccebfbfb3d11265738895a6a05 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:40:38 +0900 Subject: [PATCH 01/30] feat: add spending list read method with day to spending repository --- .../repository/SpendingCustomRepository.java | 2 ++ .../repository/SpendingCustomRepositoryImpl.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepository.java b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepository.java index 3c94a3481..c0c5d7619 100644 --- a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepository.java +++ b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepository.java @@ -10,4 +10,6 @@ public interface SpendingCustomRepository { Optional findTotalSpendingAmountByUserId(Long userId, int year, int month); List findByYearAndMonth(Long userId, int year, int month); + + List findByYearAndMonthAndDay(Long userId, int year, int month, int day); } diff --git a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepositoryImpl.java b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepositoryImpl.java index 7c780895c..89b05dfbc 100644 --- a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepositoryImpl.java +++ b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepositoryImpl.java @@ -61,4 +61,18 @@ public List findByYearAndMonth(Long userId, int year, int month) { .orderBy(orderSpecifiers.toArray(new OrderSpecifier[0])) .fetch(); } + + @Override + public List findByYearAndMonthAndDay(Long userId, int year, int month, int day) { + Sort sort = Sort.by(Sort.Order.desc("spendAt")); + + return queryFactory.selectFrom(spending) + .leftJoin(spending.spendingCustomCategory, spendingCustomCategory).fetchJoin() + .where(spending.spendAt.year().eq(year) + .and(spending.spendAt.month().eq(month)) + .and(spending.spendAt.dayOfMonth().eq(day)) + .and(spending.user.id.eq(userId)) + ) + .fetch(); + } } From 71e8ed69118b1e55a2a03425679102efab56ee29 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:32:33 +0900 Subject: [PATCH 02/30] feat: add spending list read method with day to spending service --- .../domain/domains/spending/service/SpendingRdbService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/service/SpendingRdbService.java b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/service/SpendingRdbService.java index a8c2c08a7..5f6eb2df5 100644 --- a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/service/SpendingRdbService.java +++ b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/service/SpendingRdbService.java @@ -50,6 +50,11 @@ public List readSpendings(Long userId, int year, int month) { return spendingRepository.findByYearAndMonth(userId, year, month); } + @Transactional(readOnly = true) + public List readSpendings(Long userId, int year, int month, int day) { + return spendingRepository.findByYearAndMonthAndDay(userId, year, month, day); + } + @Transactional(readOnly = true) public int readSpendingTotalCountByCategoryId(Long userId, Long categoryId) { return spendingRepository.countByUser_IdAndSpendingCustomCategory_Id(userId, categoryId); From e9cd80935d9f98f119991214345e0714df882348 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:14:09 +0900 Subject: [PATCH 03/30] feat: impl daily spending aggregate service --- .../DailySpendingAggregateService.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java new file mode 100644 index 000000000..5e78174b2 --- /dev/null +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java @@ -0,0 +1,38 @@ +package kr.co.pennyway.api.apis.ledger.service; + +import kr.co.pennyway.domain.domains.spending.domain.Spending; +import kr.co.pennyway.domain.domains.spending.dto.CategoryInfo; +import kr.co.pennyway.domain.domains.spending.service.SpendingRdbService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.summingLong; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DailySpendingAggregateService { + private final SpendingRdbService spendingRdbService; + + @Transactional(readOnly = true) + public List> execute(Long userId, int year, int month, int day) { + var spendings = spendingRdbService.readSpendings(userId, year, month, day); + + return spendings.stream() + .collect( + groupingBy( + Spending::getCategory, + summingLong(Spending::getAmount) + ) + ) + .entrySet().stream() + .sorted((o1, o2) -> (int) (o2.getValue() - o1.getValue())) + .toList(); + } +} From 48116287954dd0a33fbbcccd5f1bbc12e3c2612f Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:28:23 +0900 Subject: [PATCH 04/30] test: aggregate slice test --- .../DailySpendingAggregateServiceTest.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java diff --git a/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java new file mode 100644 index 000000000..155680bef --- /dev/null +++ b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java @@ -0,0 +1,78 @@ +package kr.co.pennyway.api.apis.ledger.service; + +import kr.co.pennyway.api.config.ExternalApiDBTestConfig; +import kr.co.pennyway.api.config.ExternalApiIntegrationTest; +import kr.co.pennyway.api.config.fixture.UserFixture; +import kr.co.pennyway.domain.domains.spending.domain.Spending; +import kr.co.pennyway.domain.domains.spending.domain.SpendingCustomCategory; +import kr.co.pennyway.domain.domains.spending.repository.SpendingCustomCategoryRepository; +import kr.co.pennyway.domain.domains.spending.repository.SpendingRepository; +import kr.co.pennyway.domain.domains.spending.type.SpendingCategory; +import kr.co.pennyway.domain.domains.user.domain.User; +import kr.co.pennyway.domain.domains.user.repository.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Slf4j +@ExternalApiIntegrationTest +public class DailySpendingAggregateServiceTest extends ExternalApiDBTestConfig { + @Autowired + private UserRepository userRepository; + + @Autowired + private SpendingRepository spendingRepository; + + @Autowired + private SpendingCustomCategoryRepository spendingCustomCategoryRepository; + + @Autowired + private DailySpendingAggregateService dailySpendingAggregateService; + + private static Spending createSpending(String accountName, LocalDateTime spendAt, SpendingCategory category, Integer amount, SpendingCustomCategory spendingCustomCategory, User user) { + return Spending.builder() + .accountName(accountName) + .spendAt(spendAt) + .category(category) + .amount(amount) + .spendingCustomCategory(spendingCustomCategory) + .user(user) + .build(); + } + + @Test + public void test() { + // given + var user = userRepository.save(UserFixture.GENERAL_USER.toUser()); + var spendingCustomCategory1 = spendingCustomCategoryRepository.save(SpendingCustomCategory.of("커스텀1", SpendingCategory.EDUCATION, user)); + var spendingCustomCategory2 = spendingCustomCategoryRepository.save(SpendingCustomCategory.of("커스텀2", SpendingCategory.FOOD, user)); + + var today = LocalDateTime.now(); + + var defaultFoodSpending1 = spendingRepository.save(createSpending("시스템 카테고리 지출1", today, SpendingCategory.FOOD, 10000, null, user)); + var defaultFoodSpending2 = spendingRepository.save(createSpending("시스템 카테고리 지출2", today, SpendingCategory.FOOD, 20000, null, user)); + var defaultEducationSpending1 = spendingRepository.save(createSpending("시스템 카테고리 지출3", today, SpendingCategory.EDUCATION, 30000, null, user)); + var defaultEducationSpending2 = spendingRepository.save(createSpending("시스템 카테고리 지출4", today, SpendingCategory.EDUCATION, 40000, null, user)); + var systemEducationSpending1 = spendingRepository.save(createSpending("커스텀 카테고리 지출1", today, SpendingCategory.CUSTOM, 50000, spendingCustomCategory1, user)); + var systemEducationSpending2 = spendingRepository.save(createSpending("커스텀 카테고리 지출2", today, SpendingCategory.CUSTOM, 60000, spendingCustomCategory1, user)); + var systemFoodSpending1 = spendingRepository.save(createSpending("커스텀 카테고리 지출3", today, SpendingCategory.CUSTOM, 70000, spendingCustomCategory2, user)); + var systemFoodSpending2 = spendingRepository.save(createSpending("커스텀 카테고리 지출4", today, SpendingCategory.CUSTOM, 80000, spendingCustomCategory2, user)); + + // when + var result = dailySpendingAggregateService.execute(user.getId(), today.getYear(), today.getMonthValue(), today.getDayOfMonth()); + + // then + assertEquals(result.get(0).getFirst(), systemFoodSpending1.getCategory()); + assertEquals(result.get(0).getSecond(), systemFoodSpending1.getAmount() + systemFoodSpending2.getAmount()); + assertEquals(result.get(1).getFirst(), systemEducationSpending1.getCategory()); + assertEquals(result.get(1).getSecond(), systemEducationSpending1.getAmount() + systemEducationSpending2.getAmount()); + assertEquals(result.get(2).getFirst(), defaultEducationSpending1.getCategory()); + assertEquals(result.get(2).getSecond(), defaultEducationSpending1.getAmount() + defaultEducationSpending2.getAmount()); + assertEquals(result.get(3).getFirst(), defaultFoodSpending1.getCategory()); + assertEquals(result.get(3).getSecond(), defaultFoodSpending1.getAmount() + defaultFoodSpending2.getAmount()); + } +} From 603858167216b212414ce56c6698c078c1d45f91 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:29:00 +0900 Subject: [PATCH 05/30] fix: change return type to pair --- .../ledger/service/DailySpendingAggregateService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java index 5e78174b2..d53edd0f0 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateService.java @@ -5,11 +5,11 @@ import kr.co.pennyway.domain.domains.spending.service.SpendingRdbService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Map; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.summingLong; @@ -21,9 +21,9 @@ public class DailySpendingAggregateService { private final SpendingRdbService spendingRdbService; @Transactional(readOnly = true) - public List> execute(Long userId, int year, int month, int day) { + public List> execute(Long userId, int year, int month, int day) { var spendings = spendingRdbService.readSpendings(userId, year, month, day); - + return spendings.stream() .collect( groupingBy( @@ -32,7 +32,8 @@ public List> execute(Long userId, int year, int mo ) ) .entrySet().stream() - .sorted((o1, o2) -> (int) (o2.getValue() - o1.getValue())) + .map(entry -> Pair.of(entry.getKey(), entry.getValue())) + .sorted((o1, o2) -> (int) (o2.getSecond() - o1.getSecond())) .toList(); } } From 0256c4cf2d2dc16d13a29fa62bd98db9c5d3002a Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:50:28 +0900 Subject: [PATCH 06/30] rename: fix test name --- .../apis/ledger/service/DailySpendingAggregateServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java index 155680bef..3016f8856 100644 --- a/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java +++ b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/DailySpendingAggregateServiceTest.java @@ -45,7 +45,7 @@ private static Spending createSpending(String accountName, LocalDateTime spendAt } @Test - public void test() { + public void shouldReturnDailySpendingDescOrder() { // given var user = userRepository.save(UserFixture.GENERAL_USER.toUser()); var spendingCustomCategory1 = spendingCustomCategoryRepository.save(SpendingCustomCategory.of("커스텀1", SpendingCategory.EDUCATION, user)); From 5a36bb10bde13d439dbe948e554ebaa6cb53b8f9 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:46:20 +0900 Subject: [PATCH 07/30] feat: spending-chat-share-event --- .../common/event/SpendingChatShareEvent.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java new file mode 100644 index 000000000..a93d7d8e0 --- /dev/null +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java @@ -0,0 +1,48 @@ +package kr.co.pennyway.infra.common.event; + +import org.springframework.data.util.Pair; + +import java.util.List; +import java.util.Objects; + +public record SpendingChatShareEvent( + Long chatRoomId, + String name, + List> spendingOnDates +) { + public SpendingChatShareEvent { + Objects.requireNonNull(chatRoomId, "chatRoomId는 null일 수 없습니다."); + Objects.requireNonNull(name, "name은 null일 수 없습니다."); + Objects.requireNonNull(spendingOnDates, "spendingOnDates는 null일 수 없습니다."); + } + + private record SpendingOnDate( + boolean isCustom, + Long categoryId, + String name, + String icon, + Long amount + ) { + public SpendingOnDate { + Objects.requireNonNull(categoryId, "categoryId는 null일 수 없습니다."); + Objects.requireNonNull(icon, "icon은 null일 수 없습니다."); + Objects.requireNonNull(amount, "amount는 null일 수 없습니다."); + + if (isCustom && categoryId < 0 || !isCustom && categoryId != -1) { + throw new IllegalArgumentException("isCustom이 " + isCustom + "일 때 categoryId는 " + (isCustom ? "0 이상" : "-1") + "이어야 합니다."); + } + + if (isCustom && icon.equals("CUSTOM")) { + throw new IllegalArgumentException("사용자 정의 카테고리는 OTHER가 될 수 없습니다."); + } + + if (!name.isEmpty()) { + throw new IllegalArgumentException("name은 null이거나 빈 문자열일 수 없습니다."); + } + } + + public static SpendingOnDate of(Long categoryId, String name, String icon, Long amount) { + return new SpendingOnDate(!categoryId.equals(-1L), categoryId, name, icon, amount); + } + } +} From 92d558671646b625fb651a36f0393875a587a696 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:49:06 +0900 Subject: [PATCH 08/30] feat: add spending-chat-share-exchange-properties --- .../SpendingChatShareExchangeProperties.java | 21 +++++++++++++++++++ .../infra/config/MessageBrokerConfig.java | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/SpendingChatShareExchangeProperties.java diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/SpendingChatShareExchangeProperties.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/SpendingChatShareExchangeProperties.java new file mode 100644 index 000000000..242c42ec0 --- /dev/null +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/SpendingChatShareExchangeProperties.java @@ -0,0 +1,21 @@ +package kr.co.pennyway.infra.common.properties; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@RequiredArgsConstructor +@ConfigurationProperties(prefix = "pennyway.rabbitmq.spending-chat-share") +public class SpendingChatShareExchangeProperties { + private final String queue; + private final String routingKey; + + @Override + public String toString() { + return "SpendingChatShareExchangeProperties{" + + "queue='" + queue + '\'' + + ", routingKey='" + routingKey + '\'' + + '}'; + } +} diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java index b56061b92..a60ed1801 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java @@ -11,6 +11,7 @@ import kr.co.pennyway.infra.common.properties.ChatExchangeProperties; import kr.co.pennyway.infra.common.properties.ChatJoinEventExchangeProperties; import kr.co.pennyway.infra.common.properties.RabbitMqProperties; +import kr.co.pennyway.infra.common.properties.SpendingChatShareExchangeProperties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.Binding; @@ -35,7 +36,7 @@ @Slf4j @EnableRabbit @RequiredArgsConstructor -@EnableConfigurationProperties({ChatExchangeProperties.class, ChatJoinEventExchangeProperties.class, RabbitMqProperties.class}) +@EnableConfigurationProperties({ChatExchangeProperties.class, ChatJoinEventExchangeProperties.class, RabbitMqProperties.class, SpendingChatShareExchangeProperties.class}) public class MessageBrokerConfig implements PennywayInfraConfig { private final RabbitMqProperties rabbitMqProperties; private final ChatExchangeProperties chatExchangeProperties; From 51ca942ffb39fd2db9c9f42d14c004a91ed0e848 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:50:55 +0900 Subject: [PATCH 09/30] chore: add spending chat share property to infra yml --- pennyway-infra/src/main/resources/application-infra.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pennyway-infra/src/main/resources/application-infra.yml b/pennyway-infra/src/main/resources/application-infra.yml index 818f50a44..4b33bc6f4 100644 --- a/pennyway-infra/src/main/resources/application-infra.yml +++ b/pennyway-infra/src/main/resources/application-infra.yml @@ -63,6 +63,9 @@ pennyway: chat-join-event: queue: ${RABBITMQ_CHAT_JOIN_QUEUE:chat.join.queue} routing-key: ${RABBITMQ_CHAT_JOIN_ROUTING:chat.join.*} + spending-chat-share: + queue: ${RABBITMQ_SPENDING_CHAT_QUEUE:spending.chat.queue} + routing-key: ${RABBITMQ_SPENDING_CHAT_EXCHANGE:chat.share.spending.*} oauth2: client: From 1598feb58511bac6e7100dbae3eabfefd292759b Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:51:51 +0900 Subject: [PATCH 10/30] feat: impl spending chat share event handler --- .../event/SpendingChatShareEventHandler.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEventHandler.java diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEventHandler.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEventHandler.java new file mode 100644 index 000000000..4619e999c --- /dev/null +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEventHandler.java @@ -0,0 +1,36 @@ +package kr.co.pennyway.infra.common.event; + +import kr.co.pennyway.infra.client.broker.MessageBrokerAdapter; +import kr.co.pennyway.infra.common.properties.ChatExchangeProperties; +import kr.co.pennyway.infra.common.properties.SpendingChatShareExchangeProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.scheduling.annotation.Async; + +import java.util.Map; + +@Slf4j +@RequiredArgsConstructor +public class SpendingChatShareEventHandler { + private final MessageBrokerAdapter messageBrokerAdapter; + private final ChatExchangeProperties chatExchangeProperties; + private final SpendingChatShareExchangeProperties spendingChatShareExchangeProperties; + + @Async + @EventListener + public void handle(SpendingChatShareEvent event) { + log.debug("handle: {}", event); + + var headers = new MessageHeaders(Map.of("Content-Type", "application/json")); + var message = MessageBuilder.createMessage(event, headers); + + messageBrokerAdapter.send( + chatExchangeProperties.getExchange(), + spendingChatShareExchangeProperties.getRoutingKey(), + message + ); + } +} From addca4c558e7a573de46d43428116bbcf2a62d79 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:02:31 +0900 Subject: [PATCH 11/30] fix: spending-on-dates type error in the spending-chat-share-event --- .../pennyway/infra/common/event/SpendingChatShareEvent.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java index a93d7d8e0..c7eb6bba5 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java @@ -1,14 +1,12 @@ package kr.co.pennyway.infra.common.event; -import org.springframework.data.util.Pair; - import java.util.List; import java.util.Objects; public record SpendingChatShareEvent( Long chatRoomId, String name, - List> spendingOnDates + List spendingOnDates ) { public SpendingChatShareEvent { Objects.requireNonNull(chatRoomId, "chatRoomId는 null일 수 없습니다."); @@ -16,7 +14,7 @@ public record SpendingChatShareEvent( Objects.requireNonNull(spendingOnDates, "spendingOnDates는 null일 수 없습니다."); } - private record SpendingOnDate( + public record SpendingOnDate( boolean isCustom, Long categoryId, String name, From f554619f497c77f1fa7c9875c8bd8c9061b78ae6 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:03:44 +0900 Subject: [PATCH 12/30] feat: impl spending-chat-share-helper --- .../helper/SpendingChatShareHelper.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java new file mode 100644 index 000000000..df8062cb9 --- /dev/null +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java @@ -0,0 +1,49 @@ +package kr.co.pennyway.api.apis.ledger.helper; + +import kr.co.pennyway.api.apis.ledger.service.DailySpendingAggregateService; +import kr.co.pennyway.common.annotation.Helper; +import kr.co.pennyway.domain.context.account.service.UserService; +import kr.co.pennyway.domain.context.chat.service.ChatMemberService; +import kr.co.pennyway.domain.domains.user.exception.UserErrorCode; +import kr.co.pennyway.domain.domains.user.exception.UserErrorException; +import kr.co.pennyway.infra.common.event.SpendingChatShareEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Helper +@RequiredArgsConstructor +public class SpendingChatShareHelper { + private final DailySpendingAggregateService dailySpendingAggregateService; + + private final UserService userService; + private final ChatMemberService chatMemberService; + + private final ApplicationEventPublisher eventPublisher; + + public void execute(Long userId, List chatRoomIds, LocalDate date) { + var user = userService.readUser(userId) + .orElseThrow(() -> new UserErrorException(UserErrorCode.NOT_FOUND)); + var aggregatedSpendings = dailySpendingAggregateService.execute(userId, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + var joinedChatRoomIds = chatMemberService.readChatRoomIdsByUserId(userId); + + var spendingOnDate = new ArrayList(); + for (var pair : aggregatedSpendings) { + var categoryInfo = pair.getFirst(); + var amount = pair.getSecond(); + + spendingOnDate.add(SpendingChatShareEvent.SpendingOnDate.of(categoryInfo.id(), categoryInfo.name(), categoryInfo.icon().name(), amount)); + } + + chatRoomIds.stream() + .filter(joinedChatRoomIds::contains) + .forEach(chatRoomId -> { + eventPublisher.publishEvent(new SpendingChatShareEvent(chatRoomId, user.getName(), spendingOnDate)); + }); + } +} From 383b6411c7243c0d63f3070f39288aa4ed46eda3 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:05:42 +0900 Subject: [PATCH 13/30] feat: add share-to-chat-room to spending usecase --- .../pennyway/api/apis/ledger/usecase/SpendingUseCase.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/usecase/SpendingUseCase.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/usecase/SpendingUseCase.java index 9a3a1d59e..fbe5e1f53 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/usecase/SpendingUseCase.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/usecase/SpendingUseCase.java @@ -2,6 +2,7 @@ import kr.co.pennyway.api.apis.ledger.dto.SpendingReq; import kr.co.pennyway.api.apis.ledger.dto.SpendingSearchRes; +import kr.co.pennyway.api.apis.ledger.helper.SpendingChatShareHelper; import kr.co.pennyway.api.apis.ledger.mapper.SpendingMapper; import kr.co.pennyway.api.apis.ledger.service.SpendingDeleteService; import kr.co.pennyway.api.apis.ledger.service.SpendingSaveService; @@ -13,6 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; import java.util.List; @Slf4j @@ -24,6 +26,8 @@ public class SpendingUseCase { private final SpendingUpdateService spendingUpdateService; private final SpendingDeleteService spendingDeleteService; + private final SpendingChatShareHelper spendingChatShareHelper; + @Transactional public SpendingSearchRes.Individual createSpending(Long userId, SpendingReq request) { Spending spending = spendingSaveService.createSpending(userId, request); @@ -62,4 +66,7 @@ public void deleteSpendings(List spendingIds) { spendingDeleteService.deleteSpendings(spendingIds); } + public void shareToChatRoom(Long userId, List chatRoomIds, LocalDate date) { + spendingChatShareHelper.execute(userId, chatRoomIds, date); + } } From 58bb94e051345ea2039762b4c1dd29347c462345 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:20:15 +0900 Subject: [PATCH 14/30] feat: add invalid share type error code to spending-error-code --- .../domain/domains/spending/exception/SpendingErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java index eb38dc5f4..e47d36255 100644 --- a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java +++ b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java @@ -15,6 +15,7 @@ public enum SpendingErrorCode implements BaseErrorCode { INVALID_ICON_WITH_CATEGORY_ID(StatusCode.BAD_REQUEST, ReasonCode.CLIENT_ERROR, "icon의 정보와 categoryId의 정보가 존재할 수 없는 조합입니다."), INVALID_TYPE_WITH_CATEGORY_ID(StatusCode.BAD_REQUEST, ReasonCode.CLIENT_ERROR, "type의 정보와 categoryId의 정보가 존재할 수 없는 조합입니다."), INVALID_CATEGORY_TYPE(StatusCode.BAD_REQUEST, ReasonCode.CLIENT_ERROR, "존재하지 않는 카테고리 타입입니다."), + INVALID_SHARE_TYPE(StatusCode.BAD_REQUEST, ReasonCode.MALFORMED_PARAMETER, "부적절한 공유 타입입니다."), /* 404 Not Found */ NOT_FOUND_SPENDING(StatusCode.NOT_FOUND, ReasonCode.REQUESTED_RESOURCE_NOT_FOUND, "존재하지 않는 지출 내역입니다."), From dc684a3ca604d6c00deb9ce4ac90709ad5264380 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:21:54 +0900 Subject: [PATCH 15/30] feat: impl spending share enum type --- .../converter/SpendingShareTypeConverter.java | 17 +++++++++++++++++ .../api/common/query/SpendingShareType.java | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/converter/SpendingShareTypeConverter.java create mode 100644 pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/query/SpendingShareType.java diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/converter/SpendingShareTypeConverter.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/converter/SpendingShareTypeConverter.java new file mode 100644 index 000000000..bb0c8503b --- /dev/null +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/converter/SpendingShareTypeConverter.java @@ -0,0 +1,17 @@ +package kr.co.pennyway.api.common.converter; + +import kr.co.pennyway.api.common.query.SpendingShareType; +import kr.co.pennyway.domain.domains.spending.exception.SpendingErrorCode; +import kr.co.pennyway.domain.domains.spending.exception.SpendingErrorException; +import org.springframework.core.convert.converter.Converter; + +public class SpendingShareTypeConverter implements Converter { + @Override + public SpendingShareType convert(String type) { + try { + return SpendingShareType.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new SpendingErrorException(SpendingErrorCode.INVALID_SHARE_TYPE); + } + } +} diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/query/SpendingShareType.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/query/SpendingShareType.java new file mode 100644 index 000000000..5b57a1f75 --- /dev/null +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/common/query/SpendingShareType.java @@ -0,0 +1,11 @@ +package kr.co.pennyway.api.common.query; + +public enum SpendingShareType { + CHAT_ROOM("chat_room"); + + private final String type; + + SpendingShareType(String type) { + this.type = type; + } +} From d3eeb88cc5fda1225ab3a2cc8b472ed927d000c1 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:31:49 +0900 Subject: [PATCH 16/30] feat: add spending-chat-share-query dto --- .../api/apis/ledger/dto/SpendingShareReq.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/dto/SpendingShareReq.java diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/dto/SpendingShareReq.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/dto/SpendingShareReq.java new file mode 100644 index 000000000..f1d704b30 --- /dev/null +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/dto/SpendingShareReq.java @@ -0,0 +1,20 @@ +package kr.co.pennyway.api.apis.ledger.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.co.pennyway.api.common.query.SpendingShareType; + +import java.util.List; + +public class SpendingShareReq { + @Schema(description = "지출 공유 요청") + public record ShareQueryParam( + @Schema(description = "공유 타입 (대/소문자 허용)", example = "chat_room") + SpendingShareType type, + int year, + int month, + int day, + @Schema(description = "공유할 채팅방 ID 배열. 공유 타입이 chat_room인 경우 필수", example = "1") + List chatRoomIds + ) { + } +} From a2922940fb71356a24264720308504178de38e0e Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:33:05 +0900 Subject: [PATCH 17/30] feat: add missing-share-param error code to spending error code --- .../domain/domains/spending/exception/SpendingErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java index e47d36255..6cfb30c2c 100644 --- a/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java +++ b/pennyway-domain/domain-rdb/src/main/java/kr/co/pennyway/domain/domains/spending/exception/SpendingErrorCode.java @@ -16,6 +16,7 @@ public enum SpendingErrorCode implements BaseErrorCode { INVALID_TYPE_WITH_CATEGORY_ID(StatusCode.BAD_REQUEST, ReasonCode.CLIENT_ERROR, "type의 정보와 categoryId의 정보가 존재할 수 없는 조합입니다."), INVALID_CATEGORY_TYPE(StatusCode.BAD_REQUEST, ReasonCode.CLIENT_ERROR, "존재하지 않는 카테고리 타입입니다."), INVALID_SHARE_TYPE(StatusCode.BAD_REQUEST, ReasonCode.MALFORMED_PARAMETER, "부적절한 공유 타입입니다."), + MISSING_SHARE_PARAM(StatusCode.BAD_REQUEST, ReasonCode.MISSING_REQUIRED_PARAMETER, "지출 내역 공유 시 필수 파라미터가 누락되었습니다."), /* 404 Not Found */ NOT_FOUND_SPENDING(StatusCode.NOT_FOUND, ReasonCode.REQUESTED_RESOURCE_NOT_FOUND, "존재하지 않는 지출 내역입니다."), From 954469a28611ee674231df44c1f63addfdd03fb5 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:33:38 +0900 Subject: [PATCH 18/30] feat: add share-spending api to controller --- .../ledger/controller/SpendingController.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java index 720619b9f..14b04d19f 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java @@ -3,7 +3,9 @@ import kr.co.pennyway.api.apis.ledger.api.SpendingApi; import kr.co.pennyway.api.apis.ledger.dto.SpendingIdsDto; import kr.co.pennyway.api.apis.ledger.dto.SpendingReq; +import kr.co.pennyway.api.apis.ledger.dto.SpendingShareReq; import kr.co.pennyway.api.apis.ledger.usecase.SpendingUseCase; +import kr.co.pennyway.api.common.query.SpendingShareType; import kr.co.pennyway.api.common.response.SuccessResponse; import kr.co.pennyway.api.common.security.authentication.SecurityUserDetails; import kr.co.pennyway.domain.domains.spending.exception.SpendingErrorCode; @@ -17,6 +19,8 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; + @Slf4j @RestController @RequiredArgsConstructor @@ -79,6 +83,27 @@ public ResponseEntity deleteSpendings(@RequestBody SpendingIdsDto spendingIds return ResponseEntity.ok(SuccessResponse.noContent()); } + @GetMapping("/share") + @PreAuthorize("isAuthenticated()") + public ResponseEntity shareSpending( + @Validated SpendingShareReq.ShareQueryParam query, + @AuthenticationPrincipal SecurityUserDetails user + ) { + var date = LocalDate.of(query.year(), query.month(), query.day()); + + if (query.type().equals(SpendingShareType.CHAT_ROOM)) { + if (query.chatRoomIds() == null || query.chatRoomIds().isEmpty()) { + throw new SpendingErrorException(SpendingErrorCode.MISSING_SHARE_PARAM); + } + + spendingUseCase.shareToChatRoom(user.getUserId(), query.chatRoomIds(), date); + } else { + throw new SpendingErrorException(SpendingErrorCode.INVALID_SHARE_TYPE); + } + + return ResponseEntity.ok(SuccessResponse.noContent()); + } + /** * categoryId가 -1이면 서비스에서 정의한 카테고리를 사용하므로 저장하려는 지출 내역의 icon은 CUSTOM이나 OTHER이 될 수 없고,
* categoryId가 -1이 아니면 사용자가 정의한 카테고리를 사용하므로 저장하려는 지출 내역의 icon은 CUSTOM임을 확인한다. From 775ebd6e4e98379180349aca0fa6d61aed86fa1c Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:53:34 +0900 Subject: [PATCH 19/30] docs: write swagger docs about spending share api --- .../api/apis/ledger/api/SpendingApi.java | 27 ++++++++++++++++--- .../ledger/controller/SpendingController.java | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/api/SpendingApi.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/api/SpendingApi.java index c0ba48b1e..04a277bd5 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/api/SpendingApi.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/api/SpendingApi.java @@ -4,16 +4,14 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.SchemaProperty; +import io.swagger.v3.oas.annotations.media.*; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import kr.co.pennyway.api.apis.ledger.dto.SpendingIdsDto; import kr.co.pennyway.api.apis.ledger.dto.SpendingReq; import kr.co.pennyway.api.apis.ledger.dto.SpendingSearchRes; +import kr.co.pennyway.api.apis.ledger.dto.SpendingShareReq; import kr.co.pennyway.api.common.annotation.ApiExceptionExplanation; import kr.co.pennyway.api.common.annotation.ApiResponseExplanations; import kr.co.pennyway.api.common.security.authentication.SecurityUserDetails; @@ -109,4 +107,25 @@ public interface SpendingApi { ) })) ResponseEntity deleteSpendings(@RequestBody SpendingIdsDto spendingIds, @AuthenticationPrincipal SecurityUserDetails user); + + @Operation(summary = "지출 내역 공유", method = "GET", description = """ + 사용자의 지출 내역을 공유하고 공유된 지출 내역을 반환합니다.
+ <채팅방 공유 시> 전송할 채팅방 아이디를 누락한 경우 예외를 발생시키지만, 가입하지 않은 채팅방 아이디를 전송한 경우는 유효한 방에만 전송하고 별도의 예외를 발생시키지 않습니다. + """) + @Parameters({ + @Parameter(name = "type", description = "공유할 목적지(타입)", required = true, in = ParameterIn.QUERY, examples = { + @ExampleObject(name = "채팅방 공유", value = "CHAT_ROOM") + }), + @Parameter(name = "year", description = "년도", example = "2025", required = true, in = ParameterIn.QUERY), + @Parameter(name = "month", description = "월", example = "1", required = true, in = ParameterIn.QUERY), + @Parameter(name = "day", description = "일", example = "28", required = true, in = ParameterIn.QUERY), + @Parameter(name = "chatRoomIds", description = "공유할 채팅방 ID 목록 배열 (채팅방 공유 시, null 혹은 빈 배열 허용하지 않음.)", in = ParameterIn.QUERY, array = @ArraySchema(schema = @Schema(type = "long"))), + @Parameter(name = "query", hidden = true) + }) + @ApiResponseExplanations(errors = { + @ApiExceptionExplanation(name = "전송 타입 오류", description = "유효하지 않은 목적지로 지출 내용을 공유할 수 없습니다.", value = SpendingErrorCode.class, constant = "INVALID_SHARE_TYPE"), + @ApiExceptionExplanation(name = "채팅방 공유 파라미터 누락", description = "지출 내역 공유 시 필수 파라미터가 누락되었습니다.", value = SpendingErrorCode.class, constant = "MISSING_SHARE_PARAM") + }) + @ApiResponse(responseCode = "200") + ResponseEntity shareSpending(@Validated SpendingShareReq.ShareQueryParam query, @AuthenticationPrincipal SecurityUserDetails user); } diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java index 14b04d19f..31e634b45 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/controller/SpendingController.java @@ -83,6 +83,7 @@ public ResponseEntity deleteSpendings(@RequestBody SpendingIdsDto spendingIds return ResponseEntity.ok(SuccessResponse.noContent()); } + @Override @GetMapping("/share") @PreAuthorize("isAuthenticated()") public ResponseEntity shareSpending( From 19acba4a3dd7cc0e2e790ef960f02aa43bd8b161 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:12:21 +0900 Subject: [PATCH 20/30] chore: apply binder & queue & event handler for share to chat room --- .../infra/config/MessageBrokerConfig.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java index a60ed1801..69961bb20 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import kr.co.pennyway.infra.client.broker.MessageBrokerAdapter; import kr.co.pennyway.infra.common.event.ChatRoomJoinEventHandler; +import kr.co.pennyway.infra.common.event.SpendingChatShareEventHandler; import kr.co.pennyway.infra.common.importer.PennywayInfraConfig; import kr.co.pennyway.infra.common.properties.ChatExchangeProperties; import kr.co.pennyway.infra.common.properties.ChatJoinEventExchangeProperties; @@ -41,6 +42,7 @@ public class MessageBrokerConfig implements PennywayInfraConfig { private final RabbitMqProperties rabbitMqProperties; private final ChatExchangeProperties chatExchangeProperties; private final ChatJoinEventExchangeProperties chatJoinEventExchangeProperties; + private final SpendingChatShareExchangeProperties spendingChatShareExchangeProperties; @Bean public TopicExchange chatExchange() { @@ -57,6 +59,11 @@ public Queue chatJoinEventQueue(ChatJoinEventExchangeProperties chatJoinEventExc return new Queue(chatJoinEventExchangeProperties.getQueue(), true); } + @Bean + public Queue spendingChatShareQueue(SpendingChatShareExchangeProperties spendingChatShareExchangeProperties) { + return new Queue(spendingChatShareExchangeProperties.getQueue(), true); + } + @Bean public Binding chatBinding(Queue chatQueue, TopicExchange chatExchange) { return BindingBuilder @@ -73,6 +80,14 @@ public Binding chatJoinEventBinding(Queue chatJoinEventQueue, TopicExchange chat .with(chatJoinEventExchangeProperties.getRoutingKey()); } + @Bean + public Binding spendingShareEventBinding(Queue spendingChatShareQueue, TopicExchange chatExchange) { + return BindingBuilder + .bind(spendingChatShareQueue) + .to(chatExchange) + .with(spendingChatShareExchangeProperties.getRoutingKey()); + } + @Bean public Module dateTimeModule() { return new JavaTimeModule(); @@ -152,4 +167,9 @@ public MessageBrokerAdapter messageBrokerAdapter(RabbitMessagingTemplate rabbitM public ChatRoomJoinEventHandler chatRoomJoinEventHandler(MessageBrokerAdapter messageBrokerAdapter, ChatExchangeProperties chatExchangeProperties, ChatJoinEventExchangeProperties chatJoinEventExchangeProperties) { return new ChatRoomJoinEventHandler(messageBrokerAdapter, chatExchangeProperties, chatJoinEventExchangeProperties); } + + @Bean + public SpendingChatShareEventHandler spendingChatShareEventHandler(MessageBrokerAdapter messageBrokerAdapter, ChatExchangeProperties chatExchangeProperties, SpendingChatShareExchangeProperties spendingChatShareExchangeProperties) { + return new SpendingChatShareEventHandler(messageBrokerAdapter, chatExchangeProperties, spendingChatShareExchangeProperties); + } } From 2d2d0995cbaf7ecd8dc96246ce3c833e7881f039 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:26:02 +0900 Subject: [PATCH 21/30] fix: invalid validation in the spending chat share event's name field --- .../pennyway/infra/common/event/SpendingChatShareEvent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java index c7eb6bba5..d05f007ab 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java @@ -1,5 +1,7 @@ package kr.co.pennyway.infra.common.event; +import org.springframework.util.StringUtils; + import java.util.List; import java.util.Objects; @@ -34,7 +36,7 @@ public record SpendingOnDate( throw new IllegalArgumentException("사용자 정의 카테고리는 OTHER가 될 수 없습니다."); } - if (!name.isEmpty()) { + if (!StringUtils.hasText(name)) { throw new IllegalArgumentException("name은 null이거나 빈 문자열일 수 없습니다."); } } From ce6e0781a7d49c5af0f36b38ed3a1efe0d0971cd Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:50:16 +0900 Subject: [PATCH 22/30] feat: add share constant to the message category type --- .../domain/domains/message/type/MessageCategoryType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennyway-domain/domain-redis/src/main/java/kr/co/pennyway/domain/domains/message/type/MessageCategoryType.java b/pennyway-domain/domain-redis/src/main/java/kr/co/pennyway/domain/domains/message/type/MessageCategoryType.java index 869f62f23..971e8cd8a 100644 --- a/pennyway-domain/domain-redis/src/main/java/kr/co/pennyway/domain/domains/message/type/MessageCategoryType.java +++ b/pennyway-domain/domain-redis/src/main/java/kr/co/pennyway/domain/domains/message/type/MessageCategoryType.java @@ -9,7 +9,8 @@ @RequiredArgsConstructor public enum MessageCategoryType { NORMAL("0", "NORMAL"), - SYSTEM("1", "SYSTEM"); + SYSTEM("1", "SYSTEM"), + SHARE("2", "SHARE"); private static final Map stringToEnum = Stream.of(values()).collect(java.util.stream.Collectors.toMap(Object::toString, e -> e)); private final String code; From ccb3b480163c6944fcced4a0d3ecd8382162b015 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:50:42 +0900 Subject: [PATCH 23/30] feat: add default create message method to the send-message-command --- .../pennyway/socket/command/SendMessageCommand.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java index f27a912e5..93f48f4a7 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java @@ -77,4 +77,16 @@ public static SendMessageCommand createUserMessage(long chatRoomId, String conte messageIdHeader ); } + + public static SendMessageCommand createMessage(long chatRoomId, String content, MessageContentType contentType, MessageCategoryType categoryType, long senderId, String senderName, Map messageIdHeader) { + return new SendMessageCommand( + chatRoomId, + content, + contentType, + categoryType, + senderId, + senderName, + messageIdHeader + ); + } } \ No newline at end of file From fab4a74a648e8f5ad683f1255efb81685c5db2a4 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:04:32 +0900 Subject: [PATCH 24/30] fix: add sender id field to spending-chat-share-event --- .../co/pennyway/infra/common/event/SpendingChatShareEvent.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java index d05f007ab..571bf32ef 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java @@ -8,11 +8,13 @@ public record SpendingChatShareEvent( Long chatRoomId, String name, + Long senderId, List spendingOnDates ) { public SpendingChatShareEvent { Objects.requireNonNull(chatRoomId, "chatRoomId는 null일 수 없습니다."); Objects.requireNonNull(name, "name은 null일 수 없습니다."); + Objects.requireNonNull(senderId, "senderId는 null일 수 없습니다."); Objects.requireNonNull(spendingOnDates, "spendingOnDates는 null일 수 없습니다."); } From 2a75ff22e61391f38f10ee8f6f20ca621c396765 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:10:08 +0900 Subject: [PATCH 25/30] fix: add user id to event parameter when publish event --- .../api/apis/ledger/helper/SpendingChatShareHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java index df8062cb9..3dec22fe9 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java @@ -43,7 +43,7 @@ public void execute(Long userId, List chatRoomIds, LocalDate date) { chatRoomIds.stream() .filter(joinedChatRoomIds::contains) .forEach(chatRoomId -> { - eventPublisher.publishEvent(new SpendingChatShareEvent(chatRoomId, user.getName(), spendingOnDate)); + eventPublisher.publishEvent(new SpendingChatShareEvent(chatRoomId, user.getName(), user.getId(), spendingOnDate)); }); } } From bfe178f52ea350cfed32117bab19a25b628bd7c2 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:11:06 +0900 Subject: [PATCH 26/30] feat: impl sending-share-event listener --- .../relay/SpendingShareEventListener.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java new file mode 100644 index 000000000..950754f94 --- /dev/null +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java @@ -0,0 +1,64 @@ +package kr.co.pennyway.socket.relay; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import kr.co.pennyway.domain.domains.message.type.MessageCategoryType; +import kr.co.pennyway.domain.domains.message.type.MessageContentType; +import kr.co.pennyway.infra.common.event.SpendingChatShareEvent; +import kr.co.pennyway.infra.common.properties.ChatExchangeProperties; +import kr.co.pennyway.socket.command.SendMessageCommand; +import kr.co.pennyway.socket.service.ChatMessageSendService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +@EnableConfigurationProperties({ChatExchangeProperties.class}) +public class SpendingShareEventListener { + private final ChatMessageSendService chatMessageSendService; + private final ObjectMapper objectMapper; + + @RabbitListener( + containerFactory = "simpleRabbitListenerContainerFactory", + bindings = @QueueBinding( + value = @Queue("${pennyway.rabbitmq.spending-chat-share.queue}"), + exchange = @Exchange(value = "${pennyway.rabbitmq.chat.exchange}", type = "topic"), + key = "${pennyway.rabbitmq.spending-chat-share.routing-key}" + ) + ) + public void handle(SpendingChatShareEvent event) { + log.debug("handle: {}", event); + + var payload = convertToJson(event.spendingOnDates()); + + chatMessageSendService.execute( + SendMessageCommand.createMessage( + event.chatRoomId(), + payload, + MessageContentType.TEXT, + MessageCategoryType.SHARE, + event.senderId(), + event.name(), + null + ) + ); + } + + private String convertToJson(List object) { + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + log.error("Failed to serialize spendingOnDates", e); + return null; + } + } +} From 9f44df108389f5d84fad49b0b1359b8912fc08a0 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:49:06 +0900 Subject: [PATCH 27/30] fix: add headers to send message command --- .../pennyway/socket/command/SendMessageCommand.java | 13 +++++++++---- .../socket/relay/SpendingShareEventListener.java | 4 +++- .../socket/service/ChatMessageSendService.kt | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java index 93f48f4a7..22c01928b 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/command/SendMessageCommand.java @@ -1,5 +1,6 @@ package kr.co.pennyway.socket.command; +import jakarta.annotation.Nullable; import kr.co.pennyway.domain.domains.message.type.MessageCategoryType; import kr.co.pennyway.domain.domains.message.type.MessageContentType; import kr.co.pennyway.socket.common.constants.SystemMessageConstants; @@ -16,7 +17,8 @@ public record SendMessageCommand( MessageCategoryType categoryType, long senderId, String senderName, - Map messageIdHeader + Map messageIdHeader, + @Nullable Map headers ) { public SendMessageCommand { if (chatRoomId <= 0) { @@ -51,6 +53,7 @@ public static SendMessageCommand createSystemMessage(long chatRoomId, String con MessageCategoryType.SYSTEM, SystemMessageConstants.SYSTEM_SENDER_ID, null, + null, null ); } @@ -74,11 +77,12 @@ public static SendMessageCommand createUserMessage(long chatRoomId, String conte MessageCategoryType.NORMAL, senderId, senderName, - messageIdHeader + messageIdHeader, + null ); } - public static SendMessageCommand createMessage(long chatRoomId, String content, MessageContentType contentType, MessageCategoryType categoryType, long senderId, String senderName, Map messageIdHeader) { + public static SendMessageCommand createMessage(long chatRoomId, String content, MessageContentType contentType, MessageCategoryType categoryType, long senderId, String senderName, Map messageIdHeader, @Nullable Map headers) { return new SendMessageCommand( chatRoomId, content, @@ -86,7 +90,8 @@ public static SendMessageCommand createMessage(long chatRoomId, String content, categoryType, senderId, senderName, - messageIdHeader + messageIdHeader, + headers ); } } \ No newline at end of file diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java index 950754f94..3338adc74 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java @@ -18,6 +18,7 @@ import org.springframework.stereotype.Component; import java.util.List; +import java.util.Map; @Slf4j @Component @@ -48,7 +49,8 @@ public void handle(SpendingChatShareEvent event) { MessageCategoryType.SHARE, event.senderId(), event.name(), - null + null, + Map.of("Content-Type", "application/json") ) ); } diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/service/ChatMessageSendService.kt b/pennyway-socket/src/main/java/kr/co/pennyway/socket/service/ChatMessageSendService.kt index 4f8fb2c61..7ba6a8382 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/service/ChatMessageSendService.kt +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/service/ChatMessageSendService.kt @@ -33,7 +33,8 @@ class ChatMessageSendService( messageBrokerAdapter.convertAndSend( exchange, "chat.room.${command.chatRoomId}", - ChatMessageDto.Response.from(message) + ChatMessageDto.Response.from(message), + command.headers ) } From 95b2622ba783e459ed4ffb9e27dbe5b241852d81 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:56:37 +0900 Subject: [PATCH 28/30] refactor: convert spending-share-event-listener to kotlin --- .../relay/SpendingShareEventListener.java | 66 ------------------- .../relay/SpendingShareEventListener.kt | 65 ++++++++++++++++++ 2 files changed, 65 insertions(+), 66 deletions(-) delete mode 100644 pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java create mode 100644 pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java deleted file mode 100644 index 3338adc74..000000000 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.java +++ /dev/null @@ -1,66 +0,0 @@ -package kr.co.pennyway.socket.relay; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import kr.co.pennyway.domain.domains.message.type.MessageCategoryType; -import kr.co.pennyway.domain.domains.message.type.MessageContentType; -import kr.co.pennyway.infra.common.event.SpendingChatShareEvent; -import kr.co.pennyway.infra.common.properties.ChatExchangeProperties; -import kr.co.pennyway.socket.command.SendMessageCommand; -import kr.co.pennyway.socket.service.ChatMessageSendService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.rabbit.annotation.Exchange; -import org.springframework.amqp.rabbit.annotation.Queue; -import org.springframework.amqp.rabbit.annotation.QueueBinding; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Map; - -@Slf4j -@Component -@RequiredArgsConstructor -@EnableConfigurationProperties({ChatExchangeProperties.class}) -public class SpendingShareEventListener { - private final ChatMessageSendService chatMessageSendService; - private final ObjectMapper objectMapper; - - @RabbitListener( - containerFactory = "simpleRabbitListenerContainerFactory", - bindings = @QueueBinding( - value = @Queue("${pennyway.rabbitmq.spending-chat-share.queue}"), - exchange = @Exchange(value = "${pennyway.rabbitmq.chat.exchange}", type = "topic"), - key = "${pennyway.rabbitmq.spending-chat-share.routing-key}" - ) - ) - public void handle(SpendingChatShareEvent event) { - log.debug("handle: {}", event); - - var payload = convertToJson(event.spendingOnDates()); - - chatMessageSendService.execute( - SendMessageCommand.createMessage( - event.chatRoomId(), - payload, - MessageContentType.TEXT, - MessageCategoryType.SHARE, - event.senderId(), - event.name(), - null, - Map.of("Content-Type", "application/json") - ) - ); - } - - private String convertToJson(List object) { - try { - return objectMapper.writeValueAsString(object); - } catch (JsonProcessingException e) { - log.error("Failed to serialize spendingOnDates", e); - return null; - } - } -} diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt new file mode 100644 index 000000000..02d03e0b5 --- /dev/null +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt @@ -0,0 +1,65 @@ +package kr.co.pennyway.socket.relay; + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.pennyway.domain.domains.message.type.MessageCategoryType +import kr.co.pennyway.domain.domains.message.type.MessageContentType +import kr.co.pennyway.infra.common.event.SpendingChatShareEvent +import kr.co.pennyway.infra.common.properties.ChatExchangeProperties +import kr.co.pennyway.socket.command.SendMessageCommand +import kr.co.pennyway.socket.common.util.logger +import kr.co.pennyway.socket.service.ChatMessageSendService +import lombok.extern.slf4j.Slf4j +import org.springframework.amqp.rabbit.annotation.Exchange +import org.springframework.amqp.rabbit.annotation.Queue +import org.springframework.amqp.rabbit.annotation.QueueBinding +import org.springframework.amqp.rabbit.annotation.RabbitListener +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.stereotype.Component + +@Slf4j +@Component +@EnableConfigurationProperties(ChatExchangeProperties::class) +class SpendingShareEventListener( + private val chatMessageSendService: ChatMessageSendService, + private val objectMapper: ObjectMapper +) { + private companion object { + private val log = logger() + } + + @RabbitListener( + containerFactory = "simpleRabbitListenerContainerFactory", + bindings = [QueueBinding( + value = Queue("\${pennyway.rabbitmq.spending-chat-share.queue}"), + exchange = Exchange(value = "\${pennyway.rabbitmq.chat.exchange}", type = "topic"), + key = ["\${pennyway.rabbitmq.spending-chat-share.routing-key}"] + )] + ) + fun handle(event: SpendingChatShareEvent) { + log.debug("handle: {}", event) + + convertToJson(event.spendingOnDates()) + .getOrNull() + ?.let { payload -> + chatMessageSendService.execute( + SendMessageCommand.createMessage( + event.chatRoomId(), + payload, + MessageContentType.TEXT, + MessageCategoryType.SHARE, + event.senderId(), + event.name(), + null, + mapOf("Content-Type" to "application/json") + ) + ) + } + } + + private fun convertToJson(spendingOnDates: List): Result = + runCatching { + objectMapper.writeValueAsString(spendingOnDates) + }.onFailure { + log.error("Failed to serialize spendingOnDates", it) + } +} \ No newline at end of file From eaf5662cff0d752110b6019a1b6262e38dc0f0c0 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:52:04 +0900 Subject: [PATCH 29/30] fix: add date field to spending-chat-share-event --- .../api/apis/ledger/helper/SpendingChatShareHelper.java | 2 +- .../co/pennyway/infra/common/event/SpendingChatShareEvent.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java index 3dec22fe9..bcd892d3a 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/ledger/helper/SpendingChatShareHelper.java @@ -43,7 +43,7 @@ public void execute(Long userId, List chatRoomIds, LocalDate date) { chatRoomIds.stream() .filter(joinedChatRoomIds::contains) .forEach(chatRoomId -> { - eventPublisher.publishEvent(new SpendingChatShareEvent(chatRoomId, user.getName(), user.getId(), spendingOnDate)); + eventPublisher.publishEvent(new SpendingChatShareEvent(chatRoomId, user.getName(), user.getId(), date, spendingOnDate)); }); } } diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java index 571bf32ef..c3f2951a8 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/event/SpendingChatShareEvent.java @@ -2,6 +2,7 @@ import org.springframework.util.StringUtils; +import java.time.LocalDate; import java.util.List; import java.util.Objects; @@ -9,12 +10,14 @@ public record SpendingChatShareEvent( Long chatRoomId, String name, Long senderId, + LocalDate date, List spendingOnDates ) { public SpendingChatShareEvent { Objects.requireNonNull(chatRoomId, "chatRoomId는 null일 수 없습니다."); Objects.requireNonNull(name, "name은 null일 수 없습니다."); Objects.requireNonNull(senderId, "senderId는 null일 수 없습니다."); + Objects.requireNonNull(date, "date는 null일 수 없습니다."); Objects.requireNonNull(spendingOnDates, "spendingOnDates는 null일 수 없습니다."); } From 676ef5ae7230896548638fc6d52e0aa9d6ebc38a Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:58:10 +0900 Subject: [PATCH 30/30] fix: add date to chat message's header --- .../kr/co/pennyway/socket/relay/SpendingShareEventListener.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt index 02d03e0b5..cde1a95b8 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/relay/SpendingShareEventListener.kt @@ -50,7 +50,7 @@ class SpendingShareEventListener( event.senderId(), event.name(), null, - mapOf("Content-Type" to "application/json") + mapOf("Content-Type" to "application/json", "date" to event.date()) ) ) }