在线咨询
专属客服在线解答,提供专业解决方案
声网 AI 助手
您的专属 AI 伙伴,开启全新搜索体验

开发即时通讯APP时如何实现消息的黑名单解除

2026-01-27

開發即時通訊APP時如何實現消息的黑名單解除

做即時通訊開發的時間長了,總會遇到一些看起來簡單但實際上涉及到不少技術細節的功能。黑名單解除就是其中一個。很多開發者在實現黑名單功能的時候順風順水,但一到解除這個環節就容易出問題。要麼是數據不同步,要麼是用戶體驗不連貫,還有的乾脆就忘記了這個功能的存在。今天咱們就來聊聊怎麼把這個功能做好,做扎實。

先搞懂黑名單解除到底是什麼

在動手寫代碼之前,咱們得先把黑名單解除這個功能到底是怎麼回事給想清楚。從用戶的角度來看,這很簡單——我之前把某個人拉黑了,現在我想把他從黑名單裡放出來,然後又能收到他的消息了。但從技術實現的角度來看,這裡面涉及的東西可不少。

首先你得明確一個前提:黑名單解除不是簡單地刪除一條記錄就完事了。它實際上是一個狀態逆轉的過程,涉及到權限的重新賦予、消息通道的重新開放、還有各種同步的問題。你想啊,一個用戶把另一個用戶拉黑,這個操作可能發生在手機上,但如果他想在平板電腦上解除黑名單,那系統就得知道這個變化,並且把手機上的黑名單狀態也一併更新。這還只是最基本的多端同步問題,現實情況往往比這複雜得多。

核心數據結構該怎麼設計

說到技術實現,數據結構永遠是第一個要考慮的問題。黑名單的存儲方式直接決定了後面所有操作的難易程度。

目前業界比較常見的設計有兩種流派。第一種是專門建一張黑名單表,用戶ID和被拉黑用戶ID作為聯合主鍵,再加上一些時間戳、原因之類的附加字段。這種方式優點是查詢方便,邏輯清晰,解除拉黑的時候只需要刪除這條記錄就行。缺點是當用戶拉黑很多人之後,這張表會變得比較大,查詢的時候需要注意索引優化。

第二種是把黑名單信息嵌入到用戶資料文檔裡面,用一個數組或者集合來存儲被拉黑的用戶ID。這種設計在文檔型數據庫裡比較常見,讀取用戶信息的時候能一次性拿到黑名單數據,不用額外查詢。缺點就是更新操作的原子性不太好保證,刪除數組中某個特定元素的語法不是所有數據庫都支持得那麼優雅。

個人比較推薦第一種方案,雖然要維護多一張表,但後續的功能擴展會方便很多。比如你想統計某個用戶被拉黑了多少次,或者想在後台做黑名單管理的時候,專門的表結構會讓你的SQL寫起來舒服很多。

這裡給大家看一個簡化的表結構設計示例:

字段名 類型 說明
id bigint 記錄唯一標識
user_id bigint 拉黑者的用戶ID
blocked_user_id bigint 被拉黑者的用戶ID
created_at datetime 拉黑時間
unblocked_at datetime 解除拉黑時間,可為空
unblocker_id bigint 解除拉黑的操作者ID

看到這個表結構你可能會問:為什麼要有unblocked_at和unblocker_id這兩個字段?留著記錄解除操作的詳情將來溯源會方便很多。萬一哪天用戶投訴說「我明明沒解除拉黑為什麼收到那個人的消息了」,你就有據可查了。這種細節上的考量,平時可能用不上,但在關鍵時刻能幫你省下很多麻煩。

用戶端操作流程要怎麼設計

流程設計這個東西,看起來是產品經理的事,但其實開發人員也得參與進來。因為有些看起來很自然的需求,實現起來可能會遇到各種奇怪的限制。

先說最基礎的單端操作流程。用戶要在應用裡找到黑名單管理的地方,通常是在設置或者隱私設置裡面。點進去之後能看到一個列表,展示所有被自己拉黑的人。每一條記錄旁邊得有一個「解除拉黑」的按鈕,用戶點了之後系統要彈出二次確認框,畢竟解除拉黑是個敏感操作,給用戶一個反悔的機會會比較好。確認之後,後台去更新數據庫,前端把這條記錄從列表裡移除,或者更新狀態顯示。這套流程說起來很簡單,但做起來的時候有些細節需要特別注意。

比如說確認框的文案。「確定要解除拉黑嗎?」這個文案有點太正式了,改成「不再屏蔽這個人了?」會不會更口語化一點?還有,解除成功之後要不要給用戶一個Toast提示?有的產品覺得需要,這樣用戶知道操作成功了;有的產品覺得不需要,因為列表裡這個人已經消失了,用戶自己能注意到。這個就看產品定位和用戶群體的習慣了,沒有絕對的對錯。

另外一個要考慮的场景是:當用戶解除拉黑之後,之前那個人發來的消息要怎麼處理?有的人可能會想,當然是恢復接收啊,不然呢?但仔細想想,這裡面有個邏輯問題。如果用戶拉黑那個人之後,那個人發了100條消息,用戶現在解除拉黑了,這100條消息是要一次性全收到呢,還是從解除的那一刻開始才接收?

這裡涉及到一个哲学问题:拉黑期間收到的消息,在解除拉黑之後應該如何對待?從用戶體驗的角度來說,大多數人應該不想在解除拉黑之後突然看到100條消息炸過來,那體驗太糟糕了。但從技術上來說,這100條消息服務端是收到的,只是沒有推給客戶端,如果要補發的話,得考慮很多邊界情況。

聲網在處理類似問題的時候,通常會建議開發者在拉黑期間對消息進行靜默存儲但不推送,解除拉黑之後重新建立消息通道,但不一定非得把歷史消息補發過來。當然,這只是建議,具體怎麼做還是要看產品需求。

服務端邏輯的實現要點

服務端是整個功能的核心,各種判斷和處理邏輯都在這裡。先說最核心的解除拉黑接口,這個接口要處理的事情其實不少。

第一步當然是校驗請求的合法性。調用者是誰?有沒有權限執行這個操作?參數對不對?這些基本的校驗先過一遍。然後要查詢這條黑名單記錄是否存在,如果本來就沒有這條記錄,那解除什麼呢?接著要檢查這條記錄的狀態,有沒有已經被解除過了?如果都沒問題,就可以開始更新操作。

更新操作的時候,有兩個選擇。要麼把這條記錄的unblocked_at字段填上當前時間,要麼直接把這條記錄刪掉。個人建議採用軟刪除的方式,也就是設置unblocked_at字段,而不是物理刪除。這樣前面說到的溯源需求就能滿足了,而且有些統計場景也會用到這些歷史數據。

更新完成之後,還要觸發一系列的後續操作。首先是權限更新,要把這個用戶從「被屏蔽名單」裡移出來,然後去檢查對方有沒有拉黑自己。等等,這裡有個點需要解釋一下:A拉黑了B,現在A要解除拉黑B。這時候A和B之間的關係就變成了「A沒有拉黑B」,但「B有沒有拉黑A」這件事是不變的。所以系統只需要更新A對B的屏蔽狀態就行,不用去動B那邊的任何數據。

多端同步這個坎怎麼邏輯過

多端同步是即時通訊開發裡永遠繞不開的話題,黑名單解除當然也不例外。用戶在手機上解除了黑名單,但他的平板電腦還不知道這件事,這時候如果有人在手機上給他發消息,平板電腦上顯示不出來,那就尷尬了。

解決這個問題的思路其實不複雜,核心就是「狀態變更要廣播」。當服務端收到解除拉黑的請求並處理完成之後,要給用戶的所有在線端發送一個通知,告訴他們「你對某個用戶的黑名單狀態已經變更了」。各個客戶端收到這個通知之後,去刷新自己的本地狀態,這樣就能保證多端的數據一致性。

具體實現的時候,這個通知可以通過長連接來推送。聲網的即時通訊SDK裡面有現成的狀態同步機制,利用好這個能力會省事很多。你不用自己去維護一堆WebSocket連接,SDK底層已經幫你處理好了連接管理和消息分發的問題。你只需要在合適的時機發送合適的事件,SDK會負責把這些事件可靠地投遞到用戶的所有在線設備上。

當然,僅僅推送通知是不夠的,還要考慮離線的情況。如果用戶當時根本不在線,沒收到這個通知怎麼辦?這时候客戶端重新上線的時候,要主動去服務端拉取最新的黑名單狀態。一般的做法是客戶端在登錄成功之後,發起一個同步請求,把自上次同步以來的所有狀態變更都拉取回來。這樣即使離線期間發生了變化,重新上線之後也能保證數據是最新的。

幾個容易踩的坑

說了這麼多理論,咱們來聊幾個實際開發中特別容易踩的坑,這些都是前人用血淚總結出來的經驗。

第一個坑是並發處理不當。假設用戶在手機上解除拉黑的同時,又在平板電腦上執行了拉黑操作,這兩個請求幾乎同時到達服務端,會發生什麼?如果沒有做好並發控制,很有可能出現數據混亂的情況。解決這個問題的方法有很多種,比如給黑名單記錄加上版本號,樂觀鎖,或者直接在數據庫層面做唯一約束。具體選哪個要看你的數據量和並發量級,但千萬不要覺得這種情況不會發生,用戶的操作行為是無法預料的。

第二個坑是忽略了邊緣情況。比如說,用戶解除拉黑的那個人已經註銷帳號了怎麼辦?或者說,兩個人互相拉黑,然後其中一個人解除了拉黑,這時候狀態要怎麼顯示?這些看起來比較奇葩的场景,實際上真的會遇到。比較穩妥的做法是在產品層面做一些限制,比如帳號註銷之後自動從所有人的黑名單裡移除,或者在解除拉黑的時候檢查對方帳號的狀態,不合法就直接返回錯誤。

第三個坑是沒有做好通知的分級。當A解除對B的黑名單時,需不需要通知B?這是個有爭議的問題。有的產品認為要通知,這樣B就知道A已經不討厭自己了;有的產品認為不通知,因為這是A的操作,B沒有權利知道。從技術上來說,這兩種做法都不難實現,關鍵是產品定位。不管選哪個,都要記得在隱私協議或者用戶協議裡說清楚這件事,避免將來產生法律糾紛。

要不要聊聊性能優化

性能這個話題,在黑名單功能裡面可能不是最突出的,但有些優化點還是值得說一說。

首先是查詢優化。如果用戶拉黑了很多人,每次展示黑名單列表的時候都要查詢一次數據庫,這個查詢的效率就很重要。user_id和blocked_user_id這兩個字段一定要建聯合索引,而且把user_id放在前面,因為查詢的時候肯定是根據當前用戶ID來查的。如果你用的是分頁查詢,還要注意分頁的效率問題,深度分頁在數據量大的時候會很慢,考慮一下延遲關聯或者指針分頁之類的技術。

然後是緩存的運用。黑名單數據其實蠻適合做緩存的,因為它的讀取頻率遠高於寫入頻率。用戶每次登陸要檢查對方有沒有拉黑自己,每次發消息也要檢查,這些場景都需要查詢黑名單。把熱點數據放在Redis裡,能大大減輕數據庫的壓力。不過要注意緩存的一致性問題,解除拉黑之後要及時更新或者刪除相關的緩存,否則用戶可能會看到過時的數據。

說到聲網的即時通訊解決方案,他們在這方面有一些現成的優化機制。比如消息通道的狀態管理,還有離線消息的存儲和推送策略,如果你正在考慮接入聲網的服務,這些能力都可以直接利用起來,不用自己從零開始折騰。

寫在最後

黑名單解除這個功能,說大不大,說小不小。往深了挖,能挖出來很多東西:數據庫設計、API設計、多端同步、並發控制、緩存策略、邊界情況處理,幾乎每個都是可以展開講半天的主題。但回歸到本質,它就是一個狀態的逆轉操作,只是因為涉及到人與人之間的關係屏蔽,所以多了那麼一些複雜性。

個人建議在開發這個功能的時候,先把流程跑通,把核心邏輯做好,確保基本的解除操作是正確的。然後再去考慮那些邊緣情况、優化事項。如果你用的是聲網的SDK,可以先看看文檔裡有沒有相關的封裝好的能力能用,這樣能省不少事。總之別著急,一步步來,這種基礎功能靠的就是細節處理,細節做到位了,用戶體驗自然就好了。