diff --git a/member/src/main/java/com/ktds/hi/member/service/PreferenceServiceImpl.java b/member/src/main/java/com/ktds/hi/member/service/PreferenceServiceImpl.java deleted file mode 100644 index ba93eea..0000000 --- a/member/src/main/java/com/ktds/hi/member/service/PreferenceServiceImpl.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.ktds.hi.member.service; - -import com.ktds.hi.member.dto.PreferenceRequest; -import com.ktds.hi.member.dto.TasteTagResponse; -import com.ktds.hi.member.domain.TagType; -import com.ktds.hi.member.repository.entity.PreferenceEntity; -import com.ktds.hi.member.repository.entity.TasteTagEntity; -import com.ktds.hi.member.repository.jpa.PreferenceRepository; -import com.ktds.hi.member.repository.jpa.TasteTagRepository; -import com.ktds.hi.common.exception.BusinessException; -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.stream.Collectors; - -/** - * 취향 관리 서비스 구현체 - * 취향 정보 등록/수정 및 태그 관리 기능을 구현 - */ -@Service -@RequiredArgsConstructor -@Slf4j -@Transactional -public class PreferenceServiceImpl implements PreferenceService { - - private final PreferenceRepository preferenceRepository; - private final TasteTagRepository tasteTagRepository; - - @Override - public void savePreference(Long memberId, PreferenceRequest request) { - // 태그 유효성 검증 - List existingTags = tasteTagRepository.findByTagNameIn(request.getTags()); - if (existingTags.size() != request.getTags().size()) { - throw new BusinessException("유효하지 않은 태그가 포함되어 있습니다"); - } - - // 기존 취향 정보 조회 - PreferenceEntity preference = preferenceRepository.findByMemberId(memberId) - .orElse(null); - - if (preference != null) { - // 기존 정보 업데이트 - preference.updatePreference(request.getTags(), request.getHealthInfo(), request.getSpicyLevel()); - } else { - // 새로운 취향 정보 생성 - preference = PreferenceEntity.builder() - .memberId(memberId) - .tags(request.getTags()) - .healthInfo(request.getHealthInfo()) - .spicyLevel(request.getSpicyLevel()) - .build(); - } - - preferenceRepository.save(preference); - - log.info("취향 정보 저장 완료: memberId={}, tags={}", memberId, request.getTags()); - } - - @Override - @Transactional(readOnly = true) - public List getAvailableTags() { - List tags = tasteTagRepository.findByIsActiveTrue(); - - return tags.stream() - .map(tag -> TasteTagResponse.builder() - .id(tag.getId()) - .tagName(tag.getTagName()) - .tagType(tag.getTagType()) - .description(tag.getDescription()) - .build()) - .collect(Collectors.toList()); - } - - @Override - @Transactional(readOnly = true) - public List getTagsByType(TagType tagType) { - List tags = tasteTagRepository.findByTagTypeAndIsActiveTrue(tagType); - - return tags.stream() - .map(tag -> TasteTagResponse.builder() - .id(tag.getId()) - .tagName(tag.getTagName()) - .tagType(tag.getTagType()) - .description(tag.getDescription()) - .build()) - .collect(Collectors.toList()); - } -} diff --git a/store/src/main/java/com/ktds/hi/store/biz/service/OrderService.java b/store/src/main/java/com/ktds/hi/store/biz/service/OrderService.java new file mode 100644 index 0000000..7edc385 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/biz/service/OrderService.java @@ -0,0 +1,88 @@ +package com.ktds.hi.store.biz.service; + +import com.ktds.hi.store.biz.usecase.in.OrderUseCase; +import com.ktds.hi.store.biz.usecase.out.OrderRepositoryPort; +import com.ktds.hi.store.domain.Order; +import com.ktds.hi.store.infra.dto.OrderListResponse; +import com.ktds.hi.store.infra.dto.OrderResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class OrderService implements OrderUseCase { + + private final OrderRepositoryPort orderRepositoryPort; + + @Override + public OrderListResponse getOrdersByStore(Long storeId) { + log.info("가게 {} 의 주문 목록 조회", storeId); + + List orders = orderRepositoryPort.findOrdersByStoreId(storeId); + List orderResponses = orders.stream() + .map(this::toResponse) + .collect(Collectors.toList()); + + return OrderListResponse.builder() + .storeId(storeId) + .totalCount(orderResponses.size()) + .orders(orderResponses) + .build(); + } + + @Override + public OrderListResponse getOrdersByStoreAndPeriod(Long storeId, LocalDateTime startDate, LocalDateTime endDate) { + log.info("가게 {} 의 기간별 주문 목록 조회: {} ~ {}", storeId, startDate, endDate); + + List orders = orderRepositoryPort.findOrdersByStoreIdAndPeriod(storeId, startDate, endDate); + List orderResponses = orders.stream() + .map(this::toResponse) + .collect(Collectors.toList()); + + return OrderListResponse.builder() + .storeId(storeId) + .totalCount(orderResponses.size()) + .orders(orderResponses) + .build(); + } + + @Override + public OrderResponse getOrder(Long orderId) { + log.info("주문 {} 조회", orderId); + + Order order = orderRepositoryPort.findOrderById(orderId) + .orElseThrow(() -> new IllegalArgumentException("주문을 찾을 수 없습니다: " + orderId)); + + return toResponse(order); + } + + @Override + public List getAllOrders() { + log.info("전체 주문 목록 조회"); + + return orderRepositoryPort.findAllOrders().stream() + .map(this::toResponse) + .collect(Collectors.toList()); + } + + private OrderResponse toResponse(Order order) { + return OrderResponse.builder() + .id(order.getId()) + .storeId(order.getStoreId()) + .menuId(order.getMenuId()) + .customerAge(order.getCustomerAge()) + .customerGender(order.getCustomerGender()) + .orderAmount(order.getOrderAmount()) + .orderDate(order.getOrderDate()) + .createdAt(order.getCreatedAt()) + .build(); + } +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/biz/usecase/in/OrderUseCase.java b/store/src/main/java/com/ktds/hi/store/biz/usecase/in/OrderUseCase.java new file mode 100644 index 0000000..2d1cbe3 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/biz/usecase/in/OrderUseCase.java @@ -0,0 +1,18 @@ +package com.ktds.hi.store.biz.usecase.in; + +import com.ktds.hi.store.infra.dto.OrderListResponse; +import com.ktds.hi.store.infra.dto.OrderResponse; + +import java.time.LocalDateTime; +import java.util.List; + +public interface OrderUseCase { + + OrderListResponse getOrdersByStore(Long storeId); + + OrderListResponse getOrdersByStoreAndPeriod(Long storeId, LocalDateTime startDate, LocalDateTime endDate); + + OrderResponse getOrder(Long orderId); + + List getAllOrders(); +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/biz/usecase/out/OrderRepositoryPort.java b/store/src/main/java/com/ktds/hi/store/biz/usecase/out/OrderRepositoryPort.java new file mode 100644 index 0000000..6d9d12f --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/biz/usecase/out/OrderRepositoryPort.java @@ -0,0 +1,20 @@ +package com.ktds.hi.store.biz.usecase.out; + +import com.ktds.hi.store.domain.Order; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +public interface OrderRepositoryPort { + + List findOrdersByStoreId(Long storeId); + + List findOrdersByStoreIdAndPeriod(Long storeId, LocalDateTime startDate, LocalDateTime endDate); + + Optional findOrderById(Long orderId); + + List findAllOrders(); + + Order saveOrder(Order order); +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/domain/Order.java b/store/src/main/java/com/ktds/hi/store/domain/Order.java new file mode 100644 index 0000000..19e0d46 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/domain/Order.java @@ -0,0 +1,22 @@ +package com.ktds.hi.store.domain; + +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Order { + private Long id; + private Long storeId; + private Long menuId; + private Integer customerAge; + private String customerGender; + private BigDecimal orderAmount; + private LocalDateTime orderDate; + private LocalDateTime createdAt; +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/controller/OrderController.java b/store/src/main/java/com/ktds/hi/store/infra/controller/OrderController.java new file mode 100644 index 0000000..c1e986c --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/controller/OrderController.java @@ -0,0 +1,60 @@ +package com.ktds.hi.store.infra.controller; + +import com.ktds.hi.common.dto.SuccessResponse; +import com.ktds.hi.store.biz.usecase.in.OrderUseCase; +import com.ktds.hi.store.infra.dto.OrderListResponse; +import com.ktds.hi.store.infra.dto.OrderResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.util.List; + +@RestController +@RequestMapping("/api/stores/orders") +@RequiredArgsConstructor +@Tag(name = "주문 데이터", description = "주문 데이터 조회 API") +public class OrderController { + + private final OrderUseCase orderUseCase; + + @GetMapping("/store/{storeId}") + @Operation(summary = "가게별 주문 조회", description = "특정 가게의 모든 주문을 조회합니다") + public ResponseEntity> getOrdersByStore( + @PathVariable Long storeId) { + + OrderListResponse response = orderUseCase.getOrdersByStore(storeId); + return ResponseEntity.ok(SuccessResponse.of(response)); + } + + @GetMapping("/store/{storeId}/period") + @Operation(summary = "가게별 기간 주문 조회", description = "특정 가게의 기간별 주문을 조회합니다") + public ResponseEntity> getOrdersByStoreAndPeriod( + @PathVariable Long storeId, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) { + + OrderListResponse response = orderUseCase.getOrdersByStoreAndPeriod(storeId, startDate, endDate); + return ResponseEntity.ok(SuccessResponse.of(response)); + } + + @GetMapping("/{orderId}") + @Operation(summary = "단일 주문 조회", description = "특정 주문 하나를 조회합니다 (테스트용)") + public ResponseEntity> getOrder(@PathVariable Long orderId) { + + OrderResponse response = orderUseCase.getOrder(orderId); + return ResponseEntity.ok(SuccessResponse.of(response)); + } + + @GetMapping("/all") + @Operation(summary = "전체 주문 조회", description = "모든 주문을 조회합니다 (Analytics 서비스용)") + public ResponseEntity>> getAllOrders() { + + List response = orderUseCase.getAllOrders(); + return ResponseEntity.ok(SuccessResponse.of(response)); + } +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/dto/OrderListResponse.java b/store/src/main/java/com/ktds/hi/store/infra/dto/OrderListResponse.java new file mode 100644 index 0000000..c99097b --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/dto/OrderListResponse.java @@ -0,0 +1,18 @@ +package com.ktds.hi.store.infra.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderListResponse { + private Long storeId; + private Integer totalCount; + private List orders; +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/dto/OrderResponse.java b/store/src/main/java/com/ktds/hi/store/infra/dto/OrderResponse.java new file mode 100644 index 0000000..c09eeb6 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/dto/OrderResponse.java @@ -0,0 +1,38 @@ +package com.ktds.hi.store.infra.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderResponse { + private Long id; + private Long storeId; + private Long menuId; + private Integer customerAge; + private String customerGender; + private BigDecimal orderAmount; + private LocalDateTime orderDate; + private LocalDateTime createdAt; + + // Entity를 Response로 변환 + public static OrderResponse from(com.ktds.hi.store.infra.gateway.entity.OrderEntity entity) { + return OrderResponse.builder() + .id(entity.getId()) + .storeId(entity.getStoreId()) + .menuId(entity.getMenuId()) + .customerAge(entity.getCustomerAge()) + .customerGender(entity.getCustomerGender()) + .orderAmount(entity.getOrderAmount()) + .orderDate(entity.getOrderDate()) + .createdAt(entity.getCreatedAt()) + .build(); + } +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/gateway/OrderRepositoryAdapter.java b/store/src/main/java/com/ktds/hi/store/infra/gateway/OrderRepositoryAdapter.java new file mode 100644 index 0000000..bb4168a --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/gateway/OrderRepositoryAdapter.java @@ -0,0 +1,82 @@ +package com.ktds.hi.store.infra.gateway; + +import com.ktds.hi.store.biz.usecase.out.OrderRepositoryPort; +import com.ktds.hi.store.domain.Order; +import com.ktds.hi.store.infra.gateway.entity.OrderEntity; +import com.ktds.hi.store.infra.gateway.repository.OrderJpaRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class OrderRepositoryAdapter implements OrderRepositoryPort { + + private final OrderJpaRepository orderJpaRepository; + + @Override + public List findOrdersByStoreId(Long storeId) { + return orderJpaRepository.findByStoreId(storeId) + .stream() + .map(this::toDomain) + .collect(Collectors.toList()); + } + + @Override + public List findOrdersByStoreIdAndPeriod(Long storeId, LocalDateTime startDate, LocalDateTime endDate) { + return orderJpaRepository.findByStoreIdAndOrderDateBetween(storeId, startDate, endDate) + .stream() + .map(this::toDomain) + .collect(Collectors.toList()); + } + + @Override + public Optional findOrderById(Long orderId) { + return orderJpaRepository.findById(orderId) + .map(this::toDomain); + } + + @Override + public List findAllOrders() { + return orderJpaRepository.findAll() + .stream() + .map(this::toDomain) + .collect(Collectors.toList()); + } + + @Override + public Order saveOrder(Order order) { + OrderEntity entity = toEntity(order); + OrderEntity saved = orderJpaRepository.save(entity); + return toDomain(saved); + } + + private Order toDomain(OrderEntity entity) { + return Order.builder() + .id(entity.getId()) + .storeId(entity.getStoreId()) + .menuId(entity.getMenuId()) + .customerAge(entity.getCustomerAge()) + .customerGender(entity.getCustomerGender()) + .orderAmount(entity.getOrderAmount()) + .orderDate(entity.getOrderDate()) + .createdAt(entity.getCreatedAt()) + .build(); + } + + private OrderEntity toEntity(Order domain) { + return OrderEntity.builder() + .id(domain.getId()) + .storeId(domain.getStoreId()) + .menuId(domain.getMenuId()) + .customerAge(domain.getCustomerAge()) + .customerGender(domain.getCustomerGender()) + .orderAmount(domain.getOrderAmount()) + .orderDate(domain.getOrderDate()) + .build(); + } +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/gateway/entity/OrderEntity.java b/store/src/main/java/com/ktds/hi/store/infra/gateway/entity/OrderEntity.java new file mode 100644 index 0000000..7a21329 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/gateway/entity/OrderEntity.java @@ -0,0 +1,46 @@ +package com.ktds.hi.store.infra.gateway.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity +@Table(name = "orders") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EntityListeners(AuditingEntityListener.class) +public class OrderEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "store_id", nullable = false) + private Long storeId; + + @Column(name = "menu_id", nullable = false) + private Long menuId; + + @Column(name = "customer_age", nullable = false) + private Integer customerAge; + + @Column(name = "customer_gender", nullable = false) + private String customerGender; + + @Column(name = "order_amount", nullable = false, precision = 10, scale = 2) + private BigDecimal orderAmount; + + @Column(name = "order_date", nullable = false) + private LocalDateTime orderDate; + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; +} \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/gateway/repository/OrderJpaRepository.java b/store/src/main/java/com/ktds/hi/store/infra/gateway/repository/OrderJpaRepository.java new file mode 100644 index 0000000..510c732 --- /dev/null +++ b/store/src/main/java/com/ktds/hi/store/infra/gateway/repository/OrderJpaRepository.java @@ -0,0 +1,20 @@ +package com.ktds.hi.store.infra.gateway.repository; + +import com.ktds.hi.store.infra.gateway.entity.OrderEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface OrderJpaRepository extends JpaRepository { + + List findByStoreId(Long storeId); + + List findByStoreIdAndOrderDateBetween( + Long storeId, + LocalDateTime startDate, + LocalDateTime endDate + ); +} \ No newline at end of file