收货记录

收货查验和收货时拍下的照片:两个集合、每一个字段,以及它们在后厨现场意味着什么。

一条收货记录就是一次收货查验:一名员工接收一批订货、记录温度,并决定是否接收 它。这就是食品安全检查员会索取的那份记录。收货时拍下的照片是它旁边的第二个 集合。

集合权限范围一条记录是什么
deliveriesdeliveries:read一次收货查验,每个产品带一条温度读数。
delivery-picturesdelivery-pictures:read在一次收货中拍下的一张照片,指回那次收货。

两者都是普通的集合:data 外面是同一个快照信封,同样的端点、 同样的游标、同样的 90 天规则。它们带着这两个集合开通时应用里仍保有的收货历史, 而不只是那之后记下的内容。

deliveries

字段类型含义
timestampstring, 必填查验发生的时间,以字符串表示的 Unix 毫秒时间戳。
isCompliantboolean, 必填员工对整批收货的判定。
supplierDeliveriesIdstring | null货物来自的那家供应商:一条 suppliers 记录的 id。
temperatureRecordsarray | null每个被测量的产品一条。见下文。
nonComplianceReasonsstring[] | null这批货被拒收、或被有保留地接收的原因。由员工自己写的自由文本;合规时为空。
correctiveActionsstring[] | null为此做了什么。不需要做什么时为空。
commentarystring | null自由文本备注。常常是 null。
userIdstring | null收下这批货的那名员工,在应用记下了一个的时候。

temperatureRecords[]

字段类型含义
productstring测量的是什么,按员工给它起的名字。绝不为空。
lotNumberstring | null批号或批次,在它被采集到的时候。
valuenumber | null读数。产品在没有测量的情况下被登记时为 null。
unit"C"始终是摄氏度。见下文。

温度始终是摄氏度。 一家应用以华氏度记录的餐厅,发来的仍是华氏度读数,它们在 到达时、存储之前就被换算。每一条的 unit 都是 "C",所以使用方永远不必换算,也 不必去猜。

{
  "id": "8f2c1b04-0d5a-4b7e-9f31-6ad2c0e77a51",
  "collection": "deliveries",
  "deleted": false,
  "capturedAt": "1787932805120",
  "receivedAt": "1787932911004",
  "sequence": "88",
  "data": {
    "timestamp": "1787932800000",
    "isCompliant": false,
    "supplierDeliveriesId": "supplier-7",
    "temperatureRecords": [
      { "product": "Poulet fermier", "lotNumber": "L2291", "value": 6.4, "unit": "C" },
      { "product": "Farine T55", "lotNumber": null, "value": null, "unit": "C" }
    ],
    "nonComplianceReasons": ["Température trop élevée"],
    "correctiveActions": ["Produit refusé", "Fournisseur prévenu"],
    "commentary": "Camion en retard, rupture de chaîne du froid probable",
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}

delivery-pictures

一张标签、一个破损的托盘、一支抵着展示柜的探针:这些照片就是 isCompliant 背后的 证据。每一张都是一条指向其收货记录的记录,而且它带着一个文件,所以字节内容来自 assets 端点。

字段类型含义
timestampstring, 必填这张照片是什么时候拍的,以字符串表示的 Unix 毫秒时间戳。
deliveryIdstring | null它所属的那条 deliveries 记录。
partnumber | null应用把一张照片分成几部分存储时,这条记录在其中的位置。请原样带着它,而不要去解读它。
groupIdstring | null把那几部分绑在一起。共用一个 groupId 的记录是同一张照片。
assetobject, 必填点出那个文件:objectKey 和 status 始终都有,一旦上传完成,还会有 contentType、byteLength 和 sha256。
{
  "id": "c61e0a7d-35b2-4f89-a1d4-7e90b2c45f18",
  "collection": "delivery-pictures",
  "deleted": false,
  "capturedAt": "1787932861240",
  "receivedAt": "1787932990517",
  "sequence": "143",
  "data": {
    "timestamp": "1787932860000",
    "deliveryId": "8f2c1b04-0d5a-4b7e-9f31-6ad2c0e77a51",
    "part": 4,
    "groupId": "b4d81f0a-c2e7-4a55-9d10-6f3e8a2b7c91",
    "asset": {
      "objectKey": "delivery-pictures/b4d81f0a/4.jpg",
      "status": "uploaded",
      "contentType": "image/jpeg",
      "byteLength": 902450,
      "sha256": "9f2a…"
    }
  }
}

字节内容要再调一次,针对这条记录自己的 id:

GET /v1/restaurants/{restaurantId}/collections/delivery-pictures/records/{recordId}/assets

它返回一组有效期十五分钟的签名 URL,与集合那一页上描述的完全 一样。对一张照片的任何一部分发起请求,你拿到的都是同一个文件:API 会替你解析这个 分组,所以你不需要弄清楚文件在哪一部分里。

要展示一次收货的照片,就遍历 delivery-pictures,留下 deliveryId 等于那条收货 记录 id 的记录,每个 groupId 留一条。

正确地读它

isCompliant 是记录,不是计算结果。 它是收货的那个人做出的决定,可能与你只看 温度会得出的结论不一致:一次用表面探针取得的读数、一个有自己阈值的产品、一次 临场判断。请把它呈现为他们的判定。如果你想要自己的判定,把它算在他们的旁边,并 标明那是你的。

自由文本字段就是自由文本。 nonComplianceReasons、correctiveActions 和 product 是员工在营业中打出来的,用的是这家餐厅的语言,带着一个忙碌周二的拼写。 不要拿它们建枚举,不要用它们做连接键,也不要假定它们是法语。

value 可以是 null。 一个产品被登记却没有读数是正常的。它往往是干货。把 null 当作「未测量」,绝不要当作 0。

时间戳是字符串。 timestamp 是 Unix 毫秒时间戳的十进制字符串,因为整个 BackResto 平台就是这样存时间戳的。Number(timestamp) 今天是安全的,往后几十万年 也是安全的,但不要让一个 JSON 解析器悄悄把它转成数字,然后再以丢了精度的样子写 回去。

一张照片可能晚于它的收货记录到达。 手机在能上传的时候才上传照片,有时是在查验 本身之后几分钟。一条暂时还没有照片的收货记录,可能只是还有一张在路上;在断定它 没有照片之前,晚些再遍历一次。

最后更新于 2026-09-29。