Merge remote-tracking branch 'origin/test' into dev
# Conflicts: # lastleg-service/src/main/java/com/baiyue/lastleg/relabel/service/RelabelApplicationService.java # lastleg-service/src/test/java/com/baiyue/lastleg/relabel/service/RelabelApplicationServiceTest.java
This commit is contained in:
+5
-3
@@ -10,7 +10,7 @@ import com.baiyue.lastleg.dto.LastlegOrderSummary;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderCommandDtos;
|
||||
import com.baiyue.lastleg.dto.ContainerPlanProgressDetail;
|
||||
import com.baiyue.lastleg.dto.ContainerPlanProgressSummary;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderFee;
|
||||
import com.baiyue.lastleg.financefee.entity.OverseasFeeSource;
|
||||
import com.baiyue.lastleg.security.InternalServiceTokenGuard;
|
||||
import com.baiyue.lastleg.security.OverseasPermissions;
|
||||
import com.baiyue.lastleg.service.LastlegOrderService;
|
||||
@@ -132,12 +132,14 @@ public class LastlegOrderController {
|
||||
@PostMapping("/api/platform/v1/last-leg/orders/{id}/fees")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.LAST_MILE_ORDER_EDIT + "')")
|
||||
@Operation(summary = "Add a fee to one tail-leg order", description = "Required authority: " + OverseasPermissions.LAST_MILE_ORDER_EDIT)
|
||||
public ApiResponse<LastlegOrderFee> createFee(
|
||||
public ApiResponse<OverseasFeeSource> createFee(
|
||||
@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@PathVariable long id,
|
||||
@RequestHeader("Idempotency-Key") String idempotencyKey,
|
||||
@Valid @RequestBody LastlegOrderRequests.CreateFee request) {
|
||||
return ApiResponse.success(orderService.createFee(platformScope(user, customerId), id, request, operatorName(user)));
|
||||
return ApiResponse.success(orderService.createFee(platformScope(user, customerId), id, request,
|
||||
operatorName(user), idempotencyKey));
|
||||
}
|
||||
|
||||
@PutMapping("/api/platform/v1/last-leg/orders/{id}/hold")
|
||||
|
||||
+18
-2
@@ -11,6 +11,7 @@ import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public final class DeliveryRequests {
|
||||
private DeliveryRequests() {}
|
||||
@@ -45,8 +46,23 @@ public final class DeliveryRequests {
|
||||
@NotNull @Size(min = 1, max = 500) List<@NotBlank @Size(max = 128) String> cartonMarks) {}
|
||||
|
||||
public record FeeItem(@NotBlank @Size(max = 64) String feeType,
|
||||
@NotBlank @Size(max = 8) String currency,
|
||||
@NotNull @Min(0) BigDecimal amount,
|
||||
@NotBlank @Size(max = 128) String feeName,
|
||||
@NotBlank @jakarta.validation.constraints.Pattern(regexp = "RECEIVABLE|PAYABLE") String direction,
|
||||
@NotBlank @jakarta.validation.constraints.Pattern(regexp = "CNY|USD") String currency,
|
||||
@NotNull @jakarta.validation.constraints.DecimalMin(value = "0.00", inclusive = false) BigDecimal amount,
|
||||
@Min(1) Long supplierId,
|
||||
@Size(max = 200) String supplierName,
|
||||
@Size(max = 32) String supplierCategory,
|
||||
@jakarta.validation.constraints.Pattern(regexp = "ON_ACCOUNT|DIRECT_PAID") String settlementMode,
|
||||
LocalDateTime paidAt,
|
||||
@jakarta.validation.constraints.Pattern(regexp = "ONLINE|BANK_TRANSFER|CASH|OTHER") String paymentMethod,
|
||||
@Size(max = 128) String paymentReferenceNo,
|
||||
@Size(max = 20) List<@NotBlank String> paymentVoucherFileIds,
|
||||
@jakarta.validation.constraints.DecimalMin(value = "0.00", inclusive = false) BigDecimal chargeableQuantity,
|
||||
@Size(max = 32) String quantityUnit,
|
||||
@jakarta.validation.constraints.DecimalMin(value = "0.00", inclusive = false) BigDecimal unitPrice,
|
||||
LocalDate periodStart,
|
||||
LocalDate periodEnd,
|
||||
@Size(max = 500) String remark) {}
|
||||
|
||||
public record Query(@Min(1) long page, @Min(1) @Max(200) long size,
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.baiyue.lastleg.delivery.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ll_delivery_fee")
|
||||
public class DeliveryFee {
|
||||
@TableId(type = IdType.ASSIGN_ID) private Long id;
|
||||
private String customerId;
|
||||
private Long deliveryOrderId;
|
||||
private String feeDirection;
|
||||
private String feeType;
|
||||
private String currency;
|
||||
private BigDecimal amount;
|
||||
private String remark;
|
||||
private String createdBy;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.baiyue.lastleg.delivery.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryFee;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DeliveryFeeMapper extends BaseMapper<DeliveryFee> {
|
||||
@Select("SELECT * FROM ll_delivery_fee WHERE customer_id=#{customerId} AND delivery_order_id=#{deliveryOrderId} ORDER BY id")
|
||||
List<DeliveryFee> findByDelivery(@Param("customerId") String customerId,
|
||||
@Param("deliveryOrderId") Long deliveryOrderId);
|
||||
}
|
||||
+31
-10
@@ -7,13 +7,14 @@ import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.lastleg.delivery.dto.DeliveryRequests;
|
||||
import com.baiyue.lastleg.delivery.dto.DeliveryResponses;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryFee;
|
||||
import com.baiyue.lastleg.financefee.entity.OverseasFeeSource;
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos;
|
||||
import com.baiyue.lastleg.financefee.service.OverseasFeeSourceService;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryOperation;
|
||||
import com.baiyue.lastleg.delivery.entity.OutboundOrder;
|
||||
import com.baiyue.lastleg.delivery.entity.OutboundPalletItem;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliveryOrderMapper;
|
||||
import com.baiyue.lastleg.delivery.model.DeliveryStatusPolicy;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliveryFeeMapper;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliveryOperationMapper;
|
||||
import com.baiyue.lastleg.delivery.mapper.OutboundOrderMapper;
|
||||
import com.baiyue.lastleg.delivery.mapper.OutboundPalletItemMapper;
|
||||
@@ -59,6 +60,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import com.baiyue.lastleg.service.LastlegOrderNoGenerator;
|
||||
import java.math.BigDecimal;
|
||||
@@ -71,7 +73,7 @@ public class DeliveryTransferService {
|
||||
@Resource private OutboundOrderMapper outboundOrders;
|
||||
@Resource private OutboundPalletItemMapper outboundPallets;
|
||||
@Resource private DeliveryOrderMapper deliveryOrders;
|
||||
@Resource private DeliveryFeeMapper deliveryFees;
|
||||
@Resource private OverseasFeeSourceService feeSources;
|
||||
@Resource private DeliveryOperationMapper deliveryOperations;
|
||||
@Resource private OutboundPdaTaskMapper pdaTasks;
|
||||
@Resource private OutboundPdaPickMapper pdaPicks;
|
||||
@@ -198,9 +200,18 @@ public class DeliveryTransferService {
|
||||
delivery.setCreatedAt(now); delivery.setUpdatedAt(now); deliveryOrders.insert(delivery);
|
||||
List<DeliveryRequests.FeeItem> feeInputs = request.fees() == null ? List.of() : request.fees();
|
||||
for (DeliveryRequests.FeeItem input : feeInputs) {
|
||||
DeliveryFee fee = new DeliveryFee(); fee.setCustomerId(customerId); fee.setDeliveryOrderId(delivery.getId());
|
||||
fee.setFeeDirection("PAYABLE"); fee.setFeeType(input.feeType().trim()); fee.setCurrency(input.currency().trim().toUpperCase(Locale.ROOT));
|
||||
fee.setAmount(input.amount()); fee.setRemark(trim(input.remark())); fee.setCreatedBy(operatorId); fee.setCreatedAt(now); deliveryFees.insert(fee);
|
||||
feeSources.publishApproved(operatorId, childKey(request.idempotencyKey(), input.feeType()),
|
||||
new OverseasFeeSourceDtos.SaveRequest(customerId, delivery.getCustomerName(),
|
||||
"OVERSEAS_DELIVERY_FEE", "DELIVERY_ORDER", delivery.getId(), delivery.getDeliveryNo(),
|
||||
firstOrder == null ? null : firstOrder.getId(), null,
|
||||
firstOrder == null ? null : firstOrder.getInboundCode(), null,
|
||||
firstOrder == null ? null : parseLong(firstOrder.getSourceContainerBookingId()),
|
||||
firstOrder == null ? null : firstOrder.getContainerNo(), parseLong(delivery.getOverseasWarehouseId()),
|
||||
input.feeType().trim(), input.feeName().trim(), input.direction(), input.supplierId(),
|
||||
input.supplierName(), input.supplierCategory(), input.settlementMode(), input.paidAt(),
|
||||
input.paymentMethod(), input.paymentReferenceNo(), input.paymentVoucherFileIds(), LocalDate.now(),
|
||||
input.currency(), input.amount(), input.chargeableQuantity(), input.quantityUnit(), input.unitPrice(),
|
||||
input.periodStart(), input.periodEnd()));
|
||||
}
|
||||
recordOperation(customerId, delivery.getId(), operatorId, "CREATE_DELIVERY", "CREATED", now);
|
||||
bindTailOrders(customerId, items, outbound.getOutboundNo(), delivery.getDeliveryNo(), request.remark(), now);
|
||||
@@ -216,8 +227,9 @@ public class DeliveryTransferService {
|
||||
String feeType = input.feeType().trim().toUpperCase(Locale.ROOT);
|
||||
if (!allowed.contains(feeType)) throw conflict("Unsupported delivery fee type: " + feeType);
|
||||
if (!seen.add(feeType)) throw conflict("Duplicate delivery fee type: " + feeType);
|
||||
if (!"USD".equalsIgnoreCase(input.currency().trim())) throw conflict("Delivery fees must use USD");
|
||||
if (input.amount().signum() < 0) throw conflict("Delivery fee amount cannot be negative");
|
||||
if (!Set.of("CNY", "USD").contains(input.currency().trim().toUpperCase(Locale.ROOT)))
|
||||
throw conflict("Delivery fees only support CNY or USD");
|
||||
if (input.amount().signum() <= 0) throw conflict("Delivery fee amount must be greater than zero");
|
||||
}
|
||||
if (!seen.contains("DELIVERY_FEE")) throw conflict("Delivery fee is required");
|
||||
if (Set.of("FLOOR", "MIXED").contains(request.loadingType().trim().toUpperCase(Locale.ROOT))
|
||||
@@ -647,7 +659,7 @@ public class DeliveryTransferService {
|
||||
new LambdaQueryWrapper<LastlegOrderDetail>().eq(LastlegOrderDetail::getCustomerId, customerId).in(LastlegOrderDetail::getOrderId, orderIds));
|
||||
List<DeliveryResponses.DeliverySku> skus = details.stream().map(detail -> sku(detail, ordersById.get(detail.getOrderId()))).toList();
|
||||
return new DeliveryResponses.DeliveryDetail(deliveryRow(delivery), skus,
|
||||
deliveryFees.findByDelivery(customerId, delivery.getId()).stream().map(this::feeRow).toList(),
|
||||
feeSources.listForBusiness(customerId, "DELIVERY_ORDER", delivery.getId()).stream().map(this::feeRow).toList(),
|
||||
deliveryOperations.findByDelivery(customerId, delivery.getId()).stream().map(this::operationRow).toList());
|
||||
}
|
||||
|
||||
@@ -704,7 +716,7 @@ public class DeliveryTransferService {
|
||||
}
|
||||
private DeliveryResponses.DeliveryRow deliveryRow(DeliveryOrder d) { return new DeliveryResponses.DeliveryRow(d.getId(), d.getCustomerId(), d.getCustomerName(), d.getDeliveryNo(), d.getOutboundNo(), d.getDispatchNo(), d.getOverseasWarehouseId(), d.getOverseasWarehouseName(), d.getStatus(), d.getWaybillNo(), d.getDeliveryMethod(), d.getCarrierName(), d.getPickupMethod(), d.getLoadingType(), d.getVehicleType(), d.getVehicleSize(), d.getVehiclePlate(), d.getDriverName(), d.getDriverPhone(), d.getAppointmentAccount(), d.getSellerName(), value(d.getPalletCount()), value(d.getPackageCount()), d.getTotalWeightKg(), d.getTotalVolumeCbm(), d.getPlannedDeliveryAt(), d.getDispatchedAt(), d.getExpectedArrivalAt(), d.getSignedAt(), d.getRemark(), d.getCreatedAt(), value(d.getVersion()), DeliveryStatusPolicy.allowedActions(d.getStatus()), d.getBolNo(), com.baiyue.lastleg.delivery.model.DeliveryDocumentSnapshot.summary(d.getDocumentsJson())); }
|
||||
private DeliveryResponses.DeliverySku sku(LastlegOrderDetail d, LastlegOrder order) { return new DeliveryResponses.DeliverySku(order == null ? null : order.getInboundCode(), order == null ? null : order.getCustomerName(), d.getDestinationWarehouseCode(), null, d.getShipmentNo(), d.getFbaTrackingNo(), d.getSkuCode(), firstText(d.getProductNameZh(), d.getProductNameEn()), value(d.getPackageCount()), d.getGrossWeightKg(), d.getVolumeCbm()); }
|
||||
private DeliveryResponses.DeliveryFeeRow feeRow(DeliveryFee f) { return new DeliveryResponses.DeliveryFeeRow(f.getId(), f.getFeeDirection(), f.getFeeType(), f.getCurrency(), f.getAmount(), f.getRemark()); }
|
||||
private DeliveryResponses.DeliveryFeeRow feeRow(OverseasFeeSource f) { return new DeliveryResponses.DeliveryFeeRow(f.getId(), f.getDirection(), f.getFeeItemCode(), f.getOriginalCurrency(), f.getOriginalAmount(), f.getReviewReason()); }
|
||||
private DeliveryResponses.DeliveryOperationRow operationRow(DeliveryOperation o) { return new DeliveryResponses.DeliveryOperationRow(o.getId(), o.getCreatedAt(), o.getOperationNode(), firstText(o.getOperatorName(), o.getOperatorId()), o.getResult()); }
|
||||
private void recordOperation(String customerId, Long deliveryId, String operatorId, String node, String result, LocalDateTime now) { DeliveryOperation operation = new DeliveryOperation(); operation.setCustomerId(customerId); operation.setDeliveryOrderId(deliveryId); operation.setOperationNode(node); operation.setOperatorId(operatorId); operation.setOperatorName(operatorId); operation.setResult(result); operation.setCreatedAt(now); deliveryOperations.insert(operation); }
|
||||
private LastlegOrder firstTailOrder(String customerId, List<DispatchItem> items) { return items.stream().map(DispatchItem::getOrderId).filter(Objects::nonNull).map(tailOrders::selectById).filter(Objects::nonNull).filter(order -> customerId.equals(order.getCustomerId())).findFirst().orElse(null); }
|
||||
@@ -720,6 +732,15 @@ public class DeliveryTransferService {
|
||||
}
|
||||
private BigDecimal totalWeight(String customerId, List<DispatchItem> items) { List<Long> ids = items.stream().map(DispatchItem::getOrderId).filter(Objects::nonNull).distinct().toList(); if (ids.isEmpty()) return BigDecimal.ZERO; return tailOrderDetails.selectList(new LambdaQueryWrapper<LastlegOrderDetail>().eq(LastlegOrderDetail::getCustomerId, customerId).in(LastlegOrderDetail::getOrderId, ids)).stream().map(LastlegOrderDetail::getGrossWeightKg).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); }
|
||||
private static String trim(String value) { return StringUtils.hasText(value) ? value.trim() : null; }
|
||||
private static Long parseLong(String value) {
|
||||
try { return StringUtils.hasText(value) ? Long.parseLong(value.trim()) : null; }
|
||||
catch (NumberFormatException ignored) { return null; }
|
||||
}
|
||||
private static String childKey(String key, String suffix) {
|
||||
String prefix = key == null ? "" : key.trim();
|
||||
int maxPrefix = Math.max(0, 128 - suffix.length() - 1);
|
||||
return prefix.substring(0, Math.min(prefix.length(), maxPrefix)) + ':' + suffix;
|
||||
}
|
||||
private static String required(String value, String message) {
|
||||
String normalized = trim(value);
|
||||
if (normalized == null) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, message);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.baiyue.lastleg.dto;
|
||||
|
||||
import com.baiyue.lastleg.entity.LastlegOrderDetail;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderFee;
|
||||
import com.baiyue.lastleg.financefee.entity.OverseasFeeSource;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -12,7 +12,7 @@ public record LastlegOrderDetailResponse(
|
||||
List<LastlegOrderCommandDtos.ManualNodeView> manualNodes,
|
||||
List<LastlegOrderDetail> cargo,
|
||||
List<History> statusHistory,
|
||||
List<LastlegOrderFee> fees,
|
||||
List<OverseasFeeSource> fees,
|
||||
List<Operation> operationHistory
|
||||
) {
|
||||
public record History(Long id, String fromStatus, String toStatus, String remark, LocalDateTime createdAt) {}
|
||||
|
||||
@@ -11,6 +11,7 @@ import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public final class LastlegOrderRequests {
|
||||
@@ -81,11 +82,25 @@ public final class LastlegOrderRequests {
|
||||
}
|
||||
|
||||
public record CreateFee(
|
||||
@NotBlank @Size(max = 128) String feeItem,
|
||||
@NotBlank @Pattern(regexp = "[A-Z]{3}") String currency,
|
||||
@NotBlank @Size(max = 64) String feeItemCode,
|
||||
@NotBlank @Size(max = 200) String feeItemName,
|
||||
@NotBlank @Pattern(regexp = "RECEIVABLE|PAYABLE") String direction,
|
||||
@NotBlank @Pattern(regexp = "CNY|USD") String currency,
|
||||
@NotNull @DecimalMin(value = "0.01") BigDecimal amount,
|
||||
@NotBlank @Pattern(regexp = "PENDING_REVIEW|APPROVED|REJECTED") String status,
|
||||
@Size(max = 500) String supplierDescription
|
||||
@NotNull LocalDate businessDate,
|
||||
@Min(1) Long supplierId,
|
||||
@Size(max = 200) String supplierName,
|
||||
@Size(max = 32) String supplierCategory,
|
||||
@Pattern(regexp = "ON_ACCOUNT|DIRECT_PAID") String settlementMode,
|
||||
LocalDateTime paidAt,
|
||||
@Pattern(regexp = "ONLINE|BANK_TRANSFER|CASH|OTHER") String paymentMethod,
|
||||
@Size(max = 128) String paymentReferenceNo,
|
||||
@Size(max = 20) List<@NotBlank String> paymentVoucherFileIds,
|
||||
@DecimalMin(value = "0.00", inclusive = false) BigDecimal chargeableQuantity,
|
||||
@Size(max = 32) String quantityUnit,
|
||||
@DecimalMin(value = "0.00", inclusive = false) BigDecimal unitPrice,
|
||||
LocalDate periodStart,
|
||||
LocalDate periodEnd
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.baiyue.lastleg.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ll_order_fee")
|
||||
public class LastlegOrderFee {
|
||||
@TableId(type = IdType.INPUT) private Long id;
|
||||
private String customerId;
|
||||
private Long orderId;
|
||||
private String feeItem;
|
||||
private String currency;
|
||||
private BigDecimal amount;
|
||||
private String status;
|
||||
private String supplierDescription;
|
||||
private String createdBy;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
+2
-2
@@ -20,8 +20,8 @@ public final class OverseasFeeSourceDtos {
|
||||
public record SaveRequest(
|
||||
@NotBlank @Size(max = 128) String customerId,
|
||||
@Size(max = 200) String customerName,
|
||||
@NotBlank @Pattern(regexp = "OVERSEAS_INBOUND_FEE|OVERSEAS_PROCUREMENT_FEE|OVERSEAS_TRUCK_FEE|OVERSEAS_LOOSEBOARD_FEE|OVERSEAS_WAREHOUSE_FEE|OVERSEAS_CONTAINER_FEE") String sourceType,
|
||||
@NotBlank @Pattern(regexp = "OVERSEAS_INBOUND|OVERSEAS_PROCUREMENT|US_TRUCK|LOCAL_LOOSEBOARD|WAREHOUSE_SERVICE|OVERSEAS_CONTAINER") String businessType,
|
||||
@NotBlank @Pattern(regexp = "OVERSEAS_INBOUND_FEE|OVERSEAS_PROCUREMENT_FEE|OVERSEAS_TRUCK_FEE|OVERSEAS_LOOSEBOARD_FEE|OVERSEAS_WAREHOUSE_FEE|OVERSEAS_CONTAINER_FEE|OVERSEAS_ORDER_FEE|OVERSEAS_DELIVERY_FEE|OVERSEAS_RELABEL_FEE") String sourceType,
|
||||
@NotBlank @Pattern(regexp = "OVERSEAS_INBOUND|OVERSEAS_PROCUREMENT|PROCUREMENT_FEE|US_TRUCK|LOCAL_LOOSEBOARD|WAREHOUSE_SERVICE|OVERSEAS_CONTAINER|LASTLEG_ORDER|DELIVERY_ORDER|RELABEL_ORDER") String businessType,
|
||||
@Min(1) Long businessId,
|
||||
@NotBlank @Size(max = 128) String businessNo,
|
||||
@Min(1) Long orderId,
|
||||
|
||||
+26
@@ -2,7 +2,11 @@ package com.baiyue.lastleg.financefee.service;
|
||||
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.SaveRequest;
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.ContainerCustomerOption;
|
||||
import com.baiyue.lastleg.client.DomesticWarehouseDirectoryClient;
|
||||
import feign.FeignException;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -14,6 +18,8 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public class OverseasFeeBusinessGuard {
|
||||
private final JdbcTemplate jdbc;
|
||||
@Resource private DomesticWarehouseDirectoryClient warehouses;
|
||||
@Value("${baiyue.security.internal-service-token}") private String internalToken;
|
||||
|
||||
public List<ContainerCustomerOption> listContainerCustomers(long containerBookingId) {
|
||||
List<ContainerCustomerOption> customers = jdbc.query(
|
||||
@@ -37,13 +43,18 @@ public class OverseasFeeBusinessGuard {
|
||||
throw bad("业务记录ID不能为空");
|
||||
if (fee.warehouseId() == null)
|
||||
throw bad("海外仓服务费必须关联海外仓;入仓记录和入仓码按实际业务选填");
|
||||
requireActiveOverseasWarehouse(fee.warehouseId());
|
||||
return;
|
||||
}
|
||||
Table table = switch (fee.businessType()) {
|
||||
case "OVERSEAS_INBOUND" -> new Table("ll_inbound_plan", "plan_no");
|
||||
case "OVERSEAS_PROCUREMENT" -> new Table("ll_overseas_procurement_order", "order_no");
|
||||
case "PROCUREMENT_FEE" -> new Table("ll_overseas_procurement_fee", "fee_no");
|
||||
case "US_TRUCK" -> new Table("ll_us_truck_order", "order_no");
|
||||
case "LOCAL_LOOSEBOARD" -> new Table("ll_local_looseboard_order", "order_no");
|
||||
case "LASTLEG_ORDER" -> new Table("ll_order", "order_no");
|
||||
case "DELIVERY_ORDER" -> new Table("ll_delivery_order", "delivery_no");
|
||||
case "RELABEL_ORDER" -> new Table("ll_relabel_application", "relabel_no");
|
||||
default -> throw bad("业务类型与业务记录ID不匹配");
|
||||
};
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM " + table.name
|
||||
@@ -83,6 +94,21 @@ public class OverseasFeeBusinessGuard {
|
||||
if (owned == null || owned < 1) throw bad("该客户不属于费用关联的真实货柜");
|
||||
}
|
||||
|
||||
private void requireActiveOverseasWarehouse(long warehouseId) {
|
||||
try {
|
||||
var response = warehouses.activeWarehouse(warehouseId, internalToken);
|
||||
var warehouse = response == null || response.data() == null ? null : response.data().warehouse();
|
||||
if (warehouse == null || !response.successful() || !Long.valueOf(warehouseId).equals(warehouse.id())
|
||||
|| !Long.valueOf(0L).equals(warehouse.tenantId()) || !"ACTIVE".equals(warehouse.status())
|
||||
|| !List.of("OVERSEAS", "OVERSEAS_PARTNER").contains(warehouse.warehouseType())) {
|
||||
throw bad("海外仓不存在、已停用或类型不正确");
|
||||
}
|
||||
} catch (FeignException exception) {
|
||||
if (exception.status() >= 400 && exception.status() < 500) throw bad("海外仓不存在、已停用或类型不正确");
|
||||
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "仓库主数据暂不可用", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean blank(String value) { return value == null || value.isBlank(); }
|
||||
private static ResponseStatusException bad(String message) {
|
||||
return new ResponseStatusException(HttpStatus.BAD_REQUEST, message);
|
||||
|
||||
+35
@@ -53,6 +53,15 @@ public class OverseasFeeSourceService {
|
||||
.orderByDesc(OverseasFeeSource::getCreatedAt).orderByDesc(OverseasFeeSource::getId));
|
||||
}
|
||||
|
||||
public List<OverseasFeeSource> listForBusiness(String customerId, String businessType, long businessId) {
|
||||
return mapper.selectList(new LambdaQueryWrapper<OverseasFeeSource>()
|
||||
.eq(OverseasFeeSource::getCustomerId, customerId)
|
||||
.eq(OverseasFeeSource::getBusinessType, businessType)
|
||||
.eq(OverseasFeeSource::getBusinessId, businessId)
|
||||
.inSql(OverseasFeeSource::getId, "SELECT MAX(v.id) FROM ll_finance_fee_source v GROUP BY v.source_fee_id")
|
||||
.orderByAsc(OverseasFeeSource::getCreatedAt).orderByAsc(OverseasFeeSource::getId));
|
||||
}
|
||||
|
||||
public List<ContainerCustomerOption> listContainerCustomers(long containerBookingId) {
|
||||
return businessGuard.listContainerCustomers(containerBookingId);
|
||||
}
|
||||
@@ -70,6 +79,22 @@ public class OverseasFeeSourceService {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OverseasFeeSource publishApproved(String operatorId, String key, SaveRequest request) {
|
||||
OverseasFeeSource value = create(operatorId, key, request);
|
||||
if ("DRAFT".equals(value.getReviewStatus()) || "REJECTED".equals(value.getReviewStatus())) {
|
||||
value = submit(value.getSourceFeeId(), operatorId, childKey(key, "submit"));
|
||||
}
|
||||
if ("PENDING".equals(value.getReviewStatus())) {
|
||||
value = review(value.getSourceFeeId(), operatorId,
|
||||
new ReviewRequest("APPROVE", "业务完成自动确认费用", childKey(key, "approve")));
|
||||
}
|
||||
if (!"APPROVED".equals(value.getReviewStatus()) || !"PUBLISHED".equals(value.getPublicationStatus())) {
|
||||
throw conflict("业务费用未能审核发布");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OverseasFeeSource submit(long sourceFeeId, String operatorId, String key) {
|
||||
OverseasFeeCommand replay = replay(operatorId, "SUBMIT", key, sourceFeeId);
|
||||
@@ -168,10 +193,14 @@ public class OverseasFeeSourceService {
|
||||
String expected = switch (value.businessType()) {
|
||||
case "OVERSEAS_INBOUND" -> "OVERSEAS_INBOUND_FEE";
|
||||
case "OVERSEAS_PROCUREMENT" -> "OVERSEAS_PROCUREMENT_FEE";
|
||||
case "PROCUREMENT_FEE" -> "OVERSEAS_PROCUREMENT_FEE";
|
||||
case "US_TRUCK" -> "OVERSEAS_TRUCK_FEE";
|
||||
case "LOCAL_LOOSEBOARD" -> "OVERSEAS_LOOSEBOARD_FEE";
|
||||
case "WAREHOUSE_SERVICE" -> "OVERSEAS_WAREHOUSE_FEE";
|
||||
case "OVERSEAS_CONTAINER" -> "OVERSEAS_CONTAINER_FEE";
|
||||
case "LASTLEG_ORDER" -> "OVERSEAS_ORDER_FEE";
|
||||
case "DELIVERY_ORDER" -> "OVERSEAS_DELIVERY_FEE";
|
||||
case "RELABEL_ORDER" -> "OVERSEAS_RELABEL_FEE";
|
||||
default -> "";
|
||||
};
|
||||
if (!expected.equals(value.sourceType())) throw bad("来源类型与业务类型不匹配");
|
||||
@@ -212,6 +241,12 @@ public class OverseasFeeSourceService {
|
||||
if (existing != null && !existing.getRequestHash().equals(hash(request))) throw conflict("幂等键已被不同请求使用");
|
||||
return existing;
|
||||
}
|
||||
|
||||
private static String childKey(String key, String suffix) {
|
||||
String prefix = key == null ? "" : key.trim();
|
||||
int maxPrefix = Math.max(0, 128 - suffix.length() - 1);
|
||||
return prefix.substring(0, Math.min(prefix.length(), maxPrefix)) + ':' + suffix;
|
||||
}
|
||||
private void command(String operator, String action, String key, Object request, long sourceFeeId, int version) {
|
||||
OverseasFeeCommand value = new OverseasFeeCommand(); value.setId(IdWorker.getId()); value.setOperatorId(operator);
|
||||
value.setAction(action); value.setIdempotencyKey(key.trim()); value.setRequestHash(hash(request)); value.setSourceFeeId(sourceFeeId);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.baiyue.lastleg.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderFee;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface LastlegOrderFeeMapper extends BaseMapper<LastlegOrderFee> {
|
||||
}
|
||||
+21
@@ -16,6 +16,8 @@ import com.baiyue.lastleg.procurement.mapper.OverseasProcurementFeeMapper;
|
||||
import com.baiyue.lastleg.procurement.gateway.ProcurementFileGateway;
|
||||
import com.baiyue.lastleg.wallet.WalletCommandOutboxService;
|
||||
import com.baiyue.lastleg.wallet.WalletResultHandler;
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos;
|
||||
import com.baiyue.lastleg.financefee.service.OverseasFeeSourceService;
|
||||
import com.baiyue.common.event.WalletCommandCompletedV1;
|
||||
import com.baiyue.lastleg.procurement.mapper.OverseasProcurementOrderMapper;
|
||||
import com.baiyue.lastleg.procurement.mapper.OverseasProcurementStatusHistoryMapper;
|
||||
@@ -33,7 +35,9 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -62,6 +66,7 @@ public class OverseasProcurementService implements WalletResultHandler {
|
||||
@Resource private OverseasProcurementFeeMapper procurementFees;
|
||||
@Resource private ProcurementFileGateway fileGateway;
|
||||
@Resource private WalletCommandOutboxService walletCommands;
|
||||
@Resource private OverseasFeeSourceService feeSources;
|
||||
|
||||
@Transactional
|
||||
public OverseasProcurementResponses.Detail create(String customerId, String operatorId,
|
||||
@@ -220,6 +225,7 @@ public class OverseasProcurementService implements WalletResultHandler {
|
||||
if ("FREEZE".equals(result.operation()) && "SUCCEEDED".equals(result.result())) {
|
||||
OverseasProcurementOrder order = orders.selectById(row.getOrderId());
|
||||
if (order != null && order.getInboundQuantity() >= order.getPurchaseQuantity()) {
|
||||
publishFeeSource(order, row);
|
||||
walletCommands.publish("SETTLE", result.customerId(), "PROCUREMENT_FEE", row.getFeeNo(), null,
|
||||
"海外采购完成入库,结算费用", 2L);
|
||||
row.setWalletStatus("SETTLE_PENDING"); procurementFees.updateById(row);
|
||||
@@ -313,6 +319,7 @@ public class OverseasProcurementService implements WalletResultHandler {
|
||||
if (order.getInboundQuantity() >= order.getPurchaseQuantity()) {
|
||||
for (OverseasProcurementFee fee : procurementFees.selectList(new LambdaQueryWrapper<OverseasProcurementFee>()
|
||||
.eq(OverseasProcurementFee::getOrderId, order.getId()).eq(OverseasProcurementFee::getWalletStatus, "FROZEN"))) {
|
||||
publishFeeSource(order, fee);
|
||||
walletCommands.publish("SETTLE", Long.parseLong(customerId), "PROCUREMENT_FEE", fee.getFeeNo(), null,
|
||||
"海外采购完成入库,结算费用", 2L);
|
||||
fee.setWalletStatus("SETTLE_PENDING"); fee.setUpdatedAt(now); procurementFees.updateById(fee);
|
||||
@@ -321,6 +328,16 @@ public class OverseasProcurementService implements WalletResultHandler {
|
||||
return detail(customerId, orderNo);
|
||||
}
|
||||
|
||||
private void publishFeeSource(OverseasProcurementOrder order, OverseasProcurementFee fee) {
|
||||
feeSources.publishApproved(StringUtils.hasText(fee.getCreatedBy()) ? fee.getCreatedBy() : "SYSTEM", "PROCUREMENT:" + fee.getFeeNo(),
|
||||
new OverseasFeeSourceDtos.SaveRequest(order.getCustomerId(), order.getCustomerName(),
|
||||
"OVERSEAS_PROCUREMENT_FEE", "PROCUREMENT_FEE", fee.getId(), fee.getFeeNo(),
|
||||
order.getId(), null, null, null, null, null, parseLong(order.getPurchaseWarehouseId()),
|
||||
fee.getFeeItemCode(), fee.getFeeItemName(), "RECEIVABLE", null, null, null, null,
|
||||
null, null, null, List.of(), LocalDate.now(), fee.getCurrency(), fee.getAmount(),
|
||||
BigDecimal.ONE, "ITEM", fee.getAmount(), null, null));
|
||||
}
|
||||
|
||||
private OverseasProcurementResponses.Detail replay(String customerId, OverseasProcurementOrder order, String hash) {
|
||||
if (!hash.equals(order.getRequestHash())) {
|
||||
throw conflict("Idempotency key was reused with different procurement data");
|
||||
@@ -491,6 +508,10 @@ public class OverseasProcurementService implements WalletResultHandler {
|
||||
}
|
||||
|
||||
private static long value(Long value) { return value == null ? 0 : value; }
|
||||
private static Long parseLong(String value) {
|
||||
try { return StringUtils.hasText(value) ? Long.parseLong(value.trim()) : null; }
|
||||
catch (NumberFormatException ignored) { return null; }
|
||||
}
|
||||
private static String trim(String value) { return value == null || value.isBlank() ? null : value.trim(); }
|
||||
private static String defaultCurrency(String value) { return trim(value) == null ? "USD" : value.trim().toUpperCase(); }
|
||||
private static void setIfText(String value, java.util.function.Consumer<String> setter) {
|
||||
|
||||
+22
-3
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos;
|
||||
import com.baiyue.lastleg.financefee.service.OverseasFeeSourceService;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
|
||||
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandRequests;
|
||||
@@ -36,9 +38,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HexFormat;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -66,6 +70,7 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
@Resource private InventoryBalanceMapper inventoryBalances;
|
||||
@Resource private InventoryPositionMapper inventoryPositions;
|
||||
@Resource private RelabelDocumentFileGateway documentFiles;
|
||||
@Resource private OverseasFeeSourceService feeSources;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public String customerId(String relabelNo) { return require(null, relabelNo).getCustomerId(); }
|
||||
@@ -159,7 +164,6 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
balance.getProductNameZh(), available, value(balance.getVersion()), positions);
|
||||
}).filter(candidate -> candidate.availablePackages() > 0).toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public RelabelResponses.Detail create(String customerId, String operatorId, RelabelRequests.Create request) {
|
||||
if (!StringUtils.hasText(request.operationWarehouseId())) throw badRequest("Operation warehouse is required");
|
||||
@@ -313,8 +317,21 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
application.setStatus(next);
|
||||
application.setUpdatedAt(now);
|
||||
applications.updateById(application);
|
||||
List<LastlegOrder> linkedOrders = tailOrders(application);
|
||||
if (linkedOrders.isEmpty()) throw conflict("Linked tail-leg order is missing");
|
||||
if ("COMPLETED".equals(next)) {
|
||||
releaseInventory(customerId, operatorId, application);
|
||||
BigDecimal quantity = BigDecimal.valueOf(Objects.requireNonNullElse(application.getTotalPackageCount(), 0));
|
||||
feeSources.publishApproved(operatorId, "RELABEL:" + relabelNo,
|
||||
new OverseasFeeSourceDtos.SaveRequest(customerId, linkedOrders.get(0).getCustomerName(),
|
||||
"OVERSEAS_RELABEL_FEE", "RELABEL_ORDER", application.getId(), relabelNo,
|
||||
application.getLastlegOrderId(), null, null, null, null, null,
|
||||
parseLong(application.getOverseasWarehouseId()), "RELABEL_SERVICE", "换标费", "RECEIVABLE",
|
||||
null, null, null, null, null, null, null, List.of(), LocalDate.now(),
|
||||
application.getChargeCurrency(), application.getChargeAmount(),
|
||||
quantity.signum() > 0 ? quantity : null, quantity.signum() > 0 ? "BOX" : null,
|
||||
quantity.signum() > 0 ? application.getChargeAmount().divide(quantity, 6, java.math.RoundingMode.HALF_UP) : null,
|
||||
null, null));
|
||||
walletCommands.publish("SETTLE", Long.parseLong(customerId), "RELABEL_ORDER", relabelNo, null,
|
||||
"换标完成,结算费用", 2L);
|
||||
application.setWalletStatus("SETTLE_PENDING"); applications.updateById(application);
|
||||
@@ -326,8 +343,6 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
application.setWalletStatus("RELEASE_PENDING"); applications.updateById(application);
|
||||
}
|
||||
}
|
||||
List<LastlegOrder> linkedOrders = tailOrders(application);
|
||||
if (linkedOrders.isEmpty()) throw conflict("Linked tail-leg order is missing");
|
||||
for (LastlegOrder order : linkedOrders) {
|
||||
String orderBefore = order.getCurrentStatus();
|
||||
String mainStatus = STATUSES.contains(orderBefore) ? "PENDING_ASSIGNMENT" : orderBefore;
|
||||
@@ -556,6 +571,10 @@ public class RelabelApplicationService implements WalletResultHandler {
|
||||
.digest(objectMapper.copy().findAndRegisterModules().writeValueAsString(value).getBytes(StandardCharsets.UTF_8))); }
|
||||
catch (Exception exception) { throw new IllegalStateException(exception); }
|
||||
}
|
||||
private static Long parseLong(String value) {
|
||||
try { return StringUtils.hasText(value) ? Long.parseLong(value.trim()) : null; }
|
||||
catch (NumberFormatException ignored) { return null; }
|
||||
}
|
||||
private static String trim(String value) { return StringUtils.hasText(value) ? value.trim() : null; }
|
||||
private static ResponseStatusException badRequest(String message) { return new ResponseStatusException(HttpStatus.BAD_REQUEST, message); }
|
||||
private static ResponseStatusException conflict(String message) { return new ResponseStatusException(HttpStatus.CONFLICT, message); }
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.baiyue.lastleg.dto.ContainerPlanProgressSummary;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderDetail;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderFee;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderOperationLog;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderManualNode;
|
||||
import com.baiyue.lastleg.entity.ReceivingWarehouseOperation;
|
||||
@@ -25,7 +24,9 @@ import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderDetailMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderFeeMapper;
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos;
|
||||
import com.baiyue.lastleg.financefee.entity.OverseasFeeSource;
|
||||
import com.baiyue.lastleg.financefee.service.OverseasFeeSourceService;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderOperationLogMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderManualNodeMapper;
|
||||
import com.baiyue.lastleg.mapper.ReceivingWarehouseOperationMapper;
|
||||
@@ -64,7 +65,7 @@ public class LastlegOrderService {
|
||||
@Resource private LastlegOrderMapper orderMapper;
|
||||
@Resource private LastlegOrderDetailMapper detailMapper;
|
||||
@Resource private LastlegOrderStatusHistoryMapper historyMapper;
|
||||
@Resource private LastlegOrderFeeMapper feeMapper;
|
||||
@Resource private OverseasFeeSourceService feeSources;
|
||||
@Resource private LastlegOrderOperationLogMapper operationLogMapper;
|
||||
@Resource private LastlegOrderManualNodeMapper manualNodeMapper;
|
||||
@Resource private InboundPlanCartonMapper cartonMapper;
|
||||
@@ -246,9 +247,7 @@ public class LastlegOrderService {
|
||||
.eq(LastlegOrderStatusHistory::getCustomerId, order.getCustomerId())
|
||||
.eq(LastlegOrderStatusHistory::getOrderId, orderId)
|
||||
.orderByAsc(LastlegOrderStatusHistory::getCreatedAt));
|
||||
List<LastlegOrderFee> fees = feeMapper.selectList(new LambdaQueryWrapper<LastlegOrderFee>()
|
||||
.eq(LastlegOrderFee::getCustomerId, order.getCustomerId())
|
||||
.eq(LastlegOrderFee::getOrderId, orderId).orderByAsc(LastlegOrderFee::getCreatedAt));
|
||||
List<OverseasFeeSource> fees = feeSources.listForBusiness(order.getCustomerId(), "LASTLEG_ORDER", orderId);
|
||||
List<LastlegOrderOperationLog> operations = operationLogMapper.selectList(
|
||||
new LambdaQueryWrapper<LastlegOrderOperationLog>()
|
||||
.eq(LastlegOrderOperationLog::getCustomerId, order.getCustomerId())
|
||||
@@ -270,24 +269,21 @@ public class LastlegOrderService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public LastlegOrderFee createFee(String customerId, long orderId, LastlegOrderRequests.CreateFee request,
|
||||
String operatorName) {
|
||||
public OverseasFeeSource createFee(String customerId, long orderId, LastlegOrderRequests.CreateFee request,
|
||||
String operatorName, String idempotencyKey) {
|
||||
LastlegOrder order = findOwned(customerId, orderId);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LastlegOrderFee fee = new LastlegOrderFee();
|
||||
fee.setId(IdWorker.getId());
|
||||
fee.setCustomerId(order.getCustomerId());
|
||||
fee.setOrderId(orderId);
|
||||
fee.setFeeItem(request.feeItem().trim());
|
||||
fee.setCurrency(request.currency());
|
||||
fee.setAmount(request.amount());
|
||||
fee.setStatus(request.status());
|
||||
fee.setSupplierDescription(StringUtils.hasText(request.supplierDescription())
|
||||
? request.supplierDescription().trim() : null);
|
||||
fee.setCreatedBy(operatorName);
|
||||
fee.setCreatedAt(now);
|
||||
fee.setUpdatedAt(now);
|
||||
feeMapper.insert(fee);
|
||||
OverseasFeeSource fee = feeSources.create(operatorName, idempotencyKey,
|
||||
new OverseasFeeSourceDtos.SaveRequest(order.getCustomerId(), order.getCustomerName(),
|
||||
"OVERSEAS_ORDER_FEE", "LASTLEG_ORDER", orderId, order.getOrderNo(), orderId,
|
||||
null, order.getInboundCode(), null, parseLong(order.getSourceContainerBookingId()),
|
||||
order.getContainerNo(), parseLong(order.getOverseasWarehouseId()), request.feeItemCode(),
|
||||
request.feeItemName(), request.direction(), request.supplierId(), request.supplierName(),
|
||||
request.supplierCategory(), request.settlementMode(), request.paidAt(), request.paymentMethod(),
|
||||
request.paymentReferenceNo(), request.paymentVoucherFileIds(), request.businessDate(),
|
||||
request.currency(), request.amount(), request.chargeableQuantity(), request.quantityUnit(),
|
||||
request.unitPrice(), request.periodStart(), request.periodEnd()));
|
||||
fee = feeSources.submit(fee.getSourceFeeId(), operatorName, childKey(idempotencyKey, "submit"));
|
||||
|
||||
LastlegOrderOperationLog operation = new LastlegOrderOperationLog();
|
||||
operation.setId(IdWorker.getId());
|
||||
@@ -295,7 +291,8 @@ public class LastlegOrderService {
|
||||
operation.setOrderId(orderId);
|
||||
operation.setOperationNode("新增费用");
|
||||
operation.setOperatorName(operatorName);
|
||||
operation.setResult("新增" + fee.getFeeItem() + ":" + fee.getCurrency() + " " + fee.getAmount().toPlainString());
|
||||
operation.setResult("新增" + fee.getFeeItemName() + ":" + fee.getOriginalCurrency() + " "
|
||||
+ fee.getOriginalAmount().toPlainString() + ",已提交财务审核");
|
||||
operation.setCreatedAt(now);
|
||||
operationLogMapper.insert(operation);
|
||||
return fee;
|
||||
@@ -585,6 +582,17 @@ public class LastlegOrderService {
|
||||
? null : order.getInboundCode();
|
||||
}
|
||||
|
||||
private static Long parseLong(String value) {
|
||||
try { return StringUtils.hasText(value) ? Long.parseLong(value.trim()) : null; }
|
||||
catch (NumberFormatException ignored) { return null; }
|
||||
}
|
||||
|
||||
private static String childKey(String key, String suffix) {
|
||||
String prefix = key == null ? "" : key.trim();
|
||||
int maxPrefix = Math.max(0, 128 - suffix.length() - 1);
|
||||
return prefix.substring(0, Math.min(prefix.length(), maxPrefix)) + ':' + suffix;
|
||||
}
|
||||
|
||||
private static String csv(Object value) {
|
||||
if (value == null) return "";
|
||||
String text = String.valueOf(value);
|
||||
|
||||
Reference in New Issue
Block a user