diff --git a/config/config_test.yaml b/config/config_test.yaml index bdbc26d..35a2888 100644 --- a/config/config_test.yaml +++ b/config/config_test.yaml @@ -124,6 +124,13 @@ eino_tools: # 货易通商品品牌查询 hytGoodsBrandSearch: base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list" + # == 电商充值系统 == + # 我们的商品统计 + rechargeStatisticsOursProduct: + base_url: "http://admin.1688sup.cn:8001/admin/statistics/oursProduct" + api_key: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY3MDc3NzcwLCJuYmYiOjE3NjcwNzU5NzAsImp0aSI6IjEiLCJQaG9uZSI6IjE4MDAwMDAwMDAwIiwiVXNlck5hbWUiOiJsc3hkIiwiUmVhbE5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9DQVNISUVSLFZDTF9PUEVSQVRFLFZDTF9BRE1JTixWQ0xfQUFBLFZDTF9WQ0xfT1BFUkFULFZDTF9JTlZPSUNFLENSTV9BRE1JTixMSUFOTElBTl9BRE1JTixNQVJLRVRNQUcyX0FETUlOLFBIT05FQklMTF9BRE1JTixRSUFOWkhVX1NVUFBFUl9BRE0sTUFSS0VUSU5HU0FBU19TVVBFUkFETUlOLENBUkRfQ09ERSxDQVJEX1BST0NVUkVNRU5ULE1BUktFVElOR1NZU1RFTV9TVVBFUixTVEFUSVNUSUNBTFNZU1RFTV9BRE1JTixaTFRYX0FETUlOLFpMVFhfT1BFUkFURSIsIkRpbmdVc2VySWQiOiIxNjIwMjYxMjMwMjg5MzM4MzQifQ.Nuw_aR6iSPmhhh9E5rhyTxHBsgWtaTZvbnc7SFTnUBJXTQvYahnk0LyZaVpsVw6FB3cU0F5xOdX3rmGyWyaiszWO6yi-o1oxGMXwhf39fMiWT2xUI6pAn9Ync8DmZ4tOMCNUTdEk4CaQFzrTwJs0c-VR4yW6LgoPmNPvUVZ-KwmusUpnPz5j9RsJItzIWE3bpGGsfB54e2UERcZdbo9BXxCZIBbpAYKBKdl73KuI8SNaXgKvGTrJ6hEN4ESpnbisJVwT5pp_kuChJlcfjHTHFsEf4RJDjN9gTrtDbBWZyY3OmO2ukqYAM7tZPs6TXJwvQGJQsFRVZUBGxS1nD_6DzQ" + excel2pic: + base_url: "http://192.168.6.109:8010/api/v1/convert" dingtalk: api_key: "dingsbbntrkeiyazcfdg" diff --git a/internal/domain/tools/common/excel_generator/client.go b/internal/domain/tools/common/excel_generator/client.go index eae9451..f2cd74f 100644 --- a/internal/domain/tools/common/excel_generator/client.go +++ b/internal/domain/tools/common/excel_generator/client.go @@ -15,19 +15,15 @@ func New() *Client { } // Call 根据模板和数据生成 Excel 字节流 -// templatePath: 模板文件路径 -// data: 二维字符串数组,不再使用反射 -// startRow: 数据填充起始行 (默认 2) -// styleRow: 样式参考行 (默认 2) -func (g *Client) Call(templatePath string, data [][]string, startRow int, styleRow int) ([]byte, error) { - if startRow <= 0 { - startRow = 2 +func (g *Client) Call(req *ExcelGeneratorRequest) ([]byte, error) { + if req.StartRow <= 0 { + req.StartRow = 2 } - if styleRow <= 0 { - styleRow = 2 + if req.StyleRow <= 0 { + req.StyleRow = 2 } - f, err := excelize.OpenFile(templatePath) + f, err := excelize.OpenFile(req.TemplatePath) if err != nil { return nil, err } @@ -35,20 +31,25 @@ func (g *Client) Call(templatePath string, data [][]string, startRow int, styleR sheet := f.GetSheetName(0) + // 若提供标题,替换第一行表格标题 + if len(req.Title) > 0 { + f.SetCellValue(sheet, "A1", req.Title) + } + // 获取样式和行高 - styleID, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", styleRow)) + styleID, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", req.StyleRow)) if err != nil { log.Errorf("获取样式失败: %v", err) styleID = 0 } - rowHeight, err := f.GetRowHeight(sheet, styleRow) + rowHeight, err := f.GetRowHeight(sheet, req.StyleRow) if err != nil { log.Errorf("获取行高失败: %v", err) rowHeight = 31 // 默认高度 } - row := startRow - for i, item := range data { + row := req.StartRow + for i, item := range req.ExcelData { currentRow := row + i // 设置行高 diff --git a/internal/domain/tools/common/excel_generator/type.go b/internal/domain/tools/common/excel_generator/type.go new file mode 100644 index 0000000..3a1b7b4 --- /dev/null +++ b/internal/domain/tools/common/excel_generator/type.go @@ -0,0 +1,9 @@ +package excel_generator + +type ExcelGeneratorRequest struct { + TemplatePath string // 模板文件路径 + ExcelData [][]string // 二维字符串数组 + StartRow int // 数据填充起始行 (默认 2) + StyleRow int // 样式参考行 (默认 2) + Title string // 表格标题(仅替换) +} diff --git a/internal/domain/workflow/recharge/statistics_ours_product.go b/internal/domain/workflow/recharge/statistics_ours_product.go index ebc33a9..8c78209 100644 --- a/internal/domain/workflow/recharge/statistics_ours_product.go +++ b/internal/domain/workflow/recharge/statistics_ours_product.go @@ -4,16 +4,19 @@ import ( "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" toolManager "ai_scheduler/internal/domain/tools" + "ai_scheduler/internal/domain/tools/common/excel_generator" "ai_scheduler/internal/domain/tools/recharge/statistics_ours_product" "ai_scheduler/internal/domain/workflow/runtime" "ai_scheduler/internal/pkg/utils_oss" "context" "errors" "fmt" + "math/rand" "path/filepath" "time" "github.com/cloudwego/eino/compose" + "github.com/go-kratos/kratos/v2/log" ) const WorkflowIDStatisticsOursProduct = "recharge.statisticsOursProduct" @@ -31,8 +34,7 @@ type statisticsOursProduct struct { } type StatisticsOursProductWorkflowInput struct { - StartTime string `json:"start_time"` - EndTime string `json:"end_time"` + Time time.Time `json:"time"` } type StatisticsOursProductWorkflowOutput struct { @@ -52,15 +54,10 @@ func (w *statisticsOursProduct) Invoke(ctx context.Context, args *runtime.Workfl } // 获取参数时间 - now := args.Args["now"].(time.Time) input := &StatisticsOursProductWorkflowInput{ - // 默认值,具体应从 rec 解析 - StartTime: now.Format("2006010200"), - EndTime: now.Format("2006010215"), + Time: args.Args["now"].(time.Time), } - input.StartTime = "2025122300" - // 工作流过程调用 output, err := runnable.Invoke(ctx, input) if err != nil { @@ -75,66 +72,107 @@ func (w *statisticsOursProduct) Invoke(ctx context.Context, args *runtime.Workfl return output, nil } +type StatisticsOursProductContext struct { + Time time.Time + StartTime string + EndTime string + Title string + ProductData []statistics_ours_product.StatisticsOursProductItem + ImgUrl string + ExcelData [][]string +} + func (w *statisticsOursProduct) buildWorkflow(ctx context.Context) (compose.Runnable[*StatisticsOursProductWorkflowInput, map[string]any], error) { c := compose.NewChain[*StatisticsOursProductWorkflowInput, map[string]any]() - // 1. 调用工具统计我们的商品 + // 1. 参数整理 + c.AppendLambda(compose.InvokableLambda(w.formatContext)) + + // 2. 调用工具统计我们的商品 c.AppendLambda(compose.InvokableLambda(w.callStatisticsTool)) - // 2. 生成 Excel 并转图片上传 + // 3. 生成 Excel 并转图片上传 c.AppendLambda(compose.InvokableLambda(w.generateExcelAndUpload)) - // 3. 转map输出 + // 4. 转map输出 c.AppendLambda(compose.InvokableLambda(w.convertToMap)) return c.Compile(ctx) } -func (w *statisticsOursProduct) callStatisticsTool(ctx context.Context, input *StatisticsOursProductWorkflowInput) ([]statistics_ours_product.StatisticsOursProductItem, error) { - req := statistics_ours_product.StatisticsOursProductRequest{ - Page: 1, - Limit: 100, // 假设取前100条 - Serial: []string{input.StartTime, input.EndTime}, - } +// formatContext 整理上下文参数 +func (w *statisticsOursProduct) formatContext(ctx context.Context, input *StatisticsOursProductWorkflowInput) (*StatisticsOursProductContext, error) { + startTime := input.Time.Format("2006010200") + endTime := input.Time.Format("2006010215") + endTimeStr := input.Time.Format("1.2号15点") - return w.toolManager.Recharge.StatisticsOursProduct.Call(ctx, req) + return &StatisticsOursProductContext{ + Time: time.Now(), + StartTime: startTime, + EndTime: endTime, + Title: fmt.Sprintf("截止 %s 我们的商品统计", endTimeStr), + }, nil } -func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, data []statistics_ours_product.StatisticsOursProductItem) (*StatisticsOursProductWorkflowOutput, error) { - // 2. 获取模板路径 (假设在项目根目录的 assets/templates 下) +func (w *statisticsOursProduct) callStatisticsTool(ctx context.Context, state *StatisticsOursProductContext) (*StatisticsOursProductContext, error) { + req := statistics_ours_product.StatisticsOursProductRequest{ + Page: 1, + Limit: 100, // 仅取前100条 + Serial: []string{state.StartTime, state.EndTime}, + } + + dataList, err := w.toolManager.Recharge.StatisticsOursProduct.Call(ctx, req) + if err != nil { + log.Errorf("调用统计我们的商品工具失败: %v", err) + return nil, fmt.Errorf("获取统计我们的商品数据失败") + } + if len(dataList) == 0 { + return nil, fmt.Errorf("我们的商品数据为空") + } + + state.ProductData = dataList + + return state, nil +} + +func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, state *StatisticsOursProductContext) (*StatisticsOursProductContext, error) { + // 1. 获取模板路径 cwd, _ := filepath.Abs(".") - templatePath := filepath.Join(cwd, "tmpl", "excel_temp", "recharge_statistics_ours_product.xlsx") - fileName := fmt.Sprintf("statistics_ours_product_%d", time.Now().Unix()) + templatePath := filepath.Join(cwd, "../../tmpl", "excel_temp", "recharge_statistics_ours_product.xlsx") + fileName := fmt.Sprintf("statistics_ours_product_%d%d", time.Now().Unix(), rand.Intn(1000)) - // 3. 转换数据为 [][]string - excelData := w.convertDataToExcelFormat(data) + // 2. 转换数据为 [][]string + excelData := w.convertDataToExcelFormat(state.ProductData) - // 4. 生成 Excel - excelBytes, err := w.toolManager.Common.ExcelGenerator.Call(templatePath, excelData, 4, 3) + // 3. 生成 Excel + req := &excel_generator.ExcelGeneratorRequest{ + TemplatePath: templatePath, + ExcelData: excelData, + StartRow: 4, + StyleRow: 3, + Title: state.Title, + } + excelBytes, err := w.toolManager.Common.ExcelGenerator.Call(req) if err != nil { return nil, fmt.Errorf("生成 Excel 失败: %v", err) } - // 5. Excel 转图片 + // 4. Excel 转图片 picBytes, err := w.toolManager.Common.ImageConverter.Call(fileName+".xlsx", excelBytes, 2) if err != nil { return nil, fmt.Errorf("Excel 转图片失败: %v", err) } - // 6. 上传 OSS + // 5. 上传 OSS url, err := w.ossClient.UploadBytes(fileName+".png", picBytes) if err != nil { return nil, fmt.Errorf("上传 OSS 失败: %v", err) } - res := &StatisticsOursProductWorkflowOutput{ - Path: "", - Url: url, - Data: excelData, - Desc: "", - } + state.ImgUrl = url + state.ExcelData = excelData - return res, nil + return state, nil } // convertDataToExcelFormat 将业务数据转换为 Excel 生成器需要的二维字符串数组 @@ -158,11 +196,11 @@ func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_ return result } -func (w *statisticsOursProduct) convertToMap(ctx context.Context, output *StatisticsOursProductWorkflowOutput) (map[string]any, error) { +func (w *statisticsOursProduct) convertToMap(ctx context.Context, state *StatisticsOursProductContext) (map[string]any, error) { return map[string]any{ - "path": output.Path, - "url": output.Url, - "data": output.Data, - "desc": output.Desc, + "path": "", + "url": state.ImgUrl, + "data": state.ExcelData, + "desc": state.Title, }, nil }