feat: add im kafka admin integration

This commit is contained in:
richjohnrambo
2026-09-04 11:57:38 +08:00
parent a2abaef3a6
commit f7f8cc0ef0
74 changed files with 6254 additions and 47 deletions
+222
View File
@@ -7,9 +7,14 @@ package sys
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/imkafka"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"strconv"
"time"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
@@ -454,3 +459,220 @@ func (s *sSysImGroup) AdminLogList(ctx context.Context, in *sysin.ImGroupAdminLo
}
return
}
// ProfileUpdate 通过Kafka修改IM群资料
func (s *sSysImGroup) ProfileUpdate(ctx context.Context, in *sysin.ImGroupProfileUpdateInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 {
return nil, gerror.New("操作IM用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"profile": map[string]any{},
"version": in.Version,
}
profile := payload["profile"].(map[string]any)
if in.Name != "" {
profile["name"] = in.Name
}
if in.AvatarFileId != "" {
profile["avatar_file_id"] = in.AvatarFileId
}
if in.Description != "" {
profile["description"] = in.Description
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupProfileUpdate, in.Topic, int64(in.Version), payload)
}
// GovernanceUpdate 通过Kafka修改IM群治理配置
func (s *sSysImGroup) GovernanceUpdate(ctx context.Context, in *sysin.ImGroupGovernanceUpdateInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 {
return nil, gerror.New("操作IM用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"governance": map[string]any{},
"version": in.Version,
}
governance := payload["governance"].(map[string]any)
if in.JoinApproval != nil {
governance["join_approval"] = *in.JoinApproval
}
if in.MuteAll != nil {
governance["mute_all"] = *in.MuteAll
}
if in.MemberUploadFiles != nil {
governance["member_upload_files"] = *in.MemberUploadFiles
}
if in.MemberPrivateChat != nil {
governance["member_private_chat"] = *in.MemberPrivateChat
}
if in.MemberInvite != nil {
governance["member_invite"] = *in.MemberInvite
}
if len(governance) == 0 {
return nil, gerror.New("请至少选择一个治理配置")
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupGovernUpdate, in.Topic, int64(in.Version), payload)
}
// AdminSet 通过Kafka设置IM群管理员
func (s *sSysImGroup) AdminSet(ctx context.Context, in *sysin.ImGroupAdminSetInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || len(in.UserIds) == 0 {
return nil, gerror.New("操作IM用户ID和用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"user_ids": imUserIds(in.UserIds),
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupAdminSet, in.Topic, 0, payload)
}
// AdminRemove 通过Kafka删除IM群管理员
func (s *sSysImGroup) AdminRemove(ctx context.Context, in *sysin.ImGroupAdminRemoveInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || in.UserId <= 0 {
return nil, gerror.New("操作IM用户ID和用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"user_id": imUserId(in.UserId),
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupAdminRemove, in.Topic, 0, payload)
}
// MemberMute 通过Kafka禁言IM群成员
func (s *sSysImGroup) MemberMute(ctx context.Context, in *sysin.ImGroupMemberMuteInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || in.UserId <= 0 {
return nil, gerror.New("操作IM用户ID和用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"user_id": imUserId(in.UserId),
"reason": in.Reason,
}
if in.ExpiresAt != nil {
payload["expires_at"] = in.ExpiresAt.Time.UTC().Format(time.RFC3339Nano)
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupMemberMute, in.Topic, 0, payload)
}
// MemberUnmute 通过Kafka解除IM群成员禁言
func (s *sSysImGroup) MemberUnmute(ctx context.Context, in *sysin.ImGroupMemberUnmuteInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || in.UserId <= 0 {
return nil, gerror.New("操作IM用户ID和用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"user_id": imUserId(in.UserId),
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupMemberUnmute, in.Topic, 0, payload)
}
// MemberRemove 通过Kafka移出IM群成员
func (s *sSysImGroup) MemberRemove(ctx context.Context, in *sysin.ImGroupMemberRemoveInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || in.UserId <= 0 {
return nil, gerror.New("操作IM用户ID和用户ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"user_id": imUserId(in.UserId),
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupMemberRemove, in.Topic, 0, payload)
}
// OwnerTransfer 通过Kafka转让IM群主
func (s *sSysImGroup) OwnerTransfer(ctx context.Context, in *sysin.ImGroupOwnerTransferInp) (res *sysin.ImGroupCommandModel, err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return nil, err
}
if in.ActorUserId <= 0 || in.TargetUserId <= 0 {
return nil, gerror.New("操作IM用户ID和新群主ID不能为空")
}
payload := map[string]any{
"actor_user_id": imUserId(in.ActorUserId),
"topic": in.Topic,
"target_user_id": imUserId(in.TargetUserId),
"version": in.Version,
}
return s.publishGroupCommand(ctx, in.TenantId, consts.ImAdminCommandGroupOwnerTransfer, in.Topic, int64(in.Version), payload)
}
func (s *sSysImGroup) publishGroupCommand(ctx context.Context, tenantId int64, eventType string, topic string, version int64, payload map[string]any) (*sysin.ImGroupCommandModel, error) {
_, tenant, err := imtenant.DB(ctx, tenantId)
if err != nil {
return nil, err
}
user := contexts.GetUser(ctx)
actorName := ""
if user != nil {
actorName = user.RealName
}
command, err := imkafka.PublishAdminCommand(ctx, imkafka.AdminCommandInput{
TenantID: tenant.Id,
TenantCode: tenant.Code,
EventType: eventType,
BizType: "group",
BizID: topic,
Version: version,
Actor: &imkafka.Actor{
Type: "admin",
ID: strconv.FormatInt(contexts.GetUserId(ctx), 10),
DisplayName: actorName,
},
Payload: payload,
CreatedBy: contexts.GetUserId(ctx),
})
if err != nil {
return nil, err
}
return &sysin.ImGroupCommandModel{
CommandID: command.CommandID,
EventID: command.EventID,
Status: command.Status,
TenantID: command.TenantID,
TenantCode: command.TenantCode,
EventType: command.EventType,
IdempotencyKey: command.IdempotencyKey,
}, nil
}
func imUserId(id int64) string {
return strconv.FormatInt(id, 10)
}
func imUserIds(ids []int64) []string {
list := make([]string, 0, len(ids))
for _, id := range ids {
if id > 0 {
list = append(list, imUserId(id))
}
}
return list
}