采购助手提交补货单后显示超时,采购员点了重试。过一会儿,两张相同订单都出现在供应商后台。第一次失败的可能只是回复,不是创建动作;屏幕上的等待结束,并没有替业务决定订单是否存在。
订单平台已有幂等键和状态查询时,应先使用它们。只有一个服务的场景,用事务和唯一约束更直接。EasyNet 不提供一个能替代这些保证的回执;它的作用是多个助手经过同一操作入口时,保留可追踪的尝试与调用身份。
适配器需要提供两个明确动作:提交这次购买意图,以及查询这次购买意图的结果。提交输入包含业务认可的稳定请求键、商品和数量;重试仍用同一业务键。调用记录的标识可以关联业务键,但两者不是天然等价,更不能每次重试都新建一笔意图。
查询返回应区分处理中、已创建、明确未创建和暂时无法确认。得到订单号后,助手展示的是原系统记录;查不到而又无法排除已提交时,进入待核对,不擅自创建第二单。业务键相同而数量变化,也必须让适配器拒绝或要求重新确认。
上游订单凭据留在提供方,调用者的权限只覆盖自己的采购范围。谁能下单、谁能查询,仍由业务适配器和授权共同检查。取消、退款是另一个有副作用的操作,不能因为原请求超时就自动执行补偿。
验证时刻意让订单已经落库、回复却丢失,再让同一采购范围内获准的第二个调用者查询原业务请求。确认只有一张订单,也确认不同参数不能被同一个键悄悄接受。业务幂等、查询和对账尚需按订单系统实现;这个方案要消除的是“未知就再做一次”,不是宣称网络调用永远只执行一次。
- 01
提交补货意图
固定业务键和数量
- 02
回复丢失
显示未知,不宣告失败
- 03
查询原订单
同一业务键取回原订单号
同一键的参数必须一致
进程内示例只演示参数冲突检查;生产需持久事务与上游认可的幂等实现,不是网络一次执行保证。
orders = {}
def submit(key, item, quantity):
intent = (item, quantity)
if key in orders:
if orders[key]["intent"] != intent:
raise ValueError("request key reused with different input")
return orders[key]["order_id"]
orders[key] = {"intent": intent, "order_id": "order-001"}
return "order-001"
submit("restock-2026-09-A", "paper", 10) # reply lost
assert submit("restock-2026-09-A", "paper", 10) == "order-001"
assert len(orders) == 1
try:
submit("restock-2026-09-A", "paper", 20)
except ValueError as error:
print(error)预期结果:保留一条记录;改成 20 件时输出参数冲突,不能悄悄接受。
接入时的检查项
- 模拟服务已提交但客户端未收到回复,是否仍只生成一笔业务记录。
- 相同业务键携带不同参数时是否明确拒绝。
- 未知状态是否不会被显示为已取消或已退款。