FEATURED · 精选文章

从零自研桌面端CRM系统DeskcommCRM:架构、选型与踩坑复盘

发布时间 / 2026/9/16 8:53:50
来源 / 创域科博编辑部
栏目 / 资讯中心
从零自研桌面端CRM系统DeskcommCRM:架构、选型与踩坑复盘 去年做的事情里最花心思也最有成就感的是带着两个人从零自研了一套桌面端客户管理系统内部代号 DeskcommCRM。这个名字没什么玄学就是 Desk Communication CRM 的缩写核心解决的是客服和销售同学每天在桌面电脑上高频处理的那些事客户资料、在线聊天、工单跟进、回访记录。我之前用过很多现成的CRM也帮客户部署过开源的、商业的但这回是完完全全自己把需求、架构、编码、上线的环节走了一遍踩了不少坑也沉淀下很多文档里查不到的经验。这篇文章就当是一份项目复盘把当初为什么这么设计、哪些环节最容易被低估、实操中有哪些坑一次性讲清楚。如果你正在犹豫要不要自研一套内部系统或者已经在规划类似的客户管理系统这篇应该能让你少走几个月的弯路。这套 DeskkcommCRM 不是简单做一个客户表格它更像一个桌面端的综合工作台。客服坐席能在一个窗口里同时处理微信公众号、企微、网页询盘等多种来源的客户消息点击客户就能看到全部历史信息再顺手把一条消息转成工单派给技术、仓库或者售后。销售这边能按公海规则领取客户、填写跟进记录、设置下次跟进时间主管能实时看到团队今天新增了多少客户、跟进了多少条。文本、通话状态、工单状态全部集中到一个查询入口。对这个项目感兴趣的人不管是产品经理、后端开发还是前端同学都能从里面找到可以借鉴的方案。1. 别急着写代码先把业务现场“摸”清楚任何一个内部系统如果一开始就陷入页面细节后面必定返工。DeskcommCRM 启动前我在客服团队和销售团队里各蹲了一周拿着小本子记他们每天打开什么软件、切换多少次窗口、用什么表格记录、被什么流程卡住。这一周比后面一个月开发都有用。1.1 业务侧的真实痛点先把业务现场看到的矛盾整理成一张表。这些痛点都不是我凭空猜的是员工自己吐槽最多、管理者也认可的问题。痛点具体表现对业务的影响客户资料分散客户信息散在Excel、手机通讯录、微信聊天记录、纸质名片里找人靠翻聊天记录交接客户时遗漏严重消息渠道割裂同一客户可能在公众号问一次、微信又问一次两边内容对不上重复沟通客户体验差坐席无法快速掌握上下文跟进靠自觉业务员口头说“我跟进了”但系统没有留痕管理者无法掌握真实销售过程离职带走客户关系工单无闭环客户报修后客服要口头转告技术技术做没做全靠问响应慢、漏单多、客户投诉无追溯数据无沉淀月底复盘只能靠人工翻聊天记录统计管理层拿不到准确的转化数据这些痛点单独看都不算复杂但合在一起就指向同一个结论需要一个统一客户视图把所有客户触点上的信息串起来并且让每一次互动都有迹可循。这就是 DeskcommCRM 立项时的核心理念以客户为中心而不是以部门为中心。1.2 为什么选自研而不买现成系统团队内部当时吵过一轮有人说直接买一套SaaS年费也不贵何必养人开发。我的看法是要看这家公司的业务形态是不是足够“通用”。我们当时的业务有强定制诉求多来源消息实时合并、局域网环境下的软电话状态、工单要跟仓储系统对接这些点买来系统往往要二次开发而二次开发的成本未必低于自研。另外一个重要因素是可维护性。自研系统最值钱的不是代码而是团队对业务的建模能力。DeskcommCRM 的最大资产不是功能列表而是把客户、会话、工单、分配规则这些概念在数据库层面理清楚了。有了这套模型以后加一个渠道、加一种工单类型都是在既定骨架上添肉而不是推倒重来。1.3 项目范围和里程碑项目范围必须一开始就锁死不然越小越大的需求会把这个项目拖垮。第一版 DeskcommCRM 只做四件事统一客户档案、在线会话接入、跟进记录留痕、工单闭环。那些漂亮的数据大屏、移动端App、智能客服机器人全部放到二期再说。里程碑也定得很明确第一周做需求梳理和数据库设计第二个到第四周做核心服务端接口和客户端页面第五周联调第六周测试加试运行。整个团队只有三个人一个负责客户端一个负责服务端我兼顾数据库和项目推进。现在回头看这个节奏其实是比较紧凑的但正因为范围卡得死才能在六周内拿出一个能真正给业务用的版本。2. 技术选型没有最好只有最不折腾技术选型是这类内部系统里最容易翻车的地方。常用的方案比如直接用Java一套Spring Boot打天下或者前端用Vue加一个管理后台模板都可行。但我更看重“三个人在六周内能稳定交付”这件事所以原则是选团队最熟、生态最稳、踩坑资料最多的组合而不是网上最新最酷的组合。2.1 客户端技术栈为什么是Electron而不是TauriDeskcommCRM 这个名字里带 Desk说明它天生就是一个桌面应用。为什么不做成网页版因为坐席要长时间盯着电脑桌面应用能常驻托盘、来消息时弹气泡、本地缓存离线数据这些在浏览器里实现要么受限制要么体验别扭。客户端技术栈里真正候选的只有 Electron 和 Tauri。Electron 的缺点是包体大、内存占得多但优点是生态成熟遇到问题能搜到答案。Tauri 包体小、性能好但当时它的生态还不够稳而且团队要额外熟悉 Rust。我衡量了一下这个项目的瓶颈从来不是性能而是开发速度。所以最终选了 Electron Vue 3桌面端界面用 Web 技术写团队成员没有任何学习成本。实测下来一个坐席常驻内存大概在 400MB 左右对现在的主流办公电脑来说完全能接受。2.2 服务端与数据库选型服务端用了 Go主要考虑并发性能好、部署就是一个二进制文件在客户内网环境里非常省事。数据库一开始在 MySQL 和 PostgreSQL 之间犹豫后来因为要做大量的报表统计和 JSON 字段扩展选了 PostgreSQL。它的 JSONB 类型对保存多来源消息的扩展字段非常友好后面会详细说。消息推送这部分第一反应是 WebSocket 自研后来为了稳定性直接在客户端和服务端之间维持一条 WebSocket 长连接同时用 Redis 做会话在线状态和分布式锁。我提一嘴自己的经验团队如果没有特别强的即时通讯背景不要自己造消息协议直接用成熟的 WebSocket 库加 JSON 消息体最稳够用就好。2.3 整体架构一条长连接两个核心库整体架构按“客户端 - 接入层 - 业务层 - 数据层”四层来设计。客户端是 Electron 桌面应用负责渲染页面、维护 WebSocket 长连接、本地缓存必要数据。接入层是一组 API 网关和消息网关统一接收 HTTP 请求和 WebSocket 消息做身份鉴权和路由转发。业务层拆成客户服务、会话服务、工单服务、用户权限服务四个模块每个模块独立维护自己的表通过接口互相调用。数据层就是 PostgreSQL 加 Redis。当时很多人建议我直接上微服务Eureka、Feign 那一套我都听进去了但没采纳。原因很简单这个项目的规模用单体应用绰绰有余微服务带来的网络开销、部署复杂度、链路追踪成本对我们三个人来说是纯消耗。最终做成了一个可拆分的单体模块之间按包隔离以后如果用户量真的起来了再按现有边界拆成独立服务成本也不高。3. 数据库建模DeskcommCRM 的灵魂在表里如果一个CRM系统页面很好看但数据库一塌糊涂那这个系统基本就是一次性玩具。DeskcommCRM 的这个环节我花的时间最长。核心要回答的问题是客户、联系人、会话、工单、跟进记录这些实体到底有什么关系怎么存才能既满足业务查询又不会在并发下出乱子。3.1 客户主数据一个客户、多个联系人、多个来源标识客户主数据设计是整个系统的地基。我采用的模型是客户表customer存核心信息包括客户名称、行业、等级、所有者、公海标记、创建时间、更新时间联系人表contact存客户的多个联系人姓名、电话、邮箱、职位渠道标识表customer_channel存这个客户在各个渠道来源上的唯一标识比如公众号openid、企微external_userid、网页访客ID。为什么要把渠道标识单独抽一张表因为同一个客户很可能是从公众号来的同时又被销售用手机号加进了系统。如果不做这张表客户之间的合并去重就会变成噩梦。用 customer_channel 表之后合并客户的操作就变成了把一张表的渠道标识改指向另一个客户ID业务逻辑非常清晰。3.2 会话消息存储JSONB 是懒人的最佳武器消息数据是 CRM 里最容易膨胀的。每天的聊天记录少则几千条多则几万条如果每条都设计成一张宽表字段会非常难维护。DeskcommCRM 的做法是消息主表只存公共字段包括消息ID、会话ID、发送者ID、接收者ID、消息类型、消息内容、发送时间、渠道类型渠道特有的字段统一放到一个 JSONB 列里面。比如公众号消息里可能有 msg_id、scene网页消息里有 user_agent、来源页这些字段不需要都建模成单独列直接塞进 ext_json。查询需要用到这些字段时PostgreSQL 的 JSONB 支持 GIN 索引可以高效过滤。这个设计让新增渠道时不用频繁改表结构节省了大量迁移成本。当然JSONB 不是万能药如果业务需要频繁联表查询这些扩展字段还是要拆出来建列但在这个系统里它非常够用。3.3 会话设计一个客户同时只允许一个有效会话会话是连接客户和坐席的桥梁。刚开始我的设计很天真一个客户可以有多个会话每次联系都新建。后来发现这是灾难因为同一客户来回咨询时坐席根本不知道应该看哪个会话。后来改成一个客户在同一渠道下同时只允许一个有效会话新的消息进来时如果没有有效会话就自动创建如果有就直接挂在已有会话下。这个逻辑听起来简单落地时要注意并发。同一客户的两条消息几乎同时到达时不能让系统创建两个会话。当时用 Redis 做一个分布式锁锁的 key 是 customer_id channel消息进入会话服务时先尝试加锁加锁失败就说明已经有其他请求在处理这个客户直接复用已有会话。这个设计在后面支撑了几百个坐席同时在线聊天的场景没有出现会话错乱。4. 核心业务实现四块最难啃的骨头是怎么拆解的技术架构定得再好到最后还是要落到业务细节上。DeskcommCRM 开发过程中有四块功能的复杂度远远超出预期。每踩过一个坑我就记录下来这些经验在普通文档里很难找到。4.1 消息统一网关从各渠道到坐席桌面的“管道工”多来源消息的统一接入是 DeskcommCRM 实现的第一块硬骨头。要让公众号、企微、网页离线消息都能进入同一个消息池再推送到坐席客户端必须抽象一个消息网关组件。消息网关的核心做法是每一种外部渠道都实现一个标准接口接口的输入是渠道原始报文输出是一条统一的内部消息结构。比如公众号回调会验签、解密企微回调要验证消息来源 IP网页离线消息则是走普通 HTTP 接口。把这些差异全部在“渠道适配器”里消化掉业务层完全不感知消息来自哪个渠道。推送方面客户端和服务端的 WebSocket 连接断线重连要特别注意心跳机制。我们用的是每 30 秒发一次 ping服务端在 90 秒内没收到 ping 就标记连接已断开。刚开始没有断线重连坐席聊着聊着就收不到消息了只能刷新页面后来封装了重连逻辑带指数退避策略第一次 5 秒、第二次 10 秒、第三次 20 秒最多延迟到 60 秒一次这个体验才稳定下来。4.2 客户360视图合并客户时如何保住聊天记录客户360视图是产品上的亮点但在技术上是典型的“宽表查询”。坐席点击一个客户需要同时看到客户基本信息、所有联系人的联系方式、最近聊天记录、历史工单、跟进记录。这个接口如果联查五六张表性能一定扛不住。我的做法是冗余加聚合。客户信息本身走主查询聊天记录只返回最近 20 条按会话时间倒序工单和跟进记录分别走独立查询最后在服务端组装返回。这个接口虽然调用三次查询但因为索引设计得好单次响应时间基本控制在 200 毫秒以内。页面端再用骨架屏先渲染客户基础字段其他模块异步加载用户体验就很顺滑。比较麻烦的是客户合并。两个客户是同一个人的时候坐席要把它们合并聊天记录不知道跟谁走。我们的方案是合并时把客户所有渠道标识和联系人转移到目标客户ID上聊天记录表里冗余的 customer_id 统一 update历史会话全部保留。这个操作放在一个事务里执行加上一个状态字段标识合并中避免操作过程中有新消息写入数据就不会错乱。4.3 工单自动流转既要有闭环又不能太机械工单是 DeskkcommCRM 里唯一一个真正需要“流程状态机”的模块。工单的状态我定为待分配、处理中、待验收、已关闭。状态流转规则如下客服创建工单时默认是待分配主管手动分配或系统按规则自动分配给某个处理人处理人认领后变为处理中处理完成后标记待验收创建人验收通过后变为已关闭。这套规则看着直观实际写代码时踩了一个坑如果客户在处理中又追加了一条诉求工单应该保持处理中但需要给处理人发一条提醒。后来我给工单表增加了一个 remind_count 字段处理人每次回复或者关闭前都能看到“该工单有客户追加消息”的提醒这个细节让工单系统的体验上升了一个台阶。关于自动分配我实测下来不要一开始就做复杂算法。先按工单类型和组内人员当前待处理数量做简单轮询分配就够了。要让一个工单系统好用最关键的是“超时提醒”待分配超过30分钟发提醒给主管处理中超过48小时未更新发提醒给处理人和主管。这些提醒规则全放在定时任务里每5分钟扫一次简单直接效果却非常明显。4.4 桌面客户端的本地缓存与离线消息桌面端最容易被忽视的是离线场景。网络断了坐席正在输入的回访记录不应该丢客户消息断线期间也不能丢。DeskcommCRM 的客户端里用 SQLite 做了本地缓存正在编辑的跟进记录定时自动保存到本地草稿箱网络恢复后再同步到服务端。消息的可靠性保障则用了“消息确认”机制。服务端推送消息时带一个消息序列号客户端收到并落库成功后回 ack服务端没收到 ack 就认为消息未到达等客户端重连后重新推送客户端本地也能根据序列号去重不会重复展示。这套机制下来实际使用中几乎没有出现过丢消息或者重复消息坐席反馈“比原来用的系统稳多了”。5. 上线落地从开发到业务真正用起来中间隔着一个鸿沟很多技术团队以为把代码写完部署就完事了实际上真正的大头在后面。DeskcommCRM 开发只用了不到六周但数据迁移、权限配置、试运行、培训、问题收集前后又折腾了一个多月。这个阶段拼的不是技术能力而是细心和耐心。5.1 数据迁移Excel 里的“脏数据”怎么处理旧数据主要在两处一个 Excel 客户花名册大概有 1 万多个客户记录另一个是旧工单系统里的历史工单大约 8000 条。第一版导入脚本跑完之后一检查崩溃了手机号位数不对、客户名带空格、重复客户超过 3000 个、只有客户名没有联系人的记录占了三成。后来我在导入脚本里加了清洗规则手机号必须为 11 位数字邮箱必须包含 符号同一手机号或同一公司名下存在多条记录时优先保留最近更新的一条其他标记为待合并所有空白字段统一填充为 NULL。清洗完再导入数据的可用性大大提高。经验就是写导入脚本之前一定要先在 Excel 里做一次数据体检知道老数据到底脏到什么程度再定清洗规则。5.2 灰度发布先让一个小组用起来上线第一天我没让全员用只让客服一组大约 8 个人先切换过来其他组继续用老Excel。这一招太关键了。第一周就发现了几个只在真实并发下才会出现的 Bug比如坐席同时接待多个会话时WebSocket 推送偶发会串会话Windows 老版本系统上字体渲染错位。如果全员一起切那几天估计就要被骂惨了。灰度期间的日志和监控非常重要。我当时给系统加了一个简单的埋点前端上报路由切换、按钮点击、接口报错后端记录请求耗时、慢SQL、WebSocket连接事件。每天下班前花半小时看日志汇总把高频问题排进第二天的修复列表这样迭代节奏非常高效。等客服一组的周报出来客户响应时长从平均 2 小时降低到 20 分钟管理层看到数字后面推全员切换就毫无阻力了。5.3 权限与安全细节不能让销售看到全公司的客户CRM 系统权限设计一旦出错轻则业务人员互相抢客户重则客户信息泄露。DeskcommCRM 的权限模型分为三个层级系统管理员、主管、普通坐席/销售。系统管理员可以配置所有字典和权限主管能看到本部门所有客户和工单可以分配公海客户普通成员默认只能看到自己名下的客户、自己创建的工单和分配给自己的会话。这个模型不复杂但字段级权限容易忽略。比如客户表里有一个“备注”字段主管填写的内部沟通信息不希望普通销售看到所以权限不只要控制到表还要控制到列。实现上我用了一套简单的注解配置每个接口返回前过一遍字段权限过滤器命中敏感字段就自动抹掉。后期我又在客户端和服务端都对敏感操作做了审计日志记录谁看了哪个客户的联系方式都查得到。安全这个东西宁可做过头不能留漏洞。6. 运行后的维护问题排查与性能优化实录系统上线稳定运行后并不意味着可以放松了。日常维护中最常遇到的问题其实不是功能缺少而是性能劣化和数据异常。这里把这段时间积累的排查经验整理出来全是实测过的。6.1 高频问题速查表直接列一个表格比较直观现象可能原因排查手段解决办法坐席收不到消息WebSocket 连接断了没重连查看在线状态日志和心跳记录重启客户端检查是否需要升级重连逻辑消息偶发重复ack 确认丢失服务端重推查看消息确认表和客户端去重日志增强去重逻辑以消息序列号为准查询客户越来越慢数据量增长索引失效慢SQL日志 explain 分析补充联合索引对老数据做归档公海分配不及时定时任务执行时间过长查看任务队列堆积情况优化批处理SQL添加执行超时监控文件上传失败上传目录磁盘满检查磁盘空间和日志接入对象存储或定期清理策略权限显示异常用户角色缓存未刷新查看Redis中角色缓存用户修改角色后主动清理缓存6.2 印象最深的两个故障第一个故障是数据库连接池被打满。某个周五下午坐席反映系统操作很卡看日志发现大量“数据库连接获取超时”。原因是当天的定时报表任务和坐席高并发操作同时触发而数据库连接池最大值配小了慢查询又把连接全占了。当时临时扩容连接池重启了一下系统恢复。后续把定时任务改成在凌晨低峰跑又拆了几个慢SQL加上数据库层面加了只读从库分担查询压力这个问题再没出现过。第二个故障是公海客户重复分配。公海规则是这样的客户超过 N 天未跟进自动退回公海其他销售可以领取。结果某个版本里我写了一行有问题的SQL导致一批已分配给A的客户在退回公海后又被B正常领取了结果 A 和 B 名下出现了同一个客户两个人都能跟进。问题排查后发现是事务隔离级别设置不当导致的检查客户是否在公海的读操作和领取后的写操作之间另一个进程抢先完成了退回。后来我改用 Redis 分布式锁锁住客户ID再执行“检查-领取”整个操作问题就彻底解决了。这种数据并发问题在开发环境很难复现只有生产环境数据量大时才会暴露所以代码上一定要从设计上就避免竞态。6.3 我对 DeskkcommCRM 后续演进的几点思考系统稳定运行之后我开始考虑二期。首先是移动端销售在外面拜访客户时希望能在手机上快速查看客户资料、记录跟进。这块准备做一个小程序服务端的接口已经预留了。然后是智能提醒结合客户最近跟进时间和下一步计划自动给销售推送今日待办提醒。最后是数据分析把客户转化漏斗和工单响应分析做成可视化报表直接同步到管理层看板。不过我也提醒自己二期再着急也不能犯一期着急的错误一定要先明确一个核心使用场景做透再做外围功能。功能越多维护成本越高小型团队撑不住大而全的系统。DeskcommCRM 目前的状态是稳定、够用、可扩展。我始终认为内部系统的目标不是炫技而是让使用它的人每天能省下半小时把精力放到真正重要的事情上。这几轮开发下来我自己最深的体会就是一个项目能不能成往往不取决于用了多先进的技术而取决于有没有把业务逻辑理清楚、把边界卡住、把该做的脏活干完。DeskcommCRM 不是什么业界标杆但它实实在在解决了业务问题也让团队对这套系统有了完全的掌控力。如果你也在做类似的内部系统希望这篇复盘里的思路、模型和那些踩坑细节能给你一些参照。后面如果继续迭代我还会再来分享。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻