package handler import ( "io" "net/http" "strings" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/service" ) // ExportHandler handles user export/import requests type ExportHandler struct { exportService *service.ExportService } // NewExportHandler creates a new ExportHandler func NewExportHandler(exportService *service.ExportService) *ExportHandler { return &ExportHandler{exportService: exportService} } // ExportUsers 导出用户 // @Summary 导出用户数据 // @Description 导出用户数据为 CSV 或 Excel 格式 // @Tags 数据导入导出 // @Accept json // @Produce json // @Security BearerAuth // @Param format query string false "导出格式" default(csv) Enums(csv, excel) // @Param fields query string false "导出字段,逗号分隔" // @Param keyword query string false "关键词过滤" // @Param status query int false "用户状态过滤" // @Success 200 {file} file "用户数据文件" // @Failure 401 {object} Response "未认证" // @Failure 500 {object} Response "服务器错误" // @Router /api/v1/exports/users [get] func (h *ExportHandler) ExportUsers(c *gin.Context) { format := c.DefaultQuery("format", "csv") fieldsStr := c.Query("fields") keyword := c.Query("keyword") statusStr := c.Query("status") var fields []string if fieldsStr != "" { fields = strings.Split(fieldsStr, ",") } var status *int if statusStr != "" { s, err := strconvAtoi(statusStr) if err == nil { status = &s } } req := &service.ExportUsersRequest{ Format: format, Fields: fields, Keyword: keyword, Status: status, } data, filename, contentType, err := h.exportService.ExportUsers(c.Request.Context(), req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "导出失败: " + err.Error()}) return } c.Header("Content-Type", contentType) c.Header("Content-Disposition", "attachment; filename="+filename) c.Data(http.StatusOK, contentType, data) } // ImportUsers 导入用户 // @Summary 导入用户数据 // @Description 从 CSV 或 Excel 文件导入用户数据 // @Tags 数据导入导出 // @Accept multipart/form-data // @Produce json // @Security BearerAuth // @Param file formData file true "导入文件" // @Param format query string false "文件格式" default(csv) Enums(csv, excel) // @Success 200 {object} Response "导入结果" // @Failure 400 {object} Response "请求参数错误" // @Failure 401 {object} Response "未认证" // @Failure 500 {object} Response "服务器错误" // @Router /api/v1/exports/users [post] func (h *ExportHandler) ImportUsers(c *gin.Context) { file, _, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "请上传文件"}) return } defer file.Close() data, err := io.ReadAll(file) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "读取文件失败"}) return } format := c.DefaultQuery("format", "csv") successCount, failCount, errs := h.exportService.ImportUsers(c.Request.Context(), data, format) c.JSON(http.StatusOK, gin.H{ "code": 0, "data": gin.H{ "success_count": successCount, "fail_count": failCount, "errors": errs, }, }) } // GetImportTemplate 获取导入模板 // @Summary 获取用户导入模板 // @Description 下载用户批量导入的 CSV 或 Excel 模板 // @Tags 数据导入导出 // @Produce json // @Security BearerAuth // @Param format query string false "模板格式" default(csv) Enums(csv, excel) // @Success 200 {file} file "导入模板文件" // @Failure 401 {object} Response "未认证" // @Failure 500 {object} Response "服务器错误" // @Router /api/v1/exports/template [get] func (h *ExportHandler) GetImportTemplate(c *gin.Context) { format := c.DefaultQuery("format", "csv") data, filename, contentType, err := h.exportService.GetImportTemplateByFormat(format) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取模板失败"}) return } c.Header("Content-Type", contentType) c.Header("Content-Disposition", "attachment; filename="+filename) c.Data(http.StatusOK, contentType, data) } func strconvAtoi(s string) (int, error) { var n int for _, c := range s { if c < '0' || c > '9' { return 0, nil } n = n*10 + int(c-'0') } return n, nil }