sim.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package tools
  2. import (
  3. "context"
  4. "fmt"
  5. "sort"
  6. "strings"
  7. "github.com/mark3labs/mcp-go/mcp"
  8. "fwh-toolbox-server/agentmcp/kit"
  9. "fwh-toolbox-server/domain"
  10. "fwh-toolbox-server/services"
  11. )
  12. // ---- sim_get ----
  13. type simGetArgs struct {
  14. ICCID string `json:"iccid" jsonschema:"SIM 卡 GICCID(19 位)"`
  15. }
  16. var SimGet = kit.Def[simGetArgs]{
  17. Name: "sim_get",
  18. Title: "查询 SIM 卡",
  19. Description: "按 GICCID 查询单张 SIM 卡的详细信息(ToolBox SIM 卡管理,平台为 SmartHub 或 Acceleronix,自动判断)。" +
  20. "返回状态、平台、关联站点 SN、本月用量(KB)、激活时间等(IMEI/IMSI/MSISDN 已脱敏)。",
  21. ReadOnly: true,
  22. Handler: func(ctx context.Context, a simGetArgs) (*mcp.CallToolResult, error) {
  23. id, err := kit.Required("iccid", a.ICCID)
  24. if err != nil {
  25. return kit.Fail(err), nil
  26. }
  27. info, err := services.GetSimCardInfo(id)
  28. if err != nil {
  29. return kit.Upstream("SIM 卡管理", err), nil
  30. }
  31. if info == nil {
  32. return kit.Empty("没有找到该 SIM 卡。"), nil
  33. }
  34. return kit.Object(info), nil
  35. },
  36. }
  37. // ---- sim_search ----
  38. type simSearchArgs struct {
  39. Search string `json:"search,omitempty" jsonschema:"搜索关键字(GICCID 片段)"`
  40. Status string `json:"status,omitempty" jsonschema:"状态过滤,多个用逗号分隔:UNKNOWN、NOT_ACTIVATED、ACTIVATED、DEACTIVATED、RETIRED"`
  41. Platform string `json:"platform,omitempty" jsonschema:"平台过滤,多个用逗号分隔:SmartHub、Acceleronix"`
  42. Page int `json:"page,omitempty" jsonschema:"页码,默认 1"`
  43. Size int `json:"size,omitempty" jsonschema:"每页条数,1~100,默认 20"`
  44. }
  45. var simStatuses = []string{"UNKNOWN", "NOT_ACTIVATED", "ACTIVATED", "DEACTIVATED", "RETIRED"}
  46. var simPlatforms = []string{"SmartHub", "Acceleronix"}
  47. var SimSearch = kit.Def[simSearchArgs]{
  48. Name: "sim_search",
  49. Title: "搜索 SIM 卡",
  50. Description: "按关键字、状态、平台分页搜索 SIM 卡(ToolBox SIM 卡管理)。返回 total 与 items(GICCID、状态、平台、关联 SN、用量等)。",
  51. ReadOnly: true,
  52. Handler: func(ctx context.Context, a simSearchArgs) (*mcp.CallToolResult, error) {
  53. status, err := enumList("status", a.Status, simStatuses)
  54. if err != nil {
  55. return kit.Fail(err), nil
  56. }
  57. platform, err := enumList("platform", a.Platform, simPlatforms)
  58. if err != nil {
  59. return kit.Fail(err), nil
  60. }
  61. page := kit.Page(a.Page)
  62. size := kit.Limit(a.Size, 20, 100)
  63. data, err := services.GetSimCardBatchInfo(uint(page), uint(size), strings.TrimSpace(a.Search), status, platform)
  64. if err != nil {
  65. return kit.Upstream("SIM 卡管理", err), nil
  66. }
  67. if data == nil {
  68. return kit.Empty(""), nil
  69. }
  70. return kit.List(data.Data, int(data.Total), size, fmt.Sprintf("共 %d 条,当前第 %d 页,可用 page 翻页", data.Total, page)), nil
  71. },
  72. }
  73. // enumList 校验逗号分隔的多选值;为空返回 nil。
  74. func enumList(name, v string, allowed []string) (*string, error) {
  75. v = strings.TrimSpace(v)
  76. if v == "" {
  77. return nil, nil
  78. }
  79. parts := strings.Split(v, ",")
  80. out := make([]string, 0, len(parts))
  81. for _, p := range parts {
  82. e, err := kit.Enum(name, p, "", allowed...)
  83. if err != nil {
  84. return nil, err
  85. }
  86. if e != "" {
  87. out = append(out, e)
  88. }
  89. }
  90. joined := strings.Join(out, ",")
  91. return &joined, nil
  92. }
  93. // ---- sim_usage_monthly ----
  94. type simUsageMonthlyArgs struct {
  95. ICCID string `json:"iccid" jsonschema:"SIM 卡 GICCID(19 位)"`
  96. Month string `json:"month" jsonschema:"月份,yyyy-MM"`
  97. }
  98. var SimUsageMonthly = kit.Def[simUsageMonthlyArgs]{
  99. Name: "sim_usage_monthly",
  100. Title: "查询 SIM 卡月度每日用量",
  101. Description: "查询单张 SIM 卡某个月每天的数据用量。返回 month、allDataUsage(月用量,KB)、updateTime(UTC),以及 days(按日排序的每日用量明细)。",
  102. ReadOnly: true,
  103. Handler: func(ctx context.Context, a simUsageMonthlyArgs) (*mcp.CallToolResult, error) {
  104. id, err := kit.Required("iccid", a.ICCID)
  105. if err != nil {
  106. return kit.Fail(err), nil
  107. }
  108. month, err := kit.Required("month", a.Month)
  109. if err != nil {
  110. return kit.Fail(err), nil
  111. }
  112. data, err := services.GetSimCardMonthlyData(month, id)
  113. if err != nil {
  114. return kit.Upstream("SIM 卡管理", err), nil
  115. }
  116. if data == nil {
  117. return kit.Empty("该月没有用量数据。"), nil
  118. }
  119. return kit.Object(MonthlyUsageView(data)), nil
  120. },
  121. }
  122. // MonthlyUsageView 把按日索引的 map 转为按日排序的列表。
  123. func MonthlyUsageView(d *domain.SimCardMonthlyData) map[string]any {
  124. days := make([]uint8, 0, len(d.DailyUsage))
  125. for k := range d.DailyUsage {
  126. days = append(days, k)
  127. }
  128. sort.Slice(days, func(i, j int) bool { return days[i] < days[j] })
  129. list := make([]map[string]any, 0, len(days))
  130. for _, day := range days {
  131. list = append(list, map[string]any{"day": day, "usage": d.DailyUsage[day]})
  132. }
  133. return map[string]any{
  134. "iccid": d.SimId,
  135. "month": d.Month,
  136. "allDataUsage": d.AllDataUsage,
  137. "updateTime": d.UpdateTime,
  138. "days": list,
  139. }
  140. }