From bb240746a6dc22f28b6731ab485b1135f5d5faf2 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 18 Sep 2026 17:09:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E7=AE=97=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/price/PriceSetController.java | 17 +- .../controller/price/ProductController.java | 11 ++ .../main/resources/i18n/messages.properties | 1 + .../price/mapper/BmsProductCountryMapper.java | 3 + .../price/mapper/PrcPriceSetDetailMapper.java | 3 + .../price/service/IBmsProductService.java | 5 + .../price/service/IPrcPriceSetService.java | 7 + .../service/impl/BmsProductServiceImpl.java | 16 ++ .../service/impl/PrcPriceSetServiceImpl.java | 47 +++++ .../impl/TmsPriceQuoteServiceImpl.java | 5 + .../price/task/BmsStatusRefreshTask.java | 4 +- .../impl/BmsRegionSyncServiceImpl.java | 18 ++ .../mapper/price/BmsProductCountryMapper.xml | 17 ++ .../mapper/price/PrcPriceSetDetailMapper.xml | 7 + .../sql/price/price_set_force_edit_menu.sql | 10 ++ bms-front/src/api/price/product.js | 9 + bms-front/src/api/price/set.js | 9 + .../components/ProductCountryDrawer.vue | 166 +++++++++++++++++- .../src/views/price/basic/product/index.vue | 10 +- .../set/components/create/StepDetails.vue | 7 - .../detail/SetVersionDetailDrawer.vue | 95 ++++++++-- .../set/components/detail/SetVersionTable.vue | 12 +- 22 files changed, 435 insertions(+), 44 deletions(-) create mode 100644 bms-back/sql/price/price_set_force_edit_menu.sql diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceSetController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceSetController.java index 043f1b3a..6629c282 100644 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceSetController.java +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceSetController.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; @@ -183,7 +184,17 @@ public class PriceSetController extends BaseController return getDataTable(list); } - @PreAuthorize("@ss.hasPermi('price:set:edit')") + /** + * 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 + */ + @PreAuthorize("@ss.hasPermi('price:set:list')") + @GetMapping("/detail/options") + public AjaxResult detailOptions(@RequestParam("psvId") Long psvId) + { + return success(priceSetService.selectDetailOptions(psvId)); + } + + @PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')") @Log(title = "套餐明细", businessType = BusinessType.INSERT) @PostMapping("/detail") public AjaxResult addDetail(@RequestBody PrcPriceSetDetail detail) @@ -191,7 +202,7 @@ public class PriceSetController extends BaseController return toAjax(priceSetService.insertDetail(detail)); } - @PreAuthorize("@ss.hasPermi('price:set:edit')") + @PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')") @Log(title = "套餐明细", businessType = BusinessType.UPDATE) @PutMapping("/detail") public AjaxResult editDetail(@RequestBody PrcPriceSetDetail detail) @@ -199,7 +210,7 @@ public class PriceSetController extends BaseController return toAjax(priceSetService.updateDetail(detail)); } - @PreAuthorize("@ss.hasPermi('price:set:edit')") + @PreAuthorize("@ss.hasPermi('price:set:edit') or @ss.hasPermi('price:set:forceEdit')") @Log(title = "套餐明细", businessType = BusinessType.DELETE) @DeleteMapping("/detail/{psdIds}") public AjaxResult removeDetail(@PathVariable Long[] psdIds) diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/ProductController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/ProductController.java index 6f794ae0..8a1cf507 100644 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/ProductController.java +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/ProductController.java @@ -80,6 +80,17 @@ public class ProductController extends BaseController return toAjax(productService.updateBmsProduct(product)); } + /** + * 修改产品国家明细行(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期) + */ + @PreAuthorize("@ss.hasPermi('price:product:edit')") + @Log(title = "产品国家明细", businessType = BusinessType.UPDATE) + @PutMapping("/country") + public AjaxResult editCountry(@RequestBody BmsProductCountry country) + { + return toAjax(productService.updateBmsProductCountry(country)); + } + @PreAuthorize("@ss.hasPermi('price:product:remove')") @Log(title = "产品列表", businessType = BusinessType.DELETE) @DeleteMapping("/{productIds}") diff --git a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties index 7047dc50..c29af558 100644 --- a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -128,6 +128,7 @@ price.quote.requestBodyRequired=请求体为空 price.quote.customerIdRequired=客户ID为空 price.quote.customerCodeRequired=客户代码不能为空 price.quote.customerCodeNotFound=客户代码不存在 +price.quote.customerDisabled=客户已停用,不可计费 price.quote.productCodeRequired=产品代码为空 price.quote.countryRequired=国家代码或国家名称为空 price.quote.weightInvalid=重量必须大于0 diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/BmsProductCountryMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/BmsProductCountryMapper.java index 65e1a907..f7cabdf5 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/BmsProductCountryMapper.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/BmsProductCountryMapper.java @@ -17,6 +17,9 @@ public interface BmsProductCountryMapper public int insertBmsProductCountry(BmsProductCountry country); + /** 按明细行主键更新可编辑字段(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期) */ + public int updateBmsProductCountry(BmsProductCountry country); + public int deleteByProductId(Long productId); public int deleteByProductCode(String productCode); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java index 33528a98..8430594e 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java @@ -16,6 +16,9 @@ public interface PrcPriceSetDetailMapper public List selectPrcPriceSetDetailList(PrcPriceSetDetail detail); + /** 版本内去重后的 产品/国家/维度组合行(用于明细搜索下拉选项) */ + public List selectDetailOptionRows(@Param("psvId") Long psvId); + public int insertPrcPriceSetDetail(PrcPriceSetDetail detail); public int updatePrcPriceSetDetail(PrcPriceSetDetail detail); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IBmsProductService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IBmsProductService.java index 125d0524..d7f9689e 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IBmsProductService.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IBmsProductService.java @@ -21,6 +21,11 @@ public interface IBmsProductService public int updateBmsProduct(BmsProduct product); + /** + * 修改产品国家明细行(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期),状态按日期重算 + */ + public int updateBmsProductCountry(BmsProductCountry country); + public int deleteBmsProductByIds(Long[] productIds); public List selectCountryListByProductCode(String productCode); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcPriceSetService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcPriceSetService.java index 9c4ecab1..51e42b63 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcPriceSetService.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcPriceSetService.java @@ -2,6 +2,7 @@ package com.ruoyi.price.service; import java.util.Date; import java.util.List; +import java.util.Map; import com.ruoyi.price.domain.PrcCustomerPriceSet; import com.ruoyi.price.domain.PrcPriceSet; import com.ruoyi.price.domain.PrcPriceSetCheckItem; @@ -55,6 +56,12 @@ public interface IPrcPriceSetService public List selectDetailList(PrcPriceSetDetail detail); + /** + * 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 列表。 + * 返回 { products: [{code,name}], countries: [{code,name}], zones: [{code,name}] } + */ + public Map selectDetailOptions(Long psvId); + public int insertDetail(PrcPriceSetDetail detail); public int updateDetail(PrcPriceSetDetail detail); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java index 8a5c4bb8..9344fabc 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java @@ -136,6 +136,22 @@ public class BmsProductServiceImpl implements IBmsProductService return productMapper.updateBmsProduct(product); } + @Override + public int updateBmsProductCountry(BmsProductCountry country) + { + if (country == null || country.getDetailId() == null) + { + throw new ServiceException("明细行主键缺失,无法修改"); + } + int rows = countryMapper.updateBmsProductCountry(country); + if (rows > 0) + { + // 生效/失效日期可能变化,状态按日期重算 + countryMapper.refreshStatusByDates(); + } + return rows; + } + @Override @Transactional(rollbackFor = Exception.class) public int deleteBmsProductByIds(Long[] productIds) diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java index 55260142..b17cd4ab 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -636,6 +637,52 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return list; } + @Override + public Map selectDetailOptions(Long psvId) + { + List rows = detailMapper.selectDetailOptionRows(psvId); + Map> products = new LinkedHashMap>(); + Map> countries = new LinkedHashMap>(); + Map> zones = new LinkedHashMap>(); + for (PrcPriceSetDetail d : rows) + { + if (d == null) + { + continue; + } + if (StringUtils.isNotBlank(d.getProductCode())) + { + Map item = new LinkedHashMap(); + item.put("code", d.getProductCode()); + item.put("name", StringUtils.isNotBlank(d.getProductName()) ? d.getProductName() : d.getProductCode()); + products.putIfAbsent(d.getProductCode(), item); + } + String countryCode = StringUtils.isNotBlank(d.getCountryCode()) ? d.getCountryCode() : null; + String countryName = StringUtils.isNotBlank(d.getCountryName()) ? d.getCountryName() : null; + if (countryCode != null || countryName != null) + { + Map item = new LinkedHashMap(); + item.put("code", countryCode); + item.put("name", countryName); + countries.putIfAbsent(countryCode != null ? countryCode : countryName, item); + } + String zoneCode = StringUtils.isNotBlank(d.getZoneCode()) ? d.getZoneCode() : null; + String zoneName = StringUtils.isNotBlank(d.getZoneName()) ? d.getZoneName() : null; + if (zoneCode != null || zoneName != null) + { + Map item = new LinkedHashMap(); + item.put("code", zoneCode); + item.put("name", zoneName); + zones.putIfAbsent(zoneCode != null ? zoneCode : zoneName, item); + } + } + Map result = new HashMap(); + result.put("products", new ArrayList>(products.values())); + result.put("countries", new ArrayList>(countries.values())); + result.put("zones", new ArrayList>(zones.values())); + return result; + } + /** Attach version surcharge snapshots to a page of details (for drawer list). */ private void fillDetailSurcharges(List details) { diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java index f5ff5603..8a4e0665 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -354,6 +354,11 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService throw new ServiceException(MessageUtils.message("price.quote.customerCodeNotFound")); } BmsCustomer hit = list.get(0); + // is_enable: 0 normal, 1 disabled — stopped customers cannot quote/bill + if ("1".equals(hit.getIsEnable())) + { + throw new ServiceException(MessageUtils.message("price.quote.customerDisabled")); + } request.setCustomerId(hit.getCustomerId()); if (StringUtils.isNotEmpty(hit.getCustomerCode())) { diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java index bf17bd87..82d8d668 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java @@ -11,7 +11,7 @@ import com.ruoyi.price.service.IPrcPriceSetService; /** * Refresh prc_price_set_version status (draft/pending/effect/expired). - * Startup + every 5 minutes so PENDING becomes EFFECT when effective_time is reached. + * Startup + every 3 hours so PENDING becomes EFFECT when effective_time is reached. */ @Component public class BmsStatusRefreshTask implements ApplicationRunner @@ -27,7 +27,7 @@ public class BmsStatusRefreshTask implements ApplicationRunner refresh("startup"); } - @Scheduled(cron = "0 0/5 * * * ?") + @Scheduled(cron = "0 0 0/3 * * ?") public void scheduledRefresh() { refresh("scheduled"); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BmsRegionSyncServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BmsRegionSyncServiceImpl.java index 4d07e67b..ca8c7b3b 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BmsRegionSyncServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BmsRegionSyncServiceImpl.java @@ -179,6 +179,9 @@ public class BmsRegionSyncServiceImpl implements IBmsRegionSyncService region.setCountryNameEn((String) name.get("common")); } + // ISO代码(先取,后续用于港/澳/台中文名归一) + String cca2 = (String) country.get("cca2"); + // 中文名 translations.zho.common(mledoze/countries 标准字段名为 zho) Map translations = (Map) country.get("translations"); if (translations != null) @@ -192,6 +195,21 @@ public class BmsRegionSyncServiceImpl implements IBmsRegionSyncService // 注:取不到中文名时不再用英文名兜底,避免污染业务匹配。 // 若确需兜底,可改为 region.setCountryNameCn(StringUtils.EMPTY); + // 中国香港/中国澳门/中国台湾:数据源中文名为繁体「台灣」等,强制归一为规范简体名, + // 避免同步覆盖后页面显示不规范、且每次同步都被繁体名刷回。 + if ("HK".equals(cca2)) + { + region.setCountryNameCn("中国香港"); + } + else if ("MO".equals(cca2)) + { + region.setCountryNameCn("中国澳门"); + } + else if ("TW".equals(cca2)) + { + region.setCountryNameCn("中国台湾"); + } + // ISO代码 region.setCountryCode2((String) country.get("cca2")); region.setCountryCode3((String) country.get("cca3")); diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml index 5ba5e5fe..654f6a0a 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml @@ -93,6 +93,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ) + + + update bms_product_country + set weight_min = #{weightMin}, + weight_max = #{weightMax}, + freight_price = #{freightPrice}, + registration_fee = #{registrationFee}, + size_limit = #{sizeLimit}, + last_mile = #{lastMile}, + lead_time = #{leadTime}, + effective_date = #{effectiveDate}, + expire_date = #{expireDate}, + update_time = sysdate() + where detail_id = #{detailId} + + delete from bms_product_country where product_id = #{productId} diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml index d1dacf97..2e520e4f 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml @@ -108,6 +108,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select count(1) from prc_price_set_detail where psv_id = #{psvId} + + insert ignore into prc_price_set_detail ( diff --git a/bms-back/sql/price/price_set_force_edit_menu.sql b/bms-back/sql/price/price_set_force_edit_menu.sql new file mode 100644 index 00000000..9b5af96c --- /dev/null +++ b/bms-back/sql/price/price_set_force_edit_menu.sql @@ -0,0 +1,10 @@ +-- price:set:forceEdit: edit any version status (design highest privilege) +SET NAMES utf8mb4; + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) +SELECT 2207, N'任意版本编辑', 2201, 6, '', NULL, '', '', 1, 0, 'F', '0', '0', 'price:set:forceEdit', '#', 'admin', sysdate(), N'设计最高权限:任意状态版本可编辑价格明细' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2207 OR perms = 'price:set:forceEdit'); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 1, 2207 FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu WHERE role_id = 1 AND menu_id = 2207); diff --git a/bms-front/src/api/price/product.js b/bms-front/src/api/price/product.js index 57cf153a..d475032b 100644 --- a/bms-front/src/api/price/product.js +++ b/bms-front/src/api/price/product.js @@ -29,6 +29,15 @@ export function listProductCountries(productCode, effectiveDate, productId) { }) } +/** 修改产品国家明细行(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期) */ +export function updateProductCountry(data) { + return request({ + url: '/price/product/country', + method: 'put', + data: data + }) +} + /** 新增产品 */ export function addProduct(data) { return request({ diff --git a/bms-front/src/api/price/set.js b/bms-front/src/api/price/set.js index ff8b31bf..007c693d 100644 --- a/bms-front/src/api/price/set.js +++ b/bms-front/src/api/price/set.js @@ -165,6 +165,15 @@ export function listSetDetail(query) { }) } +/** 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 */ +export function listSetDetailOptions(psvId) { + return request({ + url: '/price/set/detail/options', + method: 'get', + params: { psvId } + }) +} + export function addSetDetail(data) { return request({ url: '/price/set/detail', diff --git a/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue b/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue index 20042dd3..a5f63c46 100644 --- a/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue +++ b/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue @@ -11,6 +11,7 @@ max-height="calc(100vh - 120px)" empty-text="暂无国家明细" :span-method="spanMethod" + row-key="detailId" >