Merge branch 'master' of http://git.lionquexpress.com/wuweihua/BMS
This commit is contained in:
+2
-1
@@ -78,7 +78,8 @@ public class TmsPriceController extends BaseController
|
||||
|
||||
/**
|
||||
* Sync order and persist prc_order_bill / prc_income_bill_fee.
|
||||
* Same price match as quote. Same orderNo upserts.
|
||||
* Same price match as quote (bound set + active version by bizDate, else default).
|
||||
* Same orderNo upserts.
|
||||
*/
|
||||
@PostMapping("/bill")
|
||||
public AjaxResult bill(
|
||||
|
||||
+46
-2
@@ -1,6 +1,8 @@
|
||||
package com.ruoyi.web.controller.price;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -17,9 +19,11 @@ import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.MessageUtils;
|
||||
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||
import com.ruoyi.price.domain.PrcPriceSetSaveTask;
|
||||
import com.ruoyi.price.service.IPrcPriceSetService;
|
||||
|
||||
/**
|
||||
@@ -57,8 +61,23 @@ public class PriceSetController extends BaseController
|
||||
{
|
||||
set.setCreateBy(getUsername());
|
||||
set.setUpdateBy(getUsername());
|
||||
Long id = priceSetService.savePrcPriceSet(set);
|
||||
return success(id);
|
||||
String taskId = priceSetService.submitSaveAsync(set);
|
||||
// Do not pass bare String to success(): overload puts it into msg, not data.
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("taskId", taskId);
|
||||
return success(payload);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:query')")
|
||||
@GetMapping("/save/task/{taskId}")
|
||||
public AjaxResult getSaveTask(@PathVariable String taskId)
|
||||
{
|
||||
PrcPriceSetSaveTask task = priceSetService.getSaveTask(taskId);
|
||||
if (task == null)
|
||||
{
|
||||
return error(MessageUtils.message("price.set.save.task.notExists"));
|
||||
}
|
||||
return success(task);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit')")
|
||||
@@ -102,6 +121,31 @@ public class PriceSetController extends BaseController
|
||||
return toAjax(priceSetService.reject(priceSetId, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit')")
|
||||
@Log(title = "Price set version activate", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/version/activate/{psvId}")
|
||||
public AjaxResult activateVersion(@PathVariable Long psvId)
|
||||
{
|
||||
return toAjax(priceSetService.activateVersion(psvId, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:add')")
|
||||
@Log(title = "Price set version copy", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/version/copy/{psvId}")
|
||||
public AjaxResult copyVersion(@PathVariable Long psvId)
|
||||
{
|
||||
Long newPsvId = priceSetService.copyVersion(psvId, getUsername());
|
||||
return success(newPsvId);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:edit')")
|
||||
@Log(title = "Price set version delete", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/version/{psvId}")
|
||||
public AjaxResult deleteVersion(@PathVariable Long psvId)
|
||||
{
|
||||
return toAjax(priceSetService.deleteVersion(psvId, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('price:set:audit')")
|
||||
@Log(title = "价格套餐停用", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/disable/{priceSetId}")
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ public class PriceStatusController extends BaseController
|
||||
{
|
||||
int[] counts = refreshTask.refreshNow();
|
||||
String msg = MessageUtils.message("price.status.refresh.success",
|
||||
String.valueOf(counts[0]), String.valueOf(counts[1]));
|
||||
String.valueOf(counts[0]), String.valueOf(counts[1]),
|
||||
String.valueOf(counts.length > 2 ? counts[2] : 0));
|
||||
return success(msg);
|
||||
}
|
||||
}
|
||||
+13
@@ -63,6 +63,19 @@ public class SurchargeController extends BaseController
|
||||
return success(surchargeRelService.selectRelByProductCode(productCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加费配置全量(非分页,供套餐明细勾选使用)
|
||||
*
|
||||
* 不要求 price:surcharge:list 权限,套餐维护角色(price:set:*)也能读,避免套餐页拿到 403 空列表。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('price:surcharge:list') or @ss.hasPermi('price:surcharge:query')"
|
||||
+ " or @ss.hasPermi('price:set:query') or @ss.hasPermi('price:set:add') or @ss.hasPermi('price:set:edit')")
|
||||
@GetMapping("/options")
|
||||
public AjaxResult listOptions(PrcSurcharge surcharge)
|
||||
{
|
||||
return success(surchargeService.selectPrcSurchargeList(surcharge));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定附加费到产品/明细(body: scId, productId, productCode, detailId;detailId 空=产品级)
|
||||
*/
|
||||
|
||||
@@ -136,9 +136,20 @@ price.quote.quoteIdRequired=报价ID为空
|
||||
price.quote.hitCustomerFixedPackageDetail=命中客户套餐固定价格明细
|
||||
|
||||
# ---- 状态自动刷新 ----
|
||||
price.status.refresh.success=状态刷新完成:bms_product 更新 {0} 行,bms_product_country 更新 {1} 行
|
||||
price.status.refresh.success=状态刷新完成:bms_product 更新 {0} 行,bms_product_country 更新 {1} 行,价格套餐版本 {2} 套
|
||||
# receivable bill
|
||||
price.bill.orderNoRequired=订单号为空
|
||||
price.bill.billIdRequired=计费ID为空
|
||||
price.bill.notFound=计费记录不存在
|
||||
price.bill.bizDateRequired=业务日期为空
|
||||
|
||||
price.set.version.effectiveTime.empty=版本生效时间不能为空
|
||||
price.set.version.notExists=套餐版本不存在
|
||||
price.set.version.activate.statusNotAllowed=仅草稿版本可启动
|
||||
price.set.version.delete.statusNotAllowed=仅草稿版本可删除
|
||||
price.set.save.task.notExists=保存任务不存在或已过期
|
||||
price.set.library.empty=产品库无可用价格明细,请检查产品国家配置
|
||||
price.set.detail.duplicate.fixed=产品{0} 国家{1} 重量段{2} {3}(共{4}条)
|
||||
price.set.detail.duplicate.discount=产品{0}(共{1}条)
|
||||
price.set.detail.duplicate.hasZone=有分区({0})
|
||||
price.set.detail.duplicate.noZone=无分区
|
||||
|
||||
@@ -37,6 +37,15 @@ public class PrcPriceSet extends BaseEntity
|
||||
private Boolean expired;
|
||||
private String keyword;
|
||||
private String submitFlag;
|
||||
/** current version on create/edit save */
|
||||
private PrcPriceSetVersion version;
|
||||
private List<PrcPriceSetDetail> details;
|
||||
private List<PrcPriceSetVersion> versions;
|
||||
private List<PrcCustomerPriceSet> customers;
|
||||
/** async save task id (not persisted) */
|
||||
private String saveTaskId;
|
||||
/** Y: backend joins product library into details (skip front pull) */
|
||||
private String pullFromLibrary;
|
||||
/** Y: even if version already has details, delete+re-pull from library */
|
||||
private String forcePullLibrary;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.ruoyi.price.domain;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -14,6 +15,8 @@ public class PrcPriceSetDetail implements Serializable
|
||||
|
||||
private Long psdId;
|
||||
private Long priceSetId;
|
||||
/** prc_price_set_version.psv_id */
|
||||
private Long psvId;
|
||||
private String productCode;
|
||||
private String productName;
|
||||
private String countryCode;
|
||||
@@ -27,6 +30,9 @@ public class PrcPriceSetDetail implements Serializable
|
||||
private Long zdId;
|
||||
private Long wsdId;
|
||||
private String weightLabel;
|
||||
/** KG inclusive, same as bms_product_country */
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
/** bms_product_country.detail_id */
|
||||
private Long productCountryId;
|
||||
private BigDecimal fvValue;
|
||||
@@ -35,8 +41,22 @@ public class PrcPriceSetDetail implements Serializable
|
||||
/** UP / DOWN / HALF_UP */
|
||||
private String roundMode;
|
||||
private Integer prId;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date effectiveDate;
|
||||
/** null = open */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expireDate;
|
||||
private List<PrcPriceSetVersionSurcharge> surcharges;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
/** query only: product/country/zone/weight keyword */
|
||||
private String keyword;
|
||||
/** query only: product code/name like */
|
||||
private String productKeyword;
|
||||
/** query only: country code/name like */
|
||||
private String countryKeyword;
|
||||
/** query only: zone code/name like */
|
||||
private String zoneKeyword;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.ruoyi.price.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Async save progress for price set (Redis).
|
||||
*/
|
||||
public class PrcPriceSetSaveTask 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 Long priceSetId;
|
||||
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 Long getPriceSetId()
|
||||
{
|
||||
return priceSetId;
|
||||
}
|
||||
|
||||
public void setPriceSetId(Long priceSetId)
|
||||
{
|
||||
this.priceSetId = priceSetId;
|
||||
}
|
||||
|
||||
public String getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.price.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** Price set version prc_price_set_version */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PrcPriceSetVersion extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long psvId;
|
||||
private Long priceSetId;
|
||||
/** V1 / V2 */
|
||||
private String versionNo;
|
||||
private Integer versionSeq;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveTime;
|
||||
/** null = open; set to next version effective_time when that version starts */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
/** 0 draft / 1 active / 2 expired */
|
||||
private String status;
|
||||
|
||||
private List<PrcPriceSetDetail> details;
|
||||
private List<PrcPriceSetVersionSurcharge> surcharges;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** Price set version surcharge snapshot prc_price_set_version_surcharge */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PrcPriceSetVersionSurcharge extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long psvsId;
|
||||
private Long psvId;
|
||||
/** source prc_surcharge.sc_id */
|
||||
private Long scId;
|
||||
/** prc_price_set_detail.psd_id */
|
||||
private Long psdId;
|
||||
/** copied from detail */
|
||||
private String productCode;
|
||||
private String scName;
|
||||
private String feeType;
|
||||
/** FIXED / WEIGHT / RATE */
|
||||
private String chargeType;
|
||||
private BigDecimal chargeValue;
|
||||
private String currency;
|
||||
private String roundMode;
|
||||
private String countryCode;
|
||||
private String scNote;
|
||||
}
|
||||
@@ -25,6 +25,12 @@ public class TmsPriceQuoteResult implements Serializable
|
||||
private String setType;
|
||||
private Integer bindPriority;
|
||||
private Long customerPricesetId;
|
||||
/** Hit package version id (prc_price_set_version.psv_id) */
|
||||
private Long psvId;
|
||||
/** Hit package version no (e.g. V1) */
|
||||
private String versionNo;
|
||||
/** Hit package detail id when package version detail matched */
|
||||
private Long psdId;
|
||||
|
||||
private String productCode;
|
||||
private String productName;
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public interface PrcCustomerPriceSetMapper
|
||||
@Param("endDate") java.util.Date endDate);
|
||||
|
||||
/**
|
||||
* 客户在业务日有效、且套餐状态为生效(E) 的绑定,按优先级升序
|
||||
* Customer binds valid on bizDate; package not stopped. Version match is done in quote service.
|
||||
*/
|
||||
public java.util.List<PrcCustomerPriceSet> selectEffectiveByCustomer(
|
||||
@Param("customerId") Long customerId,
|
||||
|
||||
+30
-2
@@ -1,5 +1,6 @@
|
||||
package com.ruoyi.price.mapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||
@@ -23,13 +24,39 @@ public interface PrcPriceSetDetailMapper
|
||||
|
||||
public int deletePrcPriceSetDetailByPriceSetId(Long priceSetId);
|
||||
|
||||
public int deletePrcPriceSetDetailByPsvId(Long psvId);
|
||||
|
||||
/** legacy rows: price_set_id match but psv_id is null */
|
||||
public int deleteOrphanDetailsByPriceSetId(Long priceSetId);
|
||||
|
||||
public int countProductByPriceSetId(Long priceSetId);
|
||||
|
||||
public int countByPriceSetId(Long priceSetId);
|
||||
|
||||
/** FIXED: product + country + zone + productCountryId / weight */
|
||||
public int countByPsvId(Long psvId);
|
||||
|
||||
/** Copy all details from src version into new version (new psd_id, same price_set_id) */
|
||||
public int copyDetailsFromVersion(@Param("srcPsvId") Long srcPsvId, @Param("newPsvId") Long newPsvId);
|
||||
|
||||
/**
|
||||
* FIXED: insert details from active product + country library (asOf window or openOnly fallback).
|
||||
* @return inserted row count
|
||||
*/
|
||||
public int insertFixedDetailsFromLibrary(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("psvId") Long psvId,
|
||||
@Param("asOf") Date asOf,
|
||||
@Param("openOnly") boolean openOnly);
|
||||
|
||||
/** DISCOUNT: one detail row per active product */
|
||||
public int insertDiscountDetailsFromLibrary(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("psvId") Long psvId);
|
||||
|
||||
/** FIXED: product + country + zone + productCountryId / weight; optional psvId scopes to version */
|
||||
public PrcPriceSetDetail selectFixedMatch(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("psvId") Long psvId,
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@@ -37,8 +64,9 @@ public interface PrcPriceSetDetailMapper
|
||||
@Param("productCountryId") Long productCountryId,
|
||||
@Param("weightLabel") String weightLabel);
|
||||
|
||||
/** DISCOUNT:按产品命中明细折扣(可空) */
|
||||
/** DISCOUNT by product; optional psvId scopes to version */
|
||||
public PrcPriceSetDetail selectDiscountMatch(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("psvId") Long psvId,
|
||||
@Param("productCode") String productCode);
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.price.mapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.price.domain.PrcPriceSetVersion;
|
||||
|
||||
/**
|
||||
* Price set version
|
||||
*/
|
||||
public interface PrcPriceSetVersionMapper
|
||||
{
|
||||
public PrcPriceSetVersion selectPrcPriceSetVersionById(Long psvId);
|
||||
|
||||
public List<PrcPriceSetVersion> selectPrcPriceSetVersionList(PrcPriceSetVersion version);
|
||||
|
||||
public Integer selectMaxVersionSeq(@Param("priceSetId") Long priceSetId);
|
||||
|
||||
public int insertPrcPriceSetVersion(PrcPriceSetVersion version);
|
||||
|
||||
public int updatePrcPriceSetVersion(PrcPriceSetVersion version);
|
||||
|
||||
public int deletePrcPriceSetVersionByIds(Long[] psvIds);
|
||||
|
||||
public int deletePrcPriceSetVersionByPriceSetId(Long priceSetId);
|
||||
|
||||
public List<Long> selectPriceSetIds();
|
||||
|
||||
/**
|
||||
* Version effective at asOf: effective_time <= asOf and (expire_time null or > asOf).
|
||||
* Prefer latest effective_time.
|
||||
*/
|
||||
public PrcPriceSetVersion selectActiveByPriceSetAt(
|
||||
@Param("priceSetId") Long priceSetId,
|
||||
@Param("asOf") Date asOf);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.price.domain.PrcPriceSetVersionSurcharge;
|
||||
|
||||
/**
|
||||
* Price set version surcharge snapshot
|
||||
*/
|
||||
public interface PrcPriceSetVersionSurchargeMapper
|
||||
{
|
||||
public PrcPriceSetVersionSurcharge selectPrcPriceSetVersionSurchargeById(Long psvsId);
|
||||
|
||||
public List<PrcPriceSetVersionSurcharge> selectPrcPriceSetVersionSurchargeList(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int insertPrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int updatePrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int deletePrcPriceSetVersionSurchargeByIds(Long[] psvsIds);
|
||||
|
||||
public int deleteByPsvId(Long psvId);
|
||||
|
||||
public int deleteByPsdId(Long psdId);
|
||||
|
||||
public List<PrcPriceSetVersionSurcharge> selectByPsdIds(@Param("psdIds") List<Long> psdIds);
|
||||
|
||||
/** Snapshot all applicable product-rel surcharges for details under psvId (library create). */
|
||||
public int insertFromLibraryRels(@Param("psvId") Long psvId, @Param("createBy") String createBy);
|
||||
|
||||
/** Copy surcharge snapshots from src version onto new version details (match by detail keys). */
|
||||
public int copySurchargesFromVersion(
|
||||
@Param("srcPsvId") Long srcPsvId,
|
||||
@Param("newPsvId") Long newPsvId,
|
||||
@Param("createBy") String createBy);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSetCheckItem;
|
||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||
import com.ruoyi.price.domain.PrcPriceSetSaveTask;
|
||||
|
||||
/**
|
||||
* 价格套餐 服务层
|
||||
@@ -31,6 +32,19 @@ public interface IPrcPriceSetService
|
||||
|
||||
public int disable(Long priceSetId, String updateBy);
|
||||
|
||||
/** Activate a draft version: set effective_time=now, refresh version 0/1/2 */
|
||||
public int activateVersion(Long psvId, String updateBy);
|
||||
|
||||
/**
|
||||
* Copy source version into a new draft version under the same package:
|
||||
* insert prc_price_set_version + copy prc_price_set_detail (+ surcharge snapshots).
|
||||
* @return new psvId
|
||||
*/
|
||||
public Long copyVersion(Long sourcePsvId, String createBy);
|
||||
|
||||
/** Delete a draft version and its details / surcharge snapshots */
|
||||
public int deleteVersion(Long psvId, String updateBy);
|
||||
|
||||
public Long copy(Long priceSetId, String createBy);
|
||||
|
||||
public List<PrcPriceSetCheckItem> validate(PrcPriceSet set);
|
||||
@@ -50,4 +64,13 @@ public interface IPrcPriceSetService
|
||||
public int updateCustomer(PrcCustomerPriceSet customer);
|
||||
|
||||
public int deleteCustomerByIds(Long[] customerPricesetIds);
|
||||
|
||||
/** chain expire_time = next version effective_time, then refresh 0/1/2 */
|
||||
public int refreshAllVersionStatus();
|
||||
|
||||
/** submit async save, return taskId immediately */
|
||||
public String submitSaveAsync(PrcPriceSet set);
|
||||
|
||||
/** poll async save progress */
|
||||
public PrcPriceSetSaveTask getSaveTask(String taskId);
|
||||
}
|
||||
|
||||
+7
-4
@@ -6,11 +6,14 @@ import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TMS freight quote service.
|
||||
* TMS freight quote / bill calc service (shared by console trial and upstream bill).
|
||||
*
|
||||
* Match order: effective customer package (bind_priority ASC) FIXED override
|
||||
* then DISCOUNT, else bms_product_country default freight/registration.
|
||||
* After calc, persist fee lines by bms_fee_type; same trial params return stored result.
|
||||
* Match order:
|
||||
* 1) Customer binds (not stopped), bind_priority ASC
|
||||
* 2) Active package version at bill-time end-of-day (effective_time / expire_time)
|
||||
* 3) FIXED detail override, else DISCOUNT on that version
|
||||
* 4) Else bms_product_country default
|
||||
* Package hit uses version surcharge snapshot when present; else product library.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
|
||||
+1171
-92
File diff suppressed because it is too large
Load Diff
+109
-15
@@ -5,6 +5,7 @@ import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -25,6 +26,8 @@ import com.ruoyi.price.domain.BmsProductCountry;
|
||||
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||
import com.ruoyi.price.domain.PrcPriceSetVersion;
|
||||
import com.ruoyi.price.domain.PrcPriceSetVersionSurcharge;
|
||||
import com.ruoyi.price.domain.PrcQuote;
|
||||
import com.ruoyi.price.domain.PrcQuoteFee;
|
||||
import com.ruoyi.price.domain.PrcSurchargeRel;
|
||||
@@ -38,6 +41,8 @@ import com.ruoyi.price.mapper.BmsProductMapper;
|
||||
import com.ruoyi.price.mapper.PrcCustomerPriceSetMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetDetailMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetVersionMapper;
|
||||
import com.ruoyi.price.mapper.PrcPriceSetVersionSurchargeMapper;
|
||||
import com.ruoyi.price.mapper.PrcQuoteFeeMapper;
|
||||
import com.ruoyi.price.mapper.PrcQuoteMapper;
|
||||
import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
|
||||
@@ -47,8 +52,8 @@ import com.ruoyi.price.search.IBillSearchStore;
|
||||
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
||||
|
||||
/**
|
||||
* TMS freight quote: customer package first, then product default.
|
||||
* After calc: persist fee lines by bms_fee_type; same trial params return stored rows.
|
||||
* Shared quote/bill calc: bound set + active version by bizDate, else product default.
|
||||
* Quote persists prc_quote; bill path calls calculate() then persists prc_order_bill.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@@ -71,6 +76,12 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
@Autowired
|
||||
private PrcPriceSetMapper priceSetMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetVersionMapper versionMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetVersionSurchargeMapper versionSurchargeMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetDetailMapper detailMapper;
|
||||
|
||||
@@ -249,17 +260,24 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
|
||||
if (binds != null)
|
||||
{
|
||||
Date asOf = endOfDay(bizDate);
|
||||
for (PrcCustomerPriceSet bind : binds)
|
||||
{
|
||||
PrcPriceSet set = priceSetMapper.selectPrcPriceSetById(bind.getPriceSetId());
|
||||
if (set == null || !"E".equals(set.getStatus()))
|
||||
if (set == null || "S".equals(set.getStatus()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetVersion version = versionMapper.selectActiveByPriceSetAt(set.getPriceSetId(), asOf);
|
||||
if (version == null || version.getPsvId() == null)
|
||||
{
|
||||
// Bound package but no version effective at bill time -> try next bind / default
|
||||
continue;
|
||||
}
|
||||
String setType = StringUtils.isEmpty(set.getSetType()) ? TYPE_FIXED : set.getSetType();
|
||||
if (TYPE_FIXED.equals(setType))
|
||||
{
|
||||
TmsPriceQuoteResult hit = tryFixed(bind, set, base, productCode, productName, weight);
|
||||
TmsPriceQuoteResult hit = tryFixed(bind, set, version, base, productCode, productName, weight);
|
||||
if (hit != null)
|
||||
{
|
||||
stampRequestMeta(request, hit);
|
||||
@@ -268,7 +286,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
else if (TYPE_DISCOUNT.equals(setType))
|
||||
{
|
||||
TmsPriceQuoteResult hit = tryDiscount(bind, set, base, productCode, productName, weight);
|
||||
TmsPriceQuoteResult hit = tryDiscount(bind, set, version, base, productCode, productName, weight);
|
||||
if (hit != null)
|
||||
{
|
||||
stampRequestMeta(request, hit);
|
||||
@@ -334,23 +352,25 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return n;
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult tryFixed(PrcCustomerPriceSet bind, PrcPriceSet set,
|
||||
private TmsPriceQuoteResult tryFixed(PrcCustomerPriceSet bind, PrcPriceSet set, PrcPriceSetVersion version,
|
||||
BmsProductCountry base, String productCode, String productName, BigDecimal weight)
|
||||
{
|
||||
int detailCount = detailMapper.countByPriceSetId(set.getPriceSetId());
|
||||
Long psvId = version.getPsvId();
|
||||
int detailCount = detailMapper.countByPsvId(psvId);
|
||||
if (detailCount == 0)
|
||||
{
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight, null);
|
||||
fillPackage(r, bind, set);
|
||||
// 套餐固定价格没有明细,使用产品默认价格
|
||||
stampVersion(r, version, null);
|
||||
// Package fixed has no version details; use product default rates
|
||||
r.setMatchMessage(MessageUtils.message("price.quote.fixedPackageNoDetails"));
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
PrcPriceSetDetail detail = detailMapper.selectFixedMatch(
|
||||
set.getPriceSetId(), productCode,
|
||||
set.getPriceSetId(), psvId, productCode,
|
||||
base.getCountryCode(), base.getCountryName(), base.getZoneCode(),
|
||||
base.getDetailId(),
|
||||
formatWeightLabel(base.getWeightMin(), base.getWeightMax()));
|
||||
@@ -365,6 +385,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_FIXED, detail.getFvValue(), null, null,
|
||||
detail.getFvRegistrationFee(), base, productCode, productName, weight, detail.getRoundMode());
|
||||
fillPackage(r, bind, set);
|
||||
stampVersion(r, version, detail.getPsdId());
|
||||
if (StringUtils.isNotEmpty(detail.getProductName()))
|
||||
{
|
||||
r.setProductName(detail.getProductName());
|
||||
@@ -377,25 +398,28 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
{
|
||||
r.setProductCountryId(detail.getProductCountryId());
|
||||
}
|
||||
// 命中客户套餐固定价格明细
|
||||
r.setMatchMessage(MessageUtils.message("price.quote.hitCustomerFixedPackageDetail"));
|
||||
r.setMatchMessage(MessageUtils.message("price.quote.hitCustomerFixedPackageDetail")
|
||||
+ " [" + (version.getVersionNo() == null ? ("psv=" + psvId) : version.getVersionNo()) + "]");
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult tryDiscount(PrcCustomerPriceSet bind, PrcPriceSet set,
|
||||
private TmsPriceQuoteResult tryDiscount(PrcCustomerPriceSet bind, PrcPriceSet set, PrcPriceSetVersion version,
|
||||
BmsProductCountry base, String productCode, String productName, BigDecimal weight)
|
||||
{
|
||||
int detailCount = detailMapper.countByPriceSetId(set.getPriceSetId());
|
||||
Long psvId = version.getPsvId();
|
||||
int detailCount = detailMapper.countByPsvId(psvId);
|
||||
BigDecimal discount = set.getDefaultDiscount();
|
||||
String roundMode = null;
|
||||
Long psdId = null;
|
||||
if (detailCount > 0)
|
||||
{
|
||||
PrcPriceSetDetail detail = detailMapper.selectDiscountMatch(set.getPriceSetId(), productCode);
|
||||
PrcPriceSetDetail detail = detailMapper.selectDiscountMatch(set.getPriceSetId(), psvId, productCode);
|
||||
if (detail == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
psdId = detail.getPsdId();
|
||||
if (detail.getDiscountValue() != null)
|
||||
{
|
||||
discount = detail.getDiscountValue();
|
||||
@@ -419,11 +443,25 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DISCOUNT, rate, baseRate, discount,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight, roundMode);
|
||||
fillPackage(r, bind, set);
|
||||
r.setMatchMessage("hit customer DISCOUNT package; freight = default * discount%");
|
||||
stampVersion(r, version, psdId);
|
||||
r.setMatchMessage("hit customer DISCOUNT package version "
|
||||
+ (version.getVersionNo() == null ? String.valueOf(psvId) : version.getVersionNo())
|
||||
+ "; freight = default * discount%");
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
}
|
||||
|
||||
private void stampVersion(TmsPriceQuoteResult result, PrcPriceSetVersion version, Long psdId)
|
||||
{
|
||||
if (result == null || version == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result.setPsvId(version.getPsvId());
|
||||
result.setVersionNo(version.getVersionNo());
|
||||
result.setPsdId(psdId);
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult buildDefault(Long customerId, BmsProductCountry base,
|
||||
String productCode, String productName, BigDecimal weight, String message)
|
||||
{
|
||||
@@ -538,6 +576,23 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
{
|
||||
return matched;
|
||||
}
|
||||
if (result.getPsdId() != null)
|
||||
{
|
||||
List<PrcPriceSetVersionSurcharge> snaps =
|
||||
versionSurchargeMapper.selectByPsdIds(Collections.singletonList(result.getPsdId()));
|
||||
if (snaps != null && !snaps.isEmpty())
|
||||
{
|
||||
for (PrcPriceSetVersionSurcharge snap : snaps)
|
||||
{
|
||||
PrcSurchargeRel rel = toRelFromVersionSnap(snap);
|
||||
if (surchargeApplies(rel, result))
|
||||
{
|
||||
matched.add(rel);
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
PrcSurchargeRel query = new PrcSurchargeRel();
|
||||
query.setProductCode(result.getProductCode());
|
||||
List<PrcSurchargeRel> rels = surchargeRelMapper.selectPrcSurchargeRelList(query);
|
||||
@@ -555,6 +610,27 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return matched;
|
||||
}
|
||||
|
||||
private PrcSurchargeRel toRelFromVersionSnap(PrcPriceSetVersionSurcharge snap)
|
||||
{
|
||||
PrcSurchargeRel rel = new PrcSurchargeRel();
|
||||
if (snap == null)
|
||||
{
|
||||
return rel;
|
||||
}
|
||||
rel.setScId(snap.getScId());
|
||||
rel.setProductCode(snap.getProductCode());
|
||||
rel.setScName(snap.getScName());
|
||||
rel.setFeeType(snap.getFeeType());
|
||||
rel.setChargeType(snap.getChargeType());
|
||||
rel.setChargeValue(snap.getChargeValue());
|
||||
rel.setCurrency(snap.getCurrency());
|
||||
rel.setRoundMode(snap.getRoundMode());
|
||||
rel.setCountryCode(snap.getCountryCode());
|
||||
rel.setScNote(snap.getScNote());
|
||||
rel.setSsCode("on");
|
||||
return rel;
|
||||
}
|
||||
|
||||
private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result)
|
||||
{
|
||||
if (rel == null)
|
||||
@@ -961,6 +1037,24 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
}
|
||||
|
||||
/** Use end of bill day so same-day version activate still matches that day's quote. */
|
||||
private static Date endOfDay(Date day)
|
||||
{
|
||||
if (day == null)
|
||||
{
|
||||
return new Date();
|
||||
}
|
||||
try
|
||||
{
|
||||
String d = new SimpleDateFormat("yyyy-MM-dd").format(day);
|
||||
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(d + " 23:59:59");
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
return day;
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveProductName(String productCode, Date bizDate)
|
||||
{
|
||||
BmsProduct p = productMapper.selectEffectiveBmsProductByCode(productCode, bizDate);
|
||||
|
||||
@@ -3,10 +3,13 @@ package com.ruoyi.price.task;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.price.mapper.BmsProductCountryMapper;
|
||||
import com.ruoyi.price.mapper.BmsProductMapper;
|
||||
import com.ruoyi.price.service.IPrcPriceSetService;
|
||||
|
||||
/**
|
||||
* bms_product / bms_product_country 状态自动刷新
|
||||
@@ -26,7 +29,7 @@ import com.ruoyi.price.mapper.BmsProductMapper;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class BmsStatusRefreshTask
|
||||
public class BmsStatusRefreshTask implements ApplicationRunner
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BmsStatusRefreshTask.class);
|
||||
|
||||
@@ -36,6 +39,9 @@ public class BmsStatusRefreshTask
|
||||
@Autowired
|
||||
private BmsProductCountryMapper productCountryMapper;
|
||||
|
||||
@Autowired
|
||||
private IPrcPriceSetService priceSetService;
|
||||
|
||||
/** 每天 02:00 跑(cron: sec min hour DOM month DOW) */
|
||||
@Scheduled(cron = "0 0 2 * * ?")
|
||||
public void scheduled()
|
||||
@@ -43,16 +49,31 @@ public class BmsStatusRefreshTask
|
||||
refreshNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args)
|
||||
{
|
||||
try
|
||||
{
|
||||
int n = priceSetService.refreshAllVersionStatus();
|
||||
log.info("startup prc_price_set_version expire/status refreshed, sets={}", n);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("startup prc_price_set_version refresh failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即刷新两表状态。返回 [product 影响行数, country 影响行数]。
|
||||
* 异常时打印 ERROR 并原样抛出,便于调用方(手动触发接口)回滚事务。
|
||||
* Refresh product/country status and chain version expire_time.
|
||||
* Returns [product rows, country rows, price-set count].
|
||||
*/
|
||||
public int[] refreshNow()
|
||||
{
|
||||
int productCount = productMapper.refreshStatusByDates();
|
||||
int countryCount = productCountryMapper.refreshStatusByDates();
|
||||
log.info("BmsStatusRefreshTask done: bms_product={}, bms_product_country={}",
|
||||
productCount, countryCount);
|
||||
return new int[] { productCount, countryCount };
|
||||
int versionSets = priceSetService.refreshAllVersionStatus();
|
||||
log.info("BmsStatusRefreshTask done: bms_product={}, bms_product_country={}, price_set_version_sets={}",
|
||||
productCount, countryCount, versionSets);
|
||||
return new int[] { productCount, countryCount, versionSets };
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.ruoyi.price.task;
|
||||
|
||||
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.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.price.domain.PrcPriceSet;
|
||||
import com.ruoyi.price.domain.PrcPriceSetSaveTask;
|
||||
import com.ruoyi.price.service.IPrcPriceSetService;
|
||||
|
||||
/**
|
||||
* Async runner for price-set save (avoids HTTP timeout on large detail snapshots).
|
||||
*/
|
||||
@Component
|
||||
public class PrcPriceSetSaveAsyncRunner
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(PrcPriceSetSaveAsyncRunner.class);
|
||||
|
||||
public static final String CACHE_KEY_PREFIX = "price:set:save:task:";
|
||||
public static final int TTL_MINUTES = 60;
|
||||
|
||||
@Autowired
|
||||
private IPrcPriceSetService priceSetService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Async("threadPoolTaskExecutor")
|
||||
public void run(String taskId, PrcPriceSet set)
|
||||
{
|
||||
try
|
||||
{
|
||||
put(taskId, PrcPriceSetSaveTask.STATUS_RUNNING, 8, "saving basic", null, null);
|
||||
set.setSaveTaskId(taskId);
|
||||
Long id = priceSetService.savePrcPriceSet(set);
|
||||
put(taskId, PrcPriceSetSaveTask.STATUS_SUCCESS, 100, "done", id, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("async save price set failed, taskId={}", taskId, e);
|
||||
String msg = e.getMessage();
|
||||
if (StringUtils.isEmpty(msg))
|
||||
{
|
||||
msg = e.getClass().getSimpleName();
|
||||
}
|
||||
put(taskId, PrcPriceSetSaveTask.STATUS_FAIL, 100, "failed", null, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(String taskId, String status, int percent, String message, Long priceSetId, String error)
|
||||
{
|
||||
PrcPriceSetSaveTask task = new PrcPriceSetSaveTask();
|
||||
task.setTaskId(taskId);
|
||||
task.setStatus(status);
|
||||
task.setPercent(percent);
|
||||
task.setMessage(message);
|
||||
task.setPriceSetId(priceSetId);
|
||||
task.setError(error);
|
||||
redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public PrcPriceSetSaveTask get(String taskId)
|
||||
{
|
||||
return redisCache.getCacheObject(CACHE_KEY_PREFIX + taskId);
|
||||
}
|
||||
}
|
||||
@@ -97,8 +97,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
from prc_customer_price_set c
|
||||
inner join prc_price_set s on s.price_set_id = c.price_set_id
|
||||
where c.customer_id = #{customerId}
|
||||
and s.status = 'E'
|
||||
and c.pr_begindate <= #{bizDate}
|
||||
and ifnull(s.status, 'D') <> 'S'
|
||||
and (c.pr_begindate is null or c.pr_begindate <= #{bizDate})
|
||||
and ifnull(c.pr_enddate, '9999-12-31') >= #{bizDate}
|
||||
order by c.bind_priority asc, c.pr_begindate desc, c.customer_priceset_id asc
|
||||
</select>
|
||||
@@ -110,8 +110,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
from prc_customer_price_set c
|
||||
inner join prc_price_set s on s.price_set_id = c.price_set_id
|
||||
where c.customer_code = #{customerCode}
|
||||
and s.status = 'E'
|
||||
and c.pr_begindate <= #{bizDate}
|
||||
and ifnull(s.status, 'D') <> 'S'
|
||||
and (c.pr_begindate is null or c.pr_begindate <= #{bizDate})
|
||||
and ifnull(c.pr_enddate, '9999-12-31') >= #{bizDate}
|
||||
order by c.bind_priority asc, c.pr_begindate desc, c.customer_priceset_id asc
|
||||
</select>
|
||||
|
||||
@@ -7,6 +7,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<resultMap type="PrcPriceSetDetail" id="PrcPriceSetDetailResult">
|
||||
<id property="psdId" column="psd_id" />
|
||||
<result property="priceSetId" column="price_set_id" />
|
||||
<result property="psvId" column="psv_id" />
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="productName" column="product_name" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
@@ -17,21 +18,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="zdId" column="zd_id" />
|
||||
<result property="wsdId" column="wsd_id" />
|
||||
<result property="weightLabel" column="weight_label" />
|
||||
<result property="weightMin" column="weight_min" />
|
||||
<result property="weightMax" column="weight_max" />
|
||||
<result property="productCountryId" column="product_country_id" />
|
||||
<result property="fvValue" column="fv_value" />
|
||||
<result property="fvRegistrationFee" column="fv_registration_fee" />
|
||||
<result property="discountValue" column="discount_value" />
|
||||
<result property="roundMode" column="round_mode" />
|
||||
<result property="prId" column="pr_id" />
|
||||
<result property="effectiveDate" column="effective_date" />
|
||||
<result property="expireDate" column="expire_date" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPrcPriceSetDetailVo">
|
||||
select psd_id, price_set_id, product_code, product_name, country_code, country_name,
|
||||
select psd_id, price_set_id, psv_id, product_code, product_name, country_code, country_name,
|
||||
zone_id, zone_code, zone_name,
|
||||
zd_id, wsd_id, weight_label, product_country_id, fv_value, fv_registration_fee,
|
||||
discount_value, round_mode, pr_id, create_time, update_time
|
||||
zd_id, wsd_id, weight_label, weight_min, weight_max, product_country_id, fv_value, fv_registration_fee,
|
||||
discount_value, round_mode, pr_id, effective_date, expire_date, create_time, update_time
|
||||
from prc_price_set_detail
|
||||
</sql>
|
||||
|
||||
@@ -46,6 +51,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="priceSetId != null">
|
||||
AND price_set_id = #{priceSetId}
|
||||
</if>
|
||||
<if test="psvId != null">
|
||||
AND psv_id = #{psvId}
|
||||
</if>
|
||||
<if test="productCode != null and productCode != ''">
|
||||
AND product_code = #{productCode}
|
||||
</if>
|
||||
@@ -55,6 +63,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="zoneCode != null and zoneCode != ''">
|
||||
AND zone_code = #{zoneCode}
|
||||
</if>
|
||||
<if test="productKeyword != null and productKeyword != ''">
|
||||
AND (
|
||||
product_code like concat('%', #{productKeyword}, '%')
|
||||
or product_name like concat('%', #{productKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="countryKeyword != null and countryKeyword != ''">
|
||||
AND (
|
||||
country_code like concat('%', #{countryKeyword}, '%')
|
||||
or country_name like concat('%', #{countryKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="zoneKeyword != null and zoneKeyword != ''">
|
||||
AND (
|
||||
zone_code like concat('%', #{zoneKeyword}, '%')
|
||||
or zone_name like concat('%', #{zoneKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
product_code like concat('%', #{keyword}, '%')
|
||||
or product_name like concat('%', #{keyword}, '%')
|
||||
or country_code like concat('%', #{keyword}, '%')
|
||||
or country_name like concat('%', #{keyword}, '%')
|
||||
or zone_code like concat('%', #{keyword}, '%')
|
||||
or zone_name like concat('%', #{keyword}, '%')
|
||||
or weight_label like concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
order by psd_id asc
|
||||
</select>
|
||||
@@ -67,9 +104,90 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
select count(1) from prc_price_set_detail where price_set_id = #{priceSetId}
|
||||
</select>
|
||||
|
||||
<select id="countByPsvId" parameterType="Long" resultType="int">
|
||||
select count(1) from prc_price_set_detail where psv_id = #{psvId}
|
||||
</select>
|
||||
|
||||
<!-- FIXED: join active products + country by product_id only (avoid OR-join row explosion) -->
|
||||
<insert id="insertFixedDetailsFromLibrary">
|
||||
insert ignore into prc_price_set_detail (
|
||||
price_set_id, psv_id, product_code, product_name,
|
||||
country_code, country_name, zone_id, zone_code, zone_name,
|
||||
weight_label, weight_min, weight_max, product_country_id,
|
||||
fv_value, fv_registration_fee, round_mode,
|
||||
effective_date, expire_date, create_time
|
||||
)
|
||||
select
|
||||
#{priceSetId},
|
||||
#{psvId},
|
||||
p.product_code,
|
||||
p.product_name,
|
||||
c.country_code,
|
||||
c.country_name,
|
||||
c.zone_id,
|
||||
c.zone_code,
|
||||
c.zone_name,
|
||||
case
|
||||
when c.weight_min is null and c.weight_max is null then null
|
||||
else concat(
|
||||
ifnull(trim(trailing '0' from trim(trailing '.' from cast(c.weight_min as char))), ''),
|
||||
'<W≤',
|
||||
ifnull(trim(trailing '0' from trim(trailing '.' from cast(c.weight_max as char))), '')
|
||||
)
|
||||
end,
|
||||
c.weight_min,
|
||||
c.weight_max,
|
||||
c.detail_id,
|
||||
c.freight_price,
|
||||
c.registration_fee,
|
||||
'HALF_UP',
|
||||
c.effective_date,
|
||||
c.expire_date,
|
||||
sysdate()
|
||||
from bms_product p
|
||||
inner join bms_product_country c on c.product_id = p.product_id
|
||||
where p.status = '1'
|
||||
and p.product_id = (
|
||||
select max(p2.product_id) from bms_product p2
|
||||
where p2.product_code = p.product_code and p2.status = '1'
|
||||
)
|
||||
and ifnull(c.status, '1') <> '2'
|
||||
<choose>
|
||||
<when test="openOnly">
|
||||
and (c.expire_date is null or date(c.expire_date) > curdate())
|
||||
</when>
|
||||
<otherwise>
|
||||
and date(c.effective_date) <= date(#{asOf})
|
||||
and (c.expire_date is null or date(c.expire_date) > date(#{asOf}))
|
||||
</otherwise>
|
||||
</choose>
|
||||
order by p.product_code asc, c.country_name asc, c.zone_code asc, c.weight_min asc, c.detail_id asc
|
||||
</insert>
|
||||
|
||||
<insert id="insertDiscountDetailsFromLibrary">
|
||||
insert ignore into prc_price_set_detail (
|
||||
price_set_id, psv_id, product_code, product_name, round_mode, create_time
|
||||
)
|
||||
select
|
||||
#{priceSetId},
|
||||
#{psvId},
|
||||
p.product_code,
|
||||
p.product_name,
|
||||
'HALF_UP',
|
||||
sysdate()
|
||||
from bms_product p
|
||||
where p.status = '1'
|
||||
and p.product_id = (
|
||||
select max(p2.product_id) from bms_product p2
|
||||
where p2.product_code = p.product_code and p2.status = '1'
|
||||
)
|
||||
order by p.product_code asc
|
||||
</insert>
|
||||
|
||||
<insert id="insertPrcPriceSetDetail" parameterType="PrcPriceSetDetail" useGeneratedKeys="true" keyProperty="psdId">
|
||||
insert into prc_price_set_detail (
|
||||
<if test="priceSetId != null">price_set_id,</if>
|
||||
<if test="psvId != null">psv_id,</if>
|
||||
<if test="productCode != null and productCode != ''">product_code,</if>
|
||||
<if test="productName != null">product_name,</if>
|
||||
<if test="countryCode != null">country_code,</if>
|
||||
@@ -80,15 +198,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="zdId != null">zd_id,</if>
|
||||
<if test="wsdId != null">wsd_id,</if>
|
||||
<if test="weightLabel != null">weight_label,</if>
|
||||
<if test="weightMin != null">weight_min,</if>
|
||||
<if test="weightMax != null">weight_max,</if>
|
||||
<if test="productCountryId != null">product_country_id,</if>
|
||||
<if test="fvValue != null">fv_value,</if>
|
||||
<if test="fvRegistrationFee != null">fv_registration_fee,</if>
|
||||
<if test="discountValue != null">discount_value,</if>
|
||||
<if test="roundMode != null and roundMode != ''">round_mode,</if>
|
||||
<if test="prId != null">pr_id,</if>
|
||||
<if test="effectiveDate != null">effective_date,</if>
|
||||
expire_date,
|
||||
create_time
|
||||
) values (
|
||||
<if test="priceSetId != null">#{priceSetId},</if>
|
||||
<if test="psvId != null">#{psvId},</if>
|
||||
<if test="productCode != null and productCode != ''">#{productCode},</if>
|
||||
<if test="productName != null">#{productName},</if>
|
||||
<if test="countryCode != null">#{countryCode},</if>
|
||||
@@ -99,12 +222,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="zdId != null">#{zdId},</if>
|
||||
<if test="wsdId != null">#{wsdId},</if>
|
||||
<if test="weightLabel != null">#{weightLabel},</if>
|
||||
<if test="weightMin != null">#{weightMin},</if>
|
||||
<if test="weightMax != null">#{weightMax},</if>
|
||||
<if test="productCountryId != null">#{productCountryId},</if>
|
||||
<if test="fvValue != null">#{fvValue},</if>
|
||||
<if test="fvRegistrationFee != null">#{fvRegistrationFee},</if>
|
||||
<if test="discountValue != null">#{discountValue},</if>
|
||||
<if test="roundMode != null and roundMode != ''">#{roundMode},</if>
|
||||
<if test="prId != null">#{prId},</if>
|
||||
<if test="effectiveDate != null">#{effectiveDate},</if>
|
||||
#{expireDate},
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
@@ -112,6 +239,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<update id="updatePrcPriceSetDetail" parameterType="PrcPriceSetDetail">
|
||||
update prc_price_set_detail
|
||||
<set>
|
||||
<if test="psvId != null">psv_id = #{psvId},</if>
|
||||
<if test="productCode != null and productCode != ''">product_code = #{productCode},</if>
|
||||
<if test="productName != null">product_name = #{productName},</if>
|
||||
country_code = #{countryCode},
|
||||
@@ -122,12 +250,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
zd_id = #{zdId},
|
||||
wsd_id = #{wsdId},
|
||||
weight_label = #{weightLabel},
|
||||
weight_min = #{weightMin},
|
||||
weight_max = #{weightMax},
|
||||
product_country_id = #{productCountryId},
|
||||
fv_value = #{fvValue},
|
||||
fv_registration_fee = #{fvRegistrationFee},
|
||||
discount_value = #{discountValue},
|
||||
<if test="roundMode != null and roundMode != ''">round_mode = #{roundMode},</if>
|
||||
pr_id = #{prId},
|
||||
effective_date = #{effectiveDate},
|
||||
expire_date = #{expireDate},
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where psd_id = #{psdId}
|
||||
@@ -144,9 +276,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
delete from prc_price_set_detail where price_set_id = #{priceSetId}
|
||||
</delete>
|
||||
|
||||
<delete id="deletePrcPriceSetDetailByPsvId" parameterType="Long">
|
||||
delete from prc_price_set_detail where psv_id = #{psvId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteOrphanDetailsByPriceSetId" parameterType="Long">
|
||||
delete from prc_price_set_detail where price_set_id = #{priceSetId} and psv_id is null
|
||||
</delete>
|
||||
|
||||
<select id="selectFixedMatch" resultMap="PrcPriceSetDetailResult">
|
||||
<include refid="selectPrcPriceSetDetailVo"/>
|
||||
where price_set_id = #{priceSetId}
|
||||
<if test="psvId != null">
|
||||
and psv_id = #{psvId}
|
||||
</if>
|
||||
and product_code = #{productCode}
|
||||
and (
|
||||
<if test="productCountryId != null">
|
||||
@@ -184,9 +327,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<select id="selectDiscountMatch" resultMap="PrcPriceSetDetailResult">
|
||||
<include refid="selectPrcPriceSetDetailVo"/>
|
||||
where price_set_id = #{priceSetId}
|
||||
<if test="psvId != null">
|
||||
and psv_id = #{psvId}
|
||||
</if>
|
||||
and product_code = #{productCode}
|
||||
order by psd_id asc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="copyDetailsFromVersion">
|
||||
insert into prc_price_set_detail (
|
||||
price_set_id, psv_id, product_code, product_name, country_code, country_name,
|
||||
zone_id, zone_code, zone_name, zd_id, wsd_id, weight_label, weight_min, weight_max,
|
||||
product_country_id, fv_value, fv_registration_fee, discount_value, round_mode,
|
||||
pr_id, effective_date, expire_date, create_time
|
||||
)
|
||||
select
|
||||
price_set_id, #{newPsvId}, product_code, product_name, country_code, country_name,
|
||||
zone_id, zone_code, zone_name, zd_id, wsd_id, weight_label, weight_min, weight_max,
|
||||
product_country_id, fv_value, fv_registration_fee, discount_value, round_mode,
|
||||
pr_id, effective_date, expire_date, sysdate()
|
||||
from prc_price_set_detail
|
||||
where psv_id = #{srcPsvId}
|
||||
order by psd_id asc
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -140,6 +140,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
update prc_price_set
|
||||
<set>
|
||||
status = #{status},
|
||||
<if test="effectiveDate != null">effective_date = #{effectiveDate},</if>
|
||||
<if test="updateBy != null and updateBy != ''">updated_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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.ruoyi.price.mapper.PrcPriceSetVersionMapper">
|
||||
|
||||
<resultMap type="PrcPriceSetVersion" id="PrcPriceSetVersionResult">
|
||||
<id property="psvId" column="psv_id" />
|
||||
<result property="priceSetId" column="price_set_id" />
|
||||
<result property="versionNo" column="version_no" />
|
||||
<result property="versionSeq" column="version_seq" />
|
||||
<result property="effectiveTime" column="effective_time" />
|
||||
<result property="expireTime" column="expire_time" />
|
||||
<result property="status" column="status" />
|
||||
<result property="createBy" column="created_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="updated_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPrcPriceSetVersionVo">
|
||||
select psv_id, price_set_id, version_no, version_seq, effective_time, expire_time, status,
|
||||
created_by, create_time, updated_by, update_time
|
||||
from prc_price_set_version
|
||||
</sql>
|
||||
|
||||
<select id="selectPrcPriceSetVersionById" parameterType="Long" resultMap="PrcPriceSetVersionResult">
|
||||
<include refid="selectPrcPriceSetVersionVo"/>
|
||||
where psv_id = #{psvId}
|
||||
</select>
|
||||
|
||||
<select id="selectPrcPriceSetVersionList" parameterType="PrcPriceSetVersion" resultMap="PrcPriceSetVersionResult">
|
||||
<include refid="selectPrcPriceSetVersionVo"/>
|
||||
<where>
|
||||
<if test="priceSetId != null">
|
||||
AND price_set_id = #{priceSetId}
|
||||
</if>
|
||||
<if test="versionNo != null and versionNo != ''">
|
||||
AND version_no = #{versionNo}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by version_seq desc
|
||||
</select>
|
||||
|
||||
<select id="selectMaxVersionSeq" resultType="java.lang.Integer">
|
||||
select max(version_seq) from prc_price_set_version where price_set_id = #{priceSetId}
|
||||
</select>
|
||||
|
||||
<insert id="insertPrcPriceSetVersion" parameterType="PrcPriceSetVersion" useGeneratedKeys="true" keyProperty="psvId">
|
||||
insert into prc_price_set_version (
|
||||
<if test="priceSetId != null">price_set_id,</if>
|
||||
<if test="versionNo != null and versionNo != ''">version_no,</if>
|
||||
<if test="versionSeq != null">version_seq,</if>
|
||||
<if test="effectiveTime != null">effective_time,</if>
|
||||
<if test="expireTime != null">expire_time,</if>
|
||||
<if test="status != null and status != ''">status,</if>
|
||||
<if test="createBy != null and createBy != ''">created_by,</if>
|
||||
create_time
|
||||
) values (
|
||||
<if test="priceSetId != null">#{priceSetId},</if>
|
||||
<if test="versionNo != null and versionNo != ''">#{versionNo},</if>
|
||||
<if test="versionSeq != null">#{versionSeq},</if>
|
||||
<if test="effectiveTime != null">#{effectiveTime},</if>
|
||||
<if test="expireTime != null">#{expireTime},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updatePrcPriceSetVersion" parameterType="PrcPriceSetVersion">
|
||||
update prc_price_set_version
|
||||
<set>
|
||||
<if test="versionNo != null and versionNo != ''">version_no = #{versionNo},</if>
|
||||
<if test="versionSeq != null">version_seq = #{versionSeq},</if>
|
||||
<if test="effectiveTime != null">effective_time = #{effectiveTime},</if>
|
||||
expire_time = #{expireTime},
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
<if test="updateBy != null and updateBy != ''">updated_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where psv_id = #{psvId}
|
||||
</update>
|
||||
|
||||
<delete id="deletePrcPriceSetVersionByIds" parameterType="Long">
|
||||
delete from prc_price_set_version where psv_id in
|
||||
<foreach item="psvId" collection="array" open="(" separator="," close=")">
|
||||
#{psvId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deletePrcPriceSetVersionByPriceSetId" parameterType="Long">
|
||||
delete from prc_price_set_version where price_set_id = #{priceSetId}
|
||||
</delete>
|
||||
|
||||
<select id="selectPriceSetIds" resultType="Long">
|
||||
select distinct price_set_id from prc_price_set_version
|
||||
</select>
|
||||
|
||||
<select id="selectActiveByPriceSetAt" resultMap="PrcPriceSetVersionResult">
|
||||
<include refid="selectPrcPriceSetVersionVo"/>
|
||||
where price_set_id = #{priceSetId}
|
||||
and effective_time is not null
|
||||
and effective_time <= #{asOf}
|
||||
and (expire_time is null or expire_time > #{asOf})
|
||||
order by effective_time desc, version_seq desc, psv_id desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
<?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.ruoyi.price.mapper.PrcPriceSetVersionSurchargeMapper">
|
||||
|
||||
<resultMap type="PrcPriceSetVersionSurcharge" id="PrcPriceSetVersionSurchargeResult">
|
||||
<id property="psvsId" column="psvs_id" />
|
||||
<result property="psvId" column="psv_id" />
|
||||
<result property="scId" column="sc_id" />
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="psdId" column="psd_id" />
|
||||
<result property="scName" column="sc_name" />
|
||||
<result property="feeType" column="fee_type" />
|
||||
<result property="chargeType" column="charge_type" />
|
||||
<result property="chargeValue" column="charge_value" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="roundMode" column="round_mode" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="scNote" column="sc_note" />
|
||||
<result property="createBy" column="created_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="updated_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPrcPriceSetVersionSurchargeVo">
|
||||
select psvs_id, psv_id, sc_id, product_code, psd_id, sc_name, fee_type, charge_type,
|
||||
charge_value, currency, round_mode, country_code, sc_note,
|
||||
created_by, create_time, updated_by, update_time
|
||||
from prc_price_set_version_surcharge
|
||||
</sql>
|
||||
|
||||
<select id="selectPrcPriceSetVersionSurchargeById" parameterType="Long" resultMap="PrcPriceSetVersionSurchargeResult">
|
||||
<include refid="selectPrcPriceSetVersionSurchargeVo"/>
|
||||
where psvs_id = #{psvsId}
|
||||
</select>
|
||||
|
||||
<select id="selectPrcPriceSetVersionSurchargeList" parameterType="PrcPriceSetVersionSurcharge" resultMap="PrcPriceSetVersionSurchargeResult">
|
||||
<include refid="selectPrcPriceSetVersionSurchargeVo"/>
|
||||
<where>
|
||||
<if test="psvId != null">
|
||||
AND psv_id = #{psvId}
|
||||
</if>
|
||||
<if test="scId != null">
|
||||
AND sc_id = #{scId}
|
||||
</if>
|
||||
<if test="productCode != null and productCode != ''">
|
||||
AND product_code = #{productCode}
|
||||
</if>
|
||||
<if test="psdId != null">
|
||||
AND psd_id = #{psdId}
|
||||
</if>
|
||||
</where>
|
||||
order by psvs_id asc
|
||||
</select>
|
||||
|
||||
<insert id="insertPrcPriceSetVersionSurcharge" parameterType="PrcPriceSetVersionSurcharge" useGeneratedKeys="true" keyProperty="psvsId">
|
||||
insert into prc_price_set_version_surcharge (
|
||||
<if test="psvId != null">psv_id,</if>
|
||||
<if test="psdId != null">psd_id,</if>
|
||||
<if test="scId != null">sc_id,</if>
|
||||
<if test="productCode != null">product_code,</if>
|
||||
<if test="scName != null and scName != ''">sc_name,</if>
|
||||
<if test="feeType != null and feeType != ''">fee_type,</if>
|
||||
<if test="chargeType != null and chargeType != ''">charge_type,</if>
|
||||
<if test="chargeValue != null">charge_value,</if>
|
||||
<if test="currency != null and currency != ''">currency,</if>
|
||||
<if test="roundMode != null and roundMode != ''">round_mode,</if>
|
||||
<if test="countryCode != null">country_code,</if>
|
||||
<if test="scNote != null">sc_note,</if>
|
||||
<if test="createBy != null and createBy != ''">created_by,</if>
|
||||
create_time
|
||||
) values (
|
||||
<if test="psvId != null">#{psvId},</if>
|
||||
<if test="psdId != null">#{psdId},</if>
|
||||
<if test="scId != null">#{scId},</if>
|
||||
<if test="productCode != null">#{productCode},</if>
|
||||
<if test="scName != null and scName != ''">#{scName},</if>
|
||||
<if test="feeType != null and feeType != ''">#{feeType},</if>
|
||||
<if test="chargeType != null and chargeType != ''">#{chargeType},</if>
|
||||
<if test="chargeValue != null">#{chargeValue},</if>
|
||||
<if test="currency != null and currency != ''">#{currency},</if>
|
||||
<if test="roundMode != null and roundMode != ''">#{roundMode},</if>
|
||||
<if test="countryCode != null">#{countryCode},</if>
|
||||
<if test="scNote != null">#{scNote},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updatePrcPriceSetVersionSurcharge" parameterType="PrcPriceSetVersionSurcharge">
|
||||
update prc_price_set_version_surcharge
|
||||
<set>
|
||||
<if test="psdId != null">psd_id = #{psdId},</if>
|
||||
<if test="scId != null">sc_id = #{scId},</if>
|
||||
product_code = #{productCode},
|
||||
<if test="scName != null and scName != ''">sc_name = #{scName},</if>
|
||||
<if test="feeType != null and feeType != ''">fee_type = #{feeType},</if>
|
||||
<if test="chargeType != null and chargeType != ''">charge_type = #{chargeType},</if>
|
||||
<if test="chargeValue != null">charge_value = #{chargeValue},</if>
|
||||
<if test="currency != null and currency != ''">currency = #{currency},</if>
|
||||
<if test="roundMode != null and roundMode != ''">round_mode = #{roundMode},</if>
|
||||
country_code = #{countryCode},
|
||||
sc_note = #{scNote},
|
||||
<if test="updateBy != null and updateBy != ''">updated_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where psvs_id = #{psvsId}
|
||||
</update>
|
||||
|
||||
<delete id="deletePrcPriceSetVersionSurchargeByIds" parameterType="Long">
|
||||
delete from prc_price_set_version_surcharge where psvs_id in
|
||||
<foreach item="psvsId" collection="array" open="(" separator="," close=")">
|
||||
#{psvsId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteByPsvId" parameterType="Long">
|
||||
delete from prc_price_set_version_surcharge where psv_id = #{psvId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteByPsdId" parameterType="Long">
|
||||
delete from prc_price_set_version_surcharge where psd_id = #{psdId}
|
||||
</delete>
|
||||
|
||||
<select id="selectByPsdIds" resultMap="PrcPriceSetVersionSurchargeResult">
|
||||
<include refid="selectPrcPriceSetVersionSurchargeVo"/>
|
||||
where psd_id in
|
||||
<foreach item="id" collection="psdIds" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
order by psvs_id asc
|
||||
</select>
|
||||
|
||||
<!-- Join detail x surcharge_rel x surcharge; skip OF fuel; product-level or same detail_id -->
|
||||
<insert id="insertFromLibraryRels">
|
||||
insert into prc_price_set_version_surcharge (
|
||||
psv_id, psd_id, sc_id, product_code, sc_name, fee_type, charge_type,
|
||||
charge_value, currency, round_mode, country_code, sc_note,
|
||||
created_by, create_time
|
||||
)
|
||||
select
|
||||
d.psv_id,
|
||||
d.psd_id,
|
||||
r.sc_id,
|
||||
d.product_code,
|
||||
s.sc_name,
|
||||
ifnull(nullif(s.fee_type, ''), 'SURCHARGE'),
|
||||
ifnull(nullif(s.charge_type, ''), 'FIXED'),
|
||||
s.charge_value,
|
||||
ifnull(nullif(s.currency, ''), 'CNY'),
|
||||
ifnull(nullif(s.round_mode, ''), 'HALF_UP'),
|
||||
s.country_code,
|
||||
s.sc_note,
|
||||
#{createBy},
|
||||
sysdate()
|
||||
from prc_price_set_detail d
|
||||
inner join prc_surcharge_rel r on r.product_code = d.product_code
|
||||
inner join prc_surcharge s on s.sc_id = r.sc_id
|
||||
where d.psv_id = #{psvId}
|
||||
and ifnull(s.sc_name, '') <> ''
|
||||
and s.charge_value is not null
|
||||
and lower(ifnull(s.ss_code, '')) <> 'of'
|
||||
and (r.detail_id is null or r.detail_id = d.product_country_id)
|
||||
and (
|
||||
s.country_code is null or s.country_code = ''
|
||||
or d.country_code is null or d.country_code = ''
|
||||
or upper(s.country_code) = upper(d.country_code)
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="copySurchargesFromVersion">
|
||||
insert into prc_price_set_version_surcharge (
|
||||
psv_id, psd_id, sc_id, product_code, sc_name, fee_type, charge_type,
|
||||
charge_value, currency, round_mode, country_code, sc_note,
|
||||
created_by, create_time
|
||||
)
|
||||
select
|
||||
#{newPsvId},
|
||||
nd.psd_id,
|
||||
sc.sc_id,
|
||||
sc.product_code,
|
||||
sc.sc_name,
|
||||
sc.fee_type,
|
||||
sc.charge_type,
|
||||
sc.charge_value,
|
||||
sc.currency,
|
||||
sc.round_mode,
|
||||
sc.country_code,
|
||||
sc.sc_note,
|
||||
#{createBy},
|
||||
sysdate()
|
||||
from prc_price_set_version_surcharge sc
|
||||
inner join prc_price_set_detail od
|
||||
on od.psd_id = sc.psd_id and od.psv_id = #{srcPsvId}
|
||||
inner join prc_price_set_detail nd
|
||||
on nd.psv_id = #{newPsvId}
|
||||
and nd.product_code = od.product_code
|
||||
and ifnull(nd.country_code, '') = ifnull(od.country_code, '')
|
||||
and ifnull(nd.zone_code, '') = ifnull(od.zone_code, '')
|
||||
and ifnull(nd.weight_label, '') = ifnull(od.weight_label, '')
|
||||
and ifnull(nd.product_country_id, 0) = ifnull(od.product_country_id, 0)
|
||||
and ifnull(nd.discount_value, -1) = ifnull(od.discount_value, -1)
|
||||
where sc.psv_id = #{srcPsvId}
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,34 @@
|
||||
# Price set version tables
|
||||
|
||||
## Wizard (2 steps)
|
||||
|
||||
1. Basic info + view bound customers (read-only)
|
||||
2. Edit product prices (library pull on next)
|
||||
|
||||
- No manual effective_time input.
|
||||
- Draft version: status=0, effective_time null until activate.
|
||||
- Activate: sets effective_time=now; previous version expires. Does not change package header status.
|
||||
- Copy as new version: `POST /price/set/version/copy/{psvId}` inserts new draft `prc_price_set_version` and copies `prc_price_set_detail` (+ surcharge snapshots) from source psv_id.
|
||||
- Audit submit/approve/reject removed from UI save path.
|
||||
|
||||
## Graph
|
||||
|
||||
```
|
||||
prc_price_set
|
||||
-> prc_price_set_version draft / active / expired
|
||||
-> prc_price_set_detail product prices (psv_id)
|
||||
-> prc_price_set_version_surcharge
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Version: 0 draft, 1 active, 2 expired. Active when effective_time <= now and (expire_time null or > now).
|
||||
|
||||
## SQL
|
||||
|
||||
1. `price_set_version.sql`
|
||||
2. `price_set_version_eff_nullable.sql` (allow null `prc_price_set.effective_date` and `prc_price_set_version.effective_time` for drafts)
|
||||
|
||||
## Billing
|
||||
|
||||
Match by price_set_id; multi-version quote should filter active psv_id by bill time.
|
||||
@@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS prc_price_set (
|
||||
default_discount DECIMAL(5,2) DEFAULT NULL COMMENT '默认折扣百分数 1-99,仅 DISCOUNT',
|
||||
default_type CHAR(1) DEFAULT NULL COMMENT '默认套餐 Y=默认',
|
||||
calculate_sign CHAR(1) DEFAULT NULL COMMENT '试算标记 Y=外部试算套餐',
|
||||
effective_date DATE NOT NULL COMMENT '生效日期',
|
||||
effective_date DATE DEFAULT NULL COMMENT 'effective date (null until activate)',
|
||||
expire_date DATE DEFAULT NULL COMMENT '失效日期(空=长期)',
|
||||
status CHAR(1) NOT NULL DEFAULT 'D' COMMENT 'D草稿 A审核中 E生效 R驳回 S停用',
|
||||
tms_id BIGINT(20) DEFAULT NULL COMMENT '多租户系统ID',
|
||||
@@ -46,17 +46,23 @@ CREATE TABLE IF NOT EXISTS prc_price_set_detail (
|
||||
zd_id BIGINT(20) DEFAULT NULL COMMENT 'legacy zone division id',
|
||||
wsd_id BIGINT(20) DEFAULT NULL COMMENT '重量段明细id',
|
||||
weight_label VARCHAR(50) DEFAULT NULL COMMENT '重量段展示',
|
||||
weight_min DECIMAL(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)',
|
||||
weight_max DECIMAL(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)',
|
||||
product_country_id BIGINT(20) DEFAULT NULL COMMENT '关联 bms_product_country.detail_id',
|
||||
fv_value DECIMAL(10,4) DEFAULT NULL COMMENT '运费单价 RMB/KG',
|
||||
fv_registration_fee DECIMAL(10,2) DEFAULT NULL COMMENT '挂号费 RMB/票',
|
||||
discount_value DECIMAL(5,2) DEFAULT NULL COMMENT '明细折扣百分数,DISCOUNT 可覆盖默认',
|
||||
round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||
pr_id INT(11) DEFAULT NULL COMMENT '关联价格规则id',
|
||||
effective_date DATE DEFAULT NULL COMMENT '生效日期',
|
||||
expire_date DATE DEFAULT NULL COMMENT '失效日期(空=长期)',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psd_id),
|
||||
INDEX idx_psd_set (price_set_id),
|
||||
INDEX idx_psd_product (price_set_id, product_code, country_code),
|
||||
INDEX idx_psd_weight_match (price_set_id, product_code, country_code, weight_min, weight_max),
|
||||
INDEX idx_psd_eff (price_set_id, effective_date, expire_date),
|
||||
INDEX idx_psd_product_country (product_country_id),
|
||||
CONSTRAINT fk_psd_set FOREIGN KEY (price_set_id) REFERENCES prc_price_set (price_set_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐明细-固定价/折扣范围';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Prevent duplicate FIXED details within one version (same product_country snapshot row)
|
||||
-- MySQL allows multiple NULLs in UNIQUE, so DISCOUNT rows (product_country_id null) are OK.
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- Clean exact duplicates before adding unique key (keep min psd_id)
|
||||
DELETE d1 FROM prc_price_set_detail d1
|
||||
INNER JOIN prc_price_set_detail d2
|
||||
ON d1.psv_id = d2.psv_id
|
||||
AND d1.product_country_id = d2.product_country_id
|
||||
AND d1.psd_id > d2.psd_id
|
||||
WHERE d1.psv_id IS NOT NULL
|
||||
AND d1.product_country_id IS NOT NULL;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'uk_psd_psv_product_country'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD UNIQUE KEY uk_psd_psv_product_country (psv_id, product_country_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- prc_price_set_detail: weight_min/weight_max + effective_date/expire_date
|
||||
-- same types as bms_product_country
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='weight_min');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN weight_min DECIMAL(10,3) DEFAULT NULL COMMENT ''重量段最小值KG(含)'' AFTER weight_label', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='weight_max');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN weight_max DECIMAL(10,3) DEFAULT NULL COMMENT ''重量段最大值KG(含)'' AFTER weight_min', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='effective_date');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN effective_date DATE DEFAULT NULL COMMENT ''生效日期'' AFTER pr_id', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='expire_date');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN expire_date DATE DEFAULT NULL COMMENT ''失效日期(空=长期)'' AFTER effective_date', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND INDEX_NAME='idx_psd_weight_match');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_weight_match (price_set_id, product_code, country_code, weight_min, weight_max)', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND INDEX_NAME='idx_psd_eff');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_eff (price_set_id, effective_date, expire_date)', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 回填: 从 bms_product_country 带入重量段与有效期
|
||||
UPDATE prc_price_set_detail d
|
||||
INNER JOIN bms_product_country c ON c.detail_id = d.product_country_id
|
||||
SET
|
||||
d.weight_min = IFNULL(d.weight_min, c.weight_min),
|
||||
d.weight_max = IFNULL(d.weight_max, c.weight_max),
|
||||
d.effective_date = IFNULL(d.effective_date, c.effective_date),
|
||||
d.expire_date = IFNULL(d.expire_date, c.expire_date)
|
||||
WHERE d.product_country_id IS NOT NULL;
|
||||
@@ -0,0 +1,152 @@
|
||||
-- 价格套餐版本中间表 + 版本附加费
|
||||
-- 关系:
|
||||
-- prc_price_set
|
||||
-- -> prc_price_set_version (步骤2: 版本号自动生成, 生效时间)
|
||||
-- -> prc_price_set_detail (步骤3: 产品价格, 通过 psv_id)
|
||||
-- -> prc_price_set_version_surcharge (psd_id 关联明细)
|
||||
-- 可重复执行
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS prc_price_set_version (
|
||||
psv_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '套餐版本id',
|
||||
price_set_id BIGINT(20) NOT NULL COMMENT '价格套餐id',
|
||||
version_no VARCHAR(20) NOT NULL COMMENT '版本号 V1/V2,自动+1',
|
||||
version_seq INT(11) NOT NULL COMMENT '版本序号,用于自动+1',
|
||||
effective_time DATETIME DEFAULT NULL COMMENT 'version start time (null until activate)',
|
||||
expire_time DATETIME DEFAULT NULL COMMENT '失效时间(空=长期;下一版本启动时写入该启动时间)',
|
||||
status CHAR(1) NOT NULL DEFAULT '0' COMMENT '0草稿 1生效 2失效',
|
||||
created_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
updated_by VARCHAR(64) DEFAULT '' COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psv_id),
|
||||
UNIQUE KEY uk_psv_set_no (price_set_id, version_no),
|
||||
UNIQUE KEY uk_psv_set_seq (price_set_id, version_seq),
|
||||
INDEX idx_psv_set_eff (price_set_id, effective_time),
|
||||
INDEX idx_psv_status (status),
|
||||
CONSTRAINT fk_psv_set FOREIGN KEY (price_set_id) REFERENCES prc_price_set (price_set_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐版本';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS prc_price_set_version_surcharge (
|
||||
psvs_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '版本附加费id',
|
||||
psv_id BIGINT(20) NOT NULL COMMENT '套餐版本id',
|
||||
psd_id BIGINT(20) NOT NULL COMMENT '套餐明细id',
|
||||
sc_id BIGINT(20) DEFAULT NULL COMMENT '来源附加费配置id',
|
||||
product_code VARCHAR(20) DEFAULT NULL COMMENT '产品代码(与明细一致)',
|
||||
sc_name VARCHAR(100) NOT NULL COMMENT '附加费名称快照',
|
||||
fee_type VARCHAR(30) NOT NULL DEFAULT 'SURCHARGE' COMMENT '费用类型(字典bms_fee_type)',
|
||||
charge_type VARCHAR(20) NOT NULL COMMENT '计费方式 FIXED固定金额/WEIGHT按重量/RATE按比例',
|
||||
charge_value DECIMAL(18,6) NOT NULL COMMENT '计费数值快照',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种',
|
||||
round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||
country_code CHAR(2) DEFAULT NULL COMMENT '适用国家ISO2(空=全部)',
|
||||
sc_note VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
created_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
updated_by VARCHAR(64) DEFAULT '' COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psvs_id),
|
||||
INDEX idx_psvs_psv (psv_id),
|
||||
INDEX idx_psvs_psd (psd_id),
|
||||
INDEX idx_psvs_product (psv_id, product_code),
|
||||
INDEX idx_psvs_sc (sc_id),
|
||||
CONSTRAINT fk_psvs_psv FOREIGN KEY (psv_id) REFERENCES prc_price_set_version (psv_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_psvs_psd FOREIGN KEY (psd_id) REFERENCES prc_price_set_detail (psd_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐版本附加费快照';
|
||||
|
||||
|
||||
-- expire_time: previous version expire = next version effective_time (start)
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'expire_time'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_version ADD COLUMN expire_time DATETIME DEFAULT NULL COMMENT ''失效时间(空=长期;下一版本启动时写入该启动时间)'' AFTER effective_time',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- backfill: expire_time = next version start time (by version_seq)
|
||||
UPDATE prc_price_set_version cur
|
||||
INNER JOIN prc_price_set_version nxt
|
||||
ON nxt.price_set_id = cur.price_set_id
|
||||
AND nxt.version_seq = cur.version_seq + 1
|
||||
SET cur.expire_time = nxt.effective_time;
|
||||
|
||||
-- prc_price_set_detail.psv_id
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'psv_id'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD COLUMN psv_id BIGINT(20) DEFAULT NULL COMMENT ''套餐版本id'' AFTER price_set_id',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_psv'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_psv (psv_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND CONSTRAINT_NAME = 'fk_psd_psv'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD CONSTRAINT fk_psd_psv FOREIGN KEY (psv_id) REFERENCES prc_price_set_version (psv_id) ON DELETE CASCADE',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 已建表补关联: 附加费.psd_id -> 套餐明细
|
||||
DELETE FROM prc_price_set_version_surcharge
|
||||
WHERE psd_id IS NULL
|
||||
OR NOT EXISTS (
|
||||
SELECT 1 FROM prc_price_set_detail d WHERE d.psd_id = prc_price_set_version_surcharge.psd_id
|
||||
);
|
||||
|
||||
SET @sql := CONCAT(
|
||||
'ALTER TABLE prc_price_set_version_surcharge MODIFY COLUMN psd_id BIGINT(20) NOT NULL COMMENT ''套餐明细id'''
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version_surcharge' AND CONSTRAINT_NAME = 'fk_psvs_psd'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_version_surcharge ADD CONSTRAINT fk_psvs_psd FOREIGN KEY (psd_id) REFERENCES prc_price_set_detail (psd_id) ON DELETE CASCADE',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 回填: 每个已有套餐生成一条 V1
|
||||
INSERT INTO prc_price_set_version (
|
||||
price_set_id, version_no, version_seq, effective_time, status, created_by, create_time
|
||||
)
|
||||
SELECT
|
||||
s.price_set_id,
|
||||
'V1',
|
||||
1,
|
||||
COALESCE(CAST(s.effective_date AS DATETIME), s.create_time, NOW()),
|
||||
CASE
|
||||
WHEN s.status = 'S' THEN '2'
|
||||
WHEN s.expire_date IS NOT NULL AND s.expire_date <= CURDATE() THEN '2'
|
||||
WHEN s.status = 'E' AND (s.effective_date IS NULL OR s.effective_date <= CURDATE()) THEN '1'
|
||||
ELSE '0'
|
||||
END,
|
||||
IFNULL(s.created_by, ''),
|
||||
NOW()
|
||||
FROM prc_price_set s
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM prc_price_set_version v WHERE v.price_set_id = s.price_set_id
|
||||
);
|
||||
|
||||
-- 将已有明细绑定到同一套餐的 V1
|
||||
UPDATE prc_price_set_detail d
|
||||
INNER JOIN prc_price_set_version v
|
||||
ON v.price_set_id = d.price_set_id AND v.version_seq = 1
|
||||
SET d.psv_id = v.psv_id
|
||||
WHERE d.psv_id IS NULL;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- Draft package / version: effective dates nullable until activate
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- prc_price_set.effective_date
|
||||
SET @nullable := (
|
||||
SELECT IS_NULLABLE FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set' AND COLUMN_NAME = 'effective_date'
|
||||
);
|
||||
SET @sql := IF(@nullable = 'NO',
|
||||
'ALTER TABLE prc_price_set MODIFY COLUMN effective_date DATE DEFAULT NULL COMMENT ''effective date (null until version activate)''',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- prc_price_set_version.effective_time
|
||||
SET @nullable := (
|
||||
SELECT IS_NULLABLE FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'effective_time'
|
||||
);
|
||||
SET @sql := IF(@nullable = 'NO',
|
||||
'ALTER TABLE prc_price_set_version MODIFY COLUMN effective_time DATETIME DEFAULT NULL COMMENT ''version start time (null until activate)''',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
Reference in New Issue
Block a user