基础目录
供应商、产品与自制备料——记录的其余部分所指向的那几份参照清单。
基础目录,是一家餐厅列成清单、而不是拿去测量的那些东西:谁来送货、什么东西进了 门、厨房自己做了什么。这里没有任何东西是一次查验或一次读数。这些是其他一切都会 点到名的那些行,所以它们通常是你最先加载的东西,也是最后才会变的东西。
| 集合 | 权限范围 | 一条记录是什么 |
|---|---|---|
suppliers | suppliers:read | 一家来送货的公司,以及怎么联系它。 |
products | products:read | 餐厅收到并使用的某样东西,按名字。 |
preparations | preparations:read | 厨房自己做的某样东西,带着它的保质期和过敏原。 |
这三个共用同一个快照信封:id、deleted、capturedAt、
receivedAt、sequence 以及其余字段,都摆在下文描述的 data 旁边。
suppliers
餐厅录入的每一家供应商一条记录,不论最近有没有东西从他们那里送到。
| 字段 | 类型 | 含义 |
|---|---|---|
name | string, 必填 | 餐厅给这家供应商起的名字。自由文本,不是一份工商登记。 |
isDeleted | boolean, 必填 | 应用自己的停用标记。不是信封上的 deleted——见下文。 |
contactMethods | object[] | 怎么联系他们。每一项是 { "type": string, "value": string }。 |
accountNumber | string | null | 餐厅在这家供应商那里的账户,在录入了一个的时候。 |
{
"id": "1f3c2a76-5b94-4b0e-9c1a-6d8f0a2e7b31",
"collection": "suppliers",
"deleted": false,
"capturedAt": "1789431600214",
"receivedAt": "1789431604771",
"sequence": "412",
"data": {
"name": "Metro Nanterre",
"isDeleted": false,
"contactMethods": [
{ "type": "phone", "value": "+33 1 41 20 30 40" },
{ "type": "email", "value": "commandes@example.test" }
],
"accountNumber": "FR-884213"
}
}
contactMethods[].type 不是一个枚举。 schema 只说了 string,就到此为止,而
上面那些值是一种示意,不是一份清单。对它做分支时带上一个兜底分支,并且不管 type
是怎么写的,都把 value 显示出来。
一条收货记录把它的供应商以 { id, name } 的形式内嵌进来——
够放到屏幕上,仅此而已。账户编号和电话号码住在这个集合里。
products
餐厅收到并使用的东西。这条记录就像它看上去那么薄。
| 字段 | 类型 | 含义 |
|---|---|---|
name | string, 必填 | 餐厅给这个产品起的名字。 |
isDeleted | boolean, 必填 | 应用的停用标记。见下文。 |
{
"id": "6b0d4ea2-91c7-4f3d-a0be-2c5f7d1e8a44",
"collection": "products",
"deleted": false,
"capturedAt": "1789431980551",
"receivedAt": "1789432001903",
"sequence": "413",
"data": {
"name": "Beurre doux 250 g",
"isDeleted": false
}
}
整条记录就这些:没有编号、没有类别、没有单位、没有供应商。如果你的模型需要其中
任何一样,那得由你自己拿着,以信封上的 id 为键。
没有别的东西会按 id 指向一个产品。 一条收货记录把被测量的对象记成
temperatureRecords[].product,那是查验当时打出来的自由文本——见
收货记录。把它和这份清单对上,是你这边的字符串活儿,而且
「Beurre doux 250 g」不是「beurre doux」。你非做不可就做,但不要把结果呈现成一次
连接。
preparations
在店里做出来、而不是收进来的东西——一份酱汁、一锅高汤、一块肉冻派。
| 字段 | 类型 | 含义 |
|---|---|---|
name | string, 必填 | 厨房给这份备料起的名字。 |
lifespan | number, 必填 | 它能放多久。一个光秃秃的数字。 |
allergens | string[] | 它含有什么。自由字符串,不是一个封闭集合。 |
isDeleted | boolean, 必填 | 应用的停用标记。见下文。 |
{
"id": "c47e8b13-0a52-4d96-8f1b-73ae2905cd6f",
"collection": "preparations",
"deleted": false,
"capturedAt": "1789455120087",
"receivedAt": "1789455133642",
"sequence": "418",
"data": {
"name": "Sauce béarnaise",
"lifespan": 3,
"allergens": ["oeuf", "lait"],
"isDeleted": false
}
}
lifespan 不带单位。 schema 给你一个数字,却没有给你任何可以拿来解释它的东
西。不要因为三看起来像天数就印出「3 天」——请为你正在读的那家餐厅确认应用用它表示
的到底是什么,如果确认不了,就老老实实地把这一点标出来。
allergens 是一个字符串列表,不是一份法规清单。 这些条目来自应用,用的是这
家餐厅的语言。在没有核对这些字符串究竟写了什么之前,不要把它们映射到那十四种具名
过敏原上;也不要把一个空数组当成一次通过了的过敏原检查——它同样可能意味着没有人
填过。
isDeleted 不是 deleted
这一页上的每个集合都带着这两者,它们的含义不同,而一个只按其中一个来筛的使用方, 会得到一份错的清单。
- 信封上的
deleted,是这份影子副本的墓碑。true表示这条记录在应用里被删除 了,而data已经不在。它正是你得知某个东西消失了的途径——规则在 信封那一页上。 data里面的isDeleted,是应用自己打在这一行上的软删除标记。记录仍然在, 仍然有它的名字,而且仍然带着deleted: false送达。是餐厅把它停用了:它不再出现 在选择器里,但被留了下来,好让老记录仍然解得开。
所以两个都要筛。只认 deleted,你的供应商清单就会塞满餐厅两年前就不再用的供应
商。只认 isDeleted,你就会留下那些被彻底删掉的行,因为一块墓碑没有 data 可以
让你从里面读出那个标记。
不过,请把停用的那些行留着,而不是丢掉。去年三月的一条收货记录,仍然点着一家今天
isDeleted: true 的供应商的名字,而把那个名字解出来,正是你持有这份清单的全部
理由。
怎么读这份目录
这三个都很小、变得很慢,而且几乎其他一切都需要它们,这让它们既是一次同步里最廉价 的那部分,也是最容易在不知不觉间弄错的那部分。
- 先走它们,留住它们,再按一个节奏去刷新它们。 一家餐厅的整份目录,在
limit=100下不过是几页。把它以信封上的id为键存在你自己的库里,并且从那里 去解名字,而不是每条记录取一次。 - 名字会变,id 不会。
name是餐厅在营业中就会改的自由文本。把id存下来作 为你的键,把名字当作一个你会去刷新的标签——绝不要把它当成用来做连接的东西。 - 缺席在这里同样是含糊的。 一家没有出现的供应商,可能从来没被录入过,也可能 还没到我们这里。在你把一个计数报出去之前,先看那条提醒。