fix: align group management APIs

This commit is contained in:
richjohnrambo
2026-09-13 16:03:55 +08:00
parent f1279ac607
commit a9afc6ff6a
34 changed files with 1381 additions and 639 deletions
+47 -13
View File
@@ -24,6 +24,7 @@ const (
adminCommandGroupMemberUnmute = "group.member.unmute"
adminCommandGroupMemberRemove = "group.member.remove"
adminCommandGroupOwnerTransfer = "group.owner.transfer"
adminCommandGroupDissolve = "group.dissolve"
adminCommandUserBan = "user.ban"
adminCommandUserUnban = "user.unban"
adminCommandUserFreeze = "user.freeze"
@@ -75,6 +76,8 @@ type groupGovernancePatch struct {
MemberUploadFiles *bool `json:"member_upload_files"`
MemberPrivateChat *bool `json:"member_private_chat"`
MemberInvite *bool `json:"member_invite"`
MaxMemberCount *int `json:"max_member_count"`
MaxAdminCount *int `json:"max_admin_count"`
}
type adminCommandResultPayload struct {
@@ -221,7 +224,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
if err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.settings.updated",
map[string]any{"topic": topic, "version": settings.Version})
return map[string]any{"topic": topic, "version": settings.Version, "updated_at": settings.UpdatedAt}, nil
case adminCommandGroupGovernUpdate:
@@ -231,12 +235,15 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
MemberUploadFiles: payload.Governance.MemberUploadFiles,
MemberPrivateChat: payload.Governance.MemberPrivateChat,
MemberInvite: payload.Governance.MemberInvite,
MaxMemberCount: payload.Governance.MaxMemberCount,
MaxAdminCount: payload.Governance.MaxAdminCount,
Version: payload.Version,
})
if err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.settings.updated",
map[string]any{"topic": topic, "version": settings.Version})
return map[string]any{"topic": topic, "version": settings.Version, "updated_at": settings.UpdatedAt}, nil
case adminCommandGroupAdminSet:
@@ -259,7 +266,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
}
succeeded = append(succeeded, map[string]any{"user_id": internalAdminUIDRaw(target), "role": types.GroupMemberRoleAdmin})
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.role.updated",
map[string]any{"topic": topic, "succeeded": succeeded})
return map[string]any{"topic": topic, "succeeded": succeeded}, nil
case adminCommandGroupAdminRemove:
@@ -271,7 +279,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
types.GroupMemberRoleMember, types.ModeCPublic, now); err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.role.updated",
map[string]any{"user_id": internalAdminUIDRaw(target), "role": types.GroupMemberRoleMember})
return map[string]any{"topic": topic, "user_id": internalAdminUIDRaw(target), "role": types.GroupMemberRoleMember}, nil
case adminCommandGroupMemberMute:
@@ -291,7 +300,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
if err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.mute.updated",
map[string]any{"user_id": internalAdminUIDRaw(target), "muted": true, "version": mute.Version})
return map[string]any{"topic": topic, "user_id": internalAdminUIDRaw(target), "muted": true, "version": mute.Version}, nil
case adminCommandGroupMemberUnmute:
@@ -302,7 +312,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
if err := store.GroupBusiness.DeleteMute(tenantID, topic, target, actor, now); err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.mute.updated",
map[string]any{"user_id": internalAdminUIDRaw(target), "muted": false})
return map[string]any{"topic": topic, "user_id": internalAdminUIDRaw(target), "muted": false}, nil
case adminCommandGroupMemberRemove:
@@ -313,7 +324,8 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
if err := store.GroupBusiness.Leave(tenantID, topic, target, now); err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.member.left",
map[string]any{"user_id": internalAdminUIDRaw(target)})
return map[string]any{"topic": topic, "user_id": internalAdminUIDRaw(target), "status": "left"}, nil
case adminCommandGroupOwnerTransfer:
@@ -324,12 +336,32 @@ func executeKafkaAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, p
if err := store.GroupBusiness.TransferOwner(tenantID, topic, actor, target, payload.Version, now); err != nil {
return nil, err
}
dispatchIMGroupEvents(tenantID)
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.owner.transferred",
map[string]any{"owner": internalAdminUIDRaw(target), "previous_owner": internalAdminUIDRaw(actor)})
return map[string]any{"topic": topic, "owner": internalAdminUIDRaw(target), "previous_owner": internalAdminUIDRaw(actor)}, nil
case adminCommandGroupDissolve:
if err := dissolveGroupByAdminCommand(tenantID, topic, actor, strings.TrimSpace(payload.Reason), now); err != nil {
return nil, err
}
publishIMGroupRealtimeEvent(tenantID, topic, actor, "group.dissolved",
map[string]any{"topic": topic, "reason": strings.TrimSpace(payload.Reason)})
return map[string]any{"topic": topic, "status": "dissolved"}, nil
}
return nil, errors.New("unsupported admin command: " + envelope.EventType)
}
func dissolveGroupByAdminCommand(tenantID types.TenantID, topic string, actor types.Uid, reason string, now time.Time) error {
if strings.TrimSpace(topic) == "" || actor.IsZero() {
return types.ErrMalformed
}
if store.GroupBusiness == nil {
return errors.New("group business store is not initialized")
}
_, err := store.GroupBusiness.Dissolve(tenantID, topic, actor, reason, now)
return err
}
func executeKafkaMessageAdminCommand(tenantID types.TenantID, envelope kafkaEnvelope, payload adminCommandPayload) (map[string]any, error) {
conversationType := strings.TrimSpace(payload.ConversationType)
topic := strings.TrimSpace(payload.Topic)
@@ -386,7 +418,7 @@ func executeKafkaUserGovernanceCommand(tenantID types.TenantID, envelope kafkaEn
if err := store.Users.UpdateState(tenantID, target, types.StateSuspended); err != nil {
return nil, err
}
evictUserSessions(target)
evictUserSessions(tenantID, target)
notifyUserStateChanged(tenantID, target, types.StateSuspended)
result["state"] = types.StateSuspended.String()
result["evicted"] = true
@@ -407,6 +439,7 @@ func executeKafkaUserGovernanceCommand(tenantID types.TenantID, envelope kafkaEn
if globals.sessionStore != nil {
evicted = globals.sessionStore.EvictUserDevice(target, deviceID)
}
publishRealtimeEvictDevice(tenantID, target, deviceID, "admin_device_kick")
if store.Devices != nil {
if err := store.Devices.Delete(tenantID, target, deviceID); err != nil && !errors.Is(err, types.ErrNotFound) {
return nil, err
@@ -416,7 +449,7 @@ func executeKafkaUserGovernanceCommand(tenantID types.TenantID, envelope kafkaEn
result["evicted_sessions"] = evicted
return result, nil
case adminCommandUserLogoutAll:
evictUserSessions(target)
evictUserSessions(tenantID, target)
result["evicted"] = true
return result, nil
case adminCommandUserCancel, adminCommandUserDelete:
@@ -456,7 +489,7 @@ func executeKafkaUserPasswordResetCommand(tenantID types.TenantID, payload admin
}
authVersion = version
}
evictUserSessions(target)
evictUserSessions(tenantID, target)
return map[string]any{
"user_id": internalAdminUIDRaw(target),
"action": adminCommandUserPasswordReset,
@@ -465,10 +498,11 @@ func executeKafkaUserPasswordResetCommand(tenantID types.TenantID, payload admin
}, nil
}
func evictUserSessions(uid types.Uid) {
func evictUserSessions(tenantID types.TenantID, uid types.Uid) {
if globals.sessionStore != nil {
globals.sessionStore.EvictUser(uid, "")
}
publishRealtimeEvictUser(tenantID, uid, "admin_user_evict", "")
}
func notifyUserStateChanged(tenantID types.TenantID, uid types.Uid, state types.ObjState) {
@@ -479,7 +513,7 @@ func notifyUserStateChanged(tenantID types.TenantID, uid types.Uid, state types.
}
func deleteUserByAdminCommand(tenantID types.TenantID, uid types.Uid, hard bool, _ time.Time) error {
evictUserSessions(uid)
evictUserSessions(tenantID, uid)
usersRemoveUser(tenantID, uid)
if globals.hub != nil && globals.hub.unreg != nil {
done := make(chan bool)