package cp_m import ( "fmt" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/resource/mysql" "git.hilo.cn/hilo-common/rpc" "git.hilo.cn/hilo-common/txop/headwear_tx" "gorm.io/gorm" "gorm.io/gorm/clause" "hilo-user/_const/enum/cp_e" "hilo-user/_const/enum/gift_e" "hilo-user/domain/model/user_m" "hilo-user/myerr" "time" ) // cp等级 type CpLevel struct { mysql.Entity CpId mysql.ID UserId1 mysql.ID UserId2 mysql.ID Points mysql.Num Level cp_e.CpLevel ExpireAt time.Time } // cp等级积分明细 type CpLevelDetail struct { mysql.Entity CpId mysql.ID UserId1 mysql.ID UserId2 mysql.ID AddReduce mysql.AddReduce Num mysql.Num BefNum mysql.Num AftNum mysql.Num Remark string } // 获取cp等级 func GetCpLevel(model *domain.Model, cpId mysql.ID) CpLevel { var level CpLevel if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpId).First(&level).Error; err != nil { model.Log.Errorf("GetCpLevel fail:%v", err) } return level } // 批量获取cp等级 func MGetCpLevel(model *domain.Model, cpIds []mysql.ID) map[mysql.ID]CpLevel { var res = make(map[mysql.ID]CpLevel) var level []CpLevel if err := model.DB().Model(CpLevel{}).Where("cp_id in ?", cpIds).Find(&level).Error; err != nil { model.Log.Errorf("MGetCpLevel fail:%v", err) } for i, v := range level { res[v.CpId] = level[i] } return res } // 添加cp等级积分增减明细 func AddCpLevelDetail(model *domain.Model, detail CpLevelDetail) error { return model.DB().Create(&detail).Error } // 获取cpRelation func GetCpRelation(model *domain.Model, userId mysql.ID) (cpRelation CpRelation, exits bool) { if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? or user_id2 = ?", userId, userId).First(&cpRelation).Error; err != nil { if err != gorm.ErrRecordNotFound { model.Log.Errorf("GetCpRelation fail:%v", err) return } } else { exits = true } return } // 获取cpRelation pair func GetCpRelationPair(model *domain.Model, userId1, userId2 mysql.ID) (cpRelation CpRelation, exits bool) { if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId1, userId2).First(&cpRelation).Error; err != nil { if err != gorm.ErrRecordNotFound { model.Log.Errorf("GetCpRelation fail:%v", err) return } else { // gorm.ErrRecordNotFound if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId2, userId1).First(&cpRelation).Error; err != nil { if err != gorm.ErrRecordNotFound { model.Log.Errorf("GetCpRelation fail:%v", err) return } } else { exits = true } } } else { exits = true } return } // 获取cpRelation pair func MGetCpRelation(model *domain.Model, userIds []mysql.ID) (cpRelation []CpRelation) { if err := model.DB().Model(CpRelation{}).Where("user_id1 in ? or user_id2 in ?", userIds, userIds).Find(&cpRelation).Error; err != nil { model.Log.Errorf("GetCpRelation fail:%v", err) } return } // 获取是否申请解绑中 func GetApplyToUnbind(model *domain.Model, userId, cpUserId mysql.ID) bool { var total int64 if err := model.DB().Model(CpCancel{}).Where("user_id = ? AND rec_user_id = ? AND status = 1", userId, cpUserId).Count(&total).Error; err != nil { model.Log.Errorf("GetApplyToUnbind fail:%v", err) } return total > 0 } // 初始化cpLevel func InitCpLevel(model *domain.Model, cpId, userId1, userId2 mysql.ID) error { return model.DB().Model(CpLevel{}).Clauses(clause.OnConflict{DoNothing: true}).Create(&CpLevel{ CpId: cpId, UserId1: userId1, UserId2: userId2, ExpireAt: time.Now().AddDate(0, 1, 0), }).Error } // 增加cp等级积分 // 此函数并发不安全,利用mysql事件串行执行保证顺序 // 送礼1钻石=1点数 // condition // 1.记录不存在,首充加points计算level增加90天有效期 // 2.记录存在 // 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期 // 2.2 不有效期内,算首充,重置points后判断新level,升级需要更新有效期 func AddCpLevelPoints(model *domain.Model, cpRelation CpRelation, points mysql.Num, sceneType gift_e.GiftOperateSceneType, sceneUid string) (err error) { start := time.Now() defer func() { model.Log.Infof("AddCpLevelPoints cost:%v,err:%v", time.Now().Sub(start), err) }() var oldLevel cp_e.CpLevel var cpLevel CpLevel var cpLevelDetails []CpLevelDetail if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpRelation.Id).First(&cpLevel).Error; err != nil { if err != gorm.ErrRecordNotFound { return myerr.WrapErr(err) } // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.ADD, Num: points, BefNum: 0, AftNum: points, Remark: fmt.Sprintf("send %d gift diamonds", points), }) // 1.记录不存在,首充加points计算level增加90天有效期 var level cp_e.CpLevel for l := cp_e.CpLevelMax; l >= cp_e.CpLevel0; l-- { if cp_e.CpLevelPoints[l] <= points { level = l break } } if level > 0 { points = points - cp_e.CpLevelPoints[level] // 减去用于已用于升级的积分 // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.REDUCE, Num: cp_e.CpLevelPoints[level], BefNum: cp_e.CpLevelPoints[level] + points, AftNum: points, Remark: fmt.Sprintf("Become LEVEL%d", level), }) } cpLevel = CpLevel{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, Points: points, Level: level, ExpireAt: time.Now().AddDate(0, 0, cp_e.EffectDays), } } else { // 2.记录存在 // 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期 if cpLevel.ExpireAt.After(time.Now()) { cpLevel.Points += points // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.ADD, Num: points, BefNum: cpLevel.Points - points, AftNum: cpLevel.Points, Remark: fmt.Sprintf("send %d gift diamonds", points), }) oldLevel = cpLevel.Level levelPoint := cp_e.CpLevelPoints[oldLevel] // 已经用于升级的积分 for level := cp_e.CpLevelMax; level > oldLevel; level-- { if cp_e.CpLevelPoints[level] <= cpLevel.Points+levelPoint { cpLevel.Level = level break } } // 升级 if oldLevel != cpLevel.Level { // 减去已用于升级的积分 cpLevel.Points = cpLevel.Points - (cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel]) cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.REDUCE, Num: cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel], BefNum: cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel], AftNum: cpLevel.Points, Remark: fmt.Sprintf("Become LEVEL%d", cpLevel.Level), }) } } else { // 2.2 不有效期内,算首充,重置points后判断新level,更新有效期30天 oldPoints := cpLevel.Points cpLevel.Points = points // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.ADD, Num: points, BefNum: oldPoints, AftNum: cpLevel.Points, Remark: fmt.Sprintf("send %d gift diamonds", points), }) for level := cp_e.CpLevelMax; level >= cp_e.CpLevel0; level-- { if cp_e.CpLevelPoints[level] <= cpLevel.Points { cpLevel.Level = level break } } if cpLevel.Level > 0 { cpLevel.Points -= cp_e.CpLevelPoints[cpLevel.Level] // 减去已用于升级的积分 // 明细 cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ CpId: cpRelation.Id, UserId1: cpRelation.UserId1, UserId2: cpRelation.UserId2, AddReduce: mysql.REDUCE, Num: cp_e.CpLevelPoints[cpLevel.Level], BefNum: cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level], AftNum: cpLevel.Points, Remark: fmt.Sprintf("Become SVIP%d", cpLevel.Level), }) } cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) } } // 顺序增加明细 for _, detail := range cpLevelDetails { if err := AddCpLevelDetail(model, detail); err != nil { return myerr.WrapErr(err) } } // 赠送cp头像头饰 if oldLevel < 3 && cpLevel.Level >= 3 { if err := headwear_tx.SendHeadwear(model, cpRelation.UserId1, cp_e.CpHeadwearId, 30); err != nil { return err } if err := headwear_tx.SendHeadwear(model, cpRelation.UserId2, cp_e.CpHeadwearId, 30); err != nil { return err } } // 群组中送礼升级 if oldLevel != cpLevel.Level && sceneType == gift_e.GroupSceneType { go func() { userId1, userId2 := cpRelation.UserId1, cpRelation.UserId2 model := domain.CreateModelContext(model.MyContext) users, err := user_m.GetUserMapByIds(model, []mysql.ID{userId1, userId2}) if err != nil { model.Log.Errorf("") return } if err := rpc.SendCpUpgrade(users[userId1].Nick, users[userId2].Nick, users[userId1].Avatar, users[userId2].Avatar, uint32(cpLevel.Level), sceneUid); err != nil { model.Log.Errorf("SendCpUpgrade fail:%v", err) } }() } return cpLevel.Persistence(model) } // 清理过期svip积分 // 降级保级: 积分清零,svip去到大于0的等级,有效期30天 // svip0:积分清零,有效期保持过期 func ClearExpireCpPoints(model *domain.Model) error { var cpLevels []*CpLevel // 过期 + (积分 or level) 大于0 if err := model.DB().Table("cp_level").Joins("INNER JOIN cp_relation r ON cp_id = r.id").Where("expire_at < ? AND (points > 0 or level > 0) ", time.Now()).Find(&cpLevels).Error; err != nil { return myerr.WrapErr(err) } for _, cpLevel := range cpLevels { model.Log.Infof("ClearExpireCpPoints %v", *cpLevel) var doubleCheck CpLevel if err := model.DB().Model(CpLevel{}).Where("id = ?", cpLevel.ID).First(&doubleCheck).Error; err != nil { model.Log.Errorf("double check fail:%v", err) continue } if doubleCheck.ExpireAt.After(time.Now()) { continue } oldPoints := cpLevel.Points cpLevel.Level, cpLevel.Points = cp_e.CpLevel0, 0 // 清零 var newLevel cp_e.CpLevel // 0级不刷新30天有效期 for level := cp_e.CpLevelMax; level > cp_e.CpLevel0; level-- { if cp_e.CpLevelPoints[level] <= oldPoints { newLevel = level break } } // 降级/保级刷新30天有效期 if newLevel > 0 { cpLevel.Level = newLevel cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) // 明细 if err := AddCpLevelDetail(model, CpLevelDetail{ CpId: cpLevel.CpId, UserId1: cpLevel.UserId1, UserId2: cpLevel.UserId2, AddReduce: mysql.REDUCE, Num: cp_e.CpLevelPoints[newLevel], BefNum: oldPoints, AftNum: 0, Remark: fmt.Sprintf("Become LEVEL%d", newLevel), }); err != nil { model.Log.Errorf("AddCpLevelDetail fail:%v", err) } } if err := cpLevel.Persistence(model); err != nil { model.Log.Errorf("cpLevel persistence fail:%v", err) } // 明细 if err := AddCpLevelDetail(model, CpLevelDetail{ CpId: cpLevel.CpId, UserId1: cpLevel.UserId1, UserId2: cpLevel.UserId2, AddReduce: mysql.SET, Num: oldPoints - cp_e.CpLevelPoints[newLevel], BefNum: oldPoints - cp_e.CpLevelPoints[newLevel], AftNum: 0, Remark: fmt.Sprintf("Expired clear"), }); err != nil { model.Log.Errorf("AddCpLevelDetail fail:%v", err) } // 保级续送cp头像头饰 if newLevel >= 3 { if err := headwear_tx.SendHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId, 30); err != nil { model.Log.Errorf("SendHeadwear fail:%v", err) } if err := headwear_tx.SendHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId, 30); err != nil { model.Log.Errorf("SendHeadwear fail:%v", err) } } else { // 否则删除cp头像头饰 if err := headwear_tx.DelHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId); err != nil { model.Log.Errorf("DelHeadwear fail:%v", err) } if err := headwear_tx.DelHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId); err != nil { model.Log.Errorf("DelHeadwear fail:%v", err) } } } return nil }