十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DeskcommCRM自建实战:数据模型、自动化与避坑指南

DeskcommCRM自建实战:数据模型、自动化与避坑指南 DeskcommCRM这名字乍一看有点意思DeskCommCRM字面上就是“桌面沟通客户关系管理”。我最初接触它就是因为团队在用市面上那套通用CRM时实在憋屈客户数据录进去就像进了黑洞跟进记录零零散散躺在邮件和微信里销售每天手动填表填到怀疑人生管理层想看个靠谱的漏斗还得靠人肉汇总。后来我们索性自己动手围绕“桌面办公场景下的沟通式CRM”这个定位从零搭了一套DeskcommCRM。这篇文章就是这次项目从设计到落地的完整复盘包括数据模型怎么建、跟进闭环怎么做、权限和性能有哪些坑以及那些只会在实际操作中遇到的诡异问题。适合正在选型CRM、准备自建客户管理系统或者单纯想优化团队销售流程的朋友参考尤其适合10到50人规模、以项目制销售或B2B服务为主的团队。1. 为什么团队需要自己的DeskcommCRM从痛点说起1.1 通用CRM的四大痛点在决定自建之前我们用过不止一款市面上的通用CRM。不是说它们不好而是对一个以桌面端办公为主、销售流程偏项目制的小团队来说它们普遍存在四个让人坐不住的问题。第一录入成本太高。通用CRM为了保证适配各种行业表单字段动辄几十个销售每次录一个客户要翻半天下拉框。结果就是大家为了应付考核随便填几个必填项就交差数据质量惨不忍睹。第二沟通记录和客户档案是割裂的。跟进客户主要靠邮件、企业IM和电话但这些沟通内容不会自动关联到客户卡片上。想翻一下上周跟某客户聊了什么得去邮件里搜、去聊天记录里翻效率极低。第三报表对于一线销售没有任何正反馈。管层看的漏斗、转化率对销售本人来说只是“又要填数了”系统成了监控工具而不是帮手。第四权限僵硬。要么全公司都能看所有客户要么只能看自己的再细一点的规则比如项目制协作时临时共享客户很难配置。这些痛点叠加起来原本应该提高效率的系统反而成了负担。团队里开始出现“影子表格”——销售私下用Excel记自己的客户系统的数据越来越失真。到这个程度换掉它就是必须做的事了。1.2 DeskcommCRM的设计定位既然决定自建我们就没打算再做一个“小号的通用CRM”而是针对自己的使用场景重新定义了这个系统到底是干什么的。DeskcommCRM的核心定位用一句话概括就是以沟通记录为主线、以桌面办公为入口、以自动化任务为驱动的客户关系管理系统。它首先强调“Comm”这个基因——所有跟客户的交互邮件、通话、IM消息、线下会议都会沉淀到客户时间线上让客户档案本质上成为一本完整的工作日志。其次它围绕“桌面上怎么最顺手”来设计交互——不做花哨的大屏驾驶舱不做手机端复杂的审批流把高频操作查客户、记沟通、看待办全部压缩在键盘能完成的范围内。最后它把“下一步该做什么”这件事交给系统来推而不是靠销售的记忆力或主管的催促。这个定位决定了我们的技术选型和数据结构都跟通用CRM有显著差异。团队里有人一开始还在纠结用现成开源项目改后来看到这个定位后也明白了底层的客户—联系人—商机模型可以借鉴但沟通记录模型和任务生成逻辑必须自己设计。2. DeskcommCRM核心数据模型一张客户表的演进2.1 基础表设计客户、联系人、商机客户管理系统的地基就是数据模型。我们最终的设计没有搞那种一张大宽表存所有字段的偷懒方案而是拆成了相对标准的三张核心表客户表customer、联系人表contact和商机表opportunity。客户表存的是公司层面的信息比如公司名称、行业、规模、来源渠道、所属销售等。联系人表挂在客户下面一个人可以属于多个客户比如客户公司的采购和老板也可能跳槽后成为新客户联系人。商机表则是每一次可能成交的机会关联到一个客户和一个联系人记录金额、预期成交日期、阶段等。关键点在于这三张表之间不搞复杂的外键嵌套只保留明确的归属关系。客户和联系人是1对N联系人和商机是1对N商机最终都要能追溯到客户。这样设计的好处是查询路径清晰后面做时间线聚合时不用跨太多表。-- 客户表简化示例 CREATE TABLE customer ( id BIGINT PRIMARY KEY, name VARCHAR(255) NOT NULL, industry VARCHAR(80), scale VARCHAR(40), owner_id BIGINT NOT NULL, source VARCHAR(40), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 联系人表 CREATE TABLE contact ( id BIGINT PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES customer(id), name VARCHAR(120) NOT NULL, title VARCHAR(120), email VARCHAR(120), phone VARCHAR(40) ); -- 商机表 CREATE TABLE opportunity ( id BIGINT PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES customer(id), contact_id BIGINT REFERENCES contact(id), owner_id BIGINT NOT NULL, amount DECIMAL(12,2), stage VARCHAR(40), expected_close_date DATE );这套表结构看起来平平无奇但真正实用的是我们在此基础上加的一些约束。比如商机的stage不是自由文本而是用枚举值限定后台再通过配置中心决定每个阶段的可视化颜色和进入下一阶段的必要条件。这样管理看板才统计得起来不然几十种“看起来差不多但写法不同”的阶段会让漏斗彻底失真。2.2 沟通记录如何与客户强关联DeskcommCRM与通用CRM最大的不同在这里。我们把沟通记录设计成了独立的实体而不是挂在某个商机下面的备注。每一条沟通记录interaction至少包含这些字段关联客户ID、关联联系人ID、沟通方式邮件/电话/IM/会议、方向进/出、内容概要、原始内容、沟通时间、记录人。所有跟某客户的沟通按时间倒序聚合就是一条完整的时间线。这个设计的灵感来自我们自己的使用习惯。销售在跟进客户时大脑里打断点的方式不是“这个商机进行到哪了”而是“我上次跟这个客户说过什么、他当时什么反应”。沟通记录独立存储的最大好处是可以跨越商机的生命周期去看整个客户关系演变。一个商机黄了之前的沟通记录不能跟着消失否则三年后客户又回来找我们的时候我们连当年聊过什么都没法还原。为了控制录入成本我们做了两个让步。一是“内容概要”支持语音输入自动转文字二是所有IM消息和邮件可以通过插件自动归档进来。系统允许每一条记录不超过200字的概要但必须写清楚三个要素聊了什么、对方什么态度、下一步是什么。这三个要素后期会直接参与任务生成没有它们后面的自动化就是空中楼阁。2.3 字段设计的取舍原则自建CRM最怕的就是什么都想存最后整出一个录入工作量巨大的系统。我们的字段取舍原则一共三条写在这里供大家参考。第一条可枚举的绝不用自由文本。比如客户来源渠道广告、转介绍、展会、老客户复购等、客户状态潜在、接触中、已成交、流失全部用枚举。这一条会极大地降低后续清洗数据的成本也让各种统计口径能对齐。第二条能通过行为自动推导的字段不让人手动维护。最典型的是“最近跟进时间”这个字段不需要销售去填而是系统在每次新增沟通记录时自动更新。同理商机的“停留时长”也是由状态变更时间自动计算的。省掉这些手动维护销售每次保存表单时就少几个必须动脑子检查的字段。第三条允许少量冗余但只允许由系统维护的冗余。客户表里有一个字段叫last_contact_at这其实冗余于interaction表但我们不去做实时JOIN而是在写入沟通记录时同步更新。这样做牺牲了一点规范化换取了列表页加载速度的大幅提升。凡是用户手动填写的字段绝不允许冗余否则同步永远会出现不一致。3. 关键功能落地从录入到跟进的完整闭环3.1 客户录入与查重第一次沉淀数据数据质量是从录入那一刻开始决定的。我们在设计客户录入功能时最重要的一件事就是查重。如果系统里出现三条“北京某科技有限公司”的重复记录后续所有统计都失去了意义。查重策略没有一上来就上机器学习而是先用组合规则。核心逻辑是公司名称归一化后完全一致或者公司名称中包含对方完整名称且电话或网址一致就判定为疑似重复。这里有个细节公司名称归一化需要做很多字符串处理中英文括号要替换成统一格式“有限公司”和“有限責任公司”要拉齐经常连“北京”这种地域前缀都要特殊处理。def normalize_company_name(name: str) - str: name name.strip().lower() name name.replace(, ().replace(, )) name name.replace(有限责任公司, 有限公司) name name.replace(股份有限公司, 股份公司) # 去掉常见地域前缀如 北京/上海/深圳 等 for prefix in [北京, 上海, 深圳, 广州, 杭州]: if name.startswith(prefix): name name[len(prefix):] break return name查重不是一个动作而是一套流程。录入时实时查重命中疑似重复时弹窗提示“你确定这不是已有客户吗已有客户ID为xxx”批量导入时做全量扫描生成一份疑似重复报告供管理员人工判断每周跑一次定时任务把新产生的疑似重复对列出来由指定的人合并。这套三级机制跑了半年之后系统里的客户重复率从最初的5.3%降到了0.8%以内。3.2 跟进任务的自动生成逻辑客户录进去了沟通记录也在沉淀但怎么保证销售不会把一个热乎的潜在客户晾上三个月DeskcommCRM的答案是用规则引擎自动生成跟进任务。任务生成的核心逻辑是基于“沟通记录里面的下一步”加一系列条件判断。销售每次保存沟通记录时可以选一个“下一步动作”的下拉框比如“两周后打电话报价”“一周后发合同”“月底约见面”。系统根据这个动作生成一条带截止时间的待办任务分配到对应销售名下。光有这个还不够我们还加了两个兜底规则。第一个是沉默客户警报如果一个处于“接触中”状态的客户连续14天没有任何沟通记录系统自动生成一条“客户已沉默建议主动联系”的任务。第二个是商机停滞警报如果一个商机在当前阶段停留超过30天自动提醒销售要么推进阶段要么说明原因。任务不是生成了就完事。我们做了一个看板视图把任务按“今天到期”“未来三天”“已逾期”分组展示。已逾期的任务每天9点会给销售发一封提醒邮件。实测这个机制上线后逾期任务数在两周内下降了差不多40%。问题不是销售不想跟进而是没有系统一直在脑子里替他们记着。3.3 看板与报表管理层真正需要的数字报表是整个项目中商定时间最长的一部分。管理层说想要的数据有很多但大多数在第一次会议上就会被砍掉因为“就算做出来也没人会根据它做决策”。最后我们只保留了三块核心看板每一块都对应一个具体的经营问题。第一块是商机漏斗回答的问题是“从初步接触到签约的每一步转化率是多少”。要注意的是漏斗的横轴是商机阶段纵轴是商机数量或金额而不是客户数量因为一个客户可以有多个商机。第二块是销售个人的任务健康度回答的问题是“每个销售手上的待办积压了多少、逾期率是多少”。这个看板只面向销售个人和主管不做排名公开避免内部竞争导致数据失真。第三块是客户活跃度分布回答的问题是“最近30天有多少客户在正常跟进多少沉默多少已流失”。这块看板直接影响续费预警和客户挽回动作。报表的数据口径整理成了一个文档里面对“商机金额”是合同额还是预期额、“转化率”的分子分母各是什么都做了明确的定义。这个文档可能是整个项目交付物里被翻阅最多的因为只要口径不统一管理层开会时永远会吵起来。4. 实操过程中的工程细节与避坑指南4.1 权限模型谁说销售不能看全部客户权限设计是CRM系统里最容易翻车的地方。最开始我们也是照搬通用方案每个销售只能看到自己名下的客户主管可以看到全组成员的客户管理员看全部。但实际用了一周就发现这个模型在我们团队完全行不通因为项目制销售经常需要临时协作一个销售手上的客户技术支持也要看售前也要看合伙人也要了解情况。后来我们把权限从“看数据”和“改数据”两个维度彻底分开做了四层模型第一层是客户归属决定这个客户主要归谁负责。第二层是可见范围默认情况下所有人可以检索到所有客户的名称和基本信息但详细的沟通记录和商机金额默认不可见。第三层是协作共享客户归属人可以主动把某个客户共享给指定同事共享后对方可以看到完整信息并添加沟通记录但只有归属人有权限编辑客户基本资料。第四层是团队范围管理员可以为某个项目组配置一个共享组共享组内互相可见完整信息。这层模型上线后抱怨一下子少了很多。毕竟对销售来说客户是谁的这个问题很敏感但完全不让人家看见也不利于协作。把“归属”和“可见”分开之后既保护了销售的核心利益又解决了跨角色协作的基本需求。4.2 数据导入的那一夜批量迁移的教训从旧的Excel和旧系统往DeskcommCRM迁移数据是我们踩过最深的坑没有之一。原本大家以为不就是把几百行Excel塞进数据库吗结果真正做的时候发现旧的Excel里同一个客户在不同销售手里有不同写法“某科技有限公司”和“某科技有限责任公司”同时存在电话有的带区号有的不带联系人邮箱大小写都不统一。我们花了一个通宵写清洗脚本反复跑了好几轮才把数据弄干净。这里分享几条血泪教训。第一迁移之前先做排重宁可手动合并也不要带着重复数据上线否则用户一打开系统看到那么多重复记录信任感瞬间就没了。第二日期字段一定要统一格式Excel里“2024/1/5”和“2024-01-05”混在一起的时候直接入库就是灾难。第三先导一小批数据做验证不要一上来全量导入先拿50条客户记录跑通整个流程确认列表页、详情页、看板都正常了再导剩下的。第四迁移过程中保留一份原始数据备份万一出问题还能回滚不要抱着侥幸心理。那次迁移之后我们专门写了一份数据导入手册规定了导入模板的字段格式和必填要求并且要求所有批量导入操作必须在测试环境先演练一遍。这个流程虽然多花了半小时但再也没有发生过一次导入事故。4.3 性能优化从慢查询说起系统上线第三周客户列表页开始变卡点一次切换要等两三秒。排查下来发现是列表查询没有分页而且每次查询都要关联子查询统计每一个客户的商机数量和最近沟通时间。慢查询的根子在于我们前期为了图省事直接在SQL里写了相关子查询。比如查最近沟通时间是SELECT MAX(created_at) FROM interaction WHERE customer_id customer.id这样一个子查询。客户数据量到了两三千条、沟通记录上万条之后全表扫描加子查询的执行时间就完全没法看了。优化方案分三步走。第一步给interaction表的customer_id加上联合索引并利用第2.3节提到的冗余字段last_contact_at把子查询改成直接查冗余字段。第二步列表页强制分页每页50条超过1000条结果引导用户使用搜索筛选而不是翻页。第三步把看板页的统计SQL改成每晚定时跑任务生成汇总表页面直接查汇总表而不是实时聚合。优化之后列表页响应时间从平均2.8秒降到了0.4秒左右。这一步做完团队里的实际反馈是“这系统现在才算能用”。性能问题就是这么现实功能再完善卡顿一下用户就会潜意识里觉得这东西不行。5. 常见问题与排查技巧实录5.1 客户重复录入问题虽然查重规则做了很多层但使用中还是会遇到漏网之鱼。最常见的情况是销售在手机端录入客户输入法自动补全了公司名称里的英文括号或全角空格导致归一化函数没认出来。排查这个问题的技巧是不要靠人肉眼去翻列表而是写一个SQL把可疑的重复对直接查出来。我们的做法是定期跑一个基于相似度算法的脚本对所有客户名称两两计算编辑距离凡是相似度高于90%的自动进入待审列表。这里要注意的是编辑距离对中文名称的相似度判断不算准所以我们还结合了行业和地域字段做加权。最终的效果是每周能抓到三到五条肉眼容易漏掉的重复记录。5.2 跟进记录丢失问题有一个阶段用户反馈说前一天明明记了沟通内容第二天再看客户时间线居然没有了。这个bug排查了很久最后发现原因极其隐蔽我们的前端在提交表单时有一个字段校验失败的提示但因为页面样式问题这个提示被隐藏了用户点了保存以为成功了实际后端根本没有收到请求。这类问题的通用处理思路是给所有写操作加上一个统一的提交状态提示并且在网络层面加一层防止重复提交的锁。同时保存成功后在页面上生成一条带ID的轻提示用户可以确认这条记录已经写进去了。我们在时间线翻页时还加过一次SQL的count和实际列表数量不正确的问题排查最后发现是分页参数从0开始还是从1开始的差异这个排查过程也花费了一个下午。5.3 邮件与IM集成失败问题DeskcommCRM里有一个很有用的功能是自动把发给客户的邮件归档到客户时间线上。但实际运行到第二个月就出问题了一部分邮件在个别客户的时间线上缺失。排查下来发现不是邮件丢失而是邮件关联客户的逻辑有漏洞。我们当时的关联规则是“邮件发件人属于某个联系人的邮箱则归到该联系人所属的客户”但有人给客户公司的新同事发邮件时这个新同事的邮箱并没有录入到联系人表里系统找不到关联目标就默认不归档。处理办法有两个层面。一是扩大关联规则如果邮件正文或主题里包含客户公司名称的关键词也尝试匹配一次客户。二是强制要求对于无法关联的邮件在客户详情页提供一个“手动归档”入口让用户可以临时把一封邮件拖拽到客户时间线上。这个功能上线之后大家在邮件归档上的人工修正压力小了很多而且这之后数据统计也更加可靠了。6. 这个项目还能怎么延伸系统跑顺之后有几个方向是明显可以继续往下做的写出来给同样在自建CRM的朋友一个参考。第一个方向是数据自动清洗的增强。现在查重规则和归一化脚本都属于规则驱动对于很多中文公司名的别名变体比如“阿里”和“阿里巴巴集团”规则逻辑很难覆盖全。下一步我们计划引入向量化匹配但要注意的是成本会比规则高不少适合在数据量真的起来之后再做。第二个方向是任务自动生成规则的可配置化。目前沉默客户和商机停滞的规则是写死在代码里的调整周期需要改代码重新发布。如果做成熟应该做成一个可视化规则管理页面让运营人员自己配置触发条件和动作。第三个方向是移动端的轻量化。虽然我们的核心场景是桌面端但也需要让销售在外出拜访时能快速查看客户历史沟通记录和记录现场反馈。这一块不做完整移动端只做几个高频页面的适配方案就够了。第四个方向是跟数据仓库类工具的打通。让管理层可以直接用自己熟悉的看板工具去连系统数据而不是只能在CRM内看固定报表。这一块涉及到数据接口的设计如果一开始就留好只读账号和视图后面会很方便。这些延伸方向不是每一个都必须在短时间内全部实现但它们会决定DeskcommCRM是停在“一个好用的内部工具”还是能长成一个持续产生价值的团队基础设施。最后再分享一个个人感受自建CRM最大的收获不是省了多少软件的订阅费而是全团队终于对“客户数据到底应该长什么样”这件事有了统一的认识。以前大家在Excel里各写各的现在DeskcommCRM把所有人的工作语言拉齐了。过程中踩过的坑尤其是数据迁移和权限设计那些夜里的崩溃现在回头看都是最值得的学费。如果你也在考虑自建CRM我建议先把权限模型和数据模型这两个地基想清楚再动手写代码一定比我们当时边做边改要顺得多。
返回列表