随着电子商务的蓬勃发展,传统零售业正经历着深刻的数字化转型。特别是在时尚零售领域,女性消费者对购物体验的便捷性、商品信息的透明度以及个性化服务的需求日益增长。本文详细介绍了一个基于SSM(Spring + Spring MVC + MyBatis)技术栈构建的线上女装零售平台——“风尚衣橱”的设计与实现。该系统旨在通过现代化的Web技术,为中小型女装品牌商和消费者搭建一个高效、稳定、易用的在线交易环境。
系统架构与技术栈
“风尚衣橱”系统采用经典的三层架构模式,将应用逻辑清晰地划分为表现层、业务逻辑层和数据持久层。
表现层由Spring MVC框架负责,它通过DispatcherServlet作为前端控制器,统一接收所有HTTP请求。Controller层处理用户交互逻辑,进行参数绑定、数据验证,并选择合适的视图渲染结果。该层还负责管理用户会话状态,例如购物车数据和登录状态。
业务逻辑层基于Spring框架的核心IoC(控制反转)容器实现。通过依赖注入(DI)机制,系统各个组件之间的耦合度被显著降低。Spring的AOP(面向切面编程)能力被用于处理横切关注点,例如声明式事务管理、系统日志记录和安全性检查,确保了业务代码的纯粹性和可维护性。
数据持久层选用MyBatis框架,它通过XML配置或注解的方式,将Java对象(POJOs)与数据库表中的记录进行灵活的映射(ORM)。MyBatis强大的动态SQL功能使得构建复杂的商品查询条件(如多条件筛选、模糊搜索)变得高效而简洁。
项目采用Maven进行依赖管理和构建,确保了第三方库版本的一致性和项目结构的标准化。前端页面使用HTML、CSS和JavaScript开发,实现了响应式布局,以适配不同尺寸的终端设备。数据库系统选用MySQL,以其稳定性和高性能满足电商系统的数据存储需求。
核心数据库设计剖析
数据库设计是电商系统的基石,直接影响到系统的性能、数据一致性和扩展性。“风尚衣橱”的数据库设计遵循第三范式,减少了数据冗余,并通过合理的主外键关系确保了数据的完整性。以下重点分析两个核心表的设计。
1. 商品表(product)设计
商品表是系统的核心数据载体,其设计需要兼顾商品描述的丰富性和查询性能。
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
`name` varchar(255) NOT NULL COMMENT '商品名称',
`description` text COMMENT '商品描述',
`price` decimal(10,2) NOT NULL COMMENT '商品价格',
`stock` int(11) NOT NULL COMMENT '商品库存',
`category_id` int(11) DEFAULT NULL COMMENT '分类ID',
`image_url` varchar(500) DEFAULT NULL COMMENT '商品主图URL',
`status` int(11) DEFAULT '1' COMMENT '商品状态(1:上架,0:下架)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_category_id` (`category_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`),
CONSTRAINT `fk_product_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
设计亮点分析:
- 字段类型优化:
price字段使用DECIMAL(10,2)类型,精确存储货币金额,避免浮点数精度问题。stock库存字段使用整型,便于原子性操作。 - 索引策略:除了主键索引,还为
category_id(分类查询)、status(上下架状态过滤)和create_time(按新品排序)建立了索引。这种复合索引策略极大地提升了商品列表页的查询速度,特别是在多条件筛选和排序场景下。 - 外键约束:通过
FOREIGN KEY约束与分类表(category)关联,确保了数据的一致性。当某个分类被删除时,相关商品的category_id会被设置为NULL(ON DELETE SET NULL),而不是级联删除商品,这是一种更安全的业务逻辑处理方式。 - 审计字段:
create_time和update_time字段自动记录数据的生命周期,便于后续的数据分析和监控。
2. 订单表(order)与订单项表(order_item)设计
电商系统中的订单模型通常采用主表-子表的结构来设计,以应对一个订单包含多个商品的情况。
CREATE TABLE `order` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`order_no` varchar(64) NOT NULL COMMENT '订单号',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
`status` int(11) NOT NULL COMMENT '订单状态',
`payment_time` datetime DEFAULT NULL COMMENT '支付时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
CREATE TABLE `order_item` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单项ID',
`order_id` int(11) NOT NULL COMMENT '订单ID',
`product_id` int(11) NOT NULL COMMENT '商品ID',
`product_name` varchar(255) NOT NULL COMMENT '商品名称(快照)',
`product_price` decimal(10,2) NOT NULL COMMENT '商品单价(快照)',
`quantity` int(11) NOT NULL COMMENT '购买数量',
`total_price` decimal(10,2) NOT NULL COMMENT '订单项总价',
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_product_id` (`product_id`),
CONSTRAINT `fk_order_item_order` FOREIGN KEY (`order_id`) REFERENCES `order` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_order_item_product` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表';
设计亮点分析:
- 订单号唯一性:订单主表使用
order_no字段并建立唯一索引(uk_order_no),该订单号通常由系统按规则生成(如时间戳+随机数),用于对外展示和交互,避免了自增主键ID暴露带来的安全隐患。 - 数据快照:在订单项表(
order_item)中,存储了下单时刻的商品名称(product_name)和单价(product_price)。这是电商系统一个至关重要的设计。即使后续商家修改了商品信息或价格,已生成订单的历史数据也不会改变,保证了交易记录的客观性和不可篡改性。 - 数据一致性:订单总金额(
order.total_amount)理论上应等于其下所有订单项总价(order_item.total_price)之和。这一致性通常在业务逻辑层通过事务来保证。在创建订单时,系统会先计算各个订单项金额,再汇总写入订单主表,整个操作在一个数据库事务内完成。
核心功能模块深度解析
1. 商品浏览与多维度筛选
商品展示是电商平台的流量入口。“风尚衣橱”实现了高效的商品分类浏览和灵活的多条件搜索功能。
前端界面展示:
用户可以通过左侧的分类导航栏快速定位到感兴趣的服装品类,页面采用瀑布流或网格布局展示商品图片、名称和价格,视觉体验良好。
后端Controller层核心代码:
ProductController负责处理商品列表的查询请求。
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public String listProducts(
@RequestParam(value = "categoryId", required = false) Integer categoryId,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "minPrice", required = false) BigDecimal minPrice,
@RequestParam(value = "maxPrice", required = false) BigDecimal maxPrice,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "12") Integer pageSize,
Model model) {
// 构建查询条件对象
ProductQuery query = new ProductQuery();
query.setCategoryId(categoryId);
query.setKeyword(keyword);
query.setMinPrice(minPrice);
query.setMaxPrice(maxPrice);
query.setStatus(1); // 只查询上架商品
// 调用服务层分页查询
PageInfo<Product> pageInfo = productService.getProductList(query, pageNum, pageSize);
// 将数据添加到模型,供视图层渲染
model.addAttribute("pageInfo", pageInfo);
model.addAttribute("query", query);
return "product/list";
}
}
Service层与MyBatis动态SQL: 业务逻辑层封装了复杂的查询逻辑,数据层则利用MyBatis的动态SQL能力组装查询条件。
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public PageInfo<Product> getProductList(ProductQuery query, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize); // 使用PageHelper实现物理分页
List<Product> productList = productMapper.selectByQuery(query);
return new PageInfo<>(productList);
}
}
<!-- ProductMapper.xml 中的动态SQL语句 -->
<select id="selectByQuery" parameterType="com.maancode.mall.query.ProductQuery" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM product
WHERE status = 1
<if test="categoryId != null">
AND category_id = #{categoryId}
</if>
<if test="keyword != null and keyword != ''">
AND (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>
ORDER BY create_time DESC
</select>
此实现通过<if>标签动态拼接SQL条件,避免了编写多个相似查询方法的冗余,提升了代码的复用性和可维护性。PageHelper插件的使用则简化了分页逻辑的实现。
2. 购物车管理与订单生成
购物车是连接商品浏览和订单生成的关键模块,其核心在于管理用户的临时购买意向。
前端界面展示:
用户在商品详情页选择尺码、颜色和数量后,可一键将商品加入购物车。购物车页面会清晰展示已选商品列表、单价、数量和小计。
购物车Service层核心代码: 购物车业务涉及复杂的逻辑,如验证库存、合并同一商品等。
@Service
public class CartServiceImpl implements CartService {
@Autowired
private CartItemMapper cartItemMapper;
@Autowired
private ProductMapper productMapper;
@Override
@Transactional // 声明式事务管理
public void addToCart(Integer userId, Integer productId, Integer quantity) {
// 1. 校验商品是否存在且已上架
Product product = productMapper.selectByPrimaryKey(productId);
if (product == null || product.getStatus() != 1) {
throw new RuntimeException("商品不存在或已下架");
}
// 2. 校验库存是否充足
if (product.getStock() < quantity) {
throw new RuntimeException("商品库存不足");
}
// 3. 查询用户购物车中是否已有该商品
CartItem existItem = cartItemMapper.selectByUserIdAndProductId(userId, productId);
if (existItem != null) {
// 如果存在,则更新数量
existItem.setQuantity(existItem.getQuantity() + quantity);
cartItemMapper.updateByPrimaryKeySelective(existItem);
} else {
// 如果不存在,则新增一条记录
CartItem newItem = new CartItem();
newItem.setUserId(userId);
newItem.setProductId(productId);
newItem.setQuantity(quantity);
cartItemMapper.insertSelective(newItem);
}
}
@Override
@Transactional
public Order createOrderFromCart(Integer userId, List<Integer> cartItemIds, Address address) {
// 1. 查询选中的购物车项
List<CartItem> selectedItems = cartItemMapper.selectByIdsAndUserId(cartItemIds, userId);
if (selectedItems.isEmpty()) {
throw new RuntimeException("购物车为空");
}
// 2. 创建订单主表记录
Order order = new Order();
order.setOrderNo(generateOrderNo()); // 生成唯一订单号
order.setUserId(userId);
order.setStatus(OrderStatusEnum.WAIT_PAY.getCode());
// ... 设置收货地址等信息
orderMapper.insertSelective(order);
BigDecimal totalAmount = BigDecimal.ZERO;
List<OrderItem> orderItems = new ArrayList<>();
// 3. 遍历购物车项,生成订单项
for (CartItem cartItem : selectedItems) {
Product product = productMapper.selectByPrimaryKey(cartItem.getProductId());
// 再次校验库存
if (product.getStock() < cartItem.getQuantity()) {
throw new RuntimeException("商品【" + product.getName() + "】库存不足");
}
// 扣减库存(乐观锁)
int updateCount = productMapper.reduceStock(product.getId(), cartItem.getQuantity());
if (updateCount == 0) {
throw new RuntimeException("商品【" + product.getName() + "】库存更新失败,请重试");
}
// 构建订单项(数据快照)
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProductId(product.getId());
orderItem.setProductName(product.getName()); // 快照
orderItem.setProductPrice(product.getPrice()); // 快照
orderItem.setQuantity(cartItem.getQuantity());
orderItem.setTotalPrice(product.getPrice().multiply(new BigDecimal(cartItem.getQuantity())));
orderItemMapper.insertSelective(orderItem);
orderItems.add(orderItem);
totalAmount = totalAmount.add(orderItem.getTotalPrice());
// 从购物车中删除已下单项
cartItemMapper.deleteByPrimaryKey(cartItem.getId());
}
// 4. 更新订单总金额
order.setTotalAmount(totalAmount);
orderMapper.updateByPrimaryKeySelective(order);
return order;
}
private String generateOrderNo() {
// 示例:时间戳 + 随机数
return System.currentTimeMillis() + "" + (int)((Math.random() * 9 + 1) * 1000);
}
}
订单确认界面:
在确认订单页面,用户最终核对商品信息、收货地址和总金额,完成支付。
3. 后台商品与订单管理
后台管理系统是平台运营的指挥中心,管理员需要高效的工具来管理商品和订单。
商品管理界面:
管理员可以在此页面进行商品的增、删、改、查、上架/下架等操作。界面通常提供批量操作功能,提升管理效率。
订单管理界面:
管理员可以查看所有订单,并根据订单状态(待付款、待发货、已发货、已完成等)进行筛选和操作,如发货、备注、取消订单等。
后台商品管理的Controller代码示例:
@Controller
@RequestMapping("/admin/product")
public class AdminProductController {
@Autowired
private ProductService productService;
@PostMapping("/saveOrUpdate")
@ResponseBody
public ResponseEntity<?> saveOrUpdateProduct(@RequestBody @Valid Product product, BindingResult result) {
if (result.hasErrors()) {
// 处理参数校验错误
return ResponseEntity.badRequest().body("参数校验失败");
}
try {
if (product.getId() == null) {
productService.addProduct(product);
} else {
productService.updateProduct(product);
}
return ResponseEntity.ok().body("操作成功");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("操作失败: " + e.getMessage());
}
}
@PostMapping("/updateStatus")
@ResponseBody
public ResponseEntity<?> updateProductStatus(@RequestParam Integer productId, @RequestParam Integer status) {
try {
productService.updateProductStatus(productId, status);
return ResponseEntity.ok().body("状态更新成功");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("状态更新失败");
}
}
}
实体模型与业务逻辑
系统通过一系列精心设计的Java实体类(如User, Product, Category, Order, OrderItem, CartItem)来映射数据库表结构,并承载业务逻辑。这些实体类不仅是数据容器,某些核心业务逻辑也以方法的形式封装在实体类中,遵循了面向对象的设计原则。
例如,在Order实体中,可以封装状态流转的逻辑:
public class Order {
//