2025-09-01 18:29:59 +08:00
|
|
|
|
//nolint:unused
|
2025-07-13 16:36:20 +08:00
|
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-28 01:46:39 +08:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2025-07-13 16:36:20 +08:00
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
|
|
|
|
"tyapi-server/internal/application/finance"
|
|
|
|
|
|
"tyapi-server/internal/application/finance/dto/commands"
|
|
|
|
|
|
"tyapi-server/internal/application/finance/dto/queries"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
_ "tyapi-server/internal/application/finance/dto/responses"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
"tyapi-server/internal/shared/interfaces"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// FinanceHandler 财务HTTP处理器
|
|
|
|
|
|
type FinanceHandler struct {
|
2025-08-02 02:54:21 +08:00
|
|
|
|
appService finance.FinanceApplicationService
|
|
|
|
|
|
invoiceAppService finance.InvoiceApplicationService
|
|
|
|
|
|
adminInvoiceAppService finance.AdminInvoiceApplicationService
|
|
|
|
|
|
responseBuilder interfaces.ResponseBuilder
|
|
|
|
|
|
validator interfaces.RequestValidator
|
|
|
|
|
|
logger *zap.Logger
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewFinanceHandler 创建财务HTTP处理器
|
|
|
|
|
|
func NewFinanceHandler(
|
|
|
|
|
|
appService finance.FinanceApplicationService,
|
2025-08-02 02:54:21 +08:00
|
|
|
|
invoiceAppService finance.InvoiceApplicationService,
|
|
|
|
|
|
adminInvoiceAppService finance.AdminInvoiceApplicationService,
|
2025-07-13 16:36:20 +08:00
|
|
|
|
responseBuilder interfaces.ResponseBuilder,
|
2025-07-20 20:53:26 +08:00
|
|
|
|
validator interfaces.RequestValidator,
|
2025-07-13 16:36:20 +08:00
|
|
|
|
logger *zap.Logger,
|
|
|
|
|
|
) *FinanceHandler {
|
|
|
|
|
|
return &FinanceHandler{
|
2025-08-02 02:54:21 +08:00
|
|
|
|
appService: appService,
|
|
|
|
|
|
invoiceAppService: invoiceAppService,
|
|
|
|
|
|
adminInvoiceAppService: adminInvoiceAppService,
|
|
|
|
|
|
responseBuilder: responseBuilder,
|
|
|
|
|
|
validator: validator,
|
|
|
|
|
|
logger: logger,
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetWallet 获取钱包信息
|
|
|
|
|
|
// @Summary 获取钱包信息
|
|
|
|
|
|
// @Description 获取当前用户的钱包详细信息
|
|
|
|
|
|
// @Tags 钱包管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Security Bearer
|
|
|
|
|
|
// @Success 200 {object} responses.WalletResponse "获取钱包信息成功"
|
|
|
|
|
|
// @Failure 401 {object} map[string]interface{} "未认证"
|
|
|
|
|
|
// @Failure 404 {object} map[string]interface{} "钱包不存在"
|
|
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
|
|
|
|
|
// @Router /api/v1/finance/wallet [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetWallet(c *gin.Context) {
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
query := &queries.GetWalletInfoQuery{UserID: userID}
|
|
|
|
|
|
result, err := h.appService.GetWallet(c.Request.Context(), query)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.logger.Error("获取钱包信息失败",
|
|
|
|
|
|
zap.String("user_id", userID),
|
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "获取钱包信息成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetUserWalletTransactions 获取用户钱包交易记录
|
|
|
|
|
|
// @Summary 获取用户钱包交易记录
|
|
|
|
|
|
// @Description 获取当前用户的钱包交易记录列表,支持分页和筛选
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Tags 钱包管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Security Bearer
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Param page query int false "页码" default(1)
|
|
|
|
|
|
// @Param page_size query int false "每页数量" default(10)
|
|
|
|
|
|
// @Param start_time query string false "开始时间 (格式: 2006-01-02 15:04:05)"
|
|
|
|
|
|
// @Param end_time query string false "结束时间 (格式: 2006-01-02 15:04:05)"
|
|
|
|
|
|
// @Param transaction_id query string false "交易ID"
|
|
|
|
|
|
// @Param product_name query string false "产品名称"
|
|
|
|
|
|
// @Param min_amount query string false "最小金额"
|
|
|
|
|
|
// @Param max_amount query string false "最大金额"
|
|
|
|
|
|
// @Success 200 {object} responses.WalletTransactionListResponse "获取成功"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
|
|
|
|
|
// @Failure 401 {object} map[string]interface{} "未认证"
|
|
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Router /api/v1/finance/wallet/transactions [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetUserWalletTransactions(c *gin.Context) {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 解析查询参数
|
|
|
|
|
|
page := h.getIntQuery(c, "page", 1)
|
|
|
|
|
|
pageSize := h.getIntQuery(c, "page_size", 10)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建筛选条件
|
|
|
|
|
|
filters := make(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
|
|
// 时间范围筛选
|
|
|
|
|
|
if startTime := c.Query("start_time"); startTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
|
|
|
|
|
filters["start_time"] = t
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if endTime := c.Query("end_time"); endTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
|
|
|
|
|
filters["end_time"] = t
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 交易ID筛选
|
|
|
|
|
|
if transactionId := c.Query("transaction_id"); transactionId != "" {
|
|
|
|
|
|
filters["transaction_id"] = transactionId
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 产品名称筛选
|
|
|
|
|
|
if productName := c.Query("product_name"); productName != "" {
|
|
|
|
|
|
filters["product_name"] = productName
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 金额范围筛选
|
|
|
|
|
|
if minAmount := c.Query("min_amount"); minAmount != "" {
|
|
|
|
|
|
filters["min_amount"] = minAmount
|
|
|
|
|
|
}
|
|
|
|
|
|
if maxAmount := c.Query("max_amount"); maxAmount != "" {
|
|
|
|
|
|
filters["max_amount"] = maxAmount
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建分页选项
|
|
|
|
|
|
options := interfaces.ListOptions{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Sort: "created_at",
|
|
|
|
|
|
Order: "desc",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.appService.GetUserWalletTransactions(c.Request.Context(), userID, filters, options)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("获取用户钱包交易记录失败", zap.Error(err))
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "获取钱包交易记录失败")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "获取钱包交易记录成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// getIntQuery 获取整数查询参数
|
|
|
|
|
|
func (h *FinanceHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
|
|
|
|
|
|
if value := c.Query(key); value != "" {
|
|
|
|
|
|
if intValue, err := strconv.Atoi(value); err == nil && intValue > 0 {
|
|
|
|
|
|
return intValue
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
return defaultValue
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// HandleAlipayCallback 处理支付宝支付回调
|
|
|
|
|
|
// @Summary 支付宝支付回调
|
|
|
|
|
|
// @Description 处理支付宝异步支付通知
|
|
|
|
|
|
// @Tags 支付管理
|
|
|
|
|
|
// @Accept application/x-www-form-urlencoded
|
|
|
|
|
|
// @Produce text/plain
|
|
|
|
|
|
// @Success 200 {string} string "success"
|
|
|
|
|
|
// @Failure 400 {string} string "fail"
|
|
|
|
|
|
// @Router /api/v1/finance/alipay/callback [post]
|
|
|
|
|
|
func (h *FinanceHandler) HandleAlipayCallback(c *gin.Context) {
|
|
|
|
|
|
// 记录回调请求信息
|
|
|
|
|
|
h.logger.Info("收到支付宝回调请求",
|
|
|
|
|
|
zap.String("method", c.Request.Method),
|
|
|
|
|
|
zap.String("url", c.Request.URL.String()),
|
|
|
|
|
|
zap.String("remote_addr", c.ClientIP()),
|
|
|
|
|
|
zap.String("user_agent", c.GetHeader("User-Agent")),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 通过应用服务处理支付宝回调
|
|
|
|
|
|
err := h.appService.HandleAlipayCallback(c.Request.Context(), c.Request)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.logger.Error("支付宝回调处理失败", zap.Error(err))
|
|
|
|
|
|
c.String(400, "fail")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 返回成功响应(支付宝要求返回success)
|
|
|
|
|
|
c.String(200, "success")
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// HandleAlipayReturn 处理支付宝同步回调
|
|
|
|
|
|
// @Summary 支付宝同步回调
|
|
|
|
|
|
// @Description 处理支付宝同步支付通知,跳转到前端成功页面
|
|
|
|
|
|
// @Tags 支付管理
|
|
|
|
|
|
// @Accept application/x-www-form-urlencoded
|
|
|
|
|
|
// @Produce text/html
|
|
|
|
|
|
// @Success 200 {string} string "支付成功页面"
|
|
|
|
|
|
// @Failure 400 {string} string "支付失败页面"
|
|
|
|
|
|
// @Router /api/v1/finance/alipay/return [get]
|
|
|
|
|
|
func (h *FinanceHandler) HandleAlipayReturn(c *gin.Context) {
|
|
|
|
|
|
// 记录同步回调请求信息
|
|
|
|
|
|
h.logger.Info("收到支付宝同步回调请求",
|
|
|
|
|
|
zap.String("method", c.Request.Method),
|
|
|
|
|
|
zap.String("url", c.Request.URL.String()),
|
|
|
|
|
|
zap.String("remote_addr", c.ClientIP()),
|
|
|
|
|
|
zap.String("user_agent", c.GetHeader("User-Agent")),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取查询参数
|
|
|
|
|
|
outTradeNo := c.Query("out_trade_no")
|
|
|
|
|
|
tradeNo := c.Query("trade_no")
|
|
|
|
|
|
totalAmount := c.Query("total_amount")
|
|
|
|
|
|
|
|
|
|
|
|
h.logger.Info("支付宝同步回调参数",
|
|
|
|
|
|
zap.String("out_trade_no", outTradeNo),
|
|
|
|
|
|
zap.String("trade_no", tradeNo),
|
|
|
|
|
|
zap.String("total_amount", totalAmount),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 验证必要参数
|
|
|
|
|
|
if outTradeNo == "" {
|
|
|
|
|
|
h.logger.Error("支付宝同步回调缺少商户订单号")
|
|
|
|
|
|
h.redirectToFailPage(c, "", "缺少商户订单号")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 通过应用服务处理同步回调,查询订单状态
|
|
|
|
|
|
orderStatus, err := h.appService.HandleAlipayReturn(c.Request.Context(), outTradeNo)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("支付宝同步回调处理失败",
|
|
|
|
|
|
zap.String("out_trade_no", outTradeNo),
|
|
|
|
|
|
zap.Error(err))
|
|
|
|
|
|
h.redirectToFailPage(c, outTradeNo, "订单处理失败")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 根据环境确定前端域名
|
2025-08-23 14:57:37 +08:00
|
|
|
|
frontendDomain := "https://console.tianyuanapi.com"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if gin.Mode() == gin.DebugMode {
|
|
|
|
|
|
frontendDomain = "http://localhost:5173"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据订单状态跳转到相应页面
|
|
|
|
|
|
switch orderStatus {
|
|
|
|
|
|
case "TRADE_SUCCESS":
|
|
|
|
|
|
// 支付成功,跳转到前端成功页面
|
|
|
|
|
|
successURL := fmt.Sprintf("%s/finance/wallet/success?out_trade_no=%s&trade_no=%s&amount=%s",
|
|
|
|
|
|
frontendDomain, outTradeNo, tradeNo, totalAmount)
|
|
|
|
|
|
c.Redirect(http.StatusFound, successURL)
|
|
|
|
|
|
case "WAIT_BUYER_PAY":
|
|
|
|
|
|
// 支付处理中,跳转到处理中页面
|
|
|
|
|
|
h.redirectToProcessingPage(c, outTradeNo, totalAmount)
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 支付失败或取消,跳转到前端失败页面
|
|
|
|
|
|
h.redirectToFailPage(c, outTradeNo, orderStatus)
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// redirectToFailPage 跳转到失败页面
|
|
|
|
|
|
func (h *FinanceHandler) redirectToFailPage(c *gin.Context, outTradeNo, reason string) {
|
2025-08-23 14:57:37 +08:00
|
|
|
|
frontendDomain := "https://console.tianyuanapi.com"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if gin.Mode() == gin.DebugMode {
|
|
|
|
|
|
frontendDomain = "http://localhost:5173"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
failURL := fmt.Sprintf("%s/finance/wallet/fail?out_trade_no=%s&reason=%s",
|
|
|
|
|
|
frontendDomain, outTradeNo, reason)
|
|
|
|
|
|
c.Redirect(http.StatusFound, failURL)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// redirectToProcessingPage 跳转到处理中页面
|
|
|
|
|
|
func (h *FinanceHandler) redirectToProcessingPage(c *gin.Context, outTradeNo, amount string) {
|
2025-08-23 14:57:37 +08:00
|
|
|
|
frontendDomain := "https://console.tianyuanapi.com"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if gin.Mode() == gin.DebugMode {
|
|
|
|
|
|
frontendDomain = "http://localhost:5173"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
processingURL := fmt.Sprintf("%s/finance/wallet/processing?out_trade_no=%s&amount=%s",
|
|
|
|
|
|
frontendDomain, outTradeNo, amount)
|
|
|
|
|
|
c.Redirect(http.StatusFound, processingURL)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateAlipayRecharge 创建支付宝充值订单
|
|
|
|
|
|
// @Summary 创建支付宝充值订单
|
|
|
|
|
|
// @Description 创建支付宝充值订单并返回支付链接
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Tags 钱包管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Security Bearer
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Param request body commands.CreateAlipayRechargeCommand true "充值请求"
|
|
|
|
|
|
// @Success 200 {object} responses.AlipayRechargeOrderResponse "创建充值订单成功"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
|
|
|
|
|
// @Failure 401 {object} map[string]interface{} "未认证"
|
|
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Router /api/v1/finance/wallet/alipay-recharge [post]
|
|
|
|
|
|
func (h *FinanceHandler) CreateAlipayRecharge(c *gin.Context) {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
var cmd commands.CreateAlipayRechargeCommand
|
|
|
|
|
|
cmd.UserID = userID
|
2025-07-20 20:53:26 +08:00
|
|
|
|
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 调用应用服务进行完整的业务流程编排
|
|
|
|
|
|
result, err := h.appService.CreateAlipayRechargeOrder(c.Request.Context(), &cmd)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("创建支付宝充值订单失败",
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.String("user_id", userID),
|
2025-07-28 01:46:39 +08:00
|
|
|
|
zap.String("amount", cmd.Amount),
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.BadRequest(c, "创建支付宝充值订单失败: "+err.Error())
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Info("支付宝充值订单创建成功",
|
|
|
|
|
|
zap.String("user_id", userID),
|
|
|
|
|
|
zap.String("out_trade_no", result.OutTradeNo),
|
|
|
|
|
|
zap.String("amount", cmd.Amount),
|
|
|
|
|
|
zap.String("platform", cmd.Platform),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 返回支付链接和订单信息
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "支付宝充值订单创建成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// TransferRecharge 管理员对公转账充值
|
|
|
|
|
|
func (h *FinanceHandler) TransferRecharge(c *gin.Context) {
|
|
|
|
|
|
var cmd commands.TransferRechargeCommand
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if cmd.UserID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "缺少用户ID")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
result, err := h.appService.TransferRecharge(c.Request.Context(), &cmd)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("对公转账充值失败",
|
|
|
|
|
|
zap.String("user_id", cmd.UserID),
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "对公转账充值成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GiftRecharge 管理员赠送充值
|
|
|
|
|
|
func (h *FinanceHandler) GiftRecharge(c *gin.Context) {
|
|
|
|
|
|
var cmd commands.GiftRechargeCommand
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if cmd.UserID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "缺少用户ID")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
result, err := h.appService.GiftRecharge(c.Request.Context(), &cmd)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("赠送充值失败",
|
|
|
|
|
|
zap.String("user_id", cmd.UserID),
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.BadRequest(c, err.Error())
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "赠送充值成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetUserRechargeRecords 用户获取自己充值记录分页
|
|
|
|
|
|
func (h *FinanceHandler) GetUserRechargeRecords(c *gin.Context) {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 解析查询参数
|
|
|
|
|
|
page := h.getIntQuery(c, "page", 1)
|
|
|
|
|
|
pageSize := h.getIntQuery(c, "page_size", 10)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建筛选条件
|
|
|
|
|
|
filters := make(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
|
|
// 时间范围筛选
|
|
|
|
|
|
if startTime := c.Query("start_time"); startTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
|
|
|
|
|
filters["start_time"] = t
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if endTime := c.Query("end_time"); endTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
|
|
|
|
|
filters["end_time"] = t
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 充值类型筛选
|
|
|
|
|
|
if rechargeType := c.Query("recharge_type"); rechargeType != "" {
|
|
|
|
|
|
filters["recharge_type"] = rechargeType
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 状态筛选
|
|
|
|
|
|
if status := c.Query("status"); status != "" {
|
|
|
|
|
|
filters["status"] = status
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建分页选项
|
|
|
|
|
|
options := interfaces.ListOptions{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Sort: "created_at",
|
|
|
|
|
|
Order: "desc",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.appService.GetUserRechargeRecords(c.Request.Context(), userID, filters, options)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("获取用户充值记录失败", zap.Error(err))
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "获取充值记录失败")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "获取充值记录成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetAdminRechargeRecords 管理员获取充值记录分页
|
|
|
|
|
|
func (h *FinanceHandler) GetAdminRechargeRecords(c *gin.Context) {
|
|
|
|
|
|
// 解析查询参数
|
|
|
|
|
|
page := h.getIntQuery(c, "page", 1)
|
|
|
|
|
|
pageSize := h.getIntQuery(c, "page_size", 10)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建筛选条件
|
|
|
|
|
|
filters := make(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
|
|
// 用户ID筛选
|
|
|
|
|
|
if userID := c.Query("user_id"); userID != "" {
|
|
|
|
|
|
filters["user_id"] = userID
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 时间范围筛选
|
|
|
|
|
|
if startTime := c.Query("start_time"); startTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
|
|
|
|
|
filters["start_time"] = t
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if endTime := c.Query("end_time"); endTime != "" {
|
|
|
|
|
|
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
|
|
|
|
|
filters["end_time"] = t
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 充值类型筛选
|
|
|
|
|
|
if rechargeType := c.Query("recharge_type"); rechargeType != "" {
|
|
|
|
|
|
filters["recharge_type"] = rechargeType
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 状态筛选
|
|
|
|
|
|
if status := c.Query("status"); status != "" {
|
|
|
|
|
|
filters["status"] = status
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建分页选项
|
|
|
|
|
|
options := interfaces.ListOptions{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Sort: "created_at",
|
|
|
|
|
|
Order: "desc",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.appService.GetAdminRechargeRecords(c.Request.Context(), filters, options)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("获取充值记录失败", zap.Error(err))
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "获取充值记录失败")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "获取充值记录成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetRechargeConfig 获取充值配置
|
|
|
|
|
|
// @Summary 获取充值配置
|
|
|
|
|
|
// @Description 获取当前环境的充值配置信息(最低充值金额、最高充值金额等)
|
|
|
|
|
|
// @Tags 钱包管理
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Success 200 {object} responses.RechargeConfigResponse "获取充值配置成功"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Router /api/v1/finance/wallet/recharge-config [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetRechargeConfig(c *gin.Context) {
|
|
|
|
|
|
result, err := h.appService.GetRechargeConfig(c.Request.Context())
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("获取充值配置失败", zap.Error(err))
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "获取充值配置失败")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "获取充值配置成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetAlipayOrderStatus 获取支付宝订单状态
|
|
|
|
|
|
// @Summary 获取支付宝订单状态
|
|
|
|
|
|
// @Description 获取支付宝订单的当前状态,用于轮询查询
|
|
|
|
|
|
// @Tags 钱包管理
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Security Bearer
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Param out_trade_no query string true "商户订单号"
|
|
|
|
|
|
// @Success 200 {object} responses.AlipayOrderStatusResponse "获取订单状态成功"
|
|
|
|
|
|
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Failure 401 {object} map[string]interface{} "未认证"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Failure 404 {object} map[string]interface{} "订单不存在"
|
2025-07-13 16:36:20 +08:00
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// @Router /api/v1/finance/wallet/alipay-order-status [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetAlipayOrderStatus(c *gin.Context) {
|
2025-07-13 16:36:20 +08:00
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
outTradeNo := c.Query("out_trade_no")
|
|
|
|
|
|
if outTradeNo == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "缺少商户订单号")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.appService.GetAlipayOrderStatus(c.Request.Context(), outTradeNo)
|
2025-07-13 16:36:20 +08:00
|
|
|
|
if err != nil {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.logger.Error("获取支付宝订单状态失败",
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.String("user_id", userID),
|
2025-07-28 01:46:39 +08:00
|
|
|
|
zap.String("out_trade_no", outTradeNo),
|
2025-07-13 16:36:20 +08:00
|
|
|
|
zap.Error(err),
|
|
|
|
|
|
)
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.BadRequest(c, "获取订单状态失败: "+err.Error())
|
2025-07-13 16:36:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
h.responseBuilder.Success(c, result, "获取订单状态成功")
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
2025-08-02 02:54:21 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 发票相关Handler方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// ApplyInvoice 申请开票
|
|
|
|
|
|
// @Summary 申请开票
|
|
|
|
|
|
// @Description 用户申请开票
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body finance.ApplyInvoiceRequest true "申请开票请求"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceApplicationResponse}
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/apply [post]
|
|
|
|
|
|
func (h *FinanceHandler) ApplyInvoice(c *gin.Context) {
|
|
|
|
|
|
var req finance.ApplyInvoiceRequest
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "请求参数错误", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
userID := c.GetString("user_id") // 从JWT中获取用户ID
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.invoiceAppService.ApplyInvoice(c.Request.Context(), userID, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "申请开票成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserInvoiceInfo 获取用户发票信息
|
|
|
|
|
|
// @Summary 获取用户发票信息
|
|
|
|
|
|
// @Description 获取用户的发票信息
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Produce json
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceInfoResponse}
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/info [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetUserInvoiceInfo(c *gin.Context) {
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.invoiceAppService.GetUserInvoiceInfo(c.Request.Context(), userID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "获取发票信息失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "获取发票信息成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateUserInvoiceInfo 更新用户发票信息
|
|
|
|
|
|
// @Summary 更新用户发票信息
|
|
|
|
|
|
// @Description 更新用户的发票信息
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body finance.UpdateInvoiceInfoRequest true "更新发票信息请求"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/info [put]
|
|
|
|
|
|
func (h *FinanceHandler) UpdateUserInvoiceInfo(c *gin.Context) {
|
|
|
|
|
|
var req finance.UpdateInvoiceInfoRequest
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "请求参数错误", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := h.invoiceAppService.UpdateUserInvoiceInfo(c.Request.Context(), userID, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, nil, "更新发票信息成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserInvoiceRecords 获取用户开票记录
|
|
|
|
|
|
// @Summary 获取用户开票记录
|
|
|
|
|
|
// @Description 获取用户的开票记录列表
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param page query int false "页码" default(1)
|
|
|
|
|
|
// @Param page_size query int false "每页数量" default(10)
|
|
|
|
|
|
// @Param status query string false "状态筛选"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceRecordsResponse}
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/records [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetUserInvoiceRecords(c *gin.Context) {
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
|
|
|
|
|
status := c.Query("status")
|
|
|
|
|
|
startTime := c.Query("start_time")
|
|
|
|
|
|
endTime := c.Query("end_time")
|
|
|
|
|
|
|
|
|
|
|
|
req := finance.GetInvoiceRecordsRequest{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Status: status,
|
|
|
|
|
|
StartTime: startTime,
|
|
|
|
|
|
EndTime: endTime,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.invoiceAppService.GetUserInvoiceRecords(c.Request.Context(), userID, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "获取开票记录失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "获取开票记录成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DownloadInvoiceFile 下载发票文件
|
|
|
|
|
|
// @Summary 下载发票文件
|
|
|
|
|
|
// @Description 下载指定发票的文件
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Produce application/octet-stream
|
|
|
|
|
|
// @Param application_id path string true "申请ID"
|
|
|
|
|
|
// @Success 200 {file} file
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/{application_id}/download [get]
|
|
|
|
|
|
func (h *FinanceHandler) DownloadInvoiceFile(c *gin.Context) {
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
applicationID := c.Param("application_id")
|
|
|
|
|
|
if applicationID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "申请ID不能为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.invoiceAppService.DownloadInvoiceFile(c.Request.Context(), userID, applicationID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "下载发票文件失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置响应头
|
|
|
|
|
|
c.Header("Content-Type", "application/pdf")
|
|
|
|
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", result.FileName))
|
|
|
|
|
|
c.Header("Content-Length", fmt.Sprintf("%d", len(result.FileContent)))
|
|
|
|
|
|
|
|
|
|
|
|
// 直接返回文件内容
|
|
|
|
|
|
c.Data(http.StatusOK, "application/pdf", result.FileContent)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetAvailableAmount 获取可开票金额
|
|
|
|
|
|
// @Summary 获取可开票金额
|
|
|
|
|
|
// @Description 获取用户当前可开票的金额
|
|
|
|
|
|
// @Tags 发票管理
|
|
|
|
|
|
// @Produce json
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse{data=dto.AvailableAmountResponse}
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/invoices/available-amount [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetAvailableAmount(c *gin.Context) {
|
|
|
|
|
|
userID := c.GetString("user_id")
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
h.responseBuilder.Unauthorized(c, "用户未登录")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.invoiceAppService.GetAvailableAmount(c.Request.Context(), userID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "获取可开票金额失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "获取可开票金额成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 管理员发票相关Handler方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// GetPendingApplications 获取发票申请列表(支持筛选)
|
|
|
|
|
|
// @Summary 获取发票申请列表
|
|
|
|
|
|
// @Description 管理员获取发票申请列表,支持状态和时间范围筛选
|
|
|
|
|
|
// @Tags 管理员-发票管理
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param page query int false "页码" default(1)
|
|
|
|
|
|
// @Param page_size query int false "每页数量" default(10)
|
|
|
|
|
|
// @Param status query string false "状态筛选:pending/completed/rejected"
|
|
|
|
|
|
// @Param start_time query string false "开始时间 (格式: 2006-01-02 15:04:05)"
|
|
|
|
|
|
// @Param end_time query string false "结束时间 (格式: 2006-01-02 15:04:05)"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse{data=dto.PendingApplicationsResponse}
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/admin/invoices/pending [get]
|
|
|
|
|
|
func (h *FinanceHandler) GetPendingApplications(c *gin.Context) {
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
|
|
|
|
|
status := c.Query("status")
|
|
|
|
|
|
startTime := c.Query("start_time")
|
|
|
|
|
|
endTime := c.Query("end_time")
|
|
|
|
|
|
|
|
|
|
|
|
req := finance.GetPendingApplicationsRequest{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Status: status,
|
|
|
|
|
|
StartTime: startTime,
|
|
|
|
|
|
EndTime: endTime,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.adminInvoiceAppService.GetPendingApplications(c.Request.Context(), req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, result, "获取发票申请列表成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ApproveInvoiceApplication 通过发票申请(上传发票)
|
|
|
|
|
|
// @Summary 通过发票申请
|
|
|
|
|
|
// @Description 管理员通过发票申请并上传发票文件
|
|
|
|
|
|
// @Tags 管理员-发票管理
|
|
|
|
|
|
// @Accept multipart/form-data
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param application_id path string true "申请ID"
|
|
|
|
|
|
// @Param file formData file true "发票文件"
|
|
|
|
|
|
// @Param admin_notes formData string false "管理员备注"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/admin/invoices/{application_id}/approve [post]
|
|
|
|
|
|
func (h *FinanceHandler) ApproveInvoiceApplication(c *gin.Context) {
|
|
|
|
|
|
applicationID := c.Param("application_id")
|
|
|
|
|
|
if applicationID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "申请ID不能为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取上传的文件
|
|
|
|
|
|
file, err := c.FormFile("file")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "请选择要上传的发票文件")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开文件
|
|
|
|
|
|
fileHandle, err := file.Open()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "文件打开失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
defer fileHandle.Close()
|
|
|
|
|
|
|
|
|
|
|
|
// 获取管理员备注
|
|
|
|
|
|
adminNotes := c.PostForm("admin_notes")
|
|
|
|
|
|
|
|
|
|
|
|
req := finance.ApproveInvoiceRequest{
|
|
|
|
|
|
AdminNotes: adminNotes,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = h.adminInvoiceAppService.ApproveInvoiceApplication(c.Request.Context(), applicationID, fileHandle, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, nil, "通过发票申请成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RejectInvoiceApplication 拒绝发票申请
|
|
|
|
|
|
// @Summary 拒绝发票申请
|
|
|
|
|
|
// @Description 管理员拒绝发票申请
|
|
|
|
|
|
// @Tags 管理员-发票管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param application_id path string true "申请ID"
|
|
|
|
|
|
// @Param request body finance.RejectInvoiceRequest true "拒绝申请请求"
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Success 200 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/admin/invoices/{application_id}/reject [post]
|
|
|
|
|
|
func (h *FinanceHandler) RejectInvoiceApplication(c *gin.Context) {
|
|
|
|
|
|
applicationID := c.Param("application_id")
|
|
|
|
|
|
if applicationID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "申请ID不能为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req finance.RejectInvoiceRequest
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "请求参数错误", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := h.adminInvoiceAppService.RejectInvoiceApplication(c.Request.Context(), applicationID, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, nil, "拒绝发票申请成功")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// AdminDownloadInvoiceFile 管理员下载发票文件
|
|
|
|
|
|
// @Summary 管理员下载发票文件
|
|
|
|
|
|
// @Description 管理员下载指定发票的文件
|
|
|
|
|
|
// @Tags 管理员-发票管理
|
|
|
|
|
|
// @Produce application/octet-stream
|
|
|
|
|
|
// @Param application_id path string true "申请ID"
|
|
|
|
|
|
// @Success 200 {file} file
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Failure 400 {object} interfaces.APIResponse
|
|
|
|
|
|
// @Failure 500 {object} interfaces.APIResponse
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Router /api/v1/admin/invoices/{application_id}/download [get]
|
|
|
|
|
|
func (h *FinanceHandler) AdminDownloadInvoiceFile(c *gin.Context) {
|
|
|
|
|
|
applicationID := c.Param("application_id")
|
|
|
|
|
|
if applicationID == "" {
|
|
|
|
|
|
h.responseBuilder.BadRequest(c, "申请ID不能为空")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.adminInvoiceAppService.DownloadInvoiceFile(c.Request.Context(), applicationID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
h.responseBuilder.InternalError(c, "下载发票文件失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置响应头
|
|
|
|
|
|
c.Header("Content-Type", "application/pdf")
|
|
|
|
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", result.FileName))
|
|
|
|
|
|
c.Header("Content-Length", fmt.Sprintf("%d", len(result.FileContent)))
|
|
|
|
|
|
|
|
|
|
|
|
// 直接返回文件内容
|
|
|
|
|
|
c.Data(http.StatusOK, "application/pdf", result.FileContent)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// DebugEventSystem 调试事件系统
|
|
|
|
|
|
// @Summary 调试事件系统
|
|
|
|
|
|
// @Description 调试事件系统,用于测试事件触发和处理
|
|
|
|
|
|
// @Tags 系统调试
|
|
|
|
|
|
// @Accept json
|
2025-08-02 02:54:21 +08:00
|
|
|
|
// @Produce json
|
2025-09-01 18:29:59 +08:00
|
|
|
|
// @Security Bearer
|
|
|
|
|
|
// @Success 200 {object} map[string]interface{} "调试成功"
|
|
|
|
|
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
|
|
|
|
|
// @Router /api/v1/debug/event-system [post]
|
2025-08-02 02:54:21 +08:00
|
|
|
|
func (h *FinanceHandler) DebugEventSystem(c *gin.Context) {
|
|
|
|
|
|
h.logger.Info("🔍 请求事件系统调试信息")
|
|
|
|
|
|
|
|
|
|
|
|
// 这里可以添加事件系统的状态信息
|
|
|
|
|
|
// 暂时返回基本信息
|
|
|
|
|
|
debugInfo := map[string]interface{}{
|
|
|
|
|
|
"timestamp": time.Now().Format("2006-01-02 15:04:05"),
|
|
|
|
|
|
"message": "事件系统调试端点已启用",
|
|
|
|
|
|
"handler": "FinanceHandler",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
h.responseBuilder.Success(c, debugInfo, "事件系统调试信息")
|
|
|
|
|
|
}
|