最近在搞课题设计,要求是swing开发的页面,而我觉得一步到位,用vue3+Springboot做前后端分离的项目更加适合对接企业项目,本文章用于记录一个小白从零起步开发项目。
作为前后端分离项目,我采用的技术栈是:
- 后端 mysql springboot maven
- 前端 vue vite
- 环境 JDK21、 IDEA、 Node.js
一,创建前端
前端采用node.js技术栈,通过命令创建vue vite 项目
npm create vite@latest takeout-web -- --template vue
taskout-web是项目名称,接下来安装项目需要采用的组件
cd takeout-web
npm install element-plus axios pinia vue-router@4 dayjs echarts
这里主要用到了三个组件axios负责http请求,element-plus组件样式,router编写页面路由
在前端src目录下修改main.js文件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')
引入组件并注册,配置axios的基础访问URL,连接后端启动的8080端口
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import axios from 'axios'
const app = createApp(App)
axios.defaults.baseURL = 'http://localhost:8080'
app.config.globalProperties.$axios = axios
app.use(ElementPlus)
app.use(router)
app.mount('#app')
这里需要在当前目录创建了router目录及其目录下的index.js配置文件
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
修改app.vue文件
<script setup>
</script>
<template>
<!-- 修改为 -->
<router-view />
</template>
在src/views/目录下新建Home.vue
<template>
<h2>Home</h2>
</template>
<script>
</script>
<style>
</style>
启动前端查看效果 ,如下图
在task-web目录下执行npm run dev

可以看到,已经实现主页面的显示,该项目有三个页面,Login,Register,Home。根据业务逻辑,我们应该先让用户登录,然后跳转到主页面。下面我们先创建其余两个页面,并编写路由守卫保证用户不能直接访问主页进行订餐。
在src/view目录下创建Login.vue , Register.vue文件。

接下来,修改路由配置。
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
const routes = [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/home',
name: "Home",
component: Home
},
{
path: '/register',
name: "Register",
component: Register
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守卫(
router.beforeEach((to, from, next) => {
// 禁止直接访问 /home,必须从登录页跳转过来
if (to.path === '/home' && from.path === '') {
next('/login')
} else {
next()
}
})
export default router
创建后端
前端基础地基已搭建完毕,接下来创建后端springboot项目,使用Idea新建项目

引入pom依赖
- Spring Web
- Spring Data JPA
- MySQL Driver
- Lombok
数据库
现在建立数据库结构:

在后端修改配置文件,连接数据库
spring.application.name=task-master
# 端口
server.port=8080
# 数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/task-web?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
数据库中各表的创建代码可以参考:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for meal_package
-- ----------------------------
DROP TABLE IF EXISTS `meal_package`;
CREATE TABLE `meal_package` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`price` decimal(38, 2) NULL DEFAULT NULL,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for orders
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL COMMENT '订餐人ID',
`package_id` int NOT NULL COMMENT '套餐ID',
`quantity` int NOT NULL COMMENT '份数',
`unit_price` decimal(38, 2) NULL DEFAULT NULL,
`total_price` decimal(38, 2) NULL DEFAULT NULL,
`order_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订餐时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `user_id`(`user_id` ASC) USING BTREE,
INDEX `package_id`(`package_id` ASC) USING BTREE,
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `orders_ibfk_2` FOREIGN KEY (`package_id`) REFERENCES `meal_package` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`is_admin` int NULL DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
用navicat或者sql语句在sys_user表中添加两条记录,作为管理员登录账户和测试用户

编写用户登录功能
在后端src/main/java/目录下的com.example.taskmaster下创建软件包User
在这个软件包中,我们会按照四层架构进行设计,分别是控制层,业务层,数据访问层和实体层。
Controller控制层是系统的入口,负责接收前端的 HTTP 请求并返回响应结果。通过@RestController、@RequestMapping等注解,定义了统一的 RESTful 接口,比如用户模块的/user/register和/user/login。同时,我也配置了跨域支持,保证前后端联调的兼容性。
Service 业务层 这一层封装了系统的核心业务逻辑,是整个架构的核心。以用户模块为例,注册、登录的业务校验逻辑都在这里实现。它会调用 Mapper 层提供的数据操作方法,完成业务处理,同时保证 Controller 层和数据层的解耦。
Mapper 数据访问层(DAO 层) 这一层负责与数据库进行交互,封装了所有的数据读写操作。我使用JdbcTemplate实现了对 MySQL 数据库的操作,比如根据用户名查询用户、插入新用户数据等 SQL 逻辑都在这里编写,保证业务层无需关心底层数据库的具体实现。
Entity 实体层(ORM 映射层) 这一层定义了与数据库表对应的实体类,通过类的属性和数据表字段一一映射,实现 ORM 对象关系映射。比如User.java对应数据库中的sys_user表,使用 Lombok 的@Data注解简化了实体类的编写。

接下来先编写实体层,映射字段关系,在Entity下创建User.java
package com.example.taskmaster.User.Entity;
//注意路径要保持一致
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String password;
private int is_admin;
}
然后编写数据访问层,在这层通过jdbc实现数据库交互,创建UserMapper.java(可以通过继承JPA实现数据库操作的代码,但这里是为了学习java交互所以自己编写)
package com.example.taskmaster.User.Mapper;
import com.example.taskmaster.User.Entity.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
@Repository
public class UserMapper {
private final JdbcTemplate jdbcTemplate;
public UserMapper(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private static final RowMapper<User> USER_ROW_MAPPER = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setIs_admin(rs.getInt("is_admin"));
return user;
}
};
/**
* 根据用户名查询用户
*/
public User findByName(String name) {
String sql = "SELECT id, name, password, is_admin FROM sys_user WHERE name = ?";
var list = jdbcTemplate.query(sql, USER_ROW_MAPPER, name);
return list.isEmpty() ? null : list.get(0);
}
/**
* 保存用户
*/
public int save(User user) {
String sql = "INSERT INTO sys_user (name, password, is_admin) VALUES (?, ?, ?)";
return jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getIs_admin());
}
}
编写业务层,在这一层实现业务逻辑,创建UserService.java
package com.example.taskmaster.User.Service;
import com.example.taskmaster.User.Entity.User;
import com.example.taskmaster.User.Mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 注册
public String register(User user) {
User exist = userMapper.findByName(user.getName());
if (exist != null) {
return "用户名已存在";
}
userMapper.save(user);
return "注册成功";
}
// 登录
public String login(User user) {
User exist = userMapper.findByName(user.getName());
if (exist == null) {
return "用户不存在";
}
if (!exist.getPassword().equals(user.getPassword())) {
return "密码错误";
}
return "登录成功";
}
}
最后是控制层,这层重点是实现端口和http请求响应,创建UserController.java
package com.example.taskmaster.User.Controller;
import com.example.taskmaster.User.Entity.User;
import com.example.taskmaster.User.Mapper.UserMapper;
import com.example.taskmaster.User.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
@CrossOrigin(origins = "http://localhost:5173")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@PostMapping("/register")
public String register(@RequestBody User user) {
return userService.register(user);
}
@PostMapping("/login")
public Map<String, Object> login(@RequestBody User user) {
Map<String, Object> result = new HashMap<>();
User exist = userMapper.findByName(user.getName());
if (exist == null) {
result.put("msg", "用户不存在");
result.put("data", null);
return result;
}
if (!exist.getPassword().equals(user.getPassword())) {
result.put("msg", "密码错误");
result.put("data", null);
return result;
}
Map<String, Object> userData = new HashMap<>();
userData.put("id", exist.getId());
userData.put("name", exist.getName());
userData.put("isAdmin", exist.getIs_admin());
result.put("msg", "登录成功");
result.put("data", userData);
return result;
}
}
@CrossOrigin(origins = “http://localhost:5173”)这段的作用:get请求可以正常通过,但是用到post请求时会出现请求失败,跨域等问题。小白可以先了解一些常用请求方式
前端与后端的连接调用
前端,后端都准备完毕了,那该如何通信和请求资源呢?
还记得我们在前端main.js中定义的这一条吗?
axios.defaults.baseURL = 'http://localhost:8080'
这声明,每次调用axios进行http请求时默认会向http://localhost:8080该地址发送,我们后端启动的端口正是这个地址。接下来我们在前端src目录下新建api目录并在其中创建user.js
import axios from 'axios'
// 登录接口
export const loginApi = (userInfo) => {
return axios.post('/user/login', userInfo)
}
//注册接口
export const registerAPI = (userInfo) => {
return axios.post("/user/register",userInfo)
}
在这里编写了两种请求,分别对应controller层UserController.java中用@PostMapper注解声明的接口,post请求是需要参数的,同时要适配我们的业务逻辑,而登录或注册都要用用户名和密码,所以我们编写登录界面代码,接收这两个值。同时通过api接口发到后端进行处理获取响应结果,完成登录校验。
下面是Login.vue登录界面的代码,这里我注释了,不然发不上去。
<!--<template>
<div style="width: 400px; margin: 100px auto;">
<h2>用户登录</h2>
<el-input v-model="user.name" placeholder="用户名" style="margin:10px 0;" />
<el-input v-model="user.password" placeholder="密码" type="password" style="margin:10px 0;" />
<el-button type="primary" @click="login" style="width:100%;">登录</el-button>
<el-button @click="$router.push('/register')" style="width:100%;margin-top:10px;">注册</el-button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { loginApi } from '../api/user'
const router = useRouter()
const user = ref({ name: '', password: '' })
const login = async () => {
try {
const res = await loginApi(user.value)
const { msg, data } = res.data
if (msg === '登录成功' && data) {
ElMessage.success(msg)
router.push({
path: '/home',
query: {
userId: data.id,
userName: data.name,
isAdmin: data.isAdmin
}
})
} else {
ElMessage.error(msg || '登录失败')
}
} catch (err) {
ElMessage.error('登录请求失败')
}
}
</script>-->
启动前后端进行验证,输入账号后可以登录并自动跳转到Home页面视为成功
注册页面代码:
<!--<template>
<div style="width: 400px; margin: 100px auto;">
<h2>用户注册</h2>
<el-input v-model="user.name" placeholder="用户名" style="margin:10px 0;" />
<el-input v-model="user.password" placeholder="密码" type="password" style="margin:10px 0;" />
<el-button type="primary" @click="register" style="width:100%;">注册</el-button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { registerAPI } from "../api/user"
const router = useRouter()
const user = ref({ name: '', password: '' })
const register = async () => {
try {
const res = await registerAPI(user.value)
ElMessage.success(res.data)
if (res.data === '注册成功') {
router.push('/login')
}
} catch (err) {
ElMessage.warning("注册失败")
}
}
</script>-->
实现方法都是采用v-model绑定,然后将user的值作为参数通过调用api,axios发送到后端进行处理实现,流程前端发包到controller-service-mapper-mysql-data-mapper-service-controller-web
订单页面设计
这时,想到大多数网站登录进去后可以显示登录用户信息并且欢迎。那么在该项目中,我是如何实现的呢?
其实在我发的后端controller就露出端倪了,看UserController.java文件
Map<String, Object> userData = new HashMap<>();
userData.put("id", exist.getId());
userData.put("name", exist.getName());
userData.put("isAdmin", exist.getIs_admin());
result.put("msg", "登录成功");
result.put("data", userData);
在这里我将登录信息发送到页面中,用post请求中的参数来辨别用户,既能实现用户名信息,又通过is_admin字段,可以在前端页面中用v-if方法辨别是否是管理员账户,提供权限隔离。当然,这是不安全也绝对不规范的,但毕竟是课题设计,简陋点。(其实是我比较懒)
言归正传,开始实现订单业务
套餐与订单
接下来,照葫芦画瓢,创建订单和套餐的4层架构

最终实现效果
常用请求方式
GET 用于从服务器获取资源。它不会对服务器上的数据进行修改,适合请求网页、图片或 API 数据等。GET 请求的数据通常附加在 URL 中,适合传递简单参数。
POST 用于向服务器发送数据以创建新资源。常见于提交表单、上传文件或调用 API。POST 请求的数据包含在请求体中,适合传输较大或敏感的数据。
PUT 用于更新服务器上的资源。如果资源不存在,则创建新资源。PUT 是幂等的,即多次执行相同的请求不会产生不同的结果。
DELETE 用于删除服务器上的指定资源。DELETE 也是幂等的,重复执行不会导致额外的变化。
PATCH 用于对资源进行部分更新。与 PUT 不同,PATCH 只修改资源的部分字段,而不是替换整个资源。
HEAD 类似于 GET,但只返回响应头部,不包含具体内容。常用于检查资源是否存在或获取元数据。
OPTIONS 用于查询服务器支持的请求方法。常见于跨域资源共享(CORS)的预检请求。
TRACE 用于诊断请求的传输路径,服务器会回显收到的请求。由于可能存在安全风险,通常被禁用。
CONNECT 用于建立到服务器的隧道连接,通常用于 HTTPS 请求。CONNECT 方法允许客户端与目标服务器之间进行加密通信。









写的不是很好,持续优化୧(๑•̀⌄•́๑)૭
要是再详细点,power我直接给你投钱
文章不错非常喜欢,支持一下