chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xspaceagi</groupId>
<artifactId>app-platform-bill</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>app-platform-bill-web</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.xspaceagi</groupId>
<artifactId>app-platform-bill-application</artifactId>
</dependency>
<dependency>
<groupId>com.xspaceagi</groupId>
<artifactId>app-platform-bill-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,174 @@
package com.xspaceagi.bill.web.controller;
import com.xspaceagi.bill.app.service.BillOrderAppService;
import com.xspaceagi.bill.app.service.BillRevenueAppService;
import com.xspaceagi.bill.app.service.BillWithdrawAppService;
import com.xspaceagi.bill.app.service.ResourceStatAppService;
import com.xspaceagi.bill.sdk.dto.*;
import com.xspaceagi.bill.spec.enums.BizTypeEnum;
import com.xspaceagi.bill.spec.enums.ResourceStatTypeEnum;
import com.xspaceagi.bill.spec.enums.RevenueStatusEnum;
import com.xspaceagi.bill.spec.enums.WithdrawStatusEnum;
import com.xspaceagi.system.sdk.server.IUserRpcService;
import com.xspaceagi.system.sdk.service.dto.UserDetailDto;
import com.xspaceagi.system.spec.annotation.RequireResource;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.ReqResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
@Slf4j
@RestController
@RequestMapping("/api/system/bill")
@Tag(name = "系统账单管理")
public class BillAdminController {
@Resource
private BillWithdrawAppService billWithdrawAppService;
@Resource
private BillRevenueAppService billRevenueAppService;
@Resource
private BillOrderAppService billOrderAppService;
@Resource
private ResourceStatAppService resourceStatAppService;
@Resource
private IUserRpcService iUserRpcService;
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/withdraw/list")
@Operation(summary = "提现管理列表")
public ReqResult<WithdrawApplicationPageDTO> getWithdrawList(
@RequestParam(required = false) WithdrawStatusEnum status,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
WithdrawQueryRequest query = new WithdrawQueryRequest();
query.setStatus(status);
query.setKeyword(keyword);
query.setPageNum(pageNum);
query.setPageSize(pageSize);
if (StringUtils.isNotBlank(keyword)) {
UserDetailDto userDetailDto = iUserRpcService.queryUserDetailByName(keyword);
if (userDetailDto != null) {
query.setUserId(userDetailDto.getId());
}
}
return ReqResult.success(billWithdrawAppService.queryWithdrawApplications(query));
}
@RequireResource(WITHDRAW_AUDIT)
@PostMapping("/withdraw/process")
@Operation(summary = "提现处理(通过/驳回/打款完成)")
public ReqResult<Boolean> processWithdraw(@RequestBody WithdrawProcessRequest request) {
return ReqResult.success(billWithdrawAppService.processWithdraw(request));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/revenue/stats")
@Operation(summary = "收益统计(按月过滤,按用户排行)")
public ReqResult<RevenueStatsDTO> getRevenueStats(
@RequestParam(required = false) String monthStart,
@RequestParam(required = false) String monthEnd,
@RequestParam(required = false) Long userId,
@RequestParam(required = false) RevenueStatusEnum status,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
if (monthStart == null || monthEnd == null) {
java.time.LocalDate now = java.time.LocalDate.now();
monthStart = now.withDayOfMonth(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
monthEnd = now.withDayOfMonth(now.lengthOfMonth()).format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
}
return ReqResult.success(billRevenueAppService.getAdminRevenueStats(monthStart, monthEnd, userId, status, pageNum, pageSize));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/revenue/detail")
@Operation(summary = "查看用户收益明细")
public ReqResult<RevenueDetailPageDTO> getUserRevenueDetail(
@RequestParam Long userId,
@RequestParam(required = false) String dt,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
return ReqResult.success(billRevenueAppService.getUserRevenueDetails(userId, dt, pageNum, pageSize));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/resource-stat/detail")
@Operation(summary = "资源统计明细(按条件查询)")
public ReqResult<ResourceStatPageDTO> getResourceStatDetail(
@RequestParam(required = false) Long userId,
@RequestParam(required = false) ResourceStatTypeEnum type,
@RequestParam(required = false) String targetType,
@RequestParam(required = false) Long targetId,
@RequestParam(required = false) String dtStart,
@RequestParam(required = false) String dtEnd,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
return ReqResult.success(resourceStatAppService.queryResourceStats(
null, userId, type != null ? type.getCode() : null, targetType, targetId, dtStart, dtEnd, pageNum, pageSize));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/resource-stat/summary")
@Operation(summary = "资源统计汇总")
public ReqResult<ResourceStatSummaryDTO> getResourceStatSummary(
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String dtStart,
@RequestParam(required = false) String dtEnd) {
return ReqResult.success(resourceStatAppService.getResourceStatSummary(
null, userId, dtStart, dtEnd));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/order/query")
@Operation(summary = "订单查询")
public ReqResult<OrderPageDTO> queryOrders(
@RequestParam(required = false) Long userId,
@RequestParam(required = false) Long orderId,
@RequestParam(required = false) String orderStatus,
@RequestParam(required = false) String payStatus,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) BizTypeEnum bizType,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
OrderQueryRequest query = new OrderQueryRequest();
query.setUserId(userId);
query.setOrderId(orderId);
query.setOrderStatus(orderStatus);
query.setPayStatus(payStatus);
query.setStartTime(startTime);
query.setEndTime(endTime);
query.setBizType(bizType);
query.setKeyword(keyword);
query.setPageNum(pageNum);
query.setPageSize(pageSize);
return ReqResult.success(billOrderAppService.queryOrders(query));
}
@RequireResource(PAY_EARNINGS_QUERY)
@GetMapping("/withdraw/config")
@Operation(summary = "查询提现配置")
public ReqResult<WithdrawConfigDTO> getWithdrawConfig() {
return ReqResult.success(billWithdrawAppService.getWithdrawConfig(RequestContext.get().getTenantId()));
}
@RequireResource(PAY_EARNINGS_MODIFY)
@PostMapping("/withdraw/config/save")
@Operation(summary = "保存提现配置")
public ReqResult<Boolean> saveWithdrawConfig(@RequestBody SaveWithdrawConfigRequest request) {
request.setTenantId(RequestContext.get().getTenantId());
return ReqResult.success(billWithdrawAppService.saveWithdrawConfig(request));
}
}

View File

@@ -0,0 +1,212 @@
package com.xspaceagi.bill.web.controller;
import com.xspaceagi.bill.app.service.BillOrderAppService;
import com.xspaceagi.bill.app.service.BillRevenueAppService;
import com.xspaceagi.bill.app.service.BillWithdrawAppService;
import com.xspaceagi.bill.app.service.ResourceStatAppService;
import com.xspaceagi.bill.sdk.dto.*;
import com.xspaceagi.bill.spec.enums.PayStatusEnum;
import com.xspaceagi.bill.spec.enums.ResourceStatTypeEnum;
import com.xspaceagi.bill.spec.enums.RevenueTargetTypeEnum;
import com.xspaceagi.bill.spec.enums.RevenueTypeEnum;
import com.xspaceagi.pay.sdk.dto.CashierSessionCreateRequest;
import com.xspaceagi.pay.sdk.dto.CashierSessionCreateResponse;
import com.xspaceagi.pay.sdk.service.IPaymentRpcService;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.net.URI;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/bill")
@Tag(name = "用户账单相关接口")
public class BillController {
@Resource
private BillOrderAppService billOrderAppService;
@Resource
private BillRevenueAppService billRevenueAppService;
@Resource
private BillWithdrawAppService billWithdrawAppService;
@Resource
private IPaymentRpcService iPaymentRpcService;
@Resource
private ResourceStatAppService resourceStatAppService;
@GetMapping("/order/my")
@Operation(summary = "我的订单")
public ReqResult<OrderPageDTO> getMyOrders(
@RequestParam(required = false) String orderStatus,
@RequestParam(required = false) String payStatus,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
OrderQueryRequest query = new OrderQueryRequest();
query.setUserId(RequestContext.get().getUserId());
query.setOrderStatus(orderStatus);
query.setPayStatus(payStatus);
query.setPageNum(pageNum);
query.setPageSize(pageSize);
return ReqResult.success(billOrderAppService.queryOrders(query));
}
@GetMapping("/revenue/daily")
@Operation(summary = "我的每日收益")
public ReqResult<List<DailyRevenueDTO>> getMyDailyRevenue(
@RequestParam(required = false) RevenueTypeEnum type,
@RequestParam(required = false) RevenueTargetTypeEnum targetType,
@RequestParam(required = false) Long targetId,
@RequestParam(required = false) String dt) {
RevenueQueryRequest query = new RevenueQueryRequest();
query.setType(type);
query.setTargetType(targetType);
query.setTargetId(targetId);
query.setUserId(RequestContext.get().getUserId());
query.setDt(dt);
return ReqResult.success(billRevenueAppService.queryDailyRevenue(query));
}
@GetMapping("/revenue/detail")
@Operation(summary = "我的收益明细")
public ReqResult<RevenueDetailPageDTO> getMyRevenueDetail(
@RequestParam(required = false) RevenueTypeEnum type,
@RequestParam(required = false) RevenueTargetTypeEnum targetType,
@RequestParam(required = false) Long targetId,
@RequestParam(required = false) String dt,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
RevenueQueryRequest query = new RevenueQueryRequest();
query.setUserId(RequestContext.get().getUserId());
query.setType(type);
query.setTargetType(targetType);
query.setTargetId(targetId);
query.setDt(dt);
query.setPageNum(pageNum);
query.setPageSize(pageSize);
return ReqResult.success(billRevenueAppService.queryRevenueDetail(query));
}
@PostMapping("/withdraw/create")
@Operation(summary = "创建提现申请")
public ReqResult<WithdrawApplicationDTO> createWithdraw() {
RequestContext<Object> ctx = RequestContext.get();
return ReqResult.success(billWithdrawAppService.createWithdrawApplication(ctx.getTenantId(), ctx.getUserId()));
}
@GetMapping("/withdraw/records")
@Operation(summary = "我的提现记录")
public ReqResult<WithdrawApplicationPageDTO> getMyWithdrawRecords(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
WithdrawQueryRequest query = new WithdrawQueryRequest();
query.setUserId(RequestContext.get().getUserId());
query.setPageNum(pageNum);
query.setPageSize(pageSize);
return ReqResult.success(billWithdrawAppService.queryWithdrawApplications(query));
}
@GetMapping("/revenue/stats")
@Operation(summary = "我的收益统计")
public ReqResult<RevenueStatsDTO> getMyRevenueStats() {
return ReqResult.success(billRevenueAppService.getRevenueStats(RequestContext.get().getUserId()));
}
@GetMapping("/order/settlement-status")
@Operation(
summary = "支付结算页轮询订单状态",
description = "中间结算页使用;非终态时会主动同步网关并通知 Billsettled=true 表示可安全回跳业务页")
public ReqResult<OrderSettlementStatusResponse> getOrderSettlementStatus(@RequestParam Long orderId) {
Assert.notNull(orderId, "orderId can not be null");
return ReqResult.success(
billOrderAppService.getOrderSettlementStatus(RequestContext.get().getUserId(), orderId));
}
@GetMapping("/order/pay/cashier")
@Operation(summary = "获取订单收银台地址",
description = "returnUrl 为前端结算页完整地址(支付完成后收银台跳转目标),须自行携带 orderId、业务回跳 callback/returnUrl 等 query"
+ "示例:{前端域名}/pay/settlement?orderId={orderId}&returnUrl={业务页编码}")
public ReqResult<CashierSessionCreateResponse> getCashierUrl(
@RequestParam Long orderId, @RequestParam String returnUrl) {
Assert.notNull(orderId, "orderId can not be null");
Assert.hasText(returnUrl, "returnUrl can not be blank");
OrderDTO orderDTO = billOrderAppService.queryOrder(orderId);
if (orderDTO == null) {
throw new IllegalArgumentException("order not found");
}
if (!RequestContext.get().getUserId().equals(orderDTO.getUserId())) {
throw new BizException("USER_NOT_MATCH", "The order data does not match the current user.");
}
if (orderDTO.getPayStatus() != PayStatusEnum.PENDING) {
throw new BizException("ORDER_NOT_PENDING", "The order status is incorrect.");
}
if (orderDTO.getExtra().get("gatewayPaymentOrderNo") == null) {
throw new BizException("The order data is incomplete.");
}
CashierSessionCreateRequest paymentCreateCashierSessionRequest = new CashierSessionCreateRequest();
paymentCreateCashierSessionRequest.setGatewayPaymentOrderNo(String.valueOf(orderDTO.getExtra().get("gatewayPaymentOrderNo")));
paymentCreateCashierSessionRequest.setBizRedirectUrl(validateSettlementPageUrl(returnUrl.trim()));
CashierSessionCreateResponse cashierSession = iPaymentRpcService.createCashierSession(paymentCreateCashierSessionRequest);
return ReqResult.success(cashierSession);
}
/** 校验前端传入的结算页 URLhttp/https。 */
private static String validateSettlementPageUrl(String url) {
try {
URI uri = URI.create(url);
String scheme = uri.getScheme();
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
throw new BizException(ErrorCodeEnum.INVALID_PARAM.getCode(), "returnUrl must start with http or https");
}
if (!StringUtils.hasText(uri.getHost())) {
throw new BizException(ErrorCodeEnum.INVALID_PARAM.getCode(), "Invalid returnUrl");
}
return url;
} catch (IllegalArgumentException e) {
throw new BizException(ErrorCodeEnum.INVALID_PARAM.getCode(), "Invalid returnUrl");
}
}
@GetMapping("/resource-stat/my")
@Operation(summary = "我的资源统计明细")
public ReqResult<ResourceStatPageDTO> getMyResourceStat(
@RequestParam(required = false) ResourceStatTypeEnum type,
@RequestParam(required = false) String targetType,
@RequestParam(required = false) Long targetId,
@RequestParam(required = false) String dtStart,
@RequestParam(required = false) String dtEnd,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
RequestContext<Object> ctx = RequestContext.get();
return ReqResult.success(resourceStatAppService.queryResourceStats(ctx.getTenantId(), ctx.getUserId(), type != null ? type.getCode() : null, targetType, targetId, dtStart, dtEnd, pageNum, pageSize));
}
@GetMapping("/resource-stat/my-summary")
@Operation(summary = "我的资源统计汇总")
public ReqResult<ResourceStatSummaryDTO> getMyResourceStatSummary(
@RequestParam(required = false) String dtStart,
@RequestParam(required = false) String dtEnd) {
RequestContext<Object> ctx = RequestContext.get();
return ReqResult.success(resourceStatAppService.getResourceStatSummary(
ctx.getTenantId(), ctx.getUserId(), dtStart, dtEnd));
}
@GetMapping("/withdraw/config")
@Operation(summary = "查询提现配置")
public ReqResult<WithdrawConfigDTO> getWithdrawConfig() {
return ReqResult.success(billWithdrawAppService.getWithdrawConfig(RequestContext.get().getTenantId()));
}
}