系统功能预览
项目背景与需求分析
现代宠物收容机构亟需信息化管理手段,以应对日益增长的动物收养、寄养及领养需求。传统手工记录方式效率低下、易出错,且难以支持多维度数据查询与统计。本系统旨在通过构建一套完整的Web管理平台,提升收容所运营效率,优化用户服务体验,同时增强数据安全性与可追溯性。
技术架构与开发环境
- 后端语言: Java (JDK 11+)
- Web容器: Apache Tomcat 9+
- 数据库: MySQL 8.0,使用Navicat Premium进行管理
- IDE: IntelliJ IDEA 或 Eclipse
- 架构模式: Servlet + JSP + JavaBean(MVC三层结构)
- 前端组件:
- My97DatePicker:日期选择控件
- FCKeditor / UEditor:富文本编辑器
- DWR:实现AJAX异步调用Java方法
- JSTL标签库:简化JSP页面逻辑
核心数据表设计
动物收养记录表(t_adoption)
| 字段名 | 类型 | 描述 |
| id | INT | 主键ID |
| receipt_no | VARCHAR(50) | 收养单据编号 |
| adopt_time | DATETIME | 收养时间 |
| total_fee | INT | 费用总额 |
| handler | VARCHAR(50) | 经办人 |
| remark | VARCHAR(50) | 备注信息 |
动物分类表(t_category)
| 字段名 | 类型 | 描述 |
| id | INT | 分类ID |
| name | VARCHAR(50) | 分类名称 |
| description | VARCHAR(5000) | 详细说明 |
后端控制器重构示例
package controller;
import model.Animal;
import service.CategoryService;
import util.DatabaseUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/animal")
public class AnimalController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
switch (action != null ? action : "") {
case "list":
listAnimals(req, resp);
break;
case "delete":
deleteAnimal(req, resp);
break;
case "editForm":
showEditForm(req, resp);
break;
case "viewPhoto":
viewPhoto(req, resp);
break;
default:
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
}
private void listAnimals(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int page = getPageNumber(req);
final int PAGE_SIZE = 10;
// 获取总记录数
int totalRecords = getTotalAnimalCount();
int totalPages = (int) Math.ceil((double) totalRecords / PAGE_SIZE);
// 查询当前页数据
List<Animal> animals = fetchPageData(page, PAGE_SIZE);
req.setAttribute("animals", animals);
req.setAttribute("currentPage", page);
req.setAttribute("totalPages", totalPages);
req.setAttribute("pageSize", PAGE_SIZE);
req.getRequestDispatcher("/WEB-INF/views/animal/list.jsp").forward(req, resp);
}
private List<Animal> fetchPageData(int page, int pageSize) {
List<Animal> list = new ArrayList<>();
String sql = "SELECT * FROM t_animal WHERE is_deleted = 0 ORDER BY id DESC LIMIT ?, ?";
try (DatabaseUtil db = new DatabaseUtil()) {
ResultSet rs = db.executeQuery(sql, (page - 1) * pageSize, pageSize);
while (rs.next()) {
Animal animal = new Animal();
animal.setId(rs.getInt("id"));
animal.setCategoryId(rs.getInt("category_id"));
animal.setName(rs.getString("name"));
animal.setOrigin(rs.getString("origin"));
animal.setGender(rs.getString("gender"));
animal.setWeight(rs.getString("weight"));
animal.setAge(rs.getString("age"));
animal.setPhotoPath(rs.getString("photo"));
animal.setCategoryName(CategoryService.getCategoryName(rs.getInt("category_id")));
list.add(animal);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
private void deleteAnimal(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int id = Integer.parseInt(req.getParameter("id"));
String sql = "UPDATE t_animal SET is_deleted = 1 WHERE id = ?";
try (DatabaseUtil db = new DatabaseUtil()) {
db.executeUpdate(sql, id);
req.getSession().setAttribute("message", "删除成功");
} catch (Exception e) {
e.printStackTrace();
}
resp.sendRedirect(req.getContextPath() + "/animal?action=list");
}
private void showEditForm(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = Integer.parseInt(req.getParameter("id"));
Animal animal = getAnimalById(id);
req.setAttribute("animal", animal);
req.getRequestDispatcher("/WEB-INF/views/animal/edit.jsp").forward(req, resp);
}
private Animal getAnimalById(int id) {
Animal animal = null;
String sql = "SELECT * FROM t_animal WHERE id = ?";
try (DatabaseUtil db = new DatabaseUtil()) {
ResultSet rs = db.executeQuery(sql, id);
if (rs.next()) {
animal = new Animal();
animal.setId(rs.getInt("id"));
animal.setCategoryId(rs.getInt("category_id"));
animal.setName(rs.getString("name"));
animal.setOrigin(rs.getString("origin"));
animal.setGender(rs.getString("gender"));
animal.setWeight(rs.getString("weight"));
animal.setAge(rs.getString("age"));
animal.setPhotoPath(rs.getString("photo"));
animal.setRemark(rs.getString("remark"));
animal.setCategoryName(CategoryService.getCategoryName(rs.getInt("category_id")));
}
} catch (Exception e) {
e.printStackTrace();
}
return animal;
}
private void viewPhoto(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = Integer.parseInt(req.getParameter("id"));
Animal animal = getAnimalById(id);
req.setAttribute("animal", animal);
req.getRequestDispatcher("/WEB-INF/views/animal/photo.jsp").forward(req, resp);
}
private int getPageNumber(HttpServletRequest req) {
String p = req.getParameter("page");
return (p == null || p.isEmpty()) ? 1 : Integer.parseInt(p);
}
private int getTotalAnimalCount() {
String sql = "SELECT COUNT(*) as total FROM t_animal WHERE is_deleted = 0";
try (DatabaseUtil db = new DatabaseUtil()) {
ResultSet rs = db.executeQuery(sql);
if (rs.next()) return rs.getInt("total");
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
前端列表页优化示例
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String ctx = request.getContextPath();
%>
<!DOCTYPE html>
<html>
<head>
<title>动物管理</title>
<link rel="stylesheet" href="<%=ctx%>/css/style.css">
<script src="<%=ctx%>/js/utils.js"></script>
</head>
<body>
<div class="toolbar">
<button onclick="navigateTo('<%=ctx%>/animal/add')">新增动物</button>
<form method="get" action="<%=ctx%>/animal" style="display:inline-block;">
<input type="hidden" name="action" value="list"/>
<input type="text" name="keyword" placeholder="搜索名称..." />
<button type="submit">🔍</button>
</form>
</div>
<table class="data-table">
<thead>
<tr>
<th>类别</th>
<th>名称</th>
<th>产地</th>
<th>性别</th>
<th>体重(kg)</th>
<th>年龄</th>
<th>备注</th>
<th>图片</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="item" items="${animals}">
<tr>
<td>${item.categoryName}</td>
<td>${item.name}</td>
<td>${item.origin}</td>
<td>${item.gender}</td>
<td>${item.weight}</td>
<td>${item.age}</td>
<td>${item.remark}</td>
<td>
<c:if test="${not empty item.photoPath}">
<a href="#" onclick="showImageModal(${item.id})">📷 查看</a>
</c:if>
</td>
<td>
<button onclick="editAnimal(${item.id})">✏️ 编辑</button>
<button onclick="confirmDelete(${item.id})">🗑️ 删除</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<div class="pagination">
<span>共 ${totalRecords} 条记录,${totalPages} 页,当前第 ${currentPage} 页</span>
<a href="?action=list&page=1">首页</a>
<a href="?action=list&page=${currentPage > 1 ? currentPage - 1 : 1}">上一页</a>
<a href="?action=list&page=${currentPage < totalPages ? currentPage + 1 : totalPages}">下一页</a>
<a href="?action=list&page=${totalPages}">尾页</a>
跳至第 <input type="number" min="1" max="${totalPages}" value="${currentPage}" onchange="jumpToPage(this.value)" /> 页
</div>
<script>
function confirmDelete(id) {
if (confirm('确定要删除该动物记录吗?')) {
window.location.href = '<%=ctx%>/animal?action=delete&id=' + id;
}
}
function editAnimal(id) {
window.location.href = '<%=ctx%>/animal/editForm?id=' + id;
}
function showImageModal(id) {
const popup = new Popup({
contentType: 1,
width: 400,
height: 300,
isReloadOnClose: false
});
popup.setContent("contentUrl", "<%=ctx%>/animal?action=viewPhoto&id=" + id);
popup.setContent("title", "动物照片");
popup.build();
popup.show();
}
function navigateTo(url) {
window.location.href = url;
}
function jumpToPage(pageNum) {
window.location.href = '?action=list&page=' + pageNum;
}
</script>
</body>
</html>