據(jù):自建CRM的架構(gòu)設(shè)計與落地實戰(zhàn))
說到CRM系統(tǒng)很多人第一反應(yīng)就是Salesforce、HubSpot這些龐然大物但真正落到中小團(tuán)隊或者垂直業(yè)務(wù)場景里你會發(fā)現(xiàn)這些通用方案要么太重、要么太貴最關(guān)鍵的是——它們和“通信”這件事的集成往往做得非常割裂。我一年多前開始動手做DeskcommCRM起因特別樸素團(tuán)隊每天有大量客戶溝通分散在郵件、微信、電話和工單系統(tǒng)里銷售跟進(jìn)一個客戶要先翻三四套工具才能拼出完整對話鏈談何效率。DeskcommCRM的定位就是“桌面通信型CRM”把客戶檔案、溝通記錄、商機(jī)推進(jìn)全部拉通到一個工作臺消息自動沉淀、關(guān)聯(lián)、可檢索整套系統(tǒng)做到“開箱即用”的同時還能按團(tuán)隊節(jié)奏自定義。這篇文章會把整個項目的設(shè)計思路、數(shù)據(jù)建模、通信集成方案、以及我在落地過程中踩過的坑完整拆開來講。如果你正準(zhǔn)備自建一套CRM或者想改造現(xiàn)有客戶管理流程這篇文章應(yīng)該能幫你省掉不少試錯成本——尤其是通信記錄歸檔和多人協(xié)作權(quán)限這兩塊幾乎每個做CRM的人都會栽跟頭。1. 項目定位為什么做DeskcommCRM1.1 傳統(tǒng)CRM的致命傷通信和數(shù)據(jù)是斷開的先聊一個很現(xiàn)實的場景。假設(shè)你是一個十幾人的銷售團(tuán)隊用著市面上某款通用CRM每天的工作流是什么樣的早晨打開CRM看今日待辦然后切到郵箱處理客戶詢價再打開企業(yè)微信和幾個潛在客戶聊方案下午還要登錄呼叫中心系統(tǒng)導(dǎo)出通話記錄。到了晚上寫跟進(jìn)記錄時你得把這一天的對話從各個系統(tǒng)里翻出來人工歸納成幾百字的摘要填進(jìn)CRM的“跟進(jìn)記錄”字段。這個過程有兩個致命的損耗。第一是時間損耗一個銷售平均每天要花接近40分鐘在“整理溝通記錄”上這不是銷售這是文書工作。第二是信息損耗人工歸納的摘要天然會丟細(xì)節(jié)兩周之后回頭再看這條客戶記錄你可能完全想不起來當(dāng)時客戶糾結(jié)的定制化需求到底是什么——因為那幾句話留在微信里而微信的聊天記錄可能早就被清理了。我做過一次小范圍統(tǒng)計在未使用DeskcommCRM之前團(tuán)隊中大約有60%的客戶歷史信息散落在各個通信工具里不到20%的溝通記錄能完整歸檔到CRM系統(tǒng)中。這意味著客戶關(guān)系管理管理了個寂寞。1.2 DeskcommCRM的核心思路通信即數(shù)據(jù)DeskcommCRM在架構(gòu)上的核心假設(shè)和傳統(tǒng)CRM完全不同不再把“溝通記錄”當(dāng)成客戶對象上的一個附加字段而是讓通信本身成為數(shù)據(jù)主體。這套系統(tǒng)的做法是把郵件、企業(yè)微信、電話、工單系統(tǒng)全部接入統(tǒng)一的消息管道層。每一封發(fā)出去或收進(jìn)來的郵件每一條IM消息每一通電話的錄音和轉(zhuǎn)寫文本都會自動生成一條“溝通事件”然后通過規(guī)則引擎自動關(guān)聯(lián)到對應(yīng)的客戶、聯(lián)系人和商機(jī)。銷售不需要手動歸檔客戶的歷史畫像會自動長出來。說個最直觀的變化。以前銷售問“這位客戶上次提到物流要求是什么”得靠記憶或者翻聊天記錄現(xiàn)在直接在客戶時間線上搜“物流”所有相關(guān)郵件、消息、通話記錄一次性出來的感覺用過的同事說“這才叫CRM”。通信數(shù)據(jù)自動沉淀之后銷售團(tuán)隊的交接效率、陌生客戶冷啟動速度、以及管理者對跟進(jìn)質(zhì)量的判斷都不是靠人的自覺性而是靠系統(tǒng)的結(jié)構(gòu)性約束來保障。1.3 適合誰來參考這套方案DeskcommCRM不是要做成普適性的巨型平臺我的目標(biāo)是服務(wù)那些“溝通密集但客戶規(guī)模不大不小”的團(tuán)隊比如獵頭顧問團(tuán)隊、B2B銷售小組、企業(yè)服務(wù)售后團(tuán)隊這類團(tuán)隊的共同特征是每天有大量的對外溝通客戶的決策鏈路又比較長溝通記錄的管理直接決定了業(yè)務(wù)能否順利推進(jìn)。如果你正在考慮自研CRM或者公司正在從Excel/共享表格遷移到專業(yè)工具那么這套“通信優(yōu)先”的設(shè)計思路是很值得參考的。我接下來講的落地方案既有適合小團(tuán)隊快速驗證的輕量版也有能承載十萬級客戶數(shù)據(jù)的完整版。你可以根據(jù)自己的體量按圖索驥。2. 核心功能架構(gòu)與數(shù)據(jù)模型設(shè)計2.1 客戶、聯(lián)系人、溝通事件的三層模型傳統(tǒng)CRM的表結(jié)構(gòu)通常是Account客戶、Contact聯(lián)系人、Opportunity商機(jī)這三個主對象加上一堆自定義字段。DeskcommCRM在這套標(biāo)準(zhǔn)結(jié)構(gòu)上做了一個關(guān)鍵調(diào)整增加了一個獨立的“CommunicationEvent”核心對象并且把它放在數(shù)據(jù)模型的中間層。簡單解釋一下為什么這么設(shè)計。客戶是公司維度的主體聯(lián)系人是具體的人但實際的業(yè)務(wù)互動本質(zhì)上是一串事件——客戶A在周三發(fā)了一封詢盤郵件銷售B當(dāng)天回復(fù)了周五客戶A的采購添加了銷售的企業(yè)微信聊了十分鐘下周一雙方電話溝通了價格細(xì)節(jié)。這些事件之間是有時間線和因果關(guān)系的。如果把事件打散存放在不同對象里查詢的復(fù)雜度會指數(shù)級上升而且很難回答一個最基礎(chǔ)的管理問題“這個客戶最近一次有效互動是什么時候”所以DeskcommCRM的數(shù)據(jù)關(guān)系圖大致是這樣的Customers客戶 1 ──── N Contacts聯(lián)系人 Contacts聯(lián)系人 1 ──── N CommunicationEvents溝通事件 Customers客戶 1 ──── N Deals商機(jī) Deals商機(jī) 1 ──── N CommunicationEvents關(guān)聯(lián)溝通事件所有溝通事件都有一個統(tǒng)一的基表結(jié)構(gòu)不管它來自郵件、IM、電話還是內(nèi)部備注。這樣設(shè)計的好處是客戶時間線的查詢只需要order by created_at不需要去十張表里分別拉數(shù)據(jù)再合并排序性能會好很多。2.2 統(tǒng)一通信日志的字段設(shè)計CommunicationEvent這張表是整個系統(tǒng)的核心字段設(shè)計必須既要抽象通用性又要保留不同通信渠道的差異化信息。我最終敲定的核心字段長這樣CREATE TABLE communication_events ( id BIGSERIAL PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES customers(id), contact_id BIGINT REFERENCES contacts(id), deal_id BIGINT REFERENCES deals(id), channel VARCHAR(20) NOT NULL, -- email / im / phone / internal_note direction VARCHAR(10) NOT NULL, -- inbound / outbound title TEXT, -- 郵件主題/通話主題 body TEXT, -- 郵件正文/轉(zhuǎn)寫文本/備注正文 source_message_id VARCHAR(128), -- 原始渠道消息ID去重用 source_thread_id VARCHAR(128), -- 郵件thread-id/IM會話ID started_at TIMESTAMPTZ NOT NULL, ended_at TIMESTAMPTZ, created_by BIGINT REFERENCES users(id), metadata JSONB -- 渠道專屬擴(kuò)展字段 ); CREATE INDEX idx_events_customer_time ON communication_events(customer_id, started_at DESC); CREATE INDEX idx_events_thread ON communication_events(source_thread_id);這里有幾個設(shè)計細(xì)節(jié)值得展開。第一source_message_id和source_thread_id這兩個字段必須獨立存。前者用于去重因為郵件系統(tǒng)在retry時可能會重復(fù)推送Webhook后者用于關(guān)聯(lián)同一會話內(nèi)的多封郵件或多條IM消息以便前端做“會話視圖”的聚合展示。第二metadata用了JSONB類型因為電話和郵件的差異化字段差異太大。電話事件需要存錄音URL、通話時長、呼叫方向郵件事件需要存附件URL列表IM事件需要存消息類型、是否已讀。把這些全部固化進(jìn)主表表結(jié)構(gòu)會極其臃腫JSONB是兼顧靈活性和查詢性能的折中選擇。PostgreSQL的JSONB支持GIN索引后面需要按特定metadata字段篩選時不會成為瓶頸。第三每一條溝通事件上可以掛多個關(guān)聯(lián)對象。雖然我的建表語句里只放了customer_id/contact_id/deal_id三個關(guān)聯(lián)字段但實際業(yè)務(wù)中一個群聊可能關(guān)聯(lián)多個聯(lián)系人和多個商機(jī)。這種情況我建議不要用主表的固定字段去硬關(guān)聯(lián)而是單獨建一張event_links關(guān)聯(lián)表多對多關(guān)系放在里面。固定字段只存“主要歸屬”用于列表頁展示完整關(guān)系走關(guān)聯(lián)表。2.3 商機(jī)階段與溝通節(jié)奏的聯(lián)動數(shù)據(jù)模型穩(wěn)定之后業(yè)務(wù)邏輯的核心就是商機(jī)階段和溝通節(jié)奏之間的聯(lián)動。這是DeskcommCRM能真正體現(xiàn)價值的地方。傳統(tǒng)CRM里商機(jī)階段是銷售手動更新的接觸客戶→需求確認(rèn)→方案報價→談判→成交。但銷售經(jīng)常忘記更新或者報喜不報憂導(dǎo)致管理者的報表永遠(yuǎn)是滯后的。DeskcommCRM的做法是把溝通事件作為商機(jī)階段的隱式信號。規(guī)則引擎里設(shè)定了一些行為規(guī)則比如如果一條報價方案的郵件被客戶回復(fù)了正向內(nèi)容通過語義識別分類并且系統(tǒng)識別到客戶在IM中確認(rèn)了“價格可以”那么商機(jī)階段自動從“方案報價”推進(jìn)到“談判中”。如果客戶最近7天沒有任何進(jìn)入系統(tǒng)的溝通事件商機(jī)狀態(tài)自動標(biāo)記為“沉睡”同時給負(fù)責(zé)銷售的待辦列表里推一條“回訪提醒”。同一個商機(jī)關(guān)聯(lián)的溝通事件數(shù)量超過一定閾值比如超過20次但商機(jī)金額低于某個標(biāo)準(zhǔn)自動提醒管理者評估“小單大做”的風(fēng)險。某種意義上這套機(jī)制把“客戶關(guān)系溫度計”變成了一種可以讓系統(tǒng)自動讀取的數(shù)據(jù)指標(biāo)。銷售的動作會自然影響商機(jī)狀態(tài)管理者看到的數(shù)據(jù)不再是銷售自己填寫的“預(yù)計成交時間”而是從溝通行為里推斷出的業(yè)務(wù)進(jìn)展靠譜程度提升一個檔次。2.4 團(tuán)隊協(xié)作與數(shù)據(jù)權(quán)限的最小可用方案權(quán)限設(shè)計是CRM里最容易走極端的部分要么過度設(shè)計導(dǎo)致系統(tǒng)慢如蝸牛要么簡單到所有人能看到所有客戶直接造成撞單和隱私問題。DeskcommCRM采用的方案是“歸屬共享”權(quán)限模型這里的核心概念很簡單每個客戶和每個商機(jī)都有owner歸屬人owner對數(shù)據(jù)擁有完全控制權(quán)非owner成員默認(rèn)不可見除非記錄被顯式共享給某個團(tuán)隊或者owner主動分享了某個客戶。這個模型從早期MVP一直沿用到現(xiàn)在沒有大改過。實現(xiàn)上我在customers表里放了一個owner_team_id字段表示這個客戶歸哪個團(tuán)隊所有團(tuán)隊成員查詢客戶默認(rèn)帶where owner_team_id 當(dāng)前用戶團(tuán)隊ID??鐖F(tuán)隊協(xié)作則通過shares表來實現(xiàn)CREATE TABLE customer_shares ( customer_id BIGINT NOT NULL, team_id BIGINT NOT NULL, permission_level VARCHAR(20) DEFAULT read, -- read / write / admin created_at TIMESTAMPTZ DEFAULT now(), PRIMARY KEY (customer_id, team_id) );這套模型的優(yōu)點在于查詢路徑非常直接。查看客戶列表時先把自己的團(tuán)隊ID放到查詢條件里命中索引就能快速返回不需要做復(fù)雜的行級權(quán)限判斷。共享表只有在用戶明確點擊“分享”時才寫入不會拖累大多數(shù)操作的性能。踩過的坑是不要把“共享”做成自動的。早期我試圖用“同部門自動共享”的規(guī)則來減少手動操作結(jié)果導(dǎo)致銷售之間互相能看到對方的線索撞單投訴成倍增加。權(quán)限這件事寧缺勿濫讓owner掌握完全的顯式授權(quán)才能避免業(yè)務(wù)上的混亂。3. 實操從零搭建一套可用的DeskcommCRM3.1 技術(shù)選型為什么是PostgreSQL Node.js React很多朋友問我自建CRM為什么不用Python/Java這些“更主流”的框架我說你自己試試就知道了。我最終的技術(shù)棧是PostgreSQL存儲全部業(yè)務(wù)數(shù)據(jù)Node.jsNestJS框架提供API服務(wù)前端用React Tailwind CSS做桌面端優(yōu)先的Web界面。選擇這套組合的核心理由不是性能極致而是生態(tài)匹配度。Node.js最大的優(yōu)勢在于處理異步IO密集型任務(wù)大量Webhook回調(diào)、IM消息推送時代碼心智負(fù)擔(dān)很低原生async/await比Java的CompletableFuture不知道順手多少。而郵件、IM、電話系統(tǒng)做集成時各個通信服務(wù)商提供的SDK基本都是提供JavaScript/TypeScript版本的這套技術(shù)棧對接起來幾乎不需要自己封裝中間層。PostgreSQL不用多說生態(tài)成熟、JSONB靈活、全文檢索能力強(qiáng)配合pgx連接池可以輕松扛住千萬行級的事件表。前端選React是因為成熟的組件生態(tài)尤其是做“時間線/會話流”這類交互密集界面時React的狀態(tài)管理方案Zustand能讓開發(fā)效率提升很多。3.2 數(shù)據(jù)庫表結(jié)構(gòu)落地與E-R關(guān)系細(xì)節(jié)前面提過communication_events這里把完整表結(jié)構(gòu)再補(bǔ)全一些特別是customers、deals這兩張核心業(yè)務(wù)表。CREATE TABLE customers ( id BIGSERIAL PRIMARY KEY, domain VARCHAR(255), -- 客戶公司域名用于自動關(guān)聯(lián)郵箱 name VARCHAR(255) NOT NULL, owner_team_id BIGINT NOT NULL REFERENCES teams(id), lifecycle_stage VARCHAR(40) DEFAULT lead, -- lead / active / inactive / churned metadata JSONB, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE UNIQUE INDEX idx_customers_domain ON customers(domain) WHERE domain IS NOT NULL; CREATE TABLE deals ( id BIGSERIAL PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES customers(id), title VARCHAR(255) NOT NULL, amount NUMERIC(12,2) DEFAULT 0, stage VARCHAR(40) DEFAULT discovery, expected_close_date DATE, owner_id BIGINT REFERENCES users(id), created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_deals_customer ON deals(customer_id); CREATE INDEX idx_deals_owner ON deals(owner_id);這里有一個容易被忽略但極其重要的細(xì)節(jié)customers表的domain字段。我當(dāng)時跟郵件服務(wù)商做對接時發(fā)現(xiàn)超過80%的陌生客戶郵件其域名就能界定歸屬。比如你收到一封hannahacme-corp.com的詢盤系統(tǒng)可以自動把domain提取出來先查庫里有沒有acme-corp.com這個客戶如果有就直接關(guān)聯(lián)如果沒有就自動創(chuàng)建一個新客戶雛形。這個“域名自動建聯(lián)”機(jī)制直接讓線索入庫的自動化率高了很多。另外deals表的owner_id我特意設(shè)為可空因為商機(jī)可能沒有被認(rèn)領(lǐng)處于公海池狀態(tài)。這種設(shè)計方便實現(xiàn)“商機(jī)回收”規(guī)則一段時間內(nèi)無人跟進(jìn)的商機(jī)系統(tǒng)可以自動釋放回公海。3.3 通信集成實戰(zhàn)郵件、IM、呼叫中心的三方接入通信集成是整個DeskcommCRM里我最想強(qiáng)調(diào)的部分絕大多數(shù)自研CRM失敗在這塊。郵件接入的推薦方案是用IMAP IDLE或Webhook而不是自己搭一個郵件服務(wù)器。我當(dāng)時用的方案是在NestJS里跑一個IMAP監(jiān)聽服務(wù)連接辦公郵箱的收件箱新郵件到達(dá)后解析MIME體提取from/to/subject/body/attachments再按域名規(guī)則關(guān)聯(lián)到customers表。發(fā)送郵件則通過SMTP發(fā)送發(fā)送成功后把消息存回communication_events。要注意的是IMAP IDLE并不保證實時性某些企業(yè)郵箱服務(wù)商對IDLE的連接數(shù)有限制建議最好還是優(yōu)先使用郵箱服務(wù)商的Webhook推送。IM接入這邊主要做的是企業(yè)微信/釘釘類型的第三方應(yīng)用。流程是在企業(yè)微信管理后臺創(chuàng)建自建應(yīng)用配置客戶聯(lián)系功能通過回調(diào)URL接收新消息事件。拿到消息后解碼、解密、根據(jù)外部聯(lián)系人ID去查找對應(yīng)的聯(lián)系人和客戶。實現(xiàn)層面Node.js官方的加解密庫直接就能用踩的坑主要在“消息去重”上企業(yè)微信在弱網(wǎng)下會重復(fù)推送同一條消息這時候source_message_id的unique約束就派上用場了重復(fù)插入會直接報錯并被吞掉。電話接入相對簡單國內(nèi)主流呼叫中心服務(wù)商都提供話單回調(diào)接口每次通話結(jié)束后回調(diào)一個包含通話時長、錄音URL、主被叫號碼的JSON對象。需要做的是把電話號段關(guān)聯(lián)到聯(lián)系人電話號碼上這塊通常需要人工確認(rèn)一次因為一個公司可能有多個號碼。整體集成完成之后這里有一個實操心得每接入一個渠道都要單獨建立一個“渠道連接狀態(tài)監(jiān)控”頁面展示最近一次消息推送時間、失敗重試次數(shù)、錯誤信息。通信渠道是最容易出問題的環(huán)節(jié)如果出現(xiàn)問題不被及時發(fā)現(xiàn)客戶消息會靜默丟失后果比想象中嚴(yán)重得多。3.4 前端界面客戶時間線的高效交互設(shè)計Web端的界面設(shè)計上最關(guān)鍵的是客戶詳情頁的時間線視圖。這個頁面承載了所有歷史交互是整個系統(tǒng)的使用頻率最高的界面。我的實現(xiàn)思路是把整條時間線做成虛擬滾動列表按天分組渲染。每條事件卡片的左側(cè)是時間軸節(jié)點右側(cè)是內(nèi)容摘要。點擊事件卡片可以展開完整內(nèi)容郵件顯示正文、附件下載列表IM顯示聊天氣泡電話顯示錄音播放器和轉(zhuǎn)寫文本。這個界面最需要注意的性能問題是懶加載。communication_events表一旦積累到幾十萬行如果一次性查出某個客戶的所有事件會導(dǎo)致前端卡頓。步驟是首次只加載最近30天的事件滾動到頂部時再加載更早的歷史數(shù)據(jù)配合PostgreSQL的keyset分頁where started_at last_started_at order by started_at desc limit 50性能非常穩(wěn)定。另外一個我個人很堅持的交互細(xì)節(jié)是全局快捷鍵。在DeskcommCRM里按C鍵直接創(chuàng)建新客戶按F鍵聚焦搜索框按GN跳轉(zhuǎn)到今日待辦。銷售每天要錄入大量數(shù)據(jù)減少鼠標(biāo)移動的時間就是實打?qū)嵉男侍嵘?. 常見問題與排查技巧實錄4.1 郵件同步掉鏈子IMAP IDLE斷開卻不重連這是我上線第一天就遇到的事故。IMAP IDLE連接本身是長輪詢服務(wù)器端如果長時間沒有新郵件發(fā)送會主動斷開連接。如果監(jiān)聽代碼沒有正確處理斷線重連那么這個郵箱的郵件同步就會在某個時間點悄悄停下而監(jiān)控頁面因為只關(guān)注“進(jìn)程是否存活”而看不出異常。解決思路監(jiān)聽模塊除了維護(hù)IMAP連接外還要定期做一次“健康對賬”——每10分鐘拉一次郵箱最近一小時的郵件序列號和本地數(shù)據(jù)庫中最近一小時的郵件message-id做比對發(fā)現(xiàn)差異就觸發(fā)增量補(bǔ)拉。相當(dāng)于系統(tǒng)自帶一個“反熵”機(jī)制時刻保持源端和本地的一致性。4.2 客戶關(guān)聯(lián)錯誤同名公司的幽靈門檻域名自動關(guān)聯(lián)在大多數(shù)情況下是高效的但有一類情況需要特別注意大型集團(tuán)客戶有多個子域比如acme.com和acme-global.com屬于同一集團(tuán)但系統(tǒng)會判定為兩個不同客戶。這種情況下自動規(guī)則不能生成關(guān)聯(lián)否則會把兩個實體的數(shù)據(jù)攪在一起。我的做法是設(shè)置了一個“待合并隊列”當(dāng)系統(tǒng)識別到兩個客戶域名相似度高時編輯距離小于3或共享WHOIS信息不自動合并而是生成一條人工審核任務(wù)由管理員確認(rèn)后進(jìn)行合并操作。合并時要保持所有communication_events的customer_id統(tǒng)一指向保留的那個客戶同時把被合并客戶的數(shù)據(jù)備份到metadata里。這個操作不可逆所以審核邏輯寧嚴(yán)不松。4.3 事件風(fēng)暴客戶批量涌入時如何防雪崩營銷活動投放當(dāng)天系統(tǒng)可能瞬間涌入大量郵件和IM消息。最壞的情況下每個Webhook回調(diào)都會觸發(fā)一次完整的客戶關(guān)聯(lián)邏輯解析域名→查庫→可能創(chuàng)建新客戶→插入事件數(shù)據(jù)庫連接池很快會被打滿導(dǎo)致整個系統(tǒng)對外不可用。我后來加了兩個關(guān)鍵的護(hù)欄。第一是消息隊列削峰所有的Webhook請求先投遞到Redis Stream由獨立worker批量消費(fèi)消費(fèi)時做了批量歸一化處理同一個source_thread_id的消息在100毫秒窗口內(nèi)合并成一條。第二是在事件入庫前增設(shè)了一個輕量級布隆過濾器對source_message_id做存在性判斷如果已存在就直接丟棄大幅降低了重復(fù)消息帶來的數(shù)據(jù)庫寫入壓力。4.4 權(quán)限引發(fā)的數(shù)據(jù)泄漏共享過于激進(jìn)的教訓(xùn)前面一再強(qiáng)調(diào)權(quán)限要收斂是因為我真吃過虧。某次版本迭代產(chǎn)品經(jīng)理提出“同項目組成員可以互看客戶”我為了省事直接把查詢接口的權(quán)限判斷從“按共享表判斷”簡化成了“同team_id直接可見”結(jié)果上線第二天就有銷售投訴線索被同事提前跟進(jìn)。這事的教訓(xùn)是權(quán)限模型沒有“差不多”的說法任何一個邊角條件的放松都會被人性利用。回滾到顯式共享模式之后這類投訴歸零。給正在做CRM權(quán)限模型的朋友幾個具體建議權(quán)限判斷代碼必須統(tǒng)一放在服務(wù)端入口處不要在業(yè)務(wù)代碼里散落“判斷是否是本團(tuán)隊”的邏輯??蛻暨w移、共享、刪除都要寫審計日志誰在什么時候改了誰的可見性要可追溯。涉及到錢商機(jī)金額、合同的字段建議單獨做一層字段級權(quán)限控制普通成員只能看到脫敏金額。4.5 性能優(yōu)化當(dāng)communication_events過千萬行時系統(tǒng)運(yùn)行到第三個月communication_events表突破了千萬行查詢開始出現(xiàn)明顯的卡頓。有幾個優(yōu)化點簡單有效數(shù)據(jù)分區(qū)按日期做RANGE分區(qū)每月一份老數(shù)據(jù)自動遷移到冷分區(qū)。查詢時帶上時間范圍條件可以大幅縮小掃描范圍。索引裁剪不要把全部索引無條件鋪下去。比如idx_events_customer_time已經(jīng)是復(fù)合索引單列customer_id的索引就是冗余的可以刪掉。歸檔策略超過2年的溝通事件body字段移到獨立的object_storage比如S3兼容存儲中主表只保留結(jié)構(gòu)化字段和存儲地址把表體量壓下去。這一套組合拳下來查詢響應(yīng)時間從2秒多降到了300毫秒以內(nèi)對中小團(tuán)隊來說已經(jīng)綽綽有余。寫在最后的幾點體會DeskcommCRM從設(shè)計到落地我最大的體會是“CRM的本質(zhì)不是記錄而是連接”。一個客戶維度的數(shù)據(jù)如果不是從溝通行為中自動長出來的而是靠人工喂養(yǎng)的那它一定會在某個時刻悄然腐爛。反而把通信數(shù)據(jù)自動化沉淀好之后客戶畫像、商機(jī)預(yù)測、團(tuán)隊管理會順理成章地長出價值來。如果你現(xiàn)在正準(zhǔn)備啟動類似的項目我給三個優(yōu)先級建議第一優(yōu)先級是把通信渠道接入做扎實這是整個系統(tǒng)的地基其他功能再花哨也彌補(bǔ)不了數(shù)據(jù)缺失的硬傷第二優(yōu)先級是權(quán)限模型要收斂寧可保守也不要開放第三優(yōu)先級才是界面交互的打磨。方向上抓對了細(xì)節(jié)上的坑總能一個個填平。希望這些經(jīng)驗?zāi)軒湍闵僮邘撞綇澛酚芯唧w問題歡迎在評論區(qū)細(xì)聊。