基于JSP+Servlet的在线毛绒玩具商城系统 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-284 浏览

文章摘要

本项目是一个基于JSP和Servlet技术栈构建的在线毛绒玩具商城系统,旨在为消费者提供一个便捷的玩具选购平台,同时为商家提供高效的后台管理能力。其核心业务价值在于解决了传统实体玩具店在销售渠道、库存管理和客户触达方面的局限性,通过线上化实现了商品展示、交易处理和库存更新的数字化闭环,有效降低了运营...

在传统零售业数字化转型的浪潮中,毛绒玩具行业面临着销售渠道单一、库存管理效率低下以及客户触达范围有限等挑战。针对这些痛点,一个基于JSP+Servlet技术栈构建的在线商城系统应运而生。该系统被命名为“PlushPalace”,旨在为消费者打造一个便捷、友好的线上选购平台,同时为商家提供一套高效、可靠的后台管理工具,实现从商品上架、用户下单到库存更新的全流程数字化管理。

PlushPalace系统严格遵循模型-视图-控制器(MVC)设计模式,确保了代码的高内聚和低耦合。Servlet作为系统的控制器,负责处理所有来自客户端的HTTP请求,执行核心业务逻辑,如用户身份验证、购物车管理和订单处理。视图层由JSP页面构成,通过嵌入JSTL标签和EL表达式动态渲染数据,有效避免了在页面中直接编写Java代码,提升了代码的可读性和可维护性。模型层则由一系列封装了业务数据的JavaBean组成,并通过JDBC技术与MySQL数据库进行交互,完成数据的持久化操作。这种清晰的分层架构不仅便于团队协作开发,也为系统的后续功能扩展和维护奠定了坚实的基础。

系统架构与技术栈

PlushPalace的技术选型体现了经典Java Web开发的成熟与稳定。前端展示层采用HTML、CSS和JavaScript构建用户界面,确保跨浏览器的兼容性和响应式布局。JSP页面作为动态内容渲染的核心,利用JSTL(JSP Standard Tag Library)简化了逻辑控制,例如使用<c:forEach>标签遍历商品列表,使用EL表达式${product.name}直接输出对象属性,使得页面代码更加简洁。

<%-- 商品列表展示JSP片段 --%>
<div class="product-grid">
  <c:forEach items="${productList}" var="product">
    <div class="product-card">
      <img src="${product.imageUrl}" alt="${product.name}">
      <h3>${product.name}</h3>
      <p class="price">¥${product.price}</p>
      <a href="addToCart?productId=${product.id}" class="btn">加入购物车</a>
    </div>
  </c:forEach>
</div>

后端控制层由Servlet实现,每个Servlet对应一个具体的业务功能模块。通过@WebServlet注解或web.xml配置映射关系,Servlet能够精准拦截用户请求。例如,处理用户登录的LoginServlet会验证用户凭证,并创建会话(HttpSession)来管理用户状态。

@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 UserService();
        User user = userService.authenticate(username, password);
        
        if (user != null) {
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            response.sendRedirect("index.jsp");
        } else {
            request.setAttribute("errorMessage", "用户名或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}

数据访问层采用DAO(Data Access Object)模式,将数据库操作封装在独立的类中。通过使用PreparedStatement防止SQL注入,并利用连接池(如DBCP或HikariCP)管理数据库连接,提升系统性能和安全性。

public class ProductDAO {
    public List<Product> findAll() throws SQLException {
        List<Product> products = new ArrayList<>();
        String sql = "SELECT id, name, price, stock, image_url, description FROM products WHERE status = 1";
        
        try (Connection conn = DataSourceUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            
            while (rs.next()) {
                Product product = new Product();
                product.setId(rs.getInt("id"));
                product.setName(rs.getString("name"));
                product.setPrice(rs.getBigDecimal("price"));
                product.setStock(rs.getInt("stock"));
                product.setImageUrl(rs.getString("image_url"));
                product.setDescription(rs.getString("description"));
                products.add(product);
            }
        }
        return products;
    }
}

数据库设计深度剖析

PlushPalace的数据库由6张核心表构成,设计上充分考虑了业务的完整性和查询效率。以下重点分析用户表、商品表和订单表的设计亮点。

用户表(users) 的设计不仅包含了基本的身份信息,还通过user_type字段区分普通消费者和管理员角色,实现了系统权限的初步控制。registration_date字段记录了用户注册时间,为后续的用户行为分析和生命周期管理提供了数据支持。email字段设置为UNIQUE约束,确保了账户的唯一性,同时作为密码找回的重要凭证。

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL COMMENT '存储BCrypt加密后的密码',
  email VARCHAR(100) NOT NULL UNIQUE,
  phone VARCHAR(20),
  address TEXT,
  user_type ENUM('customer', 'admin') DEFAULT 'customer',
  registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  last_login TIMESTAMP NULL,
  status TINYINT DEFAULT 1 COMMENT '1-正常,0-禁用'
);

商品表(products) 的设计体现了电商系统的典型特征。价格字段使用DECIMAL类型,确保金融计算的精确性。stock字段实时反映库存数量,在用户下单时通过数据库事务保证并发安全。category_id外键关联商品分类表,支持灵活的商品分类管理。status字段实现了商品的软删除功能,下架的商品仍保留在数据库中但不对外展示。

CREATE TABLE products (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(200) NOT NULL,
  description TEXT,
  price DECIMAL(10,2) NOT NULL,
  stock INT NOT NULL DEFAULT 0,
  image_url VARCHAR(500),
  category_id INT,
  status TINYINT DEFAULT 1 COMMENT '1-上架,0-下架',
  create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  FOREIGN KEY (category_id) REFERENCES categories(id)
);

订单表(orders) 的设计是系统中最复杂的部分,采用了主订单与订单明细分离的经典模式。主订单表记录订单的整体信息,如订单号、总金额、收货地址等。订单号(order_number)使用唯一约束,通常由时间戳+随机数生成,避免重复。order_status字段使用ENUM类型明确限定订单的生命周期状态,如待支付、已支付、已发货、已完成等,便于状态跟踪和流程管理。

CREATE TABLE orders (
  id INT PRIMARY KEY AUTO_INCREMENT,
  order_number VARCHAR(64) NOT NULL UNIQUE,
  user_id INT NOT NULL,
  total_amount DECIMAL(10,2) NOT NULL,
  order_status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled') DEFAULT 'pending',
  shipping_address TEXT NOT NULL,
  payment_method VARCHAR(50),
  create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  pay_time TIMESTAMP NULL,
  ship_time TIMESTAMP NULL,
  complete_time TIMESTAMP NULL,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

订单明细表(order_items)与主订单表通过order_id关联,记录了每个订单中包含的具体商品信息。这种设计支持一个订单购买多个商品的需求,同时保留了购买时的商品快照(如价格、名称),即使后续商品信息变更,订单历史记录仍保持准确。

CREATE TABLE order_items (
  id INT PRIMARY KEY AUTO_INCREMENT,
  order_id INT NOT NULL,
  product_id INT NOT NULL,
  product_name VARCHAR(200) NOT NULL,
  product_price DECIMAL(10,2) NOT NULL,
  quantity INT NOT NULL DEFAULT 1,
  subtotal DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
  FOREIGN KEY (product_id) REFERENCES products(id)
);

核心功能实现解析

1. 用户认证与会话管理

用户登录功能由LoginServlet处理,它验证用户提交的用户名和密码。系统使用BCrypt算法对密码进行哈希加密存储,验证时比较哈希值而非明文密码,显著提升了安全性。登录成功后,用户对象被存入HttpSession中,并在后续请求中通过过滤器(Filter)验证会话有效性,实现访问控制。

用户登录界面

// 密码加密验证工具类
public class PasswordUtil {
    private static final int BCRYPT_STRENGTH = 12;
    
    public static String hashPassword(String plainPassword) {
        return BCrypt.hashpw(plainPassword, BCrypt.gensalt(BCRYPT_STRENGTH));
    }
    
    public static boolean checkPassword(String plainPassword, String hashedPassword) {
        return BCrypt.checkpw(plainPassword, hashedPassword);
    }
}

2. 商品浏览与搜索

商品展示页面采用分页技术处理大量商品数据,避免一次性加载所有数据导致的性能问题。前端通过GET请求传递页码参数,后端Servlet计算分页偏移量,查询数据库并返回指定页面的商品数据。

商品列表页面

@WebServlet("/products")
public class ProductListServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int page = 1;
        int pageSize = 12;
        String pageParam = request.getParameter("page");
        if (pageParam != null && !pageParam.isEmpty()) {
            page = Integer.parseInt(pageParam);
        }
        
        ProductService productService = new ProductService();
        PageResult<Product> pageResult = productService.getProductsByPage(page, pageSize);
        
        request.setAttribute("pageResult", pageResult);
        request.getRequestDispatcher("/product-list.jsp").forward(request, response);
    }
}

搜索功能通过SQL的LIKE语句实现模糊匹配,支持按商品名称或描述进行关键字搜索。为了提高搜索效率,可以在products表的namedescription字段上建立全文索引。

-- 为商品表添加全文索引
CREATE FULLTEXT INDEX idx_product_search ON products(name, description);

-- 使用全文索引进行搜索
SELECT * FROM products 
WHERE MATCH(name, description) AGAINST('毛绒熊' IN NATURAL LANGUAGE MODE)
AND status = 1;

3. 购物车管理与订单生成

购物车功能采用HttpSession临时存储用户选择的商品,无需频繁操作数据库。每个购物车项包含商品ID、数量和小计金额,当用户添加商品时,系统会检查库存是否充足。

购物车页面

@WebServlet("/addToCart")
public class AddToCartServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int productId = Integer.parseInt(request.getParameter("productId"));
        int quantity = Integer.parseInt(request.getParameter("quantity", "1"));
        
        HttpSession session = request.getSession();
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
            session.setAttribute("cart", cart);
        }
        
        ProductService productService = new ProductService();
        Product product = productService.getProductById(productId);
        
        if (product != null && product.getStock() >= quantity) {
            CartItem item = cart.get(productId);
            if (item != null) {
                item.setQuantity(item.getQuantity() + quantity);
            } else {
                item = new CartItem(product, quantity);
                cart.put(productId, item);
            }
        }
        
        response.sendRedirect("cart.jsp");
    }
}

订单生成是系统的核心交易环节,涉及多个数据库表的原子性操作。使用数据库事务确保订单创建、库存扣减和购物车清空等操作要么全部成功,要么全部回滚,保证数据一致性。

@WebServlet("/createOrder")
public class CreateOrderServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("currentUser");
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
        
        if (user == null) {
            response.sendRedirect("login.jsp");
            return;
        }
        
        OrderService orderService = new OrderService();
        try {
            String orderNumber = orderService.createOrder(user, cart, request.getParameter("shippingAddress"));
            session.removeAttribute("cart"); // 清空购物车
            response.sendRedirect("orderSuccess.jsp?orderNumber=" + orderNumber);
        } catch (InsufficientStockException e) {
            request.setAttribute("errorMessage", "库存不足,无法完成订单");
            request.getRequestDispatcher("cart.jsp").forward(request, response);
        } catch (Exception e) {
            request.setAttribute("errorMessage", "订单创建失败,请重试");
            request.getRequestDispatcher("cart.jsp").forward(request, response);
        }
    }
}

4. 后台管理功能

后台管理模块为商家提供了完整的商品和订单管理能力。管理员可以添加新商品、调整价格和库存、处理订单发货等。管理界面通过权限控制确保只有管理员角色可以访问。

后台商品管理

商品添加功能包含图片上传处理,使用Apache Commons FileUpload组件接收multipart/form-data类型的请求,将上传的图片保存到服务器指定目录,并在数据库中记录图片URL。

@WebServlet("/admin/addProduct")
@MultipartConfig
public class AddProductServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 验证管理员权限
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("currentUser");
        if (user == null || !user.getUserType().equals("admin")) {
            response.sendError(403, "权限不足");
            return;
        }
        
        String name = request.getParameter("name");
        String description = request.getParameter("description");
        BigDecimal price = new BigDecimal(request.getParameter("price"));
        int stock = Integer.parseInt(request.getParameter("stock"));
        int categoryId = Integer.parseInt(request.getParameter("categoryId"));
        
        Part filePart = request.getPart("image");
        String fileName = System.currentTimeMillis() + "_" + getSubmittedFileName(filePart);
        String uploadPath = getServletContext().getRealPath("/uploads");
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) uploadDir.mkdir();
        
        String filePath = uploadPath + File.separator + fileName;
        filePart.write(filePath);
        
        Product product = new Product();
        product.setName(name);
        product.setDescription(description);
        product.setPrice(price);
        product.setStock(stock);
        product.setCategoryId(categoryId);
        product.setImageUrl("uploads/" + fileName);
        
        ProductService productService = new ProductService();
        productService.addProduct(product);
        
        response.sendRedirect("productManagement.jsp?message=添加成功");
    }
    
    private String getSubmittedFileName(Part part) {
        for (String cd : part.getHeader("content-disposition").split(";")) {
            if (cd.trim().startsWith("filename")) {
                String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
                return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1);
            }
        }
        return null;
    }
}

订单管理功能允许管理员查看所有订单,并按状态进行筛选。订单状态变更(如从"已支付"改为"已发货")时会记录操作时间,并更新相关时间戳字段。

订单管理界面

@WebServlet("/admin/updateOrderStatus")
public class UpdateOrderStatusServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int orderId = Integer.parseInt(request.getParameter("orderId"));
        String newStatus = request.getParameter("status");
        
        OrderService orderService = new OrderService();
        boolean success = orderService.updateOrderStatus(orderId, newStatus);
        
        if (success) {
            response.getWriter().write("{\"success\": true}");
        } else {
            response.getWriter().write("{\"success\": false, \"message\": \"状态更新失败\"}");
        }
    }
}

实体模型设计

系统的模型层由多个JavaBean组成,每个Bean对应数据库中的一张表,封装了对象的属性和行为。以Product类为例,它遵循JavaBean规范,提供私有字段和公共的getter/setter方法。

public class Product {
    private int id;
    private String name;
    private String description;
    private BigDecimal price;
    private int stock;
    private String imageUrl;
    private int categoryId;
    private int status;
    private Date createTime;
    private Date updateTime;
    
    // 无参构造函数
    public Product() {}
    
    // Getter和Setter方法
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    
    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }
    
    public int getStock() { return stock; }
    public void setStock(int stock) { this.stock = stock; }
    
    public String getImageUrl() { return imageUrl; }
    public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
    
    public
本文关键词
JSPServlet在线毛绒玩具商城源码解析MVC设计模式

上下篇

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