init
This commit is contained in:
619
internal/handler/report_handler.go
Normal file
619
internal/handler/report_handler.go
Normal file
@@ -0,0 +1,619 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"accounting-app/pkg/api"
|
||||
"accounting-app/internal/models"
|
||||
"accounting-app/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ReportHandler handles HTTP requests for reports
|
||||
type ReportHandler struct {
|
||||
reportService *service.ReportService
|
||||
pdfExportService *service.PDFExportService
|
||||
excelExportService *service.ExcelExportService
|
||||
}
|
||||
|
||||
// NewReportHandler creates a new ReportHandler instance
|
||||
func NewReportHandler(reportService *service.ReportService, pdfExportService *service.PDFExportService, excelExportService *service.ExcelExportService) *ReportHandler {
|
||||
return &ReportHandler{
|
||||
reportService: reportService,
|
||||
pdfExportService: pdfExportService,
|
||||
excelExportService: excelExportService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTransactionSummaryRequest represents the request for transaction summary
|
||||
type GetTransactionSummaryRequest struct {
|
||||
StartDate string `form:"start_date" binding:"required"`
|
||||
EndDate string `form:"end_date" binding:"required"`
|
||||
TargetCurrency *string `form:"target_currency"`
|
||||
ConversionDate *string `form:"conversion_date"`
|
||||
}
|
||||
|
||||
// GetCategorySummaryRequest represents the request for category summary
|
||||
type GetCategorySummaryRequest struct {
|
||||
StartDate string `form:"start_date" binding:"required"`
|
||||
EndDate string `form:"end_date" binding:"required"`
|
||||
TransactionType string `form:"type" binding:"required,oneof=income expense"`
|
||||
TargetCurrency *string `form:"target_currency"`
|
||||
ConversionDate *string `form:"conversion_date"`
|
||||
}
|
||||
|
||||
// GetTrendDataRequest represents the request for trend data
|
||||
type GetTrendDataRequest struct {
|
||||
StartDate string `form:"start_date" binding:"required"`
|
||||
EndDate string `form:"end_date" binding:"required"`
|
||||
Period string `form:"period" binding:"required,oneof=day week month year"`
|
||||
Currency *string `form:"currency"`
|
||||
}
|
||||
|
||||
// GetComparisonDataRequest represents the request for comparison data
|
||||
type GetComparisonDataRequest struct {
|
||||
StartDate string `form:"start_date" binding:"required"`
|
||||
EndDate string `form:"end_date" binding:"required"`
|
||||
Currency *string `form:"currency"`
|
||||
}
|
||||
|
||||
// GetAssetsSummaryRequest represents the request for assets summary
|
||||
type GetAssetsSummaryRequest struct {
|
||||
TargetCurrency *string `form:"target_currency"`
|
||||
ConversionDate *string `form:"conversion_date"`
|
||||
}
|
||||
|
||||
// GetTransactionSummary handles GET /api/v1/reports/summary
|
||||
func (h *ReportHandler) GetTransactionSummary(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetTransactionSummaryRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional target currency
|
||||
var targetCurrency *models.Currency
|
||||
if req.TargetCurrency != nil && *req.TargetCurrency != "" {
|
||||
currency := models.Currency(*req.TargetCurrency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(currency) {
|
||||
api.BadRequest(c, "Invalid target_currency")
|
||||
return
|
||||
}
|
||||
targetCurrency = ¤cy
|
||||
}
|
||||
|
||||
// Parse optional conversion date
|
||||
var conversionDate *time.Time
|
||||
if req.ConversionDate != nil && *req.ConversionDate != "" {
|
||||
date, err := time.Parse("2006-01-02", *req.ConversionDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid conversion_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
conversionDate = &date
|
||||
}
|
||||
|
||||
// Get summary
|
||||
summary, err := h.reportService.GetTransactionSummary(userId.(uint), startDate, endDate, targetCurrency, conversionDate)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get transaction summary: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, summary)
|
||||
}
|
||||
|
||||
// GetCategorySummary handles GET /api/v1/reports/category
|
||||
func (h *ReportHandler) GetCategorySummary(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetCategorySummaryRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse transaction type
|
||||
var transactionType models.TransactionType
|
||||
switch req.TransactionType {
|
||||
case "income":
|
||||
transactionType = models.TransactionTypeIncome
|
||||
case "expense":
|
||||
transactionType = models.TransactionTypeExpense
|
||||
default:
|
||||
api.BadRequest(c, "Invalid transaction type")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional target currency
|
||||
var targetCurrency *models.Currency
|
||||
if req.TargetCurrency != nil && *req.TargetCurrency != "" {
|
||||
currency := models.Currency(*req.TargetCurrency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(currency) {
|
||||
api.BadRequest(c, "Invalid target_currency")
|
||||
return
|
||||
}
|
||||
targetCurrency = ¤cy
|
||||
}
|
||||
|
||||
// Parse optional conversion date
|
||||
var conversionDate *time.Time
|
||||
if req.ConversionDate != nil && *req.ConversionDate != "" {
|
||||
date, err := time.Parse("2006-01-02", *req.ConversionDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid conversion_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
conversionDate = &date
|
||||
}
|
||||
|
||||
// Get summary
|
||||
summary, err := h.reportService.GetCategorySummary(userId.(uint), startDate, endDate, transactionType, targetCurrency, conversionDate)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get category summary: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, summary)
|
||||
}
|
||||
|
||||
// GetTrendData handles GET /api/v1/reports/trend
|
||||
func (h *ReportHandler) GetTrendData(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetTrendDataRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse period type
|
||||
var period service.PeriodType
|
||||
switch req.Period {
|
||||
case "day":
|
||||
period = service.PeriodTypeDay
|
||||
case "week":
|
||||
period = service.PeriodTypeWeek
|
||||
case "month":
|
||||
period = service.PeriodTypeMonth
|
||||
case "year":
|
||||
period = service.PeriodTypeYear
|
||||
default:
|
||||
api.BadRequest(c, "Invalid period type")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional currency
|
||||
var currency *models.Currency
|
||||
if req.Currency != nil && *req.Currency != "" {
|
||||
curr := models.Currency(*req.Currency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(curr) {
|
||||
api.BadRequest(c, "Invalid currency")
|
||||
return
|
||||
}
|
||||
currency = &curr
|
||||
}
|
||||
|
||||
// Get trend data
|
||||
trendData, err := h.reportService.GetTrendData(userId.(uint), startDate, endDate, period, currency)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get trend data: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, trendData)
|
||||
}
|
||||
|
||||
// GetComparisonData handles GET /api/v1/reports/comparison
|
||||
func (h *ReportHandler) GetComparisonData(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetComparisonDataRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional currency
|
||||
var currency *models.Currency
|
||||
if req.Currency != nil && *req.Currency != "" {
|
||||
curr := models.Currency(*req.Currency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(curr) {
|
||||
api.BadRequest(c, "Invalid currency")
|
||||
return
|
||||
}
|
||||
currency = &curr
|
||||
}
|
||||
|
||||
// Get comparison data
|
||||
comparisonData, err := h.reportService.GetComparisonData(userId.(uint), startDate, endDate, currency)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get comparison data: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, comparisonData)
|
||||
}
|
||||
|
||||
// GetAssetsSummary handles GET /api/v1/reports/assets
|
||||
func (h *ReportHandler) GetAssetsSummary(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetAssetsSummaryRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional target currency
|
||||
var targetCurrency *models.Currency
|
||||
if req.TargetCurrency != nil && *req.TargetCurrency != "" {
|
||||
currency := models.Currency(*req.TargetCurrency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(currency) {
|
||||
api.BadRequest(c, "Invalid target_currency")
|
||||
return
|
||||
}
|
||||
targetCurrency = ¤cy
|
||||
}
|
||||
|
||||
// Parse optional conversion date
|
||||
var conversionDate *time.Time
|
||||
if req.ConversionDate != nil && *req.ConversionDate != "" {
|
||||
date, err := time.Parse("2006-01-02", *req.ConversionDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid conversion_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
conversionDate = &date
|
||||
}
|
||||
|
||||
// Get assets summary
|
||||
summary, err := h.reportService.GetAssetsSummary(userId.(uint), targetCurrency, conversionDate)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get assets summary: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, summary)
|
||||
}
|
||||
|
||||
// GetConsumptionHabitsRequest represents the request for consumption habits analysis
|
||||
type GetConsumptionHabitsRequest struct {
|
||||
StartDate string `form:"start_date" binding:"required"`
|
||||
EndDate string `form:"end_date" binding:"required"`
|
||||
Currency *string `form:"currency"`
|
||||
}
|
||||
|
||||
// GetConsumptionHabits handles GET /api/v1/reports/consumption-habits
|
||||
func (h *ReportHandler) GetConsumptionHabits(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetConsumptionHabitsRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional currency
|
||||
var currency *models.Currency
|
||||
if req.Currency != nil && *req.Currency != "" {
|
||||
curr := models.Currency(*req.Currency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(curr) {
|
||||
api.BadRequest(c, "Invalid currency")
|
||||
return
|
||||
}
|
||||
currency = &curr
|
||||
}
|
||||
|
||||
// Get consumption habits
|
||||
habits, err := h.reportService.GetConsumptionHabits(userId.(uint), startDate, endDate, currency)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get consumption habits: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, habits)
|
||||
}
|
||||
|
||||
// GetAssetLiabilityAnalysisRequest represents the request for asset liability analysis
|
||||
type GetAssetLiabilityAnalysisRequest struct {
|
||||
IncludeTrend bool `form:"include_trend"`
|
||||
TrendStartDate *string `form:"trend_start_date"`
|
||||
TrendEndDate *string `form:"trend_end_date"`
|
||||
}
|
||||
|
||||
// GetAssetLiabilityAnalysis handles GET /api/v1/reports/asset-liability-analysis
|
||||
func (h *ReportHandler) GetAssetLiabilityAnalysis(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req GetAssetLiabilityAnalysisRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional trend dates
|
||||
var trendStartDate, trendEndDate *time.Time
|
||||
if req.IncludeTrend {
|
||||
if req.TrendStartDate == nil || req.TrendEndDate == nil {
|
||||
api.BadRequest(c, "trend_start_date and trend_end_date are required when include_trend is true")
|
||||
return
|
||||
}
|
||||
|
||||
startDate, err := time.Parse("2006-01-02", *req.TrendStartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid trend_start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
trendStartDate = &startDate
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", *req.TrendEndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid trend_end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
trendEndDate = &endDate
|
||||
|
||||
// Validate date range
|
||||
if trendEndDate.Before(*trendStartDate) {
|
||||
api.BadRequest(c, "trend_end_date must be after trend_start_date")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get asset liability analysis
|
||||
analysis, err := h.reportService.GetAssetLiabilityAnalysis(userId.(uint), req.IncludeTrend, trendStartDate, trendEndDate)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get asset liability analysis: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, analysis)
|
||||
}
|
||||
|
||||
// isValidCurrency checks if a currency is supported
|
||||
func isValidCurrency(currency models.Currency) bool {
|
||||
supportedCurrencies := models.SupportedCurrencies()
|
||||
for _, c := range supportedCurrencies {
|
||||
if c == currency {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ExportReportRequest represents the request for exporting a report
|
||||
type ExportReportRequest struct {
|
||||
StartDate string `json:"start_date" binding:"required"`
|
||||
EndDate string `json:"end_date" binding:"required"`
|
||||
TargetCurrency *string `json:"target_currency"`
|
||||
Format string `json:"format" binding:"required,oneof=pdf excel"`
|
||||
}
|
||||
|
||||
// ExportReport handles POST /api/v1/reports/export
|
||||
func (h *ReportHandler) ExportReport(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var req ExportReportRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.ValidationError(c, "Invalid request parameters: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse dates
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if endDate.Before(startDate) {
|
||||
api.BadRequest(c, "end_date must be after start_date")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional target currency
|
||||
var targetCurrency *models.Currency
|
||||
if req.TargetCurrency != nil && *req.TargetCurrency != "" {
|
||||
currency := models.Currency(*req.TargetCurrency)
|
||||
// Validate currency
|
||||
if !isValidCurrency(currency) {
|
||||
api.BadRequest(c, "Invalid target_currency")
|
||||
return
|
||||
}
|
||||
targetCurrency = ¤cy
|
||||
}
|
||||
|
||||
// Handle different export formats
|
||||
switch req.Format {
|
||||
case "pdf":
|
||||
// Export as PDF
|
||||
exportReq := service.ExportReportRequest{
|
||||
StartDate: startDate,
|
||||
EndDate: endDate,
|
||||
TargetCurrency: targetCurrency,
|
||||
IncludeCharts: true,
|
||||
}
|
||||
|
||||
pdfData, err := h.pdfExportService.ExportReportToPDF(userId.(uint), exportReq)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to export report as PDF: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Set response headers for PDF download
|
||||
filename := fmt.Sprintf("report_%s_to_%s.pdf", startDate.Format("20060102"), endDate.Format("20060102"))
|
||||
c.Header("Content-Type", "application/pdf")
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
c.Data(200, "application/pdf", pdfData)
|
||||
|
||||
case "excel":
|
||||
// Export as Excel
|
||||
exportReq := service.ExportReportRequest{
|
||||
StartDate: startDate,
|
||||
EndDate: endDate,
|
||||
TargetCurrency: targetCurrency,
|
||||
IncludeCharts: false, // Charts not supported in Excel yet
|
||||
}
|
||||
|
||||
excelData, err := h.excelExportService.ExportReportToExcel(userId.(uint), exportReq)
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to export report as Excel: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Set response headers for Excel download
|
||||
filename := fmt.Sprintf("report_%s_to_%s.xlsx", startDate.Format("20060102"), endDate.Format("20060102"))
|
||||
c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
c.Data(200, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", excelData)
|
||||
|
||||
default:
|
||||
api.BadRequest(c, "Unsupported export format")
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user