Files
chis_osi/osi/client.go
T

137 lines
3.0 KiB
Go

package osi
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"chis_osi/contract"
)
type ClientConfig struct {
BaseURL string
OrgCode string
DeviceSN string
UserName string
Ask string
OperateUser string
OperateUnit string
Transport *Transport
NowMillis func() string
}
type Client struct {
config ClientConfig
transport *Transport
}
type Result struct {
HTTPStatus int
Code string
Message string
Raw []byte
Success bool
Retryable bool
}
type responseEnvelope struct {
Code string `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data"`
}
func NewClient(config ClientConfig) *Client {
return &Client{config: config, transport: config.Transport}
}
func (c *Client) Call(ctx context.Context, serviceID string, baseInfo any, out any) (Result, error) {
if c.transport == nil {
transport, err := NewTransport(TransportConfig{})
if err != nil {
return Result{}, err
}
c.transport = transport
}
path, err := PathOf(serviceID)
if err != nil {
return Result{}, err
}
payload := contract.Envelope{
ServiceID: serviceID,
UploadInfo: contract.UploadInfo{
BaseInfo: baseInfo,
ManageInfo: contract.ManageInfo{
DSFMC: c.config.UserName,
OperateUser: c.config.OperateUser,
OperateUnit: c.operateUnit(),
},
},
}
targetURL := c.urlFor(path)
status, raw, err := c.transport.PostJSON(ctx, targetURL, payload, c.headers())
result := Result{HTTPStatus: status, Raw: raw, Retryable: isRetryableHTTPStatus(status)}
if err != nil {
result.Retryable = true
return result, err
}
var resp responseEnvelope
if err := json.Unmarshal(raw, &resp); err != nil {
return result, fmt.Errorf("decode osi response: %w", err)
}
result.Code = resp.Code
result.Message = resp.Message
result.Success = IsSuccessCode(resp.Code)
result.Retryable = result.Retryable || IsRetryableCode(resp.Code)
if !result.Success {
return result, fmt.Errorf("osi call failed code=%s message=%s", resp.Code, resp.Message)
}
if out != nil && len(resp.Data) > 0 && string(resp.Data) != "null" {
if err := json.Unmarshal(resp.Data, out); err != nil {
return result, fmt.Errorf("decode osi data: %w", err)
}
}
return result, nil
}
func (c *Client) headers() map[string]string {
ts := strconv.FormatInt(time.Now().UnixMilli(), 10)
if c.config.NowMillis != nil {
ts = c.config.NowMillis()
}
return BuildHeaders(HeaderInput{
OrgCode: c.config.OrgCode,
DeviceSN: c.config.DeviceSN,
UserName: c.config.UserName,
Ask: c.config.Ask,
TS: ts,
})
}
func (c *Client) operateUnit() string {
if c.config.OperateUnit != "" {
return c.config.OperateUnit
}
return c.config.OrgCode
}
func (c *Client) urlFor(path string) string {
baseURL := strings.TrimRight(c.config.BaseURL, "/")
if strings.HasSuffix(baseURL, "/osi/api") {
return baseURL + path
}
return baseURL + "/osi/api" + path
}
func isRetryableHTTPStatus(status int) bool {
return status == http.StatusTooManyRequests || status >= 500
}