diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/LoadingManageController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/LoadingManageController.java index 320c8b22..c3b1e790 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/LoadingManageController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/LoadingManageController.java @@ -56,7 +56,7 @@ public class LoadingManageController extends BladeController { @GetMapping("/carrier-contracts") @ApiOperationSupport(order = 13) - @Operation(summary = "可选承运商合同", description = "查询已审核生效的承运商合同;传入 waybillIds 时按子运单客户合同甲方匹配后对乙方取交集") + @Operation(summary = "可选承运商合同", description = "查询已审核生效的承运商合同;传入 waybillIds 时按子运单客户合同乙方=承运商合同甲方匹配后对乙方取交集") public R> carrierContracts( @RequestParam List projectIds, @RequestParam(required = false) List waybillIds) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ILoadingManageService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ILoadingManageService.java index f0998c45..6a751337 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ILoadingManageService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ILoadingManageService.java @@ -29,7 +29,7 @@ public interface ILoadingManageService extends BaseService { /** * 查询项目下已审核生效的承运商合同。 *

- * 传入 waybillIds 时,按各子运单客户合同甲方匹配可用承运商合同,再对乙方名称取交集后返回。 + * 传入 waybillIds 时,按各子运单客户合同乙方=承运商合同甲方匹配可用承运商合同,再对乙方名称取交集后返回。 */ List carrierContracts(List projectIds, List waybillIds); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index 34795cdb..09872fc0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -97,6 +97,7 @@ import org.springblade.transport.service.IPreSettlementService; import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService; import org.springblade.transport.service.IWaybillService; import org.springblade.transport.service.KingdeeSyncContext; +import org.springblade.transport.support.TransportBusinessSupport; import org.springblade.transport.support.KingdeeApBillSyncSupport; import org.springblade.transport.support.KingdeeArBillSyncSupport; import org.springblade.transport.support.KingdeeSyncLockSupport; @@ -1878,7 +1879,29 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl map) { - map.forEach((key, value) -> result.put(String.valueOf(key), decimal(value))); + BigDecimal otherFeeAmount = BigDecimal.ZERO; + for (Map.Entry entry : map.entrySet()) { + String key = String.valueOf(entry.getKey()); + Object value = entry.getValue(); + if (Func.isBlank(key)) { + continue; + } + if ("otherFreightAmount".equals(key) || "otherFeeTotal".equals(key) || "otherAmount".equals(key)) { + otherFeeAmount = otherFeeAmount.add(decimal(value)); + continue; + } + if (TransportBusinessSupport.isFreightJsonMetaKey(key)) { + continue; + } + if (value instanceof Map || value instanceof java.util.Collection) { + continue; + } + result.put(key, decimal(value)); + } + if (otherFeeAmount.signum() > 0 && result.keySet().stream() + .noneMatch(name -> name != null && (name.contains("其它费") || name.contains("其他费")))) { + result.put("其它费用", otherFeeAmount); + } } } catch (RuntimeException ignored) { // 兼容历史费用JSON,不影响正式结算保存。 diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java index 0777be99..5123407c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java @@ -907,7 +907,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl waybills) { if (Func.isEmpty(waybills) || waybills.size() < 2) { @@ -1008,14 +1008,14 @@ public class LoadingManageServiceImpl extends BaseServiceImpl intersectCarrierPartyBs(List waybills, List projectContracts) { Set intersection = null; - Map customerPartyAMap = loadCustomerContractPartyAMap(waybills); + Map customerPartyBMap = loadCustomerContractPartyBMap(waybills); for (Waybill waybill : waybills) { - Set available = availableCarrierPartyBs(waybill, projectContracts, customerPartyAMap); + Set available = availableCarrierPartyBs(waybill, projectContracts, customerPartyBMap); if (intersection == null) { intersection = new LinkedHashSet<>(available); } else { @@ -1029,21 +1029,22 @@ public class LoadingManageServiceImpl extends BaseServiceImpl availableCarrierPartyBs(Waybill waybill, List projectContracts, - Map customerPartyAMap) { + Map customerPartyBMap) { if (waybill == null || Func.isEmpty(projectContracts)) { return Set.of(); } - String partyA = customerPartyAMap.get(waybill.getContractId()); + // 客户合同乙方即我方,应等于承运商合同甲方 + String partyB = customerPartyBMap.get(waybill.getContractId()); return projectContracts.stream() .filter(contract -> Objects.equals(contract.getProjectId(), waybill.getProjectId())) - .filter(contract -> Func.isEmpty(partyA) || Objects.equals(normalizePartyName(contract.getPartyA()), partyA)) + .filter(contract -> Func.isEmpty(partyB) || Objects.equals(normalizePartyName(contract.getPartyA()), partyB)) .map(ContractManage::getPartyB) .map(this::normalizePartyName) .filter(Func::isNotEmpty) .collect(Collectors.toCollection(LinkedHashSet::new)); } - private Map loadCustomerContractPartyAMap(List waybills) { + private Map loadCustomerContractPartyBMap(List waybills) { List contractIds = waybills.stream() .map(Waybill::getContractId) .filter(Objects::nonNull) @@ -1057,7 +1058,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl normalizePartyName(contract.getPartyA()), + contract -> normalizePartyName(contract.getPartyB()), (left, right) -> left, LinkedHashMap::new)); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java index f9bdc3a9..7b4380ca 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java @@ -70,6 +70,7 @@ import org.springblade.transport.service.IPreSettlementService; import org.springblade.transport.service.IProjectApplyService; import org.springblade.transport.service.ISettlementProjectAdvanceWriteOffService; import org.springblade.transport.service.IWaybillService; +import org.springblade.transport.support.TransportBusinessSupport; import org.springblade.transport.wrapper.PreSettlementWrapper; import org.springblade.system.cache.UserCache; import org.springblade.system.feign.ISysClient; @@ -919,11 +920,12 @@ public class PreSettlementServiceImpl extends BaseServiceImpl item.getOriginalAmount() != null)) { - return money(source.getTotalAmount()); + return resolveSourceSettlementAmount(source, money(source.getTotalAmount())); } - return sourceFees.stream().map(ReceivablePayableCargoFee::getOriginalAmount) - .map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal total = BigDecimal.ZERO; + for (ReceivablePayableCargoFee sourceFee : sourceFees) { + total = total.add(resolveCargoFeeAmount(sourceFee, sourceFee.getOriginalAmount())); + } + return money(total); } private void copySourceFees(PreSettlementDetail detail, ReceivablePayableDetail source) { @@ -973,10 +978,11 @@ public class PreSettlementServiceImpl extends BaseServiceImpl !Integer.valueOf(1).equals(row.getManualFlag())) .collect(Collectors.toMap(this::summaryKey, Function.identity(), (left, right) -> left)); Map aggregates = new LinkedHashMap<>(); - List detailIds = listDetails(settlementId).stream().map(PreSettlementDetail::getId).toList(); + List details = listDetails(settlementId); + List detailIds = details.stream().map(PreSettlementDetail::getId).toList(); List feeRows = detailIds.isEmpty() ? List.of() : detailFeeMapper.selectList(Wrappers.lambdaQuery() .in(PreSettlementDetailFee::getPreSettlementDetailId, detailIds) .eq(PreSettlementDetailFee::getIsDeleted, 0) .orderByAsc(PreSettlementDetailFee::getCreateTime)); + Set repairedDetailIds = new LinkedHashSet<>(); for (PreSettlementDetailFee feeRow : feeRows) { + boolean polluted = isPollutedFeeItemsJson(feeRow.getFeeItemsJson()); Map feeItems = parseFeeItems(feeRow.getFeeItemsJson()); + if (polluted) { + repairDetailFeeAmounts(feeRow, feeItems); + if (feeRow.getPreSettlementDetailId() != null) { + repairedDetailIds.add(feeRow.getPreSettlementDetailId()); + } + } boolean containsFreight = feeItems.keySet().stream().anyMatch(this::isFreightFeeItem); BigDecimal componentAmount = feeItems.values().stream().map(this::money) .reduce(BigDecimal.ZERO, BigDecimal::add); @@ -1035,8 +1052,8 @@ public class PreSettlementServiceImpl extends BaseServiceImpl repairedDetailIds.contains(detail.getId())) + .forEach(this::refreshDetail); + } } private void applySummaryRequest(Long settlementId, List requestRows) { @@ -1684,7 +1706,29 @@ public class PreSettlementServiceImpl extends BaseServiceImpl map) { - map.forEach((key, value) -> result.put(String.valueOf(key), decimal(value))); + BigDecimal otherFeeAmount = BigDecimal.ZERO; + for (Map.Entry entry : map.entrySet()) { + String key = String.valueOf(entry.getKey()); + Object value = entry.getValue(); + if (Func.isBlank(key)) { + continue; + } + if ("otherFreightAmount".equals(key) || "otherFeeTotal".equals(key) || "otherAmount".equals(key)) { + otherFeeAmount = otherFeeAmount.add(decimal(value)); + continue; + } + if (TransportBusinessSupport.isFreightJsonMetaKey(key)) { + continue; + } + if (value instanceof Map || value instanceof java.util.Collection) { + continue; + } + result.put(key, decimal(value)); + } + if (otherFeeAmount.signum() > 0 && result.keySet().stream() + .noneMatch(name -> name != null && (name.contains("其它费") || name.contains("其他费")))) { + result.put("其它费用", otherFeeAmount); + } } } catch (Exception ignored) { // 历史脏数据不影响结算单展示。 @@ -1692,6 +1736,74 @@ public class PreSettlementServiceImpl extends BaseServiceImpl map)) { + return false; + } + return map.keySet().stream() + .map(String::valueOf) + .anyMatch(TransportBusinessSupport::isFreightJsonMetaKey); + } catch (Exception ignored) { + return false; + } + } + + private String sanitizeFeeItemsJson(String json) { + Map feeItems = parseFeeItems(json); + return JsonUtil.toJson(feeItems); + } + + /** + * 来源应付明细金额:若 fee_items_json 混入 freightJson 结构字段,按清洗后费用项重算,避免转预结算翻倍。 + */ + private BigDecimal resolveSourceSettlementAmount(ReceivablePayableDetail source, BigDecimal fallback) { + if (source == null) { + return money(fallback); + } + if (!isPollutedFeeItemsJson(source.getFeeItemsJson())) { + return money(fallback); + } + Map feeItems = parseFeeItems(source.getFeeItemsJson()); + boolean containsFreight = feeItems.keySet().stream().anyMatch(this::isFreightFeeItem); + BigDecimal feeItemTotal = feeItems.values().stream().map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return money(containsFreight ? feeItemTotal : money(source.getFreightAmount()).add(feeItemTotal)); + } + + private BigDecimal resolveCargoFeeAmount(ReceivablePayableCargoFee sourceFee, BigDecimal fallback) { + if (sourceFee == null) { + return money(fallback); + } + if (!isPollutedFeeItemsJson(sourceFee.getFeeItemsJson())) { + return money(fallback); + } + Map feeItems = parseFeeItems(sourceFee.getFeeItemsJson()); + boolean containsFreight = feeItems.keySet().stream().anyMatch(this::isFreightFeeItem); + BigDecimal feeItemTotal = feeItems.values().stream().map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return money(containsFreight ? feeItemTotal : money(sourceFee.getFreightAmount()).add(feeItemTotal)); + } + + /** 重建合计时顺带修复已落库的脏费用行金额与 fee_items_json。 */ + private void repairDetailFeeAmounts(PreSettlementDetailFee feeRow, Map sanitizedFeeItems) { + boolean containsFreight = sanitizedFeeItems.keySet().stream().anyMatch(this::isFreightFeeItem); + BigDecimal feeItemTotal = sanitizedFeeItems.values().stream().map(this::money) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal corrected = money(containsFreight + ? feeItemTotal + : money(feeRow.getFreightAmount()).add(feeItemTotal)); + feeRow.setFeeItemsJson(JsonUtil.toJson(sanitizedFeeItems)); + feeRow.setOriginalAmount(corrected); + feeRow.setSettlementAmountTax(corrected); + feeRow.setAdjustAmount(BigDecimal.ZERO.setScale(2)); + detailFeeMapper.updateById(feeRow); + } + private Map normalizeFeeItems(Map feeItems) { Map result = new LinkedHashMap<>(); if (feeItems == null) return result; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java index c8427238..d1ff04d0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java @@ -417,11 +417,13 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl { if (currentDeptId != null) { - wrapper.apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", String.valueOf(currentDeptId)); + wrapper.eq(ProjectApply::getBusinessDeptId, currentDeptId) + .or().apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", String.valueOf(currentDeptId)); } wrapper.or().eq(ProjectApply::getCreateUser, currentUserId) .or().eq(ProjectApply::getHandlerUserId, currentUserId) @@ -1091,7 +1093,8 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl { if (currentDeptId != null) { - wrapper.apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", String.valueOf(currentDeptId)); + wrapper.eq(ProjectChange::getBusinessDeptId, currentDeptId) + .or().apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", String.valueOf(currentDeptId)); } wrapper.or().eq(ProjectChange::getCreateUser, currentUserId) .or().eq(ProjectChange::getHandlerUserId, currentUserId) @@ -502,7 +504,13 @@ public class ProjectChangeServiceImpl extends BaseServiceImpllambdaQuery() .eq(ContractManage::getIsDeleted, 0) .eq(ContractManage::getProjectId, waybill.getProjectId()) - .eq(ContractManage::getPartyA, customerContract.getPartyA()) + .eq(ContractManage::getPartyA, customerContract.getPartyB()) .eq(ContractManage::getPartyB, carrierName) .eq(ContractManage::getContractCategory, "承运商合同") .and(wrapper -> wrapper.isNull(ContractManage::getContractStage) @@ -2005,12 +2008,15 @@ public class ReceivablePayableDetailServiceImpl private ReceivablePayableCargoFee buildCargoFee(Long detailId, Waybill waybill) { BigDecimal quantity = money(waybill.getQuantity()); BigDecimal unitPrice = money(waybill.getUnitPrice()); - BigDecimal freightAmount = quantity.multiply(unitPrice).setScale(2, RoundingMode.HALF_UP); - BigDecimal otherFeeAmount = money(waybill.getOtherFeeTotal()); - Map feeItems = parseMap(waybill.getFreightJson()); - if (feeItems.isEmpty() && otherFeeAmount.compareTo(BigDecimal.ZERO) > 0) { - feeItems.put("其它费用", otherFeeAmount); + // 优先取运单 freightJson 中的运费;没有再回退数量×单价,避免与费用项重复累加 + BigDecimal freightAmount = waybillFreight(waybill); + if (freightAmount == null) { + freightAmount = quantity.multiply(unitPrice).setScale(2, RoundingMode.HALF_UP); + } else { + freightAmount = money(freightAmount); } + // freightJson 是币种/运费合计等结构体,不能整包当作费用项目 Map + Map feeItems = resolveWaybillFeeItems(waybill); BigDecimal feeItemTotal = feeItems.values().stream().map(this::decimal).reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal total = freightAmount.add(feeItemTotal).setScale(2, RoundingMode.HALF_UP); ReceivablePayableCargoFee cargoFee = new ReceivablePayableCargoFee(); @@ -2513,7 +2519,7 @@ public class ReceivablePayableDetailServiceImpl if (Func.isEmpty(vo.getDataSource())) { vo.setDataSource(isManualFee(row) ? FEE_SOURCE_MANUAL : FEE_SOURCE_AUTO); } - Map feeItems = parseMap(row.getFeeItemsJson()); + Map feeItems = sanitizeFeeItems(parseMap(row.getFeeItemsJson())); feeItemNames.addAll(feeItems.keySet()); vo.setFeeItems(feeItems); vo.setTransportQuantityText(formatQuantity(row.getTransportQuantity(), row.getQuantityUnit())); @@ -2530,6 +2536,52 @@ public class ReceivablePayableDetailServiceImpl return vo; } + /** + * 修复误把运单 freightJson 结构字段写入 fee_items_json 的历史费用行, + * 并按「运输费字段 + 清洗后费用项」重算结算金额,消除翻倍。 + */ + private void repairPollutedFreightFeeItems(ReceivablePayableDetail detail, List rows) { + if (detail == null || rows == null || rows.isEmpty()) { + return; + } + boolean repaired = false; + for (ReceivablePayableCargoFee row : rows) { + Map rawFeeItems = parseMap(row.getFeeItemsJson()); + if (!isPollutedFreightFeeItems(rawFeeItems)) { + continue; + } + Map sanitized = sanitizeFeeItems(rawFeeItems); + Map feeItemAmounts = new LinkedHashMap<>(); + sanitized.forEach((name, value) -> feeItemAmounts.put(name, decimal(value))); + BigDecimal afterAmount = adjustedAfterAmount(money(row.getFreightAmount()), feeItemAmounts); + row.setFeeItemsJson(JsonUtil.toJson(sanitized)); + row.setOriginalAmount(afterAmount); + row.setAfterAmount(afterAmount); + row.setAdjustAmount(BigDecimal.ZERO); + cargoFeeMapper.updateById(row); + repaired = true; + } + if (!repaired) { + return; + } + BigDecimal freight = rows.stream().map(fee -> money(fee.getFreightAmount())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal total = rows.stream().map(fee -> money(fee.getAfterAmount())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + detail.setFreightAmount(freight); + detail.setOtherFeeAmount(total.subtract(freight).setScale(2, RoundingMode.HALF_UP)); + detail.setTotalAmount(total); + detail.setFeeItemsJson(aggregateFeeItemsJson(rows)); + updateById(detail); + } + + private boolean isPollutedFreightFeeItems(Map feeItems) { + if (feeItems == null || feeItems.isEmpty()) { + return false; + } + return feeItems.keySet().stream().anyMatch(TransportBusinessSupport::isFreightJsonMetaKey); + } + private List contractFeeItemNames(Long contractId) { if (contractId == null) return List.of(); ContractManage contract = contractManageService.getById(contractId); @@ -3000,7 +3052,7 @@ public class ReceivablePayableDetailServiceImpl private String aggregateFeeItemsJson(List fees) { Map feeItems = new LinkedHashMap<>(); - fees.forEach(fee -> parseMap(fee.getFeeItemsJson()).forEach((name, value) -> + fees.forEach(fee -> sanitizeFeeItems(parseMap(fee.getFeeItemsJson())).forEach((name, value) -> feeItems.merge(name, decimal(value), BigDecimal::add))); return JsonUtil.toJson(feeItems); } @@ -3456,6 +3508,74 @@ public class ReceivablePayableDetailServiceImpl return code; } + /** + * 从运单 freightJson / otherFeeTotal 解析费用项目(名称→金额)。 + * 仅保留真正的费用项;结构字段(currency、totalFreightAmount 等)不会写入。 + */ + private Map resolveWaybillFeeItems(Waybill waybill) { + Map feeItems = new LinkedHashMap<>(); + Map freight = waybillFreightMap(waybill); + freight.forEach((key, value) -> { + if (Func.isBlank(key) || TransportBusinessSupport.isFreightJsonMetaKey(key)) { + return; + } + if (value instanceof Map || value instanceof Collection) { + return; + } + // 兼容历史「费用名→金额」扁平 JSON(键名通常含「费」) + if (!key.contains("费")) { + return; + } + feeItems.put(key, decimal(value)); + }); + BigDecimal otherFeeAmount = money(waybillOtherFee(waybill)); + if (otherFeeAmount.compareTo(BigDecimal.ZERO) > 0 && !containsOtherFeeItem(feeItems.keySet())) { + feeItems.put("其它费用", otherFeeAmount); + } + return feeItems; + } + + /** + * 清洗已落库的 fee_items_json:过滤误写入的 freightJson 结构字段, + * 并将 otherFreightAmount / otherFeeTotal 归一为「其它费用」。 + */ + private Map sanitizeFeeItems(Map raw) { + Map feeItems = new LinkedHashMap<>(); + if (raw == null || raw.isEmpty()) { + return feeItems; + } + BigDecimal otherFeeAmount = BigDecimal.ZERO; + for (Map.Entry entry : raw.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (Func.isBlank(key)) { + continue; + } + if ("otherFreightAmount".equals(key) || "otherFeeTotal".equals(key) || "otherAmount".equals(key)) { + otherFeeAmount = otherFeeAmount.add(decimal(value)); + continue; + } + if (TransportBusinessSupport.isFreightJsonMetaKey(key)) { + continue; + } + if (value instanceof Map || value instanceof Collection) { + continue; + } + feeItems.put(key, decimal(value)); + } + if (otherFeeAmount.compareTo(BigDecimal.ZERO) > 0 && !containsOtherFeeItem(feeItems.keySet())) { + feeItems.put("其它费用", otherFeeAmount); + } + return feeItems; + } + + private boolean containsOtherFeeItem(Collection names) { + if (names == null || names.isEmpty()) { + return false; + } + return names.stream().anyMatch(name -> name != null && (name.contains("其它费") || name.contains("其他费"))); + } + private Map parseMap(String json) { if (Func.isEmpty(json)) { return new LinkedHashMap<>(); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java index 3a88d7ac..0a5dd9e1 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java @@ -37,6 +37,7 @@ import java.time.LocalDate; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -87,9 +88,24 @@ public final class TransportBusinessSupport { /** 临时额度管理菜单编码 */ public static final String MENU_TEMPORARY_CREDIT_LIMIT = "temporary_credit_limit"; + /** + * 运单 freightJson 结构字段,不是费用项目名。 + * 误写入 fee_items_json / 预结算合计时需过滤,否则会出现 totalFreightAmount 等英文费用项并导致金额翻倍。 + */ + public static final Set FREIGHT_JSON_META_KEYS = Set.of( + "currency", "totalFreightAmount", "otherFreightAmount", "freightItems", + "freightTotal", "totalFreight", "totalAmount", "freight", "freightAmount", + "transportFee", "otherFeeTotal", "otherAmount" + ); + private TransportBusinessSupport() { } + /** 是否运单运费 JSON 的结构字段名(不可作为费用项展示)。 */ + public static boolean isFreightJsonMetaKey(String key) { + return Func.isNotBlank(key) && FREIGHT_JSON_META_KEYS.contains(key); + } + /** * 是否超级管理员:当前用户任一角色别名等于 administrator 即为超级管理员。 *