基于SSM框架的多商户在线点餐平台 - 源码深度解析

JavaJavaScriptMavenHTMLCSSSSM框架MySQL
2026-02-0713 浏览

文章摘要

本系统是基于SSM(Spring+SpringMVC+MyBatis)框架构建的多商户在线点餐平台,旨在为餐饮行业提供一个集顾客点餐与商户管理于一体的综合性解决方案。其核心业务价值在于解决了传统餐饮模式中菜单更新不及时、高峰期订单处理效率低下、多商户数据混杂难以统一管理等痛点。平台通过将商户端与顾客...

随着餐饮行业数字化转型的加速,传统点餐模式面临诸多挑战:菜单更新滞后导致信息不一致,高峰期订单处理效率低下,多商户数据管理混乱等。针对这些痛点,我们设计并实现了一个基于SSM框架的企业级多商户餐饮管理平台,该平台通过技术手段有效解决了上述问题,为餐饮行业提供了完整的数字化解决方案。

系统架构与技术栈

该平台采用经典的三层架构设计,确保系统的高可维护性和扩展性。技术栈选择上,后端基于成熟的SSM框架组合:Spring负责业务逻辑的依赖注入和事务管理,SpringMVC处理Web层请求路由,MyBatis作为数据持久层框架。前端采用JSP结合jQuery实现动态页面交互,数据库选用MySQL存储业务数据,项目构建工具使用Maven。

Spring框架通过声明式事务管理确保订单创建、库存更新等核心操作的数据一致性。SpringMVC的拦截器机制实现了统一的用户权限验证,而MyBatis的动态SQL功能支持复杂的多条件查询,如按商户、分类、价格范围筛选菜品。

<!-- Spring事务配置示例 -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="createOrder" propagation="REQUIRED" 
                   rollback-for="Exception"/>
        <tx:method name="updateInventory" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

数据库设计亮点分析

订单信息表(ordermsg)的设计优化

订单表的设计体现了对复杂业务场景的深度考量。ddno字段采用varchar类型存储订单号,支持灵活的编号规则。为提升查询性能,应在该字段上建立唯一索引:

CREATE UNIQUE INDEX idx_ordermsg_ddno ON ordermsg(ddno);

表结构中的fkstatus(付款状态)和shstatus(审核状态)字段采用分离设计,便于独立跟踪订单的不同生命周期阶段。isdd字段标识是否为订单记录,fid字段支持订单拆分场景,这种设计满足了大型餐饮集团复杂的订单处理需求。

订单表结构

商品信息表(product)的扩展性设计

商品表通过fidsid字段实现多级分类管理,支持灵活的商户商品组织方式。价格字段使用decimal(10,2)类型,确保金额计算的精确性:

ALTER TABLE product ADD INDEX idx_product_fid_sid (fid, sid);
ALTER TABLE product ADD INDEX idx_product_issj (issj);

issj(是否上架)和istj(是否特价)字段采用标记位设计,便于快速筛选商品。saver字段记录操作人员,满足审计要求。这种设计支持商户快速调整商品状态,应对促销活动等业务场景。

地址表(address)的关联设计

地址表通过memberid与用户表关联,ismr字段标识默认地址,优化用户下单体验。采用逻辑删除设计(delstatus字段),保留历史数据的同时维护数据一致性。

核心功能实现详解

多商户商品管理模块

商品管理模块支持商户自主管理菜品信息,包括上传、编辑、上下架等操作。Controller层通过注解方式实现请求映射:

@Controller
@RequestMapping("/merchant")
public class ProductController extends BaseController {
    
    @Resource
    private ProductDAO productDAO;
    
    @RequestMapping("/productList")
    public String productList(HttpServletRequest request) {
        String merchantId = getCurrentMerchantId(request);
        Map<String, Object> params = new HashMap<>();
        params.put("saver", merchantId);
        params.put("delstatus", "0");
        
        List<Product> products = productDAO.selectByMap(params);
        request.setAttribute("products", products);
        return "merchant/product_list";
    }
    
    @RequestMapping("/saveProduct")
    @ResponseBody
    public Map<String, Object> saveProduct(Product product, 
                                         HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        try {
            product.setSaver(getCurrentMerchantId(request));
            product.setDelstatus("0");
            productDAO.insert(product);
            result.put("success", true);
            result.put("message", "商品保存成功");
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "保存失败:" + e.getMessage());
        }
        return result;
    }
}

对应的MyBatis映射文件实现了动态SQL查询:

<!-- ProductMapper.xml -->
<select id="selectByMap" parameterType="map" resultType="com.entity.Product">
    SELECT * FROM product 
    <where>
        <if test="saver != null and saver != ''">
            AND saver = #{saver}
        </if>
        <if test="delstatus != null and delstatus != ''">
            AND delstatus = #{delstatus}
        </if>
        <if test="productname != null and productname != ''">
            AND productname LIKE CONCAT('%', #{productname}, '%')
        </if>
    </where>
    ORDER BY id DESC
</select>

商品管理界面

智能购物车与订单生成

购物车模块采用Session存储临时数据,支持多商户商品合并下单。前端通过jQuery实现动态增减数量:

// 购物车功能前端实现
function updateCart(productId, quantity) {
    $.ajax({
        url: 'updateCart.do',
        type: 'POST',
        data: {
            productId: productId,
            quantity: quantity
        },
        success: function(result) {
            if (result.success) {
                updateCartUI(result.cartData);
                calculateTotal();
            }
        }
    });
}

function calculateTotal() {
    var total = 0;
    $('.cart-item').each(function() {
        var price = parseFloat($(this).find('.price').text());
        var quantity = parseInt($(this).find('.quantity').val());
        total += price * quantity;
    });
    $('#totalAmount').text(total.toFixed(2));
}

后端订单生成服务包含完整的业务逻辑验证:

@Service
public class OrderService {
    
    @Resource
    private OrderMsgDAO orderMsgDAO;
    @Resource
    private ProductDAO productDAO;
    
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> createOrder(OrderMsg order, 
                                         List<CartItem> cartItems) {
        Map<String, Object> result = new HashMap<>();
        
        // 生成订单号
        String ddno = generateOrderNo();
        order.setDdno(ddno);
        order.setSavetime(new Date().toString());
        
        double total = 0;
        for (CartItem item : cartItems) {
            // 验证库存
            Product product = productDAO.findById(item.getProductId());
            if (product == null || "0".equals(product.getIssj())) {
                throw new RuntimeException("商品已下架:" + item.getProductName());
            }
            
            // 计算小计
            double subtotal = product.getPrice().doubleValue() * item.getQuantity();
            total += subtotal;
            
            // 创建订单明细
            OrderDetail detail = new OrderDetail();
            detail.setDdno(ddno);
            detail.setProductid(item.getProductId());
            detail.setNum(item.getQuantity());
            detail.setTotal(subtotal);
            orderDetailDAO.insert(detail);
        }
        
        order.setTotal(total);
        order.setFkstatus("未支付");
        order.setShstatus("待审核");
        orderMsgDAO.insert(order);
        
        result.put("success", true);
        result.put("orderNo", ddno);
        result.put("totalAmount", total);
        return result;
    }
    
    private String generateOrderNo() {
        return "DD" + System.currentTimeMillis() + 
               String.format("%04d", (int)(Math.random() * 10000));
    }
}

权限管理与多商户数据隔离

通过自定义拦截器实现商户数据隔离,确保每个商户只能访问自己的数据:

@Component
public class MerchantInterceptor extends HandlerInterceptorAdapter {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        String merchantId = (String) request.getSession()
                                .getAttribute("merchantId");
        if (merchantId == null) {
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        }
        
        // 验证数据访问权限
        String requestUri = request.getRequestURI();
        if (requestUri.contains("/merchant/")) {
            // 检查当前商户是否有权访问该数据
            if (!hasDataAccessPermission(merchantId, request)) {
                response.sendError(403, "无权访问该数据");
                return false;
            }
        }
        return true;
    }
}

实体模型设计

系统采用标准的JavaBean实体类设计,与数据库表结构严格对应。以商品实体为例:

package com.entity;

import java.math.BigDecimal;

public class Product {
    private Integer id;
    private String productno;
    private String productname;
    private String filename;
    private BigDecimal price;
    private BigDecimal tprice;
    private String fid;
    private String sid;
    private String content;
    private String delstatus;
    private String issj;
    private String istj;
    private String saver;
    private String productid;
    private String leibie;
    
    // Getter和Setter方法
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    
    public String getProductno() { return productno; }
    public void setProductno(String productno) { this.productno = productno; }
    
    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }
    
    // 其他getter/setter方法...
    
    @Override
    public String toString() {
        return "Product [id=" + id + ", productname=" + productname + 
               ", price=" + price + ", issj=" + issj + "]";
    }
}

这种设计保证了数据在不同层之间传输的一致性,同时通过重写toString()方法便于调试和日志记录。

功能展望与优化方向

1. 引入Redis缓存提升性能

当前系统在高并发场景下可能存在数据库访问瓶颈。引入Redis作为缓存层,可以显著提升热点数据的读取速度:

@Service
public class ProductServiceWithCache {
    
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private ProductDAO productDAO;
    
    private static final String PRODUCT_KEY_PREFIX = "product:";
    private static final long CACHE_EXPIRE = 3600; // 1小时
    
    public Product getProductById(Integer id) {
        String key = PRODUCT_KEY_PREFIX + id;
        Product product = (Product) redisTemplate.opsForValue().get(key);
        if (product == null) {
            product = productDAO.findById(id);
            if (product != null) {
                redisTemplate.opsForValue().set(key, product, CACHE_EXPIRE, 
                    TimeUnit.SECONDS);
            }
        }
        return product;
    }
    
    public void updateProduct(Product product) {
        // 先更新数据库
        productDAO.update(product);
        // 再删除缓存
        String key = PRODUCT_KEY_PREFIX + product.getId();
        redisTemplate.delete(key);
    }
}

2. 消息队列实现异步订单处理

引入RabbitMQ或Kafka处理订单创建后的后续操作,如发送通知、更新库存等,提升系统响应速度:

@Component
public class OrderMessageProducer {
    
    @Resource
    private AmqpTemplate rabbitTemplate;
    
    public void sendOrderCreatedMessage(OrderMsg order) {
        OrderMessage message = new OrderMessage();
        message.setOrderId(order.getId());
        message.setOrderNo(order.getDdno());
        message.setTotalAmount(order.getTotal());
        message.setTimestamp(System.currentTimeMillis());
        
        rabbitTemplate.convertAndSend("order.exchange", 
                                    "order.created", message);
    }
}

@Component
public class OrderMessageConsumer {
    
    @RabbitListener(queues = "order.created.queue")
    public void handleOrderCreated(OrderMessage message) {
        // 异步处理:发送短信通知、更新统计数据等
        smsService.sendOrderConfirm(message.getOrderNo());
        statisticsService.updateDailySales(message);
    }
}

3. 微服务架构改造

将单体应用拆分为微服务,提升系统可维护性和可扩展性:

# docker-compose.yml 示例
version: '3.8'
services:
  user-service:
    image: merchant-platform/user-service:1.0
    ports:
      - "8081:8080"
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/user_db
      
  product-service:
    image: merchant-platform/product-service:1.0
    ports:
      - "8082:8080"
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/product_db
      
  order-service:
    image: merchant-platform/order-service:1.0
    ports:
      - "8083:8080"
    depends_on:
      - user-service
      - product-service

4. 移动端适配与PWA支持

开发响应式前端,支持PWA(渐进式Web应用)特性,提升移动端用户体验:

// 注册Service Worker
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js')
        .then(registration => {
            console.log('SW registered: ', registration);
        })
        .catch(registrationError => {
            console.log('SW registration failed: ', registrationError);
        });
}

// 实现离线缓存策略
const CACHE_NAME = 'merchant-platform-v1';
const urlsToCache = [
    '/',
    '/styles/main.css',
    '/script/app.js',
    '/images/logo.png'
];

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(cache => cache.addAll(urlsToCache))
    );
});

5. 智能推荐算法集成

基于用户历史订单数据,实现个性化菜品推荐:

@Service
public class RecommendationService {
    
    public List<Product> recommendProducts(String memberId, int limit) {
        // 基于协同过滤的推荐算法
        List<OrderHistory> history = orderDAO.findUserOrderHistory(memberId);
        Map<String, Double> userPreferences = analyzePreferences(history);
        
        return productDAO.findSimilarProducts(userPreferences, limit);
    }
    
    private Map<String, Double> analyzePreferences(List<OrderHistory> history) {
        // 分析用户口味偏好
        return history.stream()
            .collect(Collectors.groupingBy(OrderHistory::getCategory,
                     Collectors.averagingDouble(OrderHistory::getQuantity)));
    }
}

该企业级餐饮管理平台通过严谨的架构设计和深入的技术实现,为餐饮行业提供了完整的数字化解决方案。系统具备良好的扩展性和维护性,为未来的功能演进奠定了坚实的技术基础。随着技术的不断发展,通过引入缓存、消息队列、微服务等现代化技术手段,平台将能够更好地满足餐饮行业日益增长的业务需求。

本文关键词
SSM框架多商户系统在线点餐平台源码解析数据库设计

上下篇

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