From 208f749483c24b942a002652b2438e6a6ec95924 Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Wed, 24 Dec 2025 11:52:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201.=E6=96=B0=E5=A2=9E=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E8=B4=A7=E6=98=93=E9=80=9A=E5=B7=A5=E5=85=B7=202.=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=B4=A7=E6=98=93=E9=80=9A=E5=88=9B=E5=BB=BA=E5=95=86?= =?UTF-8?q?=E5=93=81=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config_env.yaml | 17 + internal/config/config.go | 10 + internal/data/constants/capability.go | 31 +- internal/domain/tools/hyt/goods_add/client.go | 49 +++ .../domain/tools/hyt/goods_add/client_test.go | 51 +++ internal/domain/tools/hyt/goods_add/types.go | 35 ++ .../tools/hyt/goods_brand_search/client.go | 67 +++ .../hyt/goods_brand_search/client_test.go | 28 ++ .../tools/hyt/goods_brand_search/types.go | 25 ++ .../tools/hyt/goods_category_add/client.go | 49 +++ .../hyt/goods_category_add/client_test.go | 31 ++ .../tools/hyt/goods_category_add/types.go | 15 + .../tools/hyt/goods_category_search/client.go | 66 +++ .../tools/hyt/goods_category_search/types.go | 24 ++ .../tools/hyt/goods_media_add/client.go | 49 +++ .../tools/hyt/goods_media_add/client_test.go | 37 ++ .../domain/tools/hyt/goods_media_add/types.go | 21 + .../domain/tools/hyt/product_upload/client.go | 2 +- .../tools/hyt/product_upload/client_test.go | 61 --- .../tools/hyt/supplier_search/client.go | 1 - .../tools/hyt/warehouse_search/client.go | 1 - internal/domain/tools/registry.go | 27 +- internal/domain/workflow/hyt/goods_add.go | 387 ++++++++++++++++++ .../domain/workflow/hyt/product_upload.go | 14 +- internal/services/capability.go | 4 +- 25 files changed, 1021 insertions(+), 81 deletions(-) create mode 100644 internal/domain/tools/hyt/goods_add/client.go create mode 100644 internal/domain/tools/hyt/goods_add/client_test.go create mode 100644 internal/domain/tools/hyt/goods_add/types.go create mode 100644 internal/domain/tools/hyt/goods_brand_search/client.go create mode 100644 internal/domain/tools/hyt/goods_brand_search/client_test.go create mode 100644 internal/domain/tools/hyt/goods_brand_search/types.go create mode 100644 internal/domain/tools/hyt/goods_category_add/client.go create mode 100644 internal/domain/tools/hyt/goods_category_add/client_test.go create mode 100644 internal/domain/tools/hyt/goods_category_add/types.go create mode 100644 internal/domain/tools/hyt/goods_category_search/client.go create mode 100644 internal/domain/tools/hyt/goods_category_search/types.go create mode 100644 internal/domain/tools/hyt/goods_media_add/client.go create mode 100644 internal/domain/tools/hyt/goods_media_add/client_test.go create mode 100644 internal/domain/tools/hyt/goods_media_add/types.go delete mode 100644 internal/domain/tools/hyt/product_upload/client_test.go create mode 100644 internal/domain/workflow/hyt/goods_add.go diff --git a/config/config_env.yaml b/config/config_env.yaml index ec4f4c2..dd120a0 100644 --- a/config/config_env.yaml +++ b/config/config_env.yaml @@ -88,6 +88,23 @@ eino_tools: # 货易通仓库查询 hytWarehouseSearch: base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/warehouse/list" + # 货易通商品添加 + hytGoodsAdd: + base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/add" + # 货易通商品图片添加 + hytGoodsMediaAdd: + base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/media/add/batch" + # 货易通商品分类添加 + hytGoodsCategoryAdd: + base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/good/category/relation/add" + # 货易通商品分类查询 + hytGoodsCategorySearch: + base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/category/list" + # 货易通商品品牌查询 + hytGoodsBrandSearch: + base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list" + + default_prompt: img_recognize: diff --git a/internal/config/config.go b/internal/config/config.go index b30adae..3198ebd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -163,6 +163,16 @@ type EinoToolsConfig struct { HytSupplierSearch ToolConfig `mapstructure:"hytSupplierSearch"` // 货易通仓库查询 HytWarehouseSearch ToolConfig `mapstructure:"hytWarehouseSearch"` + // 货易通商品添加 + HytGoodsAdd ToolConfig `mapstructure:"hytGoodsAdd"` + // 货易通商品图片添加 + HytGoodsMediaAdd ToolConfig `mapstructure:"hytGoodsMediaAdd"` + // 货易通商品分类添加 + HytGoodsCategoryAdd ToolConfig `mapstructure:"hytGoodsCategoryAdd"` + // 货易通商品分类查询 + HytGoodsCategorySearch ToolConfig `mapstructure:"hytGoodsCategorySearch"` + // 货易通商品品牌查询 + HytGoodsBrandSearch ToolConfig `mapstructure:"hytGoodsBrandSearch"` } // LoggingConfig 日志配置 diff --git a/internal/data/constants/capability.go b/internal/data/constants/capability.go index 5b40d9f..2555cda 100644 --- a/internal/data/constants/capability.go +++ b/internal/data/constants/capability.go @@ -17,8 +17,8 @@ const ( // 商品属性模板-中文 const ( - // 货易通商品属性模板-中文 - HYTProductPropertyTemplateZH = `{ + // 货易通供应商商品属性模板-中文 + HYTSupplierProductPropertyTemplateZH = `{ "货品编号": "string", // 商品编号 "条码": "string", // 货品编号 "分类名称": "string", // 商品分类 @@ -47,6 +47,33 @@ const ( "默认供应商": "string", // 空 "默认存放仓库": "string", // 空 }` + // 货易通商品属性模板-中文 Ps:手机端主图、详情图文、平台资质图 (暂时无需) + HYTGoodsAddPropertyTemplateZH = `{ + "商品标题": "string", // 商品名称 + "商品编码": "string", // 商品编码 + "SPU名称": "string", // 商品SPU名称 + "SPU编码": "string", // 商品编码 + "商品货号": "string", // 商品货号 + "商品条形码": "string", // 商品编码 + "市场价": "string", // 商品市场价 decimal(10,2) + "建议销售价": "string", // 商品建议销售价 decimal(10,2) + "电商销售价格": "string", // 商品电商销售价格 decimal(10,2) + "单位": "string", // 商品单位,若无则使用'个' + "折扣(%)": "string", // 商品折扣(%),默认0% + "税率(%)": "string", // 商品税率(%),默认13% + "运费模版": "string", // 商品运费模版,默认空 + "保质期": "string", // 商品保质期,无则空 + "保质期单位": "string", // 商品保质期单位,无则空 + "品牌": "string", // 商品品牌,若无则空 + "是否热销主推": "string", // 填否 + "外部平台链接": "string", // 商品外部平台链接 + "商品卖点": "string", // 商品卖点 + "商品规格参数": "string", // 商品规格参数 + "商品说明": "string", // 商品说明 + "备注": "string", // 无则空 + "分类名称": "string", // 商品分类 + "电脑端主图": ["string"], // 商品电脑端主图 + }` ) // 缓存key diff --git a/internal/domain/tools/hyt/goods_add/client.go b/internal/domain/tools/hyt/goods_add/client.go new file mode 100644 index 0000000..7f91d66 --- /dev/null +++ b/internal/domain/tools/hyt/goods_add/client.go @@ -0,0 +1,49 @@ +package goods_add + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/pkg/l_request" + "ai_scheduler/internal/pkg/util" + "context" + "encoding/json" + "fmt" +) + +type Client struct { + cfg config.ToolConfig +} + +func New(cfg config.ToolConfig) *Client { + return &Client{ + cfg: cfg, + } +} + +func (c *Client) Call(ctx context.Context, req *GoodsAddRequest) (int, error) { + apiReq, _ := util.StructToMap(req) + + r := l_request.Request{ + Method: "Post", + Url: c.cfg.BaseURL, + Json: apiReq, + Headers: map[string]string{ + "Content-Type": "application/json", + }, + } + + res, err := r.Send() + if err != nil { + return 0, fmt.Errorf("请求失败,err: %v", err) + } + + var resData GoodsAddResponse + if err := json.Unmarshal([]byte(res.Text), &resData); err != nil { + return 0, fmt.Errorf("解析响应失败,err: %v", err) + } + + if resData.Code != 200 { + return 0, fmt.Errorf("业务错误,code: %d, msg: %s", resData.Code, resData.Msg) + } + + return resData.Data.Id, nil +} diff --git a/internal/domain/tools/hyt/goods_add/client_test.go b/internal/domain/tools/hyt/goods_add/client_test.go new file mode 100644 index 0000000..aba715a --- /dev/null +++ b/internal/domain/tools/hyt/goods_add/client_test.go @@ -0,0 +1,51 @@ +package goods_add + +import ( + "ai_scheduler/internal/config" + "context" + "fmt" + "testing" +) + +// Test_Call +func Test_Call(t *testing.T) { + req := &GoodsAddRequest{ + Unit: "元", + IsComposeGoods: 2, + GoodsAttributes: "
商品规格参数
", + Introduction: "商品卖点
", + GoodsIllustration: "商品说明
", + IsHot: 2, + Title: "fu测试001", + GoodsNum: "futest001sku", + SpuCode: "futest001spu", + SpuName: "fu测试001", + Price: 100, + SalesPrice: 80, + Discount: 15, + TaxRate: 13, + FreightId: 3, + Remark: "备注说明", + SellByDate: 180, + ExternalPrice: 120, + GoodsBarCode: "futest001code2", + GoodsCode: "futest001code1", + SellByDateUnit: "天", + BrandId: 3, + ExternalUrl: "https://www.baidu.com", + } + + cfg := config.ToolConfig{ + BaseURL: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/add", + } + + client := New(cfg) + toolResp, err := client.Call(context.Background(), req) + + if err != nil { + t.Errorf("Call() error = %v", err) + return + } + + fmt.Printf("toolResp: %v\n", toolResp) +} diff --git a/internal/domain/tools/hyt/goods_add/types.go b/internal/domain/tools/hyt/goods_add/types.go new file mode 100644 index 0000000..ef9e168 --- /dev/null +++ b/internal/domain/tools/hyt/goods_add/types.go @@ -0,0 +1,35 @@ +package goods_add + +type GoodsAddRequest struct { + Title string `json:"title"` // 商品标题 + GoodsCode string `json:"goods_code"` // 商品编码 + SpuName string `json:"spu_name"` // SPU 名称 + SpuCode string `json:"spu_code"` // SPU 编码 + GoodsNum string `json:"goods_num"` // 商品货号 + GoodsBarCode string `json:"goods_bar_code"` // 商品条形码 + Price float64 `json:"price"` // 市场价 + SalesPrice float64 `json:"sales_price"` // 建议销售价 + ExternalPrice float64 `json:"external_price"` // 电商销售价格 + Unit string `json:"unit"` // 价格单位 + Discount int `json:"discount"` // 折扣 + TaxRate int `json:"tax_rate"` // 税率 + FreightId int `json:"freight_id"` // 运费模板 ID + SellByDate int `json:"sell_by_date"` // 保质期 + SellByDateUnit string `json:"sell_by_date_unit"` // 保质期单位 + BrandId int `json:"brand_id"` // 品牌 ID + IsHot int `json:"is_hot"` // 是否热销主推 1.是 2.否(默认) + ExternalUrl string `json:"external_url"` // 外部平台链接 + Introduction string `json:"introduction"` // 商品卖点 + GoodsAttributes string `json:"goods_attributes"` // 商品规格参数 + GoodsIllustration string `json:"goods_illustration"` // 商品说明 + Remark string `json:"remark"` // 备注说明 + IsComposeGoods int `json:"is_compose_goods"` // 是否组合商品 1.是 2.否(默认) +} + +type GoodsAddResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + Id int `json:"id"` // 商品 ID + } `json:"data"` +} diff --git a/internal/domain/tools/hyt/goods_brand_search/client.go b/internal/domain/tools/hyt/goods_brand_search/client.go new file mode 100644 index 0000000..e7b3d58 --- /dev/null +++ b/internal/domain/tools/hyt/goods_brand_search/client.go @@ -0,0 +1,67 @@ +package goods_brand_search + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/pkg/l_request" + "ai_scheduler/internal/pkg/util" + "context" + "encoding/json" + "fmt" +) + +type Client struct { + cfg config.ToolConfig +} + +func New(cfg config.ToolConfig) *Client { + return &Client{ + cfg: cfg, + } +} + +func (c *Client) Call(ctx context.Context, name string) (int, error) { + if name == "" { + return 0, nil + } + + reqBody := GoodsBrandSearchRequest{ + Page: 1, + Limit: 1, + Search: SearchCondition{ + Name: name, + }, + } + + apiReq, _ := util.StructToMap(reqBody) + + req := l_request.Request{ + Method: "Post", + Url: c.cfg.BaseURL, + Json: apiReq, + Headers: map[string]string{ + "User-Agent": "Apifox/1.0.0 (https://apifox.com)", + "Content-Type": "application/json", + }, + } + + res, err := req.Send() + if err != nil { + return 0, fmt.Errorf("请求失败,err: %v", err) + } + + var resData GoodsBrandSearchResponse + if err := json.Unmarshal([]byte(res.Text), &resData); err != nil { + return 0, fmt.Errorf("解析响应失败,err: %v", err) + } + + if resData.Code != 200 { + return 0, fmt.Errorf("业务错误,code: %d, msg: %s", resData.Code, resData.Msg) + } + + if len(resData.Data.List) == 0 { + return 0, fmt.Errorf("品牌不存在") + } + + // 返回第一个匹配的品牌ID + return resData.Data.List[0].ID, nil +} diff --git a/internal/domain/tools/hyt/goods_brand_search/client_test.go b/internal/domain/tools/hyt/goods_brand_search/client_test.go new file mode 100644 index 0000000..41009f1 --- /dev/null +++ b/internal/domain/tools/hyt/goods_brand_search/client_test.go @@ -0,0 +1,28 @@ +package goods_brand_search + +import ( + "ai_scheduler/internal/config" + "context" + "fmt" + "testing" +) + +// Test_Call +func Test_Call(t *testing.T) { + // 使用示例中的查询条件 + name := "vivo" + + cfg := config.ToolConfig{ + BaseURL: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list", + } + + client := New(cfg) + toolResp, err := client.Call(context.Background(), name) + + if err != nil { + t.Errorf("Call() error = %v", err) + return + } + + fmt.Printf("toolResp (BrandID): %v\n", toolResp) +} diff --git a/internal/domain/tools/hyt/goods_brand_search/types.go b/internal/domain/tools/hyt/goods_brand_search/types.go new file mode 100644 index 0000000..c3ec8bb --- /dev/null +++ b/internal/domain/tools/hyt/goods_brand_search/types.go @@ -0,0 +1,25 @@ +package goods_brand_search + +type GoodsBrandSearchRequest struct { + Page int `json:"page"` + Limit int `json:"limit"` + Search SearchCondition `json:"search"` +} + +type SearchCondition struct { + Name string `json:"name"` +} + +type GoodsBrandSearchResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + List []BrandInfo `json:"list"` + } `json:"data"` +} + +type BrandInfo struct { + ID int `json:"id"` + Name string `json:"name"` + Logo string `json:"logo"` +} diff --git a/internal/domain/tools/hyt/goods_category_add/client.go b/internal/domain/tools/hyt/goods_category_add/client.go new file mode 100644 index 0000000..8fa0e8b --- /dev/null +++ b/internal/domain/tools/hyt/goods_category_add/client.go @@ -0,0 +1,49 @@ +package goods_category_add + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/pkg/l_request" + "ai_scheduler/internal/pkg/util" + "context" + "encoding/json" + "fmt" +) + +type Client struct { + cfg config.ToolConfig +} + +func New(cfg config.ToolConfig) *Client { + return &Client{ + cfg: cfg, + } +} + +func (c *Client) Call(ctx context.Context, req *GoodsCategoryAddRequest) (bool, error) { + apiReq, _ := util.StructToMap(req) + + r := l_request.Request{ + Method: "Post", + Url: c.cfg.BaseURL, + Json: apiReq, + Headers: map[string]string{ + "Content-Type": "application/json", + }, + } + + res, err := r.Send() + if err != nil { + return false, fmt.Errorf("请求失败,err: %v", err) + } + + var resData GoodsCategoryAddResponse + if err := json.Unmarshal([]byte(res.Text), &resData); err != nil { + return false, fmt.Errorf("解析响应失败,err: %v", err) + } + + if resData.Code != 200 { + return false, fmt.Errorf("业务错误,code: %d, msg: %s", resData.Code, resData.Msg) + } + + return resData.Data.IsSuccess, nil +} diff --git a/internal/domain/tools/hyt/goods_category_add/client_test.go b/internal/domain/tools/hyt/goods_category_add/client_test.go new file mode 100644 index 0000000..fed3a94 --- /dev/null +++ b/internal/domain/tools/hyt/goods_category_add/client_test.go @@ -0,0 +1,31 @@ +package goods_category_add + +import ( + "ai_scheduler/internal/config" + "context" + "fmt" + "testing" +) + +// Test_Call +func Test_Call(t *testing.T) { + req := &GoodsCategoryAddRequest{ + GoodsId: 8496, + CategoryIds: []int{1667}, + IsCover: false, + } + + cfg := config.ToolConfig{ + BaseURL: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/good/category/relation/add", + } + + client := New(cfg) + toolResp, err := client.Call(context.Background(), req) + + if err != nil { + t.Errorf("Call() error = %v", err) + return + } + + fmt.Printf("toolResp: %v\n", toolResp) +} diff --git a/internal/domain/tools/hyt/goods_category_add/types.go b/internal/domain/tools/hyt/goods_category_add/types.go new file mode 100644 index 0000000..e23691e --- /dev/null +++ b/internal/domain/tools/hyt/goods_category_add/types.go @@ -0,0 +1,15 @@ +package goods_category_add + +type GoodsCategoryAddRequest struct { + GoodsId int `json:"goods_id"` + CategoryIds []int `json:"category_ids"` + IsCover bool `json:"is_cover"` +} + +type GoodsCategoryAddResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + IsSuccess bool `json:"is_success"` // 是否成功 + } `json:"data"` +} diff --git a/internal/domain/tools/hyt/goods_category_search/client.go b/internal/domain/tools/hyt/goods_category_search/client.go new file mode 100644 index 0000000..185e54b --- /dev/null +++ b/internal/domain/tools/hyt/goods_category_search/client.go @@ -0,0 +1,66 @@ +package goods_category_search + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/pkg/l_request" + "ai_scheduler/internal/pkg/util" + "context" + "encoding/json" + "fmt" +) + +type Client struct { + cfg config.ToolConfig +} + +func New(cfg config.ToolConfig) *Client { + return &Client{ + cfg: cfg, + } +} + +func (c *Client) Call(ctx context.Context, name string) (int, error) { + if name == "" { + return 0, nil + } + + reqBody := GoodsCategorySearchRequest{ + Page: 1, + Limit: 1, + Search: SearchCondition{ + Name: name, + }, + } + + apiReq, _ := util.StructToMap(reqBody) + + req := l_request.Request{ + Method: "Post", + Url: c.cfg.BaseURL, + Json: apiReq, + Headers: map[string]string{ + "Content-Type": "application/json", + }, + } + + res, err := req.Send() + if err != nil { + return 0, fmt.Errorf("请求失败,err: %v", err) + } + + var resData GoodsCategorySearchResponse + if err := json.Unmarshal([]byte(res.Text), &resData); err != nil { + return 0, fmt.Errorf("解析响应失败,err: %v", err) + } + + if resData.Code != 200 { + return 0, fmt.Errorf("业务错误,code: %d, msg: %s", resData.Code, resData.Msg) + } + + if len(resData.Data.List) == 0 { + return 0, fmt.Errorf("商品分类不存在") + } + + // 返回第一个匹配的分类ID + return resData.Data.List[0].ID, nil +} diff --git a/internal/domain/tools/hyt/goods_category_search/types.go b/internal/domain/tools/hyt/goods_category_search/types.go new file mode 100644 index 0000000..dcc32e9 --- /dev/null +++ b/internal/domain/tools/hyt/goods_category_search/types.go @@ -0,0 +1,24 @@ +package goods_category_search + +type GoodsCategorySearchRequest struct { + Page int `json:"page"` + Limit int `json:"limit"` + Search SearchCondition `json:"search"` +} + +type SearchCondition struct { + Name string `json:"name"` +} + +type GoodsCategorySearchResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + List []CategoryInfo `json:"list"` + } `json:"data"` +} + +type CategoryInfo struct { + ID int `json:"id"` + Name string `json:"name"` +} diff --git a/internal/domain/tools/hyt/goods_media_add/client.go b/internal/domain/tools/hyt/goods_media_add/client.go new file mode 100644 index 0000000..6632168 --- /dev/null +++ b/internal/domain/tools/hyt/goods_media_add/client.go @@ -0,0 +1,49 @@ +package goods_media_add + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/pkg/l_request" + "ai_scheduler/internal/pkg/util" + "context" + "encoding/json" + "fmt" +) + +type Client struct { + cfg config.ToolConfig +} + +func New(cfg config.ToolConfig) *Client { + return &Client{ + cfg: cfg, + } +} + +func (c *Client) Call(ctx context.Context, req *GoodsMediaAddRequest) (bool, error) { + apiReq, _ := util.StructToMap(req) + + r := l_request.Request{ + Method: "Post", + Url: c.cfg.BaseURL, + Json: apiReq, + Headers: map[string]string{ + "Content-Type": "application/json", + }, + } + + res, err := r.Send() + if err != nil { + return false, fmt.Errorf("请求失败,err: %v", err) + } + + var resData GoodsMediaAddResponse + if err := json.Unmarshal([]byte(res.Text), &resData); err != nil { + return false, fmt.Errorf("解析响应失败,err: %v", err) + } + + if resData.Code != 200 { + return false, fmt.Errorf("业务错误,code: %d, msg: %s", resData.Code, resData.Msg) + } + + return resData.Data.IsSuccess, nil +} diff --git a/internal/domain/tools/hyt/goods_media_add/client_test.go b/internal/domain/tools/hyt/goods_media_add/client_test.go new file mode 100644 index 0000000..f6f16ca --- /dev/null +++ b/internal/domain/tools/hyt/goods_media_add/client_test.go @@ -0,0 +1,37 @@ +package goods_media_add + +import ( + "ai_scheduler/internal/config" + "context" + "fmt" + "testing" +) + +// Test_Call +func Test_Call(t *testing.T) { + req := &GoodsMediaAddRequest{ + GoodsId: 8496, + Data: []MediaItem{ + { + Type: 1, + Url: "https://lsxd-hz-store.oss-cn-hangzhou.aliyuncs.com/physicalGoodsSystems/images/goodsimages/goods/22f03d91-3cb7-45b4-ab92-07aad78a1633-screenshot_2025-12-17_17-46-00.png", + Sort: 1, + }, + }, + IsCover: true, + } + + cfg := config.ToolConfig{ + BaseURL: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/media/add/batch", + } + + client := New(cfg) + toolResp, err := client.Call(context.Background(), req) + + if err != nil { + t.Errorf("Call() error = %v", err) + return + } + + fmt.Printf("toolResp: %v\n", toolResp) +} diff --git a/internal/domain/tools/hyt/goods_media_add/types.go b/internal/domain/tools/hyt/goods_media_add/types.go new file mode 100644 index 0000000..e299d4f --- /dev/null +++ b/internal/domain/tools/hyt/goods_media_add/types.go @@ -0,0 +1,21 @@ +package goods_media_add + +type GoodsMediaAddRequest struct { + GoodsId int `json:"goods_id"` + Data []MediaItem `json:"data"` + IsCover bool `json:"is_cover"` +} + +type MediaItem struct { + Type int `json:"type"` + Url string `json:"url"` + Sort int `json:"sort"` +} + +type GoodsMediaAddResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + IsSuccess bool `json:"is_success"` + } `json:"data"` +} diff --git a/internal/domain/tools/hyt/product_upload/client.go b/internal/domain/tools/hyt/product_upload/client.go index 1cd68b8..096965c 100644 --- a/internal/domain/tools/hyt/product_upload/client.go +++ b/internal/domain/tools/hyt/product_upload/client.go @@ -43,7 +43,7 @@ func (c *Client) Call(ctx context.Context, toolReq *ProductUploadRequest) (toolR Code int `json:"code"` Msg string `json:"msg"` Data struct { - Ids []int `json:"ids"` // 预览URL + Ids []int `json:"ids"` // 商品 IDs } `json:"data"` } var resMap resType diff --git a/internal/domain/tools/hyt/product_upload/client_test.go b/internal/domain/tools/hyt/product_upload/client_test.go deleted file mode 100644 index fdd99f0..0000000 --- a/internal/domain/tools/hyt/product_upload/client_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package product_upload - -import ( - "ai_scheduler/internal/config" - "context" - "fmt" - "testing" -) - -// Test_Call -func Test_Call(t *testing.T) { - req := &ProductUploadRequest{ - SupplierId: 261, - WarehouseId: 257, - IsDefaultWarehouse: 1, - Sort: 1, - Profit: 40, - TaxRate: 13, - GoodsList: []Goods{ - { - GoodsInfo: GoodsInfo{ - Title: "Apple iPhone 17 Pro Max 星宇橙色 256GB", - Brand: "Apple/苹果", - Category: "手机", - // CostPrice: 9999.00, - GoodsAttributes: "CPU型号:A19 Pro;操作系统:iOS;机身存储:256GB;屏幕尺寸:6.86英寸;屏幕材质:OLED直屏;屏幕技术:视网膜XDR;后置摄像头:4800万像素三主摄系统(主摄4800万+超广角4800万+长焦4800万);前置摄像头:1800万像素;网络支持:5G双卡双待(移动/联通/电信);生物识别:人脸识别;防水等级:IP68;充电功率:40W;无线充电:支持;机身尺寸:163.4mm×78.0mm×8.75mm;机身重量:231g;机身颜色:星宇橙色;特征特质:轻薄、防水防尘、无线充电、NFC、磁吸无线充", - GoodsBarCode: "10181383848993", - GoodsIllustration: "Apple/苹果 iPhone 17 Pro Max 【需当面激活】支持移动联通电信 5G 双卡双待手机 星宇橙色 256GB 官方标配。搭载A19 Pro芯片,6.86英寸OLED视网膜XDR直屏,4800万像素三主摄系统,支持5G双卡双待,IP68防水防尘,40W有线充电,支持无线充电和磁吸充电。", - GoodsNum: "10181383848993", - Introduction: "Apple/苹果 iPhone 17 Pro Max 【需当面激活】支持移动联通电信 5G 双卡双待手机 星宇橙色 256GB 官方标配。搭载A19 Pro芯片,6.86英寸OLED视网膜XDR直屏,4800万像素三主摄系统,支持5G双卡双待,IP68防水防尘,40W有线充电,支持无线充电和磁吸充电。", - IsBind: 1, - SpuName: "Apple iPhone 17 Pro Max", - SpuNum: "jd_1766038130329_8721", - TaxRate: 13, - Unit: "台", - Weight: "0.231", // 单位:kg - Price: 9999.00, - SalesPrice: 9999.00, - Stock: 0, // JSON 中未提供库存信息 - Discount: 10, // JSON 中未提供折扣信息 - IsComposeGoods: 2, - IsHot: 2, - }, - GoodsMediaList: []GoodsMedia{ - { - Type: 1, - Url: "https://img10.360buyimg.com/pcpubliccms/s228x228_jfs/t1/363919/12/2409/45712/691d9970F84b99d32/9f9a5d5d16efeb79.jpg.avif", - }, - }, - }, - }, - } - client := New(config.ToolConfig{}) - toolResp, err := client.Call(context.Background(), req) - - if err != nil { - t.Errorf("Call() error = %v", err) - } - - fmt.Printf("toolResp: %v\n", toolResp) -} diff --git a/internal/domain/tools/hyt/supplier_search/client.go b/internal/domain/tools/hyt/supplier_search/client.go index cbb20b4..1f47ee8 100644 --- a/internal/domain/tools/hyt/supplier_search/client.go +++ b/internal/domain/tools/hyt/supplier_search/client.go @@ -41,7 +41,6 @@ func (c *Client) Call(ctx context.Context, name string) (int, error) { Url: c.cfg.BaseURL, Json: apiReq, Headers: map[string]string{ - "User-Agent": "Apifox/1.0.0 (https://apifox.com)", "Content-Type": "application/json", }, } diff --git a/internal/domain/tools/hyt/warehouse_search/client.go b/internal/domain/tools/hyt/warehouse_search/client.go index 4502c42..cf420b2 100644 --- a/internal/domain/tools/hyt/warehouse_search/client.go +++ b/internal/domain/tools/hyt/warehouse_search/client.go @@ -36,7 +36,6 @@ func (c *Client) Call(ctx context.Context, name string) (int, error) { Url: c.cfg.BaseURL, Params: params, Headers: map[string]string{ - "User-Agent": "Apifox/1.0.0 (https://apifox.com)", "Content-Type": "application/json", }, } diff --git a/internal/domain/tools/registry.go b/internal/domain/tools/registry.go index 31a8636..ad9439d 100644 --- a/internal/domain/tools/registry.go +++ b/internal/domain/tools/registry.go @@ -2,6 +2,11 @@ package tools import ( "ai_scheduler/internal/config" + "ai_scheduler/internal/domain/tools/hyt/goods_add" + "ai_scheduler/internal/domain/tools/hyt/goods_brand_search" + "ai_scheduler/internal/domain/tools/hyt/goods_category_add" + "ai_scheduler/internal/domain/tools/hyt/goods_category_search" + "ai_scheduler/internal/domain/tools/hyt/goods_media_add" "ai_scheduler/internal/domain/tools/hyt/product_upload" "ai_scheduler/internal/domain/tools/hyt/supplier_search" "ai_scheduler/internal/domain/tools/hyt/warehouse_search" @@ -13,17 +18,27 @@ type Manager struct { } type HytTools struct { - ProductUpload *product_upload.Client - SupplierSearch *supplier_search.Client - WarehouseSearch *warehouse_search.Client + ProductUpload *product_upload.Client + SupplierSearch *supplier_search.Client + WarehouseSearch *warehouse_search.Client + GoodsAdd *goods_add.Client + GoodsMediaAdd *goods_media_add.Client + GoodsCategoryAdd *goods_category_add.Client + GoodsCategorySearch *goods_category_search.Client + GoodsBrandSearch *goods_brand_search.Client } func NewManager(cfg *config.Config) *Manager { return &Manager{ Hyt: &HytTools{ - ProductUpload: product_upload.New(cfg.EinoTools.HytProductUpload), - SupplierSearch: supplier_search.New(cfg.EinoTools.HytSupplierSearch), - WarehouseSearch: warehouse_search.New(cfg.EinoTools.HytWarehouseSearch), + ProductUpload: product_upload.New(cfg.EinoTools.HytProductUpload), + SupplierSearch: supplier_search.New(cfg.EinoTools.HytSupplierSearch), + WarehouseSearch: warehouse_search.New(cfg.EinoTools.HytWarehouseSearch), + GoodsAdd: goods_add.New(cfg.EinoTools.HytGoodsAdd), + GoodsMediaAdd: goods_media_add.New(cfg.EinoTools.HytGoodsMediaAdd), + GoodsCategoryAdd: goods_category_add.New(cfg.EinoTools.HytGoodsCategoryAdd), + GoodsCategorySearch: goods_category_search.New(cfg.EinoTools.HytGoodsCategorySearch), + GoodsBrandSearch: goods_brand_search.New(cfg.EinoTools.HytGoodsBrandSearch), }, } } diff --git a/internal/domain/workflow/hyt/goods_add.go b/internal/domain/workflow/hyt/goods_add.go new file mode 100644 index 0000000..7bd2dd4 --- /dev/null +++ b/internal/domain/workflow/hyt/goods_add.go @@ -0,0 +1,387 @@ +package hyt + +import ( + "ai_scheduler/internal/config" + errorcode "ai_scheduler/internal/data/error" + toolManager "ai_scheduler/internal/domain/tools" + "ai_scheduler/internal/domain/tools/hyt/goods_add" + "ai_scheduler/internal/domain/tools/hyt/goods_category_add" + "ai_scheduler/internal/domain/tools/hyt/goods_media_add" + "ai_scheduler/internal/domain/workflow/runtime" + "ai_scheduler/internal/entitys" + "context" + "encoding/json" + "errors" + "fmt" + "log" + "strconv" + "strings" + "sync" + + "github.com/cloudwego/eino/compose" +) + +const WorkflowIDGoodsAdd = "hyt.goodsAdd" + +func init() { + runtime.Register(WorkflowIDGoodsAdd, func(d *runtime.Deps) (runtime.Workflow, error) { + return &goodsAdd{cfg: d.Conf, toolManager: d.ToolManager}, nil + }) +} + +type goodsAdd struct { + cfg *config.Config + toolManager *toolManager.Manager + data *GoodsAddWorkflowInput +} + +type GoodsAddWorkflowInput struct { + Text string `mapstructure:"text"` +} + +func (o *goodsAdd) ID() string { return WorkflowIDGoodsAdd } + +func (o *goodsAdd) Invoke(ctx context.Context, rec *entitys.Recognize) (map[string]any, error) { + // 构建工作流 + runnable, err := o.buildWorkflow(ctx) + if err != nil { + return nil, err + } + + o.data = &GoodsAddWorkflowInput{ + Text: rec.UserContent.Text, + } + // 工作流过程调用 + output, err := runnable.Invoke(ctx, o.data) + if err != nil { + errStr := err.Error() + if u := errors.Unwrap(err); u != nil { + errStr = u.Error() + } + return nil, errorcode.WorkflowErr(errStr) + } + + return output, nil +} + +// ProductIngestData 对应 HYTGoodsAddPropertyTemplateZH 的结构 +type GoodsAddProductIngestData struct { + Title string `json:"商品标题"` + GoodsCode string `json:"商品编码"` + SpuName string `json:"SPU名称"` + SpuCode string `json:"SPU编码"` + GoodsNum string `json:"商品货号"` + GoodsBarCode string `json:"商品条形码"` + Price string `json:"市场价"` + SalesPrice string `json:"建议销售价"` + ExternalPrice string `json:"电商销售价格"` + Unit string `json:"单位"` + Discount string `json:"折扣(%)"` + TaxRate string `json:"税率(%)"` + FreightTemplate string `json:"运费模版"` + SellByDate string `json:"保质期"` + SellByDateUnit string `json:"保质期单位"` + Brand string `json:"品牌"` + IsHot string `json:"是否热销主推"` + ExternalUrl string `json:"外部平台链接"` + Introduction string `json:"商品卖点"` + GoodsAttributes string `json:"商品规格参数"` + GoodsIllustration string `json:"商品说明"` + Remark string `json:"备注"` + CategoryName string `json:"分类名称"` + Images []string `json:"电脑端主图"` +} + +// GoodsAddContext Graph 执行上下文状态 +type GoodsAddContext struct { + mu *sync.Mutex + InputText string + IngestData *GoodsAddProductIngestData + + // 核心请求体 + AddGoodsReq *goods_add.GoodsAddRequest + + // 中间态数据 + BrandId int + CategoryId int + BrandName string + CategoryName string + + // 运行结果 + GoodsId int + Result map[string]any +} + +// buildWorkflow 构建基于 Graph 的并行工作流 +func (o *goodsAdd) buildWorkflow(ctx context.Context) (compose.Runnable[*GoodsAddWorkflowInput, map[string]any], error) { + g := compose.NewGraph[*GoodsAddWorkflowInput, map[string]any]() + + // 1. DataMapping 节点: 解析 JSON -> 填充基础 Request + g.AddLambdaNode("data_mapping", compose.InvokableLambda(func(ctx context.Context, in *GoodsAddWorkflowInput) (*GoodsAddContext, error) { + state := &GoodsAddContext{ + mu: &sync.Mutex{}, // 初始化锁 + InputText: in.Text, + AddGoodsReq: &goods_add.GoodsAddRequest{}, + Result: make(map[string]any), + } + + // 解析用户输入的中文 JSON + var ingestData GoodsAddProductIngestData + if err := json.Unmarshal([]byte(in.Text), &ingestData); err != nil { + return nil, fmt.Errorf("解析商品数据失败: %w", err) + } + + // 必填校验 + if ingestData.Title == "" { + return nil, errors.New("商品标题不能为空") + } + if ingestData.GoodsCode == "" { + return nil, errors.New("商品编码不能为空") + } + if ingestData.SpuName == "" { + return nil, errors.New("SPU名称不能为空") + } + if ingestData.SpuCode == "" { + return nil, errors.New("SPU编码不能为空") + } + if ingestData.Price == "" { + return nil, errors.New("市场价不能为空") + } + if ingestData.SalesPrice == "" { + return nil, errors.New("建议销售价不能为空") + } + if ingestData.Unit == "" { + return nil, errors.New("价格单位不能为空") + } + if ingestData.Discount == "" { + return nil, errors.New("折扣不能为空") + } + if ingestData.TaxRate == "" { + return nil, errors.New("税率不能为空") + } + + state.IngestData = &ingestData + state.BrandName = ingestData.Brand + state.CategoryName = ingestData.CategoryName + + // 映射字段到 AddGoodsReq + state.AddGoodsReq.Title = ingestData.Title + state.AddGoodsReq.GoodsCode = ingestData.GoodsCode + state.AddGoodsReq.SpuName = ingestData.SpuName + state.AddGoodsReq.SpuCode = ingestData.SpuCode + state.AddGoodsReq.GoodsNum = ingestData.GoodsNum + state.AddGoodsReq.GoodsBarCode = ingestData.GoodsBarCode + + // 价格处理 + if val, err := strconv.ParseFloat(strings.TrimSuffix(ingestData.Price, "元"), 64); err == nil { + state.AddGoodsReq.Price = val + } + if val, err := strconv.ParseFloat(strings.TrimSuffix(ingestData.SalesPrice, "元"), 64); err == nil { + state.AddGoodsReq.SalesPrice = val + } + if val, err := strconv.ParseFloat(strings.TrimSuffix(ingestData.ExternalPrice, "元"), 64); err == nil && state.AddGoodsReq.Price == 0 { + state.AddGoodsReq.ExternalPrice = val + } + + state.AddGoodsReq.Unit = ingestData.Unit + + // 折扣处理 "80%" -> 80 + discountStr := strings.TrimSuffix(ingestData.Discount, "%") + if val, err := strconv.Atoi(discountStr); err == nil { + state.AddGoodsReq.Discount = val + } + // 税率处理 "13%" -> 13 + taxStr := strings.TrimSuffix(strings.TrimSuffix(ingestData.TaxRate, "%"), " ") + if val, err := strconv.Atoi(taxStr); err == nil { + state.AddGoodsReq.TaxRate = val + } + + // 运费模板先不给 state.AddGoodsReq.FreightId = 3 + + // 保质期处理 "180天" -> 180 + sellByDateStr := strings.TrimSuffix(ingestData.SellByDate, "天") + if val, err := strconv.Atoi(sellByDateStr); err == nil { + state.AddGoodsReq.SellByDate = val + } + state.AddGoodsReq.SellByDateUnit = ingestData.SellByDateUnit + + // state.AddGoodsReq.BrandId 品牌ID后续赋值 + + state.AddGoodsReq.IsHot = 2 + if ingestData.IsHot == "是" { + state.AddGoodsReq.IsHot = 1 + } + + state.AddGoodsReq.ExternalUrl = ingestData.ExternalUrl + state.AddGoodsReq.Introduction = ingestData.Introduction + state.AddGoodsReq.GoodsAttributes = ingestData.GoodsAttributes + state.AddGoodsReq.GoodsIllustration = ingestData.GoodsIllustration + state.AddGoodsReq.Remark = ingestData.Remark + state.AddGoodsReq.IsComposeGoods = 2 // 非组合商品 + + return state, nil + })) + + // 2. 获取品牌ID 节点 (并行) + g.AddLambdaNode("get_brand_id", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (*GoodsAddContext, error) { + if state.BrandName == "" { + return state, errors.New("品牌名称不能为空") + } + + brandId, err := o.toolManager.Hyt.GoodsBrandSearch.Call(ctx, state.BrandName) + if err != nil { + log.Printf("warning: 品牌ID获取失败,%s: %v\n", state.BrandName, err) + // 如果获取失败,不阻断后续流程 + return nil, nil + } + + state.mu.Lock() + defer state.mu.Unlock() + state.BrandId = brandId + state.AddGoodsReq.BrandId = brandId + + return state, nil + })) + + // 3. 获取分类ID 节点 (并行) + g.AddLambdaNode("get_category_id", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (*GoodsAddContext, error) { + if state.CategoryName == "" { + return state, errors.New("分类名称不能为空") + } + + categoryId, err := o.toolManager.Hyt.GoodsCategorySearch.Call(ctx, state.CategoryName) + if err != nil { + log.Printf("warning: 分类ID获取失败,%s: %v\n", state.CategoryName, err) + // 如果获取失败,不阻断后续流程 + return nil, nil + } + + state.mu.Lock() + defer state.mu.Unlock() + state.CategoryId = categoryId + + return state, nil + })) + + // 4. 新增商品 节点 (依赖 get_brand_id) + g.AddLambdaNode("goods_add", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (*GoodsAddContext, error) { + // 校验 BrandId + if state.AddGoodsReq.BrandId == 0 { + return nil, errors.New("Missing Brand ID") + } + + // 调用 goods_add 工具 + goodsId, err := o.toolManager.Hyt.GoodsAdd.Call(ctx, state.AddGoodsReq) + if err != nil { + return nil, fmt.Errorf("新增商品失败: %w", err) + } + state.GoodsId = goodsId + + state.Result["goods_id"] = state.GoodsId + state.Result["spu_code"] = state.AddGoodsReq.SpuCode + return state, nil + })) + + // 5. 新增商品分类 节点 (依赖 goods_add 和 get_category_id) + g.AddLambdaNode("goods_category_add", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (*GoodsAddContext, error) { + if state.GoodsId == 0 { + return nil, errors.New("goods_id is 0") + } + if state.CategoryId == 0 { + return nil, errors.New("category_id is 0") + } + + req := &goods_category_add.GoodsCategoryAddRequest{ + GoodsId: state.GoodsId, + CategoryIds: []int{state.CategoryId}, + IsCover: false, + } + + _, err := o.toolManager.Hyt.GoodsCategoryAdd.Call(ctx, req) + if err != nil { + log.Printf("warning: 关联分类失败: %v", err) + state.mu.Lock() + state.Result["category_error"] = err.Error() + state.mu.Unlock() + } else { + state.mu.Lock() + state.Result["category_added"] = true + state.mu.Unlock() + } + + return state, nil + })) + + // 6. 新增商品图片 节点 (依赖 goods_add) + g.AddLambdaNode("goods_media_add", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (*GoodsAddContext, error) { + if state.GoodsId == 0 { + return nil, errors.New("goods_id is 0") + } + if len(state.IngestData.Images) == 0 { + return state, nil + } + + req := &goods_media_add.GoodsMediaAddRequest{ + GoodsId: state.GoodsId, + IsCover: true, + Data: make([]goods_media_add.MediaItem, 0), + } + + for i, url := range state.IngestData.Images { + req.Data = append(req.Data, goods_media_add.MediaItem{ + Type: 1, // 图片 + Url: url, + Sort: i, + }) + } + + _, err := o.toolManager.Hyt.GoodsMediaAdd.Call(ctx, req) + if err != nil { + log.Printf("warning: 添加图片失败: %v", err) + state.mu.Lock() + state.Result["media_error"] = err.Error() + state.mu.Unlock() + } else { + state.mu.Lock() + state.Result["media_added"] = true + state.mu.Unlock() + } + + return state, nil + })) + + // 7. 结果格式化节点 + g.AddLambdaNode("format_output", compose.InvokableLambda(func(ctx context.Context, state *GoodsAddContext) (map[string]any, error) { + return state.Result, nil + })) + + // 构建边 (DAG) + + // Start -> DataMapping + g.AddEdge(compose.START, "data_mapping") + + // Branching: DataMapping -> GetBrandId, DataMapping -> GetCategoryId + g.AddEdge("data_mapping", "get_brand_id") + g.AddEdge("data_mapping", "get_category_id") + + // Synchronization for GoodsAdd: Need BrandId + g.AddEdge("get_brand_id", "goods_add") + + // Synchronization for CategoryAdd: Need GoodsId AND CategoryId + // Eino supports multi-predecessor nodes which act as merge points. + // state merging is handled by the framework (usually last writer wins or custom merge, but here we modify different fields/mutex). + // However, we need to ensure goods_add is done. + g.AddEdge("goods_add", "goods_category_add") + g.AddEdge("get_category_id", "goods_category_add") + + // Synchronization for MediaAdd: Need GoodsId + g.AddEdge("goods_add", "goods_media_add") + + // Final Merge + g.AddEdge("goods_category_add", "format_output") + g.AddEdge("goods_media_add", "format_output") + + g.AddEdge("format_output", compose.END) + + return g.Compile(ctx) +} diff --git a/internal/domain/workflow/hyt/product_upload.go b/internal/domain/workflow/hyt/product_upload.go index cc9ab70..35114ed 100644 --- a/internal/domain/workflow/hyt/product_upload.go +++ b/internal/domain/workflow/hyt/product_upload.go @@ -19,10 +19,10 @@ import ( "github.com/cloudwego/eino/compose" ) -const WorkflowID = "hyt.productUpload" +const WorkflowIDProductUpload = "hyt.productUpload" func init() { - runtime.Register(WorkflowID, func(d *runtime.Deps) (runtime.Workflow, error) { + runtime.Register(WorkflowIDProductUpload, func(d *runtime.Deps) (runtime.Workflow, error) { return &productUpload{cfg: d.Conf, toolManager: d.ToolManager}, nil }) } @@ -37,7 +37,7 @@ type ProductUploadWorkflowInput struct { Text string `mapstructure:"text"` } -func (o *productUpload) ID() string { return WorkflowID } +func (o *productUpload) ID() string { return WorkflowIDProductUpload } func (o *productUpload) Invoke(ctx context.Context, rec *entitys.Recognize) (map[string]any, error) { // 构建工作流 @@ -64,8 +64,8 @@ func (o *productUpload) Invoke(ctx context.Context, rec *entitys.Recognize) (map return output, nil } -// ProductIngestData 对应 HYTProductPropertyTemplateZH 的结构 -type ProductIngestData struct { +// ProductIngestData 对应 HYTSupplierProductPropertyTemplateZH 的结构 +type SupplierProductIngestData struct { BarCode string `json:"条码"` CategoryName string `json:"分类名称"` GoodsName string `json:"货品名称"` @@ -99,7 +99,7 @@ type ProductIngestData struct { type ProductUploadContext struct { mu *sync.Mutex InputText string - IngestData *ProductIngestData + IngestData *SupplierProductIngestData UploadReq *toolPu.ProductUploadRequest SupplierName string WarehouseName string @@ -121,7 +121,7 @@ func (o *productUpload) buildWorkflow(ctx context.Context) (compose.Runnable[*Pr } // 解析用户输入的中文 JSON - var ingestData ProductIngestData + var ingestData SupplierProductIngestData if err := json.Unmarshal([]byte(in.Text), &ingestData); err != nil { return nil, fmt.Errorf("解析商品数据失败: %w", err) } diff --git a/internal/services/capability.go b/internal/services/capability.go index f163a79..759433c 100644 --- a/internal/services/capability.go +++ b/internal/services/capability.go @@ -76,7 +76,7 @@ func (s *CapabilityService) ProductIngest(c *fiber.Ctx) error { var sysProductPropertyTemplateZH string switch req.SysId { case "hyt": // 货易通 - sysProductPropertyTemplateZH = constants.HYTProductPropertyTemplateZH + sysProductPropertyTemplateZH = constants.HYTGoodsAddPropertyTemplateZH default: return errorcode.ParamErrf("invalid sys_id") } @@ -191,7 +191,7 @@ func (s *CapabilityService) ProductIngestConfirm(c *fiber.Ctx) error { switch respData.SysId { // 货易通 case "hyt": - workflowId = hytWorkflow.WorkflowID + workflowId = hytWorkflow.WorkflowIDGoodsAdd default: return errorcode.ParamErr("invalid sys_id") }