首页 / 资讯中心 / 文章详情

基于SSM框架的高校办公耗材易耗品管理信息系统设计与实现

基于SSM框架的高校办公耗材易耗品管理信息系统设计与实现 ★ FEATURED ARTICLE
在学校后勤部门待过的人都知道每个月最难熬的不是发工资那几天而是月底对账。A4打印纸、硒鼓、签字笔、文件夹、订书钉只要台账上多一个错数字月底库存就能变负数Excel在这块的使用基本等于升级版手工。我这次完整实现的基于SSM框架的高校办公耗材易耗品管理信息系统就是从这种混乱里长出来的。它不是那种应付毕业设计的演示Demo而是一套能真正跑起来的Web管理应用——管理员负责耗材分类维护、入库登记、领用审核、数据统计普通教职工在网页上提交领用申请、查看实时库存、追踪自己的领用记录库存一旦低于预警阈值系统会自动标记并提醒补货。对正在选毕业设计题目的同学或者被纸质台账折腾的行政老师来说这个项目的需求拆解、表结构设计、SSM整合实现和踩坑记录我认为都有一定的参考价值。1. 低值易耗品为什么在高校成了账外资产——项目背景与痛点拆解1.1 固定资产有人管耗材没人管高校对固定资产的管理其实相当严格单价高、使用年限长、需要定期盘点的资产要贴标签、建台账、逐级上报资产信息。但低值易耗品正好卡在中间——单价低、寿命短、消耗频繁够不上固定资产的标准却又实实在在占着预算。打印纸、硒鼓、订书钉、墨水、便利贴这些物品单看某一样不贵可一所高校几十个部门一年累加起来金额相当可观。更麻烦的是固定资产信息管理系统一般按资产卡片管理适合登记那些长期不变的设备而耗材进出频繁、型号杂、批次多按卡片管理反而累赘。于是这部分物资长期游离在系统之外靠什么管纸质的领用本、口头沟通、微信群接龙。库存是不是准完全取决于填表人记不记得写。我的客户里就有后勤老师吐槽期末盘库的时候能对上的只有门锁和货架。做这套系统之前我还特意去翻了几个院系的耗材台账发现表格样式五花八门有的记日期不记数量有的只写领取打印纸一包但没写型号。数据不规范后面想做分析完全是空中楼阁。所以项目第一件事不是写代码而是先把账实相符这个目标定义清楚每一件耗材的入库、出库、报损都要留下结构化记录任何时间点都能回答现在还剩多少、谁领走多少、还剩在哪个位置。1.2 三个让后勤崩溃的现实矛盾第一台账和实物对不上。手工签字领用写错日期、写错数量、事后涂改都很难追溯。第二消耗数据散落各处。各院系自己记一份后勤总库记一份学期末汇总靠人肉翻记录统计口径还不一样。第三采购计划拍脑袋。缺什么才买什么加急单价格高备货多了占用库房空间和经费备货少了又影响教学办公。这套低值易耗品管理系统要解决的就是这三件事用电子流程替代纸质签字用统一数据表强制规范字段用消耗统计反推采购计划。我印象很深的是刚开始演示原型时后勤老师最关心的不是界面好不好看而是能不能按部门汇总领用数量——因为年底每个部门要分摊耗材经费这个表他们手工做了一周。系统里其实就是一条带条件的SQL这是个很典型的场景。1.3 用户角色与核心使用场景整个系统的角色很简单就两类管理员和普通用户。管理员通常是后勤、资产处的老师负责耗材分类维护、耗材信息录入、入库登记、领用申请审核、报损处理和统计导出普通用户则是各院系、机关部门的教职工可以浏览耗材列表、查询当前库存、提交领用申请、查看本部门的领用历史。核心使用场景可以这样描述某学院要主办学术会议需要一次性领用A4纸5箱、硒鼓2个。以前要先发邮件问库房有没有货再拿着纸质审批单跑后勤盖章来回折腾大半天。现在教职工在系统里提交申请管理员看到待办后审核通过后库存自动扣减申请记录全程留痕。遇到打印纸库存低于预警线时系统在首页醒目标记管理员直接据此发起采购。整个过程清晰、可追溯这就是SSM框架下管理系统的典型业务闭环。2. 从业务流程到数据库表结构系统设计的关键决策2.1 为什么依然选SSM而不是更复杂的框架这套系统面向高校内部、用户量在几百到几千人之间、并发量不高SSM是性价比很高的选择。Spring负责依赖注入和事务管理SpringMVC处理请求路由和参数绑定MyBatis承担数据持久化三层职责分明。相比Spring Boot全家桶SSM配置多一些但能让你看清框架之间的协作关系这也是为什么很多课程设计和毕业设计仍然首选SSM。模块划分上我按标准的三层结构组织。表现层controller接收前端请求参数校验后转给service业务层处理入库、出库、审批流转这些核心逻辑事务边界放在这一层persistence层的Mapper接口只跟SQL打交道。项目包结构大体如下com.example.system ├── controller // 登录、耗材、入库、申请、统计接口 ├── service // 业务逻辑与事务控制 ├── dao // MyBatis Mapper接口 ├── pojo // 实体类、DTO、VO └── common // 常量、异常、分页工具等2.2 核心表结构与数据关系数据库我设计了8张表没有过度拆表够用但要留出扩展余地。部门表department存院系和职能处室用户表sys_user关联部门用role字段区分管理员和普通用户耗材分类表category维护大类比如纸张、硒鼓、笔类耗材信息表material存名称、规格、单位、单价、当前库存和预警阈值。入库明细表inbound_record和领用申请表apply_record各记一条流水。报损记录waste_record用于报废破损耗材。整体关系是分类一对多耗材耗材一对多入库记录耗材一对多申请记录。设计时我比较坚持的一点是耗材名称、部门名称这些字段全部用ID外键关联不直接存字符串。原因很简单如果直接在表里写信息工程学院后面改名信息与工程学院所有历史数据都得跟着改。用外键只需改department表一行数据。真实开发里这种名称冗余导致返工的案例我见过太多了。2.3 字段类型层面的三个经验第一个经验库存量用decimal(10,2)而不用int。你可能觉得耗材都是按包个计的整数就够了但实际很多耗材按重量或面积消耗比如热敏纸按公斤、条幅布按米预留两位小数能避免将来改表。第二个经验金额字段一律decimal(10,2)绝不用float或double浮点数在累加时会丢精度月底对账差一分钱都让人崩溃。第三个经验状态字段用int数字加常量类而不是直接存varchar。比如申请状态用0待审核、1已通过、2已驳回代码里定义常量引用。存中文字符串看着方便但扩展新状态时改起来很麻烦而且索引效率也不如int。关于时间字段MySQL端的datetime配合Java端的java.util.Date用没问题但要注意时区。开发机在本地数据库可能在云服务器连接串里不写serverTimezoneAsia/Shanghai的话入库时间会差8小时排查起来很隐蔽。3. 核心链路实现入库登记、领用审核与库存预警3.1 入库登记的事务控制入库是整个系统最基础的业务动作核心逻辑是耗材的当前库存要增加同时写入一条入库明细。两件事必须同时成功不能出现库存加了但明细没记也不能出现明细记了但库存没变。这种场景用Spring的Transactional再合适不过。我建议库存增加直接写成原子更新SQL而不是先查再改Transactional(rollbackFor Exception.class) public void inStorage(InboundAddDTO dto) { materialMapper.increaseStock(dto.getMaterialId(), dto.getQuantity()); inboundRecordMapper.insert(dto); }对应的Mapper SQLUPDATE material SET stock stock #{quantity} WHERE id #{id}这种方式连查询库存都不需要直接做增量变更天然避免并发下的丢失更新。有人习惯先SELECT stock再UPDATE stock newStock在低并发下没问题一旦两个管理员同时操作后写入的值会覆盖先写入的值形成数据错误。第4节我会详细讲这个并发问题。事务的作用就是兜底如果插入入库明细失败库存更新也会一起回滚账和物始终一致。3.2 领用申请的审批流与库存扣减领用申请的状态机是提交申请为待审核管理员点通过后变为已通过并扣减库存点驳回后变为已驳回并写明原因。有个设计决策值得说道申请提交时不做预扣库存审批通过时才扣。因为高校内部使用场景频率不高预扣库存虽然能提前锁定货品但会带来僵尸申请问题——申请挂着不批库存锁着别人领不了还得额外做超时释放逻辑。低频场景里不如提交时不扣审批时校验并扣减来得清爽。关键是审批通过时的库存扣减我用一条带条件的SQL原子完成int rows materialMapper.deductStock(apply.getMaterialId(), apply.getQuantity()); if (rows 0) { throw new BizException(库存不足本次审核驳回); } applyMapper.updateStatus(apply.getId(), ApplyStatus.PASSED, adminId);UPDATE material SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}这条SQL的意思是只有满足库存足够这个条件时才会真正扣减否则影响行数为0。程序通过受影响行数判断是否成功比先查后改严谨得多。而且审核通过后还要更新申请状态这两个操作同样放在一个事务里保证不会出现库存扣了但状态还是待审核的中间状态。驳回的逻辑更简单不改库存只更新状态和审批意见。3.3 库存预警与首页数据统计库存预警是后勤老师评价最高的功能。实现思路不复杂耗材表里有个warn_threshold预警阈值字段比如A4纸设置50包低于50包就提示采购。查询时一条SQL就能找出所有低于阈值的耗材SELECT * FROM material WHERE stock warn_threshold ORDER BY stock / warn_threshold ASC排序条件用stock / warn_threshold相当于按库存健康度从低到高排列越紧缺的越靠前。系统首页会加载这批预警数据用醒目的样式标红同时也可以用Spring自带的Scheduled定时任务每天上午九点扫描一次预警数据生成站内提醒管理员登录就能看到。统计报表这块我做了两个接口一个是按耗材分类汇总领用数量用于年度经费分摊另一个是按月份统计各部门领用趋势前端用ECharts画折线图。后端逻辑就是按时间范围和分组条件做聚合比如统计近六个月各部门领用A4纸总量SELECT d.dept_name AS deptName, MONTH(a.create_time) AS monthVal, SUM(a.quantity) AS totalQuantity FROM apply_record a JOIN sys_user u ON a.user_id u.id JOIN department d ON u.dept_id d.id JOIN material m ON a.material_id m.id WHERE m.material_name A4打印纸 AND a.create_time DATE_SUB(NOW(), INTERVAL 6 MONTH) AND a.status 1 GROUP BY d.id, MONTH(a.create_time) ORDER BY monthVal, totalQuantity DESC这类聚合语句刚写的时候容易忽略一个点记得过滤掉未通过的申请否则被驳回的数量也统计进去数据会虚高。我在测试阶段就因为这个原因出现过统计异常查了半天才发现是状态过滤条件没加。4. 开发过程中的重量级坑并发超发、MyBatis细节与Excel导入4.1 库存超发问题完整排查链路临近学期末各院系集中领用耗材两个管理员同时在审核页点通过按钮结果系统里的订书钉库存直接变成负数。这大概是我在这类系统里遇到最多的故障。问题本质是先查再改这个动作不是原子的。假设库存还剩10盒订书钉管理员A读到10管理员B也读到10A扣1改为9B扣1也改为9两次扣减只生效一次。如果两边同时扣5库存甚至变成0但实际应该卖出2份才剩余0。单靠事务隔离级别解决不了这个问题因为两个连接各自读到一致的快照数据库并不认为是冲突。排查思路建议用JMeter模拟并发复现一个耗材库存设为10开五个线程同时扣减2预期剩0实际出现数量大于0就能快速定位。解决方式上文提过把读改写换成UPDATE material SET stock stock - 2 WHERE id 1 AND stock 2数据库行锁保证同一行只有一个事务能改成功其余因条件不满足返回影响行数0。应用层加synchronized能不能解决单机部署时可以但系统一旦集群部署到多台Tomcatsynchronized锁不住其他节点。数据库条件更新是最省事、最可靠的选择。对高校内部这种业务场景没必要引入Redis分布式锁过度设计只会增加运维负担。4.2 MyBatis的#{}与${}选择MyBatis的#{}是预编译占位符会生成?再绑定参数能有效防止SQL注入这是绝大多数场景下的正确选择。${}则是字符串直接拼接有注入风险但少数场景必须用它比如动态排序字段ORDER BY ${sortColumn}。遇到这种情况我会在Service层做白名单校验只允许传入白名单里的字段名防止恶意拼接。动态SQL是MyBatis的核心利器但用不好也会埋雷。最典型的是更新语句不带set或者if条件全不满足导致整表数据被覆盖。比如批量修改耗材信息时条件没有拼上一条SQL把几百条记录的库存全改成了同一个值这种事故影响面很大。我的习惯是所有update语句写完先检查是否有set块、是否有至少一个字段会参与更新必要时在Mapper里用where包裹条件。另外几个容易踩的点一是数据库字段是下划线风格create_time需要在MyBatis配置里开启mapUnderscoreToCamelCasetrue不然查出来的create_time映射不到Java实体的createTime字段上二是foreach做批量插入时SQL长度不能超过数据库max_allowed_packet限制数据多时分批执行更稳妥三是不小心在xml里写了大于号、小于号没转义也会报语法错误要习惯用lt;、gt;。4.3 Excel批量导入耗材基础数据系统上线前需要把老台账里的几千条耗材数据录入系统一条条手动insert不现实。我的做法是用Apache POI解析Excel模板一次性读入内存批量写入数据库。踩坑主要集中在三个方面。第一个坑是日期解析Excel里的日期本质是数字序列直接用简单方式取单元格会得到一串数字而不是2024-03-01需要用POI的DateUtil.getJavaDate()转换。第二个坑是空行和合并单元格解析时必须判断当前行是否全空否则会把一堆null插进去。第三个坑是中文文件名乱码配置文件上传解析器时要显式指定编码同时Tomcat连接器的URIEncoding也要设为UTF-8。文件上传还需要注意大小限制。SpringMVC的CommonsMultipartResolver默认允许上传的文件大小比较小大概1MB左右Excel数据量大时很容易超限。我的配置是bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namedefaultEncoding valueUTF-8/ property namemaxUploadSize value5242880/ property namemaxUploadSizePerFile value2097152/ /bean为了提高导入的容错性我在前端做了模板下载功能并让后端解析时逐行校验必填字段出错则返回错误行号和原因比如第15行耗材名称为空、第17行单位未填写。行政老师照着提示修正后再上传体验比一次失败全部重来好很多。5. 从本地调试到学校服务器部署环境问题与扩展方向5.1 处理源发行版17需要目标发行版17这一类环境问题本地跑得好好的项目拿到新电脑一编译就报错提示源发行版17需要目标发行版17这大约是SSM项目最常见的第一道坎。本质是Maven的pom.xml里配置的maven.compiler.source和maven.compiler.target跟你当前电脑装的JDK版本不一致。比如用的是JDK 17但项目语法和依赖设计是按JDK 1.8写的编译等级错位就会报这种提示。我的建议是SSM项目直接固定JDK 1.8不是17不够好而是SSM生态里的老依赖对高版本JDK兼容性参差不齐没必要给自己找麻烦。pom.xml中做如下配置properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties同时保证IDE里Project Structure的Java SDK、Maven实际使用的JDK、系统环境变量里的JAVA_HOME三处统一这个坑就绕过去了。很多同学在IDE里改了Project Structure没改Maven的runtime照样编译报错。5.2 MySQL连接URL的参数细节数据库连接串是另一个容易出诡异问题的地方。长得像这样的URL是比较稳妥的jdbc:mysql://localhost:3306/office_supply_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrueuseUnicodetruecharacterEncodingutf8解决中文乱码serverTimezoneAsia/Shanghai解决时间差8小时问题useSSLfalse是避免本地连接时出现SSL警告导致连接缓慢。MySQL驱动版本也别用太老的MySQL 5.7用5.1.49或8.0.33均可8.0版本数据库建议配合驱动8.0.x使用老驱动连新版数据库会提示认证插件不兼容。5.3 上线部署与后续扩展部署方式不复杂Maven打war包放到Tomcat的webapps目录启动即可。服务器上建议先检查端口冲突和磁盘空间然后做一次基础验证登录、入库、申请、出库、统计逐个过一遍。数据库备份我会挂一个每天凌晨的crontab任务用mysqldump导出保留最近30天备份防止误操作把数据清掉。这套系统后续有几个很自然的扩展方向。第一是对接学校统一身份认证用CAS或OAuth2替换自带的账号密码登录免去多系统密码记忆负担。第二是增加微信小程序或企业微信端审批人在手机上就能处理领用申请响应速度更快。第三是给耗材生成条形码或二维码出入库时扫码操作减少手工选择耗材的时间。第四是跟学校财务系统打通领用记录直接生成部门经费分摊数据进一步减少手工统计。我个人还有一个想法等历史数据多了之后可以给每个部门的耗材领用做趋势预测辅助采购计划。比如某学院每年3月、9月都有大量打印耗材消耗系统提前一个月推送采购建议经费使用就更合理了。最后说点实际体会。这套系统上线后最受欢迎的不是复杂的统计报表而是库存预警和申请留痕两个功能。库存预警让后勤老师不用每天扒拉Excel看库存申请留痕让领用环节的扯皮明显减少——谁领了什么、什么时间领的、哪个领导批的系统里记录得清清楚楚。做这类管理系统别一上来就堆复杂功能先把账实相符这条主线做扎实后面加什么功能都好说。如果让我给后来人一个建议把每一个涉及库存变化的操作都当成事务来设计把每一次扣减都改成条件更新你就能省掉后期一大半排查问题的精力。
阅读完成 · 觉得有帮助?
咨询建站