ruby-on-rails - 涉及经常性付款和 future 事件的困难架构问题

标签 ruby-on-rails ruby design-patterns datetime architecture

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

无论如何,简而言之:我的应用程序包含代表成员(member)资格的对象,属于健身设施的成员。成员(member)资格包含一系列经常性付款。一些成员(member)资格会在其任期结束时自动续订,而其他成员(member)资格则不会。

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

您可能还拥有没有初始期限的成员(member)资格,只需按月或按周运行即可。这些以类似的方式工作,减去初始付款计划。

到现在为止还挺好。使事情变得复杂的是额外的要求:

  • 管理员可以在特定期限内“卡住”成员(member)资格(将其搁置),之后它们会自动重新激活(例如,代表在一段时间内休假的人)。我可以选择立即卡住成员(member)资格并稍后重新激活,或者我可以选择通过在 future 某个时间设置卡住日期以及重新激活日期来安排卡住(注意:总是有重新激活日期,这让事情变得容易一些)。
  • 管理员可以立即取消成员资格,也可以将取消设置为将来发生。 ( future 的取消尚未建成。)
  • 管理员可以退还成员(member)资格,这就像取消成员(member)资格一样,除非退还任何过去的付款。

  • 使这些难以处理的是对经常性付款的影响。当您卡住成员(member)资格时,经常性付款必须在卡住期内“延长”,以便代表卡住的时间段不被支付。这在概念上和编程上都难以处理。例如,付款可能会延长不同的期限(即每隔一周付款的人的每笔付款都会支付两周的成员(member)资格),并且取消日期可能是付款涵盖的期限内的任何时间。

    对于卡住,我采用了成员对象包含一些日期的方法,即“freeze_on”和“thaw_on”来处理卡住期。但是,客户现在也希望将来取消,并且我注意到卡住功能存在一些错误,这让我相信我需要重新考虑我的方法。

    我正在考虑改变一些事情,以便可以安排 future 的事件,但对应用程序的经常性付款部分没有影响。这个想法是将特定事件排队。例如,将来的卡住将通过将特定日期的卡住事件和后续日期的解冻事件排队来完成(从用户的角度来看,这两个事件将被连接为一个“计划卡住”)。 future 的取消将同样处理。

    这种方法有一些好处,例如,如果您想取消 future 的取消(这是我正在谈论的那种烦人、棘手的事情),您可以简单地从事件队列中删除预定的取消。

    然而,我有一种唠叨的感觉,我可能只是从煎锅跳到火里。我想知道是否有人可以就这个问题为我提供一些指导。我可以检查此类问题的设计模式或现有架构原则吗?

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

    不是 scanlife 条形码,它是一个二维码

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

    编辑:回应下面的两个很好的建议(评论框不允许接近这个级别的细节):

    克里斯·罗宾逊:
  • 是的,卡住期可以是任意长度,但实际上我认为它少于两周的情况很少见。但是,无论时间长短,任何解决方案都应该有效。
  • 是的,续订日期会发生变化 - 它会随着卡住的时间而提前。因此,如果卡住持续两周,它会将付款提前两周。更棘手的是,在某些企业中,只能在特定日期提取付款——例如,某些俱乐部仅在每个月的 1 号和 15 号处理付款。所以当日期被推迟时,对于这些俱乐部来说,他们必须“快速”到一个特定的日期。

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

    我对你的摊销表概念很感兴趣。这基本上正是我已经建立的——为期一年的每月付款的成员(member)创建了 12 个,每周创建了 52 个——每个都有一个金额、税收等,与它们相关联,以及一个管理状态的机器“待处理”、“已付款”、“失败”和“已退款”状态。

    我正在努力解决的部分是该表如何响应事件。现在,如果您设置卡住,它会通过更改付款日期立即影响表格。在 table 中间设置一个卡住,它会插入付款。这听起来很有效,但实际上相当复杂且难以管理。您的摊销表想法将如何改善这种情况?

    Arsen7:

    这听起来像我最初提出的事件队列。对我来说很明显你以前处理过这样的事情(你对处理日期的错误检查给我留下了深刻的印象,这是一个好主意,我打算尽快实现)所以我希望你能解释一下你的建议更详细一点。

    具体来说,我想知道您的概念将如何处理我在原始问题中以及我刚刚在 Kris Robison 的回答中留下的评论中描述的经常性付款情况。如果我已经为给定的购买设置了定期付款的时间表,并且在付款中间安排了卡住事件,那么付款时间表是否会保持不变,直到卡住日期成为当前日期,此时何时开始卡住和付款将向前推进?

    这让我觉得这可能是简化我的应用程序的好方法,但我想知道用户会如何看待它。我如何向他们表明他们在安排卡住时查看的付款时间表不再是准确的时间表,而是会在卡住发生后发生变化?

    最佳答案

    是否可以应用银行使用的方案,您每天处理一次所有帐户操作?
    每个对象都可能有一组( future )操作,例如卡住期,并且对象每天都必须做出一个简单的决定,例如:“我今天是否应该到期?”
    好的部分是,这种日常处理非常易于编程。还有一个奇怪的续订规则(如果你想要的话)很容易设计:“今天是星期五吗?是这个月的最后一个吗?如果是,把我标记为续订,在要求的付款中添加一些金额,或者做任何事情”。
    每次询问对象时动态计算状态的成本(就计算能力而言)都是非常昂贵的。如果存储当前“帐户”,则仅在要预测 future 状态时才需要进行更复杂的计算。
    将其视为伪代码:

    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
    
    回复:

    Specifically, I'm wondering how your concept would deal with the recurring payment situation I've described in my original question, and in the comment that I just left on Kris Robison's answer. If I have set up a schedule of recurring payments for a given purchase, and a freeze event is scheduled for right in the middle of the payments, would the schedule of payments remain unchanged until the date of the freeze became the current date, at which time the freeze would be instituted and the payments would move forward?

    This strikes me as perhaps a great way to simplify my application, but I am wondering how users would perceive it. How would I indicate to them that the schedule of payments they were looking at when a freeze has been scheduled is no longer an accurate schedule, but will change once the freeze takes place?


    “每日处理”方案可帮助您快速回答需要复杂计算的问题。
    你有三个“组”:当前状态(经常被问到)、历史(几乎从未改变,相对很少被问到)和 future 。
    您的日常处理程序不仅限于更新“当前”状态。如果某个事件被安排在处理日,该过程可能需要添加“历史”记录。
    如果您的用户经常会问关于 future 的问题(正如您所说,他们会),“处理”也可能会为这些问题创建一种缓存。例如:查找(计算)下一个付款日期,并将其写入帮助表(时间表)中。
    您需要做的主要事情是决定用户将提出哪些问题,以及您是否能够即时计算响应或需要准备好答案。
    在银行(当然会有所不同),如果您询问您当前的余额,他们可能会给您答案,这在一天开始时是正确的。 “更好”的银行会告诉你早上有X$,但现在还有Y$等着记账。
    因此,如果您将卡住记录放入事件队列,您可能会调用一个方法,该方法将立即更新计划。在日常处理程序中将或可能会调用相同的程序(或非常相似的程序)。

    关于ruby-on-rails - 涉及经常性付款和 future 事件的困难架构问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4105037/

    相关文章:

    mysql - 将 Rails 应用程序从 postgresql 迁移到 mysql

    ruby - 有效地检查 JSON 响应是否包含数组中的特定元素

    ruby - 将 Ruby 代码转换为字符串

    ruby - Ruby 中的 method_missing 陷阱

    java - 使用 Step Builder 模式创建包含实例变量(列表)的复杂对象

    c# - 多线程锁测试

    css - 事件管理表的自定义高度 td 在 rails 中不起作用?

    ruby-on-rails - 如何使 README_FOR_APP 的内容出现在 doc/app/index.html

    javascript - 处理嵌套表单中的许多隐藏字段

    c# - 变更通知的最佳模式(事件或委托(delegate))