自建轻量级桌面CRM:SQLite+FastAPI实现销售跟进与自动化
DeskcommCRM 这个名字是我给自己维护的一套客户关系管理工具起的内部代号前后改了三个版本断断续续用了一年多才终于敢说它是“能扛事”的工具。很多中小团队都会卡在同一个选择上上大厂 CRM 觉得贵、配置复杂、一线销售根本不想打开继续用 Excel 和微信群又总是丢客户、漏跟进、月底复盘全靠拍脑袋。DeskcommCRM 算是这两种方案之间的第三条路——数据留在自己手里功能只围绕“销售跟进”这一件事来做轻量、快速、不用烧钱买许可证。如果你正带一个小型销售团队或者自己在维护一套私有 CRM又或者只是想知道“怎么用最小的成本把客户跟进的流程管起来”这篇文章应该对你有用。下面我会把这套系统的核心思路、技术选型、功能拆解、踩坑记录全部铺开讲包括很多文档里不会写的东西。1. 为什么我最后还是选了桌面式 CRM1.1 大而全的 CRM 到底错在哪我不是反对大平台而是发现它对小团队很不友好。市面上的主流 CRM 产品功能从线索挖掘、邮件营销、工单系统到 BI 报表一应俱全听上去很美好但真用起来一线销售每天要填的字段可能超过二十个连“这个客户准备什么时候付款”都要分成好几层下拉框去选。结果是销售嫌麻烦找各种理由不上系统领导看着后台的空数据干着急。更深层的问题在于数据所有权。企业内部客户资料存在第三方服务器上虽然平台都有安全承诺但销售团队的通讯录、报价记录、历史沟通内容全部依赖一个外部系统一旦账号到期或者业务调整迁移成本非常高。很多老板没算过这笔账实际上把数据搬来搬去的隐性成本远高于订阅费本身。还有一个常被忽略的点网络依赖。不管是 SaaS 还是网页版系统办公室网络一出故障、服务器一维护所有人只能干瞪眼。销售在外面跑客户的时候想查一条客户历史记录还要看信号好坏这种体验简直劝退。1.2 桌面优先加上局域网协作才是小团队的舒服姿势DeskcommCRM 的设计思路很直接核心体验放在桌面上数据文件就在本地团队协作通过局域网或者内网穿透实现不做复杂的云同步。每个成员的本机保存一份数据副本通过一个轻量同步机制合并更新整体架构简单但足够可靠。我之所以认定这套方案核心原因是小团队的客户量级不需要一个完整的中间件集群。10 个人的销售团队一年积累的客户数撑死在 5 万条以内跟进记录、商机数据加在一起也不超过 50 万行这个数据量用 SQLite 就能轻松扛住。把数据库放在一台 1TB 硬盘的普通办公电脑上性能完全够用备份也简单直接把文件拷走就行。另外一个现实原因配置成本。SaaS CRM 要配部门、配角色、配审批流、配自动化规则搞完快则三周慢则三个月。DeskcommCRM 从安装到跑起来十分钟就够。对于一个小团队来说快速跑起来比什么都重要先把客户和跟进记录管住后面再慢慢优化流程。2. 五分钟搭起一套能用的 DeskcommCRM2.1 选型背后的取舍这套系统的技术栈非常朴素Python 3.11 加 FastAPI 做后端 API前端是 Vue 3 的桌面壳子打包工具用的 Electron数据库就是 SQLite配合 WAL 模式和若干索引定时任务用系统自带的 cron。没有上 Redis没有上消息队列甚至连 Nginx 都没装。很多人会问都做桌面应用了为什么不用 Java 或者 Go 写后端。说白了就是团队维护成本的问题。Python 在处理数据导入导出、文本清洗这种脏活上效率极高FastAPI 自带 OpenAPI 文档前端对接起来非常顺滑。SQLite 的选择理由前面说过数据量不大而且单文件数据库对于备份、迁移、远程协同有着天然优势——我甚至可以直接把数据库文件压缩后发给他人在另一台电脑上打开。Electron 被人诟病内存占用大但它解决了一个真实痛点团队成员的电脑环境不可控。有人用 Windows有人用 macOS有人还在用老旧的 Win7。Electron 打包出来的应用可以做到真正的开箱即用不用让用户去配置 Python 环境、安装依赖包这些对业务人员来说是不可接受的门槛。2.2 核心数据模型和建表语句不把数据模型设计好就急匆匆写界面后期必然要返工。DeskcommCRM 的核心表一共五张customers客户、contacts联系人、deals商机、activities跟进记录、reminders提醒事项外加一张 users 做权限控制。看起来简单但每张表都经过了好几轮迭代。customers 表最关键我保留的字段包括客户名称、所属行业、客户来源、负责人、状态、下次跟进时间。这里特别注意“下次跟进时间”这个字段它是所有自动化提醒的地基。很多 CRM 失败是因为没有这个字段或者有了也没人填导致系统并不知道下一步该干什么本质上跟 Excel 没什么区别。deals 表的字段设计更偏向销售漏斗金额、当前所属阶段、预计成交日期、赢单概率、关联客户 ID。赢单概率不是一个摆设它是用来做金额加权统计的。月底老板问“下个月能签多少”直接把所有 deal 的金额乘以概率再求和就能得到一个相对靠谱的预测值比拍脑袋准得多。activities 表记录每一次跟进的详细信息包括跟进方式电话、微信、见面、沟通摘要、下一次预约时间。这张表涨得最快也是最值得做统计分析的地方。通过 SQL 就能查出“平均需要几次跟进才能赢单”“哪个环节丢单最多”这类关键指标。一个贴士所有表都要加 created_at 和 updated_at不用 ORM 的自动时间戳直接在建表时写 DEFAULT (datetime(now, localtime))。这会在后面的数据同步和冲突处理派上大用场。2.3 本地优先多端同步怎么做桌面应用最怕的问题是数据只存在一台电脑上换台电脑就没了。我的方案是 SQLite 的 WAL 模式加自定义同步接口。WAL 模式允许读操作和写操作并发执行不会锁死整个数据库实测在 10 人同时使用的场景下基本没有感知。同步逻辑采用基于 updated_at 的增量同步每个客户端保存一个 last_sync_time每次同步只拉取这个时间点之后变更的数据再把自己的本地写入推送到中央数据库。冲突处理逻辑尽量简单——同一客户记录在两边都发生了修改时以后写入的版本为准同时在活动日志里记录一条“该记录曾被其他成员修改”。这个方案不算完美但胜在好用、易理解。团队规模突破 50 人的时候我会建议迁移到 PostgreSQLSQLite 做本地缓存这是后话但架构上没有死胡同FastAPI 换数据源就是一个连接串的事。3. 把销售流程真正跑进系统里3.1 阶段设定是生死线很多团队把 CRM 当成客户通讯录来用这完全是买椟还珠。DeskcommCRM 的商机阶段设计是整套系统的灵魂我的设置是初步接触、需求挖掘、方案报价、商务谈判、赢单、输单六个节点。为什么是这六个而不是更细的十个因为阶段太多一线销售会花大量时间在“把这条商机挪到下一个阶段”上而不是真正去跟进客户。六个节点刚好覆盖一个标准销售周期而且每个节点都有明确的动作指向处在需求挖掘阶段的商机下一步动作是准备定制方案处在商务谈判阶段的商机下一步是协调价格和付款条件。系统会根据当前阶段自动生成“建议下一步动作”提示销售不用动脑子就知道该干什么。这里有一个自己踩过的坑不要把“输单”这个阶段设成普通状态而是当成终结状态单独处理。输单的商机不能直接删除而是保留下来做复盘因为输单原因竞争对手、预算不足、决策链太长是管理层最需要的数据。3.2 自动化规则让系统追着人跑CRM 系统的价值不在于“记录发生了什么”而在于“提醒接下来该做什么”。DeskcommCRM 里我做了几个关键自动化规则全部基于 SQLite 触发器加一个每分钟跑一次的 cron 脚本实现。第一个规则是超期预警如果一条商机的下次跟进时间已经过了 48 小时还没有新增活动记录系统自动给负责人弹提醒并把这条商机的优先级提升一档。这个规则彻底解决了销售忘记跟进的问题。第二个规则是公海回收超过 15 天没有任何跟进动作的客户自动转回公共池其他成员可以认领。这个规则保证了客户资源不被浪费也逼着销售养成定期清理的习惯。实现原理不复杂。每天凌晨两点跑一个 Python 脚本扫描 customers 表里 next_follow_up_at 字段和最近一条活动记录的时间把符合条件的客户打上标记。用 SQL 一条 UPDATE 就能完成根本不需要复杂的消息队列来编排。实在要夸一句SQLite 虽小但配合合理的数据模型写自动化逻辑比很多重型数据库还顺手。3.3 一个让人愿意看的报表看板报表做得好不好直接决定管理层会不会每天打开系统。我是踩过这个坑的——第一版报表只有冷冰冰的表格结果两周后没人看。后来彻底重做核心原则是一屏只看三件事漏斗、金额、动作。销售漏斗按阶段显示商机数量和总金额每个阶段的转化率一目了然。金额看板按负责人显示本月新增商机金额、预计回款金额、实际回款金额做对比参照。动作看板统计每个人的跟进次数、打电话次数、预约会议数这不是为了监控员工而是帮销售自己发现工作节奏的问题。技术上没有用任何复杂的数据可视化库就是 ECharts 的漏斗图和柱状图。数据直接用 SQL 做聚合比如计算平均转化周期就查 deals 表里每个 deal 从创建到赢单的时间差值再取平均。报表看板的价值不只是给老板看更重要的是给销售自己看因为有了真实数据每周复盘才能从“我觉得最近单子不多”变成“这个月报价后的转化率只有 20%需要调整报价策略”。4. 一次生产环境事故排错实录4.1 从 Excel 导入导致的全员乱码上线第一天就闹了一个大乌龙。运营把 3000 多条客户资料从 Excel 里导出成 CSV 再导入 DeskcommCRM结果所有中文全部变成了乱码。排查下来发现是编码问题Excel 在中文系统环境导出的 CSV 默认是 GBK 编码而导入脚本用的是 UTF-8 解码两边就打架了。解决办法是在导入功能的编码检测里加一层兼容逻辑先用 chardet 检测文件编码再转成 UTF-8 入库。另外在导入预览界面让用户手动确认编码类型顺便展示前五行的解析结果。这个功能看似小但在实际项目中非常提升体验比自己看报错日志猜半天强太多。还有一个隐藏坑Excel 导出的 CSV 会在第一行加一个 UTF-8 BOM 标记字符串 \ufeff如果不剔除会导致第一列的列名解析失败。处理方式就是在读取文件后把字符串开头的 BOM 字符 strip 掉一行代码的事不处理就是莫名其妙的多一个横杠。4.2 重复客户到底清不清CRM 跑了一个月后重复客户开始肉眼可见地多起来。同一个公司出现了三四个不同的客户负责人跟进记录分散得乱七八糟看历史都看不利索。清理不是一次性手工删除的事得从源头控制。我在数据库层加了一条逻辑客户名称、联系人电话、公司官网三个字段任意一个相同就标记为疑似重复写入提醒列表。然后建了一个“合并客户”功能操作的时候选择保留哪个主客户其他记录全部迁移过去历史跟进记录一并合并。这里不建议做“全自动合并”因为有些公司名字相似但确实不是同一家合并错了损失不可逆。库里加唯一索引也是一个有效手段用 COALESCE 函数把空值统一成空字符串后建联合唯一索引从源头堵住完全相同的客户重复录入。这套组合下来两个月后重复率从 12% 降到 4% 左右基本可控。4.3 性能瓶颈和备份恢复的实操心得运行到第十个月的时候activities 表已经积累了 20 万条记录Electron 客户端在查询客户详情开始出现卡顿一次点击要等三四秒才有响应。分析查询日志发现问题出在几个关键查询没有走索引尤其是我在下一页和上一页翻动活动历史时用了 OFFSET 分页数据量一大效率就急剧下降。解决方式是套用最普通的索引优化activities 表建了客户 ID 加跟进时间的复合索引deals 表建了负责人加阶段索引。分页逻辑改成了“游标分页”也就是基于上次查询的最后一条 ID 去取下一页这样不管翻多深查询时间都是常量级。优化后客户详情页的打开时间从 3.8 秒降到 0.3 秒基本回到刚上线时的手感。备份方面直接用 SQLite 自带的备份命令每天凌晨用 cron 执行一次在线备份再压缩保留最近 30 天。真正让我意识到备份重要性的是一次误操作——我不小心执行了一条没有 WHERE 条件的 UPDATE把整张客户表的状态字段改错了。还好有备份恢复数据只花了五分钟。这件事之后我把备份频率改成每 6 小时一次而且备份文件必须放到另一块硬盘或者网络存储上绝不能和数据库放在同一个磁盘里否则硬盘坏了就全没了。5. 这套系统适合什么人以及什么时候该放手DeskcommCRM 适合的是对数据安全和成本敏感、销售流程清晰但尚未复杂到需要重型系统的团队实体行业渠道销售、服务型公司项目组、早期创业团队、咨询服务公司都算。它的定位从来不是替代 Salesforce 一类的大平台而是提供一个快速起步、数据自持的中间方案。运行一年多之后你会慢慢发现系统里积攒的数据越来越有价值哪个渠道来的客户质量最高、哪类销售人员转化率最好、哪种行业客户平均成交周期最短。这些决策依据是之前拍脑袋时代根本拿不出来的。如果你也在用类似方案折腾自己的业务管理系统我的建议是先盯着“销售跟进”这一个环节做透不要一开始就想着大而全的营销自动化很多功能听着高级但根本没人用反而是最大的浪费。我个人最大的体会是工具最终要为人服务。DeskcommCRM 真正改变团队的地方不是让所有人都乖乖填表而是让每个销售都养成了“录入即整理、跟进有记录、复盘有数据”的习惯。这一点比任何技术选型都重要。