ai_scheduler/internal/tools/zltx/zltx_order_detail.go

390 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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. **先输出结论**:用<conclusion></conclusion>标签包裹关键结论如失败原因或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]
}