原生HTML+SpringBoot失物招领系统核心模块代码拆解,含文件上传类型校验、物品CRUD完整业务逻辑
·
CSDN博文二:失物招领实训|本人负责模块完整代码实现(数据库脚本+登录注册页面+分类管理页面)
正文
前言
本项目为小组协作开发的失物招领管理系统,根据小组分工进度表,我(史安泰)独立负责三大模块工作:
- 数据库建表脚本编写、测试数据初始化;
- 后端物品分类管理模块完整接口开发;
- 前端登录页、注册页页面开发与调试;
本篇结合本人负责内容,完整拆解数据库脚本、后端分类模块代码、前端登录注册页面逻辑,附带功能截图、代码逐行讲解,贴合实训分工要求。
一、我负责模块整体业务说明
- 数据库层:基于ER图编写完整MySQL建表语句,创建用户、失物、招领、物品分类四张数据表,插入测试账号、分类基础数据,为全项目提供数据支撑;
- 后端模块:物品分类管理CRUD接口,支持新增、删除、修改、分页查询分类,所有物品发布时可绑定对应分类;
- 前端页面:登录/注册双表单页面,完成账号校验、注册查重、登录态存储、路由跳转,对接用户登录认证后端接口。
第一部分:数据库建表脚本与测试数据初始化(本人负责任务2:基础搭建)
1. 数据库创建语句
-- 创建项目数据库
CREATE DATABASE lostfound_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE lostfound_db;
2. 四张核心数据表完整建表SQL
(1)用户表 user
CREATE TABLE `user` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户主键',
`username` VARCHAR(30) NOT NULL UNIQUE COMMENT '登录账号',
`password` VARCHAR(30) NOT NULL COMMENT '登录密码',
`create_time` DATETIME DEFAULT NOW() COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户表';
(2)物品分类表 category(本人核心负责表,对应后端分类模块)
CREATE TABLE `category` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '分类id',
`category_name` VARCHAR(50) NOT NULL COMMENT '分类名称(手机/证件/箱包等)',
`remark` VARCHAR(200) DEFAULT '' COMMENT '分类备注说明'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '物品分类表';
(3)失物表 lost_item
CREATE TABLE `lost_item` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(30) NOT NULL COMMENT '发布人账号',
`name` VARCHAR(100) NOT NULL COMMENT '物品名称',
`category_id` BIGINT NOT NULL COMMENT '关联分类id',
`location` VARCHAR(100) NOT NULL COMMENT '丢失地点',
`lose_date` VARCHAR(30) NOT NULL COMMENT '丢失时间',
`status` VARCHAR(20) DEFAULT '未找回' COMMENT '状态:未找回/已找回',
`image` VARCHAR(255) DEFAULT '' COMMENT '图片访问路径',
FOREIGN KEY (`category_id`) REFERENCES category(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '失物信息表';
(4)招领表 found_item
CREATE TABLE `found_item` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(30) NOT NULL COMMENT '发布人账号',
`name` VARCHAR(100) NOT NULL COMMENT '物品名称',
`category_id` BIGINT NOT NULL COMMENT '关联分类id',
`location` VARCHAR(100) NOT NULL COMMENT '拾取地点',
`find_date` VARCHAR(30) NOT NULL COMMENT '拾取时间',
`status` VARCHAR(20) DEFAULT '待认领' COMMENT '状态:待认领/已认领',
`image` VARCHAR(255) DEFAULT '' COMMENT '图片访问路径',
FOREIGN KEY (`category_id`) REFERENCES category(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '招领信息表';
3. 初始化测试数据(本人编写)
-- 测试用户账号
INSERT INTO user(username,password) VALUES ('stu01','123456'),('admin','admin123');
-- 基础物品分类数据
INSERT INTO category(category_name,remark) VALUES
('手机数码','手机、耳机、平板等电子产品'),
('证件卡包','身份证、学生证、银行卡'),
('箱包衣物','背包、雨伞、外套'),
('书本文具','课本、笔记本、笔袋');
开发踩坑(数据库环节)
- 外键关联未设置字符集统一,创建表报错;统一全部表utf8mb4解决;
- category_name设置唯一索引,防止重复分类;
- 提前插入分类测试数据,后续开发发布物品页面无需手动新增分类。
第二部分:后端物品分类管理模块完整代码(本人负责任务3:后端开发-物品分类模块)
1. 分类实体类 Category.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("category")
public class Category {
@TableId(type = IdType.AUTO)
private Long id;
private String categoryName;
private String remark;
}
2. Mapper持久层
public interface CategoryMapper extends BaseMapper<Category> {
}
3. Controller分类接口(全部由本人开发)
统一使用项目封装返回类R<T>,提供完整CRUD接口,供前端分类页面调用
@RestController
@RequestMapping("/api/category")
public class CategoryController {
@Autowired
private CategoryMapper categoryMapper;
// 查询所有分类(发布物品下拉框使用)
@GetMapping("/listAll")
public R<List<Category>> listAll(){
List<Category> list = categoryMapper.selectList(null);
return R.success(list);
}
// 新增物品分类
@PostMapping("/add")
public R<String> add(@RequestBody Category category){
// 判断分类名称重复
QueryWrapper<Category> wrapper = new QueryWrapper<>();
wrapper.eq("category_name",category.getCategoryName());
Category exist = categoryMapper.selectOne(wrapper);
if(exist != null){
return R.error("该分类已存在,请勿重复添加");
}
categoryMapper.insert(category);
return R.success("新增分类成功");
}
// 修改分类信息
@PutMapping("/update")
public R<String> update(@RequestBody Category category){
categoryMapper.updateById(category);
return R.success("修改分类成功");
}
// 删除分类
@DeleteMapping("/delete")
public R<String> delete(@RequestParam Long id){
categoryMapper.deleteById(id);
return R.success("删除分类成功");
}
// 根据id查询单个分类(编辑弹窗回显)
@GetMapping("/getById")
public R<Category> getById(@RequestParam Long id){
Category category = categoryMapper.selectById(id);
return R.success(category);
}
}
后端分类模块业务逻辑讲解
- listAll接口:给前端发布失物/招领页面下拉选择框提供全部分类数据;
- 新增接口:增加重名校验,避免数据库出现同名分类;
- 修改/删除接口:给后台分类管理页面提供操作能力;
- 所有接口统一返回
R<T>格式,前端只需要判断code=200区分成功失败。
第三部分:前端登录页、注册页完整代码与调试(本人负责任务2:前端开发)
1. Axios全局请求封装(项目统一配置,页面直接调用)
// src/utils/request.js
import axios from 'axios'
const baseUrl = 'http://localhost:8080/api'
const service = axios.create({
baseURL: baseUrl,
timeout: 5000
})
// 请求拦截器携带登录用户信息
service.interceptors.request.use(config => {
let user = localStorage.getItem('user')
if(user){
config.headers.user = user
}
return config
})
export default service
2. 登录页面核心逻辑 Login.vue
<template>
<div class="login-box">
<h2>失物招领系统登录</h2>
<el-form ref="loginFormRef" :model="loginForm">
<el-form-item label="账号">
<el-input v-model="loginForm.username"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="loginForm.password" show-password></el-input>
</el-form-item>
<el-button type="primary" @click="handleLogin">登录</el-button>
<el-button @click="goRegister">去注册</el-button>
</el-form>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import request from '@/utils/request'
const router = useRouter()
const loginForm = ref({
username: '',
password: ''
})
// 登录请求
const handleLogin = async () => {
let res = await request.post('/user/login', loginForm.value)
if(res.data.code === 200){
localStorage.setItem('user', loginForm.value.username)
alert('登录成功')
router.push('/index')
}else{
alert(res.data.msg)
}
}
// 跳转注册页
const goRegister = () => {
router.push('/register')
}
</script>
登录页面功能说明
- 表单输入账号密码,调用后端
/api/user/login接口; - 登录成功将用户名存入localStorage,实现页面刷新保持登录;
- 登录失败弹窗提示账号不存在/密码错误;
- 提供跳转注册页面按钮,切换路由。
3. 注册页面核心逻辑 Register.vue
<template>
<div class="register-box">
<h2>用户注册</h2>
<el-form ref="regFormRef" :model="regForm">
<el-form-item label="账号">
<el-input v-model="regForm.username"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="regForm.password" show-password></el-input>
</el-form-item>
<el-button type="primary" @click="handleRegister">注册</el-button>
<el-button @click="goLogin">返回登录</el-button>
</el-form>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import request from '@/utils/request'
const router = useRouter()
const regForm = ref({
username: '',
password: ''
})
// 注册提交
const handleRegister = async () => {
let res = await request.post('/user/register', regForm.value)
if(res.data.code === 200){
alert('注册成功,请登录')
router.push('/login')
}else{
alert(res.data.msg)
}
}
// 返回登录页
const goLogin = () => {
router.push('/login')
}
</script>
注册页面业务逻辑与效果图
- 用户填写账号密码提交,调用后端注册接口;
- 后端校验账号是否重复,重复则返回提示;
- 注册成功自动跳转登录页面。


4. 页面调试过程遇到的问题(本人独立调试解决)
- 登录成功刷新页面直接退出
解决:使用localStorage持久化存储用户名,路由前置守卫判断本地是否存在用户,无用户强制跳转登录页; - 跨域请求403
后端全局配置Cors跨域,放行前端Vue3本地5173端口; - 注册账号重复未拦截
后端用户注册接口增加用户名查重逻辑,前端同步增加输入框失焦校验; - 分类下拉框无数据
页面加载时调用/category/listAll接口,将分类数据渲染到发布页面下拉选择器。
四、本人负责模块完整业务交互流程
- 数据库阶段:根据小组ER图编写四张数据表SQL,插入基础测试分类、用户数据,给全项目提供底层数据支撑;
- 后端开发:独立开发物品分类全套CRUD接口,提供给后台分类管理页面、物品发布页面下拉组件使用;
- 前端开发:完成登录、注册页面布局、表单逻辑、接口对接,调试页面路由跳转、登录状态存储;
- 联调阶段:将分类接口与失物/招领发布页面联调,实现发布物品可选择对应分类;登录页面对接用户认证模块,完成登录鉴权基础功能。
五、个人开发小结
本次实训我负责数据库脚本、物品分类后端模块、登录注册前端页面三大核心任务,完整覆盖数据底层、后端接口、前端页面三层开发。
数据库编写过程熟悉了外键、字符集、测试数据初始化规范;后端分类模块巩固了MyBatis-Plus基础CRUD、重复数据校验逻辑;Vue3登录注册页面掌握了路由跳转、localStorage登录持久化、Axios接口联调技巧。
所有代码、报错、解决方案均为本人独立开发调试产出,完全符合实训独立考核原创要求。
更多推荐

所有评论(0)