package controllers import ( "encoding/csv" "encoding/json" "fmt" "reflect" "strconv" "time" "github.com/gin-gonic/gin" ) // ExportHelper provides export utilities for CSV, JSON, etc. type ExportHelper struct{} // NewExportHelper creates a new ExportHelper instance func NewExportHelper() *ExportHelper { return &ExportHelper{} } // ExportToCSV exports data to CSV format func (eh *ExportHelper) ExportToCSV(c *gin.Context, data interface{}, filename string, headers []string) error { c.Header("Content-Type", "text/csv") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) writer := csv.NewWriter(c.Writer) defer writer.Flush() // Write headers if err := writer.Write(headers); err != nil { return err } // Convert data to slice of records records, err := eh.convertToCSVRecords(data) if err != nil { return err } // Write records for _, record := range records { if err := writer.Write(record); err != nil { return err } } return nil } // ExportToJSON exports data to JSON format func (eh *ExportHelper) ExportToJSON(c *gin.Context, data interface{}, filename string) error { c.Header("Content-Type", "application/json") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) return json.NewEncoder(c.Writer).Encode(data) } // convertToCSVRecords converts a slice of structs to CSV records func (eh *ExportHelper) convertToCSVRecords(data interface{}) ([][]string, error) { v := reflect.ValueOf(data) if v.Kind() != reflect.Slice { return nil, fmt.Errorf("data must be a slice") } records := make([][]string, 0, v.Len()) for i := 0; i < v.Len(); i++ { item := v.Index(i) if item.Kind() == reflect.Ptr { item = item.Elem() } record := make([]string, 0, item.NumField()) for j := 0; j < item.NumField(); j++ { field := item.Field(j) record = append(record, eh.formatFieldValue(field)) } records = append(records, record) } return records, nil } // formatFieldValue formats a field value for CSV export func (eh *ExportHelper) formatFieldValue(v reflect.Value) string { switch v.Kind() { case reflect.String: return v.String() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return strconv.FormatInt(v.Int(), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return strconv.FormatUint(v.Uint(), 10) case reflect.Float32, reflect.Float64: return strconv.FormatFloat(v.Float(), 'f', -1, 64) case reflect.Bool: return strconv.FormatBool(v.Bool()) case reflect.Struct: // Handle time.Time if t, ok := v.Interface().(time.Time); ok { return t.Format(time.RFC3339) } return fmt.Sprintf("%v", v.Interface()) case reflect.Ptr: if v.IsNil() { return "" } return eh.formatFieldValue(v.Elem()) default: return fmt.Sprintf("%v", v.Interface()) } } // ImportFromCSV imports data from CSV file func (eh *ExportHelper) ImportFromCSV(c *gin.Context, formFieldName string) ([][]string, error) { file, err := c.FormFile(formFieldName) if err != nil { return nil, err } f, err := file.Open() if err != nil { return nil, err } defer f.Close() reader := csv.NewReader(f) records, err := reader.ReadAll() if err != nil { return nil, err } return records, nil } // Global ExportHelper instance var Exporter = NewExportHelper()