账单异步生成优化
This commit is contained in:
+139
-32
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.web.controller.ar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -17,7 +18,9 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
import com.ruoyi.ar.domain.ArBankAccount;
|
||||
import com.ruoyi.ar.domain.ArBillDetailFee;
|
||||
import com.ruoyi.ar.domain.ArBillExport;
|
||||
import com.ruoyi.ar.domain.ArBillGenerateTask;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.domain.vo.ArBillActionRequest;
|
||||
import com.ruoyi.ar.domain.vo.ArBillGenerateQuery;
|
||||
@@ -33,114 +36,218 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import com.ruoyi.price.domain.PrcOrderBill;
|
||||
|
||||
/** 应收账单管理。 */
|
||||
/**
|
||||
* 应收账单 HTTP 接口层。
|
||||
*
|
||||
* <p>负责账单列表、账单生成、确认/作废、异步导出、日志和详情分页接口;
|
||||
* 具体业务规则统一下沉到 {@code IArBillService}。</p>
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ar/bill")
|
||||
@Validated
|
||||
public class ArBillController extends BaseController
|
||||
{
|
||||
public class ArBillController extends BaseController {
|
||||
@Autowired
|
||||
private IArBillService billService;
|
||||
|
||||
/**
|
||||
* 查询账单列表;分页参数由 BaseController.startPage 交给 MyBatis PageHelper 处理。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ArBillQuery query)
|
||||
{
|
||||
public TableDataInfo list(ArBillQuery query) {
|
||||
startPage();
|
||||
List<ArBill> list = billService.selectArBillList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询账单头信息;账单明细通过独立分页接口按需加载。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/{arBillId}")
|
||||
public AjaxResult getInfo(@PathVariable Long arBillId)
|
||||
{
|
||||
public AjaxResult getInfo(@PathVariable Long arBillId) {
|
||||
return success(billService.selectArBillById(arBillId));
|
||||
}
|
||||
|
||||
/** 查询指定币种的公司默认收款账户,账号返回脱敏值。 */
|
||||
/**
|
||||
* 分页查询账单明细,避免详情页一次性加载全部订单。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/{arBillId}/details")
|
||||
public TableDataInfo details(@PathVariable Long arBillId) {
|
||||
startPage();
|
||||
return getDataTable(billService.selectArBillDetails(arBillId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单条账单明细的费用明细,按展开行懒加载。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/{arBillId}/details/{detailId}/fees")
|
||||
public AjaxResult detailFees(@PathVariable Long arBillId, @PathVariable Long detailId) {
|
||||
List<ArBillDetailFee> fees = billService.selectArBillDetailFees(arBillId, detailId);
|
||||
return success(fees);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定币种的公司默认收款账户,账号返回脱敏值。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/default-bank-account")
|
||||
public AjaxResult defaultBankAccount(@RequestParam(required = false, defaultValue = "CNY") String currency)
|
||||
{
|
||||
public AjaxResult defaultBankAccount(@RequestParam(required = false, defaultValue = "CNY") String currency) {
|
||||
ArBankAccount account = billService.getDefaultBankAccount(currency);
|
||||
return success(account);
|
||||
}
|
||||
|
||||
/** 查询生成账单前的订单计费结果。 */
|
||||
/**
|
||||
* 查询生成账单前的订单计费结果。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@GetMapping("/generate/preview")
|
||||
public TableDataInfo preview(ArBillGenerateQuery query)
|
||||
{
|
||||
public AjaxResult preview(ArBillGenerateQuery query) {
|
||||
startPage();
|
||||
List<PrcOrderBill> list = billService.previewBill(query);
|
||||
return getDataTable(list);
|
||||
TableDataInfo pageData = getDataTable(list);
|
||||
AjaxResult result = success();
|
||||
result.put("rows", pageData.getRows());
|
||||
result.put("total", pageData.getTotal());
|
||||
result.put("totalAmount", billService.sumPreviewBill(query));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建账单头和异步生成任务,接口不等待全部订单明细处理完成。
|
||||
*/
|
||||
@Log(title = "应收账单", businessType = BusinessType.INSERT)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@PostMapping("/generate")
|
||||
public AjaxResult generate(@Valid @RequestBody ArBillGenerateRequest request,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId)
|
||||
{
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(request.getRequestId(), headerRequestId);
|
||||
ArBill bill = billService.generateBill(request, getUsername(), requestId);
|
||||
return success(bill).put("request_id", requestId);
|
||||
ArBillGenerateTask task = billService.generateBill(request, getUsername(), requestId);
|
||||
return success(task).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单个应收账单异步生成任务。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/generate/task/{taskId}")
|
||||
public AjaxResult generateTaskInfo(@PathVariable Long taskId) {
|
||||
return success(billService.selectGenerateTask(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户仍在处理中的应收账单生成任务。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/generate/tasks/active")
|
||||
public AjaxResult activeGenerateTasks() {
|
||||
return success(billService.selectActiveGenerateTasks(getUsername()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求取消应收账单异步生成任务。
|
||||
*/
|
||||
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@PostMapping("/generate/task/{taskId}/cancel")
|
||||
public AjaxResult cancelGenerateTask(@PathVariable Long taskId,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(null, headerRequestId);
|
||||
return success(billService.cancelGenerateTask(taskId, getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 整单重新发起应收账单异步生成任务。
|
||||
*/
|
||||
@Log(title = "应收账单", businessType = BusinessType.INSERT)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:generate')")
|
||||
@PostMapping("/generate/task/{taskId}/retry")
|
||||
public AjaxResult retryGenerateTask(@PathVariable Long taskId,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(null, headerRequestId);
|
||||
return success(billService.retryGenerateTask(taskId, getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认已生成完成的账单,使其进入可核销状态。
|
||||
*/
|
||||
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:confirm')")
|
||||
@PutMapping("/{arBillId}/confirm")
|
||||
public AjaxResult confirm(@PathVariable Long arBillId, @RequestBody(required = false) ArBillActionRequest request,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId)
|
||||
{
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId);
|
||||
return success(billService.confirmBill(arBillId, request, getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废尚未发生核销的账单;账单明细保留作为历史快照。
|
||||
*/
|
||||
@Log(title = "应收账单", businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:void')")
|
||||
@PutMapping("/{arBillId}/void")
|
||||
public AjaxResult voidBill(@PathVariable Long arBillId, @RequestBody ArBillActionRequest request,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId)
|
||||
{
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(request == null ? null : request.getRequestId(), headerRequestId);
|
||||
return success(billService.voidBill(arBillId, request, getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅提交导出任务;前端通过导出任务状态接口轮询并在完成后下载。
|
||||
*/
|
||||
@Log(title = "应收账单导出", businessType = BusinessType.EXPORT)
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:export')")
|
||||
@PostMapping("/{arBillId}/export")
|
||||
public AjaxResult export(@PathVariable Long arBillId,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId)
|
||||
{
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId) {
|
||||
String requestId = requestId(null, headerRequestId);
|
||||
return success(billService.exportBill(arBillId, getUsername(), requestId)).put("request_id", requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前账单的导出历史和任务状态。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/{arBillId}/exports")
|
||||
public AjaxResult exports(@PathVariable Long arBillId)
|
||||
{
|
||||
public AjaxResult exports(@PathVariable Long arBillId) {
|
||||
return success(billService.selectExports(arBillId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单个账单导出任务状态,供前端轮询。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:query')")
|
||||
@GetMapping("/export/{exportId}")
|
||||
public AjaxResult exportInfo(@PathVariable Long exportId) {
|
||||
return success(billService.selectExport(exportId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载已成功生成的导出文件;文件内容由服务层从临时文件或配置存储读取。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:bill:export')")
|
||||
@GetMapping("/export/{exportId}/download")
|
||||
public void download(@PathVariable Long exportId, HttpServletResponse response) throws Exception
|
||||
{
|
||||
public void download(@PathVariable Long exportId, HttpServletResponse response) throws Exception {
|
||||
billService.downloadExport(exportId, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询账单操作日志。
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ar:log:list')")
|
||||
@GetMapping("/log/list")
|
||||
public TableDataInfo logs(ArBillLog query)
|
||||
{
|
||||
public TableDataInfo logs(ArBillLog query) {
|
||||
startPage();
|
||||
return getDataTable(billService.selectLogList(query));
|
||||
}
|
||||
|
||||
private String requestId(String bodyRequestId, String headerRequestId)
|
||||
{
|
||||
/**
|
||||
* 解析接口幂等号:优先使用请求体,再使用请求头,最后生成新的 UUID。
|
||||
* 生成账单、确认、作废和导出都依赖该值避免重复提交。
|
||||
*/
|
||||
private String requestId(String bodyRequestId, String headerRequestId) {
|
||||
if (StringUtils.isNotEmpty(bodyRequestId)) return bodyRequestId;
|
||||
if (StringUtils.isNotEmpty(headerRequestId)) return headerRequestId;
|
||||
return IdUtils.fastSimpleUUID();
|
||||
|
||||
@@ -29,8 +29,8 @@ spring:
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置间隔多久检测一次空闲连接,避免远程数据库断开后连接池继续复用失效连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 30000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
@@ -38,7 +38,8 @@ spring:
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
# 异步账单任务借用连接时校验连接有效性,避免拿到已被数据库断开的连接
|
||||
testOnBorrow: true
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
@@ -59,4 +60,4 @@ spring:
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
multi-statement-allow: true
|
||||
|
||||
Reference in New Issue
Block a user