news 2026/10/2 8:29:12

CRM数据库表设计实战:从线索到合同的高可用建模

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CRM数据库表设计实战:从线索到合同的高可用建模

简介:本资源是一份面向数据库设计初学者与CRM系统开发者的《CRM客户关系管理系统数据库表设计需求规格说明书》,聚焦企业级权限管理、销售机会跟踪与客户信息建模等核心场景。文档完整定义了10张关键数据表(含角色、菜单、权限、用户、销售机会、客户、联系人、交往记录、流失客户及开发计划表),详细说明各字段类型、长度、约束条件与业务含义,覆盖RBAC权限模型、客户生命周期管理及多层级菜单结构等典型设计实践。资源为单个Word文档(.doc格式),体积精简仅191KB,便于快速查阅与复用。已有296人学习下载,适合正在开展CRM系统开发、数据库课程设计或毕业设计的学生与工程师,可直接作为数据库建模参考模板,快速理解实体关系映射、外键约束设计及状态标志字段的工程化表达。

1. 这不是写文档,是给CRM系统搭骨架:一份能跑通增删改查、扛住销售漏斗、经得起审计回溯的数据库表设计说明书

你手头这份《CRM客户关系管理系统数据库表设计需求规格说明书(1).doc》,名字里带“(1)”——说明它大概率是初稿、是评审前的交付物、是开发团队和业务方扯皮时最常被翻出来拍桌子的那张纸。它不决定UI好不好看,也不管API快不快,但它直接决定:销售录入的线索能不能自动归入正确区域、合同金额变更后历史记录是否可追溯、财务对账时“已签约未开票”状态能否精准筛选、甚至法务要调取某客户三年内全部沟通记录时,SQL会不会跑崩。这不是文档工程师闭门造车的产物,而是销售总监盯着KPI、客服主管抱怨工单重复派发、IT运维半夜被慢查询告警叫醒之后,所有人被迫坐下来,用字段名、主键约束、外键关系、索引策略这些冷冰冰的符号,重新定义“客户”到底是什么。本文不讲UML图怎么画、不教Word排版技巧,只聚焦一个动作:把这份说明书里的每一张表、每一个字段、每一条约束,变成PostgreSQL/MySQL里能执行、能验证、能上线、能迭代的真实DDL语句,并告诉你为什么这么设计、哪里最容易翻车、上线前必须跑哪三类校验脚本。适合正在接手CRM重构、刚拿到需求文档却不知从哪建第一张表的后端工程师,也适合想穿透技术细节、真正看清CRM数据底座是否牢靠的业务负责人。


2. 从“客户”开始建模:核心实体表设计与字段语义落地

CRM系统的灵魂不在界面,而在customer、lead、opportunity、contact这四张表的结构里。很多团队一上来就建user表,结果发现销售同事录入的“客户公司名称”和“联系人姓名”散落在不同字段里,做BI分析时连基础的“客户-联系人-商机”三层关系都关联不上。我们按真实业务流顺序建模:线索(Lead)→ 客户(Customer)→ 联系人(Contact)→ 商机(Opportunity),每张表都带业务强约束,不是简单堆字段。

2.1lead线索表:销售入口的过滤器,不是垃圾桶

线索是CRM的第一道闸口,必须承载“谁、从哪来、为什么值得跟进”的原始信息。常见错误是把lead当临时表,字段全设为VARCHAR(255),导致后续无法做来源渠道统计或质量评分。实际设计需强制分离元数据与业务属性:

CREATE TABLE lead ( id BIGSERIAL PRIMARY KEY, source VARCHAR(32) NOT NULL CHECK (source IN ('website', 'wechat', 'phone', 'referral', 'event')), status VARCHAR(20) NOT NULL DEFAULT 'new' CHECK (status IN ('new', 'qualified', 'disqualified', 'converted')), score INTEGER DEFAULT 0 CHECK (score BETWEEN 0 AND 100), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), converted_at TIMESTAMPTZ NULL, -- 关键:线索转为客户时,必须关联到customer.id,禁止空值 customer_id BIGINT NULL REFERENCES customer(id) ON DELETE SET NULL, -- 来源渠道细分字段,避免用text存模糊描述 utm_source VARCHAR(64) NULL, utm_medium VARCHAR(64) NULL, utm_campaign VARCHAR(64) NULL );

逻辑说明:source用枚举而非自由文本,确保BI报表中“微信来源线索数”能精确统计;score字段预留算法接口,后续可接入规则引擎(如:手机号完整+邮箱有效+行业匹配=+30分);converted_at与customer_id组合,构成线索转化率计算的原子事实——这是销售管理仪表盘的核心指标,不能靠应用层拼接。

2.2customer客户主表:法律主体与商业实体的双重身份

customer不是“公司名”,而是受法律认可的商业实体。很多系统把客户名、地址、税号全塞进一张表,结果税务稽查时发现同一公司因录入格式差异(“北京某某科技有限公司” vs “北京某某科技”)被拆成多个客户。必须拆解为legal_entity(法律主体)和business_profile(商业画像)两个维度:

-- 法律主体表:唯一性由统一社会信用代码锚定 CREATE TABLE legal_entity ( id BIGSERIAL PRIMARY KEY, credit_code CHAR(18) UNIQUE NOT NULL, -- 统一社会信用代码,18位固定长度 name VARCHAR(255) NOT NULL, registered_address TEXT NOT NULL, legal_representative VARCHAR(100) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 商业画像表:同一法律主体可有多个业务画像(如:集团总部采购部、华东分公司) CREATE TABLE business_profile ( id BIGSERIAL PRIMARY KEY, legal_entity_id BIGINT NOT NULL REFERENCES legal_entity(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, -- 如“XX集团采购中心” department VARCHAR(100) NULL, -- 部门层级 industry VARCHAR(64) NOT NULL CHECK (industry IN ('manufacturing', 'finance', 'healthcare', 'education')), annual_revenue NUMERIC(15,2) NULL CHECK (annual_revenue >= 0), employee_count INTEGER NULL CHECK (employee_count > 0), -- 业务状态:active/inactive/pending_review,非简单deleted status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'pending_review')) );

参数说明:credit_code设为CHAR(18)而非VARCHAR,强制校验长度且提升索引效率;business_profile.status不用布尔值,因为“待审核”是真实业务状态,需在审批流中体现;ON DELETE CASCADE确保删除法律主体时,其所有业务画像自动清理,避免孤儿数据。

2.3contact联系人表:绑定到商业画像,而非法律主体

联系人属于具体业务场景,不是法律文件签字人。错误设计是让contact直接关联legal_entity,导致“XX集团CEO”和“XX集团采购专员”被归为同一层级。正确做法是绑定到business_profile,并增加角色标签:

CREATE TABLE contact ( id BIGSERIAL PRIMARY KEY, business_profile_id BIGINT NOT NULL REFERENCES business_profile(id) ON DELETE CASCADE, full_name VARCHAR(100) NOT NULL, job_title VARCHAR(100) NULL, mobile_phone VARCHAR(20) NULL, email VARCHAR(255) NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'), -- 角色标签:决策者/影响者/执行者/守门人,支持多选但用JSONB存储 roles JSONB NOT NULL DEFAULT '[]'::jsonb, -- 是否为主要联系人(每个business_profile仅1个) is_primary BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 为高频查询加复合索引 CREATE INDEX idx_contact_bp_role ON contact(business_profile_id, (roles->>'decision_maker'));

逻辑说明:roles用JSONB而非单独字段,因为销售需要动态添加角色(如“ESG项目负责人”),且需支持WHERE roles @> '{"decision_maker": true}'这类高效查询;is_primary设为BOOLEAN但加业务约束——应用层需保证每个business_profile_id下至多一个TRUE,数据库用触发器或应用逻辑控制,此处不硬编码唯一索引,留出灵活性。


3. 支撑销售漏斗:商机、合同、活动三张表的时序与状态机设计

销售漏斗不是线性流程,而是带分支、回退、暂停的复杂状态机。opportunity表若只存status字段,很快会陷入“已报价-已签合同-已取消-已重启”等混乱状态。必须用状态迁移表+时间戳固化过程。

3.1opportunity商机主表:只存核心事实,状态交由关联表管理

CREATE TABLE opportunity ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, business_profile_id BIGINT NOT NULL REFERENCES business_profile(id) ON DELETE CASCADE, owner_id BIGINT NOT NULL REFERENCES user_account(id), -- 销售人员ID estimated_value NUMERIC(15,2) NOT NULL CHECK (estimated_value >= 0), close_date DATE NULL, probability_percent INTEGER NOT NULL DEFAULT 0 CHECK (probability_percent BETWEEN 0 AND 100), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 状态迁移历史表:记录每一次状态变更 CREATE TABLE opportunity_status_history ( id BIGSERIAL PRIMARY KEY, opportunity_id BIGINT NOT NULL REFERENCES opportunity(id) ON DELETE CASCADE, from_status VARCHAR(32) NULL, -- 允许NULL表示初始状态 to_status VARCHAR(32) NOT NULL CHECK (to_status IN ('prospecting', 'qualification', 'proposal', 'negotiation', 'won', 'lost', 'paused')), changed_by BIGINT NOT NULL REFERENCES user_account(id), changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), notes TEXT NULL ); -- 为查询当前状态加物化视图(PostgreSQL) CREATE MATERIALIZED VIEW current_opportunity_status AS SELECT opportunity_id, to_status AS current_status, changed_at AS status_updated_at, changed_by FROM ( SELECT opportunity_id, to_status, changed_at, changed_by, ROW_NUMBER() OVER (PARTITION BY opportunity_id ORDER BY changed_at DESC) as rn FROM opportunity_status_history ) ranked WHERE rn = 1;

逻辑说明:opportunity表本身不存status字段,避免状态被误更新;opportunity_status_history强制记录每次变更的from_status和to_status,为销售复盘提供依据(如:某商机从“谈判中”退回“方案阶段”的频次);物化视图current_opportunity_status替代应用层JOIN,提升漏斗报表查询速度——这是CRM看板加载慢的常见根因。

3.2contract合同表:与商机强绑定,但独立生命周期

合同不是商机的子集,它有独立的签署、履约、续期流程。错误设计是让contract外键指向opportunity,导致商机关闭后合同无法更新。正确做法是双向关联:

CREATE TABLE contract ( id BIGSERIAL PRIMARY KEY, opportunity_id BIGINT NULL REFERENCES opportunity(id) ON DELETE SET NULL, customer_id BIGINT NOT NULL REFERENCES customer(id), -- 直接关联客户,不通过商机 start_date DATE NOT NULL, end_date DATE NOT NULL CHECK (end_date >= start_date), total_amount NUMERIC(15,2) NOT NULL CHECK (total_amount >= 0), currency VARCHAR(3) NOT NULL DEFAULT 'CNY' CHECK (currency IN ('CNY', 'USD', 'EUR')), status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'signed', 'active', 'expired', 'terminated')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 自动更新updated_at的触发器 CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ language 'plpgsql'; CREATE TRIGGER update_contract_updated_at BEFORE UPDATE ON contract FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();

参数说明:opportunity_id设为NULLABLE,因为合同可能来自老客户续约,无对应商机;customer_id直接关联,确保合同归属清晰;currency用VARCHAR(3)存ISO 4217代码,为未来多币种结算留接口。

3.3activity活动表:销售动作的原子记录,不是日志

activity表常被误建为操作日志(如“用户A修改了客户B的地址”),但销售需要的是“与客户C的电话沟通,时长12分钟,结论:下周演示”。必须结构化存储动作类型、对象、结果:

CREATE TABLE activity ( id BIGSERIAL PRIMARY KEY, type VARCHAR(32) NOT NULL CHECK (type IN ('call', 'email', 'meeting', 'task', 'note')), subject VARCHAR(255) NOT NULL, description TEXT NULL, -- 动作关联对象:可关联客户、联系人、商机、合同中的任意一个 customer_id BIGINT NULL REFERENCES customer(id) ON DELETE SET NULL, contact_id BIGINT NULL REFERENCES contact(id) ON DELETE SET NULL, opportunity_id BIGINT NULL REFERENCES opportunity(id) ON DELETE SET NULL, contract_id BIGINT NULL REFERENCES contract(id) ON DELETE SET NULL, -- 时间范围:meeting有start/end,call只有start start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ NULL CHECK (end_time IS NULL OR end_time > start_time), owner_id BIGINT NOT NULL REFERENCES user_account(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 多字段联合索引,覆盖高频查询场景 CREATE INDEX idx_activity_owner_type_time ON activity(owner_id, type, start_time); CREATE INDEX idx_activity_target ON activity(customer_id, contact_id, opportunity_id, contract_id);

逻辑说明:activity表用四个NULLABLE外键覆盖所有关联可能性,避免为每种动作建单独表;start_time/end_time区分动作粒度(电话用start_time,会议用start_time+end_time);双索引设计解决两类查询:销售个人日程(owner_id + type + start_time)和客户全景视图(按customer_id查所有关联活动)。


4. 权限、审计与扩展:支撑企业级CRM的底层能力设计

CRM不是销售工具,而是企业数据中枢。user_account表若只存登录名密码,权限体系必然崩溃;没有审计日志,合规检查就是灾难。这部分设计决定系统能否在中大型企业落地。

4.1user_account用户表:角色、部门、租户的三层隔离

CREATE TABLE user_account ( id BIGSERIAL PRIMARY KEY, username VARCHAR(64) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'), password_hash VARCHAR(255) NOT NULL, -- bcrypt哈希 full_name VARCHAR(100) NOT NULL, -- 部门归属:支持树形结构,用ltree扩展(PostgreSQL) department_path LTREE NULL, -- 租户ID:SaaS模式下必备 tenant_id BIGINT NULL REFERENCES tenant(id), -- 状态:active/inactive/locked,非deleted status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'locked')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_login_at TIMESTAMPTZ NULL ); -- 角色权限表:RBAC模型 CREATE TABLE role ( id BIGSERIAL PRIMARY KEY, name VARCHAR(64) NOT NULL UNIQUE, -- 如'sales_manager', 'finance_analyst' description TEXT NULL, is_system BOOLEAN DEFAULT FALSE -- 系统内置角色不可删除 ); CREATE TABLE user_role ( user_id BIGINT NOT NULL REFERENCES user_account(id) ON DELETE CASCADE, role_id BIGINT NOT NULL REFERENCES role(id) ON DELETE CASCADE, PRIMARY KEY (user_id, role_id) ); -- 权限定义表:细粒度控制(如'customer:read', 'opportunity:edit:won') CREATE TABLE permission ( id BIGSERIAL PRIMARY KEY, code VARCHAR(128) NOT NULL UNIQUE, -- 权限码,约定命名规范 description TEXT NOT NULL, category VARCHAR(32) NOT NULL CHECK (category IN ('customer', 'opportunity', 'contract', 'admin')) ); CREATE TABLE role_permission ( role_id BIGINT NOT NULL REFERENCES role(id) ON DELETE CASCADE, permission_id BIGINT NOT NULL REFERENCES permission(id) ON DELETE CASCADE, PRIMARY KEY (role_id, permission_id) );

逻辑说明:department_path用LTREE类型(需启用ltree扩展),支持'sales.north.china'这样的路径查询,实现“华北区销售总监能看到下属所有客户”;user_role和role_permission构成标准RBAC,避免在user_account表里加一堆布尔字段;permission.code采用资源:操作:条件格式(如'opportunity:edit:won'),为未来ABAC(基于属性的访问控制)留扩展点。

4.2audit_log审计日志表:只存关键变更,不记查询

审计日志不是全量日志,而是满足GDPR/等保要求的最小必要记录。重点记录“谁、在何时、对哪个业务对象、做了什么变更、变更前后的关键值”。

CREATE TABLE audit_log ( id BIGSERIAL PRIMARY KEY, table_name VARCHAR(64) NOT NULL, -- 如'customer', 'opportunity' record_id BIGINT NOT NULL, -- 被操作记录的主键ID operation VARCHAR(10) NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')), user_id BIGINT NOT NULL REFERENCES user_account(id), -- 只存变更字段的旧值和新值,用JSONB压缩存储 changes JSONB NOT NULL, ip_address INET NULL, user_agent TEXT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 为审计查询优化:按表名+时间范围快速定位 CREATE INDEX idx_audit_table_time ON audit_log(table_name, created_at); CREATE INDEX idx_audit_user_time ON audit_log(user_id, created_at);

参数说明:changes字段存{"name": {"old": "ABC公司", "new": "ABC科技有限公司"}, "status": {"old": "active", "new": "inactive"}},避免存整行快照浪费空间;ip_address用INET类型,支持IP段查询(如查某办公网段的所有操作);索引设计针对两类审计场景:按业务对象查历史(table_name + record_id)、按责任人查行为(user_id + created_at)。

4.3 扩展字段设计:应对业务变化的柔性方案

业务部门总说“这个字段下次迭代再加”,结果每次加字段都要停服改表。用entity_attribute表实现动态扩展:

CREATE TABLE entity_attribute ( id BIGSERIAL PRIMARY KEY, entity_type VARCHAR(32) NOT NULL CHECK (entity_type IN ('customer', 'opportunity', 'contact')), attribute_key VARCHAR(64) NOT NULL, attribute_type VARCHAR(20) NOT NULL CHECK (attribute_type IN ('string', 'number', 'date', 'boolean', 'select')), label VARCHAR(100) NOT NULL, -- 显示名称 is_required BOOLEAN DEFAULT FALSE, options JSONB NULL, -- select类型时存选项列表 sort_order INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE entity_attribute_value ( id BIGSERIAL PRIMARY KEY, attribute_id BIGINT NOT NULL REFERENCES entity_attribute(id) ON DELETE CASCADE, entity_type VARCHAR(32) NOT NULL, entity_id BIGINT NOT NULL, -- 对应实体的主键ID string_value TEXT NULL, number_value NUMERIC(15,2) NULL, date_value DATE NULL, boolean_value BOOLEAN NULL, select_value VARCHAR(255) NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 复合索引加速查询 CREATE INDEX idx_attr_value_entity ON entity_attribute_value(entity_type, entity_id); CREATE INDEX idx_attr_value_attr ON entity_attribute_value(attribute_id, entity_type, entity_id);

逻辑说明:entity_attribute定义扩展字段元数据(如客户表的“客户等级”字段);entity_attribute_value存实际值,按entity_type+entity_id分片,避免单表膨胀;string_value/number_value等分列存储,保证查询性能——比全用JSONB更易索引和统计。


5. 避坑指南:上线前必须验证的5个致命陷阱

这份说明书落地时,80%的线上故障源于设计阶段埋下的隐性缺陷。以下是我踩过的血泪坑,按发生频率排序,每条都附带可执行的验证脚本。

5.1 现象:销售反馈“线索分配不均”,实际是lead.owner_id缺失导致轮询失效

原因:lead表未强制关联user_account.id,新线索插入时owner_id为NULL,分配逻辑跳过该记录。
解决:立即添加NOT NULL约束并补全历史数据

-- 添加约束(需先处理NULL数据) UPDATE lead SET owner_id = (SELECT id FROM user_account WHERE role = 'sales_rep' ORDER BY RANDOM() LIMIT 1) WHERE owner_id IS NULL; ALTER TABLE lead ALTER COLUMN owner_id SET NOT NULL; ALTER TABLE lead ADD CONSTRAINT fk_lead_owner FOREIGN KEY (owner_id) REFERENCES user_account(id);

5.2 现象:客户搜索响应超5秒,EXPLAIN显示全表扫描

原因:customer.name未建索引,且业务查询常用ILIKE '%关键词%',B-tree索引无效。
解决:创建GIN全文索引并改写查询

-- 创建全文索引(PostgreSQL) CREATE INDEX idx_customer_name_gin ON customer USING GIN (to_tsvector('chinese', name)); -- 应用层查询改为:WHERE to_tsvector('chinese', name) @@ to_tsquery('chinese', '关键词');

5.3 现象:导出客户列表时Excel报“数字被转成科学计数法”,原因是税号被当数字处理

原因:legal_entity.credit_code定义为VARCHAR(18)但应用层读取时自动转为数值类型。
解决:数据库层加生成列强制字符串化,并通知前端使用该列

ALTER TABLE legal_entity ADD COLUMN credit_code_display TEXT GENERATED ALWAYS AS (credit_code::TEXT) STORED; COMMENT ON COLUMN legal_entity.credit_code_display IS '供导出使用的税号字符串,避免Excel自动转换';

5.4 现象:财务对账时发现“已签约未开票”金额不准,查出是contract.status与opportunity.status状态不同步

原因:业务逻辑未强制合同状态变更时同步商机状态(如合同签署后商机应置为won)。
解决:在contract表上建触发器,状态变更为signed时自动更新关联商机

CREATE OR REPLACE FUNCTION sync_opportunity_on_contract_sign() RETURNS TRIGGER AS $$ BEGIN IF NEW.status = 'signed' AND NEW.opportunity_id IS NOT NULL THEN INSERT INTO opportunity_status_history (opportunity_id, from_status, to_status, changed_by, notes) SELECT NEW.opportunity_id, NULL, 'won', NEW.created_by, 'Contract signed'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_sync_opportunity AFTER INSERT OR UPDATE ON contract FOR EACH ROW EXECUTE FUNCTION sync_opportunity_on_contract_sign();

5.5 现象:审计日志表audit_log半年后暴涨至200GB,备份失败

原因:未配置分区和TTL,所有操作日志堆积在一张表。
解决:按月分区并设置自动清理

-- PostgreSQL 12+ 分区表 CREATE TABLE audit_log PARTITION OF audit_log_master FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'); -- 创建每日清理job(使用pg_cron扩展) SELECT cron.schedule('0 2 * * *', $$DELETE FROM audit_log WHERE created_at < NOW() - INTERVAL '180 days'$$);

6. 验证你的设计:三类必跑脚本与一份可交付的检查清单

设计再完美,不验证就是纸上谈兵。我坚持上线前跑三类脚本:数据一致性校验、性能压测基线、业务场景回放。它们不是测试报告,而是交付物的一部分。

6.1 数据一致性校验:用SQL揪出隐藏的脏数据

在测试库运行以下脚本,输出为0才代表数据关系干净:

-- 检查线索是否全部归属到有效销售员 SELECT COUNT(*) FROM lead WHERE owner_id NOT IN (SELECT id FROM user_account WHERE status = 'active'); -- 检查客户主数据是否出现同一税号多个legal_entity SELECT credit_code, COUNT(*) FROM legal_entity GROUP BY credit_code HAVING COUNT(*) > 1; -- 检查商机状态历史是否缺失最新记录(即current_opportunity_status未刷新) SELECT COUNT(*) FROM opportunity o LEFT JOIN current_opportunity_status c ON o.id = c.opportunity_id WHERE c.opportunity_id IS NULL;

执行时机:每次数据库迁移后、每月初数据清洗后、重大版本发布前。把结果存入Confluence页面,标题为“CRM数据健康度报告-YYYYMMDD”。

6.2 性能压测基线:用真实数据量模拟峰值

不要用100条测试数据。从生产环境脱敏抽取10万客户、50万线索、20万商机,用pgbench或sysbench跑以下场景:

场景SQL示例合格线(P95延迟)
销售首页加载SELECT * FROM opportunity WHERE owner_id = 123 AND status IN ('proposal','negotiation') ORDER BY updated_at DESC LIMIT 20;≤ 300ms
客户360视图SELECT c.*, bp.*, co.* FROM customer c JOIN business_profile bp ON c.id = bp.customer_id JOIN contact co ON bp.id = co.business_profile_id WHERE c.id = 45678;≤ 800ms
线索批量导入INSERT INTO lead (...) VALUES (...),(...),...;(1000条/批)≥ 500条/秒

关键动作:记录EXPLAIN (ANALYZE, BUFFERS)输出,重点关注Shared Hit占比(应>95%)和Seq Scan行数(应为0)。若不达标,优先优化索引而非加机器。

6.3 业务场景回放:用真实工单验证流程闭环

选3个典型销售工单,手动执行全流程并记录耗时:

  • 工单A:市场部提交100条微信线索 → 系统自动分配 → 销售A认领 → 录入联系人 → 创建商机 → 发送方案 → 签署合同 → 生成发票
  • 工单B:老客户续约 → 销售B新建合同 → 关联原商机 → 更新客户等级 → 触发服务升级工单
  • 工单C:客户投诉 → 客服创建活动 → 关联原合同 → 升级至销售总监 → 修改商机概率

验收标准:每个环节的数据库记录必须存在、状态正确、时间戳连续、外键完整。用psql -c "SELECT * FROM ... WHERE ..."逐表验证,拒绝任何“应该没问题”的假设。

最后,把这三类验证结果整理成一份《CRM数据库设计交付检查清单》,包含:

  • ✅ 表结构DDL已生成并执行(附Git commit hash)
  • ✅ 所有外键约束已启用(SELECT conname FROM pg_constraint WHERE contype = 'f';)
  • ✅ 核心索引已创建(SELECT indexdef FROM pg_indexes WHERE tablename IN ('lead','customer','opportunity');)
  • ✅ 一致性校验脚本返回全0
  • ✅ 压测报告达标的截图
  • ✅ 三个业务场景回放的SQL验证记录

这份清单不是文档附件,而是部署包的一部分。每次上线,运维同事必须核对清单打钩才能执行kubectl rollout restart。我见过太多团队把“设计完成”当成终点,其实真正的终点是这张清单上的最后一个✓打上。希望帮到你。

本文还有配套的精品资源,点击获取

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

ECSHOP数据字典实战:从表结构到SQL查询的二次开发指南

简介&#xff1a;这是一份面向ECSHOP电商系统开发及二次开发人员的完整版数据库字典文档&#xff0c;覆盖v3.6/v3.0版本。文档从数据库字典角度&#xff0c;系统梳理商品分类、商品资料、商品相册、关联文章等模块的表结构设计&#xff0c;逐字段列出字段名、字段描述、字段类型…

作者头像 李华
网站建设 2026/10/2 8:28:05

Jev Harness:三权分立与 System One 决策层,兼看 4 类 token 优化门

❝ 先说结论&#xff1a;Jev Harness 不是一个具体产品&#xff0c;而是一组围绕 TypeSafe 的专用决策模型 Jev 展开的「决策 / 生成 / 执行三权分立」思路&#xff0c;外加多个开源实现把它落地成编码代理&#xff08;coding agent&#xff09;的 token 优化门控层。GitHub 上…

作者头像 李华
网站建设 2026/10/2 8:28:01

AnyJev:让开源大模型给出可信概率

AnyJev&#xff1a;让开源大模型给出可信概率 如果你正在用开源模型做分类、路由、审核&#xff0c;大概率遇到过这种情况&#xff1a;模型说“billing”&#xff0c;置信度 0.95&#xff0c;但你不敢直接自动化。因为那个 0.95 不是概率&#xff0c;是 softmax 出来的 token 分…

作者头像 李华
网站建设 2026/10/2 8:27:34

动态规划之状态定义的技巧

【动态规划之状态定义的技巧】 > 核心原则&#xff1a;状态需要完整描述当前局面&#xff0c;满足“无后效性”&#xff08;过往的选择不会干扰后续决策&#xff09;&#xff0c;同时子问题能够重复使用。 > 一句话概括&#xff1a;状态记录「已经完成的操作、剩余的约束…

作者头像 李华