Commit 1c378bf6 authored by hujiebin's avatar hujiebin

feat:继续删除一下

parent 9b476f77
package user_k
import (
"fmt"
)
const (
GameAdd = "user:add:%s"
GameLock = "user:lock:%s"
)
func GetGameAddKey(txGroupId string) string {
return fmt.Sprintf(GameAdd, txGroupId)
}
package robot_k
import "fmt"
const (
RobotGroupCd = "robot:group:cd:%s"
)
// 机器人上群cd
func GetRobotGroupCdKey(txGroupId string) string {
return fmt.Sprintf(RobotGroupCd, txGroupId)
}
package diamond_m
import (
"hilo-user/_const/enum/diamond_e"
"hilo-user/domain"
"hilo-user/myerr"
"hilo-user/myerr/bizerr"
"hilo-user/resource/mysql"
"strconv"
"time"
)
type DiamondAccount struct {
mysql.Entity
*domain.Model `gorm:"-"`
UserId mysql.ID
DiamondNum mysql.Num
PinkDiamondNum mysql.Num
Status diamond_e.StatusAccount
}
//账号详情
type DiamondAccountDetail struct {
mysql.Entity
*domain.Model `gorm:"-"`
UserId mysql.ID
DiamondAccountId mysql.ID
OperateId mysql.ID
OperateType diamond_e.OperateType
OriginId mysql.ID
AddReduce mysql.AddReduce
Num mysql.Num
Remark mysql.Str
BefNum mysql.Num
AftNum mysql.Num
diamondAccount *DiamondAccount `gorm:"-"`
}
//账号操作配置
type DiamondOperateSet struct {
mysql.Entity
*domain.Model `gorm:"-"`
DiamondNum mysql.NumAll
FrequencyNum mysql.NumAll
FrequencyDay mysql.NumAll
DiamondMaxNum mysql.NumAll
AddReduce mysql.AddReduce
Type diamond_e.OperateType
Name mysql.Str
Status mysql.UserYesNo
DiamondType diamond_e.OperateType
}
//通过userId获取帐号
func GetDiamondAccountByUserId(model *domain.Model, userId mysql.ID) (*DiamondAccount, error) {
var diamondAccount DiamondAccount
if err := model.Db.WithContext(model).Where(&DiamondAccount{
UserId: userId,
}).First(&diamondAccount).Error; err != nil {
return nil, myerr.WrapErr(err)
}
diamondAccount.Model = model
return &diamondAccount, nil
}
//匹配条件扣费
func (diamondAccount *DiamondAccount) ChangeDiamondAccountDetail(operateType diamond_e.OperateType, originId mysql.ID, diamondNum mysql.Num) (*DiamondAccountDetail, error) {
return diamondAccount.addDiamondAccountDetail(operateType, originId, diamondNum)
}
//钻石操作记录,
func (diamondAccount *DiamondAccount) addDiamondAccountDetail(operateType diamond_e.OperateType, originId mysql.ID, diamondNum mysql.Num) (*DiamondAccountDetail, error) {
var diamondOperateSet DiamondOperateSet
var err error
if err = diamondAccount.Db.Where(&DiamondOperateSet{
Type: operateType,
Status: mysql.USER,
DiamondType: diamond_e.DiamondYellow,
}).First(&diamondOperateSet).Error; err != nil {
return nil, myerr.WrapErr(err)
}
//判断是增加,账号是否被冻结
if diamondAccount.Status == diamond_e.Frozen && diamondOperateSet.AddReduce == mysql.REDUCE {
return nil, bizerr.DiamondAccountFrozen
}
//无限,检查次数
var count int64
if diamondOperateSet.FrequencyDay == -1 {
if diamondOperateSet.FrequencyNum != -1 {
diamondAccount.Db.Model(&DiamondAccountDetail{}).Where(&DiamondAccountDetail{
UserId: diamondAccount.UserId,
OperateType: operateType,
}).Count(&count)
if count >= int64(diamondOperateSet.FrequencyNum) {
return nil, bizerr.DiamondFrequency
}
}
} else if diamondOperateSet.FrequencyDay == 1 {
beginTime, err := time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), time.Local)
if err != nil {
return nil, myerr.WrapErr(err)
}
//一天的次数
diamondAccount.Db.Model(&DiamondAccountDetail{}).Where(&DiamondAccountDetail{
UserId: diamondAccount.UserId,
OperateType: operateType,
}).Where("created_time >= ? ", beginTime).Count(&count)
if count >= int64(diamondOperateSet.FrequencyNum) {
return nil, bizerr.DiamondFrequency
}
//终极拦截,利用
diamondAccount.SetCheckUpdateCondition(" EXISTS (SELECT * from (SELECT COUNT(1) as n from diamond_account_detail d where d.user_id = " + strconv.FormatUint(diamondAccount.UserId, 10) + " and d.operate_type = " + strconv.FormatUint(uint64(operateType), 10) + " and d.created_time >= from_unixtime(" + strconv.FormatInt(getZeroTime(time.Now()).Unix(), 10) + ")) t where t.n < " + strconv.Itoa(diamondOperateSet.FrequencyNum) + " )")
}
//-1,代表值无效,由参数给与
var upateDiamondNum mysql.Num
if diamondOperateSet.DiamondNum == -1 {
upateDiamondNum = diamondNum
} else {
upateDiamondNum = mysql.Num(diamondOperateSet.DiamondNum)
}
var afterNum mysql.Num
if diamondOperateSet.AddReduce == mysql.ADD {
afterNum = diamondAccount.DiamondNum + upateDiamondNum
} else if diamondOperateSet.AddReduce == mysql.REDUCE {
if diamondAccount.DiamondNum < upateDiamondNum {
return nil, bizerr.DiamondNoEnough
}
afterNum = diamondAccount.DiamondNum - upateDiamondNum
} else {
return nil, myerr.NewSysError("AddReduce 值错误:" + mysql.TypeToString(diamondOperateSet.AddReduce))
}
diamondAccountDetail := &DiamondAccountDetail{
Model: diamondAccount.Model,
UserId: diamondAccount.UserId,
DiamondAccountId: diamondAccount.ID,
OperateId: diamondOperateSet.ID,
OperateType: diamondOperateSet.Type,
OriginId: originId,
AddReduce: diamondOperateSet.AddReduce,
Num: upateDiamondNum,
Remark: diamondOperateSet.Name,
BefNum: diamondAccount.DiamondNum,
AftNum: afterNum,
diamondAccount: diamondAccount,
}
return diamondAccountDetail, err
}
package diamond_m
import (
"hilo-user/_const/enum/diamond_e"
"hilo-user/domain"
"hilo-user/myerr/bizerr"
"hilo-user/resource/mysql"
)
func CheckEnoughDiamondFrozen(model *domain.Model, userId mysql.ID, diamondNum mysql.Num) (*DiamondAccount, error) {
diamondAccount, err := GetDiamondAccountByUserId(model, userId)
if err != nil {
return nil, err
}
if diamondAccount.DiamondNum < diamondNum {
return nil, bizerr.DiamondNoEnough
}
if diamondAccount.Status == diamond_e.Frozen {
return nil, bizerr.DiamondAccountFrozen
}
return diamondAccount, nil
}
package diamond_m
import (
"gorm.io/gorm"
"hilo-user/domain/model"
"hilo-user/myerr"
"hilo-user/mylogrus"
"hilo-user/resource/mysql"
"strconv"
)
func (diamondAccountDetail *DiamondAccountDetail) Persistent() error {
txDiamondAccount := diamondAccountDetail.Db.Model(diamondAccountDetail.diamondAccount)
if diamondAccountDetail.diamondAccount.CheckUpdateCondition() {
txDiamondAccount = txDiamondAccount.Where(diamondAccountDetail.diamondAccount.GetUpdateCondition())
}
if diamondAccountDetail.AddReduce == mysql.ADD {
//增加
txDiamondAccount.UpdateColumn("diamond_num", gorm.Expr("diamond_num + ?", diamondAccountDetail.Num))
} else if diamondAccountDetail.AddReduce == mysql.REDUCE {
//减少,保证不能扣成负数
txDiamondAccount.Where("diamond_num >= ?", diamondAccountDetail.Num).UpdateColumn("diamond_num", gorm.Expr("diamond_num - ?", diamondAccountDetail.Num))
} else {
myerr.NewSysError("addReduce 枚举错误 value:" + mysql.TypeToString(mysql.Type(diamondAccountDetail.AddReduce)))
}
if err := txDiamondAccount.Error; err != nil {
return myerr.WrapErr(err)
}
if txDiamondAccount.RowsAffected == 0 {
mylogrus.MyLog.Errorf("gorm condition update.RowsAffected = 0,AddReduce:%v", diamondAccountDetail.AddReduce)
return myerr.NewWaring("gorm condition update.RowsAffected = 0")
}
//持久化diamondAccountDetail
if err := model.Persistent(diamondAccountDetail.Db, diamondAccountDetail); err != nil {
return myerr.WrapErr(err)
}
//改变diamondAccount值
if diamondAccountDetail.diamondAccount == nil {
return myerr.NewSysError("持久化错误, 模型:DiamondAccountDetail 中没有diamondAccount, DiamondAccountDetail.Id =" + strconv.Itoa(int(diamondAccountDetail.ID)))
}
var newDiamondAccount DiamondAccount
if err := diamondAccountDetail.Db.First(&newDiamondAccount, diamondAccountDetail.diamondAccount.ID).Error; err != nil {
return myerr.WrapErr(err)
}
if newDiamondAccount.DiamondNum < 0 {
return myerr.NewSysError("diamond_account表中,diamond_num 不能小于0, diamondAccount.id = " + strconv.Itoa(int(newDiamondAccount.ID)))
}
return nil
}
package diamond_m
import "time"
func getZeroTime(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
package event_m
import (
"gorm.io/gorm"
"hilo-user/domain"
"hilo-user/resource/mysql"
"time"
)
// 进房事件消息
type EventGroupIn struct {
mysql.Entity
*domain.Model `gorm:"-"`
Proto mysql.Type
Payload []byte
Mark mysql.YesNo
}
// 偏移值
type EventGroupInOffset struct {
mysql.Entity
*domain.Model `gorm:"-"`
MarkOffset mysql.ID
}
// 获取当前偏移值
func Offset(model *domain.Model) (*EventGroupInOffset, error) {
offset := new(EventGroupInOffset)
if err := model.Db.WithContext(model).First(offset).Error; err != nil {
if err != gorm.ErrRecordNotFound {
model.Log.Errorf("Offset fail:%v", err)
return nil, err
}
// gorm.ErrRecordNotFound
}
offset.Model = model
return offset, nil
}
// 批量获取进房事件
func FetchEventGroupIn(model *domain.Model, limit int) ([]*EventGroupIn, *EventGroupInOffset, error) {
offset, err := Offset(model)
if err != nil {
return nil, nil, err
}
var events []*EventGroupIn
if err := model.Db.WithContext(model).Model(EventGroupIn{}).
Where("id > ?", offset.MarkOffset).
Order("id asc").Limit(limit).Find(&events).Error; err != nil {
model.Log.Errorf("FetchEventGroupIn fail:%v", err)
return nil, nil, err
}
return events, offset, nil
}
// 标记已完成
func (p *EventGroupIn) MarkDone() error {
p.Mark = mysql.YES
return p.Db.WithContext(p.Model).Model(EventGroupIn{}).Where("id = ?", p.ID).Update("mark", p.Mark).Limit(1).Error
}
// 查询过去1小时-5分钟前未mark的事件
// 用来补偿
// 记得加limit
func FetchUnMarkEvents(model *domain.Model, limit int) ([]*EventGroupIn, error) {
offset, err := Offset(model)
if err != nil {
return nil, err
}
t := time.Now().Add(-time.Minute * 5)
lt := t.Add(-time.Hour)
var events []*EventGroupIn
if err := model.Db.WithContext(model).Model(EventGroupIn{}).
Where("mark = ?", mysql.NO).
Where("id <= ?", offset.MarkOffset).
Where("created_time < ?", t).
Where("created_time > ?", lt).
Order("id asc").Limit(limit).Find(&events).Error; err != nil {
model.Log.Errorf("FetchUnMarkEvents fail:%v", err)
return nil, err
}
return events, nil
}
// 补偿加上unmark的event
func AddUnMarkEvent(model *domain.Model, event *EventGroupIn) error {
return model.Db.WithContext(model).Create(event).Error
}
package event_m
import (
"gorm.io/gorm"
"hilo-user/domain"
"hilo-user/resource/mysql"
"time"
)
// 离房事件消息
type EventGroupLeave struct {
mysql.Entity
*domain.Model `gorm:"-"`
Proto mysql.Type
Payload []byte
Mark mysql.YesNo
}
// 偏移值
type EventGroupLeaveOffset struct {
mysql.Entity
*domain.Model `gorm:"-"`
MarkOffset mysql.ID
}
// 获取当前偏移值
func GroupLeaveOffset(model *domain.Model) (*EventGroupLeaveOffset, error) {
offset := new(EventGroupLeaveOffset)
if err := model.Db.WithContext(model).First(offset).Error; err != nil {
if err != gorm.ErrRecordNotFound {
model.Log.Errorf("Offset fail:%v", err)
return nil, err
}
// gorm.ErrRecordNotFound
}
offset.Model = model
return offset, nil
}
// 批量获取进房事件
func FetchEventGroupLeave(model *domain.Model, limit int) ([]*EventGroupLeave, *EventGroupLeaveOffset, error) {
offset, err := GroupLeaveOffset(model)
if err != nil {
return nil, nil, err
}
var events []*EventGroupLeave
if err := model.Db.WithContext(model).Model(EventGroupLeave{}).
Where("id > ?", offset.MarkOffset).
Order("id asc").Limit(limit).Find(&events).Error; err != nil {
model.Log.Errorf("FetchEventGroupLeave fail:%v", err)
return nil, nil, err
}
return events, offset, nil
}
// 标记已完成
func (p *EventGroupLeave) MarkDone() error {
p.Mark = mysql.YES
return p.Db.WithContext(p.Model).Model(EventGroupLeave{}).Where("id = ?", p.ID).Update("mark", p.Mark).Limit(1).Error
}
// 查询过去1小时-5分钟前未mark的事件
// 用来补偿
// 记得加limit
func FetchGroupLeaveUnMarkEvents(model *domain.Model, limit int) ([]*EventGroupLeave, error) {
offset, err := GroupLeaveOffset(model)
if err != nil {
return nil, err
}
t := time.Now().Add(-time.Minute * 5)
lt := t.Add(-time.Hour)
var events []*EventGroupLeave
if err := model.Db.WithContext(model).Model(EventGroupLeave{}).
Where("mark = ?", mysql.NO).
Where("id <= ?", offset.MarkOffset).
Where("created_time < ?", t).
Where("created_time > ?", lt).
Order("id asc").Limit(limit).Find(&events).Error; err != nil {
model.Log.Errorf("FetchUnMarkEvents fail:%v", err)
return nil, err
}
return events, nil
}
// 补偿加上unmark的event
func AddGroupLeaveUnMarkEvent(model *domain.Model, event *EventGroupLeave) error {
return model.Db.WithContext(model).Create(event).Error
}
package event_m
import "hilo-user/domain/model"
func (p *EventGroupInOffset) Persistence() error {
return model.Persistent(p.Db, p)
}
func (p *EventGroupLeaveOffset) Persistence() error {
return model.Persistent(p.Db, p)
}
package user_m
import (
"gorm.io/gorm"
"hilo-user/_const/enum/user_e"
"hilo-user/common"
"hilo-user/domain"
"hilo-user/domain/event/user_ev"
"hilo-user/myerr"
"hilo-user/myerr/bizerr"
"hilo-user/resource/mysql"
"math/rand"
"strconv"
"time"
)
type GameInfo struct {
Id uint64 `json:"id"`
MgId string `json:"mg_id"`
GameType user_e.GameType `json:"user_type"`
Mode int32 `json:"mode"`
Piece int32 `json:"piece"`
OnOff1 uint8 `json:"on_off1"`
Diamond uint64 `json:"diamond"`
CreateId uint64 `json:"create_id"`
Status uint8 `json:"status"` // '0.未开始 1.游戏中 2.结束'
TxGroupId string `json:"tx_group_id"`
GameRoundId string `json:"user_round_id"`
BattleStartAt uint32 `json:"battle_start_at"`
BattleEndAt uint32 `json:"battle_end_at"`
BattleDuration uint32 `json:"battle_duration"`
AutoMatch uint8 `json:"auto_match"` // 是否开启自动匹配,0否1是
CreatedTime time.Time `json:"created_time"`
UpdatedTime time.Time `json:"updated_time"`
}
type GamePlayer struct {
Id uint64 `json:"id"`
GameId uint64 `json:"user_id"`
UserId uint64 `json:"user_id"`
Status uint8 `json:"status"` // '0.未开始 1.游戏中 2.游戏结束 3.逃跑 4.创建者关闭游戏',
UserCode string `json:"user_code"`
ExternalId string `json:"external_id"`
Rank uint32 `json:"rank"`
IsEscaped uint8 `json:"is_escaped"`
IsAi uint8 `json:"is_ai"`
Role int32 `json:"role"`
Score int32 `json:"score"`
IsWin uint8 `json:"is_win"`
Award uint32 `json:"award"`
Extras string `json:"extras"`
IsManaged uint8 `json:"is_managed"`
EndAt uint32 `json:"end_at"`
SeatIdx int8 `json:"seat_idx"`
Diamond int64 `json:"diamond"` // 黄钻收益,有可能负数
CreatedTime time.Time `json:"created_time"`
UpdatedTime time.Time `json:"updated_time"`
}
type GameAddParam struct {
GameId uint64 `form:"userId"`
GameType user_e.GameType `form:"userType" binding:"required"`
Mode user_e.GameMode `form:"mode" binding:"required"`
Piece int `form:"piece" binding:"required"`
OnOff1 int `form:"onOff1" binding:"required"`
Diamond uint64 `form:"diamond" binding:"required"`
AutoMatch int `form:"autoMatch" binding:"required"`
TxGroupId string `form:"groupId" binding:"required"`
GameCode string `form:"userCode"`
}
type GamePlayerOptLog struct {
Id uint64 `json:"id"`
GameId uint64 `json:"user_id"`
UserId uint64 `json:"user_id"`
Opt uint8 `json:"opt"`
TxGroupId string `json:"tx_group_id"`
CreateAt time.Time `json:"create_at"`
}
type GameSdkReport struct {
Id uint64 `json:"id"`
GameRoundId string `json:"user_round_id"`
TxGroupId string `json:"tx_group_id"`
ReportType string `json:"report_type"`
ReportMsg string `json:"report_msg"`
ExternalId string `json:"external_id"`
SsToken string `json:"ss_token"`
CreateAt time.Time `json:"create_at"`
}
func GetGameInfo(model *domain.Model, userId uint64, txGroupId, roundId string, battleEndAt, status int64) (*GameInfo, error) {
res := new(GameInfo)
db := model.Db
if userId != 0 {
db = db.Where("id = ?", userId)
}
if txGroupId != "" {
db = db.Where("tx_group_id = ?", txGroupId)
}
if roundId != "" {
db = db.Where("user_round_id = ?", roundId)
}
if battleEndAt != -1 {
db = db.Where("battle_end_at = ?", battleEndAt)
}
if status != -1 {
db = db.Where("status = ?", status)
}
err := db.First(&res).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return res, nil
}
func GetGamingInfo(model *domain.Model, txGroupId string) (*GameInfo, error) {
res := new(GameInfo)
db := model.Db
if txGroupId != "" {
db = db.Where("tx_group_id = ?", txGroupId)
}
err := db.Where("status in (0,1)").First(&res).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return res, nil
}
func Add(model *domain.Model, info *GameInfo) error {
nowTime := time.Now()
info.CreatedTime = nowTime
info.UpdatedTime = nowTime
err := model.Db.Save(info).Error
if err != nil {
return myerr.WrapErr(err)
}
return nil
}
func Edit(model *domain.Model, id uint64, para *GameAddParam) error {
if id == 0 {
model.Log.Infof("GameDao Edit id = 0")
return nil
}
err := model.Db.Exec("update user_info set mode=?,piece=?,on_off1=?,diamond=?,auto_match=? where id = ? and battle_start_at = 0",
para.Mode, para.Piece, para.OnOff1, para.Diamond, para.AutoMatch, id).Error
if err != nil {
return myerr.WrapErr(err)
}
return nil
}
func GameCloseUpdate(model *domain.Model, info *GameInfo) error {
if info.Id == 0 {
model.Log.Infof("GameCloseUpdate Id = 0")
return bizerr.GameNotFound
}
result := model.Db.Exec("update user_info set status=?,battle_end_at=? where id = ? and status != 2",
user_e.GameStatusEnd, time.Now().Unix(), info.Id)
if result.Error != nil {
model.Log.Errorf("GameCloseUpdate info.Id:%v, err:%v", info.Id, result.Error)
return myerr.WrapErr(result.Error)
}
if result.RowsAffected <= 0 {
model.Log.Errorf("GameCloseUpdate info.Id:%v, err:%v", info.Id, result.Error)
return bizerr.GameCloseWrong
}
return nil
}
func GameCloseUpdatePlayer(model *domain.Model, info *GameInfo) error {
if info.Id == 0 {
model.Log.Infof("GameCloseUpdatePlayer Id = 0")
return nil
}
err := model.Db.Exec("update user_player set status=?,end_at=? where user_id = ?", user_e.GamerStatusEnd, time.Now().Unix(), info.Id).Error
if err != nil {
return myerr.WrapErr(err)
}
return nil
}
func GameStartUpdate(model *domain.Model, info *user_ev.GameStartObject) error {
//var initGameRoundId bool
userInfo := new(GameInfo)
infoKey, err := strconv.Atoi(info.ReportGameInfoKey)
if err != nil {
model.Log.Errorf("GameStartUpdate err:%v, info:%v", err, info)
return myerr.WrapErr(err)
}
err = model.Db.Where("id = ?", infoKey).First(&userInfo).Error
if err != nil {
model.Log.Errorf("GameStartUpdate err:%v, info:%v", err, info)
return myerr.WrapErr(err)
//if err != gorm.ErrRecordNotFound {
// model.Log.Errorf("GameStartUpdate err:%v, info:%v", err, info)
// return myerr.WrapErr(err)
//}
//userInfo = new(GameInfo)
//err = model.Db.Where("tx_group_id = ? and user_round_id = ''", info.RoomId).First(&userInfo).Error
//if err != nil {
// if err != gorm.ErrRecordNotFound {
// model.Log.Errorf("GameStartUpdate err:%v, info:%v", err, info)
// return myerr.WrapErr(err)
// }
// model.Log.Infof("GameStartUpdate 找不到可以更新的记录 info:%v", info)
// return nil
//}
//initGameRoundId = true
}
if userInfo.Id == 0 {
model.Log.Errorf("GameStartUpdate 记录id为0 userInfo:%v", userInfo)
return myerr.WrapErr(bizerr.GameNotFound)
}
//if initGameRoundId {
// err = model.Db.Exec("update user_info set mode=?,user_round_id=?,battle_start_at=? where id = ? and user_round_id='' limit 1",
// info.GameMode, info.GameRoundId, info.BattleStartAt, userInfo.Id).Error
//} else {
// err = model.Db.Exec("update user_info set mode=?,user_round_id=?,battle_start_at=? where id = ? limit 1",
// info.GameMode, info.GameRoundId, info.BattleStartAt, userInfo.Id).Error
//}
err = model.Db.Exec("update user_info set mode=?,user_round_id=?,battle_start_at=? where id = ? limit 1",
info.GameMode, info.GameRoundId, info.BattleStartAt, userInfo.Id).Error
if err != nil {
model.Log.Errorf("GameStartUpdate err:%v", err)
return myerr.WrapErr(err)
}
err = model.Db.Exec("update user_player set status=? where user_id = ?", user_e.GamerStatusGaming, userInfo.Id).Error
if err != nil {
model.Log.Errorf("GameStartUpdate err:%v", err)
return myerr.WrapErr(err)
}
return nil
}
// 标记游戏已经开始
func GameStartMark(model *domain.Model, userId mysql.ID) error {
if err := model.DB().Model(GameInfo{}).Where("id = ?", userId).Update("status", user_e.GameStatusGaming).Error; err != nil {
model.Log.Errorf("GameStartMark fail:userId:%v,err:%v", userId, err)
return err
}
return nil
}
// 标记游戏已经结束
func GameEndMark(model *domain.Model, userId mysql.ID) error {
if err := model.DB().Model(GameInfo{}).Where("id = ?", userId).Update("status", user_e.GameStatusEnd).Error; err != nil {
model.Log.Errorf("GameStartMark fail:userId:%v,err:%v", userId, err)
return err
}
return nil
}
func GameSettleUpdate(model *domain.Model, info *user_ev.GameSettleObject) error {
//if info.GameRoundId == "" {
// model.Log.Infof("GameSettleUpdate GameRoundId = 0")
// return nil
//}
infoKey, err := strconv.Atoi(info.ReportGameInfoKey)
if err != nil {
model.Log.Errorf("GameSettleUpdate err:%v, info:%v", err, info)
return myerr.WrapErr(err)
}
userInfo := new(GameInfo)
err = model.Db.Where("id = ?", infoKey).First(&userInfo).Error
if err != nil {
model.Log.Errorf("GameSettleUpdate err:%v", err)
return myerr.WrapErr(err)
}
result := model.Db.Exec("update user_info set status=?,battle_end_at=?,battle_duration=? where id = ? and status=1",
user_e.GameStatusEnd, info.BattleEndAt, info.BattleDuration, userInfo.Id)
if result.Error != nil {
model.Log.Errorf("GameSettleUpdate err:%v", err)
return myerr.WrapErr(err)
}
if result.RowsAffected <= 0 {
model.Log.Errorf("GameSettleUpdate 修改失败, userInfo.Id:%v", userInfo.Id)
return myerr.WrapErr(bizerr.GameSettleWrong)
}
for _, v := range info.Results {
err = model.Db.Exec("update user_player set status=?,`rank`=?,is_escaped=?,is_ai=?,`role`=?,score=?,is_win=?,"+
"award=?,extras=?,end_at=?,diamond=?,is_managed=? where user_id = ? and external_id = ?",
user_e.GamerStatusEnd, v.Rank, v.IsEscaped, v.IsAi, v.Role, v.Score, v.IsWin, v.Award, v.Extras, info.BattleEndAt,
v.Diamond, v.IsManaged, userInfo.Id, v.Uid).Error
if err != nil {
model.Log.Errorf("GameSettleUpdate err:%v", err)
return myerr.WrapErr(err)
}
}
err = model.Db.Exec("update user_player set status=?,end_at=? where user_id=? and end_at=0",
user_e.GamerStatusEnd, time.Now().Unix(), userInfo.Id).Error
if err != nil {
model.Log.Errorf("GameSettleUpdate err:%v", err)
return myerr.WrapErr(err)
}
return nil
}
func GetGamePlayers(model *domain.Model, userId uint64) ([]*GamePlayer, error) {
res := make([]*GamePlayer, 0)
err := model.Db.Where("user_id = ?", userId).Order("created_time").Find(&res).Error
if err != nil {
return nil, err
}
return res, nil
}
func DelGamePlayers(model *domain.Model, userId uint64) error {
if userId <= 0 {
return bizerr.InvalidParameter
}
err := model.Db.Exec("delete from user_player where user_id = ?", userId).Error
if err != nil {
return myerr.WrapErr(err)
}
return nil
}
func GetGamePlayersMap(model *domain.Model, userId uint64, userExtIds []string) (map[string]*GamePlayer, error) {
list := make([]*GamePlayer, 0, len(userExtIds))
err := model.Db.Where("user_id = ? and external_id in (?)", userId, userExtIds).Find(&list).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
res := make(map[string]*GamePlayer, 0)
for _, v := range list {
res[v.ExternalId] = v
}
return res, nil
}
func GetGamePlayer(model *domain.Model, userId, userId uint64) (*GamePlayer, error) {
res := new(GamePlayer)
err := model.Db.Where("user_id = ? and user_id = ?", userId, userId).First(res).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return res, nil
}
func SaveGamePlayer(model *domain.Model, info *GamePlayer) error {
err := model.Db.Save(info).Error
if err != nil {
return myerr.WrapErr(err)
}
return nil
}
func DelGamePlayer(model *domain.Model, id uint64) error {
result := model.Db.Exec("delete from user_player where id = ? and status=0", id)
if result.Error != nil {
return result.Error
}
if result.RowsAffected <= 0 {
return myerr.WrapErr(bizerr.GameExitWrong)
}
return nil
}
func UpdateGamePlayerExit(model *domain.Model, id uint64) error {
err := model.Db.Exec("update user_player set end_at=? where id = ?", time.Now().Unix(), id).Error
if err != nil {
return err
}
return nil
}
func SaveGameOptLog(model *domain.Model, userId, userId uint64, opt user_e.GameOpt, txGroupId string) error {
defer common.CheckGoPanic()
err := model.Db.Save(&GamePlayerOptLog{GameId: userId, UserId: userId, Opt: uint8(opt), TxGroupId: txGroupId, CreateAt: time.Now()}).Error
if err != nil {
model.Log.Errorf("SaveGameOptLog err:%v", err)
return err
}
return nil
}
func SaveGameSdkReport(model *domain.Model, userRoundId, roomId, reportType, reportMsg, externalId, ssToken string) error {
defer common.CheckGoPanic()
err := model.Db.Save(&GameSdkReport{TxGroupId: roomId, GameRoundId: userRoundId, ReportType: reportType,
ReportMsg: reportMsg, ExternalId: externalId, SsToken: ssToken, CreateAt: time.Now()}).Error
if err != nil {
model.Log.Errorf("SaveGameSdkReport err:%v", err)
return err
}
return nil
}
// 获取一个正在进行游戏的房间id(房间没有密码,没有下注的游戏房间)
func GetGamingTxGroupId(model *domain.Model, userType user_e.GameType) (txGroupId string, err error) {
// 游戏类型,游戏未满人,游戏未开始
infos := make([]*GameInfo, 0)
sql := "select s1.* from (select * from user_info where user_type=? and status=0 and diamond=0 limit 50) s1 left join group_info on s1.tx_group_id=group_info.tx_group_id where group_info.password='' and group_info.tourist_mic=1"
err = model.Db.Raw(sql, userType).Find(&infos).Error
//err = model.Db.Where("status = 0 and user_type = ?", userType).Limit(10).Find(&infos).Error
if err != nil {
return
}
if len(infos) == 0 {
return
}
if len(infos) == 1 {
txGroupId = infos[0].TxGroupId
return
}
// 打乱
rand.Shuffle(len(infos), func(i, j int) {
t := infos[i]
infos[i] = infos[j]
infos[j] = t
})
txGroupId = infos[0].TxGroupId
return
}
// 获取用户在该房间已经加入的未结束的游戏
func GetGamingInfoByUserId(model *domain.Model, userId uint64, txGroupId string) (*GameInfo, error) {
res := new(GameInfo)
db := model.Db
err := db.Where("id in (select user_id from user_player where user_id=? and end_at=0) and tx_group_id = ? and status in (0,1)", userId, txGroupId).First(&res).Error
//err := db.Where("status in (0,1)").First(&res).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return res, nil
}
package user_m
import (
"hilo-user/_const/enum/user_e"
"hilo-user/domain"
"hilo-user/resource/mysql"
"sort"
"time"
)
type StartGamePlayer struct {
TxGroupId mysql.Str // 房间id
GameId mysql.ID // 游戏id
MgId mysql.Str // 游戏sdk id
UserId mysql.ID // 用户id
CreatedTime time.Time // 加入游戏时间
}
// 获取可以开始的游戏
// condition
// 1. 游戏还没开始
// 2. 游戏人数大于等于2个
// 3. 第二个加入的人距离现在已经超过15秒
func GetCanStartGames(model *domain.Model) []GameInfo {
// 1. 游戏还没开始
sql := "SELECT g.tx_group_id,p.user_id,g.mg_id,p.user_id,p.created_time FROM `user_info` g,`user_player` p where p.user_id = g.id AND g.`status` = ?;"
var startGamePlayers []StartGamePlayer
if err := model.DB().Raw(sql, user_e.GameStatusNoStart).Find(&startGamePlayers).Error; err != nil {
model.Log.Errorf("GetCanStartGames fail,sql:%v,err:%v", sql, err)
return nil
}
// userId -> userPlayer
var startGamePlayersMap = make(map[mysql.ID][]StartGamePlayer)
for i, p := range startGamePlayers {
startGamePlayersMap[p.GameId] = append(startGamePlayersMap[p.GameId], startGamePlayers[i])
}
var res []GameInfo
for userId, players := range startGamePlayersMap {
// 2. 游戏人数大于等于2个
if len(players) < 2 {
continue
}
model.Log.Infof("startGamePlayer,userId:%v,player:%+v", userId, players)
// 3. 第二个加入的人距离现在已经超过15秒
sort.Slice(players, func(i, j int) bool {
return players[i].CreatedTime.Before(players[j].CreatedTime)
})
// 客户端15秒,服务端延迟1秒
if time.Now().Sub(players[1].CreatedTime).Seconds() >= 16 {
res = append(res, GameInfo{
Id: userId,
MgId: players[1].MgId,
TxGroupId: players[1].TxGroupId,
})
}
}
return res
}
package user_m
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"hilo-user/_const/enum/robot_e"
"hilo-user/_const/enum/user_e"
"hilo-user/domain"
"hilo-user/domain/cache/robot_c"
"hilo-user/domain/model/group_m"
"hilo-user/domain/model/user_m"
"hilo-user/resource/config"
"hilo-user/resource/mysql"
"hilo-user/rpc"
"time"
)
// 机器人
type GameRobot struct {
mysql.Entity
UserId mysql.ID // 用户id
State robot_e.RobotState // 机器人状态
OpStep robot_e.RobotOp // 当前所在步骤
GroupId string // 群组id
MicIndex int // 麦位
GameUid mysql.ID // 游戏id
MgId string // sdk游戏Id
LastUseTime time.Time // 上次使用的时间
model *domain.Model `gorm:"-"` // db、ctx
Game GameTiny `gorm:"-"` // 需要加入的游戏
User user_m.User `gorm:"-"` // 用户信息
Group *group_m.GroupInfo `gorm:"-"` // 群组信息
Token string `gorm:"-"` // jwt token
Over bool `gorm:"-"` // 是否结束
Err error `gorm:"-"` // 错误日志
// service层的GameOpt
GameOpt func(userId, userId uint64, code, ext, lang, userCode, txGroupId string, opt user_e.GameOpt, seatIdx int8, isAi bool) error `gorm:"-"`
}
// 机器人日志
type GameRobotLog struct {
mysql.Entity
RobotUid mysql.ID
Op robot_e.RobotOp // 机器人操作
DataId string // 房间号 or 麦位 or 游戏id
}
// 标记正在使用中
func (robot *GameRobot) MarkUsing(model *domain.Model) error {
updates := map[string]interface{}{
"state": robot_e.RobotStateUsing,
"last_use_time": time.Now(),
}
return model.Db.Model(GameRobot{}).Where("id = ?", robot.ID).
Updates(updates).Error
}
// 标记闲置
func (robot *GameRobot) MarkIdle(model *domain.Model) {
updates := map[string]interface{}{
"state": robot_e.RobotStateIdle,
"group_id": "",
"mic_index": -1,
"user_uid": 0,
"mg_id": "",
"op_step": robot_e.RobotOpNone,
}
if err := model.Db.Model(GameRobot{}).Where("id = ?", robot.ID).
Updates(updates).Error; err != nil {
robot.model.Log.Errorf("MarkIdle fail:%v", err)
}
}
// 机器人干活了
func (robot *GameRobot) Run() {
robot.model = domain.CreateModelNil()
robot.model.Log = robot.model.Log.WithField("GameRobotRun", robot.ID)
var err error
defer func() {
if err != nil {
robot.model.Log.Errorf("Run fail,robotId:%v,uid:%v,err:%v", robot.ID, robot.UserId, err)
// reset to idle
robot.MarkIdle(robot.model)
robot.Over = true
robot.Err = err
}
}()
// 检查游戏状态
if robot.Game.Status == user_e.GameStatusNoStart { // 未开始
go robot.heartbeat() // 心跳自愈
// 进入游戏流程
switch robot.OpStep {
case robot_e.RobotOpNone:
if err = robot.GroupIn(); err != nil {
return
}
fallthrough
case robot_e.RobotOpGroupIn:
if err = robot.MicIn(); err != nil {
return
}
fallthrough
case robot_e.RobotOpMicIn:
if err = robot.GameIn(); err != nil {
return
}
fallthrough
default:
robot.model.Log.Infof("may in other step:%v", robot.OpStep)
}
} else {
robot.model.Log.Warnf("user status not normal:%v", robot.Game)
}
}
// 进程重启后的机器人自愈
// 如果走过进入游戏了,则继续走心跳逻辑
// 否则,直接走
func (robot *GameRobot) ReRun() {
robot.model = domain.CreateModelNil()
robot.model.Log = robot.model.Log.WithField("GameRobotReRun", robot.ID)
if robot.GameUid > 0 {
go robot.heartbeat()
} else {
robot.Leave("ReRun")
}
}
// 进房
func (robot *GameRobot) GroupIn() error {
url := config.GetUrlConfig().BIZ_HTTP + "/v1/imGroup/in"
header := map[string]string{
"nonce": "hilo",
"token": robot.Token,
}
form := map[string]string{
"groupId": robot.Game.GroupId,
}
// http to biz
if resp, err := rpc.HttpPutForm(robot.model, url, header, form); err != nil {
return err
} else {
type GroupInResp struct {
Code int `json:"code"`
}
res := new(GroupInResp)
if err = json.Unmarshal(resp, res); err != nil {
return err
} else {
if res.Code != 200 {
return errors.New(fmt.Sprintf("Group In Not 200:%v", res))
} else {
// 复制群组id
robot.GroupId = robot.Game.GroupId
}
}
}
// no error
return robot.model.Transaction(func(txModel *domain.Model) error {
updates := map[string]interface{}{
"group_id": robot.GroupId,
"op_step": robot_e.RobotOpGroupIn,
}
err := txModel.Db.Model(GameRobot{}).Where("id = ?", robot.ID).
Updates(updates).Error
if err != nil {
return err
}
return txModel.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpGroupIn,
DataId: robot.GroupId,
}).Error
})
}
// 上麦
func (robot *GameRobot) MicIn() error {
url := config.GetUrlConfig().BIZ_HTTP + "/v1/imGroup/mic/in"
header := map[string]string{
"nonce": "hilo",
"token": robot.Token,
}
form := map[string]string{
"groupUuid": robot.GroupId,
"i": "", // 空则随意上一个空位置
}
// http to biz
if resp, err := rpc.HttpPostForm(robot.model, url, header, form); err != nil {
robot.model.Log.Errorf("Mic In fail:%v", err)
return err
} else {
type MicInResp struct {
Code int `json:"code"`
Data struct {
MicIndex int `json:"micIndex"`
} `json:"data"`
}
res := new(MicInResp)
if err = json.Unmarshal(resp, res); err != nil {
robot.model.Log.Errorf(fmt.Sprintf("Mic In json:%v", res))
return err
} else {
if res.Code != 200 || res.Data.MicIndex < 0 {
robot.model.Log.Errorf(fmt.Sprintf("Mic In Not 200:%v,groupId:%v", res, robot.GroupId))
if res.Code == 12008 {
// 群上麦失败,群先做cd,能上麦的情况太多
_ = robot_c.CDMicInGroup(robot.model, robot.GroupId)
}
return errors.New(fmt.Sprintf("Mic In Not 200:%v", res))
} else {
robot.MicIndex = res.Data.MicIndex // 赋值麦位
}
}
}
return robot.model.Transaction(func(txModel *domain.Model) error {
updates := map[string]interface{}{
"mic_index": robot.MicIndex,
"op_step": robot_e.RobotOpMicIn,
}
err := robot.model.Db.Model(GameRobot{}).Where("id = ?", robot.ID).
Updates(updates).Error
if err != nil {
return err
}
return txModel.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpMicIn,
DataId: fmt.Sprintf("%s-%d", robot.GroupId, robot.MicIndex),
}).Error
})
}
// 加入游戏
func (robot *GameRobot) GameIn() error {
if err := robot.GameOpt(robot.UserId, robot.Game.GameUid, robot.User.Code, robot.User.ExternalId, "en", "", robot.GroupId, user_e.GameOptJoin, -1, true); err != nil {
robot.model.Log.Errorf("Game In fail,userId:%v,err:%v", robot.UserId, err)
return err
}
err := robot.model.Transaction(func(txModel *domain.Model) error {
updates := map[string]interface{}{
"user_uid": robot.Game.GameUid,
"mg_id": robot.Game.MgId,
"op_step": robot_e.RobotOpGameIn,
}
err := robot.model.Db.Model(GameRobot{}).Where("id = ?", robot.ID).
Updates(updates).Error
if err != nil {
return err
}
// 赋值游戏id
robot.GameUid = robot.Game.GameUid
return txModel.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpGameIn,
DataId: fmt.Sprintf("%d", robot.GameUid),
}).Error
})
if err != nil {
return err
}
// GameIn成功之后腾讯云通知
robot.TxGroupInPush()
return nil
}
// 机器人离开
// 下麦+离开房间+退出游戏
func (robot *GameRobot) Leave(reason string) {
// 异常情况会直接leave,保护一下model未赋值
if robot.model == nil {
robot.model = domain.CreateModelNil()
}
robot.model.Log.Infof("robot leave,id:%v,reason:%v", robot.ID, reason)
robot.MicLeave()
robot.GroupLeave()
robot.GameOut()
robot.MarkIdle(robot.model)
}
// 下麦
func (robot *GameRobot) MicLeave() {
// 未上麦
if robot.MicIndex < 0 {
return
}
url := config.GetUrlConfig().BIZ_HTTP + "/v1/imGroup/mic/leave"
header := map[string]string{
"nonce": "hilo",
"token": robot.Token,
}
form := map[string]string{
"groupUuid": robot.GroupId,
"i": fmt.Sprintf("%d", robot.MicIndex), //
}
// http to biz
if resp, err := rpc.HttpPostForm(robot.model, url, header, form); err != nil {
robot.model.Log.Errorf("Mic Leave fail:%v", err)
return
} else {
type MicLeaveResp struct {
Code int `json:"code"`
}
res := new(MicLeaveResp)
if err = json.Unmarshal(resp, res); err != nil {
robot.model.Log.Errorf(fmt.Sprintf("Mic Leave json:%v", res))
return
} else {
if res.Code != 200 {
robot.model.Log.Errorf(fmt.Sprintf("Mic Leave Not 200:%v", res))
}
}
}
if err := robot.model.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpMicLeave,
DataId: fmt.Sprintf("%s-%d", robot.GroupId, robot.MicIndex),
}).Error; err != nil {
robot.model.Log.Errorf("log fail:%v", err)
}
}
// 离房
func (robot *GameRobot) GroupLeave() {
if len(robot.GroupId) <= 0 {
return
}
url := config.GetUrlConfig().BIZ_HTTP + "/v1/imGroup/leave"
header := map[string]string{
"nonce": "hilo",
"token": robot.Token,
}
form := map[string]string{
"groupId": robot.GroupId,
}
// http to biz
if resp, err := rpc.HttpPostForm(robot.model, url, header, form); err != nil {
robot.model.Log.Errorf("GroupLeave fail:%v", err)
return
} else {
type GroupLeaveResp struct {
Code int `json:"code"`
}
res := new(GroupLeaveResp)
if err = json.Unmarshal(resp, res); err != nil {
robot.model.Log.Errorf(fmt.Sprintf("Group Leave json :%v", res))
return
} else {
if res.Code != 200 {
robot.model.Log.Errorf(fmt.Sprintf("Group Leave Not 200:%v", res))
}
}
}
if err := robot.model.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpGroupLeave,
DataId: robot.GroupId,
}).Error; err != nil {
robot.model.Log.Errorf("log fail:%v", err)
}
}
// 离开游戏
func (robot *GameRobot) GameOut() {
if robot.GameUid <= 0 || len(robot.GroupId) <= 0 {
return
}
if err := robot.GameOpt(robot.UserId, robot.GameUid, robot.User.Code, robot.User.ExternalId, "en", "", robot.GroupId, user_e.GameOptExit, -1, true); err != nil {
robot.model.Log.Errorf("Game Out fail,userId:%v,err:%v", robot.UserId, err)
return
}
if err := robot.model.Db.Create(&GameRobotLog{
RobotUid: robot.ID,
Op: robot_e.RobotOpGameOut,
DataId: fmt.Sprintf("%d", robot.GameUid),
}).Error; err != nil {
robot.model.Log.Errorf("log fail:%v", err)
}
}
// 腾讯云麦位通知
func (robot *GameRobot) TxGroupInPush() {
if robot.Group == nil || robot.User.ID <= 0 {
return
}
url := config.GetUrlConfig().BIZ_HTTP + "/inner/micAllRPush"
header := map[string]string{
"nonce": "hilo",
"token": robot.Token,
}
form := map[string]string{
"imGroupId": robot.Group.ImGroupId,
"externalId": robot.User.ExternalId,
}
// http to biz
if resp, err := rpc.HttpPostForm(robot.model, url, header, form); err != nil {
robot.model.Log.Errorf("GroupLeave fail:%v", err)
return
} else {
type Resp struct {
Code int `json:"code"`
}
res := new(Resp)
if err = json.Unmarshal(resp, res); err != nil {
robot.model.Log.Errorf(fmt.Sprintf("TxGroupInPush json :%v", res))
return
} else {
if res.Code != 200 {
robot.model.Log.Errorf(fmt.Sprintf("TxGroupInPush Not 200:%v", res))
}
}
}
}
// 机器人心跳自愈
func (robot *GameRobot) heartbeat() {
// robot.GameUid是mysql/执行完加入游戏才有
userId := robot.GameUid
if userId <= 0 {
// 这里是未加入游戏前的userId
userId = robot.Game.GameUid
}
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
user, err := getGameInfo(robot.model, userId)
if robot.Over || err != nil || user_e.GameStatus(user.Status) == user_e.GameStatusEnd {
robot.Leave("robot over")
return
}
}
}
}
// 根据id获取游戏信息
func getGameInfo(model *domain.Model, userUid mysql.ID) (*GameInfo, error) {
user := new(GameInfo)
err := model.DB().Model(user).Where("id = ?", userUid).First(user).Error
return user, err
}
package user_m
import (
"hilo-user/_const/enum/robot_e"
"hilo-user/_const/enum/user_e"
"hilo-user/domain"
"hilo-user/domain/cache/robot_c"
"hilo-user/resource/mysql"
"math/rand"
"time"
)
// 游戏结构体
type GameTiny struct {
GameUid mysql.ID
GameType user_e.GameType
GroupId string
MgId int64
Diamond int64
NeedRobots int // 需要机器人数量
Status user_e.GameStatus // 游戏进行状态,1:进行中、0:结束, 心跳实时刷新
UpdatedTime time.Time
}
// 获取需要机器人的游戏
func GetNeedRobotGames(model *domain.Model) ([]GameTiny, error) {
var resGames []GameTiny
// 获取准备开始的游戏
var users []GameTiny
sql := "SELECT user.id as user_uid,user.user_type,user.tx_group_id as group_id,mg_id,user.diamond,user.`status`,user.updated_time FROM user_info user,group_info g " +
"WHERE user.tx_group_id = g.tx_group_id AND user. STATUS = 0 AND user.auto_match = 1 AND user.battle_start_at = 0 AND g.`password` = '' AND g.tourist_mic = 1"
if err := model.DB().Raw(sql).Find(&users).Error; err != nil {
return nil, err
}
now := time.Now()
for _, user := range users {
// 使用user.UpdatedTime(非CreatedTime)去判断8秒,兼容编辑游戏的情况
if now.Sub(user.UpdatedTime).Seconds() <= 8 {
continue
}
// user players
var players []GamePlayer
if err := model.DB().Model(GamePlayer{}).
Where("user_id = ?", user.GameUid).Find(&players).Error; err == nil {
// 没有玩家,属于异常的user
if len(players) <= 0 {
model.Log.Errorf("GetNeedRobotGames no player exits,user:%v,players:%v", user, players)
continue
}
// 玩家大于2个就不需要机器人
if len(players) > 1 {
continue
}
// 机器人进群cd中
if robot_c.IsGroupInCd(model, user.GroupId) {
model.Log.Warnf("IsGroupInCd in cd,user:%v", user)
continue
}
var needRobots = 1 // 一个机器人
resGames = append(resGames, GameTiny{
GameUid: user.GameUid,
GroupId: user.GroupId,
MgId: user.MgId,
NeedRobots: needRobots,
Status: user.Status,
GameType: user.GameType,
Diamond: user.Diamond,
})
} else if err != nil {
model.Log.Errorf("GamePlayer fail:%v", err.Error())
}
}
return resGames, nil
}
// 获取正在使用中的机器人
func GetUsingRobots(model *domain.Model) ([]*GameRobot, error) {
var robots []*GameRobot
if err := model.DB().Model(GameRobot{}).Where("state = ?", robot_e.RobotStateUsing).Find(&robots).Error; err != nil {
return nil, err
}
return robots, nil
}
// 获取房间中的机器人
func GetRoomRobots(model *domain.Model, txGroupId mysql.Str) ([]*GameRobot, error) {
var robots []*GameRobot
if err := model.DB().Model(GameRobot{}).Where("group_id = ?", txGroupId).Find(&robots).Error; err != nil {
return nil, err
}
return robots, nil
}
// 获取异常的robot
// state = 0,但是状态没清空
func GetAbnormalRobots(model *domain.Model) ([]*GameRobot, error) {
var robots []*GameRobot
if err := model.DB().Model(GameRobot{}).Where("state = ? AND op_step != 0", robot_e.RobotStateIdle).Find(&robots).Error; err != nil {
return nil, err
}
return robots, nil
}
// 获取闲置中的机器人
// 并且设置为使用中
func GetSetIdleGameRobots(model *domain.Model, num int) ([]*GameRobot, error) {
if num <= 0 {
model.Log.Warnf("GetSetIdleGameRobots no need robot:%v", num)
return nil, nil
}
var robots []*GameRobot
var canUseRobots []*GameRobot
err := model.Transaction(func(txModel *domain.Model) error {
if err := txModel.Db.WithContext(txModel).Model(GameRobot{}).Where("state = ?", robot_e.RobotStateIdle).Limit(1000).Find(&robots).Error; err != nil {
return err
}
// 打乱1000个空闲的机器人
rand.Shuffle(len(robots), func(i, j int) {
robots[i], robots[j] = robots[j], robots[i]
})
for i, robot := range robots {
// 增加cd逻辑,60秒机器人不复用
if time.Now().Sub(robot.LastUseTime).Seconds() < 60 {
model.Log.Warnf("robot cannot frequency use:%v", robot)
continue
}
if err := robot.MarkUsing(txModel); err != nil {
return err
}
canUseRobots = append(canUseRobots, robots[i])
num--
if num <= 0 {
break
}
}
return nil
})
if err != nil {
model.Log.Errorf("GetIdleGameRobots fail:%v", err)
return nil, err
}
return canUseRobots, nil
}
// 游戏ai启动
func AddAi(model *domain.Model, robots []*GameRobot) {
for _, robot := range robots {
robot.Run() // 一个机器人一个协程
}
}
package groupPower_m
import (
"gorm.io/gorm"
"hilo-user/_const/enum/groupPower_e"
"hilo-user/domain"
"hilo-user/domain/model/group_m"
"hilo-user/myerr"
"hilo-user/resource/mysql"
)
type GroupPower struct {
mysql.Entity
*domain.Model `gorm:"-"`
GroupUid mysql.Str
Name mysql.Str
Status groupPower_e.GroupPowerStatus
}
type GroupPowerUser struct {
mysql.Entity
*domain.Model `gorm:"-"`
GroupPowerId mysql.ID
UserId mysql.ID
Role groupPower_e.GroupPowerUserRole
}
func GetGroupPowerOrErr(model *domain.Model, id uint64) (*GroupPower, error) {
groupPower := GroupPower{}
if err := model.Db.Model(&GroupPower{}).First(&groupPower, id).Error; err != nil {
return nil, myerr.WrapErr(err)
}
groupPower.Model = model
return &groupPower, nil
}
//获取用户所在的国家势力信息,不存在则为nil
func GetGroupPowerUserOrNil(model *domain.Model, userId mysql.ID) (*GroupPowerUser, error) {
groupPowerUser := GroupPowerUser{}
if err := model.Db.Where(&GroupPowerUser{
UserId: userId,
}).First(&groupPowerUser).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, myerr.WrapErr(err)
}
}
groupPowerUser.Model = model
return &groupPowerUser, nil
}
// 查询用户加入的国家势力ID及名称(势力绑定的群组的名称)
func GetUserGroupPower(model *domain.Model, userId uint64) (uint64, string, error) {
gpu, err := GetGroupPowerUserOrNil(model, userId)
if err != nil {
return 0, "", err
}
if gpu == nil || gpu.GroupPowerId == 0 {
return 0, "", nil
}
gp, err := GetGroupPowerOrErr(model, gpu.GroupPowerId)
if err != nil {
return 0, "", err
}
powerName := ""
if gp != nil && len(gp.GroupUid) > 0 {
gi, err := group_m.GetGroupInfo(model, gp.GroupUid)
if err != nil {
return 0, "", err
}
if gi != nil {
// 只要前15个字
s := []rune(gi.Name)
if len(s) <= 15 {
powerName = string(s)
} else {
powerName = string(s[0:15])
}
}
}
return gpu.GroupPowerId, powerName, nil
}
package group_m
import (
"context"
"encoding/json"
redisV8 "github.com/go-redis/redis/v8"
"gorm.io/gorm"
"hilo-user/common"
"hilo-user/common/rediskey"
"hilo-user/domain"
"hilo-user/myerr"
"hilo-user/myerr/bizerr"
"hilo-user/resource/mysql"
"hilo-user/resource/redisCli"
)
type GroupRoles struct {
mysql.Entity
UserId uint64
ImGroupId string
Role common.GroupRoleType
}
func (this *GroupInfo) TableName() string {
return "group_info"
}
//发言,注意(发言是在麦位上)
type MicUser struct {
model *domain.Model
//群组uuid
GroupUuid string
//麦位
I int
//麦中的人
ExternalId string
//
UserId uint64
//静音 true:静音,false:没有静音
Forbid bool
//上麦的的时间戳
Timestamp int64
}
//记录麦位上有谁。用于
type UserInMic struct {
//群组uuid
GroupUuid string
//麦位
I int
//userId
UserId uint64
}
// 查询用户在IM群组中的角色
func GetRoleInGroup(model *domain.Model, userId uint64, imGroupId string) (uint16, error) {
r := GroupRoles{}
err := model.Db.Where(&GroupRoles{
UserId: userId,
ImGroupId: imGroupId}).First(&r).Error
if err != nil {
if err != gorm.ErrRecordNotFound {
return 0, err
} else {
return 0, nil
}
}
return r.Role, nil
}
func GetByTxGroupId(model *domain.Model, txGroupId string) (*GroupInfo, error) {
if len(txGroupId) <= 0 {
return nil, bizerr.GroupNotFound
}
res := new(GroupInfo)
err := model.Db.Where(&GroupInfo{TxGroupId: txGroupId}).First(&res).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, myerr.WrapErr(bizerr.GroupNotFound)
} else {
return nil, myerr.WrapErr(err)
}
}
return res, nil
}
//获取用户在哪个麦位上。没有不在麦上,则是nil
func GetMicUserByExternalId(model *domain.Model, externalId string) (*MicUser, error) {
if str, err := redisCli.GetRedis().Get(context.Background(), rediskey.GetPrefixGroupUserInMic(externalId)).Result(); err != nil {
if err != redisV8.Nil {
return nil, myerr.WrapErr(err)
} else {
return nil, nil
}
} else {
if userInMic, err := strToUserInMic(str); err != nil {
return nil, err
} else {
return GetMicUser(model, userInMic.GroupUuid, userInMic.I)
}
}
}
//麦位上没人,返回nil
func GetMicUser(model *domain.Model, groupUuid string, i int) (*MicUser, error) {
if i < 1 || i > 30 {
return nil, myerr.NewSysErrorF("麦序不对,不在范围值内 i:%v", i)
}
str, err := redisCli.GetRedis().Get(context.Background(), rediskey.GetPrefixGroupMicUser(groupUuid, i)).Result()
if err != nil {
if err == redisV8.Nil {
return nil, nil
} else {
return nil, myerr.WrapErr(err)
}
}
var micUser MicUser
err = json.Unmarshal([]byte(str), &micUser)
if err != nil {
return nil, err
}
return &MicUser{
model: model,
GroupUuid: groupUuid,
I: i,
ExternalId: micUser.ExternalId,
UserId: micUser.UserId,
Forbid: micUser.Forbid,
Timestamp: micUser.Timestamp,
}, nil
}
func strToUserInMic(str string) (UserInMic, error) {
userInMic := UserInMic{}
if err := json.Unmarshal([]byte(str), &userInMic); err != nil {
return UserInMic{}, myerr.WrapErr(err)
}
return userInMic, nil
}
package group_m
import (
"gorm.io/gorm"
"hilo-user/_const/enum/group_e"
"hilo-user/domain"
"hilo-user/myerr/bizerr"
"time"
)
type GroupInfo struct {
Id int64
ImGroupId string
TxGroupId string
Type uint16
Code string
OriginCode string
Owner uint64
Name string
Introduction string
Notification string
FaceUrl string
Country string
ChannelId string
Password string
EntryLevel uint32 // obsolete
MicOn bool
LoadHistory bool
ThemeId int16
MicNumType group_e.GroupMicNumType
TouristMic uint8 // 游客是否能上麦1是2否
TouristSendMsg uint8 // 游客是否能发消息1是2否
TouristSendPic uint8 // 游客是否能发图片1是2否
MemberFee uint64 // 加入会员需要黄钻数
CreatedTime time.Time `gorm:"->"`
UpdatedTime time.Time `gorm:"->"`
}
func GetGroupInfo(model *domain.Model, groupId string) (*GroupInfo, error) {
if len(groupId) <= 0 {
return nil, bizerr.GroupNotFound
}
r := GroupInfo{}
err := model.Db.Where(&GroupInfo{ImGroupId: groupId}).First(&r).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, err
}
}
return &r, nil
}
func GetGroupInfoByOwner(model *domain.Model, userId uint64) (*GroupInfo, error) {
if userId <= 0 {
return nil, bizerr.GroupNotFound
}
r := GroupInfo{}
err := model.Db.Where(&GroupInfo{Owner: userId}).First(&r).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
} else {
return nil, err
}
}
return &r, nil
}
package group_m
import (
"hilo-user/domain"
"hilo-user/myerr/bizerr"
)
func ToTxGroupId(model *domain.Model, imGroupId string) (string, error) {
if len(imGroupId) <= 0 {
return "", nil
}
gi, err := GetGroupInfo(model, imGroupId)
if err != nil {
return "", err
}
if gi == nil {
return "", bizerr.GroupNotFound
}
return gi.TxGroupId, nil
}
package group_m
import (
"encoding/json"
"hilo-user/_const/enum/group_e"
"hilo-user/domain"
"hilo-user/domain/model/noble_m"
"hilo-user/domain/model/user_m"
"hilo-user/rpc"
)
// 信令消息
type GroupSystemMsg struct {
MsgId group_e.TypeSignalMsg `json:"msgId"`
Source string `json:"source"`
Target string `json:"target"`
Content string `json:"content"`
}
type HiloUserInfo struct {
WealthGrade uint32 `json:"wealthGrade"`
CharmGrade uint32 `json:"charmGrade"`
IsVip bool `json:"isVip"`
IsPretty bool `json:"isPretty"`
Medals []uint32 `json:"medals"`
PowerName string `json:"powerName"` // 用户加入的国家势力的绑定群组的名称
NobleLevel uint16 `json:"nobleLevel"`
SvipLevel int `json:"svipLevel"`
}
var GetGroupPowerNameByUserId func(model *domain.Model, userId uint64) (uint64, string, error)
//不用返回错误
func GetHiloUserInfo(model *domain.Model, extId string) string {
user, err := user_m.GetUserByExtId(model, extId)
if err != nil {
model.Log.Errorf("extId:%v, err:%+v", extId, err)
return ""
}
wealthGrade, _, err := user_m.GetWealthGrade(model, user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
charmGrade, _, err := user_m.GetCharmGrade(model, user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
isVip, _, err := user_m.IsVip(user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
//
isPretty := user.IsPrettyCode()
//
medals, err := user_m.GetUserMedalMerge(model.Log, model.Db, user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
_, powerName, err := GetGroupPowerNameByUserId(model, user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
nobleLevel, err := noble_m.GetNobleLevel(model.Db, user.ID)
if err != nil {
model.Log.Errorf("extId:%v, err:%v", extId, err)
return ""
}
svip, _ := rpc.GetUserSvip(model, user.ID)
hilo := HiloUserInfo{
WealthGrade: wealthGrade,
CharmGrade: charmGrade,
IsVip: isVip,
IsPretty: isPretty,
Medals: medals,
PowerName: powerName,
NobleLevel: nobleLevel,
SvipLevel: svip.SvipLevel,
}
buf, err := json.Marshal(hilo)
if err != nil {
model.Log.Errorf("hilo:%+v, err:%v", hilo, err)
}
return string(buf)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment