waynboot商城發(fā)布啦,整合了Redis、RabbitMQ、ElasticSearch等常用中間件, 根據(jù)生產(chǎn)環(huán)境開發(fā)經(jīng)驗(yàn)而來(lái)不斷完善、優(yōu)化、改進(jìn)中

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)目

  1. 商城接口代碼清晰、注釋完善、模塊拆分合理
  2. 使用Spring-Security進(jìn)行訪問(wèn)權(quán)限控制
  3. 使用jwt進(jìn)行接口授權(quán)驗(yàn)證
  4. ORM層使用Mybatis Plus提升開發(fā)效率
  5. 添加全局異常處理器,統(tǒng)一異常處理
  6. 添加https配置代碼,支持https訪問(wèn)
  7. 集成七牛云存儲(chǔ)配置,上傳文件至七牛
  8. 集成常用郵箱配置,方便發(fā)送郵件
  9. 集成druid連接池,進(jìn)行sql監(jiān)控
  10. 集成swagger,管理接口文檔
  11. 添加策略模式使用示例,優(yōu)化首頁(yè)金剛區(qū)跳轉(zhuǎn)邏輯
  12. 拆分出通用的數(shù)據(jù)訪問(wèn)模塊,統(tǒng)一redis & elastic配置與訪問(wèn)
  13. 使用elasticsearch-rest-high-level-client客戶端對(duì)elasticsearch進(jìn)行操作
  14. 支持商品數(shù)據(jù)同步elasticsearch操作以及elasticsearch商品搜索
  15. RabbitMQ生產(chǎn)者發(fā)送消息采用異步confirm模式,消費(fèi)者消費(fèi)消息時(shí)需手動(dòng)確認(rèn)
  16. 下單處理過(guò)程引入rabbitMQ,異步生成訂單記錄,提高系統(tǒng)下單處理能力
  17. ...

商城難點(diǎn)整理

1. 庫(kù)存扣減操作是在下單操作扣減還是在支付成功時(shí)扣減?(ps:扣減庫(kù)存使用樂(lè)觀鎖機(jī)制 where goods_num - num >= 0

  1. 下單時(shí)扣減,這個(gè)方案屬于實(shí)時(shí)扣減,當(dāng)有大量下單請(qǐng)求時(shí),由于訂單數(shù)小于請(qǐng)求數(shù),會(huì)發(fā)生下單失敗,但是無(wú)法防止短時(shí)間大量惡意請(qǐng)求占用庫(kù)存,
    造成普通用戶無(wú)法下單
  2. 支付成功扣減,這個(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))
  3. 還是下單時(shí)扣減,但是對(duì)于未支付訂單設(shè)置一個(gè)超時(shí)過(guò)期機(jī)制,比如下單時(shí)庫(kù)存減一,生成訂單后,對(duì)于未在15分鐘內(nèi)完成支付的訂單,
    自動(dòng)取消超期未支付訂單并將庫(kù)存加一,該方案基本滿足了大部分使用場(chǎng)景
  4. 針對(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 + 今日第幾次下單

  1. 秒級(jí)時(shí)間戳:時(shí)間遞增保證唯一性
  2. 加密用戶ID:加密處理,返回用戶ID6位數(shù)字,可以防并發(fā)訪問(wèn),同一秒用戶不會(huì)產(chǎn)生2個(gè)訂單
  3. 今日第幾次下單:便于運(yùn)營(yíng)查詢處理用戶當(dāng)日訂單

5. 下單流程處理過(guò)程,通過(guò)rabbitMQ異步生成訂單,提高系統(tǒng)下單處理能力

  1. 用戶點(diǎn)擊提交訂單按鈕,后臺(tái)生成訂單編號(hào)和訂單金額跳轉(zhuǎn)到訂單支付頁(yè)面,并發(fā)送rabbitMQ消息(包含訂單編號(hào)等信息)
  2. 訂單消費(fèi)者接受到訂單消息后生成訂單記錄(未支付)
  3. 用戶點(diǎn)擊支付按鈕時(shí),前端根據(jù)訂單編號(hào)輪詢訂單信息查詢接口,如果訂單編號(hào)記錄已經(jīng)入庫(kù)則進(jìn)行后續(xù)支付操作,如果訂單編號(hào)未入庫(kù)則返回錯(cuò)誤信息(訂單異常)
  4. 用戶支付完成后在回調(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)下單
  • ...

感謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容