【问题标题】:difficult architectural problem involving recurring payments and future events涉及经常性付款和未来事件的困难架构问题
【发布时间】:2010-11-05 10:26:33
【问题描述】:

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

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

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

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

到目前为止一切顺利。让事情变得复杂的是额外的要求:

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

  • 管理员可以立即取消成员资格,也可以将取消设置为将来发生。 (未来的取消尚未建立。)

  • 管理员可以退还会员资格,这与取消会员资格类似,只是退还任何过去的付款。

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

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

我正在考虑进行更改,以便可以安排未来的活动,但不会影响应用程序的定期付款部分。这个想法是将特定事件排队。例如,未来的冻结将通过在特定日期排队冻结事件和在后续日期的解冻事件来完成(从用户的角度来看,这两个事件将连接成一个“预定冻结”)。以后的取消也会以类似方式处理。

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

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

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

不是scanlife条码,是二维码

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

编辑:回应以下两个很棒的建议(评论框不允许接近这种详细程度):

克里斯·罗宾逊:

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

  2. 是的,续订日期发生了变化 - 它被冻结的长度提前。因此,如果冻结时间为两周,它会将付款提前两周。让事情变得特别棘手的是,在某些企业中,付款只能在特定日期提取 - 例如,一些俱乐部仅在每月 1 日和 15 日处理付款。所以当日期被推迟时,对于这些俱乐部来说,他们必须“快速”到一个特定的日期。

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

我对您的摊销表概念很感兴趣。这基本上正是我已经建立的 - 一个为期一年的会员,每月付款创建 12 个,每周创建 52 个 - 每一个都有与之相关的金额、税收等,以及一个管理状态机“pending”、“paid”、“failed”和“refunded”状态。

我正在努力解决的问题是该表如何响应事件。现在,如果您设置冻结,它会通过更改付款日期立即影响表格。在表格中间设置冻结,它会推动付款。这听起来很有效,但实际上非常复杂且难以管理。您的摊销表想法将如何改善这种情况?

Arsen7:

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

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

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

【问题讨论】:

  • 好的,我刚刚编辑了答案,以便为编辑后的问题提供答案。 ;-) 请原谅我耽搁了这么久——我有几天比较忙。

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


【解决方案1】:

应用银行使用的方案是否可以接受,您每天处理一次所有帐户操作? 每个对象都可能有一组(未来的)操作,例如冻结期,并且每天该对象都必须做出一个简单的决定,例如:“我应该今天到期吗?”

好的部分是,这样的日常处理程序非常简单。还有一个奇怪的续订规则(如果你想要的话)设计起来很简单:“今天是星期五吗?是本月的最后一个吗?如果是,请将我标记为续订,在所需付款中添加一些金额,或做任何事情”。

每次询问对象时动态计算状态的成本会非常高(就计算能力而言)。如果你存储当前的“账户”,那么当你想要预测未来的状态时,你只需要更复杂的计算。

将其视为伪代码:

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

回应:

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

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

“每日处理”方案可帮助您快速回答需要复杂计算的问题。

您有三个“组”:当前状态(经常询问)、历史记录(几乎从未改变,询问相对较少)和未来。

您的日常处理程序不限于仅更新“当前”状态。如果某个事件被安排在处理日,则程序可能需要添加“历史”记录。

如果您的用户会经常询问有关未来的问题(正如您所说,他们会),“处理”也可能会为这些问题创建一种缓存。例如:查找(计算)下一个付款日期,并将其写入辅助表(时间表)中。

您需要做的主要事情是确定用户将提出哪些问题,以及您是否能够即时计算响应或需要准备好答案。

在银行(当然,情况会有所不同),如果您询问您当前的余额,他们可能会给您答案,这在一天开始时是正确的。 “更好”的银行会告诉你早上有 X$,但现在还有 Y$ 等待记账。

因此,如果您将冻结记录放入事件队列中,您可以调用一个方法,该方法将立即更新调度。在日常处理程序中将或可能调用相同的程序(或非常相似的程序)。

【讨论】:

  • 我已经编辑了我的问题以回复您的回答(评论框中没有足够的空间)。
  • 只是想快速感谢您 - 在过去的几周里,我终于深入研究并开发了一个解决方案,您的 cmets 非常有帮助。你和克里斯之间都不是“正确的”,但你们都很有帮助,我很感谢你花时间回答这个问题。克里斯,如果你读到这篇文章,我没有选择你的答案只是因为你看起来不再使用这个网站了!
【解决方案2】:

想到几个问题:

  1. 冻结时间可以少于一个月吗?
  2. 如果是这样,续订日期会改变吗?或者如何为部分月份申请每月付款?

这些会影响您的事件队列系统的工作方式,但最终不会改变订阅付款的管理。

解决订阅问题的一种方法是创建订阅付款的摊销表。如果他们在一年内按月付款,则表中将排好十二笔付款。每周客户可能在同一年的表中有 52 笔付款。每次更新日期到来时,在检查是否冻结后,申请下一笔付款。

摊销表会跟踪已支付的款项。如果帐户取消,未支付的行将被退还。如果帐户根据您的事件队列冻结,则不会应用任何付款并且表格保持静止,直到帐户解冻。

回复

听起来您在摊销表中内置了续订日期的概念。我更多地将摊销表用作一个队列,并且只保留订阅的下一个续订日期。

如果续订日期是摊销表固有的,那么是的,随着事情的发展进行更改会很复杂。但是,续订日期应该只影响您检查是否应用其他付款的日期。

如果您在订阅处于暂停状态时保留部分付款,并且如果暂停可能持续未指定的时间段,则在摊销表上设置持续时间值可让您将部分付款推回队列中的“信用”状态,持续时间等于付款中剩余的时间。这样,当帐户解冻时,将首先应用部分信用,然后您根据剩余期限计算下一个续订日期。

此时使用某种形式的有序列表来保留付款顺序。如果有人出于客户服务原因想要插入续订期的信用额度,它也会派上用场。

【讨论】:

  • 我已经编辑了我的问题以回复您的回答(评论框中没有足够的空间)。
猜你喜欢
  • 1970-01-01
  • 2013-10-29
  • 2014-04-14
  • 2012-06-09
  • 2013-01-06
  • 2015-10-20
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多