【问题标题】:Suggestions for queuing up updates to be emailed out in an asp.net-mvc application?在 asp.net-mvc 应用程序中排队更新以通过电子邮件发送的建议?
【发布时间】:2016-04-30 10:29:43
【问题描述】:

我有一个 asp.net-mvc 应用程序(SQL Server 后端)及其一个基本的 CRUD Web 应用程序,用于跟踪采购订单。其中一项功能是订阅订单的能力,因此如果有人进行任何更改,您会收到电子邮件通知。在更新提交到数据库之后和之后,我在表单发布后触发电子邮件通知。

问题在于,有一天,一个订单有大量更新,而一个人一天内收到了 24 封电子邮件更新。用户要求他每天只收到一封电子邮件,其中包含所有更改的摘要。这似乎是一个合理的要求,我在其他情况下也看到过这种情况(每天或每周收到一次批量通知),但我正在努力找出构建它的最佳方法。我知道我需要坚持用户设置他们希望如何接收更新,但之后我可以:

  • 有一个临时更新表或
  • 每天对所有订单运行一次查询,查看是否有任何具有“每天一次”检查的用户的订单有任何变化。
  • 其他?

由于我在其他地方看到过这种情况,我认为可能有推荐的模式或建议来支持这一点。

【问题讨论】:

  • 你使用哪个数据库?
  • 根据我的问题,我的后端是 SQL server
  • 如果某个订单有多个更新,您需要将它们全部显示给订阅者还是只更新该订单?
  • @genichm - 如果同一订单有多个更新,我想我有 2 个所有单独的更新一个接一个或可能。因此,各个更改都是完全透明的。我想如果以后有人只想查看摘要,那么我总是可以折叠消息
  • 为什么不为用户提供一个选项,让他们可以决定以电子邮件发送更新的频率,将用户偏好保存在数据库中,然后使用它为您的查询提供必要的电子邮件。这样,如果用户觉得自己收到了太多垃圾邮件,他可以重新登录系统并更改通知频率或完全禁用它。

标签: c# asp.net-mvc email notifications scheduled-tasks


【解决方案1】:

这里需要解决三件事:

  • 存储一堆可能的消息
  • 在发送前汇总消息
  • 发送消息

存储消息很简单,您可以创建一个数据库表,其中包含带有收件人 ID 的各个消息。

然后,聚合消息完全取决于您,您可能希望允许用户指定他们想要单独的消息,每日或每周摘要。将消息合并到单个电子邮件后,您可以发送它,并删除聚合中使用的消息。 (您可能只想软删除它们,或将它们移动到日志表中)。

至于用于聚合和发送的技术,您需要一个异步作业来处理您的消息队列并发送电子邮件。

如果您不想使用任何额外的库,您可以设置一个操作方法来完成这项工作,并且只需从计划的 Windows 作业或任何现场活动 pinger 服务定期调用该操作方法。您可能希望保护此操作方法,或者设置一些逻辑以每天仅处理几次队列。

如果第三方库不是问题,那么 Scott Hanselman 不久前收集了一份可能的选项列表,see here。正如其他人所提到的,Hangfire.io 是一个受欢迎的选项。

【讨论】:

    【解决方案2】:

    我会使用日期来解决问题:

    任何订单都有“最后更新”日期时间。 每个用户都有一个“最后一次运行”的日期时间和一个 频率。 然后你可以每##分钟运行一次进程,根据用户偏好获取所有需要通知的用户,并找到所有具有最后更新日期>用户上次运行属性的订单。

    关键是您需要在您的应用程序中使用一些后台作业处理组件来安排工作并监控运行。我使用hangfire.io 完成这项工作,效果很好

    【讨论】:

      【解决方案3】:

      下面用记事本写的代码,我明天检查一下。

      更新:我检查了底部的代码,它在 SQL Server 中运行良好。

      完成所需操作的最快捷、最简单的方法是将字段 last_update_date 添加到订单表中,然后:

      这段代码返回你所有在一段时间内更新的记录和用户订阅的订单

      select o.*, u.* from orders o 
      inner join user_subscribtions us on us.order_id = o.order_id
      inner join users_intervals ui on ui.user_id = us.user_id and ui.interval = @interval
      inner join users u on u.user_id = us.user_id
      where o.last_update_date >= DATEADD(HOUR, @interval, GETDATE());
      

      如果你有服务器并且可以创建服务

      运行 2 个作业,它们将使用不同的时间间隔参数执行相同的过程。第一个作业将每 24 小时执行一次程序,参数为 -24(小时) 第二个作业将每周执行一次上述程序,参数为 -168(小时 = 周),该程序将在该时间段内更新所有订单 并将他们的数据插入到电子邮件表中

          create procedure getAllUpdatedOrders @interval datetime as
      
          begin
      
          insert into emails_to_send
          select o.*, u.* from orders o 
          inner join user_subscribtions us on us.order_id = o.order_id
          inner join users_intervals ui on ui.user_id = us.user_id and ui.interval = @interval
          inner join users u on u.user_id = us.user_id
          where o.last_update_date >= DATEADD(HOUR, @interval, GETDATE());
      
          end
      
          GO
      

      那么您需要定期检查表 emails_to_send 并从该表发送所有电子邮件的服务。

      exec getAllUpdatedOrders -24; - 过去 24 小时内更新的所有订单 + 用户订阅订单和每日邮件

      exec getAllUpdatedOrders -168; - 上周更新的所有订单 + 订阅订单和每周邮件的用户

      如果您需要显示过去 24 小时或一周内某个订单的所有更新,您需要将此类更新存储在某个单独的表中至少一周并加入上述查询

      获取订阅用户和订单在一段时间内更新的示例:

      --drop TABLE orders;

      --drop TABLE user_subscribtions;

      --drop TABLE users_intervals;

      CREATE TABLE orders(order_id int, last_update_date datetime);
      CREATE TABLE user_subscribtions(id int, order_id int, user_id int);
      CREATE TABLE users_intervals(id int, interval int, user_id int);
      
      insert into orders values(1, '2015-02-15');
      insert into orders values(2, '2015-02-02');
      
      insert into user_subscribtions values(1, 1, 10);
      insert into user_subscribtions values(1, 1, 11);
      
      insert into users_intervals values(1, -24, 10);
      insert into users_intervals values(1, -168, 11);
      
      select o.*, us.* from orders o 
      inner join user_subscribtions us on us.order_id = o.order_id
      inner join users_intervals ui on ui.user_id = us.user_id and ui.interval = -24
      where o.last_update_date >= '2015-02-15';
      

      【讨论】:

      • 好答案!基本上是我 20 天前写的相同的想法 :)。唯一缺少的是作业调度
      【解决方案4】:

      在摘要型更新系统的情况下,我会考虑将所有电子邮件消息存储到单独的表中,并创建一个触发器 - 每天运行一次的 Windows 服务或运行控制台以触发的计划任务一个函数,它将表中的一个人的消息编译成一封电子邮件,并处理发送。

      您甚至可以更进一步,将更新类型划分为优先级;某些类型会立即发送消息,而其他类型则会排队等待每日摘要。 此外,如果您跟踪偏好,您可以让用户决定他们是否需要摘要类型的更新或单个消息。选择总是一件好事。

      跟踪最后一条消息的发送时间应该很容易,但您应该记住,无论最后一条消息何时发送,客户都想知道一些事情。让他们自己决定。

      【讨论】:

        【解决方案5】:

        您肯定需要某种形式的后台处理和大量的解决方案,但是在不知道该站点的托管位置和方式的情况下,我将等待在这方面提供建议。

        至于通知本身。这听起来像是一个超级简单的请求,但是用户期望从他们的通知中获得什么样的时间范围?他们是用它们来检查订单还是只是给他们的数据?即时通知的好处是您可以实时了解用户可能喜欢的情况。然而,在高容量的情况下,他们会更喜欢将所有商场集中在一起的一封电子邮件,以节省垃圾邮件。但是,当您将这样的通知集中在一起时(尤其是在日常级别上),您将自己(最多 24 小时)与您收到通知的事情断开连接。两者都很难,因为应用程序需要以某种方式知道“我即将收到一堆订单,所以我应该等待发送这些”或“这只是一个订单,一段时间内我不会有另一个订单”所以我现在可以发送它”,我敢打赌这是用户更喜欢的,但很难构建(很难预知)。

        我认为您可能想确切地询问用户他们对通知的期望是什么。如果他们希望这些通知提醒他们这些信息,并可能触发他们的某些事情,那么每日摘要可能对他们来说并不理想(在这种高容量情况之外)。

        我将继续假设用户更喜欢每日摘要(因为在这种情况下您实际上必须更改某些内容)。我会构建一个系统,您有一个核心“通知”表,您可以在其中存储每个将触发通知的事件的记录,然后是第二个带有 UserIds 和 NotificationIds 的多对多表,然后是一个表明通知已发送的标志或不是。然后,您只需将应用程序放在一边,以设定的时间间隔(每天、每小时等)检查多对多表中需要发送的电子邮件,将它们分组并发送出去。

        通知

        • 通知 ID
        • ...(与通知相关的其他列)

        用户

        • 用户ID
        • ...

        用户通知

        • UserNotificationId(某种形式的主键)
        • 用户 ID (FK)
        • NotificationId (FK)
        • 已发送(位)

        您还可以借助这些表上的一些 DateTime 列和用户偏好设置一个系统,在该系统中应用程序等待 15 分钟以发送通知,如果在这 15 分钟内发生另一个通知,则跨越它重置冷却时间,直到 15 分钟过去没有新事件,然后将所有通知分组并发送它们。这肯定更复杂,但对用户来说可能是“两全其美”。可以方便地将大量情况集中在一起,而其他通知只会延迟 15 分钟(或任何“冷却时间”)

        通知是表面上很简单的事情之一,但是一旦您深入了解用户想要从中得到什么,通知就会被吹走,因此为什么没有一种尺寸适合所有鞋子的原因。

        【讨论】:

          【解决方案6】:

          我认为这是我在一个项目中所做的两个解决方案。

          1. 使用触发器并通过 SQL Mail 发送电子邮件
          2. 使用 Windows 服务并每天运行一次,您可以在一封电子邮件中发送所有摘要信息。

          【讨论】:

            【解决方案7】:

            我认为我们在这里讨论的不是实际逻辑,而是讨论方法。我主要看到两个选项:

            1. 每次触发标准化数据的电子邮件通知时计算并创建摘要类型的电子邮件(使用#last-updated)

            2. 对数据进行反规范化,提前准备汇总内容,按预定时间发送。

            如果某人选择了所有电子邮件通知,那么您可以立即发送电子邮件,否则您可以设置一天一次,并按照@tede24 的建议使用 Quazrtz 或 Hangfire 异步发送这些通知。我个人更喜欢hangfire并使用它。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-02-23
              • 1970-01-01
              • 2014-10-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多