//nolint:unused package handlers import ( "fmt" "net/http" "strconv" "time" "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" _ "tyapi-server/internal/application/finance/dto/responses" "tyapi-server/internal/shared/interfaces" ) // FinanceHandler 财务HTTP处理器 type FinanceHandler struct { appService finance.FinanceApplicationService invoiceAppService finance.InvoiceApplicationService adminInvoiceAppService finance.AdminInvoiceApplicationService responseBuilder interfaces.ResponseBuilder validator interfaces.RequestValidator logger *zap.Logger } // NewFinanceHandler 创建财务HTTP处理器 func NewFinanceHandler( appService finance.FinanceApplicationService, invoiceAppService finance.InvoiceApplicationService, adminInvoiceAppService finance.AdminInvoiceApplicationService, responseBuilder interfaces.ResponseBuilder, validator interfaces.RequestValidator, logger *zap.Logger, ) *FinanceHandler { return &FinanceHandler{ appService: appService, invoiceAppService: invoiceAppService, adminInvoiceAppService: adminInvoiceAppService, responseBuilder: responseBuilder, validator: validator, logger: logger, } } // 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 == "" { h.responseBuilder.Unauthorized(c, "用户未登录") 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, "获取钱包信息成功") } // GetUserWalletTransactions 获取用户钱包交易记录 // @Summary 获取用户钱包交易记录 // @Description 获取当前用户的钱包交易记录列表,支持分页和筛选 // @Tags 钱包管理 // @Accept json // @Produce json // @Security Bearer // @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 "获取成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/finance/wallet/transactions [get] func (h *FinanceHandler) GetUserWalletTransactions(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { h.responseBuilder.Unauthorized(c, "用户未登录") return } // 解析查询参数 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 } } // 交易ID筛选 if transactionId := c.Query("transaction_id"); transactionId != "" { filters["transaction_id"] = transactionId } // 产品名称筛选 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) if err != nil { h.logger.Error("获取用户钱包交易记录失败", zap.Error(err)) h.responseBuilder.BadRequest(c, "获取钱包交易记录失败") return } h.responseBuilder.Success(c, result, "获取钱包交易记录成功") } // 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 } } return defaultValue } // 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") return } // 返回成功响应(支付宝要求返回success) c.String(200, "success") } // 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) if err != nil { h.logger.Error("支付宝同步回调处理失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) h.redirectToFailPage(c, outTradeNo, "订单处理失败") return } // 根据环境确定前端域名 frontendDomain := "https://console.tianyuanapi.com" 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) } } // redirectToFailPage 跳转到失败页面 func (h *FinanceHandler) redirectToFailPage(c *gin.Context, outTradeNo, reason string) { frontendDomain := "https://console.tianyuanapi.com" 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) { frontendDomain := "https://console.tianyuanapi.com" 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 创建支付宝充值订单并返回支付链接 // @Tags 钱包管理 // @Accept json // @Produce json // @Security Bearer // @Param request body commands.CreateAlipayRechargeCommand true "充值请求" // @Success 200 {object} responses.AlipayRechargeOrderResponse "创建充值订单成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/finance/wallet/alipay-recharge [post] func (h *FinanceHandler) CreateAlipayRecharge(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { h.responseBuilder.Unauthorized(c, "用户未登录") return } var cmd commands.CreateAlipayRechargeCommand cmd.UserID = userID if err := h.validator.BindAndValidate(c, &cmd); err != nil { return } // 调用应用服务进行完整的业务流程编排 result, err := h.appService.CreateAlipayRechargeOrder(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 if err := h.validator.BindAndValidate(c, &cmd); err != nil { return } if cmd.UserID == "" { h.responseBuilder.BadRequest(c, "缺少用户ID") return } result, err := h.appService.TransferRecharge(c.Request.Context(), &cmd) if err != nil { h.logger.Error("对公转账充值失败", zap.String("user_id", cmd.UserID), zap.Error(err), ) h.responseBuilder.BadRequest(c, err.Error()) return } h.responseBuilder.Success(c, result, "对公转账充值成功") } // 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") return } result, err := h.appService.GiftRecharge(c.Request.Context(), &cmd) if err != nil { h.logger.Error("赠送充值失败", zap.String("user_id", cmd.UserID), zap.Error(err), ) h.responseBuilder.BadRequest(c, err.Error()) return } h.responseBuilder.Success(c, result, "赠送充值成功") } // GetUserRechargeRecords 用户获取自己充值记录分页 func (h *FinanceHandler) GetUserRechargeRecords(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { h.responseBuilder.Unauthorized(c, "用户未登录") return } // 解析查询参数 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 } } // 充值类型筛选 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.GetUserRechargeRecords(c.Request.Context(), userID, filters, options) if err != nil { h.logger.Error("获取用户充值记录失败", zap.Error(err)) h.responseBuilder.BadRequest(c, "获取充值记录失败") return } h.responseBuilder.Success(c, result, "获取充值记录成功") } // 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 } // 时间范围筛选 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) if err != nil { h.logger.Error("获取充值记录失败", zap.Error(err)) h.responseBuilder.BadRequest(c, "获取充值记录失败") return } h.responseBuilder.Success(c, result, "获取充值记录成功") } // GetRechargeConfig 获取充值配置 // @Summary 获取充值配置 // @Description 获取当前环境的充值配置信息(最低充值金额、最高充值金额等) // @Tags 钱包管理 // @Accept json // @Produce json // @Success 200 {object} responses.RechargeConfigResponse "获取充值配置成功" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /api/v1/finance/wallet/recharge-config [get] func (h *FinanceHandler) GetRechargeConfig(c *gin.Context) { result, err := h.appService.GetRechargeConfig(c.Request.Context()) if err != nil { h.logger.Error("获取充值配置失败", zap.Error(err)) h.responseBuilder.BadRequest(c, "获取充值配置失败") return } h.responseBuilder.Success(c, result, "获取充值配置成功") } // GetAlipayOrderStatus 获取支付宝订单状态 // @Summary 获取支付宝订单状态 // @Description 获取支付宝订单的当前状态,用于轮询查询 // @Tags 钱包管理 // @Accept json // @Produce json // @Security Bearer // @Param out_trade_no query string true "商户订单号" // @Success 200 {object} responses.AlipayOrderStatusResponse "获取订单状态成功" // @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/alipay-order-status [get] func (h *FinanceHandler) GetAlipayOrderStatus(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.GetAlipayOrderStatus(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, "获取订单状态成功") } // ==================== 发票相关Handler方法 ==================== // ApplyInvoice 申请开票 // @Summary 申请开票 // @Description 用户申请开票 // @Tags 发票管理 // @Accept json // @Produce json // @Param request body finance.ApplyInvoiceRequest true "申请开票请求" // @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceApplicationResponse} // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 // @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceInfoResponse} // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 "更新发票信息请求" // @Success 200 {object} interfaces.APIResponse // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 "状态筛选" // @Success 200 {object} interfaces.APIResponse{data=dto.InvoiceRecordsResponse} // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 // @Success 200 {object} interfaces.APIResponse{data=dto.AvailableAmountResponse} // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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)" // @Success 200 {object} interfaces.APIResponse{data=dto.PendingApplicationsResponse} // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 "管理员备注" // @Success 200 {object} interfaces.APIResponse // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 "拒绝申请请求" // @Success 200 {object} interfaces.APIResponse // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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 // @Failure 400 {object} interfaces.APIResponse // @Failure 500 {object} interfaces.APIResponse // @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) } // DebugEventSystem 调试事件系统 // @Summary 调试事件系统 // @Description 调试事件系统,用于测试事件触发和处理 // @Tags 系统调试 // @Accept json // @Produce json // @Security Bearer // @Success 200 {object} map[string]interface{} "调试成功" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @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", } h.responseBuilder.Success(c, debugInfo, "事件系统调试信息") }