基于SSM框架的水果蔬菜批发商城系统 - 源码深度解析

JavaJavaScriptHTMLCSSSSM框架MySQL
2026-02-244 浏览

文章摘要

基于SSM框架的水果蔬菜批发商城系统,旨在解决传统农产品批发交易中信息不透明、流程繁琐、效率低下的核心痛点。该系统通过线上平台整合供应商与批发商资源,实现商品展示、订单管理、库存跟踪、价格协商等核心业务闭环,显著降低中间环节成本,提升交易效率与供应链透明度。系统特别注重批发场景下的批量定价、多级客户...

农产品批发行业作为连接农业生产与消费市场的重要纽带,长期以来面临着信息不对称、交易链条冗长、价格波动频繁等挑战。传统批发模式依赖线下看货、电话询价、人工对账等低效流程,不仅增加了运营成本,还容易因信息滞后导致供需错配。特别是对于生鲜类产品,较长的交易周期会直接影响商品的新鲜度和损耗率。

针对这些行业痛点,我们设计并实现了一套基于SSM架构的数字化批发交易平台。该系统将农产品批发业务全流程线上化,通过标准化接口整合供应商管理、商品展示、智能定价、订单处理、库存监控等核心功能,为批发商和供应商构建了一个高效、透明、可追溯的交易环境。

技术架构设计

系统采用经典的三层架构模式,表现层使用SpringMVC框架处理Web请求,业务层通过Spring容器管理服务组件,数据持久层则采用MyBatis实现ORM映射。这种分层架构确保了各层之间的松耦合性,便于团队分工协作和系统维护升级。

核心依赖配置展示了项目的基础技术选型:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.5</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.11.2</version>
    </dependency>
</dependencies>

Spring的IOC容器通过注解方式统一管理Bean生命周期,以下配置类展示了核心组件的扫描规则:

@Configuration
@ComponentScan(basePackages = "com.agriculture.wholesale")
@EnableTransactionManagement
public class AppConfig {
    
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl("jdbc:mysql://localhost:3306/agriculture_db?useUnicode=true");
        ds.setUsername("root");
        ds.setPassword("123456");
        ds.setInitialSize(5);
        ds.setMaxActive(20);
        return ds;
    }
    
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath:mappers/*.xml"));
        return sessionFactory;
    }
}

数据库架构剖析

系统共设计10张核心数据表,围绕商品管理、订单处理、用户体系三大业务域构建数据模型。每张表都设置了合理的索引策略和外键约束,确保数据一致性和查询性能。

商品信息表设计

商品表采用纵向分字段设计,将基础信息、库存状态、价格策略等属性分别存储,支持灵活的查询条件组合:

CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(100) NOT NULL COMMENT '商品名称',
  `category_id` int(11) NOT NULL COMMENT '分类ID',
  `supplier_id` bigint(20) NOT NULL COMMENT '供应商ID',
  `specification` varchar(200) DEFAULT NULL COMMENT '规格描述',
  `unit` varchar(20) NOT NULL COMMENT '计量单位',
  `wholesale_price` decimal(10,2) NOT NULL COMMENT '批发价',
  `retail_price` decimal(10,2) DEFAULT NULL COMMENT '零售参考价',
  `stock_quantity` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
  `min_order_quantity` int(11) NOT NULL COMMENT '最小起订量',
  `shelf_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '上架状态',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_category` (`category_id`),
  KEY `idx_supplier` (`supplier_id`),
  KEY `idx_status` (`shelf_status`),
  KEY `idx_price` (`wholesale_price`),
  CONSTRAINT `fk_product_category` FOREIGN KEY (`category_id`) REFERENCES `product_category` (`id`),
  CONSTRAINT `fk_product_supplier` FOREIGN KEY (`supplier_id`) REFERENCES `supplier` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

该表设计具有以下技术亮点:

  1. 使用decimal(10,2)类型精确存储金额数据,避免浮点数精度问题
  2. 通过min_order_quantity字段实现批发场景特有的最小起订量控制
  3. 建立多列索引支持按分类、供应商、价格区间的联合查询
  4. 时间戳字段自动更新机制确保数据审计追踪

订单表结构设计

订单表采用主从表结构设计,主表存储订单基础信息,从表记录商品明细,这种设计支持一个订单包含多个商品项:

CREATE TABLE `order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `supplier_id` bigint(20) NOT NULL COMMENT '供应商ID',
  `total_amount` decimal(12,2) NOT NULL COMMENT '订单总金额',
  `payment_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '支付状态',
  `order_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '订单状态',
  `delivery_address` text NOT NULL COMMENT '配送地址',
  `contact_phone` varchar(20) NOT NULL COMMENT '联系电话',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user` (`user_id`),
  KEY `idx_supplier` (`supplier_id`),
  KEY `idx_status` (`order_status`),
  KEY `idx_create_time` (`create_time`),
  CONSTRAINT `fk_order_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `fk_order_supplier` FOREIGN KEY (`supplier_id`) REFERENCES `supplier` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';

CREATE TABLE `order_item` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) NOT NULL,
  `product_id` bigint(20) NOT NULL,
  `quantity` int(11) NOT NULL COMMENT '购买数量',
  `unit_price` decimal(10,2) NOT NULL COMMENT '成交单价',
  `subtotal` decimal(10,2) NOT NULL COMMENT '小计金额',
  PRIMARY KEY (`id`),
  KEY `idx_order` (`order_id`),
  KEY `idx_product` (`product_id`),
  CONSTRAINT `fk_order_item_order` FOREIGN KEY (`order_id`) REFERENCES `order` (`id`),
  CONSTRAINT `fk_order_item_product` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

订单系统的技术特色体现在:

  1. 订单编号使用业务无关的UUID生成,避免顺序编号的信息泄露风险
  2. 状态字段使用枚举类型,明确区分支付状态与订单流程状态
  3. 金额相关字段均采用decimal类型,确保计算精度
  4. 通过索引优化大幅提升订单查询效率

核心功能实现解析

商品管理模块

商品管理界面支持供应商自主维护商品信息,包括批量上架、库存调整、价格设置等功能: 商品管理

商品服务层实现了复杂的业务逻辑,包括库存校验、价格计算、状态同步等:

@Service
@Transactional
public class ProductServiceImpl implements ProductService {
    
    @Autowired
    private ProductMapper productMapper;
    
    @Autowired
    private InventoryMapper inventoryMapper;
    
    @Override
    public ProductVO getProductDetail(Long productId) {
        Product product = productMapper.selectById(productId);
        if (product == null) {
            throw new BusinessException("商品不存在");
        }
        
        // 构建商品详情视图对象
        ProductVO vo = new ProductVO();
        BeanUtils.copyProperties(product, vo);
        
        // 查询实时库存
        Integer availableStock = inventoryMapper.getAvailableStock(productId);
        vo.setAvailableStock(availableStock);
        
        // 计算批发折扣
        BigDecimal discount = calculateWholesaleDiscount(product.getWholesalePrice());
        vo.setDiscountRate(discount);
        
        return vo;
    }
    
    @Override
    @CacheEvict(value = "product", key = "#product.id")
    public void updateProduct(Product product) {
        // 验证数据完整性
        validateProductInfo(product);
        
        // 更新商品信息
        int result = productMapper.updateById(product);
        if (result == 0) {
            throw new BusinessException("商品更新失败");
        }
        
        // 记录操作日志
        logService.recordProductUpdate(product.getId(), "商品信息修改");
    }
    
    private void validateProductInfo(Product product) {
        if (StringUtils.isBlank(product.getName())) {
            throw new BusinessException("商品名称不能为空");
        }
        if (product.getWholesalePrice() == null || 
            product.getWholesalePrice().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("批发价必须大于0");
        }
        if (product.getMinOrderQuantity() <= 0) {
            throw new BusinessException("最小起订量必须大于0");
        }
    }
}

订单处理流程

订单生成流程涉及多个服务的协同操作,通过事务管理确保数据一致性:

@Service
public class OrderServiceImpl implements OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private OrderItemMapper orderItemMapper;
    
    @Autowired
    private ProductMapper productMapper;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(OrderCreateDTO createDTO) {
        // 1. 验证用户和收货地址
        User user = validateUser(createDTO.getUserId());
        validateDeliveryAddress(createDTO.getDeliveryAddress());
        
        // 2. 生成订单编号
        String orderNo = generateOrderNo();
        
        // 3. 处理订单项并计算总金额
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<OrderItem> items = new ArrayList<>();
        
        for (OrderItemDTO itemDTO : createDTO.getItems()) {
            Product product = productMapper.selectById(itemDTO.getProductId());
            if (product == null) {
                throw new BusinessException("商品不存在: " + itemDTO.getProductId());
            }
            
            // 验证库存
            inventoryService.checkStock(product.getId(), itemDTO.getQuantity());
            
            // 计算小计
            BigDecimal subtotal = product.getWholesalePrice()
                .multiply(new BigDecimal(itemDTO.getQuantity()));
            
            OrderItem item = new OrderItem();
            item.setProductId(product.getId());
            item.setQuantity(itemDTO.getQuantity());
            item.setUnitPrice(product.getWholesalePrice());
            item.setSubtotal(subtotal);
            items.add(item);
            
            totalAmount = totalAmount.add(subtotal);
        }
        
        // 4. 创建订单主记录
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setUserId(user.getId());
        order.setSupplierId(createDTO.getSupplierId());
        order.setTotalAmount(totalAmount);
        order.setDeliveryAddress(createDTO.getDeliveryAddress());
        order.setContactPhone(createDTO.getContactPhone());
        order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
        
        orderMapper.insert(order);
        
        // 5. 保存订单明细
        for (OrderItem item : items) {
            item.setOrderId(order.getId());
            orderItemMapper.insert(item);
            
            // 扣减库存
            inventoryService.deductStock(item.getProductId(), item.getQuantity());
        }
        
        return order;
    }
    
    private String generateOrderNo() {
        // 时间戳 + 随机数 + 用户ID哈希
        String timestamp = LocalDateTime.now().format(
            DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String random = String.valueOf(ThreadLocalRandom.current().nextInt(1000, 9999));
        return timestamp + random;
    }
}

用户订单管理界面提供了完整的订单生命周期视图: 订单管理

权限控制系统

系统采用基于角色的访问控制模型,不同角色拥有不同的操作权限。Spring拦截器负责权限验证:

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, Object handler) throws Exception {
        
        // 获取token
        String token = request.getHeader("Authorization");
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("token");
        }
        
        // 验证token有效性
        User user = userService.validateToken(token);
        if (user == null) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.getWriter().write("{\"code\":401,\"message\":\"未授权访问\"}");
            return false;
        }
        
        // 检查接口权限
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RequiredPermission permission = handlerMethod.getMethodAnnotation(
                RequiredPermission.class);
            
            if (permission != null && !hasPermission(user, permission.value())) {
                response.setStatus(HttpStatus.FORBIDDEN.value());
                response.getWriter().write("{\"code\":403,\"message\":\"权限不足\"}");
                return false;
            }
        }
        
        // 用户信息存入请求上下文
        RequestContext.setCurrentUser(user);
        return true;
    }
    
    private boolean hasPermission(User user, String permissionCode) {
        // 查询用户角色权限
        Set<String> permissions = userService.getUserPermissions(user.getId());
        return permissions.contains(permissionCode);
    }
}

管理员界面展示了完整的用户权限管理功能: 用户管理

数据持久层实现

MyBatis的Mapper接口和XML映射文件提供了灵活的数据访问能力,支持复杂的动态查询:

<!-- 商品动态查询映射 -->
<mapper namespace="com.agriculture.wholesale.mapper.ProductMapper">
    
    <resultMap id="ProductResultMap" type="Product">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="category_id" property="categoryId"/>
        <result column="supplier_id" property="supplierId"/>
        <result column="specification" property="specification"/>
        <result column="unit" property="unit"/>
        <result column="wholesale_price" property="wholesalePrice"/>
        <result column="retail_price" property="retailPrice"/>
        <result column="stock_quantity" property="stockQuantity"/>
        <result column="min_order_quantity" property="minOrderQuantity"/>
        <result column="shelf_status" property="shelfStatus"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>
    
    <select id="selectByCondition" parameterType="ProductQueryDTO" resultMap="ProductResultMap">
        SELECT p.*, c.name as category_name, s.company_name as supplier_name
        FROM product p
        LEFT JOIN product_category c ON p.category_id = c.id
        LEFT JOIN supplier s ON p.supplier_id = s.id
        <where>
            <if test="categoryId != null">
                AND p.category_id = #{categoryId}
            </if>
            <if test="supplierId != null">
                AND p.supplier_id = #{supplierId}
            </if>
            <if test="minPrice != null">
                AND p.wholesale_price >= #{minPrice}
            </if>
            <if test="maxPrice != null">
                AND p.wholesale_price <= #{maxPrice}
            </if>
            <if test="keyword != null and keyword != ''">
                AND (p.name LIKE CONCAT('%', #{keyword}, '%') 
                     OR p.specification LIKE CONCAT('%', #{keyword}, '%'))
            </if>
            <if test="shelfStatus != null">
                AND p.shelf_status = #{shelfStatus}
            </if>
        </where>
        ORDER BY 
        <choose>
            <when test="sortField == 'price'">
                p.wholesale_price ${sortOrder}
            </when>
            <when test="sortField == 'sales'">
                p.sales_volume ${sortOrder}
            </when>
            <when test="sortField == 'time'">
                p.create_time ${sortOrder}
            </when>
            <otherwise>
                p.update_time DESC
            </otherwise>
        </choose>
        LIMIT #{offset}, #{pageSize}
    </select>
    
    <update id="updateStock">
        UPDATE product 
        SET stock_quantity = stock_quantity - #{quantity},
            update_time = NOW()
        WHERE id = #{productId} 
        AND stock_quantity >= #{quantity}
    </update>
</mapper>

对应的Mapper接口定义:

@Repository
public interface ProductMapper extends Base
本文关键词
SSM框架水果蔬菜批发商城农产品批发数字化交易平台源码解析

上下篇

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