Compare commits
No commits in common. "2094a7b249fca44be1eea2f9cf0c7c7c2dcb568d" and "0ea412e72581b86d8aabdd2c3cb5ab6b9d6bf56d" have entirely different histories.
2094a7b249
...
0ea412e725
9
const.go
9
const.go
|
@ -1,9 +0,0 @@
|
||||||
package tpl
|
|
||||||
|
|
||||||
// ENV ...
|
|
||||||
type ENV string
|
|
||||||
|
|
||||||
const (
|
|
||||||
EnvStaging ENV = "STAGING"
|
|
||||||
EnvProd ENV = "PROD"
|
|
||||||
)
|
|
6
go.mod
6
go.mod
|
@ -1,11 +1,10 @@
|
||||||
module git.selly.red/Selly-Modules/3pl
|
module git.selly.red/Selly-Modules/3pl
|
||||||
|
|
||||||
go 1.20
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb
|
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb
|
||||||
git.selly.red/Selly-Modules/natsio v1.0.3-0.20240620022429-b891bfc9c52d
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231012084951-0dadf9f683f1
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
|
||||||
github.com/nats-io/nats.go v1.17.0
|
github.com/nats-io/nats.go v1.17.0
|
||||||
github.com/thoas/go-funk v0.9.2
|
github.com/thoas/go-funk v0.9.2
|
||||||
)
|
)
|
||||||
|
@ -15,6 +14,7 @@ require (
|
||||||
github.com/elastic/go-licenser v0.4.1 // indirect
|
github.com/elastic/go-licenser v0.4.1 // indirect
|
||||||
github.com/elastic/go-sysinfo v1.1.1 // indirect
|
github.com/elastic/go-sysinfo v1.1.1 // indirect
|
||||||
github.com/elastic/go-windows v1.0.1 // indirect
|
github.com/elastic/go-windows v1.0.1 // indirect
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||||
github.com/golang/snappy v0.0.3 // indirect
|
github.com/golang/snappy v0.0.3 // indirect
|
||||||
github.com/jcchavezs/porto v0.4.0 // indirect
|
github.com/jcchavezs/porto v0.4.0 // indirect
|
||||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -1,7 +1,13 @@
|
||||||
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb h1:AmcYd88IcdSkH+NEvKyJLT7psidSkjJQT/nAg/KuzFk=
|
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb h1:AmcYd88IcdSkH+NEvKyJLT7psidSkjJQT/nAg/KuzFk=
|
||||||
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb/go.mod h1:Q1//Z6HRmfa7VyjH2J6YyT0YV2jT8+K6SIgwnYuS4II=
|
git.selly.red/Selly-Modules/logger v0.0.2-0.20221010053254-567df039afdb/go.mod h1:Q1//Z6HRmfa7VyjH2J6YyT0YV2jT8+K6SIgwnYuS4II=
|
||||||
git.selly.red/Selly-Modules/natsio v1.0.3-0.20240620022429-b891bfc9c52d h1:fSFFbzh+Xr7fjyn3R2smaEf/EPF78uZuHCq552OxJlU=
|
git.selly.red/Selly-Modules/natsio v1.0.2-0.20221010041139-c11419a3ad33 h1:GvQjelaV4XZm++AOihYAKOD6k9510aMAr6B6MGnrXPs=
|
||||||
git.selly.red/Selly-Modules/natsio v1.0.3-0.20240620022429-b891bfc9c52d/go.mod h1:KNODhfeBqxRmHHQHHU+p3JfH42t8s5aNxfgr6X8fr6g=
|
git.selly.red/Selly-Modules/natsio v1.0.2-0.20221010041139-c11419a3ad33/go.mod h1:KNODhfeBqxRmHHQHHU+p3JfH42t8s5aNxfgr6X8fr6g=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231006093940-b3bde5cd0960 h1:wL/BW1xGoB/EXeA2HtxT6Nr/cXpPJYVNfToV3aFtGls=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231006093940-b3bde5cd0960/go.mod h1:KNODhfeBqxRmHHQHHU+p3JfH42t8s5aNxfgr6X8fr6g=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231012084951-0dadf9f683f1 h1:IGDJmsxsB9xPOh7UaUAbnU4EC4RkFmthjo5Ob5rbEw4=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231012084951-0dadf9f683f1/go.mod h1:KNODhfeBqxRmHHQHHU+p3JfH42t8s5aNxfgr6X8fr6g=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231020090841-5edec97ee393 h1:43kE03FW3NONfE6hXlghafS1d233dfc7grlFqd+15SA=
|
||||||
|
git.selly.red/Selly-Modules/natsio v1.0.3-0.20231020090841-5edec97ee393/go.mod h1:KNODhfeBqxRmHHQHHU+p3JfH42t8s5aNxfgr6X8fr6g=
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
package viettelffm
|
|
||||||
|
|
||||||
type AuthRes struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
RefreshExpiresIn int `json:"refresh_expires_in"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
TokenType string `json:"token_type"`
|
|
||||||
NotBeforePolicy int `json:"not-before-policy"`
|
|
||||||
SessionState string `json:"session_state"`
|
|
||||||
Scope string `json:"scope"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateLogisticInfoPayload struct {
|
|
||||||
PrintLabelLink string `json:"print_label_link"`
|
|
||||||
TrackingCode string `json:"tracking_code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CancelORPayload struct {
|
|
||||||
OrID int `json:"or_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ORPayload struct {
|
|
||||||
OrProductLines []ORProductLine `json:"or_product_lines"`
|
|
||||||
AmountPaid float64 `json:"amount_paid"`
|
|
||||||
CodAmount float64 `json:"cod_amount"`
|
|
||||||
CodType string `json:"cod_type"`
|
|
||||||
Note string `json:"note"`
|
|
||||||
OrCode string `json:"or_code"`
|
|
||||||
OrType string `json:"or_type"`
|
|
||||||
ShippingType string `json:"shipping_type"`
|
|
||||||
CustomerName string `json:"customer_name"`
|
|
||||||
CustomerEmail string `json:"customer_email"`
|
|
||||||
PackType string `json:"pack_type"`
|
|
||||||
PriorityType string `json:"priority_type"`
|
|
||||||
Warehouse ORWarehouse `json:"warehouse"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ORProductLine struct {
|
|
||||||
Discount float64 `json:"discount"`
|
|
||||||
Price float64 `json:"price"`
|
|
||||||
Product Product `json:"product"`
|
|
||||||
Quantity int `json:"quantity"`
|
|
||||||
SpecifiedProductLine SpecifiedProductLine `json:"specified_product_line"`
|
|
||||||
Unit string `json:"unit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SpecifiedProductLine struct {
|
|
||||||
ProductConditionType ProdCondType `json:"product_condition_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Product struct {
|
|
||||||
PartnerSku string `json:"partner_sku"`
|
|
||||||
ProductId int `json:"product_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProdCondType struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ORWarehouse struct {
|
|
||||||
WarehouseId int `json:"warehouse_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ORResult struct {
|
|
||||||
OrCode string `json:"or_code"`
|
|
||||||
OrId int `json:"or_id"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
package viettelffm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.selly.red/Selly-Modules/natsio"
|
|
||||||
"git.selly.red/Selly-Modules/natsio/model"
|
|
||||||
"git.selly.red/Selly-Modules/natsio/subject"
|
|
||||||
|
|
||||||
tpl "git.selly.red/Selly-Modules/3pl"
|
|
||||||
"git.selly.red/Selly-Modules/3pl/util/httputil"
|
|
||||||
"git.selly.red/Selly-Modules/3pl/util/pjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
baseURLStag = "https://stg-gw.viettelpost.vn"
|
|
||||||
baseURLAuthStag = "https://stg-keycloak.viettelpost.vn"
|
|
||||||
|
|
||||||
// TODO: update prod url
|
|
||||||
baseURLProd = ""
|
|
||||||
baseURLAuthProd = ""
|
|
||||||
|
|
||||||
pathAuth = "/realms/wms/protocol/openid-connect/token"
|
|
||||||
pathUpdateORLogisticInfo = "/wms-core/api/v1/obms/outbound-request/outbound-request-partner/%s/bill"
|
|
||||||
pathCreateOR = "/wms-core/api/v1/obms/outbound-request/outbound-request-partner"
|
|
||||||
pathCancelOR = "/wms-core/api/v1/obms/outbound-request/cancel"
|
|
||||||
|
|
||||||
logTarget = "viettel-ffm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
baseURLENVMapping = map[tpl.ENV]string{
|
|
||||||
tpl.EnvProd: baseURLProd,
|
|
||||||
tpl.EnvStaging: baseURLStag,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
env tpl.ENV
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
|
|
||||||
natsClient natsio.Server
|
|
||||||
authInfo *AuthRes
|
|
||||||
authTokenExpireTime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient generate OnPoint client
|
|
||||||
func NewClient(env tpl.ENV, user, pwd string, nc natsio.Server) (*Client, error) {
|
|
||||||
if user == "" || pwd == "" {
|
|
||||||
return nil, errors.New("viettelffm: cannot init with empty api key")
|
|
||||||
}
|
|
||||||
return &Client{
|
|
||||||
env: env,
|
|
||||||
username: user,
|
|
||||||
password: pwd,
|
|
||||||
natsClient: nc,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) CreateOR(p ORPayload) (*ORResult, error) {
|
|
||||||
apiURL := c.getBaseURL() + pathCreateOR
|
|
||||||
token, err := c.getToken()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
natsPayload := model.CommunicationRequestHttp{
|
|
||||||
ResponseImmediately: true,
|
|
||||||
Payload: model.HttpRequest{
|
|
||||||
URL: apiURL,
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Data: pjson.ToJSONString(p),
|
|
||||||
Header: map[string]string{
|
|
||||||
httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token),
|
|
||||||
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LogTarget: logTarget,
|
|
||||||
}
|
|
||||||
r, err := c.requestHttpViaNats(natsPayload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res := r.Response
|
|
||||||
if res.StatusCode >= http.StatusBadRequest {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats %s", res.Body)
|
|
||||||
}
|
|
||||||
var data ORResult
|
|
||||||
if err = r.ParseResponseData(&data); err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats parse response %v, %s", err, res.Body)
|
|
||||||
}
|
|
||||||
return &data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateORLogisticInfo(p UpdateLogisticInfoPayload) error {
|
|
||||||
apiURL := c.getBaseURL() + pathUpdateORLogisticInfo
|
|
||||||
token, err := c.getToken()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
natsPayload := model.CommunicationRequestHttp{
|
|
||||||
ResponseImmediately: true,
|
|
||||||
Payload: model.HttpRequest{
|
|
||||||
URL: apiURL,
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Data: pjson.ToJSONString(p),
|
|
||||||
Header: map[string]string{
|
|
||||||
httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token),
|
|
||||||
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LogTarget: logTarget,
|
|
||||||
}
|
|
||||||
r, err := c.requestHttpViaNats(natsPayload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res := r.Response
|
|
||||||
if res.StatusCode >= http.StatusBadRequest {
|
|
||||||
return fmt.Errorf("viettelffm.Client.UpdateOutboundRequestLogisticInfo - requestHttpViaNats %s", res.Body)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) CancelOR(p CancelORPayload) error {
|
|
||||||
apiURL := c.getBaseURL() + pathCancelOR
|
|
||||||
token, err := c.getToken()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
natsPayload := model.CommunicationRequestHttp{
|
|
||||||
ResponseImmediately: true,
|
|
||||||
Payload: model.HttpRequest{
|
|
||||||
URL: apiURL,
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Data: pjson.ToJSONString(p),
|
|
||||||
Header: map[string]string{
|
|
||||||
httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token),
|
|
||||||
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LogTarget: logTarget,
|
|
||||||
}
|
|
||||||
r, err := c.requestHttpViaNats(natsPayload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res := r.Response
|
|
||||||
if res.StatusCode >= http.StatusBadRequest {
|
|
||||||
return fmt.Errorf("viettelffm.Client.CancelOR - requestHttpViaNats %s", res.Body)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth ...
|
|
||||||
func (c *Client) Auth() (*AuthRes, error) {
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("username", c.username)
|
|
||||||
v.Set("password", c.password)
|
|
||||||
v.Set("grant_type", "password")
|
|
||||||
v.Set("client_id", "wms_account")
|
|
||||||
b := v.Encode()
|
|
||||||
header := map[string]string{
|
|
||||||
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationURLEncoded,
|
|
||||||
}
|
|
||||||
bURL := baseURLAuthStag
|
|
||||||
if c.env == tpl.EnvProd {
|
|
||||||
bURL = baseURLAuthProd
|
|
||||||
}
|
|
||||||
api := bURL + pathAuth
|
|
||||||
natsPayload := model.CommunicationRequestHttp{
|
|
||||||
ResponseImmediately: true,
|
|
||||||
Payload: model.HttpRequest{
|
|
||||||
URL: api,
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Data: b,
|
|
||||||
Header: header,
|
|
||||||
},
|
|
||||||
LogTarget: logTarget,
|
|
||||||
}
|
|
||||||
r, err := c.requestHttpViaNats(natsPayload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats: payload %+v, err %v\n", natsPayload, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res := r.Response
|
|
||||||
if res.StatusCode >= http.StatusBadRequest {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats %s", res.Body)
|
|
||||||
}
|
|
||||||
var data AuthRes
|
|
||||||
if err = r.ParseResponseData(&data); err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats parse response %v, %s", err, res.Body)
|
|
||||||
}
|
|
||||||
return &data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) getToken() (string, error) {
|
|
||||||
if c.authInfo == nil || time.Now().After(c.authTokenExpireTime) {
|
|
||||||
auth, err := c.Auth()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
c.authInfo = auth
|
|
||||||
c.authTokenExpireTime = time.Now().Add(time.Duration(auth.ExpiresIn) * time.Second).Add(-time.Minute)
|
|
||||||
}
|
|
||||||
return c.authInfo.AccessToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) getBaseURL() string {
|
|
||||||
return baseURLENVMapping[c.env]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) requestHttpViaNats(data model.CommunicationRequestHttp) (*model.CommunicationHttpResponse, error) {
|
|
||||||
b := pjson.ToBytes(data)
|
|
||||||
msg, err := c.natsClient.Request(subject.Communication.RequestHTTP, b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.requestHttpViaNats err: %v, url %s", err, data.Payload.URL)
|
|
||||||
}
|
|
||||||
var r model.CommunicationHttpResponse
|
|
||||||
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.requestHttpViaNats parse data err: %v, url %s, data %s", err, data.Payload.URL, string(msg.Data))
|
|
||||||
}
|
|
||||||
if r.Response == nil {
|
|
||||||
return nil, fmt.Errorf("viettelffm.Client.requestHttpViaNats empty reponse")
|
|
||||||
}
|
|
||||||
return &r, nil
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
package httputil
|
package httputil
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HeaderKeyContentType = "Content-Type"
|
HeaderKeyContentType = "Content-Type"
|
||||||
HeaderKeyAuthorization = "Authorization"
|
|
||||||
|
|
||||||
HeaderValueApplicationJSON = "application/json"
|
HeaderValueApplicationJSON = "application/json"
|
||||||
HeaderValueApplicationURLEncoded = "application/x-www-form-urlencoded"
|
HeaderValueApplicationURLEncoded = "application/x-www-form-urlencoded"
|
||||||
|
|
Loading…
Reference in New Issue