优化记录日志功能
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
package com.ruoyi.web.controller.api;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.ruoyi.price.search.EsBizLogSupport;
|
||||
import com.ruoyi.price.search.EsLogType;
|
||||
import com.ruoyi.price.search.IBillSearchStore;
|
||||
import com.ruoyi.web.controller.price.BillingController;
|
||||
|
||||
/**
|
||||
* Persist validation failures of quote/bill APIs to Elasticsearch.
|
||||
*/
|
||||
@Order(0)
|
||||
@RestControllerAdvice(assignableTypes = { TmsPriceController.class, BillingController.class })
|
||||
public class PriceSyncEsLogAdvice
|
||||
{
|
||||
@Autowired
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public AjaxResult handleValid(MethodArgumentNotValidException e)
|
||||
{
|
||||
String message = e.getBindingResult().getFieldError() == null
|
||||
? e.getMessage()
|
||||
: e.getBindingResult().getFieldError().getDefaultMessage();
|
||||
saveFail(e.getBindingResult().getTarget(), message);
|
||||
return AjaxResult.error(message);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BindException.class)
|
||||
public AjaxResult handleBind(BindException e)
|
||||
{
|
||||
String message = e.getAllErrors().isEmpty() ? e.getMessage() : e.getAllErrors().get(0).getDefaultMessage();
|
||||
saveFail(e.getTarget(), message);
|
||||
return AjaxResult.error(message);
|
||||
}
|
||||
|
||||
private void saveFail(Object target, String message)
|
||||
{
|
||||
if (target instanceof TmsPriceBillRequest)
|
||||
{
|
||||
TmsPriceBillRequest request = (TmsPriceBillRequest) target;
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.BILL, EsBizLogSupport.billFailBizId(request), request,
|
||||
message);
|
||||
return;
|
||||
}
|
||||
if (target instanceof TmsPriceQuoteRequest)
|
||||
{
|
||||
TmsPriceQuoteRequest request = (TmsPriceQuoteRequest) target;
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.QUOTE, EsBizLogSupport.quoteFailBizId(request),
|
||||
request, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -19,6 +19,9 @@ import com.ruoyi.framework.config.properties.TmsApiProperties;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.ruoyi.price.search.EsBizLogSupport;
|
||||
import com.ruoyi.price.search.EsLogType;
|
||||
import com.ruoyi.price.search.IBillSearchStore;
|
||||
import com.ruoyi.price.service.IPrcBillService;
|
||||
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
||||
|
||||
@@ -46,6 +49,9 @@ public class TmsPriceController extends BaseController
|
||||
@Autowired
|
||||
private TmsApiProperties tmsApiProperties;
|
||||
|
||||
@Autowired
|
||||
private IBillSearchStore billSearchStore;
|
||||
|
||||
/**
|
||||
* Quote by customerId + productCode + country + weight.
|
||||
* Match: customer package FIXED > DISCOUNT > product default.
|
||||
@@ -54,8 +60,17 @@ public class TmsPriceController extends BaseController
|
||||
public AjaxResult quote(
|
||||
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
|
||||
@Validated @RequestBody TmsPriceQuoteRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
assertApiCode(headerApiCode, request == null ? null : request.getApiCode());
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.QUOTE, EsBizLogSupport.quoteFailBizId(request),
|
||||
request, e);
|
||||
throw e;
|
||||
}
|
||||
TmsPriceQuoteResult result = tmsPriceQuoteService.quote(request);
|
||||
return success(result);
|
||||
}
|
||||
@@ -68,8 +83,16 @@ public class TmsPriceController extends BaseController
|
||||
public AjaxResult bill(
|
||||
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
|
||||
@Validated @RequestBody TmsPriceBillRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
assertApiCode(headerApiCode, request == null ? null : request.getApiCode());
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.BILL, EsBizLogSupport.billFailBizId(request), request, e);
|
||||
throw e;
|
||||
}
|
||||
TmsPriceQuoteResult result = prcBillService.bill(request);
|
||||
return success(result);
|
||||
}
|
||||
|
||||
+3
-2
@@ -30,12 +30,13 @@ public class EsLogController extends BaseController
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(
|
||||
@RequestParam(value = "logType", required = false) String logType,
|
||||
@RequestParam(value = "bizId", required = false) String bizId)
|
||||
@RequestParam(value = "bizId", required = false) String bizId,
|
||||
@RequestParam(value = "status", required = false) String status)
|
||||
{
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||
BillSearchPage page = billSearchStore.search(logType, bizId, pageNum, pageSize);
|
||||
BillSearchPage page = billSearchStore.search(logType, bizId, status, pageNum, pageSize);
|
||||
TableDataInfo rsp = new TableDataInfo();
|
||||
rsp.setCode(HttpStatus.SUCCESS);
|
||||
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
|
||||
|
||||
@@ -8,6 +8,8 @@ public class BillSyncSnapshot
|
||||
private String bizId;
|
||||
private String requestJson;
|
||||
private String responseJson;
|
||||
private String status;
|
||||
private String errorMessage;
|
||||
private String updatedAt;
|
||||
|
||||
public String getDocId()
|
||||
@@ -60,6 +62,26 @@ public class BillSyncSnapshot
|
||||
this.responseJson = responseJson;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getErrorMessage()
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage)
|
||||
{
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public String getUpdatedAt()
|
||||
{
|
||||
return updatedAt;
|
||||
|
||||
@@ -49,21 +49,25 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String logType, String bizId, String requestJson, String responseJson)
|
||||
public void save(String logType, String bizId, String requestJson, String responseJson, String status,
|
||||
String errorMessage)
|
||||
{
|
||||
if (StringUtils.isEmpty(logType) || StringUtils.isEmpty(bizId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String docId = EsLogType.docId(logType, bizId);
|
||||
Date now = new Date();
|
||||
String docId = EsLogType.docId(logType, bizId, now);
|
||||
try
|
||||
{
|
||||
JSONObject doc = new JSONObject();
|
||||
doc.put("logType", logType.trim());
|
||||
doc.put("bizId", bizId.trim());
|
||||
doc.put("status", StringUtils.isEmpty(status) ? EsBizLogSupport.STATUS_SUCCESS : status.trim());
|
||||
doc.put("error", StringUtils.isEmpty(errorMessage) ? null : errorMessage);
|
||||
doc.put("request", parseOrRaw(requestJson));
|
||||
doc.put("response", parseOrRaw(responseJson));
|
||||
doc.put("updatedAt", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
doc.put("updatedAt", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(now));
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> entity = new HttpEntity<String>(doc.toJSONString(), headers);
|
||||
@@ -82,7 +86,12 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return getByDocId(EsLogType.docId(logType, bizId));
|
||||
BillSearchPage page = search(logType, bizId, null, 1, 1);
|
||||
if (page.getRows() == null || page.getRows().isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return page.getRows().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,7 +128,7 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public BillSearchPage search(String logType, String bizId, int pageNum, int pageSize)
|
||||
public BillSearchPage search(String logType, String bizId, String status, int pageNum, int pageSize)
|
||||
{
|
||||
BillSearchPage page = new BillSearchPage();
|
||||
if (pageNum < 1)
|
||||
@@ -143,7 +152,7 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
sortItem.put("updatedAt.keyword", sortOpt);
|
||||
sort.add(sortItem);
|
||||
body.put("sort", sort);
|
||||
body.put("query", buildQuery(logType, bizId));
|
||||
body.put("query", buildQuery(logType, bizId, status));
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> entity = new HttpEntity<String>(body.toJSONString(), headers);
|
||||
@@ -210,14 +219,18 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
snap.setRequestJson(toJsonString(source.get("request")));
|
||||
snap.setResponseJson(toJsonString(source.get("response")));
|
||||
snap.setUpdatedAt(source.getString("updatedAt"));
|
||||
String status = source.getString("status");
|
||||
snap.setStatus(StringUtils.isEmpty(status) ? EsBizLogSupport.STATUS_SUCCESS : status);
|
||||
snap.setErrorMessage(source.getString("error"));
|
||||
return snap;
|
||||
}
|
||||
|
||||
private JSONObject buildQuery(String logType, String bizId)
|
||||
private JSONObject buildQuery(String logType, String bizId, String status)
|
||||
{
|
||||
JSONArray filters = new JSONArray();
|
||||
addTerm(filters, "logType", logType);
|
||||
addWildcard(filters, "bizId", bizId);
|
||||
addTerm(filters, "status", status);
|
||||
if (filters.isEmpty())
|
||||
{
|
||||
JSONObject matchAll = new JSONObject();
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.ruoyi.price.search;
|
||||
|
||||
import java.util.UUID;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.sign.Md5Utils;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||
|
||||
/** Helpers for ES business logs (success / fail). */
|
||||
public final class EsBizLogSupport
|
||||
{
|
||||
public static final String STATUS_SUCCESS = "SUCCESS";
|
||||
public static final String STATUS_FAIL = "FAIL";
|
||||
|
||||
private static final int ERROR_MAX = 2000;
|
||||
|
||||
private EsBizLogSupport()
|
||||
{
|
||||
}
|
||||
|
||||
public static String requestJson(Object request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
JSONObject obj = JSON.parseObject(JSON.toJSONString(request));
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
obj.remove("apiCode");
|
||||
return obj.toJSONString();
|
||||
}
|
||||
|
||||
public static String failResponseJson(String errorMessage)
|
||||
{
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("success", Boolean.FALSE);
|
||||
obj.put("msg", errorMessage);
|
||||
return obj.toJSONString();
|
||||
}
|
||||
|
||||
public static String errorText(Throwable e)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
String msg = e.getMessage();
|
||||
if (StringUtils.isEmpty(msg))
|
||||
{
|
||||
msg = e.getClass().getSimpleName();
|
||||
}
|
||||
Throwable cause = e.getCause();
|
||||
if (cause != null && StringUtils.isNotEmpty(cause.getMessage())
|
||||
&& (msg == null || !msg.contains(cause.getMessage())))
|
||||
{
|
||||
msg = msg + " | " + cause.getMessage();
|
||||
}
|
||||
return cut(msg);
|
||||
}
|
||||
|
||||
public static String cut(String msg)
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String text = msg.trim();
|
||||
if (text.length() > ERROR_MAX)
|
||||
{
|
||||
return text.substring(0, ERROR_MAX);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static String billBizId(TmsPriceBillRequest request)
|
||||
{
|
||||
if (request != null && StringUtils.isNotEmpty(request.getOrderNo()))
|
||||
{
|
||||
return request.getOrderNo().trim();
|
||||
}
|
||||
return "FAIL-" + UUID.randomUUID();
|
||||
}
|
||||
|
||||
public static String billFailBizId(TmsPriceBillRequest request)
|
||||
{
|
||||
return billBizId(request);
|
||||
}
|
||||
|
||||
public static String quoteFailBizId(TmsPriceQuoteRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return "FAIL-" + UUID.randomUUID();
|
||||
}
|
||||
String raw = nvl(request.getCustomerCode()) + "|" + nvl(request.getCustomerId()) + "|"
|
||||
+ nvl(request.getProductCode()) + "|" + nvl(request.getCountryCode()) + "|"
|
||||
+ nvl(request.getCountryName()) + "|" + nvl(request.getWeight()) + "|"
|
||||
+ nvl(request.getBizDate());
|
||||
return "FAIL-" + Md5Utils.hash(raw);
|
||||
}
|
||||
|
||||
public static void saveFail(IBillSearchStore store, String logType, String bizId, Object request, Throwable e)
|
||||
{
|
||||
saveFail(store, logType, bizId, request, errorText(e));
|
||||
}
|
||||
|
||||
public static void saveFail(IBillSearchStore store, String logType, String bizId, Object request, String errorMessage)
|
||||
{
|
||||
if (store == null || StringUtils.isEmpty(logType) || StringUtils.isEmpty(bizId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String msg = cut(errorMessage);
|
||||
store.save(logType, bizId, requestJson(request), failResponseJson(msg), STATUS_FAIL, msg);
|
||||
}
|
||||
|
||||
private static String nvl(Object value)
|
||||
{
|
||||
return value == null ? "" : String.valueOf(value).trim();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.ruoyi.price.search;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/** Common ES business-log types. */
|
||||
public final class EsLogType
|
||||
{
|
||||
@@ -10,10 +13,23 @@ public final class EsLogType
|
||||
{
|
||||
}
|
||||
|
||||
/** One document per bizId + write time. */
|
||||
public static String docId(String logType, String bizId)
|
||||
{
|
||||
return docId(logType, bizId, new Date());
|
||||
}
|
||||
|
||||
public static String docId(String logType, String bizId, Date at)
|
||||
{
|
||||
String type = logType == null ? "" : logType.trim();
|
||||
String id = bizId == null ? "" : bizId.trim();
|
||||
return type + "_" + id;
|
||||
Date time = at == null ? new Date() : at;
|
||||
String ts = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(time);
|
||||
String uniq = Long.toHexString(System.nanoTime());
|
||||
if (uniq.length() > 6)
|
||||
{
|
||||
uniq = uniq.substring(uniq.length() - 6);
|
||||
}
|
||||
return type + "_" + id + "_" + ts + "_" + uniq;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,16 @@ package com.ruoyi.price.search;
|
||||
*/
|
||||
public interface IBillSearchStore
|
||||
{
|
||||
void save(String logType, String bizId, String requestJson, String responseJson);
|
||||
default void save(String logType, String bizId, String requestJson, String responseJson)
|
||||
{
|
||||
save(logType, bizId, requestJson, responseJson, EsBizLogSupport.STATUS_SUCCESS, null);
|
||||
}
|
||||
|
||||
void save(String logType, String bizId, String requestJson, String responseJson, String status, String errorMessage);
|
||||
|
||||
BillSyncSnapshot get(String logType, String bizId);
|
||||
|
||||
BillSyncSnapshot getByDocId(String docId);
|
||||
|
||||
BillSearchPage search(String logType, String bizId, int pageNum, int pageSize);
|
||||
BillSearchPage search(String logType, String bizId, String status, int pageNum, int pageSize);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ package com.ruoyi.price.search;
|
||||
public class NoopBillSearchStore implements IBillSearchStore
|
||||
{
|
||||
@Override
|
||||
public void save(String logType, String bizId, String requestJson, String responseJson)
|
||||
public void save(String logType, String bizId, String requestJson, String responseJson, String status,
|
||||
String errorMessage)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,7 +24,7 @@ public class NoopBillSearchStore implements IBillSearchStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public BillSearchPage search(String logType, String bizId, int pageNum, int pageSize)
|
||||
public BillSearchPage search(String logType, String bizId, String status, int pageNum, int pageSize)
|
||||
{
|
||||
return new BillSearchPage();
|
||||
}
|
||||
|
||||
+9
-67
@@ -23,7 +23,7 @@ import com.ruoyi.price.domain.vo.TmsPriceQuoteFeeLine;
|
||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.ruoyi.price.mapper.PrcBillFeeMapper;
|
||||
import com.ruoyi.price.mapper.PrcBillMapper;
|
||||
import com.ruoyi.price.search.BillSyncSnapshot;
|
||||
import com.ruoyi.price.search.EsBizLogSupport;
|
||||
import com.ruoyi.price.search.EsLogType;
|
||||
import com.ruoyi.price.search.IBillSearchStore;
|
||||
import com.ruoyi.price.service.IPrcBillService;
|
||||
@@ -47,6 +47,8 @@ public class PrcBillServiceImpl implements IPrcBillService
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TmsPriceQuoteResult bill(TmsPriceBillRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request == null || StringUtils.isEmpty(request.getOrderNo()))
|
||||
{
|
||||
@@ -63,6 +65,12 @@ public class PrcBillServiceImpl implements IPrcBillService
|
||||
persistBill(request, weight, bizDate, result);
|
||||
return result;
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.BILL, EsBizLogSupport.billFailBizId(request), request, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcBill> selectBillList(PrcBill query)
|
||||
@@ -261,31 +269,9 @@ public class PrcBillServiceImpl implements IPrcBillService
|
||||
}
|
||||
}
|
||||
r.setFeeDetails(lines);
|
||||
fillSnapshotFromSearch(r, bill);
|
||||
return r;
|
||||
}
|
||||
|
||||
private void fillSnapshotFromSearch(TmsPriceQuoteResult r, PrcBill bill)
|
||||
{
|
||||
BillSyncSnapshot snap = billSearchStore.get(EsLogType.BILL, bill.getOrderNo());
|
||||
if (snap != null && StringUtils.isNotEmpty(snap.getRequestJson()))
|
||||
{
|
||||
r.setRequestJson(snap.getRequestJson());
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setRequestJson(rebuildRequestJson(bill));
|
||||
}
|
||||
if (snap != null && StringUtils.isNotEmpty(snap.getResponseJson()))
|
||||
{
|
||||
r.setResponseJson(snap.getResponseJson());
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setResponseJson(toResponseJson(r));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillResultOrder(TmsPriceQuoteResult r, PrcBill bill)
|
||||
{
|
||||
r.setTrackingNo(bill.getTrackingNo());
|
||||
@@ -328,42 +314,6 @@ public class PrcBillServiceImpl implements IPrcBillService
|
||||
return obj.toJSONString();
|
||||
}
|
||||
|
||||
private String rebuildRequestJson(PrcBill bill)
|
||||
{
|
||||
JSONObject obj = new JSONObject();
|
||||
putIfNotNull(obj, "orderNo", bill.getOrderNo());
|
||||
putIfNotNull(obj, "trackingNo", bill.getTrackingNo());
|
||||
putIfNotNull(obj, "customerId", bill.getCustomerId());
|
||||
putIfNotNull(obj, "customerCode", bill.getCustomerCode());
|
||||
putIfNotNull(obj, "customerName", bill.getCustomerName());
|
||||
putIfNotNull(obj, "productCode", bill.getProductCode());
|
||||
putIfNotNull(obj, "countryCode", bill.getCountryCode());
|
||||
putIfNotNull(obj, "countryName", bill.getCountryName());
|
||||
putIfNotNull(obj, "weight", bill.getWeight());
|
||||
if (bill.getBizDate() != null)
|
||||
{
|
||||
obj.put("bizDate", new SimpleDateFormat("yyyy-MM-dd").format(bill.getBizDate()));
|
||||
}
|
||||
putIfNotNull(obj, "tmsId", bill.getTmsId());
|
||||
putIfNotNull(obj, "pieces", bill.getPieces());
|
||||
putIfNotNull(obj, "actualWeight", bill.getActualWeight());
|
||||
putIfNotNull(obj, "volume", bill.getVolume());
|
||||
putIfNotNull(obj, "volumeWeight", bill.getVolumeWeight());
|
||||
putIfNotNull(obj, "pkgLength", bill.getPkgLength());
|
||||
putIfNotNull(obj, "pkgWidth", bill.getPkgWidth());
|
||||
putIfNotNull(obj, "pkgHeight", bill.getPkgHeight());
|
||||
putIfNotNull(obj, "shipperName", bill.getShipperName());
|
||||
putIfNotNull(obj, "consigneeName", bill.getConsigneeName());
|
||||
putIfNotNull(obj, "destPostcode", bill.getDestPostcode());
|
||||
putIfNotNull(obj, "warehouseCode", bill.getWarehouseCode());
|
||||
if (bill.getOrderTime() != null)
|
||||
{
|
||||
obj.put("orderTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(bill.getOrderTime()));
|
||||
}
|
||||
putIfNotNull(obj, "orderRemark", bill.getOrderRemark());
|
||||
return obj.toJSONString();
|
||||
}
|
||||
|
||||
private JSONObject parseJsonObject(String json)
|
||||
{
|
||||
if (StringUtils.isEmpty(json))
|
||||
@@ -380,14 +330,6 @@ public class PrcBillServiceImpl implements IPrcBillService
|
||||
}
|
||||
}
|
||||
|
||||
private void putIfNotNull(JSONObject obj, String key, Object value)
|
||||
{
|
||||
if (value != null && !(value instanceof String && ((String) value).isEmpty()))
|
||||
{
|
||||
obj.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Date resolveBizDate(String bizDate)
|
||||
{
|
||||
if (StringUtils.isEmpty(bizDate))
|
||||
|
||||
+15
@@ -39,6 +39,7 @@ import com.ruoyi.price.mapper.PrcPriceSetMapper;
|
||||
import com.ruoyi.price.mapper.PrcQuoteFeeMapper;
|
||||
import com.ruoyi.price.mapper.PrcQuoteMapper;
|
||||
import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
|
||||
import com.ruoyi.price.search.EsBizLogSupport;
|
||||
import com.ruoyi.price.search.EsLogType;
|
||||
import com.ruoyi.price.search.IBillSearchStore;
|
||||
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
||||
@@ -92,6 +93,20 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return quoteInternal(request);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
EsBizLogSupport.saveFail(billSearchStore, EsLogType.QUOTE, EsBizLogSupport.quoteFailBizId(request),
|
||||
request, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult quoteInternal(TmsPriceQuoteRequest request)
|
||||
{
|
||||
validateRequest(request);
|
||||
Date bizDate = resolveBizDate(request.getBizDate());
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<template #col-logType="{ row }">
|
||||
<el-tag size="small" :type="typeTag(row.logType)">{{ typeLabel(row.logType) }}</el-tag>
|
||||
</template>
|
||||
<template #col-status="{ row }">
|
||||
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="handleView(row)" v-hasPermi="['monitor:eslog:list']">查看</el-button>
|
||||
</template>
|
||||
@@ -23,8 +26,14 @@
|
||||
<el-descriptions-item label="类型">
|
||||
<el-tag size="small" :type="typeTag(detail.logType)">{{ typeLabel(detail.logType) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag size="small" :type="statusTag(detail.status)">{{ statusLabel(detail.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="业务ID">{{ detail.bizId || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="写入时间" :span="2">{{ detail.updatedAt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="写入时间">{{ detail.updatedAt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="detail.errorMessage" label="错误信息" :span="2">
|
||||
<span class="err-text">{{ detail.errorMessage }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="json-panel">
|
||||
<div class="json-panel__head">
|
||||
@@ -60,13 +69,20 @@ const typeOptions = [
|
||||
{ label: '试算', value: 'QUOTE' }
|
||||
]
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '成功', value: 'SUCCESS' },
|
||||
{ label: '失败', value: 'FAIL' }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '类型', prop: 'logType', component: 'select', options: typeOptions, placeholder: '请选择类型', width: '160px', props: { clearable: true } },
|
||||
{ label: '状态', prop: 'status', component: 'select', options: statusOptions, placeholder: '请选择状态', width: '140px', props: { clearable: true } },
|
||||
{ label: '业务ID', prop: 'bizId', component: 'input', placeholder: '业务ID', width: '220px' }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '类型', prop: 'logType', align: 'center', width: 110, slot: 'logType' },
|
||||
{ label: '状态', prop: 'status', align: 'center', width: 100, slot: 'status' },
|
||||
{ label: '业务ID', prop: 'bizId', minWidth: 180, showOverflowTooltip: true },
|
||||
{ label: '写入时间', prop: 'updatedAt', align: 'center', width: 180 },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
@@ -87,6 +103,16 @@ function typeTag(type) {
|
||||
return 'info'
|
||||
}
|
||||
|
||||
function statusLabel(status) {
|
||||
const found = statusOptions.find(item => item.value === status)
|
||||
if (found) return found.label
|
||||
return status === 'FAIL' ? '失败' : '成功'
|
||||
}
|
||||
|
||||
function statusTag(status) {
|
||||
return status === 'FAIL' ? 'danger' : 'success'
|
||||
}
|
||||
|
||||
function handleView(row) {
|
||||
drawerTitle.value = 'ES日志详情 #' + (row.bizId || row.docId)
|
||||
open.value = true
|
||||
@@ -126,6 +152,7 @@ function copyJson(val) {
|
||||
|
||||
<style scoped>
|
||||
.mb12 { margin-bottom: 12px; }
|
||||
.err-text { color: #f56c6c; word-break: break-all; }
|
||||
.json-panel {
|
||||
margin-bottom: 12px;
|
||||
background: #f7f9fb;
|
||||
|
||||
@@ -101,23 +101,6 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</section>
|
||||
<section v-if="detail.requestJson || detail.responseJson" class="detail-block">
|
||||
<div class="sec-title">同步报文</div>
|
||||
<div class="json-panel">
|
||||
<div class="json-panel__head">
|
||||
<span>同步请求参数</span>
|
||||
<el-button size="small" icon="CopyDocument" @click="copyJson(detail.requestJson)">复制</el-button>
|
||||
</div>
|
||||
<pre class="code-pre">{{ formatJson(detail.requestJson) }}</pre>
|
||||
</div>
|
||||
<div class="json-panel">
|
||||
<div class="json-panel__head">
|
||||
<span>计费返回数据</span>
|
||||
<el-button size="small" icon="CopyDocument" @click="copyJson(detail.responseJson)">复制</el-button>
|
||||
</div>
|
||||
<pre class="code-pre">{{ formatJson(detail.responseJson) }}</pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
@@ -223,30 +206,6 @@ function displayFeeName(row) {
|
||||
return name || typeLabel || row.feeType || '-'
|
||||
}
|
||||
|
||||
function formatJson(val) {
|
||||
if (val === null || val === undefined || val === '') return '无数据'
|
||||
if (typeof val === 'object') {
|
||||
try { return JSON.stringify(val, null, 2) } catch (e) { return String(val) }
|
||||
}
|
||||
try { return JSON.stringify(JSON.parse(val), null, 2) } catch (e) { return String(val) }
|
||||
}
|
||||
|
||||
function copyJson(val) {
|
||||
const text = formatJson(val)
|
||||
const done = () => ElMessage({ message: '已复制', type: 'success', duration: 1500 })
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(done)
|
||||
return
|
||||
}
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = text
|
||||
document.body.appendChild(ta)
|
||||
ta.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(ta)
|
||||
done()
|
||||
}
|
||||
|
||||
function sourceTagType(source) {
|
||||
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
||||
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
||||
@@ -342,32 +301,6 @@ function sourceTagType(source) {
|
||||
flex-shrink: 0;
|
||||
color: #909399;
|
||||
}
|
||||
.json-panel {
|
||||
margin-top: 10px;
|
||||
background: #f7f9fb;
|
||||
border: 1px solid #e8ecf0;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.json-panel__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #606266;
|
||||
background: #fafbfc;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.json-panel .code-pre {
|
||||
margin: 0;
|
||||
padding: 12px 14px;
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
.fee-amount {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 500;
|
||||
|
||||
Reference in New Issue
Block a user