# 追溯标签

> 员工打印出来贴在容器上的二次标签，附着被打印的那个文件，以及每一批打印各自的一个组标识符。

Source: https://developers.backresto.com/zh/docs/traceability-labels

当一个容器被打开、或者一份备料被做出来时，员工会打印一张标签并把它贴上去：这是
什么、它从哪里来、什么时候开封的、什么时候必须丢掉。这些就是二次保质期标签，检查
员会拿在手里翻过来看的那种。这个集合，就是每一张被打印出来的标签的记录，附着被
打印的那个文件本身。

| 集合 | 权限范围 | 一条记录是什么 |
| --- | --- | --- |
| `traceability-labels` | `traceability-labels:read` | 一张被打印出来的标签，以及被打印的那个文件。 |

它共用那个[快照信封](https://developers.backresto.com/zh/docs/collections)：`id`、`deleted`、`capturedAt`、
`receivedAt`、`sequence` 以及其余字段，都围在下面的 `data` 周围。

## `traceability-labels`

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `timestamp` | string, **必填** | 这张标签是什么时候打印的，以字符串表示的 Unix 毫秒时间戳。 |
| `supplier` | string \| null | 谁供应了这张标签贴着的那个东西。自由文本。 |
| `part` | number \| null | 没有文档说明。是一个小数，不是一个整数。用它之前请读下面那条说明。 |
| `commentary` | string \| null | 打印时打进去的一条备注。自由文本。 |
| `groupId` | string \| null | 这张标签所属的那一批打印。 |
| `userId` | string \| null | 打印它的那名[员工](https://developers.backresto.com/zh/docs/facilities)，在应用留下了一个的时候。 |
| `asset` | object, **必填** | 被打印的那个文件。请读下一节。 |

```json
{
  "id": "0c6a4f2b-9e17-4d50-8b3c-1f7d5a2e9046",
  "collection": "traceability-labels",
  "deleted": false,
  "capturedAt": "1789786412903",
  "receivedAt": "1789786500117",
  "sequence": "2048",
  "data": {
    "timestamp": "1789786380000",
    "supplier": "Metro",
    "part": 1.5,
    "commentary": "Bac 3, ouvert ce matin",
    "groupId": "5e8b1c47-2a90-4f63-b1d8-6c04e7a3f215",
    "userId": "b3f7c1a0-5d42-4e88-9a16-7c0e2d4b9f31",
    "asset": {
      "objectKey": "restaurants/36eaa3fa/labels/c58d10b7e4a9",
      "status": "uploaded",
      "contentType": "image/jpeg",
      "byteLength": 48210,
      "sha256": "3c9e…"
    }
  }
}
```

**`supplier` 是一个名字，不是一个引用。** 它是文本，不是某个
[供应商](https://developers.backresto.com/zh/docs/catalogue)的 id，而且没有任何东西让两者保持一致：一个在目录里改了
名的供应商，不会把上个月打印的标签重写一遍。你非要拿它做匹配也可以，但请把匹配上
当成一次猜测，把匹配不上当成正常。

## 那个文件

每一张标签都带着一个：`data.asset` 说明有一个文件存在，而字节内容来自 assets
端点。

```http
GET /v1/restaurants/{restaurantId}/collections/traceability-labels/records/{recordId}/assets
```

它返回一组签名 URL，有效**十五分钟**，每个都自带授权，所以下载不需要任何请求头。
完整的规则（作答长什么样、为什么 `sha256` 能让你省掉一次下载、为什么你要存字节
而不是存链接）都在[集合那一页](https://developers.backresto.com/zh/docs/collections)上。不过有两件事在这里还是值得
说一说。

**`asset` 描述这个文件；但只有那个端点会把它交给你。** 它带着 `objectKey`、
`status`、`contentType`、`byteLength` 和 `sha256`，但没有 URL。一个永不过期的链
接，会成为一条永久的、不需要认证就能进入一家餐厅记录的通道。请按 `status` 来分
支：`uploaded` 表示字节已经落地并通过了校验，`pending` 表示还有一台设备正在发送
它们，而一个 `pending` 的附件是被有意地不出现在 assets 端点里，而不是坏掉了。

**一张打印出来的标签不一定是图片。** 请读 assets 端点给你的那个 `contentType`，
而不要假定它是一张照片，也不要在存进你自己的仓库时把所有东西都命名成 `.jpg`。

## 一起打印出来的那些标签

`groupId` 这个字段，能把一串标签重新变回一个人认得出来的东西。当员工一次打印一
批（为同一份备料的好几个容器，一口气打出好几张标签），那一批里的每一张标签都带着
同一个 `groupId`。在你显示任何东西之前先按它分组：一批打印在时间线上是一个事件，
而不是八个。

`part` 是我们还没法告诉你其含义的那个字段，而我们宁愿把这话说出来，也不愿让你猜
错。它是一个**小数**，不是一个计数器，这就排除了「第 3 张，共 8 张」那种显而易见
的读法，并且暗示它是一个份额或者一个数量。但 schema 里没有任何东西这么讲，所以
我们不打算装作知道。请原封不动地把它带过去，非要显示就照原样显示，而如果你的集成
依赖它，就[来问我们](https://developers.backresto.com/zh/docs/support)：答案只隔着一次对话。去问做那块标签界面的人。

两个都可以为 null。一张单独打印的标签可能两个都没有，而那是一批只有一张标签的打
印，不是一条坏掉的记录：退回到信封上的 `id`，把它单独显示出来。
