From b54cb1b01804c7f2ea34f1ef1d0c5b1705cdfd2c Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 16 Oct 2026 13:40:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A7=84=E5=88=99=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/data/BmsBizRuleController.java | 73 ++++ .../service/impl/PrcOrderBillServiceImpl.java | 15 + .../bms/system/domain/BmsBizAttachment.java | 2 +- .../com/bms/system/domain/BmsBizRule.java | 113 ++++++ .../bms/system/mapper/BmsBizRuleMapper.java | 29 ++ .../system/service/IBmsBizRuleService.java | 28 ++ .../service/impl/BmsBizRuleServiceImpl.java | 383 ++++++++++++++++++ .../mapper/data/BmsBizRuleMapper.xml | 130 ++++++ .../mapper/system/BmsBizAttachmentMapper.xml | 8 +- bms-back/sql/data/biz_rule.sql | 60 +++ bms-back/sql/data/biz_rule_alter_dim.sql | 57 +++ bms-back/sql/data/biz_rule_menu.sql | 70 ++++ .../rename_drop_bms_prefix_biz_tables.sql | 24 ++ ..._biz_attachment.sql => biz_attachment.sql} | 4 +- bms-front/src/api/data/bizRule.js | 39 ++ .../src/views/data/rule/config/index.vue | 348 ++++++++++++++++ 16 files changed, 1376 insertions(+), 7 deletions(-) create mode 100644 bms-back/bms-admin/src/main/java/com/bms/web/controller/data/BmsBizRuleController.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizRule.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/system/mapper/BmsBizRuleMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java create mode 100644 bms-back/bms-system/src/main/resources/mapper/data/BmsBizRuleMapper.xml create mode 100644 bms-back/sql/data/biz_rule.sql create mode 100644 bms-back/sql/data/biz_rule_alter_dim.sql create mode 100644 bms-back/sql/data/biz_rule_menu.sql create mode 100644 bms-back/sql/data/rename_drop_bms_prefix_biz_tables.sql rename bms-back/sql/system/{bms_biz_attachment.sql => biz_attachment.sql} (90%) create mode 100644 bms-front/src/api/data/bizRule.js create mode 100644 bms-front/src/views/data/rule/config/index.vue diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/BmsBizRuleController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/BmsBizRuleController.java new file mode 100644 index 00000000..4f2eed7f --- /dev/null +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/BmsBizRuleController.java @@ -0,0 +1,73 @@ +package com.bms.web.controller.data; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RestController; +import com.bms.common.annotation.Log; +import com.bms.common.core.controller.BaseController; +import com.bms.common.core.domain.AjaxResult; +import com.bms.common.core.page.TableDataInfo; +import com.bms.common.enums.BusinessType; +import com.bms.system.domain.BmsBizRule; +import com.bms.system.service.IBmsBizRuleService; + +/** + * 规则配置 + */ +@RestController +@RequestMapping("/data/bizRule") +public class BmsBizRuleController extends BaseController +{ + @Autowired + private IBmsBizRuleService bizRuleService; + + @PreAuthorize("@ss.hasPermi('data:bizRule:list')") + @GetMapping("/list") + public TableDataInfo list(BmsBizRule query) + { + startPage(); + List list = bizRuleService.selectList(query); + return getDataTable(list); + } + + @PreAuthorize("@ss.hasPermi('data:bizRule:query')") + @GetMapping("/{ruleId}") + public AjaxResult getInfo(@PathVariable Long ruleId) + { + return success(bizRuleService.selectById(ruleId)); + } + + @PreAuthorize("@ss.hasPermi('data:bizRule:add')") + @Log(title = "biz rule", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody BmsBizRule row) + { + row.setCreateBy(getUsername()); + return toAjax(bizRuleService.insert(row)); + } + + @PreAuthorize("@ss.hasPermi('data:bizRule:edit')") + @Log(title = "biz rule", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody BmsBizRule row) + { + row.setUpdateBy(getUsername()); + return toAjax(bizRuleService.update(row)); + } + + @PreAuthorize("@ss.hasPermi('data:bizRule:remove')") + @Log(title = "biz rule", businessType = BusinessType.DELETE) + @DeleteMapping("/{ruleIds}") + public AjaxResult remove(@PathVariable Long[] ruleIds) + { + return toAjax(bizRuleService.deleteByIds(ruleIds)); + } +} 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 b8335fc2..33fcfcd5 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 @@ -35,6 +35,7 @@ import com.bms.common.utils.uuid.IdUtils; import com.bms.customer.customerinfo.domain.BmsCustomer; import com.bms.customer.customerinfo.mapper.BmsCustomerMapper; import com.bms.system.mapper.SysUserMapper; +// import com.bms.system.service.IBmsBizRuleService; import com.bms.price.domain.PrcIncomeBill; import com.bms.price.domain.PrcIncomeBillFee; import com.bms.price.domain.PrcOrderBill; @@ -142,6 +143,10 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService @Autowired private PrcRecalcBillPriceAsyncRunner recalcBillPriceAsyncRunner; + // TODO: reopen with three-side / biz weight rules + // @Autowired + // private IBmsBizRuleService bizRuleService; + private static final int BATCH_ADD_FEE_MAX = 5000; private static final int BATCH_ADD_FEE_PREVIEW_LIMIT = 20; private static final int CHARGE_WEIGHT_IMPORT_MAX = 5000; @@ -167,6 +172,16 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService PrcOrderBill existed = orderBillMapper.selectPrcOrderBillByOrderNo(request.getOrderNo()); BigDecimal systemWeight = request.getWeight(); BigDecimal calcWeight = resolveCalcWeight(existed, systemWeight); + // TODO: reopen three-side / biz weight rules later + // Manual charge_weight wins; otherwise apply active biz rules (e.g. three-side). + // if (!isManualChargeWeight(existed)) + // { + // BigDecimal ruled = bizRuleService.resolveWeightByRule(request, systemWeight); + // if (ruled != null) + // { + // calcWeight = ruled; + // } + // } request.setWeight(calcWeight); TmsPriceQuoteResult result = tmsPriceQuoteService.calculate(request); persistBill(request, systemWeight, bizDate, result); diff --git a/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizAttachment.java b/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizAttachment.java index 802ace09..2051662e 100644 --- a/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizAttachment.java +++ b/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizAttachment.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 公共业务附件 bms_biz_attachment + * 公共业务附件 biz_attachment */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizRule.java b/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizRule.java new file mode 100644 index 00000000..c1893e6f --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/system/domain/BmsBizRule.java @@ -0,0 +1,113 @@ +package com.bms.system.domain; + +import java.math.BigDecimal; +import java.util.List; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.bms.common.annotation.Excel; +import com.bms.common.core.domain.BaseEntity; + +/** + * 业务规则配置 biz_rule(产品/国家/客户维度) + */ +public class BmsBizRule extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private Long ruleId; + + /** 规则名称 */ + @Excel(name = "规则名称") + private String ruleName; + + /** 规则类型(字典 bms_biz_rule_type) */ + @Excel(name = "规则类型") + private String ruleType; + + /** 产品代码(空=全部) */ + @Excel(name = "产品") + private String productCode; + + /** 国家ISO2(空=全部) */ + @Excel(name = "国家") + private String countryCode; + + /** 客户代码(空=全部) */ + @Excel(name = "客户") + private String customerCode; + + /** 重量段最小KG(含) */ + private BigDecimal weightMin; + + /** 重量段最大KG(含) */ + private BigDecimal weightMax; + + /** 取值字段(字典 bms_biz_weight_source) */ + @Excel(name = "取值字段") + private String weightSource; + + /** 三边和上限(三边规则) */ + private BigDecimal sideSumMax; + + /** 状态(0正常 1停用) */ + private String status; + + private String delFlag; + + /** 新增时多产品展开(非表字段) */ + private List productCodes; + /** 新增时多国家展开(非表字段) */ + private List countryCodes; + /** 新增时多客户展开(非表字段) */ + private List customerCodes; + + public Long getRuleId() { return ruleId; } + public void setRuleId(Long ruleId) { this.ruleId = ruleId; } + public String getRuleName() { return ruleName; } + public void setRuleName(String ruleName) { this.ruleName = ruleName; } + public String getRuleType() { return ruleType; } + public void setRuleType(String ruleType) { this.ruleType = ruleType; } + public String getProductCode() { return productCode; } + public void setProductCode(String productCode) { this.productCode = productCode; } + public String getCountryCode() { return countryCode; } + public void setCountryCode(String countryCode) { this.countryCode = countryCode; } + public String getCustomerCode() { return customerCode; } + public void setCustomerCode(String customerCode) { this.customerCode = customerCode; } + public BigDecimal getWeightMin() { return weightMin; } + public void setWeightMin(BigDecimal weightMin) { this.weightMin = weightMin; } + public BigDecimal getWeightMax() { return weightMax; } + public void setWeightMax(BigDecimal weightMax) { this.weightMax = weightMax; } + public String getWeightSource() { return weightSource; } + public void setWeightSource(String weightSource) { this.weightSource = weightSource; } + public BigDecimal getSideSumMax() { return sideSumMax; } + public void setSideSumMax(BigDecimal sideSumMax) { this.sideSumMax = sideSumMax; } + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + public String getDelFlag() { return delFlag; } + public void setDelFlag(String delFlag) { this.delFlag = delFlag; } + public List getProductCodes() { return productCodes; } + public void setProductCodes(List productCodes) { this.productCodes = productCodes; } + public List getCountryCodes() { return countryCodes; } + public void setCountryCodes(List countryCodes) { this.countryCodes = countryCodes; } + public List getCustomerCodes() { return customerCodes; } + public void setCustomerCodes(List customerCodes) { this.customerCodes = customerCodes; } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("ruleId", getRuleId()) + .append("ruleName", getRuleName()) + .append("ruleType", getRuleType()) + .append("productCode", getProductCode()) + .append("countryCode", getCountryCode()) + .append("customerCode", getCustomerCode()) + .append("weightMin", getWeightMin()) + .append("weightMax", getWeightMax()) + .append("weightSource", getWeightSource()) + .append("sideSumMax", getSideSumMax()) + .append("status", getStatus()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/system/mapper/BmsBizRuleMapper.java b/bms-back/bms-system/src/main/java/com/bms/system/mapper/BmsBizRuleMapper.java new file mode 100644 index 00000000..9c16a6dd --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/system/mapper/BmsBizRuleMapper.java @@ -0,0 +1,29 @@ +package com.bms.system.mapper; + +import java.math.BigDecimal; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.bms.system.domain.BmsBizRule; + +public interface BmsBizRuleMapper +{ + List selectList(BmsBizRule query); + + BmsBizRule selectById(Long ruleId); + + List selectActiveByType(String ruleType); + + int countByDim(@Param("ruleType") String ruleType, + @Param("productCode") String productCode, + @Param("countryCode") String countryCode, + @Param("customerCode") String customerCode, + @Param("weightMin") BigDecimal weightMin, + @Param("weightMax") BigDecimal weightMax, + @Param("excludeId") Long excludeId); + + int insert(BmsBizRule row); + + int update(BmsBizRule row); + + int deleteByIds(Long[] ruleIds); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java b/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java new file mode 100644 index 00000000..f844905c --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java @@ -0,0 +1,28 @@ +package com.bms.system.service; + +import java.math.BigDecimal; +import java.util.List; +import com.bms.system.domain.BmsBizRule; +import com.bms.price.domain.vo.TmsPriceBillRequest; + +/** + * 业务规则配置 + */ +public interface IBmsBizRuleService +{ + List selectList(BmsBizRule query); + + BmsBizRule selectById(Long ruleId); + + int insert(BmsBizRule row); + + int update(BmsBizRule row); + + int deleteByIds(Long[] ruleIds); + + /** + * Apply active weight rules to bill request. + * @return overridden weight when a rule matches; null when no rule applies + */ + BigDecimal resolveWeightByRule(TmsPriceBillRequest request, BigDecimal systemWeight); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java new file mode 100644 index 00000000..95e77f08 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java @@ -0,0 +1,383 @@ +package com.bms.system.service.impl; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.bms.common.exception.ServiceException; +import com.bms.common.utils.StringUtils; +import com.bms.price.domain.vo.TmsPriceBillRequest; +import com.bms.system.domain.BmsBizRule; +import com.bms.system.mapper.BmsBizRuleMapper; +import com.bms.system.service.IBmsBizRuleService; +import com.bms.system.service.IBmsProductCodeMappingService; + +@Service +public class BmsBizRuleServiceImpl implements IBmsBizRuleService +{ + public static final String RULE_TYPE_THREE_SIDE = "THREE_SIDE"; + public static final String WEIGHT_SOURCE_RECEIVE = "RECEIVE_WEIGHT"; + public static final String WEIGHT_SOURCE_CHARGE = "CHARGE_WEIGHT"; + + @Autowired + private BmsBizRuleMapper bizRuleMapper; + + @Autowired + private IBmsProductCodeMappingService productCodeMappingService; + + @Override + public List selectList(BmsBizRule query) + { + return bizRuleMapper.selectList(query); + } + + @Override + public BmsBizRule selectById(Long ruleId) + { + return bizRuleMapper.selectById(ruleId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int insert(BmsBizRule row) + { + normalizeHeader(row); + validateHeader(row); + if (StringUtils.isEmpty(row.getStatus())) + { + row.setStatus("0"); + } + List products = dimValues(row.getProductCodes(), row.getProductCode()); + List countries = dimValues(row.getCountryCodes(), row.getCountryCode()); + List customers = dimValues(row.getCustomerCodes(), row.getCustomerCode()); + int n = 0; + for (String product : products) + { + for (String country : countries) + { + for (String customer : customers) + { + BmsBizRule one = copyBase(row); + one.setProductCode(product); + one.setCountryCode(country); + one.setCustomerCode(customer); + assertUnique(one, null); + n += bizRuleMapper.insert(one); + } + } + } + return n; + } + + @Override + public int update(BmsBizRule row) + { + if (row.getRuleId() == null) + { + throw new ServiceException("ruleId required"); + } + normalizeHeader(row); + validateHeader(row); + // edit: single dimension row + if (row.getProductCodes() != null && !row.getProductCodes().isEmpty()) + { + row.setProductCode(normalizeCode(row.getProductCodes().get(0))); + } + else + { + row.setProductCode(normalizeCode(row.getProductCode())); + } + if (row.getCountryCodes() != null && !row.getCountryCodes().isEmpty()) + { + row.setCountryCode(normalizeCode(row.getCountryCodes().get(0))); + } + else + { + row.setCountryCode(normalizeCode(row.getCountryCode())); + } + if (row.getCustomerCodes() != null && !row.getCustomerCodes().isEmpty()) + { + row.setCustomerCode(normalizeCode(row.getCustomerCodes().get(0))); + } + else + { + row.setCustomerCode(normalizeCode(row.getCustomerCode())); + } + assertUnique(row, row.getRuleId()); + return bizRuleMapper.update(row); + } + + @Override + public int deleteByIds(Long[] ruleIds) + { + return bizRuleMapper.deleteByIds(ruleIds); + } + + @Override + public BigDecimal resolveWeightByRule(TmsPriceBillRequest request, BigDecimal systemWeight) + { + if (request == null) + { + return null; + } + List rules = bizRuleMapper.selectActiveByType(RULE_TYPE_THREE_SIDE); + if (rules == null || rules.isEmpty()) + { + return null; + } + for (BmsBizRule rule : rules) + { + if (matchThreeSide(rule, request, systemWeight)) + { + return pickWeight(rule.getWeightSource(), request, systemWeight); + } + } + return null; + } + + private void assertUnique(BmsBizRule row, Long excludeId) + { + int cnt = bizRuleMapper.countByDim(row.getRuleType(), row.getProductCode(), row.getCountryCode(), + row.getCustomerCode(), row.getWeightMin(), row.getWeightMax(), excludeId); + if (cnt > 0) + { + throw new ServiceException("同维度规则已存在(产品/国家/客户/重量段)"); + } + } + + private static BmsBizRule copyBase(BmsBizRule row) + { + BmsBizRule one = new BmsBizRule(); + one.setRuleName(row.getRuleName()); + one.setRuleType(row.getRuleType()); + one.setWeightMin(row.getWeightMin()); + one.setWeightMax(row.getWeightMax()); + one.setWeightSource(row.getWeightSource()); + one.setSideSumMax(row.getSideSumMax()); + one.setStatus(row.getStatus()); + one.setRemark(row.getRemark()); + one.setCreateBy(row.getCreateBy()); + return one; + } + + /** empty list / blank => one null dimension (means all) */ + private static List dimValues(List list, String single) + { + Set set = new LinkedHashSet(); + if (list != null) + { + for (String s : list) + { + String c = normalizeCode(s); + if (c != null) + { + set.add(c); + } + } + } + if (set.isEmpty()) + { + String c = normalizeCode(single); + if (c != null) + { + set.add(c); + } + } + if (set.isEmpty()) + { + return Collections.singletonList(null); + } + return new ArrayList(set); + } + + private boolean matchThreeSide(BmsBizRule rule, TmsPriceBillRequest request, BigDecimal systemWeight) + { + if (rule == null) + { + return false; + } + if (!dimMatch(request.getProductCode(), rule.getProductCode(), true)) + { + return false; + } + if (!dimMatch(request.getCountryCode(), rule.getCountryCode(), false)) + { + return false; + } + if (!dimMatch(request.getCustomerCode(), rule.getCustomerCode(), false)) + { + return false; + } + BigDecimal checkWeight = firstPositive(request.getReceiveWeight(), systemWeight, request.getWeight()); + if (!inWeightRange(checkWeight, rule.getWeightMin(), rule.getWeightMax())) + { + return false; + } + if (rule.getSideSumMax() != null) + { + BigDecimal sum = sideSum(request); + if (sum == null || sum.compareTo(rule.getSideSumMax()) >= 0) + { + return false; + } + } + return true; + } + + private boolean dimMatch(String requestCode, String ruleCode, boolean tryProductMap) + { + if (StringUtils.isEmpty(ruleCode)) + { + return true; + } + if (StringUtils.isEmpty(requestCode)) + { + return false; + } + String req = requestCode.trim().toUpperCase(Locale.ROOT); + String rule = ruleCode.trim().toUpperCase(Locale.ROOT); + if (req.equals(rule)) + { + return true; + } + if (tryProductMap) + { + String mapped = productCodeMappingService.resolveBmsProductCode(requestCode); + return mapped != null && mapped.trim().toUpperCase(Locale.ROOT).equals(rule); + } + return false; + } + + private static BigDecimal pickWeight(String weightSource, TmsPriceBillRequest request, BigDecimal systemWeight) + { + if (WEIGHT_SOURCE_RECEIVE.equalsIgnoreCase(weightSource)) + { + BigDecimal rw = firstPositive(request.getReceiveWeight(), request.getActualWeight()); + return rw != null ? rw : systemWeight; + } + if (WEIGHT_SOURCE_CHARGE.equalsIgnoreCase(weightSource)) + { + return systemWeight != null ? systemWeight : request.getWeight(); + } + return null; + } + + private static boolean inWeightRange(BigDecimal weight, BigDecimal min, BigDecimal max) + { + if (min == null && max == null) + { + return true; + } + if (weight == null) + { + return false; + } + if (min != null && weight.compareTo(min) < 0) + { + return false; + } + if (max != null && weight.compareTo(max) > 0) + { + return false; + } + return true; + } + + private static BigDecimal sideSum(TmsPriceBillRequest request) + { + BigDecimal l = nzDim(request.getPkgLength()); + BigDecimal w = nzDim(request.getPkgWidth()); + BigDecimal h = nzDim(request.getPkgHeight()); + if (l == null || w == null || h == null) + { + return null; + } + return l.add(w).add(h); + } + + private static BigDecimal nzDim(BigDecimal v) + { + if (v == null || v.compareTo(BigDecimal.ZERO) <= 0) + { + return null; + } + return v; + } + + private static BigDecimal firstPositive(BigDecimal... vals) + { + if (vals == null) + { + return null; + } + for (BigDecimal v : vals) + { + if (v != null && v.compareTo(BigDecimal.ZERO) > 0) + { + return v; + } + } + return null; + } + + private void normalizeHeader(BmsBizRule row) + { + if (row.getRuleName() != null) + { + row.setRuleName(row.getRuleName().trim()); + } + if (row.getRuleType() != null) + { + row.setRuleType(row.getRuleType().trim().toUpperCase(Locale.ROOT)); + } + if (row.getWeightSource() != null) + { + row.setWeightSource(row.getWeightSource().trim().toUpperCase(Locale.ROOT)); + } + } + + private static String normalizeCode(String code) + { + if (code == null) + { + return null; + } + String t = code.trim(); + if (t.isEmpty()) + { + return null; + } + return t.toUpperCase(Locale.ROOT); + } + + private void validateHeader(BmsBizRule row) + { + if (StringUtils.isEmpty(row.getRuleName())) + { + throw new ServiceException("规则名称不能为空"); + } + if (StringUtils.isEmpty(row.getRuleType())) + { + throw new ServiceException("规则类型不能为空"); + } + if (StringUtils.isEmpty(row.getWeightSource())) + { + throw new ServiceException("取值字段不能为空"); + } + if (RULE_TYPE_THREE_SIDE.equalsIgnoreCase(row.getRuleType()) && row.getSideSumMax() == null) + { + throw new ServiceException("三边规则必须填写三边和上限"); + } + if (row.getWeightMin() != null && row.getWeightMax() != null + && row.getWeightMin().compareTo(row.getWeightMax()) > 0) + { + throw new ServiceException("重量段最小值不能大于最大值"); + } + } +} diff --git a/bms-back/bms-system/src/main/resources/mapper/data/BmsBizRuleMapper.xml b/bms-back/bms-system/src/main/resources/mapper/data/BmsBizRuleMapper.xml new file mode 100644 index 00000000..57671018 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/data/BmsBizRuleMapper.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select rule_id, rule_name, rule_type, product_code, country_code, customer_code, + weight_min, weight_max, weight_source, side_sum_max, + status, remark, create_by, create_time, update_by, update_time, del_flag + from biz_rule + + + + + + + + + + + + insert into biz_rule ( + rule_name, rule_type, product_code, country_code, customer_code, weight_min, weight_max, + weight_source, side_sum_max, status, remark, create_by, create_time, del_flag + ) values ( + #{ruleName}, #{ruleType}, #{productCode}, #{countryCode}, #{customerCode}, #{weightMin}, #{weightMax}, + #{weightSource}, #{sideSumMax}, #{status}, #{remark}, #{createBy}, sysdate(), '0' + ) + + + + update biz_rule + + rule_name = #{ruleName}, + rule_type = #{ruleType}, + product_code = #{productCode}, + country_code = #{countryCode}, + customer_code = #{customerCode}, + weight_min = #{weightMin}, + weight_max = #{weightMax}, + weight_source = #{weightSource}, + side_sum_max = #{sideSumMax}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where rule_id = #{ruleId} and del_flag = '0' + + + + update biz_rule + set del_flag = '1', + update_time = sysdate() + where rule_id in + + #{id} + + + diff --git a/bms-back/bms-system/src/main/resources/mapper/system/BmsBizAttachmentMapper.xml b/bms-back/bms-system/src/main/resources/mapper/system/BmsBizAttachmentMapper.xml index e423e6f0..ce15c54c 100644 --- a/bms-back/bms-system/src/main/resources/mapper/system/BmsBizAttachmentMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/system/BmsBizAttachmentMapper.xml @@ -24,7 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select attachment_id, biz_type, biz_id, file_name, file_path, file_size, content_type, file_extension, remark, create_by, create_time, update_by, update_time, del_flag - from bms_biz_attachment + from biz_attachment - insert into bms_biz_attachment ( + insert into biz_attachment ( biz_type, biz_id, file_name, @@ -87,7 +87,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update bms_biz_attachment + update biz_attachment biz_id = #{bizId}, remark = #{remark}, @@ -98,7 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update bms_biz_attachment set del_flag = '2' where attachment_id in + update biz_attachment set del_flag = '2' where attachment_id in #{id} diff --git a/bms-back/sql/data/biz_rule.sql b/bms-back/sql/data/biz_rule.sql new file mode 100644 index 00000000..6899eee0 --- /dev/null +++ b/bms-back/sql/data/biz_rule.sql @@ -0,0 +1,60 @@ +-- biz_rule: one row per product/country/customer dimension +SET NAMES utf8mb4; + +CREATE TABLE IF NOT EXISTS biz_rule ( + rule_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '规则ID', + rule_name varchar(100) NOT NULL COMMENT '规则名称', + rule_type varchar(32) NOT NULL COMMENT '规则类型(字典 bms_biz_rule_type)', + product_code varchar(64) DEFAULT NULL COMMENT '产品代码(空=全部)', + country_code varchar(10) DEFAULT NULL COMMENT '国家ISO2(空=全部)', + customer_code varchar(64) DEFAULT NULL COMMENT '客户代码(空=全部)', + weight_min decimal(10,3) DEFAULT NULL COMMENT '重量段最小KG(含)', + weight_max decimal(10,3) DEFAULT NULL COMMENT '重量段最大KG(含)', + weight_source varchar(32) NOT NULL COMMENT '取值字段(字典 bms_biz_weight_source)', + side_sum_max decimal(10,2) DEFAULT NULL COMMENT '三边和上限(三边规则)', + status char(1) NOT NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + remark varchar(500) DEFAULT NULL COMMENT '备注', + create_by varchar(64) DEFAULT '' COMMENT '创建者', + create_time datetime DEFAULT NULL COMMENT '创建时间', + update_by varchar(64) DEFAULT '' COMMENT '更新者', + update_time datetime DEFAULT NULL COMMENT '更新时间', + del_flag char(1) DEFAULT '0' COMMENT '删除标记(0存在 1删除)', + PRIMARY KEY (rule_id), + KEY idx_biz_rule_dim (product_code, country_code, customer_code), + KEY idx_biz_rule_type_status (rule_type, status), + KEY idx_biz_rule_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务规则配置表(产品/国家/客户维度)'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'业务规则类型', 'bms_biz_rule_type', '0', 'admin', sysdate(), N'计费/业务规则类型' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bms_biz_rule_type'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 1, N'三边规则', 'THREE_SIDE', 'bms_biz_rule_type', '', 'primary', 'Y', '0', 'admin', sysdate(), N'三边和小于阈值时按取值字段取重' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'bms_biz_rule_type' AND dict_value = 'THREE_SIDE'); + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +SELECT N'规则取重字段', 'bms_biz_weight_source', '0', 'admin', sysdate(), N'实重(收货重)/计费重' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bms_biz_weight_source'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 1, N'实重', 'RECEIVE_WEIGHT', 'bms_biz_weight_source', '', 'success', 'Y', '0', 'admin', sysdate(), N'收货重 TAB_ORDER.WEIGHT' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'bms_biz_weight_source' AND dict_value = 'RECEIVE_WEIGHT'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 2, N'计费重', 'CHARGE_WEIGHT', 'bms_biz_weight_source', '', 'warning', 'N', '0', 'admin', sysdate(), N'上游计费重/体积重因子' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'bms_biz_weight_source' AND dict_value = 'CHARGE_WEIGHT'); + +-- seed: one row per product +INSERT INTO biz_rule (rule_name, rule_type, product_code, country_code, customer_code, weight_min, weight_max, weight_source, side_sum_max, status, remark, create_by, create_time, del_flag) +SELECT N'WSP三边和<90取收货重', 'THREE_SIDE', v.product_code, NULL, NULL, NULL, NULL, 'RECEIVE_WEIGHT', 90.00, '0', + N'产品三边和小于90时以收货重为计费重', 'admin', sysdate(), '0' +FROM ( + SELECT 'WSP081' AS product_code UNION ALL SELECT 'WSP075' UNION ALL SELECT 'WSP069' + UNION ALL SELECT 'WSP068' UNION ALL SELECT 'WSP067' UNION ALL SELECT 'WSP066' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM biz_rule r + WHERE r.del_flag = '0' AND r.rule_type = 'THREE_SIDE' AND r.product_code = v.product_code + AND r.country_code IS NULL AND r.customer_code IS NULL +); diff --git a/bms-back/sql/data/biz_rule_alter_dim.sql b/bms-back/sql/data/biz_rule_alter_dim.sql new file mode 100644 index 00000000..a1ef66bc --- /dev/null +++ b/bms-back/sql/data/biz_rule_alter_dim.sql @@ -0,0 +1,57 @@ +-- Alter existing biz_rule to product/country/customer dimension storage +SET NAMES utf8mb4; + +-- add new columns if missing +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND column_name = 'product_code'), + 'SELECT 1', + 'ALTER TABLE biz_rule ADD COLUMN product_code varchar(64) DEFAULT NULL COMMENT ''产品代码(空=全部)'' AFTER rule_type' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND column_name = 'country_code'), + 'SELECT 1', + 'ALTER TABLE biz_rule ADD COLUMN country_code varchar(10) DEFAULT NULL COMMENT ''国家ISO2(空=全部)'' AFTER product_code' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND column_name = 'customer_code'), + 'SELECT 1', + 'ALTER TABLE biz_rule ADD COLUMN customer_code varchar(64) DEFAULT NULL COMMENT ''客户代码(空=全部)'' AFTER country_code' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- migrate CSV rows: take first token only as best-effort (manual fix may be needed) +UPDATE biz_rule + SET product_code = NULLIF(TRIM(SUBSTRING_INDEX(REPLACE(IFNULL(product_codes,''), ' ', ''), ',', 1)), ''), + country_code = NULLIF(TRIM(SUBSTRING_INDEX(REPLACE(IFNULL(country_codes,''), ' ', ''), ',', 1)), ''), + customer_code = NULLIF(TRIM(SUBSTRING_INDEX(REPLACE(IFNULL(customer_codes,''), ' ', ''), ',', 1)), '') + WHERE product_code IS NULL + AND EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND column_name = 'product_codes'); + +-- drop old CSV columns if present +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND column_name = 'product_codes'), + 'ALTER TABLE biz_rule DROP COLUMN product_codes, DROP COLUMN country_codes, DROP COLUMN customer_codes', + 'SELECT 1' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'biz_rule' AND index_name = 'idx_biz_rule_dim'), + 'SELECT 1', + 'ALTER TABLE biz_rule ADD KEY idx_biz_rule_dim (product_code, country_code, customer_code)' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/bms-back/sql/data/biz_rule_menu.sql b/bms-back/sql/data/biz_rule_menu.sql new file mode 100644 index 00000000..90190a96 --- /dev/null +++ b/bms-back/sql/data/biz_rule_menu.sql @@ -0,0 +1,70 @@ +-- Rule management: top-level sibling of data management (not nested under data) +SET NAMES utf8mb4; + +-- locate data top menu to place rule management next to it +SET @dataMenuId := ( + SELECT menu_id FROM sys_menu + WHERE path = 'data' AND menu_type = 'M' AND parent_id = 0 + ORDER BY menu_id ASC LIMIT 1 +); +SET @dataMenuId := IFNULL(@dataMenuId, ( + SELECT menu_id FROM sys_menu + WHERE path = 'data' AND menu_type = 'M' + ORDER BY menu_id ASC LIMIT 1 +)); +SET @dataOrderNum := (SELECT IFNULL(order_num, 10) FROM sys_menu WHERE menu_id = @dataMenuId LIMIT 1); +SET @ruleOrderNum := IFNULL(@dataOrderNum, 10) + 1; + +-- directory: rule management (parent_id = 0, sibling of data) +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 2480, N'规则管理', 0, @ruleOrderNum, 'rule', NULL, '', '', 1, 0, 'M', '0', '0', '', 'edit', 'admin', sysdate(), N'业务规则管理' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2480 OR (path = 'rule' AND parent_id = 0 AND menu_type = 'M')); + +-- if already inserted under data, move to top level +UPDATE sys_menu + SET parent_id = 0, + order_num = @ruleOrderNum, + path = 'rule', + menu_type = 'M', + component = NULL, + update_time = sysdate() + WHERE menu_id = 2480 + OR (menu_name = N'规则管理' AND menu_type = 'M' AND path = 'rule'); + +SET @ruleParentId := (SELECT menu_id FROM sys_menu WHERE menu_id = 2480 LIMIT 1); +SET @ruleParentId := IFNULL(@ruleParentId, ( + SELECT menu_id FROM sys_menu WHERE path = 'rule' AND menu_type = 'M' AND parent_id = 0 ORDER BY menu_id ASC LIMIT 1 +)); + +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 2481, N'规则配置', @ruleParentId, 1, 'config', 'data/rule/config/index', '', 'BizRuleConfig', 1, 0, 'C', '0', '0', 'data:bizRule:list', 'list', 'admin', sysdate(), N'计费重取值等业务规则' +FROM DUAL WHERE @ruleParentId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2481 OR perms = 'data:bizRule:list'); + +-- ensure page menu hangs under top-level rule management +UPDATE sys_menu + SET parent_id = @ruleParentId, + path = 'config', + component = 'data/rule/config/index', + update_time = sysdate() + WHERE @ruleParentId IS NOT NULL + AND (menu_id = 2481 OR perms = 'data:bizRule:list'); + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +SELECT 2482, N'查询', 2481, 1, '', NULL, 1, 0, 'F', '0', '0', 'data:bizRule:query', '#', 'admin', sysdate() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2482 OR perms = 'data:bizRule:query'); + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +SELECT 2483, N'新增', 2481, 2, '', NULL, 1, 0, 'F', '0', '0', 'data:bizRule:add', '#', 'admin', sysdate() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2483 OR perms = 'data:bizRule:add'); + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +SELECT 2484, N'修改', 2481, 3, '', NULL, 1, 0, 'F', '0', '0', 'data:bizRule:edit', '#', 'admin', sysdate() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2484 OR perms = 'data:bizRule:edit'); + +INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +SELECT 2485, N'删除', 2481, 4, '', NULL, 1, 0, 'F', '0', '0', 'data:bizRule:remove', '#', 'admin', sysdate() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2485 OR perms = 'data:bizRule:remove'); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 1, m.menu_id FROM (SELECT 2480 AS menu_id UNION SELECT 2481 UNION SELECT 2482 UNION SELECT 2483 UNION SELECT 2484 UNION SELECT 2485) m +WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id); diff --git a/bms-back/sql/data/rename_drop_bms_prefix_biz_tables.sql b/bms-back/sql/data/rename_drop_bms_prefix_biz_tables.sql new file mode 100644 index 00000000..449ca48c --- /dev/null +++ b/bms-back/sql/data/rename_drop_bms_prefix_biz_tables.sql @@ -0,0 +1,24 @@ +-- Rename tables: remove bms_ prefix (run once on existing DB) +SET NAMES utf8mb4; + +-- bms_biz_rule -> biz_rule +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'bms_biz_rule') + AND NOT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'biz_rule'), + 'RENAME TABLE bms_biz_rule TO biz_rule', + 'SELECT 1' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- bms_biz_attachment -> biz_attachment +SET @sql := ( + SELECT IF( + EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'bms_biz_attachment') + AND NOT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'biz_attachment'), + 'RENAME TABLE bms_biz_attachment TO biz_attachment', + 'SELECT 1' + ) +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/bms-back/sql/system/bms_biz_attachment.sql b/bms-back/sql/system/biz_attachment.sql similarity index 90% rename from bms-back/sql/system/bms_biz_attachment.sql rename to bms-back/sql/system/biz_attachment.sql index 44b849ce..5e4cac1a 100644 --- a/bms-back/sql/system/bms_biz_attachment.sql +++ b/bms-back/sql/system/biz_attachment.sql @@ -1,7 +1,7 @@ -- 公共业务附件表(可复用:批量修改计费重导入等) SET NAMES utf8mb4; -CREATE TABLE IF NOT EXISTS bms_biz_attachment ( +CREATE TABLE IF NOT EXISTS biz_attachment ( attachment_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '附件ID', biz_type varchar(64) NOT NULL COMMENT '业务类型(如 CHARGE_WEIGHT_IMPORT)', biz_id varchar(64) DEFAULT NULL COMMENT '业务关联ID(如导入任务taskId)', @@ -19,4 +19,4 @@ CREATE TABLE IF NOT EXISTS bms_biz_attachment ( PRIMARY KEY (attachment_id), KEY idx_biz_attach_type_time (biz_type, create_time), KEY idx_biz_attach_biz_id (biz_type, biz_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='公共业务附件'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公共业务附件'; diff --git a/bms-front/src/api/data/bizRule.js b/bms-front/src/api/data/bizRule.js new file mode 100644 index 00000000..e14086a8 --- /dev/null +++ b/bms-front/src/api/data/bizRule.js @@ -0,0 +1,39 @@ +import request from '@/utils/request' + +export function listBizRule(query) { + return request({ + url: '/data/bizRule/list', + method: 'get', + params: query + }) +} + +export function getBizRule(ruleId) { + return request({ + url: '/data/bizRule/' + ruleId, + method: 'get' + }) +} + +export function addBizRule(data) { + return request({ + url: '/data/bizRule', + method: 'post', + data + }) +} + +export function updateBizRule(data) { + return request({ + url: '/data/bizRule', + method: 'put', + data + }) +} + +export function delBizRule(ruleIds) { + return request({ + url: '/data/bizRule/' + ruleIds, + method: 'delete' + }) +} diff --git a/bms-front/src/views/data/rule/config/index.vue b/bms-front/src/views/data/rule/config/index.vue new file mode 100644 index 00000000..b2defbb3 --- /dev/null +++ b/bms-front/src/views/data/rule/config/index.vue @@ -0,0 +1,348 @@ + + +