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/endcall只有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_timeend_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, -- 租户IDSaaS模式下必备 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_typeentity_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。我见过太多团队把“设计完成”当成终点其实真正的终点是这张清单上的最后一个✓打上。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →