外卖O2O融合平台开发实践:商品上架、分类管理与搜索功能实现
一、业务场景与开发难点
外卖O2O平台和传统电商最大差异在于商品归属于线下门店,商品上下架、库存、价格都和门店强绑定,同时用户搜索带有地域约束,只能检索当前配送范围内的商家商品。
在中小同城平台开发中,常见问题:分类层级混乱、多门店商品同步更新慢、搜索只能精确匹配、热门关键词查询拖垮数据库。因此整套模块需要兼顾商家后台易用性、数据一致性、检索性能,优先轻量化方案,支持后续平滑扩容。
二、模块整体架构设计
采用SpringBoot + MySQL + Redis + Elasticsearch的组合方案。
MySQL存储门店、分类、商品基础数据、SKU规格、价格、库存;
Redis缓存分类树、门店热销商品列表、高频搜索结果,减轻数据库压力;
ES负责商品全文检索,支持商品名称、标签、简介模糊检索,同时叠加门店地理位置、营业状态、商品上下架状态过滤;
前端使用UniApp开发用户小程序、商家后台,商家在后台完成分类维护、商品新增、规格配置、图片上传,变更后触发消息,同步更新缓存和检索索引。
三、商品分类管理模块实现
分类支持两级结构:一级大类(主食、饮品、小吃)、二级子类(米饭、奶茶、炸串),分类绑定门店,支持排序、启用/停用。分类删除时增加校验,存在关联商品的分类不允许直接删除,避免数据孤立。
@Service
public class CategoryService {
@Resource
private CategoryMapper categoryMapper;
@Resource
private GoodsMapper goodsMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 删除分类
public void removeCategory(Long categoryId) {
LambdaQueryWrapper<Goods> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Goods::getCategoryId, categoryId);
Long count = goodsMapper.selectCount(wrapper);
if(count > 0){
throw new RuntimeException("该分类下存在商品,无法删除");
}
categoryMapper.deleteById(categoryId);
// 删除后清理分类缓存
redisTemplate.delete("goods:category:tree");
}
}
分类树预加载到Redis,用户端打开商家页面优先读取缓存,只有分类发生新增、修改、删除操作时主动失效缓存,减少频繁查库。
四、商品上架与SKU规格模块开发
商品上架信息包含基础信息、多规格SKU、图片、标签、售卖状态、库存、售价、原价。同一个商品可以配置多个SKU,例如奶茶分为大杯、中杯,不同规格独立维护库存和价格。
上架核心校验规则:商品必须归属有效分类、价格大于0、库存不能为负数;商家提交保存成功后,异步更新ES检索索引,保证搜索库数据同步。
为防止大流量下索引频繁刷新,采用异步消息队列做索引更新,同步失败单独记录重试日志,避免阻塞商家提交操作。
五、商品搜索功能实现逻辑
搜索分为两个阶段:关键词检索 + 业务过滤排序。
检索阶段:ES根据关键词分词匹配商品名称、标签;
过滤阶段:过滤不在配送范围、门店打烊、商品停售的商品;
排序策略:默认按距离优先+销量权重,支持按价格升序、好评优先切换。
@Service
public class GoodsSearchService {
@Resource
private ElasticsearchRestTemplate elasticsearchRestTemplate;
public Page<GoodsDoc> searchGoods(String keyword, Long shopId, int pageNum, int pageSize){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if(StringUtils.isNotBlank(keyword)){
boolQuery.must(QueryBuilders.matchQuery("goodsName", keyword));
}
boolQuery.filter(QueryBuilders.termQuery("shopId", shopId));
boolQuery.filter(QueryBuilders.termQuery("saleStatus", 1));
builder.withQuery(boolQuery);
builder.withPageable(PageRequest.of(pageNum-1, pageSize));
SearchHits<GoodsDoc> hits = elasticsearchRestTemplate.search(builder.build(), GoodsDoc.class);
return convertPage(hits, pageNum, pageSize);
}
}
中小平台初期订单量不大时,也可以先用MySQL全文索引做兜底方案,减少ES运维成本,业务增长后再切换。
六、缓存策略与数据一致性处理
热点数据缓存清单:门店分类树、门店商品列表、热门搜索词。
缓存更新策略:商品新增、修改、下架后,主动删除对应门店商品缓存;ES索引采用异步更新,允许短暂数据不一致,最终保证搜索结果和主库对齐。
同时增加兜底定时任务,凌晨低峰期全量同步一次商品索引,修复消息丢失导致的索引脏数据。
七、权限与商家隔离机制
不同商家只能管理自己的分类和商品,接口层增加商家ID校验,防止越权操作。商家后台提供批量上下架、批量调价功能,适合门店活动快速改价。所有商品变更记录操作日志,记录操作人、变更内容、变更时间,方便对账和问题追溯。
八、总结
外卖O2O平台商品模块,核心是解决门店隔离、分类维护、SKU规格管理以及地域化商品搜索。通过MySQL承载基础商品数据,Redis缓存分类和热门商品减轻数据库压力,ES实现关键词全文检索,异步机制保障索引同步。这套方案适配中小同城O2O平台,商家端操作简单,同时具备良好的扩展能力;在业务规模较小时,可简化检索组件降低运维成本,随着商家数量和订单上涨,再逐步优化检索排序、分词、推荐权重等能力,支撑平台持续迭代。