基于SSM框架的多商户在线点餐平台 - 源码深度解析
随着餐饮行业数字化转型的加速,传统点餐模式面临诸多挑战:菜单更新滞后导致信息不一致、高峰期订单处理效率低下、多商户数据管理混乱等。针对这些行业痛点,我们设计并实现了一个基于SSM框架的企业级多商户餐饮管理平台。该平台通过技术创新有效解决了上述问题,为餐饮行业提供了完整的数字化解决方案。
系统架构与技术栈选型
架构设计理念
该平台采用经典的三层架构设计(表示层、业务逻辑层、数据访问层),确保系统的高可维护性和扩展性。这种分层架构使得各层职责清晰,便于团队协作开发和后期维护。
核心技术栈组成
- 后端框架:基于成熟的SSM框架组合
- Spring:负责业务逻辑的依赖注入和事务管理
- SpringMVC:处理Web层请求路由和控制器管理
- MyBatis:作为数据持久层框架,提供灵活的SQL映射
- 前端技术:JSP结合jQuery实现动态页面交互
- 数据库:MySQL 5.7+,存储业务数据
- 项目构建:Maven 3.6+,管理项目依赖
框架优势体现
Spring框架通过声明式事务管理确保订单创建、库存更新等核心操作的数据一致性。SpringMVC的拦截器机制实现了统一的用户权限验证,而MyBatis的动态SQL功能支持复杂的多条件查询,如按商户、分类、价格范围筛选菜品。
<!-- Spring事务配置示例 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<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类型存储,支持自定义编号规则 - 性能优化:为提升查询性能,在关键字段上建立索引
- 状态分离设计:
fkstatus(付款状态)和shstatus(审核状态)字段独立,便于跟踪订单生命周期
-- 订单号唯一索引
CREATE UNIQUE INDEX idx_ordermsg_ddno ON ordermsg(ddno);
-- 复合索引提升查询效率
CREATE INDEX idx_ordermsg_status ON ordermsg(fkstatus, shstatus);
表结构中的isdd字段标识是否为订单记录,fid字段支持订单拆分场景,这种设计满足了大型餐饮集团复杂的订单处理需求。

商品信息表(product)的扩展性设计
商品表设计充分考虑了多商户环境下的扩展需求:
- 多级分类管理:通过
fid和sid字段实现灵活的商品分类体系 - 精确金额处理:价格字段使用decimal(10,2)类型,确保金额计算的精确性
- 状态标记设计:
issj(是否上架)和istj(是否特价)字段便于快速筛选 - 操作审计支持:
saver字段记录操作人员,满足合规要求
-- 商品表索引优化
ALTER TABLE product ADD INDEX idx_product_fid_sid (fid, sid);
ALTER TABLE product ADD INDEX idx_product_issj (issj);
ALTER TABLE product ADD INDEX idx_product_merchant (saver);
地址表(address)的关联设计
地址表通过精细的字段设计优化用户体验:
- 用户关联:
memberid字段与用户表建立外键关联 - 默认地址标识:
ismr字段标识默认地址,提升下单效率 - 逻辑删除:
delstatus字段实现软删除,保留历史数据
核心功能实现详解
多商户商品管理模块
商品管理模块支持商户自主管理菜品信息,采用MVC模式实现前后端分离:
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");
product.setCreateTime(new Date());
productDAO.insert(product);
result.put("success", true);
result.put("message", "商品保存成功");
} catch (Exception e) {
logger.error("商品保存失败", 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>
<if test="issj != null and issj != ''">
AND issj = #{issj}
</if>
</where>
ORDER BY id DESC
<if test="start != null and limit != null">
LIMIT #{start}, #{limit}
</if>
</select>

智能购物车与订单生成
前端交互实现
购物车模块采用Session存储临时数据,支持多商户商品合并下单:
/**
* 购物车功能前端实现
*/
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();
} else {
showMessage(result.message);
}
},
error: function(xhr, status, error) {
showMessage('网络错误,请重试');
}
});
}
/**
* 计算购物车总金额
*/
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));
}
/**
* 实时验证库存
*/
function validateStock(productId, quantity) {
return $.ajax({
url: 'checkStock.do',
type: 'GET',
data: {productId: productId, quantity: quantity},
async: false
}).responseJSON;
}
后端订单服务实现
@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<>();
try {
// 1. 验证库存
for (CartItem item : cartItems) {
Product product = productDAO.selectById(item.getProductId());
if (product.getStock() < item.getQuantity()) {
throw new RuntimeException("商品[" + product.getProductname() + "]库存不足");
}
}
// 2. 生成订单号
String orderNo = generateOrderNo();
order.setDdno(orderNo);
order.setCreateTime(new Date());
// 3. 保存订单主信息
orderMsgDAO.insert(order);
// 4. 更新库存
for (CartItem item : cartItems) {
productDAO.updateStock(item.getProductId(), item.getQuantity());
}
result.put("success", true);
result.put("orderNo", orderNo);
result.put("message", "订单创建成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "订单创建失败:" + e.getMessage());
// 事务回滚由@Transactional自动处理
}
return result;
}
/**
* 生成唯一订单号
*/
private String generateOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
Random random = new Random();
return "DD" + timestamp + random.nextInt(1000);
}
}
性能优化与安全考虑
数据库优化策略
- 索引优化:在频繁查询的字段上建立合适的索引
- 查询优化:使用分页查询避免大数据量查询
- 连接池配置:使用Druid连接池管理数据库连接
安全防护措施
- SQL注入防护:使用MyBatis的参数绑定功能
- XSS防护:对用户输入进行过滤和转义
- 权限控制:基于拦截器的统一权限验证
- 事务安全:关键操作使用事务保证数据一致性
该多商户在线点餐平台通过合理的架构设计和精细的技术实现,为餐饮行业提供了稳定、高效、易扩展的数字化解决方案,具有良好的市场应用前景。