package user_m import ( "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mylogrus" "git.hilo.cn/hilo-common/resource/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" "hilo-group/_const/enum/user_e" "hilo-group/myerr" "time" ) //用户Vip type UserVip struct { mysql.Entity *domain.Model `gorm:"-"` UserId mysql.ID ExpireAt time.Time //结束时间 Type user_e.UserVipType //来源类型 Platform mysql.Platform VipSubscribeOrderId mysql.ID //最后的订单ID } // 检查某用户是否Vip func (user *User) CheckVip() (bool, error) { rows := make([]UserVip, 0) err := user.Model.Db.Where("user_id = ? AND expire_at >= NOW()", user.ID).Find(&rows).Error if err != nil { return false, err } if len(rows) > 0 { return true, nil } return false, nil } // 检查某用户是否Vip func IsVip(userId uint64) (bool, *int64, error) { uv, err := GetVip(mysql.Db, userId) if err != nil { return false, nil, err } if uv == nil { return false, nil, nil } ts := uv.ExpireAt.Unix() return true, &ts, nil } func GetVip(db *gorm.DB, userId uint64) (*UserVip, error) { rows := make([]UserVip, 0) err := db.Where("user_id = ? AND expire_at >= NOW()", userId).Find(&rows).Error if err != nil { return nil, err } if len(rows) > 0 { return &rows[0], nil } return nil, nil } // 分批获取 func BatchGetVips(userIds []uint64) (map[uint64]*int64, error) { result := make(map[uint64]*int64, 0) rows := make([]UserVip, 0) end := 500 if end > len(userIds) { end = len(userIds) } start := 0 for end <= len(userIds) { if end > len(userIds) { end = len(userIds) } tmp := make([]UserVip, 0) err := mysql.Db.Where("user_id IN ?", userIds[start:end]).Find(&tmp).Error if err != nil { return nil, err } if err != nil { return result, err } else { rows = append(rows, tmp...) } start += 500 end += 500 mylogrus.MyLog.Infof("BatchGetVips start:%v-end:%v", start, end) } for _, i := range userIds { result[i] = nil } now := time.Now() for _, i := range rows { if i.ExpireAt.After(now) { ts := i.ExpireAt.Unix() result[i.UserId] = &ts } } return result, nil } // 查询当前有效的vips(google) func GetValidVipsGoogle(db *gorm.DB, userId uint64) (*UserVip, error) { rows := make([]UserVip, 0) err := db.Table("user_vip AS u"). Joins("INNER JOIN vip_subscribe_order_id AS s ON u.vip_subscribe_order_id = s.id"). Where("platform = ? and expire_at >= NOW()", user_e.Google).Find(&rows).Error if err != nil { return nil, err } if len(rows) > 0 { return &rows[0], nil } return nil, nil } //初始化用户Vip, 存在则抛出错误 func InitUserVip(model *domain.Model, userId uint64) (*UserVip, error) { var n int64 if err := model.Db.Model(&UserVip{}).Where(&UserVip{ UserId: userId, }).Count(&n).Error; err != nil { return nil, myerr.WrapErr(err) } if n > 0 { return nil, myerr.NewSysErrorF("该用户已经拥有/曾经拥有vip") } return &UserVip{ Model: model, UserId: userId, }, nil } //获取UserVip 不存在则抛出错误 func GetUserVip(model *domain.Model, userId uint64) (*UserVip, error) { userVip := UserVip{} if err := model.Db.Where(&UserVip{ UserId: userId, }).First(&userVip).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, myerr.NewSysErrorF("userVip userId:%v 不存在", userId) } else { return nil, myerr.WrapErr(err) } } userVip.Model = model return &userVip, nil } func GetUserVipNil(model *domain.Model, userId uint64) (*UserVip, error) { userVip := UserVip{} if err := model.Db.Where(&UserVip{ UserId: userId, }).First(&userVip).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil } else { return nil, myerr.WrapErr(err) } } userVip.Model = model return &userVip, nil } //设置userVip来源于管理者 func (userVip *UserVip) SetOriginByMgr(expireAt time.Time) *UserVip { userVip.Type = user_e.UserVipTypeGive userVip.Platform = 0 userVip.VipSubscribeOrderId = 0 userVip.ExpireAt = expireAt return userVip } //设置userVip来源于管理人 func (userVip *UserVip) AddDaysByMgr(days uint64) *UserVip { userVip.Type = user_e.UserVipTypeGive userVip.Platform = 0 userVip.VipSubscribeOrderId = 0 userVip.ExpireAt = userVip.ExpireAt.AddDate(0, 0, int(days)) return userVip } //删除userVip来源于管理人,支付购买的,无法删除 func (userVip *UserVip) DelByMgr() (*UserVip, error) { if userVip.Type == user_e.UserVipTypeBuy { return nil, myerr.NewSysError("真金白银购买的VIP不能删除") } else { if userVip.ExpireAt.After(time.Now()) { userVip.ExpireAt = time.Now() } } return userVip, nil } func MakeVip(db *gorm.DB, uvType user_e.UserVipType, userId uint64, expiredTime time.Time, subscribeOrderId uint64, platform mysql.Platform) error { uv := UserVip{ UserId: userId, ExpireAt: expiredTime, Type: uvType, Platform: platform, VipSubscribeOrderId: subscribeOrderId, } return db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.Assignments(map[string]interface{}{ "type": uvType, "platform": platform, // TODO:如何处理切换平台订阅的问题? "expire_at": expiredTime, "vip_subscribe_order_id": subscribeOrderId}), }).Create(&uv).Error } func ExtendVip(db *gorm.DB, userId uint64, expireAt time.Time) error { return db.Model(&UserVip{}).Where("user_id = ?", userId).Update("expire_at", expireAt).Error } // 检查一批用户中有没有一个是Vip func CheckVipByUserIds(userIds []uint64) (bool, error) { rows := make([]UserVip, 0) err := mysql.Db.Where("user_id IN ?", userIds).Find(&rows).Error if err != nil { return false, err } now := time.Now() for _, i := range rows { if i.ExpireAt.After(now) { return true, nil } } return false, nil } type VipSubscribeApple struct { mysql.Entity *domain.Model `gorm:"-"` UserId mysql.ID Receipt mysql.Str TransactionId mysql.Str OriginalTransactionId mysql.Str } func (v *VipSubscribeApple) Get() (*VipSubscribeApple, error) { rows := make([]VipSubscribeApple, 0) err := mysql.Db.Where(v).Find(&rows).Error if err != nil { return nil, err } if len(rows) <= 0 { return nil, nil } return &rows[0], nil } // 添加ios支付凭证 // 1. 检查旧订单(同origin_transaction_id) // 2. 处理完全相同的transaction_id订单,更新对应的user_id // 3. 若无相同transaction_id,则插入新的记录 // return: vip_subscribe_apple的唯一id, 旧的单子, error func (v *VipSubscribeApple) Add(model *domain.Model) (uint64, []VipSubscribeApple, error) { // 检查,订单是否已经存在 // 订阅的单子,需要看original_transaction_id判断 var olds []VipSubscribeApple if err := model.Db.Model(&VipSubscribeApple{}).Where(&VipSubscribeApple{ //TransactionId: v.TransactionId, OriginalTransactionId: v.OriginalTransactionId, }).Find(&olds).Error; err != nil { return 0, nil, myerr.WrapErr(err) } for _, old := range olds { // 完全是拿旧的transactionId if old.TransactionId == v.TransactionId { if err := model.Db.Model(&VipSubscribeApple{}).Where("transaction_id = ?", v.TransactionId).Update("user_id", v.UserId).Error; err != nil { return 0, nil, err } else { return old.ID, olds, nil } } } if err := model.Db.Model(&VipSubscribeApple{}).Create(&v).Error; err != nil { return 0, nil, myerr.WrapErr(err) } return v.ID, olds, nil } type VipSubscribeGoogle struct { mysql.Entity *domain.Model `gorm:"-"` UserId mysql.ID PackageName mysql.Str SubscriptionID mysql.Str PurchaseToken mysql.Str } func GetGoogleSubscribe(model *domain.Model, packageName, subscriptionID, purchaseToken mysql.Str) (*VipSubscribeGoogle, error) { q := VipSubscribeGoogle{ Model: model, PackageName: packageName, SubscriptionID: subscriptionID, PurchaseToken: purchaseToken} return q.Get() } func BatchGetGoogleSubscribe(db *gorm.DB, ids []uint64) ([]VipSubscribeGoogle, error) { rows := make([]VipSubscribeGoogle, 0) err := db.Model(&VipSubscribeGoogle{}).Where("id IN ?", ids).Find(&rows).Error if err != nil { return nil, err } return rows, nil } func (v *VipSubscribeGoogle) Get() (*VipSubscribeGoogle, error) { rows := make([]VipSubscribeGoogle, 0) err := v.Db.Where(v).Find(&rows).Error if err != nil { return nil, err } if len(rows) <= 0 { return nil, nil } return &rows[0], nil } func (v *VipSubscribeGoogle) Add() (uint64, error) { err := v.Db.Model(&VipSubscribeGoogle{}).Create(&v).Error if err == nil { return v.ID, nil } return 0, err } type GoogleSubscribeState struct { mysql.Entity *domain.Model `gorm:"-"` PurchaseToken mysql.Str OrderId mysql.Str CountryCode mysql.Str PriceAmountMicros int64 PriceCurrencyCode mysql.Str StartTimeMillis int64 ExpiryTimeMillis int64 AutoRenewing bool LinkedPurchaseToken mysql.Str PaymentState int64 CancelReason int64 UserCancellationTimeMillis int64 } func (gss *GoogleSubscribeState) Set() (uint64, error) { err := gss.Db.Clauses(clause.OnConflict{ UpdateAll: true, }).Create(gss).Error if err == nil { return gss.ID, nil } return 0, err }