ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MySQL智慧社区管理系统:核心设计与部署指南

SpringBoot+Vue+MySQL智慧社区管理系统:核心设计与部署指南 简介基于JavaSpringBootVueMySQL的智慧社区管理系统毕业设计源码面向高校计算机专业学生的毕设、课设与期末大作业场景覆盖居民信息管理、物业通知、服务预约、访客登记、车辆管理、费用缴纳等核心模块同时界面美观、操作简单、管理便捷系统可在校园、小区、公寓等多类社区场景中实际应用。压缩包共748个文件约22.57MB包含203个Java后端源码、141个Vue前端页面、1个SQL数据库脚本以及XML配置、bat启动/构建脚本、图标样式等辅助资源项目基于IntelliJ IDEA、MySQL 8.0、Maven、Navicat环境已调试通过导入即可运行。目前已有143人学习下载除了完整源码与数据库脚本还附带run.bat、build.bat等自动化脚本和前端图片、字体样式整体结构清晰能帮助快速理解SpringBootVue前后端分离项目的分层实现与社区管理业务逻辑可在此基础上二次开发整体既能作为高分毕设模板也可作为课程设计或期末作业的完整参考。1. 智慧社区管理系统从毕设标题看一套能落地的 SpringBoot Vue 全栈模板把「基于 javaspringbootvuemysql 的智慧社区管理系统」这个标题拆开看它其实是毕业设计里最典型的一类单体全栈项目前端 Vue 负责页面交互后端 SpringBoot 提供接口MySQL 存业务数据。真正做过这类项目的人会告诉你难点从来不在增删改查而在三件事业主、物业、管理员共用一套登录入口时权限怎么隔离房产、报修、缴费、访客这些表怎么关联才不乱以及前端上传的图片、视频流后端到底怎么吐给浏览器。这篇文章按我平时搭这类系统的顺序来讲先定表结构再写后端认证再套前端页面最后落到部署和验收技巧上。新手能照着把环境跑起来熟手可以重点看权限模型和 m3u8 播放这两节的取舍。2. 先定数据结构MySQL 表设计与存储过程别让权限模型拖垮联表2.1 房产、业主、访客、报修、缴费五张核心表怎么关联智慧社区的核心业务线是「人 - 房 - 事」。人指业主和访客房指楼栋单元房间事指报修、缴费、投诉这些动作。我一般先画一张最简单的 ER 草图房产表房间挂在楼栋下业主表通过 owner_id 和房产表关联报修表和缴费表都带 owner_id 作为外键访客表单独记录来访时间与离开时间。先别急着加一堆冗余字段五张表能跑通再回头补索引。CREATE TABLE building ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL COMMENT 楼栋名如 3 栋, address VARCHAR(100) DEFAULT NULL ) COMMENT 楼栋表; CREATE TABLE room ( id BIGINT PRIMARY KEY AUTO_INCREMENT, building_id BIGINT NOT NULL, room_no VARCHAR(20) NOT NULL, owner_id BIGINT DEFAULT NULL, KEY idx_building (building_id) ) COMMENT 房间表; CREATE TABLE owner ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL, phone VARCHAR(20) NOT NULL, id_card VARCHAR(18) DEFAULT NULL, wechat VARCHAR(50) DEFAULT NULL ) COMMENT 业主表; CREATE TABLE repair ( id BIGINT PRIMARY KEY AUTO_INCREMENT, owner_id BIGINT NOT NULL, type VARCHAR(20) COMMENT 水/电/门禁/其他, description VARCHAR(500), status TINYINT DEFAULT 0 COMMENT 0待派单 1处理中 2已完成, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 报修表; CREATE TABLE bill ( id BIGINT PRIMARY KEY AUTO_INCREMENT, owner_id BIGINT NOT NULL, item_type VARCHAR(20) COMMENT 物业费/停车费/水费, amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0未缴 1已缴, pay_time DATETIME DEFAULT NULL ) COMMENT 缴费表;这段 SQL 里有几个细节值得说明。room 表里owner_id DEFAULT NULL是刻意的因为空房状态必须能表达不能用 0 占位。bill 的amount用DECIMAL(10,2)而不是 FLOAT财务金额用浮点类型会在累计时出现精度偏差这点在答辩时经常被追问。create_time用了DEFAULT CURRENT_TIMESTAMP插入时少写一个字段。真正的业务查询通常是「某业主的未缴账单」「某楼栋的待处理报修」所以idx_building这种单列索引先加上等数据量过万再评估联合索引。2.2 用 RBAC 五表模型管理管理员、物业与业主权限毕设里最常见的错误写法是给用户表加一个role字段用 0、1、2 区分角色然后在每个 Controller 里 if 判断。两个角色时还能撑住一旦出现「物业经理能看全部报修普通物业只能看自己片区的报修」这种需求硬编码就崩了。RBAC 模型是更稳的底子核心是五张表用户表、角色表、权限表、用户角色关联表、角色权限关联表。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL COMMENT BCrypt 加密, real_name VARCHAR(30), status TINYINT DEFAULT 1 ); CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_code VARCHAR(20) NOT NULL UNIQUE COMMENT ADMIN/MANAGER/OWNER, role_name VARCHAR(30) NOT NULL ); CREATE TABLE sys_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, perm_code VARCHAR(50) NOT NULL UNIQUE COMMENT 如 repair:handle, perm_name VARCHAR(30), type TINYINT DEFAULT 1 COMMENT 1菜单 2按钮 ); CREATE TABLE sys_user_role ( user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, PRIMARY KEY (user_id, role_id) ); CREATE TABLE sys_role_permission ( role_id BIGINT NOT NULL, permission_id BIGINT NOT NULL, PRIMARY KEY (role_id, permission_id) );初始化数据按固定套路写先插三条角色再插权限码最后用关联表把「管理员拥有全部权限」「物业拥有报修处理、访客登记权限」「业主拥有报修提交、缴费查询权限」配好。id 可以写死因为这是种子数据不会变。这套模型的好处是权限判断从「用户是什么角色」变成「用户拥有什么权限」前端按钮级别的v-permission指令和后端拦截器可以共用同一套权限码。2.2.1 权限码设计要按「资源:操作」命名权限码不要叫repair1、repair2这种无意义名字用repair:list、repair:handle、bill:export、visitor:register这种「资源:操作」格式。前端判断hasPermission(repair:handle)决定派单按钮显不显示后端拦截器校验同样的码。命名的一致性是这套方案唯一需要自律的地方建议把权限码写进一个 Java 常量类前后端都从接口文档里复制。2.3 存储过程AS 关键字那点坑与 MySQL 8 的写法搜索热词里 mysql 存储过程出现频率很高在智慧社区里最合适的场景是月底批量生成缴费账单。Java 代码循环 insert 一千条没问题但放在存储过程里更接近业务语义也方便在 Navicat 里直接测试。DROP PROCEDURE IF EXISTS generate_monthly_bill; DELIMITER // CREATE PROCEDURE generate_monthly_bill(IN bill_month VARCHAR(7), IN amount DECIMAL(10,2)) BEGIN DECLARE done INT DEFAULT 0; DECLARE v_owner_id BIGINT; DECLARE cur CURSOR FOR SELECT id FROM owner WHERE status 1; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done 1; OPEN cur; read_loop: LOOP FETCH cur INTO v_owner_id; IF done 1 THEN LEAVE read_loop; END IF; INSERT INTO bill (owner_id, item_type, amount, status, bill_month) VALUES (v_owner_id, 物业费, amount, 0, bill_month); END LOOP; CLOSE cur; END // DELIMITER ;写这段时有三个坑值得记住。第一DELIMITER //必须要有否则 MySQL 客户端碰到分号就认为语句结束了。第二游标循环结束条件用CONTINUE HANDLER FOR NOT FOUND这不是可选项没有它存储过程会死循环。第三表里如果有重复的业主需要先SELECT DISTINCT id。调用方式是CALL generate_monthly_bill(2024-06, 120.00)参数bill_month用VARCHAR(7)而不是 DATE因为 2024-06 这种格式用字符串存更直接。3. SpringBoot 后端认证链、接口分层与登录会话实现3.1 登录鉴权用 JWT 拦截器还是 SecuritySpring Security 功能全但学习曲线陡毕业设计里更常见、也更稳妥的是 JWT HandlerInterceptor 这套轻量组合。JWT 天然适合前后端分离登录接口校验用户名密码后生成 token前端存在 localStorage每次请求在请求头带X-Token后端拦截器解析 token、把用户信息放进 ThreadLocal。相比 Session 方案后端不需要维护会话状态接口给小程序或 App 复用也方便。// JwtUtil.java 核心方法 public class JwtUtil { private static final SecretKey KEY Keys.hmacShaKeyFor( community-secret-key-2024.getBytes(StandardCharsets.UTF_8)); public static String generateToken(Long userId, String roleCode) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, roleCode) .setExpiration(new Date(System.currentTimeMillis() 7L * 24 * 3600 * 1000)) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); } public static Claims parseToken(String token) { return Jwts.parserBuilder().setSigningKey(KEY).build() .parseClaimsJws(token).getBody(); } }Keys.hmacShaKeyFor对密钥长度有要求HS256 算法下密钥至少 32 字节community-secret-key-2024刚好满足。过期时间设 7 天是折中方案太短业主天天重新登录太长token 泄露风险高。真实项目里密钥要放到配置文件的密文配置项里不要硬编码。拦截器里放行/api/auth/login和静态资源路径其余接口统一走 token 校验校验失败返回 401 JSON。3.2 按 Controller-Service-Mapper 拆包报警功能给数组接口留好扩展位后端工程结构我习惯按业务模块拆 controller/service/mapper 三层而不是按技术层拆包。controller下建auth、room、repair、bill、visitor五个子包每个包里放对应的 Controller。Service 层只做业务编排事务注解加在 Service 实现类上。统一的返回结构是ResultT包含code、msg、data三个字段前端 axios 拦截器只认这个结构。接口方法说明权限码/api/auth/loginPOST登录返回 token公开/api/owner/roomsGET当前业主的房间列表room:list/api/repairsPOST提交报修repair:add/api/repairs/{id}/handlePUT物业处理报修repair:handle/api/bills/pendingGET未缴账单bill:list/api/visitorsPOST访客登记visitor:register这个表里值得注意的设计是「提交报修」和「处理报修」分开接口、分开权限码。业主能提交但不能处理物业能处理但不能替业主提交。Controller 里不写业务判断只在方法上标注需要的权限码由拦截器统一校验。3.2.1 前端启动参数与端口冲突排查SpringBoot 默认端口 8080最常见的问题就是端口被占用。排查命令是netstat -ano | findstr 8080拿到 PID 后到任务管理器结束进程。开发环境我习惯在application.yml里显式配置端口和数据库连接参数。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/community?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root hikari: maximum-pool-size: 10 minimum-idle: 5 servlet: multipart: max-file-size: 50MB max-request-size: 50MB mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: trueserverTimezoneAsia/Shanghai不加MySQL 8 会报时区错误。maximum-pool-size设 10 对毕设负载足够了线程池开太大会占用数据库连接数。map-underscore-to-camel-case开启后数据库的create_time自动映射成createTime不用每个字段写 TableField。max-file-size设 50MB 是为了后文要讲的前端上传视频流预留空间。3.3 登录接口的业务逻辑细节登录接口不要只做密码比对。正确流程是先查用户是否存在且状态正常再用BCryptPasswordEncoder.matches()比对密码最后生成 token 并记录登录日志。日志表是加分项答辩时可以说「通过登录日志实现了异常登录告警」。密码千万不要明文存储BCryptPasswordEncoder是 Spring Security 里的工具类单独引进来用就行。4. Vue 前端工程从 npm run dev 到路由和播放器4.1 初始化 Vue 项目并安装依赖智慧社区前端我一般选 Vue2 Element UI 的组合原因很现实Element UI 的表格、表单、对话框组件在后台管理页面里开箱即用网上的教程和踩坑记录也最多。如果选 Vue3 搭配 Element Plus 也不是不行但部分老教程的写法要手动改成 Composition API。npm install -g vue/cli vue create community-admin cd community-admin npm install element-ui axios vue-router3 hls.js npm run devvue-router3这个版本号必须指定Vue2 项目装 vue-router 4 会报错因为 vue-router 4 只支持 Vue3。hls.js后面播放监控视频流要用提前装好。npm 安装慢或者报错时优先检查 Node 版本Vue CLI 5 要求 Node 12 以上推荐 Node 14 或 16 LTS 版本。运行npm run dev后浏览器访问http://localhost:8080如果端口被占用CLI 会自动加 1 变成 8081。4.2 路由参数与页面鉴权该如何写页面权限控制在 Vue 里分两层路由守卫控制能不能进页面按钮指令控制能不能看到操作按钮。路由守卫写在router/index.js里。router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else if (!token) { next(/login); } else { next(); } });这段逻辑只判断了有没有 token没判断 token 是否过期。实际开发中可以在 axios 响应拦截器里加一个 401 状态码的统一跳转token 过期后后端返回 401前端清空 localStorage 并跳转到登录页。路由传参有两种方式this.$router.push({ path: /repair/detail, query: { id: 1 } })后用this.$route.query.id接收或者this.$router.push({ name: RepairDetail, params: { id: 1 } })后用this.$route.params.id接收。query 方式刷新页面参数还在params 方式刷新就丢了详情页传 id 建议用 query。4.3 用 hls.js 在 Vue 里播放 m3u8 监控流热搜词「vue 播放 m3u8」在智慧社区里对应的是监控摄像头视频回放。摄像头 RTSP 流不能直接被浏览器播放需要转成 HLS 协议输出 m3u8 索引文件和 ts 分片。Chrome 不原生支持 m3u8hls.js 库解决了这个问题。template div video refvideoEl controls autoplay muted stylewidth: 100%/video /div /template script import Hls from hls.js; export default { name: VideoPlayer, props: { src: { type: String, required: true } }, mounted() { this.playVideo(); }, methods: { playVideo() { const video this.$refs.videoEl; if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(this.src); hls.attachMedia(video); this.hls hls; } else if (video.canPlayType(application/vnd.apple.mpegurl)) { video.src this.src; } } }, beforeDestroy() { if (this.hls) { this.hls.destroy(); } } }; /script代码里有两个关键点Hls.isSupported()判断浏览器是否支持 MSE主流 Chrome、Edge、Firefox 都支持video.canPlayType(application/vnd.apple.mpegurl)是 Safari 的原生 HLS 兜底方案因为 Safari 自带 HLS 播放能力不需要 hls.js 介入。组件销毁时调用hls.destroy()释放资源否则页面切换后视频流还占着带宽。4.3.1 直播地址与原生 video 标签的区别如果是摄像头实时直播src 直接指向http://服务器IP:8080/live/xxx.m3u8就行。如果是回放后端接口通常返回{ url: /record/20240601/xxx.m3u8 }前端拿到后拼接完整地址再传给组件。注意 m3u8 里的 ts 分片路径如果是相对的比如../segment/001.ts前端请求时会基于当前页面地址解析容易 404。解决办法是让后端保证 m3u8 文件里的分片地址是完整可访问的 URL或者把分片也放在同一个静态资源目录下。5. 部署与验收用一键脚本把前端产物和 jar 放到同一台服务器前端开发完npm run build会生成dist目录后端mvn clean package生成 jar 包。最省事的部署方式是让 Nginx 直接托管前端静态文件同时反向代理后端接口这样前端请求/api/xxx不会跨域。cd /opt/community # 前端构建 cd community-admin npm run build cp -r dist/* /usr/share/nginx/html/community/ # 后端构建 cd ../server mvn clean package -DskipTests nohup java -jar target/community-server.jar \ --server.port8081 \ --spring.profiles.activeprod \ app.log 21 -DskipTests跳过测试打包避免测试用例失败导致部署中断。nohup让 jar 包在 SSH 断开后继续运行日志输出到app.log。--spring.profiles.activeprod指定生产环境配置数据库密码等敏感信息放在application-prod.yml里不要提交到 Git。Nginx 配置里加两个 location一个托静态文件一个转发 APIlocation /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /upload/ { alias /data/community/upload/; expires 7d; }proxy_pass http://127.0.0.1:8081;后面的分号不能丢并且不要写路径。/upload/映射的是后端上传文件的存储目录图片和视频文件直接由 Nginx 返回不经过 Java 应用减少 Tomcat 压力。最后验证接口是否通畅用 curl 测登录再测业务接口curl -X POST http://服务器IP/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}拿到返回的 token 后再测试需要鉴权的接口把 token 放进请求头。前端浏览器打开页面开发者工具 Network 面板里看/api/开头的请求是否都带上了X-Token请求头。如果登录成功但业务接口 401优先检查拦截器是否放行了/api/auth/login如果前端能打开但图片 404检查/upload/的 alias 目录权限是否为 nginx 用户可读。部署完记得把 MySQL 的 root 密码从默认值改掉同时确认application-prod.yml里的数据库地址不是 localhost否则后端连不上数据库会直接启动失败。本文还有配套的精品资源点击获取
返回列表