From 7ddebe6fc31c882f2358d7d3ceaeeabe12adea6c Mon Sep 17 00:00:00 2001 From: zhongqiang <15375399426> Date: Fri, 23 May 2025 17:46:48 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=96=B0=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E9=93=BE-=E5=94=AF=E5=93=81=E5=B0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- weipinshang_api/req.go | 20 ++++ weipinshang_api/res.go | 1 + weipinshang_api/sign.go | 75 ++++++++++++ weipinshang_api/wei_pin_shang_client.go | 152 ++++++++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 weipinshang_api/req.go create mode 100644 weipinshang_api/res.go create mode 100644 weipinshang_api/sign.go create mode 100644 weipinshang_api/wei_pin_shang_client.go diff --git a/weipinshang_api/req.go b/weipinshang_api/req.go new file mode 100644 index 0000000..f628b54 --- /dev/null +++ b/weipinshang_api/req.go @@ -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室 +} diff --git a/weipinshang_api/res.go b/weipinshang_api/res.go new file mode 100644 index 0000000..514e5be --- /dev/null +++ b/weipinshang_api/res.go @@ -0,0 +1 @@ +package weipinshang_api diff --git a/weipinshang_api/sign.go b/weipinshang_api/sign.go new file mode 100644 index 0000000..8e849cc --- /dev/null +++ b/weipinshang_api/sign.go @@ -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) + } +} diff --git a/weipinshang_api/wei_pin_shang_client.go b/weipinshang_api/wei_pin_shang_client.go new file mode 100644 index 0000000..95ae603 --- /dev/null +++ b/weipinshang_api/wei_pin_shang_client.go @@ -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 +} From c5b73fb675e28719e63a32dd3ec614afb93fe445 Mon Sep 17 00:00:00 2001 From: zhongqiang <15375399426> Date: Fri, 23 May 2025 18:16:48 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=96=B0=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E9=93=BE-=E5=94=AF=E5=93=81=E5=B0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- weipinshang_api/res.go | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/weipinshang_api/res.go b/weipinshang_api/res.go index 514e5be..b4cc623 100644 --- a/weipinshang_api/res.go +++ b/weipinshang_api/res.go @@ -1 +1,58 @@ 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"` // 请求接口消息 +} From 96c87f015d4b0f554523c29f80813393c20d25e4 Mon Sep 17 00:00:00 2001 From: zhongqiang <15375399426> Date: Mon, 26 May 2025 09:53:44 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=96=B0=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E9=93=BE-=E5=94=AF=E5=93=81=E5=B0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- weipinshang_api/req.go | 21 +++++------- weipinshang_api/res.go | 44 ++++++++++--------------- weipinshang_api/wei_pin_shang_client.go | 28 ++++++++++++---- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/weipinshang_api/req.go b/weipinshang_api/req.go index f628b54..2ec7892 100644 --- a/weipinshang_api/req.go +++ b/weipinshang_api/req.go @@ -1,20 +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字符] 支持多张图片,用逗号分隔(英文逗号) - + 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室 + PickwareProvince string `json:"pickwareProvince"` //省份 是 String [省份] 湖南 + PickwareCity string `json:"pickwareCity"` // 城市 是 String [城市] 长沙 + PickwareCounty string `json:"pickwareCounty"` // 县区 是 String [县区] 芙蓉区 + PickwareAddress string `json:"pickwareAddress"` //详细地址 是 String [县区] 塔南路59号2号楼2单元1301室 } diff --git a/weipinshang_api/res.go b/weipinshang_api/res.go index b4cc623..224c98f 100644 --- a/weipinshang_api/res.go +++ b/weipinshang_api/res.go @@ -3,34 +3,27 @@ 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"` // 收货人联系方式 - + 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 { @@ -38,7 +31,6 @@ type IsRefundRes struct { Msg string `json:"msg"` // 请求接口消息 ResultType []Param `json:"resultType"` // 售后类型 WareReturn []Param `json:"wareReturn"` // 服务类型 - } type Param struct { diff --git a/weipinshang_api/wei_pin_shang_client.go b/weipinshang_api/wei_pin_shang_client.go index 95ae603..93d484f 100644 --- a/weipinshang_api/wei_pin_shang_client.go +++ b/weipinshang_api/wei_pin_shang_client.go @@ -17,7 +17,7 @@ type WeiPinShangClient struct { Key string } -func NewWeiPinShangClient(host string, channelType, key string) *WeiPinShangClient { +func NewWeiPinShangClient(host, channelType, key string) *WeiPinShangClient { return &WeiPinShangClient{ Host: host, ChannelType: channelType, @@ -25,7 +25,7 @@ func NewWeiPinShangClient(host string, channelType, key string) *WeiPinShangClie } } -func (w *WeiPinShangClient) GetOrderInfo(orderNo string) (res any, err error) { +func (w *WeiPinShangClient) GetOrderInfo(orderNo string) (res *GetOrderInfoRes, err error) { fmt.Println("orderNo", orderNo) paramMap := make(map[string]any) paramMap["orderNo"] = orderNo @@ -36,14 +36,14 @@ func (w *WeiPinShangClient) GetOrderInfo(orderNo string) (res any, err error) { err = json.Unmarshal(postRes, &res) if err != nil || res == nil { - err = fmt.Errorf("转换结构体失败: %s", string(postRes)) + err = fmt.Errorf("转换GetOrderInfoRes结构体失败: %s", string(postRes)) return } return } -func (w *WeiPinShangClient) IsRefund(mcOrderNo string) (res any, err error) { +func (w *WeiPinShangClient) IsRefund(mcOrderNo string) (res *IsRefundRes, err error) { fmt.Println("mcOrderNo", mcOrderNo) paramMap := make(map[string]any) paramMap["mcOrderNo"] = mcOrderNo @@ -60,10 +60,24 @@ func (w *WeiPinShangClient) IsRefund(mcOrderNo string) (res any, err error) { return } -func (w *WeiPinShangClient) CreateAfsApply(createAfsApplyReq CreateAfsApplyReq) (res any, err error) { +func (w *WeiPinShangClient) CreateAfsApply(createAfsApplyReq CreateAfsApplyReq) (res *CreateAfsApplyRes, err error) { fmt.Println("createAfsApplyReq", createAfsApplyReq) paramMap := make(map[string]any) - paramMap["createAfsApplyReq"] = createAfsApplyReq + + 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 @@ -77,7 +91,7 @@ func (w *WeiPinShangClient) CreateAfsApply(createAfsApplyReq CreateAfsApplyReq) return } -func (w *WeiPinShangClient) OrderCancel(mcOrderNo string) (res any, err error) { +func (w *WeiPinShangClient) OrderCancel(mcOrderNo string) (res *OrderCancelRes, err error) { fmt.Println("mcOrderNo", mcOrderNo) paramMap := make(map[string]any) paramMap["mcOrderNo"] = mcOrderNo