package models import ( "bytes" "encoding/json" "fmt" "github.com/go-admin-team/go-admin-core/logger" "go-admin/common/database" "go-admin/tools" "gorm.io/gorm" "io" "log" "math/rand" "net/http" "net/url" "strings" "sync" "time" ) const ( MiGUSendCaptchaUrl = "https://mg.zeqinkeji.cn/coupon-provider/captcha/request" MiGUSubmitCaptchaUrl = "https://mg.zeqinkeji.cn/coupon-provider/captcha/submit" MiGUCheckOrderUrl = "https://mg.zeqinkeji.cn/coupon-provider/api/orders/exchange-type/check" MiGUQueryRightsInfoUrl = "https://betagame.migufun.com/member/shareRights/v1.1.0.7/queryRightsInfo" ProductID = 1 ChannelCode = "40458652536" SM4KEy = "ve3N1I75AJ0Oy6nA" SubscribeOK = 1 // 订阅成功 UnsubscribeOK = 2 // 退订 SKUCODE = "miguyouxizuanshihuiyuan-yy" AddRemark = "add channel" ) // 以下是数据库表结构 // MgProduct 产品管理表对应的结构体 type MgProduct struct { Model Name string `gorm:"size:255;not null" json:"name"` // 产品名称 UniqueCode string `gorm:"size:255;not null" json:"unique_code"` // 产品唯一标识编码 SkuName string `gorm:"size:255" json:"sku_name"` // SKU名称 BillingPointID int64 `json:"billing_point_id"` // 计费点ID ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码 ProductApiID string `gorm:"size:255" json:"product_api_id"` // 产品ID(接口用) ChannelApiID string `gorm:"size:255" json:"channel_api_id"` // 渠道ID(接口用) OfficialPage string `gorm:"size:255" json:"official_page"` // 官方落地页 } // MgOrder 订单管理表对应的结构体 type MgOrder struct { Model ProductID int64 `json:"product_id"` // 产品ID ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码 OrderSerial string `gorm:"size:255;not null" json:"order_serial"` // 订单流水号 SubscribeTime *time.Time `json:"subscribe_time"` // 订阅时间 PhoneNumber string `gorm:"size:20;not null" json:"phone_number"` // 手机号 SM4PhoneNumber string `gorm:"size:255" json:"sm4_phone_number"` // SM4加密手机号 ExternalOrderID string `gorm:"size:255" json:"external_order_id"` // 外部平台订单号(如咪咕等) ChannelTradeNo string `gorm:"size:255" json:"channel_trade_no"` // 渠道订单号 State int `gorm:"size:255" json:"state"` // 用户订阅状态 1-订阅成功 2-已取消订阅 UnsubscribeTime *time.Time `json:"unsubscribe_time"` // 取消订阅时间 IsOneHourCancel int `json:"is_one_hour_cancel"` // 是否1小时内退订 1-是 其他-否 } // MgChannel 渠道列表 type MgChannel struct { Model ProductID int `json:"product_id"` // 产品ID MainChannelCode string `json:"main_channel_code"` // 主渠道编码 SubChannelCode string `json:"sub_channel_code"` // 子渠道编码 SubscribeURL string `json:"subscribe_url"` // 订购成功通知接口url UnsubscribeURL string `json:"unsubscribe_url"` // 退订通知接口url Status int `json:"status"` // 状态 (0: 停用, 1: 启用) Remarks string `json:"remarks"` // 备注 } // MgHistoricalSummary 历史汇总查询表对应的结构体 type MgHistoricalSummary struct { Date string `gorm:"type:date" json:"date"` // 日期 ProductID int64 `json:"product_id"` // 产品ID ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码 SubmissionCount int `json:"submission_count"` // 提交数 NewUserCount int `json:"new_user_count"` // 新用户数 NewUserUnsubWithinHour int `json:"new_user_unsub_within_hour"` // 当日新用户退订数(1小时以内) NewUserUnsubWithinHourRate string `json:"new_user_unsub_within_hour_rate"` // 当日新用户退订率(1小时以内) NewUserUnsubOnDay int `json:"new_user_unsub_on_day"` // 当日新用户退订数 NewUserUnsubOnDayRate string `json:"new_user_unsub_on_day_rate"` // 当日新用户退订率 //Province string `gorm:"size:255" json:"province"` // 省份 } // MgRealtimeSummary 当日实时汇总表对应的结构体 type MgRealtimeSummary struct { ProductID int64 `json:"product_id"` // 产品ID ChannelCode string `json:"channel_code"` // 渠道编码 SubmissionCount int `json:"submission_count"` // 提交数 NewUserCount int `json:"new_user_count"` // 新用户数 NewUserUnsubWithinHour int `json:"new_user_unsub_within_hour"` // 当日新用户退订数(1小时以内) NewUserUnsubWithinHourRate string `json:"new_user_unsub_within_hour_rate"` // 当日新用户退订率(1小时以内) NewUserUnsubOnDay int `json:"new_user_unsub_on_day"` // 当日新用户退订数 NewUserUnsubOnDayRate string `json:"new_user_unsub_on_day_rate"` // 当日新用户退订率 //Province string `json:"province"` // 省份 } // MgTransactionLog 交易流水记录表对应的结构体 type MgTransactionLog struct { Model ProductID int64 `json:"product_id"` // 产品ID ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码 Province string `gorm:"size:255" json:"province"` // 省份 PhoneNumber string `gorm:"size:20" json:"phone_number"` // 手机号 OutTradeNo string `gorm:"size:255" json:"out_trade_no"` // 平台订单号 LinkId string `gorm:"size:255" json:"link_id"` // linkId(咪咕订单号) ChannelTradeNo string `gorm:"size:255" json:"channel_trade_no"` // 渠道订单号 Result string `gorm:"size:255" json:"result"` // 交易结果 Reason string `gorm:"size:255" json:"reason"` // 交易失败原因 VerificationCode string `gorm:"size:255" json:"verification_code"` // 验证码 OrderTime *time.Time `gorm:"type:datetime" json:"order_time"` // 订单时间 } // MgUserRetention 用户留存记录表对应的结构体 type MgUserRetention struct { RetentionMonth string `gorm:"size:7" json:"retention_month"` // 留存月份(格式:YYYY-MM) NewUserCount int `json:"new_user_count"` // 新增用户数 RetainedUserCount int `json:"retained_user_count"` // 留存用户数 ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码 ProductID int64 `json:"product_id"` // 产品ID RetentionRate string `json:"retention_rate"` // 留存率(以百分比形式存储) //Province string `gorm:"size:255" json:"province"` // 省份 } // 以下是接口出入参结构体 type Action struct { OrderChannel string `json:"orderChannel"` // 订购渠道 OrderApp string `json:"orderApp"` // 订购包名 } type SendCaptchaReq struct { Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 UserAction Action `json:"userAction"` // 用户操作记录 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type SendCaptchaReqEx struct { Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type MiGuSendCaptchaReq struct { Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 UserAction Action `json:"userAction"` // 用户操作记录 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type SendCaptchaResp struct { LinkId string `json:"linkId"` } type SubmitOrderReq struct { LinkId string `json:"linkId" binding:"required"` // 验证码接口返回的linkId SmsCode string `json:"smsCode" binding:"required"` // 验证码 Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 UserAction Action `json:"userAction"` // 用户操作记录 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type SubmitOrderReqEx struct { LinkId string `json:"linkId" binding:"required"` // 验证码接口返回的linkId SmsCode string `json:"smsCode" binding:"required"` // 验证码 Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type MiGuSubmitOrderReq struct { LinkId string `json:"linkId" binding:"required"` // 验证码接口返回的linkId SmsCode string `json:"smsCode" binding:"required"` // 验证码 Phone string `json:"phone" binding:"required"` // 手机号码 Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 UserAction Action `json:"userAction"` // 用户操作记录 OutTradeNo string `json:"outTradeNo"` // 自定义订单号 } type SubmitOrderResp struct { LinkId string `json:"linkId"` } type MiGuRsp struct { RequestId string `json:"requestId"` Code string `json:"code"` Msg string `json:"message"` Data SubmitOrderResp `json:"data"` } type MiGuCheckRsp struct { RequestId string `json:"requestId"` Code int `json:"code"` Msg string `json:"message"` Data SubmitOrderResp `json:"data"` } type CheckOrderReq struct { OutTradeNo string `json:"outTradeNo" binding:"required"` // 自定义订单号 } type CheckOrderReqEx struct { LinkId string `json:"linkId"` } type CheckOrderResp struct { RequestId string `json:"requestId"` Code string `json:"code"` //-1:已退订,0:未退订,404:订单记录不存在 Msg string `json:"message"` } // QueryRightsInfoReq 查询用户会员权益订购信息接口-入参 type QueryRightsInfoReq struct { AppChannelList []string `json:"appChannelList" binding:"required"` // 渠道号 Mobile string `json:"mobile" binding:"required"` // 手机号(sm4加密) PackageId string `json:"packageId"` // 计费点id } type QueryRightsInfoReqEx struct { Channel string `json:"channel" binding:"required"` // 渠道号 SkuCode string `json:"skuCode" binding:"required"` // 产品编号 Phone string `json:"phone" binding:"required"` // 手机号码 } // QueryRightsInfoResp 查询用户会员权益订购信息接口-出参 type QueryRightsInfoResp struct { ReturnCode string `json:"returnCode"` Message string `json:"message"` ResultData []Package `json:"resultData"` ServerTime int64 `json:"serverTime"` } type Package struct { AppChannel string `json:"appChannel"` PackageID string `json:"packageId"` PackageName string `json:"packageName"` SubTime string `json:"subTime"` ExpireTime string `json:"expireTime"` MonthlyContinuous int `json:"monthlyContinuous"` IsUnsub int `json:"isUnsub"` UnsubTime string `json:"unsubTime"` } // TransactionListReq 查询交易流水记录-入参 type TransactionListReq struct { Phone string `json:"phone"` // 手机号码 Channel string `json:"channel"` // 渠道号 SkuCode int `json:"skuCode"` // 产品编号 Result string `json:"result"` // 交易结果 StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 } // TransactionListResp 查询交易流水记录-出参 type TransactionListResp struct { List []MgTransactionLog `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // ProductListReq 查询权益产品列表-入参 type ProductListReq struct { PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 } // ProductListResp 查询权益产品列表-出参 type ProductListResp struct { List []MgProduct `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // ChannelListReq 查询渠道列表-入参 type ChannelListReq struct { PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 IsExport uint32 `json:"is_export"` // 1-导出 } // ChannelListResp 查询渠道列表-出参 type ChannelListResp struct { List []MgChannel `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // OrderListReq 查询订单列表-入参 type OrderListReq struct { StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 SkuCode int `json:"skuCode"` // 产品编号 Channel string `json:"channel"` // 渠道号 OrderSerial string `json:"order_serial"` // 订单流水号 OutTradeNo string `json:"outTradeNo"` // 外部订单号 ChannelTradeNo string `json:"channelTradeNo"` // 渠道订单号 Phone string `json:"phone"` // 手机号码 SM4PhoneNumber string `json:"sm4_phone_number"` // SM4加密手机号 State int `json:"state"` // 退订状态 0-查所有 1-未退订 2-已退订 PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 IsExport uint32 `json:"is_export"` // 1-导出 } // OrderListResp 查询订单列表-出参 type OrderListResp struct { List []MgOrder `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // HistoricalSummaryListReq 历史汇总查询-入参 type HistoricalSummaryListReq struct { StartTime string `json:"start_time"` // 开始时间 xxxx-xx-xx EndTime string `json:"end_time"` // 结束时间 xxxx-xx-xx SkuCode int `json:"skuCode"` // 产品编号 Channel string `json:"channel"` // 渠道号 PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 //Province string `json:"province"` // 省份 } // HistoricalSummaryListResp 历史汇总查询-出参 type HistoricalSummaryListResp struct { List []MgHistoricalSummary `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // RealtimeSummaryListReq 当日实时汇总查询-入参 type RealtimeSummaryListReq struct { StartTime string `json:"start_time"` // 开始时间 00:00:00 EndTime string `json:"end_time"` // 结束时间 23:59:59 SkuCode int `json:"skuCode"` // 产品编号 Channel string `json:"channel"` // 渠道号 PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 //Province string `json:"province"` // 省份 } // RealtimeSummaryListResp 当日实时汇总查询-出参 type RealtimeSummaryListResp struct { List []MgRealtimeSummary `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // UserRetentionListReq 用户留存记录查询-入参 type UserRetentionListReq struct { //Date string `json:"date"` // 月用户(格式:YYYY-MM) RetentionMonth string `json:"retention_month"` // 留存月份(格式:YYYY-MM) SkuCode string `json:"skuCode"` // 产品编号 Channel string `json:"channel"` // 渠道号 Province string `json:"province"` // 省份 PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 } // UserRetentionListResp 用户留存记录查询-出参 type UserRetentionListResp struct { List []MgUserRetention `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } // SysChannelListReq 查询渠道列表入参 type SysChannelListReq struct { PageNum int `json:"page_num"` // 页码 PageSize int `json:"page_size"` // 每页条数 } // SysChannelListResp 查询渠道列表出参 type SysChannelListResp struct { List []ChannelData `json:"list"` Count int `json:"count"` PageSize int `json:"page_size"` PageNum int `json:"page_num"` } type ChannelData struct { ChannelCode string `json:"channel_code"` // 渠道编码 } // AddProductReq 添加新产品请求结构体 type AddProductReq struct { Name string `json:"name" binding:"required"` // 产品名称 UniqueCode string `json:"unique_code" binding:"required"` // 产品唯一标识编码 SkuName string `json:"sku_name" binding:"required"` // SKU名称 BillingPointID int64 `json:"billing_point_id" binding:"required"` // 计费点ID ChannelCode string `json:"channel_code" binding:"required"` // 渠道编码 ProductApiID string `json:"product_api_id"` // 产品ID(接口用) ChannelApiID string `json:"channel_api_id" binding:"required"` // 渠道ID(接口用) OfficialPage string `json:"official_page"` // 官方落地页 } // AddProductResp 添加新产品响应结构体 type AddProductResp struct { ID uint32 `json:"id"` // 新增产品的ID } // UpdateProductReq 修改产品请求结构体 type UpdateProductReq struct { ID int64 `json:"id" binding:"required"` // 产品ID Name string `json:"name"` // 产品名称 UniqueCode string `json:"unique_code"` // 产品唯一标识编码 SkuName string `json:"sku_name"` // SKU名称 BillingPointID int64 `json:"billing_point_id"` // 计费点ID ChannelCode string `json:"channel_code"` // 渠道编码 ProductApiID string `json:"product_api_id"` // 产品ID(接口用) ChannelApiID string `json:"channel_api_id"` // 渠道ID(接口用) OfficialPage string `json:"official_page"` // 官方落地页 } // UpdateProductResp 修改产品响应结构体 type UpdateProductResp struct { ID uint32 `json:"id"` // 修改的产品ID } // DeleteProductReq 删除产品请求结构体 type DeleteProductReq struct { ID int64 `json:"id" binding:"required"` // 产品ID } // AddChannelReq 创建渠道请求结构体 type AddChannelReq struct { ProductID int `json:"product_id" binding:"required"` // 产品ID (必填) MainChannelCode string `json:"main_channel_code" binding:"required"` // 主渠道编码 (必填) SubChannelCode string `json:"sub_channel_code" binding:"required"` // 子渠道编码 (必填) SubscribeURL string `json:"subscribe_url"` // 订购成功通知接口url (非必填) UnsubscribeURL string `json:"unsubscribe_url"` // 退订通知接口url (非必填) Status int `json:"status" binding:"required"` // 状态 (2: 停用, 1: 启用) (必填) Remarks string `json:"remarks"` // 备注 (非必填) } // AddChannelResp 创建渠道响应结构体 type AddChannelResp struct { ChannelID uint32 `json:"channel_id"` // 创建的渠道ID } // UpdateChannelReq 更新渠道请求结构体 type UpdateChannelReq struct { ID int `json:"id" binding:"required"` // 渠道ID (必填) MainChannelCode string `json:"main_channel_code"` // 主渠道编码 (非必填) SubChannelCode string `json:"sub_channel_code"` // 子渠道编码 (非必填) SubscribeURL string `json:"subscribe_url"` // 订购成功通知接口url (非必填) UnsubscribeURL string `json:"unsubscribe_url"` // 退订通知接口url (非必填) Status int `json:"status"` // 状态 (0: 停用, 1: 启用) (非必填) Remarks string `json:"remarks"` // 备注 (非必填) } // UpdateChannelResp 更新渠道响应结构体 type UpdateChannelResp struct { // Any relevant response fields can go here } // DeleteChannelReq 删除渠道请求结构体 type DeleteChannelReq struct { ID int `json:"id" binding:"required"` // 渠道ID (必填) } // DeleteChannelResp 删除渠道响应结构体 type DeleteChannelResp struct { // Any relevant response fields can go here } type HomepageDataSummaryReq struct { StartTime string `json:"start_time"` // 查询开始时间 EndTime string `json:"end_time"` // 查询结束时间 SkuCode string `json:"sku_code"` // 产品编号 Channel string `json:"channel"` // 渠道名称 } type DailyData struct { Date string `json:"date"` // 日期 NewUserCount int64 `json:"new_user_count"` // 新增用户数 UnsubscribedUserCount int64 `json:"unsubscribed_user_count"` // 退订用户数 UnsubscribedWithinOneHour int64 `json:"unsubscribed_within_one_hour"` // 1小时内退订用户数 UnsubscribeRate string `json:"unsubscribe_rate"` // 退订率 UnsubscribeWithinOneHourRate string `json:"unsubscribe_within_one_hour_rate"` // 1小时内退订率 } type HomepageDataSummaryResp struct { Summary SummaryData `json:"summary"` // 汇总数据 DailyDataList []DailyData `json:"daily_data"` // 每天的数据列表 } // SummaryData represents the overall summary data type SummaryData struct { TotalUserCount int `json:"total_user_count"` // 总用户数 UnsubscribedUserCount int `json:"unsubscribed_user_count"` // 退订用户数 OneHourUnsubscribedUserCount int `json:"one_hour_unsubscribed_user_count"` // 1小时内退订用户数 RetainedUserCount int `json:"retained_user_count"` // 留存用户数 UnsubscribeRate string `json:"unsubscribe_rate"` // 退订率 (字符串,保留两位小数,如 "26.35%") OneHourUnsubscribeRate string `json:"one_hour_unsubscribe_rate"` // 1小时退订率 (字符串,保留两位小数,如 "26.35%") RetentionRate string `json:"retention_rate"` // 留存率 (字符串,保留两位小数,如 "73.65%") } // MiGuCaptchaRequest 调用下单接口(森越转发) func MiGuCaptchaRequest(r *MiGuSendCaptchaReq) (MiGuRsp, error) { var miGuResp MiGuRsp data, err := json.Marshal(r) if err != nil { logger.Error("MiGuCaptchaRequest err:", err) return miGuResp, err } fmt.Println("data json:", string(data)) client := http.Client{} req, err := http.NewRequest("POST", MiGUSendCaptchaUrl, bytes.NewBuffer(data)) if err != nil { logger.Error("MiGuCaptchaRequest err:", err) return miGuResp, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { logger.Error("MiGuCaptchaRequest err:", err) return miGuResp, err } body, err := io.ReadAll(resp.Body) if err != nil { logger.Error("MiGuCaptchaRequest err:", err) return miGuResp, err } fmt.Println("body:", string(body)) defer resp.Body.Close() err = json.Unmarshal(body, &miGuResp) if err != nil { logger.Error("MiGuCaptchaRequest err:", err) return miGuResp, err } return miGuResp, nil } // MiGuCaptchaSubmit 调用提交接口(森越转发) func MiGuCaptchaSubmit(r *MiGuSubmitOrderReq) (MiGuRsp, error) { var miGuResp MiGuRsp data, err := json.Marshal(r) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } fmt.Println("data json:", string(data)) client := http.Client{} req, err := http.NewRequest("POST", MiGUSubmitCaptchaUrl, bytes.NewBuffer(data)) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } body, err := io.ReadAll(resp.Body) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } fmt.Println("body:", string(body)) defer resp.Body.Close() err = json.Unmarshal(body, &miGuResp) if err != nil { logger.Error("clueResp err:", err) return miGuResp, err } return miGuResp, nil } // MiGuCheckOrder 查询是否已经退订接口(森越转发) func MiGuCheckOrder(r *CheckOrderReq) (MiGuCheckRsp, error) { var miGuResp MiGuCheckRsp data, err := json.Marshal(r) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } fmt.Println("data json:", string(data)) client := http.Client{} req, err := http.NewRequest("POST", MiGUCheckOrderUrl, bytes.NewBuffer(data)) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } body, err := io.ReadAll(resp.Body) if err != nil { logger.Error("oppoSendData err:", err) return miGuResp, err } fmt.Println("body:", string(body)) defer resp.Body.Close() err = json.Unmarshal(body, &miGuResp) if err != nil { logger.Error("clueResp err:", err) return miGuResp, err } return miGuResp, nil } // MiGuQueryRightsInfo 查询用户会员权益订购信息接口(咪咕接口) func MiGuQueryRightsInfo(r *QueryRightsInfoReq) (QueryRightsInfoResp, error) { var miGuResp QueryRightsInfoResp sm4Phone, err := tools.SM4Encrypt(SM4KEy, r.Mobile) if err != nil { logger.Error("SM4Encrypt err:", err) return miGuResp, err } r.Mobile = sm4Phone data, err := json.Marshal(r) if err != nil { logger.Error("MiGuQueryRightsInfo json.Marshal err:", err) return miGuResp, err } fmt.Println("data json:", string(data)) client := http.Client{} req, err := http.NewRequest("POST", MiGUQueryRightsInfoUrl, bytes.NewBuffer(data)) if err != nil { logger.Error("MiGuQueryRightsInfo err:", err) return miGuResp, err } req.Header.Set("Content-Type", "application/json; charset=utf-8") resp, err := client.Do(req) if err != nil { logger.Error("MiGuQueryRightsInfo err:", err) return miGuResp, err } body, err := io.ReadAll(resp.Body) if err != nil { logger.Error("MiGuQueryRightsInfo err:", err) return miGuResp, err } fmt.Println("body:", string(body)) defer resp.Body.Close() err = json.Unmarshal(body, &miGuResp) if err != nil { logger.Error("miGuResp err:", err) return miGuResp, err } return miGuResp, nil } // NoticeSubChannel 回调通知接口 (GET 请求,返回string类型,响应头为text/plain) func NoticeSubChannel(baseUrl, linkId, extData, status, mainChannelCode, remarks string) (string, error) { // 构建 GET 请求的 URL requestUrl, err := url.Parse(baseUrl) if err != nil { return "", fmt.Errorf("failed to parse baseUrl: %v", err) } // 添加查询参数 query := requestUrl.Query() query.Set("orderId", linkId) query.Set("extData", extData) query.Set("status", status) if strings.Contains(remarks, AddRemark) { query.Set("channel", mainChannelCode) } requestUrl.RawQuery = query.Encode() fmt.Println("NoticeSubChannel url:", requestUrl.String()) // 发送 GET 请求 resp, err := http.Get(requestUrl.String()) if err != nil { return "", fmt.Errorf("failed to send GET request: %v", err) } defer resp.Body.Close() // 检查响应头是否为text/plain //contentType := resp.Header.Get("Content-Type") //if contentType != "text/plain; charset=utf-8" { // return "", fmt.Errorf("unexpected content type: %s", contentType) //} // 读取响应体内容 body, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response body: %v", err) } // 将响应体转换为string responseString := string(body) // 打印结果 fmt.Println("Notification response:", responseString) return responseString, nil } var mu sync.Mutex // GetOrderSerial generates a unique inventory serial number func GetOrderSerial(db *gorm.DB) string { const maxRetries = 5 mu.Lock() defer mu.Unlock() for retryCount := 0; retryCount < maxRetries; retryCount++ { nowTime := time.Now() // 使用日期格式精确到天 datePart := nowTime.Format("060102") // 格式为 YYMMDD // 生成13位随机数 rand.Seed(nowTime.UnixNano() + int64(retryCount)) // 为了确保每次生成不同的随机数 randomNum := rand.Int63n(1e13) // 10位随机数,范围从0到9999999999999 randomPart := fmt.Sprintf("%013d", randomNum) // 确保随机数是13位的,前面补零 // 拼接日期部分和随机数部分 sn := fmt.Sprintf("%s%s", datePart, randomPart) exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM mg_transaction_log WHERE out_trade_no='%s'", sn), db) if err != nil { logger.Error("sn err:", err) continue } if !exist { return sn } } return "" // 返回空字符串,如果在最大重试次数后仍未找到唯一编号 } // IsValidChannel 判断渠道号是否为 12, 13, 14 func IsValidChannel(channel string) bool { return channel == "0012" || channel == "0013" || channel == "0014" } // IsValidChannelEx 判断渠道号是否为子渠道 func IsValidChannelEx(channel string, db *gorm.DB) bool { exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM mg_channel WHERE sub_channel_code='%s'", channel), db) if err != nil { return false } return exist } // IsValidSkuCode 判断skuCode是否有效 func IsValidSkuCode(skuCode string) bool { return skuCode == SKUCODE } // IsValidSkuCodeEx 判断skuCode是否有效 func IsValidSkuCodeEx(skuCode string, db *gorm.DB) bool { exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM mg_product WHERE unique_code='%s'", skuCode), db) if err != nil { return false } return exist } // IsWithinOneHour 判断订阅时间是否在1小时之内 func IsWithinOneHour(order MgOrder) bool { if order.SubscribeTime == nil { return false } // 获取当前时间 now := time.Now() // 计算当前时间与订阅时间的差值 diff := now.Sub(*order.SubscribeTime) // 判断是否在1小时以内 return diff <= time.Hour } // IsWithinOneHourCancel 判断订阅时间是否在取消订阅时间的1小时之内 func IsWithinOneHourCancel(subscribeTime time.Time, unsubscribeTime string) bool { // 将 unsubscribeTime(string)转换为 time.Time 类型 unsubTime, err := time.ParseInLocation("2006-01-02 15:04:05", unsubscribeTime, subscribeTime.Location()) if err != nil { fmt.Println("Error parsing UnsubscribeTime:", err) return false } // 计算取消订阅时间与订阅时间的差值 diff := unsubTime.Sub(subscribeTime) // 判断是否在1小时以内,且取消时间在订阅时间之后 return diff <= time.Hour && diff >= 0 } // ParseYearMonth 解析年月格式字符串,返回年和月 func ParseYearMonth(retentionMonth string) (int, time.Month, error) { var year int var month int _, err := fmt.Sscanf(retentionMonth, "%d-%d", &year, &month) if err != nil { return 0, 0, err } return year, time.Month(month), nil } // GetMainChannelCodeAndSkuCode 通过子渠道编号和sku查询主渠道编号及其sku func GetMainChannelCodeAndSkuCode(channel, skuCode string, db *gorm.DB) (mainChannel MgChannel, mainSkuCode MgProduct, err error) { // 查询是否有记录 var channelInfo MgChannel err = db.Table("mg_channel").Where("sub_channel_code = ?", channel).First(&channelInfo).Error if err != nil { logger.Errorf("SubmitOrder query mg_transaction_log err:", err.Error()) return MgChannel{}, MgProduct{}, err } var productInfo MgProduct err = db.Table("mg_product").Where("unique_code = ?", skuCode).First(&productInfo).Error if err != nil { logger.Errorf("SubmitOrder query mg_transaction_log err:", err.Error()) return MgChannel{}, MgProduct{}, err } return channelInfo, productInfo, nil } // GetChannelInfoByChannelCode 通过渠道编号查询渠道信息 func GetChannelInfoByChannelCode(channel string, db *gorm.DB) (MgChannel, error) { // 查询是否有记录 var channelInfo MgChannel err := db.Table("mg_channel").Where("sub_channel_code = ?", channel).First(&channelInfo).Error if err != nil { logger.Errorf("SubmitOrder query mg_transaction_log err:", err.Error()) return MgChannel{}, err } return channelInfo, nil } // CheckOrderState 定时任务,检查订单状态,是否有1小时退订 func CheckOrderState() { if database.Db == nil { log.Println("Database connection is nil") fmt.Println("Database connection is nil") return } // 查询订单列表中未退订的用户,查询其是否退订;如果退订,则更新退订时间,判断是否为1小时内退订 var orderList []MgOrder // 获取当前时间前2个小时 oneHourAgo := time.Now().Add(-2 * time.Hour) err := database.Db.Where("is_one_hour_cancel != 1"). Where("created_at >= ?", oneHourAgo). Order("created_at desc"). Find(&orderList).Error if err != nil { fmt.Println("query mg_order err:", err.Error()) return } for i, _ := range orderList { for j := 0; j < 5; j++ { var req QueryRightsInfoReq req.AppChannelList = append(req.AppChannelList, ChannelCode) req.Mobile = orderList[i].PhoneNumber resp, err := MiGuQueryRightsInfo(&req) if err != nil { fmt.Println("CheckOrderState MiGuQueryRightsInfo err:", err.Error()) logger.Errorf("CheckOrderState MiGuQueryRightsInfo err:", err.Error()) continue } // 有退订数据 if len(resp.ResultData) != 0 { if resp.ResultData[0].IsUnsub == 1 { // 已退订 var cancelFlag int subscribeTime := orderList[i].CreatedAt // 检查 subscribeTime 是否为 nil if IsWithinOneHourCancel(subscribeTime, resp.ResultData[0].UnsubTime) { cancelFlag = 1 } err = database.Db.Table("mg_order").Where("order_serial = ?", orderList[i].OrderSerial).Updates(map[string]interface{}{ "state": UnsubscribeOK, "is_one_hour_cancel": cancelFlag, "unsubscribe_time": resp.ResultData[0].UnsubTime, "updated_at": time.Now(), }).Error if err != nil { fmt.Println("CheckOrderState update mg_order err:", err.Error()) logger.Errorf("CheckOrderState update mg_order err:", err.Error()) logger.Errorf("CheckOrderState order_serial:", orderList[i].OrderSerial) continue } break } else if resp.ResultData[0].IsUnsub == 0 { // 没有退订 break } } else { if j == 4 { var cancelFlag int subscribeTime := orderList[i].CreatedAt unsubTime := time.Now().Format("2006-01-02 15:04:05") // 检查 subscribeTime 是否为 nil if IsWithinOneHourCancel(subscribeTime, unsubTime) { cancelFlag = 1 } err = database.Db.Table("mg_order").Where("order_serial = ?", orderList[i].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("CheckOrderState update mg_order err:", err.Error()) logger.Errorf("CheckOrderState update mg_order err:", err.Error()) logger.Errorf("CheckOrderState order_serial:", orderList[i].OrderSerial) continue } break } continue } } } }