package express_tool import ( "context" "encoding/json" "fmt" "github.com/pkg/errors" "io" "log" "net/http" "net/url" "time" ) type AliCloudExpressClient struct { AppCode string Host string cache ICacheAdapter } func NewAliCloudExpressClient(host, appCode string) *AliCloudExpressClient { return &AliCloudExpressClient{ AppCode: appCode, Host: host, } } func (a *AliCloudExpressClient) GetLogisticsInfo(mobile, number string) (res *ExpressRes, err error) { if mobile == "" || number == "" { return nil, errors.New("请输入手机号和物流单号") } // 设置参数 params := url.Values{} params.Add("mobile", mobile) params.Add("number", number) // 拼接URL var fullURL string fullURL, err = url.JoinPath(a.Host) if err != nil { return nil, errors.Wrapf(err, "查询物流信息失败, 拼接路径错误! 手机号:%s, 物流单号:%s", mobile, number) } // 拼接参数 fullURL = fmt.Sprintf("%s?%s", fullURL, params.Encode()) // 创建HTTP客户端 client := &http.Client{} // 创建请求 var req *http.Request req, err = http.NewRequest(http.MethodGet, fullURL, nil) if err != nil { return nil, errors.Wrapf(err, "查询物流信息失败, 创建请求失败! 手机号:%s, 物流单号:%s", mobile, number) } // 设置请求头 req.Header.Add("Authorization", "APPCODE "+a.AppCode) // 发送请求 client.Timeout = 2 * time.Second var resp *http.Response resp, err = client.Do(req) if err != nil { return nil, errors.Wrapf(err, "查询物流信息失败, 发送请求失败! 手机号:%s, 物流单号:%s", mobile, number) } defer func(Body io.ReadCloser) { err = Body.Close() if err != nil { log.Printf("查询物流信息失败, 关闭响应体失败! 手机号:%s, 物流单号:%s , %+v\n", mobile, number, err) } }(resp.Body) // 读取响应体 var body []byte body, err = io.ReadAll(resp.Body) if err != nil { return nil, errors.Wrapf(err, "查询物流信息失败, 读取响应体失败! 手机号:%s, 物流单号:%s", mobile, number) } //log.Printf("查询物流信息成功! %s\n", string(body)) // 解析JSON响应 var expressRes ExpressRes err = json.Unmarshal(body, &expressRes) if err != nil { return nil, errors.Wrapf(err, "查询物流信息失败, 解析JSON响应失败, %s! 手机号:%s, 物流单号:%s", string(body), mobile, number) } if expressRes.Code != 0 { return &expressRes, errors.Wrapf(err, "查询物流信息失败! expressRes:%+v 手机号:%s, 物流单号:%s", expressRes, mobile, number) } if expressRes.Data == nil { return &expressRes, errors.Wrapf(err, "查询物流信息失败,没有查询到物流信息! expressRes:%+v 手机号:%s, 物流单号:%s", expressRes, mobile, number) } return &expressRes, nil } func (a *AliCloudExpressClient) Set(cache ICacheAdapter) { a.cache = cache } func (a *AliCloudExpressClient) GetLogisticsInfoFormCache(ctx context.Context, mobile, prefix, number string, opt ...time.Duration) (res *ExpressRes, err error) { if a.cache != nil { res, err = a.cache.Get(ctx, a.numberKey(prefix, number)) if err != nil { return nil, errors.Wrapf(err, "获取缓存失败, number:%s", number) } if res != nil { return res, nil } } res, err = a.GetLogisticsInfo(mobile, number) if err != nil { return nil, errors.Wrapf(err, "获取物流信息失败, number:%s", number) } var infoJson []byte infoJson, err = json.Marshal(res) if err != nil { return nil, errors.Wrapf(err, "无法将物流信息转换为JSON, number:%s", number) } if len(opt) > 0 { err = a.cache.Set(ctx, a.numberKey(prefix, number), string(infoJson), opt[0]) if err != nil { return nil, errors.Wrapf(err, "缓存物流信息失败, number:%s", number) } } return } func (a *AliCloudExpressClient) DeleteLogisticsInfoCache(ctx context.Context, prefix, number string) (err error) { if a.cache == nil { return errors.New("缓存不能为空") } err = a.cache.Del(ctx, a.numberKey(prefix, number)) return err } // ipKey 生成Redis key func (a *AliCloudExpressClient) numberKey(prefix, number string) string { return fmt.Sprintf("%s:number:%s", prefix, number) } type ExpressRes struct { Code int `json:"code"` Desc string `json:"desc"` Data *Data `json:"data"` } type Data struct { State int `json:"state" dc:"物流状态【1在途中,2派件中,3已签收,4派送失败,5揽收,6退回,7转单,8疑难,9退签,10待清关,11清关中,12已清关,13清关异常】"` Name string `json:"name" dc:"物流名"` Com string `json:"com"` Number string `json:"number" dc:"单号"` Logo string `json:"logo" dc:"图标地址"` List []*List `json:"list"` } type List struct { Time string `json:"time"` Status string `json:"status"` }