基于JSP+Servlet的生鲜电商平台 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-125 浏览

文章摘要

本项目是一款基于JSP+Servlet技术栈构建的生鲜电商平台,专注于为消费者提供便捷、安全的在线生鲜购物体验。其核心业务价值在于解决了传统生鲜采购中信息不透明、选购不便、交易流程繁琐等痛点,通过线上化方式将优质生鲜商品直接呈现给用户,简化从浏览、下单到支付的完整购物路径,有效提升采购效率并保障交易...

在传统生鲜采购模式面临信息不透明、选购流程繁琐等挑战的背景下,一个基于成熟技术栈的在线交易平台应运而生。该系统采用经典的JSP+Servlet架构,结合MySQL数据库,构建了一个功能完整的生鲜电商解决方案。平台严格遵循MVC设计模式,实现了前后端逻辑的有效分离,为消费者提供了从商品浏览、购物车管理到订单处理的完整购物体验。

系统架构采用分层设计,表现层使用JSP页面结合JSTL标签库进行动态内容渲染,控制层通过Servlet组件处理用户请求和业务逻辑调度,数据持久层基于JDBC技术实现与MySQL数据库的交互。这种架构确保了代码的可维护性和系统的可扩展性。

数据库设计亮点

数据库设计包含6个核心表,每个表都经过精心设计以满足业务需求。其中用户表、商品表和订单表的设计尤为关键。

用户表(user)采用合理的字段设计来保证数据完整性:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(50) NOT NULL,
  `email` varchar(100) DEFAULT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `address` varchar(200) DEFAULT NULL,
  `role` int(11) DEFAULT '0',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

该表通过自增主键确保唯一性,username字段设置唯一约束防止重复注册,role字段区分用户角色(普通用户和管理员),create_time字段自动记录注册时间。

商品表(fresh)的设计充分考虑了生鲜商品的特性:

CREATE TABLE `fresh` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `stock` int(11) NOT NULL,
  `description` text,
  `image` varchar(200) DEFAULT NULL,
  `category_id` int(11) NOT NULL,
  `status` int(11) DEFAULT '1',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `category_id_index` (`category_id`),
  CONSTRAINT `fresh_ibfk_1` FOREIGN KEY (`category_id`) 
  REFERENCES `category` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

价格字段使用decimal类型保证计算精度,库存字段实时跟踪商品数量,外键约束确保商品分类的一致性,状态字段控制商品上下架。

订单表(orders)采用多表关联设计支持复杂业务逻辑:

CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_number` varchar(50) NOT NULL,
  `user_id` int(11) NOT NULL,
  `total_amount` decimal(10,2) NOT NULL,
  `status` int(11) DEFAULT '0',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_number_unique` (`order_number`),
  KEY `user_id_index` (`user_id`),
  CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) 
  REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

订单编号保证唯一性,双时间戳字段跟踪订单状态变化,外键约束维护用户与订单的关系完整性。

核心功能实现

用户认证与权限管理

系统采用基于Session的认证机制,用户登录验证通过后建立会话。LoginServlet处理认证逻辑:

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        UserService userService = new UserServiceImpl();
        User user = userService.login(username, password);
        
        if (user != null) {
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
            
            if (user.getRole() == 1) {
                response.sendRedirect("admin/home.jsp");
            } else {
                response.sendRedirect("index.jsp");
            }
        } else {
            request.setAttribute("error", "用户名或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}

用户登录界面

权限控制通过过滤器实现,确保未登录用户无法访问受保护资源:

@WebFilter("/*")
public class AuthFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String path = req.getRequestURI();
        
        if (path.contains("/admin/")) {
            HttpSession session = req.getSession(false);
            if (session == null || session.getAttribute("user") == null) {
                res.sendRedirect(req.getContextPath() + "/login.jsp");
                return;
            }
            User user = (User) session.getAttribute("user");
            if (user.getRole() != 1) {
                res.sendRedirect(req.getContextPath() + "/error/403.jsp");
                return;
            }
        }
        chain.doFilter(request, response);
    }
}

商品浏览与分类展示

商品展示功能支持按分类浏览和关键词搜索。商品列表Servlet处理分页和筛选逻辑:

@WebServlet("/fresh/list")
public class FreshListServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        int page = 1;
        int pageSize = 12;
        int categoryId = 0;
        String keyword = request.getParameter("keyword");
        
        try {
            page = Integer.parseInt(request.getParameter("page"));
        } catch (NumberFormatException e) {
            page = 1;
        }
        
        try {
            categoryId = Integer.parseInt(request.getParameter("categoryId"));
        } catch (NumberFormatException e) {
            categoryId = 0;
        }
        
        FreshService freshService = new FreshServiceImpl();
        PageInfo<Fresh> pageInfo = freshService.getFreshList(page, pageSize, categoryId, keyword);
        
        CategoryService categoryService = new CategoryServiceImpl();
        List<Category> categories = categoryService.getAllCategories();
        
        request.setAttribute("pageInfo", pageInfo);
        request.setAttribute("categories", categories);
        request.setAttribute("categoryId", categoryId);
        request.setAttribute("keyword", keyword);
        
        request.getRequestDispatcher("/fresh-list.jsp").forward(request, response);
    }
}

商品分类浏览

前端JSP页面使用JSTL标签库动态渲染商品信息:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="row">
  <c:forEach items="${pageInfo.list}" var="fresh">
    <div class="col-md-3 mb-4">
      <div class="card fresh-card">
        <img src="${fresh.image}" class="card-img-top" alt="${fresh.name}">
        <div class="card-body">
          <h5 class="card-title">${fresh.name}</h5>
          <p class="card-text">¥${fresh.price}</p>
          <p class="card-text text-muted small">${fresh.description}</p>
          <a href="fresh/detail?id=${fresh.id}" class="btn btn-primary">查看详情</a>
          <button class="btn btn-success add-to-cart" data-id="${fresh.id}">加入购物车</button>
        </div>
      </div>
    </div>
  </c:forEach>
</div>

商品详情页面

购物车管理

购物车功能采用Session存储临时数据,支持商品添加、数量修改和删除操作:

@WebServlet("/cart/add")
public class AddToCartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        int freshId = Integer.parseInt(request.getParameter("freshId"));
        int quantity = Integer.parseInt(request.getParameter("quantity"));
        
        FreshService freshService = new FreshServiceImpl();
        Fresh fresh = freshService.getFreshById(freshId);
        
        if (fresh != null && fresh.getStock() >= quantity) {
            HttpSession session = request.getSession();
            Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
            
            if (cart == null) {
                cart = new HashMap<>();
            }
            
            if (cart.containsKey(freshId)) {
                CartItem item = cart.get(freshId);
                item.setQuantity(item.getQuantity() + quantity);
            } else {
                CartItem item = new CartItem(fresh, quantity);
                cart.put(freshId, item);
            }
            
            session.setAttribute("cart", cart);
            response.getWriter().write("success");
        } else {
            response.getWriter().write("error");
        }
    }
}

购物车实体类封装了业务逻辑:

public class CartItem {
    private Fresh fresh;
    private int quantity;
    
    public CartItem(Fresh fresh, int quantity) {
        this.fresh = fresh;
        this.quantity = quantity;
    }
    
    public BigDecimal getSubtotal() {
        return fresh.getPrice().multiply(new BigDecimal(quantity));
    }
    
    // Getter和Setter方法
    public Fresh getFresh() { return fresh; }
    public void setFresh(Fresh fresh) { this.fresh = fresh; }
    
    public int getQuantity() { return quantity; }
    public void setQuantity(int quantity) { this.quantity = quantity; }
}

购物车界面

订单处理流程

订单生成涉及复杂的业务逻辑,包括库存校验、价格计算和事务处理:

@WebServlet("/order/submit")
public class SubmitOrderServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
        
        if (user == null || cart == null || cart.isEmpty()) {
            response.sendRedirect("login.jsp");
            return;
        }
        
        OrderService orderService = new OrderServiceImpl();
        try {
            String orderNumber = orderService.createOrder(user.getId(), cart);
            session.removeAttribute("cart");
            response.sendRedirect("order/success.jsp?orderNumber=" + orderNumber);
        } catch (Exception e) {
            request.setAttribute("error", "订单提交失败: " + e.getMessage());
            request.getRequestDispatcher("cart.jsp").forward(request, response);
        }
    }
}

订单服务实现类处理核心业务逻辑:

public class OrderServiceImpl implements OrderService {
    public String createOrder(int userId, Map<Integer, CartItem> cart) throws Exception {
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            conn.setAutoCommit(false);
            
            // 生成订单号
            String orderNumber = generateOrderNumber();
            
            // 计算总金额并验证库存
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (CartItem item : cart.values()) {
                if (item.getFresh().getStock() < item.getQuantity()) {
                    throw new Exception("商品库存不足: " + item.getFresh().getName());
                }
                totalAmount = totalAmount.add(item.getSubtotal());
            }
            
            // 插入订单主记录
            String orderSql = "INSERT INTO orders (order_number, user_id, total_amount) VALUES (?, ?, ?)";
            PreparedStatement orderStmt = conn.prepareStatement(orderSql, PreparedStatement.RETURN_GENERATED_KEYS);
            orderStmt.setString(1, orderNumber);
            orderStmt.setInt(2, userId);
            orderStmt.setBigDecimal(3, totalAmount);
            orderStmt.executeUpdate();
            
            ResultSet rs = orderStmt.getGeneratedKeys();
            int orderId = 0;
            if (rs.next()) {
                orderId = rs.getInt(1);
            }
            
            // 插入订单明细并更新库存
            String detailSql = "INSERT INTO order_item (order_id, fresh_id, quantity, price) VALUES (?, ?, ?, ?)";
            String updateStockSql = "UPDATE fresh SET stock = stock - ? WHERE id = ?";
            
            PreparedStatement detailStmt = conn.prepareStatement(detailSql);
            PreparedStatement updateStmt = conn.prepareStatement(updateStockSql);
            
            for (CartItem item : cart.values()) {
                // 插入订单明细
                detailStmt.setInt(1, orderId);
                detailStmt.setInt(2, item.getFresh().getId());
                detailStmt.setInt(3, item.getQuantity());
                detailStmt.setBigDecimal(4, item.getFresh().getPrice());
                detailStmt.addBatch();
                
                // 更新库存
                updateStmt.setInt(1, item.getQuantity());
                updateStmt.setInt(2, item.getFresh().getId());
                updateStmt.addBatch();
            }
            
            detailStmt.executeBatch();
            updateStmt.executeBatch();
            
            conn.commit();
            return orderNumber;
            
        } catch (Exception e) {
            if (conn != null) {
                conn.rollback();
            }
            throw e;
        } finally {
            if (conn != null) {
                conn.setAutoCommit(true);
                conn.close();
            }
        }
    }
    
    private String generateOrderNumber() {
        return "ORD" + System.currentTimeMillis() + (int)(Math.random() * 1000);
    }
}

订单提交界面

实体模型设计

系统采用面向对象的设计思想,核心实体模型之间建立了清晰的关联关系。用户实体(User)作为系统的基础,与订单实体(Order)形成一对多关系,体现了用户可创建多个订单的业务规则。商品实体(Fresh)通过分类实体(Category)进行组织,支持灵活的商品分类管理。购物车项实体(CartItem)作为临时数据载体,桥接了用户选购行为与订单生成过程。

实体间的关联通过外键约束在数据库层面得到保障,同时在业务逻辑层通过服务类进行协调。这种设计既保证了数据的一致性,又提供了良好的业务扩展性。

功能展望与优化方向

  1. 支付系统集成:当前系统尚未集成在线支付功能。未来可接入支付宝、微信支付等第三方支付平台,实现完整的交易闭环。技术上可通过实现支付接口、处理异步通知、管理支付状态等模块来完成。

  2. 库存预警机制:建立智能库存监控系统,当商品库存低于阈值时自动发送预警通知。可通过数据库触发器或定时任务扫描库存数据,结合邮件或短信通知管理员。

  3. 商品推荐算法:基于用户浏览和购买历史实现个性化推荐。可采用协同过滤算法,分析用户行为数据,在首页和商品详情页展示相关商品推荐。

  4. 物流跟踪集成:对接主流物流公司API,为用户提供实时的物流信息查询功能。需要设计物流数据表存储跟踪信息,并实现定时同步物流状态的机制。

  5. 性能优化与缓存策略:引入Redis缓存高频访问数据(如商品分类、热门商品),减少数据库压力。对商品列表查询实现分页缓存,对静态资源实施CDN加速,提升系统响应速度。

  6. 微服务架构改造:随着业务规模扩大,可将单体应用拆分为用户服务、商品服务、订单服务等微服务模块。使用Spring Cloud等技术栈实现服务治理、配置管理和链路追踪。

该系统作为生鲜电商领域的技术实践,展示了传统JSP+Servlet技术栈在现代Web应用开发中的可行性。其清晰的架构设计、严谨的数据库规划和完善的功能实现,为同类项目的开发提供了有价值的参考。通过持续的技术优化和功能扩展,平台有望在生鲜电商领域发挥更大的价值。

本文关键词
JSPServlet生鲜电商平台MySQLMVC设计模式

上下篇

上一篇
没有更多文章
下一篇
没有更多文章