chore: 暂存一版

This commit is contained in:
fuzhongyun 2025-12-29 14:59:34 +08:00
parent 65a7e5dad2
commit 0f0da660ba
11 changed files with 463 additions and 0 deletions

View File

@ -78,6 +78,7 @@ tools:
# eino tool 配置 # eino tool 配置
eino_tools: eino_tools:
# == 货易通 hyt ==
# 货易通商品上传 # 货易通商品上传
hytProductUpload: hytProductUpload:
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/supplier/batch/add/complete" base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/supplier/batch/add/complete"
@ -104,6 +105,19 @@ eino_tools:
# 货易通商品品牌查询 # 货易通商品品牌查询
hytGoodsBrandSearch: hytGoodsBrandSearch:
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list" base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list"
# == 报表分析 data analytics ==
# 负利润分析列表、 负利润分析详情
daOursProductLoss:
base_url: "http://test.analysis.com/api/dataanalytics/statisOursProductLossSum"
api_key: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY2OTc4MTQwLCJuYmYiOjE3NjY5NzYzNDAsImp0aSI6IjEiLCJQaG9uZSI6IjE4MDAwMDAwMDAwIiwiVXNlck5hbWUiOiJsc3hkIiwiUmVhbE5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9DQVNISUVSLFZDTF9PUEVSQVRFLFZDTF9BRE1JTixWQ0xfQUFBLFZDTF9WQ0xfT1BFUkFULFZDTF9JTlZPSUNFLENSTV9BRE1JTixMSUFOTElBTl9BRE1JTixNQVJLRVRNQUcyX0FETUlOLFBIT05FQklMTF9BRE1JTixRSUFOWkhVX1NVUFBFUl9BRE0sTUFSS0VUSU5HU0FBU19TVVBFUkFETUlOLENBUkRfQ09ERSxDQVJEX1BST0NVUkVNRU5ULE1BUktFVElOR1NZU1RFTV9TVVBFUixTVEFUSVNUSUNBTFNZU1RFTV9BRE1JTixaTFRYX0FETUlOLFpMVFhfT1BFUkFURSIsIkRpbmdVc2VySWQiOiIxNjIwMjYxMjMwMjg5MzM4MzQifQ.nyQw7_JZd7IfnIkPlfySGJMkFAHy_RtIL6gRqOYdku3ikp_w3x9bt7rrSBDQMi4Z8jC4jL_qxwhJ1D1DV4uLZ_cZBmuuG-gymIDN1TSJKa8hpu4E8L3tPdbT1H6hehXvg10-_ugKAYNBgnvFUbCRnpMl-sNuqCLWZKa_v63L1v3lGZOQiPPyzEGmIulNwqSF8rlzZggJneVTWEQac2BNK4181mpbz0S_m84xuFFO3Qwen2sEntBaPKeKbJ9BAfdSAQPcIlnG9FHF3mttZfxQQF5nbxU7CAGYsu_2kjpcmP-u5x3tT3gCWsF1t84lSZ8mMxIax7rh0-Y55abRoBnrwQ"
# 利润同比排行榜
daProfitRanking:
base_url: "http://test.analysis.com/api/dataanalytics/profitRankingSum"
api_key: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY2OTc4MTQwLCJuYmYiOjE3NjY5NzYzNDAsImp0aSI6IjEiLCJQaG9uZSI6IjE4MDAwMDAwMDAwIiwiVXNlck5hbWUiOiJsc3hkIiwiUmVhbE5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9DQVNISUVSLFZDTF9PUEVSQVRFLFZDTF9BRE1JTixWQ0xfQUFBLFZDTF9WQ0xfT1BFUkFULFZDTF9JTlZPSUNFLENSTV9BRE1JTixMSUFOTElBTl9BRE1JTixNQVJLRVRNQUcyX0FETUlOLFBIT05FQklMTF9BRE1JTixRSUFOWkhVX1NVUFBFUl9BRE0sTUFSS0VUSU5HU0FBU19TVVBFUkFETUlOLENBUkRfQ09ERSxDQVJEX1BST0NVUkVNRU5ULE1BUktFVElOR1NZU1RFTV9TVVBFUixTVEFUSVNUSUNBTFNZU1RFTV9BRE1JTixaTFRYX0FETUlOLFpMVFhfT1BFUkFURSIsIkRpbmdVc2VySWQiOiIxNjIwMjYxMjMwMjg5MzM4MzQifQ.nyQw7_JZd7IfnIkPlfySGJMkFAHy_RtIL6gRqOYdku3ikp_w3x9bt7rrSBDQMi4Z8jC4jL_qxwhJ1D1DV4uLZ_cZBmuuG-gymIDN1TSJKa8hpu4E8L3tPdbT1H6hehXvg10-_ugKAYNBgnvFUbCRnpMl-sNuqCLWZKa_v63L1v3lGZOQiPPyzEGmIulNwqSF8rlzZggJneVTWEQac2BNK4181mpbz0S_m84xuFFO3Qwen2sEntBaPKeKbJ9BAfdSAQPcIlnG9FHF3mttZfxQQF5nbxU7CAGYsu_2kjpcmP-u5x3tT3gCWsF1t84lSZ8mMxIax7rh0-Y55abRoBnrwQ"
# 销售同比分析列表
daOfficialProduct:
base_url: "http://test.analysis.com/api/dataanalytics/statisOfficialProduct"
api_key: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY2OTc4MTQwLCJuYmYiOjE3NjY5NzYzNDAsImp0aSI6IjEiLCJQaG9uZSI6IjE4MDAwMDAwMDAwIiwiVXNlck5hbWUiOiJsc3hkIiwiUmVhbE5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9DQVNISUVSLFZDTF9PUEVSQVRFLFZDTF9BRE1JTixWQ0xfQUFBLFZDTF9WQ0xfT1BFUkFULFZDTF9JTlZPSUNFLENSTV9BRE1JTixMSUFOTElBTl9BRE1JTixNQVJLRVRNQUcyX0FETUlOLFBIT05FQklMTF9BRE1JTixRSUFOWkhVX1NVUFBFUl9BRE0sTUFSS0VUSU5HU0FBU19TVVBFUkFETUlOLENBUkRfQ09ERSxDQVJEX1BST0NVUkVNRU5ULE1BUktFVElOR1NZU1RFTV9TVVBFUixTVEFUSVNUSUNBTFNZU1RFTV9BRE1JTixaTFRYX0FETUlOLFpMVFhfT1BFUkFURSIsIkRpbmdVc2VySWQiOiIxNjIwMjYxMjMwMjg5MzM4MzQifQ.nyQw7_JZd7IfnIkPlfySGJMkFAHy_RtIL6gRqOYdku3ikp_w3x9bt7rrSBDQMi4Z8jC4jL_qxwhJ1D1DV4uLZ_cZBmuuG-gymIDN1TSJKa8hpu4E8L3tPdbT1H6hehXvg10-_ugKAYNBgnvFUbCRnpMl-sNuqCLWZKa_v63L1v3lGZOQiPPyzEGmIulNwqSF8rlzZggJneVTWEQac2BNK4181mpbz0S_m84xuFFO3Qwen2sEntBaPKeKbJ9BAfdSAQPcIlnG9FHF3mttZfxQQF5nbxU7CAGYsu_2kjpcmP-u5x3tT3gCWsF1t84lSZ8mMxIax7rh0-Y55abRoBnrwQ"
dingtalk: dingtalk:
api_key: "dingsbbntrkeiyazcfdg" api_key: "dingsbbntrkeiyazcfdg"

View File

@ -188,6 +188,14 @@ type EinoToolsConfig struct {
HytGoodsCategorySearch ToolConfig `mapstructure:"hytGoodsCategorySearch"` HytGoodsCategorySearch ToolConfig `mapstructure:"hytGoodsCategorySearch"`
// 货易通商品品牌查询 // 货易通商品品牌查询
HytGoodsBrandSearch ToolConfig `mapstructure:"hytGoodsBrandSearch"` HytGoodsBrandSearch ToolConfig `mapstructure:"hytGoodsBrandSearch"`
// 负利润分析列表、 详情
DaOursProductLoss ToolConfig `mapstructure:"daOursProductLoss"`
// 利润同比排行榜
DaProfitRanking ToolConfig `mapstructure:"daProfitRanking"`
// 销售同比分析列表
DaOfficialProduct ToolConfig `mapstructure:"daOfficialProduct"`
// 销售同比下滑详情
DaOfficialProductDecline ToolConfig `mapstructure:"daOfficialProductDecline"`
} }
// LoggingConfig 日志配置 // LoggingConfig 日志配置

View File

@ -0,0 +1,70 @@
package official_product
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/pkg/l_request"
"context"
"encoding/json"
"fmt"
"strings"
)
type Client struct {
cfg config.ToolConfig
}
func New(cfg config.ToolConfig) *Client {
return &Client{
cfg: cfg,
}
}
// Call 调用销售同比分析接口
func (c *Client) Call(ctx context.Context, req OfficialProductRequest) (*OfficialProductData, error) {
// 构建 URL 参数
var queryParams []string
if req.Page > 0 {
queryParams = append(queryParams, fmt.Sprintf("page=%d", req.Page))
}
if req.Limit > 0 {
queryParams = append(queryParams, fmt.Sprintf("limit=%d", req.Limit))
}
for _, pid := range req.OfficialProductIds {
queryParams = append(queryParams, fmt.Sprintf("official_product_id[]=%s", pid))
}
for _, t := range req.Ct {
queryParams = append(queryParams, fmt.Sprintf("ct[]=%s", strings.ReplaceAll(t, " ", "+")))
}
queryString := strings.Join(queryParams, "&")
fullURL := fmt.Sprintf("%s?%s", c.cfg.BaseURL, queryString)
headers := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", c.cfg.APIKey),
}
reqObj := l_request.Request{
Method: "GET",
Url: fullURL,
Headers: headers,
}
res, err := reqObj.Send()
if err != nil {
return nil, fmt.Errorf("请求失败err: %v", err)
}
var resData OfficialProductResponse
if err := json.Unmarshal([]byte(res.Text), &resData); err != nil {
return nil, fmt.Errorf("解析响应失败err: %v, resp: %s", err, res.Text)
}
if resData.Code != 200 {
return nil, fmt.Errorf("业务错误code: %d, msg: %s", resData.Code, resData.Msg)
}
return &resData.Data, nil
}

View File

@ -0,0 +1,31 @@
package official_product
// OfficialProductRequest 销售同比分析请求参数
type OfficialProductRequest struct {
Page int `json:"page"` // 页码
Limit int `json:"limit"` // 每页条数
OfficialProductIds []string `json:"official_product_ids"` // 官方产品ID列表
Ct []string `json:"ct"` // 时间范围 [开始时间, 结束时间]
}
// OfficialProductResponse 销售同比分析响应结构
type OfficialProductResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data OfficialProductData `json:"data"`
}
type OfficialProductData struct {
OfficialProductSum []OfficialProductItem `json:"officialProductSum"`
DataCount int `json:"dataCount"`
}
type OfficialProductItem struct {
OfficialProductId int `json:"officialProductId"`
OfficialProductName string `json:"officialProductName"`
CurrentNum int `json:"currentNum"`
HistoryOneNum int `json:"historyOneNum"`
HistoryTwoNum int `json:"historyTwoNum"`
HistoryOneDiff int `json:"historyOneDiff"`
HistoryTwoDiff int `json:"historyTwoDiff"`
}

View File

@ -0,0 +1,79 @@
package official_product_decline
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/pkg/l_request"
"context"
"encoding/json"
"fmt"
"strings"
)
type Client struct {
cfg config.ToolConfig
}
func New(cfg config.ToolConfig) *Client {
return &Client{
cfg: cfg,
}
}
// Call 调用销售同比下滑详情接口
func (c *Client) Call(ctx context.Context, req OfficialProductDeclineRequest) (*OfficialProductDeclineData, error) {
// 构建 URL 参数
var queryParams []string
if req.Page > 0 {
queryParams = append(queryParams, fmt.Sprintf("page=%d", req.Page))
}
if req.Limit > 0 {
queryParams = append(queryParams, fmt.Sprintf("limit=%d", req.Limit))
}
if req.DownwardValue > 0 {
queryParams = append(queryParams, fmt.Sprintf("downwardValue=%d", req.DownwardValue))
}
// showTime 可能是 0所以这里不做 > 0 判断,如果业务默认是 0 可以忽略,或者根据实际需求
// 假设始终传递该参数如果已设置
queryParams = append(queryParams, fmt.Sprintf("showTime=%d", req.ShowTime))
for _, pid := range req.OfficialProductIds {
queryParams = append(queryParams, fmt.Sprintf("official_product_id[]=%s", pid))
}
for _, t := range req.Ct {
queryParams = append(queryParams, fmt.Sprintf("ct[]=%s", strings.ReplaceAll(t, " ", "+")))
}
queryString := strings.Join(queryParams, "&")
fullURL := fmt.Sprintf("%s?%s", c.cfg.BaseURL, queryString)
headers := map[string]string{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
"Accept": "application/json, text/plain, */*",
"Authorization": fmt.Sprintf("Bearer %s", c.cfg.APIKey),
"Accept-Language": "zh-CN,zh;q=0.9,en-GB;q=0.8,en;q=0.7",
}
reqObj := l_request.Request{
Method: "GET",
Url: fullURL,
Headers: headers,
}
res, err := reqObj.Send()
if err != nil {
return nil, fmt.Errorf("请求失败err: %v", err)
}
var resData OfficialProductDeclineResponse
if err := json.Unmarshal([]byte(res.Text), &resData); err != nil {
return nil, fmt.Errorf("解析响应失败err: %v, resp: %s", err, res.Text)
}
if resData.Code != 200 {
return nil, fmt.Errorf("业务错误code: %d, msg: %s", resData.Code, resData.Msg)
}
return &resData.Data, nil
}

View File

@ -0,0 +1,35 @@
package official_product_decline
// OfficialProductDeclineRequest 销售同比下滑详情请求参数
type OfficialProductDeclineRequest struct {
Page int `json:"page"` // 页码
Limit int `json:"limit"` // 每页条数
Ct []string `json:"ct"` // 时间范围 [开始时间, 结束时间]
OfficialProductIds []string `json:"official_product_ids"` // 官方产品ID列表
DownwardValue int `json:"downward_value"` // 下滑值
ShowTime int `json:"show_time"` // 是否显示时间 (0:不显示, 1:显示)
}
// OfficialProductDeclineResponse 销售同比下滑详情响应结构
type OfficialProductDeclineResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data OfficialProductDeclineData `json:"data"`
}
type OfficialProductDeclineData struct {
OfficialProductSumDecline []OfficialProductDeclineItem `json:"officialProductSumDecline"`
DataCount int `json:"dataCount"`
}
type OfficialProductDeclineItem struct {
ResellerId int `json:"resellerId"`
OfficialProductId int `json:"officialProductId"`
OfficialProductName string `json:"officialProductName"`
ResellerName string `json:"resellerName"`
CurrentNum int `json:"currentNum"`
HistoryOneNum int `json:"historyOneNum"`
HistoryTwoNum int `json:"historyTwoNum"`
HistoryOneDiff int `json:"historyOneDiff"`
HistoryTwoDiff int `json:"historyTwoDiff"`
}

View File

@ -0,0 +1,78 @@
package ours_product_loss
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/pkg/l_request"
"context"
"encoding/json"
"fmt"
"strings"
)
type Client struct {
cfg config.ToolConfig
}
func New(cfg config.ToolConfig) *Client {
return &Client{
cfg: cfg,
}
}
// Call 调用负利润分析接口
// 支持列表查询和详情查询
// 列表查询:提供 page, limit, ct[]
// 详情查询:提供 ct[], resellerId
func (c *Client) Call(ctx context.Context, req OursProductLossRequest) (*OursProductLossData, error) {
// 处理数组参数 ct[]
// util.StructToMap 通常不支持数组到 url query array 的转换,这里手动处理查询字符串
// 或者如果 l_request 支持 map 中的 slice 自动转换最好,假设不支持需手动拼接
// 构建 URL 参数
var queryParams []string
if req.Page > 0 {
queryParams = append(queryParams, fmt.Sprintf("page=%d", req.Page))
}
if req.Limit > 0 {
queryParams = append(queryParams, fmt.Sprintf("limit=%d", req.Limit))
}
if req.ResellerId != "" {
queryParams = append(queryParams, fmt.Sprintf("resellerId=%s", req.ResellerId))
}
for _, t := range req.Ct {
// URL 编码处理,这里简单处理,实际应使用 url.QueryEscape
// 假设输入已经是合法的格式
queryParams = append(queryParams, fmt.Sprintf("ct[]=%s", strings.ReplaceAll(t, " ", "+")))
}
queryString := strings.Join(queryParams, "&")
fullURL := fmt.Sprintf("%s?%s", c.cfg.BaseURL, queryString)
headers := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", c.cfg.APIKey),
}
reqObj := l_request.Request{
Method: "GET",
Url: fullURL,
Headers: headers,
}
res, err := reqObj.Send()
if err != nil {
return nil, fmt.Errorf("请求失败err: %v", err)
}
var resData OursProductLossResponse
if err := json.Unmarshal([]byte(res.Text), &resData); err != nil {
return nil, fmt.Errorf("解析响应失败err: %v, resp: %s", err, res.Text)
}
if resData.Code != 200 {
return nil, fmt.Errorf("业务错误code: %d, msg: %s", resData.Code, resData.Msg)
}
return &resData.Data, nil
}

View File

@ -0,0 +1,29 @@
package ours_product_loss
// OursProductLossRequest 负利润分析请求参数
type OursProductLossRequest struct {
Page int `json:"page"` // 页码
Limit int `json:"limit"` // 每页条数
Ct []string `json:"ct"` // 时间范围 [开始时间, 结束时间]
ResellerId string `json:"reseller_id"` // 经销商ID (详情查询时使用)
}
// OursProductLossResponse 负利润分析响应结构
type OursProductLossResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data OursProductLossData `json:"data"`
}
type OursProductLossData struct {
List []OursProductLossItem `json:"list"`
DataCount int `json:"dataCount"`
}
type OursProductLossItem struct {
OursProductId int `json:"oursProductId"`
OursProductName string `json:"oursProductName"`
ResellerName string `json:"resellerName"`
ResellerId int `json:"resellerId"`
Loss float64 `json:"loss"`
}

View File

@ -0,0 +1,63 @@
package profit_ranking
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/pkg/l_request"
"context"
"encoding/json"
"fmt"
"strings"
)
type Client struct {
cfg config.ToolConfig
}
func New(cfg config.ToolConfig) *Client {
return &Client{
cfg: cfg,
}
}
// Call 调用利润同比排行榜接口
func (c *Client) Call(ctx context.Context, req ProfitRankingRequest) (*ProfitRankingData, error) {
// 构建 URL 参数
var queryParams []string
for _, t := range req.Ct {
queryParams = append(queryParams, fmt.Sprintf("ct[]=%s", strings.ReplaceAll(t, " ", "+")))
}
for _, rid := range req.ResellerIds {
queryParams = append(queryParams, fmt.Sprintf("resellerIds[]=%s", rid))
}
queryString := strings.Join(queryParams, "&")
fullURL := fmt.Sprintf("%s?%s", c.cfg.BaseURL, queryString)
headers := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", c.cfg.APIKey),
}
reqObj := l_request.Request{
Method: "GET",
Url: fullURL,
Headers: headers,
}
res, err := reqObj.Send()
if err != nil {
return nil, fmt.Errorf("请求失败err: %v", err)
}
var resData ProfitRankingResponse
if err := json.Unmarshal([]byte(res.Text), &resData); err != nil {
return nil, fmt.Errorf("解析响应失败err: %v, resp: %s", err, res.Text)
}
if resData.Code != 200 {
return nil, fmt.Errorf("业务错误code: %d, msg: %s", resData.Code, resData.Msg)
}
return &resData.Data, nil
}

View File

@ -0,0 +1,27 @@
package profit_ranking
import (
"ai_scheduler/internal/config"
"testing"
"github.com/stretchr/testify/assert"
)
func TestClient_Call(t *testing.T) {
cfg := config.ToolConfig{
BaseURL: "http://test.analysis.com/api/dataanalytics/profitRankingSum",
APIKey: "test_jwt_token",
}
client := New(cfg)
assert.NotNil(t, client)
req := ProfitRankingRequest{
Ct: []string{"2025-01-01 00:00:00", "2025-01-01 23:59:59"},
ResellerIds: []string{"1001", "1002"},
}
t.Logf("Testing Call with req: %+v", req)
// _, err := client.Call(context.Background(), req)
// assert.Error(t, err)
}

View File

@ -0,0 +1,29 @@
package profit_ranking
// ProfitRankingRequest 利润同比排行请求参数
type ProfitRankingRequest struct {
Ct []string `json:"ct"` // 时间范围 [开始时间, 结束时间]
ResellerIds []string `json:"reseller_ids"` // 经销商ID列表
}
// ProfitRankingResponse 利润同比排行响应结构
type ProfitRankingResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data ProfitRankingData `json:"data"`
}
type ProfitRankingData struct {
List []ProfitRankingItem `json:"list"`
DataCount int `json:"dataCount"`
}
type ProfitRankingItem struct {
ResellerId string `json:"resellerId"`
ResellerName string `json:"resellerName"`
CurrentProfit float64 `json:"currentProfit"`
HistoryOneProfit float64 `json:"historyOneProfit"`
HistoryTwoProfit float64 `json:"historyTwoProfit"`
HistoryOneDiff float64 `json:"historyOneDiff"`
HistoryTwoDiff float64 `json:"historyTwoDiff"`
}