| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package tools
- // 站点分析结果(只读,与 ToolBox“站点分析管理”同源):分析任务由页面创建,这里只读取结果。
- import (
- "context"
- "errors"
- "fmt"
- "strings"
- "github.com/mark3labs/mcp-go/mcp"
- "go.mongodb.org/mongo-driver/bson/primitive"
- "go.mongodb.org/mongo-driver/mongo"
- "fwh-toolbox-server/agentmcp/kit"
- "fwh-toolbox-server/domain"
- "fwh-toolbox-server/dto"
- "fwh-toolbox-server/services"
- )
- const siteAnalysisSource = "ToolBox 站点分析"
- // ---- site_analysis_list ----
- type siteAnalysisListArgs struct {
- SN string `json:"sn,omitempty" jsonschema:"站点 SN;不填查全部"`
- Progress string `json:"progress,omitempty" jsonschema:"进度:pending、running、complete、error"`
- Page int `json:"page,omitempty" jsonschema:"页码,默认 1"`
- Size int `json:"size,omitempty" jsonschema:"每页条数,1~100,默认 20"`
- }
- var SiteAnalysisList = kit.Def[siteAnalysisListArgs]{
- Name: "site_analysis_list",
- Title: "查询站点分析任务",
- Description: "查询 ToolBox 中已创建的站点分析任务(页面创建,分析一段时间内站点的上下线、模式切换、告警、事件与运行数据)。" +
- "返回 total 与 items:id(用于 site_analysis_timeline、site_analysis_export)、sn、description、timePeriod(分析时间段)、item(分析项)、progress、trigger、user。",
- ReadOnly: true,
- Properties: map[string]map[string]any{"progress": {"enum": []string{"pending", "running", "complete", "error"}}},
- Handler: func(ctx context.Context, a siteAnalysisListArgs) (*mcp.CallToolResult, error) {
- page, size := kit.Page(a.Page), kit.Limit(a.Size, 20, 100)
- var sn, progress *string
- if v := strings.TrimSpace(a.SN); v != "" {
- sn = &v
- }
- if a.Progress != "" {
- progress = &a.Progress
- }
- res, err := services.GetSiteAnalysisManageList(uint(page), uint(size), sn, nil, progress, nil)
- if err != nil {
- return kit.Upstream(siteAnalysisSource, err), nil
- }
- return kit.List(res.Data, int(res.Total), size, "用 page 翻页,或用 sn 缩小范围"), nil
- },
- }
- // ---- site_analysis_timeline ----
- type siteAnalysisTimelineArgs struct {
- ID string `json:"id" jsonschema:"分析任务 id(site_analysis_list 返回)"`
- Type string `json:"type,omitempty" jsonschema:"只看某类:offline(离线)、online(上线)、mode_change(模式切换)、alarm(告警)、event(事件);默认全部"`
- Limit int `json:"limit,omitempty" jsonschema:"最多返回条数,1~500,默认 200"`
- }
- var SiteAnalysisTimeline = kit.Def[siteAnalysisTimelineArgs]{
- Name: "site_analysis_timeline",
- Title: "查看站点分析时间线",
- Description: "查看一个已完成的站点分析任务的时间线:上下线、模式切换、告警、事件,按时间排序,以表格返回(time、type、info、data,data 超过 300 字截断)。" +
- "时间线很长或需要运行数据(201 报文)时用 site_analysis_export 导出 CSV 分析。",
- ReadOnly: true,
- Properties: map[string]map[string]any{"type": {"enum": []string{"offline", "online", "mode_change", "alarm", "event"}}},
- Handler: func(ctx context.Context, a siteAnalysisTimelineArgs) (*mcp.CallToolResult, error) {
- data, fail := siteAnalysisData(a.ID)
- if fail != nil {
- return fail, nil
- }
- limit := kit.Limit(a.Limit, 200, 500)
- rows := [][]string{}
- for _, it := range data.Data.Timeline {
- if a.Type != "" && string(it.Type) != a.Type {
- continue
- }
- rows = append(rows, []string{it.Time, string(it.Type), deref(it.Info), truncate(deref(it.Data), 300)})
- }
- if len(rows) == 0 {
- return kit.Empty("时间线中没有符合条件的记录(任务进度:" + string(data.Progress) + ")。"), nil
- }
- return kit.Table([]string{"time", "type", "info", "data"}, rows, limit, "超过 limit 时请指定 type,或用 site_analysis_export 导出"), nil
- },
- }
- // ---- site_analysis_export ----
- type siteAnalysisExportArgs struct {
- ID string `json:"id" jsonschema:"分析任务 id(site_analysis_list 返回)"`
- }
- var SiteAnalysisExport = kit.Def[siteAnalysisExportArgs]{
- Name: "site_analysis_export",
- Title: "导出站点分析结果",
- Description: "把一个站点分析任务的全部结果导出为 CSV 文件保存到会话工作区:section=timeline 为时间线(type、info、data)," +
- "section=runtime 为运行数据(data 为 201 报文 JSON)。请用 Python(pandas)读取分析,不要把全文读入上下文。",
- ReadOnly: true,
- Handler: func(ctx context.Context, a siteAnalysisExportArgs) (*mcp.CallToolResult, error) {
- data, fail := siteAnalysisData(a.ID)
- if fail != nil {
- return fail, nil
- }
- rows := make([][]string, 0, len(data.Data.Timeline)+len(data.Data.Runtime))
- for _, it := range data.Data.Timeline {
- rows = append(rows, []string{"timeline", it.Time, string(it.Type), deref(it.Info), deref(it.Data)})
- }
- for _, it := range data.Data.Runtime {
- rows = append(rows, []string{"runtime", it.Time, "", "", it.Data_201})
- }
- summary := fmt.Sprintf("站点 %s:时间线 %d 条,运行数据 %d 条。", data.Sn, len(data.Data.Timeline), len(data.Data.Runtime))
- return kit.Export("fwh://site-analysis/"+data.Sn+"_"+data.Id.Hex()+".csv", []string{"section", "time", "type", "info", "data"}, rows, summary), nil
- },
- }
- func siteAnalysisData(idStr string) (*dto.SiteAnalysisDataInfo, *mcp.CallToolResult) {
- id, err := primitive.ObjectIDFromHex(strings.TrimSpace(idStr))
- if err != nil {
- return nil, kit.Failf("参数 id 格式错误:%s", idStr)
- }
- data, err := services.GetSiteAnalysisData(id)
- if errors.Is(err, mongo.ErrNoDocuments) || (err == nil && data == nil) {
- return nil, kit.Failf("没有找到分析任务 %s,可以用 site_analysis_list 查看", idStr)
- }
- if err != nil {
- return nil, kit.Upstream(siteAnalysisSource, err)
- }
- if data.Progress != domain.ProgressComplete {
- return nil, kit.Failf("分析任务 %s 尚未完成(进度:%s),完成后再查看结果", idStr, data.Progress)
- }
- return data, nil
- }
- func deref(s *string) string {
- if s == nil {
- return ""
- }
- return *s
- }
|