套餐启用增加生效时间

This commit is contained in:
wuweihua
2026-09-17 14:42:48 +08:00
parent 51c8013cd9
commit f3ef2df949
25 changed files with 684 additions and 287 deletions
@@ -24,7 +24,7 @@ public class PrcPriceSetVersion extends BaseEntity
/** 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 */
/** 0 draft / 3 pending / 1 active / 2 expired */
private String status;
private List<PrcPriceSetDetail> details;
@@ -18,6 +18,8 @@ public class PrcQuote extends BaseEntity
private String quoteId;
private String quoteKey;
private Long customerId;
/** bms_customer.customer_code */
private String customerCode;
private String productCode;
private String productName;
private String countryCode;
@@ -14,6 +14,8 @@ public class TmsPriceQuoteRequest implements Serializable
private Long customerId;
/** Required. Maps to bms_customer.customer_code; used in quote unique key. */
@NotBlank(message = "customerCode is required")
private String customerCode;
@NotBlank(message = "productCode is required")
@@ -1,9 +1,13 @@
package com.ruoyi.price.search;
import java.util.UUID;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
@@ -14,7 +18,11 @@ public final class EsBizLogSupport
public static final String STATUS_SUCCESS = "SUCCESS";
public static final String STATUS_FAIL = "FAIL";
/** Request attribute: fail already written to ES in this request. */
public static final String ATTR_FAIL_LOGGED = "bms.es.fail.logged";
private static final int ERROR_MAX = 2000;
private static final int RAW_BODY_MAX = 8000;
private EsBizLogSupport()
{
@@ -26,6 +34,10 @@ public final class EsBizLogSupport
{
return null;
}
if (request instanceof CharSequence)
{
return cut(String.valueOf(request), RAW_BODY_MAX);
}
JSONObject obj = JSON.parseObject(JSON.toJSONString(request));
if (obj == null)
{
@@ -64,15 +76,20 @@ public final class EsBizLogSupport
}
public static String cut(String msg)
{
return cut(msg, ERROR_MAX);
}
public static String cut(String msg, int max)
{
if (msg == null)
{
return null;
}
String text = msg.trim();
if (text.length() > ERROR_MAX)
if (text.length() > max)
{
return text.substring(0, ERROR_MAX);
return text.substring(0, max);
}
return text;
}
@@ -104,6 +121,39 @@ public final class EsBizLogSupport
return "FAIL-" + Md5Utils.hash(raw);
}
/**
* Common entry for quote/bill API failures (validation / JSON parse / business / unexpected).
* Dedups with {@link #ATTR_FAIL_LOGGED} so controller/service/advice can all call safely.
*/
public static void saveApiFail(IBillSearchStore store, HttpServletRequest httpRequest, Object boundTarget,
Throwable e)
{
saveApiFail(store, httpRequest, boundTarget, errorText(e));
}
public static void saveApiFail(IBillSearchStore store, HttpServletRequest httpRequest, Object boundTarget,
String errorMessage)
{
if (store == null || isFailLogged(httpRequest))
{
return;
}
String rawBody = readRawBody(httpRequest);
String logType = resolveLogType(httpRequest, boundTarget);
if (StringUtils.isEmpty(logType))
{
return;
}
Object payload = boundTarget != null ? boundTarget : rawBody;
String bizId = resolveFailBizId(logType, boundTarget, rawBody);
// saveFail also marks ATTR_FAIL_LOGGED
saveFail(store, logType, bizId, payload, errorMessage);
if (!isFailLogged(httpRequest))
{
markFailLogged(httpRequest);
}
}
public static void saveFail(IBillSearchStore store, String logType, String bizId, Object request, Throwable e)
{
saveFail(store, logType, bizId, request, errorText(e));
@@ -115,8 +165,157 @@ public final class EsBizLogSupport
{
return;
}
HttpServletRequest httpRequest = currentRequest();
if (isFailLogged(httpRequest))
{
return;
}
String msg = cut(errorMessage);
store.save(logType, bizId, requestJson(request), failResponseJson(msg), STATUS_FAIL, msg);
markFailLogged(httpRequest);
}
public static String resolveLogType(HttpServletRequest httpRequest, Object boundTarget)
{
if (boundTarget instanceof TmsPriceBillRequest)
{
return EsLogType.BILL;
}
if (boundTarget instanceof TmsPriceQuoteRequest)
{
return EsLogType.QUOTE;
}
if (httpRequest == null)
{
return null;
}
String uri = httpRequest.getRequestURI();
if (uri == null)
{
return null;
}
if (uri.endsWith("/bill"))
{
return EsLogType.BILL;
}
if (uri.endsWith("/quote"))
{
return EsLogType.QUOTE;
}
return null;
}
public static String resolveFailBizId(String logType, Object boundTarget, String rawBody)
{
if (EsLogType.BILL.equals(logType))
{
if (boundTarget instanceof TmsPriceBillRequest)
{
return billFailBizId((TmsPriceBillRequest) boundTarget);
}
String orderNo = extractJsonField(rawBody, "orderNo");
if (StringUtils.isNotEmpty(orderNo))
{
return orderNo.trim();
}
return "FAIL-" + UUID.randomUUID();
}
if (EsLogType.QUOTE.equals(logType))
{
if (boundTarget instanceof TmsPriceQuoteRequest)
{
return quoteFailBizId((TmsPriceQuoteRequest) boundTarget);
}
String seed = StringUtils.isNotEmpty(rawBody) ? rawBody : UUID.randomUUID().toString();
return "FAIL-" + Md5Utils.hash(seed);
}
return "FAIL-" + UUID.randomUUID();
}
public static boolean isPriceSyncWriteApi(HttpServletRequest httpRequest)
{
if (httpRequest == null)
{
return false;
}
if (!"POST".equalsIgnoreCase(httpRequest.getMethod()))
{
return false;
}
String uri = httpRequest.getRequestURI();
if (uri == null)
{
return false;
}
return uri.endsWith("/quote") || uri.endsWith("/bill");
}
public static String readRawBody(HttpServletRequest httpRequest)
{
if (httpRequest == null)
{
return null;
}
try
{
String body = HttpHelper.getBodyString(httpRequest);
return cut(body, RAW_BODY_MAX);
}
catch (Exception ignored)
{
return null;
}
}
public static boolean isFailLogged(HttpServletRequest httpRequest)
{
HttpServletRequest req = httpRequest != null ? httpRequest : currentRequest();
return req != null && Boolean.TRUE.equals(req.getAttribute(ATTR_FAIL_LOGGED));
}
public static void markFailLogged(HttpServletRequest httpRequest)
{
HttpServletRequest req = httpRequest != null ? httpRequest : currentRequest();
if (req != null)
{
req.setAttribute(ATTR_FAIL_LOGGED, Boolean.TRUE);
}
}
public static HttpServletRequest currentRequest()
{
try
{
ServletRequestAttributes attrs =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return attrs == null ? null : attrs.getRequest();
}
catch (Exception ignored)
{
return null;
}
}
private static String extractJsonField(String rawBody, String field)
{
if (StringUtils.isEmpty(rawBody) || StringUtils.isEmpty(field))
{
return null;
}
try
{
JSONObject obj = JSON.parseObject(rawBody);
if (obj == null)
{
return null;
}
Object value = obj.get(field);
return value == null ? null : String.valueOf(value);
}
catch (Exception ignored)
{
return null;
}
}
private static String nvl(Object value)
@@ -1,5 +1,6 @@
package com.ruoyi.price.service;
import java.util.Date;
import java.util.List;
import com.ruoyi.price.domain.PrcCustomerPriceSet;
import com.ruoyi.price.domain.PrcPriceSet;
@@ -32,8 +33,11 @@ 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);
/**
* Activate a draft version with user-specified effectiveTime.
* effectiveTime &lt;= now -&gt; EFFECT; future -&gt; PENDING(3). Then refresh 0/3/1/2.
*/
public int activateVersion(Long psvId, Date effectiveTime, String updateBy);
/**
* Copy source version into a new draft version under the same package:
@@ -65,7 +69,7 @@ public interface IPrcPriceSetService
public int deleteCustomerByIds(Long[] customerPricesetIds);
/** chain expire_time = next version effective_time, then refresh 0/1/2 */
/** chain expire_time = next version effective_time, then refresh 0/3/1/2 */
public int refreshAllVersionStatus();
/** submit async save, return taskId immediately */
@@ -128,8 +128,18 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
}
bill.setBillKey(billKey);
bill.setOrderNo(orderNo);
bill.setCustomerId(request.getCustomerId() == null ? Long.valueOf(0L) : request.getCustomerId());
bill.setCustomerCode(request.getCustomerCode());
Long customerId = request.getCustomerId();
if (customerId == null && result != null)
{
customerId = result.getCustomerId();
}
bill.setCustomerId(customerId == null ? Long.valueOf(0L) : customerId);
String customerCode = request.getCustomerCode();
if (StringUtils.isEmpty(customerCode) && result != null)
{
customerCode = result.getCustomerCode();
}
bill.setCustomerCode(customerCode);
bill.setCustomerName(request.getCustomerName());
bill.setTrackingNo(request.getTrackingNo());
bill.setPieces(request.getPieces());
@@ -178,7 +188,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
bill.setRoundMode(result.getRoundMode());
result.setBillId(bill.getBillId());
result.setOrderNo(orderNo);
result.setCustomerCode(request.getCustomerCode());
result.setCustomerId(bill.getCustomerId());
result.setCustomerCode(bill.getCustomerCode());
result.setIncomeTotal(bill.getIncomeTotal());
result.setPayTotal(bill.getPayTotal());
result.setPayableTotal(bill.getIncomeTotal());
@@ -57,6 +57,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
private static final String TYPE_DISCOUNT = "DISCOUNT";
private static final String VER_DRAFT = "0";
private static final String VER_EFFECT = "1";
/** Pending: effective_time set but not yet reached */
private static final String VER_PENDING = "3";
private static final String VER_EXPIRED = "2";
@Autowired
@@ -93,6 +95,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
PrcPriceSet set = setMapper.selectPrcPriceSetById(priceSetId);
if (set != null)
{
// Reconcile version status/expire windows so UI always shows at most one EFFECT
refreshVersionStatus(priceSetId);
fillExpired(set);
fillVersions(set);
}
@@ -268,12 +272,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
@Override
@Transactional
public int activateVersion(Long psvId, String updateBy)
public int activateVersion(Long psvId, Date effectiveTime, String updateBy)
{
if (psvId == null)
{
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
}
if (effectiveTime == null)
{
throw new ServiceException(MessageUtils.message("price.set.version.effectiveTime.empty"));
}
PrcPriceSetVersion version = versionMapper.selectPrcPriceSetVersionById(psvId);
if (version == null)
{
@@ -288,34 +296,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
{
throw new ServiceException(MessageUtils.message("price.set.edit.stopped"));
}
Date now = new Date();
// Expire currently effective versions under the same package
PrcPriceSetVersion q = new PrcPriceSetVersion();
q.setPriceSetId(set.getPriceSetId());
List<PrcPriceSetVersion> siblings = versionMapper.selectPrcPriceSetVersionList(q);
if (siblings != null)
{
for (PrcPriceSetVersion other : siblings)
{
if (other.getPsvId() == null || other.getPsvId().equals(psvId))
{
continue;
}
if (VER_EFFECT.equals(other.getStatus()))
{
other.setStatus(VER_EXPIRED);
other.setExpireTime(now);
other.setUpdateBy(updateBy);
versionMapper.updatePrcPriceSetVersion(other);
}
}
}
version.setEffectiveTime(now);
// Set schedule; refreshVersionStatus decides PENDING(3) vs EFFECT(1) and chains expire_time
version.setEffectiveTime(effectiveTime);
version.setExpireTime(null);
version.setStatus(VER_EFFECT);
version.setUpdateBy(updateBy);
versionMapper.updatePrcPriceSetVersion(version);
refreshVersionStatus(set.getPriceSetId());
return 1;
}
@@ -338,6 +323,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
{
throw new ServiceException(MessageUtils.message("price.set.edit.stopped"));
}
if (hasDraftVersion(set.getPriceSetId()))
{
throw new ServiceException(MessageUtils.message("price.set.version.copy.draftExists"));
}
Integer maxSeq = versionMapper.selectMaxVersionSeq(set.getPriceSetId());
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
@@ -1925,6 +1914,34 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
version.setSurcharges(saved);
}
/** True when package already has a true draft (status=0). Pending(3) does not block copy. */
private boolean hasDraftVersion(Long priceSetId)
{
if (priceSetId == null)
{
return false;
}
PrcPriceSetVersion q = new PrcPriceSetVersion();
q.setPriceSetId(priceSetId);
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
if (list == null)
{
return false;
}
for (PrcPriceSetVersion v : list)
{
if (v == null)
{
continue;
}
if (VER_DRAFT.equals(v.getStatus()))
{
return true;
}
}
return false;
}
private void refreshVersionStatus(Long priceSetId)
{
PrcPriceSetVersion q = new PrcPriceSetVersion();
@@ -1958,10 +1975,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
return Integer.compare(nullToZero(a.getVersionSeq()), nullToZero(b.getVersionSeq()));
});
Date now = new Date();
// First pass: chain expire_time = next sibling's effective_time; compute window status
Long effectPsvId = null;
for (int i = 0; i < versions.size(); i++)
{
PrcPriceSetVersion v = versions.get(i);
// Next start only among versions that already have effective_time
Date nextStart = null;
for (int j = i + 1; j < versions.size(); j++)
{
@@ -1972,46 +1990,57 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
break;
}
}
String status;
v.setExpireTime(nextStart);
if (v.getEffectiveTime() == null)
{
status = VER_DRAFT;
v.setStatus(VER_DRAFT);
continue;
}
else if (startedNotAfter(v.getEffectiveTime(), now)
&& (nextStart == null || nextStart.after(now)))
// Not started yet -> pending
if (v.getEffectiveTime().getTime() > now.getTime())
{
status = VER_EFFECT;
v.setStatus(VER_PENDING);
continue;
}
else if (startedNotAfter(v.getEffectiveTime(), now))
// Window ended: expire_time <= now (same rule as bill: expire_time > asOf to stay active)
if (nextStart != null && nextStart.getTime() <= now.getTime())
{
status = VER_EXPIRED;
v.setStatus(VER_EXPIRED);
continue;
}
else
// In window: candidate for the single EFFECT version (latest wins)
v.setStatus(VER_EFFECT);
effectPsvId = v.getPsvId();
}
// Second pass: only the latest in-window version stays EFFECT
if (effectPsvId != null)
{
for (PrcPriceSetVersion v : versions)
{
// future-dated: keep as draft until wall-clock reaches effective_time
status = VER_DRAFT;
if (VER_EFFECT.equals(v.getStatus())
&& v.getPsvId() != null
&& !v.getPsvId().equals(effectPsvId))
{
v.setStatus(VER_EXPIRED);
}
}
boolean statusChanged = !status.equals(v.getStatus());
boolean expireChanged = !sameTime(nextStart, v.getExpireTime());
}
for (PrcPriceSetVersion v : versions)
{
PrcPriceSetVersion db = versionMapper.selectPrcPriceSetVersionById(v.getPsvId());
if (db == null)
{
continue;
}
boolean statusChanged = !String.valueOf(v.getStatus()).equals(String.valueOf(db.getStatus()));
boolean expireChanged = !sameTime(v.getExpireTime(), db.getExpireTime());
if (statusChanged || expireChanged)
{
v.setStatus(status);
v.setExpireTime(nextStart);
versionMapper.updatePrcPriceSetVersion(v);
}
}
}
/** effective_time <= now (+1s skew tolerance) */
private static boolean startedNotAfter(Date effectiveTime, Date now)
{
if (effectiveTime == null || now == null)
{
return false;
}
return effectiveTime.getTime() <= now.getTime() + 1000L;
}
@Override
public int refreshAllVersionStatus()
{
@@ -21,6 +21,8 @@ import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.customer.customerinfo.domain.BmsCustomer;
import com.ruoyi.customer.customerinfo.mapper.BmsCustomerMapper;
import com.ruoyi.price.domain.BmsProduct;
import com.ruoyi.price.domain.BmsProductCountry;
import com.ruoyi.price.domain.PrcCustomerPriceSet;
@@ -33,6 +35,7 @@ import com.ruoyi.price.domain.PrcQuoteFee;
import com.ruoyi.price.domain.PrcSurchargeRel;
import com.ruoyi.price.domain.PrcZone;
import com.ruoyi.price.mapper.PrcZoneDetailMapper;
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteFeeLine;
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
@@ -106,6 +109,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
@Autowired
private IBillSearchStore billSearchStore;
@Autowired
private BmsCustomerMapper bmsCustomerMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request)
@@ -125,6 +131,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
private TmsPriceQuoteResult quoteInternal(TmsPriceQuoteRequest request)
{
validateRequest(request);
resolveCustomer(request);
Date bizDate = resolveBizDate(request.getBizDate());
BigDecimal weight = request.getWeight().setScale(3, RoundingMode.HALF_UP);
String quoteKey = buildQuoteKey(request, weight, bizDate);
@@ -156,6 +163,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
public TmsPriceQuoteResult calculate(TmsPriceQuoteRequest request)
{
validateRequest(request);
resolveCustomer(request);
Date bizDate = resolveBizDate(request.getBizDate());
TmsPriceQuoteResult result = doQuote(request, bizDate);
applySurchargesAndFees(result);
@@ -312,6 +320,53 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
{
result.setDestPostcode(emptyToNull(request.getDestPostcode()));
}
// Always prefer customer resolved from customerCode (ignore TMS/bind ids)
if (request.getCustomerId() != null)
{
result.setCustomerId(request.getCustomerId());
}
if (StringUtils.isNotEmpty(request.getCustomerCode()))
{
result.setCustomerCode(request.getCustomerCode());
}
}
/**
* Require customerCode; resolve customer_id from bms_customer.
*/
private void resolveCustomer(TmsPriceQuoteRequest request)
{
if (request == null || StringUtils.isEmpty(request.getCustomerCode()))
{
throw new ServiceException(MessageUtils.message("price.quote.customerCodeRequired"));
}
String code = request.getCustomerCode().trim();
if (StringUtils.isEmpty(code))
{
throw new ServiceException(MessageUtils.message("price.quote.customerCodeRequired"));
}
request.setCustomerCode(code);
BmsCustomer query = new BmsCustomer();
query.setCustomerCode(code);
List<BmsCustomer> list = bmsCustomerMapper.selectBmsCustomerList(query);
if (list == null || list.isEmpty())
{
throw new ServiceException(MessageUtils.message("price.quote.customerCodeNotFound"));
}
BmsCustomer hit = list.get(0);
request.setCustomerId(hit.getCustomerId());
if (StringUtils.isNotEmpty(hit.getCustomerCode()))
{
request.setCustomerCode(hit.getCustomerCode());
}
if (request instanceof TmsPriceBillRequest)
{
TmsPriceBillRequest billReq = (TmsPriceBillRequest) request;
if (StringUtils.isEmpty(billReq.getCustomerName()) && StringUtils.isNotEmpty(hit.getCustomerName()))
{
billReq.setCustomerName(hit.getCustomerName());
}
}
}
private String resolveCountryIso2(String productCode, String countryCode, String countryName)
@@ -684,6 +739,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
quote.setQuoteId(UUID.randomUUID().toString());
quote.setQuoteKey(quoteKey);
quote.setCustomerId(request.getCustomerId() == null ? Long.valueOf(0L) : request.getCustomerId());
quote.setCustomerCode(emptyToNull(request.getCustomerCode()));
quote.setProductCode(result.getProductCode());
quote.setProductName(result.getProductName());
quote.setCountryCode(result.getCountryCode());
@@ -810,6 +866,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setMatchSource(quote.getMatchSource());
r.setMatchMessage(quote.getMatchMessage());
r.setCustomerId(quote.getCustomerId());
r.setCustomerCode(quote.getCustomerCode());
r.setPriceSetId(quote.getPriceSetId());
r.setPriceSetCode(quote.getPriceSetCode());
r.setPriceSetName(quote.getPriceSetName());
@@ -857,6 +914,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
to.setMatchSource(from.getMatchSource());
to.setMatchMessage(from.getMatchMessage());
to.setCustomerId(from.getCustomerId());
to.setCustomerCode(from.getCustomerCode());
to.setPriceSetId(from.getPriceSetId());
to.setPriceSetCode(from.getPriceSetCode());
to.setPriceSetName(from.getPriceSetName());
@@ -892,6 +950,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
private String buildQuoteKey(TmsPriceQuoteRequest request, BigDecimal weight, Date bizDate)
{
if (StringUtils.isEmpty(request.getCustomerCode()))
{
throw new ServiceException(MessageUtils.message("price.quote.customerCodeRequired"));
}
String countryCode = emptyToNull(request.getCountryCode());
if (countryCode != null)
{
@@ -899,8 +961,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
}
String countryName = emptyToNull(request.getCountryName());
String biz = new SimpleDateFormat("yyyy-MM-dd").format(bizDate);
String raw = (StringUtils.isNotEmpty(request.getCustomerCode())
? request.getCustomerCode() : String.valueOf(request.getCustomerId())) + "|"
String raw = request.getCustomerCode().trim() + "|"
+ request.getProductCode().trim() + "|"
+ nvlStr(countryCode) + "|"
+ nvlStr(countryName) + "|"
@@ -995,9 +1056,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
{
throw new ServiceException(MessageUtils.message("price.quote.requestBodyRequired"));
}
if (StringUtils.isEmpty(request.getCustomerCode()) && request.getCustomerId() == null)
if (StringUtils.isEmpty(request.getCustomerCode()))
{
throw new ServiceException(MessageUtils.message("price.quote.customerIdRequired"));
throw new ServiceException(MessageUtils.message("price.quote.customerCodeRequired"));
}
if (StringUtils.isEmpty(request.getProductCode()))
{
@@ -7,73 +7,42 @@ 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 状态自动刷新
*
* <p>每天 02:00 根据 effective_date / expire_date 与当天日期,回填 status:
* <ul>
* <li>'2' 已失效 EXPIRED:expire_date 非空且 &lt;= 今天</li>
* <li>'0' 待生效 PENDING:effective_date 为空 / 未来</li>
* <li>'1' 已生效 ACTIVE:其余(已生效且仍在有效期)</li>
* </ul>
*
* <p>对应字典 bms_product_effect_status 的 value=ACTIVE/PENDING/EXPIRED。
* 实际存储使用 0/1/2 与列 comment 保持一致;前端 dict-tag 需做 value 映射或调整 dict_value。
*
* <p>手动触发:调用 refreshNow()(运维/测试用,详见 StatusRefreshController)。
*
* @author ruoyi
* Refresh prc_price_set_version status (draft/pending/effect/expired).
* Startup + every 5 minutes so PENDING becomes EFFECT when effective_time is reached.
*/
@Component
public class BmsStatusRefreshTask implements ApplicationRunner
{
private static final Logger log = LoggerFactory.getLogger(BmsStatusRefreshTask.class);
@Autowired
private BmsProductMapper productMapper;
@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()
{
refreshNow();
}
@Override
public void run(ApplicationArguments args)
{
refresh("startup");
}
@Scheduled(cron = "0 0/5 * * * ?")
public void scheduledRefresh()
{
refresh("scheduled");
}
private void refresh(String scene)
{
try
{
int n = priceSetService.refreshAllVersionStatus();
log.info("startup prc_price_set_version expire/status refreshed, sets={}", n);
log.info("{} prc_price_set_version expire/status refreshed, sets={}", scene, n);
}
catch (Exception e)
{
log.error("startup prc_price_set_version refresh failed", e);
log.error("{} prc_price_set_version refresh failed", scene, e);
}
}
/**
* 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();
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 };
}
}
}