設(shè)計(jì)并制作一個(gè)旅游點(diǎn)評(píng)項(xiàng)目
項(xiàng)目參考:?馬蜂窩
?一、項(xiàng)目介紹? ??
????????1.? ? 技術(shù)棧:??
????????????????????????????數(shù)據(jù)庫(kù): mongodb + elastcearch
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?持久層: mongodb + redis
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?業(yè)務(wù)層: SpringBoot
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?web :SpringMVC
? ? ? ? ? ? ? ? ? ? ? ? ? ? 前端 :
????????????????????????????????????管理頁(yè)面: JQuery + Bootstrap3
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 旅游展示頁(yè)面: vue + JQuery + css
? ? ? ? 2.? ? ?項(xiàng)目搭建:?
? ? ? ? ? ? ? ? ? ? ? ? ? ? 1. 該項(xiàng)目主要拆分為三個(gè)模塊設(shè)計(jì):
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 模塊一: trip-website ---> 負(fù)責(zé)前提供給用戶瀏覽頁(yè)面的展示
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 模塊二: trip-mgrsite ---> 負(fù)責(zé)前端頁(yè)面展示內(nèi)容的后臺(tái)管理
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 模塊三: trip-website-api ---> 負(fù)責(zé)提供前端頁(yè)面數(shù)據(jù)交互的接口
二、項(xiàng)目?jī)?nèi)容概要
? ? ? ?1. 普通用戶。普通用戶即通過(guò)應(yīng)用注冊(cè)登錄的用戶,通過(guò)注冊(cè)后可對(duì)用戶進(jìn)行相關(guān)權(quán)限的開(kāi)放,比如寫游記,點(diǎn)評(píng),點(diǎn)贊, 收藏游記等等;
????????2. 后臺(tái)管理員。 需要后臺(tái)的管理人員或者平臺(tái)運(yùn)營(yíng)人員來(lái)對(duì)系統(tǒng)中的內(nèi)容進(jìn)行維護(hù)。 他們工作的區(qū)域就是我們提供的應(yīng)用后臺(tái)管理平臺(tái)。比如可以在后臺(tái)發(fā)布攻略,管理用戶發(fā)布的游記,審核用戶的點(diǎn)評(píng)信息等等;
????????3. 游客。 沒(méi)有登錄的用戶就是游客。 游客可以使用一部分功能,比如看看平臺(tái)發(fā)布的攻略,看看別人寫的游記。 但是不能做點(diǎn)評(píng),寫游記等登錄之后才能做得;
? ? ? ? 4. 整個(gè)項(xiàng)目主要實(shí)現(xiàn)的功能為: 注冊(cè)/登錄、目的地的查詢、 旅游攻略、 旅游日記、 內(nèi)容點(diǎn)評(píng)、 數(shù)據(jù)統(tǒng)計(jì)、 首頁(yè)關(guān)鍵字索引。
三、項(xiàng)目功能的實(shí)現(xiàn)
功能一: 注冊(cè)與登錄? ??

1. 通過(guò)觀察發(fā)現(xiàn),用戶注冊(cè)到用戶登錄中我們需要的字段設(shè)計(jì),如下圖所示;

2. 當(dāng)點(diǎn)擊立即注冊(cè)時(shí)會(huì)將填入的手機(jī)號(hào)碼進(jìn)行校驗(yàn), 光是前端(js)的校驗(yàn)手機(jī)號(hào)碼的合法性是不準(zhǔn)確的, 當(dāng)前端校驗(yàn)完畢后會(huì)通過(guò)異步請(qǐng)求的方式把手機(jī)號(hào)碼作為參數(shù)提交到后臺(tái), 通過(guò)api模塊編寫一個(gè)checkPhone的方法,通過(guò)用戶的服務(wù)接口,在使用JPA的規(guī)范進(jìn)行對(duì)mongodb數(shù)據(jù)庫(kù)的查詢(通過(guò)手機(jī)號(hào)碼查詢用戶信息)之前,需要對(duì)手機(jī)號(hào)碼的(格式,長(zhǎng)度, 為空...)進(jìn)行校驗(yàn),最后通過(guò)查詢數(shù)據(jù)庫(kù)判斷手機(jī)號(hào)碼的用戶是否存在。
3.如果不存在該用戶,表示手機(jī)號(hào)碼沒(méi)有被注冊(cè)過(guò),點(diǎn)擊進(jìn)入到用戶注冊(cè)頁(yè)面,短信驗(yàn)證的處理:
? ? ? ? 1.通過(guò)uuid提取前四位創(chuàng)建短信的驗(yàn)證碼
? ? ? ? 2.通過(guò)StringBuilder拼接一個(gè)短信驗(yàn)證信息
? ? ? ? 3.通過(guò)springmvcc提供的htttp請(qǐng)求的 操作類RestTemplate,調(diào)用第三方的短信接口實(shí)現(xiàn)短信的發(fā)送
? ? ? ? 4.因?yàn)槎绦膨?yàn)證有過(guò)期時(shí)間, 短信發(fā)送成功后將驗(yàn)證碼保存到redis中,在用戶的緩存服務(wù)接口中設(shè)計(jì)一個(gè)setVerifyCode方法
? ? ? ? 5. redis的key的設(shè)計(jì), 需要準(zhǔn)許可讀性, 唯一性, 靈活性等要素設(shè)計(jì), 這里設(shè)計(jì)為"verify_code:" + phone作為key, 驗(yàn)證碼作為value值,此外這里有用到時(shí)間, 時(shí)間一般為固定常量, 可以在core中編寫一個(gè)Constsutil設(shè)計(jì)時(shí)間單位
? ? ? ? 6.點(diǎn)擊注冊(cè):(充分的號(hào)碼校驗(yàn)是為了保證代碼的健壯性)
? ? ? ? ? ? ? ? 1> 參數(shù)是否為空校驗(yàn)
? ? ? ? ? ? ? ? 2> 手機(jī)格式是否正確(正則)
? ? ? ? ? ? ? ? 3> 手機(jī)號(hào)碼是否注冊(cè)過(guò)(查表)
? ? ? ? ? ? ? ?4> 兩次輸入的密碼是否一致(比較)
? ? ? ? ? ? ? ?5> 短信驗(yàn)證是否正確(比較)
? ? ? ? ? ? ? ? ? ? ????1> 獲取手機(jī)號(hào)碼與驗(yàn)證碼
? ? ? ? ? ? ? ? ? ????? 2> 將手機(jī)號(hào)碼拼接成key, 通過(guò)key去redis數(shù)據(jù)庫(kù)中進(jìn)行查找
? ? ? ? ? ? ? ? ? ????? 3> 驗(yàn)證碼存在并正確則通過(guò), 否則失敗
? ? ? ? ? ? ? ? 6.因?yàn)閙ongodb是全量保存, 為了保證數(shù)據(jù)庫(kù)中的其他字段默認(rèn)值不為null, 這里需要手動(dòng)設(shè)置
? ? ? ? ? ? ? ? 7.為了保證美觀, 錯(cuò)誤的參數(shù)都以拋出異常方式處理,可以設(shè)計(jì)一個(gè)斷言類進(jìn)行異常信息的自定義
? ? ? ? ? ? ? ? 8. 設(shè)計(jì)一個(gè)自定義異常類, 這里業(yè)務(wù)的異常使用LoginException, 非業(yè)務(wù)范圍使用Exception, 及業(yè)務(wù)是給用戶看的(手機(jī)號(hào)碼錯(cuò)誤, 密碼不一致等),非業(yè)務(wù)是自己代碼引起的問(wèn)題,是不給用戶看的
PS: 這里為什么要使用redis, 用mongodb或session不行嗎?
????????????1>? redis可以滿足多個(gè)請(qǐng)求間的數(shù)據(jù)共享
? ? ? ? ? ? 2> redis的定義就是一個(gè)臨時(shí)存儲(chǔ)的數(shù)據(jù)庫(kù),對(duì)于需要進(jìn)行數(shù)據(jù)失效很方便
? ? ? ? ? ? 3> 因?yàn)樯婕按鎯?chǔ)的數(shù)據(jù)較多,所以不建議使用session
? ? ? ? ? ? 4> mogodb, mysql也可以做到, 不過(guò)對(duì)有效時(shí)間的控制需要另外處理
功能二: 登錄? ??
需求: 手機(jī)號(hào)碼必填,登錄失敗的提示, 登錄成功跳轉(zhuǎn)到首頁(yè)

1. 登錄邏輯分析
? ? ? ? 1> 除了通過(guò)前端的對(duì)手機(jī)號(hào)碼和密碼的格式進(jìn)行校驗(yàn)外, 后端也需要保證保證代碼的健壯性再次進(jìn)行校驗(yàn)
? ? ? ? 2> 獲取username(手機(jī)號(hào)碼)和password ,查詢mongodb數(shù)據(jù)庫(kù), 對(duì)象若存在獲取該對(duì)象,登錄成功, 否則登錄失敗
? ? ? ? 3> 當(dāng)?shù)卿洺晒螅?為了提升用戶的使用體驗(yàn),即在用戶點(diǎn)擊其他頁(yè)面的時(shí)候依舊處于登錄狀態(tài),這里需要使用token令牌的方式,對(duì)用戶信息做一個(gè)緩存
? ? ? ? 4> 為了保證redis中key的唯一性, redis的
????????????????????????key存儲(chǔ)uuid生產(chǎn)的token值,
?????????????????????????value存儲(chǔ)登錄用戶對(duì)象,?
? ? ? ? 5> 給這個(gè)token設(shè)置超時(shí)時(shí)間, 30分鐘
? ? ? ? 6> 把這個(gè)token隨機(jī)碼返回給客戶端
? ? ? ? 7> 客戶端拿到給token令牌后,需要對(duì)給令牌進(jìn)行保證, 每次發(fā)送請(qǐng)求的時(shí)候需要把token發(fā)在請(qǐng)求頭中發(fā)送到后端
? ? ? ? 8> 處理客戶端請(qǐng)求的時(shí)候需要使用requers.getHead(“token”)對(duì)請(qǐng)求頭中的token進(jìn)行獲取, 并通過(guò)該token去redis中查詢, 獲取該用戶對(duì)象
? ? ? ? ? ? ? ? 1. 每次在redis中查詢到存在該key則需要對(duì)token的存在時(shí)間進(jìn)行重新設(shè)定, 30分鐘
? ? ? ? 9> 最后根據(jù)用戶對(duì)象是否存在進(jìn)行判斷是否登錄
? ? ? ? 10> 這里我使用interceptor攔截器對(duì)訪問(wèn)的頁(yè)面進(jìn)行攔截, 相對(duì)于filter過(guò)濾器, interceptor為springmvc的組件,功能復(fù)雜, 操作簡(jiǎn)單
? ? ? ? 11> 在api的主方法中,通過(guò)注入bean的方式對(duì)所以訪問(wèn)進(jìn)行攔截, 放行(登錄, 注冊(cè), 驗(yàn)證碼等請(qǐng)求)
PS: 這里因?yàn)槭褂昧瞬煌亩丝谠L問(wèn),涉及到http跨域同源的問(wèn)題,解決:這里需要在api的主方法中,提供一個(gè)跨域訪問(wèn)的corsConfigurer()方法,對(duì)GET, POST等等方法進(jìn)行放行.
功能三: 熱門目的地? ??
需求: 熱門目的地主要分為兩個(gè)板塊實(shí)現(xiàn): 一個(gè)是熱門的區(qū)域, 一個(gè)是熱門區(qū)域下面所掛載的目的地? .

1. 熱門區(qū)域的分析
? ? ? ? 1> 當(dāng)進(jìn)入到目的地的頁(yè)面時(shí),會(huì)發(fā)起熱門區(qū)域的查詢,即對(duì)mongodb中destination_region表中狀態(tài)為熱門的區(qū)域進(jìn)行查詢并把區(qū)域?qū)ο蠓祷?/p>
? ? ? ? 2>?國(guó)內(nèi)是固定寫死的,中國(guó)第一!

2. 熱門目的地的分析
? ? 1> 當(dāng)鼠標(biāo)移動(dòng)到熱門的區(qū)域,同時(shí)下方立即顯示當(dāng)前區(qū)域?qū)?yīng)的市,區(qū)等
? ? 2> 獲取區(qū)域id即refionId,內(nèi)地為-1, 其他為正常默認(rèn)的區(qū)域id
? ? 3> 創(chuàng)建一個(gè)泛型為Destination對(duì)象的list集合,目的是存儲(chǔ)所以的目的地對(duì)象
? ? 4>?根據(jù)獲取的區(qū)域id, 如果是-1? ---> 查詢destination目的地表,所有ParentName字段為中國(guó)的集合
? ? 5> 否則,先根據(jù)區(qū)域regionId查詢destination_region表中的區(qū)域?qū)ο?/p>
? ? 6> 通過(guò)區(qū)域id查詢出來(lái)的區(qū)域?qū)ο?,通過(guò)getRefIds()方法獲取該對(duì)象中的refId字段的所以直轄市的集合
? ? 7> 直轄市l(wèi)ist集合通過(guò)jpa中In語(yǔ)法查詢destination目的地表,獲取直轄市下面的所有目的地,放到list集合中
? ? 8> 只查詢前五個(gè),可以遍歷該集合, 使用PageRequest.of(0, 5)添加條件,在把前五個(gè)數(shù)據(jù)放到Destination類中寫好的子地區(qū)list字段中, 最后返回list
3. 目的地的明細(xì)分析
? 1. 吐司
需求: 當(dāng)從熱門目的地頁(yè)面點(diǎn)擊一個(gè)目的地進(jìn)來(lái)的時(shí)候, 跳轉(zhuǎn)到目的地明細(xì)的頁(yè)面, 頁(yè)面上方有導(dǎo)航吐司的顯示

? ??????????1> 獲取頁(yè)面的目的地id, 通過(guò)該目的地id查詢所以的父級(jí)目的地
? ????????? 2> 對(duì)傳進(jìn)來(lái)的id進(jìn)行空值判斷,如果為空則返回Collections.emptyList()一個(gè)空值的集合
? ? ????????3> 創(chuàng)建一個(gè)泛型為Destination對(duì)象的list集合,目的是存儲(chǔ)所以的目的地對(duì)象
? ????????? 4>?查詢當(dāng)前目的地的父親, 使用一個(gè)遞歸的調(diào)用, 查詢條件滿足當(dāng)前目的地的父類id存在則調(diào)用自己進(jìn)行添加到集合中,否則return
? ????????? 5> 通過(guò)Collections.reverse(list),?調(diào)換集合順序
? ? ? ? ? ? 6> 返回一個(gè)list集合
?2. 攻略概況? / 攻略概況明細(xì)
? ? ? ? ? ? 需求: 顯示當(dāng)前目的地所對(duì)應(yīng)的所以攻略分類,同時(shí)關(guān)聯(lián)查詢各個(gè)分類下的所以攻略

? ? ? ? ? ? 1> 獲取頁(yè)面的目的地id, 通過(guò)該目的地id查詢攻略目錄表獲得所有的攻略分類
? ????????? 2> 遍歷獲取到的所有攻略分類List<StrategyCatalog> ,根據(jù)攻略目錄的id查詢Strategy攻略表獲取所有的攻略對(duì)象,并把該對(duì)象設(shè)置到攻略目錄的類的Strategies字段中
? ? ????????3> 返回list
?3. 攻略點(diǎn)擊量前3的文章顯示
? ??????????1> 在qo中設(shè)置分頁(yè)目的地id,設(shè)置qo的分頁(yè)條件
? ????????? 2> 在query查詢方法中添加目的地id和文章id的條件判斷
? ? ????????3> 使用DBHelper方法返回pageable對(duì)象
? 4. 查看攻略詳細(xì)情況
? 5. 查看所有
3. 目的地的明細(xì)分析
1:攻略評(píng)論
? ???? 1>評(píng)論對(duì)象設(shè)計(jì)
用戶id? ? 用戶名?????? 用戶頭像?????? 用戶等級(jí)?????? 發(fā)表時(shí)間?????? 攻略id?????? 攻略標(biāo)題?????? 評(píng)論內(nèi)容?????? 點(diǎn)贊數(shù)?
? ? ? ?2>評(píng)論邏輯實(shí)現(xiàn)
? ??????????????1) 判斷當(dāng)前是否登錄, 使用@UserInfo注解
? ? ? ? ? ? ? ? 2) 通過(guò)@UserInfo注解獲取當(dāng)前的登錄用戶信息
?????? ????????3) 使用BeanUtil.copyProperties(原用戶對(duì)象, 新用戶對(duì)象)把當(dāng)前登錄的用戶信息設(shè)置到StrategyComment表中
?????? ????????4) 評(píng)論信息的設(shè)置 : 通過(guò)strategyCommentService的服務(wù)接口實(shí)現(xiàn)
????????????? ????????????設(shè)置評(píng)論的更新時(shí)間
????????????? ????????????設(shè)置主鍵id為null-->避免出錯(cuò)
?????? ????????5)更新完數(shù)據(jù)后進(jìn)行保存,通過(guò)評(píng)論的持久層接口 StrategyCommentRepository.save(comment)進(jìn)行保存
? ???? 3>評(píng)論分頁(yè)查詢
????????????????1)使用strategyCommentService.query(qo)進(jìn)行條件的設(shè)置,使用DBHelper.query的工具類, 這里默認(rèn)根據(jù)strategyId分頁(yè)
? ? ? ? ? ? ? ? ? 2)前端需要map.page, 使用newParamMap().put("page", page)進(jìn)行返回
2:評(píng)論點(diǎn)贊
? ???? 1>評(píng)論點(diǎn)贊分析理解透
? ? ? ? ? ? ? ? 1) 在旅游評(píng)論類中設(shè)計(jì)一個(gè)點(diǎn)贊數(shù)的list集合,當(dāng)當(dāng)前的旅游評(píng)論被當(dāng)前用戶點(diǎn)贊后
? ? ? ? ? ? ? ? 2) 判斷當(dāng)前登錄的用戶id是否存在集合中,
?????? ????????????????不存在則查詢當(dāng)前的旅游點(diǎn)評(píng)表thumbupnum字段+1,并把用戶id設(shè)置到list集合中
?????? ????????????????存在則查詢當(dāng)前的旅游點(diǎn)評(píng)表thumbupnum字段+1, 并把當(dāng)前用戶移除list集合
? ???? 2>評(píng)論點(diǎn)贊實(shí)現(xiàn)
? ??????????????????1) 判斷當(dāng)前是否登錄, 使用@UserInfo注解
?????? ????????????2) 前端提交了兩個(gè)參數(shù), 一個(gè)cid: 當(dāng)前攻略評(píng)論id,sid: 用戶id
?????? ????????????3) 通過(guò)comment的服務(wù)接口, 傳入cid和sid
?????? ????????????4) 判斷是點(diǎn)贊還是取消點(diǎn)贊
????????????? ????????????????通過(guò)cid查詢當(dāng)前的攻略評(píng)論對(duì)象
????????????? ????????????????獲取該對(duì)象的list點(diǎn)贊集合
????????????? ????????????????通過(guò)list.contains(sid)判斷集合中是否存在sid
????????????????????5) 不存在: 則進(jìn)行點(diǎn)贊
????????????? ????????????????comment對(duì)象設(shè)置thumbupnum字段+1并把用戶添加到集合中
?????? ????????????6) 存在: 則取消點(diǎn)贊
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? comment對(duì)象設(shè)置thumbupnum字段-1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?在list集合中移除當(dāng)前用戶id
?????? ????????????7)保存StrategyCommentRepository.save(comment)
?????? ????????????8)設(shè)置分頁(yè)
????????????? ????????????1> 使用strategyCommentService.query(qo)進(jìn)行條件的設(shè)置,使用
????????????????????????????????????DBHelper.query的工具類, 這里默認(rèn)根據(jù)strategyId分頁(yè)
????????????? ????????????2> 前端需要map.page, 使用new ParamMap().put("page",page)進(jìn)行返回
?????? ????????????9)返回一個(gè)page
3:游記評(píng)論
? ???? 1>評(píng)論對(duì)象設(shè)計(jì)
用戶id ?????? 用戶名字 ?????? 用戶等級(jí) ?????? 用戶頭像 ?????? 游記id? ? ? ? 游記標(biāo)題 ?????? 游記評(píng)論內(nèi)容 ??????
最新的修改時(shí)間 ?????? 評(píng)論類別 ?????? 關(guān)聯(lián)的評(píng)論 ?????? 關(guān)聯(lián)內(nèi)容
? ???? 2>評(píng)論邏輯實(shí)現(xiàn)
????????????????????1)判斷當(dāng)前是否登錄, 使用@UserInfo注解
?????? ????????????2)通過(guò)@UserInfo注解獲取當(dāng)前的登錄用戶信息
?????? ????????????3)使用BeanUtil.copyProperties(原用戶對(duì)象, 新用戶對(duì)象)
????????????? ????????????????把當(dāng)前登錄的用戶信息設(shè)置到StrategyComment表中
?????? ????????????4)評(píng)論信息的設(shè)置 : 通過(guò)strategyCommentService的服務(wù)接口實(shí)現(xiàn)
????????????? ????????????????設(shè)置評(píng)論的更新時(shí)間
????????????? ????????????????設(shè)置主鍵id為null-->避免出錯(cuò)
?????? ????????????5)判斷評(píng)論的評(píng)論的是否存在
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 存在則把該評(píng)論的評(píng)論設(shè)置到Travelcomment中
????????????? ????????????????????????1.通過(guò)傳進(jìn)來(lái)的游記對(duì)象獲取評(píng)論的評(píng)論的對(duì)象字段
????????????? ????????????????????????2.獲取評(píng)論的評(píng)論的對(duì)象的id,進(jìn)行判斷
????????????? ????????????????????????3.存在: 把該評(píng)論的評(píng)論對(duì)象設(shè)置到當(dāng)前的游記對(duì)象中
? ? ? ? ? ? ? ? ? ? ? 6)更新完數(shù)據(jù)后進(jìn)行保存,通過(guò)評(píng)論的持久層接口
????????????? ????????????????????StrategyCommentRepository.save(comment)進(jìn)行保存
? ???? 3>評(píng)論分頁(yè)查詢
????????????????1) 使用strategyCommentService.query(qo)進(jìn)行條件的設(shè)置,使用
????????????? ????????????DBHelper.query的工具類, 這里默認(rèn)根據(jù)strategyId分頁(yè)
?????? ????????2) 前端需要map.page, 使用newParamMap().put("page", page)進(jìn)行返回
4:數(shù)據(jù)統(tǒng)計(jì)
? ? 1>統(tǒng)計(jì)對(duì)象理解
? ??????????????1) 使用redis緩存來(lái)減輕mongodb的壓力, 即減少M(fèi)QL的操作
? ? ? ? ? ? ? ? 2) 把閱讀數(shù), 評(píng)論數(shù), 收藏?cái)?shù), 點(diǎn)贊數(shù)用一個(gè)vo對(duì)象封裝,
????????????? ????????????這樣在給頁(yè)面響應(yīng)數(shù)據(jù)的時(shí)候更方便
VO對(duì)象的設(shè)計(jì):?
private String strategyId;? //攻略id ??? ??????
private int viewnum;? //點(diǎn)擊數(shù) ??? ??????
private int replynum;? //攻略評(píng)論數(shù) ??? ??????
private int favornum; //收藏?cái)?shù)
private int sharenum; //分享數(shù) ??? ??????
private int thumbsupnum; //點(diǎn)贊個(gè)數(shù)
? ? 2>閱讀數(shù)操作
? ??????????1)通過(guò)strategyStatisVoService服務(wù)創(chuàng)建一個(gè)閱讀數(shù)添加的方法,
????????????? ????????傳入?yún)?shù)為(攻略id, 每次訪問(wèn)+1)
?????? ????2)通過(guò)Rediskeys.STRATEGY_STATIS_VO.join(sid), 設(shè)置好redis中的key值
????????????? ????????即 strategy_statis_vo : sid(鍵)????? vo對(duì)象(值)
?????? ????3)首先需要判斷VO對(duì)象是否存在
????????????? ????????通過(guò)StringRedisTemplate服務(wù).hasKey(key)進(jìn)行判斷
?????? ????4)key不存在: 就把mongodb數(shù)據(jù)庫(kù)中的閱讀數(shù)取出來(lái), 即Strategy strategy =strategyService.get(sid);
????????????? ????????然后創(chuàng)建一個(gè)VO對(duì)象, 使用BeanUtils.copyProperties(strategy, vo);進(jìn)行賦值
????????????? ????????注意: strategy中沒(méi)有sid, 需要自己手動(dòng)加入, vo.setStrategyId(sid);
????????????? ????????把key和VO對(duì)象存到redis中, 注意redis的值是json格式,需要
????????????? ????????template.opsForValue().set(key,JSON.toJSONString(vo));?????
?????? 5)key存在: 直接通過(guò)key獲取對(duì)應(yīng)的vo對(duì)象, 并把vo對(duì)象轉(zhuǎn)為json格式
????????????? ????????????StringvoStr = template.opsForValue().get(key);
???????? ???????????????? vo= JSON.parseObject(voStr, StrategyStatisVO.class);
?????? 6)進(jìn)行閱讀數(shù)+1操作
?????? ????????VO對(duì)象vo.setViewnum(vo.getViewnum() + i);
????????7) 把數(shù)據(jù)重新更新到redis中
????????????? ????重新獲取vo的鍵, 然后設(shè)置鍵和值
????????????? ????Stringkey = Rediskeys.STRATEGY_STATIS_VO.join(vo.getStrategyId());
? ? ? ? ? ? ? ? ? ?template.opsForValue().set(key,JSON.toJSONString(vo));
?????? PS:這里要暴露一個(gè)接口出去, 控制類才能將修改/新的vo的閱讀數(shù)據(jù)查詢并返回
? ? 3>回復(fù)數(shù)操作
????????如上,
?????? ????也是需要判斷vo對(duì)象是否存在
?????? ????其次對(duì)回復(fù)數(shù)進(jìn)行+1操作
?????? ????最后更新到redis數(shù)據(jù)庫(kù)中
??? ????4>收藏?cái)?shù)操作
????????????攻略的收藏redis設(shè)置: key: user:strategy:uid value: sid
????????????表示: 當(dāng)前用戶 是否收藏有當(dāng)前的攻略
????????????????1)生成攻略收藏的key, 并判斷該key是否在redis中
????????????????????????key存在: 獲取ke所對(duì)應(yīng)的值(是一個(gè)集合攻略id), 并把該值轉(zhuǎn)換為list集合
????????????????????????key不存在: 創(chuàng)建一個(gè)新的list集合
????????????????2)區(qū)分收藏與否:
????????????????????????判斷l(xiāng)ist集合中是否存在當(dāng)前攻略id
????????????????????????????????存在: 取消收藏, vo對(duì)象的收藏統(tǒng)計(jì)-1, 并把sid從list中移除
????????????????????????????????不存在: 收藏 vo對(duì)象的收藏統(tǒng)計(jì)+1, 并把sid從list中添加
? ? ? ? ? ? ? ? ? ?3)保存于更新: 把key 和 value保存到redis中
????????????????????????????????更新vo對(duì)象
1:點(diǎn)贊邏輯-體會(huì)redis中key的靈活性
? 1>分析
????????需求: 必須登錄之后才可以進(jìn)行 ---> 點(diǎn)贊
????????????????--->判斷今天是否第一次點(diǎn)贊
????????????????--->沒(méi)有:點(diǎn)贊成功--->有: 點(diǎn)贊失敗
????????????注意: 這里使用標(biāo)記進(jìn)行是否點(diǎn)贊的判斷
? 2>實(shí)現(xiàn)
????????????傳入sid和uid
????????????1) 創(chuàng)建好以角色為角度的一個(gè)key: 即 strategy_uid_sid: 表示該用戶的攻略有哪些,
????????????????????value 是 用戶所有的攻略id
????????????2) 獲取創(chuàng)建好的key去redis中查
????????????????不存在:
????????????????????????????獲取vo對(duì)象的themnsupnum+1, 并設(shè)置到vo對(duì)象中
????????????????????????????設(shè)置這個(gè)key的超時(shí)時(shí)間: (今晚最后一秒 - 當(dāng)前的時(shí)間 ),使用DateUtil工具類
????????????????????????????并把key,? 標(biāo)記, 超時(shí)時(shí)間設(shè)置到redis中
????????????????存在:?
????????????????????????已經(jīng)點(diǎn)贊了, 直接返回false(前端需要boolean)
2:redis數(shù)據(jù)初始化
? 1>分析
????????1)每次都會(huì)判斷這個(gè)東西是否存在, 是不是每次都會(huì)進(jìn)行數(shù)據(jù)初始化處理
????????2)庫(kù)里面的數(shù)據(jù)和緩存中的數(shù)據(jù)是否同步
? 2>spring事件監(jiān)聽(tīng)
????????1)選著事件監(jiān)聽(tīng), 可以再spring啟動(dòng)之后立刻進(jìn)行數(shù)據(jù)的初始化, 提升用戶體驗(yàn),
? 3>初始化
????????初始化邏輯特點(diǎn): 一次性初始即可,容器啟動(dòng)成功把所有數(shù)據(jù)都準(zhǔn)備好給你
????????1) 監(jiān)聽(tīng)器的包: 初始化把vo的數(shù)據(jù)放到redis中
????????2) 實(shí)現(xiàn)一個(gè)監(jiān)聽(tīng)接口 ApplicationListener<ContextRefreshedEvent>,
????????????????容器創(chuàng)建完成(aop ioc di)之后 就執(zhí)行改方法
????????3) 貼上一個(gè)@Component交給spring管理
????????4) 實(shí)現(xiàn)這個(gè)接口的方法,可以再里面數(shù)據(jù)的初始化,
????????????????把所有攻略數(shù)據(jù)查詢出來(lái)放到redis中
????注意: 如果vo對(duì)象已經(jīng)存在redis中了, 不需要再次初始化!!, 否則會(huì)導(dǎo)致前端提交的數(shù)據(jù)丟失
? ??????????解決: 在把數(shù)據(jù)存到redis中的時(shí)候,判斷redis中是否存在該key
? ??????????????????存在則使用continue跳過(guò)
3:redis數(shù)據(jù)持久化
? 1>分析
????????????1) 為什么進(jìn)行持久化: 緩存數(shù)據(jù)發(fā)生變動(dòng), 如果不進(jìn)行持久化數(shù)據(jù)可能會(huì)丟失, 所以需要將數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)
????????????2)? 需要持久化的數(shù)據(jù)是什么: 緩存中有用的數(shù)據(jù), 此處需要持久化的數(shù)據(jù): (1):VO數(shù)據(jù)? (2) 用戶攻略收藏?cái)?shù)據(jù)? `[擴(kuò)展]`
????????????3)在哪一個(gè)項(xiàng)目執(zhí)行持久化邏輯(mgrsite? web-api)
????????????4)在javaweb那個(gè)組件中實(shí)現(xiàn)持久化邏輯(filter servlet controlelr interceptot listener)
? ? 持久化邏輯特點(diǎn): 多次執(zhí)行, 人工執(zhí)行請(qǐng)求進(jìn)行持久化, 程序周期性執(zhí)行
? ? 持久化最佳實(shí)踐是:? 使用定時(shí)器, 周期執(zhí)行, 周期性執(zhí)行(每個(gè)1天執(zhí)行一次)
? 2>spring定時(shí)
????????1) 創(chuàng)建一個(gè)定時(shí)類記得貼注解:交給spring,
????????????該類中的方法@Scheduled: 定時(shí)任務(wù)標(biāo)簽, cron=""定時(shí)任務(wù)計(jì)劃
????????2) 在main方法中貼上@EnableScheduling 啟動(dòng)定時(shí)任務(wù)
????????3) Seconds Minutes Hours DayofMonth Month DayofWeek (springboot支持的格式)
? ? ? 秒? ? 分? ? ? 小時(shí)? ? 天? ? ? ? 月? ? ? 周? ? ?
? ? ? 0? ? 0? ? ? 2? ? ? ? 1? ? ? ? *? ? ? ? ?? ? *? 表示每月的1日的凌晨2點(diǎn)執(zhí)行
? 3>持久化
????????就是把redis中的數(shù)據(jù)存放到mongodb數(shù)據(jù)庫(kù)中, 保證數(shù)據(jù)不會(huì)丟失
????????數(shù)據(jù)的持久化需要和定時(shí)器類一起使用, 表示在某一個(gè)時(shí)間內(nèi)自動(dòng)保存數(shù)據(jù)到mongoddb
????????1) 需要獲取redis中的vo數(shù)據(jù)
????????2) 創(chuàng)建通配表達(dá)式來(lái)獲取所有的vo對(duì)象: strategy_startid_vo: *
????????3)傳入統(tǒng)配表達(dá)式, 返回vo對(duì)象集合
????????4) 通過(guò)redis的,keys(通配表達(dá)式)獲取所有的vo對(duì)象的key集合
????????5) 遍歷該集合, 將從redis中獲取vo對(duì)象
????????6)獲取到一個(gè)就使用JSON.parseObject解析該value
????????7) 把解析好的value存放到新建的泛型是Vo對(duì)象的集合
????????8)在controller中, 把獲取的vo對(duì)象進(jìn)行遍歷
????????9) 根據(jù)id的條件進(jìn)行mongodb數(shù)據(jù)的更新
? ??????注意: 這里使用了DBhelp工具, 里面穿的是字節(jié)碼對(duì)象,
? ??????所以需要把vo使用BeanUtil.Copy進(jìn)行對(duì)象的賦值, 再傳入