From da9efc422e926296ef73598beee8ac477eb9c3ca Mon Sep 17 00:00:00 2001 From: liuminghui <468701464@qq.com> Date: Tue, 10 Nov 2026 15:21:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E5=8D=95=E8=B4=B9=E7=94=A8=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/price/SurchargeController.java | 3 +- .../price/airbill/domain/SrvBillAirFee.java | 17 +- .../com/bms/price/domain/PayPriceCompare.java | 6 + .../com/bms/price/domain/PayPriceDiff.java | 3 + .../com/bms/price/domain/PrcPayBillFee.java | 6 + .../com/bms/price/pay/PayCostCalculator.java | 25 +- .../service/impl/PayProductServiceImpl.java | 360 +++++++++++++----- .../impl/SrvBillAirFeeServiceImpl.java | 149 +++++++- .../resources/mapper/price/PayBillMapper.xml | 8 +- .../mapper/price/PrcPayBillFeeMapper.xml | 17 +- .../mapper/price/SrvBillAirFeeMapper.xml | 41 +- bms-back/sql/price/air_bill_fee_dict.sql | 119 ++++++ bms-back/sql/price/pay_bill.sql | 26 ++ .../basic/pay/components/PayImportDialog.vue | 16 +- bms-front/src/views/price/basic/pay/index.vue | 136 ++++++- .../billing/components/AirBillFeeDialog.vue | 149 +++++++- 16 files changed, 911 insertions(+), 170 deletions(-) create mode 100644 bms-back/sql/price/air_bill_fee_dict.sql diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/SurchargeController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/SurchargeController.java index ddd5cbd9..effc5f6b 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/SurchargeController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/SurchargeController.java @@ -73,7 +73,8 @@ public class SurchargeController extends BaseController @PreAuthorize("@ss.hasPermi('price:surcharge:list') or @ss.hasPermi('price:surcharge:query')" + " or @ss.hasPermi('price:set:query') or @ss.hasPermi('price:set:add') or @ss.hasPermi('price:set:edit')" + " or @ss.hasPermi('price:billing:bill')" - + " or @ss.hasPermi('price:pay:query') or @ss.hasPermi('price:pay:edit') or @ss.hasPermi('price:pay:import')") + + " or @ss.hasPermi('price:pay:query') or @ss.hasPermi('price:pay:edit') or @ss.hasPermi('price:pay:import')" + + " or @ss.hasPermi('price:airBill:fee')") @GetMapping("/options") public AjaxResult listOptions(PrcSurcharge surcharge) { diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java index 4b59b770..efed20f2 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java @@ -15,9 +15,24 @@ public class SrvBillAirFee private String billCode; - /** 字典 bms_fee_type */ + /** 附加费编号 prc_surcharge.sc_id */ private String feeType; + /** 附加费名称,仅展示,不入库 */ + private String feeName; + + /** 费用阶段,字典 bms_fee_level */ + private String feeLevel; + + /** 提单费用状态,字典 bill_fee_status。只随分摊、作废操作变化 */ + private String billFeeStatus; + + /** 是否分摊,字典 bill_fee_alloc:1是 0否 */ + private String allocFlag; + + /** 分摊维度(票),字典 bill_fee_alloc_dim:1订单 2重量。是否分摊为否时为空 */ + private String allocDim; + private BigDecimal price; /** 字典 bms_currency */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceCompare.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceCompare.java index d773a2b2..1cf50f96 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceCompare.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceCompare.java @@ -11,7 +11,13 @@ public class PayPriceCompare private PayProduct newer; + /** 有效期、来源文件、币种 */ + private List versionDiffs; + private List weightDiffs; private List surchargeDiffs; + + /** 邮编等不能归进重量段和附加费的原文 */ + private List extraDiffs; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceDiff.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceDiff.java index 33fb2343..09b11289 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceDiff.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PayPriceDiff.java @@ -6,6 +6,9 @@ import lombok.Data; @Data public class PayPriceDiff { + /** 版本、重量段、附加费、其他配置 */ + private String kind; + private String name; private String zoneName; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPayBillFee.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPayBillFee.java index 1771e897..4a40a783 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPayBillFee.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPayBillFee.java @@ -26,6 +26,12 @@ public class PrcPayBillFee implements Serializable /** CALC 计算,LADING 提单分摊,MANUAL 人工 */ private String feeSource; + + /** 来源数据id */ + private String sourceId; + + /** 是否删除:0未删除 1已删除 */ + private String isDeleted; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java index d4a86313..b1366cbd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java @@ -24,6 +24,9 @@ public final class PayCostCalculator { private static final ZoneId SHANGHAI = ZoneId.of("GMT+8"); + // 定义常量集合,建议放在类级别作为 static final 常量 + private static final Set REFUND_STATUS_SET = Set.of(9, 10); + private PayCostCalculator() { } @@ -242,6 +245,23 @@ public final class PayCostCalculator return kept; } + /** + * 添加相关附加费,根据计费规则去判断 + * @param product + * @param weight + * @param length + * @param width + * @param height + * @param postcode + * @param zoneName + * @param bizDate + * @param orderStatus + * @param freight + * @param currency + * @param freightSection + * @param remoteHits + * @param result + */ private static void addSurcharges(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width, BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus, BigDecimal freight, String currency, String freightSection, List remoteHits, PayOrderCostResult result) @@ -317,6 +337,7 @@ public final class PayCostCalculator addFlat(result.getFees(), fee, currency, "计费重超过 " + fee.getCalcParam() + "kg"); } } + // 超出尺寸费用 else if (PaySurchargeRules.SIZE.equals(rule)) { if (length == null || width == null || height == null) @@ -332,13 +353,15 @@ public final class PayCostCalculator } } } + // 退件费用 else if (PaySurchargeRules.RETURN.equals(rule)) { - if (orderStatus != null && orderStatus == 9) + if (orderStatus != null && REFUND_STATUS_SET.contains(orderStatus)) { addFlat(result.getFees(), fee, currency, "订单已退件"); } } + // 超重费 else if (PaySurchargeRules.EXTRA_KG.equals(rule)) { if (postcodeDriven) diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java index 7cf5a963..79d04843 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java @@ -3,6 +3,7 @@ package com.bms.price.service.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -15,6 +16,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -164,6 +166,7 @@ public class PayProductServiceImpl implements IPayProductService } else { + product.setProductType(PAY_TYPE_AP); Long existsId = isBlank(product.getDictValue()) ? null : payProductMapper.selectTemplateId(product.getDictValue()); product.setDuplicated(existsId != null); product.setExistsId(existsId); @@ -266,7 +269,7 @@ public class PayProductServiceImpl implements IPayProductService skipped++; continue; } - product.setProductType(requireProductType(product.getProductType())); + product.setProductType(PAY_TYPE_AP); product.setFilePath(resolveSource(product)); prepareCounts(product); fitColumns(product); @@ -1694,8 +1697,10 @@ public class PayProductServiceImpl implements IPayProductService PayPriceCompare compared = new PayPriceCompare(); compared.setOlder(older); compared.setNewer(newer); + compared.setVersionDiffs(diffVersion(older, newer)); compared.setWeightDiffs(diffWeights(older, newer)); compared.setSurchargeDiffs(diffFees(older, newer)); + compared.setExtraDiffs(diffExtras(older, newer)); return compared; } @@ -1753,15 +1758,6 @@ public class PayProductServiceImpl implements IPayProductService return stableId(link.getProductId()); } - private String requireProductType(String productType) - { - if (isBlank(productType)) - { - throw new ServiceException("请选择支付类型"); - } - return payTypeOf(productType); - } - private String payTypeOf(String payType) { if (isBlank(payType)) @@ -1861,111 +1857,305 @@ public class PayProductServiceImpl implements IPayProductService return left <= otherRight && otherLeft <= right; } + /** 有效期和来源文件记在版本上,价格没变时也要能看出来选的是哪两版。 */ + private List diffVersion(PayProduct older, PayProduct newer) + { + PayProductTerm oldTerm = payProductTermMapper.selectByProductId(older.getProductId()); + PayProductTerm newTerm = payProductTermMapper.selectByProductId(newer.getProductId()); + List diffs = new ArrayList<>(); + diffs.add(metaDiff("生效开始", termStart(oldTerm, older), termStart(newTerm, newer))); + diffs.add(metaDiff("生效结束", termEnd(oldTerm), termEnd(newTerm))); + diffs.add(metaDiff("来源文件", blankTo(older.getSourceFile(), "-"), blankTo(newer.getSourceFile(), "-"))); + diffs.add(metaDiff("币种", blankTo(older.getCurrency(), "-"), blankTo(newer.getCurrency(), "-"))); + return diffs; + } + private List diffWeights(PayProduct older, PayProduct newer) { - Map oldRows = new LinkedHashMap<>(); - Map newRows = new LinkedHashMap<>(); - if (older.getWeightBands() != null) - { - for (PayWeightBand band : older.getWeightBands()) + return pairDiffs(groupBy(older.getWeightBands(), PayProductServiceImpl::bandKey), + groupBy(newer.getWeightBands(), PayProductServiceImpl::bandKey), (oldRow, newRow) -> { - oldRows.put(bandKey(band), band); - } - } - if (newer.getWeightBands() != null) - { - for (PayWeightBand band : newer.getWeightBands()) - { - newRows.put(bandKey(band), band); - } - } - List diffs = new ArrayList<>(); - for (Map.Entry entry : oldRows.entrySet()) - { - PayWeightBand current = newRows.remove(entry.getKey()); - PayPriceDiff diff = new PayPriceDiff(); - diff.setName(blankTo(entry.getValue().getWeightLabel(), "")); - diff.setZoneName(blankTo(entry.getValue().getZoneName(), "")); - diff.setOldValue(priceText(entry.getValue())); - diff.setNewValue(current == null ? "" : priceText(current)); - diff.setChange(current == null ? "删除" : (diff.getOldValue().equals(diff.getNewValue()) ? "相同" : "变更")); - diffs.add(diff); - } - for (PayWeightBand band : newRows.values()) - { - PayPriceDiff diff = new PayPriceDiff(); - diff.setName(blankTo(band.getWeightLabel(), "")); - diff.setZoneName(blankTo(band.getZoneName(), "")); - diff.setOldValue(""); - diff.setNewValue(priceText(band)); - diff.setChange("新增"); - diffs.add(diff); - } - return diffs; + PayWeightBand sample = oldRow != null ? oldRow : newRow; + PayPriceDiff diff = new PayPriceDiff(); + diff.setKind("重量段"); + diff.setName(bandName(sample)); + diff.setZoneName(blankTo(sample.getZoneName(), "")); + diff.setOldValue(oldRow == null ? "" : priceText(oldRow)); + diff.setNewValue(newRow == null ? "" : priceText(newRow)); + return diff; + }); } private List diffFees(PayProduct older, PayProduct newer) { - Map oldRows = new LinkedHashMap<>(); - Map newRows = new LinkedHashMap<>(); - if (older.getSurcharges() != null) - { - for (PaySurcharge fee : older.getSurcharges()) + return pairDiffs(groupBy(older.getSurcharges(), PayProductServiceImpl::feeKey), + groupBy(newer.getSurcharges(), PayProductServiceImpl::feeKey), (oldRow, newRow) -> { - oldRows.put(feeKey(fee), fee); - } - } - if (newer.getSurcharges() != null) - { - for (PaySurcharge fee : newer.getSurcharges()) + PaySurcharge sample = oldRow != null ? oldRow : newRow; + PayPriceDiff diff = new PayPriceDiff(); + diff.setKind("附加费"); + diff.setName(joinText(sample.getSectionName(), sample.getFeeName())); + diff.setZoneName(blankTo(sample.getZoneName(), "")); + diff.setOldValue(oldRow == null ? "" : feeText(oldRow)); + diff.setNewValue(newRow == null ? "" : feeText(newRow)); + return diff; + }); + } + + private List diffExtras(PayProduct older, PayProduct newer) + { + return pairDiffs(groupBy(older.getExtraLines(), PayProductServiceImpl::extraKey), + groupBy(newer.getExtraLines(), PayProductServiceImpl::extraKey), (oldRow, newRow) -> { - newRows.put(feeKey(fee), fee); - } - } + PayExtraLine sample = oldRow != null ? oldRow : newRow; + PayPriceDiff diff = new PayPriceDiff(); + diff.setKind("其他配置"); + diff.setName(blankTo(sample.getLineType(), "配置")); + diff.setZoneName(""); + diff.setOldValue(oldRow == null ? "" : blankTo(oldRow.getLineText(), "")); + diff.setNewValue(newRow == null ? "" : blankTo(newRow.getLineText(), "")); + return diff; + }); + } + + /** + * 同一行按顺序对齐。相同费用有多条邮编或重量档时不能并成一条,否则后面的改动会被盖掉。 + */ + private List pairDiffs(Map> oldRows, Map> newRows, RowDiff factory) + { + Set keys = new LinkedHashSet<>(); + keys.addAll(oldRows.keySet()); + keys.addAll(newRows.keySet()); List diffs = new ArrayList<>(); - for (Map.Entry entry : oldRows.entrySet()) + for (String key : keys) { - PaySurcharge current = newRows.remove(entry.getKey()); - PayPriceDiff diff = new PayPriceDiff(); - diff.setName(blankTo(entry.getValue().getFeeName(), "")); - diff.setZoneName(blankTo(entry.getValue().getZoneName(), "")); - diff.setOldValue(blankTo(entry.getValue().getChargeText(), "")); - diff.setNewValue(current == null ? "" : blankTo(current.getChargeText(), "")); - diff.setChange(current == null ? "删除" : (diff.getOldValue().equals(diff.getNewValue()) ? "相同" : "变更")); - diffs.add(diff); - } - for (PaySurcharge fee : newRows.values()) - { - PayPriceDiff diff = new PayPriceDiff(); - diff.setName(blankTo(fee.getFeeName(), "")); - diff.setZoneName(blankTo(fee.getZoneName(), "")); - diff.setOldValue(""); - diff.setNewValue(blankTo(fee.getChargeText(), "")); - diff.setChange("新增"); - diffs.add(diff); + List older = oldRows.getOrDefault(key, Collections.emptyList()); + List newer = newRows.getOrDefault(key, Collections.emptyList()); + int count = Math.max(older.size(), newer.size()); + for (int i = 0; i < count; i++) + { + T oldRow = i < older.size() ? older.get(i) : null; + T newRow = i < newer.size() ? newer.get(i) : null; + PayPriceDiff diff = factory.build(oldRow, newRow); + diff.setChange(oldRow == null ? "新增" : (newRow == null ? "删除" + : (diff.getOldValue().equals(diff.getNewValue()) ? "相同" : "变更"))); + diffs.add(diff); + } } return diffs; } - private static String bandKey(PayWeightBand band) + private Map> groupBy(List rows, Function keyFn) { - return blankTo(band.getZoneName(), "") + "|" + blankTo(band.getWeightLabel(), "") + "|" + blankTo(band.getChargeName(), ""); + Map> grouped = new LinkedHashMap<>(); + if (rows == null) + { + return grouped; + } + for (T row : rows) + { + grouped.computeIfAbsent(keyFn.apply(row), key -> new ArrayList<>()).add(row); + } + return grouped; } + private static PayPriceDiff metaDiff(String name, String oldValue, String newValue) + { + PayPriceDiff diff = new PayPriceDiff(); + diff.setKind("版本"); + diff.setName(name); + diff.setZoneName(""); + diff.setOldValue(oldValue); + diff.setNewValue(newValue); + diff.setChange(oldValue.equals(newValue) ? "相同" : "变更"); + return diff; + } + + private static String termStart(PayProductTerm term, PayProduct product) + { + if (term != null) + { + return dayOrOpen(term.getEffectiveStart()); + } + return dayOrOpen(product.getEffectiveDate()); + } + + private static String termEnd(PayProductTerm term) + { + return term == null ? "不限" : dayOrOpen(term.getEffectiveEnd()); + } + + private static String dayOrOpen(Date date) + { + return date == null ? "不限" : formatDay(date); + } + + /** 分区、重量段和费用名确定一行单价,价格本身不参与对齐。 */ + private static String bandKey(PayWeightBand band) + { + return joinKey(band.getSectionName(), band.getZoneName(), band.getCountryCode(), band.getChargeName(), + band.getWeightLabel(), numKey(band.getWeightMin()), numKey(band.getWeightMax())); + } + + /** 同一费用、同一分区、同一重量档里的多条邮编按导入顺序逐条比对。 */ private static String feeKey(PaySurcharge fee) { - return blankTo(fee.getFeeName(), "") + "|" + blankTo(fee.getZoneName(), "") + "|" + blankTo(fee.getCalcRule(), ""); + return joinKey(fee.getSectionName(), fee.getFeeName(), fee.getZoneName(), numKey(fee.getWeightMin()), numKey(fee.getWeightMax())); + } + + private static String extraKey(PayExtraLine line) + { + return blankTo(line.getLineType(), ""); + } + + private static String bandName(PayWeightBand band) + { + String label = blankTo(band.getWeightLabel(), rangeText(band.getWeightMin(), band.getWeightMax())); + return joinText(band.getSectionName(), label, band.getChargeName()); } private static String priceText(PayWeightBand band) { - String price = band.getPrice() == null ? "" : band.getPrice().stripTrailingZeros().toPlainString(); + String price = money(band.getPrice()); String unit = blankTo(band.getPriceUnit(), ""); String volume = blankTo(band.getVolumeText(), ""); String text = (price + " " + unit).trim(); return volume.isEmpty() ? text : (text + " " + volume).trim(); } + /** 金额、收费原文、计费规则和参数都算差异。只比原文会把改过的邮编和续重看成没变。 */ + private static String feeText(PaySurcharge fee) + { + StringBuilder text = new StringBuilder(); + String amount = money(fee.getAmount()); + String unit = blankTo(fee.getPriceUnit(), ""); + String priced = (amount + " " + unit).trim(); + String charge = blankTo(fee.getChargeText(), ""); + appendPart(text, priced); + if (!charge.isEmpty() && !charge.equals(priced) && !charge.equals(amount)) + { + appendPart(text, charge); + } + String rule = ruleText(fee.getCalcRule()); + String param = blankTo(fee.getCalcParam(), ""); + String postcode = blankTo(fee.getPostcodeParam(), ""); + if (!postcode.isEmpty() && !postcode.equals(param)) + { + param = param.isEmpty() ? postcode : param + " " + postcode; + } + if (!rule.isEmpty() || !param.isEmpty()) + { + appendPart(text, (rule + " " + param).trim()); + } + appendPart(text, rangeText(fee.getWeightMin(), fee.getWeightMax())); + String period = blankTo(fee.getPeriodText(), ""); + if (period.isEmpty() && (fee.getEffectiveStart() != null || fee.getEffectiveEnd() != null)) + { + period = dayOrOpen(fee.getEffectiveStart()) + " 至 " + dayOrOpen(fee.getEffectiveEnd()); + } + appendPart(text, period); + return text.toString(); + } + + private static String ruleText(String rule) + { + if (isBlank(rule) || "MANUAL".equalsIgnoreCase(rule)) + { + return ""; + } + switch (rule.toUpperCase(Locale.ROOT)) + { + case "POSTCODE": + return "邮编"; + case "EXTRA_KG": + return "续重"; + case "ALWAYS": + return "固定"; + case "ZONE": + return "分区"; + case "WEIGHT": + return "重量"; + case "SIZE": + return "尺寸"; + case "RETURN": + return "退件"; + default: + return rule; + } + } + + private static String rangeText(BigDecimal min, BigDecimal max) + { + String left = money(min); + String right = money(max); + if (left.isEmpty() && right.isEmpty()) + { + return ""; + } + return (left.isEmpty() ? "0" : left) + "-" + (right.isEmpty() ? "不限" : right) + "kg"; + } + + private static String money(BigDecimal value) + { + return value == null ? "" : value.stripTrailingZeros().toPlainString(); + } + + private static String numKey(BigDecimal value) + { + return money(value); + } + + private static String joinKey(String... parts) + { + StringBuilder text = new StringBuilder(); + for (String part : parts) + { + if (text.length() > 0) + { + text.append('|'); + } + text.append(blankTo(part, "")); + } + return text.toString(); + } + + private static String joinText(String... parts) + { + StringBuilder text = new StringBuilder(); + for (String part : parts) + { + if (isBlank(part)) + { + continue; + } + if (text.length() > 0) + { + text.append(' '); + } + text.append(part.trim()); + } + return text.toString(); + } + + private static void appendPart(StringBuilder text, String part) + { + if (isBlank(part)) + { + return; + } + if (text.length() > 0) + { + text.append(';'); + } + text.append(part.trim()); + } + + @FunctionalInterface + private interface RowDiff + { + PayPriceDiff build(T older, T newer); + } + private static Date today() { return toDate(java.time.LocalDate.now(java.time.ZoneId.of("GMT+8"))); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java index 9cc46dca..eed2ef7f 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java @@ -21,8 +21,10 @@ import com.bms.common.utils.SecurityUtils; import com.bms.price.airbill.domain.SrvBillAirFee; import com.bms.price.airbill.domain.SrvBillAirFeeLog; import com.bms.price.airbill.domain.SrvBillAirFeeSave; +import com.bms.price.domain.PrcSurcharge; import com.bms.price.mapper.SrvBillAirFeeMapper; import com.bms.price.mapper.SrvBillAirMapper; +import com.bms.price.service.IPrcSurchargeService; import com.bms.price.service.ISrvBillAirFeeService; import com.bms.system.service.ISysDictTypeService; @@ -32,13 +34,22 @@ import com.bms.system.service.ISysDictTypeService; @Service public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { - private static final String FEE_DICT = "bms_fee_type"; + private static final String LEVEL_DICT = "bms_fee_level"; + + private static final String ALLOC_DICT = "bill_fee_alloc"; + + private static final String ALLOC_DIM_DICT = "bill_fee_alloc_dim"; + + private static final String STATUS_DICT = "bill_fee_status"; private static final String CURRENCY_DICT = "bms_currency"; @Autowired private SrvBillAirFeeMapper feeMapper; + @Autowired + private IPrcSurchargeService surchargeService; + @Autowired private SrvBillAirMapper billMapper; @@ -84,7 +95,10 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { throw new ServiceException("没有需要保存的变更"); } - Map feeLabels = dictLabels(FEE_DICT, "费用类型"); + Map levelLabels = dictLabels(LEVEL_DICT, "费用阶段"); + Map allocLabels = dictLabels(ALLOC_DICT, "是否分摊"); + Map dimLabels = dictLabels(ALLOC_DIM_DICT, "分摊维度"); + Map statusLabels = dictLabels(STATUS_DICT, "费用状态"); Map currencyLabels = dictLabels(CURRENCY_DICT, "币种"); Operator operator = operator(); Date now = DateUtils.getNowDate(); @@ -110,7 +124,7 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService int rows = feeMapper.deleteFee(current.getId(), billCode, operator.account, now); if (rows > 0) { - writeLog(billCode, id, SrvBillAirFeeLog.DELETE, current, null, feeLabels, currencyLabels, operator, now); + writeLog(billCode, id, SrvBillAirFeeLog.DELETE, current, null, feeNames(current, null), levelLabels, allocLabels, dimLabels, statusLabels, currencyLabels, operator, now); removed++; } } @@ -129,8 +143,10 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { throw new ServiceException("费用已删除或不属于该提单"); } - SrvBillAirFee next = normalize(incoming, feeLabels, currencyLabels); - List changes = diff(current, next, feeLabels, currencyLabels); + SrvBillAirFee next = normalize(incoming, levelLabels, allocLabels, dimLabels, currencyLabels); + next.setBillFeeStatus(current.getBillFeeStatus() == null || current.getBillFeeStatus().isBlank() ? "0" : current.getBillFeeStatus()); + Map feeNames = feeNames(current, next); + List changes = diff(current, next, feeNames, levelLabels, allocLabels, dimLabels, statusLabels, currencyLabels); if (changes.isEmpty()) { continue; @@ -141,17 +157,19 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService next.setUpdateTime(now); if (feeMapper.updateFee(next) > 0) { - writeLog(billCode, current.getId(), SrvBillAirFeeLog.UPDATE, current, next, feeLabels, currencyLabels, operator, now); + writeLog(billCode, current.getId(), SrvBillAirFeeLog.UPDATE, current, next, feeNames, levelLabels, allocLabels, dimLabels, statusLabels, currencyLabels, operator, now); updated++; } } for (SrvBillAirFee incoming : inserts) { - if (incoming == null || blank(incoming.getFeeType()) && incoming.getPrice() == null && blank(incoming.getCurrency()) && blank(incoming.getRemark())) + if (incoming == null || blank(incoming.getFeeType()) && blank(incoming.getFeeLevel()) && incoming.getPrice() == null + && blank(incoming.getCurrency()) && blank(incoming.getRemark())) { continue; } - SrvBillAirFee next = normalize(incoming, feeLabels, currencyLabels); + SrvBillAirFee next = normalize(incoming, levelLabels, allocLabels, dimLabels, currencyLabels); + next.setBillFeeStatus("0"); next.setId(null); next.setBillCode(billCode); next.setCreateBy(operator.account); @@ -159,7 +177,7 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService next.setUpdateBy(operator.account); next.setUpdateTime(now); feeMapper.insertFee(next); - writeLog(billCode, next.getId(), SrvBillAirFeeLog.INSERT, null, next, feeLabels, currencyLabels, operator, now); + writeLog(billCode, next.getId(), SrvBillAirFeeLog.INSERT, null, next, feeNames(null, next), levelLabels, allocLabels, dimLabels, statusLabels, currencyLabels, operator, now); inserted++; } if (inserted + updated + removed == 0) @@ -174,18 +192,48 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService return feeMapper.selectLive(id, billCode); } - private SrvBillAirFee normalize(SrvBillAirFee raw, Map feeLabels, Map currencyLabels) + private SrvBillAirFee normalize(SrvBillAirFee raw, Map levelLabels, + Map allocLabels, Map dimLabels, Map currencyLabels) { String feeType = trim(raw.getFeeType()); + String feeLevel = trim(raw.getFeeLevel()); + String allocFlag = trim(raw.getAllocFlag()); + String allocDim = trim(raw.getAllocDim()); String currency = trim(raw.getCurrency()); String remark = trim(raw.getRemark()); - if (feeType.isEmpty()) + Long scId = parseScId(feeType); + if (scId == null) { throw new ServiceException("请选择费用项"); } - if (feeType.length() > 32 || !feeLabels.containsKey(feeType)) + PrcSurcharge surcharge = surchargeService.selectPrcSurchargeById(scId); + if (surcharge == null) { - throw new ServiceException("费用项不在费用类型字典中"); + throw new ServiceException("费用项不存在"); + } + feeType = String.valueOf(scId); + if (allocFlag.isEmpty() || !allocLabels.containsKey(allocFlag)) + { + throw new ServiceException("请选择是否分摊"); + } + if (!feeLevel.isEmpty() && (feeLevel.length() > 32 || !levelLabels.containsKey(feeLevel))) + { + throw new ServiceException("费用阶段不在字典中"); + } + if ("1".equals(allocFlag) && feeLevel.isEmpty()) + { + throw new ServiceException("如需分摊费用至订单,请选择费用所属阶段"); + } + if ("1".equals(allocFlag)) + { + if (allocDim.isEmpty() || !dimLabels.containsKey(allocDim)) + { + throw new ServiceException("如需分摊费用至订单,请选择分摊维度"); + } + } + else + { + allocDim = ""; } if (raw.getPrice() == null) { @@ -213,6 +261,9 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService } SrvBillAirFee fee = new SrvBillAirFee(); fee.setFeeType(feeType); + fee.setFeeLevel(feeLevel); + fee.setAllocFlag(allocFlag); + fee.setAllocDim(allocDim); fee.setPrice(raw.getPrice()); fee.setCurrency(currency); fee.setRemark(remark); @@ -220,9 +271,10 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService } private void writeLog(String billCode, Long feeId, String operation, SrvBillAirFee before, SrvBillAirFee after, - Map feeLabels, Map currencyLabels, Operator operator, Date now) + Map feeLabels, Map levelLabels, Map allocLabels, + Map dimLabels, Map statusLabels, Map currencyLabels, Operator operator, Date now) { - List changes = diff(before, after, feeLabels, currencyLabels); + List changes = diff(before, after, feeLabels, levelLabels, allocLabels, dimLabels, statusLabels, currencyLabels); SrvBillAirFeeLog log = new SrvBillAirFeeLog(); log.setBillCode(billCode); log.setFeeId(feeId); @@ -236,11 +288,20 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService } private static List diff(SrvBillAirFee before, SrvBillAirFee after, - Map feeLabels, Map currencyLabels) + Map feeLabels, Map levelLabels, Map allocLabels, + Map dimLabels, Map statusLabels, Map currencyLabels) { List changes = new ArrayList<>(); - addChange(changes, "feeType", "费用类型", label(feeLabels, value(before, SrvBillAirFee::getFeeType)), + addChange(changes, "feeType", "费用项", label(feeLabels, value(before, SrvBillAirFee::getFeeType)), label(feeLabels, value(after, SrvBillAirFee::getFeeType))); + addChange(changes, "feeLevel", "费用阶段", label(levelLabels, value(before, SrvBillAirFee::getFeeLevel)), + label(levelLabels, value(after, SrvBillAirFee::getFeeLevel))); + addChange(changes, "allocFlag", "是否分摊", label(allocLabels, value(before, SrvBillAirFee::getAllocFlag)), + label(allocLabels, value(after, SrvBillAirFee::getAllocFlag))); + addChange(changes, "allocDim", "分摊维度(票)", label(dimLabels, value(before, SrvBillAirFee::getAllocDim)), + label(dimLabels, value(after, SrvBillAirFee::getAllocDim))); + addChange(changes, "billFeeStatus", "费用状态", label(statusLabels, value(before, SrvBillAirFee::getBillFeeStatus)), + label(statusLabels, value(after, SrvBillAirFee::getBillFeeStatus))); addChange(changes, "price", "价格", priceText(before), priceText(after)); addChange(changes, "currency", "收费币种", label(currencyLabels, value(before, SrvBillAirFee::getCurrency)), label(currencyLabels, value(after, SrvBillAirFee::getCurrency))); @@ -329,6 +390,60 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService return fee.getPrice().stripTrailingZeros().toPlainString(); } + private Map feeNames(SrvBillAirFee before, SrvBillAirFee after) + { + Map names = new LinkedHashMap<>(); + putFeeName(names, before); + putFeeName(names, after); + return names; + } + + private void putFeeName(Map names, SrvBillAirFee fee) + { + if (fee == null) + { + return; + } + String id = trim(fee.getFeeType()); + if (id.isEmpty() || names.containsKey(id)) + { + return; + } + names.put(id, surchargeName(id)); + } + + private String surchargeName(String feeType) + { + Long scId = parseScId(feeType); + if (scId == null) + { + return feeType; + } + PrcSurcharge surcharge = surchargeService.selectPrcSurchargeById(scId); + if (surcharge == null || surcharge.getScName() == null || surcharge.getScName().isBlank()) + { + return feeType; + } + return surcharge.getScName().trim(); + } + + private static Long parseScId(String feeType) + { + if (feeType == null || feeType.isBlank() || !feeType.chars().allMatch(Character::isDigit)) + { + return null; + } + try + { + long id = Long.parseLong(feeType); + return id > 0 ? id : null; + } + catch (NumberFormatException ex) + { + return null; + } + } + private static String label(Map labels, String value) { if (value == null || value.isBlank()) diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PayBillMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PayBillMapper.xml index d4f0d6f1..727efe32 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PayBillMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PayBillMapper.xml @@ -47,7 +47,7 @@ e.supplier_name as supplierName, (select group_concat(concat(ifnull(nullif(trim(f.fee_name), ''), ifnull(f.fee_type, '费用')), ' ', format(f.amount, 2)) order by field(f.fee_type, 'BASIC_FREIGHT', 'REGISTRATION_FEE', 'SURCHARGE', 'REFUND'), f.fee_id separator ';') - from prc_pay_bill_fee f where f.bill_id = o.bill_id) as feeText, + from prc_pay_bill_fee f where f.bill_id = o.bill_id and f.is_deleted = '0') as feeText, (select b.bill_no from pay_bill_line l inner join pay_bill b on b.bill_id = l.bill_id where l.order_bill_id = o.bill_id and b.status <> 'VOID' @@ -81,9 +81,11 @@ diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml index a8c7410a..6d1126f7 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml @@ -15,31 +15,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + insert into prc_pay_bill_fee ( - fee_id, bill_id, pay_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, create_time + fee_id, bill_id, pay_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, + source_id, is_deleted, create_time ) values ( - #{feeId}, #{billId}, #{payBillId}, #{feeType}, #{feeName}, #{scId}, #{amount}, #{currency}, #{roundMode}, #{remark}, sysdate() + #{feeId}, #{billId}, #{payBillId}, #{feeType}, #{feeName}, #{scId}, #{amount}, #{currency}, #{roundMode}, #{remark}, + #{sourceId}, ifnull(#{isDeleted}, '0'), sysdate() ) insert into prc_pay_bill_fee ( - fee_id, bill_id, pay_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, create_time + fee_id, bill_id, pay_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, + source_id, is_deleted, create_time ) values (#{item.feeId}, #{item.billId}, #{item.payBillId}, #{item.feeType}, #{item.feeName}, #{item.scId}, #{item.amount}, - #{item.currency}, #{item.roundMode}, #{item.remark}, sysdate()) + #{item.currency}, #{item.roundMode}, #{item.remark}, #{item.sourceId}, ifnull(#{item.isDeleted}, '0'), sysdate()) diff --git a/bms-back/bms-system/src/main/resources/mapper/price/SrvBillAirFeeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/SrvBillAirFeeMapper.xml index 86a98d92..4c98e841 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/SrvBillAirFeeMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/SrvBillAirFeeMapper.xml @@ -3,31 +3,37 @@ insert into srv_bill_air_fee ( - bill_code, fee_type, price, currency, remark, + bill_code, fee_type, fee_level, bill_fee_status, alloc_flag, alloc_dim, price, currency, remark, create_by, create_time, update_by, update_time, del_flag ) values ( - #{billCode}, #{feeType}, #{price}, #{currency}, #{remark}, + #{billCode}, #{feeType}, #{feeLevel}, '0', #{allocFlag}, #{allocDim}, #{price}, #{currency}, #{remark}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, '0' ) @@ -35,6 +41,9 @@ update srv_bill_air_fee set fee_type = #{feeType}, + fee_level = #{feeLevel}, + alloc_flag = #{allocFlag}, + alloc_dim = #{allocDim}, price = #{price}, currency = #{currency}, remark = #{remark}, diff --git a/bms-back/sql/price/air_bill_fee_dict.sql b/bms-back/sql/price/air_bill_fee_dict.sql new file mode 100644 index 00000000..27933ed0 --- /dev/null +++ b/bms-back/sql/price/air_bill_fee_dict.sql @@ -0,0 +1,119 @@ +-- 提单费用阶段、提单费用状态、是否分摊、分摊维度。可重复执行。 +SET NAMES utf8mb4; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'费用阶段', 'bms_fee_level', '0', 'admin', sysdate(), N'srv_bill_air_fee.fee_level:1 a段头程仓储,2 b段提单运输,3 c段清关,4 d段尾程' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bms_fee_level'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT v.dict_sort, v.dict_label, v.dict_value, 'bms_fee_level', '', v.list_class, 'N', '0', 'admin', sysdate(), v.remark +FROM ( + SELECT 1 AS dict_sort, N'a段(头程仓储)' AS dict_label, '1' AS dict_value, 'primary' AS list_class, N'fee_level=1' AS remark + UNION ALL SELECT 2, N'b段(提单运输)', '2', 'success', N'fee_level=2' + UNION ALL SELECT 3, N'c段(清关)', '3', 'warning', N'fee_level=3' + UNION ALL SELECT 4, N'd段(尾程)', '4', 'info', N'fee_level=4' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM sys_dict_data d WHERE d.dict_type = 'bms_fee_level' AND d.dict_value = v.dict_value +); + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'提单费用状态', 'bill_fee_status', '0', 'admin', sysdate(), N'srv_bill_air_fee.bill_fee_status:0未分摊 1已分摊 2已作废' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bill_fee_status'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT v.dict_sort, v.dict_label, v.dict_value, 'bill_fee_status', '', v.list_class, v.is_default, '0', 'admin', sysdate(), v.remark +FROM ( + SELECT 1 AS dict_sort, N'未分摊' AS dict_label, '0' AS dict_value, 'info' AS list_class, 'Y' AS is_default, N'新建费用的初始状态' AS remark + UNION ALL SELECT 2, N'已分摊', '1', 'success', 'N', N'分摊操作完成后' + UNION ALL SELECT 3, N'已作废', '2', 'danger', 'N', N'作废操作完成后' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM sys_dict_data d WHERE d.dict_type = 'bill_fee_status' AND d.dict_value = v.dict_value +); + +SET @sql = ( + SELECT IF( + COUNT(*) = 0, + 'ALTER TABLE srv_bill_air_fee ADD COLUMN bill_fee_status char(1) NOT NULL DEFAULT ''0'' COMMENT ''提单费用状态,字典 bill_fee_status:0未分摊 1已分摊 2已作废'' AFTER fee_level', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'srv_bill_air_fee' AND COLUMN_NAME = 'bill_fee_status' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'是否分摊', 'bill_fee_alloc', '0', 'admin', sysdate(), N'srv_bill_air_fee.alloc_flag:1是 0否' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bill_fee_alloc'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT v.dict_sort, v.dict_label, v.dict_value, 'bill_fee_alloc', '', v.list_class, v.is_default, '0', 'admin', sysdate(), v.remark +FROM ( + SELECT 1 AS dict_sort, N'是' AS dict_label, '1' AS dict_value, 'success' AS list_class, 'N' AS is_default, N'这条费用参与分摊,须选择费用阶段' AS remark + UNION ALL SELECT 2, N'否', '0', 'info', 'Y', N'这条费用不分摊,默认' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM sys_dict_data d WHERE d.dict_type = 'bill_fee_alloc' AND d.dict_value = v.dict_value +); + +SET @sql = ( + SELECT IF( + COUNT(*) = 0, + 'ALTER TABLE srv_bill_air_fee ADD COLUMN alloc_flag char(1) NOT NULL DEFAULT ''0'' COMMENT ''是否分摊,字典 bill_fee_alloc:1是 0否。为是时必须选择费用阶段'' AFTER bill_fee_status', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'srv_bill_air_fee' AND COLUMN_NAME = 'alloc_flag' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +UPDATE sys_dict_data SET is_default = 'N', remark = N'这条费用参与分摊,须选择费用阶段' +WHERE dict_type = 'bill_fee_alloc' AND dict_value = '1'; +UPDATE sys_dict_data SET is_default = 'Y', remark = N'这条费用不分摊,默认' +WHERE dict_type = 'bill_fee_alloc' AND dict_value = '0'; + +SET @sql = ( + SELECT IF( + COUNT(*) > 0, + 'ALTER TABLE srv_bill_air_fee MODIFY COLUMN alloc_flag char(1) NOT NULL DEFAULT ''0'' COMMENT ''是否分摊,字典 bill_fee_alloc:1是 0否。为是时必须选择费用阶段''', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'srv_bill_air_fee' AND COLUMN_NAME = 'alloc_flag' + AND (COLUMN_DEFAULT IS NULL OR COLUMN_DEFAULT <> '0') +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'分摊维度(票)', 'bill_fee_alloc_dim', '0', 'admin', sysdate(), N'srv_bill_air_fee.alloc_dim:1订单 2重量。是否分摊为是时必填' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bill_fee_alloc_dim'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT v.dict_sort, v.dict_label, v.dict_value, 'bill_fee_alloc_dim', '', v.list_class, 'N', '0', 'admin', sysdate(), v.remark +FROM ( + SELECT 1 AS dict_sort, N'订单' AS dict_label, '1' AS dict_value, 'primary' AS list_class, N'按订单票数分摊' AS remark + UNION ALL SELECT 2, N'重量', '2', 'success', N'按重量占比分摊' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM sys_dict_data d WHERE d.dict_type = 'bill_fee_alloc_dim' AND d.dict_value = v.dict_value +); + +SET @sql = ( + SELECT IF( + COUNT(*) = 0, + 'ALTER TABLE srv_bill_air_fee ADD COLUMN alloc_dim char(1) NOT NULL DEFAULT '''' COMMENT ''分摊维度(票),字典 bill_fee_alloc_dim:1订单 2重量。是否分摊为是时必填'' AFTER alloc_flag', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'srv_bill_air_fee' AND COLUMN_NAME = 'alloc_dim' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/bms-back/sql/price/pay_bill.sql b/bms-back/sql/price/pay_bill.sql index 97905bc4..c097d8ee 100644 --- a/bms-back/sql/price/pay_bill.sql +++ b/bms-back/sql/price/pay_bill.sql @@ -12,6 +12,32 @@ PREPARE stmt FROM @fee_source; EXECUTE stmt; DEALLOCATE PREPARE stmt; +SET @sql = ( + SELECT IF( + COUNT(*) = 0, + 'ALTER TABLE prc_pay_bill_fee ADD COLUMN source_id varchar(64) DEFAULT NULL COMMENT ''来源数据id'' AFTER fee_source', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_pay_bill_fee' AND COLUMN_NAME = 'source_id' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF( + COUNT(*) = 0, + 'ALTER TABLE prc_pay_bill_fee ADD COLUMN is_deleted char(1) NOT NULL DEFAULT ''0'' COMMENT ''是否删除:0未删除 1已删除'' AFTER source_id', + 'SELECT 1' + ) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_pay_bill_fee' AND COLUMN_NAME = 'is_deleted' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + CREATE TABLE IF NOT EXISTS `pay_lading` ( `lading_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '提单ID', `lading_no` varchar(64) NOT NULL COMMENT '提单号或航空主单号', diff --git a/bms-front/src/views/price/basic/pay/components/PayImportDialog.vue b/bms-front/src/views/price/basic/pay/components/PayImportDialog.vue index 1bae61f7..701c2960 100644 --- a/bms-front/src/views/price/basic/pay/components/PayImportDialog.vue +++ b/bms-front/src/views/price/basic/pay/components/PayImportDialog.vue @@ -1,11 +1,6 @@ - - - - - - - - - + +
+
+ 变更 {{ compareStat.changed }} + 新增 {{ compareStat.added }} + 删除 {{ compareStat.removed }} + 相同 {{ compareStat.same }} +
+ + + + + + + + + + + + + + + +
{ + const count = (name) => compareAll.value.filter(row => row.change === name).length + return { changed: count('变更'), added: count('新增'), removed: count('删除'), same: count('相同') } +}) const detailTitle = computed(() => { if (!detail.value) return '应付配置' const no = detail.value.versionNo ? `第 ${detail.value.versionNo} 版` : '' @@ -356,23 +389,60 @@ function onHistoryPick(rows) { function openCompare() { if (historyPicked.value.length !== 2) return - const [first, second] = historyPicked.value + const [first, second] = [...historyPicked.value].sort((a, b) => (Number(a.versionNo) || 0) - (Number(b.versionNo) || 0)) + if (!first.productId || !second.productId) { + proxy.$modal.msgError('选中的版本没有价格数据') + return + } + compareTitle.value = `第 ${first.versionNo || '-'} 版 对比 第 ${second.versionNo || '-'} 版` compareVisible.value = true compareLoading.value = true compareRows.value = [] + compareAll.value = [] + compareReady.value = false comparePay(first.productId, second.productId).then(res => { const data = res.data || {} - const older = data.older?.versionNo || '-' - const newer = data.newer?.versionNo || '-' - compareTitle.value = `第 ${older} 版 对比 第 ${newer} 版` - const weights = (data.weightDiffs || []).map(row => ({ ...row, kind: '重量段' })) - const fees = (data.surchargeDiffs || []).map(row => ({ ...row, kind: '附加费' })) - compareRows.value = weights.concat(fees).filter(row => row.change !== '相同') + const older = data.older?.versionNo + const newer = data.newer?.versionNo + if (older && newer) compareTitle.value = `第 ${older} 版 对比 第 ${newer} 版` + const rows = [] + .concat(markKind(data.versionDiffs, '版本')) + .concat(markKind(data.weightDiffs, '重量段')) + .concat(markKind(data.surchargeDiffs, '附加费')) + .concat(markKind(data.extraDiffs, '其他配置')) + compareAll.value = rows + compareRows.value = rows.filter(row => row.change !== '相同') + compareReady.value = true }).finally(() => { compareLoading.value = false }) } +function markKind(rows, kind) { + return (rows || []).map(row => ({ ...row, kind: row.kind || kind })) +} + +function changeTagType(change) { + if (change === '变更') return 'primary' + if (change === '新增') return 'success' + if (change === '删除') return 'danger' + return 'info' +} + +function compareRowClass({ row }) { + if (row.change === '变更') return 'compare-changed' + if (row.change === '新增') return 'compare-added' + if (row.change === '删除') return 'compare-removed' + return '' +} + +function changeTextClass(change, side) { + if (change === '变更') return 'compare-text-changed' + if (change === '删除' && side === 'old') return 'compare-text-removed' + if (change === '新增' && side === 'new') return 'compare-text-added' + return '' +} + function loadHistory() { if (!historyProductId.value) return historyLoading.value = true @@ -446,4 +516,34 @@ function statusType(status) { margin-bottom: 12px; color: var(--el-text-color-secondary); } +.compare-text-changed { + color: var(--el-color-primary); + font-weight: 600; +} +.compare-text-added { + color: var(--el-color-success); + font-weight: 600; +} +.compare-text-removed { + color: var(--el-color-danger); + font-weight: 600; +} +.compare-panel :deep(tr.compare-changed > td.el-table__cell) { + background: var(--el-color-primary-light-9); +} +.compare-panel :deep(tr.compare-added > td.el-table__cell) { + background: var(--el-color-success-light-9); +} +.compare-panel :deep(tr.compare-removed > td.el-table__cell) { + background: var(--el-color-danger-light-9); +} +.compare-panel :deep(tr.compare-changed:hover > td.el-table__cell) { + background: var(--el-color-primary-light-8); +} +.compare-panel :deep(tr.compare-added:hover > td.el-table__cell) { + background: var(--el-color-success-light-8); +} +.compare-panel :deep(tr.compare-removed:hover > td.el-table__cell) { + background: var(--el-color-danger-light-8); +} diff --git a/bms-front/src/views/price/billing/components/AirBillFeeDialog.vue b/bms-front/src/views/price/billing/components/AirBillFeeDialog.vue index 46234e99..25507100 100644 --- a/bms-front/src/views/price/billing/components/AirBillFeeDialog.vue +++ b/bms-front/src/views/price/billing/components/AirBillFeeDialog.vue @@ -1,24 +1,57 @@