Files
wxsales/demo/agent/mock_sync.py
T
selfrelease 0c12948fa4 feat: 新增纤益减肥益生菌全套示例数据
- mock_sync.py: 新增6个减肥场景模板(成交/异议/咨询/对比/复购) + 分配给李娜和王强
- analyze.py: 扩展关键词(减肥/减脂/B420/纤益/代餐等) + 行业识别支持年轻女性 + LLM prompt增加减肥产品背景
- run_demo.sh: 新增4条减肥益生菌预置成交记录(小鹿/阿狸/小鹿复购)
2026-07-15 11:56:20 +08:00

901 lines
41 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""模拟销售设备采集代理 — 生成模拟微信数据并同步到中央 PostgreSQL。
用法:
python mock_sync.py --salesperson-id 1
python mock_sync.py --salesperson-id 1 --dsn "host=127.0.0.1 port=5434 dbname=wxchat_sales"
"""
from __future__ import annotations
import argparse
import datetime as dt
import hashlib
import json
import pathlib
import random
import sys
import psycopg2
from psycopg2.extras import execute_values
TZ = dt.timezone(dt.timedelta(hours=8))
# ---------------------------------------------------------------------------
# 数据库连接
# ---------------------------------------------------------------------------
DEFAULT_SOCKET = str(pathlib.Path(__file__).resolve().parent.parent / "db" / "socket")
DEFAULT_DSN = f"host={DEFAULT_SOCKET} port=5434 dbname=wxchat_sales"
# ---------------------------------------------------------------------------
# 销售人员信息
# ---------------------------------------------------------------------------
SALESPERSONS = {
1: {"name": "张伟", "wx_account": "wxid_zhangwei", "device_id": "macbook-zw-001"},
2: {"name": "李娜", "wx_account": "wxid_lina", "device_id": "macbook-ln-002"},
3: {"name": "王强", "wx_account": "wxid_wangqiang", "device_id": "macbook-wq-003"},
}
# ---------------------------------------------------------------------------
# 场景模板 — 真实客户对话
# ---------------------------------------------------------------------------
SCENARIO_TEMPLATES = [
{
"name": "湿疹宝宝咨询后成交",
"stage": "成交",
"contact": {
"nickname": "辰辰妈妈",
"remark": "辰辰妈-湿疹-2岁",
"industry": "宝妈",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,我家宝宝2岁,湿疹反复好几个月了,朋友推荐你这边益生菌"),
("sales", "text", "辰辰妈您好!宝宝湿疹确实让人心疼,请问现在湿疹主要在哪些部位?有用过什么药吗?"),
("customer", "text", "脸上和手臂都有,医生开了激素药膏,但停了就复发"),
("sales", "text", "理解,激素药膏只能暂时压制。益生菌是从肠道调节免疫,从根本上降低过敏反应。我们用的是丹麦进口的鼠李糖乳杆菌,有专门针对儿童湿疹的临床验证"),
("sales", "image", "[图片:临床验证报告截图]"),
("customer", "text", "这个是进口的?安全吗?2岁能吃吗?"),
("sales", "text", "是的,丹麦进口菌株,0岁以上就能用,无敏配方,不含牛奶蛋白和麸质。很多宝妈反馈坚持吃2-3个月湿疹明显好转"),
("customer", "text", "多少钱?怎么卖的?"),
("sales", "text", "单盒298元30袋,建议先吃3盒一个周期,3盒套餐798元算下来每天不到9块钱"),
("sales", "image", "[图片:产品包装图]"),
("customer", "text", "3盒798是吧,效果不好怎么办?"),
("sales", "text", "我们有售后指导,期间有任何问题随时找我。另外我发您几个同情况宝妈的反馈看看"),
("sales", "link", "[链接:宝妈真实反馈合集]"),
("customer", "text", "好的,那先来3盒试试"),
("sales", "text", "好的辰辰妈!3盒套餐798元,您方便现在付款吗?我这边给您安排发货"),
("customer", "text", "已经转过去了,麻烦尽快发货"),
("sales", "text", "收到!今天就给您发顺丰,预计后天到"),
("customer", "emoji", "[表情:谢谢]"),
],
},
{
"name": "过敏性鼻炎咨询后犹豫",
"stage": "异议处理",
"contact": {
"nickname": "乐乐妈",
"remark": "乐乐妈-鼻炎-5岁",
"industry": "宝妈",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,听说益生菌对过敏性鼻炎有帮助?"),
("sales", "text", "乐乐妈您好!是的,益生菌可以调节肠道免疫,对过敏性鼻炎有改善作用。请问宝宝现在鼻炎什么症状?"),
("customer", "text", "早上起来一直打喷嚏流鼻涕,医生说是过敏性鼻炎"),
("sales", "text", "益生菌从肠道调节免疫系统,坚持吃可以逐渐降低过敏反应。我们的菌株是丹麦进口的,有临床验证"),
("customer", "text", "有没有副作用?安全吗?"),
("sales", "text", "很安全的,无敏配方,不含牛奶蛋白和麸质,5岁完全没问题"),
("customer", "text", "多少钱?"),
("sales", "text", "单盒2983盒套餐798"),
("customer", "text", "有点贵啊..."),
("sales", "text", "算下来每天不到9块钱,比吃药划算多了"),
("customer", "text", "我再想想,和老公商量一下"),
("sales", "text", "好的,不着急。您可以先看看这个科普文章"),
("sales", "link", "[链接:益生菌与儿童过敏科普]"),
("customer", "text", "好的,我看看"),
],
},
{
"name": "朋友推荐来咨询",
"stage": "需求发现",
"contact": {
"nickname": "果果妈妈",
"remark": "果果妈-推荐来的",
"industry": "宝妈",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,是小林推荐我加你的,她说你家益生菌不错"),
("sales", "text", "果果妈您好!谢谢小林推荐,请问宝宝多大了?有什么情况吗?"),
("customer", "text", "1岁半,最近总是拉肚子,小林说吃益生菌调理一下"),
("sales", "text", "拉肚子多久了?有去看过医生吗?"),
("customer", "text", "断断续续一个多月了,医生说没什么大问题,让注意饮食"),
("sales", "text", "那益生菌确实可以帮到宝宝。我们的菌株是丹麦进口的,专门针对儿童肠道和免疫调节。1岁半完全可以吃"),
("customer", "text", "成分安全吗?"),
("sales", "text", "无敏配方,不含牛奶蛋白、麸质和大豆,很多敏宝妈妈都在用"),
("customer", "text", "好的,我先了解一下"),
("sales", "text", "没问题,我给您发个产品详情,您先看看"),
("sales", "link", "[链接:产品详情页]"),
],
},
{
"name": "长期跟进后复购",
"stage": "成交",
"contact": {
"nickname": "糖糖妈妈",
"remark": "糖糖妈-复购-湿疹已好转",
"industry": "宝妈",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,我想了解一下益生菌"),
("sales", "text", "糖糖妈您好!请问宝宝什么情况?"),
("customer", "text", "3岁,湿疹,反反复复的"),
("sales", "text", "湿疹确实需要从内调理。我们的益生菌丹麦进口菌株,专门针对儿童过敏"),
("customer", "text", "多少钱?"),
("sales", "text", "单盒2983盒套餐798"),
("customer", "text", "先了解一下,回头再说"),
("sales", "text", "好的,不着急"),
("sales", "text", "糖糖妈,宝宝最近湿疹怎么样了?"),
("customer", "text", "还是老样子,断不了根"),
("sales", "text", "湿疹确实需要坚持调理,益生菌一般吃2-3个月能看到明显改善"),
("customer", "text", "我再考虑考虑"),
("sales", "text", "糖糖妈,今天有个宝妈反馈说吃了2个月湿疹好多了,发您看看"),
("sales", "image", "[图片:宝妈反馈截图]"),
("customer", "text", "真的吗?那我也试试吧"),
("sales", "text", "好的!建议先拿3盒一个周期,798元"),
("customer", "text", "好的,付款吧"),
("sales", "text", "好的!我马上安排发货"),
("sales", "text", "糖糖妈,宝宝吃了这段时间效果怎么样?"),
("customer", "text", "比之前好多了,湿疹没那么频繁了"),
("sales", "text", "太好了!建议继续吃巩固一下,现在有6盒套餐1499更划算"),
("customer", "text", "好,那来6盒"),
("sales", "text", "好的!马上安排"),
],
},
{
"name": "对比竞品后选择",
"stage": "方案匹配",
"contact": {
"nickname": "壮壮妈妈",
"remark": "壮壮妈-对比合生元",
"industry": "宝妈",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,我想给孩子买益生菌,但在对比你们和合生元"),
("sales", "text", "壮壮妈您好!对比是正常的。请问宝宝主要什么情况?"),
("customer", "text", "经常感冒,免疫力不太好"),
("sales", "text", "我们的菌株是丹麦进口的鼠李糖乳杆菌GG,有专门针对儿童免疫力的临床验证。合生元用的是国产菌株"),
("customer", "text", "但你们比合生元贵啊"),
("sales", "text", "菌株不一样,进口菌株的临床数据更充分,效果更有保障。算下来每天不到9块钱"),
("customer", "text", "有什么区别吗?"),
("sales", "image", "[图片:菌株对比表]"),
("sales", "text", "主要是菌株来源和临床验证数量不同。我们的LGG是全球研究最多的益生菌菌株之一"),
("customer", "text", "我再看看吧"),
("sales", "text", "好的,有问题随时问我"),
],
},
]
# ---------------------------------------------------------------------------
# 场景变体 — 给不同销售使用
# ---------------------------------------------------------------------------
SCENARIO_VARIANTS = [
{
"name": "食物过敏咨询后成交",
"stage": "成交",
"contact": {
"nickname": "依依妈",
"remark": "依依妈-食物过敏-3岁",
"industry": "宝妈",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,我家宝宝对牛奶蛋白过敏,听说益生菌能帮助脱敏?"),
("sales", "text", "依依妈您好!是的,益生菌可以帮助调节肠道免疫,对食物过敏有辅助改善作用。请问宝宝现在多大了?"),
("customer", "text", "3岁,查出来对牛奶蛋白和鸡蛋过敏"),
("sales", "text", "理解,食物过敏的宝宝确实需要从肠道调理。我们的菌株是丹麦进口LGG,有临床数据支持对食物过敏的改善"),
("customer", "text", "无敏配方吗?我家对牛奶蛋白过敏"),
("sales", "text", "是的,完全无敏配方,不含牛奶蛋白、麸质和大豆,过敏宝宝可以放心吃"),
("sales", "image", "[图片:成分表]"),
("customer", "text", "看着成分确实干净,多少钱?"),
("sales", "text", "单盒298,3盒套餐798,建议先吃一个周期"),
("customer", "text", "好,来3盒吧"),
("sales", "text", "好的依依妈!3盒798元,给您安排发货"),
("customer", "text", "付款了,麻烦尽快发"),
("sales", "text", "收到!今天就发顺丰"),
],
},
{
"name": "腹泻咨询后成交",
"stage": "成交",
"contact": {
"nickname": "阳阳妈",
"remark": "阳阳妈-腹泻-1岁",
"industry": "宝妈",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,宝宝1岁,最近老拉肚子,朋友说益生菌有用"),
("sales", "text", "阳阳妈您好!益生菌对宝宝腹泻确实有帮助。请问拉肚子多久了?"),
("customer", "text", "一个多星期了,吃了药好点,停了又拉"),
("sales", "text", "这种情况益生菌可以帮到宝宝调节肠道菌群。我们的丹麦进口菌株专门针对儿童肠道健康"),
("customer", "text", "1岁能吃吗?"),
("sales", "text", "可以的,0岁以上就能用,无敏配方很安全"),
("customer", "text", "多少钱?"),
("sales", "text", "单盒2983盒套餐798"),
("customer", "text", "先来3盒试试"),
("sales", "text", "好的!3盒798元,马上安排发货"),
("customer", "text", "好的,已付款"),
],
},
{
"name": "价格异议后放弃",
"stage": "异议处理",
"contact": {
"nickname": "可可妈妈",
"remark": "可可妈-价格异议",
"industry": "宝妈",
"intent_level": "low",
},
"messages": [
("customer", "text", "益生菌多少钱?"),
("sales", "text", "可可妈您好!单盒2983盒套餐798"),
("customer", "text", "这么贵?网上益生菌才几十块"),
("sales", "text", "我们的是丹麦进口菌株,有临床验证,和网上几十块的不一样"),
("customer", "text", "但是也太贵了"),
("sales", "text", "算下来每天不到9块钱,效果有保障"),
("customer", "text", "我再看看吧"),
("sales", "text", "好的,有需要随时找我"),
],
},
{
"name": "安全顾虑后犹豫",
"stage": "异议处理",
"contact": {
"nickname": "贝贝妈妈",
"remark": "贝贝妈-安全顾虑",
"industry": "宝妈",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,宝宝6个月,能吃益生菌吗?"),
("sales", "text", "贝贝妈您好!6个月以上的宝宝可以吃的。请问宝宝什么情况?"),
("customer", "text", "老是胀气,睡眠也不好"),
("sales", "text", "益生菌可以帮宝宝调节肠道,改善胀气。我们的丹麦进口菌株0岁以上可用"),
("customer", "text", "这么小的宝宝吃安全吗?有没有副作用?"),
("sales", "text", "很安全的,无敏配方,不含牛奶蛋白和麸质"),
("customer", "text", "我还是有点担心,毕竟宝宝才6个月"),
("sales", "text", "理解您的顾虑,很多宝妈也有同样的担心。其实益生菌是很成熟的品类,我们的菌株有大量临床数据支持"),
("customer", "text", "我再想想吧"),
("sales", "text", "好的,不着急。有问题随时问我"),
],
},
{
"name": "过早报价后被拒",
"stage": "异议处理",
"contact": {
"nickname": "欢欢妈妈",
"remark": "欢欢妈-过早报价",
"industry": "宝妈",
"intent_level": "low",
},
"messages": [
("customer", "text", "益生菌多少钱?"),
("sales", "text", "您好!单盒2983盒7986盒1499"),
("customer", "text", "哦,这么贵"),
("sales", "text", "丹麦进口菌株,效果很好的"),
("customer", "text", "我先看看吧"),
("sales", "text", "好的,需要随时联系"),
("customer", "text", ""),
],
},
]
# ---------------------------------------------------------------------------
# 减肥益生菌场景模板 — 纤益益生菌(女性减肥)
# ---------------------------------------------------------------------------
DIET_SCENARIOS = [
{
"name": "减肥咨询后成交",
"stage": "成交",
"contact": {
"nickname": "小鹿同学",
"remark": "小鹿-减肥-26岁",
"industry": "年轻女性",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,看到你朋友圈发的减肥益生菌,真的有用吗?"),
("sales", "text", "小鹿你好!是的,我们这款纤益益生菌含专利B420菌株,临床验证可以减少体脂率。请问你目前身高体重多少?"),
("customer", "text", "163cm125斤,想减到110"),
("sales", "text", "了解了,BMI偏高一丢丢。B420菌株是通过调节肠道菌群来促进脂肪代谢的,不需要节食,不腹泻,健康减脂"),
("customer", "text", "不用节食?那我感兴趣,之前试过节食反弹了"),
("sales", "text", "对,节食反弹是因为代谢没调好。益生菌是从根源改善肠道代谢,配合正常饮食就能看到效果"),
("sales", "image", "[图片:B420菌株临床数据]"),
("customer", "text", "看起来有科学依据,多少钱?"),
("sales", "text", "单盒328元30天用量,建议2盒一个周期656元,坚持2个月效果明显"),
("customer", "text", "2盒656是吧,有没有优惠?"),
("sales", "text", "现在新客户首单送一盒膳食纤维体验装,配合吃效果更好"),
("customer", "text", "好的,那来2盒"),
("sales", "text", "好的小鹿!纤益益生菌2盒套餐656元,送膳食纤维体验装,您方便现在付款吗?"),
("customer", "text", "付了,麻烦尽快发货"),
("sales", "text", "收到!今天就发顺丰,另外附上饮食建议手册"),
("customer", "emoji", "[表情:期待]"),
],
},
{
"name": "产后减肥咨询后犹豫",
"stage": "异议处理",
"contact": {
"nickname": "暖暖妈咪",
"remark": "暖暖妈-产后减肥-29岁",
"industry": "年轻女性",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,生完宝宝一直瘦不下来,益生菌能帮到吗?"),
("sales", "text", "暖暖妈你好!产后体重管理确实是个痛点。我们的B420菌株专门针对脂肪代谢,很多产后妈妈反馈不错"),
("customer", "text", "哺乳期可以吃吗?"),
("sales", "text", "建议断奶后再吃哦,哺乳期不太适合"),
("customer", "text", "那我家宝宝8个月还在喂奶"),
("sales", "text", "可以等断奶后再开始,现在先了解一下"),
("customer", "text", "多少钱?"),
("sales", "text", "单盒3282盒套餐656"),
("customer", "text", "有点贵,等断奶再说吧"),
("sales", "text", "没关系的,可以先关注我朋友圈,有活动会通知你"),
("customer", "text", "好的,谢谢"),
],
},
{
"name": "朋友推荐来咨询减肥",
"stage": "需求发现",
"contact": {
"nickname": "Cici",
"remark": "Cici-朋友推荐-减肥",
"industry": "年轻女性",
"intent_level": "medium",
},
"messages": [
("customer", "text", "你好,是小鹿推荐我加你的,她说你这边减肥益生菌不错"),
("sales", "text", "Cici你好!谢谢小鹿推荐,请问你想减多少?"),
("customer", "text", "想减10斤左右,主要是肚子和大腿"),
("sales", "text", "B420菌株正好针对腹部脂肪,通过调节肠道菌群促进代谢。小鹿吃了2个月减了8斤"),
("customer", "text", "真的假的?她那么快就有效果了?"),
("sales", "text", "个体差异有的,但B420有临床数据支持,平均12周减少体脂率3-5%"),
("sales", "image", "[图片:临床数据图表]"),
("customer", "text", "成分安全吗?有没有拉肚子之类的副作用?"),
("sales", "text", "完全安全,不腹泻不节食,是通过调节肠道菌群自然减脂"),
("customer", "text", "好的,我先了解一下"),
("sales", "text", "没问题,给你发个产品详情页看看"),
("sales", "link", "[链接:纤益益生菌详情页]"),
],
},
{
"name": "对比其他减肥产品后选择",
"stage": "成交",
"contact": {
"nickname": "阿狸",
"remark": "阿狸-对比代餐-24岁",
"industry": "年轻女性",
"intent_level": "high",
},
"messages": [
("customer", "text", "你好,我在对比你们益生菌和代餐奶昔,哪个效果好?"),
("sales", "text", "阿狸你好!代餐是控制热量摄入,益生菌是调节肠道代谢,原理不同。代餐停了容易反弹,益生菌是从根源改善"),
("customer", "text", "我之前吃过代餐,确实反弹了"),
("sales", "text", "对,代餐靠饿,益生菌靠调。B420菌株有临床验证,不节食不腹泻,健康减脂"),
("customer", "text", "那你们的价格呢?代餐一个月也要三四百"),
("sales", "text", "单盒328元一个月,2盒套餐656元两个月。比代餐便宜,而且不用挨饿"),
("customer", "text", "听着不错,但效果不好怎么办?"),
("sales", "text", "我们有售后指导,配合饮食建议手册一起用。小鹿2个月减了8斤,Cici也在吃"),
("sales", "link", "[链接:客户真实反馈]"),
("customer", "text", "好的,来2盒试试"),
("sales", "text", "好的阿狸!纤益益生菌2盒656元,今天就发货"),
("customer", "text", "已付款"),
("sales", "text", "收到!顺丰今天发,附饮食建议手册"),
],
},
{
"name": "价格异议后放弃",
"stage": "异议处理",
"contact": {
"nickname": "小丸子",
"remark": "小丸子-价格异议-减肥",
"industry": "年轻女性",
"intent_level": "low",
},
"messages": [
("customer", "text", "减肥益生菌多少钱?"),
("sales", "text", "小丸子你好!单盒3282盒套餐656"),
("customer", "text", "好贵,网上益生菌才几十块"),
("sales", "text", "我们含专利B420菌株,有临床验证减脂效果,和普通益生菌不一样"),
("customer", "text", "但也太贵了,算了吧"),
("sales", "text", "理解,可以关注朋友圈,有活动时会通知"),
("customer", "text", "好的"),
],
},
{
"name": "长期跟进后复购",
"stage": "成交",
"contact": {
"nickname": "小鹿同学",
"remark": "小鹿-复购-已减8斤",
"industry": "年轻女性",
"intent_level": "high",
},
"messages": [
("customer", "text", "吃了2个月瘦了8斤,想继续买"),
("sales", "text", "太棒了小鹿!B420效果确实不错,继续吃巩固一下"),
("customer", "text", "再买2盒"),
("sales", "text", "好的!老客户复购送一盒膳食纤维,2盒656元"),
("customer", "text", "好的,付款了"),
("sales", "text", "收到!今天就发货,继续加油"),
],
},
]
# ---------------------------------------------------------------------------
# 非客户联系人模板
# ---------------------------------------------------------------------------
NON_CUSTOMER_TEMPLATES = [
{
"nickname": "韩国代购-小美",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "【韩国直邮】儿童维生素团购开始啦,DHA+益生菌组合装只要199!"),
("other", "text", "需要的姐妹私我~"),
],
},
{
"nickname": "母婴用品批发",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "姐,我们新款益生菌做活动,比你现在用的便宜一半,要不要看看?"),
("other", "text", "保证正品,支持验货"),
("other", "emoji", "[表情:微笑]"),
],
},
{
"nickname": "朵朵阿姨",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "在吗?"),
("other", "text", "你朋友圈那个是什么产品?"),
("other", "text", "好的知道了"),
],
},
{
"nickname": "老婆 ❤️",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "今晚回来吃饭吗?"),
("other", "text", "孩子放学了"),
("other", "text", "周末去哪玩?"),
("other", "text", "好的"),
],
},
{
"nickname": "快递小哥",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "你的快递到了,放门口了"),
("other", "text", "签收一下"),
],
},
{
"nickname": "大学同学",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "好久不见啊"),
("other", "text", "最近怎么样"),
("other", "text", "改天聚聚"),
],
},
{
"nickname": "同行-小张",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "张哥,最近怎么样?"),
("other", "text", "你们那边转化率怎么样?"),
("other", "text", "交流一下经验"),
],
},
{
"nickname": "健身教练",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "明天还来训练吗?"),
("other", "text", "记得带毛巾"),
],
},
{
"nickname": "房产中介小王",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "哥,最近有考虑换房吗?"),
("other", "text", "新盘有优惠"),
],
},
{
"nickname": "表妹",
"remark": "",
"is_group": False,
"messages": [
("other", "text", "哥,你那边益生菌我朋友想了解一下"),
("other", "text", "我推她微信给你"),
("other", "text", "好的"),
],
},
]
# ---------------------------------------------------------------------------
# 群聊模板
# ---------------------------------------------------------------------------
GROUP_TEMPLATES = [
{
"nickname": "宝妈育儿交流群",
"is_group": True,
"messages": [
("other", "text", "[群公告] 本群禁发广告,违者移出"),
("other", "text", "有没有宝妈推荐好的儿童面霜?"),
("other", "text", "我家用的那个丝塔芙还不错"),
("other", "text", "谢谢推荐"),
("other", "text", "有没有宝妈推荐好的儿童霜?"),
("other", "text", "大家宝宝都几岁上幼儿园的?"),
("other", "text", "我家3岁送的"),
("other", "text", "这么早吗?会不会太小了"),
("other", "text", "还好,适应期过了就好了"),
],
},
{
"nickname": "过敏宝宝互助群",
"is_group": True,
"messages": [
("other", "text", "我家宝宝湿疹终于好了"),
("other", "text", "怎么好的?用的什么?"),
("other", "text", "吃了益生菌加上注意饮食"),
("other", "text", "你们都用的什么益生菌?"),
("other", "text", "益生菌真的有用吗?"),
("other", "text", "我觉得有用的,坚持吃了2个月"),
("other", "text", "我也想试试"),
("other", "text", "可以试试,从肠道调理确实有道理"),
("other", "text", "有没有副作用?"),
("other", "text", "我们吃的没什么副作用"),
("other", "text", "好的,谢谢分享"),
],
},
{
"nickname": "育儿干货分享群",
"is_group": True,
"messages": [
("other", "text", "今天分享一篇关于儿童免疫力的文章"),
("other", "link", "[链接:儿童免疫力科普]"),
("other", "text", "写得好,收藏了"),
("other", "text", "谢谢分享"),
("other", "text", "有没有关于过敏的科普?"),
("other", "text", "有的,我找找"),
],
},
]
# ---------------------------------------------------------------------------
# 每个销售的联系人分配方案
# ---------------------------------------------------------------------------
# 格式: (scenario_index_or_variant_name, contact_nickname, contact_remark)
# scenario_index 指向 SCENARIO_TEMPLATES, variant_name 指向 SCENARIO_VARIANTS
SALESPERSON_ASSIGNMENTS = {
1: [ # 张伟 — 资深(儿童益生菌为主)
("scenario_0", None, None), # 湿疹成交
("scenario_2", None, None), # 朋友推荐
("variant_食物过敏咨询后成交", None, None),
("variant_腹泻咨询后成交", None, None),
("scenario_3", None, None), # 长期跟进复购
# 潜在客户
("scenario_2", "小宇妈", "小宇妈-咨询"),
("scenario_2", "安安妈妈", "安安妈-咨询"),
# 非客户
("non_customer", "韩国代购-小美", ""),
("non_customer", "母婴用品批发", ""),
("non_customer", "朵朵阿姨", ""),
("non_customer", "老婆 ❤️", ""),
# 群聊
("group", "宝妈育儿交流群", ""),
("group", "过敏宝宝互助群", ""),
],
2: [ # 李娜 — 新人(减肥益生菌为主)
("scenario_1", None, None), # 鼻炎犹豫(儿童)
("variant_价格异议后放弃", None, None),
("scenario_4", None, None), # 竞品对比(儿童)
("variant_过早报价后被拒", None, None),
("scenario_2", "甜甜妈", "甜甜妈-推荐咨询"),
# 减肥益生菌场景
("diet_0", None, None), # 减肥咨询后成交(小鹿)
("diet_1", None, None), # 产后减肥犹豫(暖暖妈)
("diet_2", None, None), # 朋友推荐咨询减肥(Cici
# 潜在客户
("scenario_2", "球球妈妈", "球球妈-咨询"),
("scenario_2", "米米妈", "米米妈-咨询"),
# 非客户
("non_customer", "同行-小张", ""),
("non_customer", "快递小哥", ""),
("non_customer", "大学同学", ""),
# 群聊
("group", "宝妈育儿交流群", ""),
("group", "育儿干货分享群", ""),
],
3: [ # 王强 — 中等(两个产品线都做)
("scenario_4", None, None), # 竞品对比(儿童)
("scenario_3", None, None), # 长期跟进复购(儿童)
("variant_腹泻咨询后成交", None, None),
("variant_安全顾虑后犹豫", None, None),
("scenario_2", "星星妈", "星星妈-推荐咨询"),
# 减肥益生菌场景
("diet_3", None, None), # 对比代餐后成交(阿狸)
("diet_5", None, None), # 减肥复购(小鹿)
("diet_4", None, None), # 减肥价格异议(小丸子)
# 潜在客户
("scenario_2", "晨晨妈妈", "晨晨妈-咨询"),
("scenario_2", "露露妈", "露露妈-咨询"),
# 非客户
("non_customer", "健身教练", ""),
("non_customer", "房产中介小王", ""),
("non_customer", "表妹", ""),
# 群聊
("group", "过敏宝宝互助群", ""),
("group", "宝妈育儿交流群", ""),
],
}
# ---------------------------------------------------------------------------
# 生成与同步逻辑
# ---------------------------------------------------------------------------
def get_variant(name: str):
for v in SCENARIO_VARIANTS:
if v["name"] == name:
return v
raise KeyError(f"variant not found: {name}")
def generate_contacts_for_salesperson(sp_id: int):
"""返回 [(nickname, remark, is_group, wx_username, scenario_or_template)]"""
assignments = SALESPERSON_ASSIGNMENTS.get(sp_id, [])
result = []
for i, (kind, nick_override, remark_override) in enumerate(assignments):
if kind.startswith("scenario_"):
idx = int(kind.split("_")[1])
tpl = SCENARIO_TEMPLATES[idx]
nick = nick_override or tpl["contact"]["nickname"]
remark = remark_override or tpl["contact"].get("remark", "")
is_group = False
wx_username = f"{SALESPERSONS[sp_id]['wx_account']}_contact_{i+1}"
result.append((nick, remark, is_group, wx_username, tpl))
elif kind.startswith("variant_"):
tpl = get_variant(kind[8:])
nick = nick_override or tpl["contact"]["nickname"]
remark = remark_override or tpl["contact"].get("remark", "")
is_group = False
wx_username = f"{SALESPERSONS[sp_id]['wx_account']}_contact_{i+1}"
result.append((nick, remark, is_group, wx_username, tpl))
elif kind.startswith("diet_"):
idx = int(kind.split("_")[1])
tpl = DIET_SCENARIOS[idx]
nick = nick_override or tpl["contact"]["nickname"]
remark = remark_override or tpl["contact"].get("remark", "")
is_group = False
wx_username = f"{SALESPERSONS[sp_id]['wx_account']}_contact_{i+1}"
result.append((nick, remark, is_group, wx_username, tpl))
elif kind == "non_customer":
nick = nick_override
remark = remark_override or ""
# 找到对应模板
tpl = None
for t in NON_CUSTOMER_TEMPLATES:
if t["nickname"] == nick:
tpl = t
break
if tpl is None:
tpl = NON_CUSTOMER_TEMPLATES[i % len(NON_CUSTOMER_TEMPLATES)]
nick = tpl["nickname"]
is_group = False
wx_username = f"{SALESPERSONS[sp_id]['wx_account']}_contact_{i+1}"
result.append((nick, remark, is_group, wx_username, tpl))
elif kind == "group":
nick = nick_override
remark = remark_override or ""
tpl = None
for t in GROUP_TEMPLATES:
if t["nickname"] == nick:
tpl = t
break
if tpl is None:
tpl = GROUP_TEMPLATES[i % len(GROUP_TEMPLATES)]
nick = tpl["nickname"]
is_group = True
wx_username = f"{SALESPERSONS[sp_id]['wx_account']}_group_{i+1}@chatroom"
result.append((nick, remark, is_group, wx_username, tpl))
return result
def sync_to_central(pg, sp_id: int, contacts_data, run_counter: int = 1):
"""将联系人、会话、消息写入中央数据库。"""
sp_info = SALESPERSONS[sp_id]
source_shard = f"mock_{sp_id}"
base_time = dt.datetime(2026, 6, 15, 10, 0, 0, tzinfo=TZ)
# 每次运行追加的时间偏移
time_offset = dt.timedelta(days=run_counter * 7)
contact_ids = {}
conversation_ids = {}
with pg.cursor() as cur:
# 写入联系人
for nick, remark, is_group, wx_username, tpl in contacts_data:
display_name = remark or nick
cur.execute("""
INSERT INTO contact (salesperson_id, wx_username, nickname, remark, display_name, is_group)
VALUES (%s, %s, %s, %s, %s, %s)
ON CONFLICT (salesperson_id, wx_username) DO UPDATE SET
nickname=excluded.nickname, remark=excluded.remark,
display_name=excluded.display_name, is_group=excluded.is_group
RETURNING id
""", (sp_id, wx_username, nick, remark, display_name, is_group))
contact_ids[wx_username] = cur.fetchone()[0]
# 写入会话
for nick, remark, is_group, wx_username, tpl in contacts_data:
conv_type = "group" if is_group else "single"
cur.execute("""
INSERT INTO conversation (salesperson_id, contact_id, wx_identifier, conv_type, last_synced_at)
VALUES (%s, %s, %s, %s, now())
ON CONFLICT (salesperson_id, wx_identifier) DO UPDATE SET
contact_id=excluded.contact_id, last_synced_at=now()
RETURNING id
""", (sp_id, contact_ids[wx_username], wx_username, conv_type))
conversation_ids[wx_username] = cur.fetchone()[0]
# 写入消息
local_id_counter = (run_counter - 1) * 10000 # 每次运行从不同区间开始
msg_count = 0
batch = []
for nick, remark, is_group, wx_username, tpl in contacts_data:
conv_id = conversation_ids[wx_username]
contact_id = contact_ids[wx_username]
messages = tpl["messages"]
# 确定发送者
sp_wx = sp_info["wx_account"]
sp_name = sp_info["name"]
for j, (sender_role, msg_type, content) in enumerate(messages):
local_id_counter += 1
# 时间递增:每条消息间隔 2~30 分钟
msg_time = base_time + time_offset + dt.timedelta(minutes=j * random.randint(2, 30))
if sender_role == "customer":
sender_wx = wx_username if not is_group else f"wxid_customer_{j}"
sender_name = nick
elif sender_role == "sales":
sender_wx = sp_wx
sender_name = sp_name
else: # other
sender_wx = f"wxid_other_{j}" if is_group else wx_username
sender_name = nick
source_table = "Msg_" + hashlib.md5(wx_username.encode()).hexdigest()[:16]
batch.append((
sp_id, conv_id, sender_wx, sender_name, msg_type,
content, content, msg_time,
source_shard, source_table, local_id_counter,
))
msg_count += 1
if len(batch) >= 2000:
execute_values(cur, """
INSERT INTO message (salesperson_id, conversation_id, sender_wx_username,
sender_display_name, message_type, raw_content, normalized_content,
created_at, source_shard, source_table, source_local_id)
VALUES %s ON CONFLICT (source_shard, source_table, source_local_id) DO NOTHING
""", batch, page_size=2000)
pg.commit()
batch = []
if batch:
execute_values(cur, """
INSERT INTO message (salesperson_id, conversation_id, sender_wx_username,
sender_display_name, message_type, raw_content, normalized_content,
created_at, source_shard, source_table, source_local_id)
VALUES %s ON CONFLICT (source_shard, source_table, source_local_id) DO NOTHING
""", batch, page_size=2000)
pg.commit()
return len(contact_ids), msg_count
def get_run_counter(pg, sp_id: int) -> int:
"""根据已有消息数推断运行次数。"""
with pg.cursor() as cur:
cur.execute("""
SELECT count(DISTINCT source_local_id / 10000) FROM message
WHERE salesperson_id = %s AND source_shard = %s
""", (sp_id, f"mock_{sp_id}"))
result = cur.fetchone()[0]
return int(result or 0) + 1
def main():
ap = argparse.ArgumentParser(description="模拟采集代理")
ap.add_argument("--salesperson-id", type=int, required=True)
ap.add_argument("--dsn", default=DEFAULT_DSN)
args = ap.parse_args()
sp_id = args.salesperson_id
if sp_id not in SALESPERSONS:
print(f"[ERROR] 未知销售 ID: {sp_id}", file=sys.stderr)
return 1
sp_info = SALESPERSONS[sp_id]
print(f"[INFO] 模拟采集代理启动: {sp_info['name']} (id={sp_id})")
pg = psycopg2.connect(args.dsn)
pg.autocommit = False
try:
run_counter = get_run_counter(pg, sp_id)
print(f"[INFO] 第 {run_counter} 次同步")
contacts_data = generate_contacts_for_salesperson(sp_id)
print(f"[INFO] 生成 {len(contacts_data)} 个联系人")
contact_count, msg_count = sync_to_central(pg, sp_id, contacts_data, run_counter)
with pg.cursor() as cur:
cur.execute("SELECT count(*) FROM message WHERE salesperson_id=%s", (sp_id,))
total_msgs = cur.fetchone()[0]
print(json.dumps({
"salesperson_id": sp_id,
"salesperson_name": sp_info["name"],
"run": run_counter,
"contacts": contact_count,
"new_messages": msg_count,
"total_messages": total_msgs,
}, ensure_ascii=False, indent=2))
return 0
except Exception as exc:
pg.rollback()
print(f"[ERROR] {exc}", file=sys.stderr)
return 1
finally:
pg.close()
if __name__ == "__main__":
sys.exit(main())