一套前后台全部开源的H5商城送给大家
Posted waynaqua
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一套前后台全部开源的H5商城送给大家相关的知识,希望对你有一定的参考价值。
博主给大家推荐一套全部开源的H5电商项目waynboot-mall。由博主在2020年开发至今,已有三年之久。那时候网上很多的H5商城项目都是半开源版本,要么没有H5前端代码,要么需要加群咨询,属实恶心。于是博主决定自己开发一套完整的移动端H5商城,包含一个管理后台、一个前台H5商城、一套后端接口。项目地址如下:
- H5商城前端代码:https://github.com/wayn111/waynboot-mobile
- 运营后台前端代码:https://github.com/wayn111/waynboot-admin
- 后端接口代码:https://github.com/wayn111/waynboot-mall
欢迎大家关注这个项目,点个Star让更多的人了解到这个项目。
一、简介
waynboot-mall是一套全部开源的微商城项目,实现了一个商城所需的首页展示、商品分类、商品详情、sku组合、商品搜索、购物车、结算下单、订单状态流转、商品评论等一系列功能。
技术上基于最新得Spring Boot3.0、Jdk17,整合了Redis、RabbitMQ、ElasticSearch等常用中间件,
贴近生产环境实际经验开发而来。
二、技术特点
- 订单金额计算使用BigDeciaml类型,支持小数点后两位
- 支持微信内JsApi支付、H5网页支付
- 商城接口代码清晰、注释完善、模块拆分合理
- 使用Spring-Security进行访问权限控制
- 使用jwt进行接口授权验证
- ORM层使用Mybatis Plus提升开发效率
- 添加全局异常处理器,统一异常处理
- 使用Spring Boot admin进行服务监控
- 集成七牛云存储配置,支持上传文件至七牛获取cdn下载链接
- 集成常用邮箱配置,方便发送邮件
- 添加策略模式使用示例,优化首页金刚区跳转逻辑
- 拆分出通用的数据访问模块,统一Redis & Elastic配置与访问
- 使用Elasticsearch高级客户端依赖对Elasticsearch进行操作
- 支持商品数据同步Elasticsearch操作以及中文分词搜索
- RabbitMQ生产者发送消息采用异步confirm模式,消费者消费消息时需手动确认确保消息不丢失
- 下单处理过程引入RabbitMQ,异步生成订单记录,提高系统下单处理能力
三、商城设计
文项目目录
|-- waynboot-monitor // 监控模块
|-- 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父项目依赖,定义子项目依赖版本
|-- ...
技术亮点
2.1 库存扣减
库存扣减操作是在下单操作扣减还是在支付成功时扣减?(ps:扣减库存使用乐观锁机制 where goods_num - num >= 0
)
- 下单时扣减,这个方案属于实时扣减,当有大量下单请求时,由于订单数小于请求数,会发生下单失败,但是无法防止短时间大量恶意请求占用库存,
造成普通用户无法下单 - 支付成功扣减,这个方案可以预防恶意请求占用库存,但是会存在多个请求同时下单后,在支付回调中扣减库存失败,导致订单还是下单失败并且还要退还订单金额(这种请求就是订单数超过了库存数,无法发货,影响用户体验)
- 还是下单时扣减,但是对于未支付订单设置一个超时过期机制,比如下单时库存减一,生成订单后,对于未在15分钟内完成支付的订单,
自动取消超期未支付订单并将库存加一,该方案基本满足了大部分使用场景 - 针对大流量下单场景,比如一分钟内五十万次下单请求,可以通过设置虚拟库存的方式减少下单接口对数据库的访问。具体来说就是把商品库存缓存到redis中,
下单时配合lua脚本原子的get和decr商品库存数量(这一步就拦截了大部分请求),执行成功后在扣减实际库存
2.2 首页查询
首页商品展示接口利用多线程技术进行查询优化,将多个sql语句的排队查询变成异步查询,接口时长只跟查询时长最大的sql查询挂钩
// 使用CompletableFuture异步查询
List<CompletableFuture<Void>> list = new ArrayList<>();
CompletableFuture<Void> f1 = CompletableFuture.supplyAsync(() -> iBannerService.list(Wrappers.lambdaQuery(Banner.class).eq(Banner::getStatus, 0).orderByAsc(Banner::getSort)), homeThreadPoolTaskExecutor).thenAccept(data ->
String key = "bannerList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
success.add(key, data);
);
CompletableFuture<Void> f2 = CompletableFuture.supplyAsync(() -> iDiamondService.list(Wrappers.lambdaQuery(Diamond.class).orderByAsc(Diamond::getSort).last("limit 10")), homeThreadPoolTaskExecutor).thenAccept(data ->
String key = "categoryList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
success.add(key, data);
);
list.add(f1);
list.add(f2);
// 主线程等待子线程执行完毕
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();
2.3 中文分词搜索
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);
2.4 订单编号
订单编号生成规则:秒级时间戳 + 加密用户ID + 今日第几次下单
- 秒级时间戳:时间递增保证唯一性
- 加密用户ID:加密处理,返回用户ID6位数字,可以防并发访问,同一秒用户不会产生2个订单
- 今日第几次下单:便于运营查询处理用户当日订单
/**
* 返回订单编号,生成规则:秒级时间戳 + 加密用户ID + 今日第几次下单
*
* @param userId 用户ID
* @return 订单编号
*/
public static String generateOrderSn(Long userId)
long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
return now + encryptUserId(String.valueOf(userId), 6) + countByOrderSn(userId);
/**
* 计算该用户今日内第几次下单
*
* @param userId 用户ID
* @return 该用户今日第几次下单
*/
public static int countByOrderSn(Long userId)
IOrderService orderService = SpringContextUtil.getBean(IOrderService.class);
return orderService.count(new QueryWrapper<Order>().eq("user_id", userId)
.gt("create_time", LocalDate.now())
.lt("create_time", LocalDate.now().plusDays(1)));
/**
* 加密用户ID,返回num位字符串
*
* @param userId 用户ID
* @param num 长度
* @return num位加密字符串
*/
private static String encryptUserId(String userId, int num)
return String.format("%0" + num + "d", Integer.parseInt(userId) + 1);
2.5 异步下单
下单流程处理过程,通过rabbitMQ异步生成订单,提高系统下单处理能力
- 用户点击提交订单按钮,后台生成订单编号和订单金额跳转到订单支付页面,并将订单编号等信息发送rabbitMQ消息(生成订单编号,还未生成订单)
- 订单消费者接受到订单消息后,获取订单编号生成订单记录(订单创建成功,用户待支付)
- 下单页面,前端根据订单编号轮询订单接口,订单已创建则跳转支付页面,否则提示下单失败(订单创建失败)
- 支付页面,用户点击支付按钮时,后台调用微信/支付宝下单接口后,前端唤醒微信/支付宝支付,用户输入密码
- 用户支付完成后在微信/支付宝下回调通知里更新订单状态为已支付(订单已支付)
- 用户支付完成后,返回支付状态查看页面。
2.6 设计模式
金刚区跳转使用策略模式进行代码编写
1.定义金刚位跳转策略接口以及跳转枚举类
public interface DiamondJumpType
List<Goods> getGoods(Page<Goods> page, Diamond diamond);
Integer getType();
// 金刚位跳转类型枚举
public enum JumpTypeEnum
COLUMN(0),
CATEGORY(1);
private Integer type;
JumpTypeEnum(Integer type)
this.type = type;
public Integer getType()
return type;
public JumpTypeEnum setType(Integer type)
this.type = type;
return this;
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 final 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.使用,注入DiamondJumpContext对象,调用getInstance方法传入枚举类型
@Autowired
private DiamondJumpContext diamondJumpContext;
@Test
public void test()
DiamondJumpType diamondJumpType=diamondJumpContext.getInstance(JumpTypeEnum.COLUMN.getType());
四、演示图
商城登陆 | 商城注册 |
商城首页 | 商城搜索 |
搜索结果展示 | 金刚位跳转 |
商品分类 | 商品详情 |
商品sku选择 | 购物车查看 |
确认下单 | 选择支付方式 |
商城我的页面 | 我的订单列表 |
添加商品评论 | 查看商品评论 |
后台登陆 | 后台首页 |
后台会员管理 | 后台评论管理 |
后台地址管理 | 后台添加商品 |
后台商品管理 | 后台banner管理 |
后台订单管理 | 后台分类管理 |
后台金刚区管理 | 后台栏目管理 |
五、在线体验
最后说两句waynboot-mall作为博主的开源项目集大成者,对于没有接触过商城项目的小伙伴来说是非常具有帮助和学习价值的。看完这个项目你能了解到一个商城项目的基本全貌,提前避坑。
感谢大家阅读,希望这篇文章能为你提供价值。公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力
支持商用,开源的商城系统,推荐给你
开源商城鱼龙混杂,想要挑选一套高品质的商城系统绝非易事,大家经常在这个问题上花费很多摸索的时间。因为项目需要,最近研究了不少的开源商城,无意中发现开店星,下载体验了一下,堪称为数不多的「高品质可商用」宝藏商城,资源发出来,希望大家白嫖快乐。
一、页面展示
商城叫开店星,先来看移动端和管理后台页面——
美观的界面是建立用户信任的第一步,开店星UI设计简约大气,多种商城风格可选择,无论是生鲜,美妆,电商等各类行业,都可以找到适合的风格,当下流行的主题色功能也都可以实现,可视化DIY拖拽式装修,一目了然,整体看起来高级简约。
二、商城系统
开店星属于前后端分离的商城系统,基于Yii2+Vue2.0+uniapp研发,拥有简单的安装、强大的功能和丰富的营销应用,整个商城框架逻辑严谨细致,代码质量很高,不夸张的说,安装后就能拿去运营赚钱。
①简单的安装
单从下载安装这一步来讲,绝对丝滑!不像一些商城,需要反复配置各种数据,安装环节就劝退很多人。开店星轻轻松松一键安装,对于一些小白程序猿,可以说极其友好,就算你菜鸟级别,也不用担心,安装教程一目了然,如果遇到问题,可以到官方的论坛https://q.kaidianxing.com/或QQ群610852寻求帮助,响应很及时。
②强大的功能
商城类系统,最重要的是整个下单的流程,比如商品、订单、会员这些基础模块,不得不说,开店星这方面做的真不错,从商品上传、下单、订单发货、售后整个流程很完善,细节很到位,日常使用绰绰有余,对于想要二开的朋友,便利性相当高。
运营——支持PC商城、微信公众号、头条小程序、抖音小程序、H5全渠道
商品——支持实体商品、虚拟商品及卡密类商品,日常使用完全没问题,商品分类、标签功能都支持
订单——发货、维权、评价、配送功能都涵盖,不少人想要的同城配送也支持
会员——会员等级、标签、数据统计,多个维度的会员管理
营销——满额包邮,优惠券
财务——充值记录、积分明细、余额明细、红包明细
③20+爆款营销插件
最感人的是,一款免费的开源商城系统,营销应用插件多达20+款!市场热门的分销、秒杀、拼团、卡密功能都有,近两年热门的微信视频号商城(自定义交易组件)竟然也有!有点怀疑是行业内卷严重,某公司把自家运营多年的核心系统完全开源了!
所有营销玩法、业务应用都采用插件化,模块化的开发思路,做到业务轻耦合,代码轻耦合。非常便于后续系统扩展和二次开发。功能健全是开店星的一大特色,后台提供了比较全面的商品管理、订单处理、会员管理等功能,许多功能在设计上比较灵活,提供有自定义设置,二开的话成本也很低。
之所以说下载就能开店,除了基本的商品下单流程完善外,如此丰富的营销插件是最大亮点,该有的都有,无需二开。
除此之外,开店星近期还发布了PC商城,免费的PC商城资源也是很多人感兴趣的,大家可以冲一下。
从gitee的关注数据来看,开店星算是较新的一个项目,不过更新频率很高,基本保持周更,官方论坛的活跃度也不错,每条反馈都有官方答疑,看得出来是用心运营的一个开源项目,这么小而美的宝藏商城,实属难得。
三、核心技术
前端技术栈 ES6、vue、vuex、vue-router、vue-cli、axios、uni-app、 iView、echarts、vant
后端技术栈:PHP,Yii2,Mysql,Redis
从系统设计到技术实现,每一个细节都在考虑让二开更方面,减少重复造轮子。
四、垂直行业解决方案
开店星专注于移动电商行业发展及电商系统产品研发销售,致力于为不同行业客户提供真正落地的电商解决方案。团队拥有十年电商系统研发经验,项目已被10万+商户认可推荐。除了免费开源版本之外,开店星即将发布的几个收费垂直行业版本也很有代表性,感兴趣的可以关注下——
①开店星提货宝
开店星提货宝系统是一款为企业客户量身打造的专业礼品卡券提货系统,具备卡密生成、发售提货卡、验卡提货及多种营销功能,能有效满足节日送礼、公司福利、公关礼品的提货卡需求。②开店星供货代理分销系统
开店星供货代理分销系统是一款为企业客户量身打造的供货商+代理商+分销裂变的行业性数字化的解决方案,为企业提供线上数字化商城系统,助力企业解决线下供货商与代理之间业务闭环。③开店星医药版
开店星医药版是一套专业的医药行业电商解决方案,总部提供一站式网购平台,支持在线问诊、在线开方服务+门店,打造医药新零售,线上销售配合营销活动,线下提供服务支持,同步数据、统一管理。除此之外,多门店版本等更多垂直领域的解决方案也将推出,大家可以多关注下。
五、系统体验
Gitee源码地址:https://gitee.com/kaidianxing/kaidianxing
GitHub源码地址:https://github.com/kaidianxing
宝塔一键安装:https://q.kaidianxing.com/thread/121这套源码真的不错,开发了很久才搞出来的,小伙伴拿去学习吧。
开源不易,点赞、转发支持!
以上是关于一套前后台全部开源的H5商城送给大家的主要内容,如果未能解决你的问题,请参考以下文章
APP+后台+vue前端全套打包送,电商解决方案CRMEB开源啦