在电子商务蓬勃发展的时代背景下,家具行业的数字化转型已成为必然趋势。传统的家具购买流程存在信息不对称、实物体验依赖性强、地域限制明显以及决策周期长等诸多痛点。消费者往往需要花费大量时间精力往返于各个实体卖场,仅凭有限的样品和销售人员的介绍做出高价值的购买决策,风险较高。因此,一个能够整合产品信息、提供详细规格参数、展示用户真实评价并支持便捷在线交易的平台,具有显著的市场价值和应用前景。本项目正是针对这一市场需求,设计并实现了一个功能完备的家具在线购物系统。
该系统采用业界成熟的SSM(Spring + Spring MVC + MyBatis)框架组合进行构建,旨在打造一个稳定、高效、易维护的B2C电子商务解决方案。Spring框架作为项目的核心,负责管理所有业务对象(Service Bean)的生命周期和依赖注入,其强大的声明式事务管理能力确保了订单创建、库存扣减、支付处理等核心业务操作的数据一致性和完整性。Spring MVC框架承担了Web请求的调度和处理职责,通过清晰的控制器(Controller)设计,将用户请求如商品浏览、搜索、加入购物车、下单等动作映射到相应的业务处理方法上,并最终将数据模型传递给JSP视图进行渲染,实现了前后端的有效分离。数据持久层则选用MyBatis框架,它通过灵活的XML配置或注解方式,将Java对象与数据库表记录进行映射(ORM),其动态SQL特性极大地便利了多条件商品查询、分页等复杂数据操作。整个项目使用Maven进行依赖管理和构建,数据库采用关系型数据库MySQL,前端页面综合运用了HTML、CSS和JavaScript技术,共同构建了用户友好的交互界面。
核心架构与技术栈剖析
1. 控制层(Spring MVC)设计
控制层是系统与用户交互的入口,负责接收HTTP请求、调用业务逻辑处理并返回响应。在本平台中,控制器被设计得职责清晰。例如,商品相关的请求由ProductController处理,用户和订单相关的请求则分别由UserController和OrderController处理。这种设计遵循了单一职责原则,提高了代码的可读性和可维护性。Spring MVC的@Controller注解用于标记控制器类,@RequestMapping注解则用于将URL映射到具体的处理方法上。
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/detail/{productId}")
public String getProductDetail(@PathVariable("productId") Integer productId, Model model) {
Product product = productService.getProductById(productId);
model.addAttribute("product", product);
return "product_detail";
}
@RequestMapping("/list")
public String listProducts(@RequestParam(value = "categoryId", required = false) Integer categoryId,
@RequestParam(value = "keyword", required = false) String keyword,
Model model) {
List<Product> productList = productService.getProductsByCondition(categoryId, keyword);
model.addAttribute("productList", productList);
return "product_list";
}
}
上述代码展示了商品控制器的两个核心方法。getProductDetail方法通过路径变量接收商品ID,调用服务层获取商品详情,并放入模型后跳转到详情页面。listProducts方法则支持通过查询参数(分类ID和关键词)进行商品筛选,体现了控制器对多样化请求的处理能力。
2. 业务逻辑层(Spring Service)设计
业务逻辑层封装了系统的核心业务规则,是连接控制层和数据持久层的桥梁。服务层通常使用接口与实现分离的方式,便于后续扩展和单元测试。Spring的@Service注解用于标识服务组件,并通过@Autowired实现依赖注入。
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
@Override
@Transactional
public boolean createOrder(Order order, List<OrderItem> itemList) {
// 1. 插入订单主信息
int orderResult = orderMapper.insertOrder(order);
if (orderResult <= 0) {
throw new RuntimeException("Failed to create order.");
}
// 2. 批量插入订单项
for (OrderItem item : itemList) {
item.setOrderId(order.getOrderId());
int itemResult = orderMapper.insertOrderItem(item);
if (itemResult <= 0) {
throw new RuntimeException("Failed to add order item.");
}
// 3. 更新商品库存
int updateStockResult = productMapper.updateProductStock(item.getProductId(), -item.getQuantity());
if (updateStockResult <= 0) {
throw new RuntimeException("Insufficient stock for product: " + item.getProductId());
}
}
return true;
}
}
创建订单的服务方法createOrder是典型的业务逻辑,它使用了Spring的@Transactional注解来声明事务。该方法包含了插入订单记录、插入订单明细项和更新商品库存三个数据库操作。这三个操作被绑定在同一个事务中,要么全部成功,要么全部失败回滚,有效防止了已扣减库存但订单未生成成功的数据不一致情况,保证了业务的原子性。
3. 数据持久层(MyBatis Mapper)设计 数据持久层负责与数据库进行直接交互。MyBatis的Mapper接口定义了数据操作的方法,具体的SQL语句则写在XML映射文件中。这种方式将SQL与Java代码分离,使得SQL更易于管理和优化。
<!-- OrderMapper.xml -->
<?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.furniture.mall.mapper.OrderMapper">
<insert id="insertOrder" parameterType="Order" useGeneratedKeys="true" keyProperty="orderId">
INSERT INTO orders (user_id, total_amount, status, create_time, update_time)
VALUES (#{userId}, #{totalAmount}, #{status}, NOW(), NOW())
</insert>
<insert id="insertOrderItem" parameterType="OrderItem">
INSERT INTO order_item (order_id, product_id, quantity, unit_price)
VALUES (#{orderId}, #{productId}, #{quantity}, #{unitPrice})
</insert>
<select id="selectOrdersByUserId" resultType="Order">
SELECT order_id, user_id, total_amount, status, create_time
FROM orders
WHERE user_id = #{userId}
ORDER BY create_time DESC
</select>
</mapper>
对应的Mapper接口定义如下:
public interface OrderMapper {
int insertOrder(Order order);
int insertOrderItem(OrderItem item);
List<Order> selectOrdersByUserId(Integer userId);
}
MyBatis的动态SQL功能在处理复杂查询时尤为强大,例如根据多种条件筛选商品:
<select id="selectProductsByCondition" parameterType="map" resultType="Product">
SELECT * FROM products
<where>
<if test="categoryId != null">
AND category_id = #{categoryId}
</if>
<if test="keyword != null and keyword != ''">
AND (product_name LIKE CONCAT('%', #{keyword}, '%') OR description LIKE CONCAT('%', #{keyword}, '%'))
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
<if test="maxPrice != null">
AND price <= #{maxPrice}
</if>
</where>
ORDER BY create_time DESC
</select>
这段XML配置定义了一个动态查询,根据传入的categoryId、keyword、minPrice和maxPrice等参数,动态生成WHERE条件,避免了编写大量重复的SQL语句,极大地提升了开发效率和灵活性。
数据库设计亮点解析
一个稳健的电子商务系统离不开精心设计的数据库模型。本平台共设计了12张核心数据表,围绕用户、商品、订单、分类等实体构建了清晰的数据关系。以下重点分析几个核心表的设计。
1. 商品表(products) 商品表是系统的基础,存储了所有家具商品的详细信息。
CREATE TABLE `products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(255) NOT NULL,
`description` text,
`price` decimal(10,2) NOT NULL,
`stock_quantity` int(11) NOT NULL DEFAULT '0',
`category_id` int(11) NOT NULL,
`main_image` varchar(500) DEFAULT NULL,
`detail_images` text,
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1-上架 0-下架',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`product_id`),
KEY `idx_category_id` (`category_id`),
KEY `idx_status` (`status`),
CONSTRAINT `fk_product_category` FOREIGN KEY (`category_id`) REFERENCES `categories` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
设计亮点:
- 字段完整性:包含了商品名称、描述、价格、库存等核心字段。
price字段使用DECIMAL(10,2)类型,精确存储金额,避免浮点数计算误差。 - 图像存储:设计了
main_image(主图)和detail_images(详情图集)字段。detail_images使用TEXT类型,可以存储多张图片的URL(如用分号或JSON格式分隔),满足商品详情页的多图展示需求。 - 状态管理:通过
status字段管理商品上下架状态,便于运营控制。 - 索引优化:为
category_id和status字段建立了索引,显著提升了按分类查询和筛选上架商品的速度。 - 外键约束:通过外键
category_id关联分类表(categories),保证了数据的一致性和参照完整性。
2. 订单表(orders)与订单项表(order_items)
订单系统采用经典的主子表结构,orders表存储订单概要,order_items表存储订单中的具体商品项。
CREATE TABLE `orders` (
`order_id` varchar(32) NOT NULL,
`user_id` int(11) NOT NULL,
`total_amount` decimal(10,2) NOT NULL,
`status` tinyint(4) NOT NULL COMMENT '1-待付款 2-待发货 3-待收货 4-已完成 5-已取消',
`shipping_address` text NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
CONSTRAINT `fk_order_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `order_items` (
`item_id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` varchar(32) NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(11) NOT NULL,
`unit_price` decimal(10,2) NOT NULL,
PRIMARY KEY (`item_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_product_id` (`product_id`),
CONSTRAINT `fk_item_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`order_id`) ON DELETE CASCADE,
CONSTRAINT `fk_item_product` FOREIGN KEY (`product_id`) REFERENCES `products` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
设计亮点:
- 订单号设计:
order_id没有使用常见的自增整数,而是采用varchar(32),通常用于存储分布式生成的唯一ID(如雪花算法ID或UUID),更适合分布式系统环境,且不易被猜测。 - 金额分离:
order_items表中的unit_price字段在下单时快照了商品的单价。这样做的好处是,即使后续商品价格发生变化,历史订单的金额也不会改变,保证了订单数据的准确性。 - 关系清晰:
orders表与users表关联,order_items表同时与orders和products表关联,形成了清晰的“用户-订单-商品”关系链。 - 级联删除:
fk_item_order外键设置了ON DELETE CASCADE,当主订单被删除时,其对应的所有订单项会自动删除,简化了数据维护操作。
核心功能实现深度解析
1. 商品浏览与搜索功能
商品浏览是用户接触平台的第一步。平台首页和分类页通过调用ProductService的查询方法,展示商品列表。用户可以根据分类导航或使用搜索框输入关键词进行筛选。
图:平台首页展示各类家具商品
实现此功能的核心是ProductService中一个支持多条件查询的方法。服务层接收前端传递的查询条件(如分类ID、关键词、价格区间),并调用Mapper层的动态SQL进行查询。
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public List<Product> getProductsByCondition(Integer categoryId, String keyword, BigDecimal minPrice, BigDecimal maxPrice) {
Map<String, Object> params = new HashMap<>();
params.put("categoryId", categoryId);
params.put("keyword", keyword);
params.put("minPrice", minPrice);
params.put("maxPrice", maxPrice);
return productMapper.selectProductsByCondition(params);
}
}
对应的Mapper XML文件中的动态SQL如前文所示,能够灵活组合查询条件,提供精确的商品检索能力。
2. 购物车与下单流程
购物车功能允许用户暂存心仪商品,并统一结算。核心实体是CartItem,关联用户和商品。
图:用户将选中的沙发加入购物车
当用户点击结算时,系统进入下单流程。这个过程涉及多个实体和严格的事务控制。
@Controller
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private CartService cartService;
@RequestMapping("/create")
@ResponseBody
public ResponseEntity<Map<String, Object>> createOrder(@RequestBody OrderRequest orderRequest, HttpSession session) {
// 1. 从session中获取当前用户
User currentUser = (User) session.getAttribute("currentUser");
if (currentUser == null) {
return ResponseEntity.status(401).body(Collections.singletonMap("error", "User not logged in."));
}
// 2. 构建订单对象
Order order = new Order();
order.setUserId(currentUser.getUserId());
order.setTotalAmount(orderRequest.getTotalAmount());
order.setStatus(1); // 待付款
order.setShippingAddress(orderRequest.getShippingAddress());
// 3. 构建订单项列表(从购物车或直接购买的商品)
List<OrderItem> itemList = orderRequest.getItems();
try {
// 4. 调用服务层创建订单(事务性操作)
boolean isSuccess = orderService.createOrder(order, itemList);
if (isSuccess) {
// 5. 订单创建成功,清空对应的购物车项
cartService.clearCartItems(currentUser.getUserId(), orderRequest.getCartItemIds());
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("orderId", order.getOrderId());
return ResponseEntity.ok(result);
} else {
return ResponseEntity.badRequest().body(Collections.singletonMap("error", "Failed to create order."));
}
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body(Collections.singletonMap("error", "Internal server error: " + e.getMessage()));
}
}
}
上述控制器方法处理创建订单的请求。它首先验证用户登录状态,然后组装订单数据,最后调用具有事务性的服务方法。如果订单创建成功,则清理用户购物车中已下单的商品。
图:用户确认订单信息并进行结算
3. 后台管理系统功能 平台配备了功能强大的后台管理系统,供管理员管理商品、订单、用户和公告等。
图:管理员在后台对商品信息进行增删改查
后台商品管理的关键在于对商品数据的全面控制,包括上架/下架、编辑详情、更新库存等。其服务层方法通常包含复杂的业务校验。
@Service
public class AdminProductServiceImpl implements AdminProductService {
@Autowired
private ProductMapper productMapper;
@Override
@Transactional
public boolean updateProduct(Product product) {
// 业务校验:例如检查分类是否存在,价格是否合法等
if (product.getPrice().compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Product price cannot be negative.");
}
// 更新操作
int result = productMapper.updateProduct(product);
return result > 0;
}
@Override
public PageInfo<Product> getProductList(int pageNum, int pageSize, String keyword) {
PageHelper.startPage(pageNum, pageSize);
List<Product> products = productMapper.selectProductsByKeyword(keyword);
return new PageInfo<>(products);
}
}
这里使用了PageHelper插件实现分页查询,这是MyBatis生态中非常流行的分页工具,能极大简化分页逻辑的编写。
图:管理员查看和处理用户订单,包括发货操作
订单管理是后台的核心,管理员可以查看订单状态,并进行发货等操作。
@Service
public class AdminOrderServiceImpl implements AdminOrderService {
@Autowired
private OrderMapper orderMapper;