Merge branch 'refs/heads/feature/对接新供应链-唯品尚'
This commit is contained in:
commit
160d955a0c
15
weipinshang_api/req.go
Normal file
15
weipinshang_api/req.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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室
|
||||||
|
}
|
50
weipinshang_api/res.go
Normal file
50
weipinshang_api/res.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package weipinshang_api
|
||||||
|
|
||||||
|
type GetOrderInfoRes struct {
|
||||||
|
Code int `json:"code"` // 0为成功,其它为失败
|
||||||
|
Msg string `json:"msg"` // 请求接口消息
|
||||||
|
Data []Data `json:"data"` // 返回数据 data array
|
||||||
|
}
|
||||||
|
type Data struct {
|
||||||
|
CThirdUserCode string `json:"c_third_user_code"` // 用户编码
|
||||||
|
COrderNoThird string `json:"c_order_no_third"` // 渠道订单号
|
||||||
|
COrderNoPayservice string `json:"c_order_no_payservice"` //支付订单号
|
||||||
|
CSendStatus string `json:"c_send_status"` // 订单状态 订单所处阶段【’’CHECKED订单被创建’’,’INSTOCK已备货’,’’SENDED已发货’’,’’RECEIVED已收货’’】
|
||||||
|
CIsPay int `json:"c_is_pay"` // 支付状态(0-未支付,1-已支付)
|
||||||
|
CIsClose int `json:"c_is_close"` // 关闭状态 c_is_close 是否关闭【0未关闭,1,已经关闭】
|
||||||
|
CShouldPay float64 `json:"c_should_pay"` // 订单应付金额 单位:元
|
||||||
|
CRealPay float64 `json:"c_real_pay"` // 实际支付金额 c_real_pay 单位:元
|
||||||
|
CMessage string `json:"c_message"` // 订单留言
|
||||||
|
CComment string `json:"c_comment"` // 系统操作备注
|
||||||
|
CDeliveryName string `json:"c_delivery_name"` // 快递公司
|
||||||
|
CDeliveryNo string `json:"c_delivery_no"` // 快递单号
|
||||||
|
CCreateDatetime string `json:"c_create_datetime"` // 订单创建时间
|
||||||
|
CPayDatetime string `json:"c_pay_datetime"` // 订单支付时间
|
||||||
|
CSendDatetime string `json:"c_send_datetime"` // 订单发货时间
|
||||||
|
CReceiveDatetime string `json:"c_receive_datetime"` // 订单确认收货时间
|
||||||
|
CReceiverName string `json:"c_receiver_name"` // 收货人姓名
|
||||||
|
CReceiverMobile string `json:"c_receiver_mobile"` // 收货人联系方式
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsRefundRes struct {
|
||||||
|
Code int `json:"code"` // 0为成功,其它为失败
|
||||||
|
Msg string `json:"msg"` // 请求接口消息
|
||||||
|
ResultType []Param `json:"resultType"` // 售后类型
|
||||||
|
WareReturn []Param `json:"wareReturn"` // 服务类型
|
||||||
|
}
|
||||||
|
|
||||||
|
type Param struct {
|
||||||
|
Code int `json:"code"` // 服务类型码 4 [上门取件(4)、客户发货(40)、客户送货(7)]
|
||||||
|
Name string `json:"name"` // 服务类型名称 上门取件 [上门取件、客户发货、客户送货]
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateAfsApplyRes struct {
|
||||||
|
Code int `json:"code"` // 0为成功,其它为失败
|
||||||
|
Msg string `json:"msg"` // 请求接口消息
|
||||||
|
Type string `json:"type"` // wait 等待。return_money:已退款
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderCancelRes struct {
|
||||||
|
Code int `json:"code"` // 0为成功,其它为失败
|
||||||
|
Msg string `json:"msg"` // 请求接口消息
|
||||||
|
}
|
75
weipinshang_api/sign.go
Normal file
75
weipinshang_api/sign.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
166
weipinshang_api/wei_pin_shang_client.go
Normal file
166
weipinshang_api/wei_pin_shang_client.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
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, channelType, key string) *WeiPinShangClient {
|
||||||
|
return &WeiPinShangClient{
|
||||||
|
Host: host,
|
||||||
|
ChannelType: channelType,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeiPinShangClient) GetOrderInfo(orderNo string) (res *GetOrderInfoRes, 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("转换GetOrderInfoRes结构体失败: %s", string(postRes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeiPinShangClient) IsRefund(mcOrderNo string) (res *IsRefundRes, 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 *CreateAfsApplyRes, err error) {
|
||||||
|
fmt.Println("createAfsApplyReq", createAfsApplyReq)
|
||||||
|
paramMap := make(map[string]any)
|
||||||
|
|
||||||
|
paramMap["mcOrderNo"] = createAfsApplyReq.McOrderNo
|
||||||
|
|
||||||
|
paramMap["customerExpect"] = createAfsApplyReq.CustomerExpect
|
||||||
|
paramMap["questionDesc"] = createAfsApplyReq.QuestionDesc
|
||||||
|
paramMap["questionPic"] = createAfsApplyReq.QuestionPic
|
||||||
|
paramMap["customerContactName"] = createAfsApplyReq.CustomerContactName
|
||||||
|
paramMap["customerMobilePhone"] = createAfsApplyReq.CustomerMobilePhone
|
||||||
|
paramMap["customerTel"] = createAfsApplyReq.CustomerTel
|
||||||
|
|
||||||
|
paramMap["pickwareProvince"] = createAfsApplyReq.PickwareProvince
|
||||||
|
paramMap["pickwareCity"] = createAfsApplyReq.PickwareCity
|
||||||
|
paramMap["pickwareCounty"] = createAfsApplyReq.PickwareCounty
|
||||||
|
paramMap["pickwareAddress"] = createAfsApplyReq.PickwareAddress
|
||||||
|
|
||||||
|
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 *OrderCancelRes, 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user