ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+Vue+MySQL商铺管理系统设计与实现:从选题到部署全解析

SpringBoot+Vue+MySQL商铺管理系统设计与实现:从选题到部署全解析

每年到了一年一度的毕业设计时段,最让人头大的就是选题和落地。选个纯管理系统怕太简单,选个新技术栈又怕做不完。今天这篇,我直接拿一套已经完整落地的 SpringBoot + Vue + MySQL 商铺管理系统平台来拆解,从为什么选这个题、数据库怎么建、核心功能怎么实现,到打包部署和常见坑位,一条龙讲清楚。这套系统面向的是高校校区内沿街商铺、园区商业网点的数字化管理场景,核心解决的就是商铺租给谁、合同怎么签、钱怎么收、账怎么对这类实际问题。不管你是正在发愁选题的毕业生,还是想找一个业务闭环完整、又不过分复杂的管理系统来练手的开发者,这篇内容都值得你花十分钟看完。

我在做这个项目之前,也调研过一堆五花八门的毕设题目,比如什么智能推荐商城、在线考试系统之类,后来发现商铺管理看似不起眼,实际上业务颗粒度刚刚好:有基础信息管理,有合同状态流转,有租金账单这种典型的财务场景,还有统计分析报表。这一套做下来,前端、后端、数据库、部署全部覆盖,答辩的时候每个模块都能讲出设计思路,而且逻辑自洽不会被问倒。先把整体思路捋清楚,后面所有代码和配置才有落点。

1. 选题思路与整体设计拆解

1.1 为什么选"商铺管理系统"这个方向

商铺管理系统属于典型的信息管理系统(MIS),这类题目在毕设里最大的优点就是需求明确、边界清晰,不会像人工智能类课题那样容易陷入"调包调不通"的尴尬。但同样是管理系统,商铺管理比图书管理、学生管理多了几个非常关键的维度:资源维度(商铺状态是空闲还是在租)、人员维度(租户信息)、交易维度(租金收付)、契约维度(租赁合同),这四个维度互相联动,就构成了一个真正的业务闭环,而不是简单的增删改查。

另外,商铺管理天然自带"校园/园区/后勤"的场景属性,很多高校周边都有商业街、创业孵化基地,这类系统的需求是真实存在的。毕设选题如果能贴近真实场景,答辩老师会天然觉得你有调研、有思考,而不是凭空捏造一个玩具系统。尤其当你把"合同到期提醒""租金逾期状态""退租交接"这些细节做出来的时候,整个系统的完成度就明显拉高了。

我个人的体会是,毕设选题不要追求题目听起来高级,要追求"能做完、能讲清、能演示"。商铺管理系统恰好是三者兼顾的题目。

1.2 项目角色与核心功能清单

这套系统我设计了三种角色:管理员、财务人员、普通运营人员。很多毕设系统只有一个管理员账号,那是典型的"演示玩具",答辩老师一问权限设计就露馅。我给它设计成RBAC(基于角色的访问控制)模型,不同角色看到的菜单和可操作的按钮都不一样,这个设计点本身在答辩时就是加分项。

核心功能模块我分成六大块:

功能模块核心功能点主要服务角色
商铺管理商铺信息维护、状态变更、空闲/出租/维修筛选管理员、运营人员
租户管理租户信息建档、身份证/联系方式维护、关联商铺管理员、运营人员
合同管理租赁合同登记、到期提醒、退租归档管理员、财务人员
租金账单账单生成、缴费登记、逾期列表、催缴状态财务人员
报修管理报修单提交、处理进度跟踪、历史记录运营人员、管理员
统计报表租金收入趋势、出租率统计、商铺状态分布管理员、财务人员

此外还有用户管理、公告通知、操作日志这几个基础模块,共同撑起一个完整的后台管理体系。

1.3 模块开发顺序建议

我见过不少同学上来就写前端页面,写到一半发现后端接口配合不上,又回头改表结构,效率很低。做这类系统正确顺序应该是:先设计数据库表 -> 再写后端基础CRUD -> 再做登录鉴权 -> 然后做前端页面联调 -> 最后补统计报表和部署。

其中数据库表设计是最关键的一步,表关系没理清楚,后面写代码就是反复推倒重来。我这次把表结构设计放在最前面,反复推敲了两天才动手写代码,实际编码阶段反而非常顺利,很多模块一次通过。

2. 技术栈选型与系统架构设计

2.1 为什么是 SpringBoot + Vue + MySQL

这套组合在毕设圈子里这么流行不是没有道理的。SpringBoot 解决了传统 SSM 框架大量 XML 配置的问题,内置 Tomcat,一个 main 方法就能启动项目,Maven 引入依赖也简单,对学生来说上手门槛很低。Vue 作为前端框架,配合 ElementUI 组件库,表单、表格、弹窗、分页这些管理后台常用的 UI 全部现成,能省下大量写 CSS 的时间。MySQL 就更不用说了,免费、资料多、图形化工具完善,Navicat 一导就完事。

很多同学纠结要不要上更"高级"的技术栈,比如 SpringCloud 微服务、Redis 缓存、RabbitMQ 消息队列。我的建议是,除非你毕设题目明确要求,否则不要加。微服务架构光是一个服务注册发现、配置中心就能折腾你一周,而毕设评审看的不是你技术多新,是你能不能把问题讲清楚。与其把时间耗在中间件排错上,不如把核心业务模块打磨得更完善。

2.2 前后端分离架构与目录规划

前后端分离是这套系统的核心架构思路。前端跑在 8080 端口(开发环境),通过 HTTP 请求调用后端接口,后端跑在 8081 端口,二者通过 JSON 数据交互。这样做的好处是前端开发和后端开发互不干扰,部署时也可以把前端静态文件交给 Nginx 处理,后端用 Jar 包独立运行,扩展和维护都方便。

后端目录结构我按职责分层:

com.xxx.shop ├── controller // 控制层,接收请求、返回结果 ├── service // 业务层,处理核心业务逻辑 ├── mapper // 数据访问层,MyBatis接口 ├── entity // 实体类,对应数据库表 ├── common // 通用类(全局异常、统一返回体、JWT工具) └── config // 配置类(跨域、拦截器注册)

前端目录结构:

src ├── api // 接口请求封装 ├── views // 页面组件(商铺、租户、合同、账单等) ├── router // 前端路由 ├── store // 状态管理(用户信息、token) ├── utils // 工具类(request封装、时间格式化等) └── layout // 后台布局框架(顶部导航+侧边菜单)

2.3 核心表结构与数据关系设计

数据库是这套系统的地基,我把核心表设计成 8 张,表关系围绕"商铺-租户-合同-账单"这条主线展开。

首先是最核心的商铺表shop_info,字段包括商铺编号、名称、位置、面积、状态。这里的状态字段我用了TINYINT类型,0 代表空闲、1 代表出租、2 代表维修,相比直接用字符串,数字类型查询效率更高,业务判断也更方便。

然后是租户表tenant_info,设计了租户姓名、身份证号、手机号等字段。注意身份证号在页面上展示需要做脱敏处理,比如只显示前三位和后四位,这个细节在答辩时提出来会很加分。

租赁合同表lease_contract是整个业务的核心,包含合同编号、关联商铺ID和租户ID、起止日期、月租金、押金、合同状态。这里我设计了合同状态和商铺状态的联动:合同签订生效时,自动把商铺状态改成"出租";合同到期或者退租归档时,自动把商铺置为"空闲"。这个联动逻辑放在 Service 层做事务处理,保证数据一致性。

租金账单表rent_bill记录了每一期租金应收情况,字段包括账单编号、合同ID、账期(如 2025-06)、应收金额、实收金额、账单状态(待收/已收/逾期)、生成时间和缴费时间。账单状态不是手动改的,而是由系统根据时间和缴费操作自动计算,这是体现系统智能化的重要设计。

在设计表结构的时候,我习惯每张表都加上create_time、update_time两个公共字段,后面排查数据问题的时候,这两个字段能帮你快速定位是哪一步操作出了异常。

3. 核心功能模块实现细节

3.1 登录认证与权限控制

登录模块是第一优先级,几乎所有接口都需要登录后才能访问。我用 JWT(JSON Web Token)做无状态认证,流程是这样:用户提交用户名密码 -> 后端校验通过 -> 生成一段带过期时间的 Token 返回给前端 -> 前端把 Token 存在 localStorage 中 -> 之后每次请求都在 Header 里带上 Token -> 后端拦截器统一校验。

生成 Token 的工具类核心代码如下:

public class JwtUtil { // 签名密钥,实际项目中应放在配置文件并加密存储 private static final String SECRET = "your-secret-key"; private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000; // 12小时 public static String generateToken(Integer userId, String username, String role) { Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME); return Jwts.builder() .setSubject(username) .claim("userId", userId) .claim("role", role) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } }

密码安全这里我要特别强调,千万不要明文存储密码。我用了 Spring Security 自带的BCryptPasswordEncoder对密码做哈希加密,即使数据库泄露,密码原文也不会直接暴露。

前端请求封装时,我用 Axios 的拦截器统一注入 Token,并在收到 401 状态码时自动跳转到登录页:

// request.js service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { router.push('/login') } return Promise.reject(error) } )

前端路由守卫也要配合做一层判断:没有 Token 的情况下,不允许进入任何业务页面,直接重定向到登录页。这样前后端双重校验,权限模型才算完整。

3.2 商铺、租户、合同管理的流程联动

商铺管理页面就是标准的表格 + 弹窗表单。表格展示商铺列表,支持按状态筛选和关键字搜索;弹窗表单负责新增和编辑。我在前端用 ElementUI 的el-table组件,配合el-pagination做分页,后端用 MyBatis 的 PageHelper 插件,前端传pageNum和pageSize,后端返回分页数据。

这里有一个很关键的联动逻辑:合同签订。当操作员新增一份合同时,后端需要校验这个商铺当前是否空闲,如果已经出租,必须拒绝签订。这个校验我用了一段事务方法:

@Transactional public void addContract(LeaseContract contract) { ShopInfo shop = shopMapper.selectById(contract.getShopId()); if (shop.getStatus() != 0) { throw new BusinessException("该商铺当前不可租赁"); } // 设置合同初始状态:待生效 contract.setStatus(0); contractMapper.insert(contract); // 商铺状态改为已出租 shop.setStatus(1); shopMapper.updateById(shop); }

再说合同到期提醒。毕设系统如果只有人工看合同日期,那就太原始了。我是用定时任务每天扫描合同到期状态:合同距离到期不足 30 天的,状态标记为"即将到期",并在首页和合同列表用醒目的颜色提示;到期后的合同自动在账单模块触发一次应收款结算。这个定时任务用 SpringBoot 自带的@Scheduled注解就能实现,不需要引入额外的框架。

3.3 租金账单与缴费状态闭环

租金账单是这个系统里我最花心思设计的一块。很多毕设项目做账单就是一张表,手动点新增就完事,这不叫系统,叫电子表格。我的设计是:账单由合同自动生成,不需要人工录入。

具体逻辑是,在合同签订或续签成功时,系统根据合同的起止日期和月租金额,自动把整个租赁周期内的所有账单一次性生成。比如一份合同从 2025 年 6 月 1 日到 2026 年 5 月 31 日,月租 3000 元,那系统会按账期 2025-06、2025-07……2026-05 生成 12 条账单记录,状态全部为"待收"。

缴费处理是账单闭环的最后一环。财务人员点击"确认收款",系统记录缴费时间,账单状态变为"已收"。如果账单到了应收日期的次月还没有缴费,状态会自动变成"逾期",逾期列表会按时间排序突出展示,方便财务催缴。

这个"自动生成 + 状态流转"的设计,在数据库层面只需要两张表(合同表和账单表),但体现了完整的业务逻辑,答辩时非常有的讲。

3.4 统计报表与数据看板

统计报表我用了 ECharts 图表库,在首页放三个核心图表:月度租金收入趋势(柱状图)、商铺出租率(环形图)、商铺状态分布(饼图)。这三个图表在答辩演示时视觉效果很好,而且背后的实现就是几条 SQL,完全不复杂。

月度租金收入趋势的查询逻辑是按账期分组求和:

<select id="selectMonthlyIncome" resultType="map"> SELECT DATE_FORMAT(period, '%Y-%m') AS month, SUM(amount) AS total FROM rent_bill WHERE status = 1 GROUP BY DATE_FORMAT(period, '%Y-%m') ORDER BY month </select>

后端把这个 List返回给前端,前端直接转换成 ECharts 需要的 data 格式即可。整个统计模块我从设计到实现只花了一个下午,但确实是整个系统中最"亮眼"的加分项。如果你想让报表更有含量,还可以加一个数据导出 Excel 的功能,用 Apache POI 实现,导出每个月的账单明细,这在后勤财务场景里非常实用。

4. 前后端联调与部署上线实录

4.1 本地开发环境配置

本地开发时,前端需要配置跨域代理,否则浏览器会拦截请求。我在前端根目录的vue.config.js中配置了代理转发:

module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true, pathRewrite: { '^/api': '' } } } } }

后端则需要配置统一的前缀和跨域。注意 SpringBoot 后端要开启跨域支持,我写了一个CorsConfig配置类,允许前端端口访问。这里的坑点在于,如果你用了拦截器,跨域配置要让预检请求 OPTIONS 直接放行,否则前端会报跨域错误。

4.2 项目打包部署流程

实际上这也是很多毕设项目最后环节最翻车的地方。我的部署方案是:前端打包成静态文件用 Nginx 托管,后端打包成 Jar 包用java -jar独立运行,MySQL 放到一台服务器上。在演示机上,我把这一套都跑在本地 Linux 虚拟机里,完整体现"部署文档"的价值。

前端打包命令:

npm run build

打包完成后,dist目录就是整个前端项目,配置 Nginx 指到该目录,并设置代理把/api开头的请求转发到后端端口。这一步网上教程很多,但有一个点很容易漏:前端路由可能是 History 模式,刷新页面时 Nginx 找不到对应的静态资源,会报 404。解决办法是加一行配置:

location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }

后端打包用 Maven:

mvn clean package -DskipTests

生成的shop-system.jar直接上传服务器,执行nohup java -jar shop-system.jar > log.log 2>&1 &后台启动即可。

4.3 部署后常见的配置坑

部署环境跟本地不完全一样,有几个坑我第一次部署时踩过,列出来给你们提前避雷:

第一个坑是时区问题。服务器时间默认是 UTC,MySQL 连接时如果不指定时区,查询出来的时间会比本地时间少 8 小时。解决办法是数据库连接 URL 中加上serverTimezone=Asia/Shanghai。

第二个坑是文件上传路径。我一开始把上传文件夹写死在代码里,结果部署后发现上传的文件找不到了。后来改成在配置文件中定义外部路径,并在 Nginx 里加一层静态文件映射才解决。

第三个坑是端口。很多同学本地用的是 MySQL 默认 3306 端口,但服务器上的 MySQL 可能改了端口,或者被防火墙挡住了。部署前一定要先在服务器上测试数据库连接是否通畅。

5. 典型问题与排查技巧实录

5.1 我踩过的几个典型问题

做这个系统的过程中,我前前后后遇到了不少问题,挑几个有代表性的列在下面,你如果遇到了可以少走很多弯路。

典型问题原因分析解决办法
前端请求接口报 404前端代理路径配对错误检查 vue.config.js 的 pathRewrite 是否把/api正确剥离
登录后接口返回 401Token 过期或拦截器未放行预检请求延长 Token 有效期,并在拦截器中放行 OPTIONS 请求
中文乱码MySQL 表编码不是 utf8mb4建表时统一使用 utf8mb4,连接串加 characterEncoding=utf8
页面刷新变 404Vue Router 使用 history 模式Nginx 配置 try_files 指向 index.html
定时任务不执行类上漏加 @EnableScheduling 注解在启动类上加上该注解
数据库连不上服务器防火墙未放开 3306 端口安全组/防火墙放行端口并检查 MySQL 绑定地址

这里我想展开讲一下跨域问题。有一次前端项目怎么调都报跨域,查了半天发现是因为后端拦截器把 OPTIONS 预检请求也拦截了,返回了 401 给前端。后来我在拦截器里单独判断,如果是OPTIONS请求,直接返回成功,不再做 Token 校验。这个问题很典型,很多做前后端分离项目的同学都会遇到。

5.2 给即将做毕设的同学的几点建议

如果在做类似系统,我有几个建议送给你:

第一,文档和代码一样重要。这套项目配套的论文和部署文档,是我在写代码的过程中同步积累的,不是最后草草补的。每写完一个模块,就把设计思路、遇到的坑、解决方案记录下来,论文自然就有素材了,部署文档也不会遗漏关键步骤。

第二,答辩前一定要准备一份完整的演示脚本。从登录开始,按商铺管理、合同签订、账单生成、缴费、报修处理、统计报表的顺序走一遍,每一步都讲清楚"这是在干什么、为什么这样设计"。演示流畅、逻辑清晰,比你说一堆技术名词管用得多。

第三,代码尽量在本地做好版本管理。我当时是初始化了一个 Git 仓库,每天提交一次,方便回滚。Git 的提交记录在答辩时也可以作为开发过程规范性的佐证,老师问到"你的开发流程是什么样的"时,你直接打开 Git 记录展示,非常加分。

最后再分享一点体会

这套系统从数据库设计到最后打包部署,前后花了我大约三周时间。最大的收获并不是技术能力的提升,而是我学会了如何把一个模糊的需求一步步拆解成可落地的功能模块。商铺管理系统的难度其实不算高,但正是因为它不复杂,我才有精力去打磨合同联动、账单自动生成、状态闭环这些细节,而这些细节恰恰是让系统从"像作业"变成"像产品"的关键。当你做完一个项目,能把它从头到尾讲清楚、能现场部署给被人看的时候,你才算真正掌握了这项技能。希望这篇拆解对你的毕设有所启发,动手做起来的时候保持耐心,做完的那一刻你会觉得一切都值得。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进