微信支付

This commit is contained in:
2025-12-12 15:27:15 +08:00
parent 2c89b8cb26
commit 0d4953c6d3
34 changed files with 1974 additions and 279 deletions

View File

@@ -13,7 +13,7 @@ import (
)
const (
AlipayOrdersTable = "alipay_orders"
AlipayOrdersTable = "typay_orders"
)
type GormAlipayOrderRepository struct {
@@ -72,9 +72,9 @@ func (r *GormAlipayOrderRepository) GetByRechargeID(ctx context.Context, recharg
func (r *GormAlipayOrderRepository) GetByUserID(ctx context.Context, userID string) ([]entities.AlipayOrder, error) {
var orders []entities.AlipayOrder
err := r.GetDB(ctx).
Joins("JOIN recharge_records ON alipay_orders.recharge_id = recharge_records.id").
Joins("JOIN recharge_records ON typay_orders.recharge_id = recharge_records.id").
Where("recharge_records.user_id = ?", userID).
Order("alipay_orders.created_at DESC").
Order("typay_orders.created_at DESC").
Find(&orders).Error
return orders, err
}
@@ -95,4 +95,4 @@ func (r *GormAlipayOrderRepository) Exists(ctx context.Context, id string) (bool
var count int64
err := r.GetDB(ctx).Model(&entities.AlipayOrder{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
}

View File

@@ -163,11 +163,11 @@ func (r *GormRechargeRecordRepository) Count(ctx context.Context, options interf
}
if options.Search != "" {
if hasCompanyNameFilter {
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ? OR rr.wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
} else {
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ? OR wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
}
}
return count, query.Count(&count).Error
@@ -267,11 +267,11 @@ func (r *GormRechargeRecordRepository) List(ctx context.Context, options interfa
if options.Search != "" {
if hasCompanyNameFilter {
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ? OR rr.wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
} else {
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ? OR wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
}
}

View File

@@ -0,0 +1,93 @@
package repositories
import (
"context"
"errors"
"tyapi-server/internal/domains/finance/entities"
domain_finance_repo "tyapi-server/internal/domains/finance/repositories"
"tyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
WechatOrdersTable = "typay_orders"
)
type GormWechatOrderRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.WechatOrderRepository = (*GormWechatOrderRepository)(nil)
func NewGormWechatOrderRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.WechatOrderRepository {
return &GormWechatOrderRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, WechatOrdersTable),
}
}
func (r *GormWechatOrderRepository) Create(ctx context.Context, order entities.WechatOrder) (entities.WechatOrder, error) {
err := r.CreateEntity(ctx, &order)
return order, err
}
func (r *GormWechatOrderRepository) GetByID(ctx context.Context, id string) (entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.SmartGetByID(ctx, id, &order)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.WechatOrder{}, gorm.ErrRecordNotFound
}
return entities.WechatOrder{}, err
}
return order, nil
}
func (r *GormWechatOrderRepository) GetByOutTradeNo(ctx context.Context, outTradeNo string) (*entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.GetDB(ctx).Where("out_trade_no = ?", outTradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormWechatOrderRepository) GetByRechargeID(ctx context.Context, rechargeID string) (*entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.GetDB(ctx).Where("recharge_id = ?", rechargeID).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormWechatOrderRepository) GetByUserID(ctx context.Context, userID string) ([]entities.WechatOrder, error) {
var orders []entities.WechatOrder
// 需要通过充值记录关联查询,这里简化处理
err := r.GetDB(ctx).Find(&orders).Error
return orders, err
}
func (r *GormWechatOrderRepository) Update(ctx context.Context, order entities.WechatOrder) error {
return r.UpdateEntity(ctx, &order)
}
func (r *GormWechatOrderRepository) UpdateStatus(ctx context.Context, id string, status entities.WechatOrderStatus) error {
return r.GetDB(ctx).Model(&entities.WechatOrder{}).Where("id = ?", id).Update("status", status).Error
}
func (r *GormWechatOrderRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.WechatOrder{})
}
func (r *GormWechatOrderRepository) Exists(ctx context.Context, id string) (bool, error) {
return r.ExistsEntity(ctx, id, &entities.WechatOrder{})
}

View File

@@ -2,7 +2,9 @@
package handlers
import (
"bytes"
"fmt"
"io"
"net/http"
"strconv"
"time"
@@ -19,12 +21,12 @@ import (
// FinanceHandler 财务HTTP处理器
type FinanceHandler struct {
appService finance.FinanceApplicationService
invoiceAppService finance.InvoiceApplicationService
adminInvoiceAppService finance.AdminInvoiceApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
appService finance.FinanceApplicationService
invoiceAppService finance.InvoiceApplicationService
adminInvoiceAppService finance.AdminInvoiceApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
}
// NewFinanceHandler 创建财务HTTP处理器
@@ -201,6 +203,123 @@ func (h *FinanceHandler) HandleAlipayCallback(c *gin.Context) {
c.String(200, "success")
}
// HandleWechatPayCallback 处理微信支付回调
// @Summary 微信支付回调
// @Description 处理微信支付异步通知
// @Tags 支付管理
// @Accept application/json
// @Produce text/plain
// @Success 200 {string} string "success"
// @Failure 400 {string} string "fail"
// @Router /api/v1/pay/wechat/callback [post]
func (h *FinanceHandler) HandleWechatPayCallback(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")),
zap.String("content_type", c.GetHeader("Content-Type")),
)
// 读取请求体内容用于调试(注意:读取后需要重新设置,否则后续解析会失败)
bodyBytes, err := c.GetRawData()
if err == nil && len(bodyBytes) > 0 {
h.logger.Info("微信支付回调请求体",
zap.String("body", string(bodyBytes)),
zap.Int("body_size", len(bodyBytes)),
)
// 重新设置请求体,供后续解析使用
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
// 通过应用服务处理微信支付回调
err = h.appService.HandleWechatPayCallback(c.Request.Context(), c.Request)
if err != nil {
h.logger.Error("微信支付回调处理失败",
zap.Error(err),
zap.String("remote_addr", c.ClientIP()),
)
c.String(400, "fail")
return
}
h.logger.Info("微信支付回调处理成功", zap.String("remote_addr", c.ClientIP()))
// 返回成功响应微信要求返回success
c.String(200, "success")
}
// HandleWechatRefundCallback 处理微信退款回调
// @Summary 微信退款回调
// @Description 处理微信退款异步通知
// @Tags 支付管理
// @Accept application/json
// @Produce text/plain
// @Success 200 {string} string "success"
// @Failure 400 {string} string "fail"
// @Router /api/v1/wechat/refund_callback [post]
func (h *FinanceHandler) HandleWechatRefundCallback(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.HandleWechatRefundCallback(c.Request.Context(), c.Request)
if err != nil {
h.logger.Error("微信退款回调处理失败", zap.Error(err))
c.String(400, "fail")
return
}
// 返回成功响应微信要求返回success
c.String(200, "success")
}
// GetWechatOrderStatus 获取微信订单状态
// @Summary 获取微信订单状态
// @Description 根据商户订单号查询微信订单状态
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param out_trade_no query string true "商户订单号"
// @Success 200 {object} responses.WechatOrderStatusResponse "获取订单状态成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "订单不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/wechat-order-status [get]
func (h *FinanceHandler) GetWechatOrderStatus(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
outTradeNo := c.Query("out_trade_no")
if outTradeNo == "" {
h.responseBuilder.BadRequest(c, "缺少商户订单号")
return
}
result, err := h.appService.GetWechatOrderStatus(c.Request.Context(), outTradeNo)
if err != nil {
h.logger.Error("获取微信订单状态失败",
zap.String("user_id", userID),
zap.String("out_trade_no", outTradeNo),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, "获取订单状态失败: "+err.Error())
return
}
h.responseBuilder.Success(c, result, "获取订单状态成功")
}
// HandleAlipayReturn 处理支付宝同步回调
// @Summary 支付宝同步回调
// @Description 处理支付宝同步支付通知,跳转到前端成功页面
@@ -240,7 +359,7 @@ func (h *FinanceHandler) HandleAlipayReturn(c *gin.Context) {
// 通过应用服务处理同步回调,查询订单状态
orderStatus, err := h.appService.HandleAlipayReturn(c.Request.Context(), outTradeNo)
if err != nil {
h.logger.Error("支付宝同步回调处理失败",
h.logger.Error("支付宝同步回调处理失败",
zap.String("out_trade_no", outTradeNo),
zap.Error(err))
h.redirectToFailPage(c, outTradeNo, "订单处理失败")
@@ -257,7 +376,7 @@ func (h *FinanceHandler) HandleAlipayReturn(c *gin.Context) {
switch orderStatus {
case "TRADE_SUCCESS":
// 支付成功,跳转到前端成功页面
successURL := fmt.Sprintf("%s/finance/wallet/success?out_trade_no=%s&trade_no=%s&amount=%s",
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":
@@ -275,8 +394,8 @@ func (h *FinanceHandler) redirectToFailPage(c *gin.Context, outTradeNo, reason s
if gin.Mode() == gin.DebugMode {
frontendDomain = "http://localhost:5173"
}
failURL := fmt.Sprintf("%s/finance/wallet/fail?out_trade_no=%s&reason=%s",
failURL := fmt.Sprintf("%s/finance/wallet/fail?out_trade_no=%s&reason=%s",
frontendDomain, outTradeNo, reason)
c.Redirect(http.StatusFound, failURL)
}
@@ -287,8 +406,8 @@ func (h *FinanceHandler) redirectToProcessingPage(c *gin.Context, outTradeNo, am
if gin.Mode() == gin.DebugMode {
frontendDomain = "http://localhost:5173"
}
processingURL := fmt.Sprintf("%s/finance/wallet/processing?out_trade_no=%s&amount=%s",
processingURL := fmt.Sprintf("%s/finance/wallet/processing?out_trade_no=%s&amount=%s",
frontendDomain, outTradeNo, amount)
c.Redirect(http.StatusFound, processingURL)
}
@@ -319,7 +438,6 @@ func (h *FinanceHandler) CreateAlipayRecharge(c *gin.Context) {
return
}
// 调用应用服务进行完整的业务流程编排
result, err := h.appService.CreateAlipayRechargeOrder(c.Request.Context(), &cmd)
if err != nil {
@@ -343,6 +461,53 @@ func (h *FinanceHandler) CreateAlipayRecharge(c *gin.Context) {
h.responseBuilder.Success(c, result, "支付宝充值订单创建成功")
}
// CreateWechatRecharge 创建微信充值订单
// @Summary 创建微信充值订单
// @Description 创建微信充值订单并返回预支付数据
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.CreateWechatRechargeCommand true "微信充值请求"
// @Success 200 {object} responses.WechatRechargeOrderResponse "创建充值订单成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/wechat-recharge [post]
func (h *FinanceHandler) CreateWechatRecharge(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.CreateWechatRechargeCommand
cmd.UserID = userID
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
result, err := h.appService.CreateWechatRechargeOrder(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("创建微信充值订单失败",
zap.String("user_id", userID),
zap.String("amount", cmd.Amount),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, "创建微信充值订单失败: "+err.Error())
return
}
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, "微信充值订单创建成功")
}
// TransferRecharge 管理员对公转账充值
func (h *FinanceHandler) TransferRecharge(c *gin.Context) {
var cmd commands.TransferRechargeCommand
@@ -849,8 +1014,6 @@ func (h *FinanceHandler) ApproveInvoiceApplication(c *gin.Context) {
return
}
h.responseBuilder.Success(c, nil, "通过发票申请成功")
}
@@ -932,14 +1095,14 @@ func (h *FinanceHandler) AdminDownloadInvoiceFile(c *gin.Context) {
// @Router /api/v1/debug/event-system [post]
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",
"message": "事件系统调试端点已启用",
"handler": "FinanceHandler",
}
h.responseBuilder.Success(c, debugInfo, "事件系统调试信息")
}

View File

@@ -1,6 +1,8 @@
package handlers
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"strconv"
"strings"
"time"
@@ -11,9 +13,6 @@ import (
"tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/shared/interfaces"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ProductAdminHandler 产品管理员HTTP处理器
@@ -1338,7 +1337,7 @@ func (h *ProductAdminHandler) ExportAdminWalletTransactions(c *gin.Context) {
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param user_id query string false "用户ID"
// @Param recharge_type query string false "充值类型" Enums(alipay, transfer, gift)
// @Param recharge_type query string false "充值类型" Enums(alipay, wechat, transfer, gift)
// @Param status query string false "状态" Enums(pending, success, failed)
// @Param min_amount query string false "最小金额"
// @Param max_amount query string false "最大金额"
@@ -1425,7 +1424,7 @@ func (h *ProductAdminHandler) GetAdminRechargeRecords(c *gin.Context) {
// @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv
// @Security Bearer
// @Param user_ids query string false "用户ID列表逗号分隔"
// @Param recharge_type query string false "充值类型" Enums(alipay, transfer, gift)
// @Param recharge_type query string false "充值类型" Enums(alipay, wechat, transfer, gift)
// @Param status query string false "状态" Enums(pending, success, failed)
// @Param start_time query string false "开始时间" format(date-time)
// @Param end_time query string false "结束时间" format(date-time)

View File

@@ -42,6 +42,18 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
alipayGroup.GET("/return", r.financeHandler.HandleAlipayReturn) // 支付宝同步回调
}
// 微信支付回调路由(不需要认证)
wechatPayGroup := engine.Group("/api/v1/pay/wechat")
{
wechatPayGroup.POST("/callback", r.financeHandler.HandleWechatPayCallback) // 微信支付异步回调
}
// 微信退款回调路由(不需要认证)
wechatRefundGroup := engine.Group("/api/v1/wechat")
{
wechatRefundGroup.POST("/refund_callback", r.financeHandler.HandleWechatRefundCallback) // 微信退款异步回调
}
// 财务路由组,需要用户认证
financeGroup := engine.Group("/api/v1/finance")
financeGroup.Use(r.authMiddleware.Handle())
@@ -49,12 +61,14 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
// 钱包相关路由
walletGroup := financeGroup.Group("/wallet")
{
walletGroup.GET("", r.financeHandler.GetWallet) // 获取钱包信息
walletGroup.GET("/transactions", r.financeHandler.GetUserWalletTransactions) // 获取钱包交易记录
walletGroup.GET("/recharge-config", r.financeHandler.GetRechargeConfig) // 获取充值配置
walletGroup.POST("/alipay-recharge", r.financeHandler.CreateAlipayRecharge) // 创建支付宝充值订单
walletGroup.GET("/recharge-records", r.financeHandler.GetUserRechargeRecords) // 用户充值记录分页
walletGroup.GET("", r.financeHandler.GetWallet) // 获取钱包信息
walletGroup.GET("/transactions", r.financeHandler.GetUserWalletTransactions) // 获取钱包交易记录
walletGroup.GET("/recharge-config", r.financeHandler.GetRechargeConfig) // 获取充值配置
walletGroup.POST("/alipay-recharge", r.financeHandler.CreateAlipayRecharge) // 创建支付宝充值订单
walletGroup.POST("/wechat-recharge", r.financeHandler.CreateWechatRecharge) // 创建微信充值订单
walletGroup.GET("/recharge-records", r.financeHandler.GetUserRechargeRecords) // 用户充值记录分页
walletGroup.GET("/alipay-order-status", r.financeHandler.GetAlipayOrderStatus) // 获取支付宝订单状态
walletGroup.GET("/wechat-order-status", r.financeHandler.GetWechatOrderStatus) // 获取微信订单状态
}
}
@@ -62,11 +76,11 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
invoiceGroup := engine.Group("/api/v1/invoices")
invoiceGroup.Use(r.authMiddleware.Handle())
{
invoiceGroup.POST("/apply", r.financeHandler.ApplyInvoice) // 申请开票
invoiceGroup.GET("/info", r.financeHandler.GetUserInvoiceInfo) // 获取用户发票信息
invoiceGroup.PUT("/info", r.financeHandler.UpdateUserInvoiceInfo) // 更新用户发票信息
invoiceGroup.GET("/records", r.financeHandler.GetUserInvoiceRecords) // 获取用户开票记录
invoiceGroup.GET("/available-amount", r.financeHandler.GetAvailableAmount) // 获取可开票金额
invoiceGroup.POST("/apply", r.financeHandler.ApplyInvoice) // 申请开票
invoiceGroup.GET("/info", r.financeHandler.GetUserInvoiceInfo) // 获取用户发票信息
invoiceGroup.PUT("/info", r.financeHandler.UpdateUserInvoiceInfo) // 更新用户发票信息
invoiceGroup.GET("/records", r.financeHandler.GetUserInvoiceRecords) // 获取用户开票记录
invoiceGroup.GET("/available-amount", r.financeHandler.GetAvailableAmount) // 获取可开票金额
invoiceGroup.GET("/:application_id/download", r.financeHandler.DownloadInvoiceFile) // 下载发票文件
}
@@ -74,8 +88,8 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
adminFinanceGroup := engine.Group("/api/v1/admin/finance")
adminFinanceGroup.Use(r.adminAuthMiddleware.Handle())
{
adminFinanceGroup.POST("/transfer-recharge", r.financeHandler.TransferRecharge) // 对公转账充值
adminFinanceGroup.POST("/gift-recharge", r.financeHandler.GiftRecharge) // 赠送充值
adminFinanceGroup.POST("/transfer-recharge", r.financeHandler.TransferRecharge) // 对公转账充值
adminFinanceGroup.POST("/gift-recharge", r.financeHandler.GiftRecharge) // 赠送充值
adminFinanceGroup.GET("/recharge-records", r.financeHandler.GetAdminRechargeRecords) // 管理员充值记录分页
}
@@ -83,7 +97,7 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
adminInvoiceGroup := engine.Group("/api/v1/admin/invoices")
adminInvoiceGroup.Use(r.adminAuthMiddleware.Handle())
{
adminInvoiceGroup.GET("/pending", r.financeHandler.GetPendingApplications) // 获取待处理申请列表
adminInvoiceGroup.GET("/pending", r.financeHandler.GetPendingApplications) // 获取待处理申请列表
adminInvoiceGroup.POST("/:application_id/approve", r.financeHandler.ApproveInvoiceApplication) // 通过发票申请
adminInvoiceGroup.POST("/:application_id/reject", r.financeHandler.RejectInvoiceApplication) // 拒绝发票申请
adminInvoiceGroup.GET("/:application_id/download", r.financeHandler.AdminDownloadInvoiceFile) // 下载发票文件