diff --git a/_const/enum/game_e/game.go b/_const/enum/game_e/game.go index d8a3786c4032275f59f76780ac6453c461c90dbb..5590863f0023ccceb7b255f60ba91dda05967f38 100644 --- a/_const/enum/game_e/game.go +++ b/_const/enum/game_e/game.go @@ -1,5 +1,7 @@ package game_e +import "fmt" + type GameType uint32 var GameLudoDiamondList = []uint32{0, 100, 500, 1000, 5000, 10000} @@ -8,3 +10,9 @@ const ( GameTypeLudo GameType = 1 // ludo GameTypeUno GameType = 2 // uno ) + +const GameAutoMathEnterRoom = "game:autoEnter:%d:%s" // 快速游戏,进入某个房间 + +func GetAutoMathEnterRoom(userId uint64, imGroupId string) string { + return fmt.Sprintf(GameAutoMathEnterRoom, userId, imGroupId) +} diff --git a/_const/enum/task_e/enum.go b/_const/enum/task_e/enum.go new file mode 100644 index 0000000000000000000000000000000000000000..1a20e4bc5959c860e27916f4856b41dabdec7096 --- /dev/null +++ b/_const/enum/task_e/enum.go @@ -0,0 +1,24 @@ +package task_e + +import "git.hilo.cn/hilo-common/resource/mysql" + +type RateTypeTaskConfig = mysql.Type + +const ( + Daily RateTypeTaskConfig = 1 + OnlyOne RateTypeTaskConfig = 2 +) + +type TypeTaskConfig = mysql.Type + +const ( + MicIn TypeTaskConfig = 1 //上麦10分钟 + SendGift TypeTaskConfig = 2 //送礼物 + SendGift5Person TypeTaskConfig = 3 //给5个人送出礼物 + GroupImMass TypeTaskConfig = 4 //发布会员广播 + SendGlobalBroadcast TypeTaskConfig = 5 //发布全球广播 + PayFirst TypeTaskConfig = 6 //首次充值 + Level5 TypeTaskConfig = 7 //任意等级达到5级(财富,活跃,魅力) + WatchAd TypeTaskConfig = 8 // 观看广告 + VideoChat TypeTaskConfig = 9 // 视频任务(暂只统计匹配) +) diff --git a/_const/redis_key/mic_k/keys.go b/_const/redis_key/mic_k/keys.go new file mode 100644 index 0000000000000000000000000000000000000000..dfdf5d82765856c25af53be88b9c8fb595a9e7fe --- /dev/null +++ b/_const/redis_key/mic_k/keys.go @@ -0,0 +1,18 @@ +package mic_k + +import ( + "fmt" + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-group/_const/redis_key" + "time" +) + +const ( + MicPrefix = "mic:" + MicDayInvite = MicPrefix + "day:invite:${userId}:${date}" // string 自动被邀请上麦,1天一次,TTL:24H +) + +func GetUserMicDayInvite(userId mysql.ID) string { + date := time.Now().Format("2006-01-02") + return redis_key.ReplaceKey(MicDayInvite, fmt.Sprintf("%d", userId), date) +} diff --git a/cv/group_cv/group.go b/cv/group_cv/group.go index d0da5649fabbd3c43d6c9114e9bfad7bde3318c0..b5b2035463e85c532d0336e5bea86124bc83c2bd 100644 --- a/cv/group_cv/group.go +++ b/cv/group_cv/group.go @@ -206,6 +206,29 @@ type RoomMedalInfo struct { Desc string `json:"desc"` } +type GroupChannelId struct { + ChannelId string `json:"channelId"` + Token string `json:"token"` + AgoraId uint32 `json:"agoraId"` + MicNumType uint8 `json:"micNumType"` +} + +//国籍视图 +type CvCountry struct { + //名字 + Name *string `json:"name"` + //缩写 + ShortName *string `json:"shortName"` + //图标地址 + Icon *string `json:"icon"` + //code + Code *string `json:"code"` + // 手机区号 + AreaCode *string `json:"areaCode"` + //手机号国家域名缩写 + AreaCodeName *string `json:"areaShortName"` +} + func BuildJoinedGroupInfo(myService *domain.Service, myUserId uint64, groupIds []string, pageSize, pageIndex int) ([]JoinedGroupInfo, int, error) { model := domain.CreateModel(myService.CtxAndDb) diff --git a/cv/group_cv/group_mic.go b/cv/group_cv/group_mic.go new file mode 100644 index 0000000000000000000000000000000000000000..e17d043557ce034f4e34f8acdd9e76baf07cdb3d --- /dev/null +++ b/cv/group_cv/group_mic.go @@ -0,0 +1,53 @@ +package group_cv + +import ( + "hilo-group/domain/model/group_m" + "time" +) + +//麦位信息, +type CvMic struct { + //麦位 + I int `json:"i"` + //锁,是否有锁 true:锁了, false:没锁 + Lock bool `json:"lock"` + //静音 true:静音,false:没有静音 + Forbid bool `json:"forbid"` + //如果 nil 则代表这个位置上没有人 + ExternalId *string `json:"externalId"` + //声网agoraId 如果 nil 则代表这个位置上没有人 + AgoraId *uint32 `json:"agoraId"` + //上麦时间戳 + Timestamp int64 `json:"timestamp"` +} + +//获取群组中所有的mic位信息 +func GetGroupMicAll(mics []group_m.Mic, micUsers []group_m.MicUser) ([]CvMic, error) { + timestamp := time.Now().Unix() + micUserMap := map[int]*group_m.MicUser{} + for i := 0; i < len(micUsers); i++ { + micUserMap[micUsers[i].I] = &(micUsers[i]) + } + // + var cvMics []CvMic + for _, v := range mics { + forbid := false + var externalId *string = nil + var agoraId *uint32 = nil + if u, ok := micUserMap[v.I]; ok { + forbid = u.Forbid + externalId = &u.ExternalId + tmp := uint32(u.UserId) + agoraId = &tmp + } + cvMics = append(cvMics, CvMic{ + I: v.I, + Lock: v.Lock, + Forbid: forbid, + ExternalId: externalId, + AgoraId: agoraId, + Timestamp: timestamp, + }) + } + return cvMics, nil +} diff --git a/cv/mic_cv/res.go b/cv/mic_cv/res.go new file mode 100644 index 0000000000000000000000000000000000000000..38be24e9630f4885af804676857ba9240985d037 --- /dev/null +++ b/cv/mic_cv/res.go @@ -0,0 +1,13 @@ +package mic_cv + +//麦位表情 +type CvMicEmoji struct { + //Id + Id uint64 `json:"id"` + //名字 + Name string `json:"name"` + //图片地址 + IconUrl string `json:"iconUrl"` + //特效地址 + SvagUrl string `json:"svagUrl"` +} diff --git a/cv/property_cv/property.go b/cv/property_cv/property.go index c7e61243c81b177f73e5c0b1b43f590d867f0e00..280eb4141f3379efd1e5da13602be90bd073ae07 100644 --- a/cv/property_cv/property.go +++ b/cv/property_cv/property.go @@ -232,3 +232,71 @@ func GetPropertyList(db *gorm.DB, userId uint64) ([]CvProperty, error) { } return result, nil } + +type PropertyExt struct { + Id uint64 + PicUrl mysql.Str + EffectUrl mysql.Str + Using bool + TimeLeft int64 // 离到期还有多少秒(过期则是负数) + SenderAvatar string + ReceiverAvatar string +} + +func GetExtendedProperty(db *gorm.DB) (map[uint64]PropertyExt, error) { + rp := res_m.ResProperty{} + properties, err := rp.GetAll(mysql.Db) + if err != nil { + return nil, err + } + + //获取座驾头像 + propertieAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + + userIds := []uint64{} + for _, value := range propertieAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + + result := map[uint64]PropertyExt{} + for _, r := range properties { + + var senderAvatar string = "" + var receiverAvatar string = "" + if propertieAvatar, flag := propertieAvatarMap[r.ID]; flag { + if propertieAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertieAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + + result[r.ID] = PropertyExt{ + Id: r.ID, + PicUrl: r.PicUrl, + EffectUrl: r.EffectUrl, + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + } + } + return result, nil +} diff --git a/domain/cache/game_c/game.go b/domain/cache/game_c/game.go new file mode 100644 index 0000000000000000000000000000000000000000..67298eee60fea0d640a86d371b266c3eec7a6cf6 --- /dev/null +++ b/domain/cache/game_c/game.go @@ -0,0 +1,49 @@ +package game_c + +import ( + "context" + "encoding/json" + "git.hilo.cn/hilo-common/resource/redisCli" + "hilo-group/_const/enum/game_e" + "time" +) + +type gameAutoJoinMsg struct { + TraceId string + Token string + EnterType string + GameCode string +} + +func SetAutoMathEnterRoom(userId uint64, imGroupId, traceId, token, enterType, gameCode string) error { + key := game_e.GetAutoMathEnterRoom(userId, imGroupId) + info := gameAutoJoinMsg{traceId, token, enterType, gameCode} + data, err := json.Marshal(info) + if err != nil { + return err + } + err = redisCli.GetRedis().LPush(context.Background(), key, data).Err() + if err != nil { + return err + } + redisCli.GetRedis().Expire(context.Background(), key, time.Second*3) + return nil +} + +func IsAutoMathEnterRoom(userId uint64, imGroupId string) (bool, string, string, string, string) { + key := game_e.GetAutoMathEnterRoom(userId, imGroupId) + data, err := redisCli.GetRedis().RPop(context.Background(), key).Bytes() + if err != nil { + return false, "", "", "", "" + } + info := gameAutoJoinMsg{} + err = json.Unmarshal(data, &info) + if err != nil { + return false, "", "", "", "" + } + if info.Token != "" && info.TraceId != "" && info.EnterType != "" && info.GameCode != "" { + redisCli.GetRedis().Del(context.Background(), key) + return true, info.TraceId, info.Token, info.EnterType, info.GameCode + } + return false, "", "", "", "" +} diff --git a/domain/cache/group_c/group.go b/domain/cache/group_c/group.go index 1dd9d5cf88c3cac9ac60338532b6bb2577e8a7ab..f9293cfebd0c3855a94dea9170e6789f5b6d7baf 100644 --- a/domain/cache/group_c/group.go +++ b/domain/cache/group_c/group.go @@ -62,20 +62,9 @@ func addGroupMember(groupId string, extIds []string) (int64, error) { return redisCli.RedisClient.SAdd(context.Background(), key, extIds).Result() } -func AddGroupMember(model *domain.Model, groupId string, extId string) error { +func AddGroupMember(groupId string, extIds []string) (int64, error) { key := getGroupMemberKey(groupId) - ret, err := redisCli.RedisClient.Exists(context.Background(), key).Result() - if err != nil { - model.Log.Infof("AddGroupMember %s, skip because set does not exist", groupId) - return err - } - if ret == 0 { - // 集合不存在,不要加进去! - return nil - } - ret, err = addGroupMember(groupId, []string{extId}) - model.Log.Infof("AddGroupMember %s, %s ret = %d, err: %v", groupId, extId, ret, err) - return err + return redisCli.RedisClient.SAdd(context.Background(), key, extIds).Result() } func RemoveGroupMember(groupId string, externalId string) (int64, error) { diff --git a/domain/cache/mic_c/mic.go b/domain/cache/mic_c/mic.go new file mode 100644 index 0000000000000000000000000000000000000000..dcf7a5713391d986fdbe5fd7d3ebed2caf141b26 --- /dev/null +++ b/domain/cache/mic_c/mic.go @@ -0,0 +1,26 @@ +package mic_c + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/config" + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-group/_const/redis_key/mic_k" + "time" +) + +// 增加上麦邀请次数,一天只需要第一次进房弹出 +// return show:true 今天已经展示过了 +func IsMicDayInviteDialogShowToday(model *domain.Model, userId mysql.ID) (show bool, err error) { + key := mic_k.GetUserMicDayInvite(userId) + ttl := time.Hour * 24 + if !config.AppIsRelease() { + ttl = time.Hour + } + lock, err := model.Redis.SetNX(model, key, 1, ttl).Result() + if err != nil { + model.Log.Errorf("IsMicDayInviteDialogShowToday uid:%v, fail:%v", userId, err) + return false, err + } + // !lock = show + return !lock, err +} diff --git a/domain/cache/room_c/userRoomVisit.go b/domain/cache/room_c/userRoomVisit.go index 7802fbf882b30dd03b081dae493f4b811c0abb8d..710286ae62c0d99c32df6302fa396fb1bd5a2ee3 100644 --- a/domain/cache/room_c/userRoomVisit.go +++ b/domain/cache/room_c/userRoomVisit.go @@ -12,6 +12,37 @@ import ( "time" ) +// 处理访问房间的相关缓存 +func ProcessRoomVisit(groupId string, userId uint64) error { + key := redis_key.GetPrefixGroupInUserDuration(groupId) + now := time.Now() + ret, err := redisCli.GetRedis().ZAdd(context.Background(), key, &redis2.Z{ + Score: float64(now.Unix()), + Member: userId, + }).Result() + if err != nil { + return err + } + mylogrus.MyLog.Infof("ProcessRoomVisit, ZADD %s, return %d", key, ret) + + // 每群定时清一次数据可以了 + if now.Second()%redis_key.GroupInDurationClearPeriod == 0 { + rc, err := clearRoomVisit(groupId, now.AddDate(0, 0, -redis_key.GroupInDurationTTL)) + if err == nil { + mylogrus.MyLog.Infof("ProcessRoomVisit, clearRoomVisit %s, return %d", key, rc) + } else { + mylogrus.MyLog.Warnf("ProcessRoomVisit, clearRoomVisit %s, failed %s", key, err.Error()) + } + } + + // 马上更新roomVisitCount + if _, err := GetSetRoomVisitCount(groupId); err != nil { + mylogrus.MyLog.Warnf("ProcessRoomVisit, failed for key %s, err: %s", key, err.Error()) + } + + return nil +} + func ProcessUserRoomVisit(userId uint64, groupId string) error { key := redis_key.GetUserEnterRoomKey(userId) now := time.Now() @@ -80,3 +111,17 @@ func saveRoomVisitCount(groupId string, count int64) (int64, error) { key := redis_key.GetPrefixRoomVisitCount() return redisCli.GetRedis().HSet(context.Background(), key, groupId, strconv.FormatInt(count, 10)).Result() } + +func clearRoomVisit(groupId string, t time.Time) (int64, error) { + value := strconv.FormatInt(t.Unix(), 10) + ret, err := redisCli.GetRedis().ZRemRangeByScore(context.Background(), redis_key.GetPrefixGroupInUserDuration(groupId), "0", value).Result() + if err != nil { + return 0, err + } + return ret, nil +} + +func GetAllRoomVisitCount() (map[string]string, error) { + key := redis_key.GetPrefixRoomVisitCount() + return redisCli.GetRedis().HGetAll(context.Background(), key).Result() +} diff --git a/domain/cache/tim_c/group.go b/domain/cache/tim_c/group.go new file mode 100644 index 0000000000000000000000000000000000000000..78fac6c5fd54c5ab8174c260b8e70e9cff4adff2 --- /dev/null +++ b/domain/cache/tim_c/group.go @@ -0,0 +1,49 @@ +package tim_c + +import ( + "git.hilo.cn/hilo-common/domain" + "hilo-group/domain/cache/group_c" + "time" +) + +func setGroupMember(model *domain.Model, groupId string, extIds []string) error { + err := BatchAddGroupMember(model, groupId, extIds) + if err == nil { + ret, err := group_c.SetGroupMemberTTL(groupId, time.Hour) + model.Log.Infof("SetGroupMemberTTL %s, ret = %t, err: %v", groupId, ret, err) + } + return err +} + +func BatchAddGroupMember(model *domain.Model, groupId string, extIds []string) error { + ret, err := group_c.AddGroupMember(groupId, extIds) + model.Log.Infof("BatchAddGroupMember %s, %v ret = %d, err: %v", groupId, extIds, ret, err) + return err +} + +func AddGroupMember(model *domain.Model, groupId string, extId string) error { + ret, err := group_c.SetExists(groupId) + if err != nil { + model.Log.Infof("AddGroupMember %s, skip because set does not exist", groupId) + return err + } + if ret == 0 { + // 集合不存在,不要加进去! + return nil + } + ret, err = group_c.AddGroupMember(groupId, []string{extId}) + model.Log.Infof("AddGroupMember %s, %s ret = %d, err: %v", groupId, extId, ret, err) + return err +} + +func RemoveGroupMember(model *domain.Model, groupId string, extId string) error { + ret, err := group_c.RemoveGroupMember(groupId, extId) + model.Log.Infof("RemoveGroupMember %s, %s ret = %d, err: %v", groupId, extId, ret, err) + return err +} + +func GetGroupMemberCount(model *domain.Model, groupId string) (uint, error) { + ret, err := group_c.GetGroupMemberCard(groupId) + model.Log.Infof("GetGroupMemberCount %s, ret = %d, err: %v", groupId, ret, err) + return uint(ret), err +} diff --git a/domain/event/group_ev/buy_theme.go b/domain/event/group_ev/buy_theme.go new file mode 100644 index 0000000000000000000000000000000000000000..6aa10dba36665328dcfe4f8417ed7e0345a7c193 --- /dev/null +++ b/domain/event/group_ev/buy_theme.go @@ -0,0 +1,29 @@ +package group_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +type BuyGroupCustomThemeEvent struct { + GroupCustomThemeId mysql.ID + UserId mysql.ID +} + +//注册监听 +var buyGroupCustomThemeListen = new(domain.EventBase) + +//添加领域事件,在每个领域模型中init中添加,因为这是静态业务,非动态的。 +func AddBuyGroupCustomThemeSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(buyGroupCustomThemeListen, callback) +} + +//加入到异步操作中 +func AddBuyGroupCustomThemeAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(buyGroupCustomThemeListen, callback) +} + +//领域事件发布 +func PublishBuyGroupCustomTheme(model *domain.Model, event *BuyGroupCustomThemeEvent) error { + return domain.PublishEvent(buyGroupCustomThemeListen, model, event) +} diff --git a/domain/event/group_ev/group_im_mass.go b/domain/event/group_ev/group_im_mass.go new file mode 100644 index 0000000000000000000000000000000000000000..2aef3d549daa541ab86997f4e82640e1ac007b0f --- /dev/null +++ b/domain/event/group_ev/group_im_mass.go @@ -0,0 +1,34 @@ +package group_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +/** +* IM群发 + */ +type GroupImMassEvent struct { + GroupId string + UserId mysql.ID + Members []uint64 + Content string +} + +//注册监听 +var groupImMassListen = new(domain.EventBase) + +//添加领域事件,在每个领域模型中init中添加,因为这是静态业务,非动态的。 +func AddGroupImMassSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(groupImMassListen, callback) +} + +//加入到异步操作中 +func AddGroupImMassAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(groupImMassListen, callback) +} + +//领域事件发布 +func PublishGroupImMass(model *domain.Model, event *GroupImMassEvent) error { + return domain.PublishEvent(groupImMassListen, model, event) +} diff --git a/domain/event/group_ev/group_in.go b/domain/event/group_ev/group_in.go new file mode 100644 index 0000000000000000000000000000000000000000..bfff03369d89d7f29b438d5aacd84fd6644a59d3 --- /dev/null +++ b/domain/event/group_ev/group_in.go @@ -0,0 +1,35 @@ +package group_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +type GroupInEvent struct { + GroupId string + UserId mysql.ID + ExternalId string + Nick string + Avatar string + IsMember bool //是否永久成员 + IsVip bool + NobleLevel uint16 +} + +//注册监听 +var groupInListen = new(domain.EventBase) + +//添加领域事件,在每个领域模型中init中添加,因为这是静态业务,非动态的。 +func AddGroupInSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(groupInListen, callback) +} + +//加入到异步操作中 +func AddGroupInAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(groupInListen, callback) +} + +//领域事件发布 +func PublishGroupIn(model *domain.Model, event *GroupInEvent) error { + return domain.PublishEvent(groupInListen, model, event) +} diff --git a/domain/event/group_ev/group_kickout.go b/domain/event/group_ev/group_kickout.go new file mode 100644 index 0000000000000000000000000000000000000000..f0eed74cb3bcd912305577c678595a1a839f7ff7 --- /dev/null +++ b/domain/event/group_ev/group_kickout.go @@ -0,0 +1,36 @@ +package group_ev + +import ( + "git.hilo.cn/hilo-common/domain" +) + +/** +* + */ +type GroupKickOutEvent struct { + GroupId string + OperatorExternalId string `json:"operatorExternalId"` + OperatorName string `json:"operatorName"` + OperatorFaceUrl string `json:"operatorFaceUrl"` + MemberExternalId string `json:"memberExternalId"` + MemberName string `json:"memberName"` + MemberAvatar string `json:"memberAvatar"` +} + +//注册监听 +var groupKickOutListen = new(domain.EventBase) + +//添加领域事件,在每个领域模型中init中添加,因为这是静态业务,非动态的。 +func AddGroupKickOutSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(groupKickOutListen, callback) +} + +//加入到异步操作中 +func AddGroupKickOutAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(groupKickOutListen, callback) +} + +//领域事件发布 +func PublishGroupKickOut(model *domain.Model, event interface{}) error { + return domain.PublishEvent(groupKickOutListen, model, event) +} diff --git a/domain/model/diamond_m/diamond.go b/domain/model/diamond_m/diamond.go index 501d9bd6801a21a714bd9348c87daec42f141c7b..76929c1982e48997e2d967c344fc2392c3fcaaef 100644 --- a/domain/model/diamond_m/diamond.go +++ b/domain/model/diamond_m/diamond.go @@ -268,3 +268,13 @@ func (diamondAccountDetail *DiamondAccountDetail) Persistent() error { func (diamondAccount *DiamondAccount) GroupSupportMgr(groupSupportAwardId mysql.ID, diamondNum uint32) (*DiamondAccountDetail, error) { return diamondAccount.addDiamondAccountDetail(diamond_e.GroupSupportMgr, groupSupportAwardId, diamondNum) } + +//群组IM群发 +func (diamondAccount *DiamondAccount) GroupImMass() (*DiamondAccountDetail, error) { + return diamondAccount.addDiamondAccountDetail(diamond_e.GroupIMMass, 0, 0) +} + +//购买自定义群组主题 +func (diamondAccount *DiamondAccount) BuyGroupCustomTheme(diamondAccountDetailId mysql.ID) (*DiamondAccountDetail, error) { + return diamondAccount.addDiamondAccountDetail(diamond_e.GroupCustomTheme, diamondAccountDetailId, 0) +} diff --git a/domain/model/group_m/room.go b/domain/model/group_m/room.go index c7dafde650ff988bda198704a2e7eb0049197038..e0898ed65d5648ed65ffa6adfc70e6c602c6992b 100644 --- a/domain/model/group_m/room.go +++ b/domain/model/group_m/room.go @@ -457,7 +457,7 @@ type UserEnterRoom struct { EnterTime time.Time } -func (uer *UserEnterRoom) save(db *gorm.DB) error { +func (uer *UserEnterRoom) Save(db *gorm.DB) error { return db.Clauses(clause.OnConflict{UpdateAll: true}).Create(uer).Error } diff --git a/domain/model/mgr_m/repo.go b/domain/model/mgr_m/repo.go new file mode 100644 index 0000000000000000000000000000000000000000..7d064f7fb1c13419930a0282b8d43ecce628540c --- /dev/null +++ b/domain/model/mgr_m/repo.go @@ -0,0 +1,13 @@ +package mgr_m + +import ( + "hilo-group/domain/model" + "hilo-group/myerr" +) + +func (mgrReportGroup *MgrReportGroup) Persistent() error { + if err := model.Persistent(mgrReportGroup.Db, mgrReportGroup); err != nil { + return myerr.WrapErr(err) + } + return nil +} diff --git a/domain/model/mgr_m/report.go b/domain/model/mgr_m/report.go new file mode 100644 index 0000000000000000000000000000000000000000..55bf489b0c6fd9cedb80fe53e61cb0a7ef16ec2c --- /dev/null +++ b/domain/model/mgr_m/report.go @@ -0,0 +1,103 @@ +package mgr_m + +import ( + "fmt" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-group/_const/enum/mgr_e" + "hilo-group/myerr" +) + +//投诉 +type MgrReport struct { + mysql.Entity + *domain.Model `gorm:"-"` + FromUserId mysql.ID + ToUserId mysql.ID + FromPageType mgr_e.ReportPageType + ReasonType mgr_e.ReportReasonType + OriginId mysql.ID + ImageUrl mysql.Str + Reason mysql.Str + Status mgr_e.ReportStatus + ReportDealId mysql.ID +} + +//投诉-群组 +type MgrReportGroup struct { + mysql.Entity + *domain.Model `gorm:"-"` + FromUserId mysql.ID + GroupId mysql.Str + ReasonType mgr_e.ReportReasonType + ImageUrl mysql.Str + Reason mysql.Str + Status mgr_e.ReportStatus + ReportDealId mysql.ID +} + +func ReportAdd(model *domain.Model, fromUserId mysql.ID, toUserId mysql.ID, fromPageType mgr_e.ReportPageType, originId mysql.ID, reasonType mgr_e.ReportReasonType, imageUrl mysql.Str, reason mysql.Str) *MgrReport { + return &MgrReport{ + Model: model, + FromUserId: fromUserId, + ToUserId: toUserId, + FromPageType: fromPageType, + ReasonType: reasonType, + OriginId: originId, + ImageUrl: imageUrl, + Reason: reason, + Status: mgr_e.NoDealReportStatus, + } +} + +func ReportGroupAdd(model *domain.Model, fromUserId mysql.ID, groupId mysql.Str, reasonType mgr_e.ReportReasonType, imageUrl mysql.Str, reason mysql.Str) *MgrReportGroup { + return &MgrReportGroup{ + Model: model, + FromUserId: fromUserId, + GroupId: groupId, + ReasonType: reasonType, + ImageUrl: imageUrl, + Reason: reason, + Status: mgr_e.NoDealReportStatus, + } +} + +//检查投诉 +func checkReport(model *domain.Model, reportId mysql.ID) (*MgrReport, error) { + //var report = new(MgrReport) + var report MgrReport + if err := model.Db.First(&report, reportId).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, myerr.NewSysError("投诉不存在, Id:" + fmt.Sprintf("%d", reportId)) + } else { + return nil, myerr.WrapErr(err) + } + } + /* if report.Status == mgr_m.HasDealReportStatus { + return nil, myerr.NewSysError("投诉已被处理。, Id:" + mysql.IdToStr(reportId)) + }*/ + return &report, nil +} + +//投诉处理 +func ReportDeal(model *domain.Model, reportId mysql.ID) (*MgrReport, error) { + report, err := checkReport(model, reportId) + if err != nil { + return nil, err + } + report.Status = mgr_e.HasDealReportStatus + return report, nil +} + +func GetMyReport(userId mysql.ID) ([]uint64, error) { + users := make([]uint64, 0) + report := make([]MgrReport, 0) + if err := mysql.Db.Where("from_user_id = ?", userId).Find(&report).Error; err != nil && err != gorm.ErrRecordNotFound { + return nil, myerr.WrapErr(err) + } + for _, i := range report { + users = append(users, i.ToUserId) + } + return users, nil +} diff --git a/domain/model/res_m/emoji.go b/domain/model/res_m/emoji.go new file mode 100644 index 0000000000000000000000000000000000000000..4ead3c629c35532c7e48758d2953532c81dcb2d2 --- /dev/null +++ b/domain/model/res_m/emoji.go @@ -0,0 +1,48 @@ +package res_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +type ResMicEmoji struct { + mysql.Entity + *domain.Model `gorm:"-"` + Name mysql.Str + IconUrl mysql.Str + SvagUrl mysql.Str + N mysql.Num + Status mysql.UserYesNo +} + +//不可用 +func (resMicEmoji *ResMicEmoji) Disable() *ResMicEmoji { + resMicEmoji.Status = mysql.NOUSER + return resMicEmoji +} + +//上架 +func (resMicEmoji *ResMicEmoji) Enable() *ResMicEmoji { + resMicEmoji.Status = mysql.USER + return resMicEmoji +} + +func (resMicEmoji *ResMicEmoji) EditN(n uint32) *ResMicEmoji { + resMicEmoji.N = n + return resMicEmoji +} + +func (resMicEmoji *ResMicEmoji) EditName(name string) *ResMicEmoji { + resMicEmoji.Name = name + return resMicEmoji +} + +func (resMicEmoji *ResMicEmoji) EditIconUrl(iconUrl string) *ResMicEmoji { + resMicEmoji.IconUrl = iconUrl + return resMicEmoji +} + +func (resMicEmoji *ResMicEmoji) EditSvagUrl(svagUrl string) *ResMicEmoji { + resMicEmoji.SvagUrl = svagUrl + return resMicEmoji +} diff --git a/domain/model/task_m/repo.go b/domain/model/task_m/repo.go new file mode 100644 index 0000000000000000000000000000000000000000..70c553ab90ccba3a2313c373606c35906ac1e144 --- /dev/null +++ b/domain/model/task_m/repo.go @@ -0,0 +1,20 @@ +package task_m + +import ( + "hilo-group/domain/model" + "hilo-group/myerr" +) + +func (taskUser *TaskUser) Persistent() error { + if err := model.Persistent(taskUser.Db, taskUser); err != nil { + return myerr.WrapErr(err) + } + return nil +} + +func (taskUserDetail *TaskUserDetail) Persistent() error { + if err := model.Persistent(taskUserDetail.Db, taskUserDetail); err != nil { + return myerr.WrapErr(err) + } + return nil +} \ No newline at end of file diff --git a/domain/model/task_m/task.go b/domain/model/task_m/task.go new file mode 100644 index 0000000000000000000000000000000000000000..c7dda86be6b81d4a7db8c572afa5acf9cc23cad4 --- /dev/null +++ b/domain/model/task_m/task.go @@ -0,0 +1,223 @@ +package task_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/utils" + "gorm.io/gorm" + "hilo-group/_const/enum/task_e" + "hilo-group/myerr" + "hilo-group/myerr/bizerr" + "strconv" + "time" +) + +type TaskConfig struct { + mysql.Entity + *domain.Model `gorm:"-"` + Name mysql.Str + Diamond mysql.Num + //任务类型 + Type task_e.TypeTaskConfig + //频率类型 + RateType task_e.RateTypeTaskConfig + //完成次数 + FinishN mysql.Num + //排序 + I mysql.Num + //奖励次数 + AwardN mysql.Num + Status mysql.UserYesNo +} + +type TaskUser struct { + mysql.Entity + *domain.Model `gorm:"-"` + TaskConfigId mysql.ID + UserId mysql.ID + DayStr mysql.Str + HasFinish mysql.YesNo + HasAward mysql.YesNo //整个完成奖励了 + AwardN mysql.Num //获取奖励次数 + FinishN mysql.Num +} + +type TaskUserDetail struct { + mysql.Entity + *domain.Model `gorm:"-"` + TaskConfigId mysql.ID + UserId mysql.ID + TaskUserId mysql.ID + OriginId mysql.ID +} + +func addTaskUserDetail(model *domain.Model, taskConfigId mysql.ID, userId mysql.ID, taskUserId mysql.ID, originId mysql.ID) error { + taskUserDetail := &TaskUserDetail{ + Model: model, + TaskConfigId: taskConfigId, + UserId: userId, + TaskUserId: taskUserId, + OriginId: originId, + } + return taskUserDetail.Persistent() +} + +func GetTaskUserOrErr(model *domain.Model, userId uint64, taskConfigId uint64) (*TaskUser, TaskConfig, error) { + //获取任务配置 + taskConfig := TaskConfig{} + if err := model.Db.Model(&TaskConfig{}).Where(&TaskConfig{ + Status: mysql.USER, + }).First(&taskConfig, taskConfigId).Error; err != nil { + return nil, TaskConfig{}, myerr.WrapErr(err) + } + // + paramTaskUser := TaskUser{ + TaskConfigId: taskConfig.ID, + UserId: userId, + } + if taskConfig.RateType == task_e.Daily { + paramTaskUser.DayStr = time.Now().Format(utils.COMPACT_DATE_FORMAT) + } + // + taskUser := TaskUser{} + if err := model.Db.Model(&TaskUser{}).Where(¶mTaskUser).First(&taskUser).Error; err != nil { + return nil, TaskConfig{}, myerr.WrapErr(err) + } + taskUser.Model = model + // + return &taskUser, taskConfig, nil +} + +func getTaskUserOrInit(model *domain.Model, userId uint64, taskConfig TaskConfig) (*TaskUser, error) { + //获取任务用户 + taskUser := TaskUser{} + paramTaskUser := TaskUser{ + TaskConfigId: taskConfig.ID, + UserId: userId, + } + if taskConfig.RateType == task_e.Daily { + paramTaskUser.DayStr = time.Now().Format(utils.COMPACT_DATE_FORMAT) + } + + if err := model.Db.Model(&TaskUser{}).Where(¶mTaskUser).First(&taskUser).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return &TaskUser{ + Model: model, + TaskConfigId: taskConfig.ID, + UserId: userId, + DayStr: paramTaskUser.DayStr, + HasFinish: mysql.NO, + HasAward: mysql.NO, + FinishN: 0, + }, nil + } else { + return nil, myerr.WrapErr(err) + } + } + taskUser.Model = model + return &taskUser, nil +} + +// 次数加1 +func (taskUser *TaskUser) addFinishN(taskConfig TaskConfig) bool { + //没有使用sql悲观锁,update set where,第一:观察业务,都是用户自动触发,有时间性 第二:验证业务这样写,被投诉的概率,以证明此场景业务。 + if taskUser.HasFinish == mysql.YES { + return false + } else { + taskUser.FinishN = taskUser.FinishN + 1 + if taskUser.FinishN >= taskConfig.FinishN { + taskUser.HasFinish = mysql.YES + } + return true + } +} + +// 次数覆盖 +func (taskUser *TaskUser) beFinishN(taskConfig TaskConfig, finishN uint32) bool { + //没有使用sql悲观锁,update set where,第一:观察业务,都是用户自动触发,有时间性 第二:验证业务这样写,被投诉的概率,以证明此场景业务。 + if taskUser.HasFinish == mysql.YES { + return false + } else { + taskUser.FinishN = finishN + if taskUser.FinishN >= taskConfig.FinishN { + taskUser.HasFinish = mysql.YES + } + return true + } +} + +func (taskUser *TaskUser) Award(taskConfig TaskConfig) error { + if taskUser.HasAward == mysql.YES { + return bizerr.TaskHasAward + } + taskUser.AwardN = taskUser.AwardN + 1 + if taskUser.AwardN >= taskConfig.AwardN { + taskUser.HasAward = mysql.YES + } + taskUser.SetCheckUpdateCondition("has_award = " + strconv.Itoa(int(mysql.NO))) + return nil +} + +func AddTaskUser(model *domain.Model, t task_e.TypeTaskConfig, userId uint64, orginId uint64) (mysql.ID, error) { + //获取任务配置 + taskConfig := TaskConfig{} + if err := model.Db.Model(&TaskConfig{}).Where(&TaskConfig{ + Type: t, + Status: mysql.USER, + }).First(&taskConfig).Error; err != nil { + return 0, myerr.WrapErr(err) + } + // + taskUser, err := getTaskUserOrInit(model, userId, taskConfig) + if err != nil { + return 0, err + } + if taskUser.addFinishN(taskConfig) { + if err := taskUser.Persistent(); err != nil { + return 0, myerr.WrapErr(err) + } + return taskConfig.ID, addTaskUserDetail(taskUser.Model, taskConfig.ID, userId, taskUser.ID, orginId) + } + return taskConfig.ID, nil +} + +// return true:已经完成 false:未完成 +func isTaskUserFinish(model *domain.Model, t task_e.TypeTaskConfig, userId uint64) (bool, error) { + //获取任务配置 + taskConfig := TaskConfig{} + if err := model.Db.Model(&TaskConfig{}).Where(&TaskConfig{ + Type: t, + Status: mysql.USER, + }).First(&taskConfig).Error; err != nil { + return false, myerr.WrapErr(err) + } + // + taskUser, err := getTaskUserOrInit(model, userId, taskConfig) + if err != nil { + return false, err + } + return taskUser.HasFinish == mysql.YES, nil +} + +func addTaskUserFinishN(model *domain.Model, t task_e.TypeTaskConfig, userId uint64, finishN uint32) error { + //获取任务配置 + taskConfig := TaskConfig{} + if err := model.Db.Model(&TaskConfig{}).Where(&TaskConfig{ + Type: t, + Status: mysql.USER, + }).First(&taskConfig).Error; err != nil { + return myerr.WrapErr(err) + } + // + taskUser, err := getTaskUserOrInit(model, userId, taskConfig) + if err != nil { + return err + } + if taskUser.beFinishN(taskConfig, finishN) { + if err := taskUser.Persistent(); err != nil { + return myerr.WrapErr(err) + } + return addTaskUserDetail(taskUser.Model, taskConfig.ID, userId, taskUser.ID, 0) + } + return nil +} \ No newline at end of file diff --git a/domain/model/user_m/black.go b/domain/model/user_m/black.go new file mode 100644 index 0000000000000000000000000000000000000000..b77b493873d26cc6f0f528dc0cfe4881616ef6aa --- /dev/null +++ b/domain/model/user_m/black.go @@ -0,0 +1,161 @@ +package user_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-group/myerr" +) + +//腾讯云的拉黑名单,临时表 +type UserBlackTencentyunTmp struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserExternal mysql.Str + BlockExternal mysql.Str +} + +//黑名单 +type UserBlock struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserId mysql.ID + BlockUserId mysql.ID +} + +func initUserBlock(model *domain.Model, userId mysql.ID) *UserBlock { + return &UserBlock{ + Model: model, + UserId: userId, + } +} + +//拉黑 +func (ub *UserBlock) block(blockUserId mysql.ID) (*UserBlock, error) { + err := ub.Db.Where(&UserBlock{ + UserId: ub.UserId, + BlockUserId: blockUserId, + }).First(ub).Error + //已经拉黑了 + if err == nil { + ub.SetLasyLoad() + return ub, nil + //return nil, myerr.NewWaring("已经标记拉黑") + } else if err == gorm.ErrRecordNotFound { + ub.BlockUserId = blockUserId + return ub, nil + } else { + return nil, myerr.WrapErr(err) + } +} + +//取消拉黑 +func (ub *UserBlock) blockCancel(blockUserId mysql.ID) (*UserBlock, error) { + err := ub.Db.Where(&UserBlock{ + UserId: ub.UserId, + BlockUserId: blockUserId, + }).First(ub).Error + // + if err == nil { + ub.SetDel() + return ub, nil + } else if err == gorm.ErrRecordNotFound { + return nil, myerr.NewWaring("没有拉黑的记录") + } else { + return nil, myerr.WrapErr(err) + } +} + +//检查是否存在拉黑 +/*func CheckBlock(model *domain.Model, userId mysql.ID, blockUserId mysql.ID) (bool, error) { + var n int64 + if err := model.Db.Model(&UserBlock{}).Where(&UserBlock{ + UserId: userId, + BlockUserId: blockUserId, + }).Count(&n).Error; err != nil { + return false, myerr.WrapErr(err) + } + return n > 0, nil +}*/ + +//检查是否存在拉黑(无论是我拉黑别人,还是别人拉黑我), true:拉黑, false:不拉黑 +func CheckBlockOr(model *domain.Model, userId mysql.ID, blockUserId mysql.ID) (bool, error) { + var n int64 + if err := model.Db.Model(&UserBlock{}).Where(&UserBlock{ + UserId: userId, + BlockUserId: blockUserId, + }).Count(&n).Error; err != nil { + return false, myerr.WrapErr(err) + } + if n == 0 { + if err := model.Db.Model(&UserBlock{}).Where(&UserBlock{ + BlockUserId: userId, + UserId: blockUserId, + }).Count(&n).Error; err != nil { + return false, myerr.WrapErr(err) + } + } + return n > 0, nil +} + +func CheckBlock(model *domain.Model, userId mysql.ID, blockUserId mysql.ID) (bool, error) { + var n int64 + if err := model.Db.WithContext(model).Model(&UserBlock{}).Where(&UserBlock{ + UserId: userId, + BlockUserId: blockUserId, + }).Count(&n).Error; err != nil { + return false, myerr.WrapErr(err) + } + return n > 0, nil +} + +//检查互相拉黑的成员 +func FilterBlock(model *domain.Model, userId mysql.ID, otherUserIds []mysql.ID) ([]mysql.ID, error) { + if len(otherUserIds) == 0 { + return otherUserIds, nil + } + userBlocks1 := []UserBlock{} + if err := model.Db.Model(&UserBlock{}).Where(&UserBlock{ + UserId: userId, + }).Where("block_user_id in (?)", otherUserIds).Find(&userBlocks1).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userBlocks2 := []UserBlock{} + if err := model.Db.Model(&UserBlock{}).Where(&UserBlock{ + BlockUserId: userId, + }).Where("user_id in (?)", otherUserIds).Find(&userBlocks2).Error; err != nil { + return nil, myerr.WrapErr(err) + } + + blockSet := map[uint64]struct{}{} + for i, _ := range userBlocks1 { + blockSet[userBlocks1[i].BlockUserId] = struct{}{} + } + for i, _ := range userBlocks2 { + blockSet[userBlocks2[i].UserId] = struct{}{} + } + + // + resultUserIds := make([]mysql.ID, 0, len(otherUserIds)) + for i, r := range otherUserIds { + if _, flag := blockSet[r]; !flag { + resultUserIds = append(resultUserIds, otherUserIds[i]) + } + } + return resultUserIds, nil +} + +// 获取用户拉黑的用户ids +func GetBlockUserIds(model *domain.Model, userId mysql.ID) ([]mysql.ID, error) { + var userBlocks []UserBlock + if err := model.Db.Where(&UserBlock{ + UserId: userId, + }).Find(&userBlocks).Error; err != nil { + return nil, myerr.WrapErr(err) + } + var userIds []mysql.ID + for _, v := range userBlocks { + userIds = append(userIds, v.BlockUserId) + } + return userIds, nil +} \ No newline at end of file diff --git a/domain/service/event_s/event_init.go b/domain/service/event_s/event_init.go index 0b1a374e1670a3cd397ad24c50b97722cc2d975e..dc11f8a6c4858c5b3865c2906093b4ecac24d214 100644 --- a/domain/service/event_s/event_init.go +++ b/domain/service/event_s/event_init.go @@ -1,20 +1,32 @@ package event_s import ( + "encoding/json" "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/rpc" + "git.hilo.cn/hilo-common/sdk/tencentyun" + "hilo-group/_const/enum/group_e" "hilo-group/_const/enum/msg_e" + "hilo-group/_const/enum/task_e" "hilo-group/domain/event/group_ev" "hilo-group/domain/event/group_power_ev" "hilo-group/domain/model/diamond_m" "hilo-group/domain/model/groupPower_m" + "hilo-group/domain/model/group_m" "hilo-group/domain/model/msg_m" + "hilo-group/domain/model/task_m" "hilo-group/domain/model/user_m" + "hilo-group/myerr" "strconv" + "time" ) func EventInit() { GroupPowerEvents() GroupSupportEvents() + GroupEvents() + GroupImMass() + GroupTheme() } func GroupSupportEvents() { @@ -144,3 +156,240 @@ func GroupPowerEvents() { return nil }) } + +func GroupEvents() { + // 进房事件 + group_ev.AddGroupInAsync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupInEvent) + if !ok { + model.Log.Errorf("AddGroupInAsync for room: %+v", event) + return nil + } + model.Log.Infof("AddGroupInAsync for room: %+v", event) + + uer := group_m.UserEnterRoom{ + UserId: event.UserId, + GroupId: event.GroupId, + EnterTime: time.Now(), + } + err := uer.Save(model.Db) + model.Log.Infof("AddGroupInAsync, UserEnterRoom err: %v", err) + return err + }) + + // 进入房间时, + group_ev.AddGroupInAsync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupInEvent) + if !ok { + model.Log.Errorf("AddGroupInAsync for room: %+v", event) + return nil + } + model.Log.Infof("AddGroupInAsync for user: %+v", event) + + user, err := user_m.GetUser(model, event.UserId) + if err != nil { + return err + } + + medals, err := user_m.GetUserMedalMerge(model.Log, model.Db, event.UserId) + if err != nil { + model.Log.Errorf("tim_m user AddGroupInAsync GetUserMedal err:%v, userId:%v", err, event.UserId) + return err + } + + wealthGrade, _, err := user_m.GetWealthGrade(model, event.UserId) + if err != nil { + return err + } + + charmGrade, _, err := user_m.GetCharmGrade(model, event.UserId) + if err != nil { + return err + } + + if err = FlushGrades(event.ExternalId, wealthGrade, charmGrade); err != nil { + model.Log.Info("AddGroupInAsync, FlushGrades failed: ", err) + } + + _, powerName, err := groupPower_m.GetUserGroupPower(model, event.UserId) + if err != nil { + return err + } + + if err = FlushHiloInfo(user.ExternalId, event.IsVip, user.Code != user.OriginCode, medals, powerName, event.NobleLevel); err != nil { + model.Log.Info("AddGroupInAsync, FlushHiloInfo failed: ", err) + } + return nil + }) + //被踢出 + group_ev.AddGroupKickOutAsync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupKickOutEvent) + if !ok { + model.Log.Errorf("AddGroupKickOutAsync fail data") + return nil + } + model.Log.Infof("publicScreenMsg AddGroupKickOutAsync GroupId:%v, OperatorExternalId:%v, MemberExternalId:%v", event.GroupId, event.OperatorExternalId, event.MemberExternalId) + + groupKickOutMsg := group_m.CommonPublicMsg{ + Type: group_e.UserKickPublicScreenMsg, + OperatorExternalId: event.OperatorExternalId, + OperatorNick: event.OperatorName, + OperatorAvatar: event.OperatorFaceUrl, + ExternalId: event.MemberExternalId, + Nick: event.MemberName, + Avatar: event.MemberAvatar, + } + // + body, err := json.Marshal(groupKickOutMsg) + if err != nil { + return myerr.WrapErr(err) + } + + txGroupId, err := group_m.ToTxGroupId(model, event.GroupId) + if err != nil { + return err + } + //发送公屏消息, + u, err := tencentyun.SendCustomMsg(model.Log, txGroupId, nil, string(body), "") + model.Log.Infof("publicScreenMsg AddGroupKickOutAsync result response.MsgSeq:%v, err:%v", u, err) + return err + }) +} + +func GroupImMass() { + //支付群主群发IM + group_ev.AddGroupImMassSync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupImMassEvent) + if !ok { + model.Log.Errorf("AddGroupImMassSync data fail") + return nil + } + model.Log.Infof("diamond AddGroupImMass groupId:%v userId:%v", event.GroupId, event.UserId) + diamondAccount, err := diamond_m.GetDiamondAccountByUserId(model, event.UserId) + if err != nil { + return err + } + diamondAccountDetail, err := diamondAccount.GroupImMass() + if err != nil { + return err + } + return diamondAccountDetail.Persistent() + }) + + // 任务 + group_ev.AddGroupImMassAsync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupImMassEvent) + if !ok { + model.Log.Errorf("AddGroupImMassSync data fail") + return nil + } + model.Log.Infof("task AddGroupImMassAsync %+v", event) + _, err := task_m.AddTaskUser(model, task_e.GroupImMass, event.UserId, 0) + return err + }) + + // 麦上的人/管理员群发消息,弹窗 fixme:放在这里已经不合适了 + group_ev.AddGroupImMassAsync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.GroupImMassEvent) + if !ok { + model.Log.Errorf("AddGroupImMassSync data fail") + return nil + } + model.Log.Infof("AddGroupImMassAsync begin groupId = %s, userId %d, members: %v, content: %s", + event.GroupId, event.UserId, event.Members, event.Content) + + flag, err := group_m.IsHiddenGroup(model.Db, event.GroupId) + if err != nil { + return err + } + if flag { + model.Log.Infof("AddGroupImMassAsync, skip hidden group %s", event.GroupId) + return nil + } + + if len(event.Members) <= 0 { + return nil + } + user, err := user_m.GetUser(model, event.UserId) + if err != nil { + return err + } + + //过滤用户黑名单,只要单方面拉黑就不发 + memberUserIds, err := user_m.FilterBlock(model, event.UserId, event.Members) + if err != nil { + return err + } + + groupInfo, err := group_m.GetGroupInfo(model, event.GroupId) + if err != nil { + return err + } + + userInCount, err := group_m.GetRoomVisitCount(event.GroupId) + if err != nil { + return err + } + if userInCount < 0 { + userInCount = 0 + } + + // 注意发消息使用了TxGroupId + err = rpc.SendGroupChatNotice(event.UserId, memberUserIds, user.ExternalId, user.Code, uint32(user.Sex), user.Avatar, + event.Content, groupInfo.TxGroupId, groupInfo.Name, groupInfo.Code, groupInfo.FaceUrl, uint32(userInCount)) + model.Log.Infof("AddGroupImMassAsync, groupId = %s ended, err = %v", event.GroupId, err) + return nil + }) + +} + +func GroupTheme() { + //购买自定义主题 + group_ev.AddBuyGroupCustomThemeSync(func(model *domain.Model, e interface{}) error { + event, ok := e.(*group_ev.BuyGroupCustomThemeEvent) + if !ok { + model.Log.Errorf("AddBuyGroupCustomThemeSync data fail") + return nil + } + model.Log.Infof("diamond AddBuyGroupCustomTheme userId:%v, GroupCustomThemeId:%v", event.UserId, event.GroupCustomThemeId) + diamondAccount, err := diamond_m.GetDiamondAccountByUserId(model, event.UserId) + if err != nil { + return err + } + diamondAccountDetail, err := diamondAccount.BuyGroupCustomTheme(event.GroupCustomThemeId) + if err != nil { + return err + } + if err := diamondAccountDetail.Persistent(); err != nil { + return err + } + return nil + }) +} + +func FlushGrades(userExtId string, wealthGrade uint32, charmGrade uint32) error { + level := (charmGrade & 0x000000FF << 8) | wealthGrade&0x000000FF + + return tencentyun.SetUserLevel(userExtId, level) +} + +type TimHiloInfo struct { + IsVip bool `json:"isVip"` + IsPretty bool `json:"isPretty"` + Medals []uint32 `json:"medals"` + PowerName string `json:"powerName"` // 用户加入的国家势力的绑定群组的名称 + NobleLevel uint16 `json:"nobleLevel"` +} + +func FlushHiloInfo(extId string, isVip bool, isPrettyCode bool, medals []uint32, groupPowerName string, nobleLevel uint16) error { + info := TimHiloInfo{IsVip: isVip, IsPretty: isPrettyCode, Medals: medals, PowerName: groupPowerName, NobleLevel: nobleLevel} + buf, err := json.Marshal(info) + if err != nil { + return err + } + + if err = tencentyun.SetUserHiloInfo(extId, string(buf)); err != nil { + return err + } + return nil +} diff --git a/domain/service/group_mic_s/group_mic.go b/domain/service/group_mic_s/group_mic.go new file mode 100644 index 0000000000000000000000000000000000000000..f9a54adc53800e498fd98d845d495e4767253bad --- /dev/null +++ b/domain/service/group_mic_s/group_mic.go @@ -0,0 +1,289 @@ +package group_mic_s + +import ( + "context" + "encoding/json" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/redisCli" + uuid "github.com/satori/go.uuid" + "hilo-group/_const/enum/group_e" + "hilo-group/_const/redis_key" + "hilo-group/domain/event/group_ev" + "hilo-group/domain/model/group_m" + "hilo-group/domain/service/signal_s" + "hilo-group/myerr" + "hilo-group/myerr/bizerr" + "time" +) + +type GroupMicService struct { + svc *domain.Service +} + +func NewGroupPowerService(myContext *mycontext.MyContext) *GroupMicService { + svc := domain.CreateService(myContext) + return &GroupMicService{svc} +} + +//修改群组中麦的数量 +func (s *GroupMicService) GroupMicNumChange(groupId string, userId uint64, micNumType group_e.GroupMicNumType, micOn bool) error { + model := domain.CreateModelContext(s.svc.MyContext) + //数据库修改群组麦的数量 + //检查权限 + if err := group_m.CheckPermission(model, groupId, userId); err != nil { + return err + } + //删除麦位数量类型缓存 + group_m.InitMicNumType(model, groupId, micNumType).ClearCache() + + // + groupInfo, _ := group_m.GetGroupInfo(model, groupId) + if groupInfo == nil { + return bizerr.GroupNotFound + } + + //判断数据是否发生了变化 + //关闭麦位 + if micOn == false { + if groupInfo.MicOn == micOn { + return nil + } + } else { + if groupInfo.MicOn == micOn && groupInfo.MicNumType == micNumType { + return nil + } + } + //修改数据,然后数据持久化, + g := group_m.GroupInfo{ + MicOn: micOn, + } + fields := []string{"mic_on"} + //开启麦位才修改micNumType + returnMicNumType := g.MicNumType + if micOn == true { + g.MicNumType = micNumType + fields = append(fields, "mic_num_type") + returnMicNumType = micNumType + } + db := g.Update(model, groupId, fields) + if db.Error != nil { + return myerr.WrapErr(db.Error) + } + + //增加麦位数量类型缓存 + group_m.InitMicNumType(model, groupId, micNumType).AddCache() + + //麦位上的信息,清理 + group_m.ClearMic(groupId) + + type Content struct { + MicOn bool `json:"micOn"` + MicNumType group_e.GroupMicNumType `json:"micNumType"` + Timestamp int64 `json:"timestamp"` + } + + r := Content{ + MicOn: micOn, + MicNumType: returnMicNumType, + Timestamp: time.Now().Unix(), + } + buf, err := json.Marshal(r) + if err != nil { + model.Log.Errorf("GroupMicNumChange Content json.Marshal err:%v", err) + } + // 发信令,让前端重新拉取,接受容错, + signal_s.SendSignalMsg(model, groupId, group_m.GroupSystemMsg{ + MsgId: group_e.GroupMicChangeSignal, + Content: string(buf), + }, false) + + group_m.MicNumChangeRPush(model, groupId, returnMicNumType, micOn) + + return nil +} + +//加锁, sign作为密钥存在(预防被别人删除,比如:先者删除了后者的锁), +func redisLock(key string, sign string, expiration time.Duration, callBack func() error) error { + flag, err := redisCli.GetRedis().SetNX(context.Background(), key, sign, expiration).Result() + if err != nil { + return myerr.WrapErr(err) + } + if flag { + err = callBack() + redisSign, _ := redisCli.GetRedis().Get(context.Background(), key).Result() + if redisSign == sign { + redisCli.GetRedis().Del(context.Background(), key) + } + return err + } else { + return bizerr.GroupConcurrencyLock + } +} + +//加入麦位,锁两秒 +func (s *GroupMicService) GroupMicIn(groupUuid string, i int, userId uint64, externalId string) error { + return redisLock(redis_key.GetPrefixGroupMicUserInLock(userId), uuid.NewV4().String(), time.Second*2, func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + mic, err := group_m.GetMic(model, groupUuid, i) + if err != nil { + return err + } + return mic.In(userId, externalId) + }) +} + +//离开麦位 +func (s *GroupMicService) GroupMicLeave(groupUuid string, i int, userId uint64, externalId string) error { + return redisLock(redis_key.GetPrefixGroupMicUserDelLock(groupUuid, i), uuid.NewV4().String(), time.Second*2, func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + micUser, err := group_m.GetMicUser(model, groupUuid, i) + if err != nil { + return err + } + if micUser != nil { + return micUser.LeaveByUser(userId, externalId) + } + return nil + }) +} + +//邀请上麦,锁两秒 +func (s *GroupMicService) GroupMicInvite(groupUuid string, operateUserId uint64, beInvitedExternalId string) error { + model := domain.CreateModelContext(s.svc.MyContext) + if err := group_m.InviteMicIn(model, groupUuid, operateUserId, beInvitedExternalId); err != nil { + return err + } + return nil +} + +//麦位加锁, +func (s *GroupMicService) GroupMicLock(userId uint64, externalId string, groupUuid string, i int) error { + //后果等级不高,不需要加锁 + model := domain.CreateModelContext(s.svc.MyContext) + mic, err := group_m.GetMic(model, groupUuid, i) + if err != nil { + return err + } + return mic.MgrLock(userId, externalId) +} + +//麦位解锁 +func (s *GroupMicService) GroupMicUnLock(userId uint64, externalId string, groupUuid string, i int) error { + //后果等级不高,不需要加锁 + model := domain.CreateModelContext(s.svc.MyContext) + mic, err := group_m.GetMic(model, groupUuid, i) + if err != nil { + return err + } + return mic.MgrUnLock(userId, externalId) +} + +// 麦位静音 +func (s *GroupMicService) GroupMicMute(userId uint64, externalId string, groupUuid string, i int) error { + //后果等级不高,不需要加锁 + model := domain.CreateModelContext(s.svc.MyContext) + mic, err := group_m.GetMic(model, groupUuid, i) + if err != nil { + return err + } + return mic.MgrMute(userId, externalId) +} + +// 麦位解除静音 +func (s *GroupMicService) GroupMicUnMute(userId uint64, externalId string, groupUuid string, i int) error { + //后果等级不高,不需要加锁 + model := domain.CreateModelContext(s.svc.MyContext) + mic, err := group_m.GetMic(model, groupUuid, i) + if err != nil { + return err + } + return mic.MgrUnMute(userId, externalId) +} + +//开启麦 +func (s *GroupMicService) GroupMicSpeechOpen(userId uint64, externalId string, groupUuid string, i int) error { + //自己/管理人开启禁麦,并发率不高,后果等级不高 + model := domain.CreateModelContext(s.svc.MyContext) + micUser, err := group_m.GetMicUser(model, groupUuid, i) + if err != nil { + return err + } + return micUser.SpeechOpen(userId, externalId) +} + +//关闭麦 +func (s *GroupMicService) GroupMicSpeechClose(userId uint64, externalId string, groupUuid string, i int) error { + model := domain.CreateModelContext(s.svc.MyContext) + micUser, err := group_m.GetMicUser(model, groupUuid, i) + if err != nil { + return err + } + return micUser.SpeechClose(userId, externalId) +} + +//麦上的人群发消息 +func (s *GroupMicService) GroupIMMassByInMic(groupId string, userId uint64, externalId string, content string) error { + return s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + micUser, err := group_m.GetMicUserByExternalId(model, externalId) + if err != nil { + return err + } + if err := micUser.ImMass(externalId); err != nil { + return err + } + //校验群组ID + if micUser.GroupUuid != groupId { + return myerr.NewSysError("groupId 不一致, http:groupId <> micUser.groupId") + } + + //获取群成员 + gm, err := group_m.GetMembers(model.Db, groupId) + if err != nil { + return err + } + uids := make([]uint64, 0) + for _, i := range gm { + //排除自己 + if userId != i.UserId { + uids = append(uids, i.UserId) + } + } + return group_ev.PublishGroupImMass(model, &group_ev.GroupImMassEvent{ + GroupId: groupId, + UserId: userId, + Members: uids, + Content: content, + }) + }) +} + +// 群管理人群发消息 +func (s *GroupMicService) GroupIMMassByMgr(groupId string, userId uint64, externalId string, content string) error { + return s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + //检查权限 + if err := group_m.CheckPermission(model, groupId, userId); err != nil { + return err + } + //获取群成员 + gm, err := group_m.GetMembers(model.Db, groupId) + if err != nil { + return err + } + uids := make([]uint64, 0) + for _, i := range gm { + //排除自己 + if userId != i.UserId { + uids = append(uids, i.UserId) + } + } + return group_ev.PublishGroupImMass(model, &group_ev.GroupImMassEvent{ + GroupId: groupId, + UserId: userId, + Members: uids, + Content: content, + }) + }) +} diff --git a/domain/service/group_s/group.go b/domain/service/group_s/group.go index ed37fd2912e7e64487047aaa102aa8ea1f2f4d6f..25cba0317dc3ad05f5d666b291b54ba99e65180e 100644 --- a/domain/service/group_s/group.go +++ b/domain/service/group_s/group.go @@ -1,16 +1,23 @@ package group_s import ( + "encoding/json" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/resource/config" "git.hilo.cn/hilo-common/resource/mysql" "gorm.io/gorm" "hilo-group/_const/enum/group_e" + "hilo-group/_const/enum/mgr_e" "hilo-group/_const/redis_key" + "hilo-group/domain/event/group_ev" "hilo-group/domain/model/group_m" + "hilo-group/domain/model/mgr_m" "hilo-group/domain/model/noble_m" + "hilo-group/domain/model/res_m" "hilo-group/domain/model/user_m" + "hilo-group/domain/service/signal_s" + "hilo-group/myerr" "strconv" "time" ) @@ -116,3 +123,169 @@ func (s *GroupService) GetJoinGroupLimit(userId mysql.ID) (uint, error) { } return maxJoin, nil } + +//更新用户在群上的消息状态 +func (s *GroupService) GroupUserMsgStatus(userId uint64, groupUuid string, msgStatus group_e.MsgStatusGroupUser) error { + return s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + //var groupInfo group_m.GroupInfo + //if err := model.Db.Where(&group_m.GroupInfo{ + // ImGroupId: groupUuid, + //}).First(&groupInfo).Error; err != nil { + // return nil, nil, myerr.WrapErr(err) + //} + // + groupUser, err := group_m.GetGroupUserOrInit(model, groupUuid, userId) + if err != nil { + return err + } + if msgStatus == group_e.NormalMsgStatusGroupUser { + groupUser.MsgStatusNormal() + } else if msgStatus == group_e.MuteMsgStatusGroupUser { + groupUser.MsgStatusMute() + } else if msgStatus == group_e.DoNotDisturbMsgStatusGroupUser { + groupUser.MsgStatusDoNotDisturb() + } + return groupUser.Persistent() + }) +} + +//举报群组 +func (s *GroupService) ReportGroup(fromUserId mysql.ID, groupId mysql.Str, reasonType mgr_e.ReportReasonType, imageUrl mysql.Str, reason mysql.Str) error { + return s.svc.Transactional(func() error { + model := domain.CreateModelContext(s.svc.MyContext) + reportGroup := mgr_m.ReportGroupAdd(model, fromUserId, groupId, reasonType, imageUrl, reason) + err := reportGroup.Persistent() + if err != nil { + return err + } + return nil + }) +} + +//群组自定义主题修改 +func (s *GroupService) GroupCustomThemeUsing(userId mysql.ID, externalId string, imGroupId string, groupCustomThemeId uint64) error { + err1 := s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + groupCustomTheme, err := group_m.GetGroupCustomThemeById(model, imGroupId, groupCustomThemeId) + if err != nil { + return err + } + groupCustomTheme, err = groupCustomTheme.SetUsing(userId) + if err != nil { + return err + } + if err := groupCustomTheme.Persistent(); err != nil { + return err + } + + //修改数据,然后数据持久化, + g := group_m.GroupInfo{ + ThemeId: 0, + } + fields := []string{"theme_id"} + db := g.Update(model, imGroupId, fields) + if db.Error != nil { + return myerr.WrapErr(db.Error) + } + if err := groupCustomTheme.Persistent(); err != nil { + return err + } else { + return nil + } + }) + if err1 == nil { + type signalMsg struct { + Name string `json:"name"` + Introduction string `json:"introduction"` + Notification string `json:"notification"` + FaceUrl string `json:"faceUrl"` + MicOn bool `json:"micOn"` + MicNumType group_e.GroupMicNumType + ThemeId uint64 `json:"themeId"` + ThemeUrl string `json:"themeUrl"` + //1: 官方 2:自定义 + ThemeType uint8 `json:"themeType"` + } + model := domain.CreateModelContext(s.svc.MyContext) + groupInfo, err := group_m.GetGroupInfo(model, imGroupId) + if err != nil { + model.Log.Error(err) + return nil + } + signal := signalMsg{ + Name: groupInfo.Name, + Introduction: groupInfo.Introduction, + Notification: groupInfo.Notification, + FaceUrl: groupInfo.FaceUrl, + MicOn: groupInfo.MicOn, + MicNumType: groupInfo.MicNumType, + ThemeId: 0, + ThemeUrl: "", + ThemeType: 0, + } + if groupInfo.ThemeId != 0 { + //signal.ThemeId = groupInfo.ThemeId + signal.ThemeType = 1 + if rows, err := res_m.GroupThemeGetAllInUse(model.Db); err == nil { + for _, i := range rows { + if i.ID == uint64(groupInfo.ThemeId) { + signal.ThemeId = i.ID + signal.ThemeUrl = i.Url + break + } + } + } + } else { + //可能是自定义主题 + id, url, err := group_m.GetShowCustomTheme(model, imGroupId) + if err != nil { + model.Log.Error(err) + return nil + } + if id > 0 { + signal.ThemeId = id + signal.ThemeUrl = url + signal.ThemeType = 2 + } + } + buf, err := json.Marshal(signal) + if err == nil { + systemMsg := group_m.GroupSystemMsg{MsgId: group_e.GroupEditProfileSignal, Source: externalId, Content: string(buf)} + signal_s.SendSignalMsg(model, imGroupId, systemMsg, false) + } + } + return err1 +} + +//增加群组自定义主题 +func (s *GroupService) AddGroupCustomTheme(userId mysql.ID, imGroupId string, picUrl string) (uint64, string, error) { + var themeId uint64 = 0 + var themeUrl string = "" + return themeId, themeUrl, s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + groupCustomTheme, err := group_m.AddGroupCustomTheme(model, userId, imGroupId, picUrl) + if err != nil { + return err + } + //将group_info的theme_id设置为0 + //修改数据,然后数据持久化, + g := group_m.GroupInfo{ + ThemeId: 0, + } + fields := []string{"theme_id"} + db := g.Update(model, imGroupId, fields) + if db.Error != nil { + return myerr.WrapErr(db.Error) + } + if err := groupCustomTheme.Persistent(); err != nil { + return err + } + themeId = groupCustomTheme.ID + themeUrl = groupCustomTheme.PicUrl + return group_ev.PublishBuyGroupCustomTheme(model, &group_ev.BuyGroupCustomThemeEvent{ + GroupCustomThemeId: groupCustomTheme.ID, + UserId: userId, + }) + }) +} diff --git a/domain/service/group_s/group_op.go b/domain/service/group_s/group_op.go index d0c9a25bb0ff5ef1c84bad688d5b4341d61328bc..1beb6ccc733f026ac50a7ff0a431756160df80d9 100644 --- a/domain/service/group_s/group_op.go +++ b/domain/service/group_s/group_op.go @@ -1,11 +1,32 @@ package group_s import ( + "context" + "encoding/json" + "fmt" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mylogrus" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/resource/redisCli" + "git.hilo.cn/hilo-common/rpc" + "git.hilo.cn/hilo-common/sdk/agora" + "git.hilo.cn/hilo-common/sdk/tencentyun" + "git.hilo.cn/hilo-common/utils" + "gorm.io/gorm" "hilo-group/_const/enum/group_e" + "hilo-group/_const/redis_key" + "hilo-group/cv/property_cv" "hilo-group/domain/cache/group_c" + "hilo-group/domain/cache/tim_c" + "hilo-group/domain/event/group_ev" "hilo-group/domain/model/group_m" + "hilo-group/domain/model/noble_m" + "hilo-group/domain/model/user_m" + "hilo-group/domain/service/signal_s" + "hilo-group/myerr" + "hilo-group/myerr/bizerr" + "strconv" + "time" ) // 创建群组 @@ -92,3 +113,333 @@ func (s *GroupService) LeaveGroupMember(model *domain.Model, groupId string, use return nil } + +//进入房间, 返回channelId, err +func (s *GroupService) GroupIn(userId uint64, externalId string, groupUuid string, password, imei, ip string) (string, string, error) { + var channelId string + var token string + err := s.svc.Transactional(func() error { + //检查群组是否存在, 没有真正的domel,直接service上怼 + model := domain.CreateModel(s.svc.CtxAndDb) + // 群是否被封禁 + banned := group_m.GroupBanned{ImGroupId: groupUuid} + if err := banned.Get(model); err != gorm.ErrRecordNotFound { + return bizerr.GroupIsBanned + } + var groupInfo group_m.GroupInfo + if err := model.Db.Where(&group_m.GroupInfo{ + ImGroupId: groupUuid, + }).First(&groupInfo).Error; err != nil { + return myerr.WrapErr(err) + } + + if userId != groupInfo.Owner && len(groupInfo.Password) > 0 && password != groupInfo.Password { + return bizerr.IncorrectPassword + } + + //检查群组中是否有拉黑名单 + //var n int64 + //if err := model.Db.Model(&group_m.GroupBlacklist{}).Where(&group_m.GroupBlacklist{ + // ImGroupId: groupUuid, + // UserId: userId, + //}).Count(&n).Error; err != nil { + // return nil, nil, myerr.WrapErr(err) + //} + //if n != 0 { + // return nil, nil, bizerr.InBlacklist + //} + svip, _ := rpc.GetUserSvip(model, userId) + if userId != groupInfo.Owner { + // 是否超管 + isM, err := user_m.IsSuperManager(model, userId) + if err != nil { + model.Log.Errorf("GroupIn err:%v", err) + return myerr.NewSysError("not super manager") + } + // 不是超管 且 用户是否在群的黑名单中 + if !isM && group_m.InGroupBlackList(model, groupUuid, imei, ip, userId) { + if svip.SvipLevel < 6 { // svip6暂时不判断GroupBlackList + return bizerr.InBlacklist + } + } + } + //是否被踢出 + if i, err := redisCli.GetRedis().Exists(context.Background(), redis_key.GetGroupKickGroupUuidUserId(groupUuid, userId)).Result(); err != nil { + return myerr.WrapErr(err) + } else if i == 0 { + user, err := user_m.GetUser(model, userId) + if err != nil { + return err + } + channelId, token, err = agora.CreateGroupAgora(groupInfo.ChannelId, uint32(userId)) + if err != nil { + return err + } else { + //加入房间 + group_m.RoomLivingIn(model, groupUuid, userId, externalId, false) + + groupUser, err := group_m.GetGroupUserOrInit(model, groupUuid, userId) + if err != nil { + model.Log.Errorf("GroupIn GetGroupUserOrInit err:%v, groupId:%v, userId:%v", err, groupUuid, userId) + } + if err := groupUser.SetRoomInTime().Persistent(); err != nil { + model.Log.Errorf("GroupIn groupUser Persistent err:%v, groupId:%v, userId:%v", err, groupUuid, userId) + } + + // 发送进群TIM信令。fixme: 应该发在event处理 + isVip, err := user.CheckVip() + if err != nil { + return err + } + + nobleLevel, err := noble_m.GetNobleLevel(mysql.Db, userId) + if err != nil { + return err + } + + type UserParam struct { + Nick string `json:"nick"` + UserAvatar string `json:"userAvatar"` + IsVip bool `json:"isVip"` + RideId uint64 `json:"rideId"` + RideUrl string `json:"rideUrl"` + RideEffectUrl string `json:"rideEffectUrl"` + RidSenderAvatar string `json:"ridSenderAvatar"` + RidReceiverAvatar string `json:"ridReceiverAvatar"` + NobleLevel uint16 `json:"nobleLevel"` + SvipLevel int `json:"svipLevel"` + Svip rpc.CvSvip `json:"svip"` + } + + up := user_m.UserProperty{} + rides, err := up.BatchGet(mysql.Db, []uint64{userId}) + if err != nil { + return err + } + + //rp := res_m.ResProperty{} + //properties, err := rp.GetAll(mysql.Db) + properties, err := property_cv.GetExtendedProperty(mysql.Db) + if err != nil { + return err + } + r := UserParam{ + Nick: user.Nick, + UserAvatar: user.Avatar, + IsVip: isVip, + RideId: rides[userId], + RideUrl: properties[rides[userId]].PicUrl, + RideEffectUrl: properties[rides[userId]].EffectUrl, // todo replace ui svga + RidSenderAvatar: properties[rides[userId]].SenderAvatar, + RidReceiverAvatar: properties[rides[userId]].ReceiverAvatar, + NobleLevel: nobleLevel, + Svip: rpc.CopySimpleSvip(svip), + } + + buf, err := json.Marshal(r) + if err == nil { + //发送腾讯云IM系统消息 + signal_s.SendSignalMsg(model, + groupUuid, group_m.GroupSystemMsg{ + MsgId: group_e.GroupInSignal, + Source: user.ExternalId, + Content: string(buf), + }, true) + } + + isMember, _ := group_m.IsGroupMember(model.Db, groupUuid, userId) + return group_ev.PublishGroupIn(model, &group_ev.GroupInEvent{ + GroupId: groupUuid, + UserId: userId, + ExternalId: user.ExternalId, + Nick: user.Nick, + Avatar: user.Avatar, + IsMember: isMember, + IsVip: isVip, + NobleLevel: nobleLevel, + }) + } + } else { + return bizerr.GroupInKick + } + }) + if err != nil { + return "", "", err + } else { + return channelId, token, nil + } +} + +func (s *GroupService) JoinGroup(userId uint64, externalId string, groupId string) error { + model := domain.CreateModel(s.svc.CtxAndDb) + model.Log.Infof("Async: user %d(%s) is joining group %s", userId, externalId, groupId) + + for i := 1; i < 3; i++ { + info, err, errCode := tencentyun.AddGroup(model, groupId, []string{externalId}) + if err == nil && info != nil { + for i, r := range info { + if i == externalId { + if r == group_e.ADD_GROUP_DONE || r == group_e.ADD_GROUP_DUPLICATE { + // 加群成功后,马上发送通知给客户端,让他们拉取历史消息 + err = rpc.SendJoinGroup(userId, externalId, groupId) + model.Log.Infof("joinGroup: SendJoinGroup err = %v", err) + } + + if r == group_e.ADD_GROUP_DONE { + group_c.ClearGroupMemberCount(groupId) + tim_c.AddGroupMember(model, groupId, externalId) + } + } + return nil + } + } else if errCode == 10014 || errCode == 10038 { + // 如果群成员达到上限,删除一个(10014 达到群上限;10038 达到APP上限) + extId, err := s.RemoveZombie(model, groupId) + if err != nil { + return err + } + model.Log.Infof("JoinGroup %s limit reached, errCode = %d, %s kicked", groupId, errCode, extId) + } + + } + return fmt.Errorf("failed 3 times") +} + +// 清除TIM群的僵尸 +func (s *GroupService) RemoveZombie(model *domain.Model, groupId string) (string, error) { + rows, err := group_m.GetMembers(model.Db, groupId) + if err != nil { + return "", err + } + userIds := make([]uint64, 0) + for _, i := range rows { + userIds = append(userIds, i.UserId) + } + model.Log.Infof("JoinGroup %s: members: %v", groupId, userIds) + + roomUids, err := group_m.RoomLivingExistsUserId(groupId) + if err != nil { + return "", err + } + model.Log.Infof("JoinGroup %s: roomUids: %v", groupId, roomUids) + + userIds = append(userIds, roomUids...) + userIds = utils.UniqueSliceUInt64(userIds) + + m, err := user_m.GetUserMapByIds(model, userIds) + if err != nil { + return "", err + } + extIdsMap := make(map[string]uint64) + for _, i := range m { + extIdsMap[i.ExternalId] = i.ID + } + model.Log.Infof("JoinGroup %s: extIdsMap: %v", groupId, extIdsMap) + + _, gm, err := tencentyun.GetGroupMemberInfo(model, groupId) + if err != nil { + return "", err + } + + zombie := "" + for _, i := range gm { + if _, ok := extIdsMap[i.Member_Account]; !ok { + model.Log.Infof("JoinGroup %s: to kick %s", groupId, i.Member_Account) + + tencentyun.LeaveGroup(groupId, []string{i.Member_Account}) + zombie = i.Member_Account + break + } + } + return zombie, nil +} + +//离开房间 +func (s *GroupService) GroupLeave(userId uint64, externalId string, groupId string) error { + model := domain.CreateModelContext(s.svc.MyContext) + + _, err := group_m.RoomLivingLeave(model, userId, externalId, groupId) + return err +} + +//踢人 +func (s *GroupService) GroupKick(groupUuid string, userId uint64, userExternalId string, userNick string, avatar string, beKickUserId uint64, beKickExternalId string, beKickUserNick string, beKickUserAvatar string) error { + return s.svc.Transactional(func() error { + model := domain.CreateModel(s.svc.CtxAndDb) + //木有model层给我,直接server怼了 + + //被踢的人不能是超级管理人 + if flag, err := user_m.IsSuperManager(model, beKickUserId); err != nil { + return err + } else if flag { + return bizerr.OfficialStaffLimit + } + + //超级管理人,无敌状态 + if flag, err := user_m.IsSuperManager(model, userId); err != nil { + return err + } else if !flag { + //判断权限 + if err := group_m.MgrPermission(model, groupUuid, userId, beKickUserId); err != nil { + return err + } + //检查是否是贵族 + if flag, err := noble_m.CheckNobleLevel(model.Db, beKickUserId, 5); err != nil { + return err + } else if flag { + return bizerr.NobleNoKickLevel5 + } + } + //踢人10分钟 + _, err := redisCli.GetRedis().Set(context.Background(), redis_key.GetGroupKickGroupUuidUserId(groupUuid, beKickUserId), strconv.Itoa(int(beKickUserId)), time.Minute*10).Result() + if err != nil { + return myerr.WrapErr(err) + } + + //删除房间中redis + //group_m.RoomLivingLeave(model, groupUuid, beKickUserId) + group_m.RoomLivingLeaveByKick(model, groupUuid, beKickUserId, beKickExternalId, userExternalId) + // 发信令,让前端重新拉取,接受容错, + /* SendSignalMsg(groupUuid, group_m.GroupSystemMsg{ + MsgId: group_m2.GroupKickOut, + Source: userExternalId, + Target: beKickExternalId, + })*/ + + //记录踢人(非事务性, 错误不进行处理) + if err := group_m.AddGroupKickRecord(model, groupUuid, userId, beKickUserId); err != nil { + model.Log.Errorln(err) + } + + /* //获取用户是否在麦上, 让用户离开麦 + micUser, err := group_m.GetMicUserByExternalId(model, beKickExternalId) + if err != nil { + return nil, nil, err + } + if micUser != nil { + micUser.LeaveByUser(userId, userExternalId) + }*/ + + return group_ev.PublishGroupKickOut(model, &group_ev.GroupKickOutEvent{ + GroupId: groupUuid, + OperatorExternalId: userExternalId, + OperatorName: userNick, + OperatorFaceUrl: avatar, + MemberExternalId: beKickExternalId, + MemberName: beKickUserNick, + MemberAvatar: beKickUserAvatar, + }) + }) +} + +// 群管理人清理公屏 +func (s *GroupService) GroupClearScreenByMgr(groupId string, userId uint64) error { + model := domain.CreateModelContext(s.svc.MyContext) + //检查权限 + if err := group_m.CheckPermission(model, groupId, userId); err != nil { + return err + } + systemMsg := group_m.GroupSystemMsg{MsgId: group_e.GroupClearScreen, Source: "", Content: ""} + signal_s.SendSignalMsg(model, groupId, systemMsg, false) + return nil +} diff --git a/myerr/bizerr/bizCode.go b/myerr/bizerr/bizCode.go index 6148bb6889582b84981d66183b1b62253881cad2..48eade3af5bf74f7d26ae2907148465af46e2ebd 100644 --- a/myerr/bizerr/bizCode.go +++ b/myerr/bizerr/bizCode.go @@ -37,19 +37,24 @@ var ( EditCd = myerr.NewBusinessCode(9017, "not allow to edit", myerr.BusinessData{}) // 编辑cd中 // 麦位 - GroupMicNoPermission = myerr.NewBusinessCode(12000, "Mic has no permission to mic", myerr.BusinessData{}) // 麦位没有操作的权限 - GroupMicNoUser = myerr.NewBusinessCode(12002, "No one on Mic", myerr.BusinessData{}) // 麦位上没有人 - GroupMicLock = myerr.NewBusinessCode(12003, "Mic is locked", myerr.BusinessData{}) // 麦位加锁了 - GroupMicHasUser = myerr.NewBusinessCode(12004, "Mic occupied", myerr.BusinessData{}) // 麦位中已经有人了 - GroupMicUserHasIn = myerr.NewBusinessCode(12006, "Already on Mic", myerr.BusinessData{}) // 你已经在别的麦位上了 - GroupMicNoYou = myerr.NewBusinessCode(12007, "Not on Mic", myerr.BusinessData{}) // 你不在该麦位上 - GroupInfoMicClosed = myerr.NewBusinessCode(12009, "The Group does not open the mic positions", myerr.BusinessData{}) + GroupMicNoPermission = myerr.NewBusinessCode(12000, "Mic has no permission to mic", myerr.BusinessData{}) // 麦位没有操作的权限 + GroupMicNoUser = myerr.NewBusinessCode(12002, "No one on Mic", myerr.BusinessData{}) // 麦位上没有人 + GroupMicLock = myerr.NewBusinessCode(12003, "Mic is locked", myerr.BusinessData{}) // 麦位加锁了 + GroupMicHasUser = myerr.NewBusinessCode(12004, "Mic occupied", myerr.BusinessData{}) // 麦位中已经有人了 + GroupConcurrencyLock = myerr.NewBusinessCode(12005, "concurrent Mic operation, please try ageain later", myerr.BusinessData{}) // 麦位并发操作,请重试 + GroupMicUserHasIn = myerr.NewBusinessCode(12006, "Already on Mic", myerr.BusinessData{}) // 你已经在别的麦位上了 + GroupMicNoYou = myerr.NewBusinessCode(12007, "Not on Mic", myerr.BusinessData{}) // 你不在该麦位上 + GroupMicInByInviteFail = myerr.NewBusinessCode(12008, "从邀请中上麦失败", myerr.BusinessData{}) + GroupInfoMicClosed = myerr.NewBusinessCode(12009, "The Group does not open the mic positions", myerr.BusinessData{}) + GroupMicBanTourist = myerr.NewBusinessCode(12010, "Mic need member", myerr.BusinessData{}) // 游客不能上麦 // 群组 GroupNotFound = myerr.NewBusinessCode(14001, "Group not found", myerr.BusinessData{}) // 找不到该群 NotGroupMember = myerr.NewBusinessCode(14002, "Not a group member", myerr.BusinessData{}) // 不是群成员 + IncorrectPassword = myerr.NewBusinessCode(14003, "Incorrect password", myerr.BusinessData{}) // 密码错 NoPrivileges = myerr.NewBusinessCode(14004, "Not enough permission", myerr.BusinessData{}) // 操作权限不够 InBlacklist = myerr.NewBusinessCode(14005, "Can not join the group due to blacklist", myerr.BusinessData{}) // 在群黑名单中,不能进群 + GroupInKick = myerr.NewBusinessCode(14007, "Kicked, can not join the group. Try again later", myerr.BusinessData{}) // 在被踢出的有效期中 OwnerCannotLeave = myerr.NewBusinessCode(14008, "Owner can not leave the group", myerr.BusinessData{}) // 群主不能退群 WrongPasswordLength = myerr.NewBusinessCode(14010, "Incorrect password length", myerr.BusinessData{}) // 密码长度错误 GroupIsBanned = myerr.NewBusinessCode(14011, "group is banned by ", myerr.BusinessData{}) // 群已经被管理员封禁 @@ -73,8 +78,11 @@ var ( GroupPowerNoOwner = myerr.NewBusinessCode(15005, "power owner not exits or unique", myerr.BusinessData{}) // 国家势力主不存在或不唯一 GroupPowerStayTooShort = myerr.NewBusinessCode(15006, "You joined this power not more than 10 days ago", myerr.BusinessData{}) // 加入国家势力不超过10天 + TaskHasAward = myerr.NewBusinessCode(19001, "task has award", myerr.BusinessData{}) + //贵族 NobleNoMicSpeechCloseLevel5 = myerr.NewBusinessCode(21001, "Can't mute the King", myerr.BusinessData{}) //无法禁言贵族5 + NobleNoKickLevel5 = myerr.NewBusinessCode(21002, "Can't kick the King", myerr.BusinessData{}) //无法禁言贵族5 // 超级管理人 OfficialStaffLimit = myerr.NewBusinessCode(22001, "Operation failed", myerr.BusinessData{}) diff --git a/route/group_r/group_info.go b/route/group_r/group_info.go index 3f2af92e954f7575f1fb445f713c9b652dc345c4..51f14bbb5cff50312f7ac868d74418de388af03b 100644 --- a/route/group_r/group_info.go +++ b/route/group_r/group_info.go @@ -1,16 +1,24 @@ package group_r import ( + "context" + "encoding/json" "fmt" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/resource/redisCli" + "git.hilo.cn/hilo-common/rpc" "git.hilo.cn/hilo-common/utils" "github.com/gin-gonic/gin" "github.com/spf13/cast" "gorm.io/gorm" "hilo-group/_const/enum/game_e" "hilo-group/_const/enum/group_e" + "hilo-group/_const/enum/mgr_e" + "hilo-group/_const/enum/msg_e" + "hilo-group/_const/redis_key" + "hilo-group/cv/gift_cv" "hilo-group/cv/group_cv" "hilo-group/cv/group_power_cv" "hilo-group/cv/medal_cv" @@ -20,6 +28,7 @@ import ( "hilo-group/domain/model/game_m" "hilo-group/domain/model/group_m" "hilo-group/domain/model/mgr_m" + "hilo-group/domain/model/noble_m" "hilo-group/domain/model/res_m" "hilo-group/domain/model/rocket_m" "hilo-group/domain/model/user_m" @@ -30,7 +39,9 @@ import ( "hilo-group/req" "hilo-group/resp" "sort" + "strconv" "strings" + "time" ) // @Tags 群组 @@ -650,3 +661,617 @@ func GetGroupAdmin(c *gin.Context) (*mycontext.MyContext, error) { resp.ResponseOk(c, result) return myContext, nil } + +// @Tags 群组 +// @Summary 修改群消息设置(默认,静音,免打扰)(obsolete) +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param status formData string true "消息状态 0:默认 1:静音 2:免打扰" +// @Success 200 +// @Router /v1/imGroup/user/msg/status [put] +func GroupUserMsg(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + status, err := strconv.ParseUint(c.PostForm("status"), 10, 64) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if err := group_s.NewGroupService(myContext).GroupUserMsgStatus(userId, groupId, mysql.Type(status)); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 举报 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param reasonType formData int false "投诉原因 1:语言骚扰(verbal harassment)2:Nudity 3:不良消息 4:不雅图片" Enums(1, 2, 3, 4) +// @Param imageUrl formData string false "证据图片url" +// @Param reason formData string false "投诉内容" +// @Success 200 +// @Router /v1/imGroup/report [post] +func GroupReport(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + reasonTypeStr := c.PostForm("reasonType") + var reasonType uint64 = 0 + if reasonTypeStr != "" { + reasonType, err = strconv.ParseUint(reasonTypeStr, 10, 64) + if err != nil { + return myContext, myerr.WrapErr(err) + } + } + imageUrl := c.PostForm("imageUrl") + reason := c.PostForm("reason") + if err = group_s.NewGroupService(myContext).ReportGroup(userId, groupId, mgr_e.ReportReasonType(reasonType), imageUrl, reason); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +type GroupBanner struct { + //h5链接 + H5Url string `json:"h5Url"` + //图片地址 + BannerUrl string `json:"bannerUrl"` + //群主Id + GroupId string `json:"groupId"` +} + +// @Tags 群组 +// @Summary banner列表 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Success 200 {object} GroupBanner +// @Router /v1/imGroup/banner/list [get] +func GroupBannerList(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + var groupBanners []GroupBanner + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + mgrGroupBanners := []mgr_m.GroupBanner{} + if err := mysql.Db.Model(&mgr_m.GroupBanner{}).Where(&mgr_m.GroupBanner{ + Status: mysql.YES, + }).Order("n desc").Find(&mgrGroupBanners).Error; err != nil { + return myContext, err + } + _deviceType, _ := c.Get(mycontext.DEVICETYPE) + _appVersion, _ := c.Get(mycontext.APP_VERSION) + model := domain.CreateModelNil() + deviceType, appVersion := cast.ToString(_deviceType), cast.ToString(_appVersion) + vc := version_m.GetVersionControlByPlatform(model, deviceType) + allow, err := utils.CompareVersion(appVersion, fmt.Sprintf("<= %s", vc.AuditVersion)) + model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v", appVersion, vc.AuditVersion, allow, err) + + // 获取国家信息 + country, area, err := user_m.GetUserCountryArea(model, userId) + if err != nil { + model.Log.Errorf("GroupBannerList 获取国家资源错误 userId:%d, err:%v", userId, err) + return myContext, err + } + + groupCodes := []string{} + for i := 0; i < len(mgrGroupBanners); i++ { + groupCodes = append(groupCodes, mgrGroupBanners[i].GroupCode) + } + + groupMap := map[string]string{} + + if len(groupCodes) > 0 { + var groupInfos []group_m.GroupInfo + if err := mysql.Db.Model(&group_m.GroupInfo{}).Where("code in (?) ", groupCodes).Find(&groupInfos).Error; err != nil { + return myContext, err + } + for i := 0; i < len(groupInfos); i++ { + groupMap[groupInfos[i].Code] = groupInfos[i].TxGroupId + } + } + + nowUnix := time.Now().Unix() + for _, v := range mgrGroupBanners { + if !allow && v.Url == "https://h5.whoisamy.shop/action/hiloHtml/22_05_30_recharge/topup.html" { + model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v,skip top recharge", appVersion, vc.AuditVersion, allow, err) + continue + } + if !allow && v.ID == 2301 { + model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v,skip top recharge", appVersion, vc.AuditVersion, allow, err) + continue + } + // 是否在上架时间内 + if (v.StartAt > 0 && v.EndAt > 0) && (nowUnix < v.StartAt || nowUnix > v.EndAt) { + continue + } + // 是否在上架区域内 + if (v.Area == 1 || v.Area == 2) && area != v.Area { // 不在区域内 + continue + } + if v.Area == 3 && !utils.IsInStringList(country, v.CountryList) { + continue + } + + groupBanners = append(groupBanners, GroupBanner{ + H5Url: v.Url, + BannerUrl: v.Image, + GroupId: groupMap[v.GroupCode], + }) + } + resp.ResponseOk(c, groupBanners) + return myContext, nil +} + +type RoomBanner struct { + H5Url string `json:"h5Url"` // h5链接 + BannerUrl string `json:"bannerUrl"` // 图片地址 +} + +// @Tags 群组 +// @Summary banner列表 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Success 200 {object} RoomBanner +// @Router /v1/imGroup/roomBanners [get] +func RoomBannerList(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + model := domain.CreateModelContext(myContext) + + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + // 版本控制 + _deviceType, _ := c.Get(mycontext.DEVICETYPE) + _appVersion, _ := c.Get(mycontext.APP_VERSION) + deviceType, appVersion := cast.ToString(_deviceType), cast.ToString(_appVersion) + vc := version_m.GetVersionControlByPlatform(model, deviceType) + allow, err := utils.CompareVersion(appVersion, fmt.Sprintf("<= %s", vc.AuditVersion)) + model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v", appVersion, vc.AuditVersion, allow, err) + + result := make([]RoomBanner, 0) + + // 获取国家信息 + country, area, err := user_m.GetUserCountryArea(model, userId) + if err != nil { + model.Log.Errorf("GroupBannerList 获取国家资源错误 userId:%d, err:%v", userId, err) + return myContext, err + } + + rb := mgr_m.RoomBanner{Status: mysql.YES} + rows, err := rb.GetRoomBanners(model.Db, allow) + if err != nil { + return myContext, err + } + + for _, i := range rows { + // 是否在banner设定区域内 + if (i.Area == 1 || i.Area == 2) && area != i.Area { // 不在区域内 + continue + } + if i.Area == 3 && !utils.IsInStringList(country, i.CountryList) { + continue + } + + result = append(result, RoomBanner{ + H5Url: i.Url, + BannerUrl: i.Image, + }) + } + resp.ResponseOk(c, result) + return myContext, nil +} + +// @Tags 群组 +// @Summary 通知客户端banner列表更新 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Success 200 +// @Router /v1/imGroup/roomBanners [put] +func NotifyRoomBannerListChange(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + if !user_m.IsSuperUser(userId) { + return myContext, bizerr.NoPrivileges + } + + if err = rpc.SendConfigChange(userId, rpc.RoomBannerChange); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 获取房间内的在线用户 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string true "群ID" +// @Success 200 {object} OnlineUserResult +// @Router /v1/imGroup/online/users [get] +func GroupInUsers(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupId := c.Query("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err := group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + myContext.Log.WithField("groupId", groupId) + userIds, err := group_m.RoomLivingExistsUserId(groupId) + if err != nil { + return myContext, err + } + myContext.Log.Infof("GroupInUsers userIds:%v", userIds) + cacheDate, err := redisCli.GetRedis().Get(context.Background(), redis_key.GetGroupOnlineUser(groupId)).Result() + if err != nil { + //myContext.Log.Errorf("GroupInUsers GetRedis Get err:%v, groupId:%v", err, groupId) + //查找数据库 + return GroupInUsersDb(c, myContext, userIds, groupId) + } else if cacheDate == "" { + //查找数据库 + return GroupInUsersDb(c, myContext, userIds, groupId) + } else { + onlineUserResult := OnlineUserResult{} + if err := json.Unmarshal([]byte(cacheDate), &onlineUserResult); err != nil { + myContext.Log.Errorf("GroupInUsers Unmarshal err:%v", err) + //查找数据库 + return GroupInUsersDb(c, myContext, userIds, groupId) + } + //对比数量 + if len(userIds) != len(onlineUserResult.RedisUserIds) { + myContext.Log.Infof("GroupInUsers cache groupId:%v size:%v RedisUserIds size:%v", groupId, len(userIds), len(onlineUserResult.RedisUserIds)) + //查找数据库 + return GroupInUsersDb(c, myContext, userIds, groupId) + } else { + //对比顺序 + for i, _ := range userIds { + //出现不一致 + if userIds[i] != onlineUserResult.RedisUserIds[i] { + myContext.Log.Infof("GroupInUsers cache groupId:%v RedisUserIds userId no same", groupId) + return GroupInUsersDb(c, myContext, userIds, groupId) + } + } + myContext.Log.Infof("GroupInUsers cache groupId:%v hit", groupId) + resp.ResponseOk(c, onlineUserResult) + return myContext, nil + } + + } + +} + +type OnlineUserResult struct { + Total int `json:"total"` + Users []*user_cv.CvUserBase `json:"users"` + ServiceTime int64 `json:"serviceTime"` + //利用这个来判断是否要更新缓存 + RedisUserIds []uint64 `json:"redisUserIds"` +} + +//显示的最大数量是15个。 +func GroupInUsersDb(c *gin.Context, myContext *mycontext.MyContext, userIds []uint64, groupId string) (*mycontext.MyContext, error) { + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + if len(userIds) == 0 { + if err != nil { + return myContext, err + } + resp.ResponseOk(c, OnlineUserResult{ + Total: len(userIds), + Users: nil, + }) + return myContext, nil + } + + //贵族 + nobleLevelMap, err := noble_m.BatchGetNobleLevel(mysql.Db, userIds) + if err != nil { + return myContext, err + } + + vipMap, err := user_m.BatchGetVips(userIds) + if err != nil { + return myContext, err + } + // + var maxNum int = 15 + vipList := make([]uint64, 0, maxNum) + userList := make([]uint64, 0, maxNum) + //认为需求中,不会再增加贵族6,贵族7了 + nobleLevel5 := make([]uint64, 0, maxNum) + nobleLevel4 := make([]uint64, 0, maxNum) + nobleLevel3 := make([]uint64, 0, maxNum) + nobleLevel2 := make([]uint64, 0, maxNum) + nobleLevel1 := make([]uint64, 0, maxNum) + // + for i, userId := range userIds { + if len(vipList)+len(userList) >= maxNum { + break + } + if level, flag := nobleLevelMap[userId]; flag { + if level == 5 { + nobleLevel5 = append(nobleLevel5, userId) + } else if level == 4 { + nobleLevel4 = append(nobleLevel4, userId) + } else if level == 3 { + nobleLevel3 = append(nobleLevel3, userId) + } else if level == 2 { + nobleLevel2 = append(nobleLevel2, userId) + } else if level == 1 { + nobleLevel1 = append(nobleLevel1, userId) + } else if level == 0 { + if vipMap[userIds[i]] != nil { + vipList = append(vipList, userIds[i]) + } else { + userList = append(userList, userIds[i]) + } + } + } + } + myContext.Log.Infof("GroupInUsers vip and noble ready") + + //进入房间时间排序 + roomUsers := []uint64{} + if err := mysql.Db.Model(&group_m.GroupUser{}).Select("user_id").Where(&group_m.GroupUser{ + GroupId: groupId, + }).Where("user_id IN (?)", userList).Order("in_room_time DESC").Find(&roomUsers).Error; err != nil { + return myContext, err + } + //转换成map + roomUserMap := map[uint64]struct{}{} + for i, _ := range roomUsers { + roomUserMap[roomUsers[i]] = struct{}{} + } + + // + resultList := make([]uint64, 0, maxNum) + resultList = append(resultList, nobleLevel5...) + resultList = append(resultList, nobleLevel4...) + resultList = append(resultList, nobleLevel3...) + resultList = append(resultList, nobleLevel2...) + resultList = append(resultList, nobleLevel1...) + resultList = append(resultList, vipList...) + resultList = append(resultList, roomUsers...) + //预防groupRoom中没有数据的,毕竟进入房间的时候,异常没有处理 + for i, _ := range userList { + if _, flag := roomUserMap[userList[i]]; !flag { + resultList = append(resultList, userList[i]) + } + } + myContext.Log.Infof("GroupInUsers resultList ready") + + userBaseMap, err := user_cv.GetUserBaseMap(resultList, userId) + if err != nil { + return myContext, err + } + + users := make([]*user_cv.CvUserBase, 0, maxNum) + for i, _ := range resultList { + if u, flag := userBaseMap[resultList[i]]; flag { + users = append(users, u) + } + } + onlineUserResult := OnlineUserResult{ + Total: len(userIds), + Users: users, + ServiceTime: time.Now().Unix(), + RedisUserIds: userIds, + } + resp.ResponseOk(c, onlineUserResult) + + if bytes, err := json.Marshal(onlineUserResult); err != nil { + myContext.Log.Errorf("GroupInUsers Marshal err:%v", err) + } else { + if _, err := redisCli.GetRedis().Set(context.Background(), redis_key.GetGroupOnlineUser(groupId), string(bytes), 5*time.Minute).Result(); err != nil { + myContext.Log.Infof("GroupInUsers cache Set groupId:%v, err:%v", groupId, err) + } else { + myContext.Log.Infof("GroupInUsers cache success groupId:%v", groupId) + } + } + return myContext, nil +} + +// @Tags 群组 +// @Summary 获取房间内的在线用户 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string true "群ID" +// @Success 200 {object} group_m.RoomOnlineUser +// @Router /v1/imGroup/online/users/new [get] +func GroupInUserNew(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupId := c.Query("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err := group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + roomOnlineUser, err := group_m.GetRoomOnlineUser(myContext, groupId) + //roomOnlineUser, err := cv.GetGroupInUser(myContext, groupId) + if err != nil { + return myContext, err + } + resp.ResponseOk(c, roomOnlineUser) + return myContext, nil +} + +// @Tags 群组 +// @Summary 获取勋章 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string true "群ID" +// @Success 200 {object} ResultGroupTheme +// @Router /v1/imGroup/medal/all [get] +func GroupMedalAll(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupId := c.Query("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err := group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + resMedals := []res_m.ResMedal{} + if err := mysql.Db.Model(&res_m.ResMedal{}).Joins("inner join group_medal m on m.res_medal_id = res_medal.id").Where("m.im_group_id = ?", groupId).Order("m.id desc").Find(&resMedals).Error; err != nil { + return myContext, err + } + + returnGroupMedals := make([]medal_cv.ReturnGroupMedal, 0, len(resMedals)+1) + + // 补上房间流水勋章 + var pe *medal_cv.PicElement + _, pe, err = medal_cv.GetGroupConsumeMedal(model, groupId) + if err != nil { + model.Log.Infof("GetGroupConsumeMedal: %s", err.Error()) + } else if pe != nil { + returnGroupMedals = append(returnGroupMedals, medal_cv.ReturnGroupMedal{PicUrl: pe.PicUrl}) + } + + for _, r := range resMedals { + returnGroupMedals = append(returnGroupMedals, medal_cv.ReturnGroupMedal{ + PicUrl: r.PicUrl, + SvgaUrl: r.SvgaUrl, + }) + } + + resp.ResponseOk(c, returnGroupMedals) + return myContext, nil +} // @Tags 群组 + +// @Tags 群组 +// @Summary 查询房间勋章 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string false "群ID" +// @Success 200 {object} cv.RoomMedalInfo +// @Router /v1/imGroup/medal/room [get] +func GetRoomMedal(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + groupId := c.Query("groupId") + result := make([]group_cv.RoomMedalInfo, 0) + + model := domain.CreateModelContext(myContext) + + var consumeTotal uint64 + if len(groupId) > 0 { + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, bizerr.GroupNotFound + } + } else { + groups, err := group_m.FindGroupByOwner(model, userId) + if err != nil { + return myContext, err + } + if len(groups) > 0 { + groupId = groups[0].ImGroupId + } + } + + if len(groupId) > 0 { + consumeTotal, err = gift_cv.GetGroupConsumeTotal(model, groupId) + if err != nil { + return myContext, err + } + } + model.Log.Infof("Room %s totoal consume = %d", groupId, consumeTotal) + + rec, err := res_m.GetRoomMedalConfig(model.Db) + if err != nil { + return myContext, err + } + + ui, err := user_m.GetUser(model, userId) + if err != nil { + return myContext, err + } + lang := utils.DEFAULT_LANG + if len(ui.Language) > 0 { + lang = ui.Language + } + template, err := res_m.GetResMultiTextBy(model.Db, msg_e.MSG_ID_ROOM_MEADAL, lang) + if err != nil { + template = nil + } + + for _, i := range rec { + previewUrl := i.InactiveUrl + if consumeTotal >= i.Threshold { + previewUrl = i.ActiveUrl + } + + desc := i.Desc + if template != nil { + num := strconv.Itoa(int(i.Threshold)/1000000) + "M" + desc = fmt.Sprintf(template.Content, num) + } + + result = append(result, group_cv.RoomMedalInfo{ + Level: i.Level, + PreviewUrl: previewUrl, + PicUrl: i.ActiveUrl, + Desc: desc, + }) + } + + resp.ResponseOk(c, result) + return myContext, nil +} diff --git a/route/group_r/group_list.go b/route/group_r/group_list.go index d1bfadeaa530319bf939a50eea2283817a411615..d6a8928ed0962be2235ab6dd8b631175a3eb67e9 100644 --- a/route/group_r/group_list.go +++ b/route/group_r/group_list.go @@ -16,6 +16,7 @@ import ( "hilo-group/cv/medal_cv" "hilo-group/cv/user_cv" "hilo-group/domain/cache/res_c" + "hilo-group/domain/cache/room_c" "hilo-group/domain/model/game_m" "hilo-group/domain/model/group_m" "hilo-group/domain/model/noble_m" @@ -1141,3 +1142,321 @@ func buildRoleMembers(model *domain.Model, groupId string, userId uint64) ([]gro }) return roleMembers, myRole, nil } + +// @Tags 群组 +// @Summary 国家A,群主是属于国家A下的群 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param countryShortName query string true "国家缩写" +// @Param pageSize query int true "分页大小 默认:10" default(10) +// @Param pageIndex query int true "第几个分页,从1开始 默认:1" default(1) +// @Success 200 {object} cv.PopularGroupInfo +// @Router /v1/imGroup/country [get] +func GetGroupByCountry(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + myUserId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + countryShortName := c.Query("countryShortName") + if countryShortName == "" { + return myContext, myerr.NewSysError("countryShortName 为必填项") + } + pageSize, err := strconv.Atoi(c.Query("pageSize")) + if err != nil || pageSize <= 0 { + pageSize = 10 + } + pageIndex, err := strconv.Atoi(c.Query("pageIndex")) + if err != nil || pageIndex <= 0 { + pageIndex = 1 + } + + model := domain.CreateModelContext(myContext) + + bannedGroups, err := group_m.GetBannedGroupsMap(model) + if err != nil { + return myContext, err + } + + model.Log.Infof("GetGroupByCountry: page size = %d, page index = %d, banMap %v", pageSize, pageIndex, bannedGroups) + + beginTime := time.Now() + groups, banCount, visitCount, err := getCandidatesByCountry(model, bannedGroups, countryShortName) + if err != nil { + return myContext, err + } + endTime := time.Now() + model.Log.Infof("GetGroupByCountry: candidates size = %d, takes %d ms banned = %d, visitCount size = %d", + len(groups), endTime.Sub(beginTime).Milliseconds(), banCount, len(visitCount)) + model.Log.Infof("GetGroupByCountry cost1:%v", time.Now().Sub(beginTime)) + + hotGroupList := make([]*group_m.GroupInfo, 0) + + // 获取麦上有人的所有群组及麦上人数 + micGroupNum, err := group_m.GetMicHasInGroupNum(model) + if err != nil { + return myContext, err + } + model.Log.Infof("GetGroupByCountry, micGroupNum : %v", micGroupNum) + model.Log.Infof("GetGroupByCountry cost2:%v", time.Now().Sub(beginTime)) + + sortedGroupIds := make([]string, 0) + diamondGroupIds := make([]string, 0) + for i, _ := range groups { + // 麦上没人也放出来 + sortedGroupIds = append(sortedGroupIds, i) + // 麦上有人才计算流水 + if micGroupNum[i] > 0 { + diamondGroupIds = append(diamondGroupIds, i) + } + } + + now := time.Now() + bTime := now.Add(-time.Minute * 30) + g := gift_cv.GiftOperate{SceneType: gift_e.GroupSceneType} + diamonds, err := g.GetRangeConsumeSummaryV2(bTime, now, diamondGroupIds) + if err != nil { + return myContext, err + } + model.Log.Infof("GetGroupByCountry, diamonds in 30 mins: %v", diamonds) + model.Log.Infof("GetGroupByCountry cost3:%v", time.Now().Sub(beginTime)) + + supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() + if err != nil { + return myContext, err + } + model.Log.Infof("GetGroupByCountry, supportLevels : %v", supportLevels) + model.Log.Infof("GetGroupByCountry cost4:%v", time.Now().Sub(beginTime)) + + // 排序优先级2022-07-25 + sort.Slice(sortedGroupIds, func(i, j int) bool { + gi := sortedGroupIds[i] + gj := sortedGroupIds[j] + + // 1、按麦上人数多少排序 + if micGroupNum[gi] > micGroupNum[gj] { + return true + } else if micGroupNum[gi] < micGroupNum[gj] { + return false + } + + // 2、麦上人数相同,按30分钟内送礼钻石数排序 + if diamonds[gi] > diamonds[gj] { + return true + } else if diamonds[gi] < diamonds[gj] { + return false + } + // 3. 根据热度排序groupInUserDuration + if visitCount[gi] > visitCount[gj] { + return true + } else if visitCount[gi] < visitCount[gj] { + return false + } + + // * Final resort: 群组CODE,短号优先,然后按字母序 + return len(groups[gi].Code) < len(groups[gj].Code) || len(groups[gi].Code) == len(groups[gj].Code) && groups[gi].Code < groups[gj].Code + }) + model.Log.Infof("GetGroupByCountry cost5:%v", time.Now().Sub(beginTime)) + + // for pretty log + //logstr := "" + // * 同语言区 ^ 麦上有人 + 开放群 - 需要等级的群 + for _, i := range sortedGroupIds { + hotGroupList = append(hotGroupList, groups[i]) + //prefix := " " + //if micGroupNum[i] > 0 { + //prefix += "^" + //} + //logstr += prefix + i + ":" + groups[i].Code + ":" + + // strconv.Itoa(int(supportLevels[i])) + ":" + strconv.Itoa(int(micGroupNum[i])) + ":" + strconv.Itoa(int(visitCount[i])) + } + total := len(hotGroupList) + model.Log.Infof("GetGroupByCountry: size = %d, %s", total) + + result := make([]group_cv.PopularGroupInfo, 0) + + beginPos := pageSize * (pageIndex - 1) + endPos := pageSize * pageIndex + if beginPos < total { + if endPos > total { + endPos = total + } + + groupIds := make([]string, 0) + owners := make([]uint64, 0) + for _, i := range hotGroupList[beginPos:endPos] { + groupIds = append(groupIds, i.ImGroupId) + owners = append(owners, i.Owner) + } + powerIds, powerNames, err := group_power_cv.BatchGetGroupPower(model.Db, owners) + if err != nil { + return myContext, err + } + groupMedals, err := group_m.BatchGetMedals(model.Db, groupIds) + if err != nil { + return myContext, err + } + resMedal, err := res_m.MedalGetAllMap(model.Db) + if err != nil { + return myContext, err + } + + model.Log.Infof("GetGroupByCountry: final start = %d, end = %d, groupIds %v", beginPos, endPos, groupIds) + + countryInfo, err := res_c.GetCountryIconMap(model) + if err != nil { + return myContext, err + } + + rr := rocket_m.RocketResult{} + maxStageMap, err := rr.GetMaxStage(mysql.Db, groupIds) + if err != nil { + return myContext, err + } + + roomCount, err := group_m.BatchGetRoomCount(model, groupIds) + if err != nil { + return nil, err + } + // 正在进行的游戏 + games := game_m.GetNotEndGamesMap(model) + + for _, i := range hotGroupList[beginPos:endPos] { + var maxStage *uint16 = nil + if s, ok := maxStageMap[i.ImGroupId]; ok { + maxStage = &s + } + + medals := make([]medal_cv.PicElement, 0) + if m, ok := groupMedals[i.ImGroupId]; ok { + for _, j := range m { + mId := uint32(j) + if e, ok := resMedal[mId]; ok { + medals = append(medals, medal_cv.PicElement{ + PicUrl: e.PicUrl, + }) + } + } + } + // 补上房间流水勋章 + var pe *medal_cv.PicElement + _, pe, err = medal_cv.GetGroupConsumeMedal(model, i.ImGroupId) + if err != nil { + model.Log.Infof("GetGroupByCountry: GetGroupConsumeMedal: %s", err.Error()) + } else if pe != nil { + medals = append(medals, medal_cv.PicElement{PicUrl: pe.PicUrl}) + } + + var password *string = nil + if len(i.Password) > 0 && i.Owner != myUserId { + emptyStr := "" + password = &emptyStr + } + result = append(result, group_cv.PopularGroupInfo{ + GroupInfo: group_cv.GroupInfo{ + GroupBasicInfo: group_cv.GroupBasicInfo{ + GroupId: i.TxGroupId, + Name: i.Name, + Introduction: i.Introduction, + Notification: i.Notification, + FaceUrl: i.FaceUrl, + Code: i.Code, + CountryIcon: countryInfo[i.Country], + Password: password, + SupportLevel: supportLevels[i.ImGroupId], + GroupInUserDuration: visitCount[i.ImGroupId], + MicNumType: int(i.MicNumType), + GroupMedals: medals, + }, + HasOnMic: micGroupNum[i.ImGroupId] > 0, + GroupPowerId: powerIds[i.Owner], + GroupPowerName: powerNames[i.Owner], + }, + MicUsers: []user_cv.CvUserTiny{}, + RoomUserCount: uint(roomCount[i.ImGroupId]), + MaxStage: maxStage, + GameTypes: games[i.TxGroupId], + }) + } + } + model.Log.Infof("GetGroupByCountry cost6:%v", time.Now().Sub(beginTime)) + resp.ResponsePageOk(c, result, uint(total), pageIndex) + return myContext, nil +} + +// 国家群候选:没有密码且没被封禁的群, 有国家 +func getCandidatesByCountry(model *domain.Model, bannedGroups map[string]uint64, country string) (map[string]*group_m.GroupInfo, int, map[string]int64, error) { + noPwdGroups, err := group_m.FindOwnerCountryGroups(model, country) + if err != nil { + return nil, 0, nil, err + } + + roomVisitCount, err := room_c.GetAllRoomVisitCount() + if err != nil { + return nil, 0, nil, err + } + + banCount := 0 + groups := make(map[string]*group_m.GroupInfo, 0) + visitCount := make(map[string]int64) + for i, v := range noPwdGroups { + // 过滤掉被封禁的群 + if bannedGroups[v.ImGroupId] != 0 { + banCount++ + continue + } + + // 先从二级缓存中找 + if c, ok := roomVisitCount[v.ImGroupId]; ok { + if vc, err := strconv.ParseInt(c, 10, 64); err == nil && vc > 0 { + //model.Log.Debugf("getPopularCandidates, from roomVisitCount %s(%s) - %d", v.ImGroupId, v.Code, vc) + + groups[v.ImGroupId] = &noPwdGroups[i] + visitCount[v.ImGroupId] = vc + } + } else { + // 如果没有,就从roomVisit中取 + if vc, err := room_c.GetSetRoomVisitCount(v.ImGroupId); err == nil && vc > 0 { + model.Log.Infof("getPopularCandidates, from roomVisit %s(%s) - %d", v.ImGroupId, v.Code, vc) + + groups[v.ImGroupId] = &noPwdGroups[i] + visitCount[v.ImGroupId] = vc + } + } + } + return groups, banCount, visitCount, nil +} + +// @Tags 资源 +// @Summary 国家资源 +// @Param token header string true "token" +// @Param timestamp header string true "时间戳" +// @Param nonce header string true "随机数字" +// @Param signature header string true "sha1加密结果" +// @Param deviceType header string true "系统类型 ios android" +// @Param deviceVersion header string true "系统版本" +// @Success 200 {object} cv.CvCountry +// @Router /v1/imGroup/country/prior [get] +func GroupountryPrior(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + //沙特、科威特、伊拉克、阿尔及利亚、印度、菲律宾、土耳其 + //沙特,伊拉克,科威特,土耳其。印度,巴基斯坦,印尼 + resCountry, err := res_m.GetAllCountryByFilter(domain.CreateModelNil(), []string{"KSA", "Iraq", "Kuwait", "Turkey", "India", "Pakistan", "Indonesia"}) + if err != nil { + return myContext, err + } + + var cvContrys []group_cv.CvCountry + for i := 0; i < len(resCountry); i++ { + cvContrys = append(cvContrys, group_cv.CvCountry{ + Name: &resCountry[i].Name, + ShortName: &resCountry[i].ShortName, + Icon: &resCountry[i].Icon, + Code: &resCountry[i].Code, + }) + } + + resp.ResponseOk(c, cvContrys) + return myContext, nil +} diff --git a/route/group_r/group_mic.go b/route/group_r/group_mic.go new file mode 100644 index 0000000000000000000000000000000000000000..1a6cfa8dae1e85d8fded6d077b053ab080fecc8a --- /dev/null +++ b/route/group_r/group_mic.go @@ -0,0 +1,993 @@ +package group_r + +import ( + "encoding/json" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/utils" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "hilo-group/_const/enum/diamond_e" + "hilo-group/_const/enum/group_e" + "hilo-group/_const/enum/task_e" + "hilo-group/cv/diamond_cv" + "hilo-group/cv/group_cv" + "hilo-group/cv/mic_cv" + "hilo-group/domain/cache/mic_c" + "hilo-group/domain/model/diamond_m" + "hilo-group/domain/model/group_m" + "hilo-group/domain/model/res_m" + "hilo-group/domain/model/task_m" + "hilo-group/domain/service/group_mic_s" + "hilo-group/myerr" + "hilo-group/myerr/bizerr" + "hilo-group/req" + "hilo-group/resp" + "strconv" + "time" +) + +// @Tags 群组 +// @Summary 麦位所有信息(支持高频获取,前端可以高频获取,进行校正), v2.5.0_2_211025 版本之前,只能获取5个麦位,兼容于旧版本数据 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid query string true "groupUuid" +// @Success 200 {object} group_cv.CvMic +// @Router /v1/imGroup/mic/all [get] +func GroupMicAllInfoFive(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.Query("groupUuid") + + userId, extendId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + group_m.UpdateMicExpire(model, groupUuid, extendId) + //加入房间 || 更新房间内有人信息 + //group_m.RoomLivingIn(model, groupUuid, userId) + //降低频率 + group_m.RoomLivingExpire(model, groupUuid, userId) + + mics, micUsers, err := group_m.GetAllMic(groupUuid, group_e.FiveMicNumType) + if err != nil { + return myContext, err + } + cvMics, err := group_cv.GetGroupMicAll(mics, micUsers) + if err != nil { + return myContext, err + } + resp.ResponseOk(c, cvMics) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位所有信息(支持高频获取,前端可以高频获取,进行校正)(v2.5.0_2_211025 版本之后,根据类型返回5或者10个麦位信息,数量决定了类型) +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid query string true "groupUuid" +// @Success 200 {object} group_cv.CvMic +// @Router /v1/imGroup/mic/all/type [get] +func GroupMicAllInfoTen(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.Query("groupUuid") + + userId, extendId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + group_m.UpdateMicExpire(model, groupUuid, extendId) + + //加入房间 || 更新房间内有人信息 + //group_m.RoomLivingIn(model, groupUuid, userId) + //降低频率 + group_m.RoomLivingExpire(model, groupUuid, userId) + + micNumType, err := group_m.GetMicNumType(model, groupUuid) + if err != nil { + return myContext, err + } + + //兼容于旧版本 + n := group_m.GetMicNum(micNumType) + if n >= 10 { + micNumType = group_e.TenMicNumType + } else { + micNumType = group_e.FiveMicNumType + } + + mics := []group_m.Mic{} + micUsers := []group_m.MicUser{} + mics, micUsers, err = group_m.GetAllMic(groupUuid, micNumType) + if err != nil { + return myContext, err + } + cvMics, err := group_cv.GetGroupMicAll(mics, micUsers) + if err != nil { + return myContext, err + } + resp.ResponseOk(c, cvMics) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位所有信息(支持高频获取,前端可以高频获取,进行校正)(v2.5.0_2_211025 版本之后,根据类型返回5或者10个麦位信息,数量决定了类型) +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid query string true "groupUuid" +// @Success 200 {object} group_cv.CvMic +// @Router /v1/imGroup/mic/all/type/new [get] +func GroupMicAllInfoType(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.Query("groupUuid") + + userId, extendId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + group_m.UpdateMicExpire(model, groupUuid, extendId) + + //加入房间 || 更新房间内有人信息 + //group_m.RoomLivingIn(model, groupUuid, userId) + //降低频率 + group_m.RoomLivingExpire(model, groupUuid, userId) + + micNumType, err := group_m.GetMicNumType(model, groupUuid) + if err != nil { + return myContext, err + } + + var mics []group_m.Mic + var micUsers []group_m.MicUser + + mics, micUsers, err = group_m.GetAllMic(groupUuid, micNumType) + if err != nil { + return myContext, err + } + cvMics, err := group_cv.GetGroupMicAll(mics, micUsers) + if err != nil { + return myContext, err + } + resp.ResponseOk(c, cvMics) + return myContext, nil +} + +// @Tags 群组 +// @Summary 修改麦位数量 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param micNumType formData int true "麦位数量类型 1:5个麦位 2:10个麦位, 当:micOn=fales, micNumType值不采纳,传的是0" //当:micOn=fales, micNumType值不采纳:这是前端要求的 +// @Param micOn formData bool true "是否打开麦位" +// @Success 200 +// @Router /v1/imGroup/mic/num [put] +func GroupMicNumChange(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myContext, nil + } + groupId := c.PostForm("groupId") + if groupId == "" { + return myContext, myerr.NewSysError("参数micNumType 不能为空") + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + micNumType, err := strconv.ParseUint(c.PostForm("micNumType"), 10, 8) + if err != nil { + return myContext, myerr.NewSysError("参数groupId 不能为空") + } + + micOn, err := strconv.ParseBool(c.PostForm("micOn")) + if err != nil { + return myContext, myerr.NewSysError("参数micOn 有误") + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicNumChange(groupId, userId, group_e.GroupMicNumType(micNumType), micOn); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 查询麦位数量。返回的是枚举。 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string true "群ID" +// @Success 200 +// @Router /v1/imGroup/mic/num [get] +func GroupMicNum(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + txGroupId := c.Query("groupId") + + groupInfo, err := group_m.GetInfoByTxGroupId(domain.CreateModelContext(myContext), txGroupId) + if err != nil { + return myContext, err + } + resp.ResponseOk(c, groupInfo.MicNumType) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦上表情信令 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param micEmojiId formData int true "麦上表情ID" +// @Success 200 +// @Router /v1/imGroup/mic/emoji/msg [post] +func GroupSendMicSystemMsg(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + _, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, nil + } + + txGroupId := c.PostForm("groupId") + if txGroupId == "" { + return myContext, myerr.NewSysError("参数groupId 不能为空") + } + + micEmojiId, err := strconv.ParseUint(c.PostForm("micEmojiId"), 10, 64) + if err != nil { + return myContext, myerr.WrapErr(err) + } + // + resMicEmoji := res_m.ResMicEmoji{} + if err := mysql.Db.Model(&res_m.ResMicEmoji{}).First(&resMicEmoji, micEmojiId).Error; err != nil { + return myContext, myerr.WrapErr(err) + } + if resMicEmoji.Status == mysql.NOUSER { + return myContext, myerr.NewSysError("表情已下架") + } + buf, err := json.Marshal(mic_cv.CvMicEmoji{ + Id: resMicEmoji.ID, + Name: resMicEmoji.Name, + IconUrl: resMicEmoji.IconUrl, + SvagUrl: resMicEmoji.SvagUrl, + }) + if err != nil { + return myContext, myerr.WrapErr(err) + } + // + model := domain.CreateModelContext(myContext) + + if err := group_m.MicRPush(model, txGroupId, group_m.GroupSystemMsg{ + MsgId: group_e.GroupMicEmoji, + Source: externalId, + Content: string(buf), + }); err != nil { + return myContext, err + } + + resp.ResponseOk(c, nil) + return myContext, nil +} + +type ReturnFlag struct { + Flag bool `json:"flag"` + //1:邀请上麦 2:未完成任务上麦任务 + Type uint `json:"type"` + //钻石数量(2:未完成任务上麦任务才有值) + DiamondNum uint32 `json:"diamondNum"` +} + +// @Tags 群组 +// @Summary 邀请上麦校验弹窗 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Success 200 {object} ReturnFlag +// @Router /v1/imGroup/mic/in/invite/dialog [post] +func GroupMicInInviteDialog(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + _, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, nil + } + + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupInfo, err := group_m.GetInfoByTxGroupId(model, groupId) + if err != nil { + return nil, err + } + groupId = groupInfo.ImGroupId + + mics, micUsers, err := group_m.GetAllMic(groupId, groupInfo.MicNumType) + if err != nil { + return myContext, err + } + cvMics, err := group_cv.GetGroupMicAll(mics, micUsers) + //用户没有上麦 + micInFlag := false + //群组麦上有人 + micHasUserFlag := false + //有空余麦位 + micSpareFlag := false + + for _, r := range cvMics { + //用户在麦上 + if r.ExternalId != nil && *(r.ExternalId) == externalId { + micInFlag = true + } + //麦上有人 + if r.ExternalId != nil && *(r.ExternalId) != externalId { + micHasUserFlag = true + } + if r.ExternalId == nil && r.Lock == false { + micSpareFlag = true + } + } + + if micInFlag == false && micHasUserFlag == true && micSpareFlag == true { + resp.ResponseOk(c, ReturnFlag{Flag: true}) + return myContext, nil + } else { + resp.ResponseOk(c, ReturnFlag{ + Flag: false, + Type: 1, + DiamondNum: 0, + }) + return myContext, nil + } +} + +// @Tags 群组 +// @Summary 邀请上麦钻石校验弹窗 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Success 200 {object} ReturnFlag +// @Router /v1/imGroup/mic/task/invite/dialog [post] +func GroupMicTaskInviteDialog(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, nil + } + + groupId := c.PostForm("groupId") + if groupId == "" { + return myContext, myerr.NewSysError("参数groupId 不能为空") + } + + model := domain.CreateModelContext(myContext) + + groupInfo, err := group_m.GetInfoByTxGroupId(model, groupId) + if err != nil { + return myContext, err + } + if groupInfo == nil { + return myContext, bizerr.GroupNotFound + } + groupId = groupInfo.ImGroupId + + mics, micUsers, err := group_m.GetAllMic(groupId, groupInfo.MicNumType) + if err != nil { + return myContext, err + } + cvMics, err := group_cv.GetGroupMicAll(mics, micUsers) + //用户没有上麦 + micInFlag := false + //群组麦上有人 + micHasUserFlag := false + //有空余麦位 + micSpareFlag := false + + for _, r := range cvMics { + //用户在麦上 + if r.ExternalId != nil && *(r.ExternalId) == externalId { + micInFlag = true + } + //麦上有人 + if r.ExternalId != nil && *(r.ExternalId) != externalId { + micHasUserFlag = true + } + if r.ExternalId == nil && r.Lock == false { + micSpareFlag = true + } + } + + if micInFlag == false && micHasUserFlag == true && micSpareFlag == true { + // 今天展示过了 + if show, _ := mic_c.IsMicDayInviteDialogShowToday(model, userId); show { + resp.ResponseOk(c, ReturnFlag{ + Flag: false, + }) + return myContext, nil + } + + //产品说:同是否能领取了每日钻石关联起来 + beginTime := utils.GetZeroTime(time.Now()) + endTime := beginTime.AddDate(0, 0, 1) + + var n int64 + if err := mysql.Db.Model(&diamond_m.DiamondAccountDetail{}).Where(&diamond_m.DiamondAccountDetail{ + UserId: userId, + AddReduce: mysql.ADD, + }).Where("Created_Time <= ? and Created_Time >= ?", endTime, beginTime).Where("(Operate_Type = ? or Operate_Type = ?)", diamond_e.DailyInAppVip, diamond_e.DailyInAppCommon).Count(&n).Error; err != nil { + if err != nil { + return myContext, nil + } + } + if n > 0 { + //判断是否完成上麦任务 + taskConfig := task_m.TaskConfig{} + if err := mysql.Db.Model(&task_m.TaskConfig{}).Where(&task_m.TaskConfig{ + Type: task_e.MicIn, + }).First(&taskConfig).Error; err != nil { + if err == gorm.ErrRecordNotFound { + } else { + return myContext, myerr.WrapErr(err) + } + } + //任务还存在 + if taskConfig.ID != 0 { + var n int64 + if err := mysql.Db.Model(&task_m.TaskUser{}).Where(&task_m.TaskUser{ + TaskConfigId: taskConfig.ID, + UserId: userId, + DayStr: time.Now().Format(utils.COMPACT_DATE_FORMAT), + HasFinish: mysql.YES, + }).Count(&n).Error; err != nil { + return myContext, myerr.WrapErr(err) + } + //存在还没完成 + if n == 0 { + resp.ResponseOk(c, ReturnFlag{ + Flag: true, + Type: 2, + DiamondNum: taskConfig.Diamond, + }) + return myContext, nil + } + } + } + resp.ResponseOk(c, ReturnFlag{ + Flag: true, + Type: 1, + }) + return myContext, nil + } else { + resp.ResponseOk(c, ReturnFlag{ + Flag: false, + Type: 1, + DiamondNum: 0, + }) + return myContext, nil + } +} + +// @Tags 群组 +// @Summary 上麦 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string false "麦序, 空字符串,代表有麦位就上" +// @Success 200 +// @Router /v1/imGroup/mic/in [post] +func GroupMicIn(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + nonce := c.GetHeader("nonce") + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupInfo, err := group_m.GetInfoByTxGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + if groupInfo == nil { + return myContext, bizerr.GroupNotFound + } + groupUuid = groupInfo.ImGroupId + + // 游客,且设置了游客不能上麦,则失败 + // nonce = hilo,服务端内部调用,机器人 + if groupInfo.TouristMic != 1 && nonce != "hilo" { + role, err := GetGroupRoleById(model, groupInfo.ImGroupId, userId) + if err != nil { + return myContext, err + } + if role == group_e.GROUP_VISITOR { + // 旧版本(2.32.0以下),提示升级 + _, major, minor, _, err := req.GetAppVersion(c) + if err != nil { + return myContext, err + } + if major <= 2 && minor < 32 { + return myContext, bizerr.UpgradeRequired + } + return myContext, bizerr.GroupMicBanTourist + } + } + iStr := c.PostForm("i") + micIndex := -1 + // 用户已经在麦上,必须先下麦! + micUser, err := group_m.GetMicUserByExternalId(model, externalId) + if err != nil { + return myContext, err + } + if micUser != nil { + if iStr == "" { + // 非切麦操作 + return myContext, bizerr.GroupMicUserHasIn + } + // 切换麦位,先下麦 + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicLeave(groupUuid, micUser.I, userId, externalId); err != nil { + return myContext, err + } + } + + if iStr == "" { + //判断群组设置上的麦 是否被关闭 + if groupInfo.MicOn == false { + return myContext, bizerr.GroupInfoMicClosed + } + //micNum := 5 + //if groupInfo.MicNumType == group_enum.TenMicNumType { + // micNum = 10 + //} + micNum := group_m.GetMicNum(groupInfo.MicNumType) + for i := 1; i <= micNum; i++ { + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicIn(groupUuid, i, userId, externalId); err != nil { + if i == micNum { + //最后一次了,依旧错误,则 + return myContext, bizerr.GroupMicInByInviteFail + } + } else { + //成功则跳出 + micIndex = i + break + } + } + } else { + i, err := strconv.Atoi(iStr) + if err != nil { + return myContext, err + } + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicIn(groupUuid, i, userId, externalId); err != nil { + return myContext, err + } + micIndex = i + } + data := map[string]interface{}{ + "micIndex": micIndex, + } + if nonce == "hilo" { + resp.ResponseOk(c, data) + } else { + resp.ResponseOk(c, nil) + } + return myContext, nil +} + +func GetGroupRoleById(model *domain.Model, imGroupId string, userId uint64) (role group_e.GroupRoleType, err error) { + role = group_e.GROUP_VISITOR + roles, _, err := group_m.GetRolesInGroup(model, imGroupId) + if err != nil { + return + } + for u, r := range roles { + if u == userId { + role = r + return + } + } + + isGroupMember, err := group_m.IsGroupMember(model.Db, imGroupId, userId) + if err != nil { + return + } + if isGroupMember { + role = group_e.GROUP_MEMBER + } + return +} + +// @Tags 群组 +// @Summary 邀请上麦 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param beInvitedExternalId formData string false "被邀请人的ExternalId" +// @Success 200 +// @Router /v1/imGroup/mic/invite [post] +func GroupMicInvite(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + beInvitedExternalId := c.PostForm("beInvitedExternalId") + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicInvite(groupUuid, userId, beInvitedExternalId); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 自己离开麦位, 管理人让他下麦 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/leave [post] +func GroupMicLeave(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupId := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + _, lang, err := req.GetUserIdLang(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + micUser, err := group_m.GetMicUser(model, groupId, i) + if err != nil { + return myContext, err + } + if micUser != nil && micUser.UserId != userId { // 抱下麦 + // 检查权限 + if err = CheckOptToSvip6(model, userId, micUser.UserId, lang, 11); err != nil { + return myContext, err + } + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicLeave(groupId, i, userId, externalId); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位加锁 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/lock [post] +func GroupMicLock(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicLock(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位解锁 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/unlock [post] +func GroupMicUnLock(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicUnLock(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 开麦(可以说话) +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/speech/open [post] +func GroupMicSpeechOpen(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicSpeechOpen(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 关麦(不可以说话),管理人禁言这个麦位 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/speech/close [post] +func GroupMicSpeechClose(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicSpeechClose(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位解除静音(打开座位) +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/unmute [post] +func GroupMicUnmute(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicUnMute(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦位静音(关闭座位) +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupUuid formData string true "groupUuid" +// @Param i formData string true "麦序" +// @Success 200 +// @Router /v1/imGroup/mic/mute [post] +func GroupMicMute(c *gin.Context) (*mycontext.MyContext, error) { + // todo + myContext := mycontext.CreateMyContext(c.Keys) + groupUuid := c.PostForm("groupUuid") + i, err := strconv.Atoi(c.PostForm("i")) + if err != nil { + return myContext, err + } + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + groupUuid, err = group_m.ToImGroupId(model, groupUuid) + if err != nil { + return myContext, err + } + + if err := group_mic_s.NewGroupPowerService(myContext).GroupMicMute(userId, externalId, groupUuid, i); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 麦上用户群发成员广播 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param content formData string true "内容" +// @Success 200 {object} cv.CvDiamond +// @Router /v1/imGroup/mic/mass [post] +func GroupMicMass(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + content := c.PostForm("content") + + if err := group_mic_s.NewGroupPowerService(myContext).GroupIMMassByInMic(groupId, userId, externalId, content); err != nil { + return myContext, err + } + diamond, err := diamond_cv.GetDiamond(userId) + if err != nil { + return nil, myerr.WrapErr(err) + } + resp.ResponseOk(c, diamond) + return myContext, nil +} + +// @Tags 群组 +// @Summary 用户群发成员广播 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param content formData string true "内容" +// @Success 200 {object} cv.CvDiamond +// @Router /v1/imGroup/mgr/mass [post] +func GroupMgrMass(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + content := c.PostForm("content") + + if err := group_mic_s.NewGroupPowerService(myContext).GroupIMMassByMgr(groupId, userId, externalId, content); err != nil { + return myContext, err + } + diamond, err := diamond_cv.GetDiamond(userId) + if err != nil { + return nil, myerr.WrapErr(err) + } + resp.ResponseOk(c, diamond) + return myContext, nil +} diff --git a/route/group_r/group_op.go b/route/group_r/group_op.go index 35a2716b371d47ccf57519272c155e7591708cd6..cb9109d47cf3b901464f9b92f04ba6decc202c79 100644 --- a/route/group_r/group_op.go +++ b/route/group_r/group_op.go @@ -25,9 +25,14 @@ import ( "hilo-group/cv/gift_cv" "hilo-group/cv/group_cv" "hilo-group/cv/user_cv" + "hilo-group/domain/cache/game_c" "hilo-group/domain/cache/group_c" "hilo-group/domain/cache/res_c" + "hilo-group/domain/cache/room_c" + "hilo-group/domain/cache/tim_c" + "hilo-group/domain/cache/user_c" "hilo-group/domain/model/diamond_m" + "hilo-group/domain/model/game_m" "hilo-group/domain/model/group_m" "hilo-group/domain/model/noble_m" "hilo-group/domain/model/res_m" @@ -1101,7 +1106,7 @@ func AddPermanentMember(c *gin.Context) (*mycontext.MyContext, error) { // fixme: 这些缓存还需要吗? group_c.ClearGroupMemberCount(groupId) - group_c.AddGroupMember(model, groupId, externalId) + tim_c.AddGroupMember(model, groupId, externalId) if isInvite == 1 && !needCost { // 已经接受了进群邀请 group_m.AcceptGroupInviteJoin(model, userId, groupId) @@ -1703,3 +1708,218 @@ func downgradeRoom(myContext *mycontext.MyContext, gi *group_m.GroupInfo) error } return nil } + +// @Tags 群组 +// @Summary 进入房间 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param password formData string false "房间密码" +// @Param enterType formData int false "进房类型:1.ludo游戏快速匹配进房 2:uno" +// @Param gameCode formData string false "gameCode" +// @Success 200 {object} group_cv.GroupChannelId +// @Router /v1/imGroup/in [put] +func GroupIn(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + password := c.PostForm("password") + enterType := c.PostForm("enterType") + gameCode := c.PostForm("gameCode") + + model := domain.CreateModelContext(myContext) + gi, err := group_m.GetInfoByTxGroupId(model, groupId) + if err != nil { + return myContext, err + } + if gi == nil { + return myContext, bizerr.GroupNotFound + } + groupId = gi.ImGroupId + + ip := req.GetRequestIP(c) + imei, err := req.GetAppImei(c) + if err != nil { + imei, err = user_m.GetUserImeiStr(model, userId) + if err != nil { + return myContext, err + } + } + model.Log.Infof("GroupIn ip userId:%v,imGroupId:%v,ip:%v,imei:%v", userId, groupId, ip, imei) + + if channelId, token, err := group_s.NewGroupService(myContext).GroupIn(userId, externalId, groupId, password, imei, ip); err != nil { + return myContext, err + } else { + // 加入房间缓存 + if err = room_c.ProcessRoomVisit(groupId, userId); err != nil { + myContext.Log.Infof("GroupIn, ProcessRoomVisit err: %s", err.Error()) + } + // 更新用户进入房间缓存记录 + if err = room_c.ProcessUserRoomVisit(userId, groupId); err != nil { + myContext.Log.Infof("GroupIn, ProcessUserRoomVisit err: %s", err.Error()) + } + resp.ResponseOk(c, group_cv.GroupChannelId{ + ChannelId: channelId, + Token: token, + AgoraId: uint32(userId), + MicNumType: gi.MicNumType, + }) + + // v2.26及以后,客户端自己加TIM群,不再由服务器代加 + _, major, minor, _, err := req.GetAppVersion(c) + if err != nil || major < 2 || major == 2 && minor < 26 { + go func() { + defer func() { + if r := recover(); r != nil { + //打印错误堆栈信息 + mylogrus.MyLog.Errorf("GroupIn - JoinGroup, SYSTEM ACTION PANIC: %v, stack: %v", r, string(debug.Stack())) + } + }() + err := group_s.NewGroupService(myContext).JoinGroup(userId, externalId, gi.TxGroupId) + mylogrus.MyLog.Infof("myService.JoinGroup %s, user %d, err:%v", groupId, userId, err) + }() + } + // 判断是否需要执行游戏逻辑 + if enterType != "" && gameCode != "" { + traceId := c.Writer.Header().Get(mycontext.TRACEID) + token := c.Writer.Header().Get(mycontext.TOKEN) + err := game_c.SetAutoMathEnterRoom(userId, gi.ImGroupId, traceId, token, enterType, gameCode) + if err != nil { + model.Log.Errorf("GroupIn cache.SetAutoMathEnterRoom userId:%v, imGroupId:%v, err:%v", userId, gi.ImGroupId, err) + } + //go proxy.GameAfterEnterRoom(model, userId, externalId, traceId, token, enterType, gameCode, gi) + } + //// 临时 + //go func() { + // time.Sleep(time.Second * 2) + // //发送全麦信息 + // myContext.Log.Infof("imCallBack CallbackAfterNewMemberJoin MicAllRPush begin MemberAccount:%v, gi:%v", externalId, gi) + // if err := group_m.MicAllRPush(domain.CreateModelContext(myContext), groupId, externalId); err != nil { + // myContext.Log.Errorf("imCallBack CallbackAfterNewMemberJoin MicAllRPush err MemberAccount:%v, gi:%v,err:%v", externalId, gi, err) + // } else { + // myContext.Log.Infof("imCallBack CallbackAfterNewMemberJoin MicAllRPush success MemberAccount:%v, gi:%v,err:%v", externalId, gi, err) + // } + // //加入在线列表 + // user, err := user_m.GetUserByExtId(domain.CreateModelContext(myContext), externalId) + // if err != nil { + // myContext.Log.Errorf("imCallBack CallbackAfterNewMemberJoin RoomLivingIn GetUserByExtId err:%+v, MemberAccount:%v", err, externalId) + // } + // //添加用户在线列表 + // err = group_m.RoomLivingIn(domain.CreateModelContext(myContext), groupId, user.ID, user.ExternalId, false) + // if err != nil { + // myContext.Log.Errorf("imCallBack CallbackAfterNewMemberJoin err:%+v, userId:%v", err, user.ID) + // } else { + // myContext.Log.Infof("imCallBack CallbackAfterNewMemberJoin RoomLivingIn success MemberAccount:%v, gi:%v", externalId, gi) + // } + //}() + return myContext, nil + } +} + +// @Tags 群组 +// @Summary 离开房间 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Success 200 +// @Router /v1/imGroup/leave [post] +func GroupLeave(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, exteranlId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + if err := group_s.NewGroupService(myContext).GroupLeave(userId, exteranlId, groupId); err != nil { + myContext.Log.Errorf("GroupLeave GroupLeave err:%v", err) + return myContext, err + } else { + /* roomOnlineUser, err := cv.GetGroupInUser(domain.CreateModelContext(myContext), groupId) + if err != nil { + myContext.Log.Errorf("cron socketStatus cv.GetGroupInUser err:%v", err) + } + buf, err := json.Marshal(roomOnlineUser) + if err != nil { + myContext.Log.Errorf("cron socketStatus json.Marshal err:%v", err) + } + service.SendSignalMsg(groupId, group_m.GroupSystemMsg{ + MsgId: group_enum.GroupOnlineUser, + Content: string(buf), + } , true)*/ + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +// @Tags 群组 +// @Summary 踢人 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Param externalId formData string true "用户的externalId" +// @Success 200 +// @Router /v1/imGroup/kick [post] +func GroupKick(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, nick, avatar, _, err := req.GetUserEx(c, myContext) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + _, lang, err := req.GetUserIdLang(c, myContext) + if err != nil { + return myContext, err + } + + model := domain.CreateModelContext(myContext) + txGroupId := groupId + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + beKickExternalId := c.PostForm("externalId") + beKickUser, err := user_c.GetUserByExternalId(domain.CreateModelContext(myContext), beKickExternalId) + if err != nil { + return myContext, err + } + isGaming, err := game_m.IsGaming(model, beKickUser.ID, txGroupId) + if err != nil { + return myContext, err + } + if isGaming { + return myContext, bizerr.GamingCannotKick + } + + if err = CheckOptToSvip6(model, userId, beKickUser.ID, lang, 10); err != nil { + return myContext, err + } + + //beKickUserId, err := toUserId(beKickExternalId) + if err := group_s.NewGroupService(myContext).GroupKick(groupId, userId, externalId, nick, avatar, beKickUser.ID, beKickUser.ExternalId, beKickUser.Nick, beKickUser.Avatar); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} diff --git a/route/group_r/group_setting.go b/route/group_r/group_setting.go index 25f09851686a98611f216527816e6f21d5299309..e6e63de923928512d333538365e2d400e4b8d59a 100644 --- a/route/group_r/group_setting.go +++ b/route/group_r/group_setting.go @@ -4,16 +4,22 @@ import ( "encoding/json" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/config" "git.hilo.cn/hilo-common/resource/mysql" "git.hilo.cn/hilo-common/rpc" + "git.hilo.cn/hilo-common/sdk/aws" "git.hilo.cn/hilo-common/sdk/tencentyun" + "git.hilo.cn/hilo-common/utils" "github.com/gin-gonic/gin" "gorm.io/gorm" + "hilo-group/_const/enum/diamond_e" "hilo-group/_const/enum/group_e" "hilo-group/_const/enum/mgr_e" "hilo-group/_const/enum/msg_e" + "hilo-group/cv/diamond_cv" "hilo-group/cv/user_cv" "hilo-group/domain/cache/group_c" + "hilo-group/domain/model/diamond_m" "hilo-group/domain/model/game_m" "hilo-group/domain/model/group_m" "hilo-group/domain/model/mgr_m" @@ -27,6 +33,7 @@ import ( "hilo-group/req" "hilo-group/resp" "strconv" + "time" ) // @Tags 群组 @@ -921,3 +928,214 @@ func ResetGroupInfo(c *gin.Context) (*mycontext.MyContext, error) { resp.ResponseOk(c, nil) return myContext, nil } + +// @Tags 群组 +// @Summary 清理公屏 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId formData string true "群ID" +// @Success 200 +// @Router /v1/imGroup/mgr/clearScreen [post] +func GroupMgrClearScreen(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + if err := group_s.NewGroupService(myContext).GroupClearScreenByMgr(groupId, userId); err != nil { + return myContext, err + } + resp.ResponseOk(c, "") + return myContext, nil +} + +type ReturnGroupThemeConfig struct { + Days int `json:"days"` + NumLimit int `json:"numLimit"` + DiamondNum int `json:"diamondNum"` +} + +// @Tags 群组 +// @Summary 查询自定义主题 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Success 200 {object} ReturnGroupThemeConfig +// @Router /v1/imGroup/theme/custom/config [get] +func GroupThemeConfig(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + diamondOperateSet := diamond_m.DiamondOperateSet{} + if err := mysql.Db.Model(&diamond_m.DiamondOperateSet{}).Where(&diamond_m.DiamondOperateSet{ + Type: diamond_e.GroupCustomTheme, + }).First(&diamondOperateSet).Error; err != nil { + return myContext, err + } + resp.ResponseOk(c, ReturnGroupThemeConfig{ + Days: config.GetGroupCustomThemeConfig().DAY, + NumLimit: config.GetGroupCustomThemeConfig().PIC_LIMIT, + DiamondNum: diamondOperateSet.DiamondNum, + }) + return myContext, nil +} + +type ReturnGroupThemeAdd struct { + DiamondNum uint32 `json:"diamondNum"` + ThemeId uint64 `json:"themeId"` + ThemeUrl string `json:"themeUrl"` +} + +// @Tags 群组 +// @Summary 上传自定义主题 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param picUrl formData string true "主题URL" +// @Param groupId formData string true "群ID" +// @Success 200 {object} ReturnGroupThemeAdd +// @Router /v1/imGroup/theme/custom [post] +func GroupThemeAdd(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myContext, err + } + picUrl := c.PostForm("picUrl") + if picUrl == "" { + return myContext, myerr.NewSysError("参数 picUrl 不能为空") + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + switch config.GetConfigApp().MODERATE { + case "AWS": + passed, err := aws.ModerateLabels(model.Log, userId, picUrl) + if err == nil { + if !passed { + return myContext, bizerr.ImagePolicyViolation + } + } else { + model.Log.Warnf("ModerateLabels err:%v", err) + } + case "TENCENT": + label, err := tencentyun.ModerateImage(model, userId, "", utils.StripAwsPrefix(picUrl), picUrl) + if err == nil && label != "Pass" { + return myContext, bizerr.ImagePolicyViolation + } + } + + themeId, themeUrl, err := group_s.NewGroupService(myContext).AddGroupCustomTheme(userId, groupId, picUrl) + if err != nil { + return myContext, err + } + diamond, err := diamond_cv.GetDiamond(userId) + if err != nil { + return nil, myerr.WrapErr(err) + } + resp.ResponseOk(c, ReturnGroupThemeAdd{ + DiamondNum: *diamond.DiamondNum, + ThemeId: themeId, + ThemeUrl: themeUrl, + }) + return myContext, nil +} + +// @Tags 群组 +// @Summary 使用自定义主题 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupCustomThemeId formData int true "自定义主题ID" +// @Param groupId formData string true "群ID" +// @Success 200 +// @Router /v1/imGroup/theme/custom/using [put] +func GroupThemeUsing(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, externalId, err := req.GetUserIdAndExtId(c, myContext) + if err != nil { + return myContext, err + } + groupId := c.PostForm("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err = group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + id, err := strconv.ParseUint(c.PostForm("groupCustomThemeId"), 10, 64) + if err != nil { + return myContext, err + } + if err := group_s.NewGroupService(myContext).GroupCustomThemeUsing(userId, externalId, groupId, id); err != nil { + return myContext, err + } + resp.ResponseOk(c, nil) + return myContext, nil +} + +type ResultGroupTheme struct { + Id uint64 `json:"id"` + PicUrl string `json:"picUrl"` + RemainSecond int64 `json:"remainSecond"` +} + +// @Tags 群组 +// @Summary 查询有效的全部自定义主题 +// @Accept application/x-www-form-urlencoded +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param groupId query string true "群ID" +// @Success 200 {object} ResultGroupTheme +// @Router /v1/imGroup/theme/custom/all [get] +func GroupThemeValidAll(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + groupId := c.Query("groupId") + if len(groupId) <= 0 { + return myContext, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myContext) + groupId, err := group_m.ToImGroupId(model, groupId) + if err != nil { + return myContext, err + } + + var groupCustomThemes []group_m.GroupCustomTheme + if err := mysql.Db.Where(&group_m.GroupCustomTheme{ImGroupId: groupId}).Where("expire_time > ?", time.Now()).Order("expire_time asc").Find(&groupCustomThemes).Error; err != nil { + return myContext, err + } + // + resultGroupThemes := make([]ResultGroupTheme, 0, len(groupCustomThemes)) + now := time.Now().Unix() + for _, r := range groupCustomThemes { + resultGroupThemes = append(resultGroupThemes, ResultGroupTheme{ + Id: r.ID, + PicUrl: r.PicUrl, + RemainSecond: r.ExpireTime.Unix() - now, + }) + } + resp.ResponseOk(c, resultGroupThemes) + return myContext, nil +} diff --git a/route/router.go b/route/router.go index 92d830e348e31e4479eeea22b40aebe5ff924d41..ab74633d2af07f5eb02ca78b9fb058ed00f1e31a 100644 --- a/route/router.go +++ b/route/router.go @@ -73,47 +73,47 @@ func InitRouter() *gin.Engine { imGroup.PUT("/upgrade", wrapper(group_r.UpgradeGroup)) imGroup.PUT("/downgrade", wrapper(group_r.DowngradeGroup)) // - //imGroup.GET("/mic/all", wrapper(GroupMicAllInfoFive)) - //imGroup.GET("/mic/all/type", wrapper(GroupMicAllInfoTen)) - //imGroup.GET("/mic/all/type/new", wrapper(GroupMicAllInfoType)) - //imGroup.PUT("/mic/num", wrapper(GroupMicNumChange)) - //imGroup.GET("/mic/num", wrapper(GroupMicNum)) - //imGroup.POST("/mic/emoji/msg", wrapper(GroupSendMicSystemMsg)) - //imGroup.POST("/mic/in/invite/dialog", wrapper(GroupMicInInviteDialog)) - //imGroup.POST("/mic/task/invite/dialog", wrapper(GroupMicTaskInviteDialog)) - //imGroup.POST("/mic/in", LogRequestTime, wrapper(GroupMicIn)) - //imGroup.POST("/mic/invite", LogRequestTime, wrapper(GroupMicInvite)) - //imGroup.POST("/mic/leave", LogRequestTime, wrapper(GroupMicLeave)) - //imGroup.POST("/mic/lock", wrapper(GroupMicLock)) - //imGroup.POST("/mic/unlock", wrapper(GroupMicUnLock)) - //imGroup.POST("/mic/speech/open", wrapper(GroupMicSpeechOpen)) - //imGroup.POST("/mic/speech/close", wrapper(GroupMicSpeechClose)) - //imGroup.POST("/mic/mute", wrapper(GroupMicMute)) - //imGroup.POST("/mic/unmute", wrapper(GroupMicUnmute)) - //imGroup.PUT("/in", LogRequestTime, wrapper(GroupIn)) - //imGroup.POST("/leave", wrapper(GroupLeave)) - //imGroup.POST("/kick", wrapper(GroupKick)) - //imGroup.PUT("/user/msg/status", wrapper(GroupUserMsg)) - //imGroup.POST("/report", wrapper(GroupReport)) - //imGroup.GET("/banner/list", wrapper(GroupBannerList)) - //imGroup.GET("/roomBanners", wrapper(RoomBannerList)) - //imGroup.PUT("/roomBanners", wrapper(NotifyRoomBannerListChange)) - //imGroup.POST("/mic/gift", wrapper(GroupMicGift)) - //imGroup.POST("/mic/mass", wrapper(GroupMicMass)) - //imGroup.POST("/mgr/mass", wrapper(GroupMgrMass)) - //imGroup.POST("/mgr/clearScreen", wrapper(GroupMgrClearScreen)) - //imGroup.GET("/online/users", wrapper(GroupInUsers)) - //imGroup.GET("/online/users/new", wrapper(GroupInUserNew)) - //imGroup.GET("/country", wrapper(GetGroupByCountry)) - //imGroup.GET("/country/prior", wrapper(GroupountryPrior)) + imGroup.GET("/mic/all", wrapper(group_r.GroupMicAllInfoFive)) + imGroup.GET("/mic/all/type", wrapper(group_r.GroupMicAllInfoTen)) + imGroup.GET("/mic/all/type/new", wrapper(group_r.GroupMicAllInfoType)) + imGroup.PUT("/mic/num", wrapper(group_r.GroupMicNumChange)) + imGroup.GET("/mic/num", wrapper(group_r.GroupMicNum)) + imGroup.POST("/mic/emoji/msg", wrapper(group_r.GroupSendMicSystemMsg)) + imGroup.POST("/mic/in/invite/dialog", wrapper(group_r.GroupMicInInviteDialog)) + imGroup.POST("/mic/task/invite/dialog", wrapper(group_r.GroupMicTaskInviteDialog)) + imGroup.POST("/mic/in", wrapper(group_r.GroupMicIn)) + imGroup.POST("/mic/invite", wrapper(group_r.GroupMicInvite)) + imGroup.POST("/mic/leave", wrapper(group_r.GroupMicLeave)) + imGroup.POST("/mic/lock", wrapper(group_r.GroupMicLock)) + imGroup.POST("/mic/unlock", wrapper(group_r.GroupMicUnLock)) + imGroup.POST("/mic/speech/open", wrapper(group_r.GroupMicSpeechOpen)) + imGroup.POST("/mic/speech/close", wrapper(group_r.GroupMicSpeechClose)) + imGroup.POST("/mic/mute", wrapper(group_r.GroupMicMute)) + imGroup.POST("/mic/unmute", wrapper(group_r.GroupMicUnmute)) + imGroup.PUT("/in", wrapper(group_r.GroupIn)) + imGroup.POST("/leave", wrapper(group_r.GroupLeave)) + imGroup.POST("/kick", wrapper(group_r.GroupKick)) + imGroup.PUT("/user/msg/status", wrapper(group_r.GroupUserMsg)) + imGroup.POST("/report", wrapper(group_r.GroupReport)) + imGroup.GET("/banner/list", wrapper(group_r.GroupBannerList)) + imGroup.GET("/roomBanners", wrapper(group_r.RoomBannerList)) + imGroup.PUT("/roomBanners", wrapper(group_r.NotifyRoomBannerListChange)) + //imGroup.POST("/mic/gift", wrapper(GroupMicGift)) // todo 先留在biz,内容有点多 + imGroup.POST("/mic/mass", wrapper(group_r.GroupMicMass)) + imGroup.POST("/mgr/mass", wrapper(group_r.GroupMgrMass)) + imGroup.POST("/mgr/clearScreen", wrapper(group_r.GroupMgrClearScreen)) + imGroup.GET("/online/users", wrapper(group_r.GroupInUsers)) + imGroup.GET("/online/users/new", wrapper(group_r.GroupInUserNew)) + imGroup.GET("/country", wrapper(group_r.GetGroupByCountry)) + imGroup.GET("/country/prior", wrapper(group_r.GroupountryPrior)) // - //imGroup.POST("/theme/custom", wrapper(GroupThemeAdd)) - //imGroup.GET("/theme/custom/config", wrapper(GroupThemeConfig)) - //imGroup.PUT("/theme/custom/using", wrapper(GroupThemeUsing)) - //imGroup.GET("/theme/custom/all", wrapper(GroupThemeValidAll)) + imGroup.POST("/theme/custom", wrapper(group_r.GroupThemeAdd)) + imGroup.GET("/theme/custom/config", wrapper(group_r.GroupThemeConfig)) + imGroup.PUT("/theme/custom/using", wrapper(group_r.GroupThemeUsing)) + imGroup.GET("/theme/custom/all", wrapper(group_r.GroupThemeValidAll)) // - //imGroup.GET("/medal/all", wrapper(GroupMedalAll)) - //imGroup.GET("/medal/room", wrapper(GetRoomMedal)) + imGroup.GET("/medal/all", wrapper(group_r.GroupMedalAll)) + imGroup.GET("/medal/room", wrapper(group_r.GetRoomMedal)) } groupPower := v1.Group("/groupPower")