新建应付配置的附加费开关配置
This commit is contained in:
+31
@@ -1,5 +1,6 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,6 +25,7 @@ import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.file.FileUtils;
|
||||
import com.bms.common.utils.poi.ExcelUtil;
|
||||
import com.bms.price.domain.ChannelProductMap;
|
||||
import com.bms.price.domain.PayFeeRuleGroup;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
import com.bms.price.domain.PayProductTerm;
|
||||
import com.bms.price.domain.PaySaveCommand;
|
||||
@@ -70,6 +72,35 @@ public class PayProductController extends BaseController
|
||||
return toAjax(payProductService.updateTerm(term));
|
||||
}
|
||||
|
||||
/** 勾选的应付产品各自带出附加费开关。productIds 用逗号分隔 */
|
||||
@PreAuthorize("@ss.hasPermi('price:pay:query')")
|
||||
@GetMapping("/feeRule/list")
|
||||
public AjaxResult feeRuleList(@RequestParam String productIds)
|
||||
{
|
||||
List<Long> ids = new ArrayList<>();
|
||||
if (productIds != null)
|
||||
{
|
||||
for (String part : productIds.split(","))
|
||||
{
|
||||
if (part != null && !part.isBlank())
|
||||
{
|
||||
ids.add(Long.valueOf(part.trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return success(payProductService.listFeeRules(ids));
|
||||
}
|
||||
|
||||
/** 按产品保存附加费开关。多个产品各写一份 */
|
||||
@PreAuthorize("@ss.hasPermi('price:pay:edit')")
|
||||
@Log(title = "应付附加费开关", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/feeRule")
|
||||
public AjaxResult saveFeeRule(@RequestBody List<PayFeeRuleGroup> products)
|
||||
{
|
||||
payProductService.saveFeeRules(products);
|
||||
return success();
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:pay:edit')")
|
||||
@Log(title = "应付有效期", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/term/status")
|
||||
|
||||
@@ -91,14 +91,36 @@ class PayWorkbookParserTest
|
||||
{
|
||||
PayPreview preview = parse("32、");
|
||||
PayProduct ups = product(preview, "德国UPS");
|
||||
assertEquals("Zone 1", ups.getWeightBands().get(0).getZoneName());
|
||||
assertEquals("DE", ups.getWeightBands().get(0).getCountryCode());
|
||||
assertTrue(decimal(ups.getWeightBands().get(0), "3.9"));
|
||||
assertEquals("Zone 2", ups.getWeightBands().get(1).getZoneName());
|
||||
assertTrue(ups.getWeightBands().get(1).getCountryCode().startsWith("BE,LU"));
|
||||
assertTrue(decimal(ups.getWeightBands().get(1), "7.58"));
|
||||
assertTrue(ups.getWeightBands().get(4).getZoneName().contains("Zone 5"));
|
||||
assertTrue(decimal(ups.getWeightBands().get(4), "9.45"));
|
||||
String[] codes = {
|
||||
"DE",
|
||||
"BE", "LU", "NL", "AT", "SI",
|
||||
"CZ", "PL", "SK",
|
||||
"DK", "FI", "FR", "SE", "ES", "IT", "PT", "HU",
|
||||
"GR", "IE", "BG", "HR", "EE", "LT", "LV", "RO"
|
||||
};
|
||||
String[] zones = {
|
||||
"Zone 1",
|
||||
"Zone 2", "Zone 2", "Zone 2", "Zone 2", "Zone 2",
|
||||
"Zone 3", "Zone 3", "Zone 3",
|
||||
"Zone 4", "Zone 4", "Zone 4", "Zone 4", "Zone 4", "Zone 4", "Zone 4", "Zone 4",
|
||||
"Zone 5", "Zone 5", "Zone 5", "Zone 5", "Zone 5", "Zone 5", "Zone 5", "Zone 5"
|
||||
};
|
||||
String[] prices = {
|
||||
"3.9",
|
||||
"7.58", "7.58", "7.58", "7.58", "7.58",
|
||||
"7.72", "7.72", "7.72",
|
||||
"8.41", "8.41", "8.41", "8.41", "8.41", "8.41", "8.41", "8.41",
|
||||
"9.45", "9.45", "9.45", "9.45", "9.45", "9.45", "9.45", "9.45"
|
||||
};
|
||||
assertEquals(codes.length, ups.getWeightBands().size());
|
||||
for (int i = 0; i < codes.length; i++)
|
||||
{
|
||||
PayWeightBand band = ups.getWeightBands().get(i);
|
||||
assertEquals(zones[i], band.getZoneName(), codes[i]);
|
||||
assertEquals(codes[i], band.getCountryCode());
|
||||
assertEquals(codes[i], band.getCountryName());
|
||||
assertTrue(decimal(band, prices[i]), codes[i] + " " + prices[i]);
|
||||
}
|
||||
assertTrue(ups.getSurcharges().stream().anyMatch(fee -> "报价说明".equals(fee.getFeeName())
|
||||
&& fee.getChargeText().contains("不含管理费") && fee.getChargeText().contains("不含燃油")));
|
||||
PaySurcharge handling = ups.getSurcharges().stream()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 一个应付产品上的附加费开关。多个产品各自一组,互不影响。
|
||||
*/
|
||||
@Data
|
||||
public class PayFeeRuleGroup
|
||||
{
|
||||
private Long productId;
|
||||
|
||||
private String productName;
|
||||
|
||||
private List<PayProductFeeRule> rules = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 应付产品附加费开关。挂在根产品上,价格仍在价卡版本的附加费行里。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PayProductFeeRule extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long ruleId;
|
||||
|
||||
/** 根产品编号 */
|
||||
private Long productId;
|
||||
|
||||
/** 附加费名称,用来到价卡版本里取价 */
|
||||
private String feeName;
|
||||
|
||||
/** 1 参与计算,0 不参与 */
|
||||
private String enabled;
|
||||
|
||||
/** SIZE 尺寸,VOLUME 体积,WEIGHT 重量,POSTCODE 邮编,ALWAYS 每票,RETURN 退件 */
|
||||
private String triggerType;
|
||||
|
||||
private BigDecimal sideMaxCm;
|
||||
|
||||
private BigDecimal sumMaxCm;
|
||||
|
||||
private BigDecimal volumeMaxCm3;
|
||||
|
||||
private BigDecimal minLengthCm;
|
||||
|
||||
private BigDecimal minWidthCm;
|
||||
|
||||
private BigDecimal minHeightCm;
|
||||
|
||||
private BigDecimal weightMaxKg;
|
||||
|
||||
/** ANY 任一条超出就收,ALL 写了的条件都超出才收 */
|
||||
private String joinMode;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PayProductFeeRule;
|
||||
|
||||
/**
|
||||
* 应付产品附加费开关
|
||||
*/
|
||||
public interface PayProductFeeRuleMapper
|
||||
{
|
||||
List<PayProductFeeRule> selectByProductId(Long productId);
|
||||
|
||||
int deleteByProductId(Long productId);
|
||||
|
||||
int deleteByProductIds(@Param("productIds") List<Long> productIds);
|
||||
|
||||
int insertBatch(@Param("list") List<PayProductFeeRule> list);
|
||||
}
|
||||
@@ -2811,17 +2811,22 @@ public class PayWorkbookParser
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PayWeightBand band = new PayWeightBand();
|
||||
band.setSectionName("分区报价");
|
||||
String country = countryRow < 0 ? "" : grid.text(countryRow, col);
|
||||
band.setCountryName(country);
|
||||
band.setCountryCode(zoneCodes(country));
|
||||
band.setZoneName(grid.text(header, col));
|
||||
band.setChargeName(charge);
|
||||
band.setPrice(price);
|
||||
band.setPriceUnit(unitOf(charge + " EUR"));
|
||||
band.setSortNo(product.getWeightBands().size() + 1);
|
||||
product.getWeightBands().add(band);
|
||||
String zone = grid.text(header, col);
|
||||
for (String piece : zoneCountryPieces(country))
|
||||
{
|
||||
PayWeightBand band = new PayWeightBand();
|
||||
band.setSectionName("分区报价");
|
||||
String code = zoneCodes(piece);
|
||||
band.setCountryName(zoneCountryName(piece, code, country));
|
||||
band.setCountryCode(code.contains(",") ? "" : code);
|
||||
band.setZoneName(zone);
|
||||
band.setChargeName(charge);
|
||||
band.setPrice(price);
|
||||
band.setPriceUnit(unitOf(charge + " EUR"));
|
||||
band.setSortNo(product.getWeightBands().size() + 1);
|
||||
product.getWeightBands().add(band);
|
||||
}
|
||||
}
|
||||
return row + 1;
|
||||
}
|
||||
@@ -3268,19 +3273,103 @@ public class PayWorkbookParser
|
||||
|
||||
private static String zoneCodes(String text)
|
||||
{
|
||||
Matcher matcher = Pattern.compile("\\b([A-Z]{2})\\b").matcher(text == null ? "" : text);
|
||||
StringBuilder codes = new StringBuilder();
|
||||
while (matcher.find())
|
||||
for (String code : zoneCodeList(text))
|
||||
{
|
||||
if (codes.length() > 0)
|
||||
{
|
||||
codes.append(',');
|
||||
}
|
||||
codes.append(matcher.group(1));
|
||||
codes.append(code);
|
||||
}
|
||||
return codes.toString();
|
||||
}
|
||||
|
||||
private static List<String> zoneCodeList(String text)
|
||||
{
|
||||
List<String> codes = new ArrayList<>();
|
||||
Matcher matcher = Pattern.compile("\\b([A-Z]{2})\\b").matcher(text == null ? "" : text);
|
||||
while (matcher.find())
|
||||
{
|
||||
codes.add(matcher.group(1));
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 德国 UPS 这类分区国家格把多个国家写在同一格,用顿号或换行接起来。
|
||||
* 纯国家码列表按出现顺序拆开;没有国家码、只用顿号连接的国名同样拆开。
|
||||
* 只有一个国家时保留原格,避免把说明文字拆碎。
|
||||
*/
|
||||
private static List<String> zoneCountryPieces(String text)
|
||||
{
|
||||
if (text == null || text.isBlank())
|
||||
{
|
||||
return List.of("");
|
||||
}
|
||||
String folded = text.replace('\r', '\n');
|
||||
List<String> codes = zoneCodeList(folded);
|
||||
if (codes.size() > 1 && isCountryCodeList(folded))
|
||||
{
|
||||
return codes;
|
||||
}
|
||||
if (!folded.contains("、"))
|
||||
{
|
||||
return List.of(text.trim());
|
||||
}
|
||||
List<String> pieces = new ArrayList<>();
|
||||
for (String part : folded.split("、"))
|
||||
{
|
||||
String token = part.replace('\n', ' ').trim();
|
||||
if (token.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<String> tokenCodes = zoneCodeList(token);
|
||||
if (tokenCodes.size() > 1 && isCountryCodeList(token))
|
||||
{
|
||||
pieces.addAll(tokenCodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
pieces.add(token);
|
||||
}
|
||||
}
|
||||
return pieces.size() > 1 ? pieces : List.of(text.trim());
|
||||
}
|
||||
|
||||
private static boolean isCountryCodeList(String text)
|
||||
{
|
||||
String stripped = text.replaceAll("\\b[A-Z]{2}\\b", "");
|
||||
return stripped.replaceAll("[、,,;;\\s]+", "").isEmpty();
|
||||
}
|
||||
|
||||
/** 拆开后的一格如果就是国家码,名称也用这个码;没拆开的原格保持原文。 */
|
||||
private static String zoneCountryName(String piece, String code, String original)
|
||||
{
|
||||
if (piece == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
if (original != null && piece.equals(original.trim()))
|
||||
{
|
||||
return original.trim();
|
||||
}
|
||||
String trimmed = piece.trim();
|
||||
if (code == null || code.isBlank() || code.contains(","))
|
||||
{
|
||||
return trimmed;
|
||||
}
|
||||
if (trimmed.equalsIgnoreCase(code))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
String name = trimmed.replaceAll("(?i)\\b" + Pattern.quote(code) + "\\b", "")
|
||||
.replaceAll("[()()]", " ")
|
||||
.trim();
|
||||
return name.isEmpty() ? code : name;
|
||||
}
|
||||
|
||||
private static int currencyHits(String text)
|
||||
{
|
||||
if (text == null || text.isBlank())
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.bms.price.service;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import com.bms.price.domain.PayFeeRuleGroup;
|
||||
import com.bms.price.domain.PayPreview;
|
||||
import com.bms.price.domain.PayPriceCompare;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
@@ -74,4 +75,10 @@ public interface IPayProductService
|
||||
void writeSource(Long productId, OutputStream output);
|
||||
|
||||
int deletePayProducts(Long[] productIds);
|
||||
|
||||
/** 按根产品编号带出附加费开关。没有配置过的产品返回空列表 */
|
||||
List<PayFeeRuleGroup> listFeeRules(List<Long> productIds);
|
||||
|
||||
/** 按产品整组替换附加费开关。每个产品一份,互不影响 */
|
||||
void saveFeeRules(List<PayFeeRuleGroup> products);
|
||||
}
|
||||
|
||||
+181
@@ -3,11 +3,13 @@ package com.bms.price.service.impl;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -26,6 +28,8 @@ import com.bms.common.utils.file.OssStorage;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.ChannelProductMap;
|
||||
import com.bms.price.domain.PayExtraLine;
|
||||
import com.bms.price.domain.PayFeeRuleGroup;
|
||||
import com.bms.price.domain.PayProductFeeRule;
|
||||
import com.bms.price.domain.PayPreview;
|
||||
import com.bms.price.domain.PayPriceCompare;
|
||||
import com.bms.price.domain.PayPriceDiff;
|
||||
@@ -35,6 +39,7 @@ import com.bms.price.domain.PaySaveCommand;
|
||||
import com.bms.price.domain.PaySurcharge;
|
||||
import com.bms.price.domain.PayWeightBand;
|
||||
import com.bms.price.mapper.ChannelProductMapMapper;
|
||||
import com.bms.price.mapper.PayProductFeeRuleMapper;
|
||||
import com.bms.price.mapper.PayProductMapper;
|
||||
import com.bms.price.mapper.PayProductTermMapper;
|
||||
import com.bms.price.pay.PayWorkbookParser;
|
||||
@@ -64,6 +69,11 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
@Autowired
|
||||
private PayProductTermMapper payProductTermMapper;
|
||||
|
||||
@Autowired
|
||||
private PayProductFeeRuleMapper payProductFeeRuleMapper;
|
||||
|
||||
private static final Set<String> FEE_TRIGGERS = Set.of("SIZE", "VOLUME", "WEIGHT", "POSTCODE", "ALWAYS", "RETURN");
|
||||
|
||||
@Autowired
|
||||
private ISysDictTypeService dictTypeService;
|
||||
|
||||
@@ -670,6 +680,7 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
return 0;
|
||||
}
|
||||
payProductTermMapper.deleteByProductIds(ids);
|
||||
payProductFeeRuleMapper.deleteByProductIds(ids);
|
||||
payProductMapper.deleteWeightBands(ids);
|
||||
payProductMapper.deleteSurcharges(ids);
|
||||
payProductMapper.deleteExtraLines(ids);
|
||||
@@ -1216,6 +1227,176 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
return rows == null ? 0 : rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PayFeeRuleGroup> listFeeRules(List<Long> productIds)
|
||||
{
|
||||
List<PayFeeRuleGroup> groups = new ArrayList<>();
|
||||
if (productIds == null)
|
||||
{
|
||||
return groups;
|
||||
}
|
||||
LinkedHashSet<Long> ordered = new LinkedHashSet<>();
|
||||
for (Long productId : productIds)
|
||||
{
|
||||
if (productId != null)
|
||||
{
|
||||
ordered.add(productId);
|
||||
}
|
||||
}
|
||||
for (Long productId : ordered)
|
||||
{
|
||||
PayProduct root = requireRoot(productId);
|
||||
PayFeeRuleGroup group = new PayFeeRuleGroup();
|
||||
group.setProductId(root.getProductId());
|
||||
group.setProductName(root.getProductName());
|
||||
List<PayProductFeeRule> rules = payProductFeeRuleMapper.selectByProductId(root.getProductId());
|
||||
group.setRules(rules == null ? new ArrayList<>() : rules);
|
||||
groups.add(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveFeeRules(List<PayFeeRuleGroup> products)
|
||||
{
|
||||
if (products == null || products.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择应付产品");
|
||||
}
|
||||
String operator = SecurityUtils.getUsername();
|
||||
Date now = DateUtils.getNowDate();
|
||||
for (PayFeeRuleGroup group : products)
|
||||
{
|
||||
if (group == null || group.getProductId() == null)
|
||||
{
|
||||
throw new ServiceException("请选择应付产品");
|
||||
}
|
||||
PayProduct root = requireRoot(group.getProductId());
|
||||
List<PayProductFeeRule> rules = normalizeFeeRules(root, group.getRules(), operator, now);
|
||||
payProductFeeRuleMapper.deleteByProductId(root.getProductId());
|
||||
if (!rules.isEmpty())
|
||||
{
|
||||
payProductFeeRuleMapper.insertBatch(rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PayProduct requireRoot(Long productId)
|
||||
{
|
||||
PayProduct product = payProductMapper.selectPayProductById(productId);
|
||||
if (product == null)
|
||||
{
|
||||
throw new ServiceException("应付产品不存在");
|
||||
}
|
||||
if (product.getOriginId() != null)
|
||||
{
|
||||
product = payProductMapper.selectPayProductById(product.getOriginId());
|
||||
if (product == null)
|
||||
{
|
||||
throw new ServiceException("应付产品不存在");
|
||||
}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private List<PayProductFeeRule> normalizeFeeRules(PayProduct root, List<PayProductFeeRule> source, String operator, Date now)
|
||||
{
|
||||
List<PayProductFeeRule> rules = new ArrayList<>();
|
||||
Set<String> names = new HashSet<>();
|
||||
String productName = root.getProductName() == null ? String.valueOf(root.getProductId()) : root.getProductName();
|
||||
if (source == null)
|
||||
{
|
||||
return rules;
|
||||
}
|
||||
for (PayProductFeeRule raw : source)
|
||||
{
|
||||
if (raw == null || isBlank(raw.getFeeName()))
|
||||
{
|
||||
if (raw != null && (feeRuleFilled(raw) || feeRuleChanged(raw)))
|
||||
{
|
||||
throw new ServiceException(productName + "有未选择名称的附加费");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String feeName = raw.getFeeName().trim();
|
||||
if (feeName.length() > 100)
|
||||
{
|
||||
throw new ServiceException(productName + "的附加费名称过长");
|
||||
}
|
||||
if (!names.add(feeName))
|
||||
{
|
||||
throw new ServiceException(productName + "的附加费「" + feeName + "」重复了");
|
||||
}
|
||||
String trigger = isBlank(raw.getTriggerType()) ? "ALWAYS" : raw.getTriggerType().trim().toUpperCase(Locale.ROOT);
|
||||
if (!FEE_TRIGGERS.contains(trigger))
|
||||
{
|
||||
throw new ServiceException(productName + "的触发类型不正确");
|
||||
}
|
||||
String joinMode = isBlank(raw.getJoinMode()) ? "ANY" : raw.getJoinMode().trim().toUpperCase(Locale.ROOT);
|
||||
if (!"ANY".equals(joinMode) && !"ALL".equals(joinMode))
|
||||
{
|
||||
throw new ServiceException(productName + "的命中方式不正确");
|
||||
}
|
||||
String enabled = "1".equals(raw.getEnabled()) ? "1" : "0";
|
||||
checkFeeNumber(raw.getSideMaxCm(), productName, "最长边");
|
||||
checkFeeNumber(raw.getSumMaxCm(), productName, "长宽高之和");
|
||||
checkFeeNumber(raw.getVolumeMaxCm3(), productName, "体积");
|
||||
checkFeeNumber(raw.getMinLengthCm(), productName, "最小长度");
|
||||
checkFeeNumber(raw.getMinWidthCm(), productName, "最小宽度");
|
||||
checkFeeNumber(raw.getMinHeightCm(), productName, "最小高度");
|
||||
checkFeeNumber(raw.getWeightMaxKg(), productName, "重量上限");
|
||||
String remark = raw.getRemark() == null ? null : raw.getRemark().trim();
|
||||
if (remark != null && remark.length() > 500)
|
||||
{
|
||||
throw new ServiceException(productName + "的说明过长");
|
||||
}
|
||||
PayProductFeeRule rule = new PayProductFeeRule();
|
||||
rule.setProductId(root.getProductId());
|
||||
rule.setFeeName(feeName);
|
||||
rule.setEnabled(enabled);
|
||||
rule.setTriggerType(trigger);
|
||||
rule.setSideMaxCm(raw.getSideMaxCm());
|
||||
rule.setSumMaxCm(raw.getSumMaxCm());
|
||||
rule.setVolumeMaxCm3(raw.getVolumeMaxCm3());
|
||||
rule.setMinLengthCm(raw.getMinLengthCm());
|
||||
rule.setMinWidthCm(raw.getMinWidthCm());
|
||||
rule.setMinHeightCm(raw.getMinHeightCm());
|
||||
rule.setWeightMaxKg(raw.getWeightMaxKg());
|
||||
rule.setJoinMode(joinMode);
|
||||
rule.setRemark(isBlank(remark) ? null : remark);
|
||||
rule.setCreateBy(operator);
|
||||
rule.setCreateTime(now);
|
||||
rule.setUpdateBy(operator);
|
||||
rule.setUpdateTime(now);
|
||||
rules.add(rule);
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
private static boolean feeRuleChanged(PayProductFeeRule rule)
|
||||
{
|
||||
String trigger = rule.getTriggerType() == null ? "" : rule.getTriggerType().trim();
|
||||
String joinMode = rule.getJoinMode() == null ? "" : rule.getJoinMode().trim();
|
||||
return (!trigger.isEmpty() && !"ALWAYS".equalsIgnoreCase(trigger))
|
||||
|| (!joinMode.isEmpty() && !"ANY".equalsIgnoreCase(joinMode));
|
||||
}
|
||||
|
||||
private static boolean feeRuleFilled(PayProductFeeRule rule)
|
||||
{
|
||||
return rule.getSideMaxCm() != null || rule.getSumMaxCm() != null || rule.getVolumeMaxCm3() != null
|
||||
|| rule.getMinLengthCm() != null || rule.getMinWidthCm() != null || rule.getMinHeightCm() != null
|
||||
|| rule.getWeightMaxKg() != null || !isBlank(rule.getRemark()) || "1".equals(rule.getEnabled());
|
||||
}
|
||||
|
||||
private static void checkFeeNumber(BigDecimal value, String productName, String label)
|
||||
{
|
||||
if (value != null && value.signum() < 0)
|
||||
{
|
||||
throw new ServiceException(productName + "的" + label + "不能小于0");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBlank(String value)
|
||||
{
|
||||
return value == null || value.trim().isEmpty();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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.price.mapper.PayProductFeeRuleMapper">
|
||||
|
||||
<select id="selectByProductId" resultType="com.bms.price.domain.PayProductFeeRule">
|
||||
select rule_id as ruleId, product_id as productId, fee_name as feeName, enabled,
|
||||
trigger_type as triggerType, side_max_cm as sideMaxCm, sum_max_cm as sumMaxCm,
|
||||
volume_max_cm3 as volumeMaxCm3, min_length_cm as minLengthCm, min_width_cm as minWidthCm,
|
||||
min_height_cm as minHeightCm, weight_max_kg as weightMaxKg, join_mode as joinMode,
|
||||
remark, created_by as createBy, create_time as createTime, updated_by as updateBy,
|
||||
update_time as updateTime
|
||||
from pay_product_fee_rule
|
||||
where product_id = #{productId}
|
||||
order by rule_id
|
||||
</select>
|
||||
|
||||
<delete id="deleteByProductId">
|
||||
delete from pay_product_fee_rule where product_id = #{productId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteByProductIds">
|
||||
delete from pay_product_fee_rule where product_id in
|
||||
<foreach collection="productIds" item="id" open="(" separator="," close=")">#{id}</foreach>
|
||||
</delete>
|
||||
|
||||
<insert id="insertBatch">
|
||||
insert into pay_product_fee_rule (
|
||||
product_id, fee_name, enabled, trigger_type, side_max_cm, sum_max_cm, volume_max_cm3,
|
||||
min_length_cm, min_width_cm, min_height_cm, weight_max_kg, join_mode, remark,
|
||||
created_by, create_time, updated_by, update_time
|
||||
) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.productId}, #{item.feeName}, #{item.enabled}, #{item.triggerType},
|
||||
#{item.sideMaxCm}, #{item.sumMaxCm}, #{item.volumeMaxCm3},
|
||||
#{item.minLengthCm}, #{item.minWidthCm}, #{item.minHeightCm}, #{item.weightMaxKg},
|
||||
#{item.joinMode}, #{item.remark}, #{item.createBy}, #{item.createTime},
|
||||
#{item.updateBy}, #{item.updateTime})
|
||||
</foreach>
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,162 @@
|
||||
-- 应付附加费类型。名称来自 D:\应付供应商 全部价卡的附加费收费项,已去重。
|
||||
-- 可重复执行。
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '附加费类型', 'bms_surcharge_type', '0', 'admin', sysdate(), '应付价卡附加费名称'
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'bms_surcharge_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 v.dict_sort, v.dict_label, v.dict_value, 'bms_surcharge_type', '', 'default', 'N', '0', 'admin', sysdate(), '应付价卡附加费'
|
||||
FROM (
|
||||
SELECT 1 AS dict_sort, '疫情附加费' AS dict_label, '疫情附加费' AS dict_value
|
||||
UNION ALL SELECT 2, '燃油附加费', '燃油附加费'
|
||||
UNION ALL SELECT 3, '地址及联系方式修正费', '地址及联系方式修正费'
|
||||
UNION ALL SELECT 4, '城市邮编不匹配', '城市邮编不匹配'
|
||||
UNION ALL SELECT 5, '分拣机不可操作费', '分拣机不可操作费'
|
||||
UNION ALL SELECT 6, '超重超尺寸费', '超重超尺寸费'
|
||||
UNION ALL SELECT 7, '脱碳附加费', '脱碳附加费'
|
||||
UNION ALL SELECT 8, '非法国电话联系费', '非法国电话联系费'
|
||||
UNION ALL SELECT 9, '换面单费', '换面单费'
|
||||
UNION ALL SELECT 10, '交错仓罚金', '交错仓罚金'
|
||||
UNION ALL SELECT 11, '乡村邮编单量占比过大罚金', '乡村邮编单量占比过大罚金'
|
||||
UNION ALL SELECT 12, '仿牌侵权罚款', '仿牌侵权罚款'
|
||||
UNION ALL SELECT 13, '危险品罚款', '危险品罚款'
|
||||
UNION ALL SELECT 14, '燃油费', '燃油费'
|
||||
UNION ALL SELECT 15, '重派费', '重派费'
|
||||
UNION ALL SELECT 16, '退件仓库操作费', '退件仓库操作费'
|
||||
UNION ALL SELECT 17, '重派仓库操作费', '重派仓库操作费'
|
||||
UNION ALL SELECT 18, '旺季附加费', '旺季附加费'
|
||||
UNION ALL SELECT 19, '高峰旺季附加费', '高峰旺季附加费'
|
||||
UNION ALL SELECT 20, '超尺寸超重费', '超尺寸超重费'
|
||||
UNION ALL SELECT 21, '路由码选择附加费', '路由码选择附加费'
|
||||
UNION ALL SELECT 22, '未预报附加费', '未预报附加费'
|
||||
UNION ALL SELECT 23, '托收附加费', '托收附加费'
|
||||
UNION ALL SELECT 24, '提供虚假信息罚款费', '提供虚假信息罚款费'
|
||||
UNION ALL SELECT 25, '零售网点路线', '零售网点路线'
|
||||
UNION ALL SELECT 26, '退件费', '退件费'
|
||||
UNION ALL SELECT 27, '退件重派费', '退件重派费'
|
||||
UNION ALL SELECT 28, '退件操作费', '退件操作费'
|
||||
UNION ALL SELECT 29, '重派操作费', '重派操作费'
|
||||
UNION ALL SELECT 30, '道路通行费', '道路通行费'
|
||||
UNION ALL SELECT 31, '重量信息修正附加费', '重量信息修正附加费'
|
||||
UNION ALL SELECT 32, '高风险地区附加费', '高风险地区附加费'
|
||||
UNION ALL SELECT 33, '取还服务', '取还服务'
|
||||
UNION ALL SELECT 34, '取货发货服务', '取货发货服务'
|
||||
UNION ALL SELECT 35, '现金服务', '现金服务'
|
||||
UNION ALL SELECT 36, '灵活配送服务', '灵活配送服务'
|
||||
UNION ALL SELECT 37, '安全费', '安全费'
|
||||
UNION ALL SELECT 38, '危机燃油附加费', '危机燃油附加费'
|
||||
UNION ALL SELECT 39, '能源附加费', '能源附加费'
|
||||
UNION ALL SELECT 40, '异形费', '异形费'
|
||||
UNION ALL SELECT 41, '漏预报或漏发货', '漏预报或漏发货'
|
||||
UNION ALL SELECT 42, '标签费', '标签费'
|
||||
UNION ALL SELECT 43, '无法派送费', '无法派送费'
|
||||
UNION ALL SELECT 44, '变更服务费', '变更服务费'
|
||||
UNION ALL SELECT 45, '二次派送费', '二次派送费'
|
||||
UNION ALL SELECT 46, '签名费', '签名费'
|
||||
UNION ALL SELECT 47, '危险货物费', '危险货物费'
|
||||
UNION ALL SELECT 48, '特殊产品加收', '特殊产品加收'
|
||||
UNION ALL SELECT 49, '岛屿附加费', '岛屿附加费'
|
||||
UNION ALL SELECT 50, '陆运能源附加费', '陆运能源附加费'
|
||||
UNION ALL SELECT 51, '空运能源附加费', '空运能源附加费'
|
||||
UNION ALL SELECT 52, '带电附加费', '带电附加费'
|
||||
UNION ALL SELECT 53, '特货费', '特货费'
|
||||
UNION ALL SELECT 54, '偏远费', '偏远费'
|
||||
UNION ALL SELECT 55, '偏远地区附加费', '偏远地区附加费'
|
||||
UNION ALL SELECT 56, '超大附加费(包裹)', '超大附加费(包裹)'
|
||||
UNION ALL SELECT 57, '超重附加费(包裹)', '超重附加费(包裹)'
|
||||
UNION ALL SELECT 58, '重复单号', '重复单号'
|
||||
UNION ALL SELECT 59, '未预报', '未预报'
|
||||
UNION ALL SELECT 60, '无法投递或其他原因退件运费', '无法投递或其他原因退件运费'
|
||||
UNION ALL SELECT 61, '航司可持续性附加费', '航司可持续性附加费'
|
||||
UNION ALL SELECT 62, '退件处理费', '退件处理费'
|
||||
UNION ALL SELECT 63, '超尺寸费', '超尺寸费'
|
||||
UNION ALL SELECT 64, '周末服务费', '周末服务费'
|
||||
UNION ALL SELECT 65, '空气清洁费', '空气清洁费'
|
||||
UNION ALL SELECT 66, '拖车附加费', '拖车附加费'
|
||||
UNION ALL SELECT 67, '派送附加费', '派送附加费'
|
||||
UNION ALL SELECT 68, '自送海外仓运费', '自送海外仓运费'
|
||||
UNION ALL SELECT 69, '操作费', '操作费'
|
||||
UNION ALL SELECT 70, '仓库操作费', '仓库操作费'
|
||||
UNION ALL SELECT 71, '重新包装费', '重新包装费'
|
||||
UNION ALL SELECT 72, '换包装费', '换包装费'
|
||||
UNION ALL SELECT 73, '去碳附加费', '去碳附加费'
|
||||
UNION ALL SELECT 74, '超重费', '超重费'
|
||||
UNION ALL SELECT 75, '地址修正费', '地址修正费'
|
||||
UNION ALL SELECT 76, '邮编城市不对应费', '邮编城市不对应费'
|
||||
UNION ALL SELECT 77, '邮箱和电话缺失费', '邮箱和电话缺失费'
|
||||
UNION ALL SELECT 78, '人工处理费', '人工处理费'
|
||||
UNION ALL SELECT 79, '海外省费', '海外省费'
|
||||
UNION ALL SELECT 80, '错误送仓罚款', '错误送仓罚款'
|
||||
UNION ALL SELECT 81, '更换面单费', '更换面单费'
|
||||
UNION ALL SELECT 82, '乡村地区占比超标准罚款', '乡村地区占比超标准罚款'
|
||||
UNION ALL SELECT 83, 'COD', 'COD'
|
||||
UNION ALL SELECT 84, '重新贴标附加费', '重新贴标附加费'
|
||||
UNION ALL SELECT 85, '重派运费', '重派运费'
|
||||
UNION ALL SELECT 86, '短期能源附加费', '短期能源附加费'
|
||||
UNION ALL SELECT 87, '旺季高峰期附加费', '旺季高峰期附加费'
|
||||
UNION ALL SELECT 88, '面单错误附加费', '面单错误附加费'
|
||||
UNION ALL SELECT 89, '零售网点自提费', '零售网点自提费'
|
||||
UNION ALL SELECT 90, '异形件费', '异形件费'
|
||||
UNION ALL SELECT 91, '重量差异附加费', '重量差异附加费'
|
||||
UNION ALL SELECT 92, '换单费', '换单费'
|
||||
UNION ALL SELECT 93, '无法投递处理费', '无法投递处理费'
|
||||
UNION ALL SELECT 94, '无法投递处理费-陆运', '无法投递处理费-陆运'
|
||||
UNION ALL SELECT 95, '无法投递处理费-空运', '无法投递处理费-空运'
|
||||
UNION ALL SELECT 96, '战争附加费', '战争附加费'
|
||||
UNION ALL SELECT 97, '超尺寸附加费', '超尺寸附加费'
|
||||
UNION ALL SELECT 98, '偏远附加费', '偏远附加费'
|
||||
UNION ALL SELECT 99, '空气减排费', '空气减排费'
|
||||
UNION ALL SELECT 100, '面单无法识别费', '面单无法识别费'
|
||||
UNION ALL SELECT 101, '无法派送或退件费', '无法派送或退件费'
|
||||
UNION ALL SELECT 102, '超重附加费', '超重附加费'
|
||||
UNION ALL SELECT 103, '重复单号费', '重复单号费'
|
||||
UNION ALL SELECT 104, '使用取消单号发货费', '使用取消单号发货费'
|
||||
UNION ALL SELECT 105, '面单不符费', '面单不符费'
|
||||
UNION ALL SELECT 106, '面单粘贴不合规范', '面单粘贴不合规范'
|
||||
UNION ALL SELECT 107, '未经允许乱交邮费', '未经允许乱交邮费'
|
||||
UNION ALL SELECT 108, '异形件附加费', '异形件附加费'
|
||||
UNION ALL SELECT 109, '退件费(陆运)', '退件费(陆运)'
|
||||
UNION ALL SELECT 110, '退件费(空运)', '退件费(空运)'
|
||||
UNION ALL SELECT 111, '邮编城市不对应', '邮编城市不对应'
|
||||
UNION ALL SELECT 112, '收件人信息错误处理费', '收件人信息错误处理费'
|
||||
UNION ALL SELECT 113, '无法过机操作费', '无法过机操作费'
|
||||
UNION ALL SELECT 114, '不可达地址费', '不可达地址费'
|
||||
UNION ALL SELECT 115, '销毁费', '销毁费'
|
||||
UNION ALL SELECT 116, '仓储费', '仓储费'
|
||||
UNION ALL SELECT 117, 'CO₂附加费', 'CO₂附加费'
|
||||
UNION ALL SELECT 118, '退回件处理费', '退回件处理费'
|
||||
UNION ALL SELECT 119, '异形件/超尺寸处理费', '异形件/超尺寸处理费'
|
||||
UNION ALL SELECT 120, '重量申报错误操作费', '重量申报错误操作费'
|
||||
UNION ALL SELECT 121, '垃圾处理费', '垃圾处理费'
|
||||
UNION ALL SELECT 122, '绿色附加费', '绿色附加费'
|
||||
UNION ALL SELECT 123, '无法投递费', '无法投递费'
|
||||
UNION ALL SELECT 124, '额外处理费', '额外处理费'
|
||||
UNION ALL SELECT 125, '超大件', '超大件'
|
||||
UNION ALL SELECT 126, '超限制', '超限制'
|
||||
UNION ALL SELECT 127, '地址更正', '地址更正'
|
||||
UNION ALL SELECT 128, '扩展区域收货和送货服务', '扩展区域收货和送货服务'
|
||||
UNION ALL SELECT 129, '偏远地区收货和送货服务', '偏远地区收货和送货服务'
|
||||
UNION ALL SELECT 130, '私人地址派送费', '私人地址派送费'
|
||||
UNION ALL SELECT 131, '包裹信息缺失', '包裹信息缺失'
|
||||
UNION ALL SELECT 132, '高峰附加费', '高峰附加费'
|
||||
UNION ALL SELECT 133, '更改地址费', '更改地址费'
|
||||
UNION ALL SELECT 134, '空气减排附加费', '空气减排附加费'
|
||||
UNION ALL SELECT 135, '异地取货费', '异地取货费'
|
||||
UNION ALL SELECT 136, '包裹退件运费', '包裹退件运费'
|
||||
UNION ALL SELECT 137, '退件入仓处理费', '退件入仓处理费'
|
||||
UNION ALL SELECT 138, '退件重派操作费', '退件重派操作费'
|
||||
UNION ALL SELECT 139, '退件重派运费', '退件重派运费'
|
||||
UNION ALL SELECT 140, '退件销毁费', '退件销毁费'
|
||||
UNION ALL SELECT 141, 'Bulky goods Fee', 'Bulky goods Fee'
|
||||
UNION ALL SELECT 142, '地址校验费', '地址校验费'
|
||||
UNION ALL SELECT 143, 'Labeling fee', 'Labeling fee'
|
||||
UNION ALL SELECT 144, '瑞典和芬兰门到门派送费', '瑞典和芬兰门到门派送费'
|
||||
UNION ALL SELECT 145, '特别旺季附加费', '特别旺季附加费'
|
||||
UNION ALL SELECT 146, '脱欧手续费', '脱欧手续费'
|
||||
) v
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_dict_data d
|
||||
WHERE d.dict_type = 'bms_surcharge_type' AND d.dict_value = v.dict_value
|
||||
);
|
||||
@@ -88,6 +88,29 @@ CREATE TABLE IF NOT EXISTS `pay_extra_line` (
|
||||
KEY `idx_pay_extra_product` (`product_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应付产品上的邮编或其他配置原文';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_product_fee_rule` (
|
||||
`rule_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '规则ID',
|
||||
`product_id` bigint(20) NOT NULL COMMENT '根产品编号,渠道应付绑定的产品,不是某一版导入的版本号',
|
||||
`fee_name` varchar(100) NOT NULL COMMENT '附加费名称,对应价卡附加费行,用来取价',
|
||||
`enabled` char(1) NOT NULL DEFAULT '0' COMMENT '1参与计算,0不参与',
|
||||
`trigger_type` varchar(20) NOT NULL DEFAULT 'ALWAYS' COMMENT '触发类型:SIZE尺寸 VOLUME体积 WEIGHT重量 POSTCODE邮编 ALWAYS每票 RETURN退件',
|
||||
`side_max_cm` decimal(10,2) DEFAULT NULL COMMENT '最长边上限,厘米,超出则命中',
|
||||
`sum_max_cm` decimal(10,2) DEFAULT NULL COMMENT '长+宽+高上限,厘米,超出则命中',
|
||||
`volume_max_cm3` decimal(14,2) DEFAULT NULL COMMENT '体积上限,立方厘米,长×宽×高超出则命中',
|
||||
`min_length_cm` decimal(10,2) DEFAULT NULL COMMENT '最小长度,厘米,小于则命中',
|
||||
`min_width_cm` decimal(10,2) DEFAULT NULL COMMENT '最小宽度,厘米,小于则命中',
|
||||
`min_height_cm` decimal(10,2) DEFAULT NULL COMMENT '最小高度,厘米,小于则命中',
|
||||
`weight_max_kg` decimal(10,3) DEFAULT NULL COMMENT '重量上限,千克,计费重超出则命中',
|
||||
`join_mode` varchar(10) NOT NULL DEFAULT 'ANY' COMMENT 'ANY任一条超出就收,ALL写了的条件都超出才收',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '说明,不参与判断',
|
||||
`created_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`rule_id`),
|
||||
UNIQUE KEY `uk_pay_fee_rule_product` (`product_id`, `fee_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应付产品附加费开关';
|
||||
|
||||
-- 菜单挂在价格基础下面
|
||||
SET @parentId := (
|
||||
SELECT menu_id FROM sys_menu
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 应付产品附加费开关。挂在根产品上,同一产品同一费用名称一行。可重复执行。
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_product_fee_rule` (
|
||||
`rule_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '规则ID',
|
||||
`product_id` bigint(20) NOT NULL COMMENT '根产品编号,渠道应付绑定的产品,不是某一版导入的版本号',
|
||||
`fee_name` varchar(100) NOT NULL COMMENT '附加费名称,对应价卡附加费行,用来取价',
|
||||
`enabled` char(1) NOT NULL DEFAULT '0' COMMENT '1参与计算,0不参与',
|
||||
`trigger_type` varchar(20) NOT NULL DEFAULT 'ALWAYS' COMMENT '触发类型:SIZE尺寸 VOLUME体积 WEIGHT重量 POSTCODE邮编 ALWAYS每票 RETURN退件',
|
||||
`side_max_cm` decimal(10,2) DEFAULT NULL COMMENT '最长边上限,厘米,超出则命中',
|
||||
`sum_max_cm` decimal(10,2) DEFAULT NULL COMMENT '长+宽+高上限,厘米,超出则命中',
|
||||
`volume_max_cm3` decimal(14,2) DEFAULT NULL COMMENT '体积上限,立方厘米,长×宽×高超出则命中',
|
||||
`min_length_cm` decimal(10,2) DEFAULT NULL COMMENT '最小长度,厘米,小于则命中',
|
||||
`min_width_cm` decimal(10,2) DEFAULT NULL COMMENT '最小宽度,厘米,小于则命中',
|
||||
`min_height_cm` decimal(10,2) DEFAULT NULL COMMENT '最小高度,厘米,小于则命中',
|
||||
`weight_max_kg` decimal(10,3) DEFAULT NULL COMMENT '重量上限,千克,计费重超出则命中',
|
||||
`join_mode` varchar(10) NOT NULL DEFAULT 'ANY' COMMENT 'ANY任一条超出就收,ALL写了的条件都超出才收',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '说明,不参与判断',
|
||||
`created_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`rule_id`),
|
||||
UNIQUE KEY `uk_pay_fee_rule_product` (`product_id`, `fee_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应付产品附加费开关';
|
||||
Reference in New Issue
Block a user