From 0e5e0f1c86b0feaf910b17ee10ac4465c7fc5a27 Mon Sep 17 00:00:00 2001 From: Kim Taemin Date: Tue, 28 Oct 2025 10:39:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[FEAT]=20=EA=B5=AC=EB=A7=A4=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../controller/PurchaseController.java | 66 ++++++++ .../purchase/dto/PurchaseOrderItemDto.java | 31 ++++ .../purchase/dto/PurchaseOrderRequestDto.java | 28 ++++ .../dto/PurchaseOrderResponseDto.java | 54 +++++++ .../api/purchase/entity/OrderStatus.java | 8 + .../api/purchase/entity/PurchaseOrder.java | 69 +++++++++ .../purchase/entity/PurchaseOrderItem.java | 34 ++++ .../api/purchase/entity/UrgencyLevel.java | 8 + .../PurchaseOrderItemRepository.java | 10 ++ .../repository/PurchaseOrderRepository.java | 28 ++++ .../api/purchase/service/PurchaseService.java | 145 ++++++++++++++++++ 12 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/controller/PurchaseController.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderItemDto.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderResponseDto.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/entity/OrderStatus.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrder.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/entity/UrgencyLevel.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderRepository.java create mode 100644 src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java diff --git a/build.gradle b/build.gradle index db3461c..5d96016 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.sampoom' -version = '0.0.1-SNAPSHOT' +version = '1.0.0' description = 'Demo project for Spring Boot' java { diff --git a/src/main/java/com/sampoom/purchase/api/purchase/controller/PurchaseController.java b/src/main/java/com/sampoom/purchase/api/purchase/controller/PurchaseController.java new file mode 100644 index 0000000..52735a3 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/controller/PurchaseController.java @@ -0,0 +1,66 @@ +package com.sampoom.purchase.api.purchase.controller; + +import com.sampoom.purchase.api.purchase.dto.PurchaseOrderRequestDto; +import com.sampoom.purchase.api.purchase.dto.PurchaseOrderResponseDto; +import com.sampoom.purchase.api.purchase.entity.OrderStatus; +import com.sampoom.purchase.api.purchase.entity.UrgencyLevel; +import com.sampoom.purchase.api.purchase.service.PurchaseService; +import com.sampoom.purchase.common.response.ApiResponse; +import com.sampoom.purchase.common.response.PageResponseDto; +import com.sampoom.purchase.common.response.SuccessStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "Purchase", description = "Purchase 관련 API 입니다.") +@RestController +@RequiredArgsConstructor +public class PurchaseController { + + private final PurchaseService purchaseService; + + @Operation(summary = "자재 주문 생성", description = "공장에 필요한 자재 주문을 생성합니다.") + @PostMapping() + public ResponseEntity> createMaterialOrder( + @RequestBody PurchaseOrderRequestDto requestDto) { + return ApiResponse.success(SuccessStatus.CREATED, + purchaseService.createMaterialOrder(requestDto)); + } + + + + @Operation(summary = "자재 주문 취소", description = "주문을 취소 처리합니다.") + @PatchMapping("/{orderId}/cancel") + public ResponseEntity> cancelOrder( + @PathVariable Long orderId) { + return ApiResponse.success(SuccessStatus.OK, purchaseService.cancelOrder(orderId)); + } + + @Operation(summary = "자재 주문 삭제", description = "주문을 삭제합니다(소프트 삭제).") + @DeleteMapping("/{orderId}") + public ResponseEntity> deleteOrder( + @PathVariable Long orderId) { + purchaseService.deleteOrder(orderId); + return ApiResponse.success_only(SuccessStatus.OK); + } + + @Operation(summary = "자재 주문 단건 조회", description = "특정 자재 주문의 상세를 조회합니다.") + @GetMapping("/{orderId}") + public ResponseEntity> getOrder( + @PathVariable Long orderId) { + return ApiResponse.success(SuccessStatus.OK, purchaseService.getOrder(orderId)); + } + + @Operation(summary = "자재 주문 목록 조회", description = "주문 상태 필터와 검색(자재명/자재코드/주문코드), 긴급도 필터로 목록을 조회합니다.") + @GetMapping() + public ResponseEntity>> getOrders( + @RequestParam(required = false) OrderStatus status, + @RequestParam(required = false) UrgencyLevel urgency, + @RequestParam(required = false) String query, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size) { + return ApiResponse.success(SuccessStatus.OK, purchaseService.getOrders(status, urgency, query, page, size)); + } +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderItemDto.java b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderItemDto.java new file mode 100644 index 0000000..afb30c0 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderItemDto.java @@ -0,0 +1,31 @@ +package com.sampoom.purchase.api.purchase.dto; + +import com.sampoom.purchase.api.purchase.entity.PurchaseOrderItem; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PurchaseOrderItemDto { + private String materialCode; + private String materialName; + private String unit; + private Long quantity; + private BigDecimal unitPrice; + + public static PurchaseOrderItemDto from(PurchaseOrderItem item) { + return PurchaseOrderItemDto.builder() + .materialCode(item.getMaterialCode()) + .materialName(item.getMaterialName()) + .unit(item.getUnit()) + .quantity(item.getQuantity()) + .unitPrice(item.getUnitPrice()) + .build(); + } +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java new file mode 100644 index 0000000..6ff73a8 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java @@ -0,0 +1,28 @@ +package com.sampoom.purchase.api.purchase.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PurchaseOrderRequestDto { + private Long factoryId; + private String factoryName; + + @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) + private LocalDate requiredAt; + + private String requesterName; // 요청자 이름 추가 + + private List items; +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderResponseDto.java b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderResponseDto.java new file mode 100644 index 0000000..753730f --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderResponseDto.java @@ -0,0 +1,54 @@ +package com.sampoom.purchase.api.purchase.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.sampoom.purchase.api.purchase.entity.OrderStatus; +import com.sampoom.purchase.api.purchase.entity.PurchaseOrder; +import com.sampoom.purchase.api.purchase.entity.PurchaseOrderItem; +import com.sampoom.purchase.api.purchase.entity.UrgencyLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PurchaseOrderResponseDto { + private Long id; + private String orderCode; + private LocalDateTime orderAt; + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate requiredAt; // 날짜만 + + private Long factoryId; + private String factoryName; + private String requesterName; + private UrgencyLevel urgency; + private BigDecimal expectedAmount; + private OrderStatus status; + private List items; + + public static PurchaseOrderResponseDto from(PurchaseOrder order, List orderItems) { + return PurchaseOrderResponseDto.builder() + .id(order.getId()) + .status(order.getStatus()) + .orderCode(order.getCode()) + .orderAt(order.getOrderAt()) + .requiredAt(order.getRequiredAt()) + .factoryId(order.getFactoryId()) + .factoryName(order.getFactoryName()) + .requesterName(order.getRequesterName()) + .urgency(order.getUrgency()) + .expectedAmount(order.getExpectedAmount()) + .items(orderItems.stream().map(PurchaseOrderItemDto::from).collect(Collectors.toList())) + .build(); + } +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/entity/OrderStatus.java b/src/main/java/com/sampoom/purchase/api/purchase/entity/OrderStatus.java new file mode 100644 index 0000000..be6d6fb --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/entity/OrderStatus.java @@ -0,0 +1,8 @@ +package com.sampoom.purchase.api.purchase.entity; + + +public enum OrderStatus { + ORDERED, // 주문됨 + RECEIVED, // 입고됨 + CANCELED // 발주 취소됨 +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrder.java b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrder.java new file mode 100644 index 0000000..a964576 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrder.java @@ -0,0 +1,69 @@ +package com.sampoom.purchase.api.purchase.entity; + +import com.sampoom.purchase.common.entitiy.SoftDeleteEntity; +import com.sampoom.purchase.common.exception.BadRequestException; +import com.sampoom.purchase.common.response.ErrorStatus; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Entity +@Getter +@Table(name = "purchase_order") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +@SQLDelete(sql = "UPDATE purchase_order SET deleted = true, deleted_at = now() WHERE purchase_order_id = ?") +@SQLRestriction("deleted = false") +public class PurchaseOrder extends SoftDeleteEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "purchase_order_id") + private Long id; + + private String code; + private LocalDateTime orderAt; + private LocalDateTime receivedAt; + private LocalDateTime canceledAt; + private LocalDate requiredAt; + + + @Enumerated(EnumType.STRING) + private OrderStatus status; + + + private Long factoryId; + + private String factoryName; + + @Enumerated(EnumType.STRING) + private UrgencyLevel urgency; // 긴급도 + + private String requesterName; // 요청자 이름 + + @Column(precision = 19, scale = 2) + private BigDecimal expectedAmount; // 예상 금액 + + public void receive() { + if (this.status != OrderStatus.ORDERED) { + throw new BadRequestException(ErrorStatus.ORDER_ALREADY_PROCESSED); + } + this.status = OrderStatus.RECEIVED; + this.receivedAt = LocalDateTime.now(); + } + + public void cancel() { + + if (this.status != OrderStatus.ORDERED) { + throw new BadRequestException(ErrorStatus.ORDER_ALREADY_PROCESSED); + } + this.status = OrderStatus.CANCELED; + this.canceledAt = LocalDateTime.now(); + } +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java new file mode 100644 index 0000000..38ff53b --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java @@ -0,0 +1,34 @@ +package com.sampoom.purchase.api.purchase.entity; + +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; + +@Entity +@Getter +@Table(name = "purchase_order_item") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +public class PurchaseOrderItem { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "material_order_item_id") + private Long id; + + private Long quantity; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "purchase_order_id") + private PurchaseOrder purchaseOrder; + + private String materialCode; + + private String materialName; + + private String unit; + + @Column(precision = 19, scale = 2) + private BigDecimal unitPrice; +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/entity/UrgencyLevel.java b/src/main/java/com/sampoom/purchase/api/purchase/entity/UrgencyLevel.java new file mode 100644 index 0000000..ba25359 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/entity/UrgencyLevel.java @@ -0,0 +1,8 @@ +package com.sampoom.purchase.api.purchase.entity; + +public enum UrgencyLevel { + HIGH, + MEDIUM, + LOW +} + diff --git a/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java new file mode 100644 index 0000000..2251e83 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java @@ -0,0 +1,10 @@ +package com.sampoom.purchase.api.purchase.repository; + +import com.sampoom.purchase.api.purchase.entity.PurchaseOrderItem; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface PurchaseOrderItemRepository extends JpaRepository { + List findByPurchaseOrderId(Long purchaseOrderId); +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderRepository.java b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderRepository.java new file mode 100644 index 0000000..fde536b --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderRepository.java @@ -0,0 +1,28 @@ +package com.sampoom.purchase.api.purchase.repository; + +import com.sampoom.purchase.api.purchase.entity.OrderStatus; +import com.sampoom.purchase.api.purchase.entity.PurchaseOrder; +import com.sampoom.purchase.api.purchase.entity.UrgencyLevel; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; + +public interface PurchaseOrderRepository extends JpaRepository { + + @Query("select distinct po from PurchaseOrder po left join PurchaseOrderItem i on i.purchaseOrder = po " + + "where (:status is null or po.status = :status) " + + "and (:urgency is null or po.urgency = :urgency) " + + "and (:query is null or :query = '' or lower(po.code) like lower(concat('%', :query, '%')) " + + "or lower(i.materialCode) like lower(concat('%', :query, '%')) " + + "or lower(i.materialName) like lower(concat('%', :query, '%')))" ) + Page search(@Param("status") OrderStatus status, + @Param("urgency") UrgencyLevel urgency, + @Param("query") String query, + Pageable pageable); + + Optional findTopByCodeStartingWithOrderByCodeDesc(String prefix); +} diff --git a/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java b/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java new file mode 100644 index 0000000..1b57100 --- /dev/null +++ b/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java @@ -0,0 +1,145 @@ +package com.sampoom.purchase.api.purchase.service; + +import com.sampoom.purchase.api.purchase.dto.PurchaseOrderRequestDto; +import com.sampoom.purchase.api.purchase.dto.PurchaseOrderResponseDto; +import com.sampoom.purchase.api.purchase.entity.*; +import com.sampoom.purchase.api.purchase.repository.PurchaseOrderItemRepository; +import com.sampoom.purchase.api.purchase.repository.PurchaseOrderRepository; +import com.sampoom.purchase.common.exception.NotFoundException; +import com.sampoom.purchase.common.response.ErrorStatus; +import com.sampoom.purchase.common.response.PageResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class PurchaseService { + + private final PurchaseOrderRepository orderRepository; + private final PurchaseOrderItemRepository orderItemRepository; + + @Transactional + public PurchaseOrderResponseDto createMaterialOrder(PurchaseOrderRequestDto requestDto) { + // 예상 금액 합산: Σ(unitPrice * quantity) + BigDecimal expectedAmount = requestDto.getItems() == null ? BigDecimal.ZERO : + requestDto.getItems().stream() + .map(i -> (i.getUnitPrice() == null ? BigDecimal.ZERO : i.getUnitPrice()) + .multiply(BigDecimal.valueOf(i.getQuantity() == null ? 0L : i.getQuantity()))) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 긴급도 계산: 오늘과 필요일 차이 기준 + UrgencyLevel urgency = calculateUrgency(requestDto.getRequiredAt()); + + PurchaseOrder order = PurchaseOrder.builder() + .code(generateOrderCode()) + .factoryId(requestDto.getFactoryId()) + .status(OrderStatus.ORDERED) + .orderAt(LocalDateTime.now()) + .factoryName(requestDto.getFactoryName()) + .requiredAt(requestDto.getRequiredAt()) + .requesterName(requestDto.getRequesterName()) + .expectedAmount(expectedAmount) + .urgency(urgency) + .build(); + + orderRepository.save(order); + + List orderItems = requestDto.getItems().stream() + .map(itemDto -> PurchaseOrderItem.builder() + .purchaseOrder(order) + .materialCode(itemDto.getMaterialCode()) + .materialName(itemDto.getMaterialName()) + .unit(itemDto.getUnit()) + .quantity(itemDto.getQuantity()) + .unitPrice(itemDto.getUnitPrice()) + .build()) + .collect(Collectors.toList()); + + orderItemRepository.saveAll(orderItems); + + return PurchaseOrderResponseDto.from(order, orderItems); + } + + private UrgencyLevel calculateUrgency(LocalDate requiredAt) { + if (requiredAt == null) return UrgencyLevel.LOW; + long days = ChronoUnit.DAYS.between(LocalDate.now(), requiredAt); + if (days <= 1) return UrgencyLevel.HIGH; + if (days <= 3) return UrgencyLevel.MEDIUM; + return UrgencyLevel.LOW; + } + + @Transactional(readOnly = true) + public PurchaseOrderResponseDto getOrder(Long orderId) { + PurchaseOrder order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorStatus.ORDER_NOT_FOUND)); + List items = orderItemRepository.findByPurchaseOrderId(orderId); + return PurchaseOrderResponseDto.from(order, items); + } + + @Transactional(readOnly = true) + public PageResponseDto getOrders(OrderStatus status, UrgencyLevel urgency, String query, int page, int size) { + Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "orderAt")); + Page pageResult = orderRepository.search(status, urgency, query, pageable); + List content = pageResult.getContent().stream() + .map(order -> PurchaseOrderResponseDto.from(order, orderItemRepository.findByPurchaseOrderId(order.getId()))) + .collect(Collectors.toList()); + return PageResponseDto.builder() + .content(content) + .totalElements(pageResult.getTotalElements()) + .totalPages(pageResult.getTotalPages()) + .build(); + } + + + + @Transactional + public PurchaseOrderResponseDto cancelOrder(Long orderId) { + PurchaseOrder order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorStatus.ORDER_NOT_FOUND)); + order.cancel(); + orderRepository.save(order); + List items = orderItemRepository.findByPurchaseOrderId(orderId); + return PurchaseOrderResponseDto.from(order, items); + } + + @Transactional + public void deleteOrder(Long orderId) { + PurchaseOrder order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorStatus.ORDER_NOT_FOUND)); + orderRepository.delete(order); + } + + private String generateOrderCode() { + String datePart = java.time.LocalDate.now() + .format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE); // YYYYMMDD + String prefix = "ORD-" + datePart + "-"; + + String lastCode = orderRepository + .findTopByCodeStartingWithOrderByCodeDesc(prefix) + .map(PurchaseOrder::getCode) + .orElse(null); + + int nextSeq = 1; + if (lastCode != null && lastCode.startsWith(prefix)) { + String seqStr = lastCode.substring(prefix.length()); + try { + nextSeq = Integer.parseInt(seqStr) + 1; + } catch (NumberFormatException ignored) { + // keep as 1 + } + } + return prefix + String.format("%03d", nextSeq); + } +} From 704c7cb567a8fd577ab20739a50a692810281914 Mon Sep 17 00:00:00 2001 From: Kim Taemin Date: Tue, 28 Oct 2025 11:00:58 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[FIX]=20=EC=BD=94=EB=93=9C=20=EB=9E=98?= =?UTF-8?q?=EB=B9=97=20=ED=94=BC=EB=93=9C=EB=B0=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../purchase/dto/PurchaseOrderRequestDto.java | 1 - .../api/purchase/entity/PurchaseOrderItem.java | 2 +- .../repository/PurchaseOrderItemRepository.java | 1 + .../api/purchase/service/PurchaseService.java | 17 +++++++++++++---- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java index 6ff73a8..727d004 100644 --- a/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java +++ b/src/main/java/com/sampoom/purchase/api/purchase/dto/PurchaseOrderRequestDto.java @@ -19,7 +19,6 @@ public class PurchaseOrderRequestDto { private String factoryName; @JsonFormat(pattern = "yyyy-MM-dd") - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) private LocalDate requiredAt; private String requesterName; // 요청자 이름 추가 diff --git a/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java index 38ff53b..e81be9f 100644 --- a/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java +++ b/src/main/java/com/sampoom/purchase/api/purchase/entity/PurchaseOrderItem.java @@ -14,7 +14,7 @@ public class PurchaseOrderItem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "material_order_item_id") + @Column(name = "purchase_order_item_id") private Long id; private Long quantity; diff --git a/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java index 2251e83..b2dc173 100644 --- a/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java +++ b/src/main/java/com/sampoom/purchase/api/purchase/repository/PurchaseOrderItemRepository.java @@ -7,4 +7,5 @@ public interface PurchaseOrderItemRepository extends JpaRepository { List findByPurchaseOrderId(Long purchaseOrderId); + List findByPurchaseOrderIdIn(List orderIds); } diff --git a/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java b/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java index 1b57100..d32f263 100644 --- a/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java +++ b/src/main/java/com/sampoom/purchase/api/purchase/service/PurchaseService.java @@ -21,6 +21,7 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Service @@ -92,9 +93,17 @@ public PurchaseOrderResponseDto getOrder(Long orderId) { public PageResponseDto getOrders(OrderStatus status, UrgencyLevel urgency, String query, int page, int size) { Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "orderAt")); Page pageResult = orderRepository.search(status, urgency, query, pageable); - List content = pageResult.getContent().stream() - .map(order -> PurchaseOrderResponseDto.from(order, orderItemRepository.findByPurchaseOrderId(order.getId()))) - .collect(Collectors.toList()); + List orderIds = pageResult.getContent().stream() + .map(PurchaseOrder::getId).toList(); + List allItems = + orderItemRepository.findByPurchaseOrderIdIn(orderIds); + Map> itemsByOrderId = allItems.stream() + .collect(Collectors.groupingBy(i -> i.getPurchaseOrder().getId())); + List content = pageResult.getContent().stream() + .map(order -> PurchaseOrderResponseDto.from( + order, + itemsByOrderId.getOrDefault(order.getId(), java.util.Collections.emptyList()))) + .collect(Collectors.toList()); return PageResponseDto.builder() .content(content) .totalElements(pageResult.getTotalElements()) @@ -102,7 +111,7 @@ public PageResponseDto getOrders(OrderStatus status, U .build(); } - + @Transactional public PurchaseOrderResponseDto cancelOrder(Long orderId) { From 3956ca85ca2c4d4f81678e1165a3a5c95f2a75a0 Mon Sep 17 00:00:00 2001 From: Kim Taemin Date: Tue, 28 Oct 2025 15:26:36 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[FIX]=20CORS=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/swagger/WebConfig.java | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/main/java/com/sampoom/purchase/common/config/swagger/WebConfig.java diff --git a/src/main/java/com/sampoom/purchase/common/config/swagger/WebConfig.java b/src/main/java/com/sampoom/purchase/common/config/swagger/WebConfig.java deleted file mode 100644 index b735573..0000000 --- a/src/main/java/com/sampoom/purchase/common/config/swagger/WebConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sampoom.purchase.common.config.swagger; - - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebConfig implements WebMvcConfigurer { - - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") // 모든 경로 허용 - .allowedOrigins("http://localhost:3000") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(true) // 쿠키 허용 시 필요 - .maxAge(3600); // preflight 캐싱 시간 (초) - } - -}