package zltx import ( "ai_scheduler/internal/config" "ai_scheduler/internal/entitys" "ai_scheduler/internal/pkg" "ai_scheduler/internal/pkg/lsxd" "ai_scheduler/internal/pkg/rec_extra" "ai_scheduler/internal/pkg/utils_ollama" "ai_scheduler/utils" "context" "encoding/json" "fmt" "strings" "time" "gitea.cdlsxd.cn/self-tools/l_request" "github.com/ollama/ollama/api" ) // ZltxOrderDetailTool 直连天下订单详情工具 type ZltxOrderDetailTool struct { config *config.Config llm *utils_ollama.Client rdb *utils.Rdb } // NewZltxOrderDetailTool 创建直连天下订单详情工具 func NewZltxOrderDetailTool(config *config.Config, rdb *utils.Rdb, llm *utils_ollama.Client) *ZltxOrderDetailTool { return &ZltxOrderDetailTool{config: config, rdb: rdb, llm: llm} } // Name 返回工具名称 func (w *ZltxOrderDetailTool) Name() string { return "zltxOrderDetail" } // Description 返回工具描述 func (w *ZltxOrderDetailTool) Description() string { return "获取直连天下订单详情" } // Definition 返回工具定义 func (w *ZltxOrderDetailTool) Definition(ctx context.Context) entitys.ToolDefinition { return entitys.ToolDefinition{ Type: "function", Function: entitys.FunctionDef{ Name: w.Name(), Description: w.Description(), Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "number": map[string]interface{}{ "type": "string", "description": "订单编号/流水号", }, }, "required": []string{"number"}, }, }, AuthFunc: func(rec *entitys.Recognize) ([]byte, error) { ext, err := rec_extra.GetTaskRecExt(rec) if err != nil { return nil, err } if len(ext.Auth) > 0 { return []byte(ext.Auth), nil } else { login := lsxd.NewLogin(w.config, w.rdb) token := login.GetToken(ctx) return []byte(token), nil } }, } } // ZltxOrderDetailRequest 直连天下订单详情请求参数 type ZltxOrderDetailRequest struct { OrderNumber interface{} `json:"order_number"` } //type ZltxOrderDetailResponse struct { // Code int `json:"code"` // Error string `json:"error"` // Data ZltxOrderDetailData `json:"data"` // Mes string `json:"mes"` //} // ZltxOrderDetailResponse 直连天下订单详情响应 type ZltxOrderDetailResponse struct { Code int `json:"code"` Data Data `json:"data"` Error string `json:"error"` } type Data struct { Direct *Direct `json:"direct"` Order *Order `json:"order"` } type Direct struct { SerialNumber string `json:"serialNumber"` OrderOrderNumber string `json:"orderOrderNumber"` TerminalAccount string `json:"terminalAccount"` OursProductId int `json:"oursProductId"` OursProductName string `json:"oursProductName"` Status int `json:"status"` TradePrice float64 `json:"tradePrice"` PlatformProductId int `json:"platformProductId"` PlatformProductName string `json:"platformProductName"` PlatformId int `json:"platformId"` PlatformName string `json:"platformName"` PlatformPrice float64 `json:"platformPrice"` CreateTime int `json:"createTime"` ExecuteTime int `json:"executeTime"` Type int `json:"type"` Reason string `json:"reason"` ResellerId int `json:"resellerId"` ResellerName string `json:"resellerName"` Aftermarket int `json:"aftermarket"` PurchasePrice float64 `json:"purchasePrice"` NeedAi bool `json:"needAi"` AiReason string `json:"aiReason"` } type Order struct { Id string `json:"id"` ResellerId int `json:"resellerId"` ResellerName string `json:"resellerName"` Status int `json:"status"` PayStatus int `json:"payStatus"` CreateTime int `json:"createTime"` PayTime int `json:"payTime"` Type int `json:"type"` Account string `json:"account"` Quantity int `json:"quantity"` Amount float64 `json:"amount"` PayAmount float64 `json:"payAmount"` ResellerOrderNumber string `json:"resellerOrderNumber"` Price int `json:"price"` NotifyTime int `json:"notifyTime"` FinishTime int `json:"finishTime"` Aftermarket int `json:"aftermarket"` Remark string `json:"remark"` OursProductId int `json:"oursProductId"` OursProductName string `json:"oursProductName"` OursProductPrice int `json:"oursProductPrice"` TradePrice float64 `json:"tradePrice"` } type ZltxOrderLogResponse struct { Code int `json:"code"` Error string `json:"error"` Data any `json:"data"` } // ZltxOrderDetailData 直连天下订单详情数据 type ZltxOrderDetailData struct { Direct map[string]any `json:"direct"` Order map[string]any `json:"order"` } // Execute 执行直连天下订单详情查询 func (w *ZltxOrderDetailTool) Execute(ctx context.Context, rec *entitys.Recognize) error { var req = &ZltxOrderDetailRequest{} if err := req.UnmarshalJSON([]byte(rec.Match.Parameters)); err != nil { return fmt.Errorf("invalid zltxOrderDetail request: %w", err) } if req.OrderNumber == "" { return fmt.Errorf("number is required") } // 这里可以集成真实的直连天下订单详情API return w.getZltxOrderDetail(ctx, rec, req.OrderNumber) } func (r *ZltxOrderDetailRequest) UnmarshalJSON(data []byte) error { var tmp struct { OrderNumber json.Number `json:"order_number"` // 使用 json.Number 保留原始格式 } if err := json.Unmarshal(data, &tmp); err != nil { return err } // 根据需要转换为 int64 或 string if num, err := tmp.OrderNumber.Int64(); err == nil { r.OrderNumber = num } else { r.OrderNumber = tmp.OrderNumber.String() } return nil } // getMockZltxOrderDetail 获取模拟直连天下订单详情数据 func (w *ZltxOrderDetailTool) getZltxOrderDetail(ctx context.Context, rec *entitys.Recognize, number interface{}) (err error) { var orderNum string switch number.(type) { case int, int32, int64: orderNum = fmt.Sprintf("%d", number) case float64: orderNum = fmt.Sprintf("%d", int(number.(float64))) case string: orderNum = number.(string) default: orderNum = fmt.Sprintf("%v", number) } token, err := w.Definition(ctx).AuthFunc(rec) if err != nil { return err } //查询订单详情 req := l_request.Request{ Url: fmt.Sprintf(w.config.Tools.ZltxOrderDetail.BaseURL, orderNum), Headers: map[string]string{ "Authorization": fmt.Sprintf("Bearer %s", token), }, Method: "GET", } res, err := req.Send() if err != nil { return fmt.Errorf("订单查询失败:网络请求错误:%s", err.Error()) } var codeMap map[string]interface{} if err = json.Unmarshal(res.Content, &codeMap); err != nil { return } if codeMap["code"].(float64) != 200 { return fmt.Errorf("订单查询失败:状态码错误:%s", string(res.Content)) } var resData ZltxOrderDetailResponse if err = json.Unmarshal(res.Content, &resData); err != nil { return } if rec.OutPutScene == entitys.OutPutSceneDingTalk { entitys.ResJson(rec.Ch, w.Name(), resData.ToForm()) } else { entitys.ResJson(rec.Ch, w.Name(), res.Text) entitys.ResLoading(rec.Ch, w.Name(), "正在分析订单日志") } if resData.Data.Direct != nil { req = l_request.Request{ Url: fmt.Sprintf(w.config.Tools.ZltxOrderDetail.AddURL, resData.Data.Direct.OrderOrderNumber, resData.Data.Direct.SerialNumber), Headers: map[string]string{ "Authorization": fmt.Sprintf("Bearer %s", token), }, Method: "GET", } res, err = req.Send() if err != nil { return } var orderLog ZltxOrderLogResponse if err = json.Unmarshal(res.Content, &orderLog); err != nil { return } if orderLog.Code != 200 { return fmt.Errorf("订单日志查询失败:%s", orderLog.Error) } dataJson, err := json.Marshal(orderLog.Data) if err != nil { return fmt.Errorf("订单日志解析失败:%s", err) } err = w.llm.ChatStream(ctx, rec.Ch, []api.Message{ { Role: "system", Content: "你是一个订单日志助手。用户可能会提供订单日志,你需要按以下规则处理:\n" + "1. **先输出结论**:用标签包裹关键结论(如失败原因或Base64解码内容);\n" + "2. **再输出分析过程**:详细解释如何得出结论;分析过程直接输出分析内容,不需要标明是分析过程\n" + "3. **订单类型处理**:\n" + " - 失败订单:分析失败原因(如支付超时、库存不足等);\n" + " - 成功订单:提取日志中的Base64编码JSON数据,解码后转换为用户可读的格式(如表格或JSON)。", }, { Role: "assistant", Content: fmt.Sprintf("聊天记录:%s", pkg.JsonStringIgonErr(rec.ChatHis)), }, { Role: "assistant", Content: fmt.Sprintf("需要分析的订单日志:%s", string(dataJson)), }, { Role: "user", Content: rec.UserContent.Text, }, }, w.Name(), "") if err != nil { return fmt.Errorf("订单日志解析失败:%s", err) } } if resData.Data.Direct == nil { entitys.ResText(rec.Ch, w.Name(), "该订单无充值流水记录,不需要进行订单错误分析,建议检查订单收单模式以及扣款方式等原因😘") } return } func (z *ZltxOrderDetailResponse) ToForm() string { var res strings.Builder res.WriteString("**普通订单** \n") res.WriteString("\n## 订单号:" + z.Data.Order.Id) res.WriteString("\n## 分销商订单号:" + z.Data.Order.ResellerOrderNumber) res.WriteString("\n## 分销商:" + z.Data.Order.ResellerName) res.WriteString("\n## 订单商品:" + z.Data.Order.OursProductName) res.WriteString("\n## 充值账号:" + z.Data.Order.Account) res.WriteString("\n## 折扣价/原价:" + fmt.Sprintf("%.2f", z.Data.Order.TradePrice)) res.WriteString("\n## 数量:" + fmt.Sprintf("%d", z.Data.Order.Quantity)) res.WriteString("\n## 订单总额:" + fmt.Sprintf("%.2f", z.Data.Order.Amount)) res.WriteString("\n## 订单状态:" + orderStatusMap(z.Data.Order.Status)) res.WriteString("\n## 支付状态:" + orderPayStatusMap(z.Data.Order.Status)) res.WriteString("\n## 创建时间:" + unixToDateFormat(z.Data.Order.CreateTime)) res.WriteString("\n## 完成时间:" + unixToDateFormat(z.Data.Order.FinishTime)) res.WriteString("\n---\n") res.WriteString("\n**充值流水** \n") res.WriteString("\n## 订单号:" + z.Data.Direct.OrderOrderNumber) res.WriteString("\n## 流水号:" + z.Data.Direct.SerialNumber) res.WriteString("\n## 商品:" + z.Data.Direct.OursProductName) res.WriteString("\n## 使用接口:" + z.Data.Direct.PlatformName) res.WriteString("\n## 接口商品名称:" + z.Data.Direct.PlatformProductName) res.WriteString("\n## 上游价:" + fmt.Sprintf("%.2f", z.Data.Direct.PlatformPrice)) res.WriteString("\n## 下游价:" + fmt.Sprintf("%.2f", z.Data.Direct.TradePrice)) res.WriteString("\n## 上游采购价:" + fmt.Sprintf("%.2f", z.Data.Direct.PurchasePrice)) res.WriteString("\n## 充值账号:" + z.Data.Direct.TerminalAccount) res.WriteString("\n## 创建时间:" + unixToDateFormat(z.Data.Direct.CreateTime)) res.WriteString("\n## 处理时间:" + unixToDateFormat(z.Data.Direct.ExecuteTime)) res.WriteString("\n## 状态:" + directStatusMap(z.Data.Direct.Status)) res.WriteString("\n") res.WriteString("\n") res.WriteString("\n") return res.String() } func orderStatusMap(status int) string { var OrderStatus = map[int]string{ 0: "下单中", 1: "订单完成", 2: "部分成功", 3: "充值处理中", 4: "已暂停", -1: "关闭订单", -2: "全部失败", } if _, ok := OrderStatus[status]; !ok { return "未知" } return OrderStatus[status] } func unixToDateFormat(unix int) string { return time.Unix(int64(unix), 0).Format("2006-01-02 15:04:05") } func orderPayStatusMap(status int) string { var OrderPayStatus = map[int]string{ 0: "未支付", 1: "支付中", 2: "支付成功", 3: "退款中", 4: "全部退款", 5: "部分退款", -1: "支付失败", -2: "退款失败", } if _, ok := OrderPayStatus[status]; !ok { return "未知" } return OrderPayStatus[status] } func directStatusMap(status int) string { var DirectStatus = map[int]string{ 0: "待充值", 1: "充值成功", 2: "充值中", -1: "充值失败", -2: "失败重试", -98: "下单异常", -99: "查询异常", -6: "手动失败", -5: "手动重试", -4: "叠加卡单", -3: "卡单", } if _, ok := DirectStatus[status]; !ok { return "未知" } return DirectStatus[status] }