觉得有用的铁子们给个 star 就行了,求求你们啦😘😍
waynboot-mall 是一套全部开源的微商城项目,包含一个运营后台、h5 商城和后台接口。 实现了一个商城所需的首页展示、商品分类、商品详情、sku 详情、商品搜索、加入购物车、结算下单、订单状态流转、商品评论等一系列功能。 技术上基于 Springboot2.0,整合了 Redis 、RabbitMQ 、ElasticSearch 等常用中间件, 贴近生产环境实际经验开发而来不断完善、优化、改进中。
where goods_num - num >= 0
)# 1. 通过创建子线程继承 Callable 接口
Callable<List<Banner>> bannerCall = () -> iBannerService.list(new QueryWrapper<Banner>().eq("status", 0).orderByAsc("sort"));
# 2. 传入 Callable 的任务给 FutureTask
FutureTask<List<Banner>> bannerTask = new FutureTask<>(bannerCall);
# 3. 放入线程池执行
threadPoolTaskExecutor.submit(bannerTask);
# 4. 最后可以在外部通过 FutureTask 的 get 方法异步获取执行结果
List<Banner> list = bannerTask.get()
ElasticSearch
查询操作,查询包含搜索关键字并且是上架中的商品,在根据指定字段进行排序,最后分页返回SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
MatchQueryBuilder matchFiler = QueryBuilders.matchQuery("isOnSale", true);
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", keyword);
MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("keyword", keyword);
boolQueryBuilder.filter(matchFiler).should(matchQuery).should(matchPhraseQueryBuilder).minimumShouldMatch(1);
searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
// 按是否新品排序
if (isNew) {
searchSourceBuilder.sort(new FieldSortBuilder("isNew").order(SortOrder.DESC));
}
// 按是否热品排序
if (isHot) {
searchSourceBuilder.sort(new FieldSortBuilder("isHot").order(SortOrder.DESC));
}
// 按价格高低排序
if (isPrice) {
searchSourceBuilder.sort(new FieldSortBuilder("retailPrice").order("asc".equals(orderBy) ? SortOrder.ASC : SortOrder.DESC));
}
// 按销量排序
if (isSales) {
searchSourceBuilder.sort(new FieldSortBuilder("sales").order(SortOrder.DESC));
}
// 筛选新品
if (filterNew) {
MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isNew", true);
boolQueryBuilder.filter(filterQuery);
}
// 筛选热品
if (filterHot) {
MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isHot", true);
boolQueryBuilder.filter(filterQuery);
}
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.from((int) (page.getCurrent() - 1) * (int) page.getSize());
searchSourceBuilder.size((int) page.getSize());
List<JSONObject> list = elasticDocument.search("goods", searchSourceBuilder, JSONObject.class);
# 1. 定义金刚位跳转策略接口
public interface DiamondJumpType {
List<Goods> getGoods(Page<Goods> page, Diamond diamond);
Integer getType();
}
# 2. 定义策略实现类,并使用 @Component 注解注入 spring
@Component
public class CategoryStrategy implements DiamondJumpType {
@Autowired
private GoodsMapper goodsMapper;
@Override
public List<Goods> getGoods(Page<Goods> page, Diamond diamond) {
List<Long> cateList = Arrays.asList(diamond.getValueId());
return goodsMapper.selectGoodsListPageByl2CateId(page, cateList).getRecords();
}
@Override
public Integer getType() {
return JumpTypeEnum.CATEGORY.getType();
}
}
@Component
public class ColumnStrategy implements DiamondJumpType {
@Autowired
private IColumnGoodsRelationService iColumnGoodsRelationService;
@Autowired
private IGoodsService iGoodsService;
@Override
public List<Goods> getGoods(Page<Goods> page, Diamond diamond) {
List<ColumnGoodsRelation> goodsRelationList = iColumnGoodsRelationService.list(new QueryWrapper<ColumnGoodsRelation>()
.eq("column_id", diamond.getValueId()));
List<Long> goodsIdList = goodsRelationList.stream().map(ColumnGoodsRelation::getGoodsId).collect(Collectors.toList());
Page<Goods> goodsPage = iGoodsService.page(page, new QueryWrapper<Goods>().in("id", goodsIdList).eq("is_on_sale", true));
return goodsPage.getRecords();
}
@Override
public Integer getType() {
return JumpTypeEnum.COLUMN.getType();
}
}
# 3. 定义策略上下文,通过构造器注入 spring,定义 map 属性,通过 key 获取对应策略实现类
@Component
public class DiamondJumpContext {
private Map<Integer, DiamondJumpType> map = new HashMap<>();
/**
* 由 spring 自动注入 DiamondJumpType 子类
*
* @param diamondJumpTypes 金刚位跳转类型集合
*/
public DiamondJumpContext(List<DiamondJumpType> diamondJumpTypes) {
for (DiamondJumpType diamondJumpType : diamondJumpTypes) {
map.put(diamondJumpType.getType(), diamondJumpType);
}
}
public DiamondJumpType getInstance(Integer jumpType) {
return map.get(jumpType);
}
}
# 4.使用
@Autowired
private DiamondJumpContext diamondJumpContext;
@Test
public void test(){
DiamondJumpType diamondJumpType = diamondJumpContext.getInstance(1);
}
|-- waynboot-admin-api // 运营后台 api 模块,提供后台项目 api 接口
|-- waynboot-common // 通用模块,包含项目核心基础类
|-- waynboot-data // 数据模块,通用中间件数据访问
| |-- waynboot-data-redis // redis 访问配置模块
| |-- waynboot-data-elastic // elastic 访问配置模块
|-- waynboot-generator // 代码生成模块
|-- waynboot-message-consumer // 消费者模块,处理订单消息和邮件消息
|-- waynboot-message-core // 消费者核心模块,队列、交换机配置
|-- waynboot-mobile-api // h5 商城 api 模块,提供 h5 商城 api 接口
|-- pom.xml // maven 父项目依赖,定义子项目依赖版本
|-- ...
# 1. 克隆项目
git clone [email protected]:wayn111/waynboot-mall.git
# 2. 导入项目依赖
将 waynboot-mall 目录用 idea 打开,导入 maven 依赖
# 3. 安装 Mysql8.0+、Redis3.0+、RabbitMQ3.0+、ElasticSearch7.0+到本地
# 4. 导入 sql 文件
在项目根目录下,找到`wayn_shop_*.sql`文件,新建 mysql 数据库 wayn_shop,导入其中
# 5. 修改 Mysql 、Redis 、RabbitMQ 、Elasticsearch 连接配置
修改`application-dev.yml`以及`application.yml`文件中数据连接配置相关信息
# 6. 启动项目
后台 api:
进入 waynboot-admin-api 子项目,找到 AdminApplication 文件,右键`run AdminApplication`,启动后台项目
h5 商城 api:
进入 waynboot-mobile-api 子项目,找到 MobileApplication 文件,右键`run MobileApplication`,启动 h5 商城项目
演示地址: http://www.wayn.ltd
商城登陆 | 商城注册 |
商城首页 | 商城搜索 |
搜索结果展示 | 金刚位跳转 |
商品分类 | 商品详情 |
商品 sku 选择 | 购物车查看 |
确认下单 | 选择支付方式 |
商城我的页面 | 我的订单列表 |
添加商品评论 | 查看商品评论 |
后台登陆 | 后台首页 |
后台会员管理 | 后台评论管理 |
后台地址管理 | 后台添加商品 |
后台商品管理 | 后台 banner 管理 |
后台订单管理 | 后台分类管理 |
后台金刚区管理 | 后台栏目管理 |
1
wayn111 OP 欢迎大家提出问题😁,帮助这个项目改进优化
|
2
xuanbg 2021-05-16 04:53:30 +08:00
商品库存其实可以分三种,我的策略是:
1 、卖完不能采购后发货。无库存不能下单,下单扣预警库存,支付扣实际库存; 2 、卖完可以采购后发货。无预警库存,支付扣实际库存; 3 、虚拟商品,无库存。随便卖,不扣库存。 |
3
wayn111 OP @xuanbg 感谢回复,不同的库存策略肯定是适应不同的业务场景,在我们的场景里,用户下单时到店家发货需要保证短时效性,下单库存不足直接返回失败,同时发送企业微信群报警消息,由运营联系商家是否补货
|
4
xuanbg 2021-05-16 10:44:18 +08:00
@wayn111 我定义的 3 种库存属性已经涵盖了所有的场景需求。如你们这种需求,你只需要把需要及时发货的商品的库存属性设置为第一种就行了。同时,并不妨碍第二种、第三种库存属性的商品同时存在啊。
|
6
xuanbg 2021-05-16 14:35:27 +08:00 1
不是啊,假设你商城中有 A 、B 、C3 种商品:
A 商品你需要在用户下单后立即发货,所以不能超卖。在库存下降到保留数后就不能下单了,以免无法发货以及影响售后换货。这种就设定 1,下单扣预警库存。 B 商品由于有一定的价格优势,采购周期很短,发货稍慢一点用户也不会退单,或者厂家可以代发货,那就可以允许超卖。只在支付后扣实际库存,而且允许库存为负数。卖就是了,能发货的立即发货,不够的话采购后发货。 C 商品是虚拟商品,压根不存在库存这个概念。 那么,这三种商品你能同时卖吗?你的设计不能,我的设计就可以。 |
7
xuanbg 2021-05-16 14:39:32 +08:00
对于商家来说,大部分都是 B 商品,A 可能是一些尾货,C 不需要多谈。
|
8
bsg1992 2021-05-17 09:20:00 +08:00
下单减库存 和支付减库存做成配置项就可以了。减库存这个也不是啥难点吧。。。。
|