增加规则配置
This commit is contained in:
+73
@@ -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<BmsBizRule> 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));
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
|
||||
@@ -5,7 +5,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 公共业务附件 bms_biz_attachment
|
||||
* 公共业务附件 biz_attachment
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
||||
@@ -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<String> productCodes;
|
||||
/** 新增时多国家展开(非表字段) */
|
||||
private List<String> countryCodes;
|
||||
/** 新增时多客户展开(非表字段) */
|
||||
private List<String> 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<String> getProductCodes() { return productCodes; }
|
||||
public void setProductCodes(List<String> productCodes) { this.productCodes = productCodes; }
|
||||
public List<String> getCountryCodes() { return countryCodes; }
|
||||
public void setCountryCodes(List<String> countryCodes) { this.countryCodes = countryCodes; }
|
||||
public List<String> getCustomerCodes() { return customerCodes; }
|
||||
public void setCustomerCodes(List<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<BmsBizRule> selectList(BmsBizRule query);
|
||||
|
||||
BmsBizRule selectById(Long ruleId);
|
||||
|
||||
List<BmsBizRule> 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);
|
||||
}
|
||||
@@ -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<BmsBizRule> 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);
|
||||
}
|
||||
+383
@@ -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<BmsBizRule> 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<String> products = dimValues(row.getProductCodes(), row.getProductCode());
|
||||
List<String> countries = dimValues(row.getCountryCodes(), row.getCountryCode());
|
||||
List<String> 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<BmsBizRule> 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<String> dimValues(List<String> list, String single)
|
||||
{
|
||||
Set<String> set = new LinkedHashSet<String>();
|
||||
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<String>(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("重量段最小值不能大于最大值");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.system.mapper.BmsBizRuleMapper">
|
||||
|
||||
<resultMap type="BmsBizRule" id="BmsBizRuleResult">
|
||||
<id property="ruleId" column="rule_id" />
|
||||
<result property="ruleName" column="rule_name" />
|
||||
<result property="ruleType" column="rule_type" />
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="customerCode" column="customer_code" />
|
||||
<result property="weightMin" column="weight_min" />
|
||||
<result property="weightMax" column="weight_max" />
|
||||
<result property="weightSource" column="weight_source" />
|
||||
<result property="sideSumMax" column="side_sum_max" />
|
||||
<result property="status" column="status" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectVo">
|
||||
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
|
||||
</sql>
|
||||
|
||||
<select id="selectList" parameterType="BmsBizRule" resultMap="BmsBizRuleResult">
|
||||
<include refid="selectVo"/>
|
||||
<where>
|
||||
del_flag = '0'
|
||||
<if test="ruleName != null and ruleName != ''">
|
||||
and rule_name like concat('%', #{ruleName}, '%')
|
||||
</if>
|
||||
<if test="ruleType != null and ruleType != ''">
|
||||
and rule_type = #{ruleType}
|
||||
</if>
|
||||
<if test="productCode != null and productCode != ''">
|
||||
and product_code = #{productCode}
|
||||
</if>
|
||||
<if test="countryCode != null and countryCode != ''">
|
||||
and country_code = #{countryCode}
|
||||
</if>
|
||||
<if test="customerCode != null and customerCode != ''">
|
||||
and customer_code = #{customerCode}
|
||||
</if>
|
||||
<if test="weightSource != null and weightSource != ''">
|
||||
and weight_source = #{weightSource}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
and status = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by product_code asc, country_code asc, customer_code asc, rule_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectById" parameterType="Long" resultMap="BmsBizRuleResult">
|
||||
<include refid="selectVo"/>
|
||||
where rule_id = #{ruleId} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<select id="selectActiveByType" parameterType="String" resultMap="BmsBizRuleResult">
|
||||
<include refid="selectVo"/>
|
||||
where del_flag = '0' and status = '0'
|
||||
and rule_type = #{ruleType}
|
||||
order by
|
||||
(case when product_code is null or product_code = '' then 0 else 1 end
|
||||
+ case when country_code is null or country_code = '' then 0 else 1 end
|
||||
+ case when customer_code is null or customer_code = '' then 0 else 1 end) desc,
|
||||
rule_id asc
|
||||
</select>
|
||||
|
||||
<select id="countByDim" resultType="int">
|
||||
select count(1) from biz_rule
|
||||
where del_flag = '0'
|
||||
and rule_type = #{ruleType}
|
||||
and ifnull(product_code,'') = ifnull(#{productCode},'')
|
||||
and ifnull(country_code,'') = ifnull(#{countryCode},'')
|
||||
and ifnull(customer_code,'') = ifnull(#{customerCode},'')
|
||||
and ((#{weightMin} is null and weight_min is null) or weight_min = #{weightMin})
|
||||
and ((#{weightMax} is null and weight_max is null) or weight_max = #{weightMax})
|
||||
<if test="excludeId != null">and rule_id != #{excludeId}</if>
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="BmsBizRule" useGeneratedKeys="true" keyProperty="ruleId">
|
||||
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'
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="BmsBizRule">
|
||||
update biz_rule
|
||||
<set>
|
||||
<if test="ruleName != null and ruleName != ''">rule_name = #{ruleName},</if>
|
||||
<if test="ruleType != null and ruleType != ''">rule_type = #{ruleType},</if>
|
||||
product_code = #{productCode},
|
||||
country_code = #{countryCode},
|
||||
customer_code = #{customerCode},
|
||||
weight_min = #{weightMin},
|
||||
weight_max = #{weightMax},
|
||||
<if test="weightSource != null and weightSource != ''">weight_source = #{weightSource},</if>
|
||||
side_sum_max = #{sideSumMax},
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where rule_id = #{ruleId} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<update id="deleteByIds" parameterType="Long">
|
||||
update biz_rule
|
||||
set del_flag = '1',
|
||||
update_time = sysdate()
|
||||
where rule_id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -24,7 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<sql id="selectBmsBizAttachmentVo">
|
||||
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
|
||||
</sql>
|
||||
|
||||
<select id="selectBmsBizAttachmentById" parameterType="Long" resultMap="BmsBizAttachmentResult">
|
||||
@@ -59,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<insert id="insertBmsBizAttachment" parameterType="BmsBizAttachment" useGeneratedKeys="true" keyProperty="attachmentId">
|
||||
insert into bms_biz_attachment (
|
||||
insert into biz_attachment (
|
||||
<if test="bizType != null and bizType != ''">biz_type,</if>
|
||||
<if test="bizId != null">biz_id,</if>
|
||||
<if test="fileName != null and fileName != ''">file_name,</if>
|
||||
@@ -87,7 +87,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</insert>
|
||||
|
||||
<update id="updateBmsBizAttachment" parameterType="BmsBizAttachment">
|
||||
update bms_biz_attachment
|
||||
update biz_attachment
|
||||
<set>
|
||||
<if test="bizId != null">biz_id = #{bizId},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
@@ -98,7 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</update>
|
||||
|
||||
<update id="deleteBmsBizAttachmentByIds" parameterType="Long">
|
||||
update bms_biz_attachment set del_flag = '2' where attachment_id in
|
||||
update biz_attachment set del_flag = '2' where attachment_id in
|
||||
<foreach collection="array" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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='公共业务附件';
|
||||
@@ -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'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="ruleId"
|
||||
selection
|
||||
>
|
||||
<template #toolbar="{ ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['data:bizRule:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="!ids.length" @click="handleDelete(ids)" v-hasPermi="['data:bizRule:remove']">删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #col-ruleType="{ row }">
|
||||
<dict-tag :options="bms_biz_rule_type" :value="row.ruleType" />
|
||||
</template>
|
||||
<template #col-weightSource="{ row }">
|
||||
<dict-tag :options="bms_biz_weight_source" :value="row.weightSource" />
|
||||
</template>
|
||||
<template #col-weightRange="{ row }">
|
||||
{{ formatWeightRange(row) }}
|
||||
</template>
|
||||
<template #col-dim="{ row }">
|
||||
{{ formatDim(row) }}
|
||||
</template>
|
||||
<template #col-status="{ row }">
|
||||
<el-tag size="small" :type="row.status === '0' ? 'success' : 'info'">{{ row.status === '0' ? '正常' : '停用' }}</el-tag>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['data:bizRule:edit']">修改</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete([row.ruleId])" v-hasPermi="['data:bizRule:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<el-dialog :title="title" v-model="open" width="720px" append-to-body destroy-on-close>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="规则名称" prop="ruleName">
|
||||
<el-input v-model="form.ruleName" placeholder="请输入规则名称" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规则类型" prop="ruleType">
|
||||
<el-select v-model="form.ruleType" placeholder="请选择" style="width: 100%">
|
||||
<el-option v-for="d in bms_biz_rule_type" :key="d.value" :label="d.label" :value="d.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品" prop="productCodeList">
|
||||
<el-select
|
||||
v-model="form.productCodeList"
|
||||
:multiple="!isEdit"
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
reserve-keyword
|
||||
:placeholder="isEdit ? '空=全部产品' : '多选将按产品展开为多行,空=全部'"
|
||||
:remote-method="searchProduct"
|
||||
:loading="productLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="p in productOptions"
|
||||
:key="p.productCode"
|
||||
:label="(p.productName || '') + ' (' + p.productCode + ')'"
|
||||
:value="p.productCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="国家" prop="countryCodeList">
|
||||
<el-select
|
||||
v-model="form.countryCodeList"
|
||||
:multiple="!isEdit"
|
||||
filterable
|
||||
clearable
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:placeholder="isEdit ? '空=全部国家' : '多选将按国家展开为多行,空=全部'"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="c in countryOptions"
|
||||
:key="c.countryCode2"
|
||||
:label="(c.countryNameCn || c.countryNameEn || '') + ' (' + c.countryCode2 + ')'"
|
||||
:value="c.countryCode2"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户" prop="customerCodeList">
|
||||
<customer-select
|
||||
v-model="form.customerCodeList"
|
||||
type="customer"
|
||||
value-key="customerCode"
|
||||
label-key="customerName"
|
||||
code-key="customerCode"
|
||||
:placeholder="isEdit ? '空=全部客户' : '多选将按客户展开为多行,空=全部'"
|
||||
:multiple="!isEdit"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="重量段最小" prop="weightMin">
|
||||
<el-input-number v-model="form.weightMin" :min="0" :precision="3" :controls="false" placeholder="KG" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="重量段最大" prop="weightMax">
|
||||
<el-input-number v-model="form.weightMax" :min="0" :precision="3" :controls="false" placeholder="KG" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="取值字段" prop="weightSource">
|
||||
<el-select v-model="form.weightSource" placeholder="请选择" style="width: 100%">
|
||||
<el-option v-for="d in bms_biz_weight_source" :key="d.value" :label="d.label" :value="d.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.ruleType === 'THREE_SIDE'" label="三边和上限" prop="sideSumMax">
|
||||
<el-input-number v-model="form.sideSumMax" :min="0" :precision="2" :controls="false" placeholder="L+W+H < 该值" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio value="0">正常</el-radio>
|
||||
<el-radio value="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" maxlength="500" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
<el-button @click="open = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="BizRuleConfig">
|
||||
import { listBizRule, getBizRule, addBizRule, updateBizRule, delBizRule } from '@/api/data/bizRule'
|
||||
import { listProductOptions } from '@/api/price/product'
|
||||
import { listRegionOptions } from '@/api/data/region'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { bms_biz_rule_type, bms_biz_weight_source } = useDict('bms_biz_rule_type', 'bms_biz_weight_source')
|
||||
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const open = ref(false)
|
||||
const title = ref('')
|
||||
const isEdit = ref(false)
|
||||
const productLoading = ref(false)
|
||||
const productOptions = ref([])
|
||||
const countryOptions = ref([])
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '正常', value: '0' },
|
||||
{ label: '停用', value: '1' }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '规则名称', prop: 'ruleName', component: 'input', placeholder: '规则名称' },
|
||||
{ label: '产品', prop: 'productCode', component: 'input', placeholder: '产品代码' },
|
||||
{ label: '国家', prop: 'countryCode', component: 'input', placeholder: 'ISO2' },
|
||||
{ label: '客户', prop: 'customerCode', component: 'input', placeholder: '客户代码' },
|
||||
{ label: '规则类型', prop: 'ruleType', component: 'select', dict: 'bms_biz_rule_type', placeholder: '请选择', props: { clearable: true } },
|
||||
{ label: '取值字段', prop: 'weightSource', component: 'select', dict: 'bms_biz_weight_source', placeholder: '请选择', props: { clearable: true } },
|
||||
{ label: '状态', prop: 'status', component: 'select', options: statusOptions, placeholder: '请选择', props: { clearable: true } }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '规则名称', prop: 'ruleName', minWidth: 160, showOverflowTooltip: true },
|
||||
{ label: '规则类型', prop: 'ruleType', align: 'center', width: 110, slot: 'ruleType' },
|
||||
{ label: '产品', prop: 'productCode', minWidth: 110, showOverflowTooltip: true },
|
||||
{ label: '国家', prop: 'countryCode', width: 90, align: 'center' },
|
||||
{ label: '客户', prop: 'customerCode', minWidth: 110, showOverflowTooltip: true },
|
||||
{ label: '重量段', prop: 'weightRange', align: 'center', width: 120, slot: 'weightRange' },
|
||||
{ label: '取值字段', prop: 'weightSource', align: 'center', width: 110, slot: 'weightSource' },
|
||||
{ label: '三边和上限', prop: 'sideSumMax', align: 'center', width: 110 },
|
||||
{ label: '状态', prop: 'status', align: 'center', width: 90, slot: 'status' },
|
||||
{ label: '备注', prop: 'remark', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 150, fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
]
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
rules: {
|
||||
ruleName: [{ required: true, message: '请输入规则名称', trigger: 'blur' }],
|
||||
ruleType: [{ required: true, message: '请选择规则类型', trigger: 'change' }],
|
||||
weightSource: [{ required: true, message: '请选择取值字段', trigger: 'change' }],
|
||||
sideSumMax: [{
|
||||
validator: (_r, v, cb) => {
|
||||
if (form.value.ruleType === 'THREE_SIDE' && (v == null || v === '')) cb(new Error('请填写三边和上限'))
|
||||
else cb()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||
}
|
||||
})
|
||||
const { form, rules } = toRefs(data)
|
||||
|
||||
function queryList(params) {
|
||||
return listBizRule(params)
|
||||
}
|
||||
|
||||
function formatWeightRange(row) {
|
||||
if (row.weightMin == null && row.weightMax == null) return '-'
|
||||
const a = row.weightMin == null ? '' : String(row.weightMin)
|
||||
const b = row.weightMax == null ? '' : String(row.weightMax)
|
||||
return a + ' ~ ' + b
|
||||
}
|
||||
|
||||
function formatDim(row) {
|
||||
return [row.productCode || '*', row.countryCode || '*', row.customerCode || '*'].join(' / ')
|
||||
}
|
||||
|
||||
function toList(v) {
|
||||
if (Array.isArray(v)) return v.filter(Boolean)
|
||||
if (v == null || v === '') return []
|
||||
return [v]
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.value = {
|
||||
ruleId: undefined,
|
||||
ruleName: '',
|
||||
ruleType: 'THREE_SIDE',
|
||||
productCodeList: [],
|
||||
countryCodeList: [],
|
||||
customerCodeList: [],
|
||||
weightMin: undefined,
|
||||
weightMax: undefined,
|
||||
weightSource: 'RECEIVE_WEIGHT',
|
||||
sideSumMax: 90,
|
||||
status: '0',
|
||||
remark: ''
|
||||
}
|
||||
productOptions.value = []
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
resetForm()
|
||||
isEdit.value = false
|
||||
title.value = '新增规则'
|
||||
open.value = true
|
||||
loadCountries()
|
||||
}
|
||||
|
||||
function handleUpdate(row) {
|
||||
resetForm()
|
||||
isEdit.value = true
|
||||
getBizRule(row.ruleId).then(res => {
|
||||
const d = res.data || {}
|
||||
form.value = {
|
||||
...d,
|
||||
productCodeList: d.productCode || '',
|
||||
countryCodeList: d.countryCode || '',
|
||||
customerCodeList: d.customerCode || ''
|
||||
}
|
||||
if (d.productCode) {
|
||||
productOptions.value = [{ productCode: d.productCode, productName: d.productCode }]
|
||||
searchProduct(d.productCode)
|
||||
}
|
||||
title.value = '修改规则'
|
||||
open.value = true
|
||||
loadCountries()
|
||||
})
|
||||
}
|
||||
|
||||
function buildPayload() {
|
||||
const f = form.value
|
||||
const products = toList(f.productCodeList)
|
||||
const countries = toList(f.countryCodeList)
|
||||
const customers = toList(f.customerCodeList)
|
||||
const base = {
|
||||
ruleId: f.ruleId,
|
||||
ruleName: f.ruleName,
|
||||
ruleType: f.ruleType,
|
||||
weightMin: f.weightMin,
|
||||
weightMax: f.weightMax,
|
||||
weightSource: f.weightSource,
|
||||
sideSumMax: f.ruleType === 'THREE_SIDE' ? f.sideSumMax : null,
|
||||
status: f.status,
|
||||
remark: f.remark
|
||||
}
|
||||
if (isEdit.value) {
|
||||
return {
|
||||
...base,
|
||||
productCode: products[0] || null,
|
||||
countryCode: countries[0] || null,
|
||||
customerCode: customers[0] || null
|
||||
}
|
||||
}
|
||||
return {
|
||||
...base,
|
||||
productCodes: products,
|
||||
countryCodes: countries,
|
||||
customerCodes: customers
|
||||
}
|
||||
}
|
||||
|
||||
function submitForm() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = buildPayload()
|
||||
const req = payload.ruleId ? updateBizRule(payload) : addBizRule(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
open.value = false
|
||||
tableRef.value?.refresh()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(ids) {
|
||||
if (!ids || !ids.length) return
|
||||
proxy.$modal.confirm('确认删除选中的 ' + ids.length + ' 条规则?').then(() => {
|
||||
return delBizRule(ids.join(','))
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
tableRef.value?.refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function searchProduct(keyword) {
|
||||
productLoading.value = true
|
||||
listProductOptions({ productCode: keyword || undefined, status: '1' }).then(res => {
|
||||
const rows = res.data || res.rows || []
|
||||
const map = new Map()
|
||||
;[...productOptions.value, ...rows].forEach(p => {
|
||||
if (p && p.productCode) map.set(p.productCode, p)
|
||||
})
|
||||
productOptions.value = Array.from(map.values())
|
||||
}).finally(() => {
|
||||
productLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function loadCountries() {
|
||||
if (countryOptions.value.length) return
|
||||
listRegionOptions().then(res => {
|
||||
countryOptions.value = res.data || res.rows || []
|
||||
})
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user