Ruby on rails 涉及经常性付款和未来事件的复杂体系结构问题

Ruby on rails 涉及经常性付款和未来事件的复杂体系结构问题,ruby-on-rails,ruby,design-patterns,datetime,architecture,Ruby On Rails,Ruby,Design Patterns,Datetime,Architecture,我正在寻找关于如何为这个有点棘手的问题设计一个优雅的解决方案的指导。虽然我正在使用Ruby(和Rails),但我认为我的问题主要是架构问题,尽管我选择的语言显然会对涉及库等的建议产生影响,因此该语言仍然具有相关性 总之,简而言之:我的应用程序包含表示成员身份的对象,这些对象属于健身机构的成员。会员资格包含一系列定期付款。一些会员资格在任期结束时自动续约,而另一些则不续约 例如,您可能有一个初始期限为一年的会员资格,然后按月续订。在应用程序中,创建此类成员身份会导致创建12个定期付款。当最后一个月

我正在寻找关于如何为这个有点棘手的问题设计一个优雅的解决方案的指导。虽然我正在使用Ruby(和Rails),但我认为我的问题主要是架构问题,尽管我选择的语言显然会对涉及库等的建议产生影响,因此该语言仍然具有相关性

总之,简而言之:我的应用程序包含表示成员身份的对象,这些对象属于健身机构的成员。会员资格包含一系列定期付款。一些会员资格在任期结束时自动续约,而另一些则不续约

例如,您可能有一个初始期限为一年的会员资格,然后按月续订。在应用程序中,创建此类成员身份会导致创建12个定期付款。当最后一个月到期时,会员资格也将到期。每日cron任务负责根据已完成的付款导致成员资格过期。如果成员资格设置为自动续订,则相同的cron任务将续订成员资格

您也可能有没有初始期限的会员资格,只是逐月或每周运行。这些工作以类似的方式进行,减去初始付款计划

到目前为止还不错。使事情变得复杂的是附加要求:

  • 管理员可以在特定期限内“冻结”会员资格(将其搁置),然后自动重新激活(例如,代表外出度假一段时间的人)。我可以选择立即冻结会员资格并在以后重新激活,也可以选择通过在将来某个时间点设置冻结日期以及重新激活日期来安排冻结(注意:总是有一个重新激活日期,这使事情变得更容易)

  • 管理员可以立即取消会员资格,也可以将取消设置为在将来进行。(尚未建立未来取消。)

  • 管理员可以退还会员资格,这类似于取消,但以前的任何付款都会被退还

使这些问题难以解决的是对经常性付款的影响。当您冻结会员资格时,经常性付款必须围绕冻结期“延伸”,以便表示冻结的时间段不被支付。这在概念上和编程上都很难处理。例如,付款可能会延长不同的期限(即,每隔一周付款的人的每次付款将支付会员资格的两周),取消日期可能在付款期限内的任何地方

对于冻结,我采用了成员对象包含一些日期的方法,即“冻结”和“解冻”来处理冻结期。然而,客户现在也希望将来取消预订,我注意到冻结功能存在一些缺陷,这让我相信我需要重新考虑我的方法

我正在考虑改变事情,以便可以安排未来的事件,但不会对应用程序的定期付款部分产生影响。其想法是将特定事件排队。例如,将来的冻结将通过在特定日期排队冻结事件和在后续日期排队解冻事件来完成(从用户的角度来看,这两个事件将连接到单个“计划冻结”中)。今后的取消也将以类似方式处理

这种方法有一些好处,例如,如果您想取消未来的取消(这是我所说的那种恼人、棘手的事情),您可以简单地从事件队列中删除预定的取消

然而,我有一种唠叨的感觉,我可能只是从煎锅里跳进火里。我想知道是否有人能在这个问题上给我一些指导。对于这类问题,是否有设计模式或现有的体系结构原则可供我研究

另一件需要注意的事情是,定期付款(即不是每月自动续费)必须作为数据库记录存在,可以编辑(及时移动,价格调整),因此就我所知,使用时间表达式(如Martin Fowler所建议的)不适合此问题。我意识到我提出的事件队列解决方案不会向用户显示任何现有定期付款所发生的变化,但我认为我可以接受

不是scanlife条码,而是二维码

多伦多,给我们你的创意人

编辑:对下面两条建议做出回应(评论框中不允许有如此详细的内容):

克里斯·罗宾逊:

  • 是的,冻结期可以是任意长度,尽管在实践中我认为少于两周的情况很少见。但任何解决方案都应该是有效的,不管时间长短

  • 是的,续约日期会发生变化——它会随着冻结时间的延长而提前。因此,如果冻结期为两周,则会将付款提前两周。让事情变得特别棘手的是,在某些行业,付款只能在特定日期提取——例如,一些俱乐部只在每月1日和15日处理付款。所以,当日期被推来推去时,对于这些俱乐部来说,他们必须“抓拍”到一个特定的日期

  • 您能否更详细地解释为什么这些规则影响事件队列,但不影响订阅付款的管理

    我对你们的摊销表概念感兴趣。这基本上就是我已经建立的——一年的会员制,每月支付12次,每周支付52次——每个月
    def process(day)
      raise "Already processed or missed a day" unless day == last_processed_day + 1
    
      check_expiration day
      check_frozen day
      check_anything day
      #...
    
      self.last_processed_day = day
      self.save!
    end