优化记录日志功能

This commit is contained in:
wuweihua
2026-09-11 16:12:01 +08:00
parent 06211adaac
commit 719d8c7208
13 changed files with 346 additions and 161 deletions
@@ -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);
}
}
}
@@ -19,6 +19,9 @@ import com.ruoyi.framework.config.properties.TmsApiProperties;
import com.ruoyi.price.domain.vo.TmsPriceBillRequest; import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest; import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult; 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.IPrcBillService;
import com.ruoyi.price.service.ITmsPriceQuoteService; import com.ruoyi.price.service.ITmsPriceQuoteService;
@@ -46,6 +49,9 @@ public class TmsPriceController extends BaseController
@Autowired @Autowired
private TmsApiProperties tmsApiProperties; private TmsApiProperties tmsApiProperties;
@Autowired
private IBillSearchStore billSearchStore;
/** /**
* Quote by customerId + productCode + country + weight. * Quote by customerId + productCode + country + weight.
* Match: customer package FIXED > DISCOUNT > product default. * Match: customer package FIXED > DISCOUNT > product default.
@@ -55,7 +61,16 @@ public class TmsPriceController extends BaseController
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode, @RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@Validated @RequestBody TmsPriceQuoteRequest request) @Validated @RequestBody TmsPriceQuoteRequest request)
{ {
assertApiCode(headerApiCode, request == null ? null : request.getApiCode()); 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); TmsPriceQuoteResult result = tmsPriceQuoteService.quote(request);
return success(result); return success(result);
} }
@@ -69,7 +84,15 @@ public class TmsPriceController extends BaseController
@RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode, @RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
@Validated @RequestBody TmsPriceBillRequest request) @Validated @RequestBody TmsPriceBillRequest request)
{ {
assertApiCode(headerApiCode, request == null ? null : request.getApiCode()); 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); TmsPriceQuoteResult result = prcBillService.bill(request);
return success(result); return success(result);
} }
@@ -30,12 +30,13 @@ public class EsLogController extends BaseController
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list( public TableDataInfo list(
@RequestParam(value = "logType", required = false) String logType, @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(); PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue(); int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().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(); TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS); rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("\u67e5\u8be2\u6210\u529f"); rsp.setMsg("\u67e5\u8be2\u6210\u529f");
@@ -8,6 +8,8 @@ public class BillSyncSnapshot
private String bizId; private String bizId;
private String requestJson; private String requestJson;
private String responseJson; private String responseJson;
private String status;
private String errorMessage;
private String updatedAt; private String updatedAt;
public String getDocId() public String getDocId()
@@ -60,6 +62,26 @@ public class BillSyncSnapshot
this.responseJson = responseJson; 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() public String getUpdatedAt()
{ {
return updatedAt; return updatedAt;
@@ -49,21 +49,25 @@ public class EsBillSearchStore implements IBillSearchStore
} }
@Override @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)) if (StringUtils.isEmpty(logType) || StringUtils.isEmpty(bizId))
{ {
return; return;
} }
String docId = EsLogType.docId(logType, bizId); Date now = new Date();
String docId = EsLogType.docId(logType, bizId, now);
try try
{ {
JSONObject doc = new JSONObject(); JSONObject doc = new JSONObject();
doc.put("logType", logType.trim()); doc.put("logType", logType.trim());
doc.put("bizId", bizId.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("request", parseOrRaw(requestJson));
doc.put("response", parseOrRaw(responseJson)); 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(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(doc.toJSONString(), headers); HttpEntity<String> entity = new HttpEntity<String>(doc.toJSONString(), headers);
@@ -82,7 +86,12 @@ public class EsBillSearchStore implements IBillSearchStore
{ {
return null; 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 @Override
@@ -119,7 +128,7 @@ public class EsBillSearchStore implements IBillSearchStore
} }
@Override @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(); BillSearchPage page = new BillSearchPage();
if (pageNum < 1) if (pageNum < 1)
@@ -143,7 +152,7 @@ public class EsBillSearchStore implements IBillSearchStore
sortItem.put("updatedAt.keyword", sortOpt); sortItem.put("updatedAt.keyword", sortOpt);
sort.add(sortItem); sort.add(sortItem);
body.put("sort", sort); body.put("sort", sort);
body.put("query", buildQuery(logType, bizId)); body.put("query", buildQuery(logType, bizId, status));
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(body.toJSONString(), headers); 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.setRequestJson(toJsonString(source.get("request")));
snap.setResponseJson(toJsonString(source.get("response"))); snap.setResponseJson(toJsonString(source.get("response")));
snap.setUpdatedAt(source.getString("updatedAt")); 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; return snap;
} }
private JSONObject buildQuery(String logType, String bizId) private JSONObject buildQuery(String logType, String bizId, String status)
{ {
JSONArray filters = new JSONArray(); JSONArray filters = new JSONArray();
addTerm(filters, "logType", logType); addTerm(filters, "logType", logType);
addWildcard(filters, "bizId", bizId); addWildcard(filters, "bizId", bizId);
addTerm(filters, "status", status);
if (filters.isEmpty()) if (filters.isEmpty())
{ {
JSONObject matchAll = new JSONObject(); 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; package com.ruoyi.price.search;
import java.text.SimpleDateFormat;
import java.util.Date;
/** Common ES business-log types. */ /** Common ES business-log types. */
public final class EsLogType 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) 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 type = logType == null ? "" : logType.trim();
String id = bizId == null ? "" : bizId.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 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 get(String logType, String bizId);
BillSyncSnapshot getByDocId(String docId); 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 public class NoopBillSearchStore implements IBillSearchStore
{ {
@Override @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 @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(); return new BillSearchPage();
} }
@@ -23,7 +23,7 @@ import com.ruoyi.price.domain.vo.TmsPriceQuoteFeeLine;
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult; import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
import com.ruoyi.price.mapper.PrcBillFeeMapper; import com.ruoyi.price.mapper.PrcBillFeeMapper;
import com.ruoyi.price.mapper.PrcBillMapper; 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.EsLogType;
import com.ruoyi.price.search.IBillSearchStore; import com.ruoyi.price.search.IBillSearchStore;
import com.ruoyi.price.service.IPrcBillService; import com.ruoyi.price.service.IPrcBillService;
@@ -48,20 +48,28 @@ public class PrcBillServiceImpl implements IPrcBillService
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult bill(TmsPriceBillRequest request) public TmsPriceQuoteResult bill(TmsPriceBillRequest request)
{ {
if (request == null || StringUtils.isEmpty(request.getOrderNo())) try
{ {
throw new ServiceException(MessageUtils.message("price.bill.orderNoRequired")); if (request == null || StringUtils.isEmpty(request.getOrderNo()))
{
throw new ServiceException(MessageUtils.message("price.bill.orderNoRequired"));
}
request.setOrderNo(request.getOrderNo().trim());
if (StringUtils.isEmpty(request.getBizDate()))
{
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired"));
}
Date bizDate = resolveBizDate(request.getBizDate());
BigDecimal weight = request.getWeight();
TmsPriceQuoteResult result = tmsPriceQuoteService.calculate(request);
persistBill(request, weight, bizDate, result);
return result;
} }
request.setOrderNo(request.getOrderNo().trim()); catch (RuntimeException e)
if (StringUtils.isEmpty(request.getBizDate()))
{ {
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired")); EsBizLogSupport.saveFail(billSearchStore, EsLogType.BILL, EsBizLogSupport.billFailBizId(request), request, e);
throw e;
} }
Date bizDate = resolveBizDate(request.getBizDate());
BigDecimal weight = request.getWeight();
TmsPriceQuoteResult result = tmsPriceQuoteService.calculate(request);
persistBill(request, weight, bizDate, result);
return result;
} }
@Override @Override
@@ -261,31 +269,9 @@ public class PrcBillServiceImpl implements IPrcBillService
} }
} }
r.setFeeDetails(lines); r.setFeeDetails(lines);
fillSnapshotFromSearch(r, bill);
return r; 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) private void fillResultOrder(TmsPriceQuoteResult r, PrcBill bill)
{ {
r.setTrackingNo(bill.getTrackingNo()); r.setTrackingNo(bill.getTrackingNo());
@@ -328,42 +314,6 @@ public class PrcBillServiceImpl implements IPrcBillService
return obj.toJSONString(); 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) private JSONObject parseJsonObject(String json)
{ {
if (StringUtils.isEmpty(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) private Date resolveBizDate(String bizDate)
{ {
if (StringUtils.isEmpty(bizDate)) if (StringUtils.isEmpty(bizDate))
@@ -39,6 +39,7 @@ import com.ruoyi.price.mapper.PrcPriceSetMapper;
import com.ruoyi.price.mapper.PrcQuoteFeeMapper; import com.ruoyi.price.mapper.PrcQuoteFeeMapper;
import com.ruoyi.price.mapper.PrcQuoteMapper; import com.ruoyi.price.mapper.PrcQuoteMapper;
import com.ruoyi.price.mapper.PrcSurchargeRelMapper; import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
import com.ruoyi.price.search.EsBizLogSupport;
import com.ruoyi.price.search.EsLogType; import com.ruoyi.price.search.EsLogType;
import com.ruoyi.price.search.IBillSearchStore; import com.ruoyi.price.search.IBillSearchStore;
import com.ruoyi.price.service.ITmsPriceQuoteService; import com.ruoyi.price.service.ITmsPriceQuoteService;
@@ -92,6 +93,20 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request) 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); validateRequest(request);
Date bizDate = resolveBizDate(request.getBizDate()); Date bizDate = resolveBizDate(request.getBizDate());
+28 -1
View File
@@ -10,6 +10,9 @@
<template #col-logType="{ row }"> <template #col-logType="{ row }">
<el-tag size="small" :type="typeTag(row.logType)">{{ typeLabel(row.logType) }}</el-tag> <el-tag size="small" :type="typeTag(row.logType)">{{ typeLabel(row.logType) }}</el-tag>
</template> </template>
<template #col-status="{ row }">
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
</template>
<template #col-action="{ row }"> <template #col-action="{ row }">
<el-button link type="primary" icon="View" @click="handleView(row)" v-hasPermi="['monitor:eslog:list']">查看</el-button> <el-button link type="primary" icon="View" @click="handleView(row)" v-hasPermi="['monitor:eslog:list']">查看</el-button>
</template> </template>
@@ -23,8 +26,14 @@
<el-descriptions-item label="类型"> <el-descriptions-item label="类型">
<el-tag size="small" :type="typeTag(detail.logType)">{{ typeLabel(detail.logType) }}</el-tag> <el-tag size="small" :type="typeTag(detail.logType)">{{ typeLabel(detail.logType) }}</el-tag>
</el-descriptions-item> </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="业务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> </el-descriptions>
<div class="json-panel"> <div class="json-panel">
<div class="json-panel__head"> <div class="json-panel__head">
@@ -60,13 +69,20 @@ const typeOptions = [
{ label: '试算', value: 'QUOTE' } { label: '试算', value: 'QUOTE' }
] ]
const statusOptions = [
{ label: '成功', value: 'SUCCESS' },
{ label: '失败', value: 'FAIL' }
]
const searchColumns = [ const searchColumns = [
{ label: '类型', prop: 'logType', component: 'select', options: typeOptions, placeholder: '请选择类型', width: '160px', props: { clearable: true } }, { 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' } { label: '业务ID', prop: 'bizId', component: 'input', placeholder: '业务ID', width: '220px' }
] ]
const columns = [ const columns = [
{ label: '类型', prop: 'logType', align: 'center', width: 110, slot: 'logType' }, { 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: '业务ID', prop: 'bizId', minWidth: 180, showOverflowTooltip: true },
{ label: '写入时间', prop: 'updatedAt', align: 'center', width: 180 }, { label: '写入时间', prop: 'updatedAt', align: 'center', width: 180 },
{ label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false } { label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false }
@@ -87,6 +103,16 @@ function typeTag(type) {
return 'info' 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) { function handleView(row) {
drawerTitle.value = 'ES日志详情 #' + (row.bizId || row.docId) drawerTitle.value = 'ES日志详情 #' + (row.bizId || row.docId)
open.value = true open.value = true
@@ -126,6 +152,7 @@ function copyJson(val) {
<style scoped> <style scoped>
.mb12 { margin-bottom: 12px; } .mb12 { margin-bottom: 12px; }
.err-text { color: #f56c6c; word-break: break-all; }
.json-panel { .json-panel {
margin-bottom: 12px; margin-bottom: 12px;
background: #f7f9fb; background: #f7f9fb;
@@ -101,23 +101,6 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</section> </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>
</div> </div>
</el-drawer> </el-drawer>
@@ -223,30 +206,6 @@ function displayFeeName(row) {
return name || typeLabel || row.feeType || '-' 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) { function sourceTagType(source) {
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success' if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning' if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
@@ -342,32 +301,6 @@ function sourceTagType(source) {
flex-shrink: 0; flex-shrink: 0;
color: #909399; 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 { .fee-amount {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
font-weight: 500; font-weight: 500;