-- 微信营销管理系统 MVP 建表 SQL -- 数据库: wxchat_sales -- 销售人员 CREATE TABLE IF NOT EXISTS salesperson ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, team TEXT, wx_account TEXT NOT NULL, device_id TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- 联系人(微信好友) CREATE TABLE IF NOT EXISTS contact ( id SERIAL PRIMARY KEY, salesperson_id INT NOT NULL REFERENCES salesperson(id) ON DELETE CASCADE, wx_username TEXT NOT NULL, nickname TEXT, remark TEXT, display_name TEXT NOT NULL, is_group BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(salesperson_id, wx_username) ); -- 会话 CREATE TABLE IF NOT EXISTS conversation ( id SERIAL PRIMARY KEY, salesperson_id INT NOT NULL REFERENCES salesperson(id) ON DELETE CASCADE, contact_id INT REFERENCES contact(id) ON DELETE CASCADE, wx_identifier TEXT NOT NULL, conv_type TEXT NOT NULL DEFAULT 'single', last_synced_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(salesperson_id, wx_identifier) ); -- 消息 CREATE TABLE IF NOT EXISTS message ( id BIGSERIAL PRIMARY KEY, salesperson_id INT NOT NULL REFERENCES salesperson(id) ON DELETE CASCADE, conversation_id INT NOT NULL REFERENCES conversation(id) ON DELETE CASCADE, sender_wx_username TEXT NOT NULL, sender_display_name TEXT, message_type TEXT NOT NULL, raw_content TEXT, normalized_content TEXT, created_at TIMESTAMPTZ NOT NULL, source_shard TEXT NOT NULL, source_table TEXT NOT NULL, source_local_id BIGINT NOT NULL, UNIQUE(source_shard, source_table, source_local_id) ); CREATE INDEX IF NOT EXISTS idx_msg_conv_time ON message(conversation_id, created_at); CREATE INDEX IF NOT EXISTS idx_msg_salesperson ON message(salesperson_id); CREATE INDEX IF NOT EXISTS idx_msg_created_at ON message(created_at); -- 客户(AI 识别) CREATE TABLE IF NOT EXISTS customer ( id SERIAL PRIMARY KEY, salesperson_id INT NOT NULL REFERENCES salesperson(id) ON DELETE CASCADE, contact_id INT NOT NULL REFERENCES contact(id) ON DELETE CASCADE, customer_name TEXT, industry TEXT, intent_level TEXT, key_needs TEXT[], reason TEXT, summary TEXT, stage TEXT, key_points TEXT[], objections TEXT[], next_action TEXT, last_analysis TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(salesperson_id, contact_id) ); CREATE INDEX IF NOT EXISTS idx_customer_salesperson ON customer(salesperson_id); CREATE INDEX IF NOT EXISTS idx_customer_intent ON customer(intent_level); -- 产品 CREATE TABLE IF NOT EXISTS product ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, category TEXT NOT NULL, spec TEXT, price NUMERIC(12,2), description TEXT, positioning TEXT, selling_points TEXT[], target_audience TEXT, status TEXT NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_product_category ON product(category); CREATE INDEX IF NOT EXISTS idx_product_status ON product(status); -- 成交记录 CREATE TABLE IF NOT EXISTS deal ( id SERIAL PRIMARY KEY, salesperson_id INT NOT NULL REFERENCES salesperson(id) ON DELETE CASCADE, customer_id INT REFERENCES customer(id) ON DELETE SET NULL, contact_id INT REFERENCES contact(id) ON DELETE SET NULL, product_id INT REFERENCES product(id) ON DELETE SET NULL, product_name TEXT NOT NULL, amount NUMERIC(12,2) NOT NULL, deal_date DATE NOT NULL, status TEXT NOT NULL DEFAULT 'closed', notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_deal_salesperson ON deal(salesperson_id); CREATE INDEX IF NOT EXISTS idx_deal_customer ON deal(customer_id); CREATE INDEX IF NOT EXISTS idx_deal_date ON deal(deal_date); CREATE INDEX IF NOT EXISTS idx_deal_product ON deal(product_id); -- 迁移:为已存在的 deal 表补充 product_id 列(首次执行时若列已存在则跳过) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deal' AND column_name = 'product_id') THEN ALTER TABLE deal ADD COLUMN product_id INT REFERENCES product(id) ON DELETE SET NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'product' AND column_name = 'positioning') THEN ALTER TABLE product ADD COLUMN positioning TEXT; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'product' AND column_name = 'selling_points') THEN ALTER TABLE product ADD COLUMN selling_points TEXT[]; END IF; END $$; -- 预置产品数据(幂等) INSERT INTO product (id, name, category, spec, price, description, positioning, selling_points, target_audience) VALUES (1, '益童宝益生菌', '儿童益生菌', '3盒套餐', 798.00, '丹麦进口益生菌粉,专为3-12岁儿童设计。采用科汉森DDS-1菌株,每袋含100亿活菌。主打调节肠道菌群、增强免疫力,对小儿湿疹、过敏性鼻炎、食物过敏等有显著改善效果。3盒为一个完整调理周期,建议持续服用3个月。', '进口菌株儿童抗过敏专家,宝妈信赖之选', ARRAY['丹麦进口科汉森DDS-1菌株,品质有保障', '每袋100亿活菌,活性稳定易吸收', '临床验证对湿疹、鼻炎、食物过敏改善率超85%', '3盒完整周期,从根源调节免疫', '无添加无副作用,儿童长期服用安全'], '3-12岁儿童,尤其适合过敏体质儿童'), (2, '益童宝益生菌', '儿童益生菌', '单盒', 298.00, '丹麦进口益生菌粉,专为3-12岁儿童设计。采用科汉森DDS-1菌株,每袋含100亿活菌。主打调节肠道菌群、增强免疫力,对小儿湿疹、过敏性鼻炎、食物过敏等有显著改善效果。单盒15袋,半月用量,适合初次体验。', '进口菌株儿童抗过敏专家,宝妈信赖之选', ARRAY['丹麦进口科汉森DDS-1菌株,品质有保障', '每袋100亿活菌,活性稳定易吸收', '临床验证对湿疹、鼻炎、食物过敏改善率超85%', '单盒体验装,低门槛试用'], '3-12岁儿童,尤其适合过敏体质儿童'), (3, '益童宝益生菌', '儿童益生菌', '6盒套餐', 1499.00, '丹麦进口益生菌粉,专为3-12岁儿童设计。采用科汉森DDS-1菌株,每袋含100亿活菌。6盒为两个完整调理周期,适合顽固性过敏体质儿童长期调理,性价比最高。', '进口菌株儿童抗过敏专家,宝妈信赖之选', ARRAY['丹麦进口科汉森DDS-1菌株,品质有保障', '每袋100亿活菌,活性稳定易吸收', '临床验证对湿疹、鼻炎、食物过敏改善率超85%', '6盒双周期,适合顽固性过敏长期调理', '均价最低,性价比之王'], '3-12岁儿童,尤其适合顽固性过敏体质'), (4, '纤益益生菌', '女性减肥益生菌', '2盒周期装', 596.00, '专为18-35岁年轻女性设计的体重管理益生菌。含专利菌株B420,经临床验证可减少体脂率、腰围。配合膳食纤维和绿茶提取物,促进代谢、抑制脂肪吸收。2盒为一个周期(60天),建议配合轻度运动效果更佳。', '科学减脂不反弹,年轻女性的身材管理专家', ARRAY['专利菌株B420,临床验证减脂效果', '抑制脂肪吸收,促进肠道代谢', '配合膳食纤维+绿茶提取物,三效合一', '不节食不腹泻,健康减脂', '2盒完整周期,见效明显'], '18-35岁有体重管理需求的年轻女性'), (5, '纤益益生菌', '女性减肥益生菌', '单盒', 328.00, '专为18-35岁年轻女性设计的体重管理益生菌。含专利菌株B420,经临床验证可减少体脂率、腰围。单盒30天用量,适合初次体验。', '科学减脂不反弹,年轻女性的身材管理专家', ARRAY['专利菌株B420,临床验证减脂效果', '抑制脂肪吸收,促进肠道代谢', '单盒体验装,低门槛试用', '不节食不腹泻,健康减脂'], '18-35岁有体重管理需求的年轻女性') ON CONFLICT (id) DO NOTHING; -- 重置 product 序列,避免显式 ID 插入后 SERIAL 不同步 SELECT setval('product_id_seq', COALESCE((SELECT max(id) FROM product), 1), true); -- 预置 3 名销售人员(幂等) INSERT INTO salesperson (id, name, team, wx_account, device_id) VALUES (1, '张伟', '华东团队', 'wxid_zhangwei', 'macbook-zw-001'), (2, '李娜', '华东团队', 'wxid_lina', 'macbook-ln-002'), (3, '王强', '华南团队', 'wxid_wangqiang', 'macbook-wq-003') ON CONFLICT (id) DO NOTHING; -- 重置 salesperson 序列 SELECT setval('salesperson_id_seq', COALESCE((SELECT max(id) FROM salesperson), 1), true);