migu_music_server/app/admin/models/music.go
2025-05-08 14:35:14 +08:00

561 lines
17 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 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 CallbackRequest struct {
ChannelCode string `json:"channel_code" binding:"required"` // 渠道编码例如00211NV
ExternalOrderNo string `json:"external_order_no"` // 外部平台流水号例如1909274381102424065
PhoneNumber string `json:"phone_number" binding:"required"` // 手机号例如13590011234
Province string `json:"province"` // 省份,例如:广东
ResCode int `json:"res_code"` // 交易结果1-成功0-失败
ResDesc string `json:"res_desc"` // 交易结果描述例如【OPEN】系统繁忙
}
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 []MgOrderLog
// 获取当前时间前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_log").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 ******")
}
// CheckAIMusicOrderState 定时任务检查订单状态是否有1小时退订
func CheckAIMusicOrderState() {
logger.Info("****** CheckAIMusicOrderState start ******")
fmt.Println("****** CheckAIMusicOrderState 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("****** CheckAIMusicOrderState end ******")
logger.Info("****** CheckAIMusicOrderState 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
}