
做高校或园区类管理平台一直是 Spring Boot 开发者和毕业设计选题里的“常青树”。但很多同学拿到这类题目后容易陷入两个极端一是只做简单的增删改查把系统做成了“后台管理脚手架”二是一上来就追求微服务、分布式、消息队列结果技术栈铺得很大论文写不深答辩一追问就露馅。这篇文章想聊一个相对务实的方向基于 SpringBoot3 Vue3 的“一体化”智慧管理平台怎么设计、怎么分工、怎么把 AI 能力以低成本嵌入进去同时又能满足毕业设计和实际工程项目的双重需求。我会从真实业务痛点出发把项目拆成后端、前端、AI 接入、部署验证几个部分。讲解过程中会穿插 Vue3 开发中的高频知识点比如defineEmits、defineProps、computed、Pinia 状态管理、动态路由等。也会给出可复制的代码片段和排错思路。如果你正准备做类似选题或者刚学完 SpringBoot3 和 Vue3想知道一个完整项目如何落地这篇文章应该能帮上忙。1. 高校/园区智慧管理平台到底在解决什么问题先想一个问题为什么“智慧管理平台”这类系统永远不会过时因为高校、园区、写字楼、产业新城这些场景本质上都面临同一个困境——业务系统太多但彼此不通。举几个非常典型的例子访客要进园区保安需要打电话确认被访人不在就只能干等。会议室被各科室重复预定管理员每天早上要人工核对一遍。报修工单从提交、派单、维修、验收全靠微信消息和 Excel 表格流转。新员工入职后门禁权限、网络账号、办公座位、车辆授权要分别找不同部门开通。这些问题放在一起会形成一种很差的体验人围着系统转而不是系统围着人转。所谓“一体化智慧管理平台”核心就是把人员、场所、访客、会议室、工单、公告、车辆、设备这些分散的管理动作收敛到同一个平台上。再加上 AI 能力用自然语言或自动规则辅助管理员处理重复性事务这就是标题里“智慧”二字的落点。从技术角度看这个项目非常适合用来练习 SpringBoot3 和 Vue3 的全栈开发因为它业务边界清晰、模块可大可小论文里也能画出漂亮的架构图和流程图。2. SpringBoot3 Vue3 技术选型的核心认知很多人的技术栈还停留在 SpringBoot 2.x 和 Vue 2 时代。这里需要先建立一个认知SpringBoot3 和 Vue3 不是简单的版本升级而是编程范式的变化。2.1 SpringBoot3 到底变了什么SpringBoot3 最底层的变化是它基于 Spring Framework 6 和Jakarta EE 9规范。这意味着原来的javax.servlet包名变成了jakarta.servlet很多老代码的导包需要调整。另外几个关键点JDK 17 是硬性要求。如果电脑上还是 JDK 8直接跑不起来。支持AOT 编译和 GraalVM Native Image虽然普通项目未必用得上但在高并发场景有低延迟启动的优势。对Spring AI这类新生态模块的集成更友好。如果你想在项目里引入 AI 能力SpringBoot3 已经是无可争议的基础版本。从实际开发角度来看SpringBoot3 的自动配置、Starter 机制、Actuator 监控都延续了 SpringBoot 2 的习惯Java 开发者上手并不难。难的是在细节上适应新规范。2.2 Vue3 的实战要点Vue3 最核心的变化是Composition API。相比之下Vue 2 的 Options API 强依赖data、methods、computed这些固定选项组件的逻辑一旦复杂起来一个几百行的 Vue 文件会变得很难维护。Vue3 里最常被问到的几个知识点在真实项目中会反复出现setup语法糖script setup让代码更简洁是当前主流写法。ref和reactive的区别ref常用于基础类型和对象包装模板中自动解包reactive只能用于对象。computed计算属性有缓存机制只要依赖的数据不变多次访问不会重复计算。defineProps和defineEmits这是父子组件通信的标准方式。父组件传数据用props子组件触发事件用emits。Pinia 替代 VuexPinia 类型支持更好写法更简单是 Vue3 项目的推荐状态管理方案。下面这个例子展示了子组件如何同时使用defineProps接收父组件数据、用defineEmits通知父组件更新!-- 文件路径src/components/OrderStatusTag.vue -- script setup langts defineProps{ status: number }() const emit defineEmits{ (e: change, status: number): void }() function handleNext() { emit(change, 2) } /script template el-tag clickhandleNext当前状态{{ status }}/el-tag /template这段代码虽然简单但它就是 Vue3 组件通信的最小基线。在你做智慧管理平台时状态标签、表格行操作、弹窗表单等场景都会反复用到这个模式。3. 平台的总体架构与核心模块设计智慧管理平台如果只做一个简单的“用户管理 公告管理”那谈不上“智慧”。一个能满足毕业设计深度、又具备真实工程价值的系统可以考虑下面的架构分层3.1 整体架构前端层Vue3 Vite TypeScript Element Plus Pinia Vue Router Axios。后端层SpringBoot3 MyBatis-Plus Spring Security或 JWT 拦截器实现 Redis MySQL。AI 服务层可对接大模型 API也可在早期用 Mock 模拟。主要承担智能问答、工单分类、会议纪要摘要等能力。基础设施层MySQL 存储业务数据Redis 缓存用户会话和热点数据Nginx 部署前后端静态资源生产环境。3.2 核心模块划分这里我按高校和园区最常见的通用模块来拆。模块名功能说明核心数据表用户与权限登录认证、用户管理、角色权限sys_user、sys_role访客管理访客预约、审批、来访记录visit_order、visit_record会议室管理会议室预订、审批、设备列表meeting_room、meeting_reservation工单管理报修工单提交、分派、处理、评价work_order公告通知校内/园区公告发布、置顶、已读统计notice、notice_read车辆管理车辆登记、进出记录、月租管理car_info、car_access_logAI 智能助手自然语言问答、待办事项查询、操作引导ai_chat_history这些模块单独看都不复杂但组合在一起就能体现出“一体化”的价值——一个系统解决多个管理场景的入口问题。3.3 数据库设计思路数据库设计不要为了“表多”而设计要为了“业务闭环”而设计。比如“访客管理”这个模块最基础的流程是访客提交预约 → 被访人确认 → 门卫扫码/输入访客码 → 放行 → 离场记录对应的表结构至少需要三个环节的记录。给出一个简化的visit_order表设计CREATE TABLE visit_order ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, visitor_name varchar(64) NOT NULL COMMENT 访客姓名, visitor_phone varchar(20) NOT NULL COMMENT 访客手机号, visitor_company varchar(128) DEFAULT NULL COMMENT 访客单位, visited_user_id bigint NOT NULL COMMENT 被访人用户ID, visit_reason varchar(255) DEFAULT NULL COMMENT 访问事由, visit_start_time datetime NOT NULL COMMENT 计划到达时间, visit_end_time datetime DEFAULT NULL COMMENT 计划离开时间, visit_code varchar(16) DEFAULT NULL COMMENT 访客通行码, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待审批 1已通过 2已拒绝 3已完成 4已取消, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_visited_user (visited_user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT访客预约表;这里有一个很多人容易忽略的细节状态字段用tinyint而不是varchar。原因有两个——查询效率更高而且代码里通过枚举去约束状态变更路径避免出现“已拒绝”之后还能变成“已完成”这种脏数据。4. 环境准备与项目初始化开始编码之前先把环境准备好。这个项目涉及的软件较多建议按下面的清单自查一遍。4.1 开发环境要求软件版本建议说明JDK17 或 21SpringBoot3 强制要求Node.js18 或 20Vite6 对 Node 版本有要求MySQL8.0推荐 8.0Redis6.x缓存和会话IDEIntelliJ IDEA后端专用前端工具VS Code或 WebStorm版本不需要严格一致大版本对得上就行。下面两个命令可以快速确认环境。java -version node -v如果java -version显示的是 1.8 或 11那后面运行 SpringBoot3 项目时 100% 会报错。先升级 JDK再继续往下走。4.2 SpringBoot3 后端项目初始化推荐通过 Spring Initializr 创建工程也可以用 IDEA 内置的 Spring Initializr。关键依赖选择如下依赖用途Spring Web提供 RESTful APISpring Security认证与权限控制可简化成 JWT 拦截器MySQL Driver数据库驱动MyBatis-PlusORM 框架Lombok减少样板代码Redis缓存如果你用的是 Maven核心的pom.xml依赖片段如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent properties java.version17/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.7/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这里需要注意一个版本敏感点MyBatis-Plus 对 SpringBoot3 的支持必须使用专门的mybatis-plus-spring-boot3-starter而不是老的mybatis-plus-boot-starter。很多项目启动报ClassNotFoundException都是因为版本不匹配。4.3 Vue3 前端项目初始化前端项目推荐直接用 Vite。执行下面的命令创建一个 Vue3 TypeScript 工程npm create vitelatest campus-platform -- --template vue-ts cd campus-platform npm install接着安装项目需要的核心依赖npm install vue-router4 pinia axios element-plus element-plus/icons-vue初始化完成后的目录结构应该是这样的campus-platform/ ├── src/ │ ├── api/ # 接口请求封装 │ ├── assets/ # 静态资源 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia 状态 │ ├── views/ # 页面 │ ├── App.vue │ └── main.ts ├── index.html └── package.json这里有一个实际开发中值得养成的习惯所有请求都放到src/api目录下按业务模块拆文件。比如api/user.ts、api/workOrder.ts、api/visit.ts。不要在页面组件里直接写axios.get否则后期接口变更时会改到怀疑人生。5. 后端核心功能实现登录认证与工单模块后端部分我不会把每个模块都写一遍而是挑两个最有代表性的环节展开登录认证和工单状态流转。把这两个跑通后其他模块基本是同一套模式延伸。5.1 基于 JWT 的登录认证在 SpringBoot3 项目里做登录认证最简单的方案是 JWT 拦截器。不使用 Spring Security 的完整过滤器链是因为毕业设计和中小型项目里拦截器方案更直观也更容易在论文中把流程画清楚。先写一个 JWT 工具类// 文件路径src/main/java/com/campus/platform/common/util/JwtUtil.java package com.campus.platform.common.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.util.Date; Component public class JwtUtil { Value(${jwt.secret}) private String secret; Value(${jwt.expire-hours}) private Long expireHours; private SecretKey getKey() { return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); } public String generateToken(Long userId, String username) { Date now new Date(); Date expireDate new Date(now.getTime() expireHours * 3600 * 1000); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(username, username) .setIssuedAt(now) .setExpiration(expireDate) .signWith(getKey()) .compact(); } public Claims parseToken(String token) { return Jwts.parserBuilder() .setSigningKey(getKey()) .build() .parseClaimsJws(token) .getBody(); } }然后写一个 Web MVC 拦截器在请求进入 Controller 之前校验 Token 是否有效并把用户信息放入ThreadLocal方便后续获取当前登录用户。// 文件路径src/main/java/com/campus/platform/common/interceptor/AuthInterceptor.java package com.campus.platform.common.interceptor; import com.campus.platform.common.util.JwtUtil; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; Component RequiredArgsConstructor public class AuthInterceptor implements HandlerInterceptor { private final JwtUtil jwtUtil; public static final ThreadLocalLong CURRENT_USER_ID new ThreadLocal(); Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { response.setStatus(401); response.getWriter().write({\code\:401,\message\:\未登录或Token已失效\}); return false; } try { Claims claims jwtUtil.parseToken(authHeader.substring(7)); Long userId Long.valueOf(claims.getSubject()); CURRENT_USER_ID.set(userId); return true; } catch (Exception e) { response.setStatus(401); response.getWriter().write({\code\:401,\message\:\登录状态异常请重新登录\}); return false; } } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { CURRENT_USER_ID.remove(); } }这里有一个重要的实践细节拦截器里必须用ThreadLocal来传递用户 ID并且在afterCompletion里手动调用remove()。如果不清理Tomcat 线程池复用线程时下一个请求可能会读到上一个登录用户的 ID造成越权问题。5.2 工单模块的状态机设计工单模块看起来只是几个字段的 CRUD但实际业务中它的状态流转最容易出错。一个标准的报修工单流程是待处理 → 已派单 → 处理中 → 已完成 → 已评价同时支持待处理 → 已取消已派单 → 已退回。这种状态流转如果只靠前端按钮去控制很容易出现逻辑漏洞。更好的方案是在后端 Service 层做一个状态机校验只有允许的状态迁移才能走通。// 文件路径src/main/java/com/campus/platform/service/impl/WorkOrderServiceImpl.java package com.campus.platform.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.campus.platform.entity.WorkOrder; import com.campus.platform.mapper.WorkOrderMapper; import com.campus.platform.service.WorkOrderService; import org.springframework.stereotype.Service; import java.util.Map; import java.util.Set; Service public class WorkOrderServiceImpl extends ServiceImplWorkOrderMapper, WorkOrder implements WorkOrderService { private static final MapInteger, SetInteger ALLOWED_TRANSITIONS Map.of( 0, Set.of(1, 4), // 待处理 - 已派单 / 已取消 1, Set.of(2, 5), // 已派单 - 处理中 / 已退回 2, Set.of(3), // 处理中 - 已完成 3, Set.of(6), // 已完成 - 已评价 4, Set.of(), // 已取消 5, Set.of(0), // 已退回 - 待处理 6, Set.of() // 已评价 ); Override public boolean changeStatus(Long orderId, Integer targetStatus) { WorkOrder order getById(orderId); if (order null) { throw new RuntimeException(工单不存在); } SetInteger allowed ALLOWED_TRANSITIONS.get(order.getStatus()); if (allowed null || !allowed.contains(targetStatus)) { throw new RuntimeException(非法的状态流转 order.getStatus() - targetStatus); } order.setStatus(targetStatus); return updateById(order); } }这个写法的好处非常明显工单的状态流转逻辑集中在一处谁改了什么状态、下一步可以做什么都清晰可见。即使前端页面出现 bug恶意用户拿到接口地址后也不能随意跳状态。在论文中这一部分完全可以用一张“状态流转图”配合代码来展示答辩时也更有说服力。6. 前端核心实现Vue3 Pinia 动态路由后端接口准备好后前端就是重头戏。这里重点讲三个高频模块登录与 Token 存储、动态路由、AI 悬浮助手。6.1 登录页与 Pinia 状态管理登录成功后前端的核心动作是保存 Token、保存用户信息、跳转到首页。在 Vue3 项目里推荐用 Pinia 来全局管理这些状态。// 文件路径src/stores/user.ts import { defineStore } from pinia import { loginApi } from /api/auth import type { LoginParams, LoginResult } from /api/auth/type export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , username: localStorage.getItem(username) || }), actions: { async login(params: LoginParams) { const res await loginApi(params) this.token res.token this.username res.username localStorage.setItem(token, res.token) localStorage.setItem(username, res.username) }, logout() { this.token this.username localStorage.removeItem(token) localStorage.removeItem(username) } } })在 Vue3 组合式 API 的写法下页面组件里使用 Pinia 非常简洁script setup langts import { useUserStore } from /stores/user import { useRouter } from vue-router import { ElMessage } from element-plus const userStore useUserStore() const router useRouter() async function handleLogin() { await userStore.login({ username: admin, password: 123456 }) ElMessage.success(登录成功) router.push(/dashboard) } /script这里的useRouter()是 Vue Router 4 提供的方法也是 Vue3 面试中经常问到的一个点——在 Options API 里你只能this.$router.push但在 Composition API 里必须手动引入useRouter。6.2 动态路由与权限控制管理平台上不同角色的用户看到的菜单不应该完全一样。比如普通员工不应该看到“系统管理”访客管理员不应该看到“工单操作”。实现思路用户在登录后后端返回该用户的角色和路由权限列表前端把这份权限转换成可访问的路由再动态注册。// 文件路径src/router/index.ts import { createRouter, createWebHistory } from vue-router import { useUserStore } from /stores/user const router createRouter({ history: createWebHistory(), routes: [ { path: /login, name: Login, component: () import(/views/login/index.vue) }, { path: /, component: () import(/layout/index.vue), redirect: /dashboard, children: [] } ] }) // 动态注册路由 export function addDynamicRoutes(permissionCodes: string[]) { const modules import.meta.glob(/views/**/*.vue) const permissionMap: Recordstring, any[] { workOrder:view: [{ path: /workOrder, name: WorkOrder, component: modules[/src/views/workOrder/index.vue] }], room:view: [{ path: /meetingRoom, name: MeetingRoom, component: modules[/src/views/meetingRoom/index.vue] }] } permissionCodes.forEach(code { const routes permissionMap[code] if (routes) { routes.forEach(route router.addRoute(Layout, route)) } }) } router.beforeEach((to) { const userStore useUserStore() if (!userStore.token to.path ! /login) { return /login } return true })动态路由的好处是菜单和权限天然绑定。后端只返回用户拥有的权限码前端根据权限码动态挂载路由没有权限的页面即便被猜到 URL也无法渲染出对应组件。6.3 AI 悬浮助手组件的集成“智慧管理平台”中的 AI 能力不一定要做一个复杂的训练模型。更务实的做法是把大模型的 API 能力嵌入到业务场景里。这里给一个典型的 AI 悬浮助手示例用户可以在右侧点击助手图标输入自然语言比如“帮我查一下明天有哪些会议室可用”前端调用后端接口后端把问题发给大模型拿到结果后再返回给前端展示。AiAssistant.vue的简化版本如下script setup langts import { ref } from vue import { sendChatMessage } from /api/ai const visible ref(false) const inputText ref() const messages ref{ role: user | assistant; content: string }[]([]) async function handleSend() { if (!inputText.value.trim()) return messages.value.push({ role: user, content: inputText.value }) const res await sendChatMessage({ message: inputText.value }) messages.value.push({ role: assistant, content: res.reply }) inputText.value } /script template div classai-assistant el-button typeprimary circle clickvisible !visible AI /el-button el-drawer v-modelvisible title智能助手 size420px div classmessage-list div v-for(msg, index) in messages :keyindex :classmsg.role {{ msg.content }} /div /div div classinput-area el-input v-modelinputText placeholder请输入问题 keyup.enterhandleSend/ /div /el-drawer /div /template后端 AI 调用部分的简化 Service// 文件路径src/main/java/com/campus/platform/service/impl/AiAssistantServiceImpl.java package com.campus.platform.service.impl; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Map; Service public class AiAssistantServiceImpl { private final RestTemplate restTemplate new RestTemplate(); Value(${ai.api-key}) private String apiKey; Value(${ai.api-url}) private String apiUrl; public String chat(String userMessage) { HttpHeaders headers new HttpHeaders(); headers.set(Authorization, Bearer apiKey); MapString, Object body Map.of( model, qwen-plus, messages, new Object[]{ Map.of(role, user, content, userMessage) } ); HttpEntityMapString, Object request new HttpEntity(body, headers); Map resp restTemplate.postForObject(apiUrl, request, Map.class); // 根据模型返回结构解析这里仅作为示例 return String.valueOf(resp.get(reply)); } }这个模块是整篇文章最出彩的部分也是答辩时最容易引起老师兴趣的亮点。但要注意生产环境中不要在后端硬编码 API Key应该放到配置中心或环境变量里并且配置调用频率限制。7. 运行效果与功能验证项目写完后验证环节同样重要。建议按下面的顺序从头到尾跑通一遍。7.1 后端启动验证启动 SpringBoot 应用控制台出现类似日志表示启动成功[main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) [main] com.campus.platform.PlatformApplication : Started PlatformApplication in 3.21 seconds然后用 curl 测试登录接口curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}正常响应会返回 token{ code: 200, data: { token: eyJhbGciOiJIUzI1NiJ9.xxx.yyy } }拿到 token 后再请求工单列表接口curl -X GET http://localhost:8080/api/workOrder/list \ -H Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxx.yyy如果返回401优先检查拦截器路径配置和 Token 过期时间。7.2 前端启动验证在前端目录执行npm run dev浏览器访问http://localhost:5173按照流程走一遍进入登录页输入账号密码。登录后看到首页大屏和侧边菜单。点击访客管理新增一条访客记录。切换账号查看不同角色的菜单差异。点击 AI 助手按钮输入一个问题并看到回复。如果侧边菜单没有按预期显示优先检查动态路由是否成功调用addDynamicRoutes以及后端返回的权限码是否和前端约定一致。7.3 效果验证的判断标准一个“一体化智慧管理平台”是否跑通不只是看页面能不能打开而是看核心业务链路是否闭环。以访客模块为例完整闭环是访客提交预约。被访人收到待审批消息并审批通过。访客端生成通行二维码。门卫/前台扫码后系统记录实际到访时间。访客离场时再扫码记录离场时间。只有整条链路都走通了才能说这个模块真正完成。否则只是做到了“表单提交 列表展示”论文里也没法写生产价值。8. 常见问题与排查方法在带 SpringBoot3 和 Vue3 项目时有一些高频问题反复出现。这里整理成一张排查表遇到问题可以直接对照处理。问题现象可能原因排查方式解决方案后端启动报ClassNotFoundException: javax.servlet.*项目引用了旧版依赖SpringBoot3 已改为jakarta规范查看启动日志中的详细异常堆栈统一依赖版本确保使用 SpringBoot 3.x 和兼容的第三方组件前端启动报init_runtime_dom_esm_bundler is not defineVue 版本和构建工具版本不匹配检查package.json中 vue 和 vite 的版本升级 Vue 到 3.4或重新创建 Vite 项目npm install 很慢或安装失败网络原因或 npm 源问题执行npm config get registry更换 npm 镜像源npm config set registry https://registry.npmmirror.comCORS 请求被拦截后端未配置跨域查看浏览器 Network 面板的 CORS 错误后端配置全局跨域过滤器或使用 Nginx 代理转发页面刷新后 404Vue Router 使用 history 模式未配置 fallback访问非根路径时提示错误前端部署时配置 history fallbackNginx 创建try_files规则Element Plus 图标不显示图标未按需引入检查控制台是否有组件警告使用element-plus/icons-vue全局注册数据库中文乱码数据库或连接串字符集不对执行SHOW VARIABLES LIKE character_set%;数据库、表、连接串统一使用utf8mb4其中“刷新页面 404”这个问题在开发环境几乎不会出现但只要部署到 Nginx就一定会遇到。这里给出一个标准的 Nginx 前端配置server { listen 80; server_name campus.example.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }try_files $uri $uri/ /index.html;这行的作用就是当用户访问一个前端路由路径如/meetingRoom刷新页面时Nginx 找不到对应的物理文件就让它回到index.html再由 Vue Router 接管页面渲染。9. 最佳实践与工程建议到这里核心功能已经能跑通。但如果你希望这个项目不仅是“能运行”还要能写进简历、能通过毕业论文答辩下面这些工程建议值得认真看。9.1 后端工程实践统一返回结构。不要每个接口返回裸对象。定义一个ResultT包装类至少包含code、message、data三个字段。这样前端拦截器处理错误时非常方便。参数校验不要手写。使用ValidatedNotBlank等注解做参数校验。手写 if 判断既啰嗦又容易漏。敏感操作记录日志。不是所有操作都要打日志但审批、删除、修改用户角色这类高风险操作必须记录操作人和操作时间。MySQL 表字段尽量使用bigint自增主键。不要用 UUID 做数据库主键索引查询性能和存储空间都不理想。接口路径统一规范。建议格式为/api/{模块}/{资源}例如/api/workOrder/list、/api/visitOrder/approve。敏感信息加密存储。用户密码必须使用 BCrypt 或 Spring Security 的PasswordEncoder加密禁止明文存储。生产环境关闭接口文档暴露。如果使用了 knife4j 等文档工具生产环境必须配置关闭避免接口信息泄露。Redis 缓存只缓存热点数据。比如首页统计、菜单权限、验证码。不要什么都往 Redis 里放否则缓存和数据库的数据一致性会变成很大的麻烦。涉及数据库修改的操作先备份。修改表结构或跑批量更新 SQL 前先mysqldump备份一次。日志分级规范。业务异常用warn系统异常用error操作流水用info。不要全程无差别地使用info排障时会非常痛苦。9.2 前端工程实践规范组件命名。文件夹和组件名统一使用大驼峰比如WorkOrderForm.vue。不要出现work_order_form.vue这类混搭命名。不要直接操作 localStorage 存复杂对象。存 Token 和用户名是可以的但不要存一整个用户对象。如果需要全局用户信息放到 Pinia 里。统一封装 Axios。在src/utils/request.ts中封装 Axios 实例统一配置baseURL、请求头、超时时间、401 跳转逻辑。环境变量用import.meta.env。Vite 项目里不要写死接口地址。设置.env.development和.env.production通过import.meta.env.VITE_API_BASE_URL访问。代码提交前做 ESLint 检查。至少保证npm run lint能通过不能只靠开发时自己看。路由组件使用懒加载。所有页面组件用动态import()引入避免首屏加载过大。通用逻辑抽成 Composables。比如分页处理、列表筛选、表单弹窗逻辑不要在每个页面里重复写一份。9.3 AI 能力接入的注意事项如果你在项目里接入了大模型 API需要注意三个问题。第一调用成本控制。不能让用户无限次调用 AI 接口。可以做两层限制前端按钮防重复提交后端记录每个用户的每日调用次数。第二回答内容不可控。大模型的输出不一定完全准确。在 UI 上要设计免责提示比如“内容由 AI 生成仅供参考”降低潜在风险。第三超时和异常处理。大模型接口响应可能很慢后端一定要设置超时时间避免阻塞线程池。可以在前端提示用户“正在生成回答可能需要 10~20 秒”。10. 论文写作与毕业设计答辩建议这个项目既然带了“毕业论文”的标签最后专门聊一下论文部分的思路这部分在答辩时往往最容易被提问。10.1 论文结构建议一篇软件类毕业设计论文推荐按下面的大纲组织第一章 绪论课题背景、国内外研究现状、研究意义。第二章 相关技术介绍SpringBoot3、Vue3、MyBatis-Plus、Redis、AI 大模型 API。第三章 系统分析需求分析、可行性分析、用例图、业务流程。第四章 系统设计总体架构设计、功能模块设计、数据库设计。第五章 系统实现每个核心模块的页面截图、核心代码、流水账式讲解。第六章 系统测试功能测试用例、结果分析。第七章 总结与展望。10.2 答辩高频问题根据往年经验答辩老师最喜欢问这几个问题这个项目为什么选择 SpringBoot3 Vue3相比 SpringBoot2 Vue2 有哪些优势回答思路SpringBoot3 基于 Spring Framework 6支持 JDK17 和 GraalVM 原生镜像Vue3 的 Composition API 和响应式代理机制在大项目中逻辑复用和维护更方便。JWT 和 Session 有什么区别为什么不用 Session回答思路Session 是服务端存储多节点场景需要共享JWT 是无状态认证适合前后端分离和水平扩展。如何保证接口的安全性回答思路JWT 鉴权、角色权限校验、参数校验、SQL 注入防护MyBatis 预编译、敏感信息加密。AI 是如何集成到系统中的为什么不用自己训练模型回答思路通过通用大模型 API 以 HTTP 方式调用降低训练成本系统将自然语言请求转成结构化查询参数再调用对应接口返回结果。论文写作时要有“问题意识”。不要只写“这个页面实现了什么功能”而是写“这个功能解决了什么痛点有哪些备选方案为什么选了这个方案”。论文的分数和答辩的表现差别往往就体现在这些地方。11. 总结与后续学习方向这篇博文基于 SpringBoot3 Vue3梳理了一个高校/园区一体化智慧管理平台从技术选型、模块设计、后端实现、前端实现到 AI 能力接入的完整过程。核心价值可以概括为三点第一这个项目是 SpringBoot3 Vue3 全栈实践的一个紧凑而完整的样例不是零散的知识点拼接。跑通这个项目后你对defineProps、defineEmits、Pinia、动态路由、JWT 登录、状态机流转这些高频开发点的理解会踏实很多。第二AI 不是高不可攀的东西。通过一个简单的 API 调用接入智能助手就能把“智慧管理平台”从普通的 CRUD 系统提升到具备智能交互能力的管理系统这在毕业设计和简历上都是很好的亮点。第三项目的成功运行只是第一步。真正的能力成长发生在你排查问题的过程中在你重新审视表结构设计时在你思考某个流程为什么要用状态机而不是简单改状态字段时。接下来你可以继续深入的方向有很多把系统部署到云服务器并用 Nginx 做反向代理使用 Spring AI 框架替代手写 RestTemplate 调用增加数据可视化大屏把访客流量、会议室使用率等数据通过 ECharts 展示在首页对登录模块增加验证码和手机号绑定把权限模型从 RBAC 升级为更精细的 ABAC 模型。建议收藏这篇文章把它当作动手实践时的参考清单。代码可以从后端认证开始写再写工单模块最后写前端页面和 AI 助手。每完成一个模块都实际运行验证一遍比一次性写完所有代码再调试要高效得多。