考虑 Rails 中的订单建模库存

Modelling stock considering orders in Rails

我正在开发一个 租赁 市场应用程序 Rails 5. 它有一个非常简单的结构,包含用户、产品和订单,产品属于用户,谁真正创造了它们。

为了让用户管理他们产品的 "live" 库存,已创建 StockMovement model/table,参考用户和库存变化(positive/negative ) 和日期。这样,我们可以根据 "what the user is prepared to offer" 确定特定日期的特定产品的库存。通过对单个 table/model 的简单查询,我们设法获得了 "sum" 个库存,这恰好是产品的库存。

除此之外,我们还需要考虑何时下订单并确认某个产品,导致需要在一段时间内从库存中减去该商品的数量。

目前,我们使用 StockMovement 模型中的查询来计算库存,并使用 StockMovement 模型中的自定义 select 子句手动加入受订单影响的金额,这已经开始让人觉得有点矫枉过正了(我们甚至还不是测试版)。

我的问题是,您将如何以 Rails 方式实施?在这种情况下,我总是遇到麻烦,从理论上讲,至少考虑到关系数据库逻辑(我们使用 Postgres),最佳做法是使用带有连接和计算字段的查询来即时计算所有内容,但是当涉及到使用 ActiveRecord 实现它,无法在对 table A 的查询中引用 table B 中的计算列,除非您在模型 A 的 select 语句中重新定义该计算,这是我要避免的。

到目前为止,我目前看到的选项是:

1-保持原样:重复select语句中的计算逻辑以访问"foreign calculated fields"

2- 每次确认订单时在 StockMovement table 中创建一条记录并从那里处理所有库存(恕我直言,这是不可取的,因为每次修改时都需要仔细更新订单)

3- 我一直想不到的潜在神奇(正确)解决方案...

谢谢!

就我个人而言,我认为将 SQL 与 ActiveRecord 混合使用是 Rails 的方式。这是我最喜欢 AR 的事情之一:你可以在需要时注入原始 SQL。您甚至可以使用 scopes 将其包装在封装的、可重用的方法中。例如,您可以定义一个范围来添加 select 列。

您还可以创建一个 database view called current_stocks with one record per product. Have that view query stock_movements and orders and compute the current stock, then just use it whenever you need that value. You can even make a readonly ActiveRecord class backed by the view,这样您仍然可以使用常规关联、实例方法等。同样,Rails 为您提供了一种使高级 SQL功能与您的应用程序的其余部分配合得很好。

如果您每次都在即时计算股票,最终您将遇到性能问题,因此您可以 (1) 将其设为 materialized view 并在后台定期刷新它或 (2) 在 products 上添加一个 current_stock 列并保持更新。我可能会选择 2.

或者您可以 (3) 坚持即时计算,但添加一个 starting_stocks table 给您一个 "stock as of Sunday at midnight" 值,这样您就不必计算太遥远了。我认为这可能是所有方法中最好的方法,您可以推迟实施它,直到您真正遇到问题。

如果您真的想深入了解,您可能需要阅读时态数据库。两本好书是 Richard Snodgrass 的 Developing Time-Oriented Database Applications SQL 和 Tom Johnston 的 Bitemporal Data。前者也可以从作者的网站上以免费 PDF 的形式获得。我猜现在这对你来说有点过分了,但了解它仍然是一件好事。

考虑在进行时缓存总数,并将其保存回数据库。这比必须对所有历史记录求和要有效得多。

Rails 具有 counter_cache 的概念(更多信息:http://guides.rubyonrails.org/association_basics.html#options-for-belongs-to-counter-cache),但它只计算记录 - 不计算总数。

幸运的是还有 counter_culture gem,它可以让你计算总数(见 https://github.com/magnusvk/counter_culture#totaling-instead-of-counting

我不知道您是否已经有一个 Stock 对象,但是拥有一个 Stockhas_many StockMovements 对我来说绝对是最佳方法。这样你仍然拥有所有的精细运动;您的查询很快;并且您的计数器根据数据库事务的性质保持最新和准确。

编辑:我刚刚阅读了您的评论 "I'm interested in the stock value for a particular date"。这并不一定会使这种方法无效,但意味着您可能希望将其扩展为包含诸如 StockPosition 记录之类的东西 - 可以每天创建 - 记录任何给定日期的位置。这种方法是否有意义完全取决于您将如何查询数据,并且您可以在这里采用多种方法,例如查询单个 StockPosition 一天;将所有历史记录汇总到设定日期;或从当前 Stock 总数中减去特定日期的记录总和(比查询所有历史数据更有效,if 您查找的日期往往是非常接近今天)。