对接新供应链-唯品尚

This commit is contained in:
zhongqiang 2025-05-23 17:46:48 +08:00
parent 225efe116d
commit 7ddebe6fc3
4 changed files with 248 additions and 0 deletions

20
weipinshang_api/req.go Normal file
View File

@ -0,0 +1,20 @@
package weipinshang_api
type CreateAfsApplyReq struct {
McOrderNo int `json:"mcOrderNo"` // 子订单号 是 int 下单时候返回子订单号 20190704124955600363
CustomerExpect int `json:"customerExpect"` // 售后类型 是 int [退货(10)、仅退款(40)] 10
QuestionDesc string `json:"questionDesc"` // 原因描述 否 String [产品问题描述最多600字符] 看下面请求示例
QuestionPic string `json:"questionPic"` //问题描述图片 否 String [问题描述图片.最多2000字符] 支持多张图片,用逗号分隔(英文逗号)
CustomerContactName string `json:"customerContactName"` // 用户姓名 是 String [用户姓名] 张三
CustomerTel string `json:"customerTel"` // 用户电话 是 String [用户电话] 18533136240
CustomerMobilePhone string `json:"customerMobilePhone"` // 用户手机 是 String [用户手机] 18533136240
PickwareProvince string `json:"pickwareProvince"` //省份 是 String [省份] 湖南
PickwareCity string `json:"pickwareCity"` // 城市 是 String [城市] 长沙
PickwareCounty string `json:"pickwareCounty"` // 县区 是 String [县区] 芙蓉区
PickwareAddress string `json:"pickwareAddress"` //详细地址 是 String [县区] 塔南路59号2号楼2单元1301室
}

1
weipinshang_api/res.go Normal file
View File

@ -0,0 +1 @@
package weipinshang_api

75
weipinshang_api/sign.go Normal file
View File

@ -0,0 +1,75 @@
package weipinshang_api
import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
)
func SortedString(data any) string {
// 1. 序列化成 JSON
dataBytes, err := json.Marshal(data)
if err != nil {
return ""
}
// 2. 反序列化成 map[string]interface{}
var tmp map[string]interface{}
if err = json.Unmarshal(dataBytes, &tmp); err != nil {
return ""
}
// 3. 提取并排序 keys
keys := make([]string, 0, len(tmp))
for k := range tmp {
keys = append(keys, k)
}
sort.Strings(keys)
// 4. 构建键值对字符串
var sortedParams []string
for _, key := range keys {
if key == "hmac" {
continue // 跳过 hmac 字段
}
value := tmp[key]
strValue, err := valueToString(value)
if err != nil {
return ""
}
sortedParams = append(sortedParams, fmt.Sprintf("%s=%s", key, strValue))
}
return strings.Join(sortedParams, "&")
}
// valueToString 将任意 JSON 值转换为字符串
func valueToString(v interface{}) (string, error) {
switch i := v.(type) {
case string:
return i, nil
case bool:
return strconv.FormatBool(i), nil
case float64:
// 判断是否是整数(如 2.0 → "2"
if i == float64(int64(i)) {
return strconv.FormatInt(int64(i), 10), nil
}
return strconv.FormatFloat(i, 'f', -1, 64), nil
case json.Number:
return i.String(), nil // 如果使用 json.Number直接返回其字符串形式
case nil:
return "", nil
case map[string]interface{}, []interface{}:
// 如果是嵌套结构,重新序列化成 JSON
bytes, err := json.Marshal(i)
if err != nil {
return "", err
}
return string(bytes), nil
default:
return "", fmt.Errorf("unsupported type: %T", i)
}
}

View File

@ -0,0 +1,152 @@
package weipinshang_api
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
type WeiPinShangClient struct {
Host string
ChannelType string
Key string
}
func NewWeiPinShangClient(host string, channelType, key string) *WeiPinShangClient {
return &WeiPinShangClient{
Host: host,
ChannelType: channelType,
Key: key,
}
}
func (w *WeiPinShangClient) GetOrderInfo(orderNo string) (res any, err error) {
fmt.Println("orderNo", orderNo)
paramMap := make(map[string]any)
paramMap["orderNo"] = orderNo
postRes, err := w.WPSPost("/mcang/Mcang/getOrderInfo", paramMap)
if err != nil {
return
}
err = json.Unmarshal(postRes, &res)
if err != nil || res == nil {
err = fmt.Errorf("转换结构体失败: %s", string(postRes))
return
}
return
}
func (w *WeiPinShangClient) IsRefund(mcOrderNo string) (res any, err error) {
fmt.Println("mcOrderNo", mcOrderNo)
paramMap := make(map[string]any)
paramMap["mcOrderNo"] = mcOrderNo
postRes, err := w.WPSPost("/mcang/Refunds/IsRefund", paramMap)
if err != nil {
return
}
err = json.Unmarshal(postRes, &res)
if err != nil || res == nil {
err = fmt.Errorf("转换结构体失败: %s", string(postRes))
return
}
return
}
func (w *WeiPinShangClient) CreateAfsApply(createAfsApplyReq CreateAfsApplyReq) (res any, err error) {
fmt.Println("createAfsApplyReq", createAfsApplyReq)
paramMap := make(map[string]any)
paramMap["createAfsApplyReq"] = createAfsApplyReq
postRes, err := w.WPSPost("/mcang/Refunds/IsRefund", paramMap)
if err != nil {
return
}
err = json.Unmarshal(postRes, &res)
if err != nil || res == nil {
err = fmt.Errorf("转换结构体失败: %s", string(postRes))
return
}
return
}
func (w *WeiPinShangClient) OrderCancel(mcOrderNo string) (res any, err error) {
fmt.Println("mcOrderNo", mcOrderNo)
paramMap := make(map[string]any)
paramMap["mcOrderNo"] = mcOrderNo
postRes, err := w.WPSPost("/mcang/Refunds/IsRefund", paramMap)
if err != nil {
return
}
err = json.Unmarshal(postRes, &res)
if err != nil || res == nil {
err = fmt.Errorf("转换结构体失败: %s", string(postRes))
return
}
return
}
func (w *WeiPinShangClient) Sign(paramMap map[string]any) (res string, err error) {
var sumParamString string
if len(paramMap) != 0 {
sumParamString = SortedString(paramMap) + w.ChannelType + w.Key
} else {
sumParamString = w.ChannelType + w.Key
}
md5Sum := md5.Sum([]byte(sumParamString))
md5Str := hex.EncodeToString(md5Sum[:])
md5Str = strings.ToUpper(md5Str)
return md5Str, nil
}
func (w *WeiPinShangClient) WPSPost(url string, paramMap map[string]any) (res []byte, err error) {
bodyByte, _ := json.Marshal(paramMap)
req, err := http.NewRequest("POST", w.Host+url, bytes.NewBuffer(bodyByte))
if err != nil {
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("channelType", w.ChannelType)
sign, err := w.Sign(paramMap)
if err != nil {
return
}
req.Header.Set("md5", sign)
// 创建 HTTP 客户端
client := &http.Client{}
// 发送请求
resp, err := client.Do(req)
if err != nil {
return
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
// 读取响应体
res, err = io.ReadAll(resp.Body)
if err != nil {
return
}
//fmt.Printf("res: %s\n", string(res))
if !json.Valid(res) {
return nil, fmt.Errorf("响应不是有效的JSON格式: %s", string(res))
}
return
}