
资产管理的台账更新最怕的就是资产编号抄错、盘点数据对不上账。我前阵子基于若依框架把手里的资产管理系统整体升级了一版核心就一件事让每一件资产都能“扫一下”完成查询和盘点不再靠人眼对编号、手工敲键盘。这次升级后行政、财务、IT几个部门的使用反馈都不错这篇文章把整个改造过程的思路、代码落地、权限整合和踩坑记录整理出来给正在做或者准备做类似扫码资产管理的团队一个参考。1. 资产管理的日常有多痛扫码方案又是怎么定的1.1 旧模式下的三个高频崩溃现场先说旧系统没改造前的样子。资产入库时管理员在后台手动录入资产编号、名称、责任人、存放位置然后打印一张A4标签贴在设备上。看着没什么问题但实际用起来全是坑。第一个崩溃现场是盘点。每季度盘点拿着Excel打印出来的资产清单一间一间办公室跑看到一台显示器就得弯腰看标签上的编号然后在一大张表里找这行手工打个勾。一千多件资产两个人盘一整天眼睛都快看花了回来录入Excel时还有可能抄错行。第二个崩溃现场是查询。员工打电话问IT“我这台电脑是什么时候采购的保修到什么时候能不能帮我查一下。”IT同事打开后台管理系统先问资产编号是多少对方说不知道再让把机箱上的标签念一遍一串十几位的编号在电话里念来念去很容易听错查出来还不对。第三个崩溃现场是资产变动。有人调岗电脑要从A部门调到B部门资产管理员要在系统里找到这条记录更改使用人、部门、存放位置。如果资产编号漏记了一位检索结果就为空只能拿Excel全文搜索碰运气。1.2 为何选择“一物一码”而不是RFID或NFC其实市面上的资产盘点方案不止扫码一种我当时认真比较过RFID、NFC和扫码三条路线。RFID的方案是给每件资产贴上无源RFID标签盘点时拿着手持机在一定范围内扫一圈可以批量读取。优势是速度快适合几千上万个资产的大规模盘点缺点是标签和手持机成本明显更高而且金属材质的设备机箱、服务器对RFID信号有干扰需要专门抗金属标签成本还得往上走。NFC则是近场通信手机贴上去读取距离限制在几厘米内操作上比扫码更麻烦而且NFC标签贴在金属机箱上同样有读取率问题。扫码方案的优势就是“零门槛”。资产标签可以打印成条形码或二维码成本几乎可以忽略识别设备既可以用USB扫码枪也可以用手机摄像头。虽然一次只能扫一个但绝大多数中小企业的资产规模在一两千件左右这个效率完全够用。1.3 以若依作为开发底座的原因选择若依RuoYi作为这套系统的开发底座不是因为它是“国产开源第一框架”所以无脑选而是看中了它在后台管理系统领域的几项成熟能力。首先是权限体系。资产管理虽然看起来只是查查台账但实际使用中有明确的角色边界普通员工只能查自己的资产部门主管可以看部门内的资产资产管理员才能新增、调拨、报废财务人员可以看资产原值折旧但不能操作。若依的RBAC权限模型刚好能直接覆盖这套需求不用从零设计用户角色菜单。其次是代码生成器。资产管理涉及大量基础数据的增删改查像资产分类、存放地点、供应商、领用人这些字典表用若依的代码生成功能可以直接从数据库表生成后台管理页面几天时间就能把后端管理模块铺完省下大量重复劳动。第三是前后端分离结构便于扩展。扫码盘点这类需要移动端配合的功能后期如果要做成微信小程序或者手机H5前后端分离的接口结构扩展起来很顺畅。2. 资产编码和标签打印扫码系统的地基工程2.1 资产编码规则的设计思路很多人做资产管理系统资产编码喜欢用纯流水号比如A0001、A0002这样。看着简单但实际使用中问题很大纯流水号完全没有任何业务信息看到一串编号既不知道这是什么类型的资产也不知道是哪个部门采购的。一旦资产量上来对照纸质台账找类别都费劲。我在这次升级中把编码规则调整为类型码 采购年月 部门码 四位流水号 校验位。例如PC-202403-IT-0012-X。PC代表资产类型电脑另外还有MN显示器、SR服务器、NB笔记本等202403代表采购年月IT代表使用部门0012是当月该类型资产在该部门下的流水序号X是校验位由前几位字符通过固定算法算出来防止手动输入时出现错误。这个规则的好处是管理员看到一串编码就能大致判断出资产类型、所属部门和采购时间电话里让对方念编号时少一位或者错一位马上能发现。盘点和维修时扫到码后系统还能自动校验校验位乱写的编号直接拦在门外。2.2 用Java生成资产二维码标签的实操因为若依后端是Java技术栈我直接用了Zxing库生成二维码和条形码。考虑到二维码信息容量更大后期还可以在码里携带更多扩展字段最终选择了二维码方案。在pom.xml引入依赖dependency groupIdcom.google.zxing/groupId artifactIdcore/artifactId version3.5.2/version /dependency dependency groupIdcom.google.zxing/groupId artifactIdjavase/artifactId version3.5.2/version /dependency生成二维码返回Base64字符串给前端展示前端用img标签直接渲染。核心工具方法public static String generateQRCodeBase64(String content, int width, int height) { QRCodeWriter qrCodeWriter new QRCodeWriter(); MapEncodeHintType, Object hints new HashMap(); hints.put(EncodeHintType.CHARACTER_SET, UTF-8); hints.put(EncodeHintType.MARGIN, 1); try { BitMatrix bitMatrix qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints); BufferedImage image MatrixToImageWriter.toBufferedImage(bitMatrix); ByteArrayOutputStream outputStream new ByteArrayOutputStream(); ImageIO.write(image, png, outputStream); return Base64.getEncoder().encodeToString(outputStream.toByteArray()); } catch (Exception e) { throw new RuntimeException(生成二维码失败, e); } }二维码内容我用的是一个标准化的JSON字符串而不是纯资产编号。因为后续扫码后除了查资产本身还得知道当前页面应该跳转到什么操作。比如日常查询、盘点、维修登记需要的数据字段不一样如果码里只放一个编号每次扫码后还要在前端再判断场景很麻烦。用JSON可以把资产编码、资产类型、批次号都放进去前端扫码后解析即用。实际生成的二维码内容示例{assetCode:PC-202403-IT-0012-X,type:PC,batch:202403}2.3 标签打印和粘贴的几个细节经验标签打印这块我踩过一次坑。最初图省事直接用普通A4纸打印后裁剪再用透明胶带贴在机箱上。结果过了两三个月胶带发黄变脆纸张边缘起毛二维码边缘破损严重扫码识别率大幅下降。后来换成了PET哑面材质的不干胶标签纸配一台兄弟或者得力的小型标签打印机打印效果清晰度和耐用度都好了很多。粘贴位置也有讲究。台式机机箱统一贴在正面左上角显示器贴在背面铭牌旁边笔记本贴在D面不遮挡散热孔的位置。不要在同一个位置贴两层标签后期撕下来重贴时残胶很难清理还可能撕坏设备表面的涂层。另外建议开发一个“补打标签”功能。资产生命周期内标签脱落或者磨损是必然发生的如果没有补打功能管理员只能重新录入一条资产或者手工记录编号数据就有可能重复。我在资产详情页加了重新生成标签的按钮数据不变只重新生成一张图片。3. 扫码核心链路的实现盘点、查询和资产变动怎么做3.1 先理解扫码枪的输入机制再写代码很多第一次做扫码功能的开发者容易把扫码枪想象成一种“高级设备”以为需要写串口通信或者SDK对接。实际上市面上绝大多数USB扫码枪在系统层面就是一把“键盘”扫描条码时它把码内容转换为键盘按键事件一个字符一个字符地输入到当前聚焦的输入框里并且默认在结尾自动带一个回车键。这意味着前端实现扫码功能极其简单在输入框聚焦的状态下直接扫就行不需要任何驱动。我们做个隐藏的输入框扫码枪扫完自动回车触发搜索或者提交事件。这个机制也带来一个需要注意的问题如果页面上的某个按钮处于焦点状态扫码枪回车可能会误触发按钮点击。所以扫码页面一律要管控焦点手动指定扫码内容输入到哪个字段。前端vue页面里的实现思路handleScannerInput(event) { const value event.target.value if (event.key Enter) { if (value value.startsWith({)) { // 扫码内容解析跳转或查询 const assetInfo JSON.parse(value) this.queryAssetByCode(assetInfo.assetCode) } this.$nextTick(() { this.$refs.scanInput.value }) } }3.2 扫码盘点批量提交与防重复设计盘点场景的诉求是拿着扫码枪把所有看到的资产扫一遍系统自动记录“已盘到”没扫到的就是盘亏项。传统做法是扫一件资产就调用一次后台接口资产量大的时候后台压力很大而且一旦网络波动单条提交失败很难发现。我改成前端先把扫码结果存在一个数组里盘点结束后一次性批量提交。比如扫到一个资产先在前端做展示确认确认无误后push进数组显示在页面上。盘点结束时把整个数组提交到后台{ planId: 202403, items: [ {assetCode: PC-202403-IT-0012-X, scanTime: 2024-03-20 10:30:00}, {assetCode: MN-202310-IT-0023-X, scanTime: 2024-03-20 10:31:20} ] }后台处理时需要注意防重复。同一个盘点任务里用户可能会不小心把同一件资产扫两次。我在Service层用Redis做了去重扫过的资产编码在任务有效期内不再重复记录public void submitStocktake(StocktakeSubmitDTO dto) { ListStocktakeItem items new ArrayList(); for (StocktakeItemDTO item : dto.getItems()) { String dedupKey stocktake:dedup: dto.getPlanId() : item.getAssetCode(); Boolean firstScan redisTemplate.opsForValue() .setIfAbsent(dedupKey, 1, Duration.ofMinutes(30)); if (Boolean.TRUE.equals(firstScan)) { items.add(buildStocktakeItem(item)); } } stocktakeService.saveOrUpdateBatch(items); }这个做法尤其适合扫码枪这种连续快速输入的设备后端接口无需处理大量实时写入只需在批量提交时做最终落库。3.3 扫码查询把“查资产”变成“扫出来”查询功能是最简单但使用频率最高的模块。原来的流程是打开系统、登录、进入资产列表、输入编号回车、查看详情全部做完最少十几次点击。升级后变成了打开扫码查询页面、扫一下、直接看到资产详情。这里有个细节值得说一下如果扫码查询只做了单件资产查询效率提升还不够明显。我额外加了一个“关联资产”展示。比如一台电脑扫码后不仅展示电脑本身的信息还会带出配套的显示器、键盘鼠标等关联资产。因为资产入库时做了“父资产编码”关联扫码后一条SQL就能把整组资产带出来。行政在盘点时只需要扫主资产配件列表同时展示账实核对效率提升一大截。3.4 资产领用、调拨和变更的扫码联动资产变更是资产管理里比较敏感的操作。传统方式是在资产列表找到记录修改责任人字段。我在这版升级里做成了“扫码 - 确认当前持有人 - 扫描接收人二维码/选择接收人 - 提交变更”三个步骤。接收人也可以做成一张二维码工牌。员工入职时系统生成一个包含员工ID的水印二维码贴在工牌上。调拨资产时扫一下资产码再扫一下新员工的工牌码系统自动弹出调拨确认页面核对资产信息、原使用人、新使用人、变更原因确认后提交。这个流程相比原来在系统里搜记录再编辑至少快三倍而且每一步都有留痕谁在什么时间把什么资产给了谁一清二楚。4. 和若依框架整合时绕不开的几个关键点4.1 权限控制不是每个接口都能给所有人用若依的权限体系默认是菜单权限和按钮权限两层。我在设计扫码相关接口的时候没有把所有接口都挂给任何人而是按角色做了收紧。后端接口用若依的权限注解控制PreAuthorize(ss.hasPermi(asset:stocktake:submit)) PostMapping(/stocktake/submit) public AjaxResult submitStocktake(RequestBody StocktakeSubmitDTO dto) { return success(stocktakeService.submitStocktake(dto)); } PreAuthorize(ss.hasPermi(asset:transfer:create)) PostMapping(/transfer/create) public AjaxResult createTransfer(RequestBody AssetTransferDTO dto) { return success(assetTransferService.createTransfer(dto)); }前端按钮也同步加上v-hasPermi指令控制显示。这样不同角色看到的首页功能是不同的。比如普通员工登录后只能看到“我的资产”和“扫码查询”资产管理员才能看到“资产盘点”、“调拨管理”、“标签补打”这些入口。4.2 扫码枪免登录场景下的Token处理扫码枪本身是不带登录界面的设备直接插在电脑上就是键盘。如果管理员打开盘点页面时登录态已经过期扫第一个码就会跳回登录页整个盘点流程就断了。这个问题非常影响实际使用。针对这个问题我的方案是给盘点终端配置一个“设备账号”。在若依后台创建一个名为“盘点终端”的专用账号绑定资产管理员角色密码设为强密码。盘点电脑首次使用时管理员手动登录一次前端把token存到localStorage并延长有效时间。若依的token默认存Redis有效期默认是30分钟。盘点场景一次可能需要一两个小时我在配置中心单独为这个设备账号调整了token过期时间到8小时同时在若依的配置项里开启了在线用户管理方便随时踢掉可疑会话。还有一个更顺滑的做法在盘点页面挂一个定时刷新token的定时器每隔一段时间调用若依的刷新接口让token一直保持有效。但这样需要前端定时器一直在页面活跃状态下运行实际上盘点过程本身页面上一直有扫码输入操作交互不断token刷新压力不大。4.3 用若依代码生成器快速搭建资产台账模块若依的代码生成器是这套系统能快速落地的另一个重要原因。我在数据库中设计好资产主表、分类表、存放地点表、供应商表后直接在若依管理后台的“代码生成”菜单导入表结构配置好列表查询字段、表单控件类型、关联查询字典服务器上执行生成的代码脚本直接生成前后端代码再手动补齐业务字段和校验逻辑。这套流程最大的价值在于基础CRUD几乎不用写代码能把精力全部集中在扫码盘点、批量导入、资产联动这些核心业务上。实际做下来资产台账模块从建表到后台管理页面完整可用只用了不到一天时间。4.4 多租户和多部门数据隔离的提前设计若依本身没有内置多租户但资产管理系统通常天然面临“多分公司”或者“多个独立核算部门”的场景。虽然早期版本可能只有一个公司用我在设计表的时候还是提前预留了tenant_id字段所有资产主表和变动流水表都有这个字段并且在MyBatis-Plus的实体上加了拦截器实现。Data public class AssetInfo { private Long id; private String assetCode; private String assetName; private Long tenantId; // 其他字段省略 }这样后期如果扩展成多公司共用一套系统不需要改动现有表结构只需要在数据权限拦截上增加租户过滤即可。配合若依本身的数据权限部门过滤可以实现“分公司隔离、部门内数据可见”的层级权限。5. 实测过程中的意外情况和修复记录5.1 连续扫码时最后一个字符丢失第一轮实测时就遇到了一个很隐蔽的问题。用扫码枪快速连续扫描前几件资产都能正常录入扫到第三四件的时候偶尔会出现资产编码少一位的情况。排查了很久最后发现不是扫码枪的问题而是前端输入框的change事件在输入过程中被触发了。扫码枪输入速度极快Vue的input组件在某些浏览器中会将输入过程拆成多个change事件如果输入框在字符未完整时就被处理了一遍最后的字符就被截断了。解决办法是在确认扫码完成之前加上“回车键判断”不允许在length变化时立刻取值。因为扫码枪输入结束后会自动带回车而手动键盘输入的回车则不会误判。5.2 二维码内容中的JSON特殊字符导致解析失败刚开始生成的二维码内容是用JSON字符串直接编码的字符串中的引号和花括号在部分扫码枪下会被解析成特殊字符尤其是低端扫码枪的固件对特殊字符支持不完善扫出来的内容少了开头的大括号或者引号错乱导致前端JSON.parse直接抛异常。后来做了一个容错处理前端解析失败时进入降级逻辑直接从扫码内容中提取assetCode字段。因为资产编码的规则是固定的可以用正则去匹配function parseScanResult(rawStr) { try { return JSON.parse(rawStr) } catch (e) { const match rawStr.match(/assetCode[\]?\s*:\s*[\]([^\])[\]/) if (match) { return { assetCode: match[1] } } throw new Error(无法识别的扫码内容) } }这样即使扫码枪对JSON里的花括号支持不完美只要assetCode字段能提取出来盘点流程就能继续。5.3 大批量盘点的数据库性能优化第一版盘点提交逻辑直接用MyBatis-Plus的saveBatch逐条插入一次提交500条记录用时将近20秒体验很差。后来改为分批批量插入每批50条并且关闭自动提交全部插入完成后统一提交事务用时从20秒降到4秒左右。另一个改进是盘点时使用了唯一索引。在盘点明细表上建立了plan_id和asset_code的联合唯一索引即使前端防重逻辑有遗漏数据库层面也能兜底不会出现一条盘点任务里同一资产重复计数的情况。5.4 网络不稳定环境下的盘点数据暂存有些单位的机房或者库房处于地下室网络信号很差。如果盘点到一半断网这一批扫码数据全部丢失会非常崩溃。我在前端加了localStorage暂存机制盘点过程中的数据扫描成功后先写入本地缓存每5条自动批量提交一次提交成功后把已提交的缓存删除。如果检测到断网已扫码数据继续留在本地网络恢复后弹窗提示“有未提交的盘点数据是否继续提交”。这个改动看起来不复杂但在实际使用中帮了大忙。库房盘点的场景手机和移动电脑的信号确实不稳定有这个兜底机制盘点人员明显安心很多。6. 这套系统后续还能怎么扩展这次基于若依的扫码资产管理升级核心解决了资产“怎么快速找到、怎么快速盘点、怎么快速变更”三个问题。但从实际运营角度看还有一些可以继续深入的方向。第一个方向是移动端。当前扫码主要依赖电脑端的网页配合扫码枪虽然稳定但灵活性有限。后期可以做成微信小程序或者钉钉H5员工手机扫码就能自助查询、报修、提交资产领用申请把使用门槛再降一档。第二个方向是资产维保提醒。资产入库时录入保修截止日期后台用定时任务扫描即将到期或者已过保的资产主动推送提醒给资产管理员。这个功能对IT设备特别有用很多显示器、笔记本的保修是按激活日期算的不记录的话很容易错失免费维修窗口。第三个方向是资产折旧联动。当前系统的资产原值、入账日期、折旧年限字段已经预留但资产报废和财务折旧还没有打通。后期可以考虑按资产状态自动推送到财务系统减少月底对账的重复劳动。最后再分享一个实际操作中的小技巧二维码内容里不要放太多字段够用就行。一开始我想把采购订单号、供应商、存放位置全部编进码里后来发现二维码越密打印出来对打印机的清晰度要求越高稍微磨花一点就扫不出来。现在码里只有三个字段识别率非常稳定剩下的信息全部靠后台查询补全。