package main import ( "cs" "f5" "fmt" "math/rand" "q5" ) const ( LoadGuildFlag = iota LoadGuildMemberFlag LoadGuildReqFlag LoadGuildLogFlag ) type GuildMgr struct { cs.MsgHandlerImpl guildIds []int64 // 公会列表 guild ids guilds map[int64]*Guild // 公会ID -> 公会 guildLogs map[int64][]*GuildLog // 公会ID -> []公会日志列表 userGuilds map[string]int64 // accountId -> 公会ID loadedFlags int64 } func NewGuildMgr() *GuildMgr { return &GuildMgr{ guilds: make(map[int64]*Guild), guildLogs: make(map[int64][]*GuildLog), userGuilds: make(map[string]int64), } } func (gm *GuildMgr) init() { gm.loadFromDB() } func (gm *GuildMgr) loadFromDB() { q5.SetBitFlag(&gm.loadedFlags, LoadGuildFlag) q5.SetBitFlag(&gm.loadedFlags, LoadGuildMemberFlag) q5.SetBitFlag(&gm.loadedFlags, LoadGuildReqFlag) q5.SetBitFlag(&gm.loadedFlags, LoadGuildLogFlag) // 加载公会 gm.loadGuildFromDB() // 加载公会成员 gm.loadGuildMemberFromDB() // 加载公会申请者列表 gm.loadPendingReqsFromDB() // 加载公会日志 gm.loadGuildLogsFromDB() // 加载公会ids gm.loadGuildIds() //for gm.loadedFlags != 0 { // time.Sleep(time.Millisecond * 1000) //} } // CreateGuild 创建公会 func (gm *GuildMgr) CreateGuild(name string, leaderId string, cb func(errCode int32, errMsg string, guildId int64)) { if !gm.CheckJoinGuild(leaderId) { cb(1, "xxx", 0) return } guildId := f5.GetApp().NewUuid() if gm.ExistsGuild(guildId) { cb(1, "xxx", 0) return } fields := [][]string{ {"guild_id", q5.ToString(guildId)}, {"name", name}, {"leader_account_id", leaderId}, {"max_members", q5.ToString(MaxMembers)}, } f5.GetJsStyleDb().Insert( FRIEND_DB, "t_guild", fields, func(err error, id int64, affectedRows int64) { if err != nil { cb(1, "xxx", 0) return } newMember := &GuildMember{accountId: leaderId, level: GuildMemberLevelLeader} gm.upsertGuildMember(guildId, newMember, func(err error) { if err != nil { cb(1, "xxx", 0) return } guild := &Guild{ autoId: id, guildId: guildId, name: name, leaderId: leaderId, maxMembers: MaxMembers, } guild.members = append(guild.members, newMember) gm.AddGuild(guildId, guild) }) }) } // ApplyToGuild 申请加入公会 func (gm *GuildMgr) ApplyToGuild(guildId int64, applicantAccountId string, cb func(errCode int32, errMsg string)) { guild, err := gm.GetGuild(guildId) if err != nil { cb(1, "xxx") } // 是否加入其他公会 if !gm.CheckJoinGuild(applicantAccountId) { cb(1, "xxx") } if guild.IsFull() { cb(1, "xxx") } // 可直接加入 if guild.joinCond == JoinCondFree { gm.JoinGuild(guild, applicantAccountId) cb(0, "") } if guild.joinCond == JoinCondStar { var userStar int32 = 200 if userStar >= guild.joinCondValue { gm.JoinGuild(guild, applicantAccountId) cb(0, "") } } // IF exists, then replace it fields `isJoinGuild` is 0 where := [][]string{ {"guild_id", q5.ToString(guildId)}, {"account_id", applicantAccountId}, } insertKv := [][]string{ {"guild_id", q5.ToString(guildId)}, {"account_id", applicantAccountId}, {"is_join_guild", q5.ToString(PendingReqIsJoinGuildStatusDefault)}, } updateKv := [][]string{ {"is_join_guild", q5.ToString(PendingReqIsJoinGuildStatusDefault)}, } f5.GetJsStyleDb().Upsert( FRIEND_DB, "t_guild_pending_request", where, updateKv, insertKv, func(err error, lastInsertId int64, rowsAffected int64) { if err != nil { cb(0, "") return } if rowsAffected <= 0 { return } pendingReq := &PendingReq{ applicantAccountId, PendingReqIsJoinGuildStatusDefault, } guild.AddPendingReq(pendingReq) cb(0, "") }) } // Approve 同意申请者加入公会 func (gm *GuildMgr) Approve(operatorAccountId, accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } // 通常默认为审核加入 if guild.joinCond != JoinCondDefault { cb(1, "XXX") } operatorMember := guild.GetMember(operatorAccountId) if operatorMember == nil { cb(1, "XXX") } // 公会干部及以上仅可操作 err := gm.checkOperatorPerm(operatorMember, GuildMemberLevelViceLeader) if err != nil { cb(1, "XXX") } // 是否加入其他公会 if !gm.CheckJoinGuild(accountId) { cb(1, "XXX") } if guild.IsFull() { cb(1, "XXX") } guildId := guild.guildId // 是否在申请队列中 pendingReqStatus := guild.GetPendingReqStatus(accountId) if pendingReqStatus < 0 { cb(1, "XXX") } newMember := &GuildMember{accountId: accountId, level: GuildMemberLevelDefault} gm.upsertGuildMember(guildId, newMember, func(err error) { if err != nil { cb(1, "xxx") return } guild.AddMember(newMember) gm.AddUserGuild(accountId, guildId) gm.updatePendingReqs(guildId, accountId, PendingReqIsJoinGuildStatusJoined, func(err error) { logContent := fmt.Sprintf("Approve[operator:%s]", operatorAccountId) gm.WriteLog(guildId, accountId, LogTypeApprove, logContent) guild.RemovePendingReq(accountId) cb(0, "") }) }) } // Reject 拒绝申请者加入公会 func (gm *GuildMgr) Reject(operatorAccountId, accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } // 通常默认为审核加入 if guild.joinCond != JoinCondDefault { cb(1, "XXX") } operatorMember := guild.GetMember(operatorAccountId) if operatorMember == nil { cb(1, "XXX") } // 公会干部及以上仅可操作 err := gm.checkOperatorPerm(operatorMember, GuildMemberLevelViceLeader) if err != nil { cb(1, "XXX") } // 是否加入其他公会 if !gm.CheckJoinGuild(accountId) { cb(1, "XXX") } // 是否在申请队列中 pendingReqStatus := guild.GetPendingReqStatus(accountId) if pendingReqStatus < 0 { cb(1, "XXX") } gm.updatePendingReqs(guild.guildId, accountId, PendingReqIsJoinGuildStatusReject, func(err error) { guild.RemovePendingReq(accountId) cb(0, "") }) } // JoinGuild 直接加入公会 func (gm *GuildMgr) JoinGuild(guild *Guild, accountId string) { newMember := GuildMember{accountId: accountId, level: GuildMemberLevelDefault} guildId := guild.guildId gm.upsertGuildMember(guildId, &newMember, func(err error) { if err != nil { guild.AddMember(&newMember) gm.AddUserGuild(accountId, guildId) } }) } // LeaveGuild 离开公会 func (gm *GuildMgr) LeaveGuild(accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(accountId) if guild == nil { cb(1, "XXX") } if guild.leaderId == accountId { cb(1, "XXX") } member := guild.GetMember(accountId) if member == nil { cb(1, "XXX") } fields := [][]string{{"is_leave_guild", q5.ToString(1)}} gm.updateGuildMember(guild, member.accountId, fields, func(err error) { if err != nil { cb(1, err.Error()) } guild.RemoveMember(accountId) gm.RemoveUserGuild(accountId) // Add logs logContent := fmt.Sprintf("LeaveGuild[%d-%s]", guild.guildId, accountId) gm.WriteLog(guild.guildId, accountId, LogTypeLeave, logContent) cb(0, "") }) } // DismissMember 开除成员 踢出 func (gm *GuildMgr) DismissMember(operatorAccountId, accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } if accountId == guild.leaderId || accountId == operatorAccountId { cb(1, "XXX") } operatorMember := guild.GetMember(operatorAccountId) if operatorMember == nil { cb(1, "XXX") } // 公会干部及以上仅可操作 err := gm.checkOperatorPerm(operatorMember, GuildMemberLevelViceLeader) if err != nil { cb(1, "XXX") } dismissMember := guild.GetMember(accountId) if dismissMember == nil { cb(1, "XXX") } if dismissMember.level <= operatorMember.level { cb(1, "XXX") } fields := [][]string{{"is_leave_guild", q5.ToString(1)}} gm.updateGuildMember(guild, dismissMember.accountId, fields, func(err error) { if err != nil { cb(1, err.Error()) return } guild.RemoveMember(accountId) gm.RemoveUserGuild(accountId) // Add logs logContent := fmt.Sprintf("DismissMember[%d-%s-%s]", guild.guildId, operatorAccountId, accountId) gm.WriteLog(guild.guildId, accountId, LogTypeDismiss, logContent) cb(0, "") }) } // PromoteMember 提升成员为干部 func (gm *GuildMgr) PromoteMember(operatorAccountId, accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } if accountId == guild.leaderId || accountId == operatorAccountId { cb(1, "XXX") } // 仅会长操作 operatorMember := guild.GetMember(operatorAccountId) if operatorMember == nil { cb(1, "XXX") } if operatorAccountId != guild.leaderId { cb(1, "XXX") } member := guild.GetMember(accountId) if member == nil { cb(1, "XXX") } if member.level == GuildMemberLevelViceLeader { cb(1, "XXX") } fields := [][]string{{"level", q5.ToString(GuildMemberLevelViceLeader)}} gm.updateGuildMember(guild, member.accountId, fields, func(err error) { if err != nil { cb(1, err.Error()) return } member.level = GuildMemberLevelViceLeader logContent := fmt.Sprintf("PromoteMember[%d-%s-%s]", guild.guildId, operatorAccountId, accountId) gm.WriteLog(guild.guildId, accountId, LogTypeDismiss, logContent) cb(0, "") }) } // DemoteMember 解除成员干部身份 func (gm *GuildMgr) DemoteMember(operatorAccountId, accountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } if accountId == guild.leaderId || accountId == operatorAccountId { cb(1, "XXX") } // 仅会长可操作 if operatorAccountId != guild.leaderId { cb(1, "XXX") } member := guild.GetMember(accountId) if member == nil { cb(1, "XXX") } if member.level == GuildMemberLevelDefault { cb(1, "XXX") } fields := [][]string{{"level", q5.ToString(GuildMemberLevelDefault)}} gm.updateGuildMember(guild, member.accountId, fields, func(err error) { if err != nil { cb(1, err.Error()) return } member.level = GuildMemberLevelDefault logContent := fmt.Sprintf("DemoteMember[%d-%s-%s]", guild.guildId, operatorAccountId, accountId) gm.WriteLog(guild.guildId, accountId, LogTypeDismiss, logContent) cb(0, "") }) } // Disband 解散公会 func (gm *GuildMgr) Disband(operatorAccountId string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } // 仅会长可操作 if operatorAccountId != guild.leaderId { cb(1, "XXX") } guildId := guild.guildId guildName := guild.name // 解散公会 updateFields := [][]string{ {"is_deleted", q5.ToString(1)}, } gm.updateGuild(guild, updateFields, func(err error) { fields := [][]string{{"is_leave_guild", q5.ToString(1)}} gm.updateGuildMembers(guildId, fields, func(err error) { gm.updateAllPendingReqs(guildId, PendingReqIsJoinGuildStatusDisband, func(err error) { logContent := fmt.Sprintf("GuildDisbanded[%d-%s]", guildId, guildName) gm.WriteLog(guildId, operatorAccountId, LogTypeDisband, logContent) // delete user guilds for _, member := range guild.members { delete(gm.userGuilds, member.accountId) } guild.members = nil delete(gm.guilds, guildId) delete(gm.guildLogs, guildId) gm.RemoveGuild(guildId) }) }) }) } // SetNotice 设置公告 func (gm *GuildMgr) SetNotice(operatorAccountId string, notice *string, cb func(errCode int32, errMsg string)) { guild := gm.GetGuildByAccountId(operatorAccountId) if guild == nil { cb(1, "XXX") } // 仅会长可操作 if operatorAccountId != guild.leaderId { cb(1, "XXX") } updateFields := [][]string{ {"notice", *notice}, } gm.updateGuild(guild, updateFields, func(err error) { if err != nil { cb(1, err.Error()) return } guild.SetNotice(notice) cb(0, "") }) } // WriteLog 记录公会日志 func (gm *GuildMgr) WriteLog(guildId int64, accountId string, logType int32, content string) { _, err := gm.GetGuild(guildId) if err != nil { return } _, exists := gm.guildLogs[guildId] if !exists { gm.guildLogs[guildId] = make([]*GuildLog, DefaultLogs) } log := &GuildLog{ guildId: guildId, accountId: accountId, logType: logType, content: content, } gm.guildLogs[guildId] = append(gm.guildLogs[guildId], log) gm.insertGuildLog(log) } func (gm *GuildMgr) GetLogs(guildID int64) []*GuildLog { return gm.guildLogs[guildID] } // SearchGuilds 根据关键字搜索公会 func (gm *GuildMgr) SearchGuilds(keyword string) []*Guild { var results []*Guild for _, guild := range gm.guilds { if containsSubstring(guild.name, keyword) { results = append(results, guild) } } return results } // RandomGuilds 随机10个公会 func (gm *GuildMgr) RandomGuilds() []*Guild { count := len(gm.guilds) var results []*Guild if count <= RandomGuildCount { for _, guildId := range gm.guildIds { results = append(results, gm.guilds[guildId]) } return results } for i := 0; i < RandomGuildCount; i++ { j := rand.Intn(len(gm.guildIds)) gm.guildIds[i], gm.guildIds[j] = gm.guildIds[j], gm.guildIds[i] } for i := 0; i < RandomGuildCount; i++ { randGuildId := gm.guildIds[i] results = append(results, gm.guilds[randGuildId]) } return results } func (gm *GuildMgr) checkOperatorPerm(operatorMember *GuildMember, level int) error { if operatorMember.level > level { return fmt.Errorf("checkOperatorPerm: no permission[%s-%d]", operatorMember.accountId, operatorMember.level) } return nil } func (gm *GuildMgr) ExistsGuild(guildId int64) bool { _, ok := gm.guilds[guildId] return ok } func (gm *GuildMgr) AddUserGuild(accountId string, guildId int64) { gm.userGuilds[accountId] = guildId } func (gm *GuildMgr) RemoveUserGuild(accountId string) { delete(gm.userGuilds, accountId) } func (gm *GuildMgr) AddGuild(guildId int64, guild *Guild) { if _, exists := gm.guilds[guildId]; !exists { gm.guilds[guildId] = guild gm.guildIds = append(gm.guildIds, guildId) } } func (gm *GuildMgr) GetGuild(guildId int64) (*Guild, error) { if guild, exists := gm.guilds[guildId]; exists { return guild, nil } return nil, fmt.Errorf("guild not found") } func (gm *GuildMgr) GetGuildByAccountId(accountId string) *Guild { if guildId, exists := gm.userGuilds[accountId]; exists { if guild, exists2 := gm.guilds[guildId]; exists2 { return guild } } return nil } func (gm *GuildMgr) GetGuildIdByAccountId(accountId string) int64 { if guildId, exists := gm.userGuilds[accountId]; exists { return guildId } return -1 } func (gm *GuildMgr) CheckJoinGuild(accountId string) bool { _, exists := gm.userGuilds[accountId] return exists } func (gm *GuildMgr) loadGuildIds() { gm.guildIds = make([]int64, 0, len(gm.guilds)) for guildId := range gm.guilds { gm.guildIds = append(gm.guildIds, guildId) } } func (gm *GuildMgr) RemoveGuild(guildId int64) { guildIndex := -1 for i, gId := range gm.guildIds { if gId == guildId { guildIndex = i } } if guildIndex < 0 { return } copy(gm.guildIds[guildIndex:], gm.guildIds[guildIndex+1:]) gm.guildIds[len(gm.guildIds)-1] = 0 gm.guildIds = gm.guildIds[:len(gm.guildIds)-1] } func (gm *GuildMgr) GetGuildLogs(guildId int64) []*GuildLog { if logs, exists := gm.guildLogs[guildId]; exists { return logs } return nil } func containsSubstring(s, substr string) bool { return len(s) >= len(substr) && s[len(s)-len(substr):] == substr }