套餐启用增加生效时间
This commit is contained in:
@@ -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;
|
||||
|
||||
+2
@@ -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)
|
||||
|
||||
+7
-3
@@ -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 <= now -> EFFECT; future -> 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 */
|
||||
|
||||
+14
-3
@@ -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());
|
||||
|
||||
+80
-51
@@ -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()
|
||||
{
|
||||
|
||||
+65
-4
@@ -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()))
|
||||
{
|
||||
|
||||
+16
-47
@@ -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 非空且 <= 今天</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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<id property="quoteId" column="quote_id" />
|
||||
<result property="quoteKey" column="quote_key" />
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="customerCode" column="customer_code" />
|
||||
<result property="productCode" column="product_code" />
|
||||
<result property="productName" column="product_name" />
|
||||
<result property="countryCode" column="country_code" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="destPostcode" column="dest_postcode" />
|
||||
<result property="destPostcode" column="dest_postcode" />
|
||||
<result property="weight" column="weight" />
|
||||
<result property="bizDate" column="biz_date" />
|
||||
<result property="tmsId" column="tms_id" />
|
||||
@@ -46,7 +47,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPrcQuoteVo">
|
||||
select quote_id, quote_key, customer_id, product_code, product_name,
|
||||
select quote_id, quote_key, customer_id, customer_code, product_code, product_name,
|
||||
country_code, country_name, dest_postcode, weight, biz_date, tms_id,
|
||||
match_source, match_message, price_set_id, price_set_code, price_set_name,
|
||||
set_type, bind_priority, customer_priceset_id, product_country_id,
|
||||
@@ -73,6 +74,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="customerId != null">
|
||||
AND customer_id = #{customerId}
|
||||
</if>
|
||||
<if test="customerCode != null and customerCode != ''">
|
||||
AND customer_code = #{customerCode}
|
||||
</if>
|
||||
<if test="productCode != null and productCode != ''">
|
||||
AND product_code like concat('%', #{productCode}, '%')
|
||||
</if>
|
||||
@@ -103,7 +107,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<insert id="insertPrcQuote" parameterType="PrcQuote">
|
||||
insert into prc_quote (
|
||||
quote_id, quote_key, customer_id, product_code, product_name,
|
||||
quote_id, quote_key, customer_id, customer_code, product_code, product_name,
|
||||
country_code, country_name, dest_postcode, weight, biz_date, tms_id,
|
||||
match_source, match_message, price_set_id, price_set_code, price_set_name,
|
||||
set_type, bind_priority, customer_priceset_id, product_country_id,
|
||||
@@ -112,7 +116,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
freight_amount, surcharge_amount, payable_total, currency, round_mode,
|
||||
create_time
|
||||
) values (
|
||||
#{quoteId}, #{quoteKey}, #{customerId}, #{productCode}, #{productName},
|
||||
#{quoteId}, #{quoteKey}, #{customerId}, #{customerCode}, #{productCode}, #{productName},
|
||||
#{countryCode}, #{countryName}, #{destPostcode}, #{weight}, #{bizDate}, #{tmsId},
|
||||
#{matchSource}, #{matchMessage}, #{priceSetId}, #{priceSetCode}, #{priceSetName},
|
||||
#{setType}, #{bindPriority}, #{customerPricesetId}, #{productCountryId},
|
||||
|
||||
Reference in New Issue
Block a user