最近在辅导学生毕业设计和指导团队开发时,发现很多同学在构建一个完整的、可落地的“前后端分离”项目时,常常感到无从下手。特别是像家政服务平台这类涉及多角色、多流程的业务系统,如何从零开始,清晰地划分前后端职责,并实现高效的数据交互,是大家普遍遇到的难题。本文将围绕一个基于 SpringBoot 和 Vue 的家政服务平台,为你拆解从环境搭建、技术选型到核心功能实现的完整闭环。无论你是正在寻找毕业设计课题的计算机专业学生,还是希望巩固前后端分离开发经验的初级开发者,都能从这篇实战指南中获得可直接复用的代码和清晰的架构思路。
1. 项目背景与核心概念
1.1 什么是家政服务平台?
家政服务平台是一个连接家政服务人员(如保洁师、保姆、月嫂)与雇主的在线中介系统。它本质上是一个B2C或C2C的电子商务平台,但交易标的物是“服务”而非实体商品。核心业务流程包括:雇主发布需求、服务人员接单、双方沟通确认、服务执行、在线支付、服务评价等。
1.2 为什么选择 SpringBoot + Vue 前后端分离架构?
对于毕业设计或中小型实战项目,SpringBoot + Vue 的组合是目前非常主流且高效的技术选型。
- SpringBoot:作为后端框架,它极大地简化了基于 Spring 的应用初始搭建和开发过程。通过自动配置和起步依赖,开发者可以快速构建出独立运行、生产级别的微服务或单体应用,无需繁琐的 XML 配置。它天然适合构建 RESTful API,为前端 Vue 应用提供清晰的数据接口。
- Vue.js:作为前端渐进式框架,Vue 的核心库只关注视图层,易于上手,且能与现代工具链(如 Vue CLI、Vite)以及各种支持类库结合,轻松构建复杂的单页面应用(SPA)。其响应式数据绑定和组件化开发模式,能极大地提升前端开发效率和用户体验。
- 前后端分离:这种架构模式将前端展示逻辑与后端业务逻辑、数据访问彻底解耦。后端专注于提供 API 接口,前端负责渲染和用户交互。这样做的好处是:
- 职责清晰:前后端开发可以并行进行,通过接口文档进行协作。
- 技术栈灵活:后端可以替换为其他语言框架,前端亦然,只要接口协议不变。
- 易于维护和扩展:前后端可以独立部署、升级和伸缩。
- 更适合现代前端开发:可以利用 Vue Router、Vuex/Pinia 等生态构建复杂的交互应用。
1.3 本系统核心功能模块设计
一个完整的家政服务平台通常包含以下核心模块:
- 用户管理:区分雇主、家政人员、系统管理员三种角色,实现注册、登录、个人信息管理。
- 服务项目管理:管理员或服务人员可以发布、上架、下架各类家政服务(如日常保洁、深度清洁、家电清洗),并设置价格、时长、描述等。
- 订单管理:雇主浏览服务并下单,服务人员接单,形成订单的生命周期管理(待接单、已接单、服务中、待支付、已完成、已评价、已取消)。
- 支付模块:集成第三方支付(如支付宝、微信支付沙箱环境),实现订单的在线支付。
- 评价系统:服务完成后,雇主可以对服务人员进行评分和文字评价。
- 后台管理:管理员拥有最高权限,可以管理所有用户、服务项目、订单,查看统计数据等。
2. 环境准备与版本说明
在开始编码之前,请确保你的开发环境已就绪。以下版本为本文示例所使用,你可以根据实际情况进行调整。
2.1 后端开发环境
- 操作系统:Windows 10/11, macOS 或 Linux 均可。
- JDK:Java Development Kit 17(LTS长期支持版本)。Spring Boot 3.x 要求最低 JDK 17。
- IDE:IntelliJ IDEA Ultimate 或 Community Edition(推荐),或 Eclipse with STS。
- 构建工具:Apache Maven 3.6.3+ 或 Gradle。
- 数据库:MySQL 8.0+。我们将使用 MySQL 作为持久化存储。
- 项目管理:使用 Spring Initializr 快速生成项目骨架。
2.2 前端开发环境
- Node.js:版本 16.x 或 18.x(LTS)。这是运行 npm 和 Vue CLI 的基础。
- 包管理器:npm 或 yarn。本文使用 npm。
- IDE:Visual Studio Code(推荐),或 WebStorm。
- Vue 脚手架:Vue CLI 或 Vite。本文使用 Vue CLI 创建项目。
2.3 关键依赖版本(后端)
通过 Spring Initializr 生成项目时,选择以下依赖:
- Spring Boot:3.1.x 或 3.2.x(稳定版)
- 依赖项:
- Spring Web(用于构建 RESTful API)
- Spring Data JPA(简化数据库操作)
- MySQL Driver(MySQL 数据库连接驱动)
- Lombok(简化实体类代码)
- Spring Boot DevTools(开发热部署)
- Spring Security(可选,用于权限控制,毕业设计可简化)
2.4 关键依赖版本(前端)
使用 Vue CLI 创建项目时,选择 Vue 3 生态:
- Vue:3.x
- Vue Router:4.x(用于前端路由)
- 状态管理:Pinia(Vue 官方推荐的状态管理库,替代 Vuex)
- UI 组件库:Element Plus(基于 Vue 3 的桌面端组件库)或 Vant(移动端组件库,根据项目定位选择)。本文示例使用 Element Plus。
- HTTP 客户端:Axios(用于发起 HTTP 请求)
3. 后端核心实现:SpringBoot 项目搭建与 API 开发
3.1 创建 SpringBoot 项目
- 访问 start.spring.io 。
- 选择项目信息:
- Project: Maven Project
- Language: Java
- Spring Boot: 3.2.5 (SNAPSHOT)
- Project Metadata:
- Group:
com.homemaking - Artifact:
server - Packaging: Jar
- Java: 17
- Group:
- 添加依赖:
Spring Web,Spring Data JPA,MySQL Driver,Lombok。 - 点击“GENERATE”下载项目压缩包,解压后用 IDEA 打开。
3.2 数据库设计与实体类映射
我们首先设计核心表结构,并使用 JPA 实体类进行映射。
1. 用户表 (sys_user)
CREATE TABLE `sys_user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码', `nickname` varchar(50) DEFAULT NULL COMMENT '昵称', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `avatar` varchar(255) DEFAULT NULL COMMENT '头像', `role` varchar(20) NOT NULL DEFAULT 'EMPLOYER' COMMENT '角色:EMPLOYER-雇主, WORKER-家政人员, ADMIN-管理员', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用, 1-正常', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户表';对应的 Java 实体类:
// 文件路径:src/main/java/com/homemaking/entity/User.java package com.homemaking.entity; import jakarta.persistence.*; import lombok.Data; import java.time.LocalDateTime; @Entity @Table(name = "sys_user") @Data // Lombok 注解,自动生成 getter, setter, toString 等 public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true, length = 50) private String username; @Column(nullable = false, length = 100) private String password; @Column(length = 50) private String nickname; @Column(length = 20) private String phone; @Column(length = 255) private String avatar; @Column(nullable = false, length = 20) private String role = "EMPLOYER"; // 默认角色为雇主 @Column(nullable = false) private Integer status = 1; // 1-正常, 0-禁用 @Column(updatable = false) private LocalDateTime createTime; private LocalDateTime updateTime; // JPA 生命周期回调:在持久化前设置创建时间 @PrePersist protected void onCreate() { createTime = LocalDateTime.now(); updateTime = LocalDateTime.now(); } // JPA 生命周期回调:在更新前设置更新时间 @PreUpdate protected void onUpdate() { updateTime = LocalDateTime.now(); } }2. 服务项目表 (service_item)
CREATE TABLE `service_item` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '服务名称', `description` text COMMENT '服务描述', `price` decimal(10,2) NOT NULL COMMENT '服务价格(元)', `duration` int DEFAULT NULL COMMENT '预计时长(分钟)', `category` varchar(50) DEFAULT NULL COMMENT '服务分类(如:日常保洁、深度清洁)', `worker_id` bigint DEFAULT NULL COMMENT '关联的服务人员ID(可为空,表示平台通用服务)', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-下架, 1-上架', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_worker_id` (`worker_id`), CONSTRAINT `fk_service_worker` FOREIGN KEY (`worker_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='服务项目表';对应的ServiceItem实体类请参照User实体自行创建,注意使用@ManyToOne关联User实体。
3.3 数据访问层:Repository 接口
Spring Data JPA 的强大之处在于,只需定义接口,无需实现。
// 文件路径:src/main/java/com/homemaking/repository/UserRepository.java package com.homemaking.repository; import com.homemaking.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.Optional; @Repository public interface UserRepository extends JpaRepository<User, Long> { // 根据用户名查找用户 Optional<User> findByUsername(String username); // 判断用户名是否存在 boolean existsByUsername(String username); // 根据角色查找用户列表 List<User> findByRole(String role); }ServiceItemRepository接口可以定义类似的方法,如findByCategory、findByStatus等。
3.4 业务逻辑层:Service 实现
Service 层负责处理核心业务逻辑。这里以用户注册和登录为例。
首先,定义一个通用的响应结果类:
// 文件路径:src/main/java/com/homemaking/common/Result.java package com.homemaking.common; import lombok.Data; @Data public class Result<T> { private Integer code; // 状态码,如 200成功, 500失败 private String msg; // 提示信息 private T data; // 返回的数据 public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMsg("操作成功"); result.setData(data); return result; } public static <T> Result<T> success(String msg, T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMsg(msg); result.setData(data); return result; } public static <T> Result<T> error(String msg) { Result<T> result = new Result<>(); result.setCode(500); result.setMsg(msg); return result; } }用户服务接口和实现:
// 文件路径:src/main/java/com/homemaking/service/UserService.java package com.homemaking.service; import com.homemaking.common.Result; import com.homemaking.entity.User; public interface UserService { Result<User> register(User user); Result<String> login(String username, String password); // ... 其他方法 }// 文件路径:src/main/java/com/homemaking/service/impl/UserServiceImpl.java package com.homemaking.service.impl; import com.homemaking.common.Result; import com.homemaking.entity.User; import com.homemaking.repository.UserRepository; import com.homemaking.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; import java.util.Optional; @Service @RequiredArgsConstructor // Lombok 注解,为 final 字段生成构造函数 public class UserServiceImpl implements UserService { private final UserRepository userRepository; @Override public Result<User> register(User user) { // 1. 校验用户名是否已存在 if (userRepository.existsByUsername(user.getUsername())) { return Result.error("用户名已存在"); } // 2. 对密码进行 MD5 加密(生产环境应使用更安全的 BCrypt) String encryptedPassword = DigestUtils.md5DigestAsHex( user.getPassword().getBytes(StandardCharsets.UTF_8) ); user.setPassword(encryptedPassword); // 3. 设置默认角色(如果未设置) if (user.getRole() == null) { user.setRole("EMPLOYER"); } // 4. 保存用户 User savedUser = userRepository.save(user); // 5. 返回时清空密码 savedUser.setPassword(null); return Result.success("注册成功", savedUser); } @Override public Result<String> login(String username, String password) { // 1. 根据用户名查找用户 Optional<User> userOpt = userRepository.findByUsername(username); if (userOpt.isEmpty()) { return Result.error("用户名或密码错误"); } User user = userOpt.get(); // 2. 校验密码 String encryptedInputPassword = DigestUtils.md5DigestAsHex( password.getBytes(StandardCharsets.UTF_8) ); if (!user.getPassword().equals(encryptedInputPassword)) { return Result.error("用户名或密码错误"); } // 3. 校验用户状态 if (user.getStatus() == 0) { return Result.error("账号已被禁用"); } // 4. 登录成功,这里简单返回一个成功信息。实际项目应生成 JWT Token 返回。 return Result.success("登录成功", "用户ID: " + user.getId()); } }3.5 控制层:RESTful API 控制器
Controller 层接收前端请求,调用 Service,并返回 JSON 数据。
// 文件路径:src/main/java/com/homemaking/controller/UserController.java package com.homemaking.controller; import com.homemaking.common.Result; import com.homemaking.entity.User; import com.homemaking.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/user") @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping("/register") public Result<User> register(@RequestBody User user) { return userService.register(user); } @PostMapping("/login") public Result<String> login(@RequestParam String username, @RequestParam String password) { return userService.login(username, password); } // 其他接口:获取用户信息、修改信息等 @GetMapping("/{id}") public Result<User> getUserById(@PathVariable Long id) { // 此处省略 Service 层方法实现,应返回用户信息(密码置空) return Result.success("查询成功", null); // 示例 } }3.6 应用配置文件
配置数据库连接和 JPA 属性。
# 文件路径:src/main/resources/application.yml spring: datasource: url: jdbc:mysql://localhost:3306/homemaking_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: your_password # 替换为你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 开发环境可用 update,生产环境应设为 validate 或 none,并使用 Flyway/Liquibase show-sql: true # 控制台打印 SQL,便于调试 properties: hibernate: format_sql: true # 格式化 SQL 输出 dialect: org.hibernate.dialect.MySQL8Dialect server: port: 8080 # 后端服务端口 # 自定义配置(示例) app: jwt: secret: your-jwt-secret-key # 用于 JWT 签名的密钥 expiration: 86400000 # token 过期时间(毫秒),24小时3.7 启动与测试
- 在 MySQL 中创建数据库
homemaking_db。 - 修改
application.yml中的数据库密码。 - 找到主启动类
HomemakingApplication(位于src/main/java/com/homemaking),运行它。 - 使用 Postman 或浏览器测试 API:
POST http://localhost:8080/api/user/registerBody (JSON):{"username":"test","password":"123456","nickname":"测试用户"}POST http://localhost:8080/api/user/login?username=test&password=123456
4. 前端核心实现:Vue 3 + Element Plus 项目搭建
4.1 创建 Vue 项目并安装依赖
# 1. 安装 Vue CLI(如果未安装) npm install -g @vue/cli # 2. 创建项目 vue create homemaking-frontend # 选择 Manually select features # 勾选:Babel, Router, Vuex (或 Pinia), CSS Pre-processors (如 Sass) # Vue 版本选择 3.x # Router 使用 history 模式? Yes # CSS 预处理器选择 Sass/SCSS # 其他配置按需选择或默认 # 3. 进入项目目录并安装 Element Plus 和 Axios cd homemaking-frontend npm install element-plus axios # 或者使用按需导入(推荐,减小打包体积) # npm install element-plus @element-plus/icons-vue # npm install -D unplugin-vue-components unplugin-auto-import4.2 配置 Element Plus 和 Axios
1. 全局引入 Element Plus (main.js 或 main.ts)
// 文件路径:src/main.js import { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' // 如果使用 Vuex // 引入 Element Plus 及其样式 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // 引入 axios 并配置全局实例 import axios from 'axios' axios.defaults.baseURL = 'http://localhost:8080' // 后端 API 地址 const app = createApp(App) app.use(store) app.use(router) app.use(ElementPlus) app.mount('#app')2. 创建统一的请求拦截器 (src/utils/request.js)
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '../router' // 创建 axios 实例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 可从 .env 文件读取 timeout: 10000 // 请求超时时间 }) // 请求拦截器 service.interceptors.request.use( config => { // 在发送请求之前做些什么,例如添加 token const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }, error => { // 对请求错误做些什么 console.error('Request Error:', error) return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response => { // 对响应数据做点什么 const res = response.data // 假设后端统一返回格式为 { code, msg, data } if (res.code !== 200) { ElMessage.error(res.msg || '请求失败') // 如果是 token 过期等特定错误码,可以跳转到登录页 if (res.code === 401) { router.push('/login') } return Promise.reject(new Error(res.msg || 'Error')) } else { return res.data // 直接返回后端定义的 data 字段 } }, error => { // 对响应错误做点什么 console.error('Response Error:', error) ElMessage.error(error.message || '网络错误') return Promise.reject(error) } ) export default service4.3 实现用户登录页面
<!-- 文件路径:src/views/Login.vue --> <template> <div class="login-container"> <el-card class="login-card"> <h2 style="text-align: center; margin-bottom: 30px;">家政服务平台</h2> <el-form :model="loginForm" :rules="loginRules" ref="loginFormRef" label-width="80px"> <el-form-item label="用户名" prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" @keyup.enter="handleLogin"></el-input> </el-form-item> <el-form-item> <el-button type="primary" style="width: 100%;" :loading="loading" @click="handleLogin">登录</el-button> </el-form-item> <div style="text-align: center; font-size: 14px;"> 还没有账号?<el-link type="primary" @click="$router.push('/register')">立即注册</el-link> </div> </el-form> </el-card> </div> </template> <script setup> import { ref, reactive } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import request from '@/utils/request' // 导入封装好的 axios 实例 const router = useRouter() const loginFormRef = ref() const loginForm = reactive({ username: '', password: '' }) const loginRules = { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] } const loading = ref(false) const handleLogin = () => { loginFormRef.value.validate((valid) => { if (valid) { loading.value = true // 使用 FormData 或 URLSearchParams 发送表单数据 const params = new URLSearchParams() params.append('username', loginForm.username) params.append('password', loginForm.password) request.post('/api/user/login', params) .then(res => { ElMessage.success('登录成功') // 假设后端返回了 token,存储到 localStorage // localStorage.setItem('token', res.token) // 存储用户信息到 Vuex/Pinia // store.commit('user/SET_USER_INFO', res.userInfo) // 跳转到首页 router.push('/') }) .catch(err => { console.error('登录失败:', err) // 错误信息已在 request.js 的拦截器中统一提示 }) .finally(() => { loading.value = false }) } else { console.log('表单验证失败') return false } }) } </script> <style scoped> .login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f7fa; } .login-card { width: 400px; } </style>4.4 实现服务列表页面
<!-- 文件路径:src/views/ServiceList.vue --> <template> <div class="service-list"> <el-row :gutter="20"> <el-col :span="6" v-for="item in serviceList" :key="item.id" style="margin-bottom: 20px;"> <el-card :body-style="{ padding: '0px' }"> <img :src="item.image || 'https://via.placeholder.com/300x200'" class="service-image" /> <div style="padding: 14px;"> <h3>{{ item.name }}</h3> <p class="description">{{ item.description }}</p> <div class="bottom"> <span class="price">¥{{ item.price }}</span> <span class="duration">{{ item.duration }}分钟</span> <el-button type="primary" size="small" @click="handleOrder(item)">立即预约</el-button> </div> </div> </el-card> </el-col> </el-row> <!-- 分页组件 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue' import { ElMessage } from 'element-plus' import request from '@/utils/request' const serviceList = ref([]) const total = ref(0) const queryParams = reactive({ pageNum: 1, pageSize: 10, category: '', keyword: '' }) // 获取服务列表 const fetchServiceList = () => { request.get('/api/service/list', { params: queryParams }) .then(res => { serviceList.value = res.list || [] total.value = res.total || 0 }) .catch(err => { console.error('获取服务列表失败:', err) }) } const handleSizeChange = (val) => { queryParams.pageSize = val fetchServiceList() } const handleCurrentChange = (val) => { queryParams.pageNum = val fetchServiceList() } const handleOrder = (item) => { // 跳转到订单确认页面,传递服务ID // this.$router.push({ path: '/order/confirm', query: { serviceId: item.id } }) ElMessage.info(`预约服务: ${item.name}`) } onMounted(() => { fetchServiceList() }) </script> <style scoped> .service-image { width: 100%; height: 200px; object-fit: cover; } .description { color: #999; font-size: 14px; margin: 10px 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .bottom { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; } .price { color: #f56c6c; font-size: 18px; font-weight: bold; } .duration { color: #909399; font-size: 14px; } </style>5. 前后端联调与跨域问题解决
当前后端运行在不同端口(如前端localhost:8081,后端localhost:8080)时,浏览器会因为同源策略而阻止请求,这就是跨域问题。
5.1 后端解决跨域(推荐)
在 SpringBoot 后端项目中添加一个全局跨域配置类。
// 文件路径:src/main/java/com/homemaking/config/CorsConfig.java package com.homemaking.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // 允许的域,不要写*,否则cookie无法传递 config.addAllowedOrigin("http://localhost:8081"); // 你的前端地址 // 允许的请求方式 config.addAllowedMethod("*"); // 允许的头信息 config.addAllowedHeader("*"); // 允许携带凭证(如 cookie) config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 对所有接口路径生效 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }5.2 前端代理解决跨域(开发环境)
在 Vue 项目的vue.config.js文件中配置代理。
// 文件路径:vue.config.js const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, devServer: { port: 8081, // 前端开发服务器端口 proxy: { '/api': { // 匹配所有以 /api 开头的请求路径 target: 'http://localhost:8080', // 代理目标的基础路径 changeOrigin: true, // 支持跨域 pathRewrite: { '^/api': '' // 重写路径,去掉 /api 前缀 } } } } })配置后,前端请求/api/user/login会被代理到http://localhost:8080/user/login。
6. 常见问题与排查思路
在开发过程中,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 后端启动失败,端口被占用 | 8080 端口已被其他程序(如另一个 SpringBoot 应用、Tomcat)使用。 | 1. 修改application.yml中的server.port。2. 命令行执行 netstat -ano | findstr :8080(Windows) 或lsof -i :8080(Mac/Linux) 找到进程并结束。 |
前端npm run serve报错 | Node.js 版本不兼容,或依赖包安装不完整。 | 1. 检查 Node.js 版本:node -v,确保是 16.x 或 18.x LTS。2. 删除 node_modules和package-lock.json,重新执行npm install。 |
| 访问后端 API 返回 404 | 1. 后端接口路径写错。 2. 后端 Controller 未正确映射。 3. 跨域配置未生效。 | 1. 检查浏览器 Network 面板,确认请求的 URL 是否正确。 2. 在后端 Controller 类和方法上添加 @RestController和@RequestMapping/@PostMapping等注解。3. 检查后端 CorsConfig是否生效,允许了前端源。 |
| 数据库连接失败 | 1. MySQL 服务未启动。 2. application.yml中数据库配置错误(用户名、密码、URL)。3. 数据库驱动版本不匹配。 | 1. 确保 MySQL 服务已启动。 2. 仔细核对 application.yml中的spring.datasource配置。3. 检查 pom.xml中mysql-connector-java版本,SpringBoot 3.x 建议使用 8.x 驱动。 |
| 前端页面空白,控制台报错 | 1. 路由配置错误。 2. 组件引入错误。 3. 浏览器缓存。 | 1. 检查浏览器控制台 (Console) 的具体错误信息。 2. 检查 router/index.js中的路由配置是否正确。3. 尝试 Ctrl+F5强制刷新浏览器,或使用无痕模式。 |
| JPA 实体类映射表失败 | 1. 实体类注解(如@Entity,@Table)缺失或错误。2. 数据库表不存在。 3. ddl-auto配置问题。 | 1. 检查实体类是否添加了@Entity和@Table(name=“表名”)。2. 检查数据库中表是否创建,或设置 spring.jpa.hibernate.ddl-auto=update让 Hibernate 自动建表(仅限开发)。3. 查看启动日志,是否有表创建或映射的 SQL 输出。 |
| 前端请求后端成功,但收不到响应数据 | 1. 后端 Controller 方法没有@ResponseBody或类不是@RestController。2. 后端返回的数据格式不是 JSON。 3. 前端 axios 拦截器处理响应数据有误。 | 1. 确保 Controller 类上有@RestController注解。2. 使用 Postman 直接测试后端接口,看返回的 JSON 格式是否正确。 3. 检查 src/utils/request.js中响应拦截器的逻辑,是否正确地返回了res.data。 |
7. 项目扩展与最佳实践
完成基础功能后,你可以从以下方向深化项目,这会让你的毕业设计或项目经验更具竞争力。
7.1 引入 JWT 实现无状态认证
上述示例的登录只是简单验证,生产环境需要使用 Token(如 JWT)来管理用户会话。
- 添加依赖:在
pom.xml中加入jjwt相关依赖。 - 创建 JWT 工具类:用于生成和解析 Token。
- 修改登录逻辑:登录成功后,生成 JWT 并返回给前端。
- 添加拦截器/过滤器:在后台验证请求头中的 Token 是否有效。
- 前端存储 Token:登录成功后,将 Token 存入
localStorage或Vuex/Pinia,并在后续请求的Authorization头中携带。
7.2 使用 Spring Security 进行权限控制
对于管理员、雇主、家政人员等不同角色,其可访问的 API 和页面应不同。
- 引入依赖:
spring-boot-starter-security。 - 配置 Security 配置类:继承
WebSecurityConfigurerAdapter(Spring Boot 2.x) 或使用SecurityFilterChainBean (Spring Boot 3.x),配置 URL 权限规则、密码加密方式(BCrypt)、登录成功/失败处理等。 - 在 Controller 方法上使用注解:如
@PreAuthorize(“hasRole(‘ADMIN’)”)来控制方法级别的访问。
7.3 集成 MyBatis-Plus 增强数据操作
如果你觉得 JPA 的 SQL 不够灵活,或想使用更强大的条件构造器,可以集成 MyBatis-Plus。
- 添加依赖:替换
spring-boot-starter-data-jpa为mybatis-plus-boot-starter。 - 配置 Mapper 扫描:在启动类加
@MapperScan。 - 创建 Mapper 接口:继承
BaseMapper,即可获得丰富的 CRUD 方法。 - 使用 QueryWrapper:进行复杂的动态 SQL 查询。
7.4 前端路由守卫与权限校验
防止用户未登录就访问需要认证的页面。
// 文件路径:src/router/index.js import { createRouter, createWebHistory } from 'vue-router' import store from '../store' // 假设使用 Vuex 存储用户状态 const routes = [ { path: '/login', component: () => import('@/views/Login.vue') }, { path: '/dashboard', component: () => import('@/views/Dashboard.vue'), meta: { requiresAuth: true } // 需要认证的元信息 }, // ... 其他路由 ] const router = createRouter({ history: createWebHistory(), routes }) // 全局前置守卫 router.beforeEach((to, from, next) => { const isAuthenticated = store.state.user.token // 检查是否有 token if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) { // 如果目标路由需要认证,且用户未登录,则跳转到登录页 next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() // 放行 } }) export default router7.5 项目部署建议
- 后端:使用
mvn clean package打包成可执行的 JAR 文件,在服务器上通过java -jar your-app.jar运行。建议配合nohup或 systemd 服务保持后台运行。对于生产环境,务必配置好数据库连接池、日志、监控和 HTTPS。 - 前端:运行
npm run build生成静态文件(位于dist目录)。可以将这些文件部署到 Nginx 或 Apache 等 Web 服务器上。记得配置 Nginx 的反向代理,将/api路径的请求转发到后端服务。 - 数据库:做好定期备份。对于毕业设计,可以使用云数据库(如阿里云 RDS、腾讯云 CDB)的免费试用套餐,避免本地环境问题。
通过以上步骤,你已经完成了一个具备核心功能的 SpringBoot + Vue 前后端分离家政服务平台。从环境搭建、数据库设计、API 开发到前端页面实现和联调,覆盖了一个完整 Web 应用开发的主要环节。你可以在此基础上,继续实现订单管理、支付集成(如支付宝沙箱)、评价系统、后台管理面板等更复杂的功能,使其成为一个丰满的毕业设计或作品集项目。记住,在开发过程中,多查阅官方文档,善用调试工具,遇到问题优先看日志和错误信息,祝你编码顺利!