ops.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package tools
  2. import (
  3. "context"
  4. "encoding/json"
  5. "strings"
  6. "github.com/mark3labs/mcp-go/mcp"
  7. "fwh-toolbox-server/agentmcp/kit"
  8. "fwh-toolbox-server/dto"
  9. "fwh-toolbox-server/services"
  10. )
  11. // ServiceAccount 写操作在业务系统中的执行身份(Agent MCP 不区分调用者)
  12. const ServiceAccount = "AI 助手"
  13. // ---- feedback_create ----
  14. type feedbackCreateArgs struct {
  15. Title string `json:"title" jsonschema:"标题"`
  16. Type string `json:"type" jsonschema:"类型:bug 或 feature"`
  17. Priority string `json:"priority,omitempty" jsonschema:"优先级:urgent、high、medium(默认)、low、none"`
  18. Category string `json:"category,omitempty" jsonschema:"分类:bug 类为 bug、ui、performance、other;feature 类为 new、improvement、experience"`
  19. Description string `json:"description,omitempty" jsonschema:"详细描述:背景、现象、影响站点、复现步骤等"`
  20. }
  21. var FeedbackCreate = kit.Def[feedbackCreateArgs]{
  22. Name: "feedback_create",
  23. Title: "提交问题反馈到 Plane",
  24. Description: "在 Plane 中创建一条 Intake 工作项(问题反馈或需求)。提交人固定为服务账号“AI 助手”,请在描述中写明背景与来源。" +
  25. "返回 Plane 创建结果(工作项 ID 等)。这是写操作,执行前会请求确认。",
  26. ReadOnly: false,
  27. Handler: func(ctx context.Context, a feedbackCreateArgs) (*mcp.CallToolResult, error) {
  28. title, err := kit.Required("title", a.Title)
  29. if err != nil {
  30. return kit.Fail(err), nil
  31. }
  32. typ, err := kit.Enum("type", a.Type, "", "bug", "feature")
  33. if err != nil {
  34. return kit.Fail(err), nil
  35. }
  36. if typ == "" {
  37. return kit.Failf("参数 type 必填:bug 或 feature"), nil
  38. }
  39. priority, err := kit.Enum("priority", a.Priority, "medium", "urgent", "high", "medium", "low", "none")
  40. if err != nil {
  41. return kit.Fail(err), nil
  42. }
  43. res, err := services.SubmitFeedback(dto.FeedbackSubmitDTO{
  44. Title: title,
  45. Type: typ,
  46. Priority: priority,
  47. Category: strings.TrimSpace(a.Category),
  48. Description: a.Description,
  49. }, ServiceAccount)
  50. if err != nil {
  51. return kit.Upstream("Plane", err), nil
  52. }
  53. return kit.Object(res), nil
  54. },
  55. }
  56. // ---- tou_template_save ----
  57. type touTemplateSaveArgs struct {
  58. Payload string `json:"payload" jsonschema:"模版请求体 JSON:{\"template\":{...},\"strategyList\":[{...}],\"lang\":\"ZH_CN\"}。template.templateId 有值为更新、省略为新增;字段定义见技能 references 中的 TOU 模版说明"`
  59. }
  60. var TouTemplateSave = kit.Def[touTemplateSaveArgs]{
  61. Name: "tou_template_save",
  62. Title: "保存 TOU 调度模版(测试服)",
  63. Description: "在 FWH 测试服新增或更新 TOU 调度模版,只支持测试服。保存前校验每个策略的时段首尾衔接覆盖全天、sourceType 为 0;" +
  64. "返回 templateId 与按调度模式识别后的策略摘要。这是写操作,执行前会请求确认。",
  65. ReadOnly: false,
  66. Handler: func(ctx context.Context, a touTemplateSaveArgs) (*mcp.CallToolResult, error) {
  67. raw, err := kit.Required("payload", a.Payload)
  68. if err != nil {
  69. return kit.Fail(err), nil
  70. }
  71. var payload dto.FwhTestUpdateOrSaveTouTemplatePayload
  72. if err := json.Unmarshal([]byte(raw), &payload); err != nil {
  73. return kit.Failf("参数 payload 不是有效的 JSON:%v", err), nil
  74. }
  75. if payload.Template.SourceType != 0 {
  76. return kit.Failf("template.sourceType 只能为 0(系统模版)"), nil
  77. }
  78. if len(payload.StrategyList) == 0 {
  79. return kit.Failf("strategyList 不能为空"), nil
  80. }
  81. organized, _ := OrganizeTouDetail(payload)
  82. if issues := strategyIssues(organized); len(issues) > 0 {
  83. return kit.Failf("模版校验未通过:%s", strings.Join(issues, ";")), nil
  84. }
  85. if payload.Lang == "" {
  86. payload.Lang = "ZH_CN"
  87. }
  88. id, err := services.FwhTestSaveTouTemplate(payload)
  89. if err != nil {
  90. return kit.Upstream("FWH 测试服 TOU 模版接口", err), nil
  91. }
  92. return kit.Object(map[string]any{"templateId": id, "message": "已保存到测试服", "strategies": organized["strategies"]}), nil
  93. },
  94. }
  95. // strategyIssues 汇总整理结果中的时段衔接问题。
  96. func strategyIssues(organized map[string]any) []string {
  97. var out []string
  98. sts, _ := organized["strategies"].([]map[string]any)
  99. for _, st := range sts {
  100. if is, ok := st["issues"].([]string); ok {
  101. name, _ := st["seasonName"].(string)
  102. day, _ := st["dayName"].(string)
  103. for _, i := range is {
  104. out = append(out, name+"/"+day+":"+i)
  105. }
  106. }
  107. }
  108. return out
  109. }
  110. // ---- site_info_refresh ----
  111. var SiteInfoRefresh = kit.Def[kit.NoArgs]{
  112. Name: "site_info_refresh",
  113. Title: "触发站点信息表更新",
  114. Description: "触发 ToolBox 站点信息表的更新任务(从 FWH 平台同步最新站点数据),任务在后台执行。" +
  115. "返回是否已触发;同步通常需要数分钟,完成后 site_search 的结果才会更新。这是写操作,执行前会请求确认。",
  116. ReadOnly: false,
  117. Handler: func(ctx context.Context, _ kit.NoArgs) (*mcp.CallToolResult, error) {
  118. if err := services.UpdateSiteInfoDate(); err != nil {
  119. return kit.Upstream("ToolBox 站点信息更新", err), nil
  120. }
  121. return mcp.NewToolResultText("已触发站点信息表更新,任务在后台执行。"), nil
  122. },
  123. }