Enhance surcharge management by adding support for price type filtering in SurchargeController and related services. Introduce new methods for handling redline product codes in mappers and services. Update front-end API to accommodate price type parameters in surcharge queries. Modify database schema to include redline product and country tables for improved surcharge calculations.

This commit is contained in:
wuweihua
2026-10-09 17:30:29 +08:00
parent 380577f691
commit f1f6ddd368
34 changed files with 5196 additions and 30 deletions
@@ -0,0 +1,173 @@
package com.bms.web.controller.price;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.MessageUtils;
import com.bms.price.domain.BmsRedlineProduct;
import com.bms.price.domain.BmsRedlineProductCountry;
import com.bms.price.domain.BmsRedlineProductImportTask;
import com.bms.price.domain.PrcPriceExcelFile;
import com.bms.price.service.IBmsRedlineProductService;
import com.bms.price.service.IPrcPriceExcelFileService;
/**
* redline product list (async Excel import)
*
* @author bms
*/
@RestController
@RequestMapping("/price/redline")
public class RedlineProductController extends BaseController
{
@Autowired
private IBmsRedlineProductService productService;
@Autowired
private IPrcPriceExcelFileService priceExcelFileService;
@PreAuthorize("@ss.hasPermi('price:redline:list')")
@GetMapping("/list")
public TableDataInfo list(BmsRedlineProduct product)
{
startPage();
List<BmsRedlineProduct> list = productService.selectBmsRedlineProductList(product);
return getDataTable(list);
}
/** Imported quote Excel attachments for this product_code (via product.pef_id). */
@PreAuthorize("@ss.hasPermi('price:redline:query')")
@GetMapping("/{productCode}/excelFiles")
public AjaxResult excelFiles(@PathVariable String productCode)
{
List<PrcPriceExcelFile> list = priceExcelFileService.selectByRedlineProductCode(productCode);
return success(list);
}
/**
* 产品下拉选项(非分页)。
* 供计费列表、客户绑定等场景选择产品;不做权限控制(仍需登录)。
*/
@GetMapping("/optionselect")
public AjaxResult optionselect(BmsRedlineProduct product)
{
List<BmsRedlineProduct> list = productService.selectBmsRedlineProductList(product);
return success(list);
}
@PreAuthorize("@ss.hasPermi('price:redline:import')")
@GetMapping("/import/task/{taskId}")
public AjaxResult getImportTask(@PathVariable String taskId)
{
BmsRedlineProductImportTask task = productService.getImportTask(taskId);
if (task == null)
{
return error(MessageUtils.message("price.product.import.task.notExists"));
}
return success(task);
}
@PreAuthorize("@ss.hasPermi('price:redline:query')")
@GetMapping("/{productCode}/countries")
public AjaxResult countries(@PathVariable String productCode,
@RequestParam(required = false) Date effectiveDate,
@RequestParam(required = false) Long productId)
{
List<BmsRedlineProductCountry> list = productService.selectCountryListByProductCode(productCode, effectiveDate, productId);
return success(list);
}
@PreAuthorize("@ss.hasPermi('price:redline:query')")
@GetMapping("/{productId}")
public AjaxResult getInfo(@PathVariable Long productId)
{
return success(productService.selectBmsRedlineProductById(productId));
}
@PreAuthorize("@ss.hasPermi('price:redline:add')")
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody BmsRedlineProduct product)
{
product.setCreateBy(getUsername());
return toAjax(productService.insertBmsRedlineProduct(product));
}
@PreAuthorize("@ss.hasPermi('price:redline:edit')")
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody BmsRedlineProduct product)
{
product.setUpdateBy(getUsername());
return toAjax(productService.updateBmsRedlineProduct(product));
}
@PreAuthorize("@ss.hasPermi('price:redline:edit')")
@Log(title = "\u4ea7\u54c1\u56fd\u5bb6\u660e\u7ec6", businessType = BusinessType.UPDATE)
@PutMapping("/country")
public AjaxResult editCountry(@RequestBody BmsRedlineProductCountry country)
{
return toAjax(productService.updateBmsRedlineProductCountry(country));
}
@PreAuthorize("@ss.hasPermi('price:redline:remove')")
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.DELETE)
@DeleteMapping("/{productIds}")
public AjaxResult remove(@PathVariable Long[] productIds)
{
return toAjax(productService.deleteBmsRedlineProductByIds(productIds));
}
/**
* Async import Weishi B2C workbook. Returns taskId; poll /import/task/{taskId}.
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
*/
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('price:redline:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date effectiveDate) throws Exception
{
String taskId = productService.submitImportProductExcelAsync(file, updateSupport, getUsername(), effectiveDate);
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("taskId", taskId);
return success(payload);
}
/**
* Async single-country cost-sheet import. Returns taskId; poll /import/task/{taskId}.
* effectiveDate is required: written to product / product_country; previous open rows expire at this date.
*/
@Log(title = "\u4ea7\u54c1\u5217\u8868", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('price:redline:import')")
@PostMapping("/importCountryData")
public AjaxResult importCountryData(MultipartFile file, String countryCode,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date effectiveDate) throws Exception
{
String taskId = productService.submitImportCountryProductExcelAsync(file, countryCode, getUsername(),
effectiveDate);
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("taskId", taskId);
return success(payload);
}
}
@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
@@ -58,9 +59,10 @@ public class SurchargeController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('price:surcharge:query') or @ss.hasPermi('price:set:query') or @ss.hasPermi('price:set:add')")
@GetMapping("/byProduct/{productCode}")
public AjaxResult listByProduct(@PathVariable String productCode)
public AjaxResult listByProduct(@PathVariable String productCode,
@RequestParam(required = false) String priceType)
{
return success(surchargeRelService.selectRelByProductCode(productCode));
return success(surchargeRelService.selectRelByProductCode(productCode, priceType));
}
/**
@@ -0,0 +1,46 @@
package com.bms.price.domain;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.bms.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** Redline catalog main table redline_product */
@Data
@EqualsAndHashCode(callSuper = true)
public class BmsRedlineProduct extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long productId;
private String productCode;
private String productName;
private String serviceLevel;
private String cargoAttr;
private String destRegion;
private String updateNote;
/** TAX_INCLUDED / TAX_EXCLUDED */
private String taxMode;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date effectiveDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireDate;
/** 0 pending, 1 active, 2 expired */
public static final String STATUS_PENDING = "0";
public static final String STATUS_ACTIVE = "1";
public static final String STATUS_EXPIRED = "2";
private String status;
private String sourceSheet;
/** FK prc_price_excel_file.pef_id */
private Long pefId;
private Long tmsId;
private Integer countryCount;
private Integer surchargeCount;
private List<BmsRedlineProductCountry> countries;
/** query alias of status (0/1/2) */
private String effectStatus;
/** query: active = status in (0,1); expired = status 2 */
private String statusGroup;
}
@@ -0,0 +1,49 @@
package com.bms.price.domain;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/** Redline country detail table redline_product_country */
@Data
public class BmsRedlineProductCountry implements Serializable
{
private static final long serialVersionUID = 1L;
private Long detailId;
/** FK to product.product_id */
private Long productId;
private String productCode;
private String countryName;
private String countryCode;
/** prc_zone.zone_id; null when product country has no zone */
private Long zoneId;
/** e.g. Z1; null when no zone */
private String zoneCode;
/** e.g. Australia(Z1); null when no zone */
private String zoneName;
private BigDecimal weightMin;
private BigDecimal weightMax;
private BigDecimal freightPrice;
private BigDecimal registrationFee;
private String sizeLimit;
private String lastMile;
private String leadTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date effectiveDate;
/** null=open; closed to next import effective_date */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireDate;
/** 0 pending, 1 active, 2 expired */
private String status;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** query only: true = expire_date is null */
private Boolean openOnly;
/** query alias of status (0/1/2) */
private String effectStatus;
}
@@ -0,0 +1,72 @@
package com.bms.price.domain;
import java.io.Serializable;
/**
* Async product Excel import progress (Redis).
*/
public class BmsRedlineProductImportTask implements Serializable
{
private static final long serialVersionUID = 1L;
public static final String STATUS_PENDING = "PENDING";
public static final String STATUS_RUNNING = "RUNNING";
public static final String STATUS_SUCCESS = "SUCCESS";
public static final String STATUS_FAIL = "FAIL";
private String taskId;
private String status;
private int percent;
private String message;
private String error;
public String getTaskId()
{
return taskId;
}
public void setTaskId(String taskId)
{
this.taskId = taskId;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
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 getError()
{
return error;
}
public void setError(String error)
{
this.error = error;
}
}
@@ -58,8 +58,10 @@ public class TmsPriceQuoteResult implements Serializable
private BigDecimal surchargeAmount;
/** product catalog total: base freight*weight + base registration (no package surcharge) */
private BigDecimal productAmount;
/** redline total reserved */
/** redline total from redline_product_country (+ REDLINE surcharges) */
private BigDecimal redlineAmount;
/** matched redline_product_country.detail_id for REDLINE surcharge scope */
private Long redlineCountryId;
/** cost total (currently = product catalog amount) */
private BigDecimal costAmount;
/** receivable total (order bill) */
@@ -0,0 +1,99 @@
package com.bms.price.mapper;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.bms.price.domain.BmsRedlineProductCountry;
/**
* 红线价国家明细 数据层
*
* @author bms
*/
public interface BmsRedlineProductCountryMapper
{
public List<BmsRedlineProductCountry> selectBmsRedlineProductCountryList(BmsRedlineProductCountry country);
public int insertBmsRedlineProductCountry(BmsRedlineProductCountry country);
/** 按明细行主键更新可编辑字段(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期) */
public int updateBmsRedlineProductCountry(BmsRedlineProductCountry country);
/** Import overlay: update price/zone/attrs on matched country row */
public int updateBmsRedlineProductCountryImport(BmsRedlineProductCountry country);
public int deleteByProductId(Long productId);
public int deleteByProductCode(String productCode);
public int deleteByProductCodeAndEffectiveDate(
@Param("productCode") String productCode,
@Param("effectiveDate") Date effectiveDate);
/** Max open (expire_date is null) effective_date for product */
public Date selectMaxOpenEffectiveDate(@Param("productCode") String productCode);
/** Close open versions: set expire_date = newEffectiveDate where effective_date &lt; newEffectiveDate */
public int expireOpenByProductCode(
@Param("productCode") String productCode,
@Param("newEffectiveDate") Date newEffectiveDate);
/**
* Close open rows for one product + country where effective_date &lt; newEffectiveDate.
* Match by country_code when present, otherwise country_name.
*/
public int expireOpenByProductCodeAndCountry(
@Param("productCode") String productCode,
@Param("countryCode") String countryCode,
@Param("countryName") String countryName,
@Param("newEffectiveDate") Date newEffectiveDate);
/** Recompute status from effective_date / expire_date vs today */
public int refreshStatusByDates();
/** Replace same-day version only */
public int deleteOpenByProductCodeAndEffectiveDate(
@Param("productCode") String productCode,
@Param("effectiveDate") Date effectiveDate);
/**
* Delete open rows for one product + country on a given effective_date (same-day replace).
* Needed because unique key keeps expired same-day weight bands.
*/
public int deleteOpenByProductCodeAndCountryOnDate(
@Param("productCode") String productCode,
@Param("countryCode") String countryCode,
@Param("countryName") String countryName,
@Param("effectiveDate") Date effectiveDate);
/** Open country rows (expire_date is null) for product */
public List<BmsRedlineProductCountry> selectOpenByProductCode(@Param("productCode") String productCode);
/** Copy previously open rows into a new effective_date (caller must expire first) */
public int copyOpenCountries(
@Param("productCode") String productCode,
@Param("productId") Long productId,
@Param("effectiveDate") Date effectiveDate,
@Param("status") String status);
/**
* Match by product + country + weight + bizDate (+ optional zoneCode).
* When zoneCode is empty, prefer rows without zone.
* When zoneCode is set, require matching zone_code.
*/
public BmsRedlineProductCountry selectMatchByProductCountryWeight(
@Param("productCode") String productCode,
@Param("countryCode") String countryCode,
@Param("countryName") String countryName,
@Param("zoneCode") String zoneCode,
@Param("weight") BigDecimal weight,
@Param("bizDate") Date bizDate);
/** Count product-country rows that have a zone for this product+country+bizDate */
public int countZonedByProductCountry(
@Param("productCode") String productCode,
@Param("countryCode") String countryCode,
@Param("countryName") String countryName,
@Param("bizDate") Date bizDate);
}
@@ -0,0 +1,44 @@
package com.bms.price.mapper;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.bms.price.domain.BmsRedlineProduct;
/**
* 产品主表 数据层
*
* @author bms
*/
public interface BmsRedlineProductMapper
{
public BmsRedlineProduct selectBmsRedlineProductById(Long productId);
/** Latest row by product_code (effective_date desc) */
public BmsRedlineProduct selectBmsRedlineProductByCode(String productCode);
/** Unique business key: product_code + effective_date */
public BmsRedlineProduct selectBmsRedlineProductByCodeAndDate(@Param("productCode") String productCode,
@Param("effectiveDate") Date effectiveDate);
/** Product version effective on bizDate */
public BmsRedlineProduct selectEffectiveBmsRedlineProductByCode(@Param("productCode") String productCode,
@Param("bizDate") Date bizDate);
public List<BmsRedlineProduct> selectBmsRedlineProductList(BmsRedlineProduct product);
public int insertBmsRedlineProduct(BmsRedlineProduct product);
public int updateBmsRedlineProduct(BmsRedlineProduct product);
public int deleteBmsRedlineProductByIds(Long[] productIds);
/** Close previous open versions: expire_date = newEffectiveDate */
public int expireOpenByProductCode(
@Param("productCode") String productCode,
@Param("newEffectiveDate") Date newEffectiveDate,
@Param("excludeId") Long excludeId);
/** Recompute status from effective_date / expire_date vs today */
public int refreshStatusByDates();
}
@@ -16,6 +16,8 @@ public interface PrcPriceExcelFileMapper
/** Distinct PRODUCT excel files linked by product.pef_id for this product_code */
public List<PrcPriceExcelFile> selectByProductCode(@Param("productCode") String productCode);
public List<PrcPriceExcelFile> selectByRedlineProductCode(@Param("productCode") String productCode);
public int insertPrcPriceExcelFile(PrcPriceExcelFile row);
public int updatePrcPriceExcelFile(PrcPriceExcelFile row);
@@ -0,0 +1,57 @@
package com.bms.price.service;
import java.util.Date;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
import com.bms.price.domain.BmsRedlineProduct;
import com.bms.price.domain.BmsRedlineProductCountry;
import com.bms.price.domain.BmsRedlineProductImportTask;
/**
* redline product list service
*
* @author bms
*/
public interface IBmsRedlineProductService
{
public BmsRedlineProduct selectBmsRedlineProductById(Long productId);
public List<BmsRedlineProduct> selectBmsRedlineProductList(BmsRedlineProduct product);
public int insertBmsRedlineProduct(BmsRedlineProduct product);
public int updateBmsRedlineProduct(BmsRedlineProduct product);
public int updateBmsRedlineProductCountry(BmsRedlineProductCountry country);
public int deleteBmsRedlineProductByIds(Long[] productIds);
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode);
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode, Date effectiveDate);
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode, Date effectiveDate, Long productId);
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate) throws Exception;
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName, Date effectiveDate) throws Exception;
/** Submit async all-country import; returns taskId for polling getImportTask. */
public String submitImportProductExcelAsync(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate) throws Exception;
/** Submit async single-country import; returns taskId for polling getImportTask. */
public String submitImportCountryProductExcelAsync(MultipartFile file, String countryCode, String operName, Date effectiveDate) throws Exception;
/** Poll Redis progress for product import task. */
public BmsRedlineProductImportTask getImportTask(String taskId);
/** Sync import from bytes (used by async runner; taskId may be null). */
public String importProductExcel(byte[] bytes, String filename, boolean updateSupport, String operName, String taskId, Date effectiveDate) throws Exception;
/** Sync country import from bytes (used by async runner; taskId may be null). */
public String importCountryProductExcel(byte[] bytes, String filename, String countryCode, String operName, String taskId, Date effectiveDate) throws Exception;
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(MultipartFile file) throws Exception;
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(byte[] bytes, String filename) throws Exception;
}
@@ -14,6 +14,8 @@ public interface IPrcPriceExcelFileService
/** Import Excel attachments linked to this product_code via product.pef_id */
public List<PrcPriceExcelFile> selectByProductCode(String productCode);
List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode);
/**
* Persist uploaded Excel under upload/price/excel and insert metadata row.
*
@@ -13,6 +13,8 @@ public interface IPrcSurchargeRelService
/** 查询某产品已关联的附加费(含附加费与明细展示字段) */
public List<PrcSurchargeRel> selectRelByProductCode(String productCode);
public List<PrcSurchargeRel> selectRelByProductCode(String productCode, String priceType);
public List<PrcSurchargeRel> selectPrcSurchargeRelList(PrcSurchargeRel rel);
public int insertPrcSurchargeRel(PrcSurchargeRel rel);
@@ -48,6 +48,17 @@ public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
return list == null ? Collections.emptyList() : list;
}
@Override
public List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode)
{
if (productCode == null || productCode.trim().isEmpty())
{
return java.util.Collections.emptyList();
}
List<PrcPriceExcelFile> list = excelFileMapper.selectByRedlineProductCode(productCode.trim());
return list == null ? java.util.Collections.emptyList() : list;
}
@Override
public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes,
String filename, String operName) throws Exception
@@ -23,30 +23,20 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService
@Override
public List<PrcSurchargeRel> selectRelByProductCode(String productCode)
{
return selectRelByProductCode(productCode, PrcSurchargeRel.PRICE_TYPE_PRODUCT);
}
@Override
public List<PrcSurchargeRel> selectRelByProductCode(String productCode, String priceType)
{
PrcSurchargeRel query = new PrcSurchargeRel();
query.setProductCode(productCode);
List<PrcSurchargeRel> list = relMapper.selectPrcSurchargeRelList(query);
if (list == null || list.isEmpty())
if (StringUtils.isNotEmpty(priceType))
{
return list;
query.setPriceType(priceType.trim().toUpperCase());
}
// Product drawer: PRODUCT + REDLINE only (SALES binds psp_id/psd_id)
List<PrcSurchargeRel> filtered = new java.util.ArrayList<PrcSurchargeRel>();
for (PrcSurchargeRel rel : list)
{
if (rel == null)
{
continue;
}
String type = rel.getPriceType();
if (StringUtils.isEmpty(type) || PrcSurchargeRel.PRICE_TYPE_PRODUCT.equals(type)
|| PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(type))
{
filtered.add(rel);
}
}
return filtered;
return relMapper.selectPrcSurchargeRelList(query);
}
@Override
@@ -25,6 +25,7 @@ import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
import com.bms.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.domain.BmsRedlineProductCountry;
import com.bms.price.domain.PrcCustomerPriceSet;
import com.bms.price.domain.PrcPriceSet;
import com.bms.price.domain.PrcPriceSetDetail;
@@ -42,6 +43,7 @@ import com.bms.price.domain.vo.TmsPriceQuoteResult;
import com.bms.price.enums.BillCustomerActiveStatus;
import com.bms.price.mapper.BmsProductCountryMapper;
import com.bms.price.mapper.BmsProductMapper;
import com.bms.price.mapper.BmsRedlineProductCountryMapper;
import com.bms.price.mapper.PrcCustomerPriceSetMapper;
import com.bms.price.mapper.PrcPriceSetDetailMapper;
import com.bms.price.mapper.PrcPriceSetMapper;
@@ -94,6 +96,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
@Autowired
private BmsProductCountryMapper productCountryMapper;
@Autowired
private BmsRedlineProductCountryMapper redlineProductCountryMapper;
@Autowired
private BmsProductMapper productMapper;
@@ -874,6 +879,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setRoundMode(mode);
r.setFromCache(false);
fillProductAmountAndPay(r, catalog, weight, rm, mode);
fillRedlineAmount(r, weight, rm);
return r;
}
@@ -929,6 +935,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setRoundMode(mode);
r.setFromCache(false);
fillProductAmountAndPay(r, base, weight, rm, mode);
fillRedlineAmount(r, weight, rm);
return r;
}
@@ -961,6 +968,30 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setPayFeeDetails(payLines);
}
/**
* Redline catalog amount from redline_product_country (same match keys as product catalog).
*/
private void fillRedlineAmount(TmsPriceQuoteResult r, BigDecimal weight, RoundingMode rm)
{
if (r == null || weight == null || StringUtils.isEmpty(r.getProductCode()))
{
return;
}
BmsRedlineProductCountry base = redlineProductCountryMapper.selectMatchByProductCountryWeight(
r.getProductCode(), r.getCountryCode(), r.getCountryName(), r.getZoneCode(), weight, null);
if (base == null || base.getFreightPrice() == null)
{
return;
}
if (base.getDetailId() != null)
{
r.setRedlineCountryId(base.getDetailId());
}
BigDecimal baseReg = base.getRegistrationFee() == null ? BigDecimal.ZERO : base.getRegistrationFee();
BigDecimal freight = base.getFreightPrice().multiply(weight).setScale(2, rm);
r.setRedlineAmount(freight.add(baseReg).setScale(2, rm));
}
private void applySurchargesAndFees(TmsPriceQuoteResult result)
{
BigDecimal freightAmount = nvl(result.getFreightAmount());
@@ -1140,6 +1171,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
{
return true;
}
if (PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(rel.getPriceType()))
{
return result.getRedlineCountryId() != null && rel.getDetailId().equals(result.getRedlineCountryId());
}
return result.getProductCountryId() != null && rel.getDetailId().equals(result.getProductCountryId());
}
@@ -0,0 +1,97 @@
package com.bms.price.task;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import com.bms.common.core.redis.RedisCache;
import com.bms.common.utils.StringUtils;
import com.bms.price.domain.BmsRedlineProductImportTask;
import com.bms.price.service.IBmsRedlineProductService;
/**
* Async runner for product Excel import (avoids HTTP timeout on large workbooks).
*/
@Component
public class BmsRedlineProductImportAsyncRunner
{
private static final Logger log = LoggerFactory.getLogger(BmsRedlineProductImportAsyncRunner.class);
public static final String CACHE_KEY_PREFIX = "price:redline:import:task:";
public static final int TTL_MINUTES = 60;
@Autowired
private IBmsRedlineProductService productService;
@Autowired
private RedisCache redisCache;
/** Background all-country (Weishi catalog) import. */
@Async("threadPoolTaskExecutor")
public void runImportAll(String taskId, byte[] bytes, String filename, boolean updateSupport, String operName,
Date effectiveDate)
{
try
{
put(taskId, BmsRedlineProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
String message = productService.importProductExcel(bytes, filename, updateSupport, operName, taskId,
effectiveDate);
put(taskId, BmsRedlineProductImportTask.STATUS_SUCCESS, 100, message, null);
log.info("async product import done, taskId={}", taskId);
}
catch (Exception e)
{
log.error("async product import failed, taskId={}", taskId, e);
put(taskId, BmsRedlineProductImportTask.STATUS_FAIL, 100, "failed", errorMessage(e));
}
}
/** Background single-country cost-sheet import. */
@Async("threadPoolTaskExecutor")
public void runImportCountry(String taskId, byte[] bytes, String filename, String countryCode, String operName,
Date effectiveDate)
{
try
{
put(taskId, BmsRedlineProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
String message = productService.importCountryProductExcel(bytes, filename, countryCode, operName, taskId,
effectiveDate);
put(taskId, BmsRedlineProductImportTask.STATUS_SUCCESS, 100, message, null);
log.info("async product country import done, taskId={}, country={}", taskId, countryCode);
}
catch (Exception e)
{
log.error("async product country import failed, taskId={}, country={}", taskId, countryCode, e);
put(taskId, BmsRedlineProductImportTask.STATUS_FAIL, 100, "failed", errorMessage(e));
}
}
public void put(String taskId, String status, int percent, String message, String error)
{
BmsRedlineProductImportTask task = new BmsRedlineProductImportTask();
task.setTaskId(taskId);
task.setStatus(status);
task.setPercent(percent);
task.setMessage(message);
task.setError(error);
redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES);
}
public BmsRedlineProductImportTask get(String taskId)
{
return redisCache.getCacheObject(CACHE_KEY_PREFIX + taskId);
}
private static String errorMessage(Exception e)
{
String msg = e.getMessage();
if (StringUtils.isEmpty(msg))
{
msg = e.getClass().getSimpleName();
}
return msg;
}
}
@@ -40,7 +40,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
or c.effective_date = p.effective_date
))
) as country_count,
(select count(*) from prc_surcharge_rel r where r.product_id = p.product_id) as surcharge_count
(select count(*) from prc_surcharge_rel r where r.product_id = p.product_id and r.price_type = 'PRODUCT') as surcharge_count
from product p
</sql>
@@ -0,0 +1,312 @@
<?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.BmsRedlineProductCountryMapper">
<resultMap type="BmsRedlineProductCountry" id="BmsRedlineProductCountryResult">
<id property="detailId" column="detail_id" />
<result property="productId" column="product_id" />
<result property="productCode" column="product_code" />
<result property="countryName" column="country_name" />
<result property="countryCode" column="country_code" />
<result property="zoneId" column="zone_id" />
<result property="zoneCode" column="zone_code" />
<result property="zoneName" column="zone_name" />
<result property="weightMin" column="weight_min" />
<result property="weightMax" column="weight_max" />
<result property="freightPrice" column="freight_price" />
<result property="registrationFee" column="registration_fee" />
<result property="sizeLimit" column="size_limit" />
<result property="lastMile" column="last_mile" />
<result property="leadTime" column="lead_time" />
<result property="effectiveDate" column="effective_date" />
<result property="expireDate" column="expire_date" />
<result property="status" column="status" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="effectStatus" column="status" />
</resultMap>
<sql id="selectVo">
select c.detail_id, c.product_id, c.product_code, c.country_name, c.country_code,
c.zone_id, c.zone_code, c.zone_name,
c.weight_min, c.weight_max,
c.freight_price, c.registration_fee, c.size_limit, c.last_mile, c.lead_time,
c.effective_date, c.expire_date, c.status, c.create_time, c.update_time
from redline_product_country c
</sql>
<select id="selectBmsRedlineProductCountryList" parameterType="BmsRedlineProductCountry" resultMap="BmsRedlineProductCountryResult">
<include refid="selectVo"/>
<where>
<if test="productId != null">
AND c.product_id = #{productId}
</if>
<if test="productCode != null and productCode != ''">
AND c.product_code = #{productCode}
</if>
<if test="countryName != null and countryName != ''">
AND c.country_name like concat('%', #{countryName}, '%')
</if>
<if test="countryCode != null and countryCode != ''">
AND c.country_code = #{countryCode}
</if>
<if test="zoneId != null">
AND c.zone_id = #{zoneId}
</if>
<if test="zoneCode != null and zoneCode != ''">
AND c.zone_code = #{zoneCode}
</if>
<if test="weightMin != null">
AND c.weight_min = #{weightMin}
</if>
<if test="weightMax != null">
AND c.weight_max = #{weightMax}
</if>
<if test="effectiveDate != null">
AND c.effective_date = #{effectiveDate}
</if>
<if test="openOnly != null and openOnly">
AND c.expire_date is null
</if>
<if test="status != null and status != ''">
AND c.status = #{status}
</if>
<if test="effectStatus != null and effectStatus != ''">
AND c.status = #{effectStatus}
</if>
</where>
order by c.effective_date desc, c.country_name asc, c.zone_code asc, c.weight_min asc, c.detail_id asc
</select>
<insert id="insertBmsRedlineProductCountry" parameterType="BmsRedlineProductCountry" useGeneratedKeys="true" keyProperty="detailId">
insert into redline_product_country (
product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name,
weight_min, weight_max,
freight_price, registration_fee, size_limit, last_mile, lead_time,
effective_date, expire_date, status, create_time
) values (
#{productId}, #{productCode}, #{countryName}, #{countryCode}, #{zoneId}, #{zoneCode}, #{zoneName},
#{weightMin}, #{weightMax},
#{freightPrice}, #{registrationFee}, #{sizeLimit}, #{lastMile}, #{leadTime},
#{effectiveDate}, #{expireDate}, IFNULL(#{status}, '0'), sysdate()
)
</insert>
<!-- Detail edit dialog: frontend submits full editable fields;
weight_max / expire_date may be null (= open-ended) -->
<update id="updateBmsRedlineProductCountry" parameterType="BmsRedlineProductCountry">
update redline_product_country
set weight_min = #{weightMin},
weight_max = #{weightMax},
freight_price = #{freightPrice},
registration_fee = #{registrationFee},
size_limit = #{sizeLimit},
last_mile = #{lastMile},
lead_time = #{leadTime},
effective_date = #{effectiveDate},
expire_date = #{expireDate},
update_time = sysdate()
where detail_id = #{detailId}
</update>
<update id="updateBmsRedlineProductCountryImport" parameterType="BmsRedlineProductCountry">
update redline_product_country
set product_id = #{productId},
country_code = #{countryCode},
zone_id = #{zoneId},
zone_code = #{zoneCode},
zone_name = #{zoneName},
weight_min = #{weightMin},
weight_max = #{weightMax},
freight_price = #{freightPrice},
registration_fee = #{registrationFee},
size_limit = #{sizeLimit},
last_mile = #{lastMile},
lead_time = #{leadTime},
effective_date = #{effectiveDate},
expire_date = #{expireDate},
status = IFNULL(#{status}, status),
update_time = sysdate()
where detail_id = #{detailId}
</update>
<delete id="deleteByProductId" parameterType="Long">
delete from redline_product_country where product_id = #{productId}
</delete>
<delete id="deleteByProductCode" parameterType="String">
delete from redline_product_country where product_code = #{productCode}
</delete>
<delete id="deleteByProductCodes">
delete from redline_product_country where product_code in
<foreach item="code" collection="array" open="(" separator="," close=")">
#{code}
</foreach>
</delete>
<delete id="deleteByProductCodeAndEffectiveDate">
delete from redline_product_country
where product_code = #{productCode}
and (
(#{effectiveDate} is null and effective_date is null)
or effective_date = #{effectiveDate}
)
</delete>
<select id="selectMaxOpenEffectiveDate" resultType="java.util.Date">
select max(effective_date) from redline_product_country
where product_code = #{productCode}
and expire_date is null
</select>
<update id="expireOpenByProductCode">
update redline_product_country
set expire_date = #{newEffectiveDate},
update_time = sysdate()
where product_code = #{productCode}
and expire_date is null
and (effective_date is null or effective_date &lt; #{newEffectiveDate})
</update>
<update id="expireOpenByProductCodeAndCountry">
update redline_product_country
set expire_date = #{newEffectiveDate},
update_time = sysdate()
where product_code = #{productCode}
and expire_date is null
and (effective_date is null or effective_date &lt; #{newEffectiveDate})
<choose>
<when test="countryCode != null and countryCode != ''">
and upper(ifnull(country_code,'')) = upper(#{countryCode})
</when>
<otherwise>
and country_name = #{countryName}
</otherwise>
</choose>
</update>
<update id="refreshStatusByDates">
update redline_product_country
set status = case
when expire_date is not null and expire_date &lt;= now() then '2'
when effective_date is null or effective_date &gt; now() then '0'
else '1'
end
</update>
<delete id="deleteOpenByProductCodeAndEffectiveDate">
delete from redline_product_country
where product_code = #{productCode}
and expire_date is null
and (
(#{effectiveDate} is null and effective_date is null)
or effective_date = #{effectiveDate}
)
</delete>
<delete id="deleteOpenByProductCodeAndCountryOnDate">
delete from redline_product_country
where product_code = #{productCode}
and expire_date is null
and (
(#{effectiveDate} is null and effective_date is null)
or effective_date = #{effectiveDate}
)
<choose>
<when test="countryCode != null and countryCode != ''">
and upper(ifnull(country_code,'')) = upper(#{countryCode})
</when>
<otherwise>
and country_name = #{countryName}
</otherwise>
</choose>
</delete>
<select id="selectMatchByProductCountryWeight" resultMap="BmsRedlineProductCountryResult">
<include refid="selectVo"/>
where c.product_code = #{productCode}
and c.weight_min &lt;= #{weight}
and c.weight_max &gt;= #{weight}
<choose>
<when test="countryCode != null and countryCode != ''">
and upper(c.country_code) = upper(#{countryCode})
</when>
<when test="countryName != null and countryName != ''">
and c.country_name = #{countryName}
</when>
<otherwise>
and 1 = 0
</otherwise>
</choose>
<choose>
<when test="zoneCode != null and zoneCode != ''">
and upper(ifnull(c.zone_code,'')) = upper(#{zoneCode})
</when>
<otherwise>
and (c.zone_code is null or c.zone_code = '')
</otherwise>
</choose>
<if test="bizDate != null">
and (c.effective_date is null or c.effective_date &lt;= #{bizDate})
and (c.expire_date is null or c.expire_date &gt; #{bizDate})
</if>
<if test="bizDate == null">
and c.expire_date is null
</if>
order by c.effective_date desc, c.weight_min asc, c.detail_id asc
limit 1
</select>
<select id="countZonedByProductCountry" resultType="int">
select count(1)
from redline_product_country
where product_code = #{productCode}
and zone_code is not null and zone_code &lt;&gt; ''
<choose>
<when test="countryCode != null and countryCode != ''">
and upper(country_code) = upper(#{countryCode})
</when>
<when test="countryName != null and countryName != ''">
and country_name = #{countryName}
</when>
<otherwise>
and 1 = 0
</otherwise>
</choose>
<if test="bizDate != null">
and (effective_date is null or effective_date &lt;= #{bizDate})
and (expire_date is null or expire_date &gt; #{bizDate})
</if>
<if test="bizDate == null">
and expire_date is null
</if>
</select>
<select id="selectOpenByProductCode" resultMap="BmsRedlineProductCountryResult">
<include refid="selectVo"/>
where c.product_code = #{productCode}
and c.expire_date is null
order by c.country_name asc, c.zone_code asc, c.weight_min asc, c.detail_id asc
</select>
<insert id="copyOpenCountries">
insert into redline_product_country (
product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name,
weight_min, weight_max, freight_price, registration_fee, size_limit, last_mile, lead_time,
effective_date, expire_date, status, create_time
)
select
#{productId}, product_code, country_name, country_code, zone_id, zone_code, zone_name,
weight_min, weight_max, freight_price, registration_fee, size_limit, last_mile, lead_time,
#{effectiveDate}, null, IFNULL(#{status}, '0'), sysdate()
from redline_product_country
where product_code = #{productCode}
and expire_date is null
and (effective_date is null or effective_date &lt; #{effectiveDate})
order by detail_id asc
</insert>
</mapper>
@@ -0,0 +1,219 @@
<?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.BmsRedlineProductMapper">
<resultMap type="BmsRedlineProduct" id="BmsRedlineProductResult">
<id property="productId" column="product_id" />
<result property="productCode" column="product_code" />
<result property="productName" column="product_name" />
<result property="serviceLevel" column="service_level" />
<result property="cargoAttr" column="cargo_attr" />
<result property="destRegion" column="dest_region" />
<result property="updateNote" column="update_note" />
<result property="taxMode" column="tax_mode" />
<result property="effectiveDate" column="effective_date" />
<result property="expireDate" column="expire_date" />
<result property="status" column="status" />
<result property="sourceSheet" column="source_sheet" />
<result property="pefId" column="pef_id" />
<result property="remark" column="remark" />
<result property="tmsId" column="tms_id" />
<result property="createBy" column="created_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="updated_by" />
<result property="updateTime" column="update_time" />
<result property="countryCount" column="country_count" />
<result property="surchargeCount" column="surcharge_count" />
</resultMap>
<sql id="selectBmsRedlineProductVo">
select p.product_id, p.product_code, p.product_name, p.service_level, p.cargo_attr,
p.dest_region, p.update_note, p.tax_mode, p.effective_date, p.expire_date, p.status,
p.source_sheet, p.pef_id, p.remark, p.tms_id, p.created_by, p.create_time, p.updated_by, p.update_time,
(select count(distinct c.country_name) from redline_product_country c
where c.product_id = p.product_id
or (c.product_id is null and c.product_code = p.product_code
and (
(p.effective_date is null and c.effective_date is null)
or c.effective_date = p.effective_date
))
) as country_count,
(select count(*) from prc_surcharge_rel r where r.product_id = p.product_id and r.price_type = 'REDLINE') as surcharge_count
from redline_product p
</sql>
<select id="selectBmsRedlineProductById" parameterType="Long" resultMap="BmsRedlineProductResult">
<include refid="selectBmsRedlineProductVo"/>
where p.product_id = #{productId}
</select>
<select id="selectBmsRedlineProductByCode" parameterType="String" resultMap="BmsRedlineProductResult">
<include refid="selectBmsRedlineProductVo"/>
where p.product_code = #{productCode}
order by p.effective_date desc, p.product_id desc
limit 1
</select>
<select id="selectBmsRedlineProductByCodeAndDate" resultMap="BmsRedlineProductResult">
<include refid="selectBmsRedlineProductVo"/>
where p.product_code = #{productCode}
<choose>
<when test="effectiveDate != null">
AND p.effective_date = #{effectiveDate}
</when>
<otherwise>
AND p.effective_date is null
</otherwise>
</choose>
limit 1
</select>
<select id="selectEffectiveBmsRedlineProductByCode" resultMap="BmsRedlineProductResult">
<include refid="selectBmsRedlineProductVo"/>
where p.product_code = #{productCode}
and (p.effective_date is null or #{bizDate} is null or p.effective_date &lt;= #{bizDate})
and (p.expire_date is null or #{bizDate} is null or p.expire_date &gt; #{bizDate})
order by p.effective_date desc, p.product_id desc
limit 1
</select>
<select id="selectBmsRedlineProductList" parameterType="BmsRedlineProduct" resultMap="BmsRedlineProductResult">
<include refid="selectBmsRedlineProductVo"/>
<where>
<if test="productCode != null and productCode != ''">
AND p.product_code like concat('%', #{productCode}, '%')
</if>
<if test="productName != null and productName != ''">
AND p.product_name like concat('%', #{productName}, '%')
</if>
<if test="taxMode != null and taxMode != ''">
AND p.tax_mode = #{taxMode}
</if>
<if test="status != null and status != ''">
AND p.status = #{status}
</if>
<if test="effectStatus != null and effectStatus != ''">
AND p.status = #{effectStatus}
</if>
<if test="statusGroup != null and statusGroup == 'active'">
AND p.status in ('0', '1')
</if>
<if test="statusGroup != null and statusGroup == 'expired'">
AND p.status = '2'
<if test="params != null and params.beginTime != null and params.beginTime != ''">
AND p.expire_date &gt;= #{params.beginTime}
</if>
<if test="params != null and params.endTime != null and params.endTime != ''">
AND p.expire_date &lt;= #{params.endTime}
</if>
</if>
<if test="cargoAttr != null and cargoAttr != ''">
AND p.cargo_attr like concat('%', #{cargoAttr}, '%')
</if>
<if test="params != null and params.keyword != null and params.keyword != ''">
AND (p.product_code like concat('%', #{params.keyword}, '%')
OR p.product_name like concat('%', #{params.keyword}, '%'))
</if>
</where>
<choose>
<when test="statusGroup != null and statusGroup == 'expired'">
order by p.expire_date desc, p.product_code asc, p.product_id desc
</when>
<otherwise>
order by p.product_code asc, p.effective_date desc, p.product_id desc
</otherwise>
</choose>
</select>
<insert id="insertBmsRedlineProduct" parameterType="BmsRedlineProduct" useGeneratedKeys="true" keyProperty="productId">
insert into redline_product (
<if test="productCode != null and productCode != ''">product_code,</if>
<if test="productName != null and productName != ''">product_name,</if>
<if test="serviceLevel != null">service_level,</if>
<if test="cargoAttr != null">cargo_attr,</if>
<if test="destRegion != null">dest_region,</if>
<if test="updateNote != null">update_note,</if>
<if test="taxMode != null and taxMode != ''">tax_mode,</if>
<if test="effectiveDate != null">effective_date,</if>
<if test="expireDate != null">expire_date,</if>
<if test="status != null and status != ''">status,</if>
<if test="sourceSheet != null">source_sheet,</if>
<if test="pefId != null">pef_id,</if>
<if test="remark != null">remark,</if>
<if test="tmsId != null">tms_id,</if>
<if test="createBy != null and createBy != ''">created_by,</if>
create_time
) values (
<if test="productCode != null and productCode != ''">#{productCode},</if>
<if test="productName != null and productName != ''">#{productName},</if>
<if test="serviceLevel != null">#{serviceLevel},</if>
<if test="cargoAttr != null">#{cargoAttr},</if>
<if test="destRegion != null">#{destRegion},</if>
<if test="updateNote != null">#{updateNote},</if>
<if test="taxMode != null and taxMode != ''">#{taxMode},</if>
<if test="effectiveDate != null">#{effectiveDate},</if>
<if test="expireDate != null">#{expireDate},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="sourceSheet != null">#{sourceSheet},</if>
<if test="pefId != null">#{pefId},</if>
<if test="remark != null">#{remark},</if>
<if test="tmsId != null">#{tmsId},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
sysdate()
)
</insert>
<update id="updateBmsRedlineProduct" parameterType="BmsRedlineProduct">
update product
<set>
<if test="productCode != null and productCode != ''">product_code = #{productCode},</if>
<if test="productName != null and productName != ''">product_name = #{productName},</if>
<if test="serviceLevel != null">service_level = #{serviceLevel},</if>
<if test="cargoAttr != null">cargo_attr = #{cargoAttr},</if>
<if test="destRegion != null">dest_region = #{destRegion},</if>
<if test="updateNote != null">update_note = #{updateNote},</if>
<if test="taxMode != null and taxMode != ''">tax_mode = #{taxMode},</if>
<if test="effectiveDate != null">effective_date = #{effectiveDate},</if>
expire_date = #{expireDate},
<if test="status != null and status != ''">status = #{status},</if>
<if test="sourceSheet != null">source_sheet = #{sourceSheet},</if>
<if test="pefId != null">pef_id = #{pefId},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="tmsId != null">tms_id = #{tmsId},</if>
<if test="updateBy != null and updateBy != ''">updated_by = #{updateBy},</if>
update_time = sysdate()
</set>
where product_id = #{productId}
</update>
<delete id="deleteBmsRedlineProductByIds" parameterType="Long">
delete from redline_product where product_id in
<foreach item="productId" collection="array" open="(" separator="," close=")">
#{productId}
</foreach>
</delete>
<update id="expireOpenByProductCode">
update product
set expire_date = #{newEffectiveDate},
update_time = sysdate()
where product_code = #{productCode}
and expire_date is null
and (effective_date is null or effective_date &lt; #{newEffectiveDate})
<if test="excludeId != null">
and product_id &lt;&gt; #{excludeId}
</if>
</update>
<update id="refreshStatusByDates">
update product
set status = case
when expire_date is not null and expire_date &lt;= now() then '2'
when effective_date is null or effective_date &gt; now() then '0'
else '1'
end
</update>
</mapper>
@@ -113,4 +113,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</delete>
<select id="selectByRedlineProductCode" resultMap="PrcPriceExcelFileResult">
select distinct f.pef_id, f.excel_type, f.effective_time, f.file_name, f.file_path, f.file_size,
f.content_type, f.file_extension, f.oss_id, f.sheet_names, f.remark,
f.created_by, f.create_time, f.updated_by, f.update_time
from prc_price_excel_file f
inner join redline_product p on p.pef_id = f.pef_id
where p.product_code = #{productCode}
and p.pef_id is not null
order by f.effective_time desc, f.pef_id desc
</select>
</mapper>
@@ -35,11 +35,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
r.created_by, r.create_time, r.updated_by, r.update_time,
s.sc_name, s.fee_type, s.charge_type, s.charge_value, s.currency,
s.round_mode, s.ss_code, s.sc_note,
c.country_name, c.weight_min, c.weight_max
ifnull(c.country_name, rc.country_name) as country_name, ifnull(c.weight_min, rc.weight_min) as weight_min, ifnull(c.weight_max, rc.weight_max) as weight_max
from prc_surcharge_rel r
join prc_surcharge s on s.sc_id = r.sc_id
left join product_country c on c.detail_id = r.detail_id
and r.price_type in ('PRODUCT', 'REDLINE')
left join product_country c on c.detail_id = r.detail_id and r.price_type = 'PRODUCT'
left join redline_product_country rc on rc.detail_id = r.detail_id and r.price_type = 'REDLINE'
</sql>
<select id="selectPrcSurchargeRelList" parameterType="PrcSurchargeRel" resultMap="PrcSurchargeRelResult">
@@ -102,7 +102,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<foreach item="code" collection="array" open="(" separator="," close=")">
#{code}
</foreach>
and price_type in ('PRODUCT', 'REDLINE')
and price_type = 'PRODUCT'
</delete>
<delete id="deleteByDetailIdsAndPriceType">
+62
View File
@@ -345,6 +345,68 @@ CREATE TABLE `product_country` (
CONSTRAINT `fk_product_country_pid` FOREIGN KEY (`product_id`) REFERENCES `product` (`product_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1609 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品国家服务明细-重量段+尺寸/尾程/时效(不含运费)';
-- redline_product / redline_product_country: mirror of product / product_country
-- Run once on existing DB
CREATE TABLE IF NOT EXISTS `redline_product` (
`product_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '红线价产品ID',
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品对接代码,如 WSP037',
`product_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品名称',
`service_level` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '时效等级',
`cargo_attr` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '走货属性',
`dest_region` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的区域',
`update_note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新内容说明',
`tax_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'TAX_INCLUDED' COMMENT 'TAX_INCLUDED包税 TAX_EXCLUDED不包税',
`effective_date` datetime DEFAULT NULL COMMENT '目录生效时间(到小时)',
`expire_date` datetime DEFAULT NULL COMMENT '失效时间(空=长期)',
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0待生效 1已生效 2已失效',
`source_sheet` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源Excel sheet名',
`pef_id` bigint(20) DEFAULT NULL COMMENT '关联导入价表Excel附件 prc_price_excel_file.pef_id',
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`tms_id` int(11) DEFAULT NULL COMMENT '多租户ID',
`created_by` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`product_id`) USING BTREE,
UNIQUE KEY `uk_redline_product_code_eff` (`product_code`,`effective_date`),
KEY `idx_redline_tax_status` (`tax_mode`,`status`) USING BTREE,
KEY `idx_redline_product_code` (`product_code`),
KEY `idx_redline_product_pef_id` (`pef_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='红线价产品主表';
CREATE TABLE IF NOT EXISTS `redline_product_country` (
`detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
`product_id` bigint(20) DEFAULT NULL COMMENT '红线价产品ID redline_product.product_id',
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码',
`country_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国家中文名',
`country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'ISO2',
`zone_id` bigint(20) DEFAULT NULL COMMENT 'prc_zone.zone_id',
`zone_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`zone_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`weight_min` decimal(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)',
`weight_max` decimal(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)',
`freight_price` decimal(10,4) DEFAULT NULL COMMENT '运费 RMB/KG',
`registration_fee` decimal(10,2) DEFAULT NULL COMMENT '挂号费 RMB/票',
`size_limit` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_mile` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`lead_time` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`effective_date` datetime DEFAULT NULL COMMENT '生效时间(到小时)',
`expire_date` datetime DEFAULT NULL COMMENT '失效时间(空=开放)',
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0待生效 1已生效 2已失效',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`detail_id`) USING BTREE,
UNIQUE KEY `uk_redline_country_zone_weight_eff` (`product_code`,`country_name`,`zone_code`,`weight_min`,`weight_max`,`effective_date`),
KEY `idx_redline_country_code` (`country_code`) USING BTREE,
KEY `idx_redline_weight_match` (`product_code`,`country_code`,`weight_min`,`weight_max`) USING BTREE,
KEY `idx_redline_product_eff` (`product_code`,`effective_date`,`expire_date`),
KEY `idx_redline_country_pid` (`product_id`),
KEY `idx_redline_country_zone` (`zone_id`),
CONSTRAINT `fk_redline_country_pid` FOREIGN KEY (`product_id`) REFERENCES `redline_product` (`product_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='红线价国家服务明细';
-- ----------------------------
-- 表结构:`region`
-- ----------------------------
+105
View File
@@ -0,0 +1,105 @@
-- redline_product / redline_product_country: mirror of product / product_country
-- Run once on existing DB
CREATE TABLE IF NOT EXISTS `redline_product` (
`product_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '红线价产品ID',
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品对接代码,如 WSP037',
`product_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品名称',
`service_level` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '时效等级',
`cargo_attr` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '走货属性',
`dest_region` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的区域',
`update_note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新内容说明',
`tax_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'TAX_INCLUDED' COMMENT 'TAX_INCLUDED包税 TAX_EXCLUDED不包税',
`effective_date` datetime DEFAULT NULL COMMENT '目录生效时间(到小时)',
`expire_date` datetime DEFAULT NULL COMMENT '失效时间(空=长期)',
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0待生效 1已生效 2已失效',
`source_sheet` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源Excel sheet名',
`pef_id` bigint(20) DEFAULT NULL COMMENT '关联导入价表Excel附件 prc_price_excel_file.pef_id',
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`tms_id` int(11) DEFAULT NULL COMMENT '多租户ID',
`created_by` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`product_id`) USING BTREE,
UNIQUE KEY `uk_redline_product_code_eff` (`product_code`,`effective_date`),
KEY `idx_redline_tax_status` (`tax_mode`,`status`) USING BTREE,
KEY `idx_redline_product_code` (`product_code`),
KEY `idx_redline_product_pef_id` (`pef_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='红线价产品主表';
CREATE TABLE IF NOT EXISTS `redline_product_country` (
`detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
`product_id` bigint(20) DEFAULT NULL COMMENT '红线价产品ID redline_product.product_id',
`product_code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码',
`country_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国家中文名',
`country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'ISO2',
`zone_id` bigint(20) DEFAULT NULL COMMENT 'prc_zone.zone_id',
`zone_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`zone_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`weight_min` decimal(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)',
`weight_max` decimal(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)',
`freight_price` decimal(10,4) DEFAULT NULL COMMENT '运费 RMB/KG',
`registration_fee` decimal(10,2) DEFAULT NULL COMMENT '挂号费 RMB/票',
`size_limit` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_mile` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`lead_time` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`effective_date` datetime DEFAULT NULL COMMENT '生效时间(到小时)',
`expire_date` datetime DEFAULT NULL COMMENT '失效时间(空=开放)',
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0待生效 1已生效 2已失效',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`detail_id`) USING BTREE,
UNIQUE KEY `uk_redline_country_zone_weight_eff` (`product_code`,`country_name`,`zone_code`,`weight_min`,`weight_max`,`effective_date`),
KEY `idx_redline_country_code` (`country_code`) USING BTREE,
KEY `idx_redline_weight_match` (`product_code`,`country_code`,`weight_min`,`weight_max`) USING BTREE,
KEY `idx_redline_product_eff` (`product_code`,`effective_date`,`expire_date`),
KEY `idx_redline_country_pid` (`product_id`),
KEY `idx_redline_country_zone` (`zone_id`),
CONSTRAINT `fk_redline_country_pid` FOREIGN KEY (`product_id`) REFERENCES `redline_product` (`product_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='红线价国家服务明细';
-- Menu: under 价栶基础 parent (lookup by path price/basic or menu_name)
-- Adjust @parentId if your env differs
SET @parentId := (
SELECT menu_id FROM sys_menu
WHERE path = 'basic' AND menu_type = 'M'
ORDER BY menu_id ASC LIMIT 1
);
SET @parentId := IFNULL(@parentId, (
SELECT parent_id FROM sys_menu
WHERE perms = 'price:product:list' OR component LIKE '%price/basic/product%'
ORDER BY menu_id ASC LIMIT 1
));
-- skip insert if already exists
SET @exists := (
SELECT COUNT(1) FROM sys_menu WHERE perms = 'price:redline:list' OR component = 'price/basic/redline/index'
);
-- Only insert when parent found and not exists
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
SELECT '红线价', @parentId, 15, 'redline', 'price/basic/redline/index', 1, 0, 'C', '0', '0', 'price:redline:list', 'money', 'admin', sysdate(), '红线价目录'
WHERE @parentId IS NOT NULL AND @exists = 0;
SET @menuId := (SELECT menu_id FROM sys_menu WHERE perms = 'price:redline:list' LIMIT 1);
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT '红线价查询', @menuId, 1, '', '', 1, 0, 'F', '0', '0', 'price:redline:query', '#', 'admin', sysdate()
WHERE @menuId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'price:redline:query');
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT '红线价新增', @menuId, 2, '', '', 1, 0, 'F', '0', '0', 'price:redline:add', '#', 'admin', sysdate()
WHERE @menuId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'price:redline:add');
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT '红线价修改', @menuId, 3, '', '', 1, 0, 'F', '0', '0', 'price:redline:edit', '#', 'admin', sysdate()
WHERE @menuId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'price:redline:edit');
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT '红线价删除', @menuId, 4, '', '', 1, 0, 'F', '0', '0', 'price:redline:remove', '#', 'admin', sysdate()
WHERE @menuId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'price:redline:remove');
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT '红线价导入', @menuId, 5, '', '', 1, 0, 'F', '0', '0', 'price:redline:import', '#', 'admin', sysdate()
WHERE @menuId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'price:redline:import');