4、客户管理模块表结构设计:客户表(Customer)字段设计、客户分类与标签设计、客户地址表设计
客户管理模块,说白了就是CRM系统的地基。你想想看,连客户是谁、在哪儿、怎么分类都没搞清楚,后面做销售漏斗、做客户画像,那都是空中楼阁。
我这些年接手过的CRM项目,少说也有十几个。每次做客户表设计,我都会先问自己一个问题:这张表要撑住未来三年的业务量吗? 嗯,这个问题很关键。因为客户表一旦上线,后面改字段的成本高得吓人。
4.1 客户表(Customer)字段设计
先说说客户表的核心字段。我个人的习惯是,把字段分成三组:基础信息、业务属性、系统控制。
| 字段分组 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 基础信息 | customer_id | BIGINT | 主键,自增或雪花ID |
| 基础信息 | customer_name | VARCHAR(100) | 客户名称(个人/企业) |
| 基础信息 | phone | VARCHAR(20) | 手机号,建议加唯一索引 |
| 基础信息 | VARCHAR(100) | 邮箱,可选 | |
| 业务属性 | customer_type | TINYINT | 客户类型:1个人 2企业 |
| 业务属性 | source | VARCHAR(30) | 来源渠道:官网、转介绍、广告 |
| 业务属性 | industry | VARCHAR(50) | 所属行业 |
| 系统控制 | status | TINYINT | 状态:0潜在 1正式 2流失 |
| 系统控制 | created_at | DATETIME | 创建时间 |
| 系统控制 | updated_at | DATETIME | 更新时间 |
这里有个容易忽略的点:客户ID用自增还是雪花ID? 我个人倾向用雪花ID。为什么?因为自增ID在分库分表时特别痛苦,而且容易暴露业务量。你想想看,如果客户ID是连续的,竞争对手一看就知道你每天新增多少客户。
4.2 客户分类与标签设计
客户分类和标签,这是CRM系统里最能体现业务深度的地方。很多新手会把分类和标签混为一谈,其实它们完全是两码事。
分类是树形的,标签是扁平的。 分类讲究层级关系,比如「VIP客户 → 黄金VIP → 年度消费10万+」。标签则是打散的,比如「高意向」「近期活跃」「投诉过」。一个客户只能属于一个分类,但可以挂多个标签。
4.2.1 客户分类表设计
CREATE TABLE customer_category (
category_id INT PRIMARY KEY AUTO_INCREMENT,
category_name VARCHAR(50) NOT NULL,
parent_id INT DEFAULT 0,
level TINYINT DEFAULT 1,
sort_order INT DEFAULT 0,
created_at DATETIME
);
这个表的核心是 parent_id 字段。通过它,我们可以实现无限级分类。我记得有一次,业务方要求支持五级分类,从「行业 → 规模 → 区域 → 等级 → 标签组」。如果不用 parent_id 这种设计,光建表就得建五张。
path 字段存路径(比如 "1,3,7")来实现分类查询。这种做法在数据量小的时候还行,一旦超过10万条,like查询直接让数据库崩溃。老老实实用 parent_id + 递归查询,或者用闭包表,别走捷径。
4.2.2 客户标签设计
标签设计,我推荐用KV结构。什么意思?就是标签名和标签值分开存。
CREATE TABLE customer_tag (
tag_id INT PRIMARY KEY AUTO_INCREMENT,
tag_name VARCHAR(30) NOT NULL UNIQUE,
tag_type TINYINT DEFAULT 0, -- 0系统标签 1自定义标签
color VARCHAR(10) -- 展示颜色
);
CREATE TABLE customer_tag_rel (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL,
tag_id INT NOT NULL,
created_at DATETIME,
UNIQUE KEY uk_customer_tag (customer_id, tag_id)
);
为什么用关联表?因为一个客户可能有几十个标签。如果直接在客户表里加一个 tags 字段存逗号分隔的ID,查询时你会哭的。我接手过一个遗留系统,就是用逗号分隔存的标签,每次查「所有带'高意向'标签的客户」都要用 FIND_IN_SET,那性能,啧啧。
4.3 客户地址表设计
地址这块,很多人觉得简单——不就是省市区加详细地址吗?其实坑不少。
我建议把地址拆成两张表:地址字典表 和 客户地址表。
4.3.1 地址字典表
CREATE TABLE region (
region_id INT PRIMARY KEY AUTO_INCREMENT,
region_name VARCHAR(50) NOT NULL,
parent_id INT DEFAULT 0,
level TINYINT, -- 1省 2市 3区
zip_code VARCHAR(6)
);
这张表的好处是,你可以直接用它做地址选择器,而且数据是标准化的。我在项目中遇到过,业务方要求统计「华东地区的客户数量」,如果没有标准化的地址字典,你光靠「上海市」「江苏省」这种字符串去匹配,很容易漏掉「上海」「江苏」这种简写。
4.3.2 客户地址表
CREATE TABLE customer_address (
address_id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL,
region_id INT, -- 关联到region表
detail_addr VARCHAR(200), -- 详细地址
is_default TINYINT DEFAULT 0, -- 是否默认地址
addr_type TINYINT DEFAULT 0, -- 0收货地址 1发票地址 2办公地址
created_at DATETIME
);
is_default 字段的实现有个小技巧。你不能简单设成 0 或 1,因为一个客户可能有多个地址。正确的做法是:在应用层保证每个客户只有一个默认地址。我一般会在插入新默认地址时,先把该客户所有地址的 is_default 置为 0,再设置新的。用事务包起来,不会出问题。
另外,addr_type 这个字段很多人会忽略。其实很有用。比如做发票管理时,你直接查 addr_type = 1 就能拿到发票地址,不用再去问客户「您的发票地址是哪个?」。
4.4 表关系总结
最后,我把这几张表的关系梳理一下:
- Customer 是主表,一条记录代表一个客户
- Customer 1:N Customer_Address —— 一个客户可以有多个地址
- Customer N:M Customer_Tag —— 通过关联表实现多对多
- Customer 1:1 Customer_Category —— 一个客户只能属于一个分类
嗯,这套设计我用了好几年,从单机MySQL到分库分表,基本没大改过。你如果现在要做一个CRM系统,照着这个结构来,至少未来两年不用重构客户模块。
下一章我会讲销售线索管理,那是客户进来的第一道门,设计得好不好直接影响转化率。到时候咱们再细聊。