自建DeskcommCRM实战:从数据建模到部署运维的完整指南

📅 发布时间:2026/9/16 22:17:09
自建DeskcommCRM实战:从数据建模到部署运维的完整指南
DeskcommCRM 这个项目是我在被几款免费 CRM 折腾到几乎崩溃之后下定决心自己动手做的一套客户管理系统。做之前我以为无非就是把客户名单存进数据库做之后才发现一套真正“顺手”的 CRM背后牵扯到的数据建模、权限设计、部署运维每一个环节都够写一篇长文。这篇就把我完整的设计思路、踩坑过程和落地经验拆开讲清楚想自建一套或者正在选型的朋友应该能少走不少弯路。1. 先聊聊为什么要自建一套 DeskcommCRM1.1 免费 CRM 的痒与痛我最早接触 CRM是从团队使用在线免费工具开始的。市面上的免费 SaaS CRM光看功能列表都挺唬人客户管理、跟进记录、商机阶段、报表看板一应俱全。可真放到一线业务里用起来各种别扭就全冒出来了。最难受的首先是数据所有权问题。免费套餐背后那句“我们有权在合理范围内使用您的数据”到底意味着什么几乎没人会逐字读。等你想把数据导出来迁移到别处时又会发现导出格式千奇百怪字段映射乱七八糟几千条客户的备注信息说丢就丢。其次是定制能力基本为零销售团队想加一个“客户来源渠道”字段或者想把跟进周期从按天改成按小时免费工具要么不支持要么藏在付费墙后面。我当时就反复想一个问题CRM 本质上就是一张字段可以自定义的“客户大表”加上一堆围绕客户的动作记录。这个东西为什么非要绑定在别人的服务器上受制于别人的产品路线图与其每个月交着会员费还要忍受上传速度、数据安全的各种不确定性不如自己搭一套。DeskcommCRM 这个名字其实就是“Desk Communication”的缩写我希望它成为办公桌前最顺手的客户沟通记录工具。1.2 我理想中的 CRM 该长什么样动手之前我把自己对一套合格 CRM 的诉求列了出来大致有五条。第一客户数据必须完全掌握在自己手里数据库可以随时备份、随时导出任何时候不会被平台封禁或限制访问。第二字段和业务流程要能自由配置今天加一个“意向度”下拉框明天改一下“报价状态”的选项都应该在后台界面里几分钟完成而不是找客服提工单。第三性能要快打开客户列表就是秒开搜索客户记录不会转圈三秒钟。第四部署形态要“永久在线”不是靠某个 SaaS 厂商的免费额度养活而是自己有台小服务器常年跑着域名一开随时能用。第五团队协作要直接邀请员工加入、分配权限、跟进提醒这些动作要像拉一个微信群一样简单。这五条需求定下来后面所有的技术选型和功能开发其实都是在为这五条服务。也正因为目标明确我在项目中途好几次遇到“要不要换个现成框架”的诱惑时都能很清醒地判断到底该不该换。2. 整体设计与技术选型思路2.1 技术栈怎么定下来的项目立项第一件事就是定技术栈。我当时的约束条件很明确一个人维护技术要稳、生态要熟、部署要简单。对比了一圈最终选定了 Spring Boot MyBatis-Plus Vue 这套组合数据库用 MySQL缓存看情况用 Redis。选 Java 系而不是 Node.js 或者 Python核心原因是这类管理系统数据库操作密集Java 的 JDBC 体系配合 MyBatis-Plus写 CRUD 的效率非常高而且 Spring Boot 的自动配置和丰富的 Starter 能省掉大量胶水代码。前端选 Vue 则是因为生态成熟Element Plus 组件库拿来改改就是一个后台管理界面不需要从零画表格和表单。老实说这套组合算不上新潮但作为一个人维护的业务系统稳定压倒一切团队招人接手也容易。这里要提一个模仿开源项目的经验。热词里经常看到 RuoYi Office CRM 之类的项目名说明很多人也在研究基于 RuoYi 这类快速开发平台套一个 CRM 外壳。我的做法类似但不完全照搬借鉴了 RuoYi 的代码生成器思路先建好数据表自动生成实体、Mapper、Service、Controller 的骨架代码再手工调整业务逻辑。这一套操作下来光是基础的客户增删改查就省了两三天工作量。如果你也是一个人开发管理系统强烈建议先把代码生成器那一套吃透别上来就手写所有 CRUD。2.2 数据模型设计客户、联系人、跟进记录怎么串CRM 系统的核心不是界面而是数据模型。DeskcommCRM 的数据模型围绕“客户”这个主实体展开。客户表存的是公司或个人客户的基本信息包括客户名称、行业、来源渠道、所属销售、客户等级、状态等联系人表则挂在客户下面一个客户可以对应多个联系人存姓名、职位、手机号、微信、邮箱跟进记录表记录每一次与客户的互动包括跟进方式、跟进内容、下次跟进时间商机表则是把漏斗里的潜在交易单独拎出来管理关联客户、金额、预计成交日期、阶段。表之间全部用逻辑外键关联通过 customer_id 和 contact_id 串起来。为什么不建物理外键因为后续要做批量导入导出、逻辑删除物理外键在数据操作时会带来不少限制而且容易死锁。这个设计在初期看起来没什么差别等到数据量到几万条、并发修改频繁的时候就会庆幸当初没建物理外键。权限模型上我用了 RBAC 的思路一张用户表、一张角色表、一张权限表中间加关联表。角色分管理员、主管、销售三种。管理员看全部数据主管看本团队数据销售只看自己名下客户。数据权限的过滤是通过 MyBatis-Plus 的拦截器实现的根据当前登录人的角色自动拼接 SQL 条件这样业务代码里根本不用每条查询都手动判断权限既省事又不会漏。2.3 字段自定义的实现方案字段自定义这件事想过用 EAV实体-属性-值模型就是所有自定义值存到一张通用扩展表里。后来否了因为查询性能太差而且统计报表会非常痛苦。最终方案是每个客户类型对应一套模板模板里预定义好所有字段实际存储时用 JSON 类型字段保存扩展属性。MySQL 从 5.7 开始支持 JSON 类型这简直是给这类需求准备的。我建了一个 customer_ext 字段里面存一个 JSON 对象比如{意向度: 高, 预计采购量: 5000}。使用时用 JSON_CONTAINS 或 JSON_EXTRACT 查询速度完全够用。管理后台里再做一个字段配置页面把字段名称、类型、必填、选项配置到一张表里渲染表单和列表时动态读取。这样一来一套系统就能适配好几个不同业务的团队而不用为每个团队单独开发。3. “永久在线”的落地部署与运维实战3.1 服务器、域名、HTTPS 这套流程怎么走很多人在线 CRM 用得好好的一提到自己部署就发怵觉得“永久在线”是个很玄的事情。其实拆开看就四件事一台常年不关机的服务器、一个域名、HTTPS 证书、进程守护。服务器我选的是 2 核 4G 内存的云主机跑这套系统加 MySQL 完全够日常内存占用大概在 1.5G 左右偶尔高峰也不超过 2.5G。系统装的是 Ubuntu 长期支持版装完系统第一件事就是把 SSH 从默认端口改掉并关闭密码登录只留密钥认证这是最基础的安全措施但很多人会漏掉。域名解析上我把项目部署在一个二级域名比如 crm.example.com解析到服务器 IP。以前有人问为什么要用域名而不是 IP 直接访问原因很现实现代浏览器的 HTTP 安全策略越来越严格很多高级功能比如剪切板读写、麦克风权限在不安全来源下根本不给你用而且 IP 地址的 HTTPS 证书申请也麻烦。用域名之后直接用 Certbot 帮 Nginx 自动申请并续期 Let‘s Encrypt 证书一下就把 HTTPS 和证书快过期的问题全解决了。Nginx 配置这里很多人会踩一个坑把前端打包后的静态文件直接交给 Nginx 托管然后后端 API 通过/api前缀反向代理到本地的 Java 进程。这个方案简单但要注意前端路由的 history 模式需要额外配置把所有非静态文件请求都转发到 index.html否则刷新页面就 404。配置大概是这个样子server { listen 443 ssl http2; server_name crm.example.com; ssl_certificate /etc/letsencrypt/live/crm.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/crm.example.com/privkey.pem; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { root /opt/deskcommcrm/dist; try_files $uri $uri/ /index.html; } }3.2 进程守护与自动重启别让 CRM 半夜掉线服务器上的 Java 进程不能直接java -jar跑完就完事终端一关进程就没了而且一旦内存溢出或者异常崩溃系统就再也起不来了。这里我用了 systemd 服务来托管 Java 进程相当于给 CRM 加了一个自动看护。写一个/etc/systemd/system/deskcommcrm.service文件配置 ExecStart 指向启动命令Restart 策略设为 always这样进程崩溃后 3 秒内就会自动重启。实际部署中我还加了一个启动前的健康检查脚本因为数据库偶尔会因为异常停机导致应用启动时连接不上这时直接重启应用等于白搭。脚本会先探测 MySQL 的 3306 端口是否就绪等数据库完全启动后再拉起 Java 进程从源头上避免了“应用等数据库”的鸡生蛋问题。备份这块更要养成肌肉记忆。我用 crontab 每天凌晨两点对 MySQL 全量备份备份文件通过 scp 传到另一台服务器保存。还额外做了一层 Binlog 实时同步确保即使凌晨那次的备份丢了也能恢复到前一秒的数据。数据安全这件事前期多花半小时后面出了事故能救命。4. 核心功能实现与实操要点4.1 客户池与销售漏斗怎么设计客户池是我第一个落地的核心模块。说白了就是一张客户总表所有客户数据全部进来再根据字段过滤分配给不同销售。这里有个关键设计客户状态字段我分了“公海客户”和“私有客户”两种私有客户有归属人公海客户谁都可以领取。销售超过 N 天没有跟进客户自动退回公海这样能避免客户信息在某个销售手里躺尸。销售漏斗是基于商机表的阶段字段实现的。我把阶段固定为“初步接触 - 需求确认 - 方案报价 - 商务谈判 - 赢单 - 输单”六个阶段每个阶段可以配置一个赢单概率和一个预计成交时间。看板页面的漏斗图就是把商机按阶段分组统计金额。实现上只需要一条 group by 语句加一个排序完全不需要额外引入 BI 组件。真正费功夫的是跟客户列表配套的筛选和排序。销售用的最多的操作是“看今天该跟进哪些客户”我在客户列表页加了一个智能过滤栏支持按下次跟进时间、客户等级、来源渠道、归属人多个维度组合筛选。底层实现就是动态拼接多条件 SQL但前端把查询条件的交互做成了下拉框和标签形式销售不需要学习任何语法点一点就能出结果。这个功能上线后团队使用率直线上升很多人从“被迫记录”变成了“主动打开系统”。4.2 跟进记录与提醒最容易忽略但最关键的功能做 CRM 如果只做了客户名单管理那充其量是个高级通讯录。真正让 CRM 有生命力的是跟进记录和提醒机制。跟进记录模块我做了三层设计最底层是日志表每一条记录包含跟进客户、跟进人、方式、内容、下次跟进时间中间层是自动打点比如报价单发送、邮件点击这些动作都会在后台自动生成一条跟进记录不用销售手动录最上层是提醒中心根据“下次跟进时间”和当前时间比对每天定时任务扫描生成待办提醒。这里想重点说说定时提醒的实现。我最初用 Spring 的 Scheduled 注解写了一个每五分钟扫描一次的任务后来发现一个问题如果某些客户下一次跟进时间设置为晚上十点扫描任务在十点零五分运行没问题但如果服务器当时正在重启任务被跳过了提醒就会漏掉。后来改成把提醒任务也存数据库每次扫描时把上次运行时间记下来下次启动时先补跑之前漏掉的任务。这个细节折腾了我一天时间但修完之后提醒再也没漏过。关于跟进记录还有一个心得记录内容尽量让销售用口语化的方式录入不要做一堆强制下拉框。我见过一些 CRM跟进记录里强制选“客户态度满意/一般/不满意”结果销售为了完成任务随手乱点数据全是噪声。DeskcommCRM 里跟进内容就是一个大文本框加一个可选的情绪标签。销售觉得写起来没负担记录质量反而高了很多。4.3 邀请团队成员飞鱼 CRM 邀请员工的思路拆解很多人搜“飞鱼 CRM 怎么邀请员工”本质上是想搞清楚团队成员加入一个企业客户系统的流程。其实主流 CRM 的邀请机制殊途同归DeskcommCRM 的实现就是一套标准的邀请-绑定-授权流程。管理员在用户管理页面点击“邀请成员”系统生成一个带 token 的邀请链接这个 token 有效期默认 48 小时只允许使用一次。把链接发给同事后同事打开页面如果还没有账号先注册账号再用邀请码绑定团队如果已有账号直接登录并确认加入。绑定成功后管理员会收到待审批通知审批通过后按预设角色分配权限。整个流程比飞鱼CRM里那种扫码邀请稍微麻烦了一点但数据可控性更强而且邀请链接可以随时撤销。实现这个功能要注意一个安全点邀请 token 一定不能明文存数据库。我的做法是先用 SHA-256 哈希再落库校验时比对哈希值这样即使数据库泄露攻击者也无法用已有 token 进行未授权访问。另外邀请功能还要控制次数上限每个管理员每天最多创建 20 个有效邀请避免恶意刷邀请把团队撑爆。5. 免费 CRM 与私人部署网站的差距在哪里5.1 数据所有权和自定义能力的真实差异网上经常有人问“免费 CRM 与私人网站的区别在哪”作为两套方案都用过的人我感受最深的是数据所有权和自定义能力这两个维度的差异。免费 SaaS 模式里你的客户数据、合同附件、沟通记录全部存放在对方的数据库里服务协议里通常有一条数据可迁移性条款但实际执行时导出接口响应慢、字段对不上是常态。更关键的是免费套餐意味着你要接受对方的广告投放、数据挖掘行为或者每季度收到的“升级订阅”邮件轰炸。而自建的 DeskcommCRM数据库就在你自己的服务器上一条mysqldump命令就把全部数据搬到本地想导出到 Excel、导入到 BI 分析都能自己控制。自定义能力上的差异更为显著。免费 CRM 提供的字段类型往往有限自定义报表也限制在预置模板里。我自建系统后给其中一个客户团队做了一个“回款计划表”模块把回款日期、金额、付款状态跟客户主表关联起来财务人员和销售看的是同一份实时数据。这类业务定制在 SaaS 产品面前往往需要一个销售代表反复确认需求、报价、排期自建方案里我自己一个周末就能搞定。5.2 成本与维护的真实账本自建不是没有成本这里把账算清楚才心里有底。硬件和基础服务方面云服务器一年大约七八百元域名一年几十元HTTPS 证书免费这其实是自建方案里最固定的支出。软件方面的隐形支出主要是时间成本首次搭建大概需要两个完整周末后续每次功能迭代、安全补丁更新都需要投入时间维护。但换个角度免费 SaaS 的隐性成本往往更高。数据导出要开付费会员进阶报表要按席位按月收费API 开放接口更是放在最高的付费档位。按一个十人销售团队算稍微像样的 SaaS CRM 年费五六千元是起步价而自建方案直接把这块省成了服务器费用。当然自建方案并不适合所有人如果你的团队完全没有技术人员维护那还是稳当订阅 SaaS 更省心。这是我在项目文档首页写的第一句免责声明一点不夸张。5.3 安全与合规的常见误区自建系统一听到“安全”两个字就容易慌但其实很多安全问题都是习惯问题而不是技术难度问题。我在 DeskcommCRM 里做了三层常规安全工作传输层用 HTTPS数据库账号用独立低权限账号且只允许内网访问应用层每个接口做登录校验和权限校验。这里最容易踩的坑是把数据库端口暴露到公网。即使你设置了强密码扫描和爆破脚本也会日夜不停地攻击。最安全的做法是绑定云服务商的安全组规则3306 端口只允许内网或者白名单 IP 访问。Nginx 层面再加一层简单防护比如屏蔽掉/actuator、/.env、/wp-admin这些常见扫描路径。做到这些一个个人业务系统的安全性已经能超过很多小工作室的 SaaS 站点。6. 上线半年踩过的坑与排查经验6.1 常见问题速查表自建 CRM 上线后的前半段几乎每天都会遇到各种“看起来正常但就是不对”的问题。我整理了一张高频问题速查表放到项目 Wiki 里供团队自查照着排能省下大量沟通时间。问题现象可能原因排查方法页面能打开但登录后自动跳回登录页前端请求 API 时未携带 Cookie 或 Token 导致认证失败查看浏览器开发者工具 Network 面板检查请求头中 Authorization 字段是否存在上传头像/附件总是失败Nginx 的 client_max_body_size 默认只有 1M在 Nginx 配置中加大该参数并重新加载配置定时提醒偶尔漏发定时任务使用单机内存方式重启后丢任务改为任务状态持久化到数据库启动时补跑未完成任务客户列表加载越来越慢索引缺失或查询条件使用了对字段做函数的写法导致索引失效使用 EXPLAIN 分析 SQL为 under follow time 等高频查询字段建立普通索引导出 Excel 超时或内存溢出一次性读出全部数据加载到内存改为流式查询分批写入 Excel 文件这类问题里导出 Excel 超时是我感触最深的一个。第一版导出功能是同步的客户数据到两万条左右接口就会卡到一分钟以上前端等得直接超时。后来改成异步任务导出完成后推送到消息中心用户下载时直接拿文件。这个改动不大但对用户体验的提升立竿见影。6.2 独家避坑技巧光说问题不够再分享几条我从实际运维中攒出来的经验。第一数据库连接池的 initialSize 一定要调不要用默认值。如果是个人服务器MySQL 的 max_connections 通常是 151连接池一不小心开 50 个连接同时跑几个批量任务就直接把连接数打满了。我最终调成初始 5、最大 20半年下来再也没碰到过连接数耗尽的问题。第二所有前端的日期时间最好统一传时间戳或者标准 ISO 字符串不要传“2025-06-03”这种格式。因为浏览器和服务器在不同时区解析这类字符串时可能出现偏移导致跟进提醒的时间差了八小时。这个问题排查起来非常隐蔽我花了一个下午才发现是时区在中间捣鬼。第三定期把生产数据库恢复到本地测试环境演练一遍。没出过事故的人可能觉得多此一举可真等硬盘损坏或者误操作删了表的那天你会发现“有备份”和“能恢复”是两码事。我每个月会做一次恢复演练顺手把恢复耗时、备份文件大小记录下来做到心里有数。第四给所有写操作接口加一个简单的操作日志记录谁在什么时间对哪个客户做了什么修改。这个功能平时不起眼但一旦团队里出现数据争执或误操作它就是仲裁的依据。实现上也不复杂用 AOP 拦截所有写接口把操作方法名、参数摘要、登录用户名一起异步写入日志表。第五升级系统版本前务必先在测试环境跑一遍回归测试。我有一次升级 Spring Boot 小版本本地跑得好好的上生产之后发现导出功能突然报错查了半天是文件上传临时目录被系统清理导致的问题。后来就学乖了任何依赖版本的升级都先在测试服务器完整过一遍核心流程再上生产。DeskcommCRM 做到现在功能上其实还没完全到我自己心中的满分形态手写消息、工单模块、移动端适配都还在计划表里。但就像我开头说的做这套系统的初衷就是为了让客户数据真正待在自己手里让团队用起来顺手。每次同事跟我说“这个报表导出真好用”或者“跟进提醒救了我一单”的时候我就觉得当初花那几个周末从零搭一套值了。