
1. 项目概述与整体设计思路1.1 为什么叫“DeskcommCRM”看到“DeskcommCRM”这个名字第一反应往往会落在“CRM”三个字母上这很正常。但真正值得留意的其实是前面的“Deskcomm”——它是由“Desk”和“Comm”组合而成的合成词Desk代表桌面办公场景Comm则是Communication沟通/通信的缩写。连起来理解这是一套定位于桌面端办公沟通场景下的客户关系管理系统。这个定位从一开始就和市面上大多数“重销售漏斗、轻日常沟通”的CRM区分开了。我们团队在做这套系统时最初的出发点非常简单销售团队日常最频繁的动作其实不是看报表、调漏斗而是打开电脑、回复客户消息、记录沟通要点、安排下一步跟进。这些动作高度依赖桌面端操作并且和“沟通记录”强相关。所以DeskcommCRM的核心设计思路是把“沟通”和“客户管理”揉在一起而不是让销售在CRM系统和聊天工具之间反复横跳。1.2 这套系统能解决什么问题先说一个实际场景。早些年团队用过几款通用型CRM功能很全但真正落地时会发现一个尴尬的情况销售白天花大量时间在微信、企业IM、邮件上和客户沟通到了下班前再花半小时把沟通内容手动补录进CRM系统。补录这件事大概率是能省则省能简则简。结果就是系统里的客户信息永远滞后跟进记录缺胳膊少腿管理层看数据时总觉得“不真实”。DeskcommCRM要解决的核心问题就是这种“沟通与记录脱节”的痛点。它把客户信息管理、跟进记录沉淀、任务提醒、数据看板整合到一套桌面优先的界面中。销售在系统内直接记录沟通要点系统按时间线自动归档管理者从数据看板能直接看到每名销售的跟进频率、客户转化阶段、任务完成率。同时它支持私有化部署数据完全握在自己手里适合对客户信息安全要求比较高的团队比如做B2B服务、项目制交付、咨询类业务的公司。1.3 适合谁来参考这套方案如果你是以下三类人之一这篇文章的内容会对你有直接帮助有自研CRM想法但不确定模块怎么拆、数据表怎么设计的技术负责人正在选型或优化团队客户管理流程想知道一套“重沟通记录”的CRM应该具备哪些核心能力的业务管理者对全栈开发感兴趣想通过一个完整项目理解权限设计、数据模型、操作日志这类工程要点的开发者。这篇文章不会讲大而全的CRM理论而是围绕DeskcommCRM从零搭建过程中的关键决策、核心数据结构、权限实现、常见坑点做一次系统复盘。所有内容都是基于我们实际开发和上线过程中的真实经验整理拿来就能用得上的部分会直接给出表结构和代码片段方便你复现或参考。2. 核心功能模块与数据模型设计2.1 客户-联系人-商机的三角关系CRM系统的数据模型是整个系统的基础设计得好不好直接影响后续开发效率和业务扩展空间。DeskcommCRM里最核心的三张业务表是客户表、联系人表和商机表它们之间的关系可以用一句话概括一个客户下有多个联系人一个客户下有多个商机商机归属于客户但不直接归属于联系人。为什么商机不挂在联系人下这是一个很实际的业务问题。B2B业务中一个客户的采购决策链通常涉及多个角色使用部门的人提需求采购部门的人走流程财务部门的人审批付款。这时候商机如果只关联到一个联系人销售很难看清整个决策链条。把商机挂到客户层级联系人在商机中通过“角色”字段区分比如“需求提出人”“决策人”“采购对接人”业务模型更接近真实情况。落实到表结构上客户表的核心字段除了常规的公司名称、行业、规模、来源渠道外我们另外加了一个“客户状态”字段用来区分“潜在客户、进行中客户、已成交客户、已流失客户”。这个字段看起来简单但它是后面所有统计看板的筛选基础能直接决定漏斗图、转化率这些数据是否准确。联系人的表结构比较常规核心字段包括姓名、职位、手机号、邮箱、微信、是否关键决策人。这里有一个值得提醒的细节手机号、邮箱这些联系方式应该单独存原始值同时用统一的格式清洗函数做归一化处理避免同一个客户被录成两条记录因为“138-0000-0000”和“13800000000”在系统里如果不做处理会被当成两个不同的号码。商机表则承载了整个销售流程的状态流转。我们设计的核心字段有商机名称、预计金额、当前阶段、成交概率、预计成交日期、负责人。其中“当前阶段”字段和配置中心的“销售阶段配置”挂钩默认包括“初步接触、需求确认、方案报价、商务谈判、合同签订”这几个阶段。每个阶段对应的成交概率不是硬编码在代码里的而是放在后台配置表中允许管理员按业务实际情况调整。2.2 跟进记录的轻量设计跟进记录是整个DeskcommCRM里我私心最看重的一块内容。很多CRM会把跟进记录设计成一个大字段让销售填一堆结构化表单实际上填单率会大幅下降。我们的设计原则是“轻量记录、时间线归档”。系统里跟进记录表的核心字段只有五个关联客户ID、关联商机ID可空、记录类型电话/面访/邮件/即时沟通、内容正文、创建人。不需要单独建“跟进类型表”因为字段就一个枚举硬拆成表反而浪费。内容正文采用长文本支持在编辑器里直接加提及队友被提及的人会收到任务提醒这条设计后续会讲到。数据展示上所有跟进记录按客户维度聚合形成一个按时间正序排列的时间线。销售点进客户详情页一眼就能看到这个客户的沟通全貌第一次电话聊了什么、上周发过什么方案、客户对报价的反馈是怎样的。时间线的数据粒度到分钟级排序直接用创建时间不做复杂的列表分页而是用“加载更多”的流式交互实测下来比传统分页更符合销售翻记录的直觉。2.3 任务与提醒的触发机制任务模块在DeskcommCRM里不是一个单纯的“待办清单”它和客户、商机、跟进记录是联动的。设计上任务表的核心字段包括任务主题、关联类型客户/商机、关联ID、执行人、截止时间、优先级、状态、创建方式。创建方式这个字段很有意思它区分了“手动创建”和“系统自动生成”两种来源。手动创建就是销售自己添加的任务比如“明天上午给张总回电话确认合同细节”。自动生成则是由系统事件触发的典型场景有两个商机阶段变更时系统自动给负责人生成一条“准备下一阶段所需材料”的任务例如从“需求确认”变更为“方案报价”自动生成任务“输出报价方案”。跟进记录中有人被时系统给被的人生成一条任务内容直接引用跟进记录正文点击任务可以跳转到对应的客户详情页。自动生成任务这个功能极大提升了团队的响应速度。以前靠群消息提醒消息一多就被刷没了现在任务直接进入个人待办列表配合首页的今日任务看板不容易漏事。提醒机制上我们做的不是简单的“截止前X小时提醒”而是加了多层递进式提醒。任务创建后24小时未完成系统给执行人发一次轻提醒超过截止时间仍未完成提醒会升级抄送给负责人的直属上级。这个设计是跟业务团队聊完需求后加的——销售任务普遍有“能拖就拖”的倾向稍微加一点管理压力任务完成率提升非常明显。3. 关键技术选型与架构方案3.1 为什么选单体应用而不是微服务DeskcommCRM在技术选型上主动选择了单体应用架构而不是当前更“时髦”的微服务。原因很简单这套系统的业务规模预期是内部团队或中型企业使用用户量级在几十到几百人之间数据量在百万级以内单体应用在开发效率、部署运维、故障排查上的综合成本是最低的。技术栈具体组合是后端用Java Spring Boot 2.7 MyBatis-Plus数据库用MySQL 8.0缓存用Redis前端采用Vue 3 Element Plus权限认证使用Sa-Token框架。这套组合的最大优势是生态成熟、资料多、招人容易。Spring Boot自带的约定优于配置特性配合MyBatis-Plus的代码生成器可以从数据表直接生成基础CRUD代码把重复工作省下来业务功能开发速度能提高不少。有人可能会问为什么不用前后端分离更彻底一点的方案比如后端纯提供API、前端用更重一点的工程化方案我们的选择是折中处理前端用Vue 3工程开发和部署完全独立但后端在做页面跳转时保留了少量服务端渲染的页面主要用于管理后台的配置类功能。这样做的好处是配置类功能逻辑简单、访问量低用服务端渲染可以少写不少前端代码降低维护成本。3.2 权限模型RBAC加数据范围的双层控制权限设计是所有企业级系统的核心敏感点DeskcommCRM采用“功能权限 数据权限”双层模型。功能权限走的是标准RBAC基于角色的访问控制模型角色-菜单-按钮三层结构。用户表、角色表、菜单表、用户角色关联表、角色菜单关联表这五张表是标配这里不展开细说但有一点必须提醒菜单表里除了记录菜单名称和路径外一定要加上“按钮权限标识”字段比如“customer:add”“customer:export”。这样才能实现按钮级别的权限控制否则角色配置只能管到“能不能看到这个页面”完全管不到“能不能点导出按钮”。数据权限则是一个比功能权限更隐蔽也更关键的维度。同样是查看客户列表销售经理应该能看到所有销售的客户普通销售只能看到自己名下的客户。DeskcommCRM中的数据权限采用“数据范围”字段控制在角色表上增加一个枚举字段可选值包括“仅本人”“本部门”“全部数据”。在实际查询时MyBatis-Plus的拦截器会在SQL层面自动拼接数据权限条件比如当前用户角色是“仅本人”拦截器就会在客户查询SQL后面自动加上WHERE owner_id 当前用户ID。这里有一个实际开发中很容易踩的坑数据权限的拼接一定要放在用户输入的所有查询条件之外并且必须使用参数绑定而非字符串拼接否则会有SQL注入风险。另外部门的数据范围还要考虑子部门我们用的是递归查询部门下级部门ID列表再把IN (部门ID列表)拼接进SQL。部门层级深的情况下要提前考虑性能建议给部门表加一个路径字段比如“/1/3/7/”查询子部门直接用WHERE path LIKE /1/3/%比递归高效得多。3.3 客户公海与回收机制客户公海是CRM系统里一个非常实用的功能它解决的核心问题是销售资源闲置。很多CRM新手做开发时容易忽略这个模块但实际业务中它的价值往往比想象中大。DeskcommCRM的公海规则配置为当客户负责人超过30天未更新跟进记录时客户自动掉入公海池其他销售可以领取。对应的当销售领取公海客户后客户自动归属到该销售名下同时重置计时器。这个机制我们用了一张“公海规则配置表”来实现表里保存着“掉入公海天数阈值”这个参数而不是把30天硬编码在Java代码里。这样一来不同团队可以配置不同的阈值——比如新客户要求15天必须跟进老客户可能放宽到45天灵活度完全由后台配置决定。定时任务的实现用的是Spring Boot自带的Scheduled注解每天凌晨2点跑一次扫描。扫描逻辑核心是两步查出所有“负责人不为空且最近跟进时间超过阈值”的客户ID集合批量更新这些客户的负责人为空、状态置为“公海”。为了不影响正常业务高峰建议定时任务执行时间避开工作时段同时查询时要带上索引否则这张客户表数据量上来后全表扫描会拖垮数据库。还有一个小细节容易忽略当客户掉入公海时系统要向原负责人发一条站内信通知说明客户掉入公海的原因和时间。这看起来是小事实际上非常重要。如果不通知销售发现自己跟了很久的客户突然不见了第一反应是找管理员查数据反而增加额外沟通成本。4. 从零实现核心流程4.1 登录认证与操作日志登录认证这块直接采用了Sa-Token框架没有造轮子。选择它的原因一是轻量二是和Spring Boot集成简单三是有成熟的踢人下线、账号封禁功能。用户输入账号密码登录成功后后端返回一个Token前端存储到LocalStorage中并在每次请求的Header里携带这个Token。登录流程上有一个容易忽略的安全细节密码传输必须使用加密方式。DeskcommCRM的做法是前端用RSA公钥加密密码后端用私钥解密后再做校验。数据库里存储的密码字段采用BCrypt算法加密存储绝不存明文密码。这两个措施叠加之后即使数据库泄露攻击者拿到的是BCrypt hash值和被RSA加密过的传输密文也无法直接还原出用户的原始密码。操作日志这块我们用的方案是AOP自定义注解加拦截。定义一个“操作日志”注解在Controller方法上标注后通过切面自动记录操作人、操作类型、操作内容、IP地址、耗时这些信息异步写入操作日志表。这样做的优势是代码侵入性低业务代码和日志逻辑完全解耦。操作日志不只是用来排查看谁改了什么数据更重要的是在出现数据纠纷时能追溯责任比如客户被误删、合同金额被篡改这些问题有日志在手就很好追。4.2 客户360度视图的聚合查询客户详情页是整个系统销售使用频率最高的页面它要在一个页面里聚合展示客户基本信息、联系人列表、商机列表、跟进记录时间线、待办任务。这个功能在技术实现上并不复杂但在性能和数据一致性上需要注意一些细节。最开始的方案是把所有数据放在一个接口里返回给前端前端一次请求全量渲染。但实际测试下来一个合作深度较高、跟进记录有几十条的客户接口响应时间可能超过2秒体验非常差。后来改成拆分为四个子接口按需加载进入页面先加载客户基本信息和联系人列表商机列表和跟进记录通过前端Tab切换时再异步加载同时接口层加了Redis缓存把客户基本信息缓存5分钟极大降低数据库压力。跟进记录时间线接口有一个必须优化的点分页查询时不要直接ORDER BY create_time DESC LIMIT offset, size这样数据量大了之后偏移量越大越慢。正确做法是使用“基于游标的分页”用“创建时间小于上一页最小创建时间”的方式翻页比如WHERE create_time #{lastTime} ORDER BY create_time DESC LIMIT 10实测性能稳定不会随页数增加而退化。4.3 看板统计的SQL优化技巧数据看板是管理层最喜欢用的模块也是后端最容易写出性能瓶颈的地方。看板页面展示的数据包括今日新增客户数、今日跟进次数、商机总金额、销售排行榜、客户阶段分布等。第一版看板接口逻辑比较粗暴每个指标单独查一次数据库一个页面十几个指标就是十几次查询。业务高峰期时管理层集中打开看板数据库压力直线上升。优化方案是“合并SQL 定时聚合”。合并SQL的做法比如今日新增客户数和今日跟进次数可以用一条SQL配合条件聚合来实现SELECT COUNT(DISTINCT CASE WHEN create_date CURDATE() THEN id END) AS today_new_customers, COUNT(DISTINCT CASE WHEN DATE(follow_time) CURDATE() THEN follow_id END) AS today_follow_count FROM customer c LEFT JOIN follow_record f ON c.id f.customer_id WHERE c.deleted 0;定时聚合的做法更加彻底用Scheduled每天凌晨跑一次统计任务把结果写入“每日统计汇总表”。看板页面默认读取最近5天的汇总数据不再实时查询明细表。只有用户主动点击“刷新最新数据”按钮时才走实时统计接口。这样在绝大多数情况下看板秒开数据库负载也能降下来。看板统计还有一个经常被忽略的问题——时区。如果服务器的默认时区是UTC而业务用户都在国内日期函数CURDATE()取到的日期可能和用户看到的日期不一致。解决方案是在数据库连接串上显式配置serverTimezoneAsia/Shanghai并且在JVM启动参数中加上-Duser.timezoneAsia/Shanghai双重保障。5. 常见问题与避坑指南5.1 跟进时间线为什么会出现乱序上线初期接到过反馈说客户跟进记录时间线偶尔会出现乱序比如今天补录的一条记录跑到了上周记录的中间位置。排查后发现原因在创建时间字段上。问题出在数据库的create_time字段使用了DEFAULT CURRENT_TIMESTAMP但部分历史数据是通过数据迁移脚本导入的导入时create_time被显式指定为业务发生时间。当销售在界面上补录历史跟进记录时create_time被手动设置为过去的时间而id还是自增生成的按create_time排序时就和真实的创建顺序产生了冲突。解决方案是把时间线排序改成双重排序先按“业务时间”排序允许用户指定的跟时间业务时间相同时再按ID倒序。同时在前端表单上补录历史记录时插入位置直接根据业务时间动态计算避免用户误把新记录补到错误位置。5.2 数据权限在批量导出时失效另一个真实踩过的坑是数据权限批量导出场景。在页面上数据权限拦截器正常工作销售只能看到自己的客户。但做Excel导出功能时第一版直接写了一个独立的查询SQL来导出所有客户虽然前端按钮按角色控制了“谁能点导出”但没有在导出服务层做数据权限过滤导致部分权限较小的销售可以通过拼接导出接口参数直接导走全量客户数据。这是一个很严重的数据越权漏洞在内部安全审计时被查了出来。修复方案是把数据过滤逻辑抽成一个公共的方法页面查询和导出都必须经过这个方法。方法内部根据当前登录用户的角色自动拼接数据权限SQL条件导出时重新查一遍当前用户有权限的客户ID再通过这些ID去导出而不是沿用前端传过来的“全部客户”查询条件。这个经验想特别提醒做企业系统的开发者功能权限是第一步数据权限才是企业系统安全的核心门槛每一步查询都要问自己一句“当前用户有权限看到这些数据吗”。5.3 公海回收规则误伤客户记录公海回收机制上线两个月后销售反馈了一个问题有客户虽然30天没有更新跟进记录但期间一直通过私下渠道在和客户沟通结果系统自动把客户收回公海导致客户被其他销售领走造成了内部抢客户的矛盾。这个问题的根源是规则设计太刚性没有把业务场景的所有变量都考虑进来。后来我们在规则配置里增加了“豁免条件”比如客户状态为“已成交”或“进行中项目交付阶段”的不参与公海回收。同时增加了“预警机制”客户掉入公海前3天系统会给负责人发预警通知提醒“该客户即将掉入公海请尽快更新跟进记录或申请豁免”。预警机制上线后客户被误回收的投诉量几乎降为零。5.4 数据同步与迁移的坑最后补充一个数据迁移阶段的常见问题。DeskcommCRM早期从Excel和旧系统导入客户数据时经常出现重复客户。当时团队还没有上“查重合并”功能结果导入后用了一段时间才发现数据质量很差一家客户在系统里可能有三四条记录。查重方案我们最终选择了“规则 人工确认”的两阶段方案。规则阶段系统通过公司名称精确匹配、公司名称相似度编辑距离小于等于2匹配、联系人手机号匹配自动识别出疑似重复客户分组。人工确认阶段管理员进入“查重合并”页面逐条确认哪些客户需要合并确认后系统把联系人、商机、跟进记录等关联数据全部转移到保留客户下物理删除被合并客户。整个过程在管理后台操作不需要写SQL处理对运营人员来说友好很多。6. 实测体验与后续扩展方向DeskcommCRM从立项到内部试用再到正式上线整个过程有两点体会特别深。第一点CRM系统成功的关键不在于功能堆得多全而在于真正贴合业务的使用习惯。我们把核心页面设计成“打开客户详情就能完成80%的日常工作”这个交互设计带来的使用率提升远比花哨的数据大屏有效。第二点系统上线只是开始后续的规则调优、数据清洗、用户反馈跟进才是真正耗时的工作需要持续投入。目前这套系统正在做移动端适配方案。因为销售出差在外时掏出手机快速查看客户信息、记录一条跟进、处理待办任务的需求非常强烈。我们的方案不是做一套完整的移动端App而是先做一套基于H5的移动端适配页面只包含客户查看、跟进记录、任务处理三个核心功能小程序和原生App的版本在后续迭代中再逐步规划。另一个计划中的方向是智能化的销售助手。当前系统沉淀了大量客户跟进记录和商机数据后续想基于这些数据做两个能力一是自动提取跟进记录中的关键信息比如客户预算、决策时间节点、竞品信息辅助销售快速了解客户全貌二是基于历史赢单数据分析对商机给出成交概率预测和风险提示。这些功能实现起来需要投入不少精力但方向已经验证过是有实际业务价值的。回到最早的那个问题DeskcommCRM到底解决了什么它把客户管理中最重要的信息来源——日常沟通记录变废为宝把零散的沟通沉淀成结构化的客户资产。这套系统的完整实现覆盖了数据建模、权限控制、业务流程引擎、定时任务、数据聚合这些企业级开发中的核心知识点如果你正在规划自研CRM或者想把团队客户管理流程理得更顺文中提到的这些设计思路和踩坑经验可以直接拿去做参考。