package server import ( "encoding/json" "github.com/gin-gonic/gin" "github.com/google/uuid" "kra/internal/biz" "kra/internal/service" "net/http" "net/url" "strconv" "strings" "time" ) type exportTemplateRequest struct { ID uint `json:"ID"` DBName string `json:"dbName"` Name string `json:"name"` TableName string `json:"tableName"` TemplateID string `json:"templateID"` TemplateInfo string `json:"templateInfo"` SQL string `json:"sql"` ImportSQL string `json:"importSql"` Limit *int `json:"limit"` Order string `json:"order"` Conditions []struct { From string `json:"from"` Column string `json:"column"` Operator string `json:"operator"` } `json:"conditions"` Joins []struct { Join string `json:"joins"` Table string `json:"table"` On string `json:"on"` } `json:"joinTemplate"` } func exportTemplateDomain(v exportTemplateRequest) *biz.ExportTemplate { out := &biz.ExportTemplate{ID: v.ID, DBName: v.DBName, Name: v.Name, TableName: v.TableName, TemplateID: v.TemplateID, TemplateInfo: v.TemplateInfo, SQL: v.SQL, ImportSQL: v.ImportSQL, Limit: v.Limit, Order: v.Order} for _, x := range v.Conditions { out.Conditions = append(out.Conditions, biz.ExportCondition{From: x.From, Column: x.Column, Operator: x.Operator}) } for _, x := range v.Joins { out.Joins = append(out.Joins, biz.ExportJoin{Join: x.Join, Table: x.Table, On: x.On}) } return out } func exportParams(values url.Values) map[string]string { out := map[string]string{} nested, _ := url.ParseQuery(values.Get("params")) for key, items := range nested { if len(items) > 0 { out[key] = items[0] } } for key, items := range values { if key != "params" && len(items) > 0 { out[key] = items[0] } } return out } type exportToken struct { TemplateID string `json:"templateID"` Params map[string]string `json:"params"` Blank bool `json:"blank"` } func registerExportRoutes(group, public *gin.RouterGroup, system *service.SystemService, svc *service.ExportService) { router := group.Group("/sysExportTemplate") router.POST("/createSysExportTemplate", func(c *gin.Context) { var req exportTemplateRequest if c.ShouldBindJSON(&req) != nil || req.Name == "" || req.TemplateID == "" { fail(c, "参数错误") return } if err := svc.Create(c.Request.Context(), exportTemplateDomain(req)); err != nil { fail(c, "创建失败:"+err.Error()) return } ok(c) }) router.PUT("/updateSysExportTemplate", func(c *gin.Context) { var req exportTemplateRequest if c.ShouldBindJSON(&req) != nil || req.ID == 0 { fail(c, "参数错误") return } if err := svc.Update(c.Request.Context(), exportTemplateDomain(req)); err != nil { fail(c, "更新失败:"+err.Error()) return } ok(c) }) router.DELETE("/deleteSysExportTemplate", func(c *gin.Context) { var req struct { ID uint `json:"ID"` } if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } if err := svc.Delete(c.Request.Context(), []uint{req.ID}); err != nil { fail(c, "删除失败") return } ok(c) }) router.DELETE("/deleteSysExportTemplateByIds", func(c *gin.Context) { var req struct { IDs []uint `json:"ids"` } if c.ShouldBindJSON(&req) != nil { fail(c, "参数错误") return } if err := svc.Delete(c.Request.Context(), req.IDs); err != nil { fail(c, "删除失败") return } ok(c) }) router.GET("/findSysExportTemplate", func(c *gin.Context) { id, _ := strconv.ParseUint(c.Query("ID"), 10, 64) item, err := svc.Template(c.Request.Context(), uint(id), "") if err != nil { fail(c, "查询失败") return } writeResult(c, codeSuccess, gin.H{"resysExportTemplate": item}, "查询成功") }) router.GET("/getSysExportTemplateList", func(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10")) items, total, err := svc.Templates(c.Request.Context(), page, size, &biz.ExportTemplate{Name: c.Query("name"), TableName: c.Query("tableName"), TemplateID: c.Query("templateID")}) if err != nil { fail(c, "获取失败") return } writeResult(c, codeSuccess, pageResult{List: items, Total: total, Page: page, PageSize: size}, "获取成功") }) router.GET("/previewSQL", func(c *gin.Context) { sql, err := svc.Preview(c.Request.Context(), c.Query("templateID"), exportParams(c.Request.URL.Query())) if err != nil { fail(c, "预览失败:"+err.Error()) return } writeResult(c, codeSuccess, gin.H{"sql": sql}, "获取成功") }) issue := func(blank bool) gin.HandlerFunc { return func(c *gin.Context) { tid := c.Query("templateID") if tid == "" { fail(c, "模板ID不能为空") return } token := strings.ReplaceAll(uuid.NewString(), "-", "") raw, _ := json.Marshal(exportToken{TemplateID: tid, Params: exportParams(c.Request.URL.Query()), Blank: blank}) if err := system.CacheSet(c.Request.Context(), "export:"+token, string(raw), 5*time.Minute); err != nil { fail(c, "导出令牌创建失败") return } writeResult(c, codeSuccess, gin.H{"token": token}, "获取成功") } } router.GET("/exportExcel", issue(false)) router.GET("/exportTemplate", issue(true)) router.POST("/importExcel", func(c *gin.Context) { file, err := c.FormFile("file") if err != nil { fail(c, "请选择导入文件") return } opened, err := file.Open() if err != nil { fail(c, "读取文件失败") return } defer opened.Close() tid := c.PostForm("templateID") if tid == "" { tid = c.Query("templateID") } if err = svc.Import(c.Request.Context(), tid, opened); err != nil { fail(c, err.Error()) return } ok(c) }) pub := public.Group("/sysExportTemplate") download := func(expectBlank bool) gin.HandlerFunc { return func(c *gin.Context) { token := c.Query("token") raw, okValue, err := system.CacheGet(c.Request.Context(), "export:"+token) if err != nil || !okValue { fail(c, "导出令牌无效或已过期") return } _ = system.CacheDelete(c.Request.Context(), "export:"+token) var value exportToken if json.Unmarshal([]byte(raw), &value) != nil || value.Blank != expectBlank { fail(c, "导出令牌无效") return } var data []byte var name string if expectBlank { data, name, err = svc.ExportBlankTemplate(c.Request.Context(), value.TemplateID) } else { data, name, err = svc.Export(c.Request.Context(), value.TemplateID, value.Params) } if err != nil { fail(c, "导出失败:"+err.Error()) return } c.Header("Content-Disposition", "attachment; filename*=UTF-8''"+url.QueryEscape(name)) c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", data) } } pub.GET("/exportExcelByToken", download(false)) pub.GET("/exportTemplateByToken", download(true)) }