From 380577f69188426314840534dbca84e62584a2e5 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 9 Oct 2026 17:20:10 +0800 Subject: [PATCH] Refactor surcharge management by replacing PrcPriceSetVersionSurcharge with PrcSurchargeRel, introducing price type handling (PRODUCT, SALES, REDLINE) and updating related services, mappers, and front-end components. Enhance validation and error messaging for price type and detail ID requirements. Update database schema to support new relationships and constraints. --- .../controller/price/SurchargeController.java | 2 +- .../main/resources/i18n/messages.properties | 2 + .../bms/price/domain/PrcPriceSetDetail.java | 3 +- .../bms/price/domain/PrcPriceSetVersion.java | 1 - .../domain/PrcPriceSetVersionSurcharge.java | 32 -- .../com/bms/price/domain/PrcSurchargeRel.java | 27 +- .../PrcPriceSetVersionSurchargeMapper.java | 36 -- .../price/mapper/PrcSurchargeRelMapper.java | 18 +- .../service/impl/PrcPriceSetServiceImpl.java | 461 ++++++++++++------ .../impl/PrcSurchargeRelServiceImpl.java | 49 +- .../impl/TmsPriceQuoteServiceImpl.java | 115 +++-- .../PrcPriceSetVersionSurchargeMapper.xml | 208 -------- .../mapper/price/PrcSurchargeRelMapper.xml | 48 +- bms-back/sql/bms_init.sql | 43 +- .../prc_surcharge_rel_price_type_alter.sql | 80 +++ .../components/ProductSurchargeDrawer.vue | 9 + .../product/components/SurchargeRelDialog.vue | 30 +- .../set/composables/usePriceSetRefData.js | 2 +- .../src/views/price/set/utils/priceSet.js | 11 +- 19 files changed, 647 insertions(+), 530 deletions(-) delete mode 100644 bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersionSurcharge.java delete mode 100644 bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetVersionSurchargeMapper.java delete mode 100644 bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetVersionSurchargeMapper.xml create mode 100644 bms-back/sql/price/prc_surcharge_rel_price_type_alter.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 05c71a97..42a4719c 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 @@ -78,7 +78,7 @@ public class SurchargeController extends BaseController } /** - * 绑定附加费到产品/明细(body: scId, productId, productCode, detailId;detailId 空=产品级) + * 绑定附加费(body: scId, productId, productCode, detailId, priceType, countryCode;priceType 默认 PRODUCT;countryCode 空=全部) */ @PreAuthorize("@ss.hasPermi('price:surcharge:add')") @Log(title = "附加费关联", businessType = BusinessType.INSERT) 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 399cb02d..ec80c530 100644 --- a/bms-back/bms-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/bms-admin/src/main/resources/i18n/messages.properties @@ -47,6 +47,8 @@ price.surcharge.chargeValue.negative=计费数值不能为负数 price.surchargeRel.scId.empty=附加费ID不能为空 price.surchargeRel.productId.empty=产品ID不能为空 price.surchargeRel.productCode.empty=产品代码不能为空 +price.surchargeRel.priceType.invalid=价格类型必须为 PRODUCT/SALES/REDLINE +price.surchargeRel.detailId.empty=销售价关联必须指定套餐明细ID # 套餐 PrcPriceSet price.set.notExists=套餐不存在 price.set.name.empty=套餐名称不能为空 diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetDetail.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetDetail.java index f433842a..bfc3cb39 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetDetail.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetDetail.java @@ -52,7 +52,8 @@ public class PrcPriceSetDetail implements Serializable /** null = open */ @JsonFormat(pattern = "yyyy-MM-dd") private Date expireDate; - private List surcharges; + /** SALES rels for this detail (detail_id=psd_id, product_id=psp_id) */ + private List surcharges; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java index 4eb440e3..f674724f 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java @@ -34,5 +34,4 @@ public class PrcPriceSetVersion extends BaseEntity private List details; private List products; - private List surcharges; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersionSurcharge.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersionSurcharge.java deleted file mode 100644 index 1d8106b9..00000000 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersionSurcharge.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.bms.price.domain; - -import java.math.BigDecimal; -import com.bms.common.core.domain.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** Price set version surcharge snapshot prc_price_set_version_surcharge */ -@Data -@EqualsAndHashCode(callSuper = true) -public class PrcPriceSetVersionSurcharge extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - private Long psvsId; - private Long psvId; - /** source prc_surcharge.sc_id */ - private Long scId; - /** prc_price_set_detail.psd_id */ - private Long psdId; - /** copied from detail */ - private String productCode; - private String scName; - private String feeType; - /** FIXED / WEIGHT / RATE */ - private String chargeType; - private BigDecimal chargeValue; - private String currency; - private String roundMode; - private String countryCode; - private String scNote; -} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcSurchargeRel.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcSurchargeRel.java index 3432495f..13f32007 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcSurchargeRel.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcSurchargeRel.java @@ -6,13 +6,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 附加费与产品/明细关联 prc_surcharge_rel - * 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN product_country), - * 便于产品列表抽屉直接渲染。 + * 附加费与产品/明细/套餐关联 prc_surcharge_rel + * 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN product_country,仅 PRODUCT/REDLINE)。 * - * 语义: - * detail_id 为空 = 产品级附加费(作用于该产品全部明细) - * detail_id 有值 = 明细级附加费(作用于该产品某个 国家×重量段 明细) + * price_type 语义: + * PRODUCT / REDLINE : product_id=product.product_id,detail_id=product_country.detail_id(空=产品全部明细) + * SALES : product_id=prc_price_set_product.psp_id,detail_id=prc_price_set_detail.psd_id */ @Data @EqualsAndHashCode(callSuper = true) @@ -20,11 +19,24 @@ public class PrcSurchargeRel extends BaseEntity { private static final long serialVersionUID = 1L; + /** 价格类型:产品价 */ + public static final String PRICE_TYPE_PRODUCT = "PRODUCT"; + /** 价格类型:销售价(套餐) */ + public static final String PRICE_TYPE_SALES = "SALES"; + /** 价格类型:红线价 */ + public static final String PRICE_TYPE_REDLINE = "REDLINE"; + private Long relId; private Long scId; + /** PRODUCT/REDLINE=product_id;SALES=psp_id */ private Long productId; private String productCode; + /** PRODUCT/REDLINE=product_country.detail_id;SALES=psd_id */ private Long detailId; + /** PRODUCT / SALES / REDLINE */ + private String priceType; + /** 适用国家ISO2(空=全部),rel 表字段 */ + private String countryCode; /** 附加费冗余展示字段 */ private String scName; @@ -34,10 +46,9 @@ public class PrcSurchargeRel extends BaseEntity private String currency; private String roundMode; private String ssCode; - private String countryCode; private String scNote; - /** 明细展示字段 */ + /** 明细展示字段(PRODUCT/REDLINE LEFT JOIN product_country) */ private String countryName; private BigDecimal weightMin; private BigDecimal weightMax; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetVersionSurchargeMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetVersionSurchargeMapper.java deleted file mode 100644 index 1347df87..00000000 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetVersionSurchargeMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.bms.price.mapper; - -import java.util.List; -import org.apache.ibatis.annotations.Param; -import com.bms.price.domain.PrcPriceSetVersionSurcharge; - -/** - * Price set version surcharge snapshot - */ -public interface PrcPriceSetVersionSurchargeMapper -{ - public PrcPriceSetVersionSurcharge selectPrcPriceSetVersionSurchargeById(Long psvsId); - - public List selectPrcPriceSetVersionSurchargeList(PrcPriceSetVersionSurcharge surcharge); - - public int insertPrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge); - - public int updatePrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge); - - public int deletePrcPriceSetVersionSurchargeByIds(Long[] psvsIds); - - public int deleteByPsvId(Long psvId); - - public int deleteByPsdId(Long psdId); - - public List selectByPsdIds(@Param("psdIds") List psdIds); - - /** Snapshot all applicable product-rel surcharges for details under psvId (library create). */ - public int insertFromLibraryRels(@Param("psvId") Long psvId, @Param("createBy") String createBy); - - /** Copy surcharge snapshots from src version onto new version details (match by detail keys). */ - public int copySurchargesFromVersion( - @Param("srcPsvId") Long srcPsvId, - @Param("newPsvId") Long newPsvId, - @Param("createBy") String createBy); -} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeRelMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeRelMapper.java index e7505f60..70afde5b 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeRelMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeRelMapper.java @@ -5,24 +5,36 @@ import org.apache.ibatis.annotations.Param; import com.bms.price.domain.PrcSurchargeRel; /** - * 附加费与产品/明细关联 数据层 + * 附加费与产品/明细/套餐关联 数据层 * * @author bms */ public interface PrcSurchargeRelMapper { /** - * 查询关联列表(JOIN 附加费 + 明细),支持按 productCode / scId 过滤 + * 查询关联列表(JOIN 附加费 + 明细),支持按 productCode / scId / priceType / detailId 过滤 */ public List selectPrcSurchargeRelList(PrcSurchargeRel rel); + public List selectByDetailIdsAndPriceType( + @Param("detailIds") List detailIds, + @Param("priceType") String priceType); + public int insertPrcSurchargeRel(PrcSurchargeRel rel); public int deletePrcSurchargeRelByIds(Long[] relIds); - /** 删除产品时清理其关联 */ + /** 删除产品时清理 PRODUCT/REDLINE 关联 */ public int deleteRelByProductCodes(String[] productCodes); + public int deleteByDetailIdsAndPriceType( + @Param("detailIds") List detailIds, + @Param("priceType") String priceType); + + public int deleteByProductIdsAndPriceType( + @Param("productIds") List productIds, + @Param("priceType") String priceType); + /** 附加费版本化编辑后,把产品关联从旧记录重指向新记录 */ public int rebindRelScId(@Param("oldScId") Long oldScId, @Param("newScId") Long newScId, @Param("operator") String operator); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java index c844ab6b..b28ee9e3 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java @@ -34,7 +34,6 @@ import com.bms.price.domain.PrcPriceSetDetail; import com.bms.price.domain.PrcPriceSetProduct; import com.bms.price.domain.PrcPriceSetSaveTask; import com.bms.price.domain.PrcPriceSetVersion; -import com.bms.price.domain.PrcPriceSetVersionSurcharge; import com.bms.price.domain.PrcSurcharge; import com.bms.price.domain.PrcSurchargeRel; import com.bms.price.mapper.BmsProductCountryMapper; @@ -44,7 +43,6 @@ import com.bms.price.mapper.PrcPriceSetDetailMapper; import com.bms.price.mapper.PrcPriceSetMapper; import com.bms.price.mapper.PrcPriceSetProductMapper; import com.bms.price.mapper.PrcPriceSetVersionMapper; -import com.bms.price.mapper.PrcPriceSetVersionSurchargeMapper; import com.bms.price.mapper.PrcSurchargeMapper; import com.bms.price.mapper.PrcSurchargeRelMapper; import com.bms.price.service.IBmsProductService; @@ -91,9 +89,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService @Autowired private PrcPriceSetVersionMapper versionMapper; - @Autowired - private PrcPriceSetVersionSurchargeMapper versionSurchargeMapper; - @Autowired private PrcSurchargeRelMapper surchargeRelMapper; @@ -180,10 +175,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { continue; } - // Always replace snapshot for this batch API (null would skip in updateDetail) + // Always replace SALES rels for this batch API (null would skip in updateDetail) if (detail.getSurcharges() == null) { - detail.setSurcharges(new ArrayList()); + detail.setSurcharges(new ArrayList()); } rows += updateDetail(detail); } @@ -474,7 +469,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService if (detailRows > 0) { setProductMapper.bindDetailPspByPsvId(nv.getPsvId()); - versionSurchargeMapper.copySurchargesFromVersion(sourcePsvId, nv.getPsvId(), createBy); + copySalesSurchargeRelsFromVersion(sourcePsvId, nv.getPsvId(), createBy); } refreshVersionStatus(set.getPriceSetId()); return nv.getPsvId(); @@ -527,7 +522,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService throw new ServiceException(MessageUtils.message("price.set.version.delete.statusNotAllowed")); } requireSet(version.getPriceSetId()); - versionSurchargeMapper.deleteByPsvId(psvId); + deleteSalesRelsByPsvId(psvId); detailMapper.deletePrcPriceSetDetailByPsvId(psvId); setProductMapper.deleteByPsvId(psvId); int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId }); @@ -858,7 +853,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return result; } - /** Attach version surcharge snapshots to a page of details (for drawer list). */ + /** Attach SALES surcharge rels (detail_id=psd_id) to a page of details. */ private void fillDetailSurcharges(List details) { if (details == null || details.isEmpty()) @@ -877,21 +872,22 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { return; } - List surcharges = versionSurchargeMapper.selectByPsdIds(psdIds); - Map> byPsd = new HashMap>(); + List surcharges = + surchargeRelMapper.selectByDetailIdsAndPriceType(psdIds, PrcSurchargeRel.PRICE_TYPE_SALES); + Map> byPsd = new HashMap>(); if (surcharges != null) { - for (PrcPriceSetVersionSurcharge sc : surcharges) + for (PrcSurchargeRel sc : surcharges) { - if (sc == null || sc.getPsdId() == null) + if (sc == null || sc.getDetailId() == null) { continue; } - List list = byPsd.get(sc.getPsdId()); + List list = byPsd.get(sc.getDetailId()); if (list == null) { - list = new ArrayList(); - byPsd.put(sc.getPsdId(), list); + list = new ArrayList(); + byPsd.put(sc.getDetailId(), list); } list.add(sc); } @@ -902,8 +898,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { continue; } - List list = byPsd.get(d.getPsdId()); - d.setSurcharges(list != null ? list : new ArrayList()); + List list = byPsd.get(d.getPsdId()); + d.setSurcharges(list != null ? list : new ArrayList()); } } @@ -916,9 +912,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService detail.setPsvId(resolvePsvId(detail.getPriceSetId(), detail.getPsvId())); detail.setPspId(lookupSetProductId(detail.getPsvId(), detail.getProductCode())); int rows = detailMapper.insertPrcPriceSetDetail(detail); - PrcPriceSetVersion ver = new PrcPriceSetVersion(); - ver.setPsvId(detail.getPsvId()); - snapshotVersionSurcharges(ver, java.util.Collections.singletonList(detail)); + if (detail.getSurcharges() != null) + { + replaceSalesSurchargeRels(java.util.Collections.singletonList(detail)); + } return rows; } @@ -1819,13 +1816,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } detail.setPspId(pspId); int rows = detailMapper.updatePrcPriceSetDetail(detail); - // non-null surcharges = replace snapshot for this detail (empty list clears) + // non-null surcharges = replace SALES rels for this detail (empty list clears) if (detail.getSurcharges() != null) { - versionSurchargeMapper.deleteByPsdId(detail.getPsdId()); - PrcPriceSetVersion ver = new PrcPriceSetVersion(); - ver.setPsvId(detail.getPsvId()); - snapshotVersionSurcharges(ver, java.util.Collections.singletonList(detail)); + replaceSalesSurchargeRels(java.util.Collections.singletonList(detail)); } return rows; } @@ -1848,6 +1842,21 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService touchKeys.add(old.getPsvId() + "|" + old.getProductCode()); } } + if (psdIds != null && psdIds.length > 0) + { + List ids = new ArrayList(); + for (Long id : psdIds) + { + if (id != null) + { + ids.add(id); + } + } + if (!ids.isEmpty()) + { + surchargeRelMapper.deleteByDetailIdsAndPriceType(ids, PrcSurchargeRel.PRICE_TYPE_SALES); + } + } int rows = detailMapper.deletePrcPriceSetDetailByIds(psdIds); for (String key : touchKeys) { @@ -1864,6 +1873,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService List left = detailMapper.selectPrcPriceSetDetailList(q); if (left == null || left.isEmpty()) { + Long pspId = lookupSetProductId(psvId, productCode); + if (pspId != null) + { + surchargeRelMapper.deleteByProductIdsAndPriceType( + java.util.Collections.singletonList(pspId), PrcSurchargeRel.PRICE_TYPE_SALES); + } setProductMapper.deleteByPsvIdAndCode(psvId, productCode); } } @@ -2696,9 +2711,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService pq.setPsvId(version.getPsvId()); List products = setProductMapper.selectPrcPriceSetProductList(pq); version.setProducts(products == null ? new ArrayList() : products); - // details + surcharge snapshots are loaded on demand (edit price / detail drawer) + // details + SALES surcharge rels are loaded on demand (edit price / detail drawer) version.setDetails(new ArrayList()); - version.setSurcharges(new ArrayList()); } private PrcPriceSetVersion pickCurrentVersion(List versions) @@ -2782,7 +2796,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { upsertVersionDetails(set, version, details); reportSaveProgress(set, 85, "saving surcharges"); - resnapshotSurchargesIfNeeded(version, details); + replaceSalesSurchargesIfNeeded(details); } else { @@ -2793,7 +2807,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService /** * Draft only: when priceBuildMode changes (PULL/IMPORT), delete this version's - * product / detail / surcharge rows. Same mode or non-draft: no-op. + * product / detail / SALES surcharge rel rows. Same mode or non-draft: no-op. */ private void clearDraftVersionChildrenIfBuildModeChanged(PrcPriceSetVersion db, PrcPriceSetVersion incoming) { @@ -2816,7 +2830,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return; } Long psvId = db.getPsvId(); - versionSurchargeMapper.deleteByPsvId(psvId); + deleteSalesRelsByPsvId(psvId); detailMapper.deletePrcPriceSetDetailByPsvId(psvId); setProductMapper.deleteByPsvId(psvId); } @@ -2852,8 +2866,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } /** - * Bulk create details + surcharge snapshots via SQL joins (no per-product HTTP roundtrips). + * Bulk create details via SQL joins (no per-product HTTP roundtrips). * Idempotent: if this version already has details and forcePullLibrary!=Y, skip re-insert. + * SALES surcharge rels are not auto-created; configure via package surcharge picker. */ private void replaceVersionDetailsFromLibrary(PrcPriceSet set, PrcPriceSetVersion version) { @@ -2867,7 +2882,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService set.setDetails(new ArrayList()); return; } - versionSurchargeMapper.deleteByPsvId(version.getPsvId()); + deleteSalesRelsByPsvId(version.getPsvId()); detailMapper.deletePrcPriceSetDetailByPsvId(version.getPsvId()); setProductMapper.deleteByPsvId(version.getPsvId()); // clean legacy rows without psv_id under this set (would look like duplicates in UI) @@ -2901,8 +2916,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } setProductMapper.bindDetailPspByPsvId(version.getPsvId()); reportSaveProgress(set, 75, "saving details " + inserted + "/" + inserted); - reportSaveProgress(set, 85, "saving surcharges"); - versionSurchargeMapper.insertFromLibraryRels(version.getPsvId(), version.getCreateBy()); fillVersionChildren(version); set.setDetails(new ArrayList()); } @@ -3020,10 +3033,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } if (!orphanIds.isEmpty()) { - for (Long id : orphanIds) - { - versionSurchargeMapper.deleteByPsdId(id); - } + surchargeRelMapper.deleteByDetailIdsAndPriceType(orphanIds, PrcSurchargeRel.PRICE_TYPE_SALES); detailMapper.deletePrcPriceSetDetailByIds(orphanIds.toArray(new Long[0])); } Set keepCodes = new HashSet(); @@ -3187,8 +3197,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return row.getPspId(); } - /** Only rewrite surcharge snapshots for rows that explicitly sent surcharges. */ - private void resnapshotSurchargesIfNeeded(PrcPriceSetVersion version, List details) + /** Only rewrite SALES surcharge rels for rows that explicitly sent surcharges. */ + private void replaceSalesSurchargesIfNeeded(List details) { if (details == null || details.isEmpty()) { @@ -3199,153 +3209,296 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { if (d != null && d.getPsdId() != null && d.getSurcharges() != null) { - versionSurchargeMapper.deleteByPsdId(d.getPsdId()); withSc.add(d); } } if (!withSc.isEmpty()) { - snapshotVersionSurcharges(version, withSc); + replaceSalesSurchargeRels(withSc); } } - private void snapshotVersionSurcharges(PrcPriceSetVersion version, List details) + /** + * Replace SALES prc_surcharge_rel rows for the given package details. + * product_id = psp_id, detail_id = psd_id, price_type = SALES. + * Empty surcharges list clears associations for that detail. + */ + private void replaceSalesSurchargeRels(List details) { if (details == null || details.isEmpty()) { return; } - Set codes = new HashSet(); + Map masterById = null; for (PrcPriceSetDetail d : details) { - if (StringUtils.isNotEmpty(d.getProductCode())) - { - codes.add(d.getProductCode()); - } - } - Map> relsByProduct = new HashMap>(); - for (String code : codes) - { - PrcSurchargeRel q = new PrcSurchargeRel(); - q.setProductCode(code); - List rels = surchargeRelMapper.selectPrcSurchargeRelList(q); - relsByProduct.put(code, rels == null ? new ArrayList() : rels); - } - // 勾选列表来自「价格基础 → 附加费配置」主表(prc_surcharge),不要求产品已做 rel 关联; - // rel 里没有的勾选项,回退主表取值落快照。 - List surchargeMasters = java.util.Collections.emptyList(); - for (PrcPriceSetDetail d0 : details) - { - if (d0.getSurcharges() != null && !d0.getSurcharges().isEmpty()) - { - surchargeMasters = surchargeMapper.selectPrcSurchargeList(new PrcSurcharge()); - break; - } - } - List saved = new ArrayList(); - for (PrcPriceSetDetail d : details) - { - List rels = relsByProduct.get(d.getProductCode()); - if (rels == null) + if (d == null || d.getPsdId() == null || d.getSurcharges() == null) { continue; } - // 前端勾选结果:明细带了 surcharges 就按勾选保存(空集合 = 一条都不要); - // 为 null 表示调用方没传勾选(如详情页新增单条明细),沿用「全部命中即快照」。 - Set pickedScIds = null; - if (d.getSurcharges() != null) + Long pspId = d.getPspId(); + if (pspId == null && d.getPsvId() != null && StringUtils.isNotEmpty(d.getProductCode())) { - pickedScIds = new HashSet(); - for (PrcPriceSetVersionSurcharge picked : d.getSurcharges()) - { - if (picked != null && picked.getScId() != null) - { - pickedScIds.add(picked.getScId()); - } - } + pspId = lookupSetProductId(d.getPsvId(), d.getProductCode()); + d.setPspId(pspId); } - List rowSc = new ArrayList(); + if (pspId == null) + { + continue; + } + surchargeRelMapper.deleteByDetailIdsAndPriceType( + java.util.Collections.singletonList(d.getPsdId()), PrcSurchargeRel.PRICE_TYPE_SALES); + + List saved = new ArrayList(); Set written = new HashSet(); - for (PrcSurchargeRel rel : rels) + for (PrcSurchargeRel picked : d.getSurcharges()) { - if (rel == null || StringUtils.isEmpty(rel.getScName()) || rel.getChargeValue() == null) + if (picked == null || picked.getScId() == null || written.contains(picked.getScId())) { continue; } - if ("of".equalsIgnoreCase(rel.getSsCode())) + PrcSurchargeRel row = buildSalesRel(d, pspId, picked.getScId(), picked.getCountryCode()); + if (row == null) { - continue; - } - if (pickedScIds != null && !pickedScIds.contains(rel.getScId())) - { - continue; - } - if (rel.getDetailId() != null && d.getProductCountryId() != null - && !rel.getDetailId().equals(d.getProductCountryId())) - { - continue; - } - if (rel.getDetailId() != null && d.getProductCountryId() == null) - { - continue; - } - PrcPriceSetVersionSurcharge sc = new PrcPriceSetVersionSurcharge(); - sc.setPsvId(version.getPsvId()); - sc.setPsdId(d.getPsdId()); - sc.setScId(rel.getScId()); - sc.setProductCode(d.getProductCode()); - sc.setScName(rel.getScName()); - sc.setFeeType(StringUtils.isEmpty(rel.getFeeType()) ? "SURCHARGE" : rel.getFeeType()); - sc.setChargeType(StringUtils.isEmpty(rel.getChargeType()) ? "FIXED" : rel.getChargeType()); - sc.setChargeValue(rel.getChargeValue()); - sc.setCurrency(rel.getCurrency()); - sc.setRoundMode(rel.getRoundMode()); - sc.setCountryCode(rel.getCountryCode()); - sc.setScNote(rel.getScNote()); - sc.setCreateBy(version.getCreateBy()); - versionSurchargeMapper.insertPrcPriceSetVersionSurcharge(sc); - saved.add(sc); - rowSc.add(sc); - written.add(rel.getScId()); - } - // 勾选了但产品未关联(rel 无此 scId)的附加费:回退 prc_surcharge 主表配置 - if (pickedScIds != null) - { - for (PrcSurcharge m : surchargeMasters) - { - if (m == null || m.getScId() == null || written.contains(m.getScId()) - || !pickedScIds.contains(m.getScId())) + if (masterById == null) { - continue; + masterById = loadSurchargeMasterMap(); } - if (StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null + PrcSurcharge m = masterById.get(picked.getScId()); + if (m == null || StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null || "of".equalsIgnoreCase(m.getSsCode())) { continue; } - PrcPriceSetVersionSurcharge sc = new PrcPriceSetVersionSurcharge(); - sc.setPsvId(version.getPsvId()); - sc.setPsdId(d.getPsdId()); - sc.setScId(m.getScId()); - sc.setProductCode(d.getProductCode()); - sc.setScName(m.getScName()); - sc.setFeeType(StringUtils.isEmpty(m.getFeeType()) ? "SURCHARGE" : m.getFeeType()); - sc.setChargeType(StringUtils.isEmpty(m.getChargeType()) ? "FIXED" : m.getChargeType()); - sc.setChargeValue(m.getChargeValue()); - sc.setCurrency(m.getCurrency()); - sc.setRoundMode(m.getRoundMode()); - sc.setCountryCode(m.getCountryCode()); - sc.setScNote(m.getScNote()); - sc.setCreateBy(version.getCreateBy()); - versionSurchargeMapper.insertPrcPriceSetVersionSurcharge(sc); - saved.add(sc); - rowSc.add(sc); - written.add(m.getScId()); + row = new PrcSurchargeRel(); + row.setScId(m.getScId()); + row.setProductId(pspId); + row.setProductCode(d.getProductCode()); + row.setDetailId(d.getPsdId()); + row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES); + row.setCountryCode(normalizeCountry(picked.getCountryCode() != null + ? picked.getCountryCode() : m.getCountryCode())); + row.setScName(m.getScName()); + row.setFeeType(m.getFeeType()); + row.setChargeType(m.getChargeType()); + row.setChargeValue(m.getChargeValue()); + row.setCurrency(m.getCurrency()); + row.setRoundMode(m.getRoundMode()); + row.setSsCode(m.getSsCode()); + row.setScNote(m.getScNote()); + row.setCreateBy(SecurityUtils.getUsername()); + } + surchargeRelMapper.insertPrcSurchargeRel(row); + saved.add(row); + written.add(picked.getScId()); + } + d.setSurcharges(saved); + } + } + + private PrcSurchargeRel buildSalesRel(PrcPriceSetDetail d, Long pspId, Long scId, String countryCode) + { + if (d == null || pspId == null || scId == null) + { + return null; + } + PrcSurcharge m = surchargeMapper.selectPrcSurchargeById(scId); + if (m == null || StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null + || "of".equalsIgnoreCase(m.getSsCode())) + { + return null; + } + PrcSurchargeRel row = new PrcSurchargeRel(); + row.setScId(m.getScId()); + row.setProductId(pspId); + row.setProductCode(d.getProductCode()); + row.setDetailId(d.getPsdId()); + row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES); + row.setCountryCode(normalizeCountry(countryCode != null ? countryCode : m.getCountryCode())); + row.setScName(m.getScName()); + row.setFeeType(m.getFeeType()); + row.setChargeType(m.getChargeType()); + row.setChargeValue(m.getChargeValue()); + row.setCurrency(m.getCurrency()); + row.setRoundMode(m.getRoundMode()); + row.setSsCode(m.getSsCode()); + row.setScNote(m.getScNote()); + row.setCreateBy(SecurityUtils.getUsername()); + return row; + } + + private Map loadSurchargeMasterMap() + { + Map map = new HashMap(); + List all = surchargeMapper.selectPrcSurchargeList(new PrcSurcharge()); + if (all == null) + { + return map; + } + for (PrcSurcharge m : all) + { + if (m != null && m.getScId() != null) + { + map.put(m.getScId(), m); + } + } + return map; + } + + private String normalizeCountry(String code) + { + if (StringUtils.isEmpty(code)) + { + return null; + } + return code.trim().toUpperCase(); + } + + /** Delete all SALES surcharge rels under a package version (by detail + set-product ids). */ + private void deleteSalesRelsByPsvId(Long psvId) + { + if (psvId == null) + { + return; + } + PrcPriceSetDetail dq = new PrcPriceSetDetail(); + dq.setPsvId(psvId); + List details = detailMapper.selectPrcPriceSetDetailList(dq); + if (details != null && !details.isEmpty()) + { + List psdIds = new ArrayList(); + for (PrcPriceSetDetail d : details) + { + if (d != null && d.getPsdId() != null) + { + psdIds.add(d.getPsdId()); } } - d.setSurcharges(rowSc); + if (!psdIds.isEmpty()) + { + surchargeRelMapper.deleteByDetailIdsAndPriceType(psdIds, PrcSurchargeRel.PRICE_TYPE_SALES); + } } - version.setSurcharges(saved); + PrcPriceSetProduct pq = new PrcPriceSetProduct(); + pq.setPsvId(psvId); + List products = setProductMapper.selectPrcPriceSetProductList(pq); + if (products != null && !products.isEmpty()) + { + List pspIds = new ArrayList(); + for (PrcPriceSetProduct p : products) + { + if (p != null && p.getPspId() != null) + { + pspIds.add(p.getPspId()); + } + } + if (!pspIds.isEmpty()) + { + surchargeRelMapper.deleteByProductIdsAndPriceType(pspIds, PrcSurchargeRel.PRICE_TYPE_SALES); + } + } + } + + /** + * Copy SALES surcharge rels from source version details onto newly copied details + * (match by product_code + country + zone + weight + product_country_id). + */ + private void copySalesSurchargeRelsFromVersion(Long srcPsvId, Long newPsvId, String createBy) + { + if (srcPsvId == null || newPsvId == null) + { + return; + } + PrcPriceSetDetail sq = new PrcPriceSetDetail(); + sq.setPsvId(srcPsvId); + List srcDetails = detailMapper.selectPrcPriceSetDetailList(sq); + PrcPriceSetDetail nq = new PrcPriceSetDetail(); + nq.setPsvId(newPsvId); + List newDetails = detailMapper.selectPrcPriceSetDetailList(nq); + if (srcDetails == null || srcDetails.isEmpty() || newDetails == null || newDetails.isEmpty()) + { + return; + } + List srcPsdIds = new ArrayList(); + for (PrcPriceSetDetail d : srcDetails) + { + if (d != null && d.getPsdId() != null) + { + srcPsdIds.add(d.getPsdId()); + } + } + List srcRels = + surchargeRelMapper.selectByDetailIdsAndPriceType(srcPsdIds, PrcSurchargeRel.PRICE_TYPE_SALES); + if (srcRels == null || srcRels.isEmpty()) + { + return; + } + Map> bySrcPsd = new HashMap>(); + for (PrcSurchargeRel rel : srcRels) + { + if (rel == null || rel.getDetailId() == null) + { + continue; + } + List list = bySrcPsd.get(rel.getDetailId()); + if (list == null) + { + list = new ArrayList(); + bySrcPsd.put(rel.getDetailId(), list); + } + list.add(rel); + } + Map newByKey = new HashMap(); + for (PrcPriceSetDetail nd : newDetails) + { + if (nd != null) + { + newByKey.put(detailMatchKey(nd), nd); + } + } + for (PrcPriceSetDetail od : srcDetails) + { + if (od == null || od.getPsdId() == null) + { + continue; + } + PrcPriceSetDetail nd = newByKey.get(detailMatchKey(od)); + if (nd == null || nd.getPsdId() == null || nd.getPspId() == null) + { + continue; + } + List rels = bySrcPsd.get(od.getPsdId()); + if (rels == null || rels.isEmpty()) + { + continue; + } + for (PrcSurchargeRel src : rels) + { + PrcSurchargeRel row = new PrcSurchargeRel(); + row.setScId(src.getScId()); + row.setProductId(nd.getPspId()); + row.setProductCode(nd.getProductCode()); + row.setDetailId(nd.getPsdId()); + row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES); + row.setCountryCode(normalizeCountry(src.getCountryCode())); + row.setCreateBy(createBy); + surchargeRelMapper.insertPrcSurchargeRel(row); + } + } + } + + private String detailMatchKey(PrcPriceSetDetail d) + { + if (d == null) + { + return ""; + } + return StringUtils.nvl(d.getProductCode(), "") + + "|" + StringUtils.nvl(d.getCountryCode(), "") + + "|" + StringUtils.nvl(d.getZoneCode(), "") + + "|" + StringUtils.nvl(d.getWeightLabel(), "") + + "|" + (d.getProductCountryId() == null ? "0" : d.getProductCountryId().toString()) + + "|" + (d.getDiscountValue() == null ? "-1" : d.getDiscountValue().toPlainString()); } /** True when package already has a true draft (status=0). Pending(3) does not block copy. */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcSurchargeRelServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcSurchargeRelServiceImpl.java index 0b352851..8ae35c81 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcSurchargeRelServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcSurchargeRelServiceImpl.java @@ -11,7 +11,7 @@ import com.bms.price.mapper.PrcSurchargeRelMapper; import com.bms.price.service.IPrcSurchargeRelService; /** - * 附加费与产品/明细关联 服务层实现 + * 附加费与产品/明细/套餐关联 服务层实现 * * @author bms */ @@ -26,7 +26,27 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService { PrcSurchargeRel query = new PrcSurchargeRel(); query.setProductCode(productCode); - return relMapper.selectPrcSurchargeRelList(query); + List list = relMapper.selectPrcSurchargeRelList(query); + if (list == null || list.isEmpty()) + { + return list; + } + // Product drawer: PRODUCT + REDLINE only (SALES binds psp_id/psd_id) + List filtered = new java.util.ArrayList(); + for (PrcSurchargeRel rel : list) + { + if (rel == null) + { + continue; + } + String type = rel.getPriceType(); + if (StringUtils.isEmpty(type) || PrcSurchargeRel.PRICE_TYPE_PRODUCT.equals(type) + || PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(type)) + { + filtered.add(rel); + } + } + return filtered; } @Override @@ -39,6 +59,18 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService public int insertPrcSurchargeRel(PrcSurchargeRel rel) { validate(rel); + if (StringUtils.isEmpty(rel.getPriceType())) + { + rel.setPriceType(PrcSurchargeRel.PRICE_TYPE_PRODUCT); + } + if (StringUtils.isNotEmpty(rel.getCountryCode())) + { + rel.setCountryCode(rel.getCountryCode().trim().toUpperCase()); + } + else + { + rel.setCountryCode(null); + } return relMapper.insertPrcSurchargeRel(rel); } @@ -76,5 +108,18 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService { throw new ServiceException(MessageUtils.message("price.surchargeRel.productCode.empty")); } + String type = StringUtils.isEmpty(rel.getPriceType()) + ? PrcSurchargeRel.PRICE_TYPE_PRODUCT : rel.getPriceType().trim().toUpperCase(); + if (!PrcSurchargeRel.PRICE_TYPE_PRODUCT.equals(type) + && !PrcSurchargeRel.PRICE_TYPE_SALES.equals(type) + && !PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(type)) + { + throw new ServiceException(MessageUtils.message("price.surchargeRel.priceType.invalid")); + } + rel.setPriceType(type); + if (PrcSurchargeRel.PRICE_TYPE_SALES.equals(type) && rel.getDetailId() == null) + { + throw new ServiceException(MessageUtils.message("price.surchargeRel.detailId.empty")); + } } } 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 6accd715..e280ce20 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 @@ -30,7 +30,6 @@ import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetDetail; import com.bms.price.domain.PrcPriceSetProduct; import com.bms.price.domain.PrcPriceSetVersion; -import com.bms.price.domain.PrcPriceSetVersionSurcharge; import com.bms.price.domain.PrcQuote; import com.bms.price.domain.PrcQuoteFee; import com.bms.price.domain.PrcSurchargeRel; @@ -48,7 +47,6 @@ import com.bms.price.mapper.PrcPriceSetDetailMapper; import com.bms.price.mapper.PrcPriceSetMapper; import com.bms.price.mapper.PrcPriceSetProductMapper; import com.bms.price.mapper.PrcPriceSetVersionMapper; -import com.bms.price.mapper.PrcPriceSetVersionSurchargeMapper; import com.bms.price.mapper.PrcQuoteFeeMapper; import com.bms.price.mapper.PrcQuoteMapper; import com.bms.price.mapper.PrcSurchargeRelMapper; @@ -87,9 +85,6 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService @Autowired private PrcPriceSetVersionMapper versionMapper; - @Autowired - private PrcPriceSetVersionSurchargeMapper versionSurchargeMapper; - @Autowired private PrcPriceSetDetailMapper detailMapper; @@ -973,7 +968,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService RoundingMode rm = resolveRoundingMode(result.getRoundMode()); String currency = normalizeCurrency(result.getCurrency()); - List rels = loadMatchedSurcharges(result); + List salesRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_SALES); + List productRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_PRODUCT); + List redlineRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_REDLINE); + List lines = new ArrayList(); BigDecimal productFreight = productAmountOf(result.getPayFeeDetails(), FEE_BASIC); BigDecimal productReg = productAmountOf(result.getPayFeeDetails(), FEE_REG); @@ -986,7 +984,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService BigDecimal surchargeTotal = BigDecimal.ZERO; Map surchargeLines = new LinkedHashMap(); - for (PrcSurchargeRel rel : rels) + for (PrcSurchargeRel rel : salesRels) { BigDecimal itemAmount = calcSurchargeAmount(rel, result.getWeight(), freightAmount); if (itemAmount == null) @@ -1017,41 +1015,90 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService result.setPayableTotal(freightAmount.add(registrationFee).add(surchargeTotal).setScale(2, rm)); result.setIncomeTotal(result.getPayableTotal()); result.setFeeDetails(lines); + + BigDecimal productExtra = sumSurchargeAmounts(productRels, result.getWeight(), freightAmount, rm); + BigDecimal redlineExtra = sumSurchargeAmounts(redlineRels, result.getWeight(), freightAmount, rm); + if (result.getProductAmount() != null || productExtra.compareTo(BigDecimal.ZERO) > 0) + { + result.setProductAmount(nvl(result.getProductAmount()).add(productExtra).setScale(2, rm)); + } + if (result.getCostAmount() != null || productExtra.compareTo(BigDecimal.ZERO) > 0) + { + result.setCostAmount(nvl(result.getCostAmount()).add(productExtra).setScale(2, rm)); + } + if (result.getPayTotal() != null || productExtra.compareTo(BigDecimal.ZERO) > 0) + { + result.setPayTotal(nvl(result.getPayTotal()).add(productExtra).setScale(2, rm)); + } + if (result.getRedlineAmount() != null || redlineExtra.compareTo(BigDecimal.ZERO) > 0) + { + result.setRedlineAmount(nvl(result.getRedlineAmount()).add(redlineExtra).setScale(2, rm)); + } + else if (redlineExtra.compareTo(BigDecimal.ZERO) > 0) + { + result.setRedlineAmount(redlineExtra); + } } - private List loadMatchedSurcharges(TmsPriceQuoteResult result) + private BigDecimal sumSurchargeAmounts(List rels, BigDecimal weight, + BigDecimal freightAmount, RoundingMode rm) + { + BigDecimal total = BigDecimal.ZERO; + if (rels == null) + { + return total; + } + for (PrcSurchargeRel rel : rels) + { + BigDecimal item = calcSurchargeAmount(rel, weight, freightAmount); + if (item != null) + { + total = total.add(item); + } + } + return total.setScale(2, rm); + } + + /** + * Load matched surcharge rels by price_type. + * SALES: package detail_id = psd_id. + * PRODUCT / REDLINE: product catalog associations by product_code + country/detail. + */ + private List loadMatchedSurcharges(TmsPriceQuoteResult result, String priceType) { List matched = new ArrayList(); - if (result == null || StringUtils.isEmpty(result.getProductCode())) + if (result == null || StringUtils.isEmpty(priceType)) { return matched; } - boolean packageHit = SRC_FIXED.equals(result.getMatchSource()) || SRC_DISCOUNT.equals(result.getMatchSource()); - // Package sales use version surcharge snapshot only (empty snapshot = no surcharge). - if (packageHit) + if (PrcSurchargeRel.PRICE_TYPE_SALES.equals(priceType)) { if (result.getPsdId() == null) { return matched; } - List snaps = - versionSurchargeMapper.selectByPsdIds(Collections.singletonList(result.getPsdId())); - if (snaps == null || snaps.isEmpty()) + List rels = surchargeRelMapper.selectByDetailIdsAndPriceType( + Collections.singletonList(result.getPsdId()), PrcSurchargeRel.PRICE_TYPE_SALES); + if (rels == null) { return matched; } - for (PrcPriceSetVersionSurcharge snap : snaps) + for (PrcSurchargeRel rel : rels) { - PrcSurchargeRel rel = toRelFromVersionSnap(snap); - if (surchargeApplies(rel, result)) + if (surchargeApplies(rel, result, true)) { matched.add(rel); } } return matched; } + if (StringUtils.isEmpty(result.getProductCode())) + { + return matched; + } PrcSurchargeRel query = new PrcSurchargeRel(); query.setProductCode(result.getProductCode()); + query.setPriceType(priceType); List rels = surchargeRelMapper.selectPrcSurchargeRelList(query); if (rels == null) { @@ -1059,7 +1106,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService } for (PrcSurchargeRel rel : rels) { - if (surchargeApplies(rel, result)) + if (surchargeApplies(rel, result, false)) { matched.add(rel); } @@ -1067,28 +1114,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService return matched; } - private PrcSurchargeRel toRelFromVersionSnap(PrcPriceSetVersionSurcharge snap) - { - PrcSurchargeRel rel = new PrcSurchargeRel(); - if (snap == null) - { - return rel; - } - rel.setScId(snap.getScId()); - rel.setProductCode(snap.getProductCode()); - rel.setScName(snap.getScName()); - rel.setFeeType(snap.getFeeType()); - rel.setChargeType(snap.getChargeType()); - rel.setChargeValue(snap.getChargeValue()); - rel.setCurrency(snap.getCurrency()); - rel.setRoundMode(snap.getRoundMode()); - rel.setCountryCode(snap.getCountryCode()); - rel.setScNote(snap.getScNote()); - rel.setSsCode("on"); - return rel; - } - - private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result) + /** + * @param salesRel true = SALES (detail_id already = psd_id); false = PRODUCT/REDLINE detail match + */ + private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result, boolean salesRel) { if (rel == null) { @@ -1103,6 +1132,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { return false; } + if (salesRel) + { + return true; + } if (rel.getDetailId() == null) { return true; diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetVersionSurchargeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetVersionSurchargeMapper.xml deleted file mode 100644 index 65acb22e..00000000 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetVersionSurchargeMapper.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - select psvs_id, psv_id, sc_id, product_code, psd_id, sc_name, fee_type, charge_type, - charge_value, currency, round_mode, country_code, sc_note, - created_by, create_time, updated_by, update_time - from prc_price_set_version_surcharge - - - - - - - - insert into prc_price_set_version_surcharge ( - psv_id, - psd_id, - sc_id, - product_code, - sc_name, - fee_type, - charge_type, - charge_value, - currency, - round_mode, - country_code, - sc_note, - created_by, - create_time - ) values ( - #{psvId}, - #{psdId}, - #{scId}, - #{productCode}, - #{scName}, - #{feeType}, - #{chargeType}, - #{chargeValue}, - #{currency}, - #{roundMode}, - #{countryCode}, - #{scNote}, - #{createBy}, - sysdate() - ) - - - - update prc_price_set_version_surcharge - - psd_id = #{psdId}, - sc_id = #{scId}, - product_code = #{productCode}, - sc_name = #{scName}, - fee_type = #{feeType}, - charge_type = #{chargeType}, - charge_value = #{chargeValue}, - currency = #{currency}, - round_mode = #{roundMode}, - country_code = #{countryCode}, - sc_note = #{scNote}, - updated_by = #{updateBy}, - update_time = sysdate() - - where psvs_id = #{psvsId} - - - - delete from prc_price_set_version_surcharge where psvs_id in - - #{psvsId} - - - - - delete from prc_price_set_version_surcharge where psv_id = #{psvId} - - - - delete from prc_price_set_version_surcharge where psd_id = #{psdId} - - - - - - - insert into prc_price_set_version_surcharge ( - psv_id, psd_id, sc_id, product_code, sc_name, fee_type, charge_type, - charge_value, currency, round_mode, country_code, sc_note, - created_by, create_time - ) - select - d.psv_id, - d.psd_id, - r.sc_id, - d.product_code, - s.sc_name, - ifnull(nullif(s.fee_type, ''), 'SURCHARGE'), - ifnull(nullif(s.charge_type, ''), 'FIXED'), - s.charge_value, - ifnull(nullif(s.currency, ''), 'CNY'), - ifnull(nullif(s.round_mode, ''), 'HALF_UP'), - s.country_code, - s.sc_note, - #{createBy}, - sysdate() - from prc_price_set_detail d - inner join prc_surcharge_rel r on r.product_code = d.product_code - inner join prc_surcharge s on s.sc_id = r.sc_id - where d.psv_id = #{psvId} - and ifnull(s.sc_name, '') <> '' - and s.charge_value is not null - and lower(ifnull(s.ss_code, '')) not in ('of', 'vd') - and (r.detail_id is null or r.detail_id = d.product_country_id) - and ( - s.country_code is null or s.country_code = '' - or d.country_code is null or d.country_code = '' - or upper(s.country_code) = upper(d.country_code) - ) - - - - insert into prc_price_set_version_surcharge ( - psv_id, psd_id, sc_id, product_code, sc_name, fee_type, charge_type, - charge_value, currency, round_mode, country_code, sc_note, - created_by, create_time - ) - select - #{newPsvId}, - nd.psd_id, - sc.sc_id, - sc.product_code, - sc.sc_name, - sc.fee_type, - sc.charge_type, - sc.charge_value, - sc.currency, - sc.round_mode, - sc.country_code, - sc.sc_note, - #{createBy}, - sysdate() - from prc_price_set_version_surcharge sc - inner join prc_price_set_detail od - on od.psd_id = sc.psd_id and od.psv_id = #{srcPsvId} - inner join prc_price_set_detail nd - on nd.psv_id = #{newPsvId} - and nd.product_code = od.product_code - and ifnull(nd.country_code, '') = ifnull(od.country_code, '') - and ifnull(nd.zone_code, '') = ifnull(od.zone_code, '') - and ifnull(nd.weight_label, '') = ifnull(od.weight_label, '') - and ifnull(nd.product_country_id, 0) = ifnull(od.product_country_id, 0) - and ifnull(nd.discount_value, -1) = ifnull(od.discount_value, -1) - where sc.psv_id = #{srcPsvId} - - - diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeRelMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeRelMapper.xml index a6736928..323c7bdc 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeRelMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeRelMapper.xml @@ -10,6 +10,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -17,7 +19,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - @@ -30,13 +31,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select r.rel_id, r.sc_id, r.product_id, r.product_code, r.detail_id, + r.price_type, r.country_code, r.created_by, r.create_time, r.updated_by, r.update_time, s.sc_name, s.fee_type, s.charge_type, s.charge_value, s.currency, - s.round_mode, s.ss_code, s.country_code, s.sc_note, + s.round_mode, s.ss_code, s.sc_note, c.country_name, c.weight_min, c.weight_max from prc_surcharge_rel r join prc_surcharge s on s.sc_id = r.sc_id left join product_country c on c.detail_id = r.detail_id + and r.price_type in ('PRODUCT', 'REDLINE') + + insert into prc_surcharge_rel ( - sc_id, product_id, product_code, detail_id, + sc_id, product_id, product_code, detail_id, price_type, country_code, created_by, create_time ) values ( #{scId}, #{productId}, #{productCode}, #{detailId}, + #{priceType}, #{countryCode}, #{createBy}, sysdate() ) @@ -79,6 +102,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{code} + and price_type in ('PRODUCT', 'REDLINE') + + + + delete from prc_surcharge_rel + where price_type = #{priceType} + and detail_id in + + #{id} + + + + + delete from prc_surcharge_rel + where price_type = #{priceType} + and product_id in + + #{id} + diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql index 84de9eb0..64165088 100644 --- a/bms-back/sql/bms_init.sql +++ b/bms-back/sql/bms_init.sql @@ -1351,36 +1351,6 @@ CREATE TABLE `prc_price_set_product` ( CONSTRAINT `fk_psp_set` FOREIGN KEY (`price_set_id`) REFERENCES `prc_price_set` (`price_set_id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐产品主表-与产品库快照独立'; --- ---------------------------- --- 表结构:`prc_price_set_version_surcharge` --- ---------------------------- -CREATE TABLE `prc_price_set_version_surcharge` ( - `psvs_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '版本附加费id', - `psv_id` bigint(20) NOT NULL COMMENT '套餐版本id', - `psd_id` bigint(20) NOT NULL COMMENT '套餐明细id', - `sc_id` bigint(20) DEFAULT NULL COMMENT '来源附加费配置id', - `product_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品代码(与明细一致)', - `sc_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '附加费名称快照', - `fee_type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'SURCHARGE' COMMENT '费用类型(字典bms_fee_type)', - `charge_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '计费方式 FIXED固定金额/WEIGHT按重量/RATE按比例', - `charge_value` decimal(18,6) NOT NULL COMMENT '计费数值快照', - `currency` char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '币种', - `round_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)', - `country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '适用国家ISO2(空=全部)', - `sc_note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - `created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`psvs_id`), - KEY `idx_psvs_psv` (`psv_id`), - KEY `idx_psvs_psd` (`psd_id`), - KEY `idx_psvs_product` (`psv_id`,`product_code`), - KEY `idx_psvs_sc` (`sc_id`), - CONSTRAINT `fk_psvs_psd` FOREIGN KEY (`psd_id`) REFERENCES `prc_price_set_detail` (`psd_id`) ON DELETE CASCADE, - CONSTRAINT `fk_psvs_psv` FOREIGN KEY (`psv_id`) REFERENCES `prc_price_set_version` (`psv_id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=487 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐版本附加费快照'; - -- ---------------------------- -- 表结构:`prc_quote` -- ---------------------------- @@ -1488,22 +1458,25 @@ CREATE TABLE `prc_surcharge` ( CREATE TABLE `prc_surcharge_rel` ( `rel_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '关联ID', `sc_id` bigint(20) NOT NULL COMMENT '附加费配置ID prc_surcharge.sc_id', - `product_id` bigint(20) NOT NULL COMMENT '产品ID product.product_id', + `product_id` bigint(20) NOT NULL COMMENT '产品ID或套餐产品ID:PRODUCT/REDLINE=product.product_id;SALES=prc_price_set_product.psp_id', `product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品对接代码 product.product_code', - `detail_id` bigint(20) DEFAULT NULL COMMENT '产品明细ID product_country.detail_id(空=作用于产品全部明细)', + `detail_id` bigint(20) DEFAULT NULL COMMENT '明细ID:PRODUCT/REDLINE=product_country.detail_id(空=产品全部);SALES=prc_price_set_detail.psd_id', + `price_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PRODUCT' COMMENT '价格类型 PRODUCT产品价/SALES销售价/REDLINE红线价', + `country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '适用国家ISO2(空=全部)', `created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人', `create_time` datetime DEFAULT NULL, `updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人', `update_time` datetime DEFAULT NULL, PRIMARY KEY (`rel_id`), - UNIQUE KEY `uk_rel_scope` (`sc_id`,`product_id`,`detail_id`), + UNIQUE KEY `uk_rel_scope` (`sc_id`,`product_id`,`detail_id`,`price_type`,`country_code`), KEY `idx_rel_sc` (`sc_id`), KEY `idx_rel_product` (`product_id`), KEY `idx_rel_product_code` (`product_code`), KEY `idx_rel_detail` (`detail_id`), - CONSTRAINT `fk_rel_product` FOREIGN KEY (`product_id`) REFERENCES `product` (`product_id`) ON DELETE CASCADE, + KEY `idx_rel_price_type` (`price_type`), + KEY `idx_rel_country` (`country_code`), CONSTRAINT `fk_rel_sc` FOREIGN KEY (`sc_id`) REFERENCES `prc_surcharge` (`sc_id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='附加费与产品/明细关联表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='附加费关联表(按price_type多态关联产品或套餐)'; -- ---------------------------- -- 表结构:`prc_weight_section_detail` diff --git a/bms-back/sql/price/prc_surcharge_rel_price_type_alter.sql b/bms-back/sql/price/prc_surcharge_rel_price_type_alter.sql new file mode 100644 index 00000000..bb70e5e6 --- /dev/null +++ b/bms-back/sql/price/prc_surcharge_rel_price_type_alter.sql @@ -0,0 +1,80 @@ +-- prc_surcharge_rel: add price_type + country_code; drop version surcharge snapshot +-- Run once on existing DB + +-- 1) drop FK to product (SALES rows store psp_id in product_id) +SET @fk_exists := ( + SELECT COUNT(1) FROM information_schema.TABLE_CONSTRAINTS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_surcharge_rel' + AND CONSTRAINT_NAME = 'fk_rel_product' AND CONSTRAINT_TYPE = 'FOREIGN KEY' +); +SET @sql := IF(@fk_exists > 0, + 'ALTER TABLE `prc_surcharge_rel` DROP FOREIGN KEY `fk_rel_product`', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 2) price_type +SET @col_exists := ( + SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_surcharge_rel' AND COLUMN_NAME = 'price_type' +); +SET @sql := IF(@col_exists = 0, + CONCAT( + 'ALTER TABLE `prc_surcharge_rel` ', + 'ADD COLUMN `price_type` varchar(20) NOT NULL DEFAULT ''PRODUCT'' ', + 'COMMENT ''价格类型 PRODUCT产品价/SALES销售价/REDLINE红线价'' AFTER `detail_id`' + ), + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 3) country_code on rel +SET @col_exists := ( + SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_surcharge_rel' AND COLUMN_NAME = 'country_code' +); +SET @sql := IF(@col_exists = 0, + CONCAT( + 'ALTER TABLE `prc_surcharge_rel` ', + 'ADD COLUMN `country_code` char(2) DEFAULT NULL ', + 'COMMENT ''适用国家ISO2(空=全部)'' AFTER `price_type`' + ), + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 4) backfill country from surcharge master when rel country empty +UPDATE prc_surcharge_rel r +INNER JOIN prc_surcharge s ON s.sc_id = r.sc_id +SET r.country_code = s.country_code +WHERE (r.country_code IS NULL OR r.country_code = '') + AND s.country_code IS NOT NULL AND s.country_code <> ''; + +-- 5) rebuild unique key +SET @uk_exists := ( + SELECT COUNT(1) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_surcharge_rel' AND INDEX_NAME = 'uk_rel_scope' +); +SET @sql := IF(@uk_exists > 0, + 'ALTER TABLE `prc_surcharge_rel` DROP INDEX `uk_rel_scope`', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @uk_exists2 := ( + SELECT COUNT(1) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_surcharge_rel' AND INDEX_NAME = 'uk_rel_scope' +); +SET @sql := IF(@uk_exists2 = 0, + 'ALTER TABLE `prc_surcharge_rel` ADD UNIQUE KEY `uk_rel_scope` (`sc_id`, `product_id`, `detail_id`, `price_type`, `country_code`)', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 6) drop version surcharge snapshot table +DROP TABLE IF EXISTS `prc_price_set_version_surcharge`; diff --git a/bms-front/src/views/price/basic/product/components/ProductSurchargeDrawer.vue b/bms-front/src/views/price/basic/product/components/ProductSurchargeDrawer.vue index 25c18bdf..72cee261 100644 --- a/bms-front/src/views/price/basic/product/components/ProductSurchargeDrawer.vue +++ b/bms-front/src/views/price/basic/product/components/ProductSurchargeDrawer.vue @@ -5,6 +5,9 @@ + + + @@ -98,6 +101,12 @@ function handleRelDelete(relId) { }).catch(() => {}) } +function priceTypeLabel(val) { + if (val === 'REDLINE') return '红线价' + if (val === 'SALES') return '销售价' + return '产品价' +} + function chargeTypeLabel(val) { const found = bms_charge_type.value.find(c => c.value === val) return found ? found.label : (val || '-') diff --git a/bms-front/src/views/price/basic/product/components/SurchargeRelDialog.vue b/bms-front/src/views/price/basic/product/components/SurchargeRelDialog.vue index a8b61054..acf8b52c 100644 --- a/bms-front/src/views/price/basic/product/components/SurchargeRelDialog.vue +++ b/bms-front/src/views/price/basic/product/components/SurchargeRelDialog.vue @@ -6,6 +6,22 @@ + + + + + + + + + + + 产品级(全部明细) @@ -30,6 +46,7 @@