feat: 新增群组成员数量统计功能并在接口中返回
实现了群组成员数量统计的接口,包含数据库层统计逻辑,在群组详情、设置等接口中返回实时成员数,同时修复了测试用例中参数空格问题。
This commit is contained in:
+42
-11
@@ -4217,6 +4217,25 @@ func serveIMGroupList(wrt http.ResponseWriter, req *http.Request) {
|
||||
items = append(items, item)
|
||||
seen++
|
||||
}
|
||||
if len(items) > 0 {
|
||||
listTopics := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
if topic, ok := item["topic"].(string); ok && topic != "" {
|
||||
listTopics = append(listTopics, topic)
|
||||
}
|
||||
}
|
||||
if counts, err := store.GroupBusiness.MembersCount(principal.TenantID, listTopics); err == nil {
|
||||
for _, item := range items {
|
||||
if topic, ok := item["topic"].(string); ok {
|
||||
if count, ok := counts[topic]; ok {
|
||||
item["member_count"] = count
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logs.Warn.Printf("serveIMGroupList: failed to count members for %d topics: %v", len(listTopics), err)
|
||||
}
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{"items": items, "next_cursor": nextCursor, "has_more": nextCursor != ""})
|
||||
}
|
||||
|
||||
@@ -4310,7 +4329,7 @@ func serveIMGroupCreate(wrt http.ResponseWriter, req *http.Request) {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
writeIMSuccess(wrt, req, groupDetailPayload(created, selfSub))
|
||||
writeIMSuccess(wrt, req, groupDetailPayload(created, selfSub, groupMemberCountFor(principal.TenantID, topicName)))
|
||||
}
|
||||
|
||||
func serveIMGroupResource(wrt http.ResponseWriter, req *http.Request) {
|
||||
@@ -4607,7 +4626,7 @@ func serveIMGroupDetail(wrt http.ResponseWriter, req *http.Request, topic string
|
||||
writeIMError(wrt, req, http.StatusNotFound, 30004, imReasonMalformedRequest, "group not found", nil)
|
||||
return
|
||||
}
|
||||
payload := groupDetailPayload(group, selfSub)
|
||||
payload := groupDetailPayload(group, selfSub, groupMemberCountFor(principal.TenantID, topic))
|
||||
if settings, err := store.GroupBusiness.GetSettings(principal.TenantID, topic); err == nil {
|
||||
if settings != nil && settings.GroupStatus == types.GroupStatusDissolved {
|
||||
writeIMError(wrt, req, http.StatusGone, 30004, imReasonGroupDissolved, "group dissolved", map[string]any{"topic": topic})
|
||||
@@ -4703,7 +4722,7 @@ func serveIMGroupDetail(wrt http.ResponseWriter, req *http.Request, topic string
|
||||
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
|
||||
return
|
||||
}
|
||||
payload := groupDetailPayload(updated, selfSub)
|
||||
payload := groupDetailPayload(updated, selfSub, groupMemberCountFor(principal.TenantID, topic))
|
||||
if settingsUpdated {
|
||||
if settings, err := store.GroupBusiness.GetSettings(principal.TenantID, topic); err == nil {
|
||||
payload = mergeGroupSettingsPayload(payload, settings)
|
||||
@@ -4769,7 +4788,7 @@ func serveIMGroupSettings(wrt http.ResponseWriter, req *http.Request, topic stri
|
||||
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
|
||||
return
|
||||
}
|
||||
payload := groupSettingsPayload(group, selfSub)
|
||||
payload := groupSettingsPayload(group, selfSub, groupMemberCountFor(principal.TenantID, topic))
|
||||
if settings, err := store.GroupBusiness.GetSettings(principal.TenantID, topic); err == nil {
|
||||
payload = mergeGroupSettingsPayload(payload, settings)
|
||||
payload["governance"] = groupGovernancePayloadFromSettings(settings, selfSub)
|
||||
@@ -4946,7 +4965,7 @@ func serveIMGroupQRCodes(wrt http.ResponseWriter, req *http.Request, topic strin
|
||||
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
|
||||
return
|
||||
}
|
||||
groupPayload := groupDetailPayload(group, selfSub)
|
||||
groupPayload := groupDetailPayload(group, selfSub, groupMemberCountFor(principal.TenantID, topic))
|
||||
writeIMSuccess(wrt, req, map[string]any{
|
||||
"qr_token": token,
|
||||
"qr_payload": "im://qr?token=" + url.QueryEscape(token) + "&purpose=join_group",
|
||||
@@ -6088,7 +6107,7 @@ func serveIMQRCodesResolve(wrt http.ResponseWriter, req *http.Request) {
|
||||
if selfSub != nil && (selfSub.ModeGiven & selfSub.ModeWant).IsJoiner() {
|
||||
status = "joined"
|
||||
}
|
||||
groupPayload := groupDetailPayload(group, selfSub)
|
||||
groupPayload := groupDetailPayload(group, selfSub, groupMemberCountFor(principal.TenantID, claims.Topic))
|
||||
writeIMSuccess(wrt, req, map[string]any{
|
||||
"purpose": claims.Purpose,
|
||||
"expires_at": time.Unix(claims.Expires, 0).UTC().Format(time.RFC3339Nano),
|
||||
@@ -7505,15 +7524,27 @@ func mergeGroupListSettingsPayload(payload map[string]any, settings *types.Group
|
||||
return payload
|
||||
}
|
||||
|
||||
func groupDetailPayload(group *types.Topic, selfSub *types.Subscription) map[string]any {
|
||||
// groupMemberCountFor returns the live member count for a single group topic,
|
||||
// mirroring the visibility rules of the group members list endpoint.
|
||||
func groupMemberCountFor(tenantID types.TenantID, topic string) int64 {
|
||||
if topic == "" {
|
||||
return 0
|
||||
}
|
||||
counts, err := store.GroupBusiness.MembersCount(tenantID, []string{topic})
|
||||
if err != nil {
|
||||
logs.Warn.Printf("group[%s]: failed to count members: %v", topic, err)
|
||||
return 0
|
||||
}
|
||||
return counts[topic]
|
||||
}
|
||||
|
||||
func groupDetailPayload(group *types.Topic, selfSub *types.Subscription, memberCount int64) map[string]any {
|
||||
public := map[string]any{}
|
||||
topic := ""
|
||||
memberCount := 0
|
||||
ownerID := ""
|
||||
if group != nil {
|
||||
public = groupPublicMap(group.Public)
|
||||
topic = group.Id
|
||||
memberCount = group.SubCnt
|
||||
if owner := types.ParseUid(group.Owner); !owner.IsZero() {
|
||||
ownerID = owner.UserId()
|
||||
}
|
||||
@@ -7542,8 +7573,8 @@ func groupDetailPayload(group *types.Topic, selfSub *types.Subscription) map[str
|
||||
}
|
||||
}
|
||||
|
||||
func groupSettingsPayload(group *types.Topic, selfSub *types.Subscription) map[string]any {
|
||||
data := groupDetailPayload(group, selfSub)
|
||||
func groupSettingsPayload(group *types.Topic, selfSub *types.Subscription, memberCount int64) map[string]any {
|
||||
data := groupDetailPayload(group, selfSub, memberCount)
|
||||
private := map[string]any{}
|
||||
if selfSub != nil {
|
||||
private = groupPrivateMap(selfSub.Private)
|
||||
|
||||
Reference in New Issue
Block a user