From 266309918723c2f7125f5d957b3a4711fffd1e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Wed, 19 Mar 2025 18:50:13 +0800 Subject: [PATCH] 1 --- platformapi/q_bida/q_bida_access.go | 2 +- platformapi/quick_recharge/model.go | 134 ++++++++++++++ platformapi/quick_recharge/recharge_aes.go | 170 ++++++++++++++++++ .../quick_recharge/recharge_aes_test.go | 54 ++++++ platformapi/quick_recharge/recharge_goods.go | 63 +++++++ .../quick_recharge/recharge_goods_test.go | 28 +++ platformapi/quick_recharge/recharge_order.go | 42 +++++ .../quick_recharge/recharge_order_test.go | 21 +++ 8 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 platformapi/quick_recharge/model.go create mode 100644 platformapi/quick_recharge/recharge_aes.go create mode 100644 platformapi/quick_recharge/recharge_aes_test.go create mode 100644 platformapi/quick_recharge/recharge_goods.go create mode 100644 platformapi/quick_recharge/recharge_goods_test.go create mode 100644 platformapi/quick_recharge/recharge_order.go create mode 100644 platformapi/quick_recharge/recharge_order_test.go diff --git a/platformapi/q_bida/q_bida_access.go b/platformapi/q_bida/q_bida_access.go index d727ed88..1debb605 100644 --- a/platformapi/q_bida/q_bida_access.go +++ b/platformapi/q_bida/q_bida_access.go @@ -58,7 +58,7 @@ func NewQBiDa(account, password string, config ...*platformapi.APIConfig) *Api { config: &curConfig, } if err := api.CheckTokenExpiration(); err != nil { - panic(err) + fmt.Println(err) } return api diff --git a/platformapi/quick_recharge/model.go b/platformapi/quick_recharge/model.go new file mode 100644 index 00000000..43ec349f --- /dev/null +++ b/platformapi/quick_recharge/model.go @@ -0,0 +1,134 @@ +package quick_recharge + +const ( + AppKey = "48ef1387dbea225786b910c6fce4c1fa" // api权限秘钥 + UserID = "100220" // 平台商户ID + UrlLink = "http://ads.haoshengkm.com" // 域名数据访问连接 + ResponseCodeSuccess = 1000 +) + +// GetRechargeGoodsResp 获取商品列表 +type GetRechargeGoodsResp struct { + Code int `json:"code"` + Msg string `json:"msg"` + AllPage int `json:"allPage"` // 总页数 + AllCount int `json:"allCount"` // 总条数 + Data []*RechargeGoodsRespData `json:"data"` // 商品列表 +} + +type RechargeGoodsRespData struct { + Id int `json:"id"` // 商品ID + KeyID int `json:"keyId"` // 废弃 + Price float64 `json:"price"` // 商品面值 + Money float64 `json:"money"` // 购买价格(多规格时显示本平台的默认价格) + Name string `json:"name"` // 名称 + Number int `json:"number"` // 商品编号 + Day int `json:"day"` // 质保天数:为0时无限制 + Type int `json:"type"` // 商品类型:1、卡密;3、人工代充 + Multiple int `json:"multiple"` // 发货数量:买1件时的实际发货数量 + Status int `json:"status"` // 销售状态:1、销售;2、暂停;3、禁售 + IsRepeat int `json:"isRepeat"` // 重复下单:0、不允许;1、允许 + SkuType int `json:"skuType"` // 规格类型:0、单规格;1、多规格 + IsRefOrder int `json:"isRefOrder"` // 不知道是啥 + IsRefMoney int `json:"isRefMoney"` // 不知道是撒 +} + +// GetGoodsDetailResp 根据ID获取商品详情数据 +type GetGoodsDetailResp struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data []*GetGoodsDetailData `json:"data"` +} + +type GetGoodsDetailData struct { + Id int `json:"id"` // 商品ID + Number int `json:"number"` // 商品编号 + Name string `json:"name"` // 商品名称 + Price float64 `json:"price"` // 商品面值 + Money float64 `json:"money"` // 商品面值 + Type int `json:"type"` // 商品类型:1卡密;3人工代充 + Day int `json:"day"` // 质保天数:为0时无限制 + Min int `json:"min"` // 最低下单数量 + Max int `json:"max"` // 最高下单数量 + Note string `json:"note"` // 注意事项 + Desc string `json:"desc"` // 商品介绍 + Describe string `json:"describe"` // + AccountName string `json:"accountName"` // 充值帐号名称:人工代充商品专用 + AccountType int `json:"accountType"` // 充值帐号类型:1、文本输入框;4、下拉菜单;5、多行文本;6、数字类型;7、累乘类型;8、图片类型; + AccountType1 int `json:"accountType1"` // + AccountDesc string `json:"accountDesc"` // 充值帐号提示 + AccountContent string `json:"accountContent"` //充值帐号选项内容:单选、下拉菜单专用,多个选项用“,”号隔开 + Count int `json:"count"` // 库存总数量 + TagName string `json:"TagName"` // 标签名称 + TagColor string `json:"TagColor"` // 标签颜色 + Imgs []struct { + Img string `json:"img"` // 图片 + } `json:"imgs"` // 图片地址列表 + Discounts []struct { + Min int `json:"min"` // 大于min个 + Max int `json:"max"` // 小于等于max个 + Discount float64 `json:"discount"` // 折扣:单位% + } `json:"discounts"` // 折扣列表 + Templates []struct { + Name string `json:"name"` //模版名称 + Type int `json:"type"` //模版类型:1、文本输入框;4、下拉菜单;5、多行文本;6、数字类型;7、累乘类型;8、图片类型;9、级联选择; + Desc string `json:"desc"` //文本输入框提示 + Content string `json:"content"` // 选项内容:单选、下拉菜单专用,多个选项用“,”号隔开 + } `json:"templates"` // 充值模版列表 + MainKey int `json:"mainKey"` + Status int `json:"status"` //销售状态:1、销售;2、暂停;3、禁售 + Multiple int `json:"multiple"` // 发货数量:买1件时的实际发货数量 + IsRepeat int `json:"isRepeat"` // 重复下单:0、不允许;1、允许 + SkuType int `json:"skuType"` // 规格类型:0、单规格;1、多规格 + Skus []struct { + Name string `json:"name"` // 规格分类名称 + Data []interface{} `json:"data"` // 规格数组 name 规格名称 + } `json:"skus"` // 商品规格数组(多规格专用) + SkuDetails []struct { + Names []struct { + Title string `json:"title"` // 规格分类名称 + Value string `json:"value"` // 规格名称 + } // 规格信息数组 title 规格分类名称 value + status int `json:"status"` // 规格上架状态:0、已下架;1、已上架 + sku int `json:"sku"` // 规格编码 + money int `json:"money"` // 规格购买价格 + count int `json:"count"` // 规格库存数量 + } `json:"skuDetails"` // 规格详情数组(多规格专用) +} + +// CreateOrderByGoodsReq 商品下单 +type CreateOrderByGoodsReq struct { + UserNo int `json:"userNo"` // 是 客户编号 + Id int `json:"id"` // 是 商品ID + PayType int `json:"payType"` // 是 扣款方式:0、余额下单;1、骏卡兑换 + Count int `json:"count"` // 是 购买数量 + SafePrice float64 `json:"safePrice"` // 否 安全进价,祥见下方说明 + Comment string `json:"comment"` // 否 购买备注 + OuterNumber string `json:"outerNumber"` // 否 进货方订单号(采购单号) + NotifyUrl string `json:"notifyUrl"` // 否 异步通知地址 + CardPartner string `json:"cardPartner"` // 否 骏卡商户号:payType为1时必填 + CardNumber string `json:"cardNumber"` // 否 骏卡卡号:payType为1时必填 + CardPwd string `json:"cardPwd"` // 否 骏卡密码:payType为1时必填 + CardFace float64 `json:"cardFace"` // 否 骏卡面额:payType为1时必填 + Account string `json:"account"` // 否 充值帐号 + Name0 string `json:"name0"` // 否 充值模版的充值名称0 + Val0 string `json:"val0"` // 否 充值模版的充值内容0 + Name1 string `json:"name1"` // 否 充值模版的充值名称1 + Val1 string `json:"val1"` // 否 充值模版的充值内容1 + Name2 string `json:"name2"` // 否 充值模版的充值名称2 + Val2 string `json:"val2"` // 否 充值模版的充值内容2 + Name3 string `json:"name3"` // 否 充值模版的充值名称3 + Val3 string `json:"val3"` // 否 充值模版的充值内容3 + Name4 string `json:"name4"` // 否 充值模版的充值名称4 + Val4 string `json:"val4"` // 否 充值模版的充值内容4 + Name5 string `json:"name5"` // 否 充值模版的充值名称5 + Val5 string `json:"val5"` // 否 充值模版的充值内容5 + Name6 string `json:"name6"` // 否 充值模版的充值名称6 + Val6 string `json:"val6"` // 否 充值模版的充值内容6 + Name7 string `json:"name7"` // 否 充值模版的充值名称7 + Val7 string `json:"val7"` // 否 充值模版的充值内容7 + Name8 string `json:"name8"` // 否 充值模版的充值名称8 + Val8 string `json:"val8"` // 否 充值模版的充值内容8 + Sku string `json:"sku"` // 否 规格编码(多规格必传) + Sign string `json:"sign"` // 是 接口密钥+ userNo+id+count+payType +} diff --git a/platformapi/quick_recharge/recharge_aes.go b/platformapi/quick_recharge/recharge_aes.go new file mode 100644 index 00000000..ad55c18b --- /dev/null +++ b/platformapi/quick_recharge/recharge_aes.go @@ -0,0 +1,170 @@ +package quick_recharge + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "encoding/base64" + "encoding/hex" + "fmt" + "git.rosy.net.cn/baseapi/platformapi" + "git.rosy.net.cn/baseapi/utils" + "net/http" + "net/url" + "strings" + "unicode" +) + +type API struct { + platformapi.APICookie + appKey string + userID string + client *http.Client + config *platformapi.APIConfig +} + +func New(appKey, userId string, config ...*platformapi.APIConfig) *API { + curConfig := platformapi.DefAPIConfig + if len(config) > 0 { + curConfig = *config[0] + } + return &API{ + appKey: appKey, + userID: userId, + client: &http.Client{Timeout: curConfig.ClientTimeout}, + config: &curConfig, + } +} + +// AccessInfo 发送请求 +func (a *API) AccessInfo(baseUrl, url, requestMethods string, resultKey string, param map[string]interface{}) (retVal map[string]interface{}, err error) { + err = platformapi.AccessPlatformAPIWithRetry(a.client, + func() *http.Request { + var request *http.Request + if requestMethods == http.MethodPost { + fullURL := utils.GenerateGetURL(baseUrl, url, nil) + // 获取json结构体的参数体 + request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(param).Encode())) + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + } else { + request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(baseUrl, url, param), nil) + } + + return request + }, + a.config, + func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) { + if jsonResult1 == nil { + return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil") + } + retVal = jsonResult1 + if jsonResult1["code"] == nil { + return platformapi.ErrLevelGeneralFail, fmt.Errorf("返回结果格式不正常") + } + code := int(utils.MustInterface2Int64(jsonResult1["code"])) + if code == ResponseCodeSuccess { + if resultKey == "" { + retVal = jsonResult1 + } else { + retVal, _ = jsonResult1[resultKey].(map[string]interface{}) + } + return platformapi.ErrLevelSuccess, nil + } + newErr := utils.NewErrorIntCode(jsonResult1["msg"].(string), code) + return platformapi.ErrLevelCodeIsNotOK, newErr + }) + return retVal, err +} + +// AESEncrypt 加密函数 +func AESEncrypt(plaintext, key []byte) (string, error) { + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + blockSize := block.BlockSize() + paddedPlaintext := PKCS7Padding(plaintext, blockSize) + ciphertext := make([]byte, len(paddedPlaintext)) + + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + blockMode.CryptBlocks(ciphertext, paddedPlaintext) + + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +// AESDecrypt 解密函数 +func AESDecrypt(ciphertext, key []byte) (string, error) { + decodedCiphertext, err := base64.StdEncoding.DecodeString(string(ciphertext)) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + blockSize := block.BlockSize() + plaintext := make([]byte, len(decodedCiphertext)) + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + blockMode.CryptBlocks(plaintext, decodedCiphertext) + + unpaddedPlaintext, err := PKCS7UnPadding(plaintext) + if err != nil { + return "", err + } + + return string(unpaddedPlaintext), nil +} + +// Md5Sign 签名 +func (a *API) Md5Sign(signKey string, param ...string) string { + md5Data := signKey + + for _, v := range param { + if ContainsChinese(v) { + md5Data += url.QueryEscape(v) + } else { + md5Data += v + } + } + + lowerStr := strings.ToLower(md5Data) + harsher := md5.New() + harsher.Write([]byte(lowerStr)) + hash := harsher.Sum(nil) + + return hex.EncodeToString(hash) +} + +// ContainsChinese 参数化是否包含中文 +func ContainsChinese(s string) bool { + for _, v := range s { + if unicode.Is(unicode.Han, v) { + return true + } + } + return false +} + +// PKCS7Padding 填充方法 +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +// PKCS7UnPadding 移除填充 +func PKCS7UnPadding(origData []byte) ([]byte, error) { + length := len(origData) + if length == 0 { + return nil, fmt.Errorf("invalid encrypted data") + } + unpadding := int(origData[length-1]) + if unpadding > length || unpadding <= 0 { + return nil, fmt.Errorf("invalid encrypted data") + } + return origData[:(length - unpadding)], nil +} diff --git a/platformapi/quick_recharge/recharge_aes_test.go b/platformapi/quick_recharge/recharge_aes_test.go new file mode 100644 index 00000000..a35fa328 --- /dev/null +++ b/platformapi/quick_recharge/recharge_aes_test.go @@ -0,0 +1,54 @@ +package quick_recharge + +import ( + "fmt" + "git.rosy.net.cn/baseapi" + "go.uber.org/zap" + "testing" +) + +var ( + api *API + sugarLogger *zap.SugaredLogger +) + +func init() { + logger, _ := zap.NewDevelopment() + sugarLogger = logger.Sugar() + baseapi.Init(sugarLogger) + + api = New(AppKey, UserID) +} + +func TestAes(t *testing.T) { + + key := []byte("5fb9600dd400b5e0853caed93ebbfb4e") // 256 位密钥,必须是 32 字节长 + plaintext := []byte("mpwZ8Mf03eQm0xAu7+RWG8VM5RQpSIBrqUGilYts6p1c04uydMlsM5z85Azgt8HORVbJv+EReLAxoaHW2L8ZWeyG80mskqzjyKr8wuOiv/6upkYGi0lAVTSfrxuJaf") + + // 加密 + encryptedText, err := AESEncrypt(plaintext, key) + if err != nil { + fmt.Println("Encryption failed:", err) + return + } + fmt.Println("Encrypted Text:", encryptedText) + +} + +func TestAESDecrypt(t *testing.T) { + encryptedText := "mpwZ8Mf031teQm0xAu7+RWG8VM5RQpSIBrqUGilYts6p1c04uydMlsM5z85Azgt8HORVbJv+EReLAxoaHW2L8ZWeyG80mskqzjyKr8wuOiv/6upkYGi0lAVTSfrxuJaf" + key := "5fb9600dd400b5e0853caed93ebbfb4e" + // 解密 + decryptedText, err := AESDecrypt([]byte(encryptedText), []byte(key)) + if err != nil { + fmt.Println("Decryption failed:", err) + return + } + fmt.Println("Decrypted Text:", decryptedText) + +} + +func TestSign(t *testing.T) { + md5 := api.Md5Sign(AppKey, UserID) + fmt.Println(md5) +} diff --git a/platformapi/quick_recharge/recharge_goods.go b/platformapi/quick_recharge/recharge_goods.go new file mode 100644 index 00000000..6b5182a6 --- /dev/null +++ b/platformapi/quick_recharge/recharge_goods.go @@ -0,0 +1,63 @@ +package quick_recharge + +import ( + "encoding/json" + "fmt" + "git.rosy.net.cn/baseapi/utils" + "net/http" +) + +// GetRechargeGoods 获取平台商品列表 +func (a *API) GetRechargeGoods(param map[string]interface{}) (result []*RechargeGoodsRespData, err error) { + if param["type"] != nil { + param["sign"] = a.Md5Sign(AppKey, UserID, utils.Int64ToStr(utils.ForceInterface2Int64(param["type"]))) + } else { + param["sign"] = a.Md5Sign(AppKey, UserID) + } + goods, err := a.AccessInfo(UrlLink, "api/getGoods.htm", http.MethodPost, "", param) + if err != nil { + return nil, err + } + + var goodList *GetRechargeGoodsResp + goodsByte, err := json.Marshal(goods) + if err != nil { + return nil, err + } + if err = json.Unmarshal(goodsByte, &goodList); err != nil { + return nil, err + } + + if goodList.Code != ResponseCodeSuccess { + return nil, fmt.Errorf("%s", goodList.Msg) + } + + return goodList.Data, nil +} + +// GetGoodsDetail 获取商品详情 +func (a *API) GetGoodsDetail(goodsId int64) (detail []*GetGoodsDetailData, err error) { + param := map[string]interface{}{ + "userNo": a.userID, + "id": goodsId, + } + param["sign"] = a.Md5Sign(AppKey, UserID, utils.Int64ToStr(goodsId)) + data, err := a.AccessInfo(UrlLink, "api/getGood.htm", http.MethodPost, "", param) + if err != nil { + return nil, err + } + + var result *GetGoodsDetailResp + goodsByte, err := json.Marshal(data) + if err != nil { + return nil, err + } + if err = json.Unmarshal(goodsByte, &result); err != nil { + return nil, err + } + if result.Code != ResponseCodeSuccess { + return nil, fmt.Errorf("%s", result.Msg) + } + + return result.Data, nil +} diff --git a/platformapi/quick_recharge/recharge_goods_test.go b/platformapi/quick_recharge/recharge_goods_test.go new file mode 100644 index 00000000..441adf37 --- /dev/null +++ b/platformapi/quick_recharge/recharge_goods_test.go @@ -0,0 +1,28 @@ +package quick_recharge + +import ( + "testing" +) + +func TestGetRechargeGoods(t *testing.T) { + param := map[string]interface{}{ + "userNo": 100220, + "page": 1, + "type": 1, + } + goodsList, err := api.GetRechargeGoods(param) + if err != nil { + t.Log("err := ", err) + } else { + t.Log(goodsList) + } +} + +func TestGetGoodsDetail(t *testing.T) { + goods, err := api.GetGoodsDetail(56254) + if err != nil { + t.Log("err := ", err) + } else { + t.Log(&goods) + } +} diff --git a/platformapi/quick_recharge/recharge_order.go b/platformapi/quick_recharge/recharge_order.go new file mode 100644 index 00000000..c435f348 --- /dev/null +++ b/platformapi/quick_recharge/recharge_order.go @@ -0,0 +1,42 @@ +package quick_recharge + +import ( + "encoding/json" + "fmt" + "git.rosy.net.cn/baseapi/utils" + "net/http" +) + +// CreateOrderByGoods 根据商品下单 +func (a *API) CreateOrderByGoods(param *CreateOrderByGoodsReq) (result *CreateOrderByGoodsResp, err error) { + param.Sign = a.Md5Sign(AppKey, UserID, utils.Int2Str(param.Id), utils.Int2Str(param.Count), utils.Int2Str(param.PayType)) + data, err := a.AccessInfo(UrlLink, "api/v2/addOrder", http.MethodPost, "", utils.Struct2Map(param, "", false)) + if err != nil { + return nil, err + } + + orderByte, err := json.Marshal(data) + if err != nil { + return nil, err + } + if err = json.Unmarshal(orderByte, &result); err != nil { + return nil, err + } + + if result.Code != ResponseCodeSuccess { + return nil, fmt.Errorf("%s", err) + } + + return result, nil +} + +type CreateOrderByGoodsResp struct { + Code int `json:"code"` + Msg string `json:"msg"` + Id int `json:"id"` // 订单ID + Number string `json:"number"` // 订单编号 + Cards []struct { + Number string `json:"number"` // 卡号密码 + Pwd string `json:"pwd"` + } `json:"cards"` +} diff --git a/platformapi/quick_recharge/recharge_order_test.go b/platformapi/quick_recharge/recharge_order_test.go new file mode 100644 index 00000000..f1255cff --- /dev/null +++ b/platformapi/quick_recharge/recharge_order_test.go @@ -0,0 +1,21 @@ +package quick_recharge + +import ( + "git.rosy.net.cn/baseapi/utils" + "testing" +) + +func TestGetRechargeCreateOrder(t *testing.T) { + param := &CreateOrderByGoodsReq{ + UserNo: utils.Str2Int(UserID), + Id: 56254, + PayType: 0, + Count: 1, + } + result, err := api.CreateOrderByGoods(param) + if err != nil { + t.Log(err) + } else { + t.Log(result) + } +}