FEATURED · 精选文章

Spring Boot + Vue3 全栈实战:从零构建宠物服务平台

发布时间 / 2026/8/21 23:40:06
来源 / 创域科博编辑部
栏目 / 资讯中心
Spring Boot + Vue3 全栈实战:从零构建宠物服务平台 在实际毕业设计或课程项目中选择一个贴近生活、功能完整且技术栈主流的选题是平衡学习深度与项目可行性的关键。“爪爪帮”这类基于移动平台的宠物服务应用恰好满足了这一需求。它通常需要处理用户与宠物服务提供者之间的信息匹配、订单流转、支付结算等核心业务非常适合作为 Spring Boot 后端、Vue3 前端这一经典技术组合的实战演练场。本文将以一个毕业设计级别的“爪爪帮”平台为例带你从零开始理解其业务模型并完成一个具备用户端与服务端、包含基础增删改查与状态流转的演示系统。你将不仅学会如何搭建项目骨架更能掌握如何将业务需求转化为具体的数据库设计、接口定义和前后端交互为你的毕业设计或首个全栈项目提供一个清晰的实现蓝本。1. 项目核心业务与架构设计在动手编码之前必须厘清系统要解决什么问题以及各个部分如何协作。一个清晰的业务模型和架构设计能避免后续开发陷入混乱。1.1 “爪爪帮”平台业务模型解析“爪爪帮”本质上是一个连接宠物主人用户端与宠物服务者服务端/商家端的 O2O 平台。其核心业务流程可以抽象为以下几个环节服务发布与发现服务提供者如宠物美容师、遛狗员、寄养家庭在平台上发布可预订的服务包括服务类型、价格、描述、可用时间等。宠物主人则可以根据位置、服务类型、评价等条件筛选和发现服务。订单创建与支付宠物主人选定服务后创建订单并支付。支付环节可能涉及第三方支付网关的集成在毕业设计中常简化为模拟支付。订单状态流转订单产生后会经历一系列状态变化例如“待接单”、“已接单”、“服务中”、“已完成”、“已取消”。状态机是此类业务系统的核心逻辑。评价与反馈服务完成后双方可以互相评价形成平台的信用体系。对于毕业设计我们通常聚焦于实现一个最小可行产品涵盖用户管理、服务管理、订单管理这三个核心模块。1.2 技术栈选型与前后端分离架构基于当前主流且资料丰富的技术生态我们选择如下技术栈后端Spring Boot 2.7.x。它提供了快速构建、自动配置、内嵌容器等特性极大简化了 Java Web 后端开发。配合 Spring MVC 处理 Web 请求Spring Data JPA 或 MyBatis-Plus 操作数据库Spring Security 处理安全认证毕业设计初期可简化。前端Vue 3 TypeScript Vite。Vue 3 的 Composition API 提供了更好的逻辑复用和组织能力。TypeScript 能增强代码的健壮性和可维护性。Vite 作为新一代构建工具开发体验更优。UI 组件库Element Plus。它是为 Vue 3 设计的组件库提供了丰富、美观且易用的 UI 组件能快速搭建出规范的管理后台界面。数据库MySQL 8.0。关系型数据库适合存储结构化的用户、服务、订单数据。项目管理与构建后端使用 Maven前端使用 npm/pnpm。系统采用经典的前后端分离架构[用户浏览器] --(HTTP/HTTPS)-- [Nginx] --(代理)-- [Vue3 前端静态资源] | | (API 请求) v [Spring Boot 应用] | v [MySQL 数据库]开发阶段我们可以利用 Vue CLI 或 Vite 的代理功能让前端开发服务器将 API 请求转发到后端的 Spring Boot 应用运行在localhost:8080从而解决跨域问题。2. 开发环境准备与项目初始化一个稳定的开发环境是项目顺利推进的基础。请确保你的本地环境已安装以下必要组件。2.1 后端 Spring Boot 项目搭建我们使用 Spring Initializr 来快速生成项目骨架。访问 Spring Initializr打开浏览器访问https://start.spring.io。配置项目元数据Project: Maven ProjectLanguage: JavaSpring Boot: 选择 2.7.x 版本这是一个长期支持版本稳定且资料多Project Metadata:Group:com.zhuazhuabang(可根据自己喜好修改)Artifact:backendPackaging: JarJava: 11 或 17添加依赖在 “Dependencies” 搜索并添加以下依赖Spring Web: 构建 Web 应用Spring Data JPA: 数据持久化或选择 MyBatis-Plus下文以 JPA 为例MySQL Driver: 连接 MySQL 数据库Lombok: 简化实体类代码可选但推荐生成项目点击 “Generate” 按钮下载压缩包解压后用 IDEA 或 Eclipse 打开。打开项目后检查pom.xml文件核心依赖应如下所示dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies2.2 前端 Vue 3 项目搭建我们使用 Vite 官方模板创建 Vue 3 TypeScript 项目。打开终端进入你的工作目录。创建项目执行以下命令按照提示操作。# 使用 npm npm create vuelatest frontend # 或使用 pnpm pnpm create vuelatest frontend在创建过程中通过上下键选择功能确保勾选上TypeScript和Router。对于 Pinia 和 ESLint可以根据需要选择。安装依赖并启动cd frontend npm install npm run dev项目启动后通常会运行在http://localhost:5173。访问该地址看到 Vue 欢迎页即表示成功。安装 Element Plus在项目目录下执行npm install element-plus npm install element-plus/icons-vue全局引入 Element Plus修改src/main.ts文件import { createApp } from vue import App from ./App.vue import router from ./router // 引入 Element Plus 及样式 import ElementPlus from element-plus import element-plus/dist/index.css // 引入图标组件如果需要 import * as ElementPlusIconsVue from element-plus/icons-vue const app createApp(App) // 注册所有图标可选 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(router) app.use(ElementPlus) app.mount(#app)2.3 数据库准备使用 MySQL 客户端如命令行、Navicat、DBeaver执行以下 SQL 语句创建数据库和用户表后续其他表将在实体类中通过 JPA 自动生成。CREATE DATABASE IF NOT EXISTS zhuazhuabang DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE zhuazhuabang; -- 用户表包含宠物主人和服务者 CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名, password varchar(255) NOT NULL COMMENT 密码加密后, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, role varchar(20) NOT NULL DEFAULT USER COMMENT 角色USER-普通用户 PROVIDER-服务提供者 ADMIN-管理员, 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT用户表;3. 后端核心功能实现我们将按照领域模型逐步实现用户、服务、订单等核心功能。这里以用户模块为例展示完整的 Controller-Service-Repository 结构。3.1 数据模型与实体类映射首先创建用户实体类User.java使用 JPA 注解将其映射到数据库表。package com.zhuazhuabang.backend.entity; import lombok.Data; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import javax.persistence.*; import java.time.LocalDateTime; Entity Table(name 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) private String password; // 实际存储应为加密后的密文 private String phone; private String avatar; Column(nullable false) private String role USER; // 默认角色 CreationTimestamp Column(updatable false) private LocalDateTime createTime; UpdateTimestamp private LocalDateTime updateTime; }注意生产环境中密码字段必须使用 BCryptPasswordEncoder 等工具进行哈希加密存储切勿明文保存。这里为了简化演示后续 Service 层会模拟加密过程。3.2 数据访问层与业务逻辑层创建 Repository 接口和 Service 类。UserRepository.java:package com.zhuazhuabang.backend.repository; import com.zhuazhuabang.backend.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.Optional; Repository public interface UserRepository extends JpaRepositoryUser, Long { // 根据用户名查找用户 OptionalUser findByUsername(String username); // 判断用户名是否存在 boolean existsByUsername(String username); }UserService.java:package com.zhuazhuabang.backend.service; import com.zhuazhuabang.backend.entity.User; import com.zhuazhuabang.backend.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; Service public class UserService { Autowired private UserRepository userRepository; // 模拟密码加密实际项目请使用 Spring Security 的 BCryptPasswordEncoder private String encryptPassword(String rawPassword) { // 这里使用简单的 MD5 作为示例生产环境务必使用更强的哈希算法加盐 return DigestUtils.md5DigestAsHex(rawPassword.getBytes(StandardCharsets.UTF_8)); } public User register(User user) { if (userRepository.existsByUsername(user.getUsername())) { throw new RuntimeException(用户名已存在); } user.setPassword(encryptPassword(user.getPassword())); return userRepository.save(user); } public User login(String username, String password) { OptionalUser userOpt userRepository.findByUsername(username); if (userOpt.isPresent()) { User user userOpt.get(); if (user.getPassword().equals(encryptPassword(password))) { return user; // 登录成功返回用户信息后续应生成 Token } } throw new RuntimeException(用户名或密码错误); } public ListUser getAllUsers() { return userRepository.findAll(); } public User getUserById(Long id) { return userRepository.findById(id).orElseThrow(() - new RuntimeException(用户不存在)); } public void deleteUser(Long id) { userRepository.deleteById(id); } }3.3 控制器层与 RESTful API 设计创建 Controller对外提供 HTTP 接口。这里我们设计几个简单的 API。UserController.java:package com.zhuazhuabang.backend.controller; import com.zhuazhuabang.backend.entity.User; import com.zhuazhuabang.backend.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; RestController RequestMapping(/api/users) public class UserController { Autowired private UserService userService; PostMapping(/register) public MapString, Object register(RequestBody User user) { try { User registeredUser userService.register(user); MapString, Object result new HashMap(); result.put(code, 200); result.put(message, 注册成功); result.put(data, registeredUser); return result; } catch (RuntimeException e) { MapString, Object result new HashMap(); result.put(code, 400); result.put(message, e.getMessage()); return result; } } PostMapping(/login) public MapString, Object login(RequestBody MapString, String loginRequest) { String username loginRequest.get(username); String password loginRequest.get(password); try { User user userService.login(username, password); MapString, Object result new HashMap(); result.put(code, 200); result.put(message, 登录成功); // 实际项目中这里应生成并返回 JWT Token result.put(data, user); return result; } catch (RuntimeException e) { MapString, Object result new HashMap(); result.put(code, 400); result.put(message, e.getMessage()); return result; } } GetMapping public MapString, Object getAllUsers() { ListUser users userService.getAllUsers(); MapString, Object result new HashMap(); result.put(code, 200); result.put(data, users); return result; } }3.4 应用配置与跨域处理修改src/main/resources/application.properties文件配置数据库连接和服务器端口。# 服务器端口 server.port8080 # 数据库配置 spring.datasource.urljdbc:mysql://localhost:3306/zhuazhuabang?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.passwordyour_password # 替换为你的数据库密码 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver # JPA 配置 spring.jpa.hibernate.ddl-autoupdate # 启动时更新表结构生产环境应为 validate 或 none spring.jpa.show-sqltrue # 控制台打印 SQL方便调试 spring.jpa.properties.hibernate.dialectorg.hibernate.dialect.MySQL8Dialect为了允许前端应用访问后端 API需要配置跨域。创建一个配置类CorsConfig.javapackage com.zhuazhuabang.backend.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class CorsConfig { Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) // 针对所有 /api 开头的路径 .allowedOrigins(http://localhost:5173) // 允许前端开发服务器地址 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }; } }4. 前端管理界面开发我们将使用 Vue 3、TypeScript 和 Element Plus 构建一个简单的用户管理页面展示如何调用后端 API。4.1 配置 Axios 并封装请求工具首先安装 Axiosnpm install axios创建src/utils/request.ts文件封装 Axios 实例import axios from axios const request axios.create({ baseURL: http://localhost:8080/api, // 后端 API 基础地址 timeout: 10000 // 请求超时时间 }) // 请求拦截器 request.interceptors.request.use( (config) { // 在发送请求之前做些什么例如添加 Token // const token localStorage.getItem(token) // if (token) { // config.headers.Authorization Bearer ${token} // } return config }, (error) { // 对请求错误做些什么 return Promise.reject(error) } ) // 响应拦截器 request.interceptors.response.use( (response) { // 对响应数据做点什么 return response.data }, (error) { // 对响应错误做点什么 console.error(API Request Error:, error) return Promise.reject(error) } ) export default request4.2 创建用户管理页面组件创建src/views/user/UserList.vue文件template div classuser-management el-card template #header div classcard-header span用户列表/span el-button typeprimary clickhandleAdd新增用户/el-button /div /template el-table :datauserList border stylewidth: 100% el-table-column propid labelID width80/el-table-column el-table-column propusername label用户名/el-table-column el-table-column propphone label手机号/el-table-column el-table-column proprole label角色 template #defaultscope el-tag :typeroleTagType(scope.row.role){{ scope.row.role }}/el-tag /template /el-table-column el-table-column propcreateTime label创建时间/el-table-column el-table-column label操作 width180 template #defaultscope el-button sizesmall clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(scope.row.id)删除/el-button /template /el-table-column /el-table /el-card !-- 新增/编辑用户对话框 -- el-dialog v-modeldialogVisible :titledialogTitle width30% el-form :modelform label-width80px el-form-item label用户名 el-input v-modelform.username placeholder请输入用户名/el-input /el-form-item el-form-item label密码 v-ifisAdd el-input v-modelform.password typepassword placeholder请输入密码/el-input /el-form-item el-form-item label手机号 el-input v-modelform.phone placeholder请输入手机号/el-input /el-form-item el-form-item label角色 el-select v-modelform.role placeholder请选择角色 el-option label普通用户 valueUSER/el-option el-option label服务提供者 valuePROVIDER/el-option el-option label管理员 valueADMIN/el-option /el-select /el-form-item /el-form template #footer span classdialog-footer el-button clickdialogVisible false取消/el-button el-button typeprimary clicksubmitForm确认/el-button /span /template /el-dialog /div /template script setup langts import { ref, reactive, onMounted } from vue import request from /utils/request import { ElMessage, ElMessageBox } from element-plus interface User { id?: number username: string password?: string phone: string role: string createTime?: string } const userList refUser[]([]) const dialogVisible ref(false) const isAdd ref(true) // true 为新增false 为编辑 const dialogTitle ref() const form reactiveUser({ username: , password: , phone: , role: USER }) // 获取用户列表 const fetchUsers async () { try { const response await request.get(/users) if (response.code 200) { userList.value response.data } } catch (error) { ElMessage.error(获取用户列表失败) console.error(error) } } // 处理新增 const handleAdd () { isAdd.value true dialogTitle.value 新增用户 Object.assign(form, { username: , password: , phone: , role: USER }) dialogVisible.value true } // 处理编辑 const handleEdit (row: User) { isAdd.value false dialogTitle.value 编辑用户 Object.assign(form, { ...row }) dialogVisible.value true } // 处理删除 const handleDelete (id: number) { ElMessageBox.confirm(确认删除该用户, 警告, { confirmButtonText: 确认, cancelButtonText: 取消, type: warning }).then(async () { try { // 这里调用删除接口示例中后端未实现可先注释 // await request.delete(/users/${id}) ElMessage.success(删除成功演示) fetchUsers() } catch (error) { ElMessage.error(删除失败) } }).catch(() {}) } // 提交表单新增或编辑 const submitForm async () { try { if (isAdd.value) { // 调用注册接口 await request.post(/users/register, form) ElMessage.success(新增成功) } else { // 调用更新接口示例中后端未实现 // await request.put(/users/${form.id}, form) ElMessage.success(更新成功演示) } dialogVisible.value false fetchUsers() // 刷新列表 } catch (error: any) { ElMessage.error(error.response?.data?.message || 操作失败) } } // 角色标签类型 const roleTagType (role: string) { switch (role) { case ADMIN: return danger case PROVIDER: return warning default: return } } onMounted(() { fetchUsers() }) /script style scoped .card-header { display: flex; justify-content: space-between; align-items: center; } /style4.3 配置路由并集成到主页面修改src/router/index.ts添加用户管理页面的路由。import { createRouter, createWebHistory } from vue-router import UserList from ../views/user/UserList.vue const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: /, redirect: /users }, { path: /users, name: userList, component: UserList } // ... 可以继续添加其他路由如服务管理、订单管理 ] }) export default router修改src/App.vue添加一个简单的导航菜单。template div idapp el-container el-aside width200px el-menu router :default-active$route.path el-menu-item index/users el-iconUser //el-icon span用户管理/span /el-menu-item !-- 可以继续添加其他菜单项 -- /el-menu /el-aside el-main router-view / /el-main /el-container /div /template script setup langts import { User } from element-plus/icons-vue /script style #app { height: 100vh; } /style5. 系统联调与功能验证完成前后端基础代码后需要进行联调确保数据能正确流动。5.1 启动后端服务在 IDE 中运行 Spring Boot 主类通常位于xxxApplication.java或在项目根目录下执行mvn spring-boot:run观察控制台日志看到类似Tomcat started on port(s): 8080的提示表示后端启动成功。5.2 启动前端开发服务器在前端项目根目录下执行npm run dev访问控制台输出的地址通常是http://localhost:5173。5.3 功能验证流程访问用户列表打开浏览器进入http://localhost:5173应自动跳转到用户管理页面。页面加载时会调用GET /api/users接口。由于数据库尚无数据表格为空。新增用户点击“新增用户”按钮在弹出的对话框中填写信息点击确认。前端会调用POST /api/users/register接口。成功后表格应刷新并显示新添加的用户。验证数据库使用数据库客户端连接 MySQL查询zhuazhuabang.user表确认数据已持久化且密码字段是加密后的 MD5 值。测试登录可以使用 Postman 或 Apifox 等工具测试登录接口POST /api/users/login传入正确的用户名和密码应返回用户信息。至此一个包含用户增删改查部分和前后端完整交互的“爪爪帮”平台基础骨架已经搭建完成。你可以在此基础上按照同样的模式实体 - 仓库 - 服务 - 控制器 - 前端页面扩展“服务”和“订单”模块。6. 常见问题排查与进阶思考在开发过程中你可能会遇到以下典型问题。这里提供排查思路。6.1 后端启动或数据库连接失败问题现象可能原因检查方式处理建议应用启动时报java.net.ConnectException数据库服务未启动或连接参数错误。1. 检查 MySQL 服务是否运行。2. 核对application.properties中的url,username,password。启动 MySQL 服务修正连接配置。启动时报Table ‘xxx’ doesn‘t existJPA 的ddl-auto设置为update但实体类映射有问题。检查实体类的Table注解名、字段名与数据库关键字是否冲突。检查实体类定义或先将ddl-auto改为create让 JPA 自动建表注意会清空数据。端口8080被占用已有其他进程使用了 8080 端口。在终端运行netstat -ano | findstr :8080(Windows) 或lsof -i:8080(Mac/Linux)。终止占用端口的进程或修改server.port为其他端口如8081。6.2 前端调用 API 报跨域错误如果浏览器控制台出现CORS policy相关错误说明跨域配置未生效。检查确认后端CorsConfig配置的allowedOrigins是否包含了前端运行的地址如http://localhost:5173。检查确认前端request.ts中的baseURL是否正确指向了后端地址如http://localhost:8080/api。临时方案在开发阶段可以在前端 Vite 配置中设置代理。修改vite.config.tsimport { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, // rewrite: (path) path.replace(/^\/api/, ) // 根据后端路径决定是否需要重写 } } } })同时将request.ts中的baseURL改为空字符串。这样前端对/api/xxx的请求会被 Vite 代理到后端。6.3 数据绑定或页面渲染异常表格不显示数据在 Vue 组件中使用console.log打印userList.value检查数据是否成功获取并结构正确。检查 Element Plus 表格的:data绑定和prop属性是否与数据字段名匹配。表单提交后对话框不关闭检查submitForm方法中在请求成功后是否将dialogVisible.value设置为false。TypeScript 类型错误仔细检查接口定义interface User是否与后端返回的 JSON 结构完全匹配特别是可选字段?的使用。6.4 项目扩展与生产环境考量当前实现仅为毕业设计演示级别。若要接近生产环境需要考虑以下方面认证与授权集成 Spring Security JWT实现真正的登录状态管理和接口权限控制。为不同角色USER, PROVIDER, ADMIN设计不同的权限路径。密码安全使用BCryptPasswordEncoder替代简单的 MD5 加密。全局异常处理创建ControllerAdvice类统一处理业务异常和系统异常返回结构化的错误信息而不是在 Controller 中到处写try-catch。API 文档集成 Swagger/OpenAPI自动生成接口文档便于前后端协作。数据验证在实体类或 DTO 中使用javax.validation注解如NotBlank,Email进行参数校验。服务与订单模块设计Service服务和Order订单实体并建立它们与User实体之间的关联如ManyToOne。实现订单状态机。前端路由守卫使用 Vue Router 的导航守卫在访问需要登录的页面时检查 Token。状态管理对于复杂的应用状态如用户登录信息考虑使用 Pinia 进行集中管理。部署后端打包成 Jar 文件使用java -jar命令运行。前端使用npm run build构建将生成的dist目录内容部署到 Nginx 或对象存储。通过完成这个项目你不仅能掌握 Spring Boot 和 Vue 3 的全栈开发流程更能深入理解一个典型 O2O 平台的核心业务逻辑与数据模型设计。建议你以用户模块为模板独立完成服务和订单模块的实现这将是对你学习成果最好的检验。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻