commit 5926c73ff12cc1e50cfad9dbb893ca7ecf8022ae Author: freedakgmail Date: Tue Jul 14 22:31:29 2026 +0800 微信营销管理系统 - 益童宝销售管理平台 - 数据库: PostgreSQL schema + 3名销售/21客户/341消息/6成交 - 采集代理: mock_sync.py 模拟微信聊天同步 - AI分析: analyze.py 规则模式 + 千问LLM模式 - 后端: FastAPI 11个API接口 - 前端: 仪表盘/销售列表/客户列表/成交记录/交流分析/录入成交 - 交流分析: 全部客户对话概览 + LLM标准范式对话生成 - 部署: systemd + nginx, 已部署至 sale.all8ai.top diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb9e876 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Python +__pycache__/ +*.pyc +*.pyo + +# PostgreSQL data +demo/db/data/ +demo/db/socket/ +demo/db/postgres.log + +# Runtime +demo/.run.pid +demo/web.log +demo/web/__pycache__/ + +# OS +.DS_Store diff --git a/MVP.md b/MVP.md new file mode 100644 index 0000000..9100a4a --- /dev/null +++ b/MVP.md @@ -0,0 +1,400 @@ +# 微信营销管理系统 MVP 方案 + +> 文档日期:2026-07-14 +> 定位:最小可用产品,验证"微信聊天数据 → 客户与沟通记录 → 成交关联"的核心闭环 + +--- + +## 一、MVP 目标 + +用最简路径完成一件事:**把每个销售的微信聊天采集到中央数据库,AI 分析出客户和沟通记录,成交数据手动录入,形成"沟通→成交"的证据链。** + +不做的事: +- 不做培训对练 +- 不做 AI 自动回复 +- 不做多租户 +- 不做实时同步 +- 不做复杂权限体系 + +--- + +## 二、MVP 范围 + +### 2.1 核心流程 + +```text +销售设备本机采集微信数据 + → 传输到中央数据库 + → AI 识别客户、提取沟通记录 + → 销售手动录入成交数据 + → 关联聊天与成交 + → 基础报表 +``` + +### 2.2 支持规模 + +- 3~5 名销售 +- 每人 1 个微信号 +- 每日离线同步(复用现有 launchd 机制) +- 单台中央 PostgreSQL + +--- + +## 三、系统架构 + +```text +销售设备 A (macOS) 销售设备 B (macOS) 销售设备 C (macOS) + 本机微信数据库采集 本机微信数据库采集 本机微信数据库采集 + 采集代理脚本 采集代理脚本 采集代理脚本 + │ │ │ + └──────────────┬───────────┘──────────────────────────┘ + ▼ + 中央 PostgreSQL + (wechat_sales_db) + │ + ▼ + AI 分析服务 + (客户识别 + 沟通摘要) + │ + ▼ + Web 管理后台 + (客户列表 / 聊天记录 / 成交录入 / 报表) +``` + +--- + +## 四、数据模型 + +MVP 只建 6 张核心表,不搞复杂实体关系。 + +### 4.1 salesperson — 销售人员 + +```sql +CREATE TABLE salesperson ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + team TEXT, + wx_account TEXT NOT NULL, -- 绑定的微信号 + device_id TEXT, -- 采集设备标识 + created_at TIMESTAMPTZ DEFAULT now() +); +``` + +### 4.2 contact — 联系人(微信好友) + +```sql +CREATE TABLE contact ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + wx_username TEXT NOT NULL, -- 微信内部 ID + nickname TEXT, -- 昵称 + remark TEXT, -- 备注 + display_name TEXT, -- 计算字段:备注优先,其次昵称 + is_group BOOLEAN DEFAULT FALSE, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE(salesperson_id, wx_username) +); +``` + +### 4.3 conversation — 会话 + +```sql +CREATE TABLE conversation ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + contact_id INT REFERENCES contact(id), + wx_chatroom TEXT, -- 群聊标识(群聊时用) + conv_type TEXT NOT NULL, -- 'single' | 'group' + last_synced_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now() +); +``` + +### 4.4 message — 消息 + +```sql +CREATE TABLE message ( + id BIGSERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + conversation_id INT NOT NULL REFERENCES conversation(id), + sender_wx_username TEXT NOT NULL, + sender_display_name TEXT, + message_type TEXT NOT NULL, -- text/image/voice/video/file/emoji/link/quote + raw_content TEXT, + normalized_content TEXT, -- 清洗后可读内容 + created_at TIMESTAMPTZ NOT NULL, + source_shard TEXT, + source_table TEXT, + source_local_id BIGINT, + UNIQUE(source_shard, source_table, source_local_id) +); +CREATE INDEX idx_message_conv_time ON message(conversation_id, created_at); +CREATE INDEX idx_message_salesperson ON message(salesperson_id); +``` + +### 4.5 customer — 客户(AI 从联系人中识别) + +```sql +CREATE TABLE customer ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + contact_id INT NOT NULL REFERENCES contact(id), + customer_name TEXT, -- AI 识别的客户名称 + industry TEXT, -- AI 推断行业 + intent_level TEXT, -- high/medium/low/none + key_needs TEXT[], -- AI 提取的关键需求 + last_analysis TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE(salesperson_id, contact_id) +); +``` + +### 4.6 deal — 成交记录(手动录入) + +```sql +CREATE TABLE deal ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + customer_id INT REFERENCES customer(id), + contact_id INT REFERENCES contact(id), + product_name TEXT NOT NULL, + amount NUMERIC(12,2) NOT NULL, + deal_date DATE NOT NULL, + status TEXT NOT NULL DEFAULT 'closed', -- closed/pending/refunded + notes TEXT, + created_at TIMESTAMPTZ DEFAULT now() +); +``` + +--- + +## 五、采集方案 + +### 5.1 复用现有能力 + +当前单机系统已具备: +- macOS 微信 4.x 数据库定位与解密 +- 联系人、会话、消息解析与标准化 +- 每日 launchd 定时同步 +- 增量去重(`UNIQUE(source_shard, source_table, source_local_id)`) + +MVP 阶段直接复用,只需扩展为多设备。 + +### 5.2 多设备采集流程 + +每台销售设备部署相同的采集代理: + +```text +1. launchd 每天 05:00 触发 +2. 退出微信 → 复制数据库副本 → 重启微信 +3. 解密数据库副本 +4. 解析联系人、会话、消息 +5. 通过 SSH/HTTP 将增量数据推送到中央服务器 +6. 中央服务器写入 PostgreSQL +7. 清理临时文件 +``` + +### 5.3 中央服务器接收 + +中央服务器提供一个简单的接收接口: + +```text +POST /api/sync +{ + "salesperson_id": 1, + "device_id": "macbook-abc123", + "contacts": [...], + "conversations": [...], + "messages": [...] +} +``` + +或者更简单:每台设备直接配置 PostgreSQL 远程连接,采集脚本直接写入中央库。MVP 阶段建议后者,省去中间服务。 + +### 5.4 数据隔离 + +每条数据都带 `salesperson_id`,确保销售之间数据隔离。查询时按销售过滤。 + +--- + +## 六、AI 分析 + +### 6.1 分析目标 + +MVP 只做两件事: + +1. **客户识别**:从联系人中筛选出真实客户(排除微商、广告、纯社交联系人) +2. **沟通摘要**:对每个客户会话生成结构化摘要 + +### 6.2 客户识别 + +对每个联系人,取最近 N 条消息,调用 LLM 判断: + +```json +{ + "is_customer": true, + "customer_name": "张总", + "industry": "餐饮", + "intent_level": "medium", + "key_needs": ["收银系统", "会员管理"], + "reason": "多次询问产品价格和功能,提到门店运营需求" +} +``` + +筛选规则: +- 有超过 5 条对话的联系人 +- 消息内容涉及产品咨询、价格、合作等关键词 +- 排除纯群聊通知、广告推送类联系人 + +### 6.3 沟通摘要 + +对每个客户会话,按时间窗口(如最近 7 天或全部)生成: + +```json +{ + "summary": "客户最初咨询收银系统价格,对比了竞品后关注会员管理功能,销售安排了产品演示,客户表示需要和合伙人商量。", + "stage": "产品演示", + "objections": ["价格偏高", "需要合伙人确认"], + "next_action": "等待客户反馈,建议3天后跟进", + "last_contact_date": "2026-07-12" +} +``` + +### 6.4 模型选择 + +- 优先使用现有 Ollama 本地模型(如 qwen2.5)降低成本 +- 消息量大时分批处理,每次传入不超过 50 条消息 +- 分析结果存入 `customer` 表,不单独建表 + +--- + +## 七、成交数据录入 + +### 7.1 录入方式 + +Web 后台提供简单表单: + +- 选择销售 +- 选择客户(从 AI 识别的客户列表中选) +- 填写产品名称、金额、成交日期 +- 可选填写备注 + +### 7.2 关联逻辑 + +成交记录通过 `contact_id` 自动关联到对应的微信会话。这样就能看到: + +- 某个成交客户的所有聊天记录 +- 从首次接触到成交的完整沟通时间线 +- 成交前的关键沟通节点 + +--- + +## 八、Web 管理后台 + +### 8.1 技术选型 + +| 模块 | 选型 | +|---|---| +| 前端 | React + TailwindCSS | +| 后端 | Python FastAPI | +| 数据库 | PostgreSQL 17 | +| 部署 | 单机部署,Nginx 反向代理 | + +### 8.2 页面清单 + +MVP 只做 5 个页面: + +#### 首页 / 仪表盘 + +- 今日新增消息数 +- 活跃客户数 +- 本月成交金额 +- 各销售消息量对比 + +#### 销售列表 + +- 每个销售的消息量、客户数、成交金额 +- 最后同步时间 +- 同步状态 + +#### 客户列表 + +- 按销售筛选 +- 按意向等级筛选 +- 显示客户名称、行业、意向、最后沟通时间 +- 点击进入客户详情 + +#### 客户详情 + +- 客户基本信息(AI 识别结果) +- 完整聊天记录(时间线展示,复用现有搜索页面能力) +- AI 沟通摘要 +- 关联的成交记录 + +#### 成交录入 + +- 简单表单 +- 成交列表(可按销售、日期、产品筛选) + +--- + +## 九、实施计划 + +### 第 1 周:数据采集多设备化 + +- [ ] 创建中央 PostgreSQL 数据库 `wechat_sales_db` +- [ ] 建表(6 张核心表) +- [ ] 改造现有采集脚本,支持写入中央库(增加 `salesperson_id` 字段) +- [ ] 在 2 台设备上部署采集代理并验证 + +### 第 2 周:AI 分析 + Web 后台 + +- [ ] 搭建 FastAPI 后端骨架 +- [ ] 实现客户识别分析脚本 +- [ ] 实现沟通摘要分析脚本 +- [ ] 搭建 React 前端骨架 +- [ ] 完成客户列表和客户详情页 + +### 第 3 周:成交录入 + 报表 + +- [ ] 成交录入表单 +- [ ] 成交列表页 +- [ ] 仪表盘基础数据展示 +- [ ] 聊天记录页面集成 + +### 第 4 周:联调 + 试点 + +- [ ] 3~5 名销售实际接入 +- [ ] 录入真实成交数据 +- [ ] 验证"聊天→客户→成交"关联准确性 +- [ ] 收集反馈,修复问题 + +--- + +## 十、成功标准 + +MVP 上线后需验证: + +1. **采集完整性**:每个销售每天的消息采集率 > 95% +2. **客户识别准确率**:AI 识别的客户中,> 80% 被销售确认是真实客户 +3. **成交关联可用**:手动录入的成交记录能正确关联到对应聊天记录 +4. **基础报表可用**:能看到每个销售的消息量、客户数、成交金额 +5. **闭环验证**:至少能从 1 个成交客户回溯完整沟通链路 + +--- + +## 十一、与完整方案的关系 + +MVP 是总体方案"第一阶段"的精简版,主要区别: + +| 维度 | MVP | 完整方案第一阶段 | +|---|---|---| +| 数据模型 | 6 张表 | 20+ 张表 | +| 成交数据 | 手动录入 | 接入 CRM/订单系统 | +| AI 分析 | 客户识别 + 沟通摘要 | 阶段识别 + 意向评分 + 异议分析 + 待办 | +| 权限 | 按 salesperson_id 简单隔离 | RBAC + 数据范围权限 | +| 同步 | 每日离线 | 每日 + 小时级 | +| 报表 | 基础统计 | 销售漏斗 + 能力雷达 + 话术分析 | +| 部署 | 单机 | 单机但面向多设备 | + +MVP 验证通过后,按完整方案路线图逐步扩展。 diff --git a/demo/0-req.md b/demo/0-req.md new file mode 100644 index 0000000..9950494 --- /dev/null +++ b/demo/0-req.md @@ -0,0 +1,399 @@ +# 微信营销管理系统 MVP 需求规格 + +> 文档日期:2026-07-14 +> 版本:v0.1 +> 状态:需求定义 + +--- + +## 一、项目背景 + +基于《微信营销管理系统总体设计与实施方案》和《MVP.md》,在本机构建一个可运行的演示系统,模拟"销售本地处理系统 + 中央库管理系统"的完整闭环。 + +MVP 核心命题:**把每个销售的微信聊天采集到中央数据库,AI 分析出客户和沟通记录,成交数据手动录入,形成"沟通→成交"的证据链。** + +--- + +## 二、系统边界 + +### 2.1 本期范围(In Scope) + +| 能力 | 说明 | +|---|---| +| 销售本地采集代理 | 模拟 3 个销售设备的采集代理,生成模拟微信数据并同步到中央库 | +| 中央数据库 | PostgreSQL 存储销售、联系人、会话、消息、客户、成交 6 张核心表 | +| 客户识别分析 | 从联系人中筛选真实客户,推断行业、意向等级、关键需求 | +| 沟通摘要分析 | 对每个客户会话生成结构化摘要(阶段、异议、下一步) | +| 成交数据录入 | Web 表单手动录入成交记录,关联到客户和聊天 | +| Web 管理后台 | 仪表盘、销售列表、客户列表、客户详情(含聊天记录)、成交录入 | +| 一键启动 | 单脚本启动数据库、模拟采集、AI 分析、Web 服务 | + +### 2.2 本期不做(Out of Scope) + +| 排除项 | 原因 | +|---|---| +| 真实微信数据库解密 | MVP 用模拟数据验证闭环,不依赖真实微信 | +| 培训对练系统 | 第二阶段 | +| AI 自动回复 | 第三阶段 | +| 多租户 | MVP 单租户 | +| 实时同步 | MVP 每日批量同步 | +| 复杂权限体系 | MVP 按 salesperson_id 简单隔离 | +| 向量化与语义检索 | MVP 不做向量搜索 | +| 对象存储 | MVP 不处理多媒体文件 | +| CRM/订单系统对接 | MVP 成交数据手动录入 | + +--- + +## 三、用户角色 + +| 角色 | 描述 | MVP 中的体现 | +|---|---|---| +| 销售人员 | 业务微信的使用者 | 系统自动模拟 3 名销售的数据采集 | +| 销售主管 | 查看团队数据和客户 | Web 后台的主要使用者 | +| 管理员 | 管理销售账号和系统配置 | 通过脚本或后台管理销售账号 | + +--- + +## 四、功能需求 + +### FR-1 销售账号管理 + +**描述**:系统维护销售人员列表,每个销售绑定一个微信号和设备标识。 + +**需求项**: +- FR-1.1 支持创建销售人员记录(姓名、团队、微信号、设备 ID) +- FR-1.2 系统预置 3 名销售用于演示 +- FR-1.3 每条数据都携带 `salesperson_id`,实现数据隔离 + +**验收标准**: +- 可通过 SQL 或 API 创建销售记录 +- 3 名预置销售数据正确写入数据库 + +--- + +### FR-2 本地采集代理(模拟) + +**描述**:模拟每台销售设备上的采集代理,生成模拟微信数据并同步到中央库。 + +**需求项**: +- FR-2.1 每个代理绑定一个 `salesperson_id` +- FR-2.2 生成模拟联系人(每名销售 15~30 个联系人,含 2~3 个群聊) +- FR-2.3 生成模拟会话(每名销售 10~20 个会话) +- FR-2.4 生成模拟消息(每名销售 200~500 条消息,覆盖文本/图片/语音/文件等类型) +- FR-2.5 消息内容包含预设的销售场景模板(询价、产品咨询、异议处理、成交等) +- FR-2.6 支持增量同步:多次运行只新增不重复的消息 +- FR-2.7 同步时记录 `last_synced_at` + +**验收标准**: +- 运行 `mock_sync.py --salesperson-id 1` 后,中央库中出现该销售的联系人、会话、消息 +- 重复运行不产生重复消息 +- 消息内容可读、类型分布合理 + +--- + +### FR-3 客户识别分析 + +**描述**:从联系人中识别出真实客户,排除非客户联系人。 + +**需求项**: +- FR-3.1 对每个有超过 5 条消息的联系人执行分析 +- FR-3.2 输出字段:`is_customer`、`customer_name`、`industry`、`intent_level`、`key_needs`、`reason` +- FR-3.3 `intent_level` 取值:high / medium / low / none +- FR-3.4 分析结果写入 `customer` 表 +- FR-3.5 支持两种分析模式: + - 规则模式(默认):基于关键词和消息频率的规则引擎 + - LLM 模式(可选):调用本地 Ollama 模型分析 +- FR-3.6 已分析的联系人不重复分析(除非强制 `--force`) + +**验收标准**: +- 运行分析后,`customer` 表中出现被识别的客户 +- 非客户联系人(如广告、纯社交)不被识别为客户 +- 规则模式无需外部依赖即可运行 + +--- + +### FR-4 沟通摘要分析 + +**描述**:对每个识别出的客户会话生成结构化摘要。 + +**需求项**: +- FR-4.1 对 `customer` 表中的每个客户,取其关联会话的全部消息 +- FR-4.2 输出字段:`summary`(文本摘要)、`stage`(销售阶段)、`objections`(异议列表)、`next_action`(下一步建议) +- FR-4.3 `stage` 取值:新线索 / 建立联系 / 需求发现 / 方案匹配 / 产品演示 / 报价 / 异议处理 / 决策推进 / 成交 / 未成交 +- FR-4.4 结果写入 `customer` 表的 `last_analysis` 时间戳和扩展字段 +- FR-4.5 支持规则模式和 LLM 模式 + +**验收标准**: +- 运行分析后,客户记录中出现摘要和阶段信息 +- 摘要内容与模拟对话场景一致 +- 阶段判断合理 + +--- + +### FR-5 成交数据录入 + +**描述**:通过 Web 表单手动录入成交记录。 + +**需求项**: +- FR-5.1 提供成交录入表单:选择销售 → 选择客户 → 填写产品名称、金额、成交日期 +- FR-5.2 可选填写备注 +- FR-5.3 成交记录通过 `contact_id` 自动关联到对应的微信会话 +- FR-5.4 支持查看成交列表,按销售、日期、产品筛选 +- FR-5.5 成交状态默认 `closed`,支持 `pending` 和 `refunded` + +**验收标准**: +- 通过 Web 表单成功录入一条成交记录 +- 成交列表正确展示已录入记录 +- 在客户详情页能看到关联的成交记录 + +--- + +### FR-6 Web 管理后台 + +**描述**:提供 Web 界面查看数据和管理成交录入。 + +#### FR-6.1 仪表盘 + +- 显示总消息数、活跃客户数、本月成交金额 +- 显示各销售的消息量对比 +- 显示最近同步时间 + +#### FR-6.2 销售列表 + +- 每个销售的消息量、客户数、成交金额 +- 最后同步时间 +- 同步状态 + +#### FR-6.3 客户列表 + +- 按销售筛选 +- 按意向等级筛选 +- 显示客户名称、行业、意向等级、最后沟通时间 +- 点击进入客户详情 + +#### FR-6.4 客户详情 + +- 客户基本信息(AI 识别结果:行业、意向、关键需求) +- AI 沟通摘要(阶段、异议、下一步建议) +- 完整聊天记录(时间线展示,支持分页) +- 关联的成交记录 + +#### FR-6.5 成交录入与列表 + +- 成交录入表单 +- 成交列表(按销售、日期、产品筛选) + +**验收标准**: +- 5 个页面均可正常访问 +- 数据正确展示 +- 客户详情页能看到聊天记录和成交记录 +- 成交录入后列表实时更新 + +--- + +### FR-7 一键启动与停止 + +**描述**:提供脚本一键启动和停止整个演示系统。 + +**需求项**: +- FR-7.1 `run_demo.sh` 完成以下步骤: + 1. 启动 PostgreSQL(端口 5434) + 2. 执行建库建表 + 3. 运行 3 个模拟采集代理 + 4. 运行 AI 分析(规则模式) + 5. 启动 FastAPI + Web 前端(端口 8770) + 6. 输出访问地址 +- FR-7.2 `stop_demo.sh` 停止所有服务 +- FR-7.3 启动脚本可重复运行(幂等) + +**验收标准**: +- 执行 `run_demo.sh` 后,浏览器访问 `http://127.0.0.1:8770` 能看到仪表盘 +- 执行 `stop_demo.sh` 后,所有进程停止 +- 重复运行不报错 + +--- + +## 五、数据模型 + +### 5.1 核心表(6 张) + +| 表名 | 说明 | 主键 | +|---|---|---| +| `salesperson` | 销售人员 | `id` (SERIAL) | +| `contact` | 联系人(微信好友) | `id` (SERIAL),唯一约束 `(salesperson_id, wx_username)` | +| `conversation` | 会话 | `id` (SERIAL) | +| `message` | 消息 | `id` (BIGSERIAL),唯一约束 `(source_shard, source_table, source_local_id)` | +| `customer` | 客户(AI 识别) | `id` (SERIAL),唯一约束 `(salesperson_id, contact_id)` | +| `deal` | 成交记录 | `id` (SERIAL) | + +### 5.2 关键字段补充 + +`customer` 表在 MVP.md 基础上增加分析摘要字段: + +```sql +-- 沟通摘要分析结果(FR-4) +summary TEXT, -- 文本摘要 +stage TEXT, -- 销售阶段 +objections TEXT[], -- 异议列表 +next_action TEXT, -- 下一步建议 +``` + +### 5.3 数据隔离 + +所有业务表均包含 `salesperson_id` 外键,查询时按销售过滤。 + +--- + +## 六、模拟数据规格 + +### 6.0 业务背景 + +**产品**:儿童益生菌粉(主打小儿抗过敏、调节免疫、改善肠道) + +**销售模式**:toC 微信私域销售,目标客户为宝妈/家长 + +**产品信息**: +- 产品名:益童宝儿童益生菌粉 +- 规格:30 袋/盒,每袋 2g +- 零售价:298 元/盒,3 盒套餐 798 元,6 盒套餐 1499 元 +- 核心卖点:丹麦进口菌株、抗过敏临床验证、0 岁以上可用、无敏配方 +- 适用症状:小儿湿疹、过敏性鼻炎、食物过敏、免疫力低下、腹泻/便秘 + +**目标客户画像**: +- 宝妈,25~40 岁 +- 孩子有过敏症状(湿疹、鼻炎、食物过敏) +- 关注成分安全,对"进口""临床验证"敏感 +- 价格敏感度中等,更关注效果和安全性 +- 决策链短(宝妈自己决定),但需要信任建立 + +### 6.1 销售人员(3 名) + +| ID | 姓名 | 团队 | 微信号 | 设备 ID | 人设 | +|---|---|---|---|---|---| +| 1 | 张伟 | 华东团队 | wxid_zhangwei | macbook-zw-001 | 资深销售,擅长建立信任,成交率高 | +| 2 | 李娜 | 华东团队 | wxid_lina | macbook-ln-002 | 新人销售,热情但经验不足,容易过早报价 | +| 3 | 王强 | 华南团队 | wxid_wangqiang | macbook-wq-003 | 中等水平,擅长跟进但异议处理较弱 | + +### 6.2 联系人类型分布(每名销售) + +| 类型 | 数量 | 说明 | +|---|---|---| +| 真实客户(宝妈) | 5~8 | 有过敏症状咨询、产品问价、成分讨论、成交等对话 | +| 潜在客户 | 3~5 | 群里加的好友,初步咨询过但未深入 | +| 非客户联系人 | 5~10 | 朋友圈点赞、代购广告、其他品牌推销、家人朋友 | +| 群聊 | 2~3 | 宝妈群、育儿交流群、过敏宝宝互助群 | + +### 6.3 消息场景模板 + +每个真实客户对应一个预设场景: + +| 场景 | 阶段 | 消息轮次 | 示例内容 | +|---|---|---|---| +| 湿疹宝宝咨询后成交 | 成交 | 15~25 | 宝妈说孩子湿疹反复 → 销售问年龄症状 → 介绍益生菌抗过敏原理 → 发临床报告截图 → 宝妈问价格 → 销售推荐3盒套餐 → 宝妈确认下单 | +| 过敏性鼻炎咨询后犹豫 | 异议处理 | 10~20 | 宝妈说孩子鼻炎 → 销售介绍产品 → 宝妈问有没有副作用 → 销售解释无敏配方 → 宝妈觉得价格贵 → 销售解释日均成本 → 宝妈说要和老公商量 | +| 朋友推荐来咨询 | 需求发现 | 8~15 | 宝妈说朋友推荐 → 问产品适不适合自己孩子 → 销售问症状 → 宝妈描述孩子情况 → 销售初步建议 | +| 长期跟进复购 | 建立联系 | 20~30 | 首次咨询未买 → 销售定期关心孩子情况 → 宝妈反馈吃了效果不错 → 销售推荐复购套餐 → 成交 | +| 对比竞品后选择 | 方案匹配 | 12~20 | 宝妈说在对比合生元 → 销售对比菌株和临床数据 → 宝妈问为什么贵 → 销售解释进口菌株差异 → 宝妈犹豫 | + +### 6.4 非客户联系人场景 + +| 类型 | 示例消息内容 | +|---|---| +| 代购广告 | "【韩国直邮】儿童维生素团购开始啦..." | +| 其他品牌推销 | "姐,我们新款益生菌做活动,比你现在用的便宜..." | +| 朋友圈互动 | "在吗?""你朋友圈那个是什么产品?" | +| 家人朋友 | "周末有空吗?""孩子上学怎么样了?" | +| 群聊通知 | "[群公告] 本群禁发广告..." / "有没有宝妈推荐好的儿童霜?" | + +### 6.5 消息类型分布 + +| 类型 | 占比 | 典型内容 | +|---|---|---| +| text | ~75% | 文字对话 | +| image | ~10% | 产品图、临床报告截图、宝宝湿疹照片、成分表 | +| voice | ~6% | 宝妈发语音描述孩子症状、销售语音解释 | +| file | ~2% | 产品手册 PDF、检测报告 | +| emoji | ~5% | 表情包互动 | +| link | ~2% | 产品详情页链接、科普文章 | + +--- + +## 七、接口规格 + +### 7.1 采集代理同步接口 + +采集代理直接写 PostgreSQL,不走 HTTP。MVP 阶段简化架构。 + +### 7.2 Web API(FastAPI) + +| 方法 | 路径 | 说明 | +|---|---|---| +| GET | `/api/dashboard` | 仪表盘统计数据 | +| GET | `/api/salespersons` | 销售列表 | +| GET | `/api/salespersons/{id}/stats` | 单个销售统计 | +| GET | `/api/customers` | 客户列表(支持 `salesperson_id`、`intent_level` 筛选) | +| GET | `/api/customers/{id}` | 客户详情(含摘要) | +| GET | `/api/customers/{id}/messages` | 客户聊天记录(分页) | +| GET | `/api/customers/{id}/deals` | 客户关联成交 | +| POST | `/api/deals` | 录入成交记录 | +| GET | `/api/deals` | 成交列表(支持筛选) | +| POST | `/api/analyze` | 触发 AI 分析(客户识别 + 沟通摘要) | +| GET | `/api/sync/status` | 同步状态 | + +--- + +## 八、非功能需求 + +| 维度 | 要求 | +|---|---| +| 运行环境 | macOS(本机) | +| Python | 3.11+ | +| PostgreSQL | 17(复用本机已安装版本) | +| 端口 | PostgreSQL 5434,Web 8770(避开现有 5433 和 8765) | +| 启动时间 | < 30 秒 | +| 模拟数据量 | 3 名销售,约 1000 条消息,可秒级生成 | +| 前端 | 单页 HTML + TailwindCSS CDN,无需构建 | +| 后端 | FastAPI,单文件即可 | +| 依赖 | psycopg2-binary, fastapi, uvicorn(3 个包) | + +--- + +## 九、技术约束 + +1. **不修改现有系统**:不改动 `/Users/freedak/Documents/Codex/2026-07-14/ni/` 下的任何文件 +2. **独立数据库**:使用独立数据库 `wxchat_sales`,不复用现有 `wechat_knowledge` +3. **独立端口**:PostgreSQL 5434,Web 8770,不与现有服务冲突 +4. **无外部依赖**:规则模式不依赖 Ollama 或任何外部 API +5. **可重复运行**:脚本幂等,重复运行不报错、不产生重复数据 + +--- + +## 十、验收标准汇总 + +| 编号 | 验收项 | 验证方式 | +|---|---|---| +| AC-1 | `run_demo.sh` 一键启动全部服务 | 执行脚本,观察输出 | +| AC-2 | 浏览器访问 `http://127.0.0.1:8770` 看到仪表盘 | 手动访问 | +| AC-3 | 仪表盘显示 3 名销售的数据 | 检查数据非零 | +| AC-4 | 客户列表显示 AI 识别的客户 | 检查列表非空 | +| AC-5 | 客户详情页显示聊天记录 | 检查消息时间线 | +| AC-6 | 客户详情页显示 AI 摘要 | 检查摘要、阶段、异议字段 | +| AC-7 | 成交录入表单可提交 | 填写并提交 | +| AC-8 | 成交列表显示已录入记录 | 检查列表 | +| AC-9 | 客户详情页显示关联成交 | 检查成交区域 | +| AC-10 | `stop_demo.sh` 停止所有服务 | 执行脚本,检查进程 | +| AC-11 | 重复运行 `run_demo.sh` 不报错 | 执行两次 | +| AC-12 | 模拟采集代理增量同步不产生重复 | 运行两次 mock_sync,检查消息数 | + +--- + +## 十一、后续演进 + +MVP 演示验证通过后的演进路径(不在本期范围): + +1. **接入真实微信数据**:将 mock_sync 替换为真实的数据库采集代理 +2. **接入 LLM 分析**:将规则引擎替换为 Ollama 本地模型 +3. **小时级同步**:从每日批量改为小时级增量 +4. **权限体系**:增加登录认证和 RBAC +5. **向量检索**:接入 pgvector 做语义搜索 +6. **培训对练**:按总体方案第二阶段实施 diff --git a/demo/1-design.md b/demo/1-design.md new file mode 100644 index 0000000..d0968e4 --- /dev/null +++ b/demo/1-design.md @@ -0,0 +1,476 @@ +# 微信营销管理系统 MVP 技术设计 + +> 文档日期:2026-07-14 +> 版本:v0.1 +> 依赖:0-req.md + +--- + +## 一、系统架构 + +### 1.1 部署拓扑 + +单机部署,所有组件运行在本机 macOS 上。 + +``` +┌──────────────────────────────────────────────────────────┐ +│ 本机 macOS │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ mock_sync │ │ mock_sync │ │ mock_sync │ │ +│ │ 销售A (id=1) │ │ 销售B (id=2) │ │ 销售C (id=3) │ │ +│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ +│ │ │ │ │ +│ └────────────────┼────────────────┘ │ +│ ▼ │ +│ ┌──────────────────────────────────────┐ │ +│ │ PostgreSQL 17 (端口 5434) │ │ +│ │ 数据库: wxchat_sales │ │ +│ │ socket: demo/db/socket │ │ +│ └──────────────────┬───────────────────┘ │ +│ │ │ +│ ┌──────────┼──────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌───────────┐ ┌─────────┐ ┌────────────┐ │ +│ │ analyze.py│ │ server │ │ index.html │ │ +│ │ AI分析 │ │ FastAPI │ │ 前端页面 │ │ +│ └───────────┘ └────┬────┘ └────────────┘ │ +│ │ │ +│ ▼ │ +│ http://127.0.0.1:8770 │ +└──────────────────────────────────────────────────────────┘ +``` + +### 1.2 端口分配 + +| 服务 | 端口 | 说明 | +|---|---|---| +| PostgreSQL | 5434 | 避开现有 5433 | +| FastAPI + 前端 | 8770 | 避开现有 8765 | + +### 1.3 目录结构 + +``` +wxsales/demo/ +├── 0-req.md # 需求规格 +├── 1-design.md # 本文档 +├── 2-impl.md # 实施计划 +├── 3-data-spec.md # 模拟数据规格 +├── README.md # 项目说明 +├── requirements.txt # Python 依赖 +├── run_demo.sh # 一键启动 +├── stop_demo.sh # 一键停止 +├── db/ +│ ├── schema.sql # 建表 SQL +│ ├── init_db.sh # 数据库初始化脚本 +│ ├── socket/ # Unix socket 目录 +│ └── data/ # PostgreSQL 数据目录(自动生成) +├── agent/ +│ └── mock_sync.py # 模拟采集代理 +├── ai/ +│ └── analyze.py # AI 分析服务(规则引擎 + 可选 LLM) +└── web/ + ├── server.py # FastAPI 后端 + └── static/ + └── index.html # 单页前端 +``` + +--- + +## 二、数据库设计 + +### 2.1 ER 关系 + +``` +salesperson 1───* contact 1───* conversation 1───* message + │ │ + │ 1───* customer *───1 + │ │ + │ 1───* deal *───1 +``` + +### 2.2 完整建表 SQL + +```sql +-- salesperson +CREATE TABLE salesperson ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + team TEXT, + wx_account TEXT NOT NULL, + device_id TEXT, + created_at TIMESTAMPTZ DEFAULT now() +); + +-- contact +CREATE TABLE contact ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + wx_username TEXT NOT NULL, + nickname TEXT, + remark TEXT, + display_name TEXT NOT NULL, + is_group BOOLEAN DEFAULT FALSE, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE(salesperson_id, wx_username) +); + +-- conversation +CREATE TABLE conversation ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + contact_id INT REFERENCES contact(id), + wx_identifier TEXT NOT NULL, + conv_type TEXT NOT NULL DEFAULT 'single', + last_synced_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE(salesperson_id, wx_identifier) +); + +-- message +CREATE TABLE message ( + id BIGSERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + conversation_id INT NOT NULL REFERENCES conversation(id), + 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) +); + +-- customer +CREATE TABLE customer ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + contact_id INT NOT NULL REFERENCES contact(id), + customer_name TEXT, + industry TEXT, + intent_level TEXT, + key_needs TEXT[], + reason TEXT, + summary TEXT, + stage TEXT, + objections TEXT[], + next_action TEXT, + last_analysis TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE(salesperson_id, contact_id) +); + +-- deal +CREATE TABLE deal ( + id SERIAL PRIMARY KEY, + salesperson_id INT NOT NULL REFERENCES salesperson(id), + customer_id INT REFERENCES customer(id), + contact_id INT REFERENCES contact(id), + 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 DEFAULT now() +); + +-- 索引 +CREATE INDEX idx_msg_conv_time ON message(conversation_id, created_at); +CREATE INDEX idx_msg_salesperson ON message(salesperson_id); +CREATE INDEX idx_customer_salesperson ON customer(salesperson_id); +CREATE INDEX idx_deal_salesperson ON deal(salesperson_id); +CREATE INDEX idx_deal_customer ON deal(customer_id); +``` + +### 2.3 与 MVP.md 的差异 + +- `conversation` 表增加 `wx_identifier` 字段作为唯一约束的一部分,因为同一销售可能有多个会话指向同一联系人 +- `customer` 表增加了 `summary`、`stage`、`objections`、`next_action` 字段,用于存储沟通摘要分析结果(FR-4) +- `message` 表的 `source_shard` 在模拟环境中用 `mock_` 格式 + +--- + +## 三、模拟采集代理设计 + +### 3.1 模块结构 + +``` +mock_sync.py +├── main() # 入口,解析参数 +├── generate_contacts() # 生成模拟联系人 +├── generate_conversations() # 生成模拟会话 +├── generate_messages() # 生成模拟消息(基于场景模板) +├── sync_to_central() # 写入中央 PostgreSQL +└── SCENARIOS # 预设销售场景模板 +``` + +### 3.2 场景模板结构 + +每个场景模板定义一段完整的客户对话: + +```python +{ + "name": "湿疹宝宝咨询后成交", + "stage": "成交", + "contact": { + "nickname": "辰辰妈妈", + "remark": "辰辰妈-湿疹-2岁", + "industry": "宝妈", + "intent_level": "high", + }, + "messages": [ + {"sender": "customer", "type": "text", "content": "你好,我家宝宝2岁,湿疹反复好几个月了,朋友推荐你这边益生菌"}, + {"sender": "sales", "type": "text", "content": "辰辰妈您好!宝宝湿疹确实让人心疼,请问现在湿疹主要在哪些部位?有用过什么药吗?"}, + {"sender": "customer", "type": "text", "content": "脸上和手臂都有,医生开了激素药膏,但停了就复发"}, + {"sender": "sales", "type": "text", "content": "理解,激素药膏只能暂时压制。益生菌是从肠道调节免疫,从根本上降低过敏反应。我们用的是丹麦进口的鼠李糖乳杆菌,有专门针对儿童湿疹的临床验证"}, + {"sender": "sales", "type": "image", "content": "[图片:临床验证报告截图]"}, + {"sender": "customer", "type": "text", "content": "这个是进口的?安全吗?2岁能吃吗?"}, + {"sender": "sales", "type": "text", "content": "是的,丹麦进口菌株,0岁以上就能用,无敏配方,不含牛奶蛋白和麸质。很多宝妈反馈坚持吃2-3个月湿疹明显好转"}, + {"sender": "customer", "type": "text", "content": "多少钱?怎么卖的?"}, + {"sender": "sales", "type": "text", "content": "单盒298元30袋,建议先吃3盒一个周期,3盒套餐798元算下来每天不到9块钱"}, + {"sender": "sales", "type": "image", "content": "[图片:产品包装图]"}, + {"sender": "customer", "type": "text", "content": "3盒798是吧,效果不好怎么办?"}, + {"sender": "sales", "type": "text", "content": "我们有售后指导,期间有任何问题随时找我。另外我发您几个同情况宝妈的反馈看看"}, + {"sender": "sales", "type": "link", "content": "[链接:宝妈真实反馈合集]"}, + {"sender": "customer", "type": "text", "content": "好的,那先来3盒试试"}, + {"sender": "sales", "type": "text", "content": "好的辰辰妈!3盒套餐798元,您方便现在付款吗?我这边给您安排发货"}, + ] +} +``` + +### 3.3 增量同步机制 + +- 每次运行使用递增的 `source_local_id` +- `UNIQUE(source_shard, source_table, source_local_id)` 保证不重复 +- `source_shard` = `mock_{salesperson_id}` +- `source_table` = `Msg_{conversation_wx_identifier_hash}` +- 消息时间从基准时间开始递增,每次运行追加新消息 + +### 3.4 非客户联系人模板 + +生成广告、社交等非客户联系人,消息内容为: +- 广告推送:"【XX商城】年中大促..." +- 社交寒暄:"在吗?""最近怎么样" +- 群聊通知:系统消息、转发内容 + +--- + +## 四、AI 分析服务设计 + +### 4.1 双模式架构 + +``` +analyze.py +├── analyze_customers() # 客户识别 +│ ├── rule_mode() # 规则引擎(默认) +│ └── llm_mode() # LLM 模式(可选) +├── analyze_summaries() # 沟通摘要 +│ ├── rule_mode() # 规则引擎(默认) +│ └── llm_mode() # LLM 模式(可选) +└── main() # 入口 +``` + +### 4.2 规则引擎 — 客户识别 + +**输入**:联系人 + 该联系人的全部消息 + +**规则**: +1. 消息数 ≤ 5 → `is_customer = false` +2. 关键词匹配(产品咨询类): + - 症状词:湿疹、过敏、鼻炎、腹泻、便秘、免疫力、体质、红疹 + - 产品词:益生菌、菌株、进口、配方、成分、丹麦、鼠李糖、无敏 + - 价格词:价格、多少钱、费用、报价、优惠、套餐、盒、周期 + - 成交词:买、下单、付款、发货、试试、来几盒、定了 + - 命中 ≥ 2 个关键词 → `is_customer = true` +3. 行业推断:toC 场景统一标记为"宝妈" +4. 意向等级: + - high:提到成交词 + 价格词 ≥ 1 + - medium:提到症状词 + 产品词,有价格讨论 + - low:只有一般咨询,无价格讨论 + - none:无业务关键词 + +### 4.3 规则引擎 — 沟通摘要 + +**输入**:客户会话的全部消息 + +**规则**: +1. 阶段判断: + - 成交:包含"定了""下单""付款""发货""来几盒""试试""买" + - 报价:包含"多少钱""价格""套餐""298""798""1499" + - 异议处理:包含"贵""考虑""商量""副作用""安全""效果""对比""合生元" + - 需求发现:包含"湿疹""过敏""鼻炎""症状""多大""几个月" + - 建立联系:消息少于 5 条且为初次沟通 +2. 异议提取:匹配"贵""太贵""考虑""商量""老公""副作用""安全吗""有没有效""没用过""合生元""对比" +3. 摘要生成:按时间顺序提取关键消息,拼接为摘要文本 +4. 下一步建议:根据阶段和最后一条消息内容推断 + +### 4.4 LLM 模式(可选) + +当 `--mode llm` 时,调用本地 Ollama: +- 模型:qwen2.5(或本机已有的模型) +- 输入:最近 50 条消息 +- 输出:JSON 结构化结果 +- 超时:30 秒/请求 +- 失败回退到规则模式 + +--- + +## 五、Web 后端设计 + +### 5.1 FastAPI 路由 + +```python +# 仪表盘 +GET /api/dashboard + → {total_messages, active_customers, monthly_deal_amount, salespersons: [...]} + +# 销售列表 +GET /api/salespersons + → [{id, name, team, wx_account, message_count, customer_count, deal_amount, last_synced_at}] + +# 客户列表 +GET /api/customers?salesperson_id=&intent_level= + → [{id, customer_name, industry, intent_level, last_analysis, contact_display_name}] + +# 客户详情 +GET /api/customers/{id} + → {id, customer_name, industry, intent_level, key_needs, summary, stage, objections, next_action, ...} + +# 客户聊天记录 +GET /api/customers/{id}/messages?page=1&page_size=50 + → [{id, sender_display_name, message_type, normalized_content, created_at}] + +# 客户成交 +GET /api/customers/{id}/deals + → [{id, product_name, amount, deal_date, status}] + +# 成交录入 +POST /api/deals + body: {salesperson_id, customer_id, product_name, amount, deal_date, notes?} + +# 成交列表 +GET /api/deals?salesperson_id=&start_date=&end_date= + → [{id, salesperson_name, customer_name, product_name, amount, deal_date, status}] + +# 触发分析 +POST /api/analyze + body: {mode: "rule"|"llm"} + → {customers_analyzed, summaries_generated} + +# 同步状态 +GET /api/sync/status + → [{salesperson_id, name, last_synced_at, message_count}] +``` + +### 5.2 静态文件托管 + +FastAPI 直接托管 `web/static/` 目录: +```python +app.mount("/", StaticFiles(directory="web/static", html=True)) +``` + +API 路由挂在 `/api/*` 前缀下,前端页面通过 fetch 调用。 + +--- + +## 六、Web 前端设计 + +### 6.1 技术选型 + +- 单页 HTML + TailwindCSS CDN +- 原生 JavaScript,无框架 +- Chart.js CDN(仪表盘图表) + +### 6.2 页面结构 + +``` +index.html +├──