资讯动态

从零自研CRM系统:核心架构、数据模型与落地实践

发布时间:2026/10/19 9:50:04 来源:尧图企业网站定制
DeskcommCRM 这个名字拆开看很有意思Desk 代表桌面办公场景Comm 是 Communication沟通协同的缩写合起来就是一套围绕办公桌面场景、以沟通协同为主线来管理客户关系的系统。这基本就是我自己从零搭起来的那套内部客户管理系统的真实写照——不追求大而全但把客户跟进、团队协作、数据统计这三件事做扎实。我在接手这个项目前团队的客户资料散落在 Excel、个人微信和企业微信里跟进记录靠每周例会口头汇报。业务老大提的需求很朴素打开一个页面就能看到这个客户从哪来、谁在跟、聊到哪一步了、下一步该干嘛。听起来简单背后其实藏了三个核心诉求信息要集中沉淀不能因为人员离职带走客户过程要透明可视管理者能实时看到每个商机的阶段动作要能及时提醒该跟进不跟进、该回访不回访系统要能提示。DeskcommCRM 的整个设计和开发都围绕这三点展开。如果你也准备做类似的自研系统这篇文章可以给你一份完整的落地参考从表结构怎么设计、撞单怎么防、公海怎么回收到部署性能和安全怎么搞我会把实际踩过的坑和最终采用的方案都写出来。适合正在规划自研 CRM 的团队、做企业内部系统的开发同学以及想了解 CRM 背后业务逻辑的产品经理参考。1. 项目定位DeskcommCRM 要解决什么问题1.1 从命名看系统定位DeskcommCRM 这个项目名其实暴露了它的设计倾向。Desk 指的是桌面工作台——销售、客服、跟单人员每天打开电脑就要面对的主界面Comm 则是 communication 的缩写指沟通协同。换句话说这个系统不是传统意义上躺在数据库里的客户登记本而是一个把客户信息和日常沟通动作深度绑定在一起的作业平台。我当时最深的感受是很多团队上 CRM 失败不是软件不行而是软件和业务习惯不匹配。销售不想花时间去录一堆字段主管又希望所有过程都被记录。DeskcommCRM 的解法是把销售每天本来就要做的事——打电话、发微信、跟进、回访——变成系统里的标准动作降低录入成本系统在后台自动记录时间线。销售用得顺手数据才能沉淀下来。这个项目的定位也决定了它的边界不做大而全的 ERP不搞复杂的流程引擎专注解决客户从进公海到成交的全周期管理。我后面所有技术选型和表结构设计都以这个边界为前提。1.2 自研 CRM 的取舍为什么没买现成选型阶段肯定调研过市面上成熟的 CRM 产品我的结论是买现成的确实省事但在这个场景下有几个绕不过去的坎。第一个坎是数据主权的边界。我们涉及的客户数据除了基本联系方式还有大量跟单过程记录、报价单、合同附件。放进第三方 SaaS 平台且不说合规审核的流程问题单是每次要取数做分析、跟内部 ERP 和工单系统对接就会遇到 API 配额和字段限制。第二个坎是定制成本。销售流程不是一成不变的比如意向客户超过 7 天没有跟进就自动掉入公海这个规则在现成的 CRM 里往往要么是付费功能要么需要复杂的流程引擎配置要么干脆不支持。自研系统里改一个定时任务、加一个状态字段半小时就能上线业务部门上午提需求下午就能看到效果这种响应速度对内部系统来说太重要了。第三个坎是价格。按坐席收费的 SaaS CRM四十多个账号一年下来不是小数目而且人数还在涨。自研的成本主要是两三个人的开发时间算下来性价比高得多。当然自研也有代价功能完全自己造轮子、稳定性要自己扛。考虑到我们需要的不是大而全的通用工具而是贴合自己业务流程的够用且好用的系统这个代价是值得的。1.3 整体架构与功能地图DeskcommCRM 整体采用前后端分离架构。后端用 Java Spring Boot主数据库 MySQL 8.0缓存用 Redis定时任务用 xxl-job也可以直接用 Spring Scheduling部署用 Docker Compose。前端是 Vue 3 Element PlusWebSocket 做实时消息推送。这套组合在中小企业内部系统里非常成熟招聘容易、资料多、踩坑答案网上都能搜到。功能地图上核心模块我划分成五个客户管理客户信息的增删改查、公海池、回收规则、撞单检测跟进管理跟进记录、日程提醒、商机阶段流转团队协同任务指派、消息通知、客户转移数据报表销售漏斗、业绩统计、跟进率分析系统管理用户权限、操作日志、数据字典这五个模块的开发顺序也有讲究。我建议先把客户管理和跟进管理做扎实这两个是整个系统的地基协同和报表可以后面叠加但如果地基不牢后面的功能都会摇摇欲坠。下一节讲最核心的数据模型设计这是整个项目里最不能省工夫的部分。2. 数据模型设计CRM 的底盘工程2.1 核心表结构设计CRM 的表结构设计核心其实是围绕几个实体展开客户、联系人、商机、跟进记录、用户。这里给出我实际在用的客户表和跟进记录表的精简版本。客户表CREATE TABLE customer ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 客户ID, name VARCHAR(128) NOT NULL COMMENT 客户名称公司或个人, phone VARCHAR(32) DEFAULT NULL COMMENT 手机号归一化后, phone_raw VARCHAR(32) DEFAULT NULL COMMENT 手机号原始值, level TINYINT DEFAULT 0 COMMENT 客户级别 0未知 1低 2中 3高, source VARCHAR(32) DEFAULT NULL COMMENT 来源渠道, owner_id BIGINT DEFAULT NULL COMMENT 归属人IDNULL表示在公海, status TINYINT DEFAULT 0 COMMENT 0未跟进 1跟进中 2已成交 3已流失, last_follow_at DATETIME DEFAULT NULL COMMENT 最后跟进时间, next_follow_at DATETIME DEFAULT NULL COMMENT 下次跟进时间, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_owner_status (owner_id, status), KEY idx_phone (phone), KEY idx_next_follow (next_follow_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT客户表;这个表的设计有几个思路值得说明。手机号为什么不加唯一索引因为客户可能是企业客户一个公司多个联系人也可能个人客户没有手机号只有微信。所以我存了两列phone 是经过归一化的手机号phone_raw 保留原始输入方便追溯。在服务层做去重和撞单判断而不是硬加唯一索引这样灵活性和业务语义都能兼顾。owner_id 为 NULL 表示客户在公海池这个设计非常直观查询公海客户只需要一条WHERE owner_id IS NULL的 SQL不需要额外的状态字段来区分。last_follow_at 和 next_follow_at 这两个时间字段是后面自动回收规则和未跟进提醒的数据基础没有这两个字段业务方提出的7 天未跟进自动回收就无从实现。跟进记录表CREATE TABLE follow_up ( id BIGINT NOT NULL AUTO_INCREMENT, customer_id BIGINT NOT NULL COMMENT 客户ID, owner_id BIGINT NOT NULL COMMENT 跟进人ID, type TINYINT DEFAULT 1 COMMENT 1电话 2微信 3拜访 4邮件, content TEXT COMMENT 跟进内容, next_action VARCHAR(255) DEFAULT NULL COMMENT 下一步计划, next_follow_at DATETIME DEFAULT NULL COMMENT 下次跟进时间, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_customer_time (customer_id, created_at), KEY idx_owner_time (owner_id, created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT跟进记录表;跟进记录是一个高频写入的表每次打电话、发微信、拜访客户之后都要写一条。content 我特意用 TEXT 而不是 VARCHAR因为跟进内容的长度不可控别为了省那点空间做无谓的截断。查询场景基本都是某个客户的全部跟进记录或者某个销售的时间线所以索引要建联合索引单列索引在这种场景下效果很差。2.2 状态机与业务流转客户状态是整个 CRM 的核心状态机。我的定义是未跟进 → 跟进中 → 已成交 / 已流失。这个状态流的推进规则要跟业务方反复确认因为一旦定了后续所有统计报表都会依赖它。新建客户默认状态是未跟进归属人可以立即跟进。填了第一条跟进记录后状态自动变为跟进中。客户下单付款后手动或通过订单对接置为已成交。超过一定时间没转化、且业务上判断没有希望的置为已流失。这里有个细节特别值得注意状态的变更一定要写操作日志。我加了一张 operation_log 表记录谁在什么时间把哪个客户从什么状态改成了什么状态、附带了什么备注。刚开始业务方觉得多此一举后来出现了一次客户被误操作改成已流失的争议靠日志五分钟就定位到了责任人从那以后业务方再没嫌过日志烦。状态字段用 TINYINT 数字而不是直接存中文避免因为改名导致大面积修改代码。每次状态变更统一走一个状态机 Service把校验前置状态、更新状态、写日志、触发通知四个动作放在同一个事务里这样可以避免状态跳变和数据不一致的问题。这个模式看起来笨但特别稳。2.3 数据权限与撞单去重设计数据权限是 CRM 里最容易做崩的部分。业务上的要求是普通销售只能看到自己名下的客户销售主管能看到本部门所有客户的概要老板和运营看全量数据。我采用的是 RBAC基于角色的访问控制加数据范围的方式。用户表关联角色角色配置数据范围1-仅本人2-本部门3-全部。查询客户列表时在 Service 层统一拼接数据权限条件核心逻辑类似// 查询客户列表时的数据权限判断 if (dataScope 1) { sql AND c.owner_id currentUserId; } else if (dataScope 2) { sql AND c.owner_id IN (SELECT id FROM sys_user WHERE dept_id currentDeptId ); } else { // 全部数据不加条件 }这个方案的好处是实现简单、理解容易坏处是数据量大时 in 子查询可能有性能损耗但几十万级别的客户数据量完全扛得住。如果你要处理千万级数据再考虑部门路径记录等更复杂的方案。撞单去重是业务方最看重的功能之一。两个销售同时录入同一个客户的手机号系统必须能判断出这个客户已经存在。我的方案分两步第一步是录入时实时校验前端在客户名称或手机号输入框失焦时调一个接口查询 phone 字段归一化后是否有归属人存在就弹窗提示该手机号已存在于系统、归属人是哪位让销售决定取消还是申请归属。第二步是硬兜底后端在保存客户时再查一次并且用 Redis 分布式锁防止两个并发请求同时通过校验又同时插入。这个问题后面在常见问题里展开先记住先查再插不是原子操作这个教训。3. 关键功能落地与实操细节3.1 客户公海与自动回收机制客户公海是销售型 CRM 的标配功能。逻辑很简单客户从公海里被销售领取后如果在规定时间内没有有效跟进或者跟进后长时间没有推进客户就会被系统自动退回公海让其他销售有机会跟进。公海规则我设计成两个维度。维度一新客户领取后 48 小时内没有填写第一条跟进记录自动回收。这是为了防止销售把客户圈在名下不动。实际运营中发现有些销售习惯一次领几十个客户然后慢慢消化这其实是在占用资源。48 小时没跟进的客户大概率近期也没有意向回收是合理的。维度二已进入跟进中的客户超过 7 天没有新的跟进记录且状态不是已成交自动回收。这个时间阈值是跟业务方反复讨论后定的——行业不同差异很大比如教育行业的客户咨询周期短3 天没跟进基本就凉了企业服务类的客户决策周期长可能两周才跟进一次。所以阈值不能写死要放在系统的数据字典里做成可配置项。实现上我用定时任务每小时跑一次-- 回收超过 N 天未跟进的已分配客户置回公海 UPDATE customer SET owner_id NULL, status 0 WHERE owner_id IS NOT NULL AND status ! 2 AND next_follow_at DATE_SUB(NOW(), INTERVAL 7 DAY);回收后要记录一条系统日志告知原归属人该客户因 7 天未跟进已被回收至公海方便销售知道自己的客户池发生了什么变化。这个功能上线后要特别注意的是回收前一定要有预警通知。我们吃过一次亏上线第一天因为测试数据没清理干净一批真实客户被误判回收销售意见很大。后来加了回收前 24 小时发送预警通知提醒销售客户即将被回收的逻辑才算平稳落地。3.2 跟进记录与销售漏斗统计跟进记录是销售动作的流水账但从这些流水账里能挖出很多管理信息。统计上我做了两个核心报表销售漏斗和跟进率分析。销售漏斗按商机阶段统计数量和金额。阶段我简化为五个初步沟通、需求确认、方案报价、商务谈判、成交。每个商机存一个 stage 字段漏斗报表就是按 stage 分组统计SELECT stage, COUNT(*) AS opportunity_count, SUM(amount) AS total_amount FROM opportunity WHERE deleted 0 GROUP BY stage ORDER BY stage;这个报表的意义在于运营一看就知道哪个阶段流失最严重。如果发现方案报价到商务谈判的转化率只有 20%那问题可能出在报价方案本身需要业务侧做针对性优化。我还做了一个时间维度对比按月查看各个阶段的日均数量方便发现趋势变化。跟进率分析稍微复杂一点。核心指标是平均跟进间隔和跟进覆盖率。平均跟进间隔就是两次跟进记录之间的平均天数太长说明销售对该客户的重视度不够跟进覆盖率是统计有多少比例的客户在最近 7 天有跟进记录低于某个阈值比如 60%就需要管理者关注。这段统计逻辑我不想在 SQL 里写得太复杂因为要关联多张表性能也不理想。实际实现是每天凌晨跑一个定时任务把前一天的跟进数据进行预聚合存到 report_daily 表报表页面只需查这一张表。预聚合的思路在 CRM 这种偏报表的场景里非常实用能极大降低实时查询的压力。3.3 批量导入导出与数据清洗上线初期最大的痛点不是功能不够而是历史数据怎么从 Excel 里迁进来。两百多个 Excel 文件、上万条客户记录手工录入不现实必须做批量导入。我用的方案是 Apache POI 解析 Excel然后分批写入数据库。几万条数据一次事务肯定不行事务太大会锁表而且如果有几条脏数据整个批次回滚排查起来非常痛苦。我的做法是每 500 条一个批次每条数据先做校验手机号格式是否正确、必填字段是否为空、公司名是否重复。校验通过的进正式表校验失败的记录到一个导入错误明细表导入完成后前端展示成功 9800 条失败 200 条点击查看错误明细用户可以下载错误原因逐条修改。这里有个小坑导入时手机号的归一化处理一定要和录入时保持一致否则会导致同一客户被重复导入。我专门抽了一个 normalizePhone 工具方法录入、导入、查重三处共用这也是我前面强调 phone 和 phone_raw 分两列存的原因之一。导出方面我遇到的最大问题是内存溢出。几万条数据一次性查出来塞进 ExcelJVM 直接 OOM。后来用 EasyExcel 的流式导出解决本质上是不把所有数据加载到内存而是分批从数据库读取并逐行写入文件流。代码大概是这样// EasyExcel 流式导出示例 EasyExcel.write(outputStream, CustomerExcelVO.class) .sheet(客户数据) .doWrite(() - customerMapper.scanForExport(batchSize));如果你自己写导出一定要搞清楚全量装载和流式写盘的区别这是数据量大时的生死线。3.4 消息通知与团队协同DeskcommCRM 里的 Comm重点体现在这里。系统里所有需要人知道的事件都要触发通知我梳理了几个核心场景客户被回收通知原归属人、客户被认领通知现归属人、分配了新任务通知执行人、今日待跟进客户每天早上 9 点推送、公海来了高意向客户通知有权限的销售。技术选型上站内消息用 WebSocket 推送外部通知用企业微信机器人 Webhook。站内消息表很简单就是 message 表字段包括接收人、消息类型、标题、内容、是否已读、跳转链接。WebSocket 服务端在客户数据变更时主动推送消息前端右上角弹出红点提醒。企业微信机器人 Webhook 的作用是兜底——用户没打开系统的时候通过企业微信收到通知。这里有个经验教训Webhook 推送不是百分百可靠网络波动、接口限流都可能导致消息丢失。我后来加了补偿机制发出去的每条外部通知都记录 delivery_log定时任务每 5 分钟扫一次发现发送失败或者未确认的消息自动重试最多重试 3 次。任务指派功能我做了简化版销售主管可以把某个客户临时指派给另一个销售指派后原归属人自动变为被指派人并生成一条跟进任务。这种简单的审批加自动变更比复杂的流程引擎实用得多内部系统不需要太重的流程设计。4. 部署、性能与安全加固4.1 Docker Compose 一键部署整个系统我尽量做成开箱即用部署用 Docker Compose 编排包含四个容器nacos配置中心、mysql、redis、应用服务。实际线上我甚至把 mysql 和 redis 单独部署到云数据库Docker Compose 主要用于本地开发和测试环境的一致性。docker-compose.yml 的核心部分version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} MYSQL_DATABASE: deskcomm_crm ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql redis: image: redis:7 ports: - 6379:6379 app: build: . ports: - 8080:8080 depends_on: - mysql - redis environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/deskcomm_crm SPRING_DATA_REDIS_HOST: redis如果团队规模不大这套方案足够稳定。容器化的最大好处是换服务器、加测试环境、新同事本地起项目都是一条docker compose up -d搞定彻底告别在我电脑上是好的这种低级问题。4.2 缓存、索引与慢 SQL 优化CRM 系统的查询特点是写多读多而且查询条件多样化。客户列表页可能是整个系统并发压力最大的接口之一销售每人每天要打开几十次。我的优化分三层。第一层是 Redis 缓存热点数据。客户详情页的数据组装涉及客户表、联系人表、最近跟进记录表每次请求都实时查数据库太浪费。我的做法是客户详情在第一次查询后缓存到 Rediskey 设计为customer:detail:{customerId}业务数据变更时主动删除缓存。字典数据客户来源、跟进方式、商机阶段几乎不变启动时一次性加载到本地缓存连 Redis 都不用查。第二层是 MySQL 索引优化。上线两周后我开始关注慢查询日志发现最慢的 SQL 集中在客户列表的筛选条件比如按级别加状态加归属人组合查询。我建了几个联合索引覆盖用户最常用的查询路径ALTER TABLE customer ADD INDEX idx_owner_level_status (owner_id, level, status);建索引不是越多越好每次插入更新都要维护索引所以必须根据实际查询路径来。我的方法是打开 MySQL 慢查询日志收集一两天内的慢 SQL逐个优化而不是凭感觉建一堆索引。第三层是分页优化。客户列表用传统的 LIMIT/OFFSET 分页数据量到几十万之后页数越深查询越慢。我的方案是把普通分页改成游标分页客户端传上一页最后一条记录的 ID服务端用WHERE id #{lastId} ORDER BY id LIMIT 20查询。这种方案对用户体验几乎没有影响但对数据库的压力小了一个数量级。4.3 安全加固与备份策略内部系统也绝对不能裸奔。我在 DeskcommCRM 里做了下面几件事。首先是登录安全。密码不能明文存用 BCrypt 加密。登录接口加验证码防止暴力破解。增加登录失败次数限制连续错误 5 次锁定账号 15 分钟。会话用 JWT设置合理的过期时间前端定时刷新 Token。其次是接口权限。所有接口必须在拦截器里做两件事校验用户是否登录、校验用户对该数据是否有权限。这个在权限模型那节强调过后端校验绝对不能依赖前端的隐藏按钮来实现否则懂技术的人用 Postman 就能绕过界面直接调接口。备份是很多人容易忽略的。我经历过 MySQL 数据文件损坏、误删表数据的事故从那以后备份策略就成了硬性要求每天凌晨全量备份binlog 实时增量备份备份文件同步到对象存储。恢复演练每季度至少做一次别等到出事故了才临时抱佛脚。备份这种事平时看起来是浪费资源关键时刻能救命。5. 常见问题与排坑实录5.1 并发抢单导致的丢失更新这个问题我在跑并发测试的时候踩过一个大坑。场景两个销售同时看到一个公海客户同时点击领取理论上只能有一个人成功。我最初的代码是这样的// 错误示例查出来再更新非原子操作 Customer customer customerMapper.selectById(customerId); if (customer.getOwnerId() null) { customer.setOwnerId(currentUserId); customerMapper.updateById(customer); return 领取成功; } return 已被领取;这段代码在单线程下没问题但并发请求一多两个请求可能都查到了 ownerId 为 null然后都执行更新最后一个覆盖前一个两个人都以为自己领取成功了这就是典型的多线程竞争条件。正确的做法有两种。第一种最简单粗暴用一条 SQL 完成判断和更新UPDATE customer SET owner_id #{userId}, status 1 WHERE id #{customerId} AND owner_id IS NULL;返回受影响行数如果是 1 表示领取成功0 表示被别人抢了。这是原子操作InnoDB 的行锁保证了同一时刻只有一个事务能更新成功。第二种是加 Redis 分布式锁适合更复杂的场景。比如领取客户后还要同时做很多后续动作像生成任务、发通知、修改统计需要保证整个流程串行执行。但如果是简单的状态更新直接 UPDATE 条件更新就够了没必要引入锁带来额外的复杂性和维护成本。5.2 大批量导出导致 OOM另一个高频问题是大批量数据导出时内存溢出。第一次上线导出功能时我写的是全量加载到 List再用 POI 一行行写入 Excel。数据量到三万条左右JVM 就报 OutOfMemoryError 了。后来换成 EasyExcel 流式写入问题解决。但这里还有个隐藏的坑MySQL 查询默认会把结果一次性加载到内存useCursorFetchfalse。即使 Excel 是流式写入如果你查询返回了十万条记录内存还是会爆。需要给 JDBC URL 加上参数jdbc:mysql://localhost:3306/deskcomm_crm?useCursorFetchtruedefaultFetchSize500这样驱动才会分批从数据库取数据。不然你只改了导出层数据库层的大查询一样压垮 JVM。同样是流式处理必须上下游配合单靠一头不行。5.3 消息通知丢失与重复推送WebSocket 推送的不稳定是意料之中的。用户断网、浏览器休眠、服务端重启都会让消息发送失败。我最初的做法是简单地发送后不管后来发现用户经常抱怨系统里明明有新任务但我不知道。现在的方案是消息写入数据库是绝对的推送只是加分项。用户登录后前端主动拉取未读消息列表WebSocket 只负责在线时的实时提醒Webhook 负责离线时的兜底。这样即使推送全挂用户刷新页面也能看到未读消息不会丢信息。重复推送的问题也出现过。原因是网络重试机制和消息发送逻辑耦合在一起一条消息超时后重试但实际对方已经收到了用户就会看到两条相同消息。解决方法是给每条消息生成唯一消息 ID接收端做幂等去重重复 ID 的消息直接丢弃。消息这块的教训就是发送要尽力而为消费要幂等去重两边一起做才稳。5.4 常见问题速查表现象排查方向最终方案客户列表越翻越慢分页方式改为游标分页并发领取同一客户都成功更新逻辑非原子UPDATE 条件更新大批量导出 OOM数据全量加载EasyExcel useCursorFetch消息重复推送缺少幂等机制唯一消息 ID 去重定时任务重复执行多实例部署分布式锁或 xxl-job 分片回收客户误伤真实用户无预警机制回收前 24 小时预警通知6. 后续扩展与个人体会6.1 下一步可以怎么扩展DeskcommCRM 当前已经覆盖了核心的客户管理和协同场景但往长期看有几个方向值得做也给后来者一个参考。第一个是商业智能。销售漏斗、跟进率这些报表可以做成交付式的数据看板甚至引入客户意向评分模型根据客户的活跃度、来源渠道、跟进频率等维度自动算出一个高意向客户名单让销售优先跟进。这个不需要多复杂的机器学习简单加权打分就很有效。第二个是移动端。销售在外拜访客户时需要掏出手机查看客户资料、记录跟进、接收入公海通知。技术栈可以用现有后端接口直接套一个 H5 移动端成本不高体验也够用。第三个是接口开放。公司如果有 ERP、财务系统可以把成交客户、订单金额通过标准化接口推送过去减少重复录入。这其实是我最初就提到的集成场景也一直想完善。第四个是数据安全。如果客户数据量增长到一定规模字段级加密、操作审计、敏感数据脱敏都是必须提前规划的。合规不是小事情等出问题了再补成本会很高。6.2 个人实操体会做这个项目最大的体会是内部系统的成败技术只占一半另一半是跟业务方的沟通和对业务逻辑的理解。很多技术方案不是越难越好而是越贴合实际场景越好。比如公海回收规则业务方一开始提的是 7 天不跟进就回收但实际落地过程中发现有些客户周期长7 天太激进后来才调整成可配置的。另一个体会是凡是涉及多人协作、并发操作的场景一定要在设计阶段就考虑并发控制。CRM 里最典型的就是撞单和抢单SQL 条件更新一行代码就能解决但如果一开始没意识到这个问题后面要改起来就得动核心逻辑。最后定时任务一定要做好监控。公海回收、消息补偿、报表预聚合全靠定时任务在背后悄悄跑如果哪天任务挂了你不知道数据就会出错而且错误会累积越来越难修。我后来给每个定时任务都加了执行日志和失败告警跑完自动汇报今日回收客户 128 个有问题第一时间知道。如果你也准备做一套类似的系统我的建议是先理清业务规则再设计数据模型先做核心功能再搞锦上添花。把客户管理、跟进机制、公海回收这三件事做扎实系统就已经有价值了后面的报表、通知、协同都是加分项。DeskcommCRM 这个项目做到现在至少解决了团队客户信息散落、跟进无法追溯、撞单扯皮这三个最痛的痛点作为一个内部系统这就算成功了一大半。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑