review code.

This commit is contained in:
2026-05-01 17:07:20 +08:00
parent b3565ac6d7
commit 5673e5c6ca
18 changed files with 359 additions and 208 deletions

View File

@@ -17,6 +17,7 @@ import (
type TushareClient struct {
Token string `json:"token"` // API 访问令牌
BaseUrl string `json:"base_url"` // API 基础 URL
client *http.Client
}
// TushareReq Tushare API 请求结构
@@ -53,9 +54,40 @@ func NewClient(token string) *TushareClient {
return &TushareClient{
Token: token,
BaseUrl: "http://api.tushare.pro",
client: &http.Client{Timeout: 30 * time.Second},
}
}
func (c *TushareClient) httpDo() *http.Client {
if c.client != nil {
return c.client
}
return &http.Client{Timeout: 30 * time.Second}
}
// postTushare 发送 JSON 请求并解析为 TushareResp不含业务码以外的处理
func (c *TushareClient) postTushare(reqBytes []byte) (*TushareResp, error) {
resp, err := c.httpDo().Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
if err != nil {
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("#101 读取响应失败:%w", err)
}
var tushareResp TushareResp
if err := json.Unmarshal(body, &tushareResp); err != nil {
return nil, fmt.Errorf("#101 响应解析失败:%v - %s", err, string(body))
}
if tushareResp.Code != 0 {
return nil, fmt.Errorf("#102 API 返回错误:%s", tushareResp.Msg)
}
return &tushareResp, nil
}
// Do 执行 Tushare API 请求
// req: 请求参数,包含 API 名称、参数等
// fieldsVals: 字段配置列表,每个元素是一个 mapkey 为字段名value 为字段中文描述
@@ -69,8 +101,12 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
}
// 提取字段名和对应的中文表头
var fields []string
var headers []string
n := 0
for _, setting := range fieldsVals {
n += len(setting)
}
fields := make([]string, 0, n)
headers := make([]string, 0, n)
for _, setting := range fieldsVals {
for key, value := range setting {
fields = append(fields, key)
@@ -79,40 +115,15 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
}
payload.Fields = fields
// 序列化请求体为 JSON
reqBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
}
// 创建 HTTP 请求
client := &http.Client{
Timeout: 30 * time.Second, // 设置 30 秒超时
}
resp, err := client.Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
tushareResp, err := c.postTushare(reqBytes)
if err != nil {
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
return nil, err
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("#101 读取响应失败:%w", err)
}
// 解析响应 JSON
var tushareResp TushareResp
if err := json.Unmarshal(body, &tushareResp); err != nil {
return nil, fmt.Errorf("#101 响应解析失败:%v - %s", err, string(body))
}
// 检查响应码
if tushareResp.Code != 0 {
return nil, fmt.Errorf("#102 API 返回错误:%s", tushareResp.Msg)
}
// 设置表头信息
if tushareResp.Data != nil {
tushareResp.Data.Headers = headers
}
@@ -125,12 +136,22 @@ func (c *TushareClient) Do(req TushareReq, fieldsVals []map[string]string) (*Tus
// Params: 请求参数
// 返回:响应数据和错误信息
func (c *TushareClient) Exec(ApiName string, fields string, Params map[string]any) (*TushareRespData, error) {
fieldsList := strings.Split(fields, ",")
fields = strings.TrimSpace(fields)
if fields == "" {
return nil, fmt.Errorf("#100 字段列表不能为空")
}
raw := strings.Split(fields, ",")
fieldsList := make([]string, 0, len(raw))
for _, f := range raw {
f = strings.TrimSpace(f)
if f != "" {
fieldsList = append(fieldsList, f)
}
}
if len(fieldsList) == 0 {
return nil, fmt.Errorf("#100 字段列表不能为空")
}
// 构建请求体
payload := TushareReq{
APIName: ApiName,
Token: c.Token,
@@ -138,51 +159,25 @@ func (c *TushareClient) Exec(ApiName string, fields string, Params map[string]an
Fields: fieldsList,
}
// 序列化请求体为 JSON
reqBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("#100 请求序列化失败:%w", err)
}
// 创建 HTTP 请求
client := &http.Client{
Timeout: 30 * time.Second, // 设置 30 秒超时
}
resp, err := client.Post(c.BaseUrl, "application/json", bytes.NewReader(reqBytes))
tushareResp, err := c.postTushare(reqBytes)
if err != nil {
return nil, fmt.Errorf("#100 发送请求失败:%w", err)
return nil, err
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("#101 读取响应失败:%w", err)
}
// 解析响应 JSON
var tushareResp TushareResp
if err := json.Unmarshal(body, &tushareResp); err != nil {
return nil, fmt.Errorf("#101 响应解析失败:%v - %s", err, string(body))
}
// 检查响应码
if tushareResp.Code != 0 {
return nil, fmt.Errorf("#102 API 返回错误:%s", tushareResp.Msg)
}
// 设置表头信息
return tushareResp.Data, nil
}
// Map 将响应数据转换为 map 切片
// 每个 map 代表一行数据key 为字段名value 为对应的值
func (c *TushareRespData) Map() []map[string]any {
result := make([]map[string]any, 0)
if c == nil || len(c.Items) == 0 || len(c.Fields) == 0 {
return result
return make([]map[string]any, 0)
}
result := make([]map[string]any, 0, len(c.Items))
for _, item := range c.Items {
rowMap := make(map[string]any, len(c.Fields))
for idx, fn := range c.Fields {
@@ -209,11 +204,17 @@ func (c *TushareRespData) Output(title string) table.Writer {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.SetTitle(title)
if c == nil {
return tw
}
// 构建表头行
headerRow := make(table.Row, 0, len(c.Headers))
for idx, header := range c.Headers {
headerRow = append(headerRow, header+"("+c.Fields[idx]+")")
n := len(c.Fields)
if nh := len(c.Headers); nh < n {
n = nh
}
headerRow := make(table.Row, 0, n)
for idx := 0; idx < n; idx++ {
headerRow = append(headerRow, c.Headers[idx]+"("+c.Fields[idx]+")")
}
tw.AppendHeader(headerRow)