Files
kami_backend/utility/integration/camel_oil_api/api.go
danial 85b552eec3 feat(camel_oil): add order export to Excel functionality
- Add ExportOrder RPC method to camel_oil API and service interfaces
- Implement service logic to query orders and generate Excel file with order data
- Include card number and password fields in order export
- Create HTTP handler to stream Excel file with proper headers for download
- Handle token status update on frequent error ban (oneDay case)
- Fix order processing query to filter by status and pay status correctly
- Add new error code for one-day ban in camel_oil_api and handle in client logic
- Update order model and response to include card number and password
- Remove redundant logging of SendCaptcha request data in camel_oil_api client
- Add access control checks on ExportOrder endpoint for authorized users only
2025-12-11 20:13:52 +08:00

614 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package camel_oil_api
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/glog"
"kami/internal/service"
"kami/utility/utils"
"math"
"math/rand"
"strings"
"sync"
"sync/atomic"
)
// 绑卡错误类型枚举
const (
RechargeCardSuccess = 0 // 绑卡成功
RechargeCardErrorCode = 1 // 卡密错误
RechargeCardErrorToken = 2 // Token 过期/无效
RechargeCardErrorNetwork = 3 // 网络或其他错误
RechargeCardBannedOneDay = 4 // 账户仅用一天
)
type Client struct {
Client *gclient.Client
}
func NewClient(ctx context.Context) *Client {
client := gclient.New()
client.SetBrowserMode(true)
client.SetHeaderMap(map[string]string{
"accept-language": "zh-CN,zh-Hans;q=0.9",
"channel": "app",
"Content-Type": "application/json",
"host": "app.bac365.com",
"User-Agent": "Mozilla/5.0 (iPad; CPU OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app",
})
proxy, err2 := service.ProxyPool().GetProxyByOrderId(ctx, utils.GenerateRandomUUID())
if err2 != nil {
glog.Error(ctx, "获取代理失败", err2)
}
if err2 == nil && proxy.Host != "" {
glog.Info(ctx, "代理 ip", proxy.String())
client.SetProxy(proxy.String())
}
return &Client{
Client: client,
}
}
func (c *Client) SendCaptcha(ctx context.Context, phone string) (bool, error) {
// 记录请求开始
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("发送验证码请求开始 - 手机号: %s, 渠道: app", phone))
req := struct {
Phone string `json:"phone"`
Channel string `json:"channel"`
}{
Phone: phone,
Channel: "app",
}
// 记录请求数据
resp, err := c.Client.ContentJson().Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/sendVerifyMessage", req)
if err != nil {
glog.Errorf(ctx, "发送验证码请求失败,手机号: %s, 错误: %v", phone, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("发送验证码请求失败 - 手机号: %s, 错误: %v", phone, err))
return false, err
}
respStr := resp.ReadAllString()
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("发送验证码响应数据 - 手机号: %s, 响应: %s", phone, respStr))
respStruct := struct {
Code string `json:"code"`
Message string `json:"message"`
}{}
err = json.Unmarshal([]byte(respStr), &respStruct)
if err != nil {
glog.Errorf(ctx, "解析发送验证码响应失败,手机号: %s, 错误: %v", phone, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("解析发送验证码响应失败 - 手机号: %s, 错误: %v", phone, err))
return false, err
}
success := respStruct.Code == "success"
if success {
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("发送验证码成功 - 手机号: %s, 响应消息: %s", phone, respStruct.Message))
} else {
glog.Warningf(ctx, "发送验证码失败,手机号: %s, 错误码: %s, 消息: %s", phone, respStruct.Code, respStruct.Message)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("发送验证码失败 - 手机号: %s, 错误码: %s, 消息: %s", phone, respStruct.Code, respStruct.Message))
}
return success, err
}
func (c *Client) GetCaptcha(ctx context.Context) (string, error) {
return "eyJjZXJ0aWZ5SWQiOiIxUE5NbzhlTHJGIiwic2NlbmVJZCI6IjFyYW8yZ2w1IiwiaXNTaWduIjp0cnVlLCJzZWN1cml0eVRva2VuIjoiNm9PbzdlNzJuQTYxdVZMaVpWS2lMVHlSNEpNS1pZMng2dGR1bzZqMnRmQmhobkw0TVpNY3lCUUF6eE92NzZLU3pYSTYxQ2pRTmFNTnpvaVVQaEt2dklWNjZxMUJiK3M5ZzVhMU1sZzN5VkdWNUpxNW1rVEdNT3FCazE1SzIzRjQifQ==", nil
//responseStruct := struct {
// RequestId string `json:"RequestId"`
// Message string `json:"Message"`
// HttpStatusCode int `json:"HttpStatusCode"`
// Code string `json:"Code"`
// Success bool `json:"Success"`
// Result struct {
// SecurityToken string `json:"securityToken"`
// VerifyCode string `json:"VerifyCode"`
// VerifyResult bool `json:"VerifyResult"`
// CertifyId string `json:"certifyId"`
// } `json:"Result"`
//}{}
//
//response, err := gclient.New().Get(ctx, "http://124.223.113.140:3012/api_aliv3")
//if err != nil {
// return "", err
//}
//responseStr := response.ReadAllString()
//err = json.Unmarshal([]byte(responseStr), &responseStruct)
//reqSign := struct {
// CertifyId string `json:"certifyId"`
// SceneId string `json:"sceneId"`
// IsSign bool `json:"isSign"`
// SecurityToken string `json:"securityToken"`
//}{
// CertifyId: responseStruct.Result.CertifyId,
// SceneId: "1rao2gl5",
// IsSign: true,
// SecurityToken: responseStruct.Result.SecurityToken,
//}
//signBytes, _ := json.Marshal(reqSign)
//signStr := gbase64.Encode(signBytes)
//return string(signStr), err
}
func (c *Client) LoginWithCaptcha(ctx context.Context, phone string, code string) (string, error) {
// 记录登录开始
glog.Infof(ctx, "开始登录 - 手机号: %s", phone)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录请求开始 - 手机号: %s, 验证码: %s", phone, code))
//token, err := c.GetCaptcha(ctx)
//if err != nil {
// return "", err
//}
req := struct {
Phone string `json:"phone"`
Codes string `json:"codes"`
Channel string `json:"channel"`
}{
Phone: phone,
Codes: code,
Channel: "app",
}
// 记录请求数据
reqData, _ := json.Marshal(req)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录请求数据 - 手机号:%s 数据: %s", phone, string(reqData)))
resp, err := c.Client.ContentJson().Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/loginApp", req)
if err != nil {
glog.Errorf(ctx, "登录请求失败,手机号: %s, 错误: %v", phone, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录请求失败 - 手机号: %s, 错误: %v", phone, err))
return "", err
}
respStr := resp.ReadAllString()
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录响应数据 - 手机号: %s, 响应: %s", phone, respStr))
respStruct := struct {
LoginUser struct {
UserIdApp string `json:"userIdApp"`
Phone string `json:"phone"`
UserIdCamel string `json:"userIdCamel"`
LoginTime string `json:"loginTime"`
ExpireTime string `json:"expireTime"`
Ipaddr string `json:"ipaddr"`
} `json:"loginUser,omitempty"`
Code string `json:"code"`
Message string `json:"message"`
Token string `json:"token,omitempty"`
}{}
err = json.Unmarshal([]byte(respStr), &respStruct)
if err != nil {
glog.Errorf(ctx, "解析登录响应失败,手机号: %s, 错误: %v", phone, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("解析登录响应失败 - 手机号: %s, 错误: %v", phone, err))
return "", err
}
if respStruct.Code != "success" {
glog.Warningf(ctx, "登录失败,手机号: %s, 错误码: %s, 消息: %s", phone, respStruct.Code, respStruct.Message)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录失败 - 手机号: %s, 错误码: %s, 消息: %s", phone, respStruct.Code, respStruct.Message))
return "", errors.New(respStruct.Message)
}
// 记录成功信息隐藏token敏感信息
tokenMask := respStruct.Token
if len(tokenMask) > 8 {
tokenMask = respStruct.Token[:4] + "****" + respStruct.Token[len(respStruct.Token)-4:]
}
glog.Infof(ctx, "登录成功,手机号: %s, Token: %s, 用户ID: %s", phone, tokenMask, respStruct.LoginUser.UserIdApp)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("登录成功 - 手机号: %s, 用户ID: %s, Token: %s, 登录时间: %s", phone, respStruct.LoginUser.UserIdApp, tokenMask, respStruct.LoginUser.LoginTime))
return respStruct.Token, err
}
// generateRandomCoordinates 生成指定坐标附近的随机坐标
func generateRandomCoordinates(baseLat, baseLon float64, radius float64) (float64, float64) {
// 在指定半径内生成随机坐标
// 纬度1度约等于111km
latDelta := (rand.Float64()*2 - 1) * (radius / 111000.0)
lonDelta := (rand.Float64()*2 - 1) * (radius / (111000.0 * math.Cos(baseLat*math.Pi/180.0)))
newLat := baseLat + latDelta
newLon := baseLon + lonDelta
return newLat, newLon
}
func (c *Client) getAuth(ctx context.Context, auth string) string {
base64Private := "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIHSkp+Z0Lu+lZWr/wKcMT3EcWEIihKTg/jEOyKaczqG9hWL9UULJ1dFtIQNlpWRySsVZcJLoGTFdGam557lVzpY/tbN73KG9iVMBaKALLF52cgmyg0DRve4atc0OnkhTjv7Rf8B85UokdHCAM/5MgNcjXwqBGHohJ2LGC9yN2erAgMBAAECgYATfTeqww4daTaOkhQF4cnYonl83inQMRoSSe8wuiwLQMCHqounEk4VIW9AlcOh75FaKOuuV+kbx7K6SFskNPy7nGYfS22t2aM9E9Rt+JH+caEniYi5qAfb3gCIgsGExUNI6iuSM2p3/R542EDGc2FyfPPqyht+jR4CjLOLoXHfoQJBALwvF6uIOSW0Lxh7Lo/JsKpWJ1qffDvXWYag605L9JAyP0yO64woF60Tn+mGRzcaEhNDSEjinKQqPEJnxDUGYaECQQCwm1mQKD95MaeKWBiOVJZsdzL5aJsW42xyiu0ZwA7bZUgJyUskzXG0ubeIHK/czlJbev9ODubbMNJFcngX4N3LAkAJaxH0M80oZew1fXTHHYEKBWXS00iUdiK06jjcolCLJvikDEMdsKP+tYy7U00dJODitetYOn88eCCr8iWPwdIBAkBtUjzGt6NS6iHDyXSp5kKXMdIkAVS/flgLL2RFpFWOCcvmAuy5A1N3g97QKrHSBQWGC0UulJri4/3Fb25XmaKxAkBifs9dbUifeqZRNVh2Omck4xedb1FyQPLDicUycjYug3Vca0T/LRr80aX/NhbhtpSdwzF1ukiZ6W46O9DmGuNy" // 私钥 base64 或带 PEM
base64Pub := "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpwypBPN8r3Zwv3T0XUh1Ka2m2hUe3KBgIyH4fHfN/T1jsBWnbwotKEQdZfRva7mRYiz9YrTHoH/eUAuv+WYqPMubaiqpWOu0l+BzEX1kPGA98qRC06IF2Tk4Z5xAmQ8p8u3O5jxohYFkO2XlDvPU+W9SDZgSEBTe8p80LExgo6wIDAQAB" // 公钥 base64 或带 PEM
authRes, _ := DecryptWithPrivateThenEncryptWithPublic(base64Private, base64Pub, auth)
return authRes
}
// QueryCamelOilCardAvailableDenominations 查询所有可用面额
func (c *Client) QueryCamelOilCardAvailableDenominations(ctx context.Context, token string) ([]Good, error) {
c.Client.SetHeader("authorization", "Bearer "+c.getAuth(ctx, token))
resp, err := c.Client.ContentJson().Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/eCardMall/wechatCardGoods", struct {
Channel string `json:"channel"`
}{
Channel: "app",
})
if err != nil {
return nil, err
}
queryRespStruct := struct {
Code string `json:"code"`
Goods []Good `json:"goods"`
}{}
respStr := resp.ReadAllString()
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询可用面额响应数据 - 响应: %s", respStr))
if err = json.Unmarshal([]byte(respStr), &queryRespStruct); err != nil {
glog.Errorf(ctx, "解析查询可用面额响应失败,响应: %s, 错误: %v", respStr, err)
return nil, err
}
return queryRespStruct.Goods, nil
}
func (c *Client) CreateCamelOilOrder(ctx context.Context, phone, token string, amount float64) (orderId string, payUrl string, err error) {
// 记录创建订单开始
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("创建骆驼油订单开始 - 手机号: %s, 金额: %.2f", phone, amount))
c.Client.SetHeader("Authorization", "Bearer "+c.getAuth(ctx, token))
goods, err := c.QueryCamelOilCardAvailableDenominations(ctx, token)
if err != nil {
glog.Errorf(ctx, "查询可用面额失败,手机号: %s, 金额: %.2f, 错误: %v", phone, amount, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("创建订单查询面额失败 - 手机号: %s, 金额: %.2f, 错误: %v", phone, amount, err))
return "", "", err
}
goodId := ""
for _, good := range goods {
if good.Denomination == amount {
goodId = good.GoodId
break
}
}
if goodId == "" {
glog.Warningf(ctx, "当前金额 %.2f 不支持,手机号: %s", amount, phone)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("创建订单失败 - 金额 %.2f 不支持, 手机号: %s", amount, phone))
return "", "", errors.New("当前金额不支持")
}
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("找到匹配商品 - 手机号: %s, 金额: %.2f, 商品ID: %s", phone, amount, goodId))
const maxRetries = 10
//// 获取骆驼模块设置
settings, err := service.CamelOil().GetCamelOilSettings(ctx)
var maxConcurrency int
if err != nil {
glog.Error(ctx, "获取骆驼模块设置失败,使用默认并发数", err)
// 使用默认值继续执行
maxConcurrency = 5
} else {
maxConcurrency = settings.SingleAccountConcurrency
}
// 结果存储
var resultMutex sync.Mutex
var successResult *struct {
orderId string
payUrl string
}
var resultError error
var completed atomic.Bool
// 创建协程池
var wg sync.WaitGroup
semaphore := make(chan struct{}, maxConcurrency)
for range maxRetries {
// 检查是否已经有结果
if completed.Load() {
break
}
wg.Go(func() {
// 获取信号量
semaphore <- struct{}{}
defer func() { <-semaphore }()
// 已有结果则跳过
if completed.Load() {
return
}
//captchaSign, err2 := c.GetCaptcha(ctx)
//if err2 != nil {
// return
//}
// 生成随机坐标在原坐标周围1公里范围内
paramY, paramX := generateRandomCoordinates(36.36142896926231, 118.9886283180532, 1000.0)
bodyStr := struct {
OpenId string `json:"openId"`
Phone string `json:"phone"`
GoodId string `json:"goodId"`
GoodNum int `json:"goodNum"`
BindPhone string `json:"bindPhone"`
PayType string `json:"payType"`
ParamY float64 `json:"paramY"`
ParamX float64 `json:"paramX"`
Yanqian bool `json:"yanqian"`
MobileOperatingPlatform string `json:"mobileOperatingPlatform"`
SysVersion string `json:"sysVersion"`
PlatformType string `json:"platformType"`
NetWork string `json:"netWork"`
Platform string `json:"platform"`
Brand string `json:"brand"`
DeviceId string `json:"deviceId"`
}{
OpenId: "app2511282235395452908",
Phone: phone,
GoodId: goodId,
GoodNum: 1,
BindPhone: phone,
PayType: "appAli",
ParamY: paramY,
ParamX: paramX,
Yanqian: true,
MobileOperatingPlatform: "ios",
SysVersion: "iOS 26.2",
PlatformType: "iPad Pro (12.9-inch) (3rd generation)",
NetWork: "unknown",
Platform: "ios",
Brand: "apple",
DeviceId: "4E071A21C568F1939D61F38E5F5C3813",
}
pubkey := "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkc6Xr/JhWEx/WPxG2q3VHLQ+FYk/oCmQ1y14B5j4xOJY+mAWoDOOti3sAXg0Kk662gWjWET1nLI6YED4wb9HWon1NAZn47lgc5ohIpEdU91Jao85X/kgkD3NvTTvhFicttepUOsrYUZN8rAQCE7AhzwGgKnCiIRY/kE8jOCCeZQIDAQAB"
body, _ := json.Marshal(&bodyStr)
bodyS, _ := EncryptWithPublicKey(pubkey, string(body))
req := struct {
BodyStr string `json:"bodyStr"`
Yanqian bool `json:"yanqian"`
Channel string `json:"channel"`
}{
BodyStr: bodyS,
Channel: "app",
Yanqian: true,
}
resp, err1 := c.Client.Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/eCardMall/wechatCardOrder", req)
if err1 != nil {
resultMutex.Lock()
if !completed.Load() && resultError == nil {
resultError = err1
}
resultMutex.Unlock()
return
}
respStr := resp.ReadAllString()
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("创建订单响应 - 手机号: %s, 响应: %s", phone, respStr))
respStruct := struct {
Code string `json:"code"`
Message string `json:"message"`
OrderRes struct {
Body string `json:"body"`
} `json:"orderRes,omitempty"`
OrderId string `json:"orderid,omitempty"`
}{}
err = json.Unmarshal([]byte(respStr), &respStruct)
if err != nil {
resultMutex.Lock()
if !completed.Load() && resultError == nil {
resultError = err
}
resultMutex.Unlock()
return
}
// 处理响应
switch respStruct.Code {
case "limit", "error":
if respStruct.Code == "error" && strings.Contains(respStruct.Message, "系统繁忙") {
return // 继续重试
}
return
case "auth_error":
resultMutex.Lock()
if !completed.Load() {
resultError = errors.New("auth_error")
completed.Store(true)
}
resultMutex.Unlock()
return
case "success":
base64PrivateKey := "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKRzpev8mFYTH9Y/EbardUctD4ViT+gKZDXLXgHmPjE4lj6YBagM462LewBeDQqTrraBaNYRPWcsjpgQPjBv0daifU0BmfjuWBzmiEikR1T3Ulqjzlf+SCQPc29NO+EWJy216lQ6ythRk3ysBAITsCHPAaAqcKIhFj+QTyM4IJ5lAgMBAAECgYEAg/vlMI7b3EkhBhw8JTVavLMnf8+1fe/JGXuMiU22oF5gBwCPmZ4upLwLDfJt2Q1J7WPTNetEMqgKEXUH1GwKJkFGm2tSEMHSHdTmUTQ3w6bS1C0peZghyhmlWRXUlpKk5tDOQ24sWO268YrwZyueXnVGKJ4s0hY0KOiZIU2trUECQQDxA+lzq/t/L09M/bUybjsiP6eb/HBeZeu+2+JnHekb8z9BMXTOKTHqAI0Cs9UvE6BDT3aU9IJbWHbRogIMypT9AkEArq0fccphwWtAIyS0+fns4Hqs4On7yTfXSXWiAbSVif1LxP60b5n5Xm8lo12oHkdwOvKaesvgDpnIGUM9xjFfiQJASTZrABxKNYRljnmzRTJ+/BRiEdxJNiO3zS52Q+SuHzNxD5i6ZrXU18R7EUsXg0lu8YN9/hmYT687yMpx3Pjc8QJAZBs1lSouQgIsPLfRvB1+otvLbg7KzPPivufak+2hcfanUNvEHt14a6V5RZnsOoYojK/y1oM3AkchxVCi+43aOQJAC0gI6qsZ3VaPu9QDddrHPJ1dCHTXyfcNJ0op3srCVF92HoBWX54pzeagj+9g/Z4oUT9IhaO0Q3YE07N03HuVrQ=="
respData, _ := DecryptWithPrivateKey(base64PrivateKey, respStruct.OrderRes.Body)
resultMutex.Lock()
if !completed.Load() {
successResult = &struct {
orderId string
payUrl string
}{
orderId: respStruct.OrderId,
payUrl: respData,
}
completed.Store(true)
}
resultMutex.Unlock()
return
}
})
}
wg.Wait()
// 返回结果
if successResult != nil {
return successResult.orderId, successResult.payUrl, nil
}
if resultError != nil {
return "", "", resultError
}
return "", "", errors.New("创建订单超时")
}
// QueryOrder 查询对应订单
// 返回值说明:
// - result != nil && err == nil: 查询成功,找到订单
// - result == nil && err == nil: 查询成功,但未找到订单
// - result == nil && err != nil: 查询失败,有错误
func (c *Client) QueryOrder(ctx context.Context, phone, token, orderId string) (result *QueryResult, err error) {
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单开始 - 订单ID: %s", orderId))
c.Client.SetHeader("Authorization", "Bearer "+c.getAuth(ctx, token))
// 最多查询100页防止无限循环
maxPages := 100
pageNum := 1
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("开始分页查询 - 订单ID: %s, 每页大小: 50", orderId))
for pageNum <= maxPages {
reqBody := struct {
OrderId string `json:"orderId"`
Sign string `json:"sign"`
Channel string `json:"channel"`
Status string `json:"status"`
PageNum int `json:"pageNum"`
PageSize int `json:"pageSize"`
OpenId string `json:"openId"`
}{
OrderId: orderId,
Sign: Sign("app2511181557205741495"),
Channel: "app",
Status: "unused",
PageNum: pageNum,
PageSize: 50,
OpenId: "app2511181557205741495",
}
// 记录请求数据
reqData, _ := json.Marshal(reqBody)
respData, err := c.Client.Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/eCardMall/queryWechatUserECards", reqData)
if err != nil {
glog.Errorf(ctx, "查询卡券失败,第%d页订单ID: %s错误: %v", pageNum, orderId, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单请求失败 - 订单ID: %s, 第%d页, 错误: %v", orderId, pageNum, err))
return nil, err
}
respStruct := struct {
Total string `json:"total"`
Code string `json:"code"`
Cards []struct {
RecordId string `json:"recordId"`
Denomination float64 `json:"denomination"`
CreateTime string `json:"createTime"`
ExpireTime string `json:"expireTime"`
Status string `json:"status"`
OrderId string `json:"orderId"`
ECardNo string `json:"ecardNo"`
ECardCode string `json:"ecardCode"`
} `json:"cards,omitempty"`
}{}
respDataResult := respData.ReadAllString()
glog.Infof(ctx, "查询订单第%d页响应 - 订单ID: %s, 响应: %s", pageNum, orderId, respDataResult)
err = json.Unmarshal([]byte(respDataResult), &respStruct)
if err != nil {
glog.Errorf(ctx, "解析查询响应失败,第%d页订单ID: %s错误: %v", pageNum, orderId, err)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("解析查询响应失败 - 订单ID: %s, 第%d页, 错误: %v", orderId, pageNum, err))
return nil, err
}
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单第%d页完成 - 订单ID: %s, 卡券数量: %d", pageNum, orderId, len(respStruct.Cards)))
// 如果当前页没有卡券数据,说明已查询所有数据
if len(respStruct.Cards) == 0 {
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单完成未找到 - 订单ID: %s, 共查询%d页", orderId, pageNum-1))
return nil, nil
}
// 在当前页查找目标订单
for _, card := range respStruct.Cards {
if card.OrderId == orderId {
glog.Infof(ctx, "查询订单%s成功金额: %.2f, 卡号: %s", orderId, card.Denomination, card.ECardNo)
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单成功 - 订单ID: %s, 金额: %.2f, 卡号: %s, 创建时间: %s", orderId, card.Denomination, card.ECardNo, card.CreateTime))
return &QueryResult{
Balance: card.Denomination,
CardNumber: card.ECardNo,
CardPassword: card.ECardCode,
}, nil
}
}
// 当前页未找到,继续查询下一页
pageNum++
}
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("查询订单超限 - 订单ID: %s, 超过最大页数: %d, 未找到", orderId, maxPages))
return nil, nil
}
// RechargeCard 绑卡接口
// 返回值说明:
// - errType == RechargeCardSuccess: 绑卡成功
// - errType == RechargeCardErrorCode: 卡密错误
// - errType == RechargeCardErrorToken: token 过期/无效
// - errType == RechargeCardErrorNetwork: 网络或其他错误
func (c *Client) RechargeCard(ctx context.Context, token, phone, eCardCode string) (errType int, err error) {
c.Client.SetHeader("Authorization", "Bearer "+c.getAuth(ctx, token))
req := struct {
ECardCode string `json:"eCardCode"`
OpenId string `json:"openId"`
Phone string `json:"phone"`
Channel string `json:"channel"`
}{
ECardCode: eCardCode,
OpenId: "app2511181557205741495",
Phone: phone,
Channel: "app",
}
resp, err := c.Client.ContentJson().Post(ctx, "https://app.bac365.com/camel_wechat_mini_oil_server/eCardMall/eCardRecharge", req)
if err != nil {
glog.Errorf(ctx, "绑卡请求失败,错误: %v", err)
return RechargeCardErrorNetwork, err
}
respStruct := struct {
Code string `json:"code"`
Message string `json:"message"`
}{}
responseStr := resp.ReadAllString()
service.CamelOil().SavePrefetchOrderLog(ctx, fmt.Sprintf("绑卡请求响应 手机号:%s 卡号:%s - 响应数据: %s", phone, eCardCode, responseStr))
err = json.Unmarshal([]byte(responseStr), &respStruct)
if err != nil {
glog.Errorf(ctx, "解析绑卡响应失败,错误: %v", err)
return RechargeCardErrorNetwork, err
}
// 根据不同的错误码进行分类
switch respStruct.Code {
case "success":
glog.Infof(ctx, "卡密绑卡成功,手机号: %s", phone)
return RechargeCardSuccess, nil
case "oneDay":
// 输错关于频繁
glog.Warningf(ctx, "输错关于频繁: %s", phone)
return RechargeCardBannedOneDay, err
case "codeError":
err = errors.New(respStruct.Message)
glog.Warningf(ctx, "卡密错误: %v", err)
return RechargeCardErrorCode, err
case "auth_error", "unauthorized":
err = errors.New(respStruct.Message)
glog.Warningf(ctx, "Token 错误或已过期: %v", err)
return RechargeCardErrorToken, err
default:
err = errors.New(respStruct.Message)
glog.Errorf(ctx, "绑卡失败: %v", err)
return RechargeCardErrorNetwork, err
}
}