根据前端vue3代码,用springboot+mysql实现后端 51放假任务

243 阅读8分钟

前言

51放假前老师给我们讲解了vue3+node.js+MangoDB实现了学生座位选座系统, 要求我们后端的学员使用java代码+mysql实现后端,并且把项目运行起来。

#展示效果 首先就是登录页面,查询数据库是否有当前学号姓名来实现登录。 image.png

登录进去以后就是选座页面,非选座时段,上午,下午,晚上,是前端实现的,已经实现好了,不同时间段显示会不一样,比如现在已经过了选座时间,所以非选座时段亮了。

301-506是教室,每个教室座位不一样。 image.png

当我点击164座位,会提示我是否锁定座位,点击是就会跳到当前教室的座位页面。 image.png

下面就是锁定座位后的界面,164就是我锁定的座位,点击会弹出个人信息界面, 点击学生名单,会显示当前教室的上课学生。 image.png

这个就是我点击自己的座位后弹出的个人信息界面

image.png

这个是学生名单数据我还没有给条件,所以给一个班的学生都显示了,按理来说是只显示锁定座位了的学生。搜索按钮是可以查询以往日期学生锁定座位的情况。 image.png

以上就是整个选座功能的基本介绍了。

后端实现

数据库

首先后端还是得要创建好数据库,写好了数据库才能写好代码。 我的设计思路是创建一个学生表,存放学号,姓名,token,和班级。 然后再是座位表,因为前端需要接收一个二维数组来显示座位,所以我是这样设计的

seat_id座位id

room_i_d教室id

grid_row行

grid_col列

arr值

还需要有一个锁定座位的表,存放

seat_id座位id

is_locked存放0和1,用于表示当前座位有没有学生锁定

name当前座位锁定的学生姓名

登录

我们找到登录页面代码,如下。

const handleLogin = async () => {
	try {
		// 定义请求参数对象
		const params = {
			num: num.value,
			name: name.value,
			n: n.value
		};

		// 将参数对象转换为查询字符串
		const queryString = new URLSearchParams(params).toString();

		// 目标 API 的基础 URL
		const baseUrl = `${BASE_URL}/api/login`;

		// 拼接完整的请求 URL
		const apiUrl = `${baseUrl}?${queryString}`;
		// 发起 Fetch 请求
		const response = await fetch(apiUrl);

		// 检查响应状态
		if (!response.ok) {
			throw new Error(`HTTP 错误! 状态码: ${response.status}`);
		}

		// 解析响应数据
		const data = await response.json();	
		console.log(data);
		if (data.code === 10000) {
			console.log("abc");
			// 存储 token 到 localStorage
			localStorage.setItem('token', data.data.token);
			console.log('登录成功,token 已存储');
			// 登录成功后跳转到 App.vue
	
			await router.push('/');
			return;
		} else {
			// 显示服务器返回的错误信息
			alert(data.text)
		}
	} catch (error) {
		console.error('登录失败:', error);
		alert('登录失败');
	}
};

可以看到传给了后端学号姓名班级,后端需要传输给前端code10000和token。知道了需求就可以写后端了

那我们就封装给R,返回一个code和data,data里面存放token,这样就解决了

//登录
    @GetMapping("/login")
    public R<Student> login(@RequestParam("num") String num,
                            @RequestParam("name") String name,
                            @RequestParam("n") String n) {
        System.out.println("num=" + num + "name=" + name + "n=" + n);
        // 学号、姓名参数验证
        if (num == null || num.isEmpty() || name == null || name.isEmpty()) {
            return R.error(400, "学号和姓名不能为空");
        }

        try {
            Student student = studentService.login(num, name);

            if (student == null) {
                return R.error(404, "学号或姓名错误");
            }

            return R.success(student);
        } catch (Exception e) {
            // 捕获异常并返回错误码
            return R.error(500, "登录失败: " + e.getMessage());
        }
    }

座位显示

// 获取教室数据
const fetchClassroomData = async () => {
    try {
        const token = localStorage.getItem('token');
       let URL = `${BASE_URL}/api/room/want?roomID=${vmRoom.value}`
        const response = await fetch(URL, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`
            }
        });
        if (!response.ok) {
            throw new Error(`HTTP 错误!状态:${response.status}`);
        }
        const data = await response.json();
        console.log('教室数据:', data);
        教室数据.value = data;
        console.log('教室数据:', 教室数据.value);
    } catch (error) {
        console.error('获取教室数据出错:', error);
    }
};

如上就是前端的接口代码,后端需要接收roomid,来显示当前教室的座位信息。 方法是get,并且在url上加了一个roomID

//获取所有座位信息
    @GetMapping("/room/want")
    public SeatDto getAllSeats(@RequestParam("roomID") int roomID) {
        return seatService.getAllSeats(roomID);
    }

所以我后端用了@RequestParam注解来接收。

在service层写了一些方法来将数据库中的数据存放在数组中。前端使用arr二维数组1表示座位,0表示过道来显示的。还需要一个存放座位id的二维数组layout来控制选座功能,最后返回给前端的数据是

{
arr:[1,1,0,0,1,1],
    [1,1,0,0,1,1],
    [1,1,0,0,1,1]
layout:[101,102,0,0,103,104],
       [105,106,0,0,107,108],
       [109,110,0,0,111,112],
data:{
{seatId: '101', locked: false, selected: false}
{seatId: '102', locked: false, selected: false}
{seatId: '103', locked: false, selected: false}
{seatId: '104', locked: false, selected: false}
{seatId: '105', locked: false, selected: false}
{seatId: '106', locked: false, selected: false}
{seatId: '107', locked: false, selected: false}
{seatId: '108', locked: false, selected: false}
{seatId: '109', locked: false, selected: false}
{seatId: '110', locked: false, selected: false}
{seatId: '111', locked: false, selected: false}
{seatId: '112', locked: false, selected: false}
}
}
@Override
    public SeatDto getAllSeats(int roomId) {
        List<Seat> seats = seatMapper.selectList(null);
        SeatDto seatDto = new SeatDto();
        List<SeatInfo> data = new ArrayList<>();

        // 第一步:找到最大的 grid_row 和 grid_col
        int maxRow = 0;
        int maxCol = 0;
        for (Seat seat : seats) {
            maxRow = Math.max(maxRow, seat.getGridRow());
            maxCol = Math.max(maxCol, seat.getGridCol());
        }

        // 第二步:根据最大行列创建二维数组
        int[][] arr = new int[maxRow + 1][maxCol + 1]; // +1 因为索引从0开始
        int[][] layout = new int[maxRow + 1][maxCol + 1]; // +1 因为索引从0开始

        // 第三步:填充数据
        for (Seat seat : seats) {
            int row = seat.getGridRow();
            int col = seat.getGridCol();
            int value = seat.getArr();
            String value2 = seat.getSeatId();
            if (value == 0) {
                value2 = "0";
            }
            arr[row][col] = value;
            layout[row][col] = Integer.parseInt(value2);
        }
        seatDto.setRoomID(roomId);
        seatDto.setArr(arr);
        seatDto.setLayout(layout);
        for (Seat seat : seats) {
            SeatInfo seatInfo = new SeatInfo();
            seatInfo.setSeatId(seat.getSeatId());
            seatInfo.setLocked(seat.isLocked());
            seatInfo.setSelected(seat.isSelected());
            data.add(seatInfo);
        }
        seatDto.setData(data);
//        System.out.println(Arrays.deepToString(arr));
//        System.out.println(Arrays.deepToString(layout));
//        System.out.println(seatDto);
        return seatDto;
    }

通过上面的方法最后也是成功让选座页面显示了出来

锁定座位

锁定座位前端代码如下

const handleSeatClick = async (seatId) => {
    // 先将所有座位的选中状态置为 false
    教室数据.value.data.forEach(seat => {
        seat.isSelected = false;
    });

    // 将当前点击的座位选中状态置为 true
    const currentSeat = getSeatById(seatId);
    if (currentSeat) {
        currentSeat.isSelected = true;
    }

    console.log(`你点击了座位 ID 为 ${seatId} 的座位`);
    if (confirm(`确认锁定座位 ID 为 ${seatId} 的座位吗?`)) {
        let 请求参数 = {
            seat_id: seatId,
            roomID: 教室数据.value.roomID,
            token: localStorage.getItem('token'),
        }
        console.log(请求参数)
        try {
            const 响应 = await fetch(`${BASE_URL}/api/seat/pick`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(请求参数)
            });
            if (!响应.ok) {
                throw new Error(`HTTP 错误!状态:${响应.status}`);
            }

            // 处理响应数据
            const { code, text } = await 响应.json();   //数据

            if (code === 10000) {
                alert(text);
                let 参数 = {
                    name:'look',
                    query:{
                        roomID:vmRoom.value,
                        ampm:ampm.value,
                    }
                }
 
                router.push( 参数 ); 
            } else {
                alert(text);
            }

            // 更新本地座位状态
            if (currentSeat) {
                currentSeat.isLocked = true;
            }
        } catch (error) {
            console.error('锁定座位出错:', error);
        }
    } else {
        // 如果取消锁定,将当前座位的选中状态置为 false
        if (currentSeat) {
            currentSeat.isSelected = false;
        }
    }
};

还是看前端给的什么数据给后端,以及什么类型的方法,可以看到是post方法,并且传输了座位id,教室id和token 所以我们后端需要接收到数据,封装在一个类里面。在通过教室id和座位id找到数据库锁定座位表中对应的座位id,在通过token查询姓名,存放在锁定座位表中,并且将开关改为true,来表示当前座位有人占用了。

 //锁定座位
    @PostMapping("/seat/pick")
    public R pickSeat(@RequestBody Lock lock) {
        seatService.pickSeat(lock);
        return new R(10000,"座位锁定成功",null);
    }

上面就是接口代码,Lock中存放了三个属性分别对应前端中的seatid,rooid和token。

 @Override
    public void pickSeat(Lock lock) {
        //将座位状态改为锁定
        String seatId = lock.getSeat_id();
        List<Seat> seats = seatMapper.selectList(new QueryWrapper<Seat>().eq("seat_id", seatId));
        //遍历seats
        for (Seat seat : seats) {
            seat.setLocked(true);
            List<SeatStatus> seatId1 = seatStatusMapper.selectList(new QueryWrapper<SeatStatus>().eq("seat_id", seatId));
            for (SeatStatus seatStatus : seatId1){
                seatStatus.setLocked(true);
                seatStatus.setSelected(true);
                //根据token查询名字
                Student student = studentMapper.selectOne(new QueryWrapper<Student>().eq("token", lock.getToken()));
                seatStatus.setName(student.getStuName());
                seatStatusMapper.updateById(seatStatus);
            }
        }

    }

上面就是service层,用于实现查询,更新

获取学生列表

在锁定座位后会自动获取学生列表,下面是前端接口代码

// 新增获取学生列表方法
const fetchStudentList = async () => {
    try {
        // 修改fetch请求协议为https
        const response = await fetch(`${BASE_URL}/api/stu/list/1`);
        if (!response.ok) throw new Error('获取学生列表失败');
        students.value = await response.json();
        console.log('获取学生列表成功:', students.value);
    } catch (error) {
        console.error('获取学生列表错误:', error);
    }
};

我们需要通过url最后的1也就是班级来查询当前班级中的学生列表,只需要一个简单的查询就可以完成

//返回学生列表
    @GetMapping("/stu/list/{classId}")
    public List<Stu> getStuList(@PathVariable String classId) {
        System.out.println("classId = " + classId);
        return seatService.getStuList();
    }

我这边直接固定值写死了班级,为了方便展示。

@Override
    public List<Stu> getStuList() {
        return stuMapper.selectList(new QueryWrapper<Stu>().eq("class_id", "1234567"));
    }

座位展示以及查询

最后就是最终选座后的座位页面,传输了当前教室id,当前时段,以及日期。下面是vue接口代码

// 处理搜索按钮点击事件
const handleSearch = async () => {
    try {
        
        loading.value = true;
        error.value = null;

        let date = selectedDate.value

        vmWeek.value = vmWeekArr[new Date().getDay()];

        let URL = `${BASE_URL}/api/room/look?roomID=${vmRoom.value}&ampm=${vmAmpm.value}&date=${date}`;
        const 响应 = await fetch(URL);
        if (!响应.ok) {
            throw new Error(`HTTP错误!状态码:${响应.status}`);
        }
        rooms.value = [await 响应.json()];
    } catch (err) {
        error.value = `数据加载失败:${err.message}`;
    } finally {
        loading.value = false;
    }
};

后端需要接收三个数据,并且查询对应时段的选座信息,但是我没有做出来,只做出来了一部分,就是没有时间段,只是显示了座位信息。 下面是接口

//获取教室信息
    @GetMapping("/room/look")
    public SeatDtos getRoomData(
            @RequestParam int roomID,       // 接收教室编号
            @RequestParam String ampm,     // 接收时间段
            @RequestParam String date      // 接收日期
    ){
        SeatDtos oneSeats = seatService.getOneSeats(roomID);
        System.out.println("oneSeats = " + oneSeats);
        return oneSeats;
    }

下面是service

@Override
    public SeatDtos getOneSeats(int roomID) {
        List<Seat> seats = seatMapper.selectList( new QueryWrapper<Seat>()
                .eq("room_i_d", roomID)// 根据room_id查询
                .orderByAsc("seat_id"));// 根据seat_id升序排序
        SeatDtos seatDtos = new SeatDtos();
        List<SeatInfos> data = new ArrayList<>();

        // 第一步:找到最大的 grid_row 和 grid_col
        int maxRow = 0;
        int maxCol = 0;
        for (Seat seat : seats) {
            maxRow = Math.max(maxRow, seat.getGridRow());
            maxCol = Math.max(maxCol, seat.getGridCol());
        }

        // 第二步:根据最大行列创建二维数组
        int[][] layout = new int[maxRow + 1][maxCol + 1]; // +1 因为索引从0开始

        // 第三步:填充数据
        for (Seat seat : seats) {
            int row = seat.getGridRow();
            int col = seat.getGridCol();
            int value = seat.getArr();
            String value2 = seat.getSeatId();
            if (value == 0) {
                value2 = "0";
            }
            layout[row][col] = Integer.parseInt(value2);
        }
        seatDtos.setRoomID(roomID);
        seatDtos.setLayout(layout);

        for (Seat seat : seats) {
            SeatInfos seatInfos = new SeatInfos();
            List<SeatStatus> seatId1 = seatStatusMapper.selectList(new QueryWrapper<SeatStatus>().eq("seat_id", seat.getSeatId()));

            for (SeatStatus seatStatus : seatId1){
                if(seatStatus.isLocked()){
                seatInfos.setIs_free(false);
                seatInfos.setName(seatStatus.getName());
                }else {
                    seatInfos.setIs_free(true);
                }
            }
            seatInfos.setSeat_id(Integer.parseInt(seat.getSeatId()));
            data.add(seatInfos);
        }
        seatDtos.setData(data);
        System.out.println(data);
        System.out.println(seatDtos);
        return seatDtos;
    }

结语

效果基本上就算是完成了,但是仍然有不足,我会在接下来时间里慢慢完善代码。主要难点在于我想数据库与后端之间怎么实现二维数组的传输。最后通过时间段来查询不同时间段的教室选座,可能还是没有想好具体要怎么做。要做什么。总之,加油!