SpringBoot+Android+Vue3实现仓库管理APP:从技术选型到全栈落地
发布时间:2026/9/26 13:43:53 作者:尧图编辑部 阅读量:1,286

最近把之前做的一个仓库管理APP项目整体复盘了一遍发现当年选型时纠结的PHP、asp.net、java、Springboot、SSM、vue3这些关键字其实正好覆盖了一整套移动端服务端管理后台的方案。这个项目的题目一眼看上去很吓人像是把市面上主流技术全塞进一个标题里但拆开来看内核就是做一个跑在Android手机上的仓库管理工具后端提供数据服务电脑浏览器上再挂一个管理后台。这篇文章我就用这个项目作为线索把从需求拆解、技术选型到代码落地的完整过程捋一遍希望能给正在做类似毕业设计或企业小项目的朋友提供一份能直接抄作业的参考。这类项目最忌讳一上来就写代码先把业务想透后面才会顺。仓库管理听起来简单但入库、出库、盘点、库存预警、多仓协同这些动作叠加在一起数据库设计和接口设计稍有不慎后续改起来就是牵一发动全身。本文不打算只贴代码而是把每一步选择背后的理由也讲清楚你拿到手之后不仅能复现还能根据自己的业务场景做调整。1. 项目背景一个把“所有候选技术”都写进标题的仓库管理APP1.1 我拿到这个题目时第一反应这类标题大多是毕业设计或者课程设计里最常见的格式“基于XX、XX、XX的XXX的设计与实现”。它看起来像是一堆技术名词的堆砌实际上给的信号很简单项目方自己也没想好到底用什么需要你作为开发者给出一个合理的选型和落地方案。我当初接到这个题目的时候首先是把它翻译成正常人能看懂的话一个可以运行在Android手机上的仓库管理App配一个后端服务最好再有一个电脑端管理后台。要特别提醒的是这类题目最终是否拿高分、是否能真正落地关键不在于你用了多少种技术而在于你能不能把“仓库管理”这四个字的业务逻辑讲清楚并且让系统在演示的时候不翻车。技术再炫入库单存不进去、库存数字对不上这个项目就是失败的。我见过太多同学把精力花在炫技上结果业务流全是漏洞最后答辩时被老师一个问题问倒。业务完整性永远是第一位技术是服务业务的。1.2 仓库管理到底要解决哪几件事与其先打开IDE写代码不如先花半天时间把仓库里的真实工作流程梳理一遍。我调研了几家中小型仓库后发现核心业务其实就五件事入库、出库、库存查询、盘点、预警。听起来简单但每一件都藏着一堆细节。入库不仅仅是录入一张单子它涉及核对采购单或到货单、检查货物数量与质量、安排货位、更新库存。出库则要处理拣货、核对出库单、扣减库存。这两件事有一个共同点它们都会改变库存总额一旦操作并发或流程中断账实不符的问题就出现了。盘点是对账的过程定期把系统中的库存数与仓库实物数进行核对。预警则是在库存低于安全线或高于仓库容量时给出提醒。这些业务动作放在电脑上做其实很别扭仓库管理员不可能一直坐在电脑前录单所以这个项目的核心载体才是Android手机扫码枪一扫、手机一点入库出库就完成了。这个理解到位了项目的地基才算打牢。1.3 移动端管理仓库的天然优势仓库是一个人员流动、货物搬运频繁的现场。传统方式用手工单据或Excel信息滞后经常出现货已经搬走了、系统里还有库存的情况。在仓库现场用手机App作业数据录入前置到动作发生的时刻库存的实时性会大幅提升。同时手机自带的摄像头和蓝牙能力天然适合扫码。相比专用的PDAAndroid手机加外接扫码枪的成本低很多。这也是为什么这类“基于Android的仓库管理APP”在中小物流企业里非常受认可它在不更换硬件的前提下把一套完整的库存管理体系装进了别人口袋里的手机。想清楚这些应用场景后续的功能设计和接口设计才会有依据。2. 技术选型PHP、ASP.NET、Java三大家怎么取舍2.1 五个候选技术栈的横向对比标题里出现了六个关键词PHP、asp.net、java、Springboot、SSM、vue3。它们其实属于三层后端语言/框架PHP、ASP.NET、Java、SpringBoot、SSM和前端框架Vue3。我画了一张对比表直接给出结论。技术栈后端生态成熟度移动端协作便利性国内人才储备项目落地风险PHP一般中中中小型项目够用工程化偏弱事务处理需要额外小心ASP.NET强中较少企业常见但课程设计和外包生态稍弱Java SpringBoot SSM很强高多生态最稳适合长期维护踩坑资料最丰富Vue3前端部分强高多配合前后端分离效果好学习曲线平缓需要先说明一下SSM和SpringBoot的关系很多同学在这里容易混淆。SSM是SpringSpringMVCMyBatis三个框架的组合SpringBoot则是对Spring生态的一层简化封装。换句话说用SpringBoot写业务时底层依然可以用MyBatis做持久层两者并不冲突。我的选择是后端以SpringBoot为基座持久层使用MyBatisMyBatis-Plus这样就同时覆盖了标题里的SpringBoot和SSM两个关键词而且代码量比传统SSM配置方式少很多。2.2 为什么后端最终选SpringBootMyBatis先看PHP。PHP做Web API确实开发快中小项目两个星期能上线。但仓库管理涉及大量事务性操作比如一次入库要同时插入单头、明细、修改库存PHP本身对事务支持并不差问题是类型系统和工程化能力偏弱多人协作后代码容易变得难以维护。ASP.NET则是好技术C#的语法和工具链都很成熟但在国内中小型软件企业里Java程序员基数更大甲方后续找人维护的成本也更高。Java这头SpringBoot几乎成了事实标准。它的优势在于第一Spring全家桶对事务管理、依赖注入、定时任务、消息队列都有成熟支持仓库系统需要的数据一致性可以由声明式事务直接解决第二资料极其丰富几乎你能遇到的任何报错都能在社区里找到解决方案第三部署生态成熟打一个jar包扔到服务器上就能跑升级也方便。MyBatis作为持久层SQL可控性比JPA更强仓库系统里大量复杂的多表聚合查询写SQL比对象关系映射更直观。至于SSM和SpringBoot之间怎么协调我在第4章实操部分会给出一个具体的分层结构。如果追求更极致的定制理论上也可以用PHPLaravel或ASP.NET Core重新构建一遍但考虑到题目要求、后续维护和答辩效果SpringBootMyBatis是国内这类项目最稳妥的一条路线。实际开发时你会发现网上的SpringBoot仓库管理开源项目非常多不管是复用代码还是参考思路都比冷门技术栈省力得多。2.3 Android端为什么不用uni-app这种跨端方案热搜词里有个很典型的问题“uniapp 开发 微信小程序 vs android / ios / 鸿蒙”。很多同学会在选题时纠结是不是写一套uni-app既能做App又能做小程序岂不是更好我的建议是除非题目明确要求跨端否则仓库管理这种项目优先做原生Android开发。原因是跨端方案在涉及硬件能力时总会留一些坑。仓库App最核心的扫码功能原生调用摄像头和系统相机接口要顺滑得多外接扫码枪在Android原生里可以监听键盘事件或使用串口协议跨端框架做这些要绕很远的路。数据离线缓存也是仓库地下室里经常没信号原生SQLite配合Retrofit的拦截器做离线队列实现逻辑更可控。当然如果你已经会uni-app用它做管理后台的前端部分完全没有问题。只是App端我坚持用Android Studio原生开发这也是标题里“基于Android”这个限定词的真正含义。2.4 Vue3作为管理后台的合理理由管理后台为什么不用JSP或者FreeMarker直接在后端渲染页面因为一旦你决定后端是纯RESTful API前端就该独立出来了。Vue3在这时候的优势很明显组合式API让复杂业务逻辑的组织比Vue2的选项式API更顺手尤其是在处理“登录态多角色菜单库存报表”这种中等复杂度的后台时逻辑复用度高代码不容易散。配套方案我选的是ViteElement PlusPiniaAxios。Vite启动速度快Element Plus的表格和表单组件能覆盖仓库管理后台大部分CRUD场景Pinia替代Vuex之后写起来轻便很多。这里也有一个常见的误区很多教程会让你安装SCSS并在组件里随意使用深度选择器实际上对于这个规模的后台用Element Plus自带样式加少量普通CSS就够了SCSS用了反而增加构建时间。Vue3本身用模板语法写业务就很快JSX只在极少数动态组件场景才需要没必要为了用而用。3. 总体架构设计与数据库建模3.1 三层架构Android App、后端服务、Vue3管理后台整个系统我拆成三个部分Android客户端面向仓管员和仓管主管SpringBoot后端所有业务逻辑和数据落库都在这层Vue3管理后台面向仓库经理或系统管理员负责基础数据维护、用户权限配置、数据报表查看。这样的三层结构和“前后端分离移动端”的标准企业项目形态是一致的。后端只暴露RESTful接口App和后台都通过HTTP调用同一套API复用度很高。比如入库和出库这两个动作App端扫码触发管理后台也能手动录入逻辑都收口在后端的同一个Service里不会出现两套实现、两边数字对不上的问题。通信链路统一走HTTPSAndroid端和Vue3后台的请求都由OkHttp和Axios各自管理但都遵循相同的鉴权规则这样在联调阶段能省掉大量沟通成本。3.2 数据库设计核心表以及字段细节我以MySQL为例核心表包括用户表、商品表、仓库表、货位表、库存表、入库单表、入库单明细表、出库单表、出库单明细表、盘点任务表、盘点明细表。下面列几个最关键的说明一下。表名核心字段作用userid, username, password, role登录与权限控制productsku_no, name, spec, safe_stock商品基础信息sku_no唯一索引warehouseid, name, address仓库基础信息locationid, warehouse_id, code货位一个仓库有多个货位stockproduct_id, warehouse_id, location_id, quantity, version实时库存唯一索引防重复inbound_orderid, order_no, warehouse_id, operator_id, status, create_time入库单主表inbound_order_itemid, order_id, sku_no, quantity, price入库单明细一行一个SKUoutbound_orderid, order_no, warehouse_id, operator_id, status, create_time出库单主表outbound_order_itemid, order_id, sku_no, quantity出库单明细stocktake_taskid, task_no, warehouse_id, status, creator_id盘点任务头stocktake_task_itemid, task_id, sku_no, system_qty, actual_qty, diff_qty盘点明细与差异商品表要包含sku_no唯一编码、名称、规格、单位、安全库存、预警上限。库存表是最容易设计错的表很多新手会把库存数量当成商品表的一个字段这是对的但只适合单仓场景。一旦系统要考虑多个仓库就必须把“库存”设计成一张独立的表用商品ID仓库ID货位ID共同定位一条记录。入库单和出库单的设计思路是“一主多明细”主表记录单号、类型、操作人、仓库ID、时间、审核状态、备注明细表记录每个商品编码、数量、单价、货位。为什么要拆分因为一张到货单可能对应几十种货物分散录入时需要对每一行分别确认主从表结构才能支持这种场景。所有涉及金额变化的表都要注意金额精度MySQL中用DECIMAL(10,2)代替FLOAT避免精度丢失。为了盘点对账方便我还在库存表上加了一个version字段用于乐观锁控制这个字段平时不起眼在高并发扣减库存时会救命后面第7章再细说。另外所有业务表建议都保留一个deleted字段做软删除仓库业务流程经常要追溯历史物理删数据会丢失审计线索。3.3 接口规范RESTful JWT 认证的设计要点后端接口统一使用RESTful风格返回格式固定为code、message、data。code为0表示成功非0表示业务异常。这样的好处是所有客户端Android、Vue3后台可以共用一套解析逻辑联调时省心。比如出入库接口如果库存不足后端返回code5001message“该商品库存不足”前端拿到后直接弹Toast不需要额外做错误映射。登录认证采用JWT。用户登录后服务端签发一个token后续所有受保护接口在请求头带上Authorization: Bearer 。JWT最大的问题是到底放多少信息进去我的经验是只放userId和角色名千万不要把敏感信息直接塞进token。token过期时间设置为8小时同时在Redis中保存refreshTokenApp端在token快过期时自动换一个新的这样用户就不需要反复登录。对于仓库这种操作频率高的系统接口路径的设计建议按“资源动作”来命名比如/api/stock/inbound表示入库、/api/stock/outbound表示出库、/api/stock/list分页查询库存、/api/report/turnover获取周转报表。RESTful资源理论上应该用名词但实际项目中为了可读性动作型路径反而更受仓管人员喜欢这也是一种务实取舍。参数校验统一使用Spring Validation的注解实体类字段上加上NotBlank、NotNull避免脏数据直接落到数据库。4. 后端实操SpringBootSSM项目的搭建与核心接口实现4.1 项目初始化与分层目录我用IDEA创建SpringBoot工程Java版本17打包方式jar。依赖选择Spring Web、Spring Boot Validation、MyBatis、MySQL Driver和Lombok。创建完成后项目里最重要的就是目录分层controller层只做参数接收和返回service层处理业务逻辑和事务mapper层用MyBatis与数据库交互entity是数据库实体dto是接口出入参common存放统一返回类和异常处理。很多人容易把业务逻辑堆在controller里短期内运行没问题后续要加单元测试或复用逻辑时就会很痛苦。application.yml里有一点值得专门说MyBatis开启驼峰映射。数据库字段如果叫sku_no实体属性叫skuNo如果不开启map-underscore-to-camel-case查询结果会一直为空。这个问题在联调时很容易被忽略因为SQL能查出数据但接口返回null。另外分页插件的Bean也不能漏MyBatis-Plus的PaginationInnerInterceptor需要在配置类中显式声明不然Page对象的total一直是0。spring: datasource: url: jdbc:mysql://localhost:3306/warehouse?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root jackson: time-zone: GMT8 date-format: yyyy-MM-dd HH:mm:ss mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 04.2 出入库接口的完整实现思路含多表事务处理以入库为例一段典型的流程是接收入库单主表字段明细列表校验商品是否存在校验数量是否大于0依次插入入库单主表、插入明细表、对每个SKU执行库存增加最后更新商品最近入库时间。这四步如果中途任意一步失败前面已经写入的数据必须全部回滚否则就会出现“明细写了但库存没增加”的脏数据。解决方法是使用Spring的Transactional注解让方法中的所有数据库操作处于同一个事务中。需要注意事务回调的问题在该事务提交之后才去执行某些后续动作比如写入操作日志直接放在事务方法里是不安全的。我通常会加上AfterCommit或在事务提交后再发消息确保日志不会因回滚而丢失。注意Transactional默认只回滚RuntimeException如果你的业务将异常包装成了受检异常记得指定rollbackForException.class否则事务不会生效数据会出一半。出库的流程和入库对称但在减库存时建议使用“条件更新”语句UPDATE stock SET quantity quantity - #{num} WHERE product_id #{pid} AND warehouse_id #{wid} AND quantity #{num}执行后返回受影响行数如果为0说明库存不足或商品不存在再抛出业务异常。这条SQL是解决并发超卖的关键不使用“先查后改”的方式而是让数据库完成判断和扣减的原子操作性能和安全都能兼顾。4.3 MyBatis-Plus在库存统计中的SQL优化MyBatis-PlusMP的BaseMapper能覆盖单表CRUD像商品表、仓库表这类基础数据直接用它即可。但库存报表这种多表聚合我还是倾向手写SQL在XML或注解中实现。比如统计某仓库近7天每日入库总量SELECT DATE_FORMAT(inbound_time, %Y-%m-%d) AS day, SUM(quantity) AS total FROM inbound_order_item WHERE warehouse_id #{warehouseId} AND inbound_time #{startTime} GROUP BY day ORDER BY day使用MP的时候还有一个高频坑分页插件不生效。很多人以为引入分页插件就能自动分页其实必须在配置类中显式添加PaginationInnerInterceptor否则Page对象返回的total永远是0。这一点在联调时一旦遇到数据量小看不出问题数据量大了前端分页就会对不上。分页参数建议前端传入pageNum和pageSize后端返回一个统一的PageResult对象包含total、records、pageNum三个字段App端和Vue3后台都能直接解析。5. Android端实现扫码、库存、离线能力5.1 项目骨架架构MVVM Retrofit LiveDataAndroid端用Android Studio创建语言我用Kotlin如果你对Java更熟也完全可以用Java两者在标题里都算“基于Android”。架构上采用MVVM模式具体落地是Activity/Fragment负责UI绘制和用户交互ViewModel持有界面状态并调用仓库类Repository层封装网络请求和本地数据库操作。网络层使用Retrofit2OkHttpGson。Retrofit把RESTful接口直接定义成Kotlin接口方法代码非常清爽。OkHttp的Interceptor在这里有两个用途统一往请求头里加token以及做基础的日志打印方便调试。Gson负责JSON和实体类的转换。如果你在别人的开源项目基础上做的移植经常会遇到Android Studio导入后Gradle版本冲突的问题这时不要盲目升级Gradle先看项目的compileSdk和依赖库版本是否匹配逐步排查比一把梭干净得多。这里我踩过一个坑后端返回的时间字段如果是个“2024-06-01 12:00:00”格式的字符串Gson默认只会解析成Date对象格式化时需自己处理。后来我在Retrofit的Converter上自定义了GsonBuilder设置统一的时间格式App端所有时间字段就都正常了。完整的网络层数据结构包括一个BaseResponse 包装类内部就是code、message、data三个字段所有API返回统一用这个类包装解析逻辑简单清晰。5.2 仓库APP里的必做功能设备扫码模块扫码是仓库App区别于普通进销存软件的核心。硬件上有两种形态手机自带摄像头扫码和外接USB/蓝牙扫码枪。第一种我用的是ZXing开源库把扫到的条码内容作为字符串回传。接入步骤大致是添加依赖、在AndroidManifest申请CAMERA权限、用CameraX展示取景预览、成功回调中拿到code字符串。对于仓库环境还要额外处理条形码上常见的CODE128、EAN13等格式ZXing基本都能覆盖。第二种外接扫码枪在大多数安卓手机上就是模拟键盘输入扫码结果会像打字一样输入到当前焦点控件。因此只需要在焦点落在输入框时接收字符串并以回车符作为结束标志。但这里会出现一个很常见的问题聚焦输入框时手机软键盘会弹出来扫码枪输入一串字符后触发了输入法联想导致结果被拆分或串改这个问题我在第7章详细展开。如果你用的是专用PDA系统一般会提供广播接收扫码结果的能力通过BroadcastReceiver直接拿码值比接收键盘事件更稳定。5.3 Android 7.0以上FileProvider与文件上传的坑标题热词里频繁出现content://com.tencent.wework.fileprovider和content://com.tencent.mobileqq.sharefileprovide这类Uri这说明很多人在做Android文件处理时都在跟FileProvider搏斗。仓库APP里有一个典型场景拍照上传货物破损证据。从Android 7.0开始应用之间不能直接传递file://协议的Uri系统强制要求使用FileProvider生成的content://Uri。要正确处理需要先在AndroidManifest中注册一个FileProvider并在res/xml/file_paths.xml中指定可访问的路径然后调用FileProvider.getUriForFile替换原来的Uri。provider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths / /provider如果不加这一步Intent传入一个file://路径在部分国产手机上会直接崩溃或跳转相机失败。另外如果用Retrofit上传图片请求类型要写成MultipartBody.Part配合RequestBody.create读取文件流这部分也很容易写错。我的做法是统一封装一个UploadUtil传入文件路径和content Uri都能正常上传这样业务层不需要关心底层Uri类型。6. Vue3管理后台的实现要点6.1 工程化搭建Vite Vue3 Element Plus Pinia管理后台我用Vite创建了一个Vue3项目。相比Vue CLIVite冷启动和热更新快非常多开发体验不是一个档次。基础依赖vue-router负责路由Pinia管理用户状态与权限菜单Element Plus做UI组件Axios统一发请求。如果你是参照若依这类开源后台改造需要注意它的Vue3TS版本有时候会因依赖版本升级出现TS类型报错建议锁定package.json中的关键依赖版本不要一上来就全量升级。很多教程在搭建后台时会默认引入SCSS但如果你对样式规模没把握我建议先用普通CSS。Element Plus组件足够覆盖绝大多数布局需求SCSS的嵌套和变量在这里带来的收益很小反而多了一道编译环节。我见过太多项目把依赖库装了一大堆打包出来超过1MB实际用到的不到三分之一这在企业里是会被吐槽的。等确实遇到大型定制样式时再引入也不迟。6.2 权限菜单设计与接口对接后台用户分为管理员、仓管员、经理三个角色权限天然不同。管理员维护基础数据和用户仓管员处理单据审核经理只看报表。我采用动态菜单方案登录成功后后端返回当前用户可访问的路由列表和按钮权限码前端根据权限码动态生成菜单。角色可访问菜单按钮权限管理员基础数据、用户管理、入库出库、盘点、报表增删改查全部功能含库存调整仓管员入库、出库、库存列表、盘点只能操作单据提交和审核经理库存列表、报表只读无编辑入口按钮权限的实现也不复杂在Vue3中用自定义指令或者v-if判断当前用户是否包含某个权限码。举个例子删除库存记录这种高危操作管理员有“stock:delete”权限仓管员没有前端就直接不显示删除按钮。后端接口同样要做校验不能只依赖前端隐藏这是系统安全的基本素养接口层面的越权检查比前端按钮隐藏重要得多。6.3 ECharts报表展示的几个关键配置仓库经理最关心两类报表库存总量趋势和出入库数量对比。我用ECharts实现。折线图展示近30天库存总量变化柱状图展示每日入库/出库对比另外再加一个饼图展示各仓库库存占比。ECharts本身不复杂大多数人写出来效果不好是因为没有处理时间轴数据对齐的问题。要么后端补零返回完整日期要么前端用Map填充缺失的日期否则折线图会出现断点。我推荐后端直接查全量日期并用LEFT JOIN补零把缺口交给SQL处理前端拿到就是一个干净完整的数组。Vue3里使用ECharts的另一个坑是实例销毁。组件在v-if切换时如果没调用dispose方法控制台会报“Cannot read properties of undefined”页面切换几次就会内存溢出。所以在onUnmounted钩子中记录一下实例并释放这个小细节能让后台页面保持长时间运行也稳定。7. 项目上线前后遇到的典型问题与排查实录7.1 时间字段JSON序列化差8小时的坑第一次前后端联调时时间字段总是差了8小时折腾了很久才发现是Jackson序列化的时区问题。Java后端按GMT8存储时间JSON序列化时如果Jackson的时区配置不对默认按UTC输出前端拿到的字符串就比正确时间少了8个小时。解决办法有很多最省事的是在application.yml里强制指定Jackson格式和时区。设置spring.jackson.time-zoneGMT8并把日期格式统一成yyyy-MM-dd HH:mm:ss。另外后端不要使用java.util.Date操作时间统一用LocalDateTime配合Jackson的JavaTimeModule能避免一系列格式化问题。如果Android端和Vue3后台都碰到时间显示的差异还可以在接口层约定统一返回时间戳由前端各自格式化。这样就不依赖Jackson的格式配置了但可读性会差一些调试时不如字符串直观。我个人的经验是内部接口返回格式化字符串给外部对接时用时间戳不同场景不同策略。7.2 高并发场景下的库存超卖问题出库时两个仓管员同时操作同一商品如果代码是“先SELECT库存→判断足够→UPDATE库存”在并发场景下一定会超卖。我实测过一个系统两个请求同时读到库存为10各自扣8、扣5最后库存变成-3账单对不上。解决办法有两个层次。第一层是数据库层直接写条件更新语句UPDATE stock SET quantity quantity - #{num} WHERE product_id #{pid} AND warehouse_id #{wid} AND quantity #{num}让扣减动作原子化受影响行数为0就说明库存不足。第二层是使用version字段做乐观锁UPDATE stock SET quantity #{newQty}, version version 1 WHERE id #{id} AND version #{oldVersion}前者在仓库系统中更高效因为库存扣减本来就是个数学计算不需要每次都回读原值。如果业务要求严格记录每次操作前后的库存快照那就用乐观锁方案。无论哪种都不建议在高并发路径上使用“先查后改”的编程模式。这个问题在期末演示时最容易暴露你只要让两个同学同时扫码出库一次系统立刻就会现出原形。7.3 外接扫码枪输入法串码问题这个问题在真机上遇到过很多次。USB扫码枪模拟键盘直出字符串当输入框获得焦点时Android键盘和扫码枪同时工作扫出来的内容会被输入法的联想词打断比如“ABC123”可能变成“ABC12 3”。我的处理方式是针对扫码输入框设置android:inputTypetextNoSuggestions和imeOptionsactionDone同时监听回车键。如果用的是经典PDA还可以在输入框非聚焦状态下扫码然后把结果通过广播或剪贴板读取。最干脆的方案是给扫码输入框加一个扫码状态开关扫码模式下强制隐藏软键盘。另外扫码枪在输入汉字状态时也容易出问题有些扫码枪原本输入的是英文数字但输入法全角状态下会变成中文标点。所以我的扫码页统一禁止中文输入法介入只保留英文数字键盘。这个细节看起来小实际影响非常大仓库每天都依赖扫码效率一旦串码录单错误率会直接飙升。7.4 数据量大了之后接口变慢的排查思路仓库系统运行半年后出入库明细表可能上百万行。此时分页查询延迟明显上升问题通常出在翻页深和联合索引缺失。排查第一步是看SQL执行计划EXPLAIN SELECT ...确认有没有走索引type是不是range或refExtra有没有Using filesort。翻页深度大的场景比如LIMIT 200000, 20即使有索引也容易慢因为MySQL要扫描并丢弃前20万行。我的调整动作是三条明细表上加入库单号、商品编号、操作时间三个字段的复合索引前端分页由传统“第几页”改成“基于游标”即每次传入上一页最后一个ID用条件大于/小于这个ID来找下一页这样即使上万条数据也能秒开定时归档历史单据到归档表业务查询只读最近三个月的数据需要历史报表时再走专用统计表。这几条做完接口响应时间从原来的2秒降到了200毫秒以内效果非常明显。8. 写在最后的经验总结做完这个仓库管理APP我个人最大的体会是技术选型其实是最简单的一步真正耗时间的是把仓库业务吃透。一开始我总觉得SpringBoot、Vue3这些框架才是项目的核心做到后面发现入库、出库、盘点、库存一致性的设计才是决定系统好坏的关键。给正在做类似项目的朋友一个建议开工前先用两天时间到仓库现场待一待看仓管员怎么干活、单据怎么流转、谁对什么数据负责然后在纸上画出业务流程图和状态流转图再开始设计数据库。数据库稳定了接口和页面都只是时间问题。最后再分享一个小技巧这种多端项目一定要尽早统一接口返回格式和异常码。我在第3章写的code、message、data三层结构虽然简单但极大减少了Android端和Vue3后台的联调成本。你可以在后端增加一个全局异常处理器所有业务异常统一从这里抛出前端只需要判断code是否为0剩下的交给message展示开发效率能提升一大截。这个小项目后续还能扩展的方向很多比如把扫码结果直接对接电子面单运输系统、在Android端加入蓝牙打印小票、用定时任务生成每日库存日报并推送企业微信。如果你正在为毕业设计或者企业内部小工具发愁完全可以从我这个流程里抄一份骨架把业务换成你手头的场景。代码不是最重要的业务才是数据永远是仓库的灵魂写代码的时候记得多给它设几道保险。