public static Config Read(IConfigurationSection configuration)
        {
            var config = new Config();

            foreach (var setting in configuration.GetChildren())
            {
                // some environments capitalize config keys, we need them to be lower case
                // for the underlying c lib
                var key = setting.Key.ToLowerInvariant();
                if (key != "default.topic.config")
                {
                    config[key] = setting.Value;
                }
                else
                {
                    TopicConfig tc          = new TopicConfig();
                    var         topicConfig = setting.GetChildren();
                    foreach (var topicSetting in topicConfig)
                    {
                        key     = topicSetting.Key.ToLowerInvariant();
                        tc[key] = topicSetting.Value;
                    }
                    config.DefaultTopicConfig = tc;
                }
            }
            config.EnableAutoCommit = false;
            return(config);
        }
예제 #2
0
        private void AddTopic <T>(
            Func <T, string> id,
            Func <T, string> name,
            Func <T, string> title,
            Func <T, string> notes,
            Func <T, string> folder_name,
            Func <T, IEnumerable> diagrams   = null,
            Func <T, string> description     = null,
            Action <TopicConfig <T> > config = null)
        {
            var cfg = new ExportTopicConfig(
                x => id((T)x),
                x => name((T)x),
                x => title((T)x),
                x => notes((T)x),
                x => folder_name((T)x),
                x => (diagrams == null) ? new object[0] :  diagrams((T)x),
                x => description?.Invoke((T)x)
                );

            var c = new TopicConfig <T>(cfg);

            config(c);
            this.topics.Add(typeof(T), cfg);
        }
예제 #3
0
        public void PublishTopic(MessageBase msg, string queueName, string borkerList)
        {
            if (string.IsNullOrWhiteSpace(borkerList))
            {
                borkerList = ConnectionBuilder.getBorkerList();
            }
            var topicConfig = new TopicConfig
            {
                CustomPartitioner = (top, key, cnt) =>
                {
                    cnt = 2;
                    key = Encoding.UTF8.GetBytes(queueName);
                    var  kt        = (key != null) ? Encoding.UTF8.GetString(key, 0, key.Length) : "(null)";
                    int  partition = (key?.Length ?? 0) % cnt;
                    bool available = top.PartitionAvailable(partition);
                    _logger.LogInformation("Partitioner topic: {0} key: {1} partition count: {2} -> {3} {4}", top.Name, kt, cnt, partition, available);
                    return(partition);
                }
            };

            using (Producer producer = new Producer(borkerList))
                using (Topic topic = producer.Topic(queueName, topicConfig))//topicConfig
                {
                    DeliveryReport deliveryReport = topic.Produce(msg.MessageBodyByte).Result;
                    _logger.LogInformation("发送到分区:{0}, Offset 为: {1}", deliveryReport.Partition, deliveryReport.Offset);
                }
        }
예제 #4
0
        public static void Main()
        {
            TopicConfig config  = new TopicConfig();
            var         factory = new ConnectionFactory()
            {
                HostName = "localhost"
            };

            using (var connection = factory.CreateConnection())
                using (var channel = connection.CreateModel())
                {
                    var message = "Hello World topic exchange";


                    var body = Encoding.UTF8.GetBytes(message);

                    channel.BasicPublish(exchange: config.exchangeName,
                                         routingKey: config.routingKey,
                                         basicProperties: null,
                                         body: body);
                    Console.WriteLine(" [x] Sent {0}", message);
                }

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
예제 #5
0
        /// <summary>
        /// 动态处理程序
        /// </summary>
        /// <param name="groupMember"></param>
        /// <param name="eventArgs"></param>
        private void TopicMemberActivityModule_After(TopicMember groupMember, CommonEventArgs eventArgs)
        {
            ActivityService activityService = new ActivityService();

            if (eventArgs.EventOperationType == EventOperationType.Instance().Create())
            {
                //生成动态
                if (groupMember == null)
                {
                    return;
                }
                var group = new TopicService().Get(groupMember.TopicId);
                if (group == null)
                {
                    return;
                }
                //生成Owner为专题的动态
                Activity actvityOfTopic = Activity.New();
                actvityOfTopic.ActivityItemKey       = ActivityItemKeys.Instance().CreateTopicMember();
                actvityOfTopic.ApplicationId         = TopicConfig.Instance().ApplicationId;
                actvityOfTopic.IsOriginalThread      = true;
                actvityOfTopic.IsPrivate             = !group.IsPublic;
                actvityOfTopic.UserId                = groupMember.UserId;
                actvityOfTopic.ReferenceId           = 0;
                actvityOfTopic.ReferenceTenantTypeId = string.Empty;
                actvityOfTopic.SourceId              = groupMember.Id;
                actvityOfTopic.TenantTypeId          = TenantTypeIds.Instance().User();
                actvityOfTopic.OwnerId               = group.TopicId;
                actvityOfTopic.OwnerName             = group.TopicName;
                actvityOfTopic.OwnerType             = ActivityOwnerTypes.Instance().Topic();

                activityService.Generate(actvityOfTopic, false);

                //生成Owner为用户的动态
                Activity actvityOfUser = Activity.New();
                actvityOfUser.ActivityItemKey        = ActivityItemKeys.Instance().JoinTopic();
                actvityOfUser.ApplicationId          = actvityOfTopic.ApplicationId;
                actvityOfUser.HasImage               = actvityOfTopic.HasImage;
                actvityOfUser.HasMusic               = actvityOfTopic.HasMusic;
                actvityOfUser.HasVideo               = actvityOfTopic.HasVideo;
                actvityOfUser.IsOriginalThread       = actvityOfTopic.IsOriginalThread;
                actvityOfUser.IsPrivate              = actvityOfTopic.IsPrivate;
                actvityOfUser.UserId                 = actvityOfTopic.UserId;
                actvityOfUser.ReferenceId            = actvityOfTopic.ReferenceId;
                actvityOfTopic.ReferenceTenantTypeId = actvityOfTopic.ReferenceTenantTypeId;
                actvityOfUser.SourceId               = actvityOfTopic.SourceId;

                actvityOfUser.TenantTypeId = actvityOfTopic.TenantTypeId;
                actvityOfUser.OwnerId      = groupMember.UserId;
                actvityOfUser.OwnerName    = groupMember.User.DisplayName;
                actvityOfUser.OwnerType    = ActivityOwnerTypes.Instance().User();
                activityService.Generate(actvityOfUser, false);
            }
            else if (eventArgs.EventOperationType == EventOperationType.Instance().Delete()) //删除动态
            {
                activityService.DeleteSource(TenantTypeIds.Instance().User(), groupMember.UserId);
            }
        }
예제 #6
0
    /// <summary>
    ///     AddTopic adds a given topic config to the general bucket notification config
    /// </summary>
    /// <param name="topicConfig"></param>
    public void AddTopic(TopicConfig topicConfig)
    {
        var isTopicFound = TopicConfigs.Exists(t => t.Topic.Equals(topicConfig));

        if (!isTopicFound)
        {
            TopicConfigs.Add(topicConfig);
        }
    }
 static QueueProperties CreateQueueProperty(
     TopicConfig userTopic,
     PartitionMetadata partition = null,
     int partitionId             = -1
     ) => new QueueProperties(
     userTopic.Name,
     (uint)(partition?.PartitionId ?? partitionId),
     userTopic.IsExternal,
     userTopic.ExternalContractType
     );
예제 #8
0
        /// <summary>
        /// 设置/取消管理员通知处理程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private void SetManagerNoticeEventModule_After(TopicMember sender, CommonEventArgs eventArgs)
        {
            if (eventArgs.EventOperationType != EventOperationType.Instance().SetTopicManager() && eventArgs.EventOperationType != EventOperationType.Instance().CancelTopicManager())
            {
                return;
            }

            TopicService groupService = new TopicService();
            TopicEntity  entity       = groupService.Get(sender.TopicId);

            if (entity == null)
            {
                return;
            }

            User senderUser = DIContainer.Resolve <IUserService>().GetFullUser(sender.UserId);

            if (senderUser == null)
            {
                return;
            }

            NoticeService noticeService = DIContainer.Resolve <NoticeService>();

            Notice notice = Notice.New();

            notice.UserId             = sender.UserId;
            notice.ApplicationId      = TopicConfig.Instance().ApplicationId;
            notice.TypeId             = NoticeTypeIds.Instance().Hint();
            notice.LeadingActorUserId = 0;
            notice.LeadingActor       = string.Empty;
            notice.LeadingActorUrl    = string.Empty;
            notice.RelativeObjectId   = sender.TopicId;
            notice.RelativeObjectName = StringUtility.Trim(entity.TopicName, 64);
            notice.RelativeObjectUrl  = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));

            if (eventArgs.EventOperationType == EventOperationType.Instance().SetTopicManager())
            {
                notice.TemplateName = NoticeTemplateNames.Instance().SetTopicManager();
            }
            else
            {
                notice.TemplateName = NoticeTemplateNames.Instance().CannelTopicManager();
            }
            noticeService.Create(notice);
        }
예제 #9
0
        public static void Main(string[] args)
        {
            string brokerList = args[0];
            string topicName  = args[1];

            var topicConfig = new TopicConfig
            {
                CustomPartitioner = (top, key, cnt) =>
                {
                    var  kt        = (key != null) ? Encoding.UTF8.GetString(key, 0, key.Length) : "(null)";
                    int  partition = (key?.Length ?? 0) % cnt;
                    bool available = top.PartitionAvailable(partition);
                    Console.WriteLine($"Partitioner topic: {top.Name} key: {kt} partition count: {cnt} -> {partition} {available}");
                    return(partition);
                }
            };

            using (Producer producer = new Producer(brokerList))
                using (Topic topic = producer.Topic(topicName, topicConfig))
                {
                    Console.WriteLine($"{producer.Name} producing on {topic.Name}. q to exit.");

                    string text;
                    while ((text = Console.ReadLine()) != "q")
                    {
                        byte[] data = Encoding.UTF8.GetBytes(text);
                        byte[] key  = null;
                        // Use the first word as the key
                        int index = text.IndexOf(" ");
                        if (index != -1)
                        {
                            key = Encoding.UTF8.GetBytes(text.Substring(0, index));
                        }

                        Task <DeliveryReport> deliveryReport = topic.Produce(data, key);
                        var unused = deliveryReport.ContinueWith(task =>
                        {
                            Console.WriteLine($"Partition: {task.Result.Partition}, Offset: {task.Result.Offset}");
                        });
                    }
                }
        }
예제 #10
0
 public static Props Props <TMessage>(
     TopicConfig topicConfig,
     string topicId,
     Func <string, Offset, Source <KeyValuePair <TMessage, Offset>, NotUsed> > eventStreamFactory,
     ISerializer serializer,
     IMessagePropertyExtractor extractor,
     IOffsetStore offsetStore
     ) where TMessage : class
 {
     return(Akka.Actor.Props.Create(() =>
                                    new TaggedOffsetProducerActor <TMessage>(
                                        topicConfig,
                                        topicId,
                                        eventStreamFactory,
                                        serializer,
                                        extractor,
                                        offsetStore
                                        )
                                    ));
 }
예제 #11
0
        // WIP, not producing useful numbers yet. Assumes one partition.
        public static async Task <long> Consume(string broker, string topic)
        {
            long n = 0;

            var topicConfig = new TopicConfig();

            topicConfig["auto.offset.reset"] = "smallest";
            var config = new Config()
            {
                GroupId            = "benchmark-consumer",
                DefaultTopicConfig = topicConfig
            };

            using (var consumer = new EventConsumer(config, broker))
            {
                var signal = new SemaphoreSlim(0, 1);

                consumer.OnMessage += (obj, msg) =>
                {
                    n += 1;
                };

                consumer.OnEndReached += (obj, end) =>
                {
                    Console.WriteLine($"End reached");
                    signal.Release();
                };

                consumer.Subscribe(new List <string> {
                    topic
                });
                consumer.Start();

                await signal.WaitAsync();

                Console.WriteLine($"Shutting down");
            }

            return(n);
        }
예제 #12
0
        /// <summary>
        /// 专题操作日志事件处理
        /// </summary>
        private void TopicOperationLogEventModule_After(TopicEntity senders, CommonEventArgs eventArgs)
        {
            if (eventArgs.EventOperationType == EventOperationType.Instance().Delete() ||
                eventArgs.EventOperationType == EventOperationType.Instance().Approved() ||
                eventArgs.EventOperationType == EventOperationType.Instance().Disapproved() ||
                eventArgs.EventOperationType == EventOperationType.Instance().SetEssential() ||
                eventArgs.EventOperationType == EventOperationType.Instance().SetSticky() ||
                eventArgs.EventOperationType == EventOperationType.Instance().CancelEssential() ||
                eventArgs.EventOperationType == EventOperationType.Instance().CancelSticky())
            {
                OperationLogEntry entry = new OperationLogEntry(eventArgs.OperatorInfo);
                entry.ApplicationId       = entry.ApplicationId;
                entry.Source              = TopicConfig.Instance().ApplicationName;
                entry.OperationType       = eventArgs.EventOperationType;
                entry.OperationObjectName = senders.TopicName;
                entry.OperationObjectId   = senders.TopicId;
                entry.Description         = string.Format(ResourceAccessor.GetString("OperationLog_Pattern_" + eventArgs.EventOperationType, entry.ApplicationId), "专题", entry.OperationObjectName);

                OperationLogService logService = Tunynet.DIContainer.Resolve <OperationLogService>();
                logService.Create(entry);
            }
        }
예제 #13
0
    /// <summary>
    /// Start a Tagged Offset Producer over a cluster distribution
    /// </summary>
    /// <param name="system"></param>
    /// <param name="tags"></param>
    /// <param name="topicConfig"></param>
    /// <param name="topicId"></param>
    /// <param name="Func<string"></param>
    /// <param name="eventStreamFactory"></param>
    /// <param name="offsetStore"></param>
    /// <typeparam name="TMessage"></typeparam>
    public static void StartTaggedOffsetProducer <TMessage>(
        ActorSystem system,
        ImmutableArray <AggregateEventTag> tags,
        TopicConfig topicConfig,
        string topicId,
        Func <string, Offset, Source <KeyValuePair <TMessage, Offset>, NotUsed> > eventStreamFactory,
        ISerializer serializer,
        IMessagePropertyExtractor extractor,
        IOffsetStore offsetStore
        ) where TMessage : class
    {
        var producerConfig = new ProducerConfig(system.Settings.Config);
        var publisherProps = TaggedOffsetProducerActor.Props <TMessage>(
            topicConfig, topicId, eventStreamFactory, serializer, extractor, offsetStore
            );

        var backoffPublisherProps = BackoffSupervisor.PropsWithSupervisorStrategy(
            publisherProps,
            "producer",
            producerConfig.MinBackoff,
            producerConfig.MaxBackoff,
            producerConfig.RandomBackoffFactor,
            SupervisorStrategy.StoppingStrategy
            );

        var clusterShardingSettings = ClusterShardingSettings
                                      .Create(system)
                                      .WithRole(producerConfig.Role);

        new ClusterDistribution(system)
        .Start(
            $"serviceBusProducer-{topicId}",
            backoffPublisherProps,
            tags.Select(x => x.Tag).ToArray(),
            new ClusterDistributionSettings(system, clusterShardingSettings)
            );
    }
예제 #14
0
        /// <summary>
        /// 动态处理程序
        /// </summary>
        /// <param name="group"></param>
        /// <param name="eventArgs"></param>
        private void TopicEntityActivityModule_After(TopicEntity group, AuditEventArgs eventArgs)
        {
            //生成动态
            ActivityService activityService = new ActivityService();
            AuditService    auditService    = new AuditService();
            bool?           auditDirection  = auditService.ResolveAuditDirection(eventArgs.OldAuditStatus, eventArgs.NewAuditStatus);

            if (auditDirection == true) //生成动态
            {
                if (group == null)
                {
                    return;
                }

                //生成Owner为用户的动态
                Activity actvityOfBar = Activity.New();
                actvityOfBar.ActivityItemKey       = ActivityItemKeys.Instance().CreateTopic();
                actvityOfBar.ApplicationId         = TopicConfig.Instance().ApplicationId;
                actvityOfBar.IsOriginalThread      = true;
                actvityOfBar.IsPrivate             = !group.IsPublic;
                actvityOfBar.UserId                = group.UserId;
                actvityOfBar.ReferenceId           = 0;//没有涉及到的实体
                actvityOfBar.ReferenceTenantTypeId = string.Empty;
                actvityOfBar.SourceId              = group.TopicId;
                actvityOfBar.TenantTypeId          = TenantTypeIds.Instance().Topic();
                actvityOfBar.OwnerId               = group.UserId;
                actvityOfBar.OwnerName             = group.User.DisplayName;
                actvityOfBar.OwnerType             = ActivityOwnerTypes.Instance().User();

                activityService.Generate(actvityOfBar, true);
            }
            else if (auditDirection == false) //删除动态
            {
                activityService.DeleteSource(TenantTypeIds.Instance().Topic(), group.TopicId);
            }
        }
        /// <summary>
        /// 通知处理程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private void TopicMemberApplyNoticeModule_After(TopicMemberApply sender, CommonEventArgs eventArgs)
        {
            TopicService groupService = new TopicService();
            TopicEntity  entity       = groupService.Get(sender.TopicId);

            if (entity == null)
            {
                return;
            }

            User senderUser = DIContainer.Resolve <IUserService>().GetFullUser(sender.UserId);

            if (senderUser == null)
            {
                return;
            }
            InvitationService invitationService = new InvitationService();
            Invitation        invitation;
            NoticeService     noticeService = DIContainer.Resolve <NoticeService>();
            Notice            notice;

            if (eventArgs.EventOperationType == EventOperationType.Instance().Create())
            {
                if (sender.ApplyStatus == TopicMemberApplyStatus.Pending)
                {
                    List <long> toUserIds = new List <long>();
                    toUserIds.Add(entity.UserId);
                    toUserIds.AddRange(entity.TopicManagers.Select(n => n.UserId));
                    foreach (var toUserId in toUserIds)
                    {
                        //申请加入专题的请求
                        if (!groupService.IsMember(sender.TopicId, sender.UserId))
                        {
                            invitation = Invitation.New();
                            invitation.ApplicationId      = TopicConfig.Instance().ApplicationId;
                            invitation.InvitationTypeKey  = InvitationTypeKeys.Instance().ApplyJoinTopic();
                            invitation.UserId             = toUserId;
                            invitation.SenderUserId       = sender.UserId;
                            invitation.Sender             = senderUser.DisplayName;
                            invitation.SenderUrl          = SiteUrls.Instance().SpaceHome(sender.UserId);
                            invitation.RelativeObjectId   = sender.TopicId;
                            invitation.RelativeObjectName = entity.TopicName;
                            invitation.RelativeObjectUrl  = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));
                            invitation.Remark             = sender.ApplyReason;
                            invitationService.Create(invitation);
                        }
                    }
                }
            }

            string noticeTemplateName = string.Empty;

            if (eventArgs.EventOperationType == EventOperationType.Instance().Approved())
            {
                if (sender.ApplyStatus == TopicMemberApplyStatus.Approved)
                {
                    noticeTemplateName = NoticeTemplateNames.Instance().MemberApplyApproved();
                }
            }
            else if (eventArgs.EventOperationType == EventOperationType.Instance().Disapproved())
            {
                if (sender.ApplyStatus == TopicMemberApplyStatus.Disapproved)
                {
                    noticeTemplateName = NoticeTemplateNames.Instance().MemberApplyDisapproved();
                }
            }

            if (string.IsNullOrEmpty(noticeTemplateName))
            {
                return;
            }

            notice = Notice.New();

            notice.UserId        = sender.UserId;
            notice.ApplicationId = TopicConfig.Instance().ApplicationId;
            notice.TypeId        = NoticeTypeIds.Instance().Hint();
            //notice.LeadingActorUserId = UserContext.CurrentUser.UserId;
            //notice.LeadingActor = UserContext.CurrentUser.DisplayName;
            //notice.LeadingActorUrl = SiteUrls.FullUrl(SiteUrls.Instance().SpaceHome(UserContext.CurrentUser.UserId));
            notice.RelativeObjectId   = sender.TopicId;
            notice.RelativeObjectName = StringUtility.Trim(entity.TopicName, 64);
            notice.RelativeObjectUrl  = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));
            notice.TemplateName       = noticeTemplateName;
            noticeService.Create(notice);
        }
예제 #16
0
 /// <summary>
 /// Disable logging for this <see cref="Materialized{K, V, S}"/>
 /// </summary>
 /// <returns>Itself</returns>
 public Materialized <K, V, S> WithLoggingDisabled()
 {
     LoggingEnabled = false;
     TopicConfig?.Clear();
     return(this);
 }
예제 #17
0
        public ActionResult UserCreatedTopics(string spaceKey)
        {
            string       title       = "我创建的专题";
            IUserService userService = DIContainer.Resolve <IUserService>();
            User         spaceUser   = userService.GetFullUser(spaceKey);

            if (spaceUser == null)
            {
                return(HttpNotFound());
            }
            bool ignoreAudit = false;
            var  currentUser = UserContext.CurrentUser;

            if (currentUser != null)
            {
                if (currentUser.UserId == spaceUser.UserId || authorizer.IsAdministrator(TopicConfig.Instance().ApplicationId))
                {
                    ignoreAudit = true;
                }

                if (currentUser.UserId != spaceUser.UserId)
                {
                    title = spaceUser.DisplayName + "创建的专题";
                }
            }
            else
            {
                title = spaceUser.DisplayName + "创建的专题";
            }

            pageResourceManager.InsertTitlePart(title);
            var groups = topicService.GetMyCreatedTopics(spaceUser.UserId, ignoreAudit);

            if (Request.IsAjaxRequest())
            {
                return(PartialView("_List", groups));
            }

            ViewData["spaceUser"]   = spaceUser;
            ViewData["currentUser"] = currentUser;


            return(View(groups));
        }
        private void CreateConsumer(List <string> topics = null)
        {
            var config = new RdKafka.Config()
            {
                GroupId = endpointName, EnableAutoCommit = false
            };

            bool debugEnabled;

            if (settingsHolder.TryGet <bool>(WellKnownConfigurationKeys.KafkaDebugEnabled, out debugEnabled) && debugEnabled)
            {
                config["debug"] = "all";
            }

            var defaultConfig = new TopicConfig();

            defaultConfig["auto.offset.reset"] = "earliest";

            string sessionTimeout;

            if (settingsHolder.TryGet <string>(WellKnownConfigurationKeys.KafkaSessionTimeout, out sessionTimeout))
            {
                config["session.timeout.ms"] = sessionTimeout;
            }
            else
            {
                config["session.timeout.ms"] = "15000";
            }


            string heartBeatInterval;

            if (settingsHolder.TryGet <string>(WellKnownConfigurationKeys.KafkaHeartBeatInterval, out heartBeatInterval))
            {
                config["heartbeat.interval.ms"] = heartBeatInterval;
            }
            else
            {
                config["heartbeat.interval.ms"] = "5000";
            }

            config.DefaultTopicConfig = defaultConfig;

            if (consumer != null)
            {
                // consumer.Dispose();
            }

            consumer = new EventConsumer(config, connectionString);

            if (topics != null && consumer != null)
            {
                consumer.AddSubscriptions(topics);
            }

            consumer.OnPartitionsAssigned += Consumer_OnPartitionsAssigned;

            consumer.OnPartitionsRevoked += Consumer_OnPartitionsRevoked;

            consumer.OnEndReached += Consumer_OnEndReached;
        }
예제 #19
0
        /// <summary>
        /// 通知处理程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private void TopicMemberNoticeModule_After(TopicMember sender, CommonEventArgs eventArgs)
        {
            if (eventArgs.EventOperationType != EventOperationType.Instance().Delete() && eventArgs.EventOperationType != EventOperationType.Instance().Create() && sender != null)
            {
                return;
            }
            TopicService groupService = new TopicService();
            TopicEntity  entity       = groupService.Get(sender.TopicId);

            if (entity == null)
            {
                return;
            }

            User senderUser = DIContainer.Resolve <IUserService>().GetFullUser(sender.UserId);

            if (senderUser == null)
            {
                return;
            }

            NoticeService noticeService = DIContainer.Resolve <NoticeService>();
            Notice        notice;

            List <long> toUserIds = new List <long>();

            toUserIds.Add(entity.UserId);
            toUserIds.AddRange(entity.TopicManagers.Select(n => n.UserId));
            //删除专题成员通知群管理员
            if (eventArgs.EventOperationType == EventOperationType.Instance().Delete())
            {
                foreach (var toUserId in toUserIds)
                {
                    if (toUserId == sender.UserId)
                    {
                        continue;
                    }
                    notice                    = Notice.New();
                    notice.UserId             = toUserId;
                    notice.ApplicationId      = TopicConfig.Instance().ApplicationId;
                    notice.TypeId             = NoticeTypeIds.Instance().Hint();
                    notice.LeadingActorUserId = sender.UserId;
                    notice.LeadingActor       = senderUser.DisplayName;
                    notice.LeadingActorUrl    = SiteUrls.FullUrl(SiteUrls.Instance().SpaceHome(sender.UserId));
                    notice.RelativeObjectId   = sender.TopicId;
                    notice.RelativeObjectName = StringUtility.Trim(entity.TopicName, 64);
                    notice.RelativeObjectUrl  = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));
                    notice.TemplateName       = NoticeTemplateNames.Instance().MemberQuit();
                    noticeService.Create(notice);
                }
            }
            else if (eventArgs.EventOperationType == EventOperationType.Instance().Create()) //添加群成员时向群管理员发送通知
            {
                foreach (var toUserId in toUserIds)
                {
                    if (toUserId == sender.UserId)
                    {
                        continue;
                    }
                    notice                    = Notice.New();
                    notice.UserId             = toUserId;
                    notice.ApplicationId      = TopicConfig.Instance().ApplicationId;
                    notice.TypeId             = NoticeTypeIds.Instance().Hint();
                    notice.LeadingActorUserId = sender.UserId;
                    notice.LeadingActor       = senderUser.DisplayName;
                    notice.LeadingActorUrl    = SiteUrls.FullUrl(SiteUrls.Instance().SpaceHome(sender.UserId));
                    notice.RelativeObjectId   = sender.TopicId;
                    notice.RelativeObjectName = StringUtility.Trim(entity.TopicName, 64);
                    notice.RelativeObjectUrl  = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));
                    notice.TemplateName       = NoticeTemplateNames.Instance().MemberJoin();
                    noticeService.Create(notice);
                }
                //向加入者发送通知

                //notice = Notice.New();
                //notice.UserId = sender.UserId;
                //notice.ApplicationId = TopicConfig.Instance().ApplicationId;
                //notice.TypeId = NoticeTypeIds.Instance().Hint();
                //notice.LeadingActorUserId = sender.UserId;
                //notice.LeadingActor = senderUser.DisplayName;
                //notice.LeadingActorUrl = SiteUrls.FullUrl(SiteUrls.Instance().SpaceHome(sender.UserId));
                //notice.RelativeObjectId = sender.TopicId;
                //notice.RelativeObjectName = StringUtility.Trim(entity.TopicName, 64);
                //notice.RelativeObjectUrl = SiteUrls.FullUrl(SiteUrls.Instance().TopicHome(entity.TopicKey));
                //notice.TemplateName = NoticeTemplateNames.Instance().MemberApplyApproved();
                //noticeService.Create(notice);
            }
        }