diff --git a/cron/cron.go b/cron/cron.go index be5fb39d43c70d1eb5596fa56169c4dbdc554f3f..b9c89c23c9e7a7c5b3c16834d6d39159c00e0cf4 100644 --- a/cron/cron.go +++ b/cron/cron.go @@ -16,5 +16,6 @@ func Init() { mic_cron.OnMicCheck() // 检查上麦 group_cron.GroupPowerExpClear() // 清理家族经验/等级 group_cron.GroupPowerMonthRankAct() + group_cron.GroupInEventInit() // 进房事件 group_cron.GroupPowerGradeExp() // 家族升级 } diff --git a/cron/group_cron/group_in.go b/cron/group_cron/group_in.go new file mode 100644 index 0000000000000000000000000000000000000000..8581770f9097b7c106d5a04abd687e6b46969da9 --- /dev/null +++ b/cron/group_cron/group_in.go @@ -0,0 +1,34 @@ +package group_cron + +import ( + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/mylogrus" + "git.hilo.cn/hilo-common/resource/config" + "hilo-group/domain/service/event_s" + "time" +) + +// 进房事件 +func GroupInEventInit() { + if !config.IsMaster() { + return + } + mylogrus.MyLog.Infof("GroupInEventInit") + go func() { + ticker := time.NewTicker(time.Millisecond * 500) + defer ticker.Stop() + for { + select { + case <-ticker.C: + //start := time.Now() + myCtx := mycontext.CreateMyContext(nil) + // 消费进房事件 + if err := event_s.NewGroupInEventService(myCtx).Consume(); err != nil { + myCtx.Log.Errorf("groupInEvent consume fail:%v", err) + } else { + //myCtx.Log.Infof("groupInEvent consume success,cost:%v", time.Now().Sub(start)) + } + } + } + }() +} diff --git a/cv/billboard_cv/billboard.go b/cv/billboard_cv/billboard.go index 3abd9015e18a760874154ae77777486382ae6e7e..9ce2a46f20cdeaeb4d99d820c6c1c77dff062e37 100644 --- a/cv/billboard_cv/billboard.go +++ b/cv/billboard_cv/billboard.go @@ -22,7 +22,15 @@ type BillboardUserInfo struct { Num uint64 `json:"num"` } -func GetGroupTop3Consume(model *domain.Model, groupId string, myUserId uint64) ([]BillboardUserInfo, error) { +//榜单中用户信息 +type GroupTop3ConsumeUser struct { + //用户基本信息 + UserBase user_cv.TopConsumersUser `json:"userBase"` + //数值 + Num uint64 `json:"num"` +} + +func GetGroupTop3Consume(model *domain.Model, groupId string, myUserId uint64) ([]GroupTop3ConsumeUser, error) { now := time.Now() period := now.Format(utils.COMPACT_MONTH_FORMAT) data, err := getGroupTop3Consume(period, groupId) @@ -59,7 +67,15 @@ func GetGroupTop3Consume(model *domain.Model, groupId string, myUserId uint64) ( ret, err := saveGroupTop3Consume(period, groupId, diamonds) model.Log.Infof("GetGroupTop3Consume SAVE ret = %d, err: %v", ret, err) - return result, nil + + list := make([]GroupTop3ConsumeUser, 0, len(result)) + for _, v := range result { + list = append(list, GroupTop3ConsumeUser{ + UserBase: user_cv.TopConsumersUser{Id: v.UserBase.Id, Avatar: v.UserBase.Avatar, ExternalId: v.UserBase.ExternalId, Nick: v.UserBase.Nick}, + }) + } + + return list, nil } userIds := make([]uint64, 0) @@ -86,7 +102,15 @@ func GetGroupTop3Consume(model *domain.Model, groupId string, myUserId uint64) ( }) } } - return result, nil + + list := make([]GroupTop3ConsumeUser, 0, len(result)) + for _, v := range result { + list = append(list, GroupTop3ConsumeUser{ + UserBase: user_cv.TopConsumersUser{Id: v.UserBase.Id, Avatar: v.UserBase.Avatar, ExternalId: v.UserBase.ExternalId, Nick: v.UserBase.Nick}, + }) + } + + return list, nil } func getGroupTop3Consume(period string, groupId string) (map[string]string, error) { diff --git a/cv/group_cv/group.go b/cv/group_cv/group.go index cef91333ae998d6e6d9aeef5a70ec86f527311fd..13971b1e7db3945423cb32ccfd8e3dd9c0b0f15b 100644 --- a/cv/group_cv/group.go +++ b/cv/group_cv/group.go @@ -123,6 +123,13 @@ type MemberDetail struct { InRoom bool `json:"inRoom"` // 是否在房间内 } +type GroupVisitorsDetail struct { + user_cv.CvGroupMember + Role group_e.GroupRoleType `json:"role"` + OnlineStatus online_e.OnlineStatusType `json:"onlineStatus"` // IM在线状态 + InRoom bool `json:"inRoom"` // 是否在房间内 +} + type GroupDetail struct { GroupBasicInfo @@ -133,14 +140,14 @@ type GroupDetail struct { ThemeId uint64 `json:"themeId"` ThemeUrl string `json:"themeUrl"` // 1:官方 2:自定义 - ThemeType uint8 `json:"themeType"` - Role group_e.GroupRoleType `json:"role"` - MsgStatus uint8 `json:"msgStatus"` // 消息提醒状态 - WelcomeText string `json:"welcomeText"` // 新成员入群欢迎语 - TotalConsume uint64 `json:"totalConsume"` // 群内消费总额 - TopConsumers []billboard_cv.BillboardUserInfo `json:"topConsumers"` // 月最高消费三甲 - DiceNum uint16 `json:"diceNum"` // 骰子游戏的数量 - DiceType uint16 `json:"diceType"` // 骰子游戏的数字点数 + ThemeType uint8 `json:"themeType"` + Role group_e.GroupRoleType `json:"role"` + MsgStatus uint8 `json:"msgStatus"` // 消息提醒状态 + WelcomeText string `json:"welcomeText"` // 新成员入群欢迎语 + TotalConsume uint64 `json:"totalConsume"` // 群内消费总额 + TopConsumers []billboard_cv.GroupTop3ConsumeUser `json:"topConsumers"` // 月最高消费三甲 + DiceNum uint16 `json:"diceNum"` // 骰子游戏的数量 + DiceType uint16 `json:"diceType"` // 骰子游戏的数字点数 // "AppDefinedData": 群组维度的自定义字段 【暂时不用】 RoleMembers []RoleMemberInfo `json:"role_members"` Owner *user_cv.CvUserDetail `json:"owner"` // 群主信息 @@ -166,20 +173,20 @@ type RoomInfo struct { ThemeId uint64 `json:"themeId"` ThemeUrl string `json:"themeUrl"` // 1:官方 2:自定义 - ThemeType uint8 `json:"themeType"` - Role group_e.GroupRoleType `json:"role"` - DiceNum uint16 `json:"diceNum"` // 骰子游戏的数量 - DiceType uint16 `json:"diceType"` // 骰子游戏类型 - RoleMembers []SimpleRoleInfo `json:"roleMembers"` - WelcomeText string `json:"welcomeText"` // 新成员入群欢迎语 - Banners []BannerElement `json:"banners"` - LuckyWheel LuckyWheelState `json:"luckyWheel"` - TotalConsume uint64 `json:"totalConsume"` - GameConfig *game_m.GameConfig `json:"gameConfig"` - Owner *user_cv.CvUserDetail `json:"owner"` - EntryEffectType int `json:"entryEffectType"` // 进场特效类型 1: CP 2:神秘人 3:贵族 4:vip - CpUserAvatar string `json:"cpUserAvatar"` // cp对象头像 - CpLevel int `json:"cpLevel"` // cp等级 + ThemeType uint8 `json:"themeType"` + Role group_e.GroupRoleType `json:"role"` + DiceNum uint16 `json:"diceNum"` // 骰子游戏的数量 + DiceType uint16 `json:"diceType"` // 骰子游戏类型 + RoleMembers []SimpleRoleInfo `json:"roleMembers"` + WelcomeText string `json:"welcomeText"` // 新成员入群欢迎语 + Banners []BannerElement `json:"banners"` + LuckyWheel LuckyWheelState `json:"luckyWheel"` + TotalConsume uint64 `json:"totalConsume"` + GameConfig *game_m.GameConfig `json:"gameConfig"` + Owner *user_cv.RoomInfoOwner `json:"owner"` + EntryEffectType int `json:"entryEffectType"` // 进场特效类型 1: CP 2:神秘人 3:贵族 4:vip + CpUserAvatar string `json:"cpUserAvatar"` // cp对象头像 + CpLevel int `json:"cpLevel"` // cp等级 } type SupportPageDetail struct { diff --git a/cv/user_cv/user.go b/cv/user_cv/user.go index 0955c8426b6daf75e7bfa1aab667b8ac339d6743..0969d433b85dfbd81c83e15874b9d531fe521592 100644 --- a/cv/user_cv/user.go +++ b/cv/user_cv/user.go @@ -1,6 +1,7 @@ package user_cv import ( + "encoding/json" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mylogrus" "git.hilo.cn/hilo-common/resource/mysql" @@ -69,6 +70,25 @@ type CvUserDetail struct { CountryManager *country_cv.CVCountryManager `json:"countryManager,omitempty"` // 国家管理员 } +type RoomInfoOwner struct { + //头像,不存在为nil + Avatar *string `json:"avatar"` + //用户对外ID + ExternalId *string `json:"externalId"` + //昵称,不存在为nil + Nick *string `json:"nick"` + IsPrettyCode bool `json:"isPrettyCode"` // 是否靓号 + //是否VIP用户 + IsVip bool `json:"isVip"` + Svip rpc.CvSvip `json:"svip"` // svip结构,等级+权限 + Medals []uint32 `json:"medals"` // 勋章列表 + Noble noble_cv.CvNoble `json:"noble"` // 当前的 + WealthUserGrade uint32 `json:"wealthUserGrade"` //财富等级 + CharmUserGrade uint32 `json:"charmUserGrade"` //魅力等级 + MyGroupPowerName string `json:"myGroupPowerName"` // 当前用户所在势力绑定群组的名称 + GroupPower GroupPower `json:"groupPower"` // 当前用户势力信息 +} + type GroupPower struct { Id uint64 `json:"id"` // 群主所在的势力ID Icon string `json:"icon"` // 家族头像 @@ -150,6 +170,45 @@ type CvUserExtend struct { ActiveGrade uint32 `json:"activityUserGrade"` } +type CvGroupMember struct { + //不会有返回值 + Id *mysql.ID `json:"id,omitempty"` + //是否默认头像 true:是 false:不是 + DefaultAvatar *bool `json:"defaultAvatar"` + //头像,不存在为nil + Avatar *string `json:"avatar"` + //用户对外ID + ExternalId *string `json:"externalId"` + //昵称,不存在为nil + Nick *string `json:"nick"` + //签名,不存在为nil + Description *string `json:"description"` + Birthday *uint64 `json:"birthday"` + //邀请码 + Code *string `json:"code"` + //国家,不存在为nil + Country *string `json:"country"` + //国旗图标,不存在为nil + CountryIcon *string `json:"countryIcon"` + Sex *uint8 `json:"sex"` + //是否代理管理员, 只有自己查自己的时候才有值,其他情况为nil + IsAgentMgr *bool `json:"isAgentMgr"` + //是否展示年龄, 是本人才有数据,看其他用户均为nil + IsShowAge *uint8 `json:"isShowAge"` + //是否工会成员, 只有是自己查自己,这个才有值,其它全为nil, 20220329 数据开放:原因:产品1对1视频聊天中,公会用户视频需要送礼物。改为: 全部人可以知道是否是公会用户。 + IsTradeUnion *bool `json:"isTradeUnion"` + //工会成员,是否开启了,匹配通知,只有 isTradeUnion值为true,这里才有值, + IsTradeUnionMatchNotification *bool `json:"isTradeUnionMatchNotification"` + //是否VIP用户 + IsVip bool `json:"isVip"` + Ride property_cv.CvProperty `json:"ride"` // 当前使用的座驾 + Noble noble_cv.CvNoble `json:"noble"` // 当前的 + Svip rpc.CvSvip `json:"svip"` // svip结构,等级+权限 + WealthGrade uint32 `json:"wealthUserGrade"` + CharmGrade uint32 `json:"charmUserGrade"` + ActiveGrade uint32 `json:"activityUserGrade"` +} + // 批量获取用户tiny信息 func GetUserTinyMap(userIds []mysql.ID) (map[mysql.ID]CvUserTiny, error) { userTinys, _, err := GetUserTinys(userIds) @@ -1288,3 +1347,38 @@ func getMedalInfo(db *gorm.DB, medals []uint32) ([]uint32, []medal_cv.CvMedal, e } return validMedals, medalInfo, nil } + +func UserDetailToRoomInfoOwner(user *CvUserDetail) *RoomInfoOwner { + return &RoomInfoOwner{ + Avatar: user.Avatar, + ExternalId: user.ExternalId, + Nick: user.Nick, + IsPrettyCode: user.IsPrettyCode, + IsVip: user.IsVip, + Svip: user.Svip, + Medals: user.Medals, + Noble: user.Noble, + WealthUserGrade: user.WealthUserGrade, + CharmUserGrade: user.CharmUserGrade, + MyGroupPowerName: user.MyGroupPowerName, + GroupPower: user.GroupPower, + } +} + +type TopConsumersUser struct { + //不会有返回值 + Id *mysql.ID `json:"id,omitempty"` + //头像,不存在为nil + Avatar *string `json:"avatar"` + //用户对外ID + ExternalId *string `json:"externalId"` + //昵称,不存在为nil + Nick *string `json:"nick"` +} + +func CvUserExtendToCvGroupMember(detail CvUserExtend) CvGroupMember { + jData, _ := json.Marshal(detail) + res := CvGroupMember{} + _ = json.Unmarshal(jData, &res) + return res +} diff --git a/domain/event/group_ev/tx_group_in.go b/domain/event/group_ev/tx_group_in.go new file mode 100644 index 0000000000000000000000000000000000000000..602810dab9b517c128afdaf79f1218c215baf229 --- /dev/null +++ b/domain/event/group_ev/tx_group_in.go @@ -0,0 +1,32 @@ +package group_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +var txGroupInListen = new(domain.EventBase) + +// 进房事件 +type TxGroupInEvent struct { + GroupId string // imGroupId + UserId mysql.ID + ExternalId string + Nick string + Avatar string + IsMember bool //是否永久成员 + IsVip bool + NobleLevel uint16 +} + +func AddTxGroupInEventSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(txGroupInListen, callback) +} + +func AddTxGroupInEventAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(txGroupInListen, callback) +} + +func PublishTxGroupInEvent(model *domain.Model, event interface{}) error { + return domain.PublishEvent(txGroupInListen, model, event) +} diff --git a/domain/model/event_m/group_in.go b/domain/model/event_m/group_in.go new file mode 100644 index 0000000000000000000000000000000000000000..fdf9fc66935f24963b584add2bc3cf31d4f45afc --- /dev/null +++ b/domain/model/event_m/group_in.go @@ -0,0 +1,64 @@ +package event_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" +) + +// 进房事件消息 +type EventGroupIn struct { + mysql.Entity + *domain.Model `gorm:"-"` + Proto mysql.Type + Payload []byte + Mark mysql.YesNo + MarkHiloGroup mysql.YesNo +} + +func (EventGroupIn) TableName() string { + return "event_group_in" +} + +// 偏移值 +type EventGroupInOffsetHiloGroup struct { + mysql.Entity + *domain.Model `gorm:"-"` + MarkOffset mysql.ID +} + +// 获取当前偏移值 +func GroupOffset(model *domain.Model) (*EventGroupInOffsetHiloGroup, error) { + offset := new(EventGroupInOffsetHiloGroup) + 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, *EventGroupInOffsetHiloGroup, error) { + offset, err := GroupOffset(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.MarkHiloGroup = mysql.YES + return p.Db.WithContext(p.Model).Model(EventGroupIn{}).Where("id = ?", p.ID).Update("mark_hilo_group", p.MarkHiloGroup).Limit(1).Error +} diff --git a/domain/model/event_m/repo.go b/domain/model/event_m/repo.go index d4870364601c75073d61e36c31fea06ecd715075..83d8c9d1fe3ade75ce98b1517f68fc12ca70f2dc 100644 --- a/domain/model/event_m/repo.go +++ b/domain/model/event_m/repo.go @@ -9,3 +9,7 @@ func (p *EventGiftSendOffsetHiloGroup) Persistence() error { func (p *EventGiftSend) Persistence() error { return model.Persistent(p.Db, p) } + +func (p *EventGroupInOffsetHiloGroup) Persistence() error { + return model.Persistent(p.Db, p) +} \ No newline at end of file diff --git a/domain/model/group_m/mic.go b/domain/model/group_m/mic.go index b33508bd542b3f100c1c7a0f270c52b2498b3cb0..25e340317ec1cf76814e7b23b28f211a724c92bb 100644 --- a/domain/model/group_m/mic.go +++ b/domain/model/group_m/mic.go @@ -98,8 +98,8 @@ func GetMicNumType(model *domain.Model, groupUuid string) (group_e.GroupMicNumTy } } -//6个小时 -const expireMinute = 60 * 60 * 12 +// 3天 +const expireMinute = 60 * 60 * 24 * 3 const micExpire = expireMinute * time.Second //麦位基本信息。 diff --git a/domain/model/group_m/micData.go b/domain/model/group_m/micData.go index 7d74c352fee4f116fbbceaf6e0ca677b2220183e..546458dc1cc0b358b8ba5dc1b564c71f7f57120b 100644 --- a/domain/model/group_m/micData.go +++ b/domain/model/group_m/micData.go @@ -259,9 +259,9 @@ func MicAllRPush(model *domain.Model, groupUid string, externalId string) error } // - micContents, err := getMicAllContent(model, groupUid) + micContents, err := GetMicAllContent(model, groupUid) if err != nil { - model.Log.Errorf("MicChangeRPush MicAllRPush getMicAllContent err:%+v, micContents:%v groupUuid:%v, externalId:%v", err, micContents, groupUid, externalId) + model.Log.Errorf("MicChangeRPush MicAllRPush GetMicAllContent err:%+v, micContents:%v groupUuid:%v, externalId:%v", err, micContents, groupUid, externalId) return err } for _, micContent := range micContents { @@ -319,7 +319,7 @@ func MicRPush(model *domain.Model, txGroupId string, msg GroupSystemMsg) error { } //得使用旧的imGroupId -func getMicAllContent(model *domain.Model, groupUid string) ([]MicContent, error) { +func GetMicAllContent(model *domain.Model, groupUid string) ([]MicContent, error) { txGroupId, err := ToTxGroupId(model, groupUid) if err != nil { @@ -340,6 +340,9 @@ func getMicAllContent(model *domain.Model, groupUid string) ([]MicContent, error userIds := make([]uint64, 0, len(micUsers)) for _, r := range micUsers { userIds = append(userIds, r.UserId) + if r.CpUserId > 0 { + userIds = append(userIds, r.CpUserId) + } } model.Log.Infof("MicChangeRPush getMicAllContent groupUid:%v, userIds:%+v", groupUid, userIds) @@ -350,6 +353,13 @@ func getMicAllContent(model *domain.Model, groupUid string) ([]MicContent, error micContents := make([]MicContent, 0, len(mics)) for _, r := range mics { + var micEffect string + cpUserId := micUserMap[r.I].CpUserId + micUserData := micUserDataMap[micUserMap[r.I].UserId] + if cpUserId > 0 { + micEffect = "https://image.whoisamy.shop/hilo/resource/svga/mic_effect_cp.svga" + micUserData.MicEffect = micEffect + } micContents = append(micContents, MicContent{ GroupId: txGroupId, I: r.I, @@ -359,7 +369,7 @@ func getMicAllContent(model *domain.Model, groupUid string) ([]MicContent, error ExternalId: micUserMap[r.I].ExternalId, AgoraId: uint32(micUserMap[r.I].UserId), Timestamp: time.Now().UnixNano(), - User: micUserDataMap[micUserMap[r.I].UserId], + User: micUserData, }) } return micContents, nil @@ -448,6 +458,7 @@ func getMicUserDatas(model *domain.Model, userIds []uint64) (map[uint64]*MicUser return nil, err } svips, _ := rpc.MGetUserSvip(model, userIds) + cpRelations, _ := rpc.MGetUserCpRelations(model, userIds) micUserDataMap := map[uint64]*MicUserData{} for _, id := range userIds { @@ -461,11 +472,11 @@ func getMicUserDatas(model *domain.Model, userIds []uint64) (map[uint64]*MicUser } var headwearPicUrl string var headwearEffectUrl string - var reverseHeadwearEffectUrl string + var headwearReverseEffectUrl string if headwearUser, flag := headwearMap[id]; flag { headwearPicUrl = resHeadwearMap[headwearUser.HeadwearId].PicUrl headwearEffectUrl = resHeadwearMap[headwearUser.HeadwearId].EffectUrl - reverseHeadwearEffectUrl = resHeadwearMap[headwearUser.HeadwearId].ReverseEffectUrl + headwearReverseEffectUrl = resHeadwearMap[headwearUser.HeadwearId].ReverseEffectUrl } micUserDataMap[id] = &MicUserData{ Id: user.ID, @@ -478,9 +489,10 @@ func getMicUserDatas(model *domain.Model, userIds []uint64) (map[uint64]*MicUser NobleLeave: nobleMap[id], HeadwearPicUrl: headwearPicUrl, HeadwearEffectUrl: headwearEffectUrl, - HeadwearReverseEffectUrl: reverseHeadwearEffectUrl, + HeadwearReverseEffectUrl: headwearReverseEffectUrl, SvipLevel: svips[id].SvipLevel, Svip: rpc.CopySimpleSvip(svips[id]), + HeadwearIcon: cpRelations[user.ID].CpUserAvatar, } } return micUserDataMap, nil diff --git a/domain/service/event_s/event_init.go b/domain/service/event_s/event_init.go index 32e7f6fbcd52ee78f0e8e430af6181431ba6caac..d64f65393493ae35dc0b0c3eb7c9853f9223f96d 100644 --- a/domain/service/event_s/event_init.go +++ b/domain/service/event_s/event_init.go @@ -35,8 +35,9 @@ func EventInit() { GroupEvents() GroupImMass() GroupTheme() - SendGift() // 送礼事件 - OnMic() // 在麦上事件 + SendGift() // 送礼事件 + OnMic() // 在麦上事件 + GroupInMicChangeEvent() // 用户进房推送mic位置信息 } func GroupSupportEvents() { diff --git a/domain/service/event_s/group_in.go b/domain/service/event_s/group_in.go new file mode 100644 index 0000000000000000000000000000000000000000..ac4705ae6b52ba8ad055edd3e4366b3e791aba5d --- /dev/null +++ b/domain/service/event_s/group_in.go @@ -0,0 +1,83 @@ +package event_s + +import ( + "encoding/json" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/mysql" + "golang.org/x/sync/errgroup" + "hilo-group/domain/event/group_ev" + "hilo-group/domain/model/event_m" + "runtime/debug" +) + +type GroupInEventService struct { + svc *domain.Service +} + +func NewGroupInEventService(myContext *mycontext.MyContext) *GroupInEventService { + svc := domain.CreateService(myContext) + return &GroupInEventService{svc} +} + +// +func (s *GroupInEventService) Consume() error { + defer func() { + if err := recover(); err != nil { + s.svc.Log.Errorf("ExceptionHandle GroupInEventService Consume SYSTEM ACTION PANIC: %v, stack: %v", err, string(debug.Stack())) + } + }() + var model = domain.CreateModel(s.svc.CtxAndDb) + events, offset, err := event_m.FetchEventGroupIn(model, BatchCount) + if err != nil { + return err + } + var wg errgroup.Group + for k := range events { + cpEvent := &event_m.EventGroupIn{ + Entity: mysql.Entity{ + ID: events[k].ID, + CreatedTime: events[k].CreatedTime, + UpdatedTime: events[k].UpdatedTime, + }, + Proto: events[k].Proto, + Payload: events[k].Payload, + Mark: events[k].Mark, + MarkHiloGroup: events[k].MarkHiloGroup, + } + wg.Go(func() error { + if cpEvent.MarkHiloGroup == mysql.YES { + model.Log.Warnf("already consume msg :%v", cpEvent) + return nil + } + groupInEvent := new(group_ev.TxGroupInEvent) + if err := json.Unmarshal(cpEvent.Payload, groupInEvent); err != nil { + model.Log.Errorf("json msg fail,event:%v,err:%v", cpEvent, err) + return nil + } + // 发布事件 + if err := group_ev.PublishTxGroupInEvent(model, groupInEvent); err != nil { + model.Log.Errorf("PublishTxGroupInEvent,event:%v,err:%v", cpEvent, err) + return err + } + // 标记已经处理 + cpEvent.Model = model + err = cpEvent.MarkDone() + if err != nil { + model.Log.Errorf("consume msg fail,event:%v,err:%v", cpEvent, err) + } + return err + }) + } + err = wg.Wait() + if err != nil { + model.Log.Errorf("batch consume msg has fail,event,err:%v", err) + // 暂时先允许丢数据,继续mark offset + } + // 最后一次提交offset + if len(events) > 0 { + offset.MarkOffset = events[len(events)-1].ID + return offset.Persistence() + } + return nil +} diff --git a/domain/service/event_s/mic_change.go b/domain/service/event_s/mic_change.go new file mode 100644 index 0000000000000000000000000000000000000000..30b473f00362019cb06ad83a95f1ea78ac791d71 --- /dev/null +++ b/domain/service/event_s/mic_change.go @@ -0,0 +1,94 @@ +package event_s + +import ( + "encoding/json" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/protocol/userProxy" + "git.hilo.cn/hilo-common/rpc" + uuid "github.com/satori/go.uuid" + "hilo-group/domain/event/group_ev" + "hilo-group/domain/model/group_m" + "hilo-group/myerr/bizerr" +) + +// 用户进房后推送mic信息 +func GroupInMicChangeEvent() { + group_ev.AddTxGroupInEventAsync(func(model *domain.Model, event interface{}) error { + e, ok := event.(*group_ev.TxGroupInEvent) + if !ok { + return bizerr.InvalidParameter + } + return MicAllRPush(model, e.GroupId, e.UserId, e.ExternalId) + }) +} + +func MicAllRPush(model *domain.Model, imGroupId string, userId uint64, externalId string) error { + model.Log.Infof("MicChangeRPush MicAllRPush begin groupUuid:%v, externalId:%v", imGroupId, externalId) + + txGroupId, err := group_m.ToTxGroupId(model, imGroupId) + if err != nil { + model.Log.Errorf("ToImGroupId fail:%v", err) + return err + } + // + micContents, err := group_m.GetMicAllContent(model, imGroupId) + if err != nil { + model.Log.Errorf("MicChangeRPush MicAllRPush getMicAllContent err:%+v, micContents:%v groupUuid:%v, externalId:%v", err, micContents, txGroupId, externalId) + return err + } + for _, micContent := range micContents { + //麦上是默认值,就不用推 + if micContent.Forbid == false && micContent.User == nil && micContent.AgoraId == 0 && micContent.Lock == false && micContent.ExternalId == "" && micContent.MicForbid == false { + model.Log.Infof("MicChangeRPush MicAllRPush default micContent:%v, groupUuid:%v, externalId:%v, micContent:%+v", micContent, txGroupId, externalId, micContent) + continue + } + micContentStr, err := json.Marshal(micContent) + if err != nil { + continue + } + var micExternalId string + var micUserData *userProxy.MicUserData + if micContent.User != nil { + micExternalId = micContent.ExternalId + micUserData = &userProxy.MicUserData{ + Id: micContent.User.Id, + ExternalId: micContent.User.ExternalId, + Avatar: micContent.User.Avatar, + Nick: micContent.User.Nick, + Sex: uint32(micContent.User.Sex), + Code: micContent.User.Code, + IsVip: micContent.User.IsVip, + Noble: uint32(micContent.User.NobleLeave), + HeadwearPicUrl: micContent.User.HeadwearPicUrl, + HeadwearEffectUrl: micContent.User.HeadwearEffectUrl, + HeadwearReverseEffectUrl: micContent.User.HeadwearReverseEffectUrl, + SvipLevel: uint32(micContent.User.SvipLevel), + MicEffect: micContent.User.MicEffect, + HeadwearIcon: micContent.User.HeadwearIcon, + Svip: nil, + } + if micContent.User.Svip.SvipLevel > 0 || len(micContent.User.Svip.Privileges) > 0 { + micUserData.Svip = &userProxy.Svip{ + SvipLevel: uint64(micContent.User.SvipLevel), + } + for _, v := range micContent.User.Svip.Privileges { + micUserData.Svip.Privileges = append(micUserData.Svip.Privileges, &userProxy.SvipPrivilege{ + Type: int32(v.Type), + CanSwitch: v.CanSwitch, + UserSwitch: v.UserSwitch, + MysteryCode: v.MysteryCode, + }) + } + } + } + seqId := uuid.NewV4().String() + + if err := rpc.SendSocketMicChange(seqId, userId, micExternalId, txGroupId, uint32(micContent.I), micContent.Lock, micContent.Forbid, micContent.MicForbid, micContent.AgoraId, micUserData); err != nil { + model.Log.Errorf("MicAllRPush err:%+v, groupUuid:%v, externalId:%v, micContent:%+v", err, txGroupId, externalId, string(micContentStr)) + } else { + model.Log.Infof("MicChangeRPush MicAllRPush Marshal micContent, groupUuid:%v, externalId:%v, micContent:%+v", txGroupId, externalId, string(micContentStr)) + } + } + model.Log.Infof("MicAllRPush end groupUuid:%v, externalId:%v", txGroupId, externalId) + return nil +} diff --git a/go.mod b/go.mod index b08ac132bbdfa140b4e43eddf74a718643ae49f7..0224f6bac35923055c2c6c661ab3267527ccc14c 100644 --- a/go.mod +++ b/go.mod @@ -93,6 +93,7 @@ require ( github.com/tencentyun/tls-sig-api-v2-golang v1.0.0 // indirect github.com/ugorji/go/codec v1.1.7 // indirect golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e // indirect diff --git a/go.sum b/go.sum index 589d196488fc37938fe91bc5857e004fb2acc526..cdde2bb016e63bf18760c146739d03b81d2b327b 100644 --- a/go.sum +++ b/go.sum @@ -401,6 +401,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/route/group_r/group_info.go b/route/group_r/group_info.go index 68b8a4ce987930d2c49b529b8fde6219cda1b313..b98de503b47c53c6a56827f70812bc12161dac27 100644 --- a/route/group_r/group_info.go +++ b/route/group_r/group_info.go @@ -604,7 +604,8 @@ func GetRoomInfo(c *gin.Context) (*mycontext.MyContext, error) { model.Log.Infof("GetRoomInfo: GetLuckWheelState: %s", err.Error()) } // 群主的详情 - result.Owner, err = user_cv.GetUserDetail(model, groupInfo.Owner, userId) + owner, err := user_cv.GetUserDetail(model, groupInfo.Owner, userId) + result.Owner = user_cv.UserDetailToRoomInfoOwner(owner) if err != nil { model.Log.Errorf("GetRoomInfo: GetUserBase: %s", err.Error()) } diff --git a/route/group_r/group_list.go b/route/group_r/group_list.go index e9d97f2fc8383fd6f6a42020f1e09bb36585292c..03e570d4d8589d25fada90b01008e6d3c01a1fb5 100644 --- a/route/group_r/group_list.go +++ b/route/group_r/group_list.go @@ -796,7 +796,7 @@ func GetMyGroup(c *gin.Context) (*mycontext.MyContext, error) { // @Param groupId path string true "群ID" // @Param pageSize query int false "分页大小 默认:10" default(10) // @Param pageIndex query int false "第几个分页,从1开始 默认:1" default(1) -// @Success 200 {object} GroupMembersRsp +// @Success 200 {object} GetGroupVisitorsRsp // @Router /v1/imGroup/visitors/{groupId} [get] func GetGroupVisitors(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) @@ -847,7 +847,7 @@ func GetGroupVisitors(c *gin.Context) (*mycontext.MyContext, error) { } model.Log.Infof("GetGroupVisitors %s: memberNum = %d, user size = %d", groupId, len(rows), len(userIds)) - result := GroupMembersRsp{Total: uint(len(userIds))} + result := GetGroupVisitorsRsp{Total: uint(len(userIds))} beginPos := pageSize * (pageIndex - 1) if uint(beginPos) < result.Total { @@ -961,11 +961,11 @@ func GetGroupVisitors(c *gin.Context) (*mycontext.MyContext, error) { if _, ok := roomUserMap[u]; ok { inRoom = true } - result.Members = append(result.Members, group_cv.MemberDetail{ - CvUserExtend: userExtends[u], - Role: roles[u], - OnlineStatus: statusMap[users[u].ExternalId], - InRoom: inRoom, + result.Members = append(result.Members, group_cv.GroupVisitorsDetail{ + CvGroupMember: user_cv.CvUserExtendToCvGroupMember(userExtends[u]), + Role: roles[u], + OnlineStatus: statusMap[users[u].ExternalId], + InRoom: inRoom, }) } } diff --git a/route/group_r/group_mic.go b/route/group_r/group_mic.go index c85a3002d55ec13876905411c02101baa4b3f4e6..8484b949e7ce5988fa0e166772f32dc5b5d09631 100644 --- a/route/group_r/group_mic.go +++ b/route/group_r/group_mic.go @@ -550,12 +550,13 @@ func GroupMicIn(c *gin.Context) (*mycontext.MyContext, error) { return myContext, err } if micUser != nil { - if iStr == "" { + // 同房间且上麦序列为空 + if iStr == "" && micUser.GroupUuid == groupUuid { // 非切麦操作 return myContext, bizerr.GroupMicUserHasIn } // 切换麦位,先下麦 - if err := group_mic_s.NewGroupPowerService(myContext).GroupMicLeave(groupUuid, micUser.I, userId, externalId); err != nil { + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicLeave(micUser.GroupUuid, micUser.I, userId, externalId); err != nil { return myContext, err } } diff --git a/route/group_r/group_op.go b/route/group_r/group_op.go index f74a21c6f6acac391dadf836bc6edf8eec808204..120bc8f9f96993204edb329816f85e54b91ed042 100644 --- a/route/group_r/group_op.go +++ b/route/group_r/group_op.go @@ -1238,6 +1238,12 @@ type GroupMembersRsp struct { Total uint `json:"total"` } +type GetGroupVisitorsRsp struct { + Members []group_cv.GroupVisitorsDetail `json:"members"` + Online uint `json:"online"` // 在线人数 + Total uint `json:"total"` +} + // @Tags 群组 // @Summary 获取永久成员列表 // @Accept application/x-www-form-urlencoded