package eastmoney import ( "context" "fmt" "net/url" "strconv" "strings" "stock-analysis/internal/model" ) // datacenterFundamentalResp 是 datacenter 财务数据接口响应。 type datacenterFundamentalResp struct { Result struct { Pages int `json:"pages"` Data []datacenterFundamentalItem `json:"data"` } `json:"result"` Success bool `json:"success"` Message string `json:"message"` } // datacenterFundamentalItem 是单条财务数据记录。 type datacenterFundamentalItem struct { SecurityCode string `json:"SECURITY_CODE"` SecurityName string `json:"SECURITY_NAME_ABBR"` ROE *float64 `json:"ROEJQ"` // 加权净资产收益率(%) DebtRatio *float64 `json:"ZCFZL"` // 资产负债率(%) NetProfit *float64 `json:"PARENTNETPROFIT"` // 归属净利润(元) IsBZ string `json:"IS_BZ"` // ST状态:0=正常, 1=ST, 2=*ST ReportDate string `json:"REPORT_DATE"` } // FetchStockFundamentals 批量拉取指定正股的最新年度基本面数据。 // 使用 datacenter API(非 push2),按 REPORT_DATE 倒序取每只股票的最新一条年报。 func (c *Client) FetchStockFundamentals(ctx context.Context, codes []string) ([]model.StockFundamental, error) { if len(codes) == 0 { return nil, nil } codeSet := make(map[string]bool, len(codes)) for _, code := range codes { codeSet[code] = true } const pageSize = 5000 seen := make(map[string]bool, len(codes)) var funds []model.StockFundamental // 按 REPORT_DATE 倒序拉取,每只股票取第一条(最新年报)。 for page := 1; page <= 3; page++ { params := url.Values{} params.Set("reportName", "RPT_F10_FINANCE_MAINFINADATA") params.Set("columns", "SECURITY_CODE,SECURITY_NAME_ABBR,ROEJQ,ZCFZL,PARENTNETPROFIT,IS_BZ,REPORT_DATE") params.Set("filter", `(REPORT_TYPE="年报")`) params.Set("pageNumber", strconv.Itoa(page)) params.Set("pageSize", strconv.Itoa(pageSize)) params.Set("sortColumns", "REPORT_DATE") params.Set("sortTypes", "-1") var resp datacenterFundamentalResp if err := c.getJSON(ctx, []string{datacenterHost}, "/api/data/v1/get", params, &resp); err != nil { return nil, fmt.Errorf("datacenter page %d: %w", page, err) } if !resp.Success { return nil, fmt.Errorf("datacenter API error: %s", resp.Message) } if len(resp.Result.Data) == 0 { break } for _, d := range resp.Result.Data { code := d.SecurityCode if code == "" || seen[code] || !codeSet[code] { continue } seen[code] = true roe := 0.0 if d.ROE != nil { roe = *d.ROE } debtRatio := 0.0 if d.DebtRatio != nil { debtRatio = *d.DebtRatio } netProfit := 0.0 if d.NetProfit != nil { netProfit = *d.NetProfit } status := 1 if d.IsBZ == "1" || d.IsBZ == "2" { status = 2 } updateDate := strings.Split(d.ReportDate, " ")[0] funds = append(funds, model.StockFundamental{ Code: code, Name: d.SecurityName, ROE: roe, DebtRatio: debtRatio, NetProfit: netProfit, Status: status, UpdateDate: updateDate, }) } // 已找到全部目标股票,停止翻页。 if len(seen) >= len(codes) { break } } return funds, nil }