基于SSM框架的在线工艺品销售平台 - 源码深度解析

JavaJavaScriptMavenHTMLCSSSSM框架MySQL
2026-02-244 浏览

文章摘要

本项目是一款基于SSM(Spring+SpringMVC+MyBatis)框架构建的在线工艺品销售平台,旨在为手工艺品创作者、小型工作室及收藏爱好者提供一个专业、便捷的线上交易空间。平台的核心业务价值在于精准解决了传统工艺品销售渠道窄、展示效果有限、交易流程繁琐的行业痛点。通过数字化的商品陈列、安全...

在传统工艺品行业,数字化转型的需求日益迫切。手工艺人、非遗传承人及小型工作室往往受限于地域和传统销售渠道,难以将蕴含深厚文化底蕴的作品有效推向更广阔的市场。与此同时,追求个性与品质的消费者也苦于缺乏一个能够便捷发现、深入了解并安全购买独特工艺品的专业平台。这一供需之间的断层,催生了对专业化线上交易空间的迫切需求。

“艺数坊”——一个基于SSM(Spring+SpringMVC+MyBatis)全栈架构的在线工艺品销售平台,正是为解决这一行业痛点而构建。它不仅仅是一个简单的电商网站,更是一个连接创作者与鉴赏家的桥梁,通过精准的技术实现,为工艺品这一垂直领域提供了完整的线上陈列、交易与管理的解决方案。

技术架构选型与优势

项目采用经典的SSM三层架构,这是一种在Java企业级开发中久经考验的成熟模式,具有良好的分层清晰度、可维护性和可扩展性。

  • Spring框架作为整个应用的核心容器,负责管理所有的业务逻辑对象(Service Beans)。其强大的依赖注入(DI)机制,使得各层之间的耦合度降至最低,例如,Service层需要调用DAO层时,只需通过@Autowired注解声明依赖,Spring容器便会自动完成注入。同时,面向切面编程(A8OP)能力被用于统一处理事务管理(@Transactional)、日志记录等横切关注点,确保了业务操作的原子性和服务的稳定性。
  • SpringMVC模块承担了Web层的职责,它采用了清晰的前端控制器(DispatcherServlet)模式。所有的HTTP请求首先由DispatcherServlet接收,然后根据配置的映射关系(如@RequestMapping)分发给对应的控制器(Controller)。控制器处理请求参数,调用业务服务,并最终将模型数据封装后返回给视图解析器。这种职责分离的设计,使得Web交互逻辑与核心业务逻辑得以清晰划分,便于团队协作与功能迭代。
  • 数据持久层MyBatis框架实现。与完全的ORM框架不同,MyBatis允许开发者对SQL语句进行高度优化和精确控制,这对于需要复杂查询和性能调优的电商场景尤为重要。通过XML映射文件或注解,将Java接口方法与SQL语句动态绑定,既享受了面向对象编程的便利,又保留了SQL的灵活性。
  • 前端展示层使用JSP(JavaServer Pages)技术,结合JavaScript(如jQuery)和CSS来构建动态、交互式的用户界面。JSP页面可以方便地嵌入JSTL标签和EL表达式,用于展示从控制器传递过来的模型数据,实现服务器端渲染。

整个项目通过Maven进行依赖管理和构建,确保了第三方库版本的一致性和项目结构的标准化。数据库则选用开源且性能稳定的MySQL,存储平台的核心业务数据。

核心数据模型设计与业务逻辑体现

一个稳健的电商系统,其根基在于合理、高效的数据库设计。本项目包含10张核心数据表,以下是几个关键表的设计剖析,它们深刻反映了平台的业务逻辑。

1. 商品分类表(product_category):实现灵活的商品导航

CREATE TABLE `product_category` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `category_name` varchar(50) NOT NULL COMMENT '分类名称,如陶瓷、刺绣、木雕',
  `category_description` text COMMENT '分类详细描述',
  `parent_id` int(11) DEFAULT NULL COMMENT '父分类ID,用于实现多级分类',
  `category_image` varchar(255) DEFAULT NULL COMMENT '分类展示图片',
  `sort_order` int(11) DEFAULT '0' COMMENT '排序权重',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志(0:未删除,1:已删除)',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`category_id`),
  KEY `idx_parent_id` (`parent_id`),
  KEY `idx_sort_order` (`sort_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表';

设计亮点分析:

  • 多级分类支持:通过parent_id字段实现了无限层级的树状分类结构。例如,可以设置“陶瓷”为一级分类,其下又可细分为“青花瓷”、“景德镇瓷”等子分类。这种设计极大地增强了商品组织的灵活性,满足了工艺品细分领域的展示需求。在查询时,通常使用递归或嵌套集合模型等算法来高效获取整棵分类树。
  • 逻辑删除与审计字段is_deleted字段实现了数据的软删除,避免物理删除导致的数据丢失风险,符合企业级应用的数据安全规范。gmt_creategmt_modified是标准的审计字段,由数据库自动维护,便于追踪数据生命周期。
  • 索引优化:对parent_idsort_order字段建立了索引,前者加速了基于父ID查询子分类的速度(如在导航菜单中渲染),后者则确保了分类列表能够按照管理员的设定顺序快速呈现。

分类管理

2. 订单主表(order_master):保障交易流程的完整性

CREATE TABLE `order_master` (
  `order_id` varchar(32) NOT NULL COMMENT '订单号,使用UUID或雪花算法生成',
  `user_id` int(11) NOT NULL COMMENT '下单用户ID',
  `order_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
  `order_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态(0:新订单,1:已支付,2:已发货,3:已完成,4:已取消)',
  `pay_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态(0:未支付,1:支付成功,2:支付失败)',
  `shipping_address` text NOT NULL COMMENT '收货地址详情',
  `buyer_name` varchar(64) NOT NULL COMMENT '收货人姓名',
  `buyer_phone` varchar(32) NOT NULL COMMENT '收货人电话',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `ship_time` datetime DEFAULT NULL COMMENT '发货时间',
  `end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`order_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_create_time` (`gmt_create`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';

设计亮点分析:

  • 状态机设计order_statuspay_status字段清晰地定义了订单的生命周期。这种状态机模式是电商系统的核心,业务逻辑(如支付成功后自动修改订单状态、库存扣减)都围绕状态变迁展开。它保证了交易流程的严谨性和可追溯性。
  • 非自增主键:订单号(order_id)没有使用常见的自增整数,而是采用更长、无规律的字符串(如UUID或雪花算法ID)。这避免了通过订单号推测平台销量等敏感信息,提升了安全性,也便于在分布式系统中生成唯一ID。
  • 查询优化:为user_idgmt_create(创建时间)建立了索引,这使得用户查询“我的订单”以及管理员按时间筛选订单时,数据库性能得到显著提升。

订单管理

核心功能模块的代码级深度解析

1. 用户认证与购物车管理

用户登录后,核心操作之一便是将心仪的工艺品加入购物车。购物车的数据结构设计和业务逻辑至关重要。

实体类 - 购物车项(CartItem)

public class CartItem {
    private Integer productId;     // 商品ID
    private String productName;    // 商品名称
    private String productImage;   // 商品主图
    private BigDecimal unitPrice;  // 商品单价
    private Integer quantity;      // 购买数量
    private BigDecimal totalPrice; // 此项总价(unitPrice * quantity)

    // 省略getter和setter方法...

    /**
     * 计算当前购物车项的总价
     */
    public void calculateTotalPrice() {
        if (this.unitPrice != null && this.quantity != null) {
            this.totalPrice = this.unitPrice.multiply(new BigDecimal(quantity));
        }
    }
}

控制器 - 处理加入购物车请求(CartController)

@Controller
@RequestMapping("/cart")
public class CartController {

    @Autowired
    private ProductService productService;

    /**
     * 将商品添加到购物车(Session实现)
     * @param productId 商品ID
     * @param quantity 数量
     * @param session HttpSession
     * @return 重定向到购物车页面
     */
    @PostMapping("/add")
    public String addToCart(@RequestParam Integer productId,
                           @RequestParam(defaultValue = "1") Integer quantity,
                           HttpSession session) {
        // 1. 根据商品ID查询商品详细信息
        Product product = productService.findProductById(productId);
        if (product == null) {
            throw new RuntimeException("商品不存在");
        }

        // 2. 从Session中获取当前用户的购物车(Map结构,key为productId,value为CartItem)
        Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
        }

        // 3. 判断购物车中是否已有该商品
        CartItem item = cart.get(productId);
        if (item != null) {
            // 已有,则增加数量
            item.setQuantity(item.getQuantity() + quantity);
            item.calculateTotalPrice(); // 重新计算总价
        } else {
            // 没有,则创建新的购物车项
            item = new CartItem();
            item.setProductId(product.getId());
            item.setProductName(product.getName());
            item.setProductImage(product.getMainImage());
            item.setUnitPrice(product.getPrice());
            item.setQuantity(quantity);
            item.calculateTotalPrice();
            cart.put(productId, item);
        }

        // 4. 将更新后的购物车存回Session
        session.setAttribute("cart", cart);
        return "redirect:/cart/view";
    }
}

加入购物车

代码解析:此功能采用HttpSession在服务器端临时存储用户的购物车信息,适用于非持久化的购物场景。代码逻辑清晰:验证商品有效性、获取或初始化购物车、更新商品数量、重新计算价格。这种实现方式简单高效,但需要注意Session的生命周期和分布式环境下的同步问题。

2. 商品详情展示与用户评论

工艺品的价值不仅在于实物,更在于其背后的故事和工艺。因此,商品详情页需要丰富的信息展示。

服务层 - 获取商品详情(ProductServiceImpl)

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private ProductReviewMapper reviewMapper;

    /**
     * 根据商品ID获取商品详情,包括基本信息和评论列表
     * @param productId
     * @return 商品详情DTO(数据传输对象)
     */
    @Override
    public ProductDetailDTO getProductDetailById(Integer productId) {
        // 1. 获取商品基本信息
        Product product = productMapper.selectByPrimaryKey(productId);
        if (product == null || product.getIsDeleted() == 1) {
            return null;
        }

        // 2. 构建DTO对象
        ProductDetailDTO detailDTO = new ProductDetailDTO();
        detailDTO.setProduct(product);

        // 3. 查询该商品的评论列表
        Map<String, Object> params = new HashMap<>();
        params.put("productId", productId);
        params.put("status", 1); // 只查询已审核通过的评论
        List<ProductReview> reviews = reviewMapper.selectByParams(params);
        detailDTO.setReviews(reviews);

        // 4. 计算平均评分(可选)
        if (reviews != null && !reviews.isEmpty()) {
            Double avgRating = reviews.stream()
                    .mapToInt(ProductReview::getRating)
                    .average()
                    .orElse(0.0);
            detailDTO.setAverageRating(avgRating);
        }

        return detailDTO;
    }
}

MyBatis Mapper XML - 复杂查询映射

<!-- ProductReviewMapper.xml -->
<mapper namespace="com.artisansplatform.dao.ProductReviewMapper">
    <select id="selectByParams" parameterType="map" resultMap="BaseResultMap">
        SELECT
            r.*,
            u.username as author_name
        FROM product_review r
        LEFT JOIN user u ON r.user_id = u.user_id
        WHERE r.product_id = #{productId}
        AND r.status = #{status}
        ORDER BY r.gmt_create DESC
    </select>
</mapper>

查看商品详情

代码解析:服务层方法getProductDetailById封装了获取商品详情的完整逻辑。它通过MyBatis的Mapper接口查询数据库,并利用Java 8 Stream API高效地计算商品的平均评分。MyBatis的XML映射文件展示了如何执行多表关联查询(product_review JOIN user),将评论与其作者信息一并取出,避免了N+1查询问题,提升了性能。返回的DTO对象整合了商品实体和评论列表,为前端提供了结构化的数据。

3. 订单创建与库存校验

提交订单是电商流程中最关键且最复杂的环节,涉及数据一致性校验、库存锁定和订单生成。

服务层 - 创建订单(OrderServiceImpl)

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMasterMapper orderMasterMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private ProductMapper productMapper;

    /**
     * 创建订单(带事务管理)
     * @param orderCreateRequest 订单创建请求,包含用户ID、地址、商品清单等
     * @return 生成的订单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 声明式事务,异常时回滚
    public String createOrder(OrderCreateRequest orderCreateRequest) {
        // 1. 生成唯一的订单号
        String orderId = generateOrderId();

        // 2. 创建订单主表记录
        OrderMaster orderMaster = new OrderMaster();
        orderMaster.setOrderId(orderId);
        orderMaster.setUserId(orderCreateRequest.getUserId());
        // ... 设置其他字段如地址、总金额等
        orderMaster.setOrderAmount(calculateOrderTotal(orderCreateRequest.getItems()));
        orderMasterMapper.insertSelective(orderMaster);

        // 3. 遍历订单商品项,创建订单明细并校验库存
        for (OrderItemRequest item : orderCreateRequest.getItems()) {
            Product product = productMapper.selectByPrimaryKey(item.getProductId());

            // 3.1 库存校验
            if (product.getStock() < item.getQuantity()) {
                throw new RuntimeException("商品【" + product.getName() + "】库存不足");
            }

            // 3.2 创建订单明细
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setProductId(item.getProductId());
            orderDetail.setProductName(product.getName());
            orderDetail.setProductPrice(product.getPrice());
            orderDetail.setProductQuantity(item.getQuantity());
            orderDetailMapper.insertSelective(orderDetail);

            // 3.3 扣减商品库存(悲观锁或乐观锁实现,此处为简化版)
            product.setStock(product.getStock() - item.getQuantity());
            productMapper.updateByPrimaryKeySelective(product);
        }

        return orderId;
    }

    private BigDecimal calculateOrderTotal(List<OrderItemRequest> items) {
        // 计算订单总金额的逻辑
        return items.stream()
                .map(item -> {
                    Product p = productMapper.selectByPrimaryKey(item.getProductId());
                    return p.getPrice().multiply(new BigDecimal(item.getQuantity()));
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private String generateOrderId() {
        // 使用时间戳+随机数生成订单号,实际生产环境可用雪花算法
        return System.currentTimeMillis() + "" + (int)((Math.random() * 9 + 1) * 1000);
    }
}

提交订单

代码解析createOrder方法是整个下单流程的核心,并使用@Transactional注解开启了声明式事务。这意味着方法内的所有数据库操作(插入订单主表、插入订单明细表、更新商品库存)要么全部成功,要么全部失败回滚,有效防止了数据不一致的情况(如订单生成成功但库存扣减失败)。代码逻辑严谨:先生成订单号,再保存主订单信息,然后遍历商品清单,对每一项进行库存校验并保存明细,最后实时扣减库存。在高并发场景下,库存扣减部分需要引入更复杂的并发控制机制,如悲观锁(SELECT ... FOR UPDATE)或乐观锁(通过版本号字段)。

未来功能展望与技术优化方向

“艺数坊”平台已经具备了工艺品在线交易的核心功能。为了进一步提升用户体验、平台价值和系统 robustness,可以考虑以下优化方向:

  1. 引入Redis缓存与Session集群管理:当前购物车依赖于服务器Session,在应用重启或分布式部署时会丢失。引入Redis作为分布式缓存,可以将用户Session、热门商品信息、分类树等数据存储在内存中,极大提升读取速度并解决状态同步问题。
  2. 实现全文搜索与智能推荐:集成Elasticsearch等搜索引擎,为工艺品名称、描述、作者等信息提供高效的模糊搜索和高亮显示。基于用户的浏览、购买行为,利用协同过滤或内容推荐算法,构建“猜你喜欢”、“相似工艺品”等智能推荐模块,提升转化率。
  3. 构建微服务架构以应对业务增长:随着
本文关键词
SSM框架在线工艺品销售源码解析电商平台Spring+SpringMVC+MyBatis

上下篇

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