
简介基于Vue和SpringBoot构建的资产出入库管理系统面向中小企业管理员、仓管员与员工覆盖资产类型维护、资产入库、领用出库及出库审核四大核心业务通过唯一标识对各作业环节进行快速批量数据采集帮助企业及时掌握库存真实数据同时集成员工管理、组织机构、权限管理与图表分析也可作为计算机相关专业的毕业设计或课程项目。压缩包共364个文件以169个java后端代码、103个vue前端页面及js、less、xml、yml等配置为主附带sql数据库脚本整体仅1.46MB目录结构清晰便于直接导入运行。已有700人学习浏览。项目源码完整且配套数据库下载后无需修改即可运行适合需要实战的Java学习者快速掌握前后端分离开发、权限设计与资产流转流程也可直接用于毕设答辩演示或二次扩展开发。1. 为什么一套出入库系统要先管住“不出错”行政资产管理最容易出现的不是“没有记录”而是“记录对不上账”。入库单签了字出库却只在 Excel 里删了一行等到年度盘点才发现账面还剩 20 台显示器仓库里实际只剩 5 台。这套基于 Vue 和 SpringBoot 的资产出入库管理系统核心思路是把“资产档案”作为唯一主线入库建档、领用出库、主管审核、台账变更全部围绕资产编码展开每一步都产生流水记录。系统面向中小企业行政办公场景管理员维护资产类型仓管员负责出入库录入员工提交领用申请主管出库审核四类角色对应四条核心业务流程。对正在做毕业设计或需要 Java 项目实战的开发者来说它把“一个前后端分离管理系统”从建表、接口、页面到权限串完整适合作为课程设计和期末大作业的基线工程。2. 盘点表结构与状态机后端的数据骨架2.1 核心表拆解从资产档案到出入库流水这套系统的业务链条可以用三个核心表串起来资产类型表、资产档案表、库存流水表。资产类型表先定义“有哪些类资产”比如笔记本电脑、投影仪、办公桌椅资产档案表记录“每一件具体资产”一台电脑对应一条档案靠资产编码唯一标识库存流水表记录“这台资产什么时候入库、什么时候被谁领走、审核状态如何”。三个表层层递进和实际盘点时“先分类、再清点、最后对账”的动作完全对应。权限侧还涉及员工表和组织机构表。员工表挂部门和角色出库审核时需要确认申请人是否有权领用审核人会看到申请人所属部门等信息。文件管理和图表分析则是附属功能前者存出入库单据附件后者从库存流水表聚合出“月度入库量”“部门领用排行”等指标数据来源仍然是流水表。2.2 建表 SQL让唯一标识与审核状态落在数据库以出库审核链路为例最小可运行的表结构大约是这样的CREATE TABLE asset_type ( id BIGINT AUTO_INCREMENT PRIMARY KEY, type_name VARCHAR(64) NOT NULL COMMENT 资产类型名称, parent_id BIGINT DEFAULT 0 COMMENT 父类型ID0为顶级, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 资产类型表; CREATE TABLE asset_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, asset_code VARCHAR(32) NOT NULL UNIQUE COMMENT 资产唯一编码, asset_name VARCHAR(128) NOT NULL COMMENT 资产名称, type_id BIGINT NOT NULL COMMENT 资产类型ID, spec VARCHAR(255) DEFAULT NULL COMMENT 规格型号, location VARCHAR(128) DEFAULT NULL COMMENT 存放位置, status TINYINT DEFAULT 1 COMMENT 1在库 2已出库 3报废, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_type_id (type_id) ) COMMENT 资产档案表; CREATE TABLE stock_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, asset_code VARCHAR(32) NOT NULL COMMENT 关联资产编码, biz_type TINYINT NOT NULL COMMENT 1入库 2领用出库, quantity INT NOT NULL DEFAULT 1 COMMENT 数量, apply_user VARCHAR(64) DEFAULT NULL COMMENT 领用人, apply_dept VARCHAR(64) DEFAULT NULL COMMENT 领用部门, status TINYINT DEFAULT 0 COMMENT 0待审核 1已通过 2已驳回, audit_user VARCHAR(64) DEFAULT NULL COMMENT 审核人, audit_time DATETIME DEFAULT NULL COMMENT 审核时间, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_asset_code (asset_code), KEY idx_biz_type (biz_type) ) COMMENT 出入库流水表;asset_info 表的 asset_code 使用唯一索引这是整套系统的关键约束。出库时业务代码通过 asset_code 定位资产流水表 stock_record 同样用 asset_code 关联两边口径一致不会出现“档案是档案、流水是流水”的脱节。stock_record 里的 biz_type 区分入和出status 记录审核状态后续主管审核时只需要更新这个字段不需要回写资产表属于典型的流水账设计。2.3 状态流转约束出库状态不能从“待审核”直接跳到“已出库”中间必须经过审核人动作。通常在 Service 层定义枚举来限制流转当前状态操作目标状态说明待审核审核通过已通过库存扣减资产状态改为已出库待审核审核驳回已驳回库存不变资产仍保留在库已通过取消出库可选已驳回库存回补需要写反向流水这里的设计意图是“审核通过时才真正改变库存”待审核期间资产仍然在库。业务上这更接近真实场景员工提交领用申请后主管没批准之前资产不能从仓库物理离开。若没有这道状态约束就会出现“明明没审核资产已经出库”的账实不符。3. 基于 Spring Boot 的出入库审核链路实现3.1 分层结构与 MyBatis-Plus 的映射关系从文件预览里能看到 mpController、entity、mpServiceImpl 这类命名基本判断后端采用了 MyBatis-Plus 的 Mapper 三层结构Controller 接收请求Service 处理业务Mapper 做数据库交互。实体类用TableName注解映射表名Service 继承 MyBatis-Plus 的IService接口这是 Spring Boot 管理类项目最常见的组合。控制层写法和 Spring MVC 标准写法一致关键在 Service 层如何把“入库、出库、审核”串成完整事务。入库先生成资产编码再插入资产档案最后写一条 biz_type1 的流水出库先校验资产状态必须是“在库”再生成出库申请流水状态置为待审核审核通过时才把 asset_info.status 改为已出库并更新流水状态。3.2 出库审核核心方法状态校验与事务边界以下是一个简化但可直接套用的 Service 实现Service public class StockRecordServiceImpl extends ServiceImplStockRecordMapper, StockRecord implements StockRecordService { Autowired private AssetInfoService assetInfoService; Override Transactional(rollbackFor Exception.class) public boolean auditOutbound(Long recordId, Integer auditResult, String auditUser) { StockRecord record this.getById(recordId); // 逻辑一审核状态校验 if (record null || record.getStatus() ! 0) { throw new RuntimeException(记录不存在或已审核); } if (record.getBizType() ! 2) { throw new RuntimeException(非出库单不允许审核); } // 逻辑二审核通过时才改资产状态 if (auditResult 1) { AssetInfo asset assetInfoService.lambdaQuery() .eq(AssetInfo::getAssetCode, record.getAssetCode()) .one(); if (asset null || asset.getStatus() ! 1) { throw new RuntimeException(资产不存在或不在库); } AssetInfo update new AssetInfo(); update.setId(asset.getId()); update.setStatus(2); assetInfoService.updateById(update); } // 逻辑三更新流水审核状态 StockRecord update new StockRecord(); update.setId(recordId); update.setStatus(auditResult 1 ? 1 : 2); update.setAuditUser(auditUser); update.setAuditTime(new Date()); return this.updateById(update); } }方法上标注Transactional保证“改资产状态”和“更新审核状态”要么都成功要么都回滚。逻辑一先查状态避免重复审核同一张单子逻辑二判断资产是否在库防止已出库资产被二次领用逻辑三把审核结果写回流水的 status 字段。这里的入参 recordId 是流水表主键auditResult 传 1 或 2对应通过和驳回由 Controller 层从请求体取出。3.3 库存扣减的防重复方案审核通过时改了资产状态但如果同一时刻两个管理员同时审核同一张出库单就会产生竞态问题。常见做法有两种一是给 stock_record 表的 status 字段加乐观锁版本号更新时where status 0条件命中才生效二是直接使用数据库的行锁在事务里先SELECT ... FOR UPDATE锁定记录。对课程设计来说第一种更容易实现MyBatis-Plus 的Version注解配一个 version 字段即可。生产环境更建议两者结合先按主键锁行再更新状态。4. Vue 前端与出库表单的数据链路4.1 路由与页面结构前端 Vue 页面按角色拆视图管理员进入资产管理、类型维护、员工管理、图表分析仓管员进入入库登记、出库审核员工进入领用申请。路由配置需要处理参数传递比如编辑资产档案时把资产编码通过路由参数传给编辑页const router new VueRouter({ routes: [ { path: /asset/list, component: AssetList }, { path: /asset/add, component: AssetEdit }, { path: /asset/edit/:assetCode, component: AssetEdit, props: true }, { path: /stock/outbound/apply, component: OutboundApply }, { path: /stock/outbound/audit, component: OutboundAudit }, { path: /dashboard/analysis, component: AnalysisChart } ] });props: true让 vue-router 把路由参数assetCode直接作为组件的 prop 传入避免在组件内部写this.$route.params.assetCode取值的零散逻辑。编辑页既承担新增也承担编辑通过this.assetCode是否存在判断当前是新增还是编辑这是 Vue 管理系统中常见的单页双用写法。4.2 领用出库表单与数量校验领用出库页面需要先选择资产类型再加载该类型下的具体资产最后填写领用人和部门。前端拿到资产列表后要做一道状态过滤只显示“在库”的资产template el-form refoutboundForm :modelform :rulesrules label-width90px el-form-item label资产类型 proptypeId el-select v-modelform.typeId changeloadAssetsByType el-option v-fort in typeList :keyt.id :labelt.typeName :valuet.id / /el-select /el-form-item el-form-item label资产 propassetCode el-select v-modelform.assetCode placeholder仅显示在库资产 el-option v-fora in availableAssets :keya.assetCode :labela.assetName a.assetCode :valuea.assetCode / /el-select /el-form-item el-form-item label领用人 propapplyUser el-input v-modelform.applyUser / /el-form-item el-form-item el-button typeprimary clicksubmitApply提交申请/el-button /el-form-item /el-form /template script export default { data() { return { availableAssets: [], form: { typeId: undefined, assetCode: , applyUser: }, rules: { typeId: [{ required: true, message: 请选择资产类型, trigger: change }], assetCode: [{ required: true, message: 请选择资产, trigger: change }], applyUser: [{ required: true, message: 请填写领用人, trigger: blur }] } }; }, methods: { async loadAssetsByType(typeId) { const res await this.$http.get(/asset/list, { params: { typeId, status: 1 } }); this.availableAssets res.data; }, submitApply() { this.$refs.outboundForm.validate(async valid { if (!valid) return; await this.$http.post(/stock/outbound/apply, { ...this.form, applyDept: this.$store.state.user.deptName }); this.$message.success(提交成功等待审核); this.$refs.outboundForm.resetFields(); }); } } }; /scriptloadAssetsByType 方法在切换资产类型时触发请求后端接口时带status: 1条件服务端只返回在库资产前端同时自然过滤。这样就算有人篡改前端提交数据后端审核方法里仍然有资产状态校验双重保障。validate是 Element UI 表单的兜底校验三项必填不满足时不会走提交逻辑。提交成功后重置表单但类型下拉框保留方便下一次连续录入。4.3 图表分析模块图表分析的核心是把库存流水按时间聚合。后端提供一个统计接口返回“近 12 个月每月入库和出库数量”和“各部门领用占比”前端用 ECharts 渲染柱状图和饼图。接口返回结构固定为[{ month: 2025-01, inCount: 12, outCount: 8 }]的格式前端不需要处理数据重组直接绑定到 series 即可。5. 本地部署运行与高频排错5.1 环境准备运行这套系统需要 JDK 1.8 以上、Node.js 14 以上、MySQL 5.7 及以上版本。前端使用 npm 安装依赖首次安装时建议使用国内镜像源后端使用 IDEA 打开 Maven 工程等待依赖下载完成。数据库导入根目录下的 SQL 脚本会同时创建库和表结构。5.2 启动步骤以 IDEA 加命令行混合操作方式为标准流程# 步骤一导入数据库脚本项目根目录 asset.sql mysql -u root -p asset.sql # 步骤二启动后端服务IDEA 中直接运行 Application 主类 # 或使用 Maven 命令行 mvn spring-boot:run # 步骤三启动前端开发服务项目 frontend 目录下,需要先 npm install npm install npm run serve后端默认端口常见配置为 8080前端开发服务器运行在 8080 端口在 vue.config.js 里配了 devServer.proxy 把/api前缀转发到 8080 端口。正常情况浏览器访问http://localhost:8080进入登录页。5.3 三个高频报错和处理方式第一个是数据库时区问题。MySQL 8.x 连接串没配serverTimezone时启动后访问接口会报The server time zone value异常。在 application.yml 的数据源 URL 末尾加?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8即可解决。第二个是前端访问后端接口跨域。Spring Boot 2.4 以上版本里allowCredentials(true)和allowedOrigins(*)不能同时使用需要换allowedOriginPatterns(*)。如果用的是旧版 Spring Boot则保持allowedOrigins不变。第三个是刷新页面后 404。前端使用 history 模式路由后刷新非首页路由时后端返回 404页面白屏。需要后端起一个 fallback 转发把未匹配的请求转到 index.html实现一个WebMvcConfigureraddViewControllers 里注册registry.addViewController(/{path:[^\\.]*}).setViewName(forward:/index.html)。6. 进阶给资产加上二维码标识做移动端盘点把资产编码升级成二维码是这套系统从“能用”到“好用”的关键一步。后端引入 zxing 依赖提供一个生成二维码的接口把 asset_code 编码成二维码图片返回管理员打印后贴在资产上盘点时手机扫码直接调接口查看资产档案。GetMapping(/asset/qrcode/{assetCode}) public void generateQrCode(PathVariable String assetCode, HttpServletResponse response) throws Exception { String content http://localhost:8080/asset/detail/ assetCode; int width 300, height 300; MapEncodeHintType, Object hints new HashMap(); hints.put(EncodeHintType.CHARACTER_SET, UTF-8); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); // 输出 PNG 图片到 response 输出流 MatrixToImageWriter.writeToStream(bitMatrix, PNG, response.getOutputStream()); }二维码内容里放了资产详情页地址扫码后能在手机浏览器看到该资产的入出库历史现场盘点时直接核对实物标签和系统状态。再配合批量盘点接口接收一组 assetCode 列表服务端批量核对资产状态在库的返回正常现实是已出库却仍扫到在库记录的资产列表会标记为异常。这一步把资产台账和物理世界的绑定关系做扎实也让整套出入库逻辑从毕设层面提升到可以执行真实年度盘点的状态。本文还有配套的精品资源点击获取