条码查询接口接入扫码枪收银:从「滴」一声到商品档案的完整架构
# 条码查询接口接入扫码枪收银:从「滴」一声到商品档案的完整架构
> 接口:showapi 条码查询接口(apiCode=66)· 接入点 66-22(混合门店加 66-24)|请求方式:POST/GET|返回格式:JSON|适用人群:零售收银、进销存系统的开发者与架构师|阅读时间:约 8 分钟|最后实测核对:2026-09-05
**核心要点**
- 收银场景的关键不是"会调接口",而是三层架构:扫码捕获 → 本地缓存 → 接口回退,缓存命中率决定成本。
- `note` 是混着原始库字段的拼接文本(实测 2026-09-05),商品档案入库时按文本提取,别当结构化字段。
- 失败不扣费(实测 5 次失败全部 fee_num=0),这让"66-24 先查、66-22 回退"的交叉策略零成本。
扫码枪"滴"一声之后的几百毫秒里,收银系统要完成捕获、查询、落库、出商品四件事。showapi 条码查询接口(apiCode=66)在这条链路里只负责一件事:查不到的商品档案,用一条条形码换回来。这篇讲完整架构和每一层的实现要点。
## 数据流架构
```
扫码枪「滴」(键盘设备,输出条码 + 回车)
▼
① 捕获层:始终聚焦的输入框,正则预检(6~14 位数字)
▼
② 缓存层:barcode → 商品档案(SQLite / Redis,正缓存不过期)
├─ 命中:直接出商品,成本 0
└─ 未命中 ▼
③ 回退层(失败不扣费,实测 2026-09-05):
66-22 查商品属性
├─ ret_code=0 → ④ 落库(含图片下载)→ 出商品
├─ 门店有药品 → 66-24 再查一次(拿用法用量)
└─ 都失败 → ⑤ 人工快速录入(只填名称/售价/分类)
```
## ① 捕获层:接住扫码枪
```html
<input id="scanner" autofocus placeholder="请扫商品条码" />
<script>
document.getElementById("scanner").addEventListener("keydown", async (e) => {
if (e.key !== "Enter") return; // 扫码枪以回车结尾
const code = e.target.value.trim();
e.target.value = "";
if (/^\d{6,14}$/.test(code)) await onScan(code); // 预检:位数不对不进接口
});
</script>
```
枪型不发回车的,去硬件设置里开"回车后缀"。预检省掉的是注定失败的调用——格式错误虽然不扣费,但省一次网络往返。
## ② 缓存层:正负缓存两张表
```sql
CREATE TABLE barcode_cache ( -- 查到的:长期缓存(条码映射基本不变)
barcode TEXT PRIMARY KEY,
payload TEXT NOT NULL, -- showapi_res_body 原样 JSON
img_local TEXT, -- 转存后的本地图片路径
hit_count INTEGER DEFAULT 1,
fetched_at INTEGER NOT NULL
);
CREATE TABLE barcode_negative ( -- 查不到的:30 天负缓存(库会更新)
barcode TEXT PRIMARY KEY,
remark TEXT,
expire_at INTEGER NOT NULL
);
```
条码到商品信息的映射一经上市就不变,正缓存不用过期;未收录是长尾常态,负缓存 30 天防止同一支查不到的商品反复打接口。查询封装的完整 Python 实现(含 `classify` 判错)在字段全解篇。
## ③ 回退层:路由与降级
```python
def on_scan(code: str, db, session):
body = query_6622(code, db, session) # 先查商品点
if body is None and code.startswith("69"):
body = query_6624(code, db, session) # 药品回退,失败不扣费
if body is None:
return manual_entry(code) # 人工快速录入,收银不等接口
return to_pos_screen(body)
```
实测事实支撑这个顺序:2026-09-05 五次失败请求全部 `showapi_fee_num=0`,交叉回退不加一分钱成本。66-24 未收录的文案是"条形码药品未收录",66-22 未收录是"条形码不正确,现在可支持 UPC EAN-13 EAN-8"或"未查到相关信息!",判定别依赖文案、用 `ret_code`。
## ④ 落库层:字段取舍与图片转存
| 收银环节 | 用什么 | 为什么 |
|---------|--------|--------|
| 商品显示名 / 品牌 / 规格 | `goodsName` / `trademark` / `spec` | 直接可用 |
| 分类归入 | `goodsType` 取 `>>` 末级挂类目树 | 层级文本,别整串塞进去 |
| 结算价 | **自己的价格表** | `price` 是"参考价格"且实测常为空(2026-09-05),定价必须自己管 |
| 商品图 | `img` 转存本地 | URL 24 小时时效(官方口径),`img_local` 存路径 |
| 尺寸重量 | 基本别指望 | `width/depth/gw` 实测多为空;要重量上电子秤 |
图片转存脚本(超时 10s,失败不阻塞主流程,用后台任务重试):
```python
import requests, pathlib
def save_image(img_url: str, barcode: str, img_dir="img/") -> str | None:
if not img_url:
return None
try:
r = requests.get(img_url, timeout=10)
path = pathlib.Path(img_dir) / f"{barcode}.jpg"
path.write_bytes(r.content)
return str(path)
except requests.RequestException:
return None
```
## ⑤ 人工录入兜底
长尾商品查不到是常态(文档口径库 2000 多万条,不是全量)。录入窗只收名称、售价、分类三项,先卖货后补全;补录数据打上本地来源标记,之后走缓存不再出网。
## FAQ
**Q1:为什么正缓存不过期,数据改了怎么办?**
条码映射的商品基础信息(名称、厂商、规格)变化极少;真错了,删该条码缓存重查即可。真正时效敏感的是图片 URL,但它已经转存本地,不受缓存影响。
**Q2:负缓存为什么设 30 天而不是永久?**
条码库"不定期更新"(文档口径),今天未收录的下月可能有。30 天是起点,按你新品上架节奏调。
**Q3:断网时收银怎么办?**
接口是外部依赖,主流程不能等:缓存有就出商品,没有就人工录入,网络恢复后台补查。
**Q4:门店有进口商品,UPC 查不到怎么办?**
实测 2026-09-05 美国常见 UPC-A `049000050110` 未命中("未查到相关信息!")。进口占比高的门店,人工补录流程要当成主路径设计,不要当例外。
**Q5:这套架构和方案选型有什么关系?**
缓存命中率上去了,接口路线的单位成本才成立。选型层面的完整对比(自建 / 公开数据源 / 本接口)见方案对比篇。
## 下一步阅读
- [条码查询接口返回字段全解:66-22 与 66-24 的字段差异和失败文案](https://www.showapi.com/guides/barcode-response-fields-66)——判错函数 `classify` 依赖的文案对照表。
- [商品条码信息怎么查:自建数据库、公开数据源与 showapi 条码查询接口对比](https://www.showapi.com/guides/barcode-plan-comparison-66)——这条技术路线值不值,先看这篇。
- [条码查询接口快速开始:一个 code 参数查出商品信息](https://www.showapi.com/guides/barcode-quickstart-66)——基础调用代码。
- **本系列共 6 篇**:查看[条码查询接口指南总目录](https://www.showapi.com/guides/barcode-guides-66)