This commit is contained in:
luojiaquan
2026-09-17 10:55:46 +08:00
57 changed files with 4865 additions and 737 deletions
@@ -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;
}
@@ -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;
@@ -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,
@@ -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);
}
@@ -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 &lt;= asOf and (expire_time null or &gt; asOf).
* Prefer latest effective_time.
*/
public PrcPriceSetVersion selectActiveByPriceSetAt(
@Param("priceSetId") Long priceSetId,
@Param("asOf") Date asOf);
}
@@ -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);
}
@@ -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
*/
@@ -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 };
}
}
@@ -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);
}
}