简介:本资源是一份完整的CRM客户管理模块软件需求说明书(PRD),面向企业数字化转型中的产品经理、系统分析师及CRM开发实施人员,聚焦客户档案管理、交互历史追踪、销售漏斗分析等核心业务场景,助力团队精准定义需求、对齐业务目标与技术实现。文档由蜂网供应链管理(上海)有限公司于2017年8月正式发布,编写人为周旭丽,全文共54页PDF,结构严谨,覆盖项目背景、目标顾客、平台政策、竞品分析、名词解释、业务用例、功能列表及基本流程等关键章节,附有详细变更记录与版本控制说明。资源为单文件PDF格式,大小3.11MB,轻量易读,适合作为CRM需求分析范本、行业方案参考或高校信管/电商专业教学案例。目前已有310人学习下载,内容机密性标注清晰,具备真实企业级交付文档的规范性与实操参考价值。
1. 这份2017年的CRM PRD不是过时文档,而是可复用的客户建模骨架
很多人看到“V1.0-20170825”就直接划走,觉得这是历史遗迹。但实际拆过蜂网这份《客户关系管理CRM_客户管理_软件需求说明书_V1.0》后会发现:它没写一行代码,却精准框定了CRM系统最硬核的客户数据结构、状态流转逻辑和权限边界——这些恰恰是今天搭建SaaS型CRM或私有化部署客户中台时,最容易被跳过的底层基建。它不讲微服务怎么拆,也不提React还是Vue,而是用54页纸把“客户是谁、谁管客户、客户怎么变、数据怎么活”这四件事钉死在字段级、流程级和规则级。比如“上级客户”字段明确要求“不允许互为上下级”,“工商注册”字段标注“=系统判定”且“非空不可编辑”,这种设计不是拍脑袋,而是来自供应链场景下对集团客户、子公司、经销商三级架构的真实约束。适合正在从Excel手工管理转向系统化客户运营的中小团队,也适合需要快速验证客户模型是否完备的ToB产品负责人——你不需要照搬它的UI截图,但必须吃透它定义的37个客户关键数据项、5类预置查询方案、4种负责人变更路径,以及那个被标注为“二期实现”却至今仍被多数CRM忽略的线索自动归集逻辑。
2. 客户实体建模:从37个字段到状态机驱动的生命周期管理
2.1 字段设计背后的业务刚性约束
蜂网PRD在3.1.3.1.2.3节列出了客户列表默认关键数据项,共37个字段,但真正决定系统健壮性的不是数量,而是每个字段携带的业务契约。以“上级客户”为例,它不仅是普通参照字段,PRD明确要求:“参照CRM客户,上下级关系不允许互为上下级”。这意味着数据库设计时必须引入循环检测机制,而非简单外键关联。再看“工商注册”字段,类型为布尔值,但取值来源标注为“=系统判定”,合法性说明为“非空,不可编辑”。这暗示系统需集成国家企业信用信息公示系统API,在客户录入公司名称和统一社会信用代码后,自动调用接口校验并回填结果——这个字段本质是风控开关,而非展示属性。
提示:很多团队在开发初期把“工商注册”设为手动勾选,导致销售随意标记“已认证”,后续审计时无法追溯真实性。蜂网的设计强制将校验环节前置,把合规责任交给系统而非人。
2.1.1 关键字段的可编辑性与来源映射表
| 字段名 | 类型 | 可编辑性 | 来源 | 业务含义 | 开发注意事项 |
|---|---|---|---|---|---|
| 客户名称 | 字符 | 可编辑 | 手工输入 | 非空,≤200字符 | 前端需实时校验长度,后端SQL加CHECK(LENGTH(name) <= 200) |
| 上级客户 | 参照 | 可编辑 | 手工输入 | 禁止A→B→A循环 | 插入/更新时执行递归查询检测环路,建议用闭包表(Closure Table)优化性能 |
| 行业 | 枚举 | 可编辑 | 数据字典 | 从启用行业列表选择 | 字典表需带is_enabled字段,前端请求时过滤WHERE is_enabled = 1 |
| 成交状态 | 枚举 | 可编辑 | 手工输入 | 默认“未成交”,选项含“已成交” | 状态变更需触发事件,如发送通知、更新统计报表缓存 |
| 最新活动记录时间 | 日期时间 | 不可编辑 | 系统自动 | 最后活动保存时的时间戳 | 每次创建/更新联系人、商机、动态记录时,同步更新客户主表该字段 |
2.2 客户状态转换:从静态枚举到动态审批流
PRD在3.1.3.3.1节附有状态转换图,虽未提供图形,但文字描述清晰界定出客户生命周期的四个核心状态:新建 → 待审核 → 已启用 → 已停用。更关键的是3.1.3.3.3节“审批流”要求:“客户创建需经部门负责人审批,超500万年采购额客户需升级至区域总监审批”。这揭示出状态机不是孤立存在,而是与组织架构深度耦合。
2.2.1 审批规则落地的两种实现方式
方式一:硬编码角色判断(适用于组织稳定场景)
# Python伪代码:根据客户预估年采购额动态路由审批人 def get_approver(customer): if customer.estimated_annual_purchase < 5000000: return DepartmentManager.objects.get(dept_id=customer.department_id) else: return RegionalDirector.objects.get(region_id=customer.region_id) # 调用示例 approver = get_approver(new_customer) ApprovalTask.objects.create( customer_id=new_customer.id, approver_id=approver.id, level="department" if new_customer.estimated_annual_purchase < 5000000 else "regional" )参数说明:estimated_annual_purchase字段在PRD中虽未列于关键数据项,但在“业务规则”章节隐含要求——需在客户新增/编辑界面提供输入入口,并作为审批路由依据。代码中level字段用于后续统计不同层级审批通过率。
方式二:配置化审批引擎(推荐用于多变组织)
建立approval_rule表:
| rule_id | condition_field | condition_value | operator | target_role | priority |
|---|---|---|---|---|---|
| 1 | estimated_annual_purchase | 5000000 | GT | regional_director | 10 |
| 2 | department_id | 101 | EQ | department_manager | 5 |
运行时解析规则:
-- PostgreSQL示例:动态获取审批人 SELECT u.user_id, u.name FROM users u JOIN approval_rule ar ON u.role = ar.target_role WHERE ar.condition_field = 'estimated_annual_purchase' AND new_customer.estimated_annual_purchase > ar.condition_value ORDER BY ar.priority DESC LIMIT 1;逻辑说明:当客户预估采购额超过500万,优先匹配regional_director角色;若未命中,则降级匹配部门经理。这种方式避免修改代码即可调整审批策略,符合PRD中“审批流需支持灵活配置”的隐含需求。
2.3 权限控制:基于“负责人+部门+组织”的三维校验
PRD在3.1.3.3.4节强调:“客户数据可见性遵循‘我负责的客户’、‘我参与的客户’、‘我关注的客户’三重维度”。这不是简单的RBAC(基于角色的访问控制),而是ABAC(基于属性的访问控制)雏形。具体到操作层面,“更改负责人”按钮支持批量操作,但删除操作必须弹出确认界面——这种差异源于权限粒度设计:负责人变更属协作行为,而删除是数据主权动作。
2.3.1 查询方案背后的SQL生成逻辑
PRD预置5类查询方案,其SQL生成需动态拼接WHERE条件:
-- “七日未跟进客户”查询方案(对应PRD 3.1.3.1.2.4节第5条) SELECT * FROM crm_customer c WHERE c.org_id = :current_org_id -- 所属组织=我所在组织(基础过滤) AND c.last_activity_time < NOW() - INTERVAL '7 days' AND NOT EXISTS ( SELECT 1 FROM crm_activity a WHERE a.customer_id = c.id AND a.created_at > NOW() - INTERVAL '7 days' );参数说明::current_org_id由当前登录用户会话注入,确保租户隔离;last_activity_time字段在PRD中定义为系统自动维护,此处利用其减少子查询开销;NOT EXISTS替代LEFT JOIN避免因活动记录为空导致客户重复。
注意:PRD要求“列表默认按创建时间倒序”,但“七日未跟进”方案需按
last_activity_time升序排列以便优先处理陈旧客户。前端应允许用户切换排序字段,后端SQL需支持ORDER BY动态参数化。
3. 客户操作闭环:从新增、合并到导入导出的工程化实现
3.1 新增客户的关键路径与防错设计
PRD 3.1.3.1.2.8节的“客户-新增界面示意图”虽无图形,但3.1.3.1.2.9按钮清单明确要求:“新增界面需包含‘保存’、‘保存并新建’、‘取消’三按钮”。这暗示着用户高频操作路径——销售每天录入数十客户,必须降低单次操作成本。“保存并新建”意味着表单提交后清空字段并保持焦点在客户名称输入框,而非跳转回列表页。
3.1.1 查重机制的双重校验实现
PRD 3.1.4.2节“查重设置”仅一句话:“支持按客户名称、电话、邮箱组合查重”。但实际落地需分层防御:
- 前端实时校验:用户输入客户名称时,AJAX请求
/api/customers/check-duplicate?name=xxx&phone=yyy,返回{exists: true, ids: [101,102]},提示“已存在同名客户,ID:101,102”; - 后端最终校验:
INSERT前执行原子性检查:
INSERT INTO crm_customer (name, phone, email, created_by, org_id) SELECT 'ABC公司', '13800138000', 'contact@abc.com', 123, 456 WHERE NOT EXISTS ( SELECT 1 FROM crm_customer WHERE (name = 'ABC公司' OR phone = '13800138000' OR email = 'contact@abc.com') AND org_id = 456 );逻辑说明:WHERE NOT EXISTS确保插入前无冲突,避免应用层与数据库层时间差导致的脏数据。org_id = 456限定查重范围在本租户内,符合PRD“客户所属组织=我所在组织”的默认规则。
3.2 客户合并的技术难点与数据血缘保留
PRD 3.1.3.1.2.17节“客户-合并”功能看似简单,实则涉及复杂的数据迁移。当合并客户A(主)与客户B(从)时,需处理:
- 联系人、商机、合同等关联记录的
customer_id外键更新; - 动态记录(如邮件、通话)的归属转移;
- 合并操作本身需生成审计日志,记录“客户B已合并至客户A”。
3.2.1 合并操作的事务安全实现
BEGIN TRANSACTION; -- 步骤1:锁定被合并客户,防止并发修改 SELECT * FROM crm_customer WHERE id = 201 FOR UPDATE; -- 步骤2:更新所有关联表外键 UPDATE crm_contact SET customer_id = 101 WHERE customer_id = 201; UPDATE crm_opportunity SET customer_id = 101 WHERE customer_id = 201; UPDATE crm_contract SET customer_id = 101 WHERE customer_id = 201; -- 步骤3:标记原客户为已合并(非物理删除) UPDATE crm_customer SET status = 'merged', merged_to_id = 101, updated_at = NOW() WHERE id = 201; -- 步骤4:记录合并日志 INSERT INTO crm_merge_log (main_customer_id, merged_customer_id, operator_id, created_at) VALUES (101, 201, 123, NOW()); COMMIT;参数说明:FOR UPDATE确保行锁,避免其他事务同时合并同一客户;merged_to_id字段在PRD中未明确定义,但为满足“客户层级关系”(3.1.3.1.2.25节)和审计要求,必须新增;日志表crm_merge_log需单独建表,便于后续分析合并频次与客户质量。
3.3 导入导出:模板驱动与增量同步的平衡
PRD 3.1.3.1.2.27/28节要求“客户-导入/导出”,但未指定格式。结合2017年企业实践,Excel是事实标准。然而直接读取Excel易引发内存溢出(单文件超10万行),且字段映射错误难定位。
3.3.1 分片导入与错误反馈机制
# 后端接收导入请求(curl示例) curl -X POST http://crm-api/v1/customers/import \ -H "Authorization: Bearer token" \ -F "file=@customers_template.xlsx" \ -F "org_id=456" \ -F "batch_size=500"Python处理逻辑:
def import_customers(file_path, org_id, batch_size=500): # 1. 读取Excel首行获取字段映射 headers = pd.read_excel(file_path, nrows=0).columns.tolist() # 2. 分块读取,每500行一个批次 for chunk in pd.read_excel(file_path, chunksize=batch_size): # 3. 字段校验:检查必填字段(客户名称、负责人)是否存在空值 errors = [] for idx, row in chunk.iterrows(): if pd.isna(row['客户名称']): errors.append(f"第{idx+2}行:客户名称不能为空") if pd.isna(row['负责人']): errors.append(f"第{idx+2}行:负责人不能为空") if errors: raise ImportValidationError(errors) # 返回给前端具体行号错误 # 4. 批量插入(使用ON CONFLICT DO NOTHING避免重复) insert_sql = """ INSERT INTO crm_customer (name, phone, email, owner_id, org_id, created_by) VALUES %s ON CONFLICT (name, org_id) DO NOTHING; """ execute_values(cursor, insert_sql, chunk.values.tolist())逻辑说明:execute_values是psycopg2的高效批量插入方法;ON CONFLICT利用唯一索引(UNIQUE INDEX ON crm_customer(name, org_id))自动去重,避免先查后插的竞态;错误反馈精确到Excel行号(idx+2因首行为标题),符合PRD“导入失败需明确提示错误位置”的隐含要求。
4. 查询方案实战:用5个预置条件构建销售作战地图
4.1 “我负责的客户”方案的权限穿透实现
PRD 3.1.3.1.2.4节第2条“我负责的客户:客户负责人=我本人”,表面是简单WHERE条件,但需穿透组织架构。例如销售张三属于北京销售部,但其负责客户可能跨华北区多个子公司——此时customer.owner_id = current_user.id只是起点,还需关联user_department和department_hierarchy表获取其管理范围。
4.1.1 多级部门管辖的SQL展开
-- 获取张三及其下属部门所有客户 WITH RECURSIVE dept_tree AS ( -- 锚点:张三所在部门 SELECT id, parent_id, name FROM departments WHERE id = ( SELECT dept_id FROM users WHERE id = 123 ) UNION ALL -- 递归:子部门 SELECT d.id, d.parent_id, d.name FROM departments d INNER JOIN dept_tree dt ON d.parent_id = dt.id ) SELECT c.* FROM crm_customer c WHERE c.owner_id = 123 -- 直接负责 OR c.department_id IN (SELECT id FROM dept_tree); -- 部门管辖参数说明:current_user.id=123为张三用户ID;dept_tree递归CTE获取其部门及所有下级部门ID;OR条件确保既包含张三直管客户,也包含其团队管理的客户,符合PRD“我负责的客户”业务语义。
4.2 “七日未跟进客户”的预警自动化改造
PRD将此列为预置查询方案,但未提自动化。实际落地时,可将其转化为定时任务,每日凌晨扫描并推送预警:
# Celery定时任务(每日0点执行) @app.task def alert_inactive_customers(): # 查询七日未跟进客户(复用PRD逻辑) customers = Customer.objects.filter( org_id=settings.CURRENT_ORG_ID, last_activity_time__lt=timezone.now() - timedelta(days=7) ).select_related('owner').values( 'id', 'name', 'owner__name', 'owner__email' ) # 按负责人分组发送邮件 from collections import defaultdict alerts = defaultdict(list) for c in customers: alerts[c['owner__email']].append(c) for email, cust_list in alerts.items(): send_mail( subject=f"【CRM预警】您有{len(cust_list)}个客户超7天未跟进", message=render_to_string('inactive_alert.txt', {'customers': cust_list}), from_email="crm@fengwang.com", recipient_list=[email] )逻辑说明:select_related('owner')减少N+1查询;render_to_string渲染模板,邮件内容包含客户名称、最后活动时间、一键跟进链接;settings.CURRENT_ORG_ID确保多租户隔离。此改造将PRD的静态查询方案升级为动态预警机制,直击销售管理痛点。
4.3 高级查询的字段扩展与性能保障
PRD 3.1.3.1.2.5节要求“高级查询条件:客户表字段(包含自定义字段)”。自定义字段通常以EAV(Entity-Attribute-Value)模式存储,易引发性能问题。推荐采用JSONB字段(PostgreSQL)或动态列(MySQL 5.7+):
-- PostgreSQL JSONB方案 ALTER TABLE crm_customer ADD COLUMN custom_fields JSONB DEFAULT '{}'; -- 查询含自定义字段的客户(如:行业偏好=IT) SELECT * FROM crm_customer WHERE custom_fields @> '{"industry_preference": "IT"}';参数说明:@>为JSONB包含操作符,支持Gin索引加速;custom_fields字段存储销售自定义的标签、评分、来源渠道等,避免EAV表JOIN导致的慢查询。此设计在PRD框架下平滑扩展,无需修改主表结构。
提示:PRD未定义自定义字段,但“业务类型”、“客户级别”等枚举字段已预留扩展空间。JSONB方案让销售团队可自主添加字段,而IT团队只需维护索引,符合PRD“支持灵活配置”的底层精神。
本文还有配套的精品资源,点击获取