waynboot-mall項(xiàng)目
覺得有用的鐵子們給個(gè)star就行了,求求你們啦????
waynboot-mall是一套全部開源的微商城項(xiàng)目,包含一個(gè)運(yùn)營(yíng)后臺(tái)、h5商城和后臺(tái)接口。
實(shí)現(xiàn)了一個(gè)商城所需的首頁(yè)展示、商品分類、商品詳情、sku詳情、商品搜索、加入購(gòu)物車、結(jié)算下單、訂單狀態(tài)流轉(zhuǎn)、商品評(píng)論等一系列功能。
技術(shù)上基于Springboot2.0,整合了Redis、RabbitMQ、ElasticSearch等常用中間件,
貼近生產(chǎn)環(huán)境實(shí)際經(jīng)驗(yàn)開發(fā)而來(lái)不斷完善、優(yōu)化、改進(jìn)中。
后臺(tái)接口項(xiàng)目
運(yùn)營(yíng)后臺(tái)項(xiàng)目
h5商城項(xiàng)目
waynboot-mall接口項(xiàng)目
- 商城接口代碼清晰、注釋完善、模塊拆分合理
- 使用Spring-Security進(jìn)行訪問(wèn)權(quán)限控制
- 使用jwt進(jìn)行接口授權(quán)驗(yàn)證
- ORM層使用Mybatis Plus提升開發(fā)效率
- 添加全局異常處理器,統(tǒng)一異常處理
- 添加https配置代碼,支持https訪問(wèn)
- 集成七牛云存儲(chǔ)配置,上傳文件至七牛
- 集成常用郵箱配置,方便發(fā)送郵件
- 集成druid連接池,進(jìn)行sql監(jiān)控
- 集成swagger,管理接口文檔
- 添加策略模式使用示例,優(yōu)化首頁(yè)金剛區(qū)跳轉(zhuǎn)邏輯
- 拆分出通用的數(shù)據(jù)訪問(wèn)模塊,統(tǒng)一redis & elastic配置與訪問(wèn)
- 使用elasticsearch-rest-high-level-client客戶端對(duì)elasticsearch進(jìn)行操作
- 支持商品數(shù)據(jù)同步elasticsearch操作以及elasticsearch商品搜索
- RabbitMQ生產(chǎn)者發(fā)送消息采用異步confirm模式,消費(fèi)者消費(fèi)消息時(shí)需手動(dòng)確認(rèn)
- 下單處理過(guò)程引入rabbitMQ,異步生成訂單記錄,提高系統(tǒng)下單處理能力
- ...
商城難點(diǎn)整理
1. 庫(kù)存扣減操作是在下單操作扣減還是在支付成功時(shí)扣減?(ps:扣減庫(kù)存使用樂(lè)觀鎖機(jī)制 where goods_num - num >= 0)
- 下單時(shí)扣減,這個(gè)方案屬于實(shí)時(shí)扣減,當(dāng)有大量下單請(qǐng)求時(shí),由于訂單數(shù)小于請(qǐng)求數(shù),會(huì)發(fā)生下單失敗,但是無(wú)法防止短時(shí)間大量惡意請(qǐng)求占用庫(kù)存,
造成普通用戶無(wú)法下單 - 支付成功扣減,這個(gè)方案可以預(yù)防惡意請(qǐng)求占用庫(kù)存,但是會(huì)存在多個(gè)請(qǐng)求同時(shí)下單后,在支付回調(diào)中扣減庫(kù)存失敗,導(dǎo)致訂單還是下單失敗并且還要退還訂單金額(這種請(qǐng)求就是訂單數(shù)超過(guò)了庫(kù)存數(shù),無(wú)法發(fā)貨,影響用戶體驗(yàn))
- 還是下單時(shí)扣減,但是對(duì)于未支付訂單設(shè)置一個(gè)超時(shí)過(guò)期機(jī)制,比如下單時(shí)庫(kù)存減一,生成訂單后,對(duì)于未在15分鐘內(nèi)完成支付的訂單,
自動(dòng)取消超期未支付訂單并將庫(kù)存加一,該方案基本滿足了大部分使用場(chǎng)景 - 針對(duì)大流量下單場(chǎng)景,比如一分鐘內(nèi)五十萬(wàn)次下單請(qǐng)求,可以通過(guò)設(shè)置虛擬庫(kù)存的方式減少下單接口對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)。具體來(lái)說(shuō)就是把商品實(shí)際庫(kù)存保存到redis中,
下單時(shí)配合lua腳本原子的get和decr商品庫(kù)存數(shù)量(這一步就攔截了大部分請(qǐng)求),執(zhí)行成功后在扣減實(shí)際庫(kù)存
2. 首頁(yè)商品展示接口利用多線程技術(shù)進(jìn)行查詢優(yōu)化,將多個(gè)sql語(yǔ)句的排隊(duì)查詢變成異步查詢,接口時(shí)長(zhǎng)只跟查詢時(shí)長(zhǎng)最大的sql查詢掛鉤
# 1. 通過(guò)創(chuàng)建子線程繼承Callable接口
Callable<List<Banner>> bannerCall = () -> iBannerService.list(new QueryWrapper<Banner>().eq("status", 0).orderByAsc("sort"));
# 2. 傳入Callable的任務(wù)給FutureTask
FutureTask<List<Banner>> bannerTask = new FutureTask<>(bannerCall);
# 3. 放入線程池執(zhí)行
threadPoolTaskExecutor.submit(bannerTask);
# 4. 最后可以在外部通過(guò)FutureTask的get方法異步獲取執(zhí)行結(jié)果
List<Banner> list = bannerTask.get()
3. ElasticSearch查詢操作,查詢包含搜索關(guān)鍵字并且是上架中的商品,在根據(jù)指定字段進(jìn)行排序,最后分頁(yè)返回
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));
}
// 按價(jià)格高低排序
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);
4. 訂單編號(hào)生成規(guī)則:秒級(jí)時(shí)間戳 + 加密用戶ID + 今日第幾次下單
- 秒級(jí)時(shí)間戳:時(shí)間遞增保證唯一性
- 加密用戶ID:加密處理,返回用戶ID6位數(shù)字,可以防并發(fā)訪問(wèn),同一秒用戶不會(huì)產(chǎn)生2個(gè)訂單
- 今日第幾次下單:便于運(yùn)營(yíng)查詢處理用戶當(dāng)日訂單
5. 下單流程處理過(guò)程,通過(guò)rabbitMQ異步生成訂單,提高系統(tǒng)下單處理能力
- 用戶點(diǎn)擊提交訂單按鈕,后臺(tái)生成訂單編號(hào)和訂單金額跳轉(zhuǎn)到訂單支付頁(yè)面,并發(fā)送rabbitMQ消息(包含訂單編號(hào)等信息)
- 訂單消費(fèi)者接受到訂單消息后生成訂單記錄(未支付)
- 用戶點(diǎn)擊支付按鈕時(shí),前端根據(jù)訂單編號(hào)輪詢訂單信息查詢接口,如果訂單編號(hào)記錄已經(jīng)入庫(kù)則進(jìn)行后續(xù)支付操作,如果訂單編號(hào)未入庫(kù)則返回錯(cuò)誤信息(訂單異常)
- 用戶支付完成后在回調(diào)通知里更新訂單狀態(tài)為已支付
6. 金剛區(qū)跳轉(zhuǎn)使用策略模式
# 1. 定義金剛位跳轉(zhuǎn)策略接口
public interface DiamondJumpType {
List<Goods> getGoods(Page<Goods> page, Diamond diamond);
Integer getType();
}
# 2. 定義策略實(shí)現(xiàn)類,并使用@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. 定義策略上下文,通過(guò)構(gòu)造器注入spring,定義map屬性,通過(guò)key獲取對(duì)應(yīng)策略實(shí)現(xiàn)類
@Component
public class DiamondJumpContext {
private Map<Integer, DiamondJumpType> map = new HashMap<>();
/**
* 由spring自動(dòng)注入DiamondJumpType子類
*
* @param diamondJumpTypes 金剛位跳轉(zhuǎn)類型集合
*/
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);
}
- todo
文件目錄
|-- waynboot-admin-api // 運(yùn)營(yíng)后臺(tái)api模塊,提供后臺(tái)項(xiàng)目api接口
|-- waynboot-common // 通用模塊,包含項(xiàng)目核心基礎(chǔ)類
|-- waynboot-data // 數(shù)據(jù)模塊,通用中間件數(shù)據(jù)訪問(wèn)
| |-- waynboot-data-redis // redis訪問(wèn)配置模塊
| |-- waynboot-data-elastic // elastic訪問(wèn)配置模塊
|-- waynboot-generator // 代碼生成模塊
|-- waynboot-message-consumer // 消費(fèi)者模塊,處理訂單消息和郵件消息
|-- waynboot-message-core // 消費(fèi)者核心模塊,隊(duì)列、交換機(jī)配置
|-- waynboot-mobile-api // h5商城api模塊,提供h5商城api接口
|-- pom.xml // maven父項(xiàng)目依賴,定義子項(xiàng)目依賴版本
|-- ...
開發(fā)部署
# 1. 克隆項(xiàng)目
git clone git@github.com:wayn111/waynboot-mall.git
# 2. 導(dǎo)入項(xiàng)目依賴
將waynboot-mall目錄用idea打開,導(dǎo)入maven依賴
# 3. 安裝Mysql8.0+、Redis3.0+、RabbitMQ3.0+、ElasticSearch7.0+到本地
# 4. 導(dǎo)入sql文件
在項(xiàng)目根目錄下,找到`wayn_shop_*.sql`文件,新建mysql數(shù)據(jù)庫(kù)wayn_shop,導(dǎo)入其中
# 5. 修改Mysql、Redis、RabbitMQ、Elasticsearch連接配置
修改`application-dev.yml`以及`application.yml`文件中數(shù)據(jù)連接配置相關(guān)信息
# 6. 啟動(dòng)項(xiàng)目
后臺(tái)api:
進(jìn)入waynboot-admin-api子項(xiàng)目,找到AdminApplication文件,右鍵`run AdminApplication`,啟動(dòng)后臺(tái)項(xiàng)目
h5商城api:
進(jìn)入waynboot-mobile-api子項(xiàng)目,找到MobileApplication文件,右鍵`run MobileApplication`,啟動(dòng)h5商城項(xiàng)目
在線體驗(yàn)
- 注冊(cè)一個(gè)賬號(hào)
- 然后登陸
演示地址:http://www.wayn.ltd
演示圖
<table>
<tr>
<td>商城登陸<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/65b4034d88e84a42b11c7b6a87814edb~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>商城注冊(cè)<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2b0e04791564dfa8bb0b9634ea7702b~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>商城首頁(yè)<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df80bcb33e474d4197cda0348121dcd5~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>商城搜索<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aeac5c60607b405d919aaaa02370b8fc~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>搜索結(jié)果展示<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61566ef889464e8d9688d88fcac61f0d~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>金剛位跳轉(zhuǎn)<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b4ce663a2424dc6a7350a5dbb3d5e0f~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>商品分類<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b83f6aa52f44cbfa44b98afd41524ee~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>商品詳情<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/238ea7cb111845e1b0423de68e81316c~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>商品sku選擇<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe3fc39d847f40ae9f5e58f7e2586b46~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>購(gòu)物車查看<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4cb2462cb19247d990d7e701f22c955a~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>確認(rèn)下單<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e9fd55784e034b67bc2b589a8aee645f~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>選擇支付方式<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0cb06a3b3fa24311b8238a8521f6f888~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>商城我的頁(yè)面<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/da54b9d18f0742529a784746dc87a89d~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>我的訂單列表<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5c919f1dffc46ffbec1eab3ef7e33a6~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>添加商品評(píng)論<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c477e09b8e3489e9b100671c715d2ae~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>查看商品評(píng)論<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/64d012554a9045f5b42c1702b49ef2e2~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)登陸<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45dbbbe3cbc14b63911d35fa12136256~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)首頁(yè)<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/811e525fa18844e98ef681bce08653ff~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)會(huì)員管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8f77c617def347919e0861a53d7c07b1~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)評(píng)論管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d06dabfe36984ed5847f450e7e4c89ad~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)地址管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a73a2db045be4e759ecc97561cf2c0af~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)添加商品<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/14b2bd3639934406ae689524bd4838ab~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)商品管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d5223528f4349928dacfe879b2152e5~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)banner管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/67026d2de99448e297e56ae22ad6795e~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)訂單管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61b97057306c47b4925c4f9b33eff5a8~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)分類管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/16466b2c54844128bad9aff2257e6e57~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
<tr>
<td>后臺(tái)金剛區(qū)管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2ebab134a6a4729ace351d5cf894094~tplv-k3u1fbpfcp-zoom-1.image"/></td>
<td>后臺(tái)欄目管理<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cb4d43ae5c2441d8464eb9ca9df9968~tplv-k3u1fbpfcp-zoom-1.image"/></td>
</tr>
</table>
waynboot-mall交流群
QQ群:862417126 有問(wèn)題可以先提issue??
todo
- 支持多店鋪
- 訂單詳情頁(yè)面
- 秒殺專區(qū)
- 優(yōu)惠卷使用
- 團(tuán)購(gòu)下單
- ...