1. 项目概述作为一名Java全栈开发者最近完成了一个基于SSM框架的宠物商城项目。这个项目源于当下宠物经济的蓬勃发展越来越多的人开始饲养宠物但市面上很多宠物电商平台功能单一用户体验不佳。我决定开发一个集商品浏览、购买、订单管理和资讯获取于一体的综合性平台。项目采用经典的SSMSpringSpring MVCMyBatis框架组合搭配MySQL数据库实现了前后端分离的架构设计。前端使用HTML5CSS3JavaScript技术栈后端基于Java 8开发通过Maven进行依赖管理。整个开发周期约3个月最终实现了一个功能完善、性能稳定的宠物电商系统。2. 技术选型与架构设计2.1 技术栈选择考量选择SSM框架组合主要基于以下几个方面的考虑Spring框架提供了全面的IoC和AOP支持简化了企业级应用开发。通过依赖注入降低了组件间的耦合度事务管理模块特别适合电商系统的高并发场景。Spring MVC轻量级的Web框架清晰的MVC分层结构便于团队协作。它的拦截器机制非常适合实现权限控制、日志记录等横切关注点。MyBatis相比Hibernate更加灵活可以编写优化过的SQL语句。对于需要复杂查询的电商系统MyBatis的SQL映射方式更符合我们的需求。数据库选择MySQL 5.7版本主要考虑因素包括成熟稳定社区支持完善良好的事务支持适合电商场景与Java生态集成度高对于中小型电商系统性能足够2.2 系统架构设计系统采用典型的三层架构表示层(Web) → 业务逻辑层(Service) → 数据访问层(DAO)这种分层设计使得各层职责明确便于维护和扩展。具体实现上表示层使用JSPJSTL渲染视图配合jQuery处理前端交互。采用RESTful风格的接口设计前后端通过JSON格式交换数据。业务逻辑层Spring管理的Service组件处理核心业务逻辑如订单创建、库存扣减等。通过声明式事务确保数据一致性。数据访问层MyBatis实现数据库操作使用Mapper接口方式。配置了二级缓存提升查询性能。3. 数据库设计与实现3.1 数据库表结构设计数据库设计遵循第三范式主要包含以下几类表用户相关表user存储用户基本信息user_address用户收货地址user_favorite用户收藏商品商品相关表goods商品基本信息goods_category商品分类goods_spec商品规格订单相关表order订单主表order_item订单明细order_log订单状态日志系统管理表admin管理员账号system_config系统配置operation_log操作日志3.2 核心表字段设计示例以商品表(goods)为例展示详细设计CREATE TABLE goods ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 商品ID, name varchar(100) NOT NULL COMMENT 商品名称, category_id int(11) NOT NULL COMMENT 分类ID, price decimal(10,2) NOT NULL COMMENT 销售价, original_price decimal(10,2) DEFAULT NULL COMMENT 原价, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, sales int(11) NOT NULL DEFAULT 0 COMMENT 销量, main_image varchar(255) DEFAULT NULL COMMENT 主图, sub_images text COMMENT 子图(多个图片URL,逗号分隔), detail text COMMENT 商品详情, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态(1-上架,0-下架), create_time datetime NOT NULL COMMENT 创建时间, update_time datetime NOT NULL COMMENT 更新时间, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;3.3 数据库优化措施索引优化为所有外键字段和常用查询条件创建索引如商品分类ID、状态等。分表策略对于可能产生大量数据的表(如订单表)设计时考虑了按时间分表的方案。字段类型选择金额使用DECIMAL类型避免精度问题大文本使用TEXT类型。缓存策略热点数据(如商品信息)使用Redis缓存减轻数据库压力。4. 核心功能模块实现4.1 用户模块4.1.1 用户注册与登录采用经典的邮箱/手机号密码注册方式后端处理流程参数校验(非空、格式等)检查用户名/邮箱/手机号是否已存在密码加密(使用BCrypt)保存用户信息发送激活邮件(异步处理)关键代码片段Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; Autowired private PasswordEncoder passwordEncoder; Override Transactional public User register(UserRegisterDTO dto) { // 检查用户名是否已存在 if(userMapper.existsByUsername(dto.getUsername())) { throw new BusinessException(用户名已存在); } // 密码加密 String encodedPassword passwordEncoder.encode(dto.getPassword()); // 构建用户实体 User user new User(); user.setUsername(dto.getUsername()); user.setPassword(encodedPassword); user.setEmail(dto.getEmail()); user.setPhone(dto.getPhone()); user.setStatus(0); // 未激活 // 保存用户 userMapper.insert(user); // 发送激活邮件(异步) sendActivationEmail(user); return user; } }4.1.2 权限控制使用Spring Security实现基于角色的访问控制(RBAC)定义用户角色ROLE_USER、ROLE_ADMIN配置URL权限规则实现自定义的UserDetailsService使用注解进行方法级权限控制安全配置示例Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/admin/**).hasRole(ADMIN) .antMatchers(/user/**).hasRole(USER) .antMatchers(/public/**).permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage(/login) .permitAll() .and() .logout() .permitAll(); } }4.2 商品模块4.2.1 商品展示商品列表页支持分页查询按分类筛选按价格/销量排序关键词搜索后端实现采用MyBatis的动态SQLselect idselectByCondition resultTypeGoods SELECT * FROM goods where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND (name LIKE CONCAT(%,#{keyword},%) OR description LIKE CONCAT(%,#{keyword},%)) /if if testminPrice ! null AND price #{minPrice} /if if testmaxPrice ! null AND price #{maxPrice} /if AND status 1 /where choose when testorderBy price_asc ORDER BY price ASC /when when testorderBy price_desc ORDER BY price DESC /when when testorderBy sales ORDER BY sales DESC /when otherwise ORDER BY create_time DESC /otherwise /choose LIMIT #{offset}, #{pageSize} /select4.2.2 商品详情商品详情页包含基础信息展示多图轮播规格选择(如有)库存检查加入购物车按钮库存检查是关键功能需要考虑并发场景Service public class GoodsServiceImpl implements GoodsService { Autowired private GoodsMapper goodsMapper; Override Transactional public boolean reduceStock(Long goodsId, int quantity) { // 使用乐观锁方式扣减库存 int affected goodsMapper.reduceStockWithOptimisticLock( goodsId, quantity); return affected 0; } }对应的Mapper方法update idreduceStockWithOptimisticLock UPDATE goods SET stock stock - #{quantity}, version version 1 WHERE id #{id} AND stock #{quantity} AND version #{version} /update4.3 购物车模块4.3.1 购物车实现方案考虑到用户体验和性能采用混合存储方案已登录用户购物车数据存数据库未登录用户购物车数据存浏览器本地存储(如localStorage)登录时合并本地购物车到服务端// 前端合并逻辑示例 function mergeCart() { const localCart JSON.parse(localStorage.getItem(cart) || []); if(localCart.length 0) { axios.post(/api/cart/merge, localCart) .then(() { localStorage.removeItem(cart); }); } }4.3.2 购物车关键操作添加商品到购物车Service public class CartServiceImpl implements CartService { Autowired private CartMapper cartMapper; Autowired private GoodsMapper goodsMapper; Override Transactional public void addToCart(Long userId, CartItemDTO item) { // 检查商品是否存在且状态正常 Goods goods goodsMapper.selectById(item.getGoodsId()); if(goods null || goods.getStatus() ! 1) { throw new BusinessException(商品不存在或已下架); } // 检查库存 if(goods.getStock() item.getQuantity()) { throw new BusinessException(库存不足); } // 检查购物车是否已有该商品 CartItem existing cartMapper.selectByUserAndGoods( userId, item.getGoodsId()); if(existing ! null) { // 更新数量 existing.setQuantity(existing.getQuantity() item.getQuantity()); cartMapper.update(existing); } else { // 新增记录 CartItem newItem new CartItem(); newItem.setUserId(userId); newItem.setGoodsId(item.getGoodsId()); newItem.setQuantity(item.getQuantity()); newItem.setSelected(true); cartMapper.insert(newItem); } } }更新购物车商品数量Override Transactional public void updateQuantity(Long userId, Long itemId, int quantity) { CartItem item cartMapper.selectById(itemId); if(item null || !item.getUserId().equals(userId)) { throw new BusinessException(购物车项不存在); } // 检查库存 Goods goods goodsMapper.selectById(item.getGoodsId()); if(goods.getStock() quantity) { throw new BusinessException(库存不足); } item.setQuantity(quantity); cartMapper.update(item); }4.4 订单模块4.4.1 订单创建流程订单创建是电商系统的核心功能涉及多个步骤验证购物车商品计算订单总金额扣减库存(预占)生成订单数据清空购物车(已结算商品)发送订单创建通知关键实现Service public class OrderServiceImpl implements OrderService { Autowired private CartService cartService; Autowired private GoodsService goodsService; Autowired private OrderMapper orderMapper; Autowired private OrderItemMapper orderItemMapper; Override Transactional public Order createOrder(Long userId, OrderCreateDTO dto) { // 1. 获取购物车选中商品 ListCartItem cartItems cartService.getCheckedItems(userId); if(cartItems.isEmpty()) { throw new BusinessException(没有选中商品); } // 2. 验证商品并计算总价 BigDecimal totalAmount BigDecimal.ZERO; ListOrderItem orderItems new ArrayList(); for(CartItem cartItem : cartItems) { Goods goods goodsService.getById(cartItem.getGoodsId()); // 验证商品状态 if(goods null || goods.getStatus() ! 1) { throw new BusinessException(商品goods.getName()已下架); } // 验证库存 if(goods.getStock() cartItem.getQuantity()) { throw new BusinessException(商品goods.getName()库存不足); } // 构建订单项 OrderItem item new OrderItem(); item.setGoodsId(goods.getId()); item.setGoodsName(goods.getName()); item.setGoodsImage(goods.getMainImage()); item.setCurrentPrice(goods.getPrice()); item.setQuantity(cartItem.getQuantity()); // 计算小计 BigDecimal itemTotal goods.getPrice() .multiply(new BigDecimal(cartItem.getQuantity())); item.setTotalPrice(itemTotal); orderItems.add(item); totalAmount totalAmount.add(itemTotal); } // 3. 扣减库存 for(OrderItem item : orderItems) { boolean success goodsService.reduceStock( item.getGoodsId(), item.getQuantity()); if(!success) { throw new BusinessException(商品库存不足请重新下单); } } // 4. 生成订单 Order order new Order(); order.setUserId(userId); order.setOrderNo(generateOrderNo()); order.setTotalAmount(totalAmount); order.setStatus(OrderStatus.WAIT_PAYMENT.getCode()); order.setAddressId(dto.getAddressId()); orderMapper.insert(order); // 5. 保存订单项 for(OrderItem item : orderItems) { item.setOrderId(order.getId()); orderItemMapper.insert(item); } // 6. 清空购物车(已结算商品) cartService.clearCheckedItems(userId); return order; } private String generateOrderNo() { // 时间戳随机数生成订单号 return System.currentTimeMillis() String.valueOf((int)(Math.random() * 900 100)); } }4.4.2 订单状态管理订单状态流转设计待支付 → 已支付/已取消 → 已发货 → 已完成 ↘ 超时未支付自动取消使用状态模式实现订单状态管理public interface OrderState { void pay(Order order); void cancel(Order order); void ship(Order order); void complete(Order order); } Service public class OrderStateMachine { private MapInteger, OrderState states; public OrderStateMachine() { states new HashMap(); states.put(OrderStatus.WAIT_PAYMENT.getCode(), new WaitPaymentState()); states.put(OrderStatus.PAID.getCode(), new PaidState()); // 其他状态... } public void changeState(Order order, int event) { OrderState state states.get(order.getStatus()); if(state null) { throw new BusinessException(无效的订单状态); } switch(event) { case OrderEvent.PAY: state.pay(order); break; case OrderEvent.CANCEL: state.cancel(order); break; // 其他事件... } } } Component public class WaitPaymentState implements OrderState { Override public void pay(Order order) { // 更新订单状态为已支付 order.setStatus(OrderStatus.PAID.getCode()); order.setPaymentTime(new Date()); // 记录支付日志 // 发送支付成功通知 } Override public void cancel(Order order) { // 检查是否超时取消 if(order.getCreateTime().before( DateUtils.addMinutes(new Date(), -30))) { // 超时取消恢复库存 restoreStock(order); } // 更新订单状态为已取消 order.setStatus(OrderStatus.CANCELLED.getCode()); order.setCancelTime(new Date()); } }4.5 支付模块4.5.1 支付流程设计用户提交订单系统生成支付订单跳转到支付页面(对接第三方支付)支付成功回调处理更新订单状态支付回调处理是关键需要考虑幂等性处理(防止重复通知)签名验证(确保通知来源可信)异步处理(提高响应速度)4.5.2 支付回调实现RestController RequestMapping(/api/payment) public class PaymentController { Autowired private OrderService orderService; PostMapping(/callback) public String paymentCallback(HttpServletRequest request) { // 1. 验证签名 if(!verifySign(request)) { return fail; } // 2. 解析参数 String orderNo request.getParameter(order_no); String paymentNo request.getParameter(payment_no); BigDecimal amount new BigDecimal(request.getParameter(amount)); // 3. 处理支付结果(异步) asyncProcessPayment(orderNo, paymentNo, amount); return success; } private void asyncProcessPayment(String orderNo, String paymentNo, BigDecimal amount) { CompletableFuture.runAsync(() - { try { Order order orderService.getByOrderNo(orderNo); if(order null) { log.error(订单不存在: {}, orderNo); return; } // 检查金额是否匹配 if(order.getTotalAmount().compareTo(amount) ! 0) { log.error(金额不匹配, order:{}, payment:{}, order.getTotalAmount(), amount); return; } // 更新订单状态 orderService.payOrder(order.getId(), paymentNo); // 记录支付日志 // 发送支付成功通知 } catch (Exception e) { log.error(处理支付回调异常, e); } }); } }5. 系统部署与优化5.1 部署架构系统采用分层部署架构负载均衡(Nginx) → 应用服务器(Tomcat) → 数据库(MySQL) ↘ 缓存(Redis)5.2 性能优化措施前端优化静态资源CDN加速图片懒加载合并压缩CSS/JS文件后端优化启用MyBatis二级缓存热点数据Redis缓存数据库连接池配置优化异步处理耗时操作(如发送邮件)数据库优化合理的索引设计查询语句优化读写分离(后续扩展)5.3 安全防护Web安全XSS防护HTML转义输出CSRF防护使用Spring Security的CSRF保护SQL注入防护使用预编译语句数据安全敏感信息加密存储(如用户密码)支付数据传输加密(HTTPS)日志脱敏处理接口安全接口限流(防止暴力请求)敏感操作二次验证完善的权限控制6. 项目总结与经验分享6.1 技术难点与解决方案并发下单问题现象高并发时可能出现超卖解决方案采用乐观锁Redis分布式锁库存预扣机制订单状态一致性问题现象支付回调与订单状态可能不一致解决方案引入状态机定时任务对账性能瓶颈问题现象商品列表页响应慢解决方案多级缓存(Redis本地缓存)数据库读写分离6.2 项目经验总结设计阶段提前考虑扩展性如表设计预留字段定义清晰的接口规范便于前后端协作制定合理的枚举值避免魔法数字开发阶段重视日志记录便于问题排查编写单元测试确保核心逻辑正确性代码审查保持代码风格一致测试阶段模拟真实场景的压力测试边界条件测试(如库存为0时下单)支付流程的完整测试(包括异常情况)6.3 未来优化方向架构升级服务拆分向微服务架构演进引入消息队列(如RabbitMQ)解耦系统实现真正的分布式事务功能扩展增加秒杀功能实现智能推荐系统开发移动端APP运维增强完善监控系统(PrometheusGrafana)实现CI/CD自动化部署日志集中收集分析(ELK)通过这个项目的开发我深刻体会到电商系统的复杂性和挑战性。一个稳定的电商系统不仅需要良好的架构设计还需要考虑各种异常情况和边界条件。在未来的项目中我会更加重视系统的可观测性和可维护性为业务的快速发展打下坚实基础。
阅读完成 · 觉得有帮助?