Springboot整合搜索(Elasticsearch)頁面渲染(thymeleaf)

前言:

??今天我們要做的是基于Elasticsearch搜索和thymeleaf渲染頁面,在今天的任務(wù)中將簡(jiǎn)單的介紹thymeleaf的基本使用,以及結(jié)合項(xiàng)目如何使用thymeleaf渲染頁面。
觀看thymeleaf的基本使用我已經(jīng)寫了一篇博客,連接地址:http://www.itdecent.cn/p/29afd801bcc4 通過上一篇《基于Elasticsearch商品搜索(模仿某東搜索頁面的簡(jiǎn)單實(shí)現(xiàn))》

基礎(chǔ)數(shù)據(jù)渲染

(1)更新SearchController,定義跳轉(zhuǎn)搜索結(jié)果頁面方法

@GetMapping("list")
    public String list(@RequestParam Map<String, String> searchMap, Model model) {
        //對(duì)搜索入?yún)в刑厥夥?hào)進(jìn)行處理
        if (null != searchMap) {
            Set<Map.Entry<String, String>> entries = searchMap.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                if (entry.getKey().startsWith("spec_")) {
                    searchMap.put(entry.getKey(), entry.getValue().replace("+", "%2B"));
                }
            }
        }
       
        Map resultMap = searchService.search(searchMap);
       
    //設(shè)置視圖數(shù)據(jù)
        //返回?cái)?shù)據(jù)
        model.addAttribute("resultMap", resultMap);
        //查詢條件數(shù)據(jù)
        model.addAttribute("searchMap", searchMap);
        return "search";
    }

(2) 搜索結(jié)果頁面渲染

(2.1)用戶選擇條件回顯

      <div class="bread">
            <ul class="fl sui-breadcrumb">
                <li>
                    <a href="#">全部結(jié)果</a>
                </li>
                <li class="active"><span th:text="${searchMap.keywords}"></span></li>
            </ul>
            <ul class="fl sui-tag">
                <!-- 品牌-->
                <li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}">品牌:
                    <span th:text="${searchMap.brand}"></span>
                   <i>×</i>
                </li>
                <!-- 價(jià)格-->
                <li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}">價(jià)格:<span
                        th:text="${searchMap.price}"></span>
                        <i>×</i>
                </li>
                <!-- 規(guī)格-->
                <li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}">
                    <span th:text="${#strings.replace(sm.key,'spec_','')}"></span>:
                    <span th:text="${#strings.replace(sm.value,'%2B','+')}"></span>
                    <i>×</i>
                </li>

            </ul>
            <form class="fl sui-form form-dark">
                <div class="input-control control-right">
                    <input type="text"/>
                    <i class="sui-icon icon-touch-magnifier"></i>
                </div>
            </form>
        </div>

(2.2)商品屬性及規(guī)格顯示

 <!--selector-->
        <div class="clearfix selector">
            <div class="type-wrap logo" th:unless="${#maps.containsKey(searchMap,'brand')}">
                <div class="fl key brand">品牌</div>
                <div class="value logos">
                    <ul class="logo-list">
                        <li th:each="brand,brandSate:${resultMap.brandList}">
                            <a th:text="${brand}" th:href="@{${url}(brand=${brand})}"></a>
                        </li>
                    </ul>
                </div>
                <div class="ext">
                    <a href="javascript:void(0);" class="sui-btn">多選</a>
                    <a href="javascript:void(0);">更多</a>
                </div>
            </div>
            <div class="type-wrap" th:each="spec,specStat:${resultMap.skuSpecList}"
                 th:unless="${#maps.containsKey(searchMap,'spec_'+spec.key)}">
                <div class="fl key" th:text="${spec.key}"></div>
                <div class="fl value">
                    <ul class="type-list">
                        <li th:each="op,opStat:${spec.value}">
                            <a th:text="${op}" th:href="@{${url}('spec_'+${spec.key}=${op})}"></a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext"></div>
            </div>

            <div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}">
                <div class="fl key">價(jià)格</div>
                <div class="fl value">
                    <ul class="type-list">
                        <li>
                            <a th:text="0-500元" th:href="@{${url}(price='0-500')}"></a>
                        </li>
                        <li>
                            <a th:text="500-1000元" th:href="@{${url}(price='500-1000')}"></a>
                        </li>
                        <li>
                            <a th:text="1000-1500元" th:href="@{${url}(price='1000-1500')}"></a>
                        </li>
                        <li>
                            <a th:text="1500-2000元" th:href="@{${url}(price='1500-2000')}"></a>
                        </li>
                        <li>
                            <a th:text="2000-3000元" th:href="@{${url}(price='2000-3000')}"></a>
                        </li>
                        <li>
                            <a th:text="3000元以上" th:href="@{${url}(price='3000')}"></a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext">
                </div>
            </div>
            <div class="type-wrap">
                <div class="fl key">更多篩選項(xiàng)</div>
                <div class="fl value">
                    <ul class="type-list">
                        <li>
                            <a>特點(diǎn)</a>
                        </li>
                        <li>
                            <a>系統(tǒng)</a>
                        </li>
                        <li>
                            <a>手機(jī)內(nèi)存 </a>
                        </li>
                        <li>
                            <a>單卡雙卡</a>
                        </li>
                        <li>
                            <a>其他</a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext">
                </div>
            </div>
        </div>

(2.3)商品列表顯示

<!--商品列表-->
            <div class="goods-list">
                <ul class="yui3-g">
                    <li class="yui3-u-1-5" th:each="sku,skuStat:${resultMap.rows}">
                        <div class="list-wrap">
                            <div class="p-img">
                               <a href="item.html" target="_blank"><img src="/img/_/mobile01.png" /></a>
                            </div>
                            <div class="price">
                                <strong>
                                    <em>¥</em>
                                    <i th:text="${sku.price}"></i>
                                </strong>
                            </div>
                            <div class="attr">
                                <a target="_blank" href="item.html" th:title="${sku.spec}" th:utext="${sku.name}"></a>
                            </div>
                            <div class="commit">
                                <i class="command">已有<span>2000</span>人評(píng)價(jià)</i>
                            </div>
                            <div class="operate">
                                <a href="success-cart.html" target="_blank" class="sui-btn btn-bordered btn-danger">加入購(gòu)物車</a>
                                <a href="javascript:void(0);" class="sui-btn btn-bordered">收藏</a>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>

(3)關(guān)鍵字搜索

                        <div class="search">
                            <form th:action="@{/search/list}" class="sui-form form-inline">
                                <!--searchAutoComplete-->
                                <div class="input-append">
                                    <input th:type="text" id="autocomplete" name="keywords"
                                           th:value="${searchMap.keywords}" class="input-error input-xxlarge"/>
                                    <button class="sui-btn btn-xlarge btn-danger" th:type="submit">搜索</button>
                                </div>
                            </form>
                        </div>

(4) 條件搜索實(shí)現(xiàn)

用戶每次點(diǎn)擊搜索的時(shí)候,其實(shí)在上次搜索的基礎(chǔ)之上加上了新的搜索條件,也就是在上一次請(qǐng)求的URL后面追加了新的搜索條件,我們可以在后臺(tái)每次拼接組裝出上次搜索的URL,然后每次將URL存入到Model中,頁面每次點(diǎn)擊不同條件的時(shí)候,從Model中取出上次請(qǐng)求的URL,然后再加上新點(diǎn)擊的條件參數(shù)實(shí)現(xiàn)跳轉(zhuǎn)即可。

(4.1)后臺(tái)記錄搜索URL

        StringBuilder url = new StringBuilder("/search/list");
        //拼接url
        if (searchMap != null && searchMap.size() > 0) {
            url.append("?");
            for (String paramKey : searchMap.keySet()) {
                if (!"sortRule".equals(paramKey) && !"sortField".equals(paramKey) && !"PageNum".equals(paramKey)) {
                    url.append(paramKey).append("=").append(searchMap.get(paramKey)).append("&");
                }
            }
            String urlString = url.toString();
            //去掉最后一個(gè)&
            urlString = urlString.substring(0, urlString.length() - 1);
            model.addAttribute("url", urlString);
        } else {
            model.addAttribute("url", url);
        }

(4.2)頁面搜索對(duì)接

        <div class="clearfix selector">
            <div class="type-wrap logo" th:unless="${#maps.containsKey(searchMap,'brand')}">
                <div class="fl key brand">品牌</div>
                <div class="value logos">
                    <ul class="logo-list">
                        <li th:each="brand,brandSate:${resultMap.brandList}">
                            <a th:text="${brand}" th:href="@{${url}(brand=${brand})}"></a>
                        </li>
                    </ul>
                </div>
                <div class="ext">
                    <a href="javascript:void(0);" class="sui-btn">多選</a>
                    <a href="javascript:void(0);">更多</a>
                </div>
            </div>
            <div class="type-wrap" th:each="spec,specStat:${resultMap.skuSpecList}"
                 th:unless="${#maps.containsKey(searchMap,'spec_'+spec.key)}">
                <div class="fl key" th:text="${spec.key}"></div>
                <div class="fl value">
                    <ul class="type-list">
                        <li th:each="op,opStat:${spec.value}">
                            <a th:text="${op}" th:href="@{${url}('spec_'+${spec.key}=${op})}"></a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext"></div>
            </div>

            <div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}">
                <div class="fl key">價(jià)格</div>
                <div class="fl value">
                    <ul class="type-list">
                        <li>
                            <a th:text="0-500元" th:href="@{${url}(price='0-500')}"></a>
                        </li>
                        <li>
                            <a th:text="500-1000元" th:href="@{${url}(price='500-1000')}"></a>
                        </li>
                        <li>
                            <a th:text="1000-1500元" th:href="@{${url}(price='1000-1500')}"></a>
                        </li>
                        <li>
                            <a th:text="1500-2000元" th:href="@{${url}(price='1500-2000')}"></a>
                        </li>
                        <li>
                            <a th:text="2000-3000元" th:href="@{${url}(price='2000-3000')}"></a>
                        </li>
                        <li>
                            <a th:text="3000元以上" th:href="@{${url}(price='3000')}"></a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext">
                </div>
            </div>
            <div class="type-wrap">
                <div class="fl key">更多篩選項(xiàng)</div>
                <div class="fl value">
                    <ul class="type-list">
                        <li>
                            <a>特點(diǎn)</a>
                        </li>
                        <li>
                            <a>系統(tǒng)</a>
                        </li>
                        <li>
                            <a>手機(jī)內(nèi)存 </a>
                        </li>
                        <li>
                            <a>單卡雙卡</a>
                        </li>
                        <li>
                            <a>其他</a>
                        </li>
                    </ul>
                </div>
                <div class="fl ext">
                </div>
            </div>
        </div>  

(5)移除搜索條件

用戶點(diǎn)擊條件搜索后,要將選中的條件顯示出來,并提供移除條件的 x 按鈕,顯示條件我們可以從searchMap中獲取,移除其實(shí)就是將之前的請(qǐng)求地址中的指定條件刪除即可。

<ul class="fl sui-tag">
                <li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}">品牌:
                    <span th:text="${searchMap.brand}"></span>
                    <a th:href="@{${#strings.replace(url,'&brand='+searchMap.brand,'')}}">×</a>
                </li>

                <li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}">價(jià)格:<span
                        th:text="${searchMap.price}"></span>
                    <a th:href="@{${#strings.replace(url,'&price='+searchMap.price,'')}}">×</a>
                </li>
                <!--規(guī)格-->
                <li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}">
                    <span th:text="${#strings.replace(sm.key,'spec_','')}"></span>:
                    <span th:text="${#strings.replace(sm.value,'%2B','+')}"></span>
                    <a th:href="@{${#strings.replace(url,'&'+sm.key+'='+sm.value,'')}}">×</a>
                </li>
                
            </ul>

(6)排序

                        <li>
                            <a th:href="@{${url}(sortRule='ASC',sortField='price')}">價(jià)格↑</a>
                        </li>
                        <li>
                            <a th:href="@{${url}(sortRule='DESC',sortField='price')}">價(jià)格↓</a>
                        </li>

(7)分頁

加入分頁工具類——傳送門:http://www.itdecent.cn/p/acdffb8b3859
實(shí)現(xiàn)分頁信息封裝:

        //設(shè)置分頁查詢數(shù)據(jù)
        Page<Skuinfo> page = new Page<Skuinfo>(
                Long.parseLong(String.valueOf(resultMap.get("total"))),
                Integer.parseInt(String.valueOf(resultMap.get("pageNum"))),
                Page.pageSize
        );

看累了吧,來幾張圖片放松一下


86.jpg

87.jpg

88.jpg

89.jpg

90.jpg

91.jpg

92.jpg

商品詳情頁靜態(tài)化

需求分析

??當(dāng)系統(tǒng)審核完成商品,需要將商品詳情頁進(jìn)行展示,那么采用靜態(tài)頁面生成的方能的web服務(wù)器中進(jìn)行訪問是比較合適的。所以,開發(fā)流程如下圖所示:


流程圖

執(zhí)行步驟解釋:
??系統(tǒng)管理員(商家運(yùn)維人員)修改或者審核商品的時(shí)候, 會(huì)更改數(shù)據(jù)庫(kù)中商品上架狀態(tài)并發(fā)送商品id給rabbitMq中的上架交換器。
??上架交換器會(huì)將商品id發(fā)給靜態(tài)頁生成隊(duì)列。
??靜態(tài)頁微服務(wù)設(shè)置監(jiān)聽器, 監(jiān)聽靜態(tài)頁生成隊(duì)列, 根據(jù)商品id獲取商品詳細(xì)數(shù)據(jù)并使用thymeleaf的模板技術(shù)生成靜態(tài)頁。
靜態(tài)頁面生成服務(wù)中的具體代碼實(shí)現(xiàn):

1.創(chuàng)建PageListener監(jiān)聽類,監(jiān)聽page_create_queue隊(duì)列,獲取消息,并生成靜態(tài)化頁面

@Component
public class PageListener {

    @Autowired
    private PageService pageService;

    @RabbitListener(queues = RabbitmqConfig.PAGE_CREATE_QUEUE)
    public void receiveMessage(String spuId) {
        System.out.println("生成靜態(tài)頁面的spuId:" + spuId);
        pageService.generateHtml(spuId);
    }
}

2.PageServiceImpl實(shí)現(xiàn):

@Service
public class PageServiceImpl implements PageService {

    @Value("${pagepath}")
    private String pagepath;

    @Autowired
    private TemplateEngine templateEngine;

    /**
     * @param spuId
     * @return void
     * @description: 生產(chǎn)靜態(tài)化頁面
     * @author 大佬味的小男孩
     * @date 2020/07/27 21:57
     */
    @Override
    public void generateHtml(String spuId) {

        //獲取context對(duì)象
        Context context = new Context();
        //獲取靜態(tài)化頁面的數(shù)據(jù)
        Map<String, Object> map = this.getItemData(spuId);
        context.setVariables(map);


        //獲取商品存儲(chǔ)位置(本地磁盤)
        File file = new File(pagepath);
        if (!file.exists()) {
            //判斷存儲(chǔ)靜態(tài)化頁面的文件夾是否存在  如果不存在 就創(chuàng)建
            file.mkdir();
        }
        //定義輸出流  生產(chǎn)文件
        File file1 = new File(file + "/" + spuId + ".html");
        Writer out = null;
        try {
            out = new PrintWriter(file1);
            templateEngine.process("item", context, out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                //關(guān)閉流
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @Autowired
    private SpuFeign spuFeign;
    @Autowired
    private CategoryFeign categoryFeign;
    @Autowired
    private SkuFeign skuFeign;

    /**
     * @param spuId
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @description: //獲取靜態(tài)化頁面的數(shù)據(jù)
     * @author 大佬味的小男孩
     * @date 2020/07/27 22:01
     */
    private Map<String, Object> getItemData(String spuId) {

        HashMap<String, Object> map = new HashMap<>();
        //獲取spu數(shù)據(jù)
        Spu spu = spuFeign.findSpuById(spuId).getData();

        map.put("spu", spu);

        //獲取圖片數(shù)據(jù)
        if (spu != null) {
            if (!StringUtils.isEmpty(spu.getImages())) {
                map.put("ImageList", spu.getImages().split(","));
            }
        }

        //獲取商品分類數(shù)據(jù)
        Category category1 = categoryFeign.findById(spu.getCategory1Id());
        Category category2 = categoryFeign.findById(spu.getCategory2Id());
        Category category3 = categoryFeign.findById(spu.getCategory3Id());
        map.put("category1", category1);
        map.put("category2", category2);
        map.put("category3", category3);

        //獲取sku的數(shù)據(jù)
        List<Sku> skuList = skuFeign.findSkuListByspuid(spuId);
        map.put("skuList", skuList);

        //獲取規(guī)格信息
        map.put("specifcationList", JSON.parseObject(spu.getSpecItems(), Map.class));

        return map;
    }
}

3. 數(shù)據(jù)監(jiān)控服務(wù)我就不演示了

4 模板填充

(1)面包屑數(shù)據(jù)
        <div class="crumb-wrap">
            <ul class="sui-breadcrumb">
                <li>
                    <a href="#" th:text="${category1.name}"></a>
                </li>
                <li>
                    <a href="#" th:text="${category2.name}"></a>
                </li>
                <li>
                    <a href="#" th:text="${category3.name}"></a>
                </li>
            </ul>
        </div>
(2)商品圖片
            <div class="fl preview-wrap">
                <!--放大鏡效果-->
                <div class="zoom">
                    <!--默認(rèn)第一個(gè)預(yù)覽-->
                    <div id="preview" class="spec-preview">
                        <span class="jqzoom"><img th:jqimg="${ImageList[0]}" th:src="${ImageList[0]}"/></span>
                    </div>
                    <!--下方的縮略圖-->
                    <div class="spec-scroll">
                        <a class="prev">&lt;</a>
                        <!--左右按鈕-->
                        <div class="items">
                            <ul>
                                <li th:each="img:${ImageList}"><img th:src="${img}" th:bimg="${img}"  onmousemove="preview(this)"/></li>
                            </ul>
                        </div>
                        <a class="next">&gt;</a>
                    </div>
                </div>
            </div>
(3)規(guī)格輸出
                    <div id="specification" class="summary-wrap clearfix">
                        <!--循環(huán)MAP-->
                        <dl th:each="spec,specStat:${specifcationList}">
                            <dt>
                                <div class="fl title">
                                    <i th:text="${spec.key}"></i>
                                </div>
                            </dt>
                            <dd th:each="arrValue:${specStat.current.value}">
                                <a href="javascript:;"
                                   th:v-bind:class="|{selected:sel('${spec.key}','${arrValue}')}|"
                                   th:@click="|selectSpecification('${spec.key}','${arrValue}')|">
                                    <i th:text="${arrValue}"></i>
                                    <span title="點(diǎn)擊取消選擇">&nbsp;</span>
                                </a>
                            </dd>
                        </dl>
                    </div>
(4)默認(rèn)SKU顯示

??靜態(tài)頁生成后,需要顯示默認(rèn)的Sku,我們這里默認(rèn)顯示第1個(gè)Sku即可,這里可以結(jié)合著Vue一起實(shí)現(xiàn)。可以先定義一個(gè)集合,再定義一個(gè)spec和sku,用來存儲(chǔ)當(dāng)前選中的Sku信息和Sku的規(guī)格,代碼如下:

<script th:inline="javascript">
    var item = new Vue({
        el: '#itemArray',
        data: {
            skuList: [[${skuList}]],   //SKU集合
            sku: {},                     //當(dāng)前選中的sku
            spec: {},                   //選中的sku的規(guī)格
            num: 1
        },
        created: function () {
            //默認(rèn)選中第1個(gè)SKU,深克隆
            this.sku = JSON.parse(JSON.stringify(this.skuList[0]));
            //第1個(gè)SKU的規(guī)格,深克隆
            this.spec = JSON.parse(this.skuList[0].spec);
        }
    })
</script>

最后請(qǐng)?jiān)徫也恢涝趺纯偨Y(jié)前端頁面!

記得一鍵三連

?著作權(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ù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

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