从Excel到自研CRM:DeskcommCRM数据库设计实战与踩坑记录
DeskcommCRM上线第47天我养成了一个新习惯每天早上到工位的第一件事不是开邮箱而是打开它看一遍昨天的跟进漏斗。这个习惯的背后是团队从Excel表格管理客户到被一个自研小系统彻底扭转工作方式的真实变化。DeskcommCRM是我和另一位后端同事利用零散时间做的客户关系管理系统名字拆开看就是Desk桌面办公Comm沟通定位很清楚把散落在聊天记录、Excel表格和个人通讯录里的客户信息统一收口到一套围绕“沟通”展开的系统里。今天回头总结最想说的不是技术多牛而是踩过的坑、想明白的逻辑和那些代码之外的东西。如果你也正纠结要不要自研CRM、怎么设计客户字段、怎么让销售真的愿意用这篇文章值得看完。里面有完整的数据表设计思路、状态机设计、权限模型还有从Excel迁移历史的完整流程都是我实际跑过的。1. 为什么放着现成CRM不用非要自研DeskcommCRM1.1 团队最初的痛点客户数据散落在三个“黑洞”里项目启动之前我们团队管理客户的方式非常原始销售各自维护一张Excel表每周五汇总一次加上企业微信里密密麻麻的聊天记录。这套方式撑到了团队12个人开始明显绷不住了。第一个问题客户资料跟着人走。有销售离职交接表里就几十行客户名称和联系方式之前聊过什么、报价多少、卡在哪个环节基本说不清。接手的人等于重新开发一遍。第二个问题数据口径不统一。同一个客户在A的Excel里叫“华信科技”在B的表里叫“华信科技有限公司”月底汇总的时候根本对不上。更别说有些销售习惯把备注写在聊天记录里翻起来要命的慢。第三个问题过程数据完全没有。老板问“这个月成交周期是不是变长了”答案是不知道。连最基本的漏斗分析都做不了因为手上只有结果没有过程。当时我也买过一些SaaS CRM的试用账号研究了一周后放弃了。不是产品不好而是用不起来原因下面细说。1.2 市面CRM的“最后一公里”问题现在市面上的CRM产品从国际大厂到国内各类SaaS功能其实都做得很全。但对我们这种20人左右的团队来说反而有几种“不合脚”的尴尬。一是业务模型匹配度低。很多CRM是给标准化销售流程设计的比如线索-L机会-客户层层转换但我们团队的实际流程更灵活客户可能是直接从老客户转介绍来的可能先有订单后有建档。系统流程卡得太死销售就会绕过系统。二是按坐席收费越用越贵。客户管理是全员的事情财务、市场、售后都要看数据每加一个账号都是成本。而真正需要的定制功能比如对接我们内部的企业微信、自动生成特定格式的报价单往往在标准版里做不到要上更高套餐或者单独购买API能力。三是数据主权问题。客户数据是业务最核心的资产放在别人的服务器上虽然协议写得好好的但导出限制、接口配额、迁移成本都是隐形成本。对一个小团队来说万一平台调整收费策略很难受。这些“最后一公里”问题让我确定了自己做一套更符合业务的系统。1.3 自研的成本账两开发仨月换来主数据自由决定自研之前我认真算过一笔账。人力方面我和后端同事利用弹性时间开发核心功能大概花了两个多月我负责整体设计和后端接口另一个同事负责前端页面。基础设施成本很低一台云服务器加域名一年的费用还不如SaaS CRM两个坐席的订阅费。维护成本方面每周固定两小时迭代日常问题随时修。这个投入换来的东西很明确数据全在自己手里字段想加就加流程想调就调没有供应商沟通成本。需要说明的是自研CRM并不适合所有团队。如果你们的销售流程非常标准、预算充足、团队超过50人用成熟产品很合理。但如果你们像我一样团队规模不大、业务流程特殊、想把数据资产实实在在握在手里、又有一定开发能力那自研不仅可行而且在长期竞争中是笔划算的投资。2. DeskcommCRM核心模块拆解客户、跟进、订单一张网2.1 客户主数据模型不是一张“客户表”那么简单CRM最核心的是一张客户表但真正设计的时候你会发现“客户”这个概念至少得拆成两个实体客户customer和联系人contact。很多小团队做CRM容易犯一个错误把联系人和客户混在一张表里。对一个贸易公司来说一个客户公司可能有采购负责人、财务负责人、技术对接人三个联系人如果混在一张表里要么联系人字段重复要么只能存一个人都很难受。我在DeskcommCRM里的表结构是这样的CREATE TABLE customer ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_name VARCHAR(200) NOT NULL, industry VARCHAR(50), customer_level TINYINT DEFAULT 1 COMMENT 1-普通 2-重要 3-VIP, source VARCHAR(50) COMMENT 客户来源渠道, owner_id BIGINT COMMENT 归属销售ID, status TINYINT DEFAULT 1 COMMENT 客户状态对应状态机, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); CREATE TABLE contact ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_id BIGINT NOT NULL, contact_name VARCHAR(100) NOT NULL, phone VARCHAR(20), wechat VARCHAR(50), position VARCHAR(100), is_primary TINYINT DEFAULT 0 COMMENT 是否主要联系人, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_customer_id (customer_id) );这样设计以后在一个客户详情页里能清楚看到所有联系人每个联系人还能单独记录跟进历史。主联系人是默认展示的人其余作为补充这个设计后期在打电话、发邮件时非常有用。2.2 跟进记录把“时间线”作为客户故事线跟进记录模块是DeskcommCRM里我认为设计得最对的地方。这不是简单的备忘而是把每个客户的生命周期故事串起来的关键。我采用了一种“时间线”思路客户主页的所有记录按时间倒序排列每一条跟进记录都带上类型标签电话沟通、微信聊天、线下拜访、邮件往来还能关联对应的订单。销售点开一个客户就能像翻朋友圈一样看到这个客户从建立联系到现在全部经过。跟进记录表的简要设计CREATE TABLE follow_up_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_id BIGINT NOT NULL, contact_id BIGINT, record_type TINYINT COMMENT 1-电话 2-微信 3-见面 4-邮件, content TEXT NOT NULL, next_action_date DATE COMMENT 下次跟进日期, creator_id BIGINT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_customer_time (customer_id, created_at) );有一个细节对销售很重要跟进记录只允许管理员软删除普通销售只能编辑自己的记录。这个规则一开始被吐槽“不自由”但后来证明是对的。客户资料是公司的资产记录一旦删了一段客户关系史就断了。我们允许“标记为无效记录”来替代删除既保留了数据完整性又让有权限的人能处理错误。2.3 订单与收款CRM必须能看出“钱在哪里”如果CRM只做客户档案和跟进记录本质上就是电子通讯录。一个真正好用的CRM必须把订单和收款金额纳入进来因为销售漏斗的核心维度是金额。订单模块我设计得很克制只保留必要字段CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_id BIGINT NOT NULL, order_no VARCHAR(32) UNIQUE NOT NULL, product_summary VARCHAR(500), total_amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 1 COMMENT 1-草稿 2-已确认 3-交付中 4-已完成, expected_sign_date DATE COMMENT 预计签约日期, sign_date DATE COMMENT 实际签约日期, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );有金额字段以后漏斗分析就真正落地了。首页看板可以直接算出潜在客户数 × 平均客单价 潜在商机总额。还能按阶段看金额分布比如“报价中的订单总金额有80万”这在以前Excel时代是做不到的每次问销售都要现看半天再说一个大概数。3. 字段与流程CRM系统里最容易翻车的业务细节3.1 客户状态机拦住那些“拍脑袋”的修改客户状态是所有CRM系统绕不开的环节。在这个问题上比“有状态”更重要的是“状态怎么流转”。我见过一些团队用简单的下拉框销售想怎么选就怎么选。然后就会出现一种情况一个跟了三个月毫无进展的客户在录入当天就被标记为“成交”。这种数据对管理没有任何意义反而污染报表。DeskcommCRM的做法是做一个明确的状态机状态编码状态名称允许流转到1线索2, 52已联系3, 53实质沟通中4, 54成交45流失2状态机上唯一不能跳过的是“成交”这个状态。任何客户要标记为成交必须经过“实质沟通中”并且要填写成交金额和签约日期。流失也一样必须填写流失原因价格太高、决策周期长、被竞品抢走等。代码层面其实很简单后端接口里做一个状态流转校验# 以Python伪代码表示状态流转判断逻辑 ALLOWED_TRANSITIONS { 1: [2, 5], 2: [3, 5], 3: [4, 5], 4: [], 5: [2] } def transition_status(customer, new_status): allowed ALLOWED_TRANSITIONS.get(customer.status, []) if new_status not in allowed: raise ValueError(f非法状态流转: {customer.status} - {new_status}) customer.status new_status customer.save()这个简单的约束把数据质量直接拔高了一个层次。到了月底老板看漏斗的时候每一层的数据都是可信的不再需要反复追着销售改。3.2 去重逻辑早做早省事晚做天天烦去重是CRM里特别容易忽略、但影响特别大的功能。团队12个人一个月能录入几百条客户资料重复率大概在10%左右。如果没有去重同一个客户会分散在两个销售名下跟进历史互相看不到到了月底抢单纠纷能吵到老板那去。我的去重策略分两层。第一层是防——录入时自动查重。用户在录入界面填完手机号前端调一个接口查重如果库里已有同手机号的客户立刻弹窗提醒。客户名称则用MySQL的LIKE匹配加编辑距离算法做模糊判断比如“华信科技”和“华信科技公司”能识别出相似但是需要一个阈值避免把“华信科技”和“华强科技”也当成同一个。第二层是治——定期合并。后台有一个“疑似重复客户”列表每周管理员跑一遍把确认重复的客户合并。合并操作有个关键细节合并不是简单删除一条而是把两条客户下的所有联系人和跟进记录都转移到保留的那条下。前端操作很简单但后端的合并事务要写仔细否则容易丢数据。去重做得好的直接效果是销售之间因为撞单扯皮的事基本消失了因为系统里一眼就能看到谁在建档、谁在跟进。3.3 必填项与评分规则给销售“软约束”而不是“硬捆绑”CRM系统失败的常见原因就一个字烦。录一条客户要填二十个字段谁都不愿意用。我这里有一个原则让录入便捷让分析完整。录入界面只强制填三样客户名称、联系人名称、手机号。其他行业、规模、来源都选填。老板要的分析维度靠后面线索评分模块补而不是靠强制录入。线索评分是一个很精巧的设计。系统根据已有信息给每个客户自动打分-- 线索评分逻辑MySQL查询示例简版 SELECT id, customer_name, (CASE WHEN industry IS NOT NULL THEN 15 ELSE 0 END CASE WHEN customer_level 3 THEN 30 ELSE 0 END CASE WHEN source IN (转介绍, 老客户) THEN 25 ELSE 0 END CASE WHEN (SELECT COUNT(*) FROM follow_up_record WHERE customer_id customer.id) 3 THEN 20 ELSE 0 END ) AS lead_score FROM customer ORDER BY lead_score DESC;这个评分不会直接给销售压力而是显示在客户列表的“优先级”列里。销售一眼能看出哪些客户该优先跟老板也能用评分筛出高潜客户。相比强制执行十几个必填项这种方式接受度高得多。4. 多角色权限与数据隔离让老板看得见让销售不慌4.1 角色-权限-数据范围三角约束模型CRM里最难设计的是权限因为业务牵扯到“谁的数据、谁能看、谁能改”这三个纠缠在一起的问题。我采用业界成熟的RBAC模型加数据范围控制角色、权限、数据范围三者组合出一套清晰的访问控制。角色定身份权限定操作数据范围定边界。三个要素独立配置角色数据范围可用操作管理员全部客户查看、编辑、删除、导入导出、系统配置销售总监本部门全部客户查看、分配、编辑、导出销售本人持有客户查看、编辑、创建只读用户全部客户只读这个模型的优点是灵活。比如市场部的人想看客户统计数据但不需要改数据给一个“只读用户”角色就行。财务要看订单金额同样只读。权限设置页面里勾一勾不用写一行代码。4.2 客户池让“僵尸客户”流动起来权限设计之外最影响团队协作的是客户归属机制。DeskcommCRM里做了一个“客户池”概念类似公海机制。每个销售都有一个持有上限默认是200个客户。超过上限后继续添加客户会被系统拦截提示先处理现有客户。客户在持有者名下超过30天没有跟进记录会自动退回客户池变成“无主客户”其他销售可以领取跟进。这个机制一开始阻力很大。销售觉得我谈不下来的客户凭啥给别人但运行三周之后效果非常明显。原来很多销售手里几百个客户真正在跟进的可能不到一半大多数躺在Excel里吃灰。退回池子里的客户被其他销售重新挖掘反而转化了好几单。对团队管理来说客户池还解决了一个问题新销售没有客户资源。以前新来的人只能从老销售的“残羹冷炙”里碰运气现在可以直接去客户池领取起步平等了团队活力也上来了。4.3 操作日志权限系统的“最后一公里”权限系统只有堵截是不够的还得有审计。DeskcommCRM里加了一个关键字段变更日志表记录谁在什么时间改了什么字段从什么值改成什么值。CREATE TABLE field_change_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, table_name VARCHAR(50) NOT NULL, record_id BIGINT NOT NULL, field_name VARCHAR(100) NOT NULL, old_value TEXT, new_value TEXT, changed_by BIGINT NOT NULL, changed_at DATETIME DEFAULT CURRENT_TIMESTAMP );这个功能上线时没人觉得有用直到有一次两个销售争论一个客户到底是谁先改的状态翻日志一目了然。还有一次我误删了一批测试数据靠操作日志才发现删除路径有问题及时修掉了Bug。这样的日志也是给销售的一颗定心丸。因为系统记录一切所以不该动的数据没人动该共享的信息大家都看得到团队氛围反而更健康。5. 历史数据迁移从Excel到DeskcommCRM的完整踩坑记录5.1 数据清洗比想象中更“脏”的Excel表旧数据迁移是自研CRM里最容易被低估的环节。我们当时拿到销售们交上来的Excel表整理完才发现问题比预想的多太多。手机号格式五花八门有的带86有的带86有的中间带空格有的是11位但前两位对不上号。客户名称有简写、有全称、有英文名。最夸张的是一个客户的跟进备注里填着“联系过太贵了明年再看”连日期都没有。这些数据直接导入系统唯一的结局就是把新系统也变成垃圾堆。清洗这一步我用的Python脚本处理核心逻辑就几个import pandas as pd df pd.read_excel(customers_raw.xlsx) # 清洗手机号提取数字保留11位 df[phone] df[phone].astype(str).str.replace(r\D, , regexTrue) df[phone] df[phone].str[-11:] # 客户名称去空格、统一大小写 df[customer_name] df[customer_name].str.strip().str.title() # 筛选出有效数据 df df[df[customer_name].notna() df[phone].fillna().astype(bool)] # 写入迁移中间表 df.to_csv(customers_clean.csv, indexFalse)那个阶段最耗时间的其实不是写脚本而是跟销售核对每一条存疑数据。我前后拉了三轮会议逐条确认“这个客户还跟不跟”“这个金额有没有写错”。这一步没有任何捷径数据要人工确认因为它直接影响后续判断。5.2 双写策略与过渡期别把鸡蛋一次性放在新篮子里数据迁移不是“某天把Excel导入系统”就完了那样风险太大。我给团队设计了三段式过渡。第一段预演迁移。挑一个销售的全部客户数据完整跑一遍清洗、导入、校验流程确认系统里展示无误。预演发现的典型问题包括字段映射错误、状态值对不上、部分客户的归属人被分配到了默认组。第二段并行演练。让两个核心销售每天在系统里记录新增客户同时继续维护自己的Excel。这个阶段持续了两周。不是让他们双写全部数据——那太累了——而是让他们感受系统的录入流程、跟进界面把问题反馈给我。第三段正式切换。选择一个周五下班后批量导入所有清洗好的历史客户数据。周一全体直接用系统Excel表格归档封存。三阶段跑下来最大的经验是不要追求一次完美要让团队先“用起来”再“用好”。5.3 迁移校验验证数据不只是“导进去”了数据导入完成不代表迁移结束还得做严格的校验。当时我设了三个检查点。数量校验清洗后的客户总数、联系人总数逐表核对导入后的数量差一个都不行。这个用SQLCOUNT(*)一查就能对简单但重要。完整性校验随机抽取30个客户逐条核对企业名称、联系人、手机号、状态字段是否与源Excel一致。再把关键客户金额大、级别高拿出来全量核验。如果关键客户的数据错了会很麻烦。权限校验确认每一个客户的所有者owner是不是原来负责的销售。系统里如果导入时字段对错了客户的归属人就换了正规业务里这就是事故。校验过程中我还真发现过一个坑某个销售的Excel里客户状态写的是英文“Won”清洗脚本里没映射到这个值导入后状态为空。因为预演时没覆盖到这个销售的数据最后靠完整性校验才揪出来。这之后我的经验是清洗规则必须跟所有销售确认一遍不能只看两三个人的表格就定规则。6. 上线46天后团队真实的用法与下一步迭代方向6.1 数据说话上线前后的关键指标对比系统上线到现在将近七周我拉了一组对比数据感受比较直观指标上线前Excel时期上线后第6周人均每周录入跟进记录无法统计基本没有约7条客户建档数量约400条大量重复508条去重后找回历史沟通记录平均耗时5-10分钟10秒内销售漏斗阶段可见性不可见实时可见客户池流失再分配无8个客户被重新跟进最让我意外的是销售们开始在系统里主动标记“下次跟进日期”和“预计签约日期”了这是我在培训时没有刻意强调的功能。说明系统一旦被用起来工作流程会自己往更结构化的方向演化。6.2 销售不用系统的原因排查不是功能少是入口远上线第三周我一度很焦虑。虽然数据指标在涨但后台数据显示有两三个销售每天只在电脑端登一次录入全靠自动同步。我逐一找他们聊了一圈发现原因既不是界面难用也不是权限不够而是工位边上的手机就在手边打开系统还要走到电脑前太远了。这个反馈直接推动了我们做移动端适配。虽然还没有原生App但是我们优化了手机端H5页面把“快速添加跟进”做成了首页的悬浮大按钮点进去只需三步选客户选类型输入一两句话。整个操作在手机上十几秒就能完成。另外还有一个心理因素。有的销售隐隐担心“我干了多少活老板全看见了”。为了消除这个顾虑我在周会上明确公布了数据使用原则数据看板用于团队资源调配和问题发现不用于绩效扣罚。并且把系统里“本周跟进排行榜”做成了一个中性展示重点是鼓励而不是惩罚。这样做以后销售对系统的排斥感明显降下来了。6.3 下一阶段的三个迭代方向根据这些天的实际使用反馈我把下一阶段的迭代方向定在三个方面也算给大家做个参考。第一进一步集成企业微信聊天记录把沟通上下文自动沉淀到客户时间线里。这是最大的一块效率提升也是销售最常念叨的需求。第二增加自动化提醒。目前“超过30天未跟进自动退回客户池”还是后台定时任务下一步要把它做成销售端消息提醒比如“你有3个客户15天没跟进了”让规则从“秋后算账”变成“事前提醒”。第三报表中心升级。按产品线、按行业、按销售个人维度交叉分析订单金额和转化周期。很多销售和老板其实并不知道各环节的真实转化数据有了这个功能后续经营决策会更有依据。做DeskcommCRM这几十天我最大的感受是一个内部系统能不能成功能做多少反而排在第二位第一位是它有没有真正嵌进团队的日常动作。数据模型设计得再精巧表格建得再规范如果销售觉得“多一步操作都麻烦”系统就会被架空。所以凡是新增字段、新增页面我都先问一句这玩意儿能让用的人自己省力气吗能再上。这个标准我建议所有做内部系统的人参考。