新增路线产品
This commit is contained in:
@@ -3,6 +3,7 @@ package com.bms.masterdata.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.bms.common.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -14,72 +15,136 @@ import lombok.Data;
|
||||
@Data
|
||||
public class LineProduct
|
||||
{
|
||||
/** 源主键 ID */
|
||||
/** 源主键 ID。列表、表单和导出都不展示。 */
|
||||
private String id;
|
||||
|
||||
/** 产品(线路)编码 */
|
||||
@Excel(name = "产品编码")
|
||||
private String lineCode;
|
||||
|
||||
/** 产品(线路)名称 */
|
||||
@Excel(name = "产品名称")
|
||||
private String lineName;
|
||||
|
||||
@Excel(name = "产品类型")
|
||||
private String productType;
|
||||
|
||||
@Excel(name = "线路类型")
|
||||
private String lineType;
|
||||
|
||||
/** 是否可收货(1是 0否) */
|
||||
@Excel(name = "是否可收货", readConverterExp = "1=是,0=否")
|
||||
private String blCargo;
|
||||
|
||||
@Excel(name = "货物名称")
|
||||
private String cargoName;
|
||||
|
||||
@Excel(name = "仓储服务名称")
|
||||
private String productStorage;
|
||||
|
||||
@Excel(name = "头程服务名称")
|
||||
private String productFirst;
|
||||
|
||||
@Excel(name = "清关服务名称")
|
||||
private String productClearance;
|
||||
|
||||
@Excel(name = "仓储服务编码")
|
||||
private String productStorageCode;
|
||||
|
||||
@Excel(name = "头程服务编码")
|
||||
private String productFirstCode;
|
||||
|
||||
@Excel(name = "清关服务编码")
|
||||
private String productClearanceCode;
|
||||
|
||||
/** 是否启用(1启用 0停用) */
|
||||
@Excel(name = "是否启用", readConverterExp = "1=启用,0=停用")
|
||||
private String blOpen;
|
||||
|
||||
/** 是否分段(1是 0否) */
|
||||
@Excel(name = "是否分段", readConverterExp = "1=是,0=否")
|
||||
private String blSection;
|
||||
|
||||
@Excel(name = "派送商编码")
|
||||
private String dispCode;
|
||||
|
||||
@Excel(name = "派送商名称")
|
||||
private String dispName;
|
||||
|
||||
@Excel(name = "费用类别")
|
||||
private String feeClass;
|
||||
|
||||
@Excel(name = "发货人ID")
|
||||
private String shipperId;
|
||||
|
||||
/** 发货规则 */
|
||||
@Excel(name = "发货规则")
|
||||
private String shipmentRule;
|
||||
|
||||
/** 限重(kg) */
|
||||
@Excel(name = "限重(kg)")
|
||||
private BigDecimal weight;
|
||||
|
||||
@Excel(name = "货物类型")
|
||||
private String cargoType;
|
||||
|
||||
@Excel(name = "产品分类")
|
||||
private String productCategory;
|
||||
|
||||
/** 体积基数(抛货系数) */
|
||||
@Excel(name = "体积基数")
|
||||
private BigDecimal volumeBasis;
|
||||
|
||||
@Excel(name = "重量类型")
|
||||
private String weightType;
|
||||
|
||||
@Excel(name = "服务类型")
|
||||
private String serviceType;
|
||||
|
||||
@Excel(name = "发货类型")
|
||||
private String shipmentType;
|
||||
|
||||
@Excel(name = "税费金额")
|
||||
private BigDecimal taxAmount;
|
||||
|
||||
@Excel(name = "税费币种")
|
||||
private String taxCurrency;
|
||||
|
||||
@Excel(name = "电池类型")
|
||||
private String batteryType;
|
||||
|
||||
@Excel(name = "备注")
|
||||
private String remark;
|
||||
|
||||
/** 源创建人,页面不展示 */
|
||||
private String createName;
|
||||
|
||||
/** 源创建人工号,页面不展示 */
|
||||
private String createManCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "创建时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createDate;
|
||||
|
||||
@Excel(name = "修改人")
|
||||
private String modifier;
|
||||
|
||||
@Excel(name = "修改人工号")
|
||||
private String modifierCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "修改时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date modifyDate;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "GMT创建时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date gmtCreate;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "GMT修改时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date gmtModified;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Excel(name = "同步时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date syncTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bms.masterdata.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
|
||||
/**
|
||||
* 路线产品 srv_line_product
|
||||
*/
|
||||
public interface LineProductMapper
|
||||
{
|
||||
List<LineProduct> selectLineProductList(LineProduct query);
|
||||
|
||||
LineProduct selectLineProductById(String id);
|
||||
|
||||
LineProduct selectLineProductByCode(String lineCode);
|
||||
|
||||
int countProductChannelByProductId(String productId);
|
||||
|
||||
int insertLineProduct(LineProduct product);
|
||||
|
||||
int updateLineProduct(LineProduct product);
|
||||
|
||||
int updateLineProductStatus(LineProduct product);
|
||||
|
||||
int deleteLineProductByIds(String[] ids);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.bms.masterdata.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
|
||||
/**
|
||||
* 路线产品
|
||||
*/
|
||||
public interface ILineProductService
|
||||
{
|
||||
List<LineProduct> selectLineProductList(LineProduct query);
|
||||
|
||||
LineProduct selectLineProductById(String id);
|
||||
|
||||
int insertLineProduct(LineProduct product);
|
||||
|
||||
int updateLineProduct(LineProduct product);
|
||||
|
||||
int updateLineProductStatus(LineProduct product);
|
||||
|
||||
int deleteLineProductByIds(String[] ids);
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
package com.bms.masterdata.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
import com.bms.masterdata.mapper.LineProductMapper;
|
||||
import com.bms.masterdata.service.ILineProductService;
|
||||
|
||||
/**
|
||||
* 路线产品
|
||||
*/
|
||||
@Service
|
||||
public class LineProductServiceImpl implements ILineProductService
|
||||
{
|
||||
@Autowired
|
||||
private LineProductMapper lineProductMapper;
|
||||
|
||||
@Override
|
||||
public List<LineProduct> selectLineProductList(LineProduct query)
|
||||
{
|
||||
return lineProductMapper.selectLineProductList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineProduct selectLineProductById(String id)
|
||||
{
|
||||
return lineProductMapper.selectLineProductById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertLineProduct(LineProduct product)
|
||||
{
|
||||
prepare(product, true);
|
||||
product.setId(IdUtils.fastSimpleUUID());
|
||||
assertCodeUnique(product);
|
||||
return lineProductMapper.insertLineProduct(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateLineProduct(LineProduct product)
|
||||
{
|
||||
if (product == null || StringUtils.isEmpty(product.getId()))
|
||||
{
|
||||
throw new ServiceException("路线产品不存在");
|
||||
}
|
||||
LineProduct existing = lineProductMapper.selectLineProductById(product.getId());
|
||||
if (existing == null)
|
||||
{
|
||||
throw new ServiceException("路线产品不存在或已被删除");
|
||||
}
|
||||
prepare(product, false);
|
||||
assertCodeUnique(product);
|
||||
return lineProductMapper.updateLineProduct(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateLineProductStatus(LineProduct product)
|
||||
{
|
||||
if (product == null || StringUtils.isEmpty(product.getId()))
|
||||
{
|
||||
throw new ServiceException("路线产品不存在");
|
||||
}
|
||||
if (!"0".equals(product.getBlOpen()) && !"1".equals(product.getBlOpen()))
|
||||
{
|
||||
throw new ServiceException("启用状态只能是启用或停用");
|
||||
}
|
||||
LineProduct existing = lineProductMapper.selectLineProductById(product.getId());
|
||||
if (existing == null)
|
||||
{
|
||||
throw new ServiceException("路线产品不存在或已被删除");
|
||||
}
|
||||
return lineProductMapper.updateLineProductStatus(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteLineProductByIds(String[] ids)
|
||||
{
|
||||
if (ids == null || ids.length == 0)
|
||||
{
|
||||
throw new ServiceException("请选择要删除的路线产品");
|
||||
}
|
||||
for (String id : ids)
|
||||
{
|
||||
if (StringUtils.isEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LineProduct existing = lineProductMapper.selectLineProductById(id);
|
||||
if (existing == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (lineProductMapper.countProductChannelByProductId(id) > 0)
|
||||
{
|
||||
throw new ServiceException("路线产品「" + existing.getLineName() + "」已被渠道关系引用,不能删除,请改为停用");
|
||||
}
|
||||
}
|
||||
return lineProductMapper.deleteLineProductByIds(ids);
|
||||
}
|
||||
|
||||
private void prepare(LineProduct product, boolean creating)
|
||||
{
|
||||
if (product == null)
|
||||
{
|
||||
throw new ServiceException("路线产品不能为空");
|
||||
}
|
||||
product.setLineCode(StringUtils.trim(product.getLineCode()));
|
||||
product.setLineName(StringUtils.trim(product.getLineName()));
|
||||
product.setProductType(limit(product.getProductType(), 20, "产品类型"));
|
||||
product.setLineType(limit(product.getLineType(), 50, "线路类型"));
|
||||
product.setCargoName(limit(product.getCargoName(), 100, "货物名称"));
|
||||
product.setProductStorage(trimToNull(product.getProductStorage()));
|
||||
product.setProductFirst(trimToNull(product.getProductFirst()));
|
||||
product.setProductClearance(trimToNull(product.getProductClearance()));
|
||||
product.setProductStorageCode(limit(product.getProductStorageCode(), 1000, "仓储服务编码"));
|
||||
product.setProductFirstCode(limit(product.getProductFirstCode(), 1000, "头程服务编码"));
|
||||
product.setProductClearanceCode(limit(product.getProductClearanceCode(), 1000, "清关服务编码"));
|
||||
product.setDispCode(limit(product.getDispCode(), 60, "派送商编码"));
|
||||
product.setDispName(limit(product.getDispName(), 100, "派送商名称"));
|
||||
product.setFeeClass(limit(product.getFeeClass(), 100, "费用类别"));
|
||||
product.setShipperId(limit(product.getShipperId(), 64, "发货人ID"));
|
||||
product.setShipmentRule(flagOrNull(product.getShipmentRule(), "发货规则"));
|
||||
product.setCargoType(limit(product.getCargoType(), 64, "货物类型"));
|
||||
product.setProductCategory(limit(product.getProductCategory(), 100, "产品分类"));
|
||||
product.setWeightType(flagOrNull(product.getWeightType(), "重量类型"));
|
||||
product.setServiceType(flagOrNull(product.getServiceType(), "服务类型"));
|
||||
product.setShipmentType(limit(product.getShipmentType(), 32, "发货类型"));
|
||||
product.setTaxCurrency(limit(product.getTaxCurrency(), 16, "税费币种"));
|
||||
product.setBatteryType(limit(product.getBatteryType(), 64, "电池类型"));
|
||||
product.setRemark(limit(product.getRemark(), 500, "备注"));
|
||||
product.setBlCargo(yesNo(product.getBlCargo(), "是否可收货"));
|
||||
product.setBlSection(yesNo(product.getBlSection(), "是否分段"));
|
||||
if (StringUtils.isEmpty(product.getLineCode()))
|
||||
{
|
||||
throw new ServiceException("产品编码不能为空");
|
||||
}
|
||||
if (product.getLineCode().length() > 64)
|
||||
{
|
||||
throw new ServiceException("产品编码不能超过64个字符");
|
||||
}
|
||||
if (StringUtils.isEmpty(product.getLineName()))
|
||||
{
|
||||
throw new ServiceException("产品名称不能为空");
|
||||
}
|
||||
if (product.getLineName().length() > 200)
|
||||
{
|
||||
throw new ServiceException("产品名称不能超过200个字符");
|
||||
}
|
||||
if (StringUtils.isEmpty(product.getBlOpen()))
|
||||
{
|
||||
if (creating)
|
||||
{
|
||||
product.setBlOpen("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceException("请选择启用状态");
|
||||
}
|
||||
}
|
||||
if (!"0".equals(product.getBlOpen()) && !"1".equals(product.getBlOpen()))
|
||||
{
|
||||
throw new ServiceException("启用状态只能是启用或停用");
|
||||
}
|
||||
if (product.getWeight() != null && product.getWeight().compareTo(BigDecimal.ZERO) < 0)
|
||||
{
|
||||
throw new ServiceException("限重不能为负数");
|
||||
}
|
||||
if (product.getVolumeBasis() != null && product.getVolumeBasis().compareTo(BigDecimal.ZERO) < 0)
|
||||
{
|
||||
throw new ServiceException("体积基数不能为负数");
|
||||
}
|
||||
if (product.getTaxAmount() != null && product.getTaxAmount().compareTo(BigDecimal.ZERO) < 0)
|
||||
{
|
||||
throw new ServiceException("税费金额不能为负数");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCodeUnique(LineProduct product)
|
||||
{
|
||||
LineProduct same = lineProductMapper.selectLineProductByCode(product.getLineCode());
|
||||
if (same != null && !StringUtils.equals(same.getId(), product.getId()))
|
||||
{
|
||||
throw new ServiceException("产品编码「" + product.getLineCode() + "」已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private String yesNo(String value, String label)
|
||||
{
|
||||
String text = trimToNull(value);
|
||||
if (text != null && !"0".equals(text) && !"1".equals(text))
|
||||
{
|
||||
throw new ServiceException(label + "只能是是或否");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/** 单字符标记:空则清空,有值时只能是一个字符。 */
|
||||
private String flagOrNull(String value, String label)
|
||||
{
|
||||
String text = trimToNull(value);
|
||||
if (text != null && text.length() > 1)
|
||||
{
|
||||
throw new ServiceException(label + "只能填写一个字符");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private String limit(String value, int max, String label)
|
||||
{
|
||||
String text = trimToNull(value);
|
||||
if (text != null && text.length() > max)
|
||||
{
|
||||
throw new ServiceException(label + "不能超过" + max + "个字符");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private String trimToNull(String value)
|
||||
{
|
||||
String text = StringUtils.trim(value);
|
||||
return StringUtils.isEmpty(text) ? null : text;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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,62 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
/**
|
||||
* 应付价卡解析进度。给导入弹框轮询,不入库。
|
||||
*/
|
||||
public class PayParseProgress
|
||||
{
|
||||
public static final String PENDING = "PENDING";
|
||||
|
||||
public static final String RUNNING = "RUNNING";
|
||||
|
||||
public static final String SUCCESS = "SUCCESS";
|
||||
|
||||
public static final String FAIL = "FAIL";
|
||||
|
||||
private int percent;
|
||||
|
||||
private String message;
|
||||
|
||||
private String status;
|
||||
|
||||
public PayParseProgress()
|
||||
{
|
||||
}
|
||||
|
||||
public PayParseProgress(int percent, String message, String status)
|
||||
{
|
||||
this.percent = percent;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public int getPercent()
|
||||
{
|
||||
return percent;
|
||||
}
|
||||
|
||||
public void setPercent(int percent)
|
||||
{
|
||||
this.percent = percent;
|
||||
}
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class PaySurcharge
|
||||
/** 固定收取、产生时收取、旺季期间 */
|
||||
private String chargeTiming;
|
||||
|
||||
/** ALWAYS、POSTCODE、WEIGHT、SIZE、RETURN、MANUAL */
|
||||
/** ALWAYS、POSTCODE、WEIGHT、SIZE、RETURN、EXTRA_KG、MANUAL */
|
||||
private String calcRule;
|
||||
|
||||
/** 邮编列表、重量阈值或尺寸表达式 */
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -68,8 +68,14 @@ public final class PayCostCalculator
|
||||
PayWeightBand band = matchBand(bands, weight);
|
||||
if (band == null)
|
||||
{
|
||||
result.setMessage(outOfRange(product, bands, weight));
|
||||
return result;
|
||||
PayWeightBand top = highestBand(bands);
|
||||
if (top == null || weight.compareTo(top.getWeightMax()) <= 0
|
||||
|| !hasExtraKg(product, zoneName, top.getWeightMax(), top.getSectionName()))
|
||||
{
|
||||
result.setMessage(outOfRange(product, bands, weight));
|
||||
return result;
|
||||
}
|
||||
band = top;
|
||||
}
|
||||
String currency = currencyOf(product, band);
|
||||
BigDecimal freight = bandAmount(band, weight);
|
||||
@@ -81,7 +87,8 @@ public final class PayCostCalculator
|
||||
PayCostLine freightLine = line("BASIC_FREIGHT", "基础运费", 0L, freight, currency, freightRemark);
|
||||
result.getFees().add(freightLine);
|
||||
result.setCurrency(currency);
|
||||
addSurcharges(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, freight, currency, result);
|
||||
addSurcharges(product, weight, length, width, height, postcode, zoneName, bizDate, orderStatus, freight, currency,
|
||||
band.getSectionName(), result);
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (PayCostLine fee : result.getFees())
|
||||
{
|
||||
@@ -111,7 +118,7 @@ public final class PayCostCalculator
|
||||
|
||||
private static void addSurcharges(PayProduct product, BigDecimal weight, BigDecimal length, BigDecimal width,
|
||||
BigDecimal height, String postcode, String zoneName, Date bizDate, Integer orderStatus, BigDecimal freight, String currency,
|
||||
PayOrderCostResult result)
|
||||
String freightSection, PayOrderCostResult result)
|
||||
{
|
||||
if (product.getSurcharges() == null)
|
||||
{
|
||||
@@ -171,6 +178,13 @@ public final class PayCostCalculator
|
||||
addFlat(result.getFees(), fee, currency, "订单已退件");
|
||||
}
|
||||
}
|
||||
else if (PaySurchargeRules.EXTRA_KG.equals(rule))
|
||||
{
|
||||
if (sameSection(fee.getSectionName(), freightSection))
|
||||
{
|
||||
addExtraKg(result.getFees(), fee, weight, currency);
|
||||
}
|
||||
}
|
||||
else if (PaySurchargeRules.ALWAYS.equals(rule))
|
||||
{
|
||||
PayCostLine matched = matchedFee(fee, freight, currency, blank(fee.getChargeText()));
|
||||
@@ -214,6 +228,97 @@ public final class PayCostCalculator
|
||||
return kept;
|
||||
}
|
||||
|
||||
private static void addExtraKg(List<PayCostLine> fees, PaySurcharge fee, BigDecimal weight, String currency)
|
||||
{
|
||||
if (fee.getAmount() == null || weight == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BigDecimal threshold;
|
||||
try
|
||||
{
|
||||
threshold = new BigDecimal(fee.getCalcParam() == null ? "" : fee.getCalcParam().trim());
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (weight.compareTo(threshold) <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
String feeCurrency = normalize(fee.getCurrency());
|
||||
if (!feeCurrency.isEmpty() && !feeCurrency.equals(currency))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BigDecimal over = weight.subtract(threshold);
|
||||
BigDecimal amount = fee.getAmount().multiply(over).setScale(2, RoundingMode.HALF_UP);
|
||||
long scId = fee.getSurchargeId() == null ? 0L : fee.getSurchargeId();
|
||||
String remark = "超出 " + plain(over) + "kg × " + plain(fee.getAmount()) + " " + blank(fee.getPriceUnit());
|
||||
fees.add(line("SURCHARGE", blank(fee.getFeeName()), scId, amount, currency, remark));
|
||||
}
|
||||
|
||||
/** 同一地区有多张服务表时,续重只跟本次首重所在的那一张表。 */
|
||||
private static boolean sameSection(String feeSection, String freightSection)
|
||||
{
|
||||
if (feeSection == null || feeSection.isBlank() || freightSection == null || freightSection.isBlank())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return feeSection.trim().equals(freightSection.trim());
|
||||
}
|
||||
|
||||
/** 重量超过最高档,且该地区、该服务表有每公斤续重时,基础运费先用最高档。 */
|
||||
private static boolean hasExtraKg(PayProduct product, String zoneName, BigDecimal threshold, String section)
|
||||
{
|
||||
if (product.getSurcharges() == null || threshold == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (PaySurcharge fee : product.getSurcharges())
|
||||
{
|
||||
if (fee == null || !PaySurchargeRules.EXTRA_KG.equals(fee.getCalcRule()) || fee.getAmount() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (zoneName != null && !zoneName.isBlank() && fee.getZoneName() != null && !fee.getZoneName().isBlank()
|
||||
&& !PayZoneMatcher.sameZone(fee.getZoneName(), zoneName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!sameSection(fee.getSectionName(), section))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (fee.getCalcParam() != null && new BigDecimal(fee.getCalcParam().trim()).compareTo(threshold) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PayWeightBand highestBand(List<PayWeightBand> bands)
|
||||
{
|
||||
PayWeightBand top = null;
|
||||
for (PayWeightBand band : bands)
|
||||
{
|
||||
if (top == null || band.getWeightMax().compareTo(top.getWeightMax()) > 0)
|
||||
{
|
||||
top = band;
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
private static void addFlat(List<PayCostLine> fees, PaySurcharge fee, String currency, String remark)
|
||||
{
|
||||
PayCostLine line = matchedFee(fee, null, currency, remark);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.bms.price.pay;
|
||||
|
||||
/**
|
||||
* 应付价卡解析进度。只用于界面展示,不参与识别。
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PayParseProgressListener
|
||||
{
|
||||
void onProgress(int percent, String message);
|
||||
}
|
||||
@@ -26,6 +26,9 @@ public final class PaySurchargeRules
|
||||
|
||||
public static final String MANUAL = "MANUAL";
|
||||
|
||||
/** 超过重量段上限后,按超出的公斤数乘单价 */
|
||||
public static final String EXTRA_KG = "EXTRA_KG";
|
||||
|
||||
private static final Pattern RANGE = Pattern.compile("([A-Z]{1,2})(\\d{1,2})\\s*-\\s*(\\d{1,2})");
|
||||
|
||||
private static final Pattern DISTRICT = Pattern.compile("([A-Z]{1,2})(\\d{1,2})");
|
||||
|
||||
@@ -112,6 +112,15 @@ public class PayWorkbookParser
|
||||
* 单元格里用分号隔开的国家、分区或费用名称,拆成多条。
|
||||
*/
|
||||
public PayPreview parseMatched(byte[] bytes, String filename, String selected, Collection<String> dictValues)
|
||||
{
|
||||
return parseMatched(bytes, filename, selected, dictValues, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 {@link #parseMatched(byte[], String, String, Collection)} 相同。listener 只接收进度,不改变识别结果。
|
||||
*/
|
||||
public PayPreview parseMatched(byte[] bytes, String filename, String selected, Collection<String> dictValues,
|
||||
PayParseProgressListener listener)
|
||||
{
|
||||
if (bytes == null || bytes.length == 0)
|
||||
{
|
||||
@@ -128,6 +137,7 @@ public class PayWorkbookParser
|
||||
}
|
||||
String safeName = filename == null ? "价卡.xlsx" : filename;
|
||||
String wanted = norm(selected);
|
||||
report(listener, 8, "正在打开工作簿");
|
||||
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(bytes)))
|
||||
{
|
||||
if (workbook.getNumberOfSheets() == 0)
|
||||
@@ -141,29 +151,43 @@ public class PayWorkbookParser
|
||||
preview.setSupplierName(meta.supplier);
|
||||
preview.setEffectiveDate(PayTexts.toDate(meta.effective));
|
||||
List<String> skipped = new ArrayList<>();
|
||||
for (int i = 0; i < workbook.getNumberOfSheets(); i++)
|
||||
int sheetCount = workbook.getNumberOfSheets();
|
||||
for (int i = 0; i < sheetCount; i++)
|
||||
{
|
||||
Sheet sheet = workbook.getSheetAt(i);
|
||||
String sheetName = sheet.getSheetName() == null ? "" : sheet.getSheetName().trim();
|
||||
int start = 12 + i * 78 / sheetCount;
|
||||
int end = 12 + (i + 1) * 78 / sheetCount;
|
||||
String dictValue = dict.get(norm(sheetName));
|
||||
boolean holdForFallback = sheetCount == 1 && !wanted.isEmpty();
|
||||
if (dictValue == null)
|
||||
{
|
||||
skipped.add(sheetName);
|
||||
if (!holdForFallback)
|
||||
{
|
||||
report(listener, end, "已跳过「" + sheetName + "」");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!wanted.isEmpty() && !wanted.equals(norm(dictValue)) && !wanted.equals(norm(sheetName)))
|
||||
{
|
||||
skipped.add(sheetName);
|
||||
if (!holdForFallback)
|
||||
{
|
||||
report(listener, end, "已跳过「" + sheetName + "」");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
preview.getProducts().add(readMatchedSheet(sheet, sheetName, dictValue, meta, preview));
|
||||
report(listener, start, "正在解析「" + sheetName + "」");
|
||||
preview.getProducts().add(readMatchedSheet(sheet, sheetName, dictValue, meta, preview, listener, start, end));
|
||||
}
|
||||
if (preview.getProducts().isEmpty() && !wanted.isEmpty() && workbook.getNumberOfSheets() == 1)
|
||||
if (preview.getProducts().isEmpty() && !wanted.isEmpty() && sheetCount == 1)
|
||||
{
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
String sheetName = sheet.getSheetName() == null ? "" : sheet.getSheetName().trim();
|
||||
String dictValue = dict.containsValue(selected.trim()) ? selected.trim() : labelOf(dict, wanted);
|
||||
preview.getProducts().add(readMatchedSheet(sheet, sheetName, dictValue, meta, preview));
|
||||
report(listener, 12, "正在解析「" + sheetName + "」");
|
||||
preview.getProducts().add(readMatchedSheet(sheet, sheetName, dictValue, meta, preview, listener, 12, 90));
|
||||
skipped.clear();
|
||||
}
|
||||
if (preview.getProducts().isEmpty())
|
||||
@@ -172,8 +196,10 @@ public class PayWorkbookParser
|
||||
? "没有工作表能对上应付产品字典"
|
||||
: "文件里没有「" + selected.trim() + "」这张工作表");
|
||||
}
|
||||
report(listener, 92, "正在整理附加费");
|
||||
attachWorkbookRates(workbook, preview.getProducts());
|
||||
preview.setSkippedSheets(skipped);
|
||||
report(listener, 96, "工作表解析完成");
|
||||
return preview;
|
||||
}
|
||||
catch (ServiceException ex)
|
||||
@@ -186,7 +212,24 @@ public class PayWorkbookParser
|
||||
}
|
||||
}
|
||||
|
||||
private PayProduct readMatchedSheet(Sheet sheet, String sheetName, String dictValue, FileMeta meta, PayPreview preview)
|
||||
private static void report(PayParseProgressListener listener, int percent, String message)
|
||||
{
|
||||
if (listener == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
listener.onProgress(percent, message);
|
||||
}
|
||||
catch (RuntimeException ignored)
|
||||
{
|
||||
// 进度写失败不影响价卡识别
|
||||
}
|
||||
}
|
||||
|
||||
private PayProduct readMatchedSheet(Sheet sheet, String sheetName, String dictValue, FileMeta meta, PayPreview preview,
|
||||
PayParseProgressListener listener, int start, int end)
|
||||
{
|
||||
PayProduct product = new PayProduct();
|
||||
product.setProductName(dictValue);
|
||||
@@ -199,11 +242,30 @@ public class PayWorkbookParser
|
||||
product.setWeightBands(new ArrayList<>());
|
||||
product.setSurcharges(new ArrayList<>());
|
||||
product.setExtraLines(new ArrayList<>());
|
||||
fillSheet(ExcelGrid.read(sheet), product);
|
||||
fillSheet(readGrid(sheet, listener, start, end, sheetName), product);
|
||||
expandSeparated(product);
|
||||
report(listener, end, "已解析「" + sheetName + "」");
|
||||
return product;
|
||||
}
|
||||
|
||||
/** 读取这一张表。前半段是统计列宽,后半段是读取单元格。 */
|
||||
private ExcelGrid readGrid(Sheet sheet, PayParseProgressListener listener, int start, int end, String sheetName)
|
||||
{
|
||||
int span = Math.max(1, end - start);
|
||||
int scanSpan = Math.max(1, span * 2 / 5);
|
||||
return ExcelGrid.read(sheet, ExcelGrid.MAX_ROWS, (phase, row, total) -> {
|
||||
if (total < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int done = Math.min(row + 1, total);
|
||||
int percent = phase == 0
|
||||
? start + scanSpan * done / total
|
||||
: start + scanSpan + (span - scanSpan) * done / total;
|
||||
report(listener, Math.min(end, percent), "正在解析「" + sheetName + "」");
|
||||
});
|
||||
}
|
||||
|
||||
/** 分号拆开的是多条数据,不是一句话。句子太长时保持原样。 */
|
||||
private void expandSeparated(PayProduct product)
|
||||
{
|
||||
@@ -1848,7 +1910,8 @@ public class PayWorkbookParser
|
||||
|
||||
/**
|
||||
* 岛屿价卡一张表有两级表头:上面是群岛,下面是具体岛屿。
|
||||
* Up to 1 kg 是首重,Additional Kg 是续重,Cubic Capacity 是体积重系数,不是运费。
|
||||
* Up to 1 kg 是首重,记入重量段。Additional Kg 是超出首重后的每公斤加收,记入附加费。
|
||||
* Cubic Capacity 是体积重系数,不是运费。
|
||||
*/
|
||||
private int readInsularRates(ExcelGrid grid, int header, PayProduct product)
|
||||
{
|
||||
@@ -1869,6 +1932,7 @@ public class PayWorkbookParser
|
||||
zoneCols.add(col);
|
||||
}
|
||||
}
|
||||
BigDecimal firstLimit = null;
|
||||
int row = header + 1;
|
||||
while (row < grid.rows() && !grid.blankRow(row))
|
||||
{
|
||||
@@ -1887,6 +1951,10 @@ public class PayWorkbookParser
|
||||
row++;
|
||||
continue;
|
||||
}
|
||||
if (firstKg)
|
||||
{
|
||||
firstLimit = firstKgLimit(label);
|
||||
}
|
||||
for (int col : zoneCols)
|
||||
{
|
||||
String child = grid.text(header, col).replace('\n', ' ').trim();
|
||||
@@ -1902,23 +1970,20 @@ public class PayWorkbookParser
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (extraKg)
|
||||
{
|
||||
addExtraKg(product, section, zone, label, price, firstLimit == null ? BigDecimal.ONE : firstLimit);
|
||||
continue;
|
||||
}
|
||||
PayWeightBand band = new PayWeightBand();
|
||||
band.setSectionName(section);
|
||||
band.setZoneName(zone);
|
||||
band.setWeightLabel(label);
|
||||
band.setPrice(price);
|
||||
if (extraKg)
|
||||
{
|
||||
band.setChargeName("每公斤");
|
||||
band.setPriceUnit("EUR/公斤");
|
||||
}
|
||||
else
|
||||
{
|
||||
band.setWeightMin(BigDecimal.ZERO);
|
||||
band.setWeightMax(firstKgLimit(label));
|
||||
band.setChargeName("每件");
|
||||
band.setPriceUnit("EUR/件");
|
||||
}
|
||||
band.setWeightMin(BigDecimal.ZERO);
|
||||
band.setWeightMax(firstKgLimit(label));
|
||||
band.setChargeName("每件");
|
||||
band.setPriceUnit("EUR/件");
|
||||
band.setSortNo(product.getWeightBands().size() + 1);
|
||||
product.getWeightBands().add(band);
|
||||
}
|
||||
@@ -1928,7 +1993,8 @@ public class PayWorkbookParser
|
||||
}
|
||||
|
||||
/**
|
||||
* 西班牙 CTT:上面一行是大陆或群岛,下一行是密度和岛屿。1kg 到 5kg 是该重量的票价,Kg.ad. 是超出后的续重。
|
||||
* 西班牙 CTT:上面一行是大陆或群岛,下一行是密度和岛屿。
|
||||
* 1kg 到 5kg 是该重量的票价,记入重量段。Kg.ad. 是超出最高档后的每公斤加收,记入附加费。
|
||||
*/
|
||||
private int readKgStepTable(ExcelGrid grid, int header, PayProduct product)
|
||||
{
|
||||
@@ -1969,25 +2035,25 @@ public class PayWorkbookParser
|
||||
}
|
||||
String child = grid.text(header, col).replace('\n', ' ').trim();
|
||||
String parent = groupLabel(grid, groupRow, col, service);
|
||||
PayWeightBand band = new PayWeightBand();
|
||||
band.setSectionName(service);
|
||||
band.setZoneName(parent.isBlank() ? child : parent + " / " + child);
|
||||
band.setWeightLabel(label);
|
||||
band.setPrice(price);
|
||||
String zone = parent.isBlank() ? child : parent + " / " + child;
|
||||
if (extra)
|
||||
{
|
||||
band.setChargeName("每公斤");
|
||||
band.setPriceUnit("EUR/公斤");
|
||||
addExtraKg(product, service, zone, label, price, previousMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
PayWeightBand band = new PayWeightBand();
|
||||
band.setSectionName(service);
|
||||
band.setZoneName(zone);
|
||||
band.setWeightLabel(label);
|
||||
band.setPrice(price);
|
||||
band.setWeightMin(minKg);
|
||||
band.setWeightMax(maxKg);
|
||||
band.setChargeName("每件");
|
||||
band.setPriceUnit("EUR/件");
|
||||
band.setSortNo(product.getWeightBands().size() + 1);
|
||||
product.getWeightBands().add(band);
|
||||
}
|
||||
band.setSortNo(product.getWeightBands().size() + 1);
|
||||
product.getWeightBands().add(band);
|
||||
}
|
||||
if (!extra)
|
||||
{
|
||||
@@ -1998,6 +2064,26 @@ public class PayWorkbookParser
|
||||
return row;
|
||||
}
|
||||
|
||||
/** Kg.ad. 按地区各记一条每公斤续重,不进入重量段。 */
|
||||
private static void addExtraKg(PayProduct product, String section, String zone, String label, BigDecimal price, BigDecimal threshold)
|
||||
{
|
||||
BigDecimal from = threshold == null ? BigDecimal.ZERO : threshold;
|
||||
PaySurcharge fee = new PaySurcharge();
|
||||
fee.setSectionName(section);
|
||||
fee.setFeeName("每公斤续重");
|
||||
fee.setZoneName(zone);
|
||||
fee.setChargeText(label + " " + price.stripTrailingZeros().toPlainString() + " EUR/KG");
|
||||
fee.setAmount(price);
|
||||
fee.setCurrency("EUR");
|
||||
fee.setPriceUnit("EUR/KG");
|
||||
fee.setFeeDesc("超过" + from.stripTrailingZeros().toPlainString() + "kg后,按超出的重量每公斤加收");
|
||||
fee.setChargeTiming("产生时收取");
|
||||
fee.setCalcRule(PaySurchargeRules.EXTRA_KG);
|
||||
fee.setCalcParam(from.stripTrailingZeros().toPlainString());
|
||||
fee.setSortNo(product.getSurcharges().size() + 1);
|
||||
product.getSurcharges().add(fee);
|
||||
}
|
||||
|
||||
private static boolean isKgStepHeader(ExcelGrid grid, int row)
|
||||
{
|
||||
int names = 0;
|
||||
@@ -2260,6 +2346,16 @@ public class PayWorkbookParser
|
||||
itemFee = true;
|
||||
}
|
||||
}
|
||||
if (product.getSurcharges() != null)
|
||||
{
|
||||
for (PaySurcharge fee : product.getSurcharges())
|
||||
{
|
||||
if (fee != null && PaySurchargeRules.EXTRA_KG.equals(fee.getCalcRule()))
|
||||
{
|
||||
extraKg = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extraKg)
|
||||
{
|
||||
return "续重计费";
|
||||
@@ -2297,6 +2393,24 @@ public class PayWorkbookParser
|
||||
high = band.getPrice();
|
||||
}
|
||||
}
|
||||
if (product.getSurcharges() != null)
|
||||
{
|
||||
for (PaySurcharge fee : product.getSurcharges())
|
||||
{
|
||||
if (fee == null || fee.getAmount() == null || !PaySurchargeRules.EXTRA_KG.equals(fee.getCalcRule()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (low == null || fee.getAmount().compareTo(low) < 0)
|
||||
{
|
||||
low = fee.getAmount();
|
||||
}
|
||||
if (high == null || fee.getAmount().compareTo(high) > 0)
|
||||
{
|
||||
high = fee.getAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (low == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -3,7 +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.PayParseProgress;
|
||||
import com.bms.price.domain.PayPreview;
|
||||
import com.bms.price.domain.PayPriceCompare;
|
||||
import com.bms.price.domain.PayProduct;
|
||||
@@ -21,6 +21,14 @@ public interface IPayProductService
|
||||
*/
|
||||
PayPreview preview(byte[] bytes, String filename, String dictValue);
|
||||
|
||||
/**
|
||||
* 与 {@link #preview(byte[], String, String)} 相同。progressId 有值时写入解析进度,供页面轮询。
|
||||
*/
|
||||
PayPreview preview(byte[] bytes, String filename, String dictValue, String progressId);
|
||||
|
||||
/** 读取一次导入的解析进度。没有对应任务时返回等待中。 */
|
||||
PayParseProgress parseProgress(String progressId);
|
||||
|
||||
/** 把预览里勾选的产品写入数据库。同名字典产品再次导入时另存一版,旧价格留在有效期表里 */
|
||||
String save(PaySaveCommand command);
|
||||
|
||||
@@ -75,10 +83,4 @@ public interface IPayProductService
|
||||
void writeSource(Long productId, OutputStream output);
|
||||
|
||||
int deletePayProducts(Long[] productIds);
|
||||
|
||||
/** 按根产品编号带出附加费开关。没有配置过的产品返回空列表 */
|
||||
List<PayFeeRuleGroup> listFeeRules(List<Long> productIds);
|
||||
|
||||
/** 按产品整组替换附加费开关。每个产品一份,互不影响 */
|
||||
void saveFeeRules(List<PayFeeRuleGroup> products);
|
||||
}
|
||||
|
||||
+112
-191
@@ -3,13 +3,11 @@ 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;
|
||||
@@ -28,8 +26,7 @@ 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.PayParseProgress;
|
||||
import com.bms.price.domain.PayPreview;
|
||||
import com.bms.price.domain.PayPriceCompare;
|
||||
import com.bms.price.domain.PayPriceDiff;
|
||||
@@ -39,9 +36,9 @@ 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.PayParseProgressListener;
|
||||
import com.bms.price.pay.PayWorkbookParser;
|
||||
import com.bms.price.service.IPayProductService;
|
||||
import com.bms.system.service.ISysDictTypeService;
|
||||
@@ -69,11 +66,6 @@ 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;
|
||||
|
||||
@@ -85,23 +77,110 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
/** 预览时已经传到 OSS 的文件。保存不再只靠前端把地址传回来。 */
|
||||
private final Map<String, UploadedSource> uploadedSources = new ConcurrentHashMap<>();
|
||||
|
||||
/** 导入弹框轮询的解析进度。只留在本机内存,过期后丢掉。 */
|
||||
private final Map<String, ParseSlot> parseProgress = new ConcurrentHashMap<>();
|
||||
|
||||
private static final long PARSE_PROGRESS_KEEP_MS = 15L * 60 * 1000;
|
||||
|
||||
private static final long SOURCE_REMEMBER_MS = 6L * 60 * 60 * 1000;
|
||||
|
||||
@Override
|
||||
public PayPreview preview(byte[] bytes, String filename, String dictValue)
|
||||
{
|
||||
PayPreview preview = parser.parseMatched(bytes, filename, dictValue, loadDictValues());
|
||||
String stored = storeSource(bytes, filename);
|
||||
rememberSource(preview.getFileMd5(), stored);
|
||||
preview.setFilePath(stored);
|
||||
for (PayProduct product : preview.getProducts())
|
||||
return preview(bytes, filename, dictValue, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayPreview preview(byte[] bytes, String filename, String dictValue, String progressId)
|
||||
{
|
||||
String progressKey = progressKey(progressId);
|
||||
PayParseProgressListener listener = progressKey == null ? null : (percent, message) ->
|
||||
writeProgress(progressKey, percent, message, PayParseProgress.RUNNING);
|
||||
if (progressKey != null)
|
||||
{
|
||||
product.setFilePath(stored);
|
||||
Long existsId = isBlank(product.getDictValue()) ? null : payProductMapper.selectTemplateId(product.getDictValue());
|
||||
product.setDuplicated(existsId != null);
|
||||
product.setExistsId(existsId);
|
||||
writeProgress(progressKey, 2, "正在准备解析", PayParseProgress.RUNNING);
|
||||
}
|
||||
return preview;
|
||||
try
|
||||
{
|
||||
PayPreview preview = parser.parseMatched(bytes, filename, dictValue, loadDictValues(), listener);
|
||||
writeProgress(progressKey, 98, "正在核对已有产品", PayParseProgress.RUNNING);
|
||||
String stored = storeSource(bytes, filename);
|
||||
rememberSource(preview.getFileMd5(), stored);
|
||||
preview.setFilePath(stored);
|
||||
for (PayProduct product : preview.getProducts())
|
||||
{
|
||||
product.setFilePath(stored);
|
||||
Long existsId = isBlank(product.getDictValue()) ? null : payProductMapper.selectTemplateId(product.getDictValue());
|
||||
product.setDuplicated(existsId != null);
|
||||
product.setExistsId(existsId);
|
||||
}
|
||||
writeProgress(progressKey, 100, "解析完成", PayParseProgress.SUCCESS);
|
||||
return preview;
|
||||
}
|
||||
catch (RuntimeException ex)
|
||||
{
|
||||
String message = ex.getMessage() == null || ex.getMessage().isBlank() ? "价卡解析失败" : ex.getMessage();
|
||||
writeProgress(progressKey, 100, message, PayParseProgress.FAIL);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayParseProgress parseProgress(String progressId)
|
||||
{
|
||||
String key = progressKey(progressId);
|
||||
if (key == null)
|
||||
{
|
||||
return new PayParseProgress(0, "等待解析", PayParseProgress.PENDING);
|
||||
}
|
||||
ParseSlot slot = parseProgress.get(key);
|
||||
if (slot == null || System.currentTimeMillis() - slot.at > PARSE_PROGRESS_KEEP_MS)
|
||||
{
|
||||
return new PayParseProgress(0, "等待解析", PayParseProgress.PENDING);
|
||||
}
|
||||
return slot.progress;
|
||||
}
|
||||
|
||||
private void writeProgress(String key, int percent, String message, String status)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int safePercent = Math.max(0, Math.min(100, percent));
|
||||
ParseSlot current = parseProgress.get(key);
|
||||
if (current != null && safePercent < current.progress.getPercent()
|
||||
&& !PayParseProgress.FAIL.equals(status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current != null && safePercent == current.progress.getPercent()
|
||||
&& message.equals(current.progress.getMessage())
|
||||
&& status.equals(current.progress.getStatus()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
parseProgress.put(key, new ParseSlot(new PayParseProgress(safePercent, message, status), System.currentTimeMillis()));
|
||||
if (parseProgress.size() > 200)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
parseProgress.entrySet().removeIf(entry -> now - entry.getValue().at > PARSE_PROGRESS_KEEP_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/** 只接受页面生成的短编号,避免把任意字符串当成缓存键。 */
|
||||
private static String progressKey(String progressId)
|
||||
{
|
||||
if (progressId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String key = progressId.trim();
|
||||
if (key.length() < 8 || key.length() > 64 || !key.matches("[A-Za-z0-9-]+"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -680,7 +759,6 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
return 0;
|
||||
}
|
||||
payProductTermMapper.deleteByProductIds(ids);
|
||||
payProductFeeRuleMapper.deleteByProductIds(ids);
|
||||
payProductMapper.deleteWeightBands(ids);
|
||||
payProductMapper.deleteSurcharges(ids);
|
||||
payProductMapper.deleteExtraLines(ids);
|
||||
@@ -1247,176 +1325,6 @@ 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();
|
||||
@@ -1427,6 +1335,19 @@ public class PayProductServiceImpl implements IPayProductService
|
||||
return isBlank(value) ? fallback : value;
|
||||
}
|
||||
|
||||
private static final class ParseSlot
|
||||
{
|
||||
private final PayParseProgress progress;
|
||||
|
||||
private final long at;
|
||||
|
||||
private ParseSlot(PayParseProgress progress, long at)
|
||||
{
|
||||
this.progress = progress;
|
||||
this.at = at;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UploadedSource
|
||||
{
|
||||
private final String path;
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<?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.masterdata.mapper.LineProductMapper">
|
||||
|
||||
<resultMap id="LineProductResult" type="com.bms.masterdata.domain.LineProduct">
|
||||
<result property="id" column="id"/>
|
||||
<result property="lineCode" column="line_code"/>
|
||||
<result property="lineName" column="line_name"/>
|
||||
<result property="productType" column="product_type"/>
|
||||
<result property="lineType" column="line_type"/>
|
||||
<result property="blCargo" column="bl_cargo"/>
|
||||
<result property="cargoName" column="cargo_name"/>
|
||||
<result property="productStorage" column="product_storage"/>
|
||||
<result property="productFirst" column="product_first"/>
|
||||
<result property="productClearance" column="product_clearance"/>
|
||||
<result property="productStorageCode" column="product_storage_code"/>
|
||||
<result property="productFirstCode" column="product_first_code"/>
|
||||
<result property="productClearanceCode" column="product_clearance_code"/>
|
||||
<result property="blOpen" column="bl_open"/>
|
||||
<result property="blSection" column="bl_section"/>
|
||||
<result property="dispCode" column="disp_code"/>
|
||||
<result property="dispName" column="disp_name"/>
|
||||
<result property="feeClass" column="fee_class"/>
|
||||
<result property="shipperId" column="shipper_id"/>
|
||||
<result property="shipmentRule" column="shipment_rule"/>
|
||||
<result property="weight" column="weight"/>
|
||||
<result property="cargoType" column="cargo_type"/>
|
||||
<result property="productCategory" column="product_category"/>
|
||||
<result property="volumeBasis" column="volume_basis"/>
|
||||
<result property="weightType" column="weight_type"/>
|
||||
<result property="serviceType" column="service_type"/>
|
||||
<result property="shipmentType" column="shipment_type"/>
|
||||
<result property="taxAmount" column="tax_amount"/>
|
||||
<result property="taxCurrency" column="tax_currency"/>
|
||||
<result property="batteryType" column="battery_type"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createName" column="create_name"/>
|
||||
<result property="createManCode" column="create_man_code"/>
|
||||
<result property="createDate" column="create_date"/>
|
||||
<result property="modifier" column="modifier"/>
|
||||
<result property="modifierCode" column="modifier_code"/>
|
||||
<result property="modifyDate" column="modify_date"/>
|
||||
<result property="gmtCreate" column="gmt_create"/>
|
||||
<result property="gmtModified" column="gmt_modified"/>
|
||||
<result property="syncTime" column="sync_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectLineProductVo">
|
||||
select id, line_code, line_name, product_type, line_type, bl_cargo, cargo_name,
|
||||
product_storage, product_first, product_clearance,
|
||||
product_storage_code, product_first_code, product_clearance_code,
|
||||
bl_open, bl_section, disp_code, disp_name, fee_class, shipper_id, shipment_rule,
|
||||
weight, cargo_type, product_category, volume_basis, weight_type, service_type, shipment_type,
|
||||
tax_amount, tax_currency, battery_type, remark,
|
||||
create_name, create_man_code, create_date, modifier, modifier_code, modify_date,
|
||||
gmt_create, gmt_modified, sync_time
|
||||
from srv_line_product
|
||||
</sql>
|
||||
|
||||
<select id="selectLineProductList" parameterType="com.bms.masterdata.domain.LineProduct" resultMap="LineProductResult">
|
||||
<include refid="selectLineProductVo"/>
|
||||
<where>
|
||||
<if test="lineCode != null and lineCode != ''">
|
||||
AND line_code like concat('%', #{lineCode}, '%')
|
||||
</if>
|
||||
<if test="lineName != null and lineName != ''">
|
||||
AND line_name like concat('%', #{lineName}, '%')
|
||||
</if>
|
||||
<if test="productType != null and productType != ''">
|
||||
AND product_type like concat('%', #{productType}, '%')
|
||||
</if>
|
||||
<if test="lineType != null and lineType != ''">
|
||||
AND line_type like concat('%', #{lineType}, '%')
|
||||
</if>
|
||||
<if test="blOpen != null and blOpen != ''">
|
||||
AND bl_open = #{blOpen}
|
||||
</if>
|
||||
<if test="blCargo != null and blCargo != ''">
|
||||
AND bl_cargo = #{blCargo}
|
||||
</if>
|
||||
<if test="dispName != null and dispName != ''">
|
||||
AND disp_name like concat('%', #{dispName}, '%')
|
||||
</if>
|
||||
<if test="cargoType != null and cargoType != ''">
|
||||
AND cargo_type like concat('%', #{cargoType}, '%')
|
||||
</if>
|
||||
<if test="productCategory != null and productCategory != ''">
|
||||
AND product_category like concat('%', #{productCategory}, '%')
|
||||
</if>
|
||||
</where>
|
||||
order by line_code
|
||||
</select>
|
||||
|
||||
<select id="selectLineProductById" parameterType="String" resultMap="LineProductResult">
|
||||
<include refid="selectLineProductVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectLineProductByCode" parameterType="String" resultMap="LineProductResult">
|
||||
<include refid="selectLineProductVo"/>
|
||||
where line_code = #{lineCode}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="countProductChannelByProductId" parameterType="String" resultType="int">
|
||||
select count(1) from srv_product_channel where product_id = #{productId}
|
||||
</select>
|
||||
|
||||
<insert id="insertLineProduct" parameterType="com.bms.masterdata.domain.LineProduct">
|
||||
insert into srv_line_product (
|
||||
id, line_code, line_name, product_type, line_type, bl_cargo, cargo_name,
|
||||
product_storage, product_first, product_clearance,
|
||||
product_storage_code, product_first_code, product_clearance_code,
|
||||
bl_open, bl_section, disp_code, disp_name, fee_class, shipper_id, shipment_rule,
|
||||
weight, cargo_type, product_category, volume_basis, weight_type, service_type, shipment_type,
|
||||
tax_amount, tax_currency, battery_type, remark,
|
||||
create_name, create_date, modifier, modify_date, gmt_create, gmt_modified
|
||||
) values (
|
||||
#{id}, #{lineCode}, #{lineName}, #{productType}, #{lineType}, #{blCargo}, #{cargoName},
|
||||
#{productStorage}, #{productFirst}, #{productClearance},
|
||||
#{productStorageCode}, #{productFirstCode}, #{productClearanceCode},
|
||||
#{blOpen}, #{blSection}, #{dispCode}, #{dispName}, #{feeClass}, #{shipperId}, #{shipmentRule},
|
||||
#{weight}, #{cargoType}, #{productCategory}, #{volumeBasis}, #{weightType}, #{serviceType}, #{shipmentType},
|
||||
#{taxAmount}, #{taxCurrency}, #{batteryType}, #{remark},
|
||||
#{createName}, sysdate(), #{modifier}, sysdate(), sysdate(), sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateLineProduct" parameterType="com.bms.masterdata.domain.LineProduct">
|
||||
update srv_line_product
|
||||
set line_code = #{lineCode},
|
||||
line_name = #{lineName},
|
||||
product_type = #{productType},
|
||||
line_type = #{lineType},
|
||||
bl_cargo = #{blCargo},
|
||||
cargo_name = #{cargoName},
|
||||
product_storage = #{productStorage},
|
||||
product_first = #{productFirst},
|
||||
product_clearance = #{productClearance},
|
||||
product_storage_code = #{productStorageCode},
|
||||
product_first_code = #{productFirstCode},
|
||||
product_clearance_code = #{productClearanceCode},
|
||||
bl_open = #{blOpen},
|
||||
bl_section = #{blSection},
|
||||
disp_code = #{dispCode},
|
||||
disp_name = #{dispName},
|
||||
fee_class = #{feeClass},
|
||||
shipper_id = #{shipperId},
|
||||
shipment_rule = #{shipmentRule},
|
||||
weight = #{weight},
|
||||
cargo_type = #{cargoType},
|
||||
product_category = #{productCategory},
|
||||
volume_basis = #{volumeBasis},
|
||||
weight_type = #{weightType},
|
||||
service_type = #{serviceType},
|
||||
shipment_type = #{shipmentType},
|
||||
tax_amount = #{taxAmount},
|
||||
tax_currency = #{taxCurrency},
|
||||
battery_type = #{batteryType},
|
||||
remark = #{remark},
|
||||
modifier = #{modifier},
|
||||
modify_date = sysdate(),
|
||||
gmt_modified = sysdate()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateLineProductStatus" parameterType="com.bms.masterdata.domain.LineProduct">
|
||||
update srv_line_product
|
||||
set bl_open = #{blOpen},
|
||||
modifier = #{modifier},
|
||||
modify_date = sysdate(),
|
||||
gmt_modified = sysdate()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteLineProductByIds" parameterType="String">
|
||||
delete from srv_line_product where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?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>
|
||||
Reference in New Issue
Block a user