Browse Source

睿姿得数据对接

gmy 6 months ago
parent
commit
a252ef77b6

+ 218 - 0
services/ruizide/data_processor.go

@@ -0,0 +1,218 @@
+package liangyou
+
+import (
+	"context"
+	"encoding/json"
+	"eta/eta_data_analysis/models"
+	"eta/eta_data_analysis/utils"
+	"fmt"
+	"log"
+	"os"
+	"time"
+
+	"github.com/chromedp/chromedp"
+)
+
+var (
+	rzdLoginPath = "https://clients.rystadenergy.com/clients/"
+)
+
+func RzdDataDeal(cont context.Context) (err error) {
+
+	// 读取 JSON 文件
+	configFile, err := os.ReadFile(utils.RZD_JSON_PATH)
+	if err != nil {
+		fmt.Printf("读取配置文件错误: %v\n", err)
+		return
+	}
+
+	// 定义通用的 map 结构体来解析 JSON
+	var data map[string]map[string][]string
+
+	// 解析 JSON 文件内容
+	err = json.Unmarshal(configFile, &data)
+	if err != nil {
+		fmt.Printf("解析配置文件错误: %v\n", err)
+		return
+	}
+
+	// 打印解析后的数据以验证
+	fmt.Printf("%+v\n", data)
+
+	// 创建 chromedp 执行上下文
+	// 下载目录
+	downloadDir := "./downloads"
+	os.MkdirAll(downloadDir, os.ModePerm) // 创建下载目录
+
+	options := []chromedp.ExecAllocatorOption{
+		chromedp.Flag("headless", false),
+		chromedp.Flag("disable-blink-features", "AutomationControlled"),
+		chromedp.UserAgent(`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36`),
+
+		// 创建下载路径
+		chromedp.Flag("download.default_directory", downloadDir),
+		chromedp.Flag("download.prompt_for_download", false), // 不弹出下载对话框
+		chromedp.Flag("safebrowsing.enabled", true),          // 启用安全浏览
+	}
+
+	allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)
+	defer cancel()
+	ctx, cancel := chromedp.NewContext(allocCtx)
+	defer cancel()
+
+	// 登录操作
+	err = login(ctx)
+	if err != nil {
+		fmt.Printf("登录错误: %v\n", err)
+		return
+	}
+
+	// step_1:Analytics Library
+	err = chromedp.Run(ctx,
+		chromedp.WaitVisible(`a[href="/clients/search/"]`, chromedp.ByQuery),
+		chromedp.Click(`a[href="/clients/search/"]`, chromedp.ByQuery), // 点击链接
+		chromedp.Sleep(5*time.Second),
+
+		// 输入搜索内容
+		chromedp.SetValue(`input[class="ais-SearchBox-input rounded border py-2 px-3 shadow-sm font-size-14 w-100"]`, "oil demand signals weekly report", chromedp.ByQuery),
+		chromedp.Sleep(3*time.Second),
+
+		// 点击第一个 li 标签下的第一个 Excel 图标
+		chromedp.Click(`div.ais-InfiniteHits li:first-child img[src="/Static/img/icons/xls.png"]`, chromedp.ByQuery),
+		chromedp.Sleep(5*time.Second), // 等待下载完成
+	)
+	if err != nil {
+		return err
+	}
+
+	// step_2:Cube Dashboards
+	err = chromedp.Run(ctx,
+		// Supply Revision Analysis 数据读取
+		chromedp.WaitVisible(`a[href="/clients/subscription/"]`, chromedp.ByQuery),
+		chromedp.Click(`a[href="/clients/subscription/"]`, chromedp.ByQuery), // 点击链接
+		chromedp.Sleep(5*time.Second),
+
+		// 点击包含 Supply Revision Analysis 的 li 元素
+		chromedp.Click(`div.ais-Hits li[contains(., 'Supply Revision Analysis')]`, chromedp.ByQuery),
+		chromedp.Sleep(10*time.Second),
+
+		// 设置查询时间范围
+		chromedp.WaitVisible(`div.visual.customPadding.allow-deferred-rendering.visual-slicer`, chromedp.ByQuery),                                                         // 等待 div 可见
+		chromedp.SetValue(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "2020", chromedp.ByQuery), // 设置第一个 input 的值为 2020
+		chromedp.Sleep(2*time.Second),
+		// 回车查询
+		chromedp.SendKeys(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "\n"),
+
+		// 点击下载按钮 下载表格
+		chromedp.Click(`div.[ btn btn-link btn-sm ] [ dashboard-action dashboard-action--download-data ]]`, chromedp.ByQuery),
+		chromedp.Sleep(5*time.Second), // 等待下载完成
+
+		// Oil Demand Analysis 数据读取
+		chromedp.WaitVisible(`a[href="/clients/subscription/"]`, chromedp.ByQuery),
+		chromedp.Click(`a[href="/clients/subscription/"]`, chromedp.ByQuery), // 点击链接
+		chromedp.Sleep(5*time.Second),
+
+		// 点击包含 Supply Revision Analysis 的 li 元素
+		chromedp.Click(`div.ais-Hits li[contains(., 'Oil Demand Analysis')]`, chromedp.ByQuery),
+		chromedp.Sleep(10*time.Second),
+
+		// 设置查询时间范围
+		chromedp.WaitVisible(`div.visual.customPadding.allow-deferred-rendering.visual-slicer`, chromedp.ByQuery), // 等待 div 可见
+		chromedp.SetValue(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "2015", chromedp.ByQuery),
+		chromedp.Sleep(2*time.Second),
+		// 回车查询
+		chromedp.SendKeys(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "\n"),
+
+		// 点击下载按钮 下载表格
+		chromedp.Click(`div.[ btn btn-link btn-sm ] [ dashboard-action dashboard-action--download-data ]]`, chromedp.ByQuery),
+		chromedp.Sleep(5*time.Second), // 等待下载完成
+
+		// Oil Supply Analysis 数据读取
+		chromedp.WaitVisible(`a[href="/clients/subscription/"]`, chromedp.ByQuery),
+		chromedp.Click(`a[href="/clients/subscription/"]`, chromedp.ByQuery), // 点击链接
+		chromedp.Sleep(5*time.Second),
+
+		// 点击包含 Supply Revision Analysis 的 li 元素
+		chromedp.Click(`div.ais-Hits li[contains(., 'Oil Demand Analysis')]`, chromedp.ByQuery),
+		chromedp.Sleep(10*time.Second),
+
+		// 设置查询时间范围
+		chromedp.WaitVisible(`div.visual.customPadding.allow-deferred-rendering.visual-slicer`, chromedp.ByQuery), // 等待 div 可见
+		chromedp.SetValue(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "2010", chromedp.ByQuery),
+		chromedp.Sleep(2*time.Second),
+		// 回车查询
+		chromedp.SendKeys(`div.visual.customPadding.allow-deferred-rendering.visual-slicer input.date-slicer-input.enable-hover:first-of-type`, "\n"),
+
+		// 点击下载按钮 下载表格
+		chromedp.Click(`div.[ btn btn-link btn-sm ] [ dashboard-action dashboard-action--download-data ]]`, chromedp.ByQuery),
+		chromedp.Sleep(5*time.Second), // 等待下载完成
+	)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func login(ctx context.Context) error {
+
+	return chromedp.Run(ctx,
+		chromedp.Navigate(rzdLoginPath),
+		chromedp.SetValue(`input[id="Username"]`, utils.LY_USERNAME, chromedp.ByQuery),
+		chromedp.SetValue(`input[id="Username"]`, utils.LY_USERNAME, chromedp.ByQuery),
+		chromedp.SetValue(`input[id="Password"]`, utils.LY_PASSWORD, chromedp.ByQuery),
+		chromedp.WaitEnabled(`//button[text()='Login']`, chromedp.BySearch),
+		chromedp.Click(`//button[text()='Login']`, chromedp.BySearch),
+		chromedp.Sleep(5*time.Second),
+
+		// 等待并点击登录后页面的链接
+		chromedp.WaitVisible(`a[href="/clients/"]`, chromedp.ByQuery), // 等待 Analytics Library 链接可见
+		chromedp.Sleep(5*time.Second),                                 // 等待页面加载完成
+	)
+}
+
+func addLyIndex(classifyId int, indexCode string, indexName string, unit string, frequency string) (int, error) {
+	// 添加指标
+	index := &models.BaseFromLyIndex{
+		CreateTime:           utils.GetCurrentTime(),
+		ModifyTime:           utils.GetCurrentTime(),
+		BaseFromLyClassifyId: classifyId,
+		IndexCode:            indexCode,
+		IndexName:            indexName,
+		Frequency:            frequency,
+		Unit:                 unit,
+		EdbExist:             0,
+	}
+	postEdbLib, err := httpRequestFill(index, utils.ADD_LY_INDEX)
+	if err != nil {
+		// 有错误就不继续执行
+		log.Printf("postEdbLib err: %v", err)
+		return 0, err
+	}
+	var requestResponse models.RequestResponse[int64]
+	err = json.Unmarshal(postEdbLib, &requestResponse)
+	indexId := requestResponse.Data
+	return int(indexId), nil
+}
+
+func httpRequestFill(data interface{}, urlMethod string) (postEdbLib []byte, err error) {
+	// 转换成json
+	marshal, err := json.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+	// json 转 interface
+	var result map[string]interface{}
+	err = json.Unmarshal(marshal, &result)
+	if err != nil {
+		return nil, err
+	}
+
+	postEdbLib, err = utils.PostEdbLibRequest(result, urlMethod)
+	if err != nil {
+		// 有错误就不继续执行
+		log.Printf("postEdbLib err: %v", err)
+		return nil, err
+	}
+	return postEdbLib, nil
+}

+ 2456 - 0
services/ruizide/processor_business_logic.go

@@ -0,0 +1,2456 @@
+// @Author gmy 2024/8/6 10:50:00
+package liangyou
+
+import (
+	"context"
+	"encoding/json"
+	"eta/eta_data_analysis/models"
+	"eta/eta_data_analysis/utils"
+	"fmt"
+	"github.com/PuerkitoBio/goquery"
+	"github.com/beego/beego/v2/core/logs"
+	"github.com/chromedp/chromedp"
+	"log"
+	"regexp"
+	"strconv"
+	"strings"
+	"unicode"
+)
+
+var (
+	sourceName = "lysww" // 粮油商务网
+)
+
+// TableData 用于存储表格的数据
+type TableData struct {
+	Headers []string   `json:"headers"`
+	Rows    [][]string `json:"rows"`
+}
+
+// ImportCostProcessor
+// @Description: 进口成本处理器
+type ImportCostProcessor struct{}
+
+func (p *ImportCostProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing import cost...")
+
+	// 解析关键字
+	if len(keywords) < 5 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingImportCostProcessor Process() : keywords must contain at least 5 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[len(keywords)-4]
+	rowVariety := keywords[0]
+	rowPort := keywords[len(keywords)-3]
+	indexNamePrefix := keywords[:1]
+	indexNameSuffix := keywords[1:]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 解析日期并计算当前月份
+	var targetMonths []string
+	if product == "油菜籽" {
+		targetMonths, err = utils.ParseDateAndMonthColzaOil(format)
+	} else {
+		targetMonths, err = utils.ParseDateAndMonth(dateText)
+	}
+	if err != nil {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingImportCostProcessor Process() : Failed to parse date: %v", err)
+	}
+	fmt.Printf("Target Month: %s\n", targetMonths)
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnName) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("ProcessingImportCostProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 处理表格中的每一行
+		//var flag bool = true
+		var previousRowVariety string
+		var previousRowPort string
+		for rowIndex, row := range tableRows {
+			if len(row) == len(tableHeaders) {
+				previousRowVariety = row[0]
+				previousRowPort = row[1]
+			} else if len(row) == len(tableHeaders)-1 {
+				previousRowPort = row[0]
+				row = append([]string{previousRowVariety}, row...)
+				tableRows[rowIndex] = row
+			} else if len(row) == len(tableHeaders)-2 {
+				row = append([]string{previousRowVariety, previousRowPort}, row...)
+				tableRows[rowIndex] = row
+			}
+			for _, targetMonth := range targetMonths {
+				if len(row) >= len(tableHeaders) && strings.Contains(rowVariety, row[0]) && row[1] == targetMonth && row[len(row)-1] == rowPort {
+					if columnIdx < len(row) {
+						// 指标名称
+						indexNameList := append(indexNamePrefix, append([]string{targetMonth}, indexNameSuffix...)...)
+						indexName := strings.Join(indexNameList[:len(keywords)-2], ":")
+						// 指标编码
+						indexCode := utils.GenerateIndexCode(sourceName, indexName)
+						// 指标id获取
+						indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+						if err != nil {
+							logs.Error("ProcessingImportCostProcessor Process() : Failed to get index id: %v", err)
+							continue
+						}
+						paramsLib := make(map[string]interface{})
+						paramsLib["IndexId"] = indexId
+						paramsLib["DataTime"] = format
+						postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+						if err != nil {
+							logs.Error("ProcessingImportCostProcessor Process() : Failed to get data by index id and date: %v", err)
+							continue
+						}
+						var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+						err = json.Unmarshal(postEdbLib, &requestResponse)
+
+						indexData := requestResponse.Data
+						if len(indexData) > 0 {
+							logs.Info("ProcessingImportCostProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+							continue
+						}
+
+						valueStr := row[columnIdx]
+						value, err := strconv.ParseFloat(valueStr, 64)
+						if err != nil {
+							return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+						}
+						// 创建并添加到结果列表
+						baseFromLyData := models.BaseFromLyData{
+							DataTime:          format,
+							Value:             value,
+							BaseFromLyIndexId: indexId,
+							IndexCode:         indexCode,
+						}
+						result = append(result, baseFromLyData)
+					} else {
+						log.Printf("ProcessingImportCostProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, rowPort)
+					}
+					break
+				}
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// ProcessingProfitProcessor
+// @Description: 加工利润处理器
+type ProcessingProfitProcessor struct{}
+
+func (p *ProcessingProfitProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	fmt.Println("Processing processing profit...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingProfitProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[1]
+	rowVariety := keywords[0]
+	indexNamePrefix := keywords[:1]
+	indexNameSuffix := keywords[1:]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 解析日期并计算当前月份 和 后两月
+	yearMonths, err := utils.ConvertTimeFormatToYearMonth(format)
+	if err != nil {
+		return nil, err
+	}
+	fmt.Printf("Target yearMonth: %s\n", yearMonths)
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(columnName, header) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("ProcessingProfitProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 处理表格中的每一行
+		var previousRowVariety string
+		for rowIndex, row := range tableRows {
+			if len(row) == len(tableHeaders) {
+				previousRowVariety = row[0]
+			} else if len(row) == len(tableHeaders)-1 {
+				row = append([]string{previousRowVariety}, row...)
+				tableRows[rowIndex] = row
+			}
+
+			for _, targetMonth := range yearMonths {
+				if len(row) >= len(tableHeaders) && row[0] == rowVariety && row[1] == targetMonth {
+					if columnIdx < len(row) {
+						// 指标名称
+						indexNameList := append(indexNamePrefix, append([]string{targetMonth}, indexNameSuffix...)...)
+						indexName := strings.Join(indexNameList[:len(keywords)-1], ":")
+						// 指标编码
+						indexCode := utils.GenerateIndexCode(sourceName, indexName)
+						// 指标id获取
+						indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+						if err != nil {
+							logs.Error("ProcessingProfitProcessor Process() : Failed to get index id: %v", err)
+							continue
+						}
+
+						paramsLib := make(map[string]interface{})
+						paramsLib["IndexId"] = indexId
+						paramsLib["DataTime"] = format
+						postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+						if err != nil {
+							logs.Error("ProcessingProfitProcessor Process() : Failed to get data by index id and date: %v", err)
+							continue
+						}
+						var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+						err = json.Unmarshal(postEdbLib, &requestResponse)
+
+						indexData := requestResponse.Data
+						if len(indexData) > 0 {
+							logs.Info("ProcessingProfitProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+							continue
+						}
+
+						valueStr := row[columnIdx]
+						value, err := strconv.ParseFloat(valueStr, 64)
+						if err != nil {
+							return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+						}
+						// 创建并添加到结果列表
+						baseFromLyData := models.BaseFromLyData{
+							DataTime:          format,
+							Value:             value,
+							BaseFromLyIndexId: indexId,
+							IndexCode:         indexCode,
+						}
+						result = append(result, baseFromLyData)
+					} else {
+						log.Printf("ProcessingProfitProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnName)
+					}
+					break
+				}
+			}
+
+		}
+	}
+
+	return result, nil
+}
+
+// ShippingCostProcessor
+// @Description: 船运费用处理器
+type ShippingCostProcessor struct{}
+
+func (p *ShippingCostProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	fmt.Println("Processing processing profit...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ShippingCostProcessor Process() : keywords must contain at least 5 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[len(keywords)-3]
+	rowVariety := keywords[0]
+	rowDestination := keywords[1]
+	rowShipType := keywords[2]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnName) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("ShippingCostProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 处理表格中的每一行
+		for rowIndex, row := range tableRows {
+			if len(row) == len(tableHeaders)-1 {
+				row = append([]string{rowVariety}, row...)
+				tableRows[rowIndex] = row
+				rowShipType, err = extractValueInParentheses(rowVariety)
+				if err != nil {
+					logs.Error("ShippingCostProcessor Process() : Failed to extract value in parentheses: %v", err)
+					continue
+				}
+
+			}
+			if len(row) >= len(tableHeaders) && row[0] == rowVariety && (row[1] == rowDestination || strings.Contains(row[0], row[1])) && row[2] == rowShipType {
+				if columnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-3], `:`)
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("ShippingCostProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("ShippingCostProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("ShippingCostProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						continue
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("ShippingCostProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnName)
+				}
+				break
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// SupplyDemandBalanceProcessor
+// @Description: 供需平衡处理器
+type SupplyDemandBalanceProcessor struct{}
+
+func (p *SupplyDemandBalanceProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	// https://www.fao.com.cn/art/gG7gKTCNDHLJNsq9QRYjoQ==.htm
+	logs.Info("Processing processing report...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	var columnName string
+	rowVariety := keywords[1]
+
+	// 提取所有表格数据
+	tableData := getTableData(reportContent, true)
+	logs.Info("SupplyDemandBalanceProcessor Process() : Table data: %v", tableData)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	currentYearAndNextYear, err := utils.GetCurrentYearAndNextYear(format)
+	if err != nil {
+		return nil, err
+	}
+
+	month, err := utils.GetCurrentMonth(format)
+	if err != nil {
+		return nil, err
+	}
+	monthSuffix := "预估"
+	logs.Info("SupplyDemandBalanceProcessor Process() : Target Year: %s:%s\n", currentYearAndNextYear, month+monthSuffix)
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	headers := tableData.Headers
+	rows := tableData.Rows
+
+	for _, year := range currentYearAndNextYear {
+		columnName = year + month + monthSuffix
+		isCurrentYear, err := utils.IsCurrentYear(year)
+		if err != nil {
+			logs.Error("SupplyDemandBalanceProcessor Process() : Failed to determine if year is current year: %v", err)
+			continue
+		}
+		if !isCurrentYear {
+			format, err = utils.GetNextYearLastDay(format)
+			if err != nil {
+				logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get next year last day: %v", err)
+				continue
+			}
+		}
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range headers {
+			if strings.Contains(columnName, header) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			logs.Error("SupplyDemandBalanceProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+		// 处理表格中的每一行
+		for _, row := range rows {
+			if len(row) >= len(headers) && row[0] == rowVariety {
+				if columnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-2], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+					}
+					yearMonth, err := utils.GetYearMonth(format)
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get year month: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["YearMonth"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME_YM)
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("SupplyDemandBalanceProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 存在走更新逻辑 主要更新今年在去年的预估值
+						indexData := indexData[0]
+						if indexData.Value != value {
+							time, err := utils.StringToTime(indexData.ModifyTime)
+							if err != nil {
+								return nil, err
+							}
+
+							timeZero, err := utils.StringToTimeZero(format)
+							if err != nil {
+								return nil, err
+							}
+
+							if time.Before(timeZero) {
+								// 更新指标数据
+								paramsLib := make(map[string]interface{})
+								paramsLib["Id"] = indexData.BaseFromLyDataId
+								paramsLib["Value"] = value
+								_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_DATA_BY_ID)
+								if err != nil {
+									return nil, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+								}
+								// 更新指标库数据
+								paramsLib = make(map[string]interface{})
+								paramsLib["IndexCode"] = indexData.IndexCode
+								paramsLib["DataTime"] = yearMonth
+								postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_EDB_DATA_BY_INDEX_CODE_AND_DATA_TIME)
+								if err != nil {
+									logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+									continue
+								}
+								var requestResponse models.RequestResponse[[]models.EdbDataLy]
+								err = json.Unmarshal(postEdbLib, &requestResponse)
+								if err != nil {
+									return nil, err
+								}
+
+								edbIndexData := requestResponse.Data
+								if len(edbIndexData) > 0 {
+									paramsLib := make(map[string]interface{})
+									paramsLib["Id"] = edbIndexData[0].EdbInfoId
+									paramsLib["Value"] = value
+									_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_EDB_DATA_BY_ID)
+									if err != nil {
+										return nil, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+									}
+								}
+							}
+						}
+						continue
+					}
+
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("SupplyDemandBalanceProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnName)
+				}
+				break
+			}
+		}
+	}
+	return result, nil
+}
+
+// PurchaseShippingProcessor
+// @Description: 采购装船处理器
+type PurchaseShippingProcessor struct{}
+
+func (p *PurchaseShippingProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing purchase shipping...")
+	// 解析关键字
+	if len(keywords) < 3 {
+		return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : keywords must contain at least 3 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[len(keywords)-3]
+
+	// 提取所有表格数据
+	tableData := getPurchaseShippingTableData(reportContent)
+	logs.Info("SupplyDemandBalanceProcessor Process() : Table data: %v", tableData)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	headers := tableData.Headers
+	rows := tableData.Rows
+
+	// 查找目标列
+	columnIdx := -1
+	for i, header := range headers {
+		if strings.Contains(columnName, header) {
+			columnIdx = i
+			break
+		}
+	}
+	if columnIdx == -1 {
+		log.Printf("SupplyDemandBalanceProcessor Process() : Column '%s' not found in table", columnName)
+	} else {
+		// 处理表格中的每一行
+		for _, row := range rows {
+			if len(row) >= len(headers) {
+				if columnIdx < len(row) {
+					if !isNumber(row[columnIdx]) {
+						continue
+					}
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-3], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+					var yearMonth string
+					number, err := utils.ConvertMonthToNumber(row[1])
+					if err != nil {
+						return nil, err
+					}
+					yearMonth = row[0] + "-" + number
+					isSameMonth, err := utils.IsSameMonth(format, yearMonth)
+					if err != nil {
+						return nil, err
+					}
+					if isSameMonth {
+						yearMonth = format
+					} else {
+						lastDayOfMonth, err := utils.GetLastDayOfMonth(yearMonth)
+						if err != nil {
+							return nil, err
+						}
+						yearMonth = lastDayOfMonth
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+					}
+
+					month, err := utils.GetYearMonth(yearMonth)
+					if err != nil {
+						return nil, err
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["YearMonth"] = month
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME_YM)
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						if indexData[0].Value != value {
+							logs.Info("SupplyDemandBalanceProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+
+							lyData := indexData[0]
+							time, err := utils.StringToTime(lyData.ModifyTime)
+							if err != nil {
+								return nil, err
+							}
+
+							timeZero, err := utils.StringToTimeZero(format)
+							if err != nil {
+								return nil, err
+							}
+
+							if time.Before(timeZero) {
+								// 更新指标数据
+								paramsLib := make(map[string]interface{})
+								paramsLib["Id"] = lyData.BaseFromLyDataId
+								paramsLib["Value"] = value
+								_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_DATA_BY_ID)
+								if err != nil {
+									return nil, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+								}
+
+								// 同步更新指标库数据 须根据指标编码和日期更新
+								paramsLib = make(map[string]interface{})
+								paramsLib["IndexCode"] = lyData.IndexCode
+								paramsLib["DataTime"] = yearMonth
+								postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_EDB_DATA_BY_INDEX_CODE_AND_DATA_TIME)
+								if err != nil {
+									logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+									continue
+								}
+								var requestResponse models.RequestResponse[[]models.EdbDataLy]
+								err = json.Unmarshal(postEdbLib, &requestResponse)
+
+								edbIndexData := requestResponse.Data
+								if err != nil {
+									return nil, err
+								}
+								if len(edbIndexData) > 0 {
+									paramsLib := make(map[string]interface{})
+									paramsLib["Id"] = edbIndexData[0].EdbInfoId
+									paramsLib["Value"] = value
+									_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_EDB_DATA_BY_ID)
+									if err != nil {
+										return nil, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+									}
+								}
+							}
+						}
+						continue
+					}
+
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          yearMonth,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+					continue
+				} else {
+					log.Printf("SupplyDemandBalanceProcessor Process() : Column index out of range for row '%s'", columnName)
+				}
+				break
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// ProcessingReportProcessor
+// @Description: 加工报告处理器
+type ProcessingReportProcessor struct {
+}
+
+func (p *ProcessingReportProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing processing report...")
+	// 解析关键字
+	if len(keywords) < 3 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingReportProcessor Process() : keywords must contain at least 3 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[0]
+	rowName := keywords[1]
+
+	// 提取所有表格数据
+	tableData := getAllTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+	indexName := strings.Join(keywords[:len(keywords)-2], ":")
+	// 指标编码
+	indexCode := utils.GenerateIndexCode(sourceName, indexName)
+	// 指标id获取
+	indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+	if err != nil {
+		return nil, err
+	}
+
+	// 校验指标数据是否存在 根据指标id和日期 存在则跳过,不存在正常往下走
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	paramsLib := make(map[string]interface{})
+	paramsLib["IndexId"] = indexId
+	paramsLib["DataTime"] = format
+	postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+	if err != nil {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingReportProcessor Process() : Failed to get data by index id and date: %v", err)
+	}
+	var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+	err = json.Unmarshal(postEdbLib, &requestResponse)
+
+	indexData := requestResponse.Data
+	if len(indexData) > 0 {
+		logs.Info("ProcessingReportProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+		// 不必做更新处理,报告每周刷新,即使本周和上周数据一致,也需要每周记录
+		return []models.BaseFromLyData{}, nil
+	}
+
+	// 解析日期并计算当前周数
+	targetWeek, err := utils.ParseDateAndWeek(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingReportProcessor Process() : Failed to parse date: %v", err)
+	}
+
+	fmt.Printf("Target Week: %s\n", targetWeek)
+
+	var result []models.BaseFromLyData
+	// 处理提取的表格数据
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			headerString := extractChinese(header)
+			if strings.Contains(columnName, headerString) {
+				// 这个表格不是很好处理,这里写的有些僵硬,后续需要优化
+				if columnName == "国内大豆开机率" {
+					i = i + 2
+				}
+				columnIdx = i
+				break
+			}
+		}
+
+		if columnIdx == -1 {
+			logs.Error("ProcessingReportProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 查找本周的列位置
+		weekIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, targetWeek) && i > columnIdx {
+				weekIdx = i
+				break
+			}
+		}
+
+		if weekIdx == -1 {
+			fmt.Printf("Week column '%s' not found in table\n", targetWeek)
+			continue
+		}
+
+		// 查找目标行
+		for _, row := range tableRows {
+			if strings.Contains(row[0], rowName) {
+				if columnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-2], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("ProcessingReportProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("ProcessingReportProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("ProcessingReportProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 无需走更新逻辑,报告每日更新,即使今天和昨天数据一致,也需要每天记录,如果到这里也只是说,今天这个报告被读取了两次
+						continue
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("ProcessingReportProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+					continue
+				} else {
+					log.Printf("ProcessingReportProcessor Process() : Column index out of range for row '%s', '%s'", rowName, columnName)
+				}
+				break
+			}
+
+			/*if len(row) > 0 && strings.Contains(row[0], rowName) {
+				if weekIdx < len(row) {
+					logs.Info("Value in column '%s' - '%s': %s", columnName, rowName, row[columnIdx])
+					numFlag := isNumeric(row[columnIdx])
+					if numFlag {
+						value, err := strconv.ParseFloat(row[columnIdx], 64)
+						if err != nil {
+							logs.Error("ProcessingReportProcessor Process() : Error converting value to float64: %v", err)
+							return []models.BaseFromLyData{}, err
+						}
+						// 返回BaseFromLyData对象的数据
+						baseFromLyData := models.BaseFromLyData{
+							DataTime: dateText,
+							Value:    value,
+						}
+						result = append(result, baseFromLyData)
+					}
+				} else {
+					logs.Error("ProcessingReportProcessor Process() : Column index out of range")
+				}
+			}*/
+		}
+	}
+
+	return result, nil
+}
+
+// InventoryAnalysisProcessor
+// @Description: 库存分析处理器
+type InventoryAnalysisProcessor struct{}
+
+func (p *InventoryAnalysisProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	// https://www.fao.com.cn/art/yg1IKj9FpPEIDv2LefnPhQ==.htm
+	logs.Info("Processing inventory analysis...")
+
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[0]
+	rowVariety := keywords[1]
+
+	columnSuffix := "本周"
+	columnName = columnName + columnSuffix
+
+	// 提取所有表格数据
+	tableData := getTableData(reportContent, true)
+	logs.Info("SupplyDemandBalanceProcessor Process() : Table data: %v", tableData)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	headers := tableData.Headers
+	rows := tableData.Rows
+
+	// 查找目标列
+	columnIdx := -1
+	for i, header := range headers {
+		header := removeParentheses(header)
+		if strings.Contains(columnName, header) {
+			columnIdx = i
+			break
+		}
+	}
+	if columnIdx == -1 {
+		logs.Error("SupplyDemandBalanceProcessor Process() : Column '%s' not found in table", columnName)
+	} else {
+		// 处理表格中的每一行
+		for _, row := range rows {
+			if len(row) >= len(headers) && strings.Contains(row[0], rowVariety) {
+				if columnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-2], ":")
+					indexName = removeParentheses(indexName)
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("SupplyDemandBalanceProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 无需走更新逻辑,报告每周更新,即使本周和上周数据一致,也需要每周记录,如果到这里也只是说,今天这个报告被读取了两次
+						continue
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+					continue
+				} else {
+					log.Printf("SupplyDemandBalanceProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnName)
+				}
+				break
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// PriceSpreadArbitrageProcessor
+// @Description: 价差套利处理器
+type PriceSpreadArbitrageProcessor struct{}
+
+func (p *PriceSpreadArbitrageProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	fmt.Println("Processing processing profit...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingProfitProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	var columnDate string
+	rowVariety := keywords[0]
+	rowBourse := keywords[1]
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+	day, err := utils.ConvertTimeFormatToYearMonthDay(format)
+	if err != nil {
+		return nil, err
+	}
+	columnDate = day
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnDate) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("ProcessingProfitProcessor Process() : Column '%s' not found in table", columnDate)
+			continue
+		}
+
+		// 处理表格中的每一行
+		for _, row := range tableRows {
+			if len(row) >= len(tableHeaders) && row[0] == rowVariety && row[1] == rowBourse {
+				if columnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-2], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("ProcessingProfitProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("ProcessingProfitProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("ProcessingProfitProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 无需走更新逻辑,报告每天更新,即使今天和每天数据一致,也需要每天记录,如果到这里也只是说,今天这个报告被读取了两次
+						continue
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("ProcessingProfitProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnDate)
+				}
+				break
+			}
+
+		}
+	}
+
+	return result, nil
+}
+
+// DailyTransactionProcessor
+// @Description: 每日成交处理器
+type DailyTransactionProcessor struct{}
+
+func (p *DailyTransactionProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	fmt.Println("Processing processing profit...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("DailyTransactionProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 获取第一个表格
+	areaTableDataList := getNoHeadTableData(reportContent)
+	if len(areaTableDataList) == 0 {
+		return []models.BaseFromLyData{}, fmt.Errorf("DailyTransactionProcessor Process() : No table data found")
+	}
+	areaTableData := areaTableDataList[0]
+	// 获取第二个表格
+	blocTableData := getTableData(reportContent, false)
+	if blocTableData.Headers == nil {
+		return []models.BaseFromLyData{}, fmt.Errorf("DailyTransactionProcessor Process() : No table data found")
+
+	}
+	logs.Info("SupplyDemandBalanceProcessor Process() : areaTableData data: %v, blocTableData data: %v", areaTableData, blocTableData)
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	areaHeaders := areaTableData.Headers
+	areaRows := areaTableData.Rows
+
+	// 第一个表格
+	// 拿到 行关键字和列关键字
+	columnArea := keywords[1]
+	var rowAreaMonthDays []string
+	rowWeek := "地区总计"
+
+	monthDay, err := utils.GetWeekdaysInSameWeek(format)
+	if err != nil {
+		return nil, err
+	}
+	rowAreaMonthDays = monthDay
+
+	// 查找目标列
+	areaColumnIdx := -1
+	for i, header := range areaHeaders {
+		if strings.Contains(header, columnArea) {
+			areaColumnIdx = i
+			break
+		}
+	}
+	if areaColumnIdx == -1 {
+		log.Printf("DailyTransactionProcessor Process() : One Column '%s' not found in table", columnArea)
+	} else if !strings.Contains(strings.Join(keywords[:len(keywords)-3], ":"), "主要集团") {
+		for _, row := range areaRows {
+			if len(row) >= len(areaHeaders) && row[0] == rowWeek {
+				if areaColumnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-3], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("DailyTransactionProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("DailyTransactionProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("DailyTransactionProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 无需走更新逻辑,报告每周更新,一周出来一周中每天得数据,即使本周和上周数据一致,也需要每天记录,如果到这里也只是说,今天这个报告被读取了两次
+						continue
+					}
+
+					valueStr := row[areaColumnIdx]
+					isChinese := IsChinese(valueStr)
+					if isChinese {
+						continue
+					}
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					var dealDate string
+					if row[0] == rowWeek {
+						dealDate = format
+					} else {
+						date, err := utils.ConvertToDate(row[0])
+						if err != nil {
+							return nil, err
+						}
+						dealDate = date
+					}
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          dealDate,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("DailyTransactionProcessor Process() : Column index out of range for row '%s', '%s'", monthDay, columnArea)
+				}
+				break
+			} else {
+				for _, monthDay := range rowAreaMonthDays {
+					if len(row) >= len(areaHeaders) && (row[0] == monthDay && !strings.Contains(strings.Join(keywords[:len(keywords)-3], ":"), "周度")) {
+						if areaColumnIdx < len(row) {
+							// 指标名称
+							indexName := strings.Join(keywords[:len(keywords)-3], ":")
+							// 指标编码
+							indexCode := utils.GenerateIndexCode(sourceName, indexName)
+							// 指标id获取
+							indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+							if err != nil {
+								logs.Error("DailyTransactionProcessor Process() : Failed to get index id: %v", err)
+								continue
+							}
+
+							paramsLib := make(map[string]interface{})
+							paramsLib["IndexId"] = indexId
+							paramsLib["DataTime"] = format
+							postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+							if err != nil {
+								logs.Error("DailyTransactionProcessor Process() : Failed to get data by index id and date: %v", err)
+								continue
+							}
+							var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+							err = json.Unmarshal(postEdbLib, &requestResponse)
+
+							indexData := requestResponse.Data
+							if len(indexData) > 0 {
+								logs.Info("DailyTransactionProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+								// 无需走更新逻辑,报告每周更新,即使本周和上周数据一致,也需要每周记录,如果到这里也只是说,今天这个报告被读取了两次
+								continue
+							}
+
+							valueStr := row[areaColumnIdx]
+							isChinese := IsChinese(valueStr)
+							if isChinese {
+								continue
+							}
+							value, err := strconv.ParseFloat(valueStr, 64)
+							if err != nil {
+								return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+							}
+							// 创建并添加到结果列表
+							var dealDate string
+							if row[0] == rowWeek {
+								dealDate = format
+							} else {
+								date, err := utils.ConvertToDate(row[0])
+								if err != nil {
+									return nil, err
+								}
+								dealDate = date
+							}
+							baseFromLyData := models.BaseFromLyData{
+								DataTime:          dealDate,
+								Value:             value,
+								BaseFromLyIndexId: indexId,
+								IndexCode:         indexCode,
+							}
+
+							result = append(result, baseFromLyData)
+						} else {
+							log.Printf("DailyTransactionProcessor Process() : Column index out of range for row '%s', '%s'", monthDay, columnArea)
+						}
+						break
+					}
+				}
+			}
+		}
+	}
+
+	// 第二个表格
+	// 拿到 行关键字和列关键字
+	columnBloc := keywords[len(keywords)-3]
+	rowBloc := keywords[1]
+
+	blocHeaders := blocTableData.Headers
+	blocRows := blocTableData.Rows
+
+	// 查找目标列
+	blocColumnIdx := -1
+	for i, header := range blocHeaders {
+		if strings.Contains(header, columnBloc) {
+			blocColumnIdx = i
+			break
+		}
+	}
+
+	if blocColumnIdx == -1 {
+		log.Printf("DailyTransactionProcessor Process() : Two Column '%s' not found in table", columnBloc)
+	} else {
+		// 处理表格中的每一行
+		for _, row := range blocRows {
+			if len(row) >= len(blocHeaders) && strings.Contains(row[0], rowBloc) {
+				if blocColumnIdx < len(row) {
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-3], ":")
+					indexName = removeParentheses(indexName)
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("SupplyDemandBalanceProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("SupplyDemandBalanceProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						continue
+					}
+
+					valueStr := row[blocColumnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("SupplyDemandBalanceProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("SupplyDemandBalanceProcessor Process() : Column index out of range for row '%s', '%s'", rowBloc, columnBloc)
+				}
+				break
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// PalmOilImportCostProcessor 棕榈油进口成本
+type PalmOilImportCostProcessor struct{}
+
+func (p *PalmOilImportCostProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing palm oil import cost...")
+	// 解析关键字
+	if len(keywords) < 5 {
+		return []models.BaseFromLyData{}, fmt.Errorf("PalmOilImportCostProcessor Process() : keywords must contain at least 5 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[len(keywords)-4]
+	rowVariety := keywords[0]
+	rowPort := keywords[len(keywords)-3]
+	indexNamePrefix := keywords[:1]
+	indexNameSuffix := keywords[1:]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 解析日期并计算当前月份
+	targetMonths, err := utils.GetYearMonthNoYear(format)
+	if err != nil {
+		return []models.BaseFromLyData{}, fmt.Errorf("PalmOilImportCostProcessor Process() : Failed to parse date: %v", err)
+	}
+	fmt.Printf("Target Month: %s\n", targetMonths)
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnName) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("PalmOilImportCostProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 处理表格中的每一行
+		//var flag bool = true
+		var previousRowVariety string
+		var previousRowPort string
+		var previousRowFob string
+		for rowIndex, row := range tableRows {
+			if len(row) == len(tableHeaders) {
+				previousRowVariety = row[0]
+				previousRowPort = row[1]
+				previousRowFob = row[2]
+			} else if len(row) == len(tableHeaders)-1 {
+				previousRowPort = row[0]
+				previousRowFob = row[1]
+				row = append([]string{previousRowVariety}, row...)
+				tableRows[rowIndex] = row
+			} else if len(row) == len(tableHeaders)-2 {
+				// 这段这里不需要。。。先保留吧
+				previousRowFob = row[0]
+				row = append([]string{previousRowVariety, previousRowPort}, row...)
+				tableRows[rowIndex] = row
+			} else if len(row) == len(tableHeaders)-3 {
+				row = append([]string{previousRowVariety, previousRowPort, previousRowFob}, row...)
+				tableRows[rowIndex] = row
+			}
+			for _, targetMonth := range targetMonths {
+				if len(row) >= len(tableHeaders) && strings.Contains(rowVariety, row[0]) && row[1] == targetMonth && row[len(row)-1] == rowPort {
+					if columnIdx < len(row) {
+						// 指标名称
+						indexNameList := append(indexNamePrefix, append([]string{targetMonth}, indexNameSuffix...)...)
+						indexName := strings.Join(indexNameList[:len(keywords)-2], ":")
+						// 指标编码
+						indexCode := utils.GenerateIndexCode(sourceName, indexName)
+						// 指标id获取
+						indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+						if err != nil {
+							logs.Error("PalmOilImportCostProcessor Process() : Failed to get index id: %v", err)
+							continue
+						}
+
+						paramsLib := make(map[string]interface{})
+						paramsLib["IndexId"] = indexId
+						paramsLib["DataTime"] = format
+						postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+						if err != nil {
+							logs.Error("PalmOilImportCostProcessor Process() : Failed to get data by index id and date: %v", err)
+							continue
+						}
+						var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+						err = json.Unmarshal(postEdbLib, &requestResponse)
+
+						indexData := requestResponse.Data
+						if len(indexData) > 0 {
+							logs.Info("PalmOilImportCostProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+							continue
+						}
+
+						valueStr := row[columnIdx]
+						value, err := strconv.ParseFloat(valueStr, 64)
+						if err != nil {
+							return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+						}
+						// 创建并添加到结果列表
+						baseFromLyData := models.BaseFromLyData{
+							DataTime:          format,
+							Value:             value,
+							BaseFromLyIndexId: indexId,
+							IndexCode:         indexCode,
+						}
+						result = append(result, baseFromLyData)
+					} else {
+						log.Printf("PalmOilImportCostProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, rowPort)
+					}
+					break
+				}
+			}
+
+		}
+
+	}
+
+	return result, nil
+}
+
+// ImportEstimateProcessor
+// @Description: 进口预估处理器
+type ImportEstimateProcessor struct{}
+
+func (p *ImportEstimateProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing import estimate...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ImportEstimateProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	var columnDates []string
+	rowVariety := keywords[1]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	columnDates, err = utils.GetNextThreeMonthsNoYear(format)
+	if err != nil {
+		return nil, err
+	}
+
+	monthsLastDay, err := utils.GetNextThreeMonthsLastDay(format)
+	if err != nil {
+		return nil, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		for _, columnDate := range columnDates {
+			columnIdx := -1
+			for i, tableHeader := range tableHeaders {
+				if strings.Contains(tableHeader, columnDate) {
+					columnIdx = i
+					break
+				}
+			}
+
+			if columnIdx == -1 {
+				log.Printf("ImportEstimateProcessor Process() : Column '%s' not found in table", columnDate)
+				continue
+			} else {
+				// 处理表格中的每一行
+				for _, row := range tableRows {
+					if len(row) >= len(tableHeaders) && strings.Contains(row[0], rowVariety) && isNumber(row[columnIdx]) {
+						if columnIdx < len(row) {
+							// 指标名称
+							indexName := strings.Join(keywords[:len(keywords)-2], `:`)
+							// 指标编码
+							indexCode := utils.GenerateIndexCode(sourceName, indexName)
+							// 指标id获取
+							indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+							if err != nil {
+								logs.Error("ImportEstimateProcessor Process() : Failed to get index id: %v", err)
+								continue
+							}
+							toNumber, err := utils.ConvertMonthToNumber(columnDate)
+							if err != nil {
+								logs.Error("ImportEstimateProcessor Process() : Failed to convert month to number: %v", err)
+								continue
+							}
+							slice, err := utils.GetElementInSlice(monthsLastDay, toNumber)
+							if err != nil {
+								logs.Error("ImportEstimateProcessor Process() : Failed to get element in slice: %v", err)
+								continue
+							}
+
+							paramsLib := make(map[string]interface{})
+							paramsLib["IndexId"] = indexId
+							paramsLib["DataTime"] = format
+							postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+							if err != nil {
+								logs.Error("ImportEstimateProcessor Process() : Failed to get data by index id and date: %v", err)
+								continue
+							}
+							var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+							err = json.Unmarshal(postEdbLib, &requestResponse)
+
+							indexData := requestResponse.Data
+
+							valueStr := row[columnIdx]
+							value, err := strconv.ParseFloat(valueStr, 64)
+							if err != nil {
+								return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+							}
+
+							if len(indexData) > 0 {
+								if indexData[0].Value != value {
+									logs.Info("ImportEstimateProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+
+									lyData := indexData[0]
+									time, err := utils.StringToTime(lyData.ModifyTime)
+									if err != nil {
+										return nil, err
+									}
+
+									timeZero, err := utils.StringToTimeZero(format)
+									if err != nil {
+										return nil, err
+									}
+
+									if lyData.Value != value && time.Before(timeZero) {
+										// 更新指标数据
+										paramsLib := make(map[string]interface{})
+										paramsLib["Id"] = lyData.BaseFromLyDataId
+										paramsLib["Value"] = value
+										_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_DATA_BY_ID)
+										if err != nil {
+											return nil, fmt.Errorf("ImportEstimateProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+										}
+
+										// 同步更新指标库数据
+										paramsLib = make(map[string]interface{})
+										paramsLib["IndexCode"] = lyData.IndexCode
+										paramsLib["DataTime"] = lyData.DataTime
+										postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_EDB_DATA_BY_INDEX_CODE_AND_EXACT_DATA_TIME)
+										if err != nil {
+											logs.Error("ImportEstimateProcessor Process() : Failed to get ly edb data by index code and exact data time: %v", err)
+											continue
+										}
+										var requestResponse models.RequestResponse[[]models.EdbDataLy]
+										err = json.Unmarshal(postEdbLib, &requestResponse)
+										if err != nil {
+											return nil, err
+										}
+
+										lyEdbIndexData := requestResponse.Data
+										if len(lyEdbIndexData) > 0 {
+											paramsLib := make(map[string]interface{})
+											paramsLib["Id"] = lyEdbIndexData[0].EdbInfoId
+											paramsLib["Value"] = value
+											_, err := httpRequestFill(paramsLib, utils.UPDATE_LY_EDB_DATA_BY_ID)
+											if err != nil {
+												return nil, fmt.Errorf("ImportEstimateProcessor Process() : failed to parse value '%s': %v", valueStr, err)
+											}
+										}
+									}
+								}
+
+								continue
+							}
+
+							// 创建并添加到结果列表
+							baseFromLyData := models.BaseFromLyData{
+								DataTime:          slice,
+								Value:             value,
+								BaseFromLyIndexId: indexId,
+								IndexCode:         indexCode,
+							}
+							result = append(result, baseFromLyData)
+						} else {
+							log.Printf("ImportEstimateProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnDate)
+						}
+						break
+					}
+				}
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// InternationalPriceProcessor
+// @Description: 国际价格处理器
+type InternationalPriceProcessor struct{}
+
+func (p *InternationalPriceProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing international price...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("InternationalPriceProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnName := keywords[1]
+	rowVariety := keywords[0]
+	indexNamePrefix := keywords[:1]
+	indexNameSuffix := keywords[1:]
+
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 解析日期并计算当前月份 和 后两月
+	yearMonths, err := utils.ConvertTimeFormatToYearMonth(format)
+	if err != nil {
+		return nil, err
+	}
+	fmt.Printf("Target yearMonth: %s\n", yearMonths)
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnName) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("InternationalPriceProcessor Process() : Column '%s' not found in table", columnName)
+			continue
+		}
+
+		// 处理表格中的每一行
+		var previousRowVariety string
+		for rowIndex, row := range tableRows {
+			if len(row) == len(tableHeaders) {
+				previousRowVariety = row[0]
+			} else if len(row) == len(tableHeaders)-1 {
+				row = append([]string{previousRowVariety}, row...)
+				tableRows[rowIndex] = row
+			}
+
+			for _, targetMonth := range yearMonths {
+				if len(row) >= len(tableHeaders) && row[0] == rowVariety && row[1] == targetMonth {
+					if columnIdx < len(row) {
+						// 指标名称
+						indexNameList := append(indexNamePrefix, append([]string{targetMonth}, indexNameSuffix...)...)
+						indexName := strings.Join(indexNameList[:len(keywords)-2], ":")
+						// 指标编码
+						indexCode := utils.GenerateIndexCode(sourceName, indexName)
+						// 指标id获取
+						indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+						if err != nil {
+							logs.Error("InternationalPriceProcessor Process() : Failed to get index id: %v", err)
+							continue
+						}
+
+						paramsLib := make(map[string]interface{})
+						paramsLib["IndexId"] = indexId
+						paramsLib["DataTime"] = format
+						postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+						if err != nil {
+							logs.Error("InternationalPriceProcessor Process() : Failed to get data by index id and date: %v", err)
+							continue
+						}
+						var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+						err = json.Unmarshal(postEdbLib, &requestResponse)
+
+						indexData := requestResponse.Data
+						if len(indexData) > 0 {
+							logs.Info("InternationalPriceProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+							// 无需更新 指标展示本月和后两月的数据,报告每天更新,每天的值可能会改变,即使今天和每天数据一致,也需要每天记录,如果到这里也只是说,今天这个报告被读取了两次
+							continue
+						}
+
+						valueStr := row[columnIdx]
+						value, err := strconv.ParseFloat(valueStr, 64)
+						if err != nil {
+							return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+						}
+						// 创建并添加到结果列表
+						baseFromLyData := models.BaseFromLyData{
+							DataTime:          format,
+							Value:             value,
+							BaseFromLyIndexId: indexId,
+							IndexCode:         indexCode,
+						}
+						result = append(result, baseFromLyData)
+					} else {
+						log.Printf("InternationalPriceProcessor Process() : Column index out of range for row '%s', '%s'", rowVariety, columnName)
+					}
+					break
+				}
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// CanadaStatisticsBureauProcessor
+// @Description: 加拿大统计局处理器
+type CanadaStatisticsBureauProcessor struct{}
+
+func (p *CanadaStatisticsBureauProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	logs.Info("Processing Canada statistics bureau...")
+	// 解析关键字
+	if len(keywords) < 4 {
+		return []models.BaseFromLyData{}, fmt.Errorf("CanadaStatisticsBureauProcessor Process() : keywords must contain at least 4 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	columnDate := "本周"
+	rowVariety := keywords[1]
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		// 查找目标列
+		columnIdx := -1
+		for i, header := range tableHeaders {
+			if strings.Contains(header, columnDate) {
+				columnIdx = i
+				break
+			}
+		}
+		if columnIdx == -1 {
+			log.Printf("CanadaStatisticsBureauProcessor Process() : Column '%s' not found in table", columnDate)
+			continue
+		}
+
+		// 处理表格中的每一行
+		for _, row := range tableRows {
+			if len(row) >= len(tableHeaders) {
+				if columnIdx < len(row) {
+					if row[0] != rowVariety {
+						continue
+					}
+					// 指标名称
+					indexName := strings.Join(keywords[:len(keywords)-2], ":")
+					// 指标编码
+					indexCode := utils.GenerateIndexCode(sourceName, indexName)
+					// 指标id获取
+					indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+					if err != nil {
+						logs.Error("CanadaStatisticsBureauProcessor Process() : Failed to get index id: %v", err)
+						continue
+					}
+
+					paramsLib := make(map[string]interface{})
+					paramsLib["IndexId"] = indexId
+					paramsLib["DataTime"] = format
+					postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+					if err != nil {
+						logs.Error("CanadaStatisticsBureauProcessor Process() : Failed to get data by index id and date: %v", err)
+						continue
+					}
+					var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+					err = json.Unmarshal(postEdbLib, &requestResponse)
+
+					indexData := requestResponse.Data
+					if len(indexData) > 0 {
+						logs.Info("CanadaStatisticsBureauProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+						// 无需更新 指标展示本周的数据,报告每周更新,即使本周和上周数据一致,也需要每周记录,如果到这里也只是说,今天这个报告被读取了两次
+						continue
+					}
+
+					valueStr := row[columnIdx]
+					value, err := strconv.ParseFloat(valueStr, 64)
+					if err != nil {
+						return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+					}
+					// 创建并添加到结果列表
+					baseFromLyData := models.BaseFromLyData{
+						DataTime:          format,
+						Value:             value,
+						BaseFromLyIndexId: indexId,
+						IndexCode:         indexCode,
+					}
+					result = append(result, baseFromLyData)
+				} else {
+					log.Printf("CanadaStatisticsBureauProcessor Process() : Column index out of range for row '%s'", columnDate)
+				}
+				break
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// ImportExportAnalysisProcessor
+// @Description: 进出口分析处理器
+type ImportExportAnalysisProcessor struct{}
+
+func (p *ImportExportAnalysisProcessor) Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error) {
+	fmt.Println("Processing processing profit...")
+	// 解析关键字
+	if len(keywords) < 3 {
+		return []models.BaseFromLyData{}, fmt.Errorf("ProcessingProfitProcessor Process() : keywords must contain at least 3 elements")
+	}
+
+	// 拿到 行关键字和列关键字
+	var columnDates []string
+	// 提取所有表格数据
+	tableData := getNoHeadTableData(reportContent)
+
+	// 提取日期信息
+	dateText, err := getDateInfo(ctx)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 时间格式转换
+	format, err := utils.ConvertTimeFormat(dateText)
+	if err != nil {
+		return []models.BaseFromLyData{}, err
+	}
+
+	// 2025年1月可能才出2024年12月的数据,所以往前取一年
+	columnDates, err = utils.GetCurrentYearAndLastYear(format)
+	if err != nil {
+		return nil, err
+	}
+
+	// 处理提取的表格数据
+	var result []models.BaseFromLyData
+
+	for _, data := range tableData {
+		tableHeaders := data.Headers
+		tableRows := data.Rows
+
+		for _, columnDate := range columnDates {
+			// 查找目标列
+			columnIdx := -1
+			for i, header := range tableHeaders {
+				if strings.Contains(header, columnDate) {
+					columnIdx = i
+					break
+				}
+			}
+
+			if columnIdx == -1 {
+				log.Printf("ProcessingProfitProcessor Process() : Column '%s' not found in table", columnDate)
+				continue
+			}
+
+			// 处理表格中的每一行
+			for _, row := range tableRows {
+				if len(row) >= len(tableHeaders) {
+					if columnIdx < len(row) && isNumber(row[columnIdx]) && isNumber(row[0]) {
+						// 指标名称
+						indexName := strings.Join(keywords[:len(keywords)-2], ":")
+						// 指标编码
+						indexCode := utils.GenerateIndexCode(sourceName, indexName)
+						// 指标id获取
+						indexId, err := getIndexId(indexCode, indexName, classifyId, sourceName, keywords[len(keywords)-2], keywords[len(keywords)-1])
+						if err != nil {
+							logs.Error("ProcessingProfitProcessor Process() : Failed to get index id: %v", err)
+							continue
+						}
+						atoi, err := strconv.Atoi(row[0])
+						if err != nil {
+							return nil, err
+						}
+						date := columnDate[:4] + "-" + fmt.Sprintf("%02d", atoi)
+						lastDayOfMonth, err := utils.GetLastDayOfMonth(date)
+						if err != nil {
+							return nil, err
+						}
+
+						paramsLib := make(map[string]interface{})
+						paramsLib["IndexId"] = indexId
+						paramsLib["DataTime"] = format
+						postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_DATA_BY_INDEX_ID_AND_DATA_TIME)
+						if err != nil {
+							logs.Error("ProcessingProfitProcessor Process() : Failed to get data by index id and date: %v", err)
+							continue
+						}
+						var requestResponse models.RequestResponse[[]models.BaseFromLyData]
+						err = json.Unmarshal(postEdbLib, &requestResponse)
+
+						indexData := requestResponse.Data
+						if len(indexData) > 0 {
+							logs.Info("ProcessingProfitProcessor Process() : Data already exists for index %d and date %s", indexId, dateText)
+							continue
+						}
+
+						valueStr := row[columnIdx]
+						value, err := strconv.ParseFloat(valueStr, 64)
+						if err != nil {
+							return []models.BaseFromLyData{}, fmt.Errorf("failed to parse value '%s': %v", valueStr, err)
+						}
+						// 创建并添加到结果列表
+						baseFromLyData := models.BaseFromLyData{
+							DataTime:          lastDayOfMonth,
+							Value:             value,
+							BaseFromLyIndexId: indexId,
+							IndexCode:         indexCode,
+						}
+						result = append(result, baseFromLyData)
+						continue
+					} else {
+						log.Printf("ProcessingProfitProcessor Process() : Column index out of range for row '%s'", columnDate)
+					}
+					break
+				}
+			}
+		}
+	}
+
+	return result, nil
+}
+
+// ExtractValueInParentheses 从字符串中提取括号中的值
+func extractValueInParentheses(input string) (string, error) {
+	re := regexp.MustCompile(`(([^)]+))`)
+	matches := re.FindStringSubmatch(input)
+
+	if len(matches) > 1 {
+		return matches[1], nil
+	}
+
+	return "", fmt.Errorf("no value found in parentheses")
+}
+
+// 获取指标id,根据指标名称判断,没有插入指标生成返回
+func getIndexId(indexCode string, indexName string, classifyId int, sourceName string, frequency string, unit string) (int, error) {
+	if indexCode == "lysww" {
+		return 0, fmt.Errorf("indexCode is error")
+	}
+
+	// 判断指标是否存在
+	var indexId int
+	paramsLib := make(map[string]interface{})
+	paramsLib["IndexCode"] = indexCode
+	postEdbLib, err := httpRequestFill(paramsLib, utils.GET_LY_INDEX_BY_CODE)
+	if err != nil {
+		return 0, fmt.Errorf("getIndexId() : Failed to get ly index by code: %v", err)
+	}
+	var requestResponse models.RequestResponse[models.BaseFromLyIndex]
+	err = json.Unmarshal(postEdbLib, &requestResponse)
+	if err != nil {
+		return 0, fmt.Errorf("getIndexId() : Failed to unmarshal response: %v", err)
+	}
+	indexInfo := requestResponse.Data
+	if indexInfo.IndexName == "" {
+		// 新增指标
+		index, err := addLyIndex(classifyId, indexCode, indexName, frequency, unit)
+		if err != nil {
+			return 0, err
+		}
+		indexId = index
+	} else {
+		indexId = indexInfo.BaseFromLyIndexId
+	}
+	return indexId, nil
+}
+
+// 获取页面时间信息
+func getDateInfo(ctx context.Context) (string, error) {
+	var dateText string
+	err := chromedp.Run(ctx,
+		chromedp.Evaluate(`document.querySelector('div.a_date span').innerText`, &dateText),
+	)
+	if err != nil {
+		return "", fmt.Errorf("processing Process() : Failed to extract report date: %v", err)
+	}
+
+	logs.Info("Processing Process() : Report Extracted Date: %s", dateText)
+	return dateText, nil
+}
+
+// 获取所有表格数据 获取表格中有thead标签的数据
+func getAllTableData(reportContent string) []TableData {
+	var tableData []TableData
+
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(reportContent))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// 选择 id 为 "a_content" 的 div
+	doc.Find("#a_content").Each(func(index int, item *goquery.Selection) {
+		item.Find("table").Each(func(index int, table *goquery.Selection) {
+			var headers []string
+			var rows [][]string
+
+			// 提取表头
+			table.Find("thead th").Each(func(index int, th *goquery.Selection) {
+				headers = append(headers, th.Text())
+			})
+
+			// 提取表格行数据
+			table.Find("tbody tr").Each(func(index int, row *goquery.Selection) {
+				var rowData []string
+				row.Find("td").Each(func(index int, td *goquery.Selection) {
+					rowData = append(rowData, td.Text())
+				})
+				rows = append(rows, rowData)
+			})
+
+			// 仅在表头存在时添加到结果中
+			if len(headers) > 0 {
+				tableData = append(tableData, TableData{
+					Headers: headers,
+					Rows:    rows,
+				})
+			}
+		})
+	})
+	return tableData
+}
+
+// 获取无头表格数据
+func getNoHeadTableData(reportContent string) []TableData {
+	var tableData []TableData
+
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(reportContent))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Find the div with id "a_content"
+	doc.Find("#a_content").Each(func(index int, div *goquery.Selection) {
+		// Find all tables within the div
+		div.Find("table").Each(func(index int, table *goquery.Selection) {
+			var headers []string
+			var rows [][]string
+
+			// Extract table headers if any
+			table.Find("tr").Each(func(index int, tr *goquery.Selection) {
+				var rowData []string
+				tr.Find("td, th").Each(func(index int, cell *goquery.Selection) {
+					cellText := cell.Text()
+					rowData = append(rowData, cellText)
+				})
+
+				if index == 0 && len(rowData) > 0 {
+					// The first row is treated as the header row
+					headers = rowData
+				} else if len(rowData) > 0 {
+					// Add the row data to the rows slice
+					rows = append(rows, rowData)
+				}
+			})
+
+			// Only add table data if headers are present
+			if len(headers) > 0 {
+				tableData = append(tableData, TableData{
+					Headers: headers,
+					Rows:    rows,
+				})
+			}
+		})
+	})
+
+	return tableData
+}
+
+// 获取表格数据 获取id 为 a_content 的 div 中的第一个表格 左上角那个单元格会拼在第一个,会拼上列上的合并单元格
+func getTableData(reportContent string, isFirst bool) TableData {
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(reportContent))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	tableData := &TableData{}
+
+	// 只提取 id 为 a_content 的 div 中的第一个表格
+	var firstTable *goquery.Selection
+	if isFirst {
+		firstTable = doc.Find("div#a_content table").First()
+	} else {
+		firstTable = doc.Find("div#a_content table").Last()
+	}
+
+	var combinedHeaders []string
+
+	// 提取表头
+	firstTable.Find("tr").Each(func(i int, row *goquery.Selection) {
+		if i == 0 {
+			// 第一行处理合并单元格,保存到 combinedHeaders
+			row.Find("td,th").Each(func(j int, cell *goquery.Selection) {
+				if j == 0 {
+					// 把左上角的“年度(10/9月)”放入 Headers 第一个元素
+					tableData.Headers = append(tableData.Headers, strings.TrimSpace(cell.Text()))
+				} else {
+					// 处理其他单元格
+					colspan, exists := cell.Attr("colspan")
+					if exists {
+						spanCount := 0
+						fmt.Sscanf(colspan, "%d", &spanCount)
+						for k := 0; k < spanCount; k++ {
+							combinedHeaders = append(combinedHeaders, strings.TrimSpace(cell.Text()))
+						}
+					} else {
+						combinedHeaders = append(combinedHeaders, strings.TrimSpace(cell.Text()))
+					}
+				}
+			})
+		} else if i == 1 {
+			// 第二行处理具体标题,组合后保存到 Headers
+			row.Find("td,th").Each(func(j int, cell *goquery.Selection) {
+				if j < len(combinedHeaders) {
+					fullHeader := combinedHeaders[j] + strings.TrimSpace(cell.Text())
+					tableData.Headers = append(tableData.Headers, fullHeader)
+				}
+			})
+		} else {
+			// 处理数据行
+			var rowData []string
+			row.Find("td").Each(func(j int, cell *goquery.Selection) {
+				rowData = append(rowData, strings.TrimSpace(cell.Text()))
+			})
+			if len(rowData) > 0 {
+				tableData.Rows = append(tableData.Rows, rowData)
+			}
+		}
+	})
+
+	return *tableData
+}
+
+// 获取采购装船表格数据
+func getPurchaseShippingTableData(reportContent string) TableData {
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(reportContent))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	var tableData TableData
+
+	// 只提取 id 为 a_content 的 div 中的第一个表格
+	firstTable := doc.Find("div#a_content table").First()
+
+	// 提取表头
+	var headers []string
+	var subHeaders []string
+
+	firstTable.Find("thead tr").Each(func(i int, row *goquery.Selection) {
+		row.Find("th").Each(func(j int, cell *goquery.Selection) {
+			headerText := strings.TrimSpace(cell.Text())
+
+			if i == 0 {
+				// 处理第一行表头
+				colspan, exists := cell.Attr("colspan")
+				if exists {
+					spanCount := 0
+					fmt.Sscanf(colspan, "%d", &spanCount)
+					for k := 0; k < spanCount; k++ {
+						headers = append(headers, headerText)
+					}
+				} else {
+					headers = append(headers, headerText)
+				}
+			} else if i == 1 {
+				// 处理第二行表头
+				subHeaders = append(subHeaders, headerText)
+			}
+		})
+	})
+
+	// 合并第一行和第二行表头信息
+	if len(subHeaders) > 0 {
+		for i := 0; i < len(subHeaders); i++ {
+			// 从第四个单元格开始拼接
+			headers[3+i] = headers[3+i] + subHeaders[i]
+		}
+	}
+
+	tableData.Headers = headers
+
+	// 处理数据行
+	firstTable.Find("tbody tr").Each(func(i int, row *goquery.Selection) {
+		var rowData []string
+		row.Find("td").Each(func(j int, cell *goquery.Selection) {
+			rowData = append(rowData, strings.TrimSpace(cell.Text()))
+		})
+		if len(rowData) > 0 {
+			tableData.Rows = append(tableData.Rows, rowData)
+		}
+	})
+
+	return tableData
+}
+
+// 判断字符串是否是数字
+func isNumeric(value string) bool {
+	// 正则表达式匹配整数和浮点数
+	re := regexp.MustCompile(`^[+-]?(\d+(\.\d*)?|\.\d+)$`)
+	return re.MatchString(value)
+}
+
+// 只保留汉字
+func extractChinese(text string) string {
+	re := regexp.MustCompile(`[^\p{Han}]`) // 匹配非汉字字符
+	return re.ReplaceAllString(text, "")
+}
+
+// 去除括号中的内容 包含括号 ()
+func removeParentheses(text string) string {
+	re := regexp.MustCompile(`\([^)]*\)`)
+	return re.ReplaceAllString(text, "")
+}
+
+// IsChinese 判断传入的是否是汉字
+func IsChinese(str string) bool {
+	for _, r := range str {
+		if unicode.Is(unicode.Han, r) {
+			return true
+		}
+	}
+	return false
+}
+
+// 判断是否是数字
+func isNumber(str string) bool {
+	_, err := strconv.ParseFloat(str, 64)
+	return err == nil
+}

+ 110 - 0
services/ruizide/processor_factory.go

@@ -0,0 +1,110 @@
+// @Author gmy 2024/8/6 10:48:00
+package liangyou
+
+import (
+	"context"
+	"eta/eta_data_analysis/models"
+	"fmt"
+)
+
+type ReportProcessor interface {
+	Process(ctx context.Context, product string, reportContent string, keywords []string, classifyId int) ([]models.BaseFromLyData, error)
+}
+
+func GetProcessor(product string, category string) (ReportProcessor, error) {
+	if product == "大豆" {
+		switch category {
+		case "进口成本":
+			return &ImportCostProcessor{}, nil
+		case "加工利润":
+			return &ProcessingProfitProcessor{}, nil
+		case "船运费用":
+			return &ShippingCostProcessor{}, nil
+		case "供需平衡":
+			return &SupplyDemandBalanceProcessor{}, nil
+		case "采购装船":
+			return &PurchaseShippingProcessor{}, nil
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "加工报告":
+			return &ProcessingReportProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "豆粕" {
+		switch category {
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "大豆油" {
+		switch category {
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "价差套利":
+			return &PriceSpreadArbitrageProcessor{}, nil
+		case "每日成交":
+			return &DailyTransactionProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "棕榈油" {
+		switch category {
+		case "国际价格":
+			return &InternationalPriceProcessor{}, nil
+		case "进口成本":
+			return &PalmOilImportCostProcessor{}, nil
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "每日成交":
+			return &DailyTransactionProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "油菜籽" {
+		switch category {
+		case "进口成本":
+			return &ImportCostProcessor{}, nil
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "进口预估":
+			return &ImportEstimateProcessor{}, nil
+		case "加拿大统计局":
+			return &CanadaStatisticsBureauProcessor{}, nil
+		case "进出口分析":
+			return &ImportExportAnalysisProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "菜粕" {
+		switch category {
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "进出口分析":
+			return &ImportExportAnalysisProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "菜籽油" {
+		switch category {
+		case "库存分析":
+			return &InventoryAnalysisProcessor{}, nil
+		case "进口预估":
+			return &ImportEstimateProcessor{}, nil
+		case "每日成交":
+			return &DailyTransactionProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	} else if product == "葵花粕" {
+		switch category {
+		case "进口预估":
+			return &ImportEstimateProcessor{}, nil
+		default:
+			return nil, fmt.Errorf("unknown category: %s", category)
+		}
+	}
+	// 可以添加更多的逻辑来处理其他产品和类别
+	return nil, fmt.Errorf("no processor found for product %s and category %s", product, category)
+}

+ 339 - 0
static/ruizide.json

@@ -0,0 +1,339 @@
+{
+  "Analytics Library": {
+    "Road Index": [
+      "Road Index/Brazil/Index:日度:%",
+      "Road Index/Canada/Index:日度:%",
+      "Road Index/China/Index:日度:%",
+      "Road Index/Europe/Index:日度:%",
+      "Road Index/India/Index:日度:%",
+      "Road Index/Indonesia/Index:日度:%",
+      "Road Index/United States/Index:日度:%",
+      "Road Index/Brazil/Index 7DMA:日度:%",
+      "Road Index/Canada/Index 7DMA:日度:%",
+      "Road Index/China/Index 7DMA:日度:%",
+      "Road Index/Europe/Index 7DMA:日度:%",
+      "Road Index/India/Index 7DMA:日度:%",
+      "Road Index/Indonesia/Index 7DMA:日度:%",
+      "Road Index/United States/Index 7DMA:日度:%"
+    ],
+    "Road Active Fleet": [
+      "Road Active Fleet/Brazil/Active Fleet:日度:辆",
+      "Road Active Fleet/Canada/Active Fleet:日度:辆",
+      "Road Active Fleet/China/Active Fleet:日度:辆",
+      "Road Active Fleet/Europe/Active Fleet:日度:辆",
+      "Road Active Fleet/India/Active Fleet:日度:辆",
+      "Road Active Fleet/Indonesia/Active Fleet:日度:辆",
+      "Road Active Fleet/United States/Active Fleet:日度:辆",
+      "Road Active Fleet/Brazil/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/Canada/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/China/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/Europe/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/India/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/Indonesia/Active Fleet 7DMA:日度:辆",
+      "Road Active Fleet/United States/Active Fleet 7DMA:日度:辆"
+    ],
+    "Aviation Index": [
+      "Aviation Index/Brazil/Index:日度:%",
+      "Aviation Index/Canada/Index:日度:%",
+      "Aviation Index/China/Index:日度:%",
+      "Aviation Index/Europe/Index:日度:%",
+      "Aviation Index/India/Index:日度:%",
+      "Aviation Index/Indonesia/Index:日度:%",
+      "Aviation Index/United States/Index:日度:%",
+      "Aviation Index/Brazil/Index 7DMA:日度:%",
+      "Aviation Index/Canada/Index 7DMA:日度:%",
+      "Aviation Index/China/Index 7DMA:日度:%",
+      "Aviation Index/Europe/Index 7DMA:日度:%",
+      "Aviation Index/India/Index 7DMA:日度:%",
+      "Aviation Index/Indonesia/Index 7DMA:日度:%",
+      "Aviation Index/United States/Index 7DMA:日度:%"
+    ],
+    "Aviation Active Fleet": [
+      "Aviation Active Fleet/Brazil/Active Fleet:日度:辆",
+      "Aviation Active Fleet/Canada/Active Fleet:日度:辆",
+      "Aviation Active Fleet/China/Active Fleet:日度:辆",
+      "Aviation Active Fleet/Europe/Active Fleet:日度:辆",
+      "Aviation Active Fleet/India/Active Fleet:日度:辆",
+      "Aviation Active Fleet/Indonesia/Active Fleet:日度:辆",
+      "Aviation Active Fleet/United States/Active Fleet:日度:辆",
+      "Aviation Active Fleet/Brazil/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/Canada/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/China/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/Europe/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/India/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/Indonesia/Active Fleet 7DMA:日度:辆",
+      "Aviation Active Fleet/United States/Active Fleet 7DMA:日度:辆"
+    ],
+    "Gasoline Demand": [
+      "Gasoline Demand/Europe/Demand:日度:千桶每天",
+      "Gasoline Demand/Germany/Demand:日度:千桶每天",
+      "Gasoline Demand/Indonesia/Demand:日度:千桶每天",
+      "Gasoline Demand/United Kingdom/Demand:日度:千桶每天",
+      "Gasoline Demand/United States/Demand:日度:千桶每天",
+      "Gasoline Demand/Europe/Demand 7DMA:日度:千桶每天",
+      "Gasoline Demand/Germany/Demand 7DMA:日度:千桶每天",
+      "Gasoline Demand/Indonesia/Demand 7DMA:日度:千桶每天",
+      "Gasoline Demand/United Kingdom/Demand 7DMA:日度:千桶每天",
+      "Gasoline Demand/United States/Demand 7DMA:日度:千桶每天"
+    ],
+    "Diesel Demand": [
+      "Diesel Demand/Brazil/Demand:日度:千桶每天",
+      "Diesel Demand/Europe/Demand:日度:千桶每天",
+      "Diesel Demand/France/Demand:日度:千桶每天",
+      "Diesel Demand/Germany/Demand:日度:千桶每天",
+      "Diesel Demand/United States/Demand:日度:千桶每天",
+      "Diesel Demand/Brazil/Demand 7DMA:日度:千桶每天",
+      "Diesel Demand/Europe/Demand 7DMA:日度:千桶每天",
+      "Diesel Demand/France/Demand 7DMA:日度:千桶每天",
+      "Diesel Demand/Germany/Demand 7DMA:日度:千桶每天",
+      "Diesel Demand/United States/Demand 7DMA:日度:千桶每天"
+    ],
+    "Jet Fuel Demand": [
+      "Jet Fuel Demand/China/Demand:日度:千桶每天",
+      "Jet Fuel Demand/Europe/Demand:日度:千桶每天",
+      "Jet Fuel Demand/UAE/Demand:日度:千桶每天",
+      "Jet Fuel Demand/United Kingdom/Demand:日度:千桶每天",
+      "Jet Fuel Demand/United States/Demand:日度:千桶每天",
+      "Jet Fuel Demand/China/Demand 7DMA:日度:千桶每天",
+      "Jet Fuel Demand/Europe/Demand 7DMA:日度:千桶每天",
+      "Jet Fuel Demand/UAE/Demand 7DMA:日度:千桶每天",
+      "Jet Fuel Demand/United Kingdom/Demand 7DMA:日度:千桶每天",
+      "Jet Fuel Demand/United States/Demand 7DMA:日度:千桶每天"
+    ],
+    "Maritime Bunker Demand": [
+      "Maritime Bunker Demand/China/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/Europe/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/Saudi Arabia/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/Singapore/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/UAE/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/United States/Demand:日度:千桶每天",
+      "Maritime Bunker Demand/China/Demand 7DMA:日度:千桶每天",
+      "Maritime Bunker Demand/Europe/Demand 7DMA:日度:千桶每天",
+      "Maritime Bunker Demand/Saudi Arabia/Demand 7DMA:日度:千桶每天",
+      "Maritime Bunker Demand/Singapore/Demand 7DMA:日度:千桶每天",
+      "Maritime Bunker Demand/UAE/Demand 7DMA:日度:千桶每天",
+      "Maritime Bunker Demand/United States/Demand 7DMA:日度:千桶每天"
+    ]
+  },
+  "Cube Dashboards": {
+    "Oil Supply Analysis": [
+      "Oil Supply Analysis/Oil And Gas Category/Crude Oil:月度:千桶每天",
+      "Oil Supply Analysis/Oil And Gas Category/Condensate:月度:千桶每天",
+      "Oil Supply Analysis/Region/Australasia:月度:千桶每天",
+      "Oil Supply Analysis/Region/Caribbean:月度:千桶每天",
+      "Oil Supply Analysis/Region/Central America :月度:千桶每天",
+      "Oil Supply Analysis/Region/Central Asia :月度:千桶每天",
+      "Oil Supply Analysis/Region/East Africa:月度:千桶每天",
+      "Oil Supply Analysis/Region/East Asia :月度:千桶每天",
+      "Oil Supply Analysis/Region/Eastern Europe:月度:千桶每天",
+      "Oil Supply Analysis/Region/Melanesia, Micronesia&Polynesia:月度:千桶每天",
+      "Oil Supply Analysis/Region/Middle East:月度:千桶每天",
+      "Oil Supply Analysis/Region/North Africa:月度:千桶每天",
+      "Oil Supply Analysis/Region/North America:月度:千桶每天",
+      "Oil Supply Analysis/Region/Russia :月度:千桶每天",
+      "Oil Supply Analysis/Region/South Africa:月度:千桶每天",
+      "Oil Supply Analysis/Region/South America:月度:千桶每天",
+      "Oil Supply Analysis/Region/South Asia:月度:千桶每天",
+      "Oil Supply Analysis/Region/South East Asia:月度:千桶每天",
+      "Oil Supply Analysis/Region/Southern Europe:月度:千桶每天",
+      "Oil Supply Analysis/Region/West Africa:月度:千桶每天",
+      "Oil Supply Analysis/Region/Western Europe:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Accidental Damage:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Civil Unrest or Attacks :月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Maintenance:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Natural Events:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Other Planned:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Other Unplanned:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Production:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Reguatory Order :月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Strike:月度:千桶每天",
+      "Oil Supply Analysis/CapacityDetail/Unutilized Capacity:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Heavy Reguar:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Heavy Sour:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Heavy Sweet:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Heavy Unspecified:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Light Reguar:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Light Sour:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Light Sweet:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Light Unspecified :月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Medium Regular :月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Medium Sour:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Medium Sweet:月度:千桶每天",
+      "Oil Supply Analysis/Oil Classification Group/Medium lnspecified:月度:千桶每天"
+    ],
+    "Oil Demand Analysis": [
+      "Oil Demand Analysis/Continent/Africa:月度:千桶每天",
+      "Oil Demand Analysis/Continent/Asia:月度:千桶每天",
+      "Oil Demand Analysis/Continent/Europe:月度:千桶每天",
+      "Oil Demand Analysis/Continent/Middle East:月度:千桶每天",
+      "Oil Demand Analysis/Continent/North America:月度:千桶每天",
+      "Oil Demand Analysis/Continent/Oceania:月度:千桶每天",
+      "Oil Demand Analysis/Continent/Russia:月度:千桶每天",
+      "Oil Demand Analysis/Continent/South America:月度:千桶每天",
+      "Oil Demand Analysis/Region/Australasia:月度:千桶每天",
+      "Oil Demand Analysis/Region/Caribbean:月度:千桶每天",
+      "Oil Demand Analysis/Region/Central America:月度:千桶每天",
+      "Oil Demand Analysis/Region/Central Asia:月度:千桶每天",
+      "Oil Demand Analysis/Region/East Africa:月度:千桶每天",
+      "Oil Demand Analysis/Region/East Asia:月度:千桶每天",
+      "Oil Demand Analysis/Region/Eastern Europe:月度:千桶每天",
+      "Oil Demand Analysis/Region/MiddleEast:月度:千桶每天",
+      "Oil Demand Analysis/Region/North Africa:月度:千桶每天",
+      "Oil Demand Analysis/Region/North America:月度:千桶每天",
+      "Oil Demand Analysis/Region/Russia:月度:千桶每天",
+      "Oil Demand Analysis/Region/South Africa:月度:千桶每天",
+      "Oil Demand Analysis/Region/South Ameries :月度:千桶每天",
+      "Oil Demand Analysis/Region/South Asia :月度:千桶每天",
+      "Oil Demand Analysis/Region/South East Asia:月度:千桶每天",
+      "Oil Demand Analysis/Region/Southern Europe:月度:千桶每天",
+      "Oil Demand Analysis/Region/West Africa:月度:千桶每天",
+      "Oil Demand Analysis/Region/Western Europe:月度:千桶每天",
+      "Oil Demand Analysis/Country/Others:月度:千桶每天",
+      "Oil Demand Analysis/Country/Algeria:月度:千桶每天",
+      "Oil Demand Analysis/Country/Argentina:月度:千桶每天",
+      "Oil Demand Analysis/Country/Australia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Belgium:月度:千桶每天",
+      "Oil Demand Analysis/Country/Brazil:月度:千桶每天",
+      "Oil Demand Analysis/Country/Canada:月度:千桶每天",
+      "Oil Demand Analysis/Country/Chile:月度:千桶每天",
+      "Oil Demand Analysis/Country/China:月度:千桶每天",
+      "Oil Demand Analysis/Country/Colombia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Egypt:月度:千桶每天",
+      "Oil Demand Analysis/Country/France:月度:千桶每天",
+      "Oil Demand Analysis/Country/Germany:月度:千桶每天",
+      "Oil Demand Analysis/Country/Hong Kong(China):月度:千桶每天",
+      "Oil Demand Analysis/Country/India:月度:千桶每天",
+      "Oil Demand Analysis/Country/Indonesia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Iran:月度:千桶每天",
+      "Oil Demand Analysis/Country/Iraq:月度:千桶每天",
+      "Oil Demand Analysis/Country/Italy:月度:千桶每天",
+      "Oil Demand Analysis/Country/Japan:月度:千桶每天",
+      "Oil Demand Analysis/Country/Kazakhstan:月度:千桶每天",
+      "Oil Demand Analysis/Country/Kuwait :月度:千桶每天",
+      "Oil Demand Analysis/Country/Malaysia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Mexico:月度:千桶每天",
+      "Oil Demand Analysis/Country/Netherlands:月度:千桶每天",
+      "Oil Demand Analysis/Country/Nigeria:月度:千桶每天",
+      "Oil Demand Analysis/Country/Other Caribbean:月度:千桶每天",
+      "Oil Demand Analysis/Country/Other South Asia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Other West Africa:月度:千桶每天",
+      "Oil Demand Analysis/Country/Pakistan:月度:千桶每天",
+      "Oil Demand Analysis/Country/Philippines:月度:千桶每天",
+      "Oil Demand Analysis/Country/Poland:月度:千桶每天",
+      "Oil Demand Analysis/Country/RuSsia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Saudi Arabia:月度:千桶每天",
+      "Oil Demand Analysis/Country/Singapore:月度:千桶每天",
+      "Oil Demand Analysis/Country/South Africa:月度:千桶每天",
+      "Oil Demand Analysis/Country/South Korea:月度:千桶每天",
+      "Oil Demand Analysis/Country/Spain:月度:千桶每天",
+      "Oil Demand Analysis/Country/Taiwan (China):月度:千桶每天",
+      "Oil Demand Analysis/Country/Thailand:月度:千桶每天",
+      "Oil Demand Analysis/Country/Turkey:月度:千桶每天",
+      "Oil Demand Analysis/Country/UAE:月度:千桶每天",
+      "Oil Demand Analysis/Country/United Kingdom :月度:千桶每天",
+      "Oil Demand Analysis/Country/United States:月度:千桶每天",
+      "Oil Demand Analysis/Country/Venezuela:月度:千桶每天",
+      "Oil Demand Analysis/Country/Vietnam:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Biodiesel:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Biojet:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Direct crude burn:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Distillates:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Ethanol:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Fuel oil :月度:千桶每天",
+      "Oil Demand Analysis/Product category/Gasoline:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Jet fuel and kerosene:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Naphtha:月度:千桶每天",
+      "Oil Demand Analysis/Product category/NGL:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Other:月度:千桶每天",
+      "Oil Demand Analysis/Product category/Other products:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Biodiesel:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Biojet:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Bitumen:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Butane:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Diesel:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Direct crude burn:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Ethane:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Ethanol:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Fuel oil :月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Fuel oil+scrubber:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Gasoil:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/GasoLine:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Jet fuel:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Kerosene:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Low sulfur fuel oil:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Lubricants:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Naphtha:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Other:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Paraffine :月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Petroleum Coke:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/Propane:月度:千桶每天",
+      "Oil Demand Analysis/Product detail/White spirits :月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Agriculture:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Aviation:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Buildings:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Energy generation:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Industry:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Maritime:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Non energy use:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Petrochemicals:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Rail:月度:千桶每天",
+      "Oil Demand Analysis/Sector category/Road transport:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Agriculture:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Aviation:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Buildings:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Buses:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Energy own use:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Industry:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Maritime:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Non energy use:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Petrochemicals:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Power:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Rail:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Trucks:月度:千桶每天",
+      "Oil Demand Analysis/Sector detail/Vehieles:月度:千桶每天",
+      "Oil Demand Analysis/Scenario/Products Demand Base Case:月度:千桶每天"
+    ],
+    "Supply Revision Analysis": [
+      "Supply Revision Analysis/Country Revision Group/Azerbaijan/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Iraq/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kazakhstan/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kuwait/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Oman/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Other/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Qatar/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Russia/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Saudi Arabia/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/UAE/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/United States/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Current/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Previous/YearQuarter:季度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Azerbaijan/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Iraq/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kazakhstan/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kuwait/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Oman/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Other/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Qatar/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Russia/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Saudi Arabia/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/UAE/Year:年度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/United States/Year:年度:千桶每天",
+      "Supply Revision Analysis/Current/Year:年度:千桶每天",
+      "Supply Revision Analysis/Previous/Year:年度:千桶每天",
+      "Supply Revision Analysis/Current/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Previous/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Azerbaijan/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Iraq/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kazakhstan/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Kuwait/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Oman/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Other/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Qatar/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Russia/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/Saudi Arabia/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/UAE/YearMonth:月度:千桶每天",
+      "Supply Revision Analysis/Country Revision Group/United States/YearMonth:月度:千桶每天"
+    ]
+  }
+
+}

+ 15 - 0
utils/config.go

@@ -80,6 +80,14 @@ var (
 	LY_OPEN      string
 )
 
+// 睿姿得数据
+var (
+	RZD_USERNAME  string
+	RZD_PASSWORD  string
+	RZD_JSON_PATH string
+	RZD_OPEN      string
+)
+
 // CCF化纤信息
 var (
 	CCFOpen           string // 是否配置CCF
@@ -214,6 +222,13 @@ func init() {
 		LY_OPEN = config["ly_open"]
 	}
 
+	{
+		RZD_USERNAME = config["rzd_username"]
+		RZD_PASSWORD = config["rzd_password"]
+		RZD_JSON_PATH = config["rzd_json_path"]
+		RZD_OPEN = config["rzd_open"]
+	}
+
 	// 隆众数据
 	{
 		OilchemAccount = config["oilchem_account"]