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

feat: 기존 메뉴 카테고리 이름 변경 #423

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package in.koreatech.koin.domain.coop.model;

import static in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType.DINING_SOLD_OUT;
import static in.koreatech.koin.global.fcm.MobileAppPath.HOME;
import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

import in.koreatech.koin.domain.user.model.User;
import in.koreatech.koin.domain.user.repository.UserRepository;
import in.koreatech.koin.global.domain.notification.model.NotificationFactory;
import in.koreatech.koin.global.domain.notification.repository.NotificationSubscribeRepository;
import in.koreatech.koin.global.domain.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class CoopEventListener {

private final NotificationService notificationService;
private final UserRepository userRepository;
private final NotificationSubscribeRepository notificationSubscribeRepository;
private final NotificationFactory notificationFactory;

@TransactionalEventListener(phase = AFTER_COMMIT)
public void onDiningSoldOutRequest(DiningSoldOutEvent event) {
var notifications = notificationSubscribeRepository.findAllBySubscribeType(DINING_SOLD_OUT).stream()
.map(subscribe -> userRepository.getById(subscribe.getUser().getId()))
.filter(user -> user.getDeviceToken() != null)
.map(user -> notificationFactory.generateSoldOutNotification(
HOME,
event.place(),
user
)).toList();
notificationService.push(notifications);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package in.koreatech.koin.domain.coop.model;

public record DiningSoldOutEvent(
String place
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import java.time.Clock;
import java.time.LocalDateTime;

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

import in.koreatech.koin.domain.coop.dto.DiningImageRequest;
import in.koreatech.koin.domain.coop.dto.SoldOutRequest;
import in.koreatech.koin.domain.coop.model.DiningSoldOutEvent;
import in.koreatech.koin.domain.dining.model.Dining;
import in.koreatech.koin.domain.dining.repository.DiningRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,17 +22,18 @@ public class CoopService {
private final DiningRepository diningRepository;

private final Clock clock;
private final ApplicationEventPublisher eventPublisher;

@Transactional
public void changeSoldOut(SoldOutRequest soldOutRequest) {
Dining dining = diningRepository.getById(soldOutRequest.menuId());

if (Boolean.TRUE.equals(soldOutRequest.soldOut())) {
dining.setSoldOut(LocalDateTime.now(clock));
}
else {
} else {
dining.setSoldOut(null);
}
eventPublisher.publishEvent(new DiningSoldOutEvent(dining.getPlace()));
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,20 @@ public Notification generateShopEventCreateNotification(
target
);
}

public Notification generateSoldOutNotification(
MobileAppPath path,
String place,
User target
) {
return new Notification(
path,
"학식 품절 알림!",
"%s 품절되었습니다."
.formatted(place),
null,
NotificationType.MESSAGE,
target
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public interface NotificationSubscribeRepository extends Repository<Notification

NotificationSubscribe save(NotificationSubscribe notificationSubscribe);

List<NotificationSubscribe> findAllBySubscribeType(NotificationSubscribeType type);

Optional<NotificationSubscribe> findByUserIdAndSubscribeType(Integer userId, NotificationSubscribeType type);

default NotificationSubscribe getByUserIdAndSubscribeType(Integer userId, NotificationSubscribeType type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package in.koreatech.koin.global.domain.notification.service;

import static in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType.DINING_SOLD_OUT;
import static in.koreatech.koin.global.fcm.MobileAppPath.HOME;

import java.util.ArrayList;
import java.util.List;

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

import in.koreatech.koin.domain.coop.model.DiningSoldOutEvent;
import in.koreatech.koin.domain.user.model.User;
import in.koreatech.koin.domain.user.repository.UserRepository;
import in.koreatech.koin.global.domain.notification.dto.NotificationStatusResponse;
import in.koreatech.koin.global.domain.notification.dto.NotificationSubscribePermitRequest;
import in.koreatech.koin.global.domain.notification.model.Notification;
import in.koreatech.koin.global.domain.notification.model.NotificationFactory;
import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe;
import in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType;
import in.koreatech.koin.global.domain.notification.repository.NotificationRepository;
Expand All @@ -24,6 +30,7 @@ public class NotificationService {
private final UserRepository userRepository;
private final NotificationRepository notificationRepository;
private final FcmClient fcmClient;
private final NotificationFactory notificationFactory;
private final NotificationSubscribeRepository notificationSubscribeRepository;

public void push(List<Notification> notifications) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
update `shop_menu_categories` set name = '추천 메뉴' where name = '이벤트 메뉴';
update `shop_menu_categories` set name = '메인 메뉴' where name = '대표 메뉴';
4 changes: 4 additions & 0 deletions src/test/java/in/koreatech/koin/AcceptanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.testcontainers.utility.DockerImageName;

import in.koreatech.koin.domain.bus.util.CityBusOpenApiClient;
import in.koreatech.koin.domain.coop.model.CoopEventListener;
import in.koreatech.koin.domain.owner.model.OwnerEventListener;
import in.koreatech.koin.domain.shop.model.ShopEventListener;
import in.koreatech.koin.domain.user.model.StudentEventListener;
Expand Down Expand Up @@ -60,6 +61,9 @@ public abstract class AcceptanceTest {
@MockBean
protected ShopEventListener shopEventListener;

@MockBean
protected CoopEventListener coopEventListener;

@Autowired
private DBInitializer dataInitializer;

Expand Down
76 changes: 72 additions & 4 deletions src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import static in.koreatech.koin.domain.user.model.UserType.COOP;
import static in.koreatech.koin.domain.user.model.UserType.STUDENT;
import static in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType.DINING_SOLD_OUT;
import static io.restassured.RestAssured.given;
import static java.time.format.DateTimeFormatter.ofPattern;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.Clock;
Expand All @@ -28,6 +31,8 @@
import in.koreatech.koin.domain.user.model.UserGender;
import in.koreatech.koin.domain.user.repository.UserRepository;
import in.koreatech.koin.global.auth.JwtProvider;
import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe;
import in.koreatech.koin.global.domain.notification.repository.NotificationSubscribeRepository;
import io.restassured.http.ContentType;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
Expand All @@ -43,6 +48,9 @@ class DiningApiTest extends AcceptanceTest {
@Autowired
private UserRepository userRepository;

@Autowired
private NotificationSubscribeRepository notificationSubscribeRepository;

@Test
@DisplayName("특정 날짜의 모든 식단들을 조회한다.")
void findDinings() {
Expand Down Expand Up @@ -98,7 +106,8 @@ void findDinings() {
softly.assertThat(response.body().jsonPath().getList(".").size()).isEqualTo(2);

softly.assertThat(response.body().jsonPath().getInt("[0].id")).isEqualTo(dining1.getId());
softly.assertThat(response.body().jsonPath().getString("[0].date")).isEqualTo(dining1.getDate().format(ofPattern("yyyy-MM-dd")));
softly.assertThat(response.body().jsonPath().getString("[0].date"))
.isEqualTo(dining1.getDate().format(ofPattern("yyyy-MM-dd")));
softly.assertThat(response.body().jsonPath().getString("[0].type")).isEqualTo(dining1.getType());
softly.assertThat(response.body().jsonPath().getString("[0].place")).isEqualTo(dining1.getPlace());
softly.assertThat(response.body().jsonPath().getInt("[0].price_card"))
Expand All @@ -116,7 +125,8 @@ void findDinings() {
.isEqualTo(dining1.getIsChanged());

softly.assertThat(response.body().jsonPath().getInt("[1].id")).isEqualTo(dining3.getId());
softly.assertThat(response.body().jsonPath().getString("[1].date")).isEqualTo(dining3.getDate().format(ofPattern("yyyy-MM-dd")));
softly.assertThat(response.body().jsonPath().getString("[1].date"))
.isEqualTo(dining3.getDate().format(ofPattern("yyyy-MM-dd")));
softly.assertThat(response.body().jsonPath().getString("[1].type")).isEqualTo(dining3.getType());
softly.assertThat(response.body().jsonPath().getString("[1].place")).isEqualTo(dining3.getPlace());
softly.assertThat(response.body().jsonPath().getInt("[1].price_card"))
Expand Down Expand Up @@ -276,7 +286,6 @@ void requestSoldOut() {

diningRepository.save(dining1);
diningRepository.save(dining2);

SoldOutRequest soldOutRequest = new SoldOutRequest(2, true);

ExtractableResponse<Response> response = given()
Expand Down Expand Up @@ -329,7 +338,6 @@ void requestSoldOutNoAuth() {
.build();

diningRepository.save(dining1);

SoldOutRequest soldOutRequest = new SoldOutRequest(1, true);

ExtractableResponse<Response> response = given()
Expand Down Expand Up @@ -437,4 +445,64 @@ void ImageUploadWithNoAuth() {
.statusCode(HttpStatus.FORBIDDEN.value())
.extract();
}

@Test
@DisplayName("품절 이벤트가 발생한다.")
void checkSoldOutEventListener() {
when(clock.instant()).thenReturn(ZonedDateTime.parse(
"2024-04-04 18:00:00 KST",
ofPattern("yyyy-MM-dd " + "HH:mm:ss z")
)
.toInstant());
when(clock.getZone()).thenReturn(Clock.systemDefaultZone().getZone());

User user = User.builder()
.password("1234")
.nickname("준기")
.name("허준기")
.phoneNumber("010-1234-5678")
.userType(COOP)
.gender(UserGender.MAN)
.email("test@koreatech.ac.kr")
.isAuthed(true)
.isDeleted(false)
.build();

userRepository.save(user);

String token = jwtProvider.createToken(user);

notificationSubscribeRepository.save(NotificationSubscribe.builder()
.user(user)
.subscribeType(DINING_SOLD_OUT)
.build());

Dining dining = Dining.builder()
.date(LocalDate.parse("2024-04-04"))
.type("LUNCH")
.place("A코스")
.priceCard(6000)
.priceCash(6000)
.kcal(881)
.menu("""
["병아리콩밥", "(탕)소고기육개장", "땡초부추전", "누룽지탕"]""")
.isChanged(LocalDateTime.now(clock))
.build();

diningRepository.save(dining);

SoldOutRequest soldOutRequest = new SoldOutRequest(1, true);

given()
.contentType(ContentType.JSON)
.header("Authorization", "Bearer " + token)
.body(soldOutRequest)
.when()
.patch("/coop/dining/soldout")
.then()
.statusCode(HttpStatus.OK.value())
.extract();

verify(coopEventListener).onDiningSoldOutRequest(any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import in.koreatech.koin.global.auth.JwtProvider;
import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe;
import in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType;
import in.koreatech.koin.global.domain.notification.repository.NotificationRepository;
import in.koreatech.koin.global.domain.notification.repository.NotificationSubscribeRepository;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
Expand All @@ -33,9 +32,6 @@ class NotificationApiTest extends AcceptanceTest {
@Autowired
private NotificationSubscribeRepository notificationSubscribeRepository;

@Autowired
private NotificationRepository notificationRepository;

@Autowired
private UserRepository userRepository;

Expand Down
Loading