Merge remote-tracking branch 'origin/test' into dev

# Conflicts:
#	deploy/sql/头程数据库统一增量-MySQL8.sql
#	event-bridge-service/src/main/java/com/baiyue/eventbridge/metrics/EventBridgeMetrics.java
#	firstleg/src/main/resources/db/phase3-container-customs-schema.sql
#	order/src/main/resources/db/order.sql
#	scripts/db/incremental.sql
This commit is contained in:
2026-09-02 17:56:20 +08:00
45 changed files with 1005 additions and 1063 deletions
@@ -51,6 +51,7 @@ public class PlatformContainerFeeDetailController {
public ApiResponse<List<ContainerFeeDetailEntity>> createAllocationGroup(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long loadingPlanId,
@Valid @RequestBody AllocationGroupRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.createAllocationGroup(loadingPlanId, user.userId(), request));
}
@@ -59,6 +60,7 @@ public class PlatformContainerFeeDetailController {
public ApiResponse<List<ContainerFeeDetailEntity>> approveAllocationGroup(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long loadingPlanId,
@PathVariable long groupId) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.approveAllocationGroup(loadingPlanId, groupId, user.userId(), user.username()));
}
@@ -67,6 +69,7 @@ public class PlatformContainerFeeDetailController {
public ApiResponse<List<ContainerFeeDetailEntity>> rejectAllocationGroup(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long loadingPlanId,
@PathVariable long groupId, @Valid @RequestBody RejectRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.rejectAllocationGroup(loadingPlanId, groupId, user.userId(), user.username(), request.reason()));
}
@@ -75,6 +78,7 @@ public class PlatformContainerFeeDetailController {
public ApiResponse<List<ContainerFeeDetailEntity>> adjustAllocationGroup(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long loadingPlanId,
@PathVariable long groupId, @Valid @RequestBody AllocationGroupAdjustRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.adjustAllocationGroup(loadingPlanId, groupId, user.userId(), request));
}
@@ -83,6 +87,7 @@ public class PlatformContainerFeeDetailController {
public ApiResponse<Void> voidAllocationGroup(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable long loadingPlanId,
@PathVariable long groupId, @Valid @RequestBody VersionRequest request) {
FirstlegScopeGuard.platform(user);
service.voidAllocationGroup(loadingPlanId, groupId, request.expectedSourceVersion(), user.userId());
return ApiResponse.success(null);
}
@@ -47,8 +47,10 @@ public final class ContainerFeeDetailDtos {
public record Allocation(
@NotNull @Min(1) Long customerId,
@NotNull @Min(1) Long inboundId,
@NotBlank @Size(max = 64) String inboundCode,
@NotNull @Min(1) Long orderId,
@NotBlank @Size(max = 64) String orderNo,
@Min(1) Long inboundId,
@Size(max = 64) String inboundCode,
@NotNull @DecimalMin(value = "0.00", inclusive = false) BigDecimal amount
) { }
@@ -22,6 +22,8 @@ public class ContainerFeeDetailEntity {
private Long id;
private Long tenantId;
private Long allocationGroupId;
private Long allocationOrderId;
private String allocationOrderNo;
private Long allocationInboundId;
private String allocationInboundCode;
private BigDecimal allocationGroupAmount;
@@ -24,6 +24,8 @@ public class ContainerFeeSourceSnapshotEntity {
private Integer sourceVersion;
private Long tenantId;
private Long allocationGroupId;
private Long allocationOrderId;
private String allocationOrderNo;
private Long allocationInboundId;
private String allocationInboundCode;
private BigDecimal allocationGroupAmount;
@@ -20,6 +20,8 @@ public class ContainerFeeSourceSnapshotRepository {
value.setSourceVersion(source.getSourceVersion());
value.setTenantId(source.getTenantId());
value.setAllocationGroupId(source.getAllocationGroupId());
value.setAllocationOrderId(source.getAllocationOrderId());
value.setAllocationOrderNo(source.getAllocationOrderNo());
value.setAllocationInboundId(source.getAllocationInboundId());
value.setAllocationInboundCode(source.getAllocationInboundCode());
value.setAllocationGroupAmount(source.getAllocationGroupAmount());
@@ -52,12 +52,15 @@ public class ContainerFeeDetailService {
PlanContext plan = requireOwnedPlan(tenantId, loadingPlanId);
List<InboundAllocationKey> owned = loading.items(loadingPlanId).stream()
.filter(item -> Long.valueOf(tenantId).equals(item.getAccountId()))
.map(item -> new InboundAllocationKey(item.getInboundId(), item.getInboundCode()))
.map(item -> new InboundAllocationKey(item.getOrderId(), text(item.getOrderNo()),
item.getInboundId(), text(item.getInboundCode())))
.distinct().toList();
if (owned.size() != 1) throw conflict("柜次包含多个入仓码,必须使用柜级费用分摊录入");
if (owned.size() != 1) throw conflict("柜次包含多个业务归属,必须使用柜级费用分摊录入");
FeeItemEntity item = requireItem(request.feeItemId(), request.direction());
ContainerFeeDetailEntity value = new ContainerFeeDetailEntity();
value.setTenantId(tenantId);
value.setAllocationOrderId(owned.get(0).orderId());
value.setAllocationOrderNo(owned.get(0).orderNo());
value.setAllocationInboundId(owned.get(0).inboundId());
value.setAllocationInboundCode(owned.get(0).inboundCode());
value.setAllocationGroupAmount(request.amount().setScale(2, RoundingMode.HALF_UP));
@@ -86,19 +89,25 @@ public class ContainerFeeDetailService {
if (allocated.compareTo(total) != 0) throw conflict("柜级费用分摊合计必须等于费用总额");
Map<InboundAllocationKey, Long> ownership = new HashMap<>();
loading.items(loadingPlanId).forEach(value -> ownership.put(
new InboundAllocationKey(value.getInboundId(), value.getInboundCode()), value.getAccountId()));
new InboundAllocationKey(value.getOrderId(), text(value.getOrderNo()), value.getInboundId(),
text(value.getInboundCode())), value.getAccountId()));
Set<InboundAllocationKey> unique = new HashSet<>();
long groupId = IdWorker.getId();
List<ContainerFeeDetailEntity> rows = new java.util.ArrayList<>();
for (Allocation allocation : request.allocations()) {
InboundAllocationKey key = new InboundAllocationKey(allocation.inboundId(), allocation.inboundCode().trim());
if (!unique.add(key)) throw conflict("同一入仓码不得重复分摊");
String inboundCode = text(allocation.inboundCode());
if ((allocation.inboundId() == null) != (inboundCode == null))
throw new BizException(ResultCode.VALIDATION_FAILED, "入仓ID与入仓码必须同时填写或同时留空");
InboundAllocationKey key = new InboundAllocationKey(allocation.orderId(), allocation.orderNo().trim(),
allocation.inboundId(), inboundCode);
if (!unique.add(key)) throw conflict("同一业务归属不得重复分摊");
if (!allocation.customerId().equals(ownership.get(key)))
throw conflict("分摊客户、入仓码与柜内真实归属不一致");
throw conflict("分摊客户、订单及入仓信息与柜内真实归属不一致");
ContainerFeeDetailEntity value = new ContainerFeeDetailEntity();
value.setId(IdWorker.getId()); value.setTenantId(allocation.customerId());
value.setAllocationGroupId(groupId); value.setAllocationInboundId(allocation.inboundId());
value.setAllocationInboundCode(allocation.inboundCode().trim()); value.setAllocationGroupAmount(total);
value.setAllocationGroupId(groupId); value.setAllocationOrderId(allocation.orderId());
value.setAllocationOrderNo(allocation.orderNo().trim()); value.setAllocationInboundId(allocation.inboundId());
value.setAllocationInboundCode(inboundCode); value.setAllocationGroupAmount(total);
value.setLoadingPlanId(loadingPlanId); value.setContainerBookingId(plan.plan().getContainerBookingId());
value.setContainerNo(plan.booking() == null ? null : plan.booking().getContainerNo());
value.setSourceVersion(1); value.setSourceStatus("ACTIVE"); value.setReviewStatus("PENDING");
@@ -359,5 +368,7 @@ public class ContainerFeeDetailService {
private record PlanContext(LoadingPlanEntity plan, ContainerBookingEntity booking) {
}
private record InboundAllocationKey(Long inboundId, String inboundCode) { }
private static String text(String value) { return value == null || value.isBlank() ? null : value.trim(); }
private record InboundAllocationKey(Long orderId, String orderNo, Long inboundId, String inboundCode) { }
}
@@ -70,12 +70,13 @@ public class FinanceFeeSourceSnapshotService {
nullSafe(detail.getSettlementMode()), nullSafe(detail.getPaidAt()),
nullSafe(detail.getPaymentMethod()), nullSafe(detail.getPaymentReferenceNo()),
nullSafe(detail.getPaymentVoucherFileIds()),
nullSafe(detail.getAllocationGroupId()), nullSafe(detail.getAllocationInboundId()),
nullSafe(detail.getAllocationGroupId()), nullSafe(detail.getAllocationOrderId()),
nullSafe(detail.getAllocationOrderNo()), nullSafe(detail.getAllocationInboundId()),
nullSafe(detail.getAllocationInboundCode()), nullSafe(detail.getAllocationGroupAmount()),
detail.getAmount().toPlainString(), nullSafe(detail.getCurrency()),
nullSafe(detail.getReviewStatus()));
return new FinanceFeeSourceSnapshot(detail.getTenantId(), "CONTAINER_FEE", feeId,
version, detail.getAllocationInboundId(), detail.getAllocationInboundCode(), null, containerId, detail.getContainerBookingId(),
version, detail.getAllocationInboundId(), detail.getAllocationInboundCode(), detail.getAllocationOrderId(), containerId, detail.getContainerBookingId(),
detail.getContainerNo(), detail.getTenantId(), null, detail.getSupplierId(),
detail.getSupplierName(), detail.getSupplierCategory(), detail.getSettlementMode(),
detail.getPaidAt(), detail.getPaymentMethod(), detail.getPaymentReferenceNo(),