在传统校园、社区及大型公共场所中,失物招领一直是一个高频但低效的痛点。信息分散在公告栏、物业办公室、社交媒体群等不同渠道,失主与拾取者之间信息不对称,导致物品归还率低、寻物周期长。针对这一现实需求,我们设计并实现了一套基于SSM(Spring + Spring MVC + MyBatis)架构的智慧寻物协作平台,通过数字化、集中化的信息管理,显著提升了失物匹配与归还的效率。
系统架构与技术栈
该平台采用经典的三层架构模式,前后端分离设计,确保了系统的高内聚、低耦合特性。
表现层:基于JSP+HTML+CSS+JavaScript构建用户界面,提供响应式布局,适配不同设备访问。Spring MVC的DispatcherServlet统一拦截和处理HTTP请求,通过注解驱动的控制器(@Controller)实现请求路由和视图解析。
业务逻辑层:Spring框架作为核心容器,通过依赖注入(@Autowired)管理业务组件生命周期,声明式事务管理确保数据操作的事务性。服务层(Service)封装核心业务逻辑,如物品信息匹配、用户权限验证、感谢信发布等。
数据持久层:MyBatis作为ORM框架,通过XML映射文件或注解方式将Java对象与数据库表映射,提供灵活的SQL编写和结果集映射能力。连接池技术优化数据库访问性能。
技术栈明细:
- 后端框架:Spring 5.x + Spring MVC + MyBatis 3.x
- 数据库:MySQL 8.0(字符集utf8mb4,支持全字符集)
- 项目管理:Maven 3.6+
- 服务器:Tomcat 9.x
- 辅助工具:Hutool工具库(JSON处理、Excel导入导出)
数据库设计亮点分析
1. 核心业务表关系设计
平台的核心业务围绕"拾物管理"展开,t_gethingfile表作为中心实体,通过外键关联多个维度表,形成星型数据结构:
CREATE TABLE `t_gethingfile` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`t_uploadName` varchar(255) DEFAULT NULL COMMENT '图片',
`t_fileName` varchar(255) DEFAULT NULL COMMENT '文件名',
`t_uploadTime` varchar(255) DEFAULT NULL COMMENT '上传时间',
`t_title` varchar(255) DEFAULT NULL COMMENT '标题',
`t_wupin` varchar(255) DEFAULT NULL COMMENT '物品名称',
`t_diutime` varchar(255) DEFAULT NULL COMMENT '捡到时间',
`t_address` varchar(255) DEFAULT NULL COMMENT '捡到地点',
`t_bz` text DEFAULT NULL COMMENT '备注',
`addTime` datetime DEFAULT NULL COMMENT '插入数据库时间',
`user_id` int(11) DEFAULT NULL COMMENT '对应用户ID',
`wtype_id` int(11) DEFAULT NULL COMMENT '对应物品类型ID',
`wstaus_id` int(11) DEFAULT NULL COMMENT '对应物品状态ID',
PRIMARY KEY (`id`),
KEY `FK711F4D012D852AE4` (`user_id`),
KEY `FK711F4D01A94D2E4` (`wstaus_id`),
KEY `FK711F4D01A9ED0850` (`wtype_id`),
CONSTRAINT `FK711F4D012D852AE4` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
CONSTRAINT `FK711F4D01A94D2E4` FOREIGN KEY (`wstaus_id`) REFERENCES `t_wstaus` (`id`),
CONSTRAINT `FK711F4D01A9ED0850` FOREIGN KEY (`wtype_id`) REFERENCES `t_wtype` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='拾物管理表'
设计亮点:
- 外键约束完整性:通过FOREIGN KEY约束确保数据引用完整性,防止孤儿记录
- 索引优化:为所有外键字段建立索引,提升联表查询性能
- 字符集设计:采用utf8mb4_unicode_ci字符集,全面支持Emoji表情和生僻字
- 注释完整性:每个字段都有详细的中文注释,便于维护
2. 状态与类型字典表设计
平台采用字典表管理物品状态和类型,实现数据规范化和易扩展性:
CREATE TABLE `t_wstaus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`t_name` varchar(255) DEFAULT NULL COMMENT '状态',
`t_bz` text DEFAULT NULL COMMENT '备注',
`addTime` datetime DEFAULT NULL COMMENT '插入数据库时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='物品状态表'
CREATE TABLE `t_wtype` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`t_name` varchar(255) DEFAULT NULL COMMENT '类型',
`t_bz` text DEFAULT NULL COMMENT '备注',
`addTime` datetime DEFAULT NULL COMMENT '插入数据库时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='物品类型表'
设计优势:
- 数据一致性:避免在业务表中硬编码状态/类型值,确保数据一致性
- 易于维护:新增状态或类型只需在字典表中添加记录,无需修改代码
- 查询效率:整数字段关联比字符串关联性能更高
3. 感谢信业务关联设计
t_thanks表设计体现了业务闭环思维,通过用户外键关联实现完整的感谢流程:
CREATE TABLE `t_thanks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`t_duix` varchar(255) DEFAULT NULL COMMENT '感谢对象',
`t_reason` varchar(255) DEFAULT NULL COMMENT '是由',
`t_diutime` varchar(255) DEFAULT NULL COMMENT '发布时间',
`t_bz` text DEFAULT NULL COMMENT '备注',
`addTime` datetime DEFAULT NULL COMMENT '插入数据库时间',
`user_id` int(11) DEFAULT NULL COMMENT '对应用户ID',
PRIMARY KEY (`id`),
KEY `FK48B6B5142D852AE4` (`user_id`),
CONSTRAINT `FK48B6B5142D852AE4` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='感谢信管理表'
核心功能实现深度解析
1. 拾物信息管理功能
拾物管理是平台的核心功能,涉及复杂的数据关联和业务逻辑。控制器层通过@RequestMapping注解实现RESTful风格接口:
@Controller
@RequestMapping(value = "GethingFile")
public class GethingFileController {
@Autowired
private GethingFileService gethingFileService;
@Autowired
private UserService userService;
@Autowired
private WtypeService wtypeService;
@Autowired
private WstausService wstausService;
/**
* 初始化添加页面,加载用户、类型、状态等下拉框数据
*/
@RequestMapping(value = "/initPage.do")
public String initPage(HttpServletRequest request, Model model) {
// 过滤管理员用户,只显示普通用户
List<User> listUser = userService.getList(null, null);
List<User> returnUser = new ArrayList<>();
for (int i = 0; i < listUser.size(); i++) {
if(!listUser.get(i).getS_11().equals("admin")){
returnUser.add(listUser.get(i));
}
}
model.addAttribute("listUser", listUser);
List<Wtype> listWtype = wtypeService.getList(null, null);
model.addAttribute("listWtype", listWtype);
List<Wstaus> listWstaus = wstausService.getList(null, null);
model.addAttribute("listWstaus", listWstaus);
return "GethingFile/add";
}
/**
* 处理拾物信息提交,包含文件上传逻辑
*/
@Tip(value="添加拾物信息")
@RequestMapping(value = "/add.do")
public String add(GethingFile gethingFile,
@RequestParam(value = "file", required = false) MultipartFile file,
HttpServletRequest request) {
try {
if (file != null && !file.isEmpty()) {
// 文件上传处理逻辑
String originalFilename = file.getOriginalFilename();
String filePath = request.getSession().getServletContext()
.getRealPath("/upload/");
File destFile = new File(filePath + originalFilename);
file.transferTo(destFile);
gethingFile.setT_uploadName(originalFilename);
gethingFile.setT_fileName(originalFilename);
gethingFile.setT_uploadTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date()));
}
gethingFile.setAddTime(new Date());
gethingFileService.save(gethingFile);
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "success";
}
/**
* 分页查询拾物信息列表
*/
@RequestMapping(value = "/list.do")
public String list(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
Model model) {
PageModel<GethingFile> pageModel = gethingFileService
.getPageList(null, null, page, size);
// 关联查询用户、类型、状态信息
for (GethingFile item : pageModel.getList()) {
if (item.getUser_id() != null) {
User user = userService.get(item.getUser_id());
item.setUser(user);
}
if (item.getWtype_id() != null) {
Wtype wtype = wtypeService.get(item.getWtype_id());
item.setWtype(wtype);
}
if (item.getWstaus_id() != null) {
Wstaus wstaus = wstausService.get(item.getWstaus_id());
item.setWstaus(wstaus);
}
}
model.addAttribute("page", pageModel);
return "GethingFile/list";
}
}

服务层实现复杂的业务逻辑和数据库操作:
@Service
public class GethingFileServiceImpl implements GethingFileService {
@Autowired
private GethingFileMapper gethingFileMapper;
@Override
@Transactional(readOnly = true)
public PageModel<GethingFile> getPageList(Map<String, Object> params,
Map<String, String> order,
Integer page, Integer size) {
// 计算分页参数
Integer offset = (page - 1) * size;
params.put("offset", offset);
params.put("size", size);
// 执行分页查询
List<GethingFile> list = gethingFileMapper.getList(params, order);
Long total = gethingFileMapper.getCount(params);
PageModel<GethingFile> pageModel = new PageModel<>();
pageModel.setList(list);
pageModel.setTotal(total);
pageModel.setPage(page);
pageModel.setSize(size);
pageModel.setPages((int) Math.ceil((double) total / size));
return pageModel;
}
@Override
@Transactional
public void save(GethingFile gethingFile) {
if (gethingFile.getId() == null) {
gethingFileMapper.insert(gethingFile);
} else {
gethingFileMapper.update(gethingFile);
}
}
}
MyBatis映射文件实现灵活的SQL查询:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.edu.mapper.GethingFileMapper">
<resultMap id="BaseResultMap" type="com.edu.model.GethingFile">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="t_uploadName" property="t_uploadName" jdbcType="VARCHAR"/>
<result column="t_fileName" property="t_fileName" jdbcType="VARCHAR"/>
<result column="t_uploadTime" property="t_uploadTime" jdbcType="VARCHAR"/>
<result column="t_title" property="t_title" jdbcType="VARCHAR"/>
<result column="t_wupin" property="t_wupin" jdbcType="VARCHAR"/>
<result column="t_diutime" property="t_diutime" jdbcType="VARCHAR"/>
<result column="t_address" property="t_address" jdbcType="VARCHAR"/>
<result column="t_bz" property="t_bz" jdbcType="TEXT"/>
<result column="addTime" property="addTime" jdbcType="TIMESTAMP"/>
<result column="user_id" property="user_id" jdbcType="INTEGER"/>
<result column="wtype_id" property="wtype_id" jdbcType="INTEGER"/>
<result column="wstaus_id" property="wstaus_id" jdbcType="INTEGER"/>
</resultMap>
<select id="getList" resultMap="BaseResultMap" parameterType="map">
SELECT * FROM t_gethingfile
WHERE 1=1
<if test="t_title != null and t_title != ''">
AND t_title LIKE CONCAT('%', #{t_title}, '%')
</if>
<if test="t_wupin != null and t_wupin != ''">
AND t_wupin LIKE CONCAT('%', #{t_wupin}, '%')
</if>
<if test="user_id != null">
AND user_id = #{user_id}
</if>
ORDER BY addTime DESC
<if test="offset != null and size != null">
LIMIT #{offset}, #{size}
</if>
</select>
<insert id="insert" parameterType="com.edu.model.GethingFile"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_gethingfile (
t_uploadName, t_fileName, t_uploadTime, t_title,
t_wupin, t_diutime, t_address, t_bz, addTime,
user_id, wtype_id, wstaus_id
) VALUES (
#{t_uploadName}, #{t_fileName}, #{t_uploadTime}, #{t_title},
#{t_wupin}, #{t_diutime}, #{t_address}, #{t_bz}, #{addTime},
#{user_id}, #{wtype_id}, #{wstaus_id}
)
</insert>
</xml>
2. 感谢信管理功能
感谢信功能体现了平台的社会价值,促进用户间的正向互动:
@Controller
@RequestMapping(value = "Thanks")
public class ThanksController {
@Autowired
private ThanksService thanksService;
@Autowired
private UserService userService;
/**
* 发布感谢信
*/
@Tip(value="发布感谢信")
@RequestMapping(value = "/add.do")
public String add(Thanks thanks, HttpSession session) {
try {
// 从session中获取当前登录用户
User currentUser = (User) session.getAttribute("currentUser");
if (currentUser != null) {
thanks.setUser_id(currentUser.getId());
}
thanks.setAddTime(new Date());
thanks.setT_diutime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date()));
thanksService.save(thanks);
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "redirect:/Thanks/list.do";
}
/**
* 感谢信列表,支持按用户筛选
*/
@RequestMapping(value = "/list.do")
public String list(@RequestParam Map<String, Object> params,
@RequestParam(value = "page", defaultValue = "1") Integer page,
Model model) {
PageModel<Thanks> pageModel = thanksService.getPageList(params, null, page, 10);
// 关联查询用户信息
for (Thanks item : pageModel.getList()) {
if (item.getUser_id() != null) {
User user = userService.get(item.getUser_id());
item.setUser(user);
}
}
// 加载用户下拉框数据
List<User> userList = userService.getList(null, null);
model.addAttribute("userList", userList);
model.addAttribute("page", pageModel);
model.addAttribute("params", params);
return "Thanks/list";
}
}

3. 公告管理功能
公告系统为平台运营提供信息发布渠道:
@Controller
@RequestMapping(value = "Gonggao")
public class GonggaoController {
@Autowired
private GonggaoService gonggaoService;
/**
* 前台公告列表展示
*/
@RequestMapping(value = "/frontlist.do")
public String frontlist(Model model) {
Map<String, Object> params = new HashMap<>();
params.put("orderBy", "addTime desc");
List<Gonggao> gonggaoList = gonggaoService.getList(params, null);
model.addAttribute("gonggaoList", gonggaoList);