- 所有Handler方法使用标准{code:0,message:"success",data:...}响应格式
- 修复Cursor分页响应包装(GetAllDevices,GetLoginLogs,ListUsers等)
- 修复AuthHandler和SMSHandler认证方法响应格式
- 修复operation_log.go admin用户operation_type前缀问题
- 修复DashboardPage嵌套stats结构
- 修复LoginLogsPage reset功能stale closure问题
- 修复UsersPage批量操作API调用
- 修复多个前端测试(mock格式、按钮选择、断言逻辑)
- 添加OAuth测试域名白名单
- 新增代码审查流程文档
110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
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}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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,
|
|
},
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|