从Excel到自研CRM:Deskcomm架构设计与落地全记录

发布时间:2026/9/16 19:23:49
从Excel到自研CRM:Deskcomm架构设计与落地全记录 DeskcommCRM这个项目是我去年牵头做的内部系统。项目推动过程中销售和客服团队总共三十多人日常管理客户的方式还是“Excel表格微信聊天记录个人手机通讯录”谁也说不准某个客户上一次沟通是什么内容、谁在跟进、有没有遗漏。业务负责人一开始想的还是买一套现成的CRM但评估了两周之后这个活最后还是落到我们自己头上。这篇文章会把从需求调研、模块划分、技术选型、核心功能实现到上线后踩坑救火的完整过程写出来给同样考虑自研CRM的同学提供一个真实样本。1. 拍板自研之前先算清楚“为什么不买现成的”1.1 市面上的CRM很多但销售真的不想用如果只看产品彩页市面上的CRM确实功能都很全从线索管理、销售漏斗、商机阶段、回款计划到营销自动化一应俱全。可真要让一线销售每天打开那个系统“做记录”抵触情绪是非常明显的。原因主要有几点一是录入路径太长一个简单的电话跟进往往要点开好几个页面填完还能莫名其妙掉线二是页面信息过载销售关心的是昨天答应客户什么、今天要做什么产品却拼命引导你看漏斗三是很多SaaS系统的定位在“管理销售”并不在“帮助销售”这两种感受差异巨大。拿我们自己内部试用某款轻量版SaaS的经历来说试用两周后打开率已经低到20%大部分同事还是回到微信聊天的老路上。更深层的问题是业务数据安全。我们的客户名单和沟通内容本身就是公司资产放到第三方平台上虽然合同上写了数据归属权但真到导出和删除的时候总会有扯皮。更麻烦的是通讯模块的对接公司已经自建了SIP软交换外部CRM不可能直接对接我们内部电话系统的登录态和分机信息就得在他们标准API之上二次开发成本反而更高。1.2 我们自己的业务场景到底长什么样我们主要业务是面向实体商家的电话咨询和上门拜访客户数量大、单个金额不算高、决策周期短销售人员需要的工具其实不是一套复杂的销售过程管理软件而是一个“能记得住所有客户联系历史的桌面便签”。具体拆开就三件事客户基本信息和联系人每一次电话、微信、当面沟通的记录针对客户建立的待办任务。就这么简单。老板嘴上说要报表但仔细聊下来他要的其实不是销售漏斗那种预测曲线而是能回答几个问题昨天团队联系了多少客户哪些客户已经超过两周没有跟进这个渠道的客户集中分布在哪些区域这些问题完全可以在基础记录之上用几张报表实现不需要一开始就设计复杂的销售过程管理。所以DeskcommCRM这个名字本身就很直白Desk是桌面comm是沟通最终目标是在桌面上把客户资料和通讯事件串起来让“沟通”和“客户档案”自然长在一起。1.3 自研ROI的粗略估算我们当时做过一笔账。SaaS产品如果开通30个账号按主流价格人均每月100到300元一年下来几万到十几万不等这个钱其实能接受。但把定制的成本算进去事情就没这么便宜了。第三方要搞来电弹屏、离职客户批量转移、内部系统的单点登录都要走API轻则按调用量收费重则要买企业版或定制包整体下来一年奔着二十多万去了。自研这边团队正好有两个后端和一个前端排除其他日常任务紧一紧两个月能出MVP服务器一台4核8G的云主机一个月几百块加上开发时间折算总体成本并不比买三年SaaS贵多少而且数据和代码都在自己手里。最大的风险其实是“烂尾”所以我们立项时提了三个硬性条件第一MVP必须砍到不能再砍第二业务部门必须抽出一个人做接口人随时确认需求第三上线日期写死不能无限拖。最终内部决策很干脆DeskcommCRM立项目标不是做第二个纷享逍客而是做一个业务团队愿意每天打开的内部工具。2. 需求拆解把“感觉功能很多”收敛成一张表2.1 和一线sales聊出来的核心场景想要让销售用起来第一步不是画产品原型而是蹲在工位上观察他们怎么工作。我印象最深的是销售接到客户来电时会先潜意识地去翻自己和对方的聊天记录经常要往上翻很久才想起上次说到哪。即使有一张Excel总表Excel也只保存了最基本信息根本不记录过程。于是“来电时带着上下文”成了我们第一优先级需求。第二是录入要快。销售们经常只有一分钟空闲要能快速写下跟客户沟通的结论而不是写一篇小作文。我们的设计指标定成完成一条客户跟进的录入操作不超过10秒包括打开页面、填写内容、点击保存。这个指标听起来很严但做完之后发现真正限制速度的其实不是打字而是页面要不要让用户等、有没有自动补全、有没有预填内容这些交互优化比后端性能更影响体验。第三是团队管理需求。主管不需要看到所有人的客户细节但必须能看到自己小组最近的工作量和重点客户有没有人跟进管理员则需要处理离职员工的客户转移避免客户跟着离职员工一起“蒸发”。这些需求听起来都是常识但如果没有提前访谈很容易做成一锅权限大杂烩。2.2 核心实体与字段设计围绕这些场景数据库层面我们只建了四张核心表clients客户、contacts联系人、follow_ups跟进记录、tasks任务再加一些辅助表。clients保存组织级客户信息比如公司名、地址、渠道来源、所属行业contacts保存具体联系人比如姓名、手机号、座机、微信号、QQ。follow_ups是整套系统里最重要的数据因为它记录了销售和客户的每一次真实沟通。这里我们定了一个原则跟进记录一旦创建就不允许修改如果要补充说明只能新增一条“补充记录”。这个设计刚开始被企业内部质疑过说万一写错了怎么办但实施之后才发现正是“不可篡改”的属性才让跟进记录具备了可信度上级看到的信息才不会被随意粉饰。错别字可以通过追加“纠错”记录处理但历史内容始终保留。tasks则用来承接待办字段上要支持关联客户和联系人还要有due_at和done_at。辅助表包括tags、phone_normalized映射、audit_log等。这里的设计原则是“能少一张表就少一张表”MVP阶段千万不要为了未来扩展而额外建一堆空表因为空表意味着没人维护、没人用的心智负担。2.3 权限模型尽量简化CRM权限如果按大厂标准做可以做到字段级、记录级、角色组多维矩阵但对三十人团队来说这种复杂度只会拖慢开发进度。我们第一版只做了三级普通成员只能看自己创建的客户及关联记录主管可以看自己部门成员的数据管理员可以看全量数据。实现上就是两个字段走天下owner_id和team_id。每个客户在创建时就确定归属人归属人所属部门也一并冗余到客户表里这样主管查询时不需要关联部门表只要WHERE team_id 当前人team_id就行。员工离职时管理员把客户批量转移给新负责人再把旧工号归档数据不会丢。唯一需要注意的就是不要搞“隐藏自己客户给其他组”这种精细权限操作至少在MVP阶段没有意义。2.4 明确不做的清单需求会上总有业务方提出销售漏斗、合同回款、发票开票、营销邮件这些建议我全部记录下来但没有放进MVP。原因很直接每一个功能都要额外的设计和联调成本但核心痛点还是“沟通记录散落”。所以MVP范围严格控制为客户管理、跟进记录、任务提醒、来电弹屏、搜索结果、基础权限、Excel导入。至于漏斗、回款这些后续再说。现在复盘特别庆幸当时没有“顺手做掉”。如果真做了上线时间至少推迟一个月而且团队精力被稀释后连基础功能都未必能做好。这种想加需求的情况每周都有产品负责人要学会说“下次迭代”。3. 技术选型和项目骨架为了“桌面通讯”才选了这套组合3.1 为什么主程序用Electron选型时我们对着“桌面通讯”这个定位纠结了很久。业务团队的现状是电脑上经常打开一堆浏览器窗口如果做纯Web CRM信息容易被埋没系统级通知也受限如果做原生客户端团队没有桌面开发经验现学WinForms或WPF效率太低。最后选了Electron。理由很清楚一是可以直接复用React前端能力两个前端同学不用重新学一门语言二是Electron有能力做系统托盘、全局快捷键、桌面通知、开机自启这些对“一直在桌面帮我盯着消息”的工具来说非常关键。Electron的生态也成熟什么屏幕钉住、剪贴板监听、文件拖拽都有现成库踩坑基本百度一下就有答案。有些人纠结Electron打包文件大、内存占用高但我们做的是企业内工具用户电脑配置不差多占两三百MB内存换来开发效率是值得的。如果你愿意折腾Tauri也是很好的方向只是当时团队没有Rust经验做Demo试了一个下午后放弃。选型讲究的是团队能hold住不是纸面上技术最先进。3.2 后端服务和数据库后端选择的是NestJS框架底层还是Node.js模块化做得比较清楚适合小团队快速产出。为什么不用Java因为我们团队没有专职Java后端Node.js技术栈能前后端复用同一批人。NestJS自带依赖注入、守卫、管道这些能力写起来比Express体系更规整也不太容易出现“每个路由一个风格”的混乱状态。数据库没有悬念地用了PostgreSQL业务数据量不大但客户查重、模糊搜索、JSON字段扩展PG的扩展能力更顺手。ORM用Prisma类型安全做得不错改表之后TS类型能自动同步省了不少写DTO的时间。实时推送使用WebSocket主要用来做来电弹屏和任务通知。部署就一台4核8G的云服务器装上Docker Compose把PostgreSQL、NestJS、Nginx反向代理一次性编排起来没有上K8s也没有搞微服务。这种规模如果强行微服务化只会增加排查问题的成本。3.3 通讯模块设计不是做个拨号器而是和PBX/网关对接DeskcommCRM之所以叫Deskcomm就在于通讯集成不是简单地在系统里加个“记录电话”按钮而是让电话事件自动落到客户档案上。我们公司用的是自建的SIP软交换通话事件能通过API推出来。整体链路是这样的SIP软交换在电话呼入、接通、挂断时通过Webhook把事件推给DeskcommCRM的通信网关模块通信网关模块先对主叫号码做归一化再去客户库查询查到客户后通过WebSocket把“来电客户资料”推给对应座席的桌面客户端如果没查到则给座席一个“新建客户/临时登记”的入口。去电则相反销售点下呼叫按钮系统通过网关发起呼叫并把去电记录登记到对应客户下。这段逻辑开始看着简单实际开发时踩的坑不少比如PBX返回的事件顺序偶尔乱序、Webhook偶发超时、座席分机和登录用户绑定关系不明确等等。通讯模块这件事几乎每一家公司的PBX型号和配置都不同很难有通用方案必须按自己的硬件环境定制。3.4 技术栈总览技术栈整体如下表给考虑类似方案的同学一个参考部分方案选型理由桌面端Electron React Ant Design团队前端背景组件生态成熟后端NestJS TypeScript模块化清晰维护成本低数据库PostgreSQL Prisma强一致性和类型安全实时WebSocket来电推送、任务通知部署Docker Compose Nginx单机规模足够这套组合中规中矩没有任何炫技成分但正是这种“保守选型”让项目在两个月内完成后续排查问题也有社区经验可以抄。4. 开发攻坚三个最容易翻车的功能实现4.1 跟进记录“快进快出”一句话也能存而且必须能回溯拍脑袋想跟进记录接口之前我先列了三条约束接口要好调用、数据要可靠、保存后要能回看。最终POST /follow-ups接收的字段非常简单client_id、content、contact_ids(可选)、task_ids(可选)、datetime。content不允许为空。如果销售只打了一个电话但没谈什么内容也必须写一句“电话未接通”或“短暂沟通后续再回访”之类的内容。这样设计是为了保证每一条通讯都有归宿。后端存储时自动把操作者的user_id和当时的客户端IP写进审计字段。因为是不可变设计表中没有update_at只有created_at和created_by。Prisma的Schema片段如下model FollowUp { id String id default(uuid()) clientId String content String ownerId String contactIds String[] createdAt DateTime default(now()) index([clientId, createdAt]) }我把这张表的索引建在(clientId, createdAt)上查询客户详情时按时间倒序拉取非常快。乐观锁方面我们没有对follow_ups做版本控制因为只插入不更新不存在并发覆盖问题真正需要乐观锁的是clients表的合并操作。这里还有一个很细节的设计前端提交跟随时把当前页面的客户ID直接锁死避免用户在输入期间切换客户导致记录挂错。有时候销售手速极快连续保存两条跟进如果前端没有做节流后端可能收到重复请求。我们在接口层面做了幂等键前端每次初始化表单时生成一个uuid提交时带上后端收到相同uuid就直接返回第一条的结果。4.2 来电弹屏从“拿到号码”到“带着上下文接起电话”来电弹屏是整个系统里最有价值的生态功能也是最容易烂尾的。技术上我们从PBX拿到的初始事件只有频道ID和主叫号码必须由通信网关做号码标准化。标准化的规则当时花了不少时间手机号要去掉前导0和86座机号要去掉长途区号前的0如果来电显示是9开头的5位号码基本判断为内线直接关联公司通讯录特服号、广告推销号段要单独打标记。标准化之后先走一遍精确查重查不到再用模糊匹配给出“可能客户”的列表让座席自己确认。前端收到事件后会用系统级Notification弹窗同时把客户档案窗口顶到前台显示。为了不让每一个骚扰电话都弹屏我们还维护了一个灰名单被销售手动标记过“无效”的号码之后不再弹屏这个细节很受好评。来电事件完整回调用例用NestJS实现的话大概是Post(pbx/events) async handlePbxEvent(Body() event: PbxEvent) { if (event.event_type Ring) { const normalized normalizePhone(event.caller); const clients await this.clientService.searchByPhone(normalized); if (clients.length 0) { this.notificationGateway.pushToUser(event.agentId, { type: incoming_call, clientId: clients[0].id, caller: event.caller }); } } }如果来电号码和客户关联上了座席接起电话之前就能看到这个客户的所有历史跟进记录沟通体验完全是两个层级。这部分上线第一天就被销售部门点名表扬自研系统的价值在这一刻体现得最直观。4.3 全局搜索与重复客户合并全局搜索的入口放在顶栏CtrlK快捷键上销售习惯了会在这里直接敲手机号或客户姓名。最初我们用PostgreSQL的ILIKE查询数据量上千条后依然秒出所以暂时没有上专门的搜索引擎。搜索的返回结果分两类客户名匹配和联系人号码匹配两类结果用不同颜色标识避免用户误认。但真正麻烦的是重复客户。同一客户可能被不同销售导入两次A记录用公司座机B记录用手机号我们靠号码归一化加唯一索引防新增重复但历史垃圾数据必须靠合并功能。合并时需要考虑四个维度联系人合并、跟进记录改挂新客户、任务重分配、搜索索引更新。我们做了一个迁移脚本整体放在一个事务里执行先记录合并日志再执行更新万一失败可以回滚。这里踩的最大坑是忘记处理旧客户下的tags表导致合并后tags丢失后来加了一段级联更新才解决。合并功能本身很敏感所以上线时安排管理员手工确认不能在界面上让普通销售随意合并必须有审批动作并把合并前后关系写进审计日志。5. 上线第一天就暴露的三个真实问题5.1 时区与“昨天”打架上线后销售主管一早打开统计看到“昨日新增跟进”数字不对。他看到的昨日是当天0点到9点但系统按服务器时区UTC计算昨天其实是北京时间的前一天下午4点到凌晨0点很多凌晨产生的记录被分到了错误的日期桶位。排查下来问题出在统计查询一直在用NOW()且依赖Node.js进程时区。修复方案很简单但容易忽略数据库统一存UTC时间所有日期过滤操作都转换成“目标时区对应的UTC区间”再查询。我们写了一个工具函数把北京时间凌晨0点转成UTC时间戳然后按这个区间查询前端展示时再转回本地格式。这个细节不修的话每周统计都会错而且业务方会直接质疑数据可信度。5.2 同一个客户被重复创建大量客户数据从Excel导入那几天重复率肉眼可见高。原因主要是号码归一化规则不够有人在手机号前加了0086有人把座机最后的0删了还有人把“86-010-12345678”这种字符串直接放进去。我们统一在客户端和后端两端做归一化手机号只保留11位数字座机只保留区号和号码但去掉前导0处理过的号码存进一个phone_normalized字段并建唯一索引。导入时如果撞键就返回已存在客户并给出选项“新建联系人还是跳转客户”。这个操作看起来小但把重复率从最初的15%以上降到了1%以内。更重要的是我们在创建客户的接口里加了前端提示如果输入的号码已经存在直接弹窗提醒而不是闷头创建成功让用户之后花时间找。5.3 权限边界漏风权限问题其实是最尴尬的因为我们最初以为模型很简单但实现时还是漏了。列表接口本来是给主管用的结果没有在Query里强制带上team_id普通员工只要手工拼参数就能看到其他同事的客户列表。这是典型的只“藏了入口”没有“护住接口”。修复办法是在后端的Service层统一封装数据范围过滤器规定所有查询必须经过这个函数禁止在Controller里直接访问Prisma模型。同时给关键表加了审计日志谁在什么时间查了哪个客户详情、来源IP和UserAgent都会被记录。安全上没有侥幸心态宁可写代码的时候多一层检查也不要等出事再补。这件事之后我们养成了一个习惯任何接口上线前都用一个低权限账号实际测一遍用“能不能越权”当验收标准之一。6. 运营策略和最终收益这个系统是怎么被团队真正用起来的6.1 降低录入门槛才是全线要解决的核心命题上线之前我最担心的是“销售装都不装”。所以除了功能我们还做了几个运营动作给客户端加了全局快捷键CtrlK打开搜索、CtrlShiftF快速创建跟进记录表单支持随手写点文字后自动保存草稿团队每周抽出二十分钟做一次“录入答疑”。更重要的是我们直接把来电弹屏和历史记录合并到一个页面让销售从“填写记录”变成“确认记录”。因为系统已经自动带出了大部分内容他们只需要补一句结论。上线第二周日新增跟进记录就稳定在一百二十条以上。这个事实说明产品好不好用不是看界面多漂亮而是看完成核心任务需要几步。每多一步留存率就会掉一个档次。6.2 Excel数据迁移洗数据才是最费时间的项目的另一个隐形工作是数据迁移。公司三年多的Excel客户表格总共一万多行但质量参差不齐有手机号写“暂无”的有客户名和联系人混在一个框里的有重复三遍的行政区域。我们写了一个临时清洗脚本把电话号码、微信、QQ、地址、联系人姓名按规则拆到对应字段再用之前说的归一化逻辑去重。导入设计成幂等某一行如果导入失败不会导致前面已导入的数据回滚而是把失败原因记录在案修完可以重新触发。这个经验特别值得注意否则盲目导入可能导致客户表崩溃。迁移完成后我们还会导出几十条客户数据做人工抽检核对地址、渠道来源有没有错位。数据质量是CRM的命根子宁可多花几天洗数据也不要把脏数据倒进系统里。6.3 自研CRM到底值不值复盘时我给自己三个维度打分业务价值、技术价值、可持续性。业务价值上销售找历史沟通记录的时间从平均四五分钟缩短到十秒以内主管能很容易发现哪些客户长期没有跟进这直接影响了客户维护质量。技术价值上团队获得了IM连接、WebSocket推送、数据迁移、权限治理一整条实战经验这些不是花时间看教程能学到的。可持续性上因为系统客户量不过几万单机部署完全够用后续只需要不断完善导入导出、BI报表和移动端查阅能力即可。对我来说这个项目的最大收获不是技术复杂度而是“把工具做成团队愿意用”的产品思维。如果再来一次我依然会坚持自研但不是因为外部CRM不行而是我们需要的场景太小众太具体只有内部团队才会愿意打磨这些看起来不性感但极其重要的细节。最后分享一点个人经验我们在项目初期差点把“跟进记录可修改”做成默认选项还好坚持了不可变设计。现在每当销售想补一句我们只需要保留原记录并添加补充这比直接改内容真实得多。如果你准备做类似的内部工具建议第一周不要写代码先去工位上蹲两天记录真实操作路径你得到的价值比读一百篇技术文章都大。DeskcommCRM目前还在公司内部运行每次看到同事在屏幕上敲CtrlK检索客户的时候我都觉得当初这个决定是值得的。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询