Merge branch 'test' into feauture-20261104

# Conflicts:
#	bms-front/src/views/price/billing/components/BillDetail.vue
This commit is contained in:
liuminghui
2026-11-06 17:30:48 +08:00
52 changed files with 3529 additions and 456 deletions
@@ -16,6 +16,7 @@ import com.bms.common.core.page.TableSupport;
import com.bms.price.search.BillSearchPage;
import com.bms.price.search.EsLogType;
import com.bms.price.search.IBillSearchStore;
import com.bms.price.search.PriceChangeGroupPage;
/**
* 来自 ES 的价格变更前后日志(销售价 / 产品价 / 红线价)。
@@ -31,12 +32,40 @@ public class PriceLogController extends BaseController
@GetMapping("/list")
public TableDataInfo list(
@RequestParam(value = "bizId", required = false) String bizId,
@RequestParam(value = "status", required = false) String status)
@RequestParam(value = "status", required = false) String status,
@RequestParam(value = "psvId", required = false) Long psvId,
@RequestParam(value = "priceKind", required = false) String priceKind,
@RequestParam(value = "productCode", required = false) String productCode,
@RequestParam(value = "updateTime", required = false) String updateTime,
@RequestParam(value = "updateTimePrefix", required = false) String updateTimePrefix,
@RequestParam(value = "countryKeyword", required = false) String countryKeyword)
{
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(EsLogType.PRICE_CHANGE, bizId, status, pageNum, pageSize);
BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, psvId, priceKind,
productCode, updateTime, updateTimePrefix, countryKeyword, pageNum, pageSize);
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("查询成功");
rsp.setRows(page.getRows());
rsp.setTotal(page.getTotal());
return rsp;
}
/** Group by productCode + write time for package-version drill-down. */
@PreAuthorize("@ss.hasPermi('monitor:priceLog:list')")
@GetMapping("/groups")
public TableDataInfo groups(
@RequestParam(value = "psvId", required = true) Long psvId,
@RequestParam(value = "status", required = false) String status,
@RequestParam(value = "priceKind", required = false) String priceKind)
{
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
PriceChangeGroupPage page = billSearchStore.searchPriceChangeGroups(psvId, priceKind, status, pageNum,
pageSize);
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("查询成功");
@@ -22,9 +22,12 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.bms.common.annotation.Log;
import com.bms.common.constant.HttpStatus;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.PageDomain;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.core.page.TableSupport;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.DateUtils;
import com.bms.common.utils.MessageUtils;
@@ -54,6 +57,11 @@ import com.bms.price.domain.vo.TmsPriceQuoteRequest;
import com.bms.price.domain.vo.TmsPriceQuoteResult;
import com.bms.price.enums.BillTaskSuccessStatus;
import com.bms.price.enums.BillTaskType;
import com.bms.price.search.BillSearchPage;
import com.bms.price.search.BillSyncSnapshot;
import com.bms.price.search.EsBizLogSupport;
import com.bms.price.search.EsLogType;
import com.bms.price.search.IBillSearchStore;
import com.bms.price.service.IPrcBillingTaskService;
import com.bms.price.service.IPrcOrderBillService;
import com.bms.price.service.ITmsPriceQuoteService;
@@ -84,6 +92,9 @@ public class BillingController extends BaseController
@Autowired
private OrderBillProductCheckService orderBillProductCheckService;
@Autowired
private IBillSearchStore billSearchStore;
/**
* Historical quote list.
*/
@@ -355,6 +366,97 @@ public class BillingController extends BaseController
return success(prcOrderBillService.getBill(billId));
}
/**
* ES BILL logs for this order (billing / upstream sync / fail snapshots).
* bizId prefers transferNo then orderNo (same as write path).
*/
@PreAuthorize("@ss.hasPermi('price:billing:bill') or @ss.hasPermi('price:orderBill:list')")
@GetMapping("/bill/{billId}/history")
public TableDataInfo billHistory(@PathVariable("billId") String billId)
{
if (StringUtils.isEmpty(billId))
{
return getDataTable(new ArrayList<BillSyncSnapshot>());
}
TmsPriceQuoteResult bill = prcOrderBillService.getBill(billId.trim());
if (bill == null)
{
TableDataInfo empty = new TableDataInfo();
empty.setCode(HttpStatus.SUCCESS);
empty.setMsg("查询成功");
empty.setRows(new ArrayList<BillSyncSnapshot>());
empty.setTotal(0L);
return empty;
}
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
String primary = EsBizLogSupport.billBizId(bill.getTransferNo(), bill.getOrderNo());
BillSearchPage page = billSearchStore.search(EsLogType.BILL, primary, null, pageNum, pageSize);
// Also pull by orderNo when transferNo was used as bizId, so older orderNo-keyed rows appear.
if (StringUtils.isNotEmpty(bill.getTransferNo()) && StringUtils.isNotEmpty(bill.getOrderNo())
&& !bill.getTransferNo().trim().equalsIgnoreCase(bill.getOrderNo().trim())
&& pageNum == 1)
{
BillSearchPage byOrder = billSearchStore.search(EsLogType.BILL, bill.getOrderNo().trim(), null, 1,
Math.max(pageSize, 50));
page = mergeBillHistory(page, byOrder, pageSize);
}
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("查询成功");
rsp.setRows(page.getRows());
rsp.setTotal(page.getTotal());
return rsp;
}
private BillSearchPage mergeBillHistory(BillSearchPage primary, BillSearchPage secondary, int pageSize)
{
BillSearchPage out = new BillSearchPage();
Map<String, BillSyncSnapshot> byId = new HashMap<String, BillSyncSnapshot>();
if (primary != null && primary.getRows() != null)
{
for (BillSyncSnapshot row : primary.getRows())
{
if (row != null && StringUtils.isNotEmpty(row.getDocId()))
{
byId.put(row.getDocId(), row);
}
}
}
if (secondary != null && secondary.getRows() != null)
{
for (BillSyncSnapshot row : secondary.getRows())
{
if (row != null && StringUtils.isNotEmpty(row.getDocId()) && !byId.containsKey(row.getDocId()))
{
byId.put(row.getDocId(), row);
}
}
}
List<BillSyncSnapshot> merged = new ArrayList<BillSyncSnapshot>(byId.values());
merged.sort((a, b) -> {
String ta = a == null || a.getUpdateTime() == null ? "" : a.getUpdateTime();
String tb = b == null || b.getUpdateTime() == null ? "" : b.getUpdateTime();
return tb.compareTo(ta);
});
long total = Math.max(primary == null ? 0L : primary.getTotal(), merged.size());
if (secondary != null)
{
total = Math.max(total, secondary.getTotal());
}
out.setTotal(total);
if (merged.size() > pageSize)
{
out.setRows(new ArrayList<BillSyncSnapshot>(merged.subList(0, pageSize)));
}
else
{
out.setRows(merged);
}
return out;
}
/**
* Refund one receivable bill via billing task billType=REFUND.
*/
@@ -1,18 +1,26 @@
package com.bms.web.controller.price;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.annotation.Log;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.StringUtils;
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
import com.bms.price.airbill.domain.SrvBillAirFeeSave;
import com.bms.price.domain.vo.SrvBillAirQuery;
import com.bms.price.domain.vo.SrvBillAirVo;
import com.bms.price.service.ISrvBillAirFeeService;
import com.bms.price.service.ISrvBillAirService;
/**
@@ -25,6 +33,9 @@ public class SrvBillAirController extends BaseController
@Autowired
private ISrvBillAirService srvBillAirService;
@Autowired
private ISrvBillAirFeeService srvBillAirFeeService;
/**
* 提单列表
*/
@@ -55,4 +66,32 @@ public class SrvBillAirController extends BaseController
}
return success(vo);
}
/** 一票提单下未删除的费用 */
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
@GetMapping("/fee/list")
public AjaxResult feeList(@RequestParam("billCode") String billCode)
{
return success(srvBillAirFeeService.listFees(billCode));
}
/** 提单费用的新增、修改、删除日志,按页返回 */
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
@GetMapping("/fee/log")
public TableDataInfo feeLog(@RequestParam("billCode") String billCode)
{
List<SrvBillAirFeeLog> list = srvBillAirFeeService.listLogs(billCode);
TableDataInfo table = getDataTable(list);
table.setRows(new ArrayList<>(list));
return table;
}
/** 只保存本次新增、修改、删除的费用,并写入提单日志 */
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
@Log(title = "提单费用", businessType = BusinessType.UPDATE)
@PostMapping("/fee/save")
public AjaxResult saveFee(@RequestBody SrvBillAirFeeSave command)
{
return success(srvBillAirFeeService.save(command));
}
}
@@ -38,8 +38,8 @@ spring:
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 最大连接池数量(local rebill 16 workers + UI/MQ headroom)
maxActive: 40
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
@@ -51,8 +51,8 @@ spring:
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 最大连接池数量(local rebill 16 workers + UI/MQ headroom)
maxActive: 40
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
@@ -40,6 +40,8 @@ bms:
# 同步已直接复用订单行(跳过每单 Oracle 公网查询),瓶颈在 MySQL 计费写入,
# 同客户行锁冲突由 billWithDbRetry 3 次重试兜底,16 路实测吞吐最高。
order-source-sync-threads: 16
# Order-list local rebill workers (2~24). Pair with Druid maxActive >= threads + 8.
local-rebill-threads: 16
# Night auto-rebill: hours 0-8, pull 500 (all statuses) -> bill -> next 500 until empty / 09:00.
# Empty page => no billing.
night-retry: