设备的身份是它自己的 MAC,本来就全局唯一,定位一台设备不需要 pid。 原先按产品切分表,pid 既是查询键、又得由客户端猜出来,猜错就报 "这台 MAC 没登记在该产品下"——提示指向数据缺失,实际数据好好的。 触发改造的事故:一台已正确导入的 DEEPVOICE 耳机(MAC 在 license_b022, 产品 45090,status=0 未绑过)连不上、弹"设备连接受限"。根因是产品 45090 的 devicetype 被配成 2(BLE双端)而不是 1(经典蓝牙):客户端 _resolveProductId 第一步就 where(devicetype==1) 把整个产品滤掉,候选只剩 Echo-one,名字互不包含 → 兜底退回 headsets.first.id = 45091,服务端拿错 pid 去 license_b023 查,自然查不到。而且就算 pid 猜对也过不去——服务端 只有 devicetype==1 才走 MAC 反查,2 会走 else 分支从空 code 反解 pid、 回退写死的 45058。 改动: - 新增全局表 device_mac(comm.TableDeviceMac),主键 code = MAC; productid 降为归属列,不再是查询键。TableLicense 标记废弃,只留给迁移读。 - registry.go 的 migrateLicenseToDeviceMac 每次启动跑、幂等: ① code := devicemac 修存量 → ② 按两表列名交集做 INSERT ... ON CONFLICT DO NOTHING(不能 SELECT *:各分表建于不同时期,有的多 factoryid/probatch) → ③ productid 为空的用表名里的 pid 补上。孤儿分表里也是真实发出去的设备, 一并搬。搬完刻意不删旧表——不可逆数据,旧表是出问题时唯一的回退依据。 - binddevice / unbinddevice 改为先按 code 再按 MAC 全局定位,命中行的 productid 才是产品。req.Pid 不再采信。杰理链路也顺带不需要 devcode 反解 pid 了。 - 新增 comm.ScopeToProduct:合表后分表那层天然隔离没有了,每个"按产品" 的读写都必须显式带 productid,漏一处就跨产品操作——查询多列几行只是 难看,批量禁用 / 按批次删除漏掉就是删别人产品的数据。它会把调用方的 条件整体括起来,防止 "a=? or b=?" 让 or 把 productid 短路掉。 - 导入查重改成全局:同一个 MAC 出现在两个产品下会让按 MAC 反查出现歧义。 - 客户端删掉 _resolveProductId,binddevice 只报 MAC 不传 pid。 按需求明确不做应用隔离:MAC 存在即放行。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>