package handler import ( "crypto/rand" "encoding/hex" "encoding/json" "net/http" "github.com/gin-gonic/gin" "github.com/sundynix/sundynix-shared/contract" ) // GenerateReport: POST /api/v1/reports —— 触发报告生成。 // 组装一个 intent=report 的任务发到 NATS,Dispatcher 走专用编排(规划→分章并行→渲染 docx)。 // 返回 task_id;客户端用 GET /tasks/:id/stream 看实时进度,完成后用 /reports/:id/download 取 Word。 func (h *Handler) GenerateReport(c *gin.Context) { var body struct { Topic string `json:"topic"` KB string `json:"kb"` } if err := c.ShouldBindJSON(&body); err != nil || body.Topic == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "topic required"}) return } id := newReportID() graph, _ := json.Marshal(map[string]any{"topic": body.Topic}) // 占位 DSL,报告编排实际读 Meta task := &contract.Task{ ID: id, Graph: graph, Meta: map[string]any{ contract.MetaIntent: contract.IntentReport, contract.MetaTopic: body.Topic, contract.MetaKB: body.KB, contract.MetaUserID: userID(c), contract.MetaSessionID: sessionID(c), }, } if err := h.bus.PublishTask(c.Request.Context(), task); err != nil { c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()}) return } c.JSON(http.StatusAccepted, gin.H{"task_id": id}) } // ExportReport: GET /api/v1/reports/:id/export?format=docx|md —— 按需把报告源渲染为指定格式并下载。 // 生成阶段只存源;此处经 mcp-go report_export 现渲染("导出时再处理")。PDF 由前端打印预览生成。 func (h *Handler) ExportReport(c *gin.Context) { id := c.Param("id") format := c.DefaultQuery("format", "docx") res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("report_export"), &contract.ToolCall{Tool: "report_export", Args: map[string]any{"task_id": id, "format": format}}) if err != nil || res == nil || !res.OK { msg := "报告尚未生成或已过期" if res != nil && res.Error != "" { msg = res.Error } c.JSON(http.StatusNotFound, gin.H{"error": msg}) return } switch format { case "md", "markdown": c.Header("Content-Disposition", `attachment; filename="`+id+`.md"`) c.Header("Content-Type", "text/markdown; charset=utf-8") c.String(http.StatusOK, res.Content) default: // docx:res.Content 为 mcp-go 落盘的 .docx 路径 c.Header("Content-Disposition", `attachment; filename="`+id+`.docx"`) c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document") c.File(res.Content) } } func newReportID() string { var b [8]byte _, _ = rand.Read(b[:]) return "report_" + hex.EncodeToString(b[:]) }