基于JSP+Servlet的水产品在线销售与库存管理系统 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-264 浏览

文章摘要

本项目是一款基于JSP与Servlet技术构建的水产品在线销售与库存管理系统,旨在为水产品批发商、零售商或养殖企业提供一体化的业务运营解决方案。系统的核心业务价值在于打通了从前端销售到后端库存的关键数据流,解决了传统人工记录方式下信息割裂、数据更新不及时、易出现超卖或库存积压的经营痛点。通过将销售订...

水产品供应链管理一直是传统农业电商信息化的重点与难点领域。该系统采用经典的JSP+Servlet技术栈,构建了一个集前端销售、库存管理、订单处理于一体的全链路业务平台。在技术架构上严格遵循MVC设计模式,通过Servlet控制器统一处理业务逻辑,JSP页面负责数据展示,JDBC实现数据持久化,形成了清晰的三层架构体系。

数据库架构设计亮点

系统采用MySQL数据库,六张核心表的设计体现了业务场景的完整性。其中商品表(products)的设计尤为关键:

CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    category_id INT NOT NULL,
    product_name VARCHAR(100) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL,
    stock_quantity INT DEFAULT 0,
    image_url VARCHAR(200),
    is_hot BOOLEAN DEFAULT FALSE,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(category_id)
);

该表通过category_id与分类表建立外键关联,支持商品的多级分类管理。price字段采用DECIMAL(10,2)类型确保金额计算的精确性,stock_quantity字段实时反映库存数量,is_hot标志位支持热门商品标识,为前端页面差异化展示提供数据支撑。

库存流水表(inventory_logs)的设计体现了系统的核心业务逻辑:

CREATE TABLE inventory_logs (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    product_id INT NOT NULL,
    change_type ENUM('IN','OUT','ADJUST') NOT NULL,
    change_quantity INT NOT NULL,
    previous_quantity INT NOT NULL,
    current_quantity INT NOT NULL,
    reference_id INT, -- 关联订单ID或调整单ID
    operator_id INT NOT NULL,
    operate_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    remarks VARCHAR(200),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

该表通过change_type枚举字段清晰记录库存变动类型(入库、出库、调整),保留变动前后的数量值,形成完整的审计轨迹。reference_id字段灵活关联业务单据,满足不同场景下的溯源需求。

核心业务功能实现

用户身份认证与会话管理

系统采用基于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");
        String userType = request.getParameter("userType");
        
        UserDAO userDAO = new UserDAO();
        User user = userDAO.authenticate(username, password, userType);
        
        if (user != null) {
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            session.setAttribute("userType", userType);
            
            if ("admin".equals(userType)) {
                response.sendRedirect("admin/dashboard.jsp");
            } else {
                response.sendRedirect("user/home.jsp");
            }
        } else {
            request.setAttribute("errorMsg", "用户名或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}

用户登录界面

认证过程通过UserDAO执行数据库验证,成功后建立会话并根据用户类型跳转到相应工作台。会话数据包含用户对象和类型标识,为后续权限控制提供基础。

商品库存联动更新机制

订单提交时,系统通过Transaction实现库存的原子性更新:

public class OrderService {
    public boolean createOrder(Order order, List<OrderItem> items) {
        Connection conn = null;
        try {
            conn = DataSourceUtil.getConnection();
            conn.setAutoCommit(false);
            
            // 插入订单主记录
            OrderDAO orderDAO = new OrderDAO(conn);
            int orderId = orderDAO.insertOrder(order);
            
            // 处理订单明细和库存扣减
            InventoryDAO inventoryDAO = new InventoryDAO(conn);
            for (OrderItem item : items) {
                item.setOrderId(orderId);
                orderDAO.insertOrderItem(item);
                
                // 检查并扣减库存
                boolean stockSufficient = inventoryDAO.reduceStock(
                    item.getProductId(), item.getQuantity());
                if (!stockSufficient) {
                    conn.rollback();
                    return false;
                }
            }
            
            conn.commit();
            return true;
        } catch (SQLException e) {
            if (conn != null) {
                try { conn.rollback(); } catch (SQLException ex) {}
            }
            return false;
        } finally {
            DataSourceUtil.closeConnection(conn);
        }
    }
}

提交订单界面

该机制确保订单创建与库存扣减的原子性,避免超卖情况。通过事务管理和连接传递,保证多个数据库操作的一致性。

动态商品展示与分类检索

商品列表页面采用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}">
            <h3>${product.productName}</h3>
            <p class="price">¥${product.price}</p>
            <p class="stock">库存: ${product.stockQuantity}</p>
            <c:if test="${product.stockQuantity > 0}">
                <button onclick="addToCart(${product.productId})">加入购物车</button>
            </c:if>
            <c:if test="${product.stockQuantity <= 0}">
                <button disabled>缺货</button>
            </c:if>
        </div>
    </c:forEach>
</div>

热门水产品展示

前端页面通过EL表达式动态绑定商品数据,JSTL条件判断实现库存状态的可视化提示,提升用户体验。

购物车管理与会话存储

购物车功能采用HttpSession存储临时数据:

@WebServlet("/cart")
public class CartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        String action = request.getParameter("action");
        HttpSession session = request.getSession();
        Map<Integer, CartItem> cart = getOrCreateCart(session);
        
        if ("add".equals(action)) {
            int productId = Integer.parseInt(request.getParameter("productId"));
            int quantity = Integer.parseInt(request.getParameter("quantity"));
            
            ProductDAO productDAO = new ProductDAO();
            Product product = productDAO.getProductById(productId);
            
            if (cart.containsKey(productId)) {
                CartItem item = cart.get(productId);
                item.setQuantity(item.getQuantity() + quantity);
            } else {
                cart.put(productId, new CartItem(product, quantity));
            }
        } else if ("remove".equals(action)) {
            int productId = Integer.parseInt(request.getParameter("productId"));
            cart.remove(productId);
        }
        
        session.setAttribute("shoppingCart", cart);
        response.sendRedirect("cart.jsp");
    }
    
    private Map<Integer, CartItem> getOrCreateCart(HttpSession session) {
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("shoppingCart");
        return cart != null ? cart : new HashMap<>();
    }
}

购物车查看界面

购物车数据存储在用户会话中,支持商品的添加、移除和数量修改,无需频繁访问数据库,提升系统响应性能。

实体模型与业务逻辑封装

系统通过JavaBean封装业务实体,如订单实体包含完整的业务属性:

public class Order {
    private int orderId;
    private int userId;
    private String orderNumber;
    private BigDecimal totalAmount;
    private String status; // PENDING, PAID, SHIPPED, COMPLETED, CANCELLED
    private String shippingAddress;
    private String recipientName;
    private String recipientPhone;
    private Date createTime;
    private Date updateTime;
    private List<OrderItem> items;
    
    // 计算订单总金额
    public BigDecimal calculateTotal() {
        return items.stream()
            .map(item -> item.getUnitPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    // 验证订单状态流转
    public boolean canChangeStatus(String targetStatus) {
        Map<String, List<String>> allowedTransitions = Map.of(
            "PENDING", Arrays.asList("PAID", "CANCELLED"),
            "PAID", Arrays.asList("SHIPPED", "CANCELLED"),
            "SHIPPED", Arrays.asList("COMPLETED")
        );
        return allowedTransitions.getOrDefault(this.status, Collections.emptyList())
                .contains(targetStatus);
    }
}

实体类内封装业务规则验证方法,确保状态流转的合法性和金额计算的准确性。

管理端功能深度解析

分类管理与商品关联

分类管理Servlet支持分类的增删改查操作:

@WebServlet("/admin/category")
public class CategoryServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        String action = request.getParameter("action");
        CategoryDAO categoryDAO = new CategoryDAO();
        
        if ("create".equals(action)) {
            String categoryName = request.getParameter("categoryName");
            String description = request.getParameter("description");
            Category category = new Category(0, categoryName, description);
            categoryDAO.insertCategory(category);
        } else if ("update".equals(action)) {
            int categoryId = Integer.parseInt(request.getParameter("categoryId"));
            String categoryName = request.getParameter("categoryName");
            Category category = new Category(categoryId, categoryName, "");
            categoryDAO.updateCategory(category);
        }
        response.sendRedirect("category-management.jsp");
    }
}

分类管理界面

分类数据通过外键与商品表关联,确保数据完整性。管理界面提供直观的操作入口,支持分类信息的维护。

订单处理与状态跟踪

订单管理模块实现多状态流转控制:

public class OrderDAO {
    public boolean updateOrderStatus(int orderId, String newStatus, String oldStatus) {
        String sql = "UPDATE orders SET status = ?, update_time = NOW() " +
                    "WHERE order_id = ? AND status = ?";
        try (Connection conn = DataSourceUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, newStatus);
            stmt.setInt(2, orderId);
            stmt.setString(3, oldStatus);
            return stmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public List<Order> getOrdersByStatus(String status) {
        String sql = "SELECT * FROM orders WHERE status = ? ORDER BY create_time DESC";
        // 执行查询并映射结果集
    }
}

订单管理界面

通过乐观锁机制防止并发状态更新冲突,确保订单状态变更的准确性。

性能优化与安全考量

系统在数据访问层实现连接池管理:

public class DataSourceUtil {
    private static BasicDataSource dataSource;
    
    static {
        dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/aquatic_sales?useSSL=false");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        dataSource.setInitialSize(5);
        dataSource.setMaxTotal(20);
        dataSource.setMaxIdle(10);
        dataSource.setMinIdle(5);
    }
    
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

连接池配置优化数据库连接资源管理,提升系统并发处理能力。参数设置平衡了资源利用率和系统性能。

输入验证与SQL注入防护:

public class InputValidator {
    public static boolean validateProductInput(String productName, String price) {
        if (productName == null || productName.trim().isEmpty()) {
            return false;
        }
        if (!productName.matches("[\\u4e00-\\u9fa5a-zA-Z0-9\\s]{1,100}")) {
            return false;
        }
        try {
            BigDecimal decimalPrice = new BigDecimal(price);
            return decimalPrice.compareTo(BigDecimal.ZERO) >= 0;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    public static String sanitizeSql(String input) {
        return input.replace("'", "''")
                   .replace(";", "")
                   .replace("--", "")
                   .replace("/*", "")
                   .replace("*/", "");
    }
}

通过正则表达式和参数化查询结合的方式,有效防止SQL注入攻击,确保系统安全性。

技术架构优化方向

缓存层引入与性能提升

当前系统每次商品查询均直接访问数据库,可引入Redis缓存层:

public class ProductServiceWithCache {
    private JedisPool jedisPool;
    private ProductDAO productDAO;
    
    public Product getProductById(int productId) {
        String cacheKey = "product:" + productId;
        try (Jedis jedis = jedisPool.getResource()) {
            String cached = jedis.get(cacheKey);
            if (cached != null) {
                return JSON.parseObject(cached, Product.class);
            }
        }
        
        Product product = productDAO.getProductById(productId);
        if (product != null) {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.setex(cacheKey, 3600, JSON.toJSONString(product));
            }
        }
        return product;
    }
}

缓存热点商品数据,显著降低数据库访问压力,提升系统响应速度。

异步处理与消息队列集成

订单创建后的库存扣减、通知发送等操作可异步化:

@WebServlet("/async-order")
public class AsyncOrderServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.start(() -> {
            try {
                // 创建订单主记录
                Order order = createOrder(request);
                
                // 异步处理库存扣减
                inventoryService.asyncReduceStock(order);
                
                // 异步发送通知
                notificationService.asyncSendOrderConfirm(order);
                
                asyncContext.complete();
            } catch (Exception e) {
                asyncContext.complete();
            }
        });
    }
}

通过Servlet异步支持实现非阻塞处理,提升系统吞吐量。

微服务架构改造

将单体应用拆分为商品服务、订单服务、库存服务等微服务:

// 商品服务接口
@RestController
@RequestMapping("/products")
public class ProductController {
    @Autowired
    private ProductService productService;
    
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable int id) {
        return productService.getProductById(id);
    }
}

// 库存服务接口  
@RestController
@RequestMapping("/inventory")
public class InventoryController {
    @PutMapping("/reduce")
    public boolean reduceStock(@RequestBody StockReduceRequest request) {
        return inventoryService.reduceStock(request);
    }
}

通过Spring Cloud等技术实现服务治理,提升系统可扩展性和维护性。

该系统通过严谨的架构设计和扎实的技术实现,为水产品行业提供了可靠的数字化管理解决方案。其在库存联动、订单处理等核心业务场景的技术实现具有行业参考价值,为传统农产品电商信息化建设提供了实践范例。

本文关键词
JSPServlet水产品在线销售库存管理

上下篇

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