将 NodeJS 应用程序中的订单请求延迟一段时间

Delay order requests in NodeJS Application for some specific duration

我正在为我的一个客户开发一个订购应用程序,他们想为他们的客户实现一种点击和收集功能。因此,客户通常从我们的应用程序中选择商品并下订单,然后我们处理付款和相关事宜并将其发送到第三方 POS,剩下的由他们完成。客户希望让客户能够灵活地在他们选择的时间收集物品。客户发送一个请求,我们将其保留一段时间(默认为一小时),并在一小时后将其发送到 POS。我刚刚用 setTimeout 取消了请求,它似乎工作正常,但如果服务器运行一段时间,可能还有其他我不知道的含义。那么,我的问题的最佳解决方案是什么?

您是在谈论在给定的等待时间(比如一小时)后交付订购商品的请求,对吧?对我来说,这听起来像是一项合同义务,必须安全地存储在您客户的系统中,而不能简单地保存在 Javascript 应用程序的内存中。正如你所说,这个应用程序可能会崩溃并重新启动,但合同义务仍然存在。

您的订购应用程序应将计算出的送货时间(例如从现在起一小时)的送货请求写入数据库,例如

{
 "OrderNumber": ...,
 "POS": ...,
 "DeliveryTime": "2022-04-02T12:00:00+02:00",
 "Status": "AwaitingDelivery"
}

那么您应该有一个进程重复扫描此数据库以查找状态为 AwaitingDeliveryDeliveryTime 已通过且

的条目
  1. 根据他们的 OrderNumber 使用订单信息丰富他们(假设您有另一个包含订单的数据库 table)
  2. 将丰富的数据发送到给定的 POS
  3. 将他们的状态从 AwaitingDelivery 更改为 HandoverToPOS
  4. 当 POS 的响应进来时,将状态从 HandoverToPOS 更改为 Completed

虽然步骤 1 到 3 可以同步执行,但在步骤 4 中等待响应会引入异步性。你可以在 Node.js 中这样写

var entry = readFromDatabase(...);
enrichWithOrderData(entry); // step 1
https.request("https://url-of-pos/delivery", {method: "POST", ...})
.on("response", function(r) {
  if (r.statusCode === 200) {
    entry.status = "Completed";
    writeToDatabase(entry); // step 4
  }
})
.end(JSON.stringify(entry)); // step 2
entry.status = "HandoverToPOS";
writeToDatabase(entry); // step 3

请注意,这再次依赖于 Javascript 应用程序的内存:如果应用程序在第 3 步后重启,则永远不会执行第 4 步并且条目保持状态 HandoverToPOS。但如果 POS 使用 200 以外的 HTTP 代码进行响应,条目也会保持此状态。

拥有额外状态 HandoverToPOS 的好处是您(或者更确切地说,您的客户)可以跟进此类条目。为了与 POS 建立顺畅的业务关系,并为了客户的利益,这也应该自动化,例如,通过另一个进程重复扫描数据库以查找状态 HandoverToPOS 中的条目并将它们发送到 POS再次,这次有一些迹象表明这是一个重复请求。此指示可防止 POS 仅因为对第一个 https://url-of-pos/delivery 请求的响应丢失而向同一客户发送两台洗衣机。

(请注意,上面的代码片段发送第一个带有 "Status": "AwaitingDelivery"https://url-of-pos/delivery 请求,然后 然后 在数据库上将其更改为 "Status": "HandoverToPOS" , 因此第二个 https://url-of-pos/delivery 请求的状态将与第一个不同。这可以向 POS 指示这是一个重复请求。但是入站服务 https://url-of-pos/delivery 必须能够理解当然是这条信息。)

附录:

“重复扫描”过程可以在 Node.js 中实现为 re-invokes 本身在末尾使用 setTimeout 的函数。

async function scanDeliveries() {
  var entries = await db.query("select * from ...");
  for (var entry of entries) {
    await theCodeAbove(entry);
  }
  setTimeout(scanDeliveries, 60000);
}

成功扫描交付后(这可能需要一些时间,因为它本质上是异步的),该函数将在 60 秒后 re-invoke 自身。在这 60 秒内以及扫描的异步部分,您的其余代码将能够处理更多传入的客户订单。

没有数据保存在内存中,因此如果重新启动整个应用程序也不会造成伤害。