備戰(zhàn)春招秋招沒項(xiàng)目?吃透這套開源社區(qū)系統(tǒng),不愁面試啞火(非廣告)
寒假前半個月到現(xiàn)在差不多一個多月,斷斷續(xù)續(xù)做完了這個項(xiàng)目,現(xiàn)在終于可以開源出來了,我的想法是為這個項(xiàng)目編寫一套完整的教程,包括技術(shù)選型分析、架構(gòu)分析、業(yè)務(wù)邏輯分析、核心技術(shù)點(diǎn)分析、常見面試題等。不過說實(shí)話,這里面涉及的一些技術(shù)我仍然停留在僅僅是會用的階段,當(dāng)然,后面我會不斷學(xué)習(xí),了解透徹這些技術(shù)的底層原理,不斷完善這套教程。
?? 項(xiàng)目簡介
Echo 是一套前后端不分離的開源社區(qū)系統(tǒng),基于目前主流 Java Web 技術(shù)棧(SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ...),并提供詳細(xì)的開發(fā)文檔和配套教程。包含帖子、評論、私信、系統(tǒng)通知、點(diǎn)贊、關(guān)注、搜索、用戶設(shè)置、數(shù)據(jù)統(tǒng)計等模塊。
「源碼鏈接」:已托管在 Github 和 Gitee:
Github:https://github.com/Veal98/Echo Gitee:https://gitee.com/veal98/Echo
「在線體驗(yàn)」:項(xiàng)目已經(jīng)部署到騰訊云服務(wù)器,各位小伙伴們可直接線上體驗(yàn):http://1.15.127.74/。已內(nèi)置三種不同身份的用戶:
| username | password | 特殊權(quán)限 | |
|---|---|---|---|
| 管理員 | admin | admin | 數(shù)據(jù)統(tǒng)計、刪除帖子 |
| 版主 | master | master | 置頂帖子、加精帖子 |
| 普通用戶 | user | user |
「文檔地址」:文檔通過 Docsify + Gitee Pages 生成,國內(nèi)訪問速度較快,在線訪問地址:https://veal98.gitee.io/echo
?? 核心技術(shù)棧
后端:
Spring Spring Boot 2.1.5 RELEASE Spring MVC ORM:MyBatis 數(shù)據(jù)庫:MySQL 5.7 分布式緩存:Redis 本地緩存:Caffeine 消息隊列:Kafka 2.13-2.7.0 搜索引擎:Elasticsearch 6.4.3 安全:Spring Security 郵件任務(wù):Spring Mail 分布式定時任務(wù):Spring Quartz 日志:SLF4J(日志接口) + Logback(日志實(shí)現(xiàn))
前端:
Thymeleaf Bootstrap 4.x Jquery Ajax
?? 開發(fā)環(huán)境
操作系統(tǒng):Windows 10 構(gòu)建工具:Apache Maven 集成開發(fā)工具:Intellij IDEA 應(yīng)用服務(wù)器:Apache Tomcat 接口測試工具:Postman 壓力測試工具:Apache JMeter 版本控制工具:Git Java 版本:8
?? 界面展示
首頁:

登錄頁:

帖子詳情頁:

個人主頁:
朋友私信頁:
私信詳情頁:
系統(tǒng)通知頁:

通知詳情頁:
賬號設(shè)置頁:

數(shù)據(jù)統(tǒng)計頁:
搜索詳情頁:

?? 功能列表

「注冊」
用戶注冊成功,將用戶信息存入 MySQL,但此時該用戶狀態(tài)為未激活 向用戶發(fā)送激活郵件,用戶點(diǎn)擊鏈接則激活賬號(Spring Mail) 「登錄 | 登出」
進(jìn)入登錄界面,動態(tài)生成驗(yàn)證碼,并將驗(yàn)證碼短暫存入 Redis(60 秒) 用戶登錄成功(驗(yàn)證用戶名、密碼、驗(yàn)證碼),生成登錄憑證且設(shè)置狀態(tài)為有效,并將登錄憑證存入 Redis 注意:登錄憑證存在有效期,在所有的請求執(zhí)行之前,都會檢查憑證是否有效和是否過期,只要該用戶的憑證有效并在有效期時間內(nèi),本次請求就會一直持有該用戶信息(使用 ThreadLocal 持有用戶信息) 勾選記住我,則延長登錄憑證有效時間 用戶登錄成功,將用戶信息短暫存入 Redis(1 小時) 用戶登出,將憑證狀態(tài)設(shè)為無效,并更新 Redis 中該用戶的登錄憑證信息 「賬號設(shè)置」
將用戶選擇的頭像圖片文件上傳至七牛云服務(wù)器 修改頭像 修改密碼 「帖子模塊」
未登錄用戶無法發(fā)帖 “版主” 可以看到帖子的置頂和加精按鈕并執(zhí)行相應(yīng)操作 “管理員” 可以看到帖子的刪除按鈕并執(zhí)行相應(yīng)操作 “普通用戶” 無法看到帖子的置頂、加精、刪除按鈕,也無法執(zhí)行相應(yīng)操作 支持按照 “發(fā)帖時間” 顯示 支持按照 “熱度排行” 顯示(Spring Quartz) 發(fā)布帖子(過濾敏感詞),將其存入 MySQL 分頁顯示所有的帖子 查看帖子詳情 權(quán)限管理(Spring Security + Thymeleaf Security) 「評論模塊」
未登錄用戶無法使用評論功能 發(fā)布對帖子的評論(過濾敏感詞),將其存入 MySQL 分頁顯示評論 發(fā)布對評論的回復(fù)(過濾敏感詞) 權(quán)限管理(Spring Security) 「私信模塊」
未登錄用戶無法使用私信功能 查詢某個會話所包含的所有私信 訪問私信詳情時,將顯示的私信設(shè)為已讀狀態(tài) 支持分頁顯示 查詢當(dāng)前用戶的會話列表 每個會話只顯示一條最新的私信 支持分頁顯示 發(fā)送私信(過濾敏感詞) 私信列表 私信詳情 權(quán)限管理(Spring Security) 「統(tǒng)一處理 404 / 500 異常」
普通請求異常 異步請求異常 「統(tǒng)一記錄日志」
「點(diǎn)贊模塊」
未登錄用戶無法使用點(diǎn)贊相關(guān)功能 支持對帖子、評論/回復(fù)點(diǎn)贊 第 1 次點(diǎn)贊,第 2 次取消點(diǎn)贊 首頁統(tǒng)計帖子的點(diǎn)贊數(shù)量 詳情頁統(tǒng)計帖子和評論/回復(fù)的點(diǎn)贊數(shù)量 詳情頁顯示當(dāng)前登錄用戶的點(diǎn)贊狀態(tài)(贊過了則顯示已贊) 統(tǒng)計我的獲贊數(shù)量 權(quán)限管理(Spring Security) 「關(guān)注模塊」
未登錄用戶無法使用關(guān)注相關(guān)功能 關(guān)注功能 取消關(guān)注功能 統(tǒng)計用戶的關(guān)注數(shù)和粉絲數(shù) 我的關(guān)注列表(查詢某個用戶關(guān)注的人),支持分頁 我的粉絲列表(查詢某個用戶的粉絲),支持分頁 權(quán)限管理(Spring Security) 「系統(tǒng)通知模塊」
未登錄用戶無法使用系統(tǒng)通知功能 分別顯示每種類型的系統(tǒng)通知的未讀數(shù)量 顯示所有系統(tǒng)通知的未讀數(shù)量 分頁顯示某一類主題所包含的通知 進(jìn)入某種類型的系統(tǒng)通知詳情,則將該頁的所有未讀的系統(tǒng)通知狀態(tài)設(shè)置為已讀 顯示評論、點(diǎn)贊、關(guān)注三種類型的通知 通知列表 通知詳情 未讀數(shù)量 導(dǎo)航欄顯示所有消息的未讀數(shù)量(未讀私信 + 未讀系統(tǒng)通知) 權(quán)限管理(Spring Security) 「搜索模塊」
從 Elasticsearch 服務(wù)器搜索帖子 從 Elasticsearch 服務(wù)器刪除帖子(當(dāng)帖子從數(shù)據(jù)庫中被刪除時) 發(fā)布帖子時,通過消息隊列將帖子異步地提交到 Elasticsearch 服務(wù)器 為帖子增加評論時,通過消息隊列將帖子異步地提交到 Elasticsearch 服務(wù)器 發(fā)布事件 搜索服務(wù) 顯示搜索結(jié)果 「網(wǎng)站數(shù)據(jù)統(tǒng)計」(管理員專屬)
只有管理員可以查看網(wǎng)站數(shù)據(jù)統(tǒng)計 存入 Redis 的 Bitmap 支持單日查詢和區(qū)間日期查詢 存入 Redis 的 HyperLogLog 支持單日查詢和區(qū)間日期查詢 獨(dú)立訪客 UV 日活躍用戶 DAU 權(quán)限管理(Spring Security) 「優(yōu)化網(wǎng)站性能」
使用本地緩存 Caffeine 緩存熱帖列表以及所有用戶帖子的總數(shù)
?? 本地運(yùn)行
各位如果需要將項(xiàng)目部署在本地進(jìn)行測試,以下環(huán)境請?zhí)崆皞浜茫?/p>
Java 8 MySQL 5.7 Redis Kafka 2.13-2.7.0 Elasticsearch 6.4.3
然后「修改配置文件中的信息為你自己的本地環(huán)境,直接運(yùn)行是運(yùn)行不了的」,而且相關(guān)私密信息我全部用 xxxxxxx 代替了。
本地運(yùn)行需要修改的配置文件信息如下:
1)application-develop.properties:
MySQL Spring Mail(郵箱需要開啟 SMTP 服務(wù)) Kafka:consumer.group-id(該字段見 Kafka 安裝包中的 consumer.proerties,可自行修改, 修改完畢后需要重啟 Kafka) Elasticsearch:cluster-name(該字段見 Elasticsearch 安裝包中的 elasticsearch.yml,可自行修改) 七牛云(需要新建一個七牛云的對象存儲空間,用來存放上傳的頭像圖片)
2)logback-spring-develop.xml:
LOG_PATH:日志存放的位置
每次運(yùn)行需要打開:
MySQL Redis Elasticsearch Kafka
另外,還需要事件建好數(shù)據(jù)庫 greatecommunity,然后依次運(yùn)行項(xiàng)目 sql 文件夾下的這幾個 sql 文件建立數(shù)據(jù)庫表:

?? 數(shù)據(jù)庫設(shè)計
用戶 user:
DROP?TABLE?IF?EXISTS?`user`;
SET?character_set_client?=?utf8mb4?;
CREATE?TABLE?`user`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`username`?varchar(50)?DEFAULT?NULL,
??`password`?varchar(50)?DEFAULT?NULL,
??`salt`?varchar(50)?DEFAULT?NULL,
??`email`?varchar(100)?DEFAULT?NULL,
??`type`?int(11)?DEFAULT?NULL?COMMENT?'0-普通用戶;?1-超級管理員;?2-版主;',
??`status`?int(11)?DEFAULT?NULL?COMMENT?'0-未激活;?1-已激活;',
??`activation_code`?varchar(100)?DEFAULT?NULL,
??`header_url`?varchar(200)?DEFAULT?NULL,
??`create_time`?timestamp?NULL?DEFAULT?NULL,
??PRIMARY?KEY?(`id`),
??KEY?`index_username`?(`username`(20)),
??KEY?`index_email`?(`email`(20))
)?ENGINE=InnoDB?AUTO_INCREMENT=101?DEFAULT?CHARSET=utf8;
討論帖 discuss_post:
DROP?TABLE?IF?EXISTS?`discuss_post`;
SET?character_set_client?=?utf8mb4?;
CREATE?TABLE?`discuss_post`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`user_id`?int(11)?DEFAULT?NULL,
??`title`?varchar(100)?DEFAULT?NULL,
??`content`?text,
??`type`?int(11)?DEFAULT?NULL?COMMENT?'0-普通;?1-置頂;',
??`status`?int(11)?DEFAULT?NULL?COMMENT?'0-正常;?1-精華;?2-拉黑;',
??`create_time`?timestamp?NULL?DEFAULT?NULL,
??`comment_count`?int(11)?DEFAULT?NULL,
??`score`?double?DEFAULT?NULL,
??PRIMARY?KEY?(`id`),
??KEY?`index_user_id`?(`user_id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8;
評論(回復(fù))comment:
CREATE?TABLE?`comment`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`user_id`?int(11)?DEFAULT?NULL,
??`entity_type`?int(11)?DEFAULT?NULL?COMMENT?'評論目標(biāo)的類別:1 帖子;2 評論?',
??`entity_id`?int(11)?DEFAULT?NULL?COMMENT?'評論目標(biāo)的?id',
??`target_id`?int(11)?DEFAULT?NULL?COMMENT?'指明對誰進(jìn)行評論',
??`content`?text,
??`status`?int(11)?DEFAULT?NULL?COMMENT?'狀態(tài):0?正常;1 禁用',
??`create_time`?timestamp?NULL?DEFAULT?NULL,
??PRIMARY?KEY?(`id`),
??KEY?`index_user_id`?(`user_id`),
??KEY?`index_entity_id`?(`entity_id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=247?DEFAULT?CHARSET=utf8;
私信 message:
DROP?TABLE?IF?EXISTS?`message`;
SET?character_set_client?=?utf8mb4?;
CREATE?TABLE?`message`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`from_id`?int(11)?DEFAULT?NULL,
??`to_id`?int(11)?DEFAULT?NULL,
??`conversation_id`?varchar(45)?NOT?NULL,
??`content`?text,
??`status`?int(11)?DEFAULT?NULL?COMMENT?'0-未讀;1-已讀;2-刪除;',
??`create_time`?timestamp?NULL?DEFAULT?NULL,
??PRIMARY?KEY?(`id`),
??KEY?`index_from_id`?(`from_id`),
??KEY?`index_to_id`?(`to_id`),
??KEY?`index_conversation_id`?(`conversation_id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8;
?? 部署架構(gòu)
我每個都只部署了一臺,以下是理想的部署架構(gòu):

?? 功能邏輯圖
畫了一些不是那么嚴(yán)謹(jǐn)?shù)膱D幫助各位小伙伴理清思緒。
?單向綠色箭頭:
前端模板 -> Controller:表示這個前端模板中有一個超鏈接是由這個 Controller 處理的 Controller -> 前端模板:表示這個 Controller 會像該前端模板傳遞數(shù)據(jù)或者跳轉(zhuǎn) 雙向綠色箭頭:表示 Controller 和前端模板之間進(jìn)行參數(shù)的相互傳遞或使用
單向藍(lán)色箭頭:A -> B,表示 A 方法調(diào)用了 B 方法
單向紅色箭頭:數(shù)據(jù)庫或緩存操作
?
注冊
用戶注冊成功,將用戶信息存入 MySQL,但此時該用戶狀態(tài)為未激活 向用戶發(fā)送激活郵件,用戶點(diǎn)擊鏈接則激活賬號(Spring Mail)

登錄 | 登出
進(jìn)入登錄界面,動態(tài)生成驗(yàn)證碼,并將驗(yàn)證碼短暫存入 Redis(60 秒)
用戶登錄成功(驗(yàn)證用戶名、密碼、驗(yàn)證碼),生成登錄憑證且設(shè)置狀態(tài)為有效,并將登錄憑證存入 Redis
注意:登錄憑證存在有效期,在所有的請求執(zhí)行之前,都會檢查憑證是否有效和是否過期,只要該用戶的憑證有效并在有效期時間內(nèi),本次請求就會一直持有該用戶信息(使用 ThreadLocal 持有用戶信息)
勾選記住我,則延長登錄憑證有效時間
用戶登錄成功,將用戶信息短暫存入 Redis(1 小時)
用戶登出,將憑證狀態(tài)設(shè)為無效,并更新 Redis 中該用戶的登錄憑證信息
下圖是登錄模塊的功能邏輯圖,并沒有使用 Spring Security 提供的認(rèn)證邏輯(我覺得這個模塊是最復(fù)雜的,這張圖其實(shí)很多細(xì)節(jié)還沒有畫全)

分頁顯示所有的帖子
支持按照 “發(fā)帖時間” 顯示 支持按照 “熱度排行” 顯示(Spring Quartz) 將熱帖列表和所有帖子的總數(shù)存入本地緩存 Caffeine(利用分布式定時任務(wù) Spring Quartz 每隔一段時間就刷新計算帖子的熱度/分?jǐn)?shù) — 見下文,而 Caffeine 里的數(shù)據(jù)更新不用我們操心,它天生就會自動的更新它擁有的數(shù)據(jù),給它一個初始化方法就完事兒)

賬號設(shè)置
修改頭像(異步請求) 將用戶選擇的頭像圖片文件上傳至七牛云服務(wù)器 修改密碼
此處只畫出修改頭像:

發(fā)布帖子(異步請求)

顯示評論及相關(guān)信息
?評論部分前端的名稱顯示有些缺陷,有興趣的小伙伴歡迎提 PR 解決~
?
關(guān)于評論模塊需要注意的就是評論表的設(shè)計,把握其中字段的含義,才能透徹了解這個功能的邏輯。
評論 Comment 的目標(biāo)類型(帖子,評論) entityType 和 entityId 以及對哪個用戶進(jìn)行評論/回復(fù) targetId 是由前端傳遞給 DiscussPostController 的

一個帖子的詳情頁需要封裝的信息大概如下:

添加評論(事務(wù)管理)

私信列表和詳情頁

發(fā)送私信(異步請求)

點(diǎn)贊(異步請求)
將點(diǎn)贊相關(guān)信息存入 Redis 的數(shù)據(jù)結(jié)構(gòu) set 中。其中,key 命名為 ?like:entity:entityType:entityId,value 即點(diǎn)贊用戶的 id。比如 key = ?like:entity:2:246 ?value = ?11 表示用戶 11 對實(shí)體類型 2 即評論進(jìn)行了點(diǎn)贊,該評論的 id 是 246
某個用戶的獲贊數(shù)量對應(yīng)的存儲在 Redis 中的 key 是 like:user:userId,value 就是這個用戶的獲贊數(shù)量

我的獲贊數(shù)量

關(guān)注(異步請求)
若 A 關(guān)注了 B,則 A 是 B 的粉絲 Follower,B 是 A 的目標(biāo) Followee 關(guān)注的目標(biāo)可以是用戶、帖子、題目等,在實(shí)現(xiàn)時將這些目標(biāo)抽象為實(shí)體(目前只做了關(guān)注用戶)
將某個用戶關(guān)注的實(shí)體相關(guān)信息存儲在 Redis 的數(shù)據(jù)結(jié)構(gòu) zset 中:key 是 followee:userId:entityType ,對應(yīng)的 value 是 zset(entityId, now) ,以關(guān)注的時間進(jìn)行排序。比如說 followee:111:3 對應(yīng)的value (20, 2020-02-03-xxxx),表明用戶 111 關(guān)注了實(shí)體類型為 3 即人(用戶),該帖子的 id 是 20,關(guān)注該帖子的時間是 2020-02-03-xxxx
同樣的,將某個實(shí)體擁有的粉絲相關(guān)信息也存儲在 Redis 的數(shù)據(jù)結(jié)構(gòu) zset 中:key 是 follower:entityType:entityId,對應(yīng)的 value 是 zset(userId, now),以關(guān)注的時間進(jìn)行排序

關(guān)注列表

發(fā)送系統(tǒng)通知

顯示系統(tǒng)通知

搜索

類似的,置頂、加精也會觸發(fā)發(fā)帖事件,就不再圖里面畫出來了。
置頂加精刪除(異步請求)

網(wǎng)站數(shù)據(jù)統(tǒng)計

帖子熱度計算
每次發(fā)生點(diǎn)贊(給帖子點(diǎn)贊)、評論(給帖子評論)、加精的時候,就將這些帖子信息存入緩存 Redis 中,然后通過分布式的定時任務(wù) Spring Quartz,每隔一段時間就從緩存中取出這些帖子進(jìn)行計算分?jǐn)?shù)。
帖子分?jǐn)?shù)/熱度計算公式:分?jǐn)?shù)(熱度) = 權(quán)重 + 發(fā)帖距離天數(shù)
//?計算權(quán)重
double?w?=?(wonderful???75?:?0)?+?commentCount?*?10?+?likeCount?*?2;
//?分?jǐn)?shù)?=?權(quán)重?+?發(fā)帖距離天數(shù)
double?score?=?Math.log10(Math.max(w,?1))
????????+?(post.getCreateTime().getTime()?-?epoch.getTime())?/?(1000?*?3600?*?24);

?? 配套教程
想要自己從零開始實(shí)現(xiàn)這個項(xiàng)目或者深入理解的小伙伴,可以點(diǎn)擊下方卡片關(guān)注公眾號『「飛天小牛肉」』,第一時間獲取配套教程, 不僅會詳細(xì)解釋本項(xiàng)目涉及的各大技術(shù)點(diǎn),還會匯總相關(guān)的常見面試題,目前尚在更新中。
???并向大家強(qiáng)烈推薦我維護(hù)的?Gitee 倉庫?「CS-Wiki」(Gitee 推薦項(xiàng)目,目前已 0.9k star。面向全棧,致力于構(gòu)建完善的知識體系:數(shù)據(jù)結(jié)構(gòu)、計算機(jī)網(wǎng)絡(luò)、操作系統(tǒng)、算法、數(shù)據(jù)庫、設(shè)計模式、Java 技術(shù)棧、機(jī)器學(xué)習(xí)、深度學(xué)習(xí)、強(qiáng)化學(xué)習(xí)等),相比公眾號,該倉庫擁有更健全的知識體系,歡迎前來 star,倉庫地址 https://gitee.com/veal98/CS-Wiki。也可直接下方掃碼訪問
?? 聯(lián)系我
有什么問題也可以添加我的微信,記得備注來意:格式 (學(xué)校或公司 - 姓名或昵稱 - 來意)
?? 鳴謝
博主水平有限,本項(xiàng)目參考牛客網(wǎng) — Java 高級工程師課程,在此感謝老師和平臺。
