给固定资产贴二维码,最容易实现的部分其实是“生成一张二维码图片”。真正影响系统长期使用的,是二维码里面放什么、资产编码能不能修改、标签丢失后怎样补打、旧标签是否还能访问,以及扫码接口会不会因为一个可猜测的编号泄露其他部门的资产信息。
如果直接把数据库主键、资产名称、责任人和位置拼进二维码,第一版演示通常没有问题;等组织调整、标签补打、资产调拨和多租户接入以后,编码、标签和资产记录之间的边界就会越来越混乱。
本文以Spring Boot + MySQL资产系统为例,从身份标识、唯一约束、标签版本、二维码载荷、扫码鉴权和批量打印几个方面,整理一套可持续维护的资产编码与标签设计。
目录
一、数据库主键、资产编码和标签码要分开
一项资产至少可能拥有三种标识:
| id | 数据库关联和内部主键 | 通常不展示 | 不变 |
| asset_code | 企业内部识别、查询和单据展示 | 展示 | 原则上稳定,必要时受控变更 |
| tag_code | 二维码或RFID标签识别 | 标签可见或可扫描 | 标签更换时可变化 |

图1:数据库身份、业务身份和物理标签身份承担不同职责,不应被一个字段取代。
数据库主键适合关联,但直接把连续数字主键暴露在扫码URL里会泄露记录规模和创建顺序;资产编码适合人工识别,但可能因历史制度变化而调整;标签码则需要支持丢失、损坏和重新制作。
三者分开以后,换标签不需要修改资产编码,调整编码也不会破坏数据库外键。
二、资产编码不要承载过多可变含义
很多编码规则喜欢把公司、部门、分类、购置年份和流水号全部拼进去:
GZ-IT-COMPUTER-2026-000123
这种编码在打印时很直观,但只要资产跨部门调拨、分类口径调整或公司名称变化,编码中的信息就可能过期。
建议区分两类信息:
- 稳定身份:唯一流水号或不随业务变化的资产编号;
- 当前属性:部门、责任人、位置、状态和分类,由系统实时查询。
编码可以保留少量稳定前缀,例如管理组织或资产大类,但不建议把经常变化的责任人、位置和使用部门放进编码。
如果企业必须保留历史编码,可以增加别名表,而不是覆盖旧值:
CREATE TABLE asset_code_alias (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
asset_id BIGINT NOT NULL,
code_type VARCHAR(24) NOT NULL,
code_value VARCHAR(64) NOT NULL,
valid_from DATETIME NOT NULL,
valid_to DATETIME NULL,
UNIQUE KEY uk_code_alias (tenant_id, code_type, code_value),
KEY idx_alias_asset (tenant_id, asset_id)
) ENGINE=InnoDB;
三、标签表要支持补打、更换和作废
如果每项资产永远只有一个标签,tag_code 放在资产主表里也能工作。但实际使用中经常出现:
- 标签污损后重新打印;
- 二维码标签升级为RFID;
- 原标签丢失,需要让旧标签失效;
- 一项资产同时保留二维码和RFID;
- 标签打印错误,尚未启用就作废。
因此更稳妥的设计是建立独立标签表:
CREATE TABLE asset_tag (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
asset_id BIGINT NOT NULL,
tag_type VARCHAR(16) NOT NULL,
tag_code VARCHAR(96) NOT NULL,
status VARCHAR(16) NOT NULL,
label_template_id BIGINT NULL,
activated_time DATETIME NULL,
invalidated_time DATETIME NULL,
invalid_reason VARCHAR(200) NULL,
created_time DATETIME NOT NULL,
UNIQUE KEY uk_tag_code (tenant_id, tag_type, tag_code),
KEY idx_tag_asset (tenant_id, asset_id, status)
) ENGINE=InnoDB;
标签状态可以使用:
CREATED → PRINTED → ACTIVE → REPLACED
├→ LOST
└→ VOID

图2:标签可以更换和作废,但每一次变化都继续关联同一项资产。
如果业务规定一项资产同一时间只能有一个有效二维码标签,可以在服务层事务中锁定该资产的标签集合,先使旧标签失效,再激活新标签。仅建立 (asset_id, status) 唯一索引通常不能直接表达“只有ACTIVE状态唯一”,需要结合数据模型、生成列或事务校验按实际MySQL版本实现。
四、二维码里只放稳定且受控的标识
二维码不建议直接保存以下信息:
资产名称=研发部笔记本
责任人=张三
位置=广州办公室3楼
原值=8999.00
这些字段会变化,也可能涉及内部信息。更合适的载荷是一个不透明标签码,或指向受控扫码入口的地址:
https://asset.example.com/s/t/7F3K9P2M6Q
扫码后由服务端完成以下步骤:

图3:二维码只负责指向对象,服务端负责身份、权限、状态和返回范围。
如果扫码入口允许匿名访问,应单独定义匿名可见字段,例如只显示“资产已登记”和报失联系方式,不应默认返回责任人、位置、金额、附件和业务记录。
五、扫码接口仍然必须执行对象级权限
将标签码改成随机字符串可以降低枚举风险,但不能代替服务端授权。只要某个有效二维码被拍照、转发或从日志中获得,攻击者仍可能访问该对象。
查询不能只写:
SELECT a.*
FROM asset_tag t
JOIN asset_info a ON a.id = t.asset_id
WHERE t.tag_code = ? AND t.status = 'ACTIVE';
还要把租户和当前用户数据范围加入查询或后续统一授权:
SELECT a.id, a.asset_code, a.asset_name, a.asset_status,
a.use_dept_id, a.location_id
FROM asset_tag t
JOIN asset_info a
ON a.tenant_id = t.tenant_id
AND a.id = t.asset_id
WHERE t.tenant_id = ?
AND t.tag_code = ?
AND t.status = 'ACTIVE'
AND EXISTS (
SELECT 1
FROM user_asset_scope s
WHERE s.tenant_id = a.tenant_id
AND s.user_id = ?
AND s.dept_id = a.use_dept_id
);
OWASP关于不安全直接对象引用的建议强调,即使使用复杂、不可预测的标识,也仍然需要对每一次对象访问执行服务端权限检查。
需要重点测试:修改二维码中的标签码、使用A部门账号扫描B部门标签、扫描已作废标签、未登录扫描受保护入口,以及通过历史标签访问已换标资产。
六、使用ZXing生成二维码时关注哪些参数
ZXing是常见的Java条码处理库,支持QR Code等多种格式。下面是一个简化的服务端生成示例:
public BufferedImage createQrCode(String content, int size)
throws WriterException {
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix matrix = new QRCodeWriter().encode(
content, BarcodeFormat.QR_CODE, size, size, hints);
return MatrixToImageWriter.toBufferedImage(matrix);
}
工程上还需要注意:
- 内容越长,二维码密度越高,污损后的识别效果可能越差;
- 容错等级并非越高越好,提高容错也会增加编码密度;
- 打印尺寸、打印机精度、标签材质和使用距离需要联合测试;
- 不要只在电脑屏幕上测试,应使用实际标签机、手机和现场光线验收;
- 批量生成前先打印一小批样张,验证扫码率和裁切位置。
二维码图片可以重新生成,但“哪一版标签在什么时候被打印并贴到哪项资产”仍需要系统记录。
七、批量打印需要模板版本和打印任务
批量打印不是循环调用二维码生成方法就结束。一个可追踪的打印任务至少应记录:
打印任务号
标签模板版本
资产范围与数量
生成时间与操作人
打印批次与重新打印次数
成功、失败和作废数量
输出文件校验值或存储位置
标签模板中可能包含公司名称、资产编号、分类、服务热线和二维码位置。如果只覆盖模板文件,历史标签来自哪一版就无法判断。

图4:选定资产、冻结数据、生成标签、样张验收、批量打印和激活标签应形成完整批次。
建议标签在实际贴标确认后再进入 ACTIVE,不要因为PDF生成成功就认定标签已经启用。打印失败、重复出纸和未贴标都需要单独处理。
八、怎样迁移现有Excel编码和旧标签
从Excel台账迁移时,不要一边导入一边自动生成并启用新标签。可以分成四步:
重复检测示例:
SELECT tenant_id, UPPER(TRIM(asset_code)) AS normalized_code,
COUNT(*) AS amount
FROM asset_import_stage
GROUP BY tenant_id, UPPER(TRIM(asset_code))
HAVING COUNT(*) > 1;
正式表仍应建立数据库唯一约束。应用层“先查询是否存在再插入”无法消除两个并发请求同时通过检查的窗口。
旧标签和新标签并行期间,要明确哪个入口优先、旧标签何时失效,以及扫描旧标签时是否跳转到新资产记录并留下迁移提示。
九、最低编码与标签设计清单
- 数据库主键、资产编码和标签码职责分离;
- 资产编码不包含经常变化的责任人和位置;
- 租户范围内的资产编码有数据库唯一约束;
- 历史编码通过别名或变更记录保留;
- 标签支持创建、打印、激活、更换、丢失和作废;
- 二维码不直接保存责任人、金额等可变或敏感字段;
- 扫码接口每次执行身份、租户和对象级权限检查;
- 已失效标签不会继续返回当前资产详情;
- 实际标签尺寸、材质、打印精度和现场扫码率经过测试;
- 批量打印保留范围、模板版本、批次和失败记录;
- Excel迁移先检测重复与空值,再生成新标签;
- 换标后可以追溯旧标签、新标签和操作人。

图5:可靠标签体系不仅需要“一物一码”,还要覆盖唯一性、安全、打印和全生命周期追溯。
二维码只是资产识别的入口。真正稳定的设计,是让资产身份不依赖标签是否完好,让标签更换不破坏业务编码,让每一次扫码都经过服务端权限和状态判断。
当数据库主键、资产编码和标签码各自承担清楚职责以后,二维码、RFID、批量打印和历史迁移才有继续扩展的基础。
你的资产标签目前最常遇到的是重复编码、标签损坏、扫码权限,还是批量打印问题?



