diff --git a/bms-back/bms-admin/src/main/resources/i18n/messages.properties b/bms-back/bms-admin/src/main/resources/i18n/messages.properties index bb8b5bf7..02b50f2f 100644 --- a/bms-back/bms-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/bms-admin/src/main/resources/i18n/messages.properties @@ -206,3 +206,6 @@ price.set.detail.duplicate.fixed=产品{0} 国家{1} 重量段{2} {3}(共{4} price.set.detail.duplicate.discount=产品{0}(共{1}条) price.set.detail.duplicate.hasZone=有分区({0}) price.set.detail.duplicate.noZone=无分区 +price.bill.billingFail.product=计费失败:未匹配产品 +price.bill.billingFail.country=计费失败:未匹配国家 +price.bill.billingFail.weight=计费失败:未匹配重量段 diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcOrderBill.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcOrderBill.java index f6dd927b..500b6d68 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcOrderBill.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcOrderBill.java @@ -116,6 +116,8 @@ public class PrcOrderBill extends BaseEntity private String creditArrearsStatus; /** NONE / REFUNDED */ private String refundStatus; + /** 计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段;软失败时金额为0且不落费用明细 */ + private Integer billingFailStatus; private String roundMode; private List incomeFees; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeePreview.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeePreview.java index 8ef72f0b..51edaac8 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeePreview.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/BatchAddIncomeBillFeePreview.java @@ -4,10 +4,8 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; import com.bms.price.domain.PrcOrderBill; -import lombok.Data; /** Preview matched bills before async batch add fee. */ -@Data public class BatchAddIncomeBillFeePreview implements Serializable { private static final long serialVersionUID = 1L; @@ -15,4 +13,24 @@ public class BatchAddIncomeBillFeePreview implements Serializable private int matchCount; /** First N rows for UI preview. */ private List samples = new ArrayList(); + + public int getMatchCount() + { + return matchCount; + } + + public void setMatchCount(int matchCount) + { + this.matchCount = matchCount; + } + + public List getSamples() + { + return samples; + } + + public void setSamples(List samples) + { + this.samples = samples; + } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TmsPriceQuoteResult.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TmsPriceQuoteResult.java index 238c047b..b3a67242 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TmsPriceQuoteResult.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/TmsPriceQuoteResult.java @@ -103,6 +103,8 @@ public class TmsPriceQuoteResult implements Serializable private String creditArrearsStatus; /** NONE / REFUNDED */ private String refundStatus; + /** 计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段(仅上游同步计费软失败) */ + private Integer billingFailStatus; private Integer pieces; private BigDecimal actualWeight; /** Receive weight from upstream TAB_ORDER.WEIGHT */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/enums/BillBillingFailStatus.java b/bms-back/bms-system/src/main/java/com/bms/price/enums/BillBillingFailStatus.java new file mode 100644 index 00000000..cb51d10e --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/enums/BillBillingFailStatus.java @@ -0,0 +1,46 @@ +package com.bms.price.enums; + +/** + * prc_order_bill.billing_fail_status 计费失败状态。 + * 0=成功;1=未匹配产品;2=未匹配国家;3=未匹配重量段。 + */ +public enum BillBillingFailStatus +{ + SUCCESS(0), + PRODUCT(1), + COUNTRY(2), + WEIGHT(3); + + private final int code; + + BillBillingFailStatus(int code) + { + this.code = code; + } + + public int getCode() + { + return code; + } + + public static BillBillingFailStatus of(Integer code) + { + if (code == null) + { + return SUCCESS; + } + for (BillBillingFailStatus status : values()) + { + if (status.code == code.intValue()) + { + return status; + } + } + return null; + } + + public static boolean isFail(Integer code) + { + return code != null && code.intValue() > SUCCESS.code; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java index 96cbe5ae..0731c149 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java @@ -104,4 +104,20 @@ public interface BmsProductCountryMapper @Param("countryCode") String countryCode, @Param("countryName") String countryName, @Param("bizDate") Date bizDate); + + /** Count catalog rows by product (+ bizDate window), ignore country/zone/weight. */ + public int countByProduct( + @Param("productCode") String productCode, + @Param("bizDate") Date bizDate); + + /** + * Count catalog rows by product + country + zone (+ bizDate), ignore weight. + * Used to diagnose country miss vs weight miss. + */ + public int countByProductCountryZone( + @Param("productCode") String productCode, + @Param("countryCode") String countryCode, + @Param("countryName") String countryName, + @Param("zoneCode") String zoneCode, + @Param("bizDate") Date bizDate); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java index 97f45ec8..a9a6a15e 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java @@ -82,6 +82,22 @@ public interface PrcPriceSetDetailMapper @Param("countryCode") String countryCode, @Param("countryName") String countryName); + /** Count FIXED details by version + product (ignore country/zone/weight). */ + public int countByPsvProduct( + @Param("psvId") Long psvId, + @Param("productCode") String productCode); + + /** + * Count FIXED details by version + product + country + zone (ignore weight). + * Used to diagnose country miss vs weight miss. + */ + public int countByPsvProductCountryZone( + @Param("psvId") Long psvId, + @Param("productCode") String productCode, + @Param("countryCode") String countryCode, + @Param("countryName") String countryName, + @Param("zoneCode") String zoneCode); + /** DISCOUNT by product; optional psvId scopes to version */ public PrcPriceSetDetail selectDiscountMatch( @Param("priceSetId") Long priceSetId, diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java index ab59f058..9a26b925 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java @@ -60,6 +60,7 @@ import com.bms.price.domain.vo.TmsPriceQuoteResult; import com.bms.price.enums.BillCreditArrearsStatus; import com.bms.price.enums.BillCustomerActiveStatus; import com.bms.price.enums.BillRefundStatus; +import com.bms.price.enums.BillBillingFailStatus; import com.bms.price.enums.TmsOrderBillMatchType; import com.bms.price.mapper.PrcIncomeBillFeeMapper; import com.bms.price.mapper.PrcIncomeBillMapper; @@ -1129,15 +1130,51 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService bill.setWeightLabel(result.getWeightLabel()); bill.setEffectiveDate(result.getEffectiveDate()); bill.setChargeWeight(resolvePersistChargeWeight(weight, result.getChargeWeight())); - bill.setFreightRate(result.getFreightRate()); - bill.setBaseFreightRate(result.getBaseFreightRate()); - bill.setDiscountPercent(result.getDiscountPercent()); - bill.setRegistrationFee(result.getRegistrationFee()); - bill.setFreightAmount(result.getFreightAmount()); - bill.setSurchargeAmount(result.getSurchargeAmount()); - bill.setIncomeTotal(result.getPayableTotal()); - // 应付总额与产品目录价解耦:试算未产出 pay_total 时留空,不再用 product_amount 兜底 - bill.setPayTotal(result.getPayTotal()); + Integer billingFailStatus = result.getBillingFailStatus(); + if (billingFailStatus == null) + { + billingFailStatus = Integer.valueOf(BillBillingFailStatus.SUCCESS.getCode()); + } + bill.setBillingFailStatus(billingFailStatus); + boolean billingFailed = BillBillingFailStatus.isFail(billingFailStatus); + if (billingFailed) + { + // Soft-fail: persist order snapshot with zero amounts; skip fee detail rows. + bill.setFreightRate(BigDecimal.ZERO); + bill.setBaseFreightRate(BigDecimal.ZERO); + bill.setDiscountPercent(null); + bill.setRegistrationFee(BigDecimal.ZERO); + bill.setFreightAmount(BigDecimal.ZERO); + bill.setSurchargeAmount(BigDecimal.ZERO); + bill.setIncomeTotal(BigDecimal.ZERO); + bill.setPayTotal(BigDecimal.ZERO); + result.setFreightRate(BigDecimal.ZERO); + result.setBaseFreightRate(BigDecimal.ZERO); + result.setDiscountPercent(null); + result.setRegistrationFee(BigDecimal.ZERO); + result.setFreightAmount(BigDecimal.ZERO); + result.setSurchargeAmount(BigDecimal.ZERO); + result.setProductAmount(BigDecimal.ZERO); + result.setRedlineAmount(BigDecimal.ZERO); + result.setCostAmount(BigDecimal.ZERO); + result.setIncomeTotal(BigDecimal.ZERO); + result.setPayTotal(BigDecimal.ZERO); + result.setPayableTotal(BigDecimal.ZERO); + result.setFeeDetails(new ArrayList()); + result.setPayFeeDetails(new ArrayList()); + } + else + { + bill.setFreightRate(result.getFreightRate()); + bill.setBaseFreightRate(result.getBaseFreightRate()); + bill.setDiscountPercent(result.getDiscountPercent()); + bill.setRegistrationFee(result.getRegistrationFee()); + bill.setFreightAmount(result.getFreightAmount()); + bill.setSurchargeAmount(result.getSurchargeAmount()); + bill.setIncomeTotal(result.getPayableTotal()); + // Payable total is decoupled from product catalog; leave null when quote has no pay_total. + bill.setPayTotal(result.getPayTotal()); + } bill.setCurrency(result.getCurrency()); bill.setRoundMode(result.getRoundMode()); // Credit soft-fail before persist so insert/update stores arrears flag in one write. @@ -1155,6 +1192,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService result.setCustomerActiveStatus(customerActiveStatus); result.setCreditArrearsStatus(creditArrearsStatus); result.setRefundStatus(bill.getRefundStatus()); + result.setBillingFailStatus(billingFailStatus); result.setIncomeTotal(bill.getIncomeTotal()); result.setPayTotal(bill.getPayTotal()); result.setPayableTotal(bill.getIncomeTotal()); @@ -1169,23 +1207,34 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService else { orderBillMapper.updatePrcOrderBill(bill); - // Keep manually added fees (manual_flag=1) across rebill - incomeBillFeeMapper.deleteAutoFeesByBillId(bill.getBillId()); + if (billingFailed) + { + // Soft-fail rebill: clear all fee details (no fee rows for failed billing). + incomeBillFeeMapper.deleteByBillId(bill.getBillId()); + } + else + { + // Keep manually added fees (manual_flag=1) across rebill + incomeBillFeeMapper.deleteAutoFeesByBillId(bill.getBillId()); + } // Payable not in scope yet: skip prc_pay_bill_fee cleanup/write. // payBillFeeMapper.deleteByBillId(bill.getBillId()); } PrcIncomeBill incomeBill = persistIncomeBill(bill, result); - List fees = toFeeRows(bill.getBillId(), - incomeBill == null ? null : incomeBill.getIncomeBillId(), result); - if (existed != null) + if (!billingFailed) { - fees = filterAutoFeesAgainstManual(bill.getBillId(), fees); + List fees = toFeeRows(bill.getBillId(), + incomeBill == null ? null : incomeBill.getIncomeBillId(), result); + if (existed != null) + { + fees = filterAutoFeesAgainstManual(bill.getBillId(), fees); + } + if (!fees.isEmpty()) + { + incomeBillFeeMapper.insertBatch(fees); + } } - if (!fees.isEmpty()) - { - incomeBillFeeMapper.insertBatch(fees); - } - if (existed != null) + if (existed != null && !billingFailed) { // Quote totals were written above; recalc so retained manual fees join income/credit TmsPriceQuoteResult refreshed = refreshBillIncomeTotals(bill); @@ -1199,7 +1248,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService result.setFeeDetails(refreshed.getFeeDetails()); } } - else + else if (existed == null) { bill.setRefundStatus(BillRefundStatus.NONE.getCode()); result.setRefundStatus(bill.getRefundStatus()); @@ -1619,6 +1668,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService r.setCustomerActiveStatus(bill.getCustomerActiveStatus()); r.setCreditArrearsStatus(bill.getCreditArrearsStatus()); r.setRefundStatus(bill.getRefundStatus()); + r.setBillingFailStatus(bill.getBillingFailStatus() == null + ? Integer.valueOf(BillBillingFailStatus.SUCCESS.getCode()) : bill.getBillingFailStatus()); r.setFeeDetails(toIncomeFeeLines(incomeFees)); r.setPayFeeDetails(toPayFeeLines(payFees)); PrcIncomeBill incomeBill = incomeBillMapper.selectByBillId(bill.getBillId()); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java index 194b2cfa..96806f60 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -42,6 +42,7 @@ import com.bms.price.domain.vo.TmsPriceBillRequest; import com.bms.price.domain.vo.TmsPriceQuoteFeeLine; import com.bms.price.domain.vo.TmsPriceQuoteRequest; import com.bms.price.domain.vo.TmsPriceQuoteResult; +import com.bms.price.enums.BillBillingFailStatus; import com.bms.price.enums.BillCustomerActiveStatus; import com.bms.price.mapper.BmsProductCountryMapper; import com.bms.price.mapper.BmsProductMapper; @@ -64,7 +65,8 @@ import com.bms.system.service.IBmsProductCodeMappingService; * Shared quote/bill calc: customer bind + package version by bizDate (version effective_time). * Bind pr_begindate/pr_enddate is not used for billing time gate. * Sales price comes from package snapshot only (FIXED detail / DISCOUNT on catalog base). - * No bind / no active version / package miss: quote and bill both fail hard (no PRODUCT_DEFAULT). + * No bind / no active version: quote and bill both fail hard (no PRODUCT_DEFAULT). + * Bill sync soft-fails for product/country/weight miss (billing_fail_status 1/2/3, zero amounts). * product_country is only for product_amount/cost and DISCOUNT freight base. * * @author bms @@ -207,7 +209,11 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService String customerActiveStatus = softResolveCustomer(request); Date bizDate = resolveBizDate(request.getBizDate()); TmsPriceQuoteResult result = doQuote(request, bizDate); - applySurchargesAndFees(result); + if (!BillBillingFailStatus.isFail(result.getBillingFailStatus())) + { + applySurchargesAndFees(result); + result.setBillingFailStatus(BillBillingFailStatus.SUCCESS.getCode()); + } result.setCustomerActiveStatus(customerActiveStatus); return result; } @@ -405,6 +411,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService boolean productExcludedBySetProducts = false; boolean attemptedPriceMatch = false; boolean anyZoneRequired = false; + // Highest specificity among soft-failable misses: WEIGHT(3) > COUNTRY(2) > PRODUCT(1). + int missCode = 0; // Match version by exact bill datetime (effective_time / expire_time), not end-of-day. Date asOf = bizDate != null ? bizDate : new Date(); for (PrcCustomerPriceSet bind : binds) @@ -450,6 +458,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService if (anyProducts != null && !anyProducts.isEmpty()) { productExcludedBySetProducts = true; + missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode()); continue; } } @@ -482,11 +491,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService stampRequestMeta(request, hit); return hit; } + missCode = Math.max(missCode, diagnoseFixedMiss(version.getPsvId(), productCode, cc, cn, zone.zoneCode)); } else if (TYPE_DISCOUNT.equals(setType)) { if (catalog == null) { + missCode = Math.max(missCode, diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate)); continue; } TmsPriceQuoteResult hit = tryDiscount(bind, set, version, catalog, productCode, productName, weight, bizDate); @@ -495,6 +506,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService stampRequestMeta(request, hit); return hit; } + // Discount detail missing for product (or invalid discount) after catalog hit. + missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode()); } } @@ -506,6 +519,15 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService throw new ServiceException(MessageUtils.message(msgKey)); } if (!attemptedPriceMatch && productExcludedBySetProducts) + { + missCode = Math.max(missCode, BillBillingFailStatus.PRODUCT.getCode()); + } + if (request instanceof TmsPriceBillRequest && BillBillingFailStatus.isFail(Integer.valueOf(missCode))) + { + return buildBillingMissResult(request, productCode, productName, countryCode, countryName, + weight, bizDate, missCode); + } + if (!attemptedPriceMatch && productExcludedBySetProducts) { String msgKey = request instanceof TmsPriceBillRequest ? "price.bill.productNotInPackage" @@ -528,6 +550,118 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService throw new ServiceException(MessageUtils.message(missKey)); } + /** + * Diagnose FIXED miss: product rows absent -> 1; country/zone absent -> 2; else weight -> 3. + */ + private int diagnoseFixedMiss(Long psvId, String productCode, String countryCode, + String countryName, String zoneCode) + { + if (psvId == null || StringUtils.isEmpty(productCode)) + { + return BillBillingFailStatus.PRODUCT.getCode(); + } + int byProduct = detailMapper.countByPsvProduct(psvId, productCode); + if (byProduct <= 0) + { + return BillBillingFailStatus.PRODUCT.getCode(); + } + int byCountry = detailMapper.countByPsvProductCountryZone( + psvId, productCode, countryCode, countryName, zoneCode); + if (byCountry <= 0) + { + return BillBillingFailStatus.COUNTRY.getCode(); + } + return BillBillingFailStatus.WEIGHT.getCode(); + } + + /** + * Diagnose catalog miss for DISCOUNT base price. + */ + private int diagnoseCatalogMiss(String productCode, String countryCode, String countryName, + String zoneCode, Date bizDate) + { + if (StringUtils.isEmpty(productCode)) + { + return BillBillingFailStatus.PRODUCT.getCode(); + } + int byProduct = productCountryMapper.countByProduct(productCode, bizDate); + if (byProduct <= 0) + { + return BillBillingFailStatus.PRODUCT.getCode(); + } + int byCountry = productCountryMapper.countByProductCountryZone( + productCode, countryCode, countryName, zoneCode, bizDate); + if (byCountry <= 0) + { + return BillBillingFailStatus.COUNTRY.getCode(); + } + return BillBillingFailStatus.WEIGHT.getCode(); + } + + /** + * Soft-fail bill result: keep order snapshot fields, all amounts zero, set billingFailStatus. + */ + private TmsPriceQuoteResult buildBillingMissResult(TmsPriceQuoteRequest request, String productCode, + String productName, String countryCode, String countryName, BigDecimal weight, Date bizDate, + int missCode) + { + TmsPriceQuoteResult r = new TmsPriceQuoteResult(); + r.setBillingFailStatus(Integer.valueOf(missCode)); + r.setMatchSource("BILLING_FAIL"); + r.setMatchMessage(billingFailMessage(missCode)); + r.setProductCode(productCode); + r.setProductName(productName); + r.setCountryCode(countryCode); + r.setCountryName(countryName); + r.setWeight(weight); + r.setChargeWeight(weight); + r.setBizDate(bizDate); + r.setFreightRate(BigDecimal.ZERO); + r.setBaseFreightRate(BigDecimal.ZERO); + r.setDiscountPercent(null); + r.setRegistrationFee(BigDecimal.ZERO); + r.setFreightAmount(BigDecimal.ZERO); + r.setSurchargeAmount(BigDecimal.ZERO); + r.setProductAmount(BigDecimal.ZERO); + r.setRedlineAmount(BigDecimal.ZERO); + r.setCostAmount(BigDecimal.ZERO); + r.setIncomeTotal(BigDecimal.ZERO); + r.setPayTotal(BigDecimal.ZERO); + r.setPayableTotal(BigDecimal.ZERO); + r.setCurrency("CNY"); + r.setRoundMode("HALF_UP"); + r.setFromCache(Boolean.FALSE); + r.setFeeDetails(Collections.emptyList()); + r.setPayFeeDetails(Collections.emptyList()); + stampRequestMeta(request, r); + if (request instanceof TmsPriceBillRequest) + { + TmsPriceBillRequest billReq = (TmsPriceBillRequest) request; + if (StringUtils.isEmpty(r.getCustomerName()) && StringUtils.isNotEmpty(billReq.getCustomerName())) + { + r.setCustomerName(billReq.getCustomerName()); + } + } + return r; + } + + private String billingFailMessage(int missCode) + { + if (missCode == BillBillingFailStatus.PRODUCT.getCode()) + { + return MessageUtils.message("price.bill.billingFail.product"); + } + if (missCode == BillBillingFailStatus.COUNTRY.getCode()) + { + return MessageUtils.message("price.bill.billingFail.country"); + } + if (missCode == BillBillingFailStatus.WEIGHT.getCode()) + { + return MessageUtils.message("price.bill.billingFail.weight"); + } + return MessageUtils.message("price.bill.packagePriceNotHit"); + } + /** * Soft lookup of package product row; null when version has no such product. */ diff --git a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml index 3793251f..5da98ca7 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml @@ -317,6 +317,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + + + +