From 4f2b670ebfb1476edc0f69656732765656ccf316 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Fri, 28 Aug 2026 14:55:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E6=89=A3=20=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E5=90=88=E6=89=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/海外区域统一增量-MySQL8.sql | 4 +++- .../dto/OverseasProcurementRequests.java | 12 +++++++++- .../dto/OverseasProcurementResponses.java | 4 +++- .../entity/OverseasProcurementOrder.java | 1 + .../service/OverseasProcurementService.java | 23 +++++++++++++++++-- .../src/main/resources/db/lastleg.sql | 2 +- .../openapi/overseas-business-v1.yaml | 13 +++++++++++ 7 files changed, 53 insertions(+), 6 deletions(-) diff --git a/deploy/sql/海外区域统一增量-MySQL8.sql b/deploy/sql/海外区域统一增量-MySQL8.sql index 61a281a6c..d55f1a6dd 100644 --- a/deploy/sql/海外区域统一增量-MySQL8.sql +++ b/deploy/sql/海外区域统一增量-MySQL8.sql @@ -195,7 +195,7 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order ( purchase_quantity BIGINT NOT NULL COMMENT '采购数量', budget_unit_price DECIMAL(18,2) NULL COMMENT '预算单价', currency CHAR(3) NOT NULL DEFAULT 'USD' COMMENT '币种', receiving_address VARCHAR(1000) NULL COMMENT '收货地址', contact_name VARCHAR(128) NOT NULL COMMENT '联系人姓名', contact_phone VARCHAR(64) NOT NULL COMMENT '联系人电话', - purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称', + purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称', outbound_details JSON NULL COMMENT '出库明细', inbound_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '采购单据累计入库数量', domestic_carrier VARCHAR(128) NULL COMMENT '境内承运商', domestic_tracking_no VARCHAR(128) NULL COMMENT '境内物流单号', overseas_carrier VARCHAR(128) NULL COMMENT '海外承运商', overseas_tracking_no VARCHAR(128) NULL COMMENT '海外物流单号', estimated_arrival_at DATETIME(3) NULL COMMENT '预计到达时间', @@ -211,6 +211,8 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order ( KEY idx_ll_overseas_procurement_list(customer_id,status,created_at), KEY idx_ll_overseas_procurement_warehouse(customer_id,purchase_warehouse_id,created_at) ) ENGINE=InnoDB COMMENT='海外采购订单'; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_overseas_procurement_order' AND column_name='outbound_details'), 'SELECT 1', 'ALTER TABLE ll_overseas_procurement_order ADD COLUMN outbound_details JSON NULL COMMENT ''出库明细'' AFTER inbound_quantity')); +PREPARE procurement_outbound_stmt FROM @ddl; EXECUTE procurement_outbound_stmt; DEALLOCATE PREPARE procurement_outbound_stmt; SET @ddl = (SELECT IF(EXISTS ( SELECT 1 FROM information_schema.columns diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementRequests.java b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementRequests.java index d31cd0672..59581f373 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementRequests.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementRequests.java @@ -56,5 +56,15 @@ public final class OverseasProcurementRequests { JsonNode overseasCarrier, JsonNode overseasTrackingNo, JsonNode estimatedArrivalAt, - JsonNode remark) {} + JsonNode remark, + JsonNode outboundDetails) { + public Update(Long version, String purchaseCategory, String productName, JsonNode productSpecification, + JsonNode productUrl, Long inboundQuantity, JsonNode purchaseWarehouseId, JsonNode purchaseWarehouseName, + JsonNode domesticCarrier, JsonNode domesticTrackingNo, JsonNode overseasCarrier, JsonNode overseasTrackingNo, + JsonNode estimatedArrivalAt, JsonNode remark) { + this(version, purchaseCategory, productName, productSpecification, productUrl, inboundQuantity, + purchaseWarehouseId, purchaseWarehouseName, domesticCarrier, domesticTrackingNo, overseasCarrier, + overseasTrackingNo, estimatedArrivalAt, remark, null); + } + } } diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementResponses.java b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementResponses.java index d0f94de6b..638245829 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementResponses.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/dto/OverseasProcurementResponses.java @@ -7,6 +7,8 @@ import java.util.List; public final class OverseasProcurementResponses { private OverseasProcurementResponses() {} + public record OutboundDetail(String id, long quantity, String carrier, String trackingNo, LocalDateTime shippedAt, String remark) {} + public record Summary( Long id, String orderNo, String customerId, String customerName, String purchaseCategory, String productName, String productSpecification, @@ -32,5 +34,5 @@ public final class OverseasProcurementResponses { String overseasCarrier, String overseasTrackingNo, LocalDateTime estimatedArrivalAt, String remark, String createdBy, String reviewedBy, String reviewRemark, LocalDateTime reviewedAt, - List histories) {} + List histories, List outboundDetails) {} } diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/entity/OverseasProcurementOrder.java b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/entity/OverseasProcurementOrder.java index 0a0c52dd9..df14cd381 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/entity/OverseasProcurementOrder.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/entity/OverseasProcurementOrder.java @@ -30,6 +30,7 @@ public class OverseasProcurementOrder { @TableField(updateStrategy = FieldStrategy.ALWAYS) private String purchaseWarehouseId; @TableField(updateStrategy = FieldStrategy.ALWAYS) private String purchaseWarehouseName; private Long inboundQuantity; + @TableField(updateStrategy = FieldStrategy.ALWAYS) private String outboundDetails; @TableField(updateStrategy = FieldStrategy.ALWAYS) private String domesticCarrier; @TableField(updateStrategy = FieldStrategy.ALWAYS) private String domesticTrackingNo; @TableField(updateStrategy = FieldStrategy.ALWAYS) private String overseasCarrier; diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java index 44eafd56e..9ef5d938c 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java @@ -148,7 +148,7 @@ public class OverseasProcurementService { new OverseasProcurementResponses.History(value.getOperationType(), value.getFromStatus(), value.getToStatus(), value.getOperatorId(), value.getIdempotencyKey(), value.getChangeDetail(), value.getRemark(), - value.getCreatedAt())).toList()); + value.getCreatedAt())).toList(), outboundDetails(order)); } @Transactional @@ -205,6 +205,7 @@ public class OverseasProcurementService { patchText(request.overseasTrackingNo(), 128, "overseasTrackingNo", order::setOverseasTrackingNo); patchDateTime(request.estimatedArrivalAt(), order::setEstimatedArrivalAt); patchText(request.remark(), 1000, "remark", order::setRemark); + if (request.outboundDetails() != null) order.setOutboundDetails(normalizeOutbound(request.outboundDetails(), order.getInboundQuantity())); Map after = editableSnapshot(order); if (before.equals(after)) return detail(customerId, orderNo); LocalDateTime now = LocalDateTime.now(); @@ -230,10 +231,11 @@ public class OverseasProcurementService { private OverseasProcurementResponses.Summary summary(OverseasProcurementOrder order) { long inbound = value(order.getInboundQuantity()); + long outbound = outboundDetails(order).stream().mapToLong(OverseasProcurementResponses.OutboundDetail::quantity).sum(); return new OverseasProcurementResponses.Summary(order.getId(), order.getOrderNo(), order.getCustomerId(), order.getCustomerName(), order.getPurchaseCategory(), order.getProductName(), order.getProductSpecification(), value(order.getPurchaseQuantity()), order.getBudgetUnitPrice(), - order.getCurrency(), order.getPurchaseWarehouseId(), order.getPurchaseWarehouseName(), inbound, inbound, + order.getCurrency(), order.getPurchaseWarehouseId(), order.getPurchaseWarehouseName(), inbound, Math.max(0, inbound - outbound), order.getStatus(), value(order.getVersion()), order.getCreatedAt(), order.getUpdatedAt()); } @@ -336,9 +338,26 @@ public class OverseasProcurementService { result.put("overseasTrackingNo", order.getOverseasTrackingNo()); result.put("estimatedArrivalAt", order.getEstimatedArrivalAt()); result.put("remark", order.getRemark()); + result.put("outboundDetails", order.getOutboundDetails()); return result; } + private List outboundDetails(OverseasProcurementOrder order) { + if (!StringUtils.hasText(order.getOutboundDetails())) return List.of(); + try { return objectMapper.readValue(order.getOutboundDetails(), objectMapper.getTypeFactory().constructCollectionType(List.class, OverseasProcurementResponses.OutboundDetail.class)); } + catch (JsonProcessingException e) { return List.of(); } + } + private String normalizeOutbound(JsonNode node, Long inbound) { + if (!node.isArray()) throw badRequest("outboundDetails must be an array"); + long total = 0; + for (JsonNode item : node) { + if (!item.isObject() || item.path("quantity").asLong(-1) < 0) throw badRequest("outboundDetails quantity must be non-negative"); + total += item.path("quantity").asLong(); + } + if (total > value(inbound)) throw badRequest("outbound quantity cannot exceed inbound quantity"); + try { return objectMapper.writeValueAsString(node); } catch (JsonProcessingException e) { throw badRequest("Invalid outboundDetails"); } + } + private String changeDetail(Map before, Map after) { Map changes = new LinkedHashMap<>(); before.forEach((field, oldValue) -> { diff --git a/lastleg-service/src/main/resources/db/lastleg.sql b/lastleg-service/src/main/resources/db/lastleg.sql index 45c775f1e..2bd800dca 100644 --- a/lastleg-service/src/main/resources/db/lastleg.sql +++ b/lastleg-service/src/main/resources/db/lastleg.sql @@ -1556,7 +1556,7 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order ( purchase_quantity BIGINT NOT NULL COMMENT '采购数量', budget_unit_price DECIMAL(18,2) NULL COMMENT '预算单价', currency CHAR(3) NOT NULL DEFAULT 'USD' COMMENT '币种', receiving_address VARCHAR(1000) NULL COMMENT '收货地址', contact_name VARCHAR(128) NOT NULL COMMENT '联系人姓名', contact_phone VARCHAR(64) NOT NULL COMMENT '联系人电话', - purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称', + purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称', outbound_details JSON NULL COMMENT '出库明细', inbound_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '采购单据累计入库数量', domestic_carrier VARCHAR(128) NULL COMMENT '境内承运商', domestic_tracking_no VARCHAR(128) NULL COMMENT '境内物流单号', overseas_carrier VARCHAR(128) NULL COMMENT '海外承运商', overseas_tracking_no VARCHAR(128) NULL COMMENT '海外物流单号', diff --git a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml index 884f6ac5e..48cf108a2 100644 --- a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml @@ -1093,6 +1093,19 @@ components: overseasTrackingNo: { $ref: '#/components/schemas/NullableTextPatch' } estimatedArrivalAt: { type: string, format: date-time, nullable: true, description: 缺失不修改;null 或空串清空 } remark: { $ref: '#/components/schemas/NullableTextPatch' } + outboundDetails: + type: array + description: 出库明细,可一次提交多条;总出库量不得超过入库量 + items: + type: object + required: [quantity] + properties: + id: { type: string } + quantity: { type: integer, format: int64, minimum: 0 } + carrier: { type: string, nullable: true, maxLength: 128 } + trackingNo: { type: string, nullable: true, maxLength: 128 } + shippedAt: { type: string, format: date-time, nullable: true } + remark: { type: string, nullable: true, maxLength: 500 } CustomerOptionResponse: type: object properties: