校园跑腿系统毕业设计全流程复盘:SSM+Flask混合架构实践
2026/9/14 7:12:50 网站建设 项目流程

毕业设计做校园跑腿系统,从选题到答辩全流程复盘

每年的毕业季都会有不少人选择“校园跑腿管理系统”这类题目,原因很简单:业务场景贴近学生生活,技术栈成熟可控,功能模块容易展开。我当初选这个题时,身边还有人说“跑腿不就一个下单接单吗,有什么好做的”,但真正把需求掰开揉碎之后会发现,里面涉及的订单状态机、并发抢单、权限分级、混合框架协作,任何一个点都够你写好几千字。这篇文章就结合我做这套基于Java+SSM+Flask校园跑腿管理系统的完整过程,从项目设计到具体实现,再到调试部署和常见坑位,一条线讲清楚。无论你是正在选毕设题目,还是已经开工卡在某个环节,希望能给你一些能直接落地的参考。

1. 项目整体设计与技术选型思路

1.1 跑腿系统的核心需求拆解

校园跑腿系统,表面上看起来就是一个“人找人办事”的信息撮合平台。但从软件工程的角度去拆,它至少要覆盖四条完整链路:用户下单与支付链路、跑腿员接单与配送链路、订单全生命周期管理链路、平台运营方的审核与统计链路。

先说说用户侧。学生用户是典型的C端角色,他们要能注册登录、发布跑腿需求(代取快递、代买饭、代打印、代办各种校园事务)、查看订单进度、确认收货、评价跑腿员。这些需求听起来简单,但每一个环节都有隐含条件。比如代取快递,用户可能只知道快递到了哪个驿站,但不知道驿站的具体取件码,那就需要在下单时留备注或上传截图;比如代买饭,用户对店铺、口味、预算可能都有要求,系统不能只填一个“帮我买饭”就算完事。

再说跑腿员侧。跑腿员本质上是平台服务的供给方,他们的核心诉求是“能方便地找到单子、清晰地知道怎么干、准时拿到报酬”。所以系统需要提供抢单大厅或派单列表、订单详情中的取件码和地址信息、送达后的确认机制、以及个人收益流水。这里还有一个容易被忽视的点:跑腿员的接单资格审核。虽然这是个课程设计级别的系统,但只要有实名认证、学生证上传、管理员审核这几个步骤,系统的完整性就会上一个档次。

管理员侧的核心需求是用户管理、跑腿员审核、订单监管、分类管理、基础参数配置。这部分用传统的管理后台去实现,表格加搜索加状态筛选,经典的SSM三板斧正好派上用场。

平台运营层面的需求还包括数据看板,比如每天订单数量、完成率、活跃用户数。这些统计功能在后面会被当成“亮点”写进论文里,实际上就是SQL聚合加简单图表,但确实能体现你对需求理解足够全面。

1.2 为什么用SSM加Flask的混合架构

很多同学看到“Java+SSM+Flask”这个技术组合时会觉得奇怪:既然已经有SSM了,为什么还要搭一个Flask?这就要说回实际开发时的权衡了。

我当时选SSM作为主体框架,最直接的原因是这个框架在高校毕业设计中几乎是“标准答案”。Spring负责Bean管理和事务控制,SpringMVC负责请求分发和参数绑定,MyBatis负责数据库操作。这套组合经过了无数项目的验证,资料多、问题好查、答辩的时候老师也认可。尤其在订单这类需要事务保障的业务里,Spring的声明式事务用起来非常顺手,一个@Transactional注解就能搞定回滚逻辑。

但SSM也有它不那么顺手的场景。跑腿系统里有两类功能用Python做效率要高得多:一类是快递地址文本的解析和相似度匹配,另一类是配送距离的估算和智能推荐。当然这些功能用Java一样能做,但Flask的生态里有现成的库可以快速实现原型,代码量小,迭代也快。我当时的做法是把Flask作为一个轻量级的辅助服务,主要承担两个职责:一是提供距离计算和跑腿员推荐接口,二是做定时任务和通知推送的调度端。

这种“Java主业务处理加Python辅助服务”的混合架构,看起来像是在炫技,但实际是有道理的。我们做一个系统,不是把所有代码都堆在一个工程里就叫好,而是要让每个技术组件出现在它最擅长的地方。Java负责强类型的业务建模和事务控制,Python负责灵活的数据计算和算法逻辑,两者通过HTTP接口进行通信。后续如果你有兴趣把推荐算法换成更复杂的模型,Python端也可以平滑升级,不影响主业务,这也是微服务思想在毕设层面的一种简化落地。

1.3 项目目录结构与开发环境规划

提前把工程拆分成清晰的结构很重要,尤其是写到后面你会有几十个Java文件和一整套Flask脚本,如果一开始目录混乱,后面每改一个功能都是噩梦。

我当时在IDEA里创建的是Maven工程,包结构大致是这样:com.campus.runner下面按照controller/service/mapper/entity分包,这是SSM项目的经典分层。controller层不写业务逻辑,只做参数接收和结果封装;service层放业务规则,比如下单时的库存校验、余额校验;mapper层对应MyBatis的接口和XML映射文件。

Flask端单独放在一个scheduler_server目录下,和Java工程并列,不混在一起。里面有main.py作为入口,还有api/目录放接口逻辑,jobs/目录放定时任务,utils/里是距离计算等工具函数。数据库脚本单独放在sql目录里,包括建库语句、初始化数据、以及每次改动后的增量脚本。文档方面我会在项目根目录放一个README.md写启动步骤,然后在docs里放答辩用的架构说明和功能清单。

开发环境没有太特殊的:JDK 8加Tomcat 8.5加MySQL 5.7,这套组合最稳,千万别用太高版本给自己添堵。Python端用的是Python 3.8加Flask 2.0,用虚拟环境管理依赖。Redis用来做抢单时的缓存和锁,这是整个系统并发性能的关键,后面会详细说。

2. 数据库设计:表结构、订单状态与核心字段详解

2.1 核心数据表与关联关系设计

数据库是一个管理系统的地基,我前后大概改了四版才最终定稿。刚开始想得比较简单,觉得只要把用户表、订单表、跑腿员表建出来就行,但画ER图的时候才发现,光是一个“订单”表,就要跟用户、跑腿员、地址、分类、支付流水、评价等多个实体产生关联。

最终的核心表设计是这样的:用户表t_user存放学生用户数据,含账号、密码、手机号、学号、余额字段;跑腿员表t_runner独立于用户表,因为一个用户可以根据需要申请成为跑腿员,但跑腿员有额外的审核状态、接单数量限制、评分等属性;分类表t_category定义跑腿任务的类型,比如代取快递、代买食物、代排队、代办事务;订单表t_order是整个系统的核心,字段包括订单编号、下单用户ID、服务类型、取件地址、送达地址、期望时间、小费金额、订单金额、接单跑腿员ID、订单状态、创建时间和完成时间。

这里有一个设计细节值得注意:为什么小费金额和订单金额要分开?因为系统需要从订单金额中抽取平台佣金,而小费是全额给到跑腿员的。如果混在一起,后面做结算逻辑的时候会非常痛苦。另外地址字段我建议单独建表或者至少存成结构化的多个字段,不要图省事直接塞一个长字符串。省那一时的功夫,后面做距离计算和推荐排序的时候会发现完全没法用。

支付流水表t_payment记录每一笔订单的资金变动,包括用户支付、平台佣金、跑腿员收入、退款等。这个表的存在,让整个系统在答辩时可以被描述成“资金流转清晰可追溯”。评价表t_comment关联订单,包含评分、内容、评价时间,跑腿员的评分就是从这里聚合出来的。

2.2 订单状态机:从下单到完成的全过程

订单状态是跑腿系统最核心的业务规则,我把它设计成了七个状态:待支付、已支付待接单、已接单、配送中、已送达、已完成、已取消。

用户创建订单后进入待支付状态;支付成功之后进入已支付待接单状态,此时订单会出现在抢单大厅里;跑腿员抢单成功后状态变为已接单,系统会把取件信息和联系方式推送给跑腿员;跑腿员到达取件地并通过操作确认后状态进入配送中;送达之后进入已送达状态,等待用户确认;用户确认收货后状态变为已完成,资金完成结算;在支付之后、接单之前,用户如果改变主意可以取消订单;已接单后如果要取消,则需要申请并由管理员审核。

这个状态机看着简单,但关键的坑在于状态流转的“唯一性”。比如已送达这个状态,必须确保用户只能确认一次,否则重复确认会导致跑腿员被结算两次。我在service层用状态条件更新来处理这个并发问题:更新的SQL语句里带上WHERE order_status = 已送达,如果影响的记录数为0,说明状态已经被改过了,直接抛出异常。这是一种典型的乐观锁思路,比单纯在Java代码里判断要可靠得多。

另外一个和状态相关的联动逻辑是支付与退款。我设计的是用户先充值到平台余额,下单时从余额扣款,订单完成时再把配送费(扣除佣金后)打入跑腿员余额。这样资金全程在平台内流转,监控和管理都方便。课程设计阶段不需要真的对接第三方支付,但你在设计文档里要解释清楚,如果需要对接微信支付或支付宝,只需要在支付环节增加一个网关调用即可,这个扩展点写进论文里加分不少。

2.3 让系统更完整的辅助表设计

除了核心的业务表之外,还有一些辅助表会让系统的完整度明显提升。公告表t_notice用来发布平台通知,比如运营规则调整、节假日配送安排;管理员表t_admin独立于用户表,包含角色字段,便于以后扩展超级管理员和运营人员的权限区分;操作日志表t_log记录关键操作,比如用户提现、后台修改订单状态、跑腿员审核通过等,这些记录既是安全审计的依据,也是你调试问题时的重要线索。

还有一个容易被忽略但实际很有用的表是t_feedback意见反馈表。用户可以把使用中遇到的问题提交给平台,管理员在后台看到之后进行回复。这个功能虽然代码量不大,但它体现的是需求分析阶段有没有从“用户体验”的角度去思考问题。答辩时老师经常问“你的系统相比其他跑腿软件有什么特点”,我的回答里就包含了一条:重视用户反馈闭环,用户在App里可以随时提交问题,管理员后台有专门的待处理列表,而不是只留一个客服电话放那儿不管。

3. 后端核心接口与业务流程实现

3.1 用户模块和登录鉴权怎么做

用户模块看起来是标准的增删改查,但里面有一个重要设计点:登录鉴权和密码安全。我在t_user表中保存的不是明文密码,而是加盐后的MD5值。加密算法本身并不高级,但你要在系统中养成“敏感信息一律不存明文”的意识,这在你答辩的时候也能体现出专业性。

登录接口我用的是Token机制。用户登录成功后,服务端生成一个UUID作为Token,存到Redis里并设置有效期,同时把这个Token返回给前端。前端每次请求业务接口时在请求头里带上Token,后端写了一个拦截器统一校验。这里没有用Spring Security,因为对一个课程设计来说它太重了,手写Token机制反而能把原理讲得很透彻。拦截器校验通过后,把用户信息塞到ThreadLocal里,后面controller和service就能随时获取当前登录用户。

注册接口里要处理几个校验:学号不能重复、手机号格式是否正确、密码长度至少六位。这些校验既要写在前端表单校验,也要在后端service再做一遍,双重校验的逻辑是后端永远不信任前端传过来的参数。比如用户输入的金额,如果前端改成负数提交过来,后端不做校验就会产生严重的数据错误。

3.2 下单流程:参数校验、余额支付与消息通知

创建订单的接口是整套系统业务逻辑密度最高的地方,我前后迭代了很多次才把所有边界情况处理干净。

下单请求到达后,controller先做参数绑定和基础格式校验,然后调用service层的createOrder方法。这个方法标注了@Transactional,内部按顺序执行以下步骤:校验用户是否存在且账号正常;校验订单参数,包括服务类型是否有效、取件地址是否为空、期望送达时间是否在合理范围内;计算订单金额,这里用了一个全局配置表来维护每个服务分类的起步价和每公里加价;检查用户余额是否充足,不足则直接返回支付失败;扣减用户余额并生成支付流水;插入订单记录,状态设为待支付。

扣减余额用的是条件更新:UPDATE t_user SET balance = balance - #{amount} WHERE id = #{userId} AND balance >= #{amount}。这样就把“余额检查”和“余额扣减”合并成一条原子SQL,不用额外加锁。如果更新的影响行数为0,说明余额不够,整个事务回滚。这一步是整个下单接口的精髓,也是并发安全的关键。

订单创建成功后,还需要发送一个通知给用户。这里用到了Flask端的通知服务接口:Java端向Flask发一个HTTP请求,Flask再去调用校园通或邮件接口。当然实际做的时候往往简化成站内信或者短信模拟,但接口的调用链路是完整的。

3.3 抢单机制:如何防止高并发下超抢

抢单是跑腿系统区别于普通外卖系统的核心场景,也是最容易出线上事故的地方。同一个订单如果同时被多个跑腿员看到,大家同时点击接单,如果代码处理不好,同一个订单就会被多个跑腿员抢到。

我的方案是Redis加分布式锁。跑腿员发起接单请求后,后端尝试给订单ID加一个Redis锁,使用SET NX EX命令保证同一时刻只有一个线程能拿到锁。拿到锁之后,需要重新查询订单状态,确认仍然是待接单状态,然后执行接单操作即把订单状态改为已接单、绑定跑腿员ID,最后释放锁。

这里面有一个特别关键的细节:锁一定要设置过期时间,而且加锁和设置过期时间必须是原子操作。如果先加锁再设置过期时间,中间一旦进程崩溃,锁永远得不到释放,所有抢单请求都会卡死。Redis的SET key value NX EX 5这个原子操作完美解决这个问题。

另外还要讲一下锁的粒度。有些同学会把锁加到整个抢单接口上,也就是所有订单共用一个锁,这样虽然不会超抢,但性能极差,一个跑腿员抢单的时候其他人全部阻塞。正确做法是锁的key设置为订单ID,也就是只锁住当前这个订单的接单操作,不同订单之间互不干扰。

3.4 配送流程与确认收货的权限校验

跑腿员接单之后,整个配送流程是从取件到送达的过程。跑腿员到了取件地点,需要点击“确认取件”,此时后端要校验订单状态是已接单且当前操作用户是该订单的接单跑腿员,校验通过后把状态改为配送中。跑腿员送达后点击“确认送达”,状态改为已送达,并可以上传送达凭证照片。

用户端确认收货的接口是整个流程的最后一环。用户确认后,系统需要执行一系列联动操作:把订单状态改为已完成;根据订单金额和平台佣金比例计算跑腿员收入;把收入金额加到跑腿员余额里,同时记录资金流水;给跑腿员发送消息通知。

这里同步完成还是异步完成就值得讨论了。当时我试过直接在事务里同步做资金结算,结果是用户确认收货后接口响应要等一会儿,因为涉及多张表的更新和通知发送。后来我把通知发送改成了异步线程池处理,主链路只做状态更新和资金结算,响应速度快了很多。这是一个很实用的优化技巧,写论文的时候可以放在“系统优化”这一章。

3.5 取消订单和超时处理的设计

取消订单是边界情况最多的环节。我的设计是分成三种情况处理。

第一种是未支付前取消,用户直接取消,不需要任何审批,订单变为已取消,不涉及退款。第二种是已支付但尚未接单时取消,用户点击取消后订单状态变为已取消,同时系统自动执行退款,把订单金额退回用户余额。这个退款操作同样使用事务控制,保证退款流水和余额更新要么全部成功要么全部失败。第三种是已接单后发起取消申请,这时候不能直接取消,因为跑腿员可能已经去取件的路上了。取消申请会生成一条待审核记录,管理员在后台根据实际情况决定是否同意取消以及是否扣除部分费用补偿跑腿员。

超时处理我用了Flask端的定时调度。每两分钟扫描一次所有“已支付待接单”且创建时间超过15分钟的订单,如果一直没人接单,就通过站内信和短信通知提醒用户,并建议用户增加小费或者更换更方便的服务分类。同时,对已接单但超过60分钟未确认取件的订单,系统会发预警给管理员,由管理员介入联系跑腿员确认情况。

4. Flask辅助服务:距离计算、定时任务与推荐逻辑

4.1 配送距离的经纬度计算原理

Flask服务里最基础也是最实用的一个能力,就是计算两个地址之间的配送距离。Java端在下单时会计算起步费是否覆盖了配送距离,系统根据配送距离加收额外运费。

地址存储时,除了文字描述字段,我建议增加经纬度字段。用户在下单时通过地图拉取选点,前端把经纬度一并提交上来。后端计算距离常用的方法是Haversine公式,它假设地球是一个标准球体,根据两点的经纬度计算球面距离。公式本身不复杂:先将经纬度从角度转为弧度,然后计算两个点之间的中心夹角,最后乘以地球半径(取6371公里)得出距离。

实际开发中,我在Flask里专门写了一个utils/distance.py模块,包含haversine_distance(lat1, lng1, lat2, lng2)方法。Java端需要计算距离时,通过HTTP接口调用Flask的/api/calc_distance,传入两个地址的经纬度,得到返回的公里数。Java端用RestTemplate来做HTTP调用,返回的JSON用Map接收后解析。这样的好处是距离计算逻辑集中在Python端,以后如果换更精确的路网距离算法,只需要改Python端代码,Java端完全不用动。

作为备选方案,如果你的地图服务商已经提供了距离计算API,那可以直接在前端算好距离,然后传给后端。但这里有个安全性的问题:如果完全信任前端传过来的距离,用户就可能通过修改请求参数来降低配送费。所以我的做法是前端展示的距离只作为交互参考,后端必须依据经纬度重新计算,以计算结果为准。

4.2 跑腿员推荐:基于距离和评分的加权排序

为了让订单更容易被接单,也为让跑腿员的工作更高效,系统在订单进入抢单大厅时会做一个推荐排序。这个排序功能我放在Flask端实现,思路是拉取当前空闲的跑腿员列表,计算他们当前位置与订单取件地点的距离,然后结合跑腿员的评分和接单量做一个综合加权。

排序公式的大致逻辑是:综合得分 = 权重1 × 距离指数 + 权重2 × 评分指数 + 权重3 × 活跃度指数,其中距离越近得分越高、评分越高得分越高、当天已接单数适中的跑腿员得分越高。这个公式本质上非常简单,但它完整体现了你对业务的理解。答辩时我甚至被问到“如果跑腿员数量特别多,这个排序能支撑吗”,我承认它不适合海量用户的场景,但作为课程设计,它证明了你有能力把业务规则转化为程序逻辑。

Flask端写好推荐算法后,通过/api/recommend_runners接口暴露给Java端调用。Java端在返回抢单大厅数据时,先从Redis取缓存,缓存没有就会远程调用Flask获取推荐排序的结果,把排序好的跑腿员ID列表作为业务数据返回给前端展示。

4.3 定时任务与通知推送的工程实现

Flask端另外一个重要职责是跑定时任务。我用了APScheduler这个Python定时任务库,配合Flask应用一起启动。任务包括三种类型:超时未支付订单的自动关闭、已支付超时未接单订单的提醒、已完成订单的每日数据归档。

这里需要注意的坑是:Flask的定时任务要访问MySQL数据库,如果直接在线程里写SQL,很容易遇到数据库连接不释放的问题。我的做法是在Flask初始化时配置好连接池,每次定时任务执行时从连接池获取连接,执行完毕之后释放回连接池,而不是每次都新建连接。另外,定时任务的日志要输出到独立文件,一方面方便排查问题,另一方面写进论文的时候也有截图素材。

以及一个实施上的建议:定时任务的执行时间尽量跟Java端的高峰期错开。比如晚上12点左右做数据归档,凌晨4点左右清理超时会话,这样不会影响白天用户正常使用。

5. 部署调试:从本机联调到环境配置

5.1 开发环境搭建与版本踩坑记录

开发过程中最容易出问题的环节反而不是写代码,而是环境配置。我先说我这套东西的版本组合,全是实测稳定运行的:JDK 1.8、Maven 3.6.3、Tomcat 8.5.84、MySQL 5.7.24、Redis 5.0、Python 3.8.10、Flask 2.0.3。

Java端用Maven管理依赖,核心的依赖包括Spring(5.1.8版本)、SpringMVC、MyBatis(3.5.3)、MyBatis-Spring、MySQL驱动、Druid连接池、FastJson、Lombok等。要注意MyBatis和Spring版本之间有兼容性问题,有些版本我的项目里就会遇到Mapper扫描不到的问题。

SSM整合的时候有一个最容易踩的坑:Spring的配置文件有applicationContext.xmlspring-mvc.xml两份。前者管数据源、事务、MyBatis的SqlSessionFactory,后者管controller的扫描、视图解析器、静态资源映射。如果配置写混了,会出现service注入不了、Mapper找不到之类的奇怪报错。我建议配置扫描器时把controller的扫描放在spring-mvc.xml里,把service和mapper的扫描放在applicationContext.xml里,两者严格分开。

Flask端的依赖相对简单,一个Flask加上一个Requests、一个APScheduler,总共就几个包。用pip install -r requirements.txt一次性装好。Flask服务默认跑在5000端口,和Java端的8080端口错开,避免端口冲突。

5.2 Java端与Flask端联调的关键细节

两端联调最核心的工作是确保HTTP接口的协议一致。Java端用RestTemplate向Flask发请求,接收JSON格式的响应;Flask端用Flask-RESTful风格定义接口,返回JSON格式的数据。这里面有几个要点:第一是统一响应结构,比如{"code": 200, "msg": "success", "data": {...}},这样Java端解析时只用处理一种格式;第二是接口超时时间一定要设置,我设定的是2000毫秒,如果Flask服务迟迟不响应,Java端不要无限等下去,要给出友好的错误提示;第三是编码问题,Flask端返回的中文要在代码里设置app.config['JSON_AS_ASCII'] = False,否则Java端收到的JSON会是unicode编码,虽然能解析但调试时看不出内容。

联调阶段我还踩过一个跨域问题的坑。前端页面跑在Tomcat上,Flask接口跑在5000端口,浏览器请求Flask接口时会被跨域策略拦截。解决办法是在Flask端配置CORS中间件,允许来自Tomcat域的跨域请求。注意不要用*通配符搞全开,明确的域名配置更安全。

5.3 调试文档与问题定位的那些心得

这套系统做下来,我最大的收获之一就是学会了系统化地排查问题。Java后端的问题,第一步看Tomcat日志,里面有异常堆栈。常见的异常无非几种:空指针通常是对象没初始化或者查询结果为空但我没判空;SQL异常通常是字段名对不上或者数据库连接配置错误;依赖注入失败通常是扫描包路径没配对。

我强烈建议把日志做分级:控制台输出DEBUG级别,文件日志输出INFO级别。配置Logback时,把com.campus.runner.mapper包的日志级别设为DEBUG,这样就能在控制台直接看到MyBatis执行的具体SQL和参数值,排查SQL问题效率特别高。同时把org.springframework包的日志级别设为WARN,不然一大堆框架日志会淹没你的业务信息。

Flask端的问题主要看控制台输出和独立的日志文件。APScheduler定时任务如果某次执行报错,不会影响Flask主进程,但错误不会显示在请求日志里,必须在scheduler任务的回调里捕获异常并记录到日志文件。这些细节不打磨好的话,排查问题的过程会非常痛苦。

6. 常见问题与排查技巧实录

6.1 登录失效与Token过期问题

Token过期是用户反馈比较多的问题。用户用着用着突然提示登录失效,需要重新登录。调试后发现是因为我把Token的有效期设置得太短,后来改成2小时,同时增加了一个Token续期机制,即用户在正常操作过程中如果Token剩余有效时间小于30分钟,接口会自动返回新的Token,前端拿到之后更新本地存储。这样只要用户持续使用系统,就不会被强制下线。

还有一次遇到的情况是:部分接口无需登录就能访问,比如公告列表和跑腿员排行榜。我没在前端做路由拦截,导致用户直接访问需要权限的页面时后端返回了401,但前端没有全局处理,页面就白屏了。解决办法是在前端axios拦截器里统一处理401状态码,弹出提示并跳转到登录页。

6.2 并发接单导致的数据异常

并发问题在数据库层面和Redis层面各出现了一次。第一次是用户反馈同一个订单在跑腿员端显示被抢了两次,跑腿员端页面显示接单成功但实际订单列表里没有。排查后发现是多个跑腿员同时请求时,后端在确认订单状态之后、更新数据库之前存在一个时间窗口,第二个请求也能通过校验。

解决方法是把状态更新的SQL改成了条件更新,加上了WHERE order_status = 待接单的条件。这样即使多个请求都通过了Java层的状态校验,数据库层面也只会有一条记录更新成功,其他更新影响行数为0就直接报错返回。

第二次是Redis锁过期时间设置太短导致的问题。订单量高峰时期,业务处理时间可能超过锁的过期时间,第一个线程还在处理时锁就过期了,第二个线程加锁成功进入,产生重复处理。解决思路是给锁设置一个合理的过期时间,同时在使用Redis锁时记录锁的持有方标识,释放锁之前先校验是不是自己持有的锁,避免误删其他线程的锁。

6.3 图片上传失败与静态资源配置问题

跑腿员确认送达时会上传凭证照片,管理员审核跑腿员资质时也需要上传学生证照片。这个地方遇到的问题是,图片上传到Tomcat部署目录后,重新部署项目时图片会被清空。根本原因是我把图片存在了Web应用的内部目录里,而Tomcat重新部署会覆盖整个应用目录。

正确做法是把上传的文件存到服务器的一个独立目录,比如/data/upload,然后在Tomcat配置一个虚拟路径映射,通过URL访问时映射到独立目录。修改server.xml的Host节点添加Context配置,把/upload/**映射到物理磁盘的指定目录。这样重新部署项目不会影响已经上传的文件,运维上更规范。

6.4 数据库连接超时与连接池配置

项目跑了一段时间后开始出现“连接超时”的报错,查阅资料才知道是Druid连接池的默认配置问题。MySQL有一个wait_timeout参数,如果连接空闲时间超过这个值,MySQL会主动断开连接。当连接池里的连接已经失效,但连接池不知道,下次取出来用时就会报错。

解决办法是在Druid配置里设置testWhileIdle为true,validationQuerySELECT 1,让连接池定期检查空闲连接的可用性,发现失效就丢弃并新建连接。同时设置合理的minEvictableIdleTimeMillistimeBetweenEvictRunsMillis参数。这个地方虽然改动只有几行配置,但直接解决了系统长期运行后的稳定性问题,也是答辩时可以从实战角度讲的一个点。

7. 系统部署上线与项目文档整理

7.1 服务器部署步骤与配置要点

做完功能之后,把系统部署到服务器上供测试使用是一个必经环节。我的部署方案是:买一台最基础的云服务器(2核4G配置),装好CentOS 7系统,部署JDK 8、MySQL 5.7、Redis 5.0、Nginx 1.20,Tomcat直接运行在8080端口,Flask运行在5000端口,Nginx作为统一入口做反向代理。

把Java工程用Maven打成War包,放到Tomcat的webapps/目录下重启Tomcat即可。数据库方面,在服务器MySQL里执行SQL脚本,修改db.properties里的连接地址、用户名、密码。Flask端的部署稍微灵活一点,我用gunicorn配合gevent来跑Flask应用,命令大致是gunicorn -w 2 -b 0.0.0.0:5000 main:app,用supervisor做进程守护,保证Flask进程挂了能自动重启。

前端页面我打包好了直接放到Nginx的静态文件目录里,接口地址通过配置文件在构建时指定为服务器的Nginx域名。Nginx配置里添加两条location规则:一个把/api/前缀的请求代理到Tomcat的8080端口,另一个把/flask/前缀的请求代理到Flask的5000端口,注意替换内部的实际路径。这样一个域名就能同时搞定前端和两个后端服务,用户访问体验也好很多。

7.2 毕业设计论文划重点:架构图怎么画、流程图怎么写

论文写作是这个项目的重头戏,很多代码写得好的同学最后反而栽在论文上。我觉得最有用的建议是:不要等项目做完了再写论文,而是边做边整理。在做的时候把每个模块的时序图、ER图、功能截图都顺手保存下来,最后拼装论文的时候会轻松非常多。

架构图建议画两层:逻辑架构图体现“浏览器—Nginx—Java后端/Flask辅助服务—MySQL/Redis”的整体结构;功能架构图从用户端、跑腿员端、管理后台三个视角去画功能菜单。流程图重点画两条线:一条是下单到完成的业务流程图,另一条是抢单的并发处理流程图。这两张图画清楚,整个系统的核心逻辑就都讲明白了。

论文里一定要写清楚为什么做这个系统、现有方案有什么不足、你的系统做了什么改进。不要只罗列功能,要写有分析的内容。比如“传统跑腿平台无法满足校内短距配送的轻量化和低成本需求,因此本系统聚焦校园场景,在订单调度、运力推荐和资金流转上做了针对性设计”,这样写比单纯罗列功能强得多。

7.3 答辩准备:老师最可能追问的四个问题

答辩之前,我把自己觉得最可能被问到的问题都过了一遍,最后发现老师们的问题高度集中在四个方面。

第一个高频追问是:“为什么用SSM框架而不用Spring Boot?”回答的关键是不要贬低任何一个技术,要说清楚SSM是经典的企业级开发组合,分层清晰、事务管理成熟;同时说明虽然Spring Boot提供了大量自动配置,但对学生项目来说,手动整合SSM反而能更深入理解框架原理。

第二个追问是:“Java和Flask之间断开连接或者Flask挂了怎么办?”这个问题考查你是否有容灾意识。我的回答是:核心业务全部在Java端,Flask挂了不影响下单和接单;骑手推荐和距离计算接口会降级为Java端的备用计算方法;定时任务会有补偿机制,服务恢复后重新执行。这个回答比“Flask很稳定不会挂”要可靠得多。

第三个追问往往针对数据库设计:“订单状态为什么要做成七个而不是三个?”我的回答是从业务闭环讲起,解释每个状态对应一个操作节点,状态机保证订单流程的完整性,也让系统在运营中能精确知道订单在什么环节、出了什么问题该找谁处理。

第四个问题是关于安全的:“用户余额操作如何防止并发问题?”这就是前面讲的条件更新和Redis分布式锁能派上用场的地方了。把这个讲清楚,基本就稳了。

8. 写在最后

做这个项目前后大概花了四周时间,回过头来最大的感受是:课程设计和毕业设计真正考察的不是你会不会用某个框架,而是你能不能从无到有地定义问题、拆解问题、用合适的技术去解决它。选SSM加Flask的混合架构,不是为了炫技,而是让每一个技术组件都待在最合适它的位置上。

如果让我给正在做类似题目的同学几个最核心的建议:第一,数据库设计一定要在写代码之前彻底定稿,字段不全或者关系设计不合理,后面返工的代价远比你想象中要大;第二,并发和安全问题从一开始就要考虑,不要等出问题了再补丁连天,分布式锁和条件更新并不难写,难的是你有安全意识;第三,文档和代码同步更新,每天做完功能顺手截图、记录踩坑过程,最后写论文时你会感谢那个勤快的自己。

跑腿系统只是一个具体的业务场景,你从中学到的需求分析能力、架构设计能力、并发处理思路,才是真正能带走的东西。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询