标签的设计目标只有一个:拿到标签 ID 和哈希,一个外部人可以不依赖我们,独立重算出同一个数。所以必需字段里没有任何一项是"装饰性"的——少一项,验证链就断。
| 字段 | 示例 | 为什么必须有 |
|---|---|---|
| label_id | CJV-CN-YN-2026-000417 | 对外唯一标识;解析规则 站-国-省-年-序号,可离线校验格式合法性 |
| batch_id | CJV-B-2026-0417 | 回到批次账本;一个标签对应且仅对应一个封账批次 |
| declared_unit | plantlet | 功能单位枚举:plantlet / kg_green_bean。否则 0.200 是在说什么都不知道 |
| kgco2e_per_unit | 0.200 | 声明值本身,向上取整后的口径 |
| breakdown | {electricity:0.1192, …} | 分项可加性校验:外部人能求和、能定位差异 |
| boundary | cradle-to-gate | 边界不同不可比;必须与 §2.2 清单同版本 |
| gwp_version | IPCC AR6 · GWP100 | AR5/AR6 结果不可直接比;不写版本等于不可比 |
| data_quality_grade | A | 决定积分乘数(1.0/0.9/0.8),也是买方可信的代理指标 |
| verification | {status, body, date} | 核证状态必须显式:self / third_party。未核证不得印 VERIFIED |
| hash | sha256:9d2f…4b71 | 防篡改;覆盖以上全部字段的规范化 JSON |
| prev_hash | sha256:5c71…2938 | 形成批次链;删除或改写任一批次都会断链 |
| primary_data_share | 0.655 | 14067 要求披露;也是本方法学的核心卖点 |
{
"label_id": "CJV-CN-YN-2026-000417",
"version": 1,
"batch_id": "CJV-B-2026-0417",
"declared_unit": "plantlet",
"quantity": 480,
"kgco2e_per_unit": 0.200,
"kgco2e_total": 96.00,
"breakdown": {
"electricity": 0.11923,
"nutrient_n": 0.00810,
"substrate": 0.00000,
"packaging": 0.01200,
"transport_outbound": 0.02000,
"hardware_amortised": 0.03500,
"rounding_reserve": 0.00567
},
"computed_per_unit": 0.19433,
"boundary": "cradle-to-gate",
"gwp_version": "IPCC AR6 · GWP100",
"method": ["ISO 14067", "PAS 2050"],
"data_quality_grade": "A",
"primary_data_share": 0.655,
"activity": { "kwh": 134.4, "n_kg": 0.288, "units": 480, "days": 45 },
"factors": { "mef_bands": [0.31,0.38,0.44,0.47,0.58,0.41], "mef_applied": 0.425833,
"ef_n": 13.5, "ef_pack": 0.012, "ef_trans": 0.020, "ef_hw": 0.035 },
"verification": { "status": "pending", "body": null, "date": null },
"inputs_digest": {
"power_intervals": "sha256:1c07…e9a2",
"nutrient_lots": "sha256:44b1…7d03",
"transport_docs": "sha256:9f52…0c6b"
},
"prev_hash": "sha256:5c71a0e3d8b46f92a1c07e5b3d84f9026e1a7c4d0b93f8e25a6d1c0b7f4e2938",
"hash": "sha256:9d2f4c1b7e0a5f836c2b0d94ae17f5c30b8e6d2a4f9c1e7b5a3d8f06c29e4b71",
"issued_at": "2026-11-03T09:12:44+08:00",
"valid_until": "2028-11-02T23:59:59+08:00"
}
hash 的覆盖范围:除 hash 字段自身外全部字段(含 verification、factors、inputs_digest),按 JSON.stringify 的键序规范化(字典序、无空白、数字不补零)后取 SHA-256。规范化算法必须公开,否则同一份数据会算出不同哈希。
label_id + 哈希前 12 位,不含任何个人信息。hash 逐字节比对。不一致 = 页面或记录被改过。activity × factors:134.4 × 0.425833 = 57.232,再逐项求和得 93.280,ceil → 0.200。任一步不符即在页面标红。prev_hash 回溯上一批次,直到链首(可离线校验,不需要全量数据)。断链即失效。inputs_digest 对应的原始区间序列,用同一摘要算法核对。这一步需要数据方授权(核证方、买方尽调、监管),不是全公开。version+1)并保留旧版factors 全部内嵌在记录里,不引用外部链接——否则五年后无法复算若把验证做成"必须登录我们的平台才能看",那它就不是验证,是展示。
| 端点 | 方法 | 返回 |
|---|---|---|
| /api/carbon/batches | GET | 批次列表:batch_id / 期间 / 株数 / kgco2e_total / grade / status;支持 ?from=&to=&grade= |
| /api/carbon/batch/{id} | GET | 批次明细:breakdown / activity / factors / inputs_digest / 区间能耗汇总(小时段 6 行) |
| /api/carbon/label/{id} | GET | 签名字段记录 + prev_hash 链证明;供扫码验证页与第三方核证方读取 |
| /api/points | GET | 积分台账:balance / 逐条 entries[](批次、避免量、乘数、签发/兑付、状态);?grower=&year= |
与现有平台的关系:复用 /api/telemetry 的同一份环缓冲与 /api/overview 的设备注册表;设备名、遥测键(power_kw / kwh_total / tariff)沿用冻结契约,不得为碳模块新增设备名或改名。四个端点均为只读;封账与签发走内部任务,不暴露写接口。