package models import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "github.com/go-admin-team/go-admin-core/logger" "go-admin/common/database" "go-admin/common/storage" "io" "net/http" "net/url" "strings" "time" ) const ( MusicProductID = 2 LoginType = "3" MaxRetries = 3 ServiceId = "698039049108516515" MiGuSubscriptionUrl = "http://hz.migu.cn/order/rest/201906/all/bjhy/and/package/query.do" MiGuLoginUrl = "http://hz.migu.cn/order/rest/login/secret/url.do" SignSecretKey = "1524b4ed9eef4cdca934056499a1dd14" // 签名密钥 LoginSecretKey = "913efe5c6f274c278988af817f9d1c7d" // 登陆密钥 ) type ResultFeedbackEvt struct { MSISDN string `json:"MSISDN"` // 用户手机号码,长度11 RevDate string `json:"revdate"` // 平台接收时间戳,格式为:yyyymmddHHMMss ResCode string `json:"rescode"` // 结果代码,长度6 ResDesc string `json:"resdesc"` // 结果描述,长度1-128 Msg string `json:"msg"` // 短信或订购信息,长度1-80 } type MsgDetails struct { OrderKeyword string // 订购关键字 ChannelCode string // 渠道编码 ChannelCustomNo string // 渠道自用编码 SongID string // 歌曲ID PlatformType string // 平台类型 Price string // 价格 CustomSerialNo string // 自定义流水号(可选) } type MusicFeedbackResp struct { ReturnCode string `json:"return_code"` // 返回代码 ReturnDesc string `json:"return_desc"` // 返回描述 } func ParseMsg(msg string) (*MsgDetails, error) { parts := strings.Split(msg, "@") if len(parts) < 6 { return nil, fmt.Errorf("invalid message format") } details := &MsgDetails{ OrderKeyword: parts[0], ChannelCode: parts[1], ChannelCustomNo: parts[2], SongID: parts[3], PlatformType: parts[4], Price: parts[5], } if details.PlatformType == "sdk" && len(parts) == 7 { details.CustomSerialNo = parts[6] } return details, nil } func ParseRevDate(revDate string) (time.Time, error) { layout := "20060102150405" // 使用本地时区解析 loc, _ := time.LoadLocation("Asia/Shanghai") // 你也可以用 time.Local t, err := time.ParseInLocation(layout, revDate, loc) if err != nil { return time.Time{}, fmt.Errorf("invalid revdate format: %v", err) } return t, nil } // CheckMusicOrderState 定时任务,检查订单状态,是否有1小时退订 func CheckMusicOrderState() { logger.Info("****** CheckMusicOrderState start ******") fmt.Println("****** CheckMusicOrderState start ******") if database.Db == nil { logger.Error("Database connection is nil") fmt.Println("Database connection is nil") return } // 查询订单列表中未退订的用户,查询其是否退订;如果退订,则更新退订时间,判断是否为1小时内退订 var orderList []MgOrder // 获取当前时间前2个小时 oneHourAgo := time.Now().Add(-2 * time.Hour) fmt.Println("check oneHourAgo is:", oneHourAgo) logger.Info("check oneHourAgo is:", oneHourAgo) err := database.Db.Where("state = 1"). Where("created_at >= ?", oneHourAgo). Where("product_id = ?", MusicProductID). Order("created_at desc"). Find(&orderList).Error if err != nil { fmt.Println("query mg_order err:", err.Error()) logger.Error("query mg_order err:", err.Error()) return } fmt.Println("orderList size is:", len(orderList)) logger.Info("orderList size is:", len(orderList)) for _, order := range orderList { var loginResp LoginResponse var token string // 获取手机号并检查Redis中是否已有token phoneNumber := order.PhoneNumber token, err = getTokenFromRedis(phoneNumber) // 假设有此函数查询Redis // 如果token不存在,则调用Login接口获取token,添加重试机制 if err != nil || token == "" { maxRetries := MaxRetries for j := 0; j < maxRetries; j++ { loginResp, err = Login(order.ChannelCode, LoginType, "", phoneNumber) if err != nil || loginResp.ResCode != "000000" { if err != nil { fmt.Println("Login failed:", err.Error()) logger.Error("query mg_order err:", err.Error()) } else { fmt.Println("Login failed, loginResp.ResCode:", loginResp.ResCode) logger.Error("Login failed, loginResp.ResCode:", loginResp.ResCode) } if j < maxRetries-1 { // 如果不是最后一次尝试,继续重试 continue } break } token = loginResp.Token // 存储token到Redis err = storeTokenInRedis(phoneNumber, token) // 假设有此函数存储到Redis if err != nil { fmt.Println("Failed to store token in Redis:", err.Error()) logger.Error("Failed to store token in Redis:", err.Error()) continue } break } } // 重试最大次数 maxRetries := MaxRetries for j := 0; j < maxRetries; j++ { // 尝试调用MonthlySubscriptionQuery接口获取订阅状态 resp, err := MonthlySubscriptionQuery(token, ServiceId, order.ChannelCode) if err != nil || resp.ResCode != "000000" { if err != nil { fmt.Println("MonthlySubscriptionQuery failed:", err.Error()) logger.Error("MonthlySubscriptionQuery failed:", err.Error()) } else { fmt.Println("MonthlySubscriptionQuery failed, loginResp.ResCode:", resp.ResCode) logger.Error("MonthlySubscriptionQuery failed, loginResp.ResCode:", resp.ResCode) } if j < maxRetries-1 { // 如果不是最后一次尝试,继续重试 continue } break } if resp.Status == "0" { // 已订购 // 订购状态为已订购,不处理 break } else if resp.Status == "1" { // 未订购 // 未订购,更新订单状态 var cancelFlag int subscribeTime := order.CreatedAt unsubTime := time.Now().Format("2006-01-02 15:04:05") // 检查是否在1小时内取消 if IsWithinOneHourCancel(subscribeTime, unsubTime) { cancelFlag = 1 } // 更新订单状态 err = database.Db.Table("mg_order").Where("order_serial = ?", order.OrderSerial).Updates(map[string]interface{}{ "state": UnsubscribeOK, "is_one_hour_cancel": cancelFlag, "unsubscribe_time": unsubTime, "updated_at": time.Now(), }).Error if err != nil { fmt.Println("Failed to update order:", err.Error()) logger.Error("Failed to update order:", err.Error()) continue } break } } } fmt.Println("****** CheckMusicOrderState end ******") logger.Info("****** CheckMusicOrderState end ******") } type MonthlySubscriptionReq struct { ChannelCode string `json:"channelCode"` Timestamp string `json:"timestamp"` Signature string `json:"signature"` Token string `json:"token"` ServiceId string `json:"serviceId"` } type MonthlySubscriptionResp struct { ResCode string `json:"resCode"` ResMsg string `json:"resMsg"` Status string `json:"status"` ValidTime string `json:"validTime"` } func generateSignature(channelCode, timestamp string) string { data := channelCode + timestamp + SignSecretKey hash := md5.Sum([]byte(data)) return hex.EncodeToString(hash[:]) } // getTokenFromRedis 获取用户 token 从 Redis func getTokenFromRedis(phoneNumber string) (string, error) { //fmt.Println("enter getTokenFromRedis") //logger.Info("enter getTokenFromRedis") // 根据手机号从 Redis 获取 token token, err := storage.CacheAdapter.Get(phoneNumber) if err != nil { return "", err } //fmt.Println("leave getTokenFromRedis") //logger.Info("leave getTokenFromRedis") return token, nil } // storeTokenInRedis 设置用户 token 到 Redis func storeTokenInRedis(phoneNumber, token string) error { //fmt.Println("enter storeTokenInRedis") //logger.Info("enter storeTokenInRedis") // 设置 token 有效期为 30 分钟 expiration := time.Minute * 30 err := storage.CacheAdapter.Set(phoneNumber, token, int(expiration.Seconds())) if err != nil { return err } //fmt.Println("leave storeTokenInRedis") //logger.Info("leave storeTokenInRedis") return nil } func MonthlySubscriptionQuery(token, serviceId, channelCode string) (MonthlySubscriptionResp, error) { fmt.Println("MonthlySubscriptionQuery start") logger.Info("MonthlySubscriptionQuery start") var respData MonthlySubscriptionResp timestamp := time.Now().Format("20060102150405") signature := generateSignature(channelCode, timestamp) reqBody := MonthlySubscriptionReq{ ChannelCode: channelCode, Timestamp: timestamp, Signature: signature, Token: token, ServiceId: serviceId, } jsonData, err := json.Marshal(reqBody) if err != nil { fmt.Println("Error marshalling request body:", err) return respData, err } fmt.Println("MonthlySubscriptionQuery req:", string(jsonData)) logger.Info("MonthlySubscriptionQuery req:", string(jsonData)) client := &http.Client{} req, err := http.NewRequest("GET", MiGuSubscriptionUrl+"?data="+url.QueryEscape(string(jsonData)), nil) if err != nil { fmt.Println("Error creating HTTP request:", err) logger.Error("Error creating HTTP request:", err) return respData, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { fmt.Println("Error making HTTP request:", err) logger.Error("Error making HTTP request:", err) return respData, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) logger.Error("Error reading response body:", err) return respData, err } fmt.Println("MonthlySubscriptionQuery resp:", string(body)) logger.Info("MonthlySubscriptionQuery resp:", string(body)) err = json.Unmarshal(body, &respData) if err != nil { fmt.Println("Error unmarshalling response body:", err) logger.Error("Error unmarshalling response body:", err) return respData, err } fmt.Println("MonthlySubscriptionQuery end") logger.Info("MonthlySubscriptionQuery end") return respData, nil } type LoginRequest struct { ChannelCode string `json:"channelCode"` Timestamp string `json:"timestamp"` Signature string `json:"signature"` LoginType string `json:"loginType"` CallBackUrl string `json:"callBackUrl,omitempty"` Key string `json:"key,omitempty"` Msisdn string `json:"msisdn,omitempty"` } type LoginResponse struct { ResCode string `json:"resCode"` ResMsg string `json:"resMsg"` Token string `json:"token"` } func Login(channelCode, loginType, callBackUrl, msisdn string) (LoginResponse, error) { fmt.Println("login start") logger.Info("login start") var respData LoginResponse timestamp := time.Now().Format("20060102150405") signature := generateSignature(channelCode, timestamp) reqBody := LoginRequest{ ChannelCode: channelCode, Timestamp: timestamp, Signature: signature, LoginType: loginType, } if loginType == "1" && callBackUrl != "" { reqBody.CallBackUrl = callBackUrl } else if loginType == "3" { reqBody.Key = LoginSecretKey reqBody.Msisdn = msisdn } jsonData, err := json.Marshal(reqBody) if err != nil { fmt.Println("Error marshalling request body:", err) return respData, err } fmt.Println("login req:", string(jsonData)) logger.Info("login req:", string(jsonData)) client := &http.Client{} req, err := http.NewRequest("GET", MiGuLoginUrl+"?data="+url.QueryEscape(string(jsonData)), nil) if err != nil { fmt.Println("Error creating HTTP request:", err) logger.Error("Error creating HTTP request:", err) return respData, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { fmt.Println("Error making HTTP request:", err) logger.Error("Error making HTTP request:", err) return respData, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) logger.Error("Error reading response body:", err) return respData, err } fmt.Println("login resp:", string(body)) logger.Info("login resp:", string(body)) err = json.Unmarshal(body, &respData) if err != nil { fmt.Println("Error unmarshalling response body:", err) logger.Error("Error unmarshalling response body:", err) return respData, err } fmt.Println("login end") logger.Info("login end") return respData, nil }