ARTICLE DETAIL

资讯详情

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

自研CRM系统实战:从表结构到工单状态机的完整设计

自研CRM系统实战:从表结构到工单状态机的完整设计 在做 DeskcommCRM 这个项目之前我们团队其实踩过不少 CRM 产品的坑。市面上叫得上名字的客户管理系统不是太重就是太死板销售说录入麻烦客服说查不到历史管理层想看数据又得等月底导表格。最后干脆自己动手从零搭一套贴合业务的系统。DeskcommCRM 这个名字听起来像某个开源项目的代号但真正把它拆开看它代表了一种很务实的思路客户管理不该是一堆档案的堆砌而应该是一张“能干活的工作台”。这套系统经历了需求梳理、表结构设计、功能落地、部署上线、线上排障几个阶段期间踩过的坑和沉淀下来的方法我觉得对正在做 CRM 选型或自研的朋友很有参考价值。这个项目适合谁来参考一个是准备做 CRM 自研但不确定模块怎么拆、数据怎么建模的技术同学另一个是正准备把团队从 Excel 表格迁移到客户管理系统的小团队负责人。我会把核心表结构、工单状态机、自动分配、权限隔离、通知预警这些关键环节一个一个说清楚同时附上真实上线过程中的排查经验尽量做到看完就能直接套用。1. 拆解 DeskcommCRM先搞清楚它解决什么问题1.1 名字里藏着的产品定位DeskcommCRM 拆开看有三个关键词Desk、comm、CRM。Desk 对应的是“桌面/工作台”它不是传统意义上那个只有客户列表和跟进记录的 CRM而是把员工每天要干的活集中到一张桌面上——今天要打哪些电话、还有哪些工单没处理、哪几个客户的回访超期了打开系统第一眼就能看到。comm 是 communication也就是沟通这个部分是这个产品最核心的东西。我们经常遇到销售说“客户上周微信上答应要签合同了”换一个人接手聊天记录在哪、承诺了什么完全对不上。DeskcommCRM 把电话录音、微信聊天记录、邮件往来、线下拜访摘要统一沉淀到客户档案里让任何接手的人都能看到完整上下文。CRM 本身并不稀奇但把“沟通记录”作为贯穿所有模块的主线这个定位很重要。它意味着系统设计的重心不是“登记客户”而是“还原沟通过程”。客户提了什么需求、客服给了什么承诺、销售上次聊到哪个阶段全部有迹可循。这样一来销售、客服、售后看到的不是一份静态资料而是一条连续的动态时间线。1.2 功能全景与数据流转主线从功能模块看DeskcommCRM 围绕沟通主线分成了五个中心客户档案中心管理客户基础信息、所属行业、客户来源、负责人、标签、自定义字段。工单流转中心承接售前咨询、售后维修、内部协作等不同类型的任务支持状态流转、指派、转派、SLA 预警。沟通记录中心统一沉淀电话、微信、邮件、在线聊天、线下拜访等沟通内容。任务提醒中心生成跟进计划、回访任务、审批提醒并通过站内信、邮件、办公软件机器人触达。统计分析中心基于工单量、响应时长、解决时长、客户满意度等维度生成报表。数据在主流程里的走向可以这样理解外部线索进入系统后先形成一条客户记录客户名下可以维护多个联系人围绕这条客户记录会产生销售“商机”或者服务“工单”工单在流转过程中每一次沟通都会追加到沟通记录里系统根据沟通内容自动判断是否需要生成回访任务最终所有环节的数据聚合成报表。这样设计的好处是每个模块都不是孤岛客户 ID 是贯穿所有数据表的唯一主键任何一条工单、任何一次通话都能追溯到客户。2. 数据底座核心表结构与状态机设计2.1 四张核心表怎么设计才够用CRM 系统一上来不需要搞几十张表先把最核心的四张表做好业务就能跑起来。这四张表是客户表、联系人表、工单表和沟通记录表。客户表建议包含这些字段客户 ID、客户名称、行业、客户来源、负责人 ID、客户状态、创建时间、更新时间、最近跟进时间、下次跟进时间。特别注意“最近跟进时间”和“下次跟进时间”这两个冗余字段它们的值其实可以从沟通记录和任务表里算出来但单独存下来之后列表页排序和提醒功能会好用很多。没有这两个字段每次打开首页都要做一次全表聚合数据量上去之后性能很难看。联系人表要跟客户表是多对一的关系一个客户下面可以挂多个联系人。关键字段包括联系人姓名、职位、手机号、微信号、邮箱、是否决策人、是否默认联系人。手机号建议单独做唯一索引但要注意一个手机号可能同时是多个客户的联系人所以不能直接全局唯一比较稳妥的做法是在“客户 ID 手机号”上加唯一约束避免同一个客户下面录入重复联系人。工单表是整个系统里最复杂的一张表。建议字段包括工单号、所属客户 ID、工单标题、工单类型、优先级、当前状态、指派人、创建人、期望解决时间、实际解决时间、SLA 截止时间、满意度评分。这里我要特别提醒一个容易踩的坑很多团队会在工单表里直接存客户名称的冗余字段担心客户改名后历史工单无法追溯。这个考虑是对的但更好的做法是只存客户 ID同时在工单列表页展示时二次查询客户名称。如果为了列表查询省事也可以冗余客户名称但一旦客户改名必须同步更新所有相关工单这个逻辑要提前写在事务里。沟通记录表建议设计成多态关联字段包括沟通 ID、关联类型customer/ticket、关联 ID、沟通渠道、沟通方向、沟通内容摘要、完整内容存储地址、沟通时间、操作人。多态关联的意思是一条沟通记录既可能挂在客户下面也可能挂在某个工单下面。这样设计的好处是销售在客户 360° 视图里能看到所有沟通客服在处理某个工单时也能看到这个工单的所有沟通而且不需要复制数据。2.2 工单状态机的坑与正确做法工单状态看起来很简单无非是新提交、处理中、已解决。但实际业务跑起来之后如果状态设计得不够严谨会出现很多“死工单”和“幽灵状态”。我见过最典型的问题是把状态做成一个纯粹的字符串字段想怎么改就怎么改。今天有人把工单从“处理中”直接改成“已关闭”中间跳过了“已解决”和“客户确认”明天有人把已经关闭的工单又改回“处理中”但处理时间从哪算、SLA 要不要重新计时全都乱套了。正确做法是把工单状态做成一个状态机明确定义每个状态允许跳转到哪些状态。DeskcommCRM 里我们是这么设计的待分配工单刚创建还没有负责人。处理中已经指派给具体的人正在处理。待客户确认解决方案已经给出等客户回复确认。已解决客户确认问题解决工单进入待关闭状态。已关闭终态工单正常结束。已挂起临时挂起等待客户补充信息或者等待外部条件挂起期间不计入 SLA。这套状态机允许的动作是固定的待分配可以转处理中处理中可以转待客户确认待客户确认可以转处理中客户反馈没解决或者已解决已解决可以转已关闭已关闭之后原则上不允许再打开如果客户又重新反馈问题合理的做法是创建一张新工单并关联原工单号而不是在原工单上来回折腾。这里要强调的是状态不只是“名字”每个状态都要有对应的时间戳字段。比如“进入处理中的时间”“进入待客户确认的时间”“实际解决时间”。报表里要统计平均响应时长、平均解决时长都是靠这些时间戳计算出来的如果只记录一个更新时间什么时长都算不准。当时我们上线两周后发现的第一个报表数据异常就是“响应时长”忽高忽低排查到最后发现是根本没有记录进入处理中的时间全部拿了创建时间在算。3. 几个重点功能实现分配、权限、360° 视图、通知3.1 工单自动分配轮询与技能匹配怎么权衡工单创建之后第一步是分配给谁。人少的团队可以直接手动分配但工单量上来之后自动分配几乎是必须的。DeskcommCRM 里我们用的是“技能标签 负载均衡”的组合策略。先说技能匹配。每张工单会带上一个“工单类型”比如售前咨询、售后维修、投诉升级每个客服/工程师可以维护自己擅长处理的工单类型这个在用户配置表里是一个 JSON 数组或者多对多关联表。自动分配时第一层过滤先筛掉所有不擅长这种类型的处理人避免乱派单导致工单流转时间变长。第二层是负载均衡。候选集合里可能有多个处理人我们要选择当前最空闲的人。当时我们用的评分规则是这样的技能匹配得 20 分当前活跃工单数每张扣 3 分如果这个人今天还没分配过工单额外加 5 分保底最后取分数最高的那个人。这个方案兼顾了公平和效率用了一段时间之后工单积压的情况明显减少。还有一个容易被忽略的点是“二次分配”。第一轮自动分配过去了但处理人当天请假或者工单太多忙不过来系统里必须支持“转派”和“回收”。转派就是处理人把工单转给另一个人转派时要写转派原因回收是管理员可以把工单从处理人手里收回重新进入待分配池。这两个操作都要留审计日志否则出了问题说不清楚是谁的责任。3.2 数据权限不能只靠前端隐藏按钮CRM 系统最敏感的就是客户数据。一个销售绝对不想让另一个部门的人看到自己跟进了半年的客户名单但又在某些场景下希望“公共客户池”里的客户能被别人领取。数据权限这里我们从一开始定的原则是所有数据访问都要在服务端做权限过滤前端按钮只能控制“看得到/看不到”不能控制“能不能看”。DeskcommCRM 的权限模型分三层第一层是数据可见范围包括三类“仅本人”只能看到自己负责的客户和工单“本部门”能看到整个部门的数据“全部”适用于管理层。第二层是操作权限例如“编辑客户”、“删除工单”、“导出客户名单”每个角色单独配置。第三层是字段权限某些敏感字段比如客户手机号、合同金额可以设置成对某些角色脱敏比如客服看的客户手机号中间四位打码。这里我建议把权限过滤逻辑写成一个统一的服务层方法不要在每个查询接口里分别写 where 条件。我们早期就是图省事每个接口自己带过滤逻辑后来出现了一次越权事故某个列表接口漏加了部门过滤A 部门的销售能看到 B 部门的客户名称。排查下来发现就是因为在新增一个导出接口时忘记继承原来的过滤方法。之后我们统一封装了一个 dataScope 过滤器所有客户和工单查询都走这个入口这个问题才彻底解决。3.3 客户 360° 视图一条查询背后要做的事客户 360° 视图是 CRM 系统里最有价值、也最容易做砸的功能。业务方的预期是打开某个客户页面能看到客户所有资料、所有联系人、所有工单、所有沟通记录、所有跟进任务、所有回款记录最好一眼就能判断这个客户的整体情况。这个功能最忌讳的实现方式是在前端发起十几个并行请求一个接口查客户资料一个接口查联系人一个接口查工单一个接口查沟通记录。这样做的后果是接口很难保护数据库压力也大前端加载时间在数据量大了之后会非常慢。比较靠谱的做法是在后端写一个聚合服务用一次查询尽量捞回所需数据。第一步根据客户 ID 查出客户基础资料第二步批量查这个客户下的联系人列表第三步查最近 30 天的沟通记录这里必须做数量限制比如只取最近 100 条不能把几年的聊天记录全部返回第四步查未关闭的工单列表和最近 10 条已关闭工单第五步查待办任务。最后把这些数据按统一的响应结构返回前端只需要调一个接口。这里有个小技巧沟通记录的查询尽量用时间倒序加 limit不要一次性取出全表数据。客户跟进了三四年全量数据可能有几千条直接把几千条记录丢给前端去渲染页面上显示一个长长的列表用户体验极差而且后端传输耗时也大。合理的方式是默认显示最近 20 条用户点击“加载更多”再去查下一页。3.4 通知与 SLA 提醒让系统学会催人CRM 的很多价值体现在“及时提醒”上不然工单丢在那里三天没人管客户早就跑掉了。通知模块我们采用的是事件驱动的思路工单状态变化、新分配任务、SLA 即将超时都会触发一个通用事件事件再转发给不同的通知渠道。通知渠道包括站内信、邮件、办公软件机器人企业微信/钉钉机器人。站内信是保底方案只要系统里有通知中心用户登录后就能看到邮件适合发给不常登录系统的管理层办公软件机器人触达率最高适合催办和预警。SLA 提醒这里我建议分几个梯度来做不要只在最后一刻才通知。当时我们配置的策略是工单优先级为 P1紧急时SLA 目标响应时间是 30 分钟目标解决时间是 4 小时SLA 时间进度到达 50% 时提醒处理人到达 80% 时提醒处理人和直属上级超时后把工单自动升级到部门负责人。这里有个细节一定要考虑SLA 计时并不是简单地从创建时间开始算一路到解决时间。如果工单进入“待客户确认”状态那段时间不应该计入解决时长否则对处理人不公平。所以在计算 SLA 是否超时时要排除挂起和待客户确认的时间段。这个逻辑不复杂但一旦忘记处理超时提醒就会满天飞团队很快就不信任这个功能了。4. 部署上线与数据迁移少走弯路的实操建议4.1 技术选型与服务器配置参考有很多人问我 DeskcommCRM 用了什么技术栈问的人多了我也想过要不要直接套用微服务架构但最后我们用的是一个非常务实的中型单体方案服务和维护成本都低很多。后端选择了 Java Spring Boot稳定、生态成熟招人也好招如果你团队更熟悉 PHP 或者 Go用 Laravel 或 Go 的 Gin 框架也完全没问题核心业务逻辑是一样的。前端用的 Vue 3 Element Plus浏览器端渲染开发效率高。数据库选了 MySQL 8缓存用 Redis搜索在数据量大之后引入了 Elasticsearch初期直接用 MySQL 的 like 查询也能顶得住。部署环境就是两台云服务器一台跑应用和数据库一台跑 Redis 和 Elasticsearch用 docker-compose 做编排备份策略是每天凌晨全量备份数据库。这里我建议不要一上来就上微服务。CRM 系统的业务逻辑高度聚合用户、客户、工单、通知之间互相依赖强行拆成多个服务会让一次查询变成多次跨服务调用网络开销和数据一致性都是麻烦事。我们在最开始也想“一步到位拆微服务”后来发现团队就那么几个人一个单体能解决 90% 的问题剩下的 10% 留到真的出现性能瓶颈再说。服务器配置方面如果只是几十人团队的内部使用一台 4 核 8G 的云服务器就够跑数据库和应用了再加一台 2 核 4G 的机器跑 Redis 和任务队列初期完全够用。真正吃内存的是 Elasticsearch但也可以先不上等客户量上了 5 万条再考虑迁移也不迟。4.2 上线节奏与数据迁移经验CRM 系统上线最容易翻车的地方不是技术而是“用户根本不用”。如果一把梭把公司所有人的工作习惯都改成新的 CRM很多人会产生抵触心理数据录入质量会非常差最后系统里全是脏数据。当时我们定的节奏是分三步走。第一步只上线客户档案、沟通记录、工单三个核心模块其他功能像报表、自动化流程先藏起来。让团队先养成“每天打开系统看工单、录沟通”的习惯。这一步走了两周期间我们守在办公室里谁不会操作就现场指导遇到流程不合理当场改配置。第二步上线任务提醒和报表模块。当核心数据已经有了一定积累报表才能算出有意义的数据。这一步开始我们给管理者演示数据看板用真实数据说明哪些销售跟进率高、哪些工单环节耗时长管理者看到价值之后从上往下推动会更轻松。第三步再逐步开放更多自定义配置和自动化能力。数据迁移也是一个大坑。如果公司之前在用 Excel 表格记录客户导入系统之前一定要先做清洗。最常见的问题是同一个客户在表格里出现了两遍一次是“北京某某科技有限公司”另一次是“北京某某科技公司”差了“有限”两个字其实是同一家。我们当时的处理方案是先把导入数据按客户名称和联系人手机号两个维度去重去重算法不需要太复杂优先用手机号完全匹配再用启用相似度的模糊匹配给管理员一个候选列表人工确认。导入过程要支持分批导入和回滚不要指望一次把几万条全部导完。我们写了一个导入任务来管理这个过程每条数据导入后记录成功或失败原因失败的数据可以下载失败列表修改后重新上传全部导入完成后对账导入数量和现有系统数量是否一致。这样即使出了问题也不会造成无法挽回的后果。5. 常见问题与排查技巧实录5.1 数据权限越权问题越权问题是我们上线后遇到的第一类严重问题。表面症状是某个员工在浏览器地址栏手动改了 URL 里的客户 ID就能打开其他部门的客户详情页。排查思路很简单但也最容易忽略检查所有接口是否统一走了数据权限过滤。我们当时用脚本把所有包含“客户 ID”参数的接口列出来逐个查看是否调用了统一的权限校验方法。果然发现有两个老接口是在权限模块开发之前写的后来改造时漏掉了。修复方式是在网关层加了一个全局拦截器但凡请求路径包含 customer 和 ticket 的统一从 Token 解析用户角色再调用数据权限服务校验。这里我的经验是权限校验这种横切逻辑一定要设计成全局中间件或者注解不要在每个 Controller 里手写判断。否则接口一多迟早有人漏写。而且越权问题在安全测试中都是高优先级漏洞必须高度重视。5.2 通知丢失与 webhook 超时通知模块上线后我们遇到的问题不是通知触达率太高而是偶尔有人反馈“系统说发了邮件我没收到”。排查之后发现有两个原因一是邮件服务商的每日发送量有限超过额度后邮件直接被丢弃二是给办公软件机器人发送消息时机器人服务响应慢我们的发送代码没有做超时控制导致一直挂着。后来我们做了一套补偿机制所有通知先生成通知记录状态是待发送发送成功后标记为成功发送失败或者超时的记录进入重试队列最多重试三次三次之后标记为失败由管理员手动处理。同时给第三方接口设置了 3 秒超时时间避免一个慢接口拖垮整个通知线程。5.3 客户搜索越来越慢系统运行了大概半年客户数据到了三四万条搜索就开始变慢了。原来用的模糊查询是 where customer_name like %关键词%这种查询即使加了索引也没用因为以通配符开头的 like 无法走索引只能全表扫描。当时没有立刻上 Elasticsearch而是先做了两个优化第一把搜索改成多关键词分词客户名称里包含空格或者特殊符号的统一按词组拆分第二给 MySQL 加上全文索引用 MATCH AGAINST 方式检索。这些优化后搜索速度从三四秒降到了几百毫秒基本够用。一直到数据量到了二十万条查询压力又变大我们才把搜索服务切到 Elasticsearch。5.4 重复数据与状态统计口径还有一个很容易被忽略的问题就是时区。系统如果同时面向国内用户和海外用户时间字段统一用 UTC 存储展示时按用户所在时区转换这个大家都知道。但坑在于很多统计报表按自然日分组如果直接用 UTC 时间分组国内用户看到的“今天”就会差 8 个小时。当时我们的报表连续几天凌晨的数据对不上账排查了两个小时才发现是这个原因。正确处理是报表服务在查询时先按照配置好的时区偏移量做时间转换再去分组统计。另外一个常见的统计口径坑是“已解决工单数”。如果系统里存在从“已解决”退回“处理中”的工单报表统计时要搞清楚用哪个状态作为最终结果。我们当时的约定是工单从“已解决”变成“已关闭”才算完整结束只要还在“已解决”状态就不计入历史解决工单数避免重复统计。最后再说点实在的做 DeskcommCRM 最大的体会是CRM 系统真正的重点不在“管理”而在“让使用者觉得好用”。一线销售和客服愿意每天打开它把每一次沟通都认真记录下来这套系统才有价值。我们在上线过程中做了很多“减负”设计常用操作不超过两次点击客户页面默认展示最近 20 条沟通记录团队领导能实时看到自己团队的工单压力。如果你现在正准备做类似的系统我建议第一版不要贪多把客户管理、工单流转、沟通记录这三件事做扎实就足够跑起来。报表、自动化、高级权限这些可以等业务跑顺了再迭代。技术选型上不用盲目追随热门的微服务和容器化一台 4 核 8G 的服务器加一个单体应用在团队规模百人以内时能撑很久。数据权限和通知补偿这两个模块是容易出问题的地方值得在一开始就设计得严密一些。
返回列表