news 2026/9/16 19:12:47

自研CRM系统实战:从技术选型到客户数据架构的踩坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自研CRM系统实战:从技术选型到客户数据架构的踩坑指南

DeskcommCRM,这个名字第一次出现在我们团队讨论群里,是去年年初。当时销售总监在会后发了很长一段吐槽:客户资料散在微信聊天记录、Excel表格、报价邮件和个人通讯录里,新人上手靠拷历史文件,客户撞单要翻半天聊天记录才能判断归属,月底汇报业绩只能人工拼数据。他说得直白——我们需要一套能真正管住客户全流程的工具。

市面上现成的CRM产品我基本都试用过一轮,要么太重,实施周期按月起算;要么太轻,只能当通讯录用。后来团队拍板,自己动手搭一套适合我们业务节奏的客户关系管理系统,这就是DeskcommCRM的起点。前后从需求梳理到上线,花了大概九个月,中途砍过需求、推倒过两次数据模型,也踩了不少部署和权限设计的坑。这篇文章把我个人踩过的一个一个坑梳理出来,给在纠结自建CRM还是做技术选型的朋友当个参考。

1. 项目背景与整体定位

1.1 为什么放弃现成产品,选择自研

讨论自研之前,我们先把市面主流的CRM产品做了一次比较详细的选型调研。当时团队规模在四十人左右,销售加售前大概二十人,年付预算控制在三五万以内。这个预算段能买到的产品,普遍有几个痛点。

第一是系统重、配置门槛高。多数正统CRM产品对标的是大型企业的完整销售体系,字段可以自定义到非常细的程度,但同时要求有专门的系统管理员去维护。对我们这种没有专职IT运维岗位的团队来说,光是把部门架构、审批流、字段权限调通,就要花好几周。第二是数据归属问题。客户资料和跟进记录都存在服务商那边,虽然合同上写了数据所有权归我们,但真要迁出来的时候,接口字段和导出格式往往要二次开发,迁移成本高。第三是价格模型不友好。按坐席收年费的计费方式,二十人一年就是四五万起步,而且加一个用户就要加钱,扩团队的成本几乎是线性的。

自研最大的好处是功能完全掌控。我们可以先做最小闭环:客户管理、跟进记录、商机阶段、基础报表。等团队用顺了再逐步加日历同步、周报推送、数据大屏这些锦上添花的模块。还有一点容易被忽略的自研动力,就是字段和流程的调整成本。销售团队的业务节奏是动态变化的,产品增加一条新业务线、销售流程加一个审批节点,自研系统改一个表加一个页面就能交付,而用外部产品往往要提工单等排期。

当然,自研也不是没有代价。最明显的是开发时间挤占了核心业务,所以我们一开始定了铁律:MVP版本控制在三个月内,不做完美设计,只做销售每天必须用的四五个功能。

1.2 产品边界与技术选型

DeskcommCRM最开始的目标非常克制,它就是一个"销售客户管理工具",不是OA、不是财务系统、也不是数据分析平台。产品边界清晰,开发过程就会少很多拉扯。我们甚至主动砍掉了在线聊天和工单系统,因为那对应的是客服业务,和销售管理是两个场景。

技术选型围绕团队熟练度和部署成本来定。后端用的是 Java 17 加 Spring Boot 3.x,配合 MyBatis-Plus 操作数据库。之所以不选 Node.js 或者 Python,主要是团队里写 Java 的人最多,而且 Spring 生态里的 Spring Security、定时任务、数据校验这些组件非常成熟,写业务代码能省不少事。数据库用的 PostgreSQL 15,主要看重它对 JSON 字段的支持和多表复杂查询的能力,后面做客户360视图和动态字段扩展时特别有用。缓存用了 Redis,单实例就够,主要是存登录会话、热点客户详情和防并发编辑的锁。前端是 Vue 3 加 Element Plus,表格、表单、弹窗这些管理后台的常用组件开箱即用,视觉风格我们稍微做了一层定制,让员工用起来不觉得是"开发写给自己看的东西"。

部署方式用的是 Docker Compose,把后端、前端、数据库、Redis、Nginx 拆成五个容器,一台 8核16G 的云主机就能跑得很稳。这套架构放到今天来看确实比较传统,没有服务网格也没有微服务,但对一个二十人使用的内部系统来说,复杂架构除了增加故障点没有任何实际收益。我个人的观点是,选架构首先是匹配团队规模和业务复杂度,而不是追求技术热点。

2. 核心模块设计与数据建模

2.1 线索、客户、商机的数据链路设计

业务数据模型是整个DeskcommCRM的骨架,这块我们前后推翻了两稿,最后才定下来"线索-客户-联系人-商机"四层结构。

线索指的是尚未验证有效性的原始信息,比如在行业大会上收集的名片、官网留资的潜在用户、市场活动拿到的电话名单。线索统一进公共池,销售认领后先电话沟通确认意向,如果对方确实有合作可能,就转成正式客户。为什么要把线索单独拆出来?因为线索量大且信息不完整,如果直接混进客户表,报表统计时很难区分"有效客户数"和"待洗名单数",而且容易导致重复跟进。

客户是真正进入销售漏斗的对象,一条客户记录应该是一个公司主体。公司可能有多个联系人,所以我们单独建立了联系人表,一个客户下挂多个联系人,每个联系人可以有自己的手机号、微信、职位和决策角色。早期我们图省事只建了客户表,把联系人和客户拆分好之后,才发现做联系人去重和找关键决策人时效率完全不一样。

商机则关联到一个客户下的具体生意机会。客户是长期关系,商机是短期项目。比如某家公司已经是我们的正式客户,最近有一个新产品的采购计划,这就是一个新的商机。商机表里维护了预计金额、预计成交日期、当前阶段和赢单概率。整体数据关系就是:线索可以转化为客户,客户下面挂联系人和商机,商机成交后生成订单记录。

这里有个设计细节值得提一下。我们把"状态"字段设计成了三态:通用状态、业务状态和内部状态。比如线索有"新建、跟进中、已转化、已作废"等业务状态,还有"是否公海池、是否锁定、是否重复"这种内部状态。刚开始设计的时候,不少人提议把所有状态放在同一个字段里,后来发现查询逻辑会越来越复杂,因为"作废的线索又出现在公海池"这种状态组合很难用一个枚举值表达。拆开之后,每个维度各自独立,维护起来清晰很多。

2.2 客户360度视图的实现思路

客户360度视图是DeskcommCRM中使用频率最高的页面,也是CEO和销售主管最关注的页面。它的核心目标很简单:让一个销售在进入客户详情页的十秒钟内,能快速掌握这个客户的基本情况、最近跟进动态、正在推进的商机、以及团队成员在这个客户上有过多少沟通。

具体实现上,客户详情页拆成五个Tab区域:基本信息、联系人、跟进记录、商机列表、操作日志。基本信息展示客户名称、行业、规模、来源渠道、负责人和创建时间等。联系人Tab是当前客户下所有联系人的列表,突出显示"关键决策人"标签。跟进记录是一个时间线组件,所有电话、拜访、微信沟通的反馈都按时间倒序展示,新内容自动置顶。商机列表则展示当前客户下处于不同阶段的所有商机,方便销售判断下一步该推动哪个项目。

技术实现方面,为了减少页面加载时的数据库压力,我们做了一层 Redis 缓存,key 用客户ID,value 是序列化后的详情JSON。这个缓存不能存太久,因为销售每次跟进都会更新数据,所以我们设置五分钟过期,离开页面再回来重新读取就是最新数据。另一个经验是,客户详情页的慢查询一定要提前优化。我们第一次联调时发现详情页接口要两秒多才返回,后来用执行计划排查,发现是联系人表和跟进记录表的索引没建对,补上 customer_id 和 create_time 的联合索引后,响应时间降到了一百毫秒内。

还有一个容易踩坑的地方是并发编辑。两个销售同时打开同一个客户详情页编辑基本信息,后提交的人会覆盖先提交的内容。为了防止这种情况,我在保存接口里加了一个简单的乐观锁:更新时带上记录原来的更新时间戳,如果数据库里的时间戳和提交时不一致,就提示"该客户信息已被其他人修改,请刷新后重试"。这个实现成本很低,但确实避免了多次数据互相覆盖的问题。

2.3 权限模型:读、写、删都要有边界

权限模型是CRM系统里最容易出问题的地方,也是销售总监每次开会都反复提的痛点。销售数据的敏感性体现在两个层面:一是不能让人随便看所有客户的联系方式,否则存在飞单风险;二是不能让业务人员删除跟进记录,否则出了争议无据可查。

DeskcommCRM的权限模型分三个维度:菜单权限、数据权限和操作权限。

菜单权限控制的是用户能看到哪些功能模块。我们定义了四种角色:系统管理员、销售主管、销售专员、只读访客(财务和客服用)。系统管理员拥有全部菜单的操作权,销售主管比销售专员多出团队报表和公海池分配两个菜单,只读访客只能浏览不能新增和编辑。

数据权限控制的是用户能看到哪些记录行。这是权限模型里最绞尽脑汁的部分。我们采用了最简单的行级权限策略:每条客户记录上有一个 owner_id 字段,对应销售负责人的用户ID。普通销售的默认数据范围是"仅本人负责的客户",销售主管的范围是"本人团队内所有销售负责的客户",系统管理员是"全部客户"。这个逻辑谁来看都不复杂,但实现时一定要把所有入口都覆盖到,因为列表页、搜索接口、报表统计、导出功能各自都要拼接数据权限过滤条件。我们第一版只改了列表页的SQL,结果导出功能没加条件,销售主管导出了全公司的客户名单,幸好是在测试环境发现的,不然后果比较严重。

操作权限控制的是增删改按钮是否可用。删除操作我们做了严格控制:只有系统管理员可以物理删除客户记录,其他角色最多只能标记"无效"或"不再跟进",记录本身永久保留。跟进记录只允许创建和补充,一旦提交就不允许修改和删除。当时销售总监提了一个很有价值的理由:万一客户投诉销售服务态度不好,公司需要完整还原整个跟进过程,修改和删除功能会破坏证据链。

权限配置的时候还有一个小细节:用户数据范围发生变化时,比如某销售离职、客户被转交给别人,必须要有一个批量转移操作。我们做了"客户转移"功能,管理员可以选择原负责人,指定新负责人,然后系统把所有未成交的客户批量转移,同时保留原负责人的历史跟进记录和新负责人的接手备注。这个功能在后续团队人员调整时帮了大忙。

3. 关键功能实现与实操要点

3.1 客户导入与去重:批量操作中的最大坑

DeskcommCRM上线初期,摆在我们面前最头疼的问题就是存量数据迁移。二十个销售的客户资料分布在各种地方,统计下来有接近两万条不规范的记录。如果靠人工逐条录入,光是录入就要花掉将近两周时间,而且录入过程中出错率高。所以客户导入功能成了MVP版本的刚需。

导入功能表面上看起来很简单:上传Excel,解析每一行,插入数据库。但第一次联调就出了大问题。测试人员上传了一个两千行的Excel,页面转圈转了三十秒,然后接口超时。原因很简单,我们最初实现是同步解析加循环插入数据库,两千条记录逐一执行INSERT语句,事务太长导致MySQL锁了大量行。后来改成批量插入,每五百条作为一个批次,在内存里拼好再一次性写入数据库,两万条数据大概十秒就导完了。这一点是第一个值得记录的经验——批量数据操作千万别用循环单条INSERT。

比导入性能更重要的是去重策略。同一家客户可能出现在多个销售的Excel里,只是公司名称写法略有差异,比如"北京某某科技有限责任公司"和"北京某某科技有限公司"只有一字之差,还有的填的是"某某科技"简称。我们最开始想去重直接比对公司名称字符串,效果惨不忍睹,一个名称差一个字就跳过了。

后来参考了不少开源系统的做法,定了一套三级去重规则:第一级,联系人手机号完全一致;第二级,公司名称去掉后缀词和空格后完全一致;第三级,邮箱前缀一致且域名一致。命中任一规则,就判定为疑似重复。执行去重时,如果有同一个人名下的两条记录,系统自动保留最近更新的一条,另一条标记为"疑似重复"进入公共池,由管理员人工确认是否合并。这套规则不能保证百分百准确,但实际操作下来,去重准确率能到九成左右,剩下的人工处理成本可接受。

导入模板的设计也要注意。模板里的每一列都要有明确说明和示例值,不要只有列名没有示例。比如"客户规模"这一列,模板里要写清楚可枚举的值范围:初创、小型、中型、大型,否则销售填出来五花八门的"很大的公司""三百号人"这种格式,后端解析时还得再做一层映射处理。

3.2 跟进提醒与日程联动

CRM系统最怕的不是没人录入数据,而是录了数据之后没有后续动作。销售跟进客户是有节奏的,比如新线索必须在二十四小时之内首次电话联系,意向客户每三天要跟进一次,商机到了最终报价阶段每天都要关注。如果没有提醒机制,这些规则写在制度里没有任何意义。

DeskcommCRM在跟进提醒上做了两件事。第一件是内置的待办任务。销售在录入跟进记录时可以顺手创建一个后续任务,比如"下周二上午十点给张总回电确认报价方案",任务到了截止时间还没有完成,系统会把当天所有未完成任务汇总推送给销售本人。第二件是定时扫描提醒。系统跑了一个每日定时任务,扫描所有客户资料中的"下次跟进时间"字段,超过三天没有新增跟进记录的客户,自动生成一条高优先级的提醒,同时抄送给销售主管。

提醒渠道选择上也纠结过。一开始做了邮件订阅,后来发现销售根本不看邮件,尤其是外勤路上。后来接入了企业微信机器人,把待办提醒推送到对应的企微用户,这个转化率一下就上来了。技术实现上,提醒任务是用 Spring 的 @Scheduled 注解写的,每十分钟扫一次待提醒的任务表,批量调用企微机器人接口发送。这里有一个小坑:企微机器人接口有每秒频率限制,如果待办任务特别多,直接循环调用容易被限流。我们的处理方式是加了一个简单的令牌桶限流,每秒最多发送五条,剩余的排到队列里下一批次发送。

日程联动是后续迭代加的功能。很多销售习惯用日历管理自己的时间,我们为每条跟进任务生成一个iCal格式的日历邀请,销售可以一键把系统里的跟进计划同步到自己的Outlook或手机日历。实现上其实不复杂,就是用数据拼接一个标准iCal文件,加上ics的请求头返回给前端,浏览器就会自动识别并弹出添加到日历的提示。虽然后面不少销售反馈还是习惯直接在系统里看,但这一步对提升专业度的价值还是有的。

3.3 数据报表与销售看板

报表是销售管理层使用得最多的功能。DeskcommCRM的报表模块没有采用市面常见的拖拽式报表设计器,而是针对几个固定的业务场景提前写好查询和图表,保证常用报表打开就能看,不需要培训。

最核心的报表是销售漏斗,也就是商机阶段转化分析。我们把商机阶段定义为:初步接触-需求确认-方案报价-商务谈判-赢单,每个阶段对应一个预计赢单概率。报表按团队维度或销售维度统计当前在跟进的有效商机数量和总金额,展示每个阶段的分布情况。它能让管理者直观看到,"方案报价"阶段积累了大量金额但迟迟没有推进,说明销售在价格谈判上遇到了共性阻力。

目标完成度报表是另一个高频入口。每年年初我们会把年度销售目标拆分到每个季度、每个团队和每个人,系统在月底自动计算每个销售的已完成金额和目标完成率。这里就要用到订单表和商机表的关联统计。订单表记录的是实际签约金额,商机表记录的是预计签约金额。月底的完成率计算,只能算已经赢单并创建了订单的金额,不能把"预计本月能签约"的商机金额算进去。否则报表数字好看,实际回款对不上,财务那边第一个不答应。

还有一个客户分布报表,按行业、员工规模、来源渠道、负责人等多个维度对客户进行统计。这个报表用的是PG数据库的行转列语法,直接在一个查询里完成多维度聚合,代码简单,响应也快。报表页面的数据都不需要实时刷新,我们设置了一个五分钟的缓存,减少对主库的压力。

如果让我给报表模块一个建议,那就是不要一上来就做几十张报表。先跟销售管理层访谈,把最常用的三五张报表做扎实,图表颜色、字段命名、汇总口径都要跟业务人员反复确认。等他们习惯了看报表-做决策的模式,自然会反馈新的需求来。

4. 部署上线与落地经验

4.1 部署环境与CI/CD流程

DeskcommCRM的部署架构选择了两套环境:测试环境和生产环境。测试环境部署在同一台云主机上,用Docker Compose管理,方便开发自测和测试人员回归;生产环境单独一台云主机,配置更高一些,同时接了一个云数据库实例,避免数据库和应用抢资源。

Docker Compose文件中我们定义了五个服务:nginx、frontend、backend、redis、postgres。nginx负责静态资源托管和反向代理,把 /api 前缀的请求转发到后端容器,把 / 开头的静态资源请求直接映射到前端构建产物。每个服务都设置了资源限制,比如后端容器最大内存限制为2G,Redis为512M,避免某个容器内存泄漏拖垮整个主机。

CI/CD用的是GitLab CI,流程比较精简但够用:代码推到主分支,自动触发编译和单元测试;测试通过后,构建Docker镜像并推送到私有镜像仓库;之后通过SSH登录生产服务器,执行docker compose pull和docker compose up -d完成滚动更新。整个流程从提交代码到上线大概五分钟,比手工打包上传可靠得多。

上线之后最容易被忽视的是备份策略。我们吃了两次亏之后,专门写了一个每日凌晨的定时备份脚本:用 pg_dump 导出数据库全量备份,同时把服务器上用户上传的附件目录用 rsync 同步到另一台独立的备份机器,保留最近三十天的数据。这里说的附件不是指销售群里聊天图片,而是合同扫描件、报价单PDF、客户Logo这类业务文档。一旦服务器磁盘故障,没有备份就只能傻眼。云服务商自带快照功能,我们保留了三天的每日快照,但快照只能防误操作,真正要恢复带文件的数据还是得靠定期导出加异地拷贝。

4.2 账号体系与数据安全配置

账号体系这块,DeskcommCRM第一版是自己实现了一套简单的登录注册逻辑:用户名加密码Md5加盐,存到用户表里。上线后不到两周,就发现有同事密码设置得极其简单,比如"123456"、"deskcomm2024"这种,并且长期不换。我们意识到,如果密码策略不强,再完善的权限模型都是摆设。

后来我们做了一次加固,强制所有账号首次登录必须修改密码,密码最少8位,必须包含数字和字母,连续输错五次触发账号锁定半小时。另外增加了基于时间的一次性验证码,用TOTP算法,员工在自己的企微上绑定一个动态令牌,登录时需要输入账号密码加六位动态码。这一套配置下来,安全性提升了一个档次,而且对用户操作影响不大,因为动态码在企微里直接能看到。

数据安全方面,我们重点处理了两类内容:一类是客户联系方式的敏感字段,比如手机号、微信、邮箱;另一类是系统操作日志。敏感字段的存储我们用了AES加密,密钥放在环境变量中,和代码分离管理。查询时默认只显示脱敏信息,比如手机号只显示前三位和后四位,点击"查看"按钮时会先校验当前用户是否有对应客户的查看权限,然后再解密返回完整号码。操作日志记录的是每一次用户增加、修改、导出操作的操作人、操作时间、对象ID和变更摘要。这个日志对追溯数据泄露很有帮助,但前提是系统本身具备查询日志的界面,而不是只写进数据库不展示。

权限设计再好,如果开发人员可以绕过系统直连数据库查看明文数据,一切防护都形同虚设。所以我们内部也约束了只有数据库管理员拥有直连生产库的权限,而且所有SQL查询命令都会记录到审计日志里。这条规矩虽然给开发排障带来了一点不便,但客户数据的敏感性值得这种牺牲。

4.3 团队推广节奏:先试点再全量

系统做出来没人用,是内部工具最常见的失败原因。DeskcommCRM上线时我们吸取了这个教训,没有搞"一刀切"式的强制全量推广,而是先选了一个销售小组做试点,跑通之后再逐步扩大范围。

试点团队选的是华南区销售小组,八个人,特点是年轻、对新技术接受度高,更重要的是他们的组长愿意每天花十分钟收集问题反馈。试运行两周后,我们每天和组长对一次反馈,整理出高频问题和需求,按优先级快速迭代。印象最深的是第一周就有销售提出,录入客户时希望支持从手机通讯录导入联系人,这个功能我们花了一个周末就开发完成,因为客户列表页本来就有读取Excel和手动录入的入口,多加一个弹窗导入联系人复用现有解析逻辑就行。这种快速响应的正反馈,让试点团队感觉这个系统是他们自己参与设计出来的,用起来自然带劲。

试点跑了两周,第三周开始把华南、华东、华北三个区域全部开通,同时安排了两场培训。培训内容没有讲复杂功能,只讲四个高频操作:录入客户、写跟进记录、创建商机和看报表。每场培训控制在四十五分钟以内,留十五分钟现场答疑。之后还有一批"顽固用户",习惯了原来的Excel表格,不太愿意切换到系统。我们的对策不是强制,而是请销售主管把Excel模板升级成"从系统导出再填写"的方式,引导他们逐步过渡。先从每周一次导出Excel汇报,到后来发现系统报表更直观,Excel使用频率自然就降下来了。

推广过程中还有一点很关键:制定简单的使用规范,不要写一本厚的手册。比如,客户负责人变更时必须走"客户转移"功能;跟进记录里不允许粘贴大段邮件原文,只要概括要点;商机金额统一填写合同金额并注明币种。就这三条,大家容易记住也容易遵守,比起复杂的操作说明要有用得多。

5. 常见问题与排查技巧

5.1 数据导入乱码与丢失排查

客户导入功能测试和上线初期,出现最多的问题就是Excel解析乱码和部分数据静默丢失。乱码的原因绝大多数是文件编码不匹配:Windows办公软件导出的CSV默认是GBK编码,而Linux服务器上的Java默认读取UTF-8,直接按字符串读就会出现中文变成问号。解决方式是解析文件前先读取文件头几个字节,识别编码类型,再用对应的字符集做解码。Apache POI对.xlsx格式的兼容性更好,所以我们最终统一要求用户上传.xlsx文件,不做CSV兼容,减少编码判断的工作量。

数据静默丢失的情况比较隐蔽。有一次销售反馈导入后客户总数比Excel行数少了几十条,排查下来发现是Excel里有一些空行,空行的单元格也可能带着格式标记和不可见字符,被解析出的对象字段都是Null,插入数据库时因为非空约束直接抛出异常。我们的批量操作逻辑里捕获到异常后只跳过了这一条,没有把错误信息反馈给前端。后来在导入结果页面增加了"成功N条、失败M条、失败原因"的提示,并对失败数据提供下载,用户可以直接下载出错的Excel查看错误明细,问题就好排查了。

5.2 跟进提醒不触发的根源

提醒功能上线后,有用户反馈自己给客户设置了"明天上午十点跟进"的提醒,结果第二天完全没收到通知。第一反应是定时任务没有跑。服务器上查看日志发现任务确实执行了,但发送消息时提示目标用户不存在。

进一步排查后发现,提醒绑定的是用户在系统里的user_id,而企微通知需要的是企微用户ID。新入职员工的账号在DeskcommCRM里创建后,需要管理员在后台手动绑定企微账号,这个绑定关系漏了就会导致提醒发不出去。我们在用户管理页面加了一个"未绑定企微账号"的筛选列表,每周由HR检查一次,确保新人都完成绑定。

还有一类提醒不触发的原因是时区问题。服务器部署在云上,默认时区是UTC,而业务逻辑要求的是北京时间。我们刚开始没有在应用启动时设置默认时区,导致定时任务扫描"下次跟进时间"时,比较的时间基准比实际晚了八个小时,本来明天上午的提醒,系统以为还没到。解决方式是启动类里显式设置 Spring 的时区为 Asia/Shanghai,同时数据库连接串上加上 serverTimezone=Asia/Shanghai。这样从应用到数据库整个链路的时区就统一了。这个坑非常典型,只要你做过和定时任务、日期时间有关的系统,大概率都踩过。

5.3 客户详情页加载慢的排查记录

有一次销售集中反馈客户详情页打开要转圈好几秒,尤其是在浏览器开着F12调试工具访问时,接口返回时间波动很大。我们抓了后端接口的日志,发现大部分请求耗时都在八百毫秒到两秒之间,数据库的慢查询日志里能看到几条按客户ID查联系人和跟进记录的SQL扫描行数非常大。

分析执行计划后,原因很清晰:联系人表和跟进记录表的数据量已经增长到一定规模,而这两张表最初没有为 customer_id 单独建索引,导致每次查询都是全表扫描。为什么会漏建索引?因为之前表数据量很小,几百条记录全表扫描也就几十毫秒,开发时完全没意识到性能瓶颈,只在客户表上建了主键索引。补上 customer_id 和 create_time 的联合索引后,查询直接从全表扫描变成了索引范围扫描,接口耗时降到一百毫秒左右。

另外,详情页多Tab的数据是并行请求还是串行返回,对整体加载时间影响也很大。前端最初是等基本信息返回后,再去请求联系人和商机列表,相当于串行链路,叠加网络延迟后体感就慢。我们改成前端同时发出多个请求,后端分别处理,前端用Promise.all等待全部返回后统一渲染。这样即使某一个接口慢一点,也不会白白拖累整个页面的加载。

5.4 权限越权问题的排查方法

权限配置不当引起的越权是内部系统最严重的问题之一。有一次销售主管反馈,自己在客户列表里能看到其他团队负责的客户记录。当时排查的第一步是查看该主管的角色和用户表。系统里这条数据的数据范围是"本团队",理论上只应该看到自己团队成员的客户。

继续检查SQL拼接逻辑,发现数据权限过滤条件是通过一个工具类的方法拼接到查询语句中的,而这个方法里处理了一个空值场景:当用户ID为空时,默认不追加权限过滤条件。开发时这样做是为了方便定时任务和系统内置用户查询全量数据,但忽略了如果没有显式传入当前登录用户,接口就会默认放开权限。这个逻辑本身没有错,错在从外部请求进入时没有强制要求当前用户上下文初始化。

修复方式是在网关层设置了一个Servlet Filter,每个请求进入Controller之前先解析Token,把当前用户信息设置到ThreadLocal上下文对象中。如果解析不到用户信息,接口直接返回401,不允许进入业务代码。这样即使业务代码里忘记获取用户信息,也不会出现权限被绕过的情况。

这轮排查给我们的教训是:权限过滤条件不能依赖"防御式"的默认行为,必须在入口处做强制校验,越早挡住的攻击面越大。

6. 写在最后

DeskcommCRM上线到现在半年多,团队从最初的不习惯到逐渐依赖,销售录入客户成了日常工作的一部分。对我个人来说,最大的成就感不是写了多少行代码,而是看到一个一个原来散落在Excel里的客户记录,变成了系统里清晰可追踪的业务资产。销售能说出"这个客户半年前跟过什么方案、报过什么价格",新人能通过历史跟进记录快速了解客户情况,这种信息顺畅流动带来的价值,比任何复杂的功能都让我觉得当初的选择值得。

如果正在考虑自己搭一套类似的系统,我的建议是先承认一个现实:自研CRM不是做一次就完事,它需要持续迭代和长期维护。团队的精力投入到开发上,就要接受短期内其他项目可能会被挤压。但从另一个角度看,真正贴合业务、能随业务一起调整适应变化的系统,往往是那些内部自己动手写、亲手磨出来的工具。做之前想清楚边界,做的时候克制功能,做出来之后认真推广、认真听反馈,这套系统才能真正活起来。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:11:41

蓝牙耳机iOS音量不同步?AB5756C的AVRCP绝对音量修复实战

前阵子一直在折腾中科蓝讯AB5756C这颗芯片的SDK适配,客户那边反馈过来一个问题:用iPhone连接耳机,把音量调到60%,断开后重新连接,音量自己跑回默认值去了,而且手机端的音量条和耳机实际音量明显对不上。And…

作者头像 李华
网站建设 2026/9/16 19:11:14

免费PDF补丁丁:一键拿下书签、页面、限制三大难题

免费PDF补丁丁:一键拿下书签、页面、限制三大难题 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/9/16 19:10:39

文件夹图标变白故障排查与数据恢复指南

1. 文件夹图标变白故障现象解析最近遇到一个让人抓狂的问题——电脑里某些文件夹图标突然变成白色,双击后完全无法打开。这种故障看似简单,实则暗藏数据丢失风险。作为一名经历过多次数据灾难的IT从业者,我深知这种"白色图标"背后可…

作者头像 李华
网站建设 2026/9/16 19:09:15

Dify工作流模板库:5分钟导入你的第一个AI应用完整指南

Dify工作流模板库:5分钟导入你的第一个AI应用完整指南 【免费下载链接】Awesome-Dify-Workflow 分享一些好用的 Dify DSL 工作流程,自用、学习两相宜。 Sharing some Dify workflows. 项目地址: https://gitcode.com/GitHub_Trending/aw/Awesome-Dify-…

作者头像 李华