Files
sundynix-agentix/sundynix-gateway/internal/handler/report.go
T
Blizzard ba8c6b3c43 feat(report): 报告生成端到端 — 规划→分章并行检索撰写→渲染真实 Word
- shared: 新增 intent=report 任务约定 + ReportPath(跨进程共享落盘目录,零配置对齐)
- dispatcher: handleReport 专用编排(DeepSeek 规划大纲 → 各章并行 RAG 检索+撰写
  → 汇聚 → report_render),Pool.Chat 非流式聚合;进度与正文经 Token 流实时回流
- mcp-go: 用标准库 archive/zip + OOXML 拼出真实可打开的 .docx(零额外依赖),
  report_render 工具落盘到共享目录;附 docx 有效性测试
- gateway: POST /reports 触发;GET /reports/:id/download 下发 Word
- desktop: 新增「报告」页(主题→实时编排进度→下载 Word),左导航置为就绪

实测:DeepSeek 生成 5 章报告 → 渲染 5KB docx → file 识别为 Microsoft Word 2007+
→ textutil 提取标题/各章正文完整。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 14:02:21 +08:00

65 lines
2.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/sundynix/sundynix-shared/contract"
)
// GenerateReport: POST /api/v1/reports —— 触发报告生成。
// 组装一个 intent=report 的任务发到 NATSDispatcher 走专用编排(规划→分章并行→渲染 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})
}
// DownloadReport: GET /api/v1/reports/:id/download —— 下载已渲染的 Word 文档。
func (h *Handler) DownloadReport(c *gin.Context) {
id := c.Param("id")
path := contract.ReportPath(id)
if _, err := os.Stat(path); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "报告尚未生成或已过期"})
return
}
c.Header("Content-Disposition", `attachment; filename="`+id+`.docx"`)
c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
c.File(path)
}
func newReportID() string {
var b [8]byte
_, _ = rand.Read(b[:])
return "report_" + hex.EncodeToString(b[:])
}