基于JSP+Servlet的二手商品交易平台 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-093 浏览

文章摘要

本项目是一个基于JSP与Servlet技术栈构建的二手商品交易平台,旨在为用户提供一个安全、便捷的线上闲置物品流转渠道。其核心业务价值在于有效解决了传统二手交易中信息不对称、交易流程繁琐、信任缺失等痛点。通过标准化的商品发布、在线沟通与订单管理功能,平台降低了个人用户参与二手交易的门槛,促进了资源的...

在当今数字化消费时代,闲置资源的有效流通已成为促进可持续消费的重要环节。传统线下二手交易存在信息不对称、交易效率低下、信任机制不完善等痛点,亟需通过技术手段构建标准化、可信赖的线上交易环境。本项目采用经典的JSP+Servlet技术栈,构建了一个功能完备的二手商品交易平台,实现了从商品展示、交易协商到订单管理的全流程数字化。

系统采用典型的三层架构模式,表现层由JSP页面负责数据渲染和用户交互,业务逻辑层通过Servlet控制器处理核心业务流程,数据持久层使用JDBC进行数据库操作。这种分层设计使得系统具有良好的可维护性和扩展性,各层之间职责分明,耦合度低。

在安全机制方面,系统全面采用PreparedStatement防止SQL注入攻击,对用户输入进行严格校验,敏感操作要求权限验证。交易流程中引入了多重状态管理机制,确保每个交易环节的可追溯性和安全性。

数据库架构设计与核心表分析

系统共设计8张核心数据表,涵盖了用户管理、商品交易、订单处理等核心业务模块。其中用户表、商品表和订单表的设计尤为关键,直接决定了系统的数据完整性和业务稳定性。

用户表(users)采用分级权限设计:

CREATE TABLE users (
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    phone VARCHAR(20),
    address TEXT,
    user_type ENUM('admin','user') DEFAULT 'user',
    registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_login TIMESTAMP,
    status ENUM('active','inactive') DEFAULT 'active'
);

该表设计的亮点在于通过user_type字段实现角色分离,管理员与普通用户共享同一张表但具有不同的操作权限。registration_datelast_login时间戳字段为用户行为分析提供了数据基础,而status字段支持灵活的账户状态管理。

商品表(products)支持多维度分类:

CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL,
    category_id INT NOT NULL,
    seller_id INT NOT NULL,
    image_url VARCHAR(500),
    stock_quantity INT DEFAULT 1,
    condition ENUM('new','like_new','good','fair') NOT NULL,
    status ENUM('available','sold','removed') DEFAULT 'available',
    created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    view_count INT DEFAULT 0,
    FOREIGN KEY (category_id) REFERENCES categories(category_id),
    FOREIGN KEY (seller_id) REFERENCES users(user_id)
);

商品表通过condition字段标准化商品成色描述,避免买卖双方对商品状态的认知差异。view_count字段为热门商品推荐提供数据支持,而状态字段确保已售商品不会继续出现在交易流程中。外键约束保证了数据的一致性和完整性。

订单表(orders)设计体现交易完整性:

CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    order_number VARCHAR(50) UNIQUE NOT NULL,
    buyer_id INT NOT NULL,
    total_amount DECIMAL(10,2) NOT NULL,
    status ENUM('pending','confirmed','shipped','completed','cancelled') DEFAULT 'pending',
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    payment_method VARCHAR(50),
    shipping_address TEXT NOT NULL,
    contact_phone VARCHAR(20) NOT NULL,
    FOREIGN KEY (buyer_id) REFERENCES users(user_id)
);

订单表采用多状态管理机制,完整覆盖从下单到交易完成的整个生命周期。order_number字段使用唯一标识符便于订单追踪,而total_amount字段确保金额计算的准确性。支付方式和配送信息的独立存储为后续功能扩展奠定了基础。

核心业务功能实现解析

用户认证与权限控制系统

系统采用基于Session的用户认证机制,通过统一的登录验证Servlet处理各类用户的认证请求。登录过程包含密码加密验证和权限识别两个关键环节。

@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");
        
        UserDAO userDAO = new UserDAO();
        User user = userDAO.authenticate(username, password);
        
        if (user != null) {
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            session.setMaxInactiveInterval(30 * 60); // 30分钟超时
            
            if ("admin".equals(user.getUserType())) {
                response.sendRedirect("admin/dashboard.jsp");
            } else {
                response.sendRedirect("user/home.jsp");
            }
        } else {
            request.setAttribute("errorMessage", "用户名或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}

用户登录界面

认证模块通过MD5加密存储用户密码,确保即使数据库泄露也不会直接暴露用户密码。Session超时机制有效防止用户长时间不操作导致的安全风险。基于用户类型的重定向逻辑实现了管理员与普通用户的界面分离。

商品管理与展示系统

商品管理模块支持卖家自主发布商品,系统自动生成商品列表并按分类展示。商品发布过程包含完整的表单验证和图片上传功能。

@WebServlet("/product/add")
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) {
            response.sendRedirect("../login.jsp");
            return;
        }
        
        // 获取表单数据
        String productName = request.getParameter("productName");
        String description = request.getParameter("description");
        BigDecimal price = new BigDecimal(request.getParameter("price"));
        int categoryId = Integer.parseInt(request.getParameter("categoryId"));
        String condition = request.getParameter("condition");
        
        // 处理图片上传
        Part filePart = request.getPart("image");
        String imageUrl = null;
        if (filePart != null && filePart.getSize() > 0) {
            imageUrl = FileUploadUtil.saveFile(filePart, "product_images");
        }
        
        // 创建商品对象并保存
        Product product = new Product();
        product.setProductName(productName);
        product.setDescription(description);
        product.setPrice(price);
        product.setCategoryId(categoryId);
        product.setSellerId(user.getUserId());
        product.setCondition(condition);
        product.setImageUrl(imageUrl);
        
        ProductDAO productDAO = new ProductDAO();
        boolean success = productDAO.addProduct(product);
        
        if (success) {
            response.sendRedirect("../user/my-products.jsp?message=add_success");
        } else {
            request.setAttribute("error", "商品发布失败,请重试");
            request.getRequestDispatcher("add-product.jsp").forward(request, response);
        }
    }
}

商品管理界面

商品展示页面采用分页技术处理大量商品数据,通过JSTL标签库动态渲染商品信息。每个商品卡片包含图片、标题、价格和卖家信息等关键数据。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="product-grid">
    <c:forEach var="product" items="${productList}">
        <div class="product-card">
            <img src="${product.imageUrl}" alt="${product.productName}" 
                 onerror="this.src='images/default-product.png'">
            <div class="product-info">
                <h3>${product.productName}</h3>
                <p class="price">¥${product.price}</p>
                <p class="condition">成色:${product.condition}</p>
                <p class="seller">卖家:${product.seller.username}</p>
                <a href="product-detail.jsp?id=${product.productId}" 
                   class="view-detail-btn">查看详情</a>
            </div>
        </div>
    </c:forEach>
</div>

<!-- 分页控件 -->
<div class="pagination">
    <c:if test="${currentPage > 1}">
        <a href="?page=${currentPage - 1}">上一页</a>
    </c:if>
    
    <c:forEach begin="1" end="${totalPages}" var="i">
        <a href="?page=${i}" class="${i == currentPage ? 'active' : ''}">${i}</a>
    </c:forEach>
    
    <c:if test="${currentPage < totalPages}">
        <a href="?page=${currentPage + 1}">下一页</a>
    </c:if>
</div>

购物车与订单处理流程

购物车功能采用Session存储临时数据,用户可以将心仪商品加入购物车,统一结算。订单生成过程包含库存检查、金额计算和状态初始化。

@WebServlet("/cart/add")
public class AddToCartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        int productId = Integer.parseInt(request.getParameter("productId"));
        int quantity = Integer.parseInt(request.getParameter("quantity"));
        
        // 验证商品存在性和库存
        ProductDAO productDAO = new ProductDAO();
        Product product = productDAO.getProductById(productId);
        
        if (product == null || product.getStockQuantity() < quantity) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "商品不存在或库存不足");
            return;
        }
        
        // 获取或创建购物车
        HttpSession session = request.getSession();
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
        }
        
        // 添加商品到购物车
        if (cart.containsKey(productId)) {
            CartItem item = cart.get(productId);
            item.setQuantity(item.getQuantity() + quantity);
        } else {
            CartItem newItem = new CartItem(product, quantity);
            cart.put(productId, newItem);
        }
        
        session.setAttribute("cart", cart);
        response.sendRedirect("cart.jsp?message=add_success");
    }
}

购物车界面

订单生成模块处理购物车中的商品集合,创建完整的订单记录并更新商品库存:

@WebServlet("/order/create")
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 || cart == null || cart.isEmpty()) {
            response.sendRedirect("../login.jsp");
            return;
        }
        
        try {
            OrderDAO orderDAO = new OrderDAO();
            Order order = new Order();
            order.setBuyerId(user.getUserId());
            order.setOrderNumber(generateOrderNumber());
            order.setShippingAddress(request.getParameter("shippingAddress"));
            order.setContactPhone(request.getParameter("contactPhone"));
            order.setPaymentMethod(request.getParameter("paymentMethod"));
            
            // 计算总金额
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (CartItem item : cart.values()) {
                totalAmount = totalAmount.add(item.getProduct().getPrice()
                    .multiply(new BigDecimal(item.getQuantity())));
            }
            order.setTotalAmount(totalAmount);
            
            // 创建订单和订单项
            boolean success = orderDAO.createOrder(order, cart);
            
            if (success) {
                session.removeAttribute("cart"); // 清空购物车
                response.sendRedirect("order-success.jsp?orderId=" + order.getOrderId());
            } else {
                throw new Exception("订单创建失败");
            }
            
        } catch (Exception e) {
            request.setAttribute("error", "订单创建失败: " + e.getMessage());
            request.getRequestDispatcher("checkout.jsp").forward(request, response);
        }
    }
    
    private String generateOrderNumber() {
        return "ORD" + System.currentTimeMillis() + (int)(Math.random() * 1000);
    }
}

管理员后台管理系统

管理员后台提供全面的系统管理功能,包括用户管理、商品审核、订单处理等。采用基于过滤器(Filter)的权限控制机制确保管理功能的安全性。

@WebFilter("/admin/*")
public class AdminAuthFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        HttpSession session = httpRequest.getSession(false);
        if (session == null || session.getAttribute("currentUser") == null) {
            httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.jsp");
            return;
        }
        
        User user = (User) session.getAttribute("currentUser");
        if (!"admin".equals(user.getUserType())) {
            httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "无权访问管理员功能");
            return;
        }
        
        chain.doFilter(request, response);
    }
}

用户管理界面

管理员商品管理界面提供批量操作和高级搜索功能:

<%@ page import="java.util.List" %>
<%@ page import="com.market.model.Product" %>
<%
// 获取搜索参数
String keyword = request.getParameter("keyword");
String categoryId = request.getParameter("categoryId");
String status = request.getParameter("status");

ProductDAO productDAO = new ProductDAO();
List<Product> products = productDAO.searchProducts(keyword, categoryId, status);
request.setAttribute("products", products);
%>

<div class="admin-toolbar">
    <form method="get" class="search-form">
        <input type="text" name="keyword" placeholder="搜索商品名称..." 
               value="${param.keyword}">
        <select name="categoryId">
            <option value="">全部分类</option>
            <!-- 分类选项 -->
        </select>
        <select name="status">
            <option value="">全部状态</option>
            <option value="available">在售</option>
            <option value="sold">已售</option>
        </select>
        <button type="submit">搜索</button>
    </form>
    
    <div class="batch-actions">
        <button onclick="batchDelete()">批量删除</button>
        <button onclick="batchUpdateStatus('available')">批量上架</button>
    </div>
</div>

<table class="admin-table">
    <thead>
        <tr>
            <th><input type="checkbox" id="selectAll"></th>
            <th>商品ID</th>
            <th>商品名称</th>
            <th>价格</th>
            <th>卖家</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="product" items="${products}">
        <tr>
            <td><input type="checkbox" name="productIds" value="${product.productId}"></td>
            <td>${product.productId}</td>
            <td>${product.productName}</td>
            <td>¥${product.price}</td>
            <td>${product.seller.username}</td>
            <td>
                <span class="status-${product.status}">${product.status}</span>
            </td>
            <td>
                <a href="product-edit.jsp?id=${product.productId}">编辑</a>
                <a href="#" onclick="deleteProduct(${product.productId})">删除</a>
            </td>
        </tr>
        </c:forEach>
    </tbody>
</table>

数据模型与业务逻辑实体

系统核心业务实体通过JavaBean进行建模,每个实体类包含完整的属性定义和业务方法:

public class Product {
    private int productId;
    private String productName;
    private String description;
    private BigDecimal price;
    private int categoryId;
    private int sellerId;
    private String imageUrl;
    private int stockQuantity;
    private String condition;
    private String status;
    private Date createdDate;
    private int viewCount;
    private User seller; // 关联对象
    private Category category;
    
    // 构造方法、getter和setter
    public Product() {}
    
    public Product(int productId, String productName, BigDecimal price) {
        this.productId = productId;
        this.productName = productName;
        this.price = price;
    }
    
    // 业务方法
    public boolean isAvailable() {
        return "available".equals(status) && stockQuantity > 0;
    }
    
    public void incrementViewCount() {
        this.viewCount++;
    }
}

public class Order {
    private int orderId;
    private String orderNumber;
    private int buyerId;
    private BigDecimal totalAmount;
    private String status;
    private Date orderDate;
    private String paymentMethod;
    private String shippingAddress;
    private String contactPhone;
    private User buyer;
    private List<OrderItem> orderItems;
    
    // 状态转换方法
    public boolean canBeCancelled() {
        return "pending".equals(status) || "confirmed".equals(status);
    }
    
    public void cancel() {
        if (canBeCancelled()) {
            this.status = "cancelled";
        }
    }
}

数据访问层采用DAO模式,提供统一的数据操作接口:

public class ProductDAO {
    private Connection connection;
    
    public ProductDAO() {
        this.connection = DatabaseConnection.getConnection();
    }
    
    public List<Product> getProductsByCategory(int categoryId, int limit, int offset) {
        List<Product> products = new ArrayList<>();
        String sql = "SELECT * FROM products WHERE category_id = ? AND status = 'available' " +
                    "ORDER BY created_date DESC LIMIT ? OFFSET ?";
        
本文关键词
JSPServlet二手商品交易平台源码解析数据库设计

上下篇

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