ba8c6b3c43
- 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>
65 lines
2.0 KiB
Go
65 lines
2.0 KiB
Go
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 的任务发到 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})
|
||
}
|
||
|
||
// 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[:])
|
||
}
|