137 lines
3.0 KiB
Go
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
|
|
}
|