在数字化浪潮席卷各行各业的今天,宠物交易领域也迎来了深刻的变革。传统线下交易模式存在信息不对称、交易流程繁琐、缺乏有效监管等诸多痛点,制约了行业的健康发展。为此,一个基于成熟稳定的SSM技术栈构建的在线宠物交易平台应运而生。该平台不仅为宠物爱好者与合规商家搭建了安全、便捷的数字桥梁,更通过严谨的架构设计与业务逻辑,重塑了宠物交易的线上体验。
系统架构与技术栈
该平台采用经典的SSM三层架构,即Spring + SpringMVC + MyBatis的组合,这是Java Web领域经久不衰的成熟解决方案。
- Spring框架作为整个应用的核心容器,负责管理所有业务对象(如宠物、订单、用户等)的生命周期,并通过其强大的依赖注入机制,解耦了各个组件之间的依赖关系。同时,Spring的声明式事务管理确保了核心交易流程的原子性、一致性、隔离性和持久性。
- SpringMVC框架承担了Web表现层的职责。其核心
DispatcherServlet作为前端控制器,统一接收所有HTTP请求,根据配置的映射关系,将请求分发给对应的控制器(如PetController、OrderController)。控制器调用Service层完成业务逻辑处理后,返回JSON数据或视图模型给前端。 - MyBatis作为持久层框架,负责与MySQL数据库进行交互。它通过XML映射文件或注解的方式,将Java对象与数据库记录灵活地映射起来,并支持动态SQL,能够高效地应对复杂的多条件查询场景。
项目采用Maven进行依赖管理和构建,前端使用HTML、CSS、JavaScript以及Ajax技术实现异步数据交互,保证了用户操作的流畅性。整个技术栈选型稳健,社区资源丰富,为平台的长期稳定运行和功能迭代奠定了坚实基础。
数据库设计亮点
数据库是系统的基石,其设计的优劣直接影响到系统的性能、可扩展性和数据一致性。该平台拥有23张数据表,涵盖了用户、商品、订单、分类等核心业务实体。以下对几个关键表的设计进行深度剖析。
1. 订单信息表 (dingdanxinxi)
订单是电商系统的核心,其设计至关重要。
CREATE TABLE `dingdanxinxi` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dingdanbianhao` varchar(50) NOT NULL COMMENT '订单编号',
`dingdanxinxi` text NOT NULL COMMENT '订单信息',
`zongjijine` decimal(18,2) NOT NULL COMMENT '总计金额',
`shouhuoren` varchar(50) NOT NULL COMMENT '收货人',
`dianhua` varchar(50) NOT NULL COMMENT '电话',
`dizhi` varchar(255) NOT NULL COMMENT '地址',
`beizhu` text NOT NULL COMMENT '备注',
`zhuangtai` varchar(255) NOT NULL COMMENT '状态',
`xiadanren` varchar(50) NOT NULL COMMENT '下单人',
`iszf` varchar(10) NOT NULL DEFAULT '否' COMMENT '是否支付',
`addtime` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '添加时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='订单信息'
设计亮点分析:
- 字段类型选择精准:
id字段使用AUTO_INCREMENT自增主键,确保唯一性且性能高效。金额字段zongjijine采用decimal(18,2)类型,精确到分,完全符合金融计算要求,避免了浮点数精度丢失问题。addtime使用timestamp类型并设置默认值为当前时间,由数据库自动维护,保证了数据创建时间的准确性。 - 状态字段设计灵活:
zhuangtai(状态)和iszf(是否支付)字段均使用varchar类型,而非枚举或整型。这种设计虽然牺牲了部分数据库层面的约束,但带来了极大的灵活性,便于后续扩展新的状态值而无需修改表结构。状态机逻辑可以完全由业务代码控制。 - 可扩展性考量:
dingdanxinxi(订单信息)字段使用了text类型,可以存储结构化的JSON字符串或XML数据。这种设计常用于存储订单快照,即下单时商品的价格、名称等信息。即使后续商品信息发生变化,订单快照也能保证历史数据的准确性,这对于售后、纠纷处理至关重要。
2. 购物车表 (gouwuche)
购物车是用户交互最频繁的模块之一,其设计直接影响用户体验。
CREATE TABLE `gouwuche` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`petxinxiid` int(10) unsigned NOT NULL COMMENT '书籍信息id',
`petbianhao` varchar(50) NOT NULL COMMENT '书籍编号',
`petmingcheng` varchar(255) NOT NULL COMMENT '书籍名称',
`fenlei` int(10) unsigned NOT NULL COMMENT '分类',
`xiaoshoujiage` decimal(18,2) NOT NULL COMMENT '销售价格',
`goumaishuliang` int(11) NOT NULL COMMENT '购买数量',
`xiaoji` decimal(18,2) NOT NULL COMMENT '小计',
`goumairen` varchar(50) NOT NULL COMMENT '购买人',
`addtime` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '添加时间',
PRIMARY KEY (`id`),
KEY `gouwuche_petxinxiid_index` (`petxinxiid`),
KEY `gouwuche_fenlei_index` (`fenlei`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='购物车'
设计亮点分析:
- 冗余字段优化查询:表中不仅包含了外键
petxinxiid,还冗余存储了petbianhao(宠物编号)、petmingcheng(宠物名称)、xiaoshoujiage(销售价格)等商品信息。这种反范式设计是一种典型的“用空间换时间”策略。在展示购物车列表时,无需再关联petxinxi表进行查询,极大地提升了查询性能。 - 索引策略得当:为
petxinxiid和fenlei(分类)字段建立了索引。petxinxiid索引可用于快速定位某个商品在购物车中的记录(如修改数量或删除),而fenlei索引则可能用于后台的数据分析,例如统计各类别商品的加购情况。 - 价格快照机制:
xiaoshoujiage字段在加入购物车时即被固定下来。这意味着即使后台管理员后来修改了宠物的售价,用户购物车中的价格也不会改变,避免了结算时的价格纠纷,保证了交易的公平性。
3. 分类表 (petfenlei) 与关联设计
分类表结构简单,但其关联设计体现了业务逻辑。
CREATE TABLE `petfenlei` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`fenleimingcheng` varchar(255) NOT NULL COMMENT '分类名称',
`addtime` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '添加时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='书籍分类'
在dingdanqianshou_dingdanxinxi(订单签收-订单信息)表中,使用fenlei字段与分类表关联。这是一种典型的维度表设计,将分类这种相对稳定的信息独立出来,避免了在事实表(如订单表)中重复存储分类名称,符合数据库第三范式,保证了数据的一致性和可维护性。
核心功能实现
1. 购物车管理与下单流程
购物车功能是电商平台的枢纽。用户可以将心仪的宠物加入购物车,统一结算。
前端添加购物车逻辑(Ajax示例):
function addToCart(petxinxiid) {
$.ajax({
url: "gouwuche_add.do",
type: "post",
data: {
petxinxiid: petxinxiid,
goumaishuliang: $("#goumaishuliang").val() // 获取用户输入的数量
},
dataType: "json",
success: function(result) {
if (result.code == 200) {
alert("添加成功!");
// 更新页面购物车图标数量
updateCartCount();
} else {
alert(result.msg);
}
}
});
}
后端购物车Controller核心代码:
@Controller
@RequestMapping("/gouwuche")
public class GouWuCheController extends BaseController {
@Autowired
private GouWuCheService gouWuCheService;
@Autowired
private PetXinXiService petXinXiService;
/**
* 加入购物车
*/
@RequestMapping("/add")
@ResponseBody
public HashMap<String, Object> add() {
HashMap<String, Object> result = new HashMap<>();
// 1. 获取当前登录用户
String goumairen = (String) request.getSession().getAttribute("username");
if (goumairen == null) {
result.put("code", 500);
result.put("msg", "请先登录");
return result;
}
// 2. 获取前台参数
Integer petxinxiid = Request.getInt("petxinxiid");
Integer goumaishuliang = Request.getInt("goumaishuliang", 1); // 默认数量为1
// 3. 查询宠物信息
PetXinXi petXinXi = petXinXiService.find(petxinxiid);
if (petXinXi == null) {
result.put("code", 500);
result.put("msg", "宠物信息不存在");
return result;
}
// 4. 检查是否已存在该宠物的购物车记录
Example example = new Example(GouWuChe.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("petxinxiid", petxinxiid);
criteria.andEqualTo("goumairen", goumairen);
List<GouWuChe> list = gouWuCheService.selectExample(example);
GouWuChe gouWuChe;
if (list.isEmpty()) {
// 新增记录
gouWuChe = new GouWuChe();
gouWuChe.setPetxinxiid(petxinxiid);
gouWuChe.setPetbianhao(petXinXi.getBianhao());
gouWuChe.setPetmingcheng(petXinXi.getMingcheng());
gouWuChe.setFenlei(petXinXi.getFenlei());
gouWuChe.setXiaoshoujiage(petXinXi.getXiaoshoujiage());
gouWuChe.setGoumairen(goumairen);
gouWuChe.setGoumaishuliang(goumaishuliang);
// 计算小计:销售价格 * 购买数量
gouWuChe.setXiaoji(petXinXi.getXiaoshoujiage().multiply(new BigDecimal(goumaishuliang)));
gouWuCheService.insert(gouWuChe);
} else {
// 更新已有记录的数量和小计
gouWuChe = list.get(0);
int newShuliang = gouWuChe.getGoumaishuliang() + goumaishuliang;
gouWuChe.setGoumaishuliang(newShuliang);
gouWuChe.setXiaoji(petXinXi.getXiaoshoujiage().multiply(new BigDecimal(newShuliang)));
gouWuCheService.update(gouWuChe);
}
result.put("code", 200);
result.put("msg", "操作成功");
return result;
}
}

图示:购物车管理界面,用户可以清晰地查看所选宠物、单价、数量和小计,并进行管理。
下单时,系统会遍历购物车,生成订单主表(dingdanxinxi)记录和详情的快照记录,并清空当前用户的购物车。整个过程在一个事务中完成,确保数据一致性。
2. 后台订单管理与状态流转
后台管理员需要对订单进行全生命周期管理,包括确认、发货、标记签收等。
订单列表查询与分页(Controller层):
/**
* 后台列表页
*/
@RequestMapping("/dingdanxinxi_list")
public String list() {
// 权限校验
if (!checkLogin()) {
return showError("尚未登录", "./login.do");
}
// 获取排序参数
String order = Request.get("order", "id");
String sort = Request.get("sort", "desc");
// 构建MyBatis查询条件
Example example = new Example(DingDanXinXi.class);
Example.Criteria criteria = example.createCriteria();
String where = " 1=1 ";
where += getWhere(); // 拼接搜索条件
criteria.andCondition(where);
// 设置排序
if (sort.equals("desc")) {
example.orderBy(order).desc();
} else {
example.orderBy(order).asc();
}
// 分页处理
int page = request.getParameter("page") == null ? 1 : Integer.valueOf(request.getParameter("page"));
page = Math.max(1, page);
List<DingDanXinXi> list = service.selectPageExample(example, page, 12); // 每页12条
// 数据传递到前端
assign("list", list);
assign("orderby", order);
assign("sort", sort);
assign("where", where);
return "dingdanxinxi_list";
}
/**
* 构建搜索条件
*/
public String getWhere() {
String where = " ";
// 按订单编号搜索
if (!Request.get("dingdanbianhao").equals("")) {
where += " AND dingdanbianhao LIKE '%" + Request.get("dingdanbianhao") + "%' ";
}
// 按下单人搜索
if (!Request.get("xiadanren").equals("")) {
where += " AND xiadanren LIKE '%" + Request.get("xiadanren") + "%' ";
}
// 按状态搜索
if (!Request.get("zhuangtai").equals("")) {
where += " AND zhuangtai = '" + Request.get("zhuangtai") + "' ";
}
return where;
}
订单发货Service层逻辑:
@Service
public class DingDanXinXiService {
@Autowired
private DingDanXinXiMapper mapper;
/**
* 更新订单状态为已发货
* @param id 订单ID
* @param wuliuInfo 物流信息
*/
@Transactional // 声明式事务,确保操作原子性
public void fahuo(Integer id, String wuliuInfo) {
DingDanXinXi dingDan = mapper.selectByPrimaryKey(id);
if (dingDan == null) {
throw new RuntimeException("订单不存在");
}
if (!"待发货".equals(dingDan.getZhuangtai())) {
throw new RuntimeException("订单当前状态不允许发货");
}
// 更新订单状态和物流信息
dingDan.setZhuangtai("已发货");
// 这里可以将wuliuInfo存入某个扩展字段
mapper.updateByPrimaryKey(dingDan);
// 可以在此处添加记录发货日志、发送通知消息等逻辑
// ...
}
}

图示:后台订单管理界面,管理员可以查看所有订单的详细信息,并进行状态操作。
3. 宠物信息分类浏览与搜索
前端用户需要能够方便地浏览和查找宠物。
宠物列表查询(动态SQL示例,MyBatis Mapper XML):
<!-- PetXinXiMapper.xml -->
<select id="selectByExample" parameterType="com.spring.entity.PetXinXiExample" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from petxinxi
<where>
<if test="fenlei != null">
and fenlei = #{fenlei}
</if>
<if test="mingcheng != null and mingcheng != ''">
and petmingcheng like CONCAT('%', #{mingcheng}, '%')
</if>
<if test="jianjie != null and jianjie != ''">
and jianjie like CONCAT('%', #{jianjie}, '%')
</if>
<if test="priceMin != null">
and xiaoshoujiage >= #{priceMin}
</if>
<if test="priceMax != null">
and xiaoshoujiage <= #{priceMax}
</if>
</where>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
对应的Controller调用:
@RequestMapping("/petxinxi_list")
public String list() {
PetXinXiExample example = new PetXinXiExample();
PetXinXiExample.Criteria criteria = example.createCriteria();
// 分类筛选
if (!Request.get("fenlei").equals("")) {
criteria.andFenleiEqualTo(Request.getInt("fenlei"));
}
// 关键词搜索
if (!Request.get("mingcheng").equals("")) {
criteria.andMingchengLike("%" + Request.get("mingcheng") + "%");
}
// 价格区间筛选
if (!Request.get("priceMin").equals("")) {
criteria.andXiaoshoujiageGreaterThanOrEqualTo(new BigDecimal(Request.get("priceMin")));
}
if (!Request.get("priceMax").equals("")) {
criteria.andXiaoshoujiageLessThanOrEqualTo(new BigDecimal(Request.get("priceMax")));
}
// 排序:默认按添加时间倒序,新发布的在前
example.setOrderByClause("addtime desc");
int page = Request.getInt("page", 1);
List<PetXinXi> list = petXinXiService