Compare commits
2 Commits
5bd587318a
...
68241dc78a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68241dc78a | ||
|
|
c1ef7c31b0 |
@ -639,3 +639,12 @@ To https://git.valavala.com/ai_member_only/ai_member_xiaoban
|
||||
3685359..9d6999a master -> master
|
||||
[2026-06-18 08:10:03] 工作区备份成功:自动备份 2026-06-18 08:10:01
|
||||
[2026-06-19 08:10:01] 开始备份工作区...
|
||||
[master 5bd5873] 自动备份 2026-06-19 08:10:01
|
||||
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||
delete mode 100644 tmp_daily_summary.md
|
||||
remote: . Processing 1 references
|
||||
remote: Processed 1 references in total
|
||||
To https://git.valavala.com/ai_member_only/ai_member_xiaoban
|
||||
9d6999a..5bd5873 master -> master
|
||||
[2026-06-19 08:10:02] 工作区备份成功:自动备份 2026-06-19 08:10:01
|
||||
[2026-06-20 08:10:01] 开始备份工作区...
|
||||
|
||||
@ -104,3 +104,4 @@
|
||||
{"type":"memory.recall.recorded","timestamp":"2026-06-18T04:44:32.687Z","query":"陈逸鸫审批 S2U16 数据导出 王璐辰","resultCount":3,"results":[{"path":"memory/2026-06-17.md","startLine":1,"endLine":23,"score":1},{"path":"memory/2026-05-24.md","startLine":46,"endLine":71,"score":1},{"path":"memory/2026-05-24.md","startLine":66,"endLine":92,"score":1}]}
|
||||
{"type":"memory.recall.recorded","timestamp":"2026-06-18T10:17:31.215Z","query":"知识巩固做题记录 数据表","resultCount":3,"results":[{"path":"memory/2026-05-24.md","startLine":46,"endLine":71,"score":1},{"path":"memory/2026-05-24.md","startLine":85,"endLine":110,"score":1},{"path":"memory/2026-05-24.md","startLine":66,"endLine":92,"score":1}]}
|
||||
{"type":"memory.recall.recorded","timestamp":"2026-06-18T10:17:37.951Z","query":"知识巩固 表名 vala_game 做题记录 knowledge_consolidation","resultCount":4,"results":[{"path":"memory/2026-05-24.md","startLine":46,"endLine":71,"score":1},{"path":"memory/2026-05-24.md","startLine":85,"endLine":110,"score":1},{"path":"memory/2026-05-24.md","startLine":106,"endLine":126,"score":1},{"path":"memory/2026-06-17.md","startLine":20,"endLine":30,"score":1}]}
|
||||
{"type":"memory.recall.recorded","timestamp":"2026-06-19T12:45:15.278Z","query":"damai v2 fill 细水 CP7BsOjYdhtcmft5iz2csIaHnKe","resultCount":4,"results":[{"path":"memory/2026-06-19.md","startLine":1,"endLine":12,"score":1},{"path":"memory/2026-06-16.md","startLine":28,"endLine":49,"score":1},{"path":"memory/2026-05-28.md","startLine":684,"endLine":709,"score":1},{"path":"memory/2026-06-17.md","startLine":20,"endLine":30,"score":1}]}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"updatedAt": "2026-06-18T10:17:37.951Z",
|
||||
"updatedAt": "2026-06-19T12:45:15.278Z",
|
||||
"entries": {
|
||||
"memory:memory/2026-05-24.md:1:30": {
|
||||
"key": "memory:memory/2026-05-24.md:1:30",
|
||||
@ -2375,19 +2375,21 @@
|
||||
"endLine": 30,
|
||||
"source": "memory",
|
||||
"snippet": "- 发现核心 bug:`pick_valid_order()` 只取最新一笔有效订单,未做 GSV 聚合 - **根因:** 18621578529(account_id=13147)有 2 笔有效订单(P785... GSV=1,999 + 3734... GSV=1,599),汇总只收了最新那笔 1,599,丢了 1,999 - **影响范围:** 13 个账户各 2 笔有效订单,漏 GSV ¥23,185 - **修复(v2.1):** - 新增 `aggregate_valid_orders()` 函数:同一账户多笔有效订单 GSV/GMV/退款累加 - 订单号取未退款那笔(多笔未退款取最新),产品列多单用 `+` 拼接 - Step 4 线索绑单仍用 `pick_valid_order()` 不变 - 已更新 `sales_leads_full_refresh.py` + `skills/full-data-refresh/SKILL.md` - **数据源确认:** 大麦和小溪查的是同一个 PG `vala_bi.bi_vala_order`,数据一致 - **MySQL Makee 交易系统:** 大麦有 read_only 权限但 vala/vala_user/vala_order 库中无订单表,无法直接对比交易系统后台",
|
||||
"recallCount": 2,
|
||||
"recallCount": 3,
|
||||
"dailyCount": 0,
|
||||
"groundedCount": 0,
|
||||
"totalScore": 2,
|
||||
"totalScore": 3,
|
||||
"maxScore": 1,
|
||||
"firstRecalledAt": "2026-06-18T00:09:26.923Z",
|
||||
"lastRecalledAt": "2026-06-18T10:17:37.951Z",
|
||||
"lastRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"queryHashes": [
|
||||
"ee468f64688e",
|
||||
"d83437ba9cad"
|
||||
"d83437ba9cad",
|
||||
"290e7f4aaa74"
|
||||
],
|
||||
"recallDays": [
|
||||
"2026-06-18"
|
||||
"2026-06-18",
|
||||
"2026-06-19"
|
||||
],
|
||||
"conceptTags": [
|
||||
"pick-valid-order",
|
||||
@ -2430,6 +2432,99 @@
|
||||
"数据",
|
||||
"查询"
|
||||
]
|
||||
},
|
||||
"memory:memory/2026-06-19.md:1:12": {
|
||||
"key": "memory:memory/2026-06-19.md:1:12",
|
||||
"path": "memory/2026-06-19.md",
|
||||
"startLine": 1,
|
||||
"endLine": 12,
|
||||
"source": "memory",
|
||||
"snippet": "# 2026-06-19 ## v2_fill 首次跑(陈逸鸫 18:06) - 表: CP7BsOjYdhtcmft5iz2csIaHnKe(细水新架构版) - 脚本: `scripts/damai_v2_fill.py` - ① 线索明细 (7fdb4b): F列95个手机号 → XXTEA加密 → PG匹配 → K列UID 63个(新增12个) - ② 订单明细 (vrYbiX): DB查询63个UID → 17行订单 → 写入A-P(Q/R未填) - db_info: 2026-06-19 18:21:44 - 使用实习虾 app (cli_aa898f32d4799bea) 的 tenant token 绕过 xiaoban bot 的 sheets:spreadsheet:read 权限缺失 - 注意: clear_range 用空字符串覆盖而非空数组(飞书API拒绝空数组)",
|
||||
"recallCount": 1,
|
||||
"dailyCount": 0,
|
||||
"groundedCount": 0,
|
||||
"totalScore": 1,
|
||||
"maxScore": 1,
|
||||
"firstRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"lastRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"queryHashes": [
|
||||
"290e7f4aaa74"
|
||||
],
|
||||
"recallDays": [
|
||||
"2026-06-19"
|
||||
],
|
||||
"conceptTags": [
|
||||
"v2-fill",
|
||||
"scripts/damai-v2-fill.py",
|
||||
"写入a-p",
|
||||
"q/r未填",
|
||||
"db-info",
|
||||
"cli-aa898f32d4799bea",
|
||||
"clear-range",
|
||||
"首次"
|
||||
]
|
||||
},
|
||||
"memory:memory/2026-06-16.md:28:49": {
|
||||
"key": "memory:memory/2026-06-16.md:28:49",
|
||||
"path": "memory/2026-06-16.md",
|
||||
"startLine": 28,
|
||||
"endLine": 49,
|
||||
"source": "memory",
|
||||
"snippet": "- **大麦**: full_refresh · 手机/UID/行课回填 · 订单汇总 merge · 完成后群回「full_refresh 完成」 - **小溪**: 不再参与 Bot 刷新 ### 5. 验收标准 - gate X = 汇总 W(当前 406=406) - 绑单审计 E1–E9 全部 0 - 孤儿 X = 0 ### 6. 脚本修改清单 - `bot_sales_step2_refresh.py`: DB 层改为逐单存储 + `pick_valid_order()` + Y≠1 不写 X - `sales_leads_full_refresh.py`: 同上 + 汇总改为 gate 全量重建 - `refresh_order_summary.py`: A-W(23列) + 渠道分类改用 L 列 - 新增 `audit_lead_primary_order_bind.py`: 线索绑单审计脚本 ### 7. 环境修复 - `secrets.env` 需要软链接: `ln -sf /root/.openclaw/workspace/secrets.env /root/.openclaw/workspace-xiaoban/secrets.env` ### 8. Skill 文档已更新 - `skills/full-data-refresh/SKILL.md` → v2 定稿,含 6 条核心架构规则 - 协作契约: `xhs-ark-dashboard/docs/bot-full-refresh-v2.md` - 大麦侧主文档: `xhs-ark-dash",
|
||||
"recallCount": 1,
|
||||
"dailyCount": 0,
|
||||
"groundedCount": 0,
|
||||
"totalScore": 1,
|
||||
"maxScore": 1,
|
||||
"firstRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"lastRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"queryHashes": [
|
||||
"290e7f4aaa74"
|
||||
],
|
||||
"recallDays": [
|
||||
"2026-06-19"
|
||||
],
|
||||
"conceptTags": [
|
||||
"full-refresh",
|
||||
"手机/uid/行课回填",
|
||||
"bot-sales-step2-refresh.py",
|
||||
"pick-valid-order",
|
||||
"sales-leads-full-refresh.py",
|
||||
"refresh-order-summary.py",
|
||||
"a-w",
|
||||
"audit-lead-primary-order-bind.py"
|
||||
]
|
||||
},
|
||||
"memory:memory/2026-05-28.md:684:709": {
|
||||
"key": "memory:memory/2026-05-28.md:684:709",
|
||||
"path": "memory/2026-05-28.md",
|
||||
"startLine": 684,
|
||||
"endLine": 709,
|
||||
"source": "memory",
|
||||
"snippet": "### ~21:55 lark-cli `+write` bug 修复 **根因:** `bin/lark-cli-impl` 的 `+write` action 从未读取 `--value-input-option` 参数,PUT URL 里也没加 `?valueInputOption=USER_ENTERED`。参数被完全忽略。 **修复:** ```python vio = pargs.get(\"value_input_option\", \"RAW\") url_params = f\"?valueInputOption={vio}\" result = api(\"PUT\", f\"{SHEETS_V2}/spreadsheets/{token}/values{url_params}\", ...) ``` 同理 `+append` 也加了参数支持。 **验证:** 修复后写 int 到全新单元格 → 飞书正确显示为数字 ✅。但 Row 18 旧单元格 TEXT 格式仍覆盖显示。 ### ~22:00 飞书 Sheets API 公式求值测试 **结论:公式无法通过 API 写入并求值。** 即使 `valueInputOption=USER_ENTERED` 修复后,在全新单元格写 `=1+2` → FormattedValue 显示文本 \"=1+2\",不计算为 3。 **飞书 Sheets API v2 的 USER_ENTERED 的作用:** 数字/日期解析正常 → 公式不支持求值。 **陈逸鸫提到 Cursor 可以写公式** — 待确认他用的是什么工具/AP",
|
||||
"recallCount": 1,
|
||||
"dailyCount": 0,
|
||||
"groundedCount": 0,
|
||||
"totalScore": 1,
|
||||
"maxScore": 1,
|
||||
"firstRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"lastRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||
"queryHashes": [
|
||||
"290e7f4aaa74"
|
||||
],
|
||||
"recallDays": [
|
||||
"2026-06-19"
|
||||
],
|
||||
"conceptTags": [
|
||||
"lark-cli",
|
||||
"bin/lark-cli-impl",
|
||||
"value-input-option",
|
||||
"user-entered",
|
||||
"pargs.get",
|
||||
"url-params",
|
||||
"sheets-v2",
|
||||
"数字/日期解析正常"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
memory/2026-06-19.md
Normal file
11
memory/2026-06-19.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 2026-06-19
|
||||
|
||||
## v2_fill 首次跑(陈逸鸫 18:06)
|
||||
|
||||
- 表: CP7BsOjYdhtcmft5iz2csIaHnKe(细水新架构版)
|
||||
- 脚本: `scripts/damai_v2_fill.py`
|
||||
- ① 线索明细 (7fdb4b): F列95个手机号 → XXTEA加密 → PG匹配 → K列UID 63个(新增12个)
|
||||
- ② 订单明细 (vrYbiX): DB查询63个UID → 17行订单 → 写入A-P(Q/R未填)
|
||||
- db_info: 2026-06-19 18:21:44
|
||||
- 使用实习虾 app (cli_aa898f32d4799bea) 的 tenant token 绕过 xiaoban bot 的 sheets:spreadsheet:read 权限缺失
|
||||
- 注意: clear_range 用空字符串覆盖而非空数组(飞书API拒绝空数组)
|
||||
71
output/lingxi_short_1.md
Normal file
71
output/lingxi_short_1.md
Normal file
@ -0,0 +1,71 @@
|
||||
## 灵犀第一篇(改)
|
||||
|
||||
**「灵犀不是聚光的'数据看板'——我以前根本懒得打开它。直到用它改了一组计划,ROI从0.8拉到1.6。」**
|
||||
|
||||
以前我的后台日常就是聚光。看消耗、看线索、看CPL。灵犀?偶尔点进去扫一眼,关掉。心想这不就是个花里胡哨的数据看板吗,小红书真能整活。
|
||||
|
||||
我的逻辑很简单:我投的是硬广,我出价,平台给我量。灵犀里那些A啊I啊TI啊,跟我有什么关系?
|
||||
|
||||
直到今年发现出价卷不动了。
|
||||
|
||||
同样的赛道,竞品越来越多,出价越来越高。以前出50块能拿到的线索,现在出80块都不一定跑得动。ROI从勉强打平变成了越投越亏。
|
||||
|
||||
这时候我被逼着打开了灵犀,认真看了一次数据。然后做了一件事:把30%的预算从信息流搬到了视频流。
|
||||
|
||||
为什么?因为灵犀告诉我,信息流的转化率只有0.12%,视频流是0.47%——差了将近4倍。但我当时70%的预算都在信息流上。
|
||||
|
||||
搬完第二个月,线索量没变,消耗降了18%。ROI从0.8拉到了1.6。
|
||||
|
||||
没加一分钱预算,只是看了灵犀一眼。
|
||||
|
||||
聚光告诉你的是"花了多少钱、拿回来多少线索"。这是结果。灵犀告诉你的是"花出去的钱,在哪个口子上效率最高"。这是过程。
|
||||
|
||||
举一个更具体的例子。你投了一个达人,聚光告诉你这条笔记触达了50万人、带来了200条线索。看起来还行对吧?
|
||||
|
||||
但灵犀会告诉你:这50万人里,只有3万进了I层,8000进了TI层,最后200人成交。剩下47万人只是"刷到过",然后就没了。
|
||||
|
||||
这条笔记到底是在种草还是在烧钱?聚光回答不了,灵犀能。
|
||||
|
||||
我以前不看灵犀是因为出价能解决问题。出价50不行就出80,总能砸出线索。但现在出价卷到天花板了,再往上加就是亏。这时候拼的不是谁出价高,是谁能把每一块钱花在效率最高的口子上。
|
||||
|
||||
这个答案,聚光没有,灵犀有。
|
||||
|
||||
---
|
||||
|
||||
## 灵犀第二篇(改)
|
||||
|
||||
**「灵犀里有个被大多数人忽略的数字,它决定了你下个月的线索量。我抓准了之后,线索成本降了30%。」**
|
||||
|
||||
灵犀后台有一个数字叫"TI净增"——就是你这个月深度兴趣人群比上个月多了还是少了。
|
||||
|
||||
我以前从来不看这个数字。理由还是那个:我投硬广,我出价,人群自然会进来。TI池子大不大,跟我有什么关系?我又不靠它吃饭。
|
||||
|
||||
直到出价卷不动了,我才被迫回头看这个数字。
|
||||
|
||||
一看,是负的。负了一万多。
|
||||
|
||||
当时没太在意,心想线索不是还在出吗,怕什么。
|
||||
|
||||
第二个月,线索量掉了20%。
|
||||
|
||||
第三个月,又掉了15%。
|
||||
|
||||
我这才慌了,回去翻数据,发现TI净增已经连续三个月在掉了。而我的线索,70%以上来自TI人群。TI池子在缩水,线索怎么可能不降?
|
||||
|
||||
这就像一个水库。你每个月从里面抽水(收割线索),但上游进水(种草进TI)越来越少。水位一直在降,你还觉得"反正还能抽到水"——直到有一天抽不上来了。
|
||||
|
||||
进水从哪来?灵犀也告诉你了:新增直接进TI、A层流转进TI、I层流转进TI,三条管子。
|
||||
|
||||
我拆开一看,91%的进水靠的是"新增直接进TI"——就是不断买新流量硬种。但新增流量越来越贵、越来越少。而效率最高的那根管子——"已经有点兴趣的人(I)再推一把进TI",成功率是A的1.8倍,却只贡献了不到2%的进水。
|
||||
|
||||
最省力的管子,开得最小。
|
||||
|
||||
发现问题之后我做了一件事:从收割预算里挪了20%出来,专门投种草计划,把I层的人往TI推。
|
||||
|
||||
第二个月,TI净增转正了。线索成本降了30%。
|
||||
|
||||
没加总预算,只是把钱花在了"养池子"上。
|
||||
|
||||
以前我不在乎这个,因为出价能补。出价50不行就出80,总能抽到水。但现在出价卷到天花板了,再往上加就是亏。这时候你才发现,水库水位才是命门。
|
||||
|
||||
现在我每周固定看一次灵犀的TI净增。不为别的,就为知道下个月的线索会不会崩。
|
||||
463
scripts/damai_v2_fill.py
Normal file
463
scripts/damai_v2_fill.py
Normal file
@ -0,0 +1,463 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
大麦 v2_fill — 细水新架构版 线索UID + 订单全量回填
|
||||
|
||||
表: CP7BsOjYdhtcmft5iz2csIaHnKe
|
||||
① 线索明细 (7fdb4b): F列手机号 → XXTEA加密 → PG tel_encrypt精确匹配 → K列UID
|
||||
② 订单明细 (vrYbiX): clear后全量写 A-P (勿填 Q/R)
|
||||
· G 挂主进线 · B=线索C主归属 · N≠重复*
|
||||
|
||||
用法:
|
||||
python3 scripts/damai_v2_fill.py
|
||||
|
||||
契约: docs/damai-v2-fill-skill.md (与 xiaoxi 版同契约)
|
||||
"""
|
||||
|
||||
import json, re, time, sys, os, requests, psycopg2
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
WORKSPACE = os.path.dirname(SCRIPTS_DIR)
|
||||
|
||||
sys.path.insert(0, SCRIPTS_DIR)
|
||||
from phone_encrypt import encrypt_phone
|
||||
|
||||
# ── 配置 ──
|
||||
SPREADSHEET_TOKEN = "CP7BsOjYdhtcmft5iz2csIaHnKe"
|
||||
LEADS_SHEET_ID = "7fdb4b" # 线索明细
|
||||
ORDERS_SHEET_ID = "vrYbiX" # 订单明细
|
||||
|
||||
# 实习虾 app 凭证 (有 sheets 读写权限)
|
||||
FS_APP_ID = "cli_aa898f32d4799bea"
|
||||
FS_APP_SECRET = "wGBjexDxHsHBsx9lk8O2gdbkMFzaJ3kd"
|
||||
|
||||
# 渠道映射
|
||||
CHANNEL_MAP = {
|
||||
"Apple App Store": "苹果", "科大讯飞学习机": "讯飞", "学而思学习机": "学而思",
|
||||
"华为应用市场": "华为", "小米应用市场": "小米", "应用宝应用市场": "应用宝",
|
||||
"希沃学习机": "希沃", "荣耀应用市场": "荣耀", "小度学习机": "小度",
|
||||
"oppo应用市场": "OPPO", "vivo应用市场": "VIVO", "京东方学习机": "京东方",
|
||||
"步步高学习机": "步步高", "作业帮学习机": "作业帮", "魅族应用市场": "魅族",
|
||||
"官网": "官网",
|
||||
}
|
||||
|
||||
LOG_FILE = "/var/log/damai_v2_fill.log"
|
||||
|
||||
|
||||
def log(msg):
|
||||
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
line = f"[{ts}] {msg}"
|
||||
print(line)
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(line + "\n")
|
||||
|
||||
|
||||
def get_secret(key):
|
||||
with open(os.path.join(WORKSPACE, "secrets.env")) as f:
|
||||
for line in f:
|
||||
if line.startswith(f"{key}="):
|
||||
return line.strip().split("=", 1)[1].strip("'\"")
|
||||
|
||||
|
||||
def get_fs_token():
|
||||
resp = requests.post(
|
||||
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
|
||||
json={"app_id": FS_APP_ID, "app_secret": FS_APP_SECRET},
|
||||
timeout=15
|
||||
)
|
||||
r = resp.json()
|
||||
if r.get("code") != 0:
|
||||
raise RuntimeError(f"获取飞书token失败: {r}")
|
||||
return r["tenant_access_token"]
|
||||
|
||||
|
||||
def read_sheet(token, sheet_id, range_str=None):
|
||||
url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{sheet_id}"
|
||||
if range_str:
|
||||
url += f"!{range_str}"
|
||||
resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, timeout=30)
|
||||
data = resp.json()
|
||||
if data.get("code") != 0:
|
||||
raise RuntimeError(f"读取失败 {sheet_id}: {data}")
|
||||
return data["data"]["valueRange"]["values"]
|
||||
|
||||
|
||||
def put_values(token, sheet_id, range_str, values):
|
||||
"""单次写入,自动分批"""
|
||||
url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values"
|
||||
body = {"valueRange": {"range": f"{sheet_id}!{range_str}", "values": values}}
|
||||
resp = requests.put(url, headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}, json=body, timeout=30)
|
||||
r = resp.json()
|
||||
if r.get("code") != 0:
|
||||
log(f" ❌ {range_str}: {r.get('code')} {r.get('msg')}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def clear_range(token, sheet_id, range_str):
|
||||
"""清空指定范围 — 写入空字符串覆盖"""
|
||||
m = re.match(r'([A-Z]+)(\d+):([A-Z]+)(\d+)', range_str)
|
||||
if not m:
|
||||
log(f" ❌ 无法解析范围: {range_str}")
|
||||
return False
|
||||
sc, sr, ec, er = m.group(1), int(m.group(2)), m.group(3), int(m.group(4))
|
||||
ncols = ord(ec) - ord(sc) + 1
|
||||
BATCH = 500
|
||||
for start in range(sr, er + 1, BATCH):
|
||||
end = min(start + BATCH - 1, er)
|
||||
batch_rows = end - start + 1
|
||||
empty_row = [""] * ncols
|
||||
values = [empty_row[:] for _ in range(batch_rows)]
|
||||
rng = f"{sc}{start}:{ec}{end}"
|
||||
url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values"
|
||||
body = {"valueRange": {"range": f"{sheet_id}!{rng}", "values": values}}
|
||||
resp = requests.put(url, headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}, json=body, timeout=60)
|
||||
r = resp.json()
|
||||
if r.get("code") != 0:
|
||||
log(f" ❌ clear {rng}: {r.get('code')} {r.get('msg')}")
|
||||
return False
|
||||
time.sleep(0.2)
|
||||
return True
|
||||
|
||||
|
||||
def batch_in(cur, sql_tpl, params, chunk=500):
|
||||
results = []
|
||||
for i in range(0, len(params), chunk):
|
||||
batch = params[i:i+chunk]
|
||||
ph = ",".join(["%s"] * len(batch))
|
||||
cur.execute(sql_tpl % ph, batch)
|
||||
results.extend(cur.fetchall())
|
||||
return results
|
||||
|
||||
|
||||
def classify_channel(key_from):
|
||||
"""渠道归属分类: 端内/销转/达人/直购"""
|
||||
if not key_from:
|
||||
return "直购"
|
||||
kf = key_from.strip()
|
||||
if kf in ("app-active-h5-0-0", "app-sales-bj-qhm-0", "app-sales-bj-wd-0"):
|
||||
return "端内"
|
||||
if kf.startswith("sales-adp-"):
|
||||
return "销转"
|
||||
if kf.startswith("newmedia-daren-") or kf == "newmedia-dianpu-wwxx-0-0":
|
||||
return "达人"
|
||||
return "直购"
|
||||
|
||||
|
||||
def parse_date(date_str):
|
||||
"""解析 '6月14日 19:09:12' 或 '6月14日' → '2026-06-14'"""
|
||||
if not date_str:
|
||||
return ""
|
||||
date_str = str(date_str).strip()
|
||||
m = re.match(r'(\d+)月(\d+)日', date_str)
|
||||
if not m:
|
||||
return ""
|
||||
month, day = int(m.group(1)), int(m.group(2))
|
||||
return f"2026-{month:02d}-{day:02d}"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ① 线索明细: F→K UID
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
def fill_lead_uids(token):
|
||||
"""读取线索明细F列手机号 → XXTEA加密 → PG匹配 → 写K列UID"""
|
||||
log("=" * 60)
|
||||
log("① 线索明细 F→K UID")
|
||||
log("=" * 60)
|
||||
|
||||
# 读取线索明细 (A3:K200, 跳过表头2行)
|
||||
rows = read_sheet(token, LEADS_SHEET_ID, "A3:K200")
|
||||
log(f" 读取 {len(rows)} 行线索数据")
|
||||
|
||||
# 解析: (row_idx, phone, existing_uid, 主归属, 昵称, 进线时间)
|
||||
leads = []
|
||||
for i, row in enumerate(rows):
|
||||
row_idx = i + 3 # 飞书行号
|
||||
phone = ""
|
||||
if len(row) > 5 and row[5]:
|
||||
try:
|
||||
phone = str(int(float(row[5])))
|
||||
except:
|
||||
phone = str(row[5]).strip()
|
||||
existing_uid = ""
|
||||
if len(row) > 10 and row[10]:
|
||||
try:
|
||||
existing_uid = str(int(float(row[10])))
|
||||
except:
|
||||
existing_uid = str(row[10]).strip()
|
||||
master_cs = str(row[2]).strip() if len(row) > 2 and row[2] else "" # C=主归属
|
||||
nickname = str(row[4]).strip() if len(row) > 4 and row[4] else "" # E=昵称
|
||||
entry_time = str(row[1]).strip() if len(row) > 1 and row[1] else "" # B=进线时间
|
||||
leads.append((row_idx, phone, existing_uid, master_cs, nickname, entry_time))
|
||||
|
||||
# 找出需要查UID的行 (有手机号但无UID)
|
||||
need_uid = [(idx, phone) for idx, phone, uid, *_ in leads
|
||||
if re.match(r'^\d{11}$', phone) and (not uid or not uid.isdigit() or int(uid) <= 0)]
|
||||
log(f" 需要查UID: {len(need_uid)} 个手机号")
|
||||
|
||||
if not need_uid:
|
||||
log(" 无需查询,跳过")
|
||||
return leads
|
||||
|
||||
# XXTEA 加密
|
||||
phone_enc_map = {}
|
||||
for _, phone in need_uid:
|
||||
try:
|
||||
enc = encrypt_phone(phone)
|
||||
phone_enc_map[enc] = phone
|
||||
except Exception as e:
|
||||
log(f" 加密失败 {phone}: {e}")
|
||||
|
||||
log(f" 加密完成, 唯一密文: {len(phone_enc_map)}")
|
||||
|
||||
# PG 精确查询
|
||||
conn = psycopg2.connect(
|
||||
host="bj-postgres-16pob4sg.sql.tencentcdb.com", port=28591,
|
||||
user="ai_member", password=get_secret("PG_ONLINE_PASSWORD"),
|
||||
dbname="vala_bi", connect_timeout=30
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
enc_list = list(phone_enc_map.keys())
|
||||
phone_to_uid = {}
|
||||
for i in range(0, len(enc_list), 500):
|
||||
chunk = enc_list[i:i+500]
|
||||
ph = ",".join(["%s"] * len(chunk))
|
||||
cur.execute(
|
||||
f"SELECT id, tel_encrypt FROM bi_vala_app_account WHERE tel_encrypt IN ({ph}) AND status=1 AND deleted_at IS NULL",
|
||||
chunk
|
||||
)
|
||||
for uid, tel_enc in cur.fetchall():
|
||||
plain = phone_enc_map.get(tel_enc)
|
||||
if plain:
|
||||
phone_to_uid[plain] = str(uid)
|
||||
time.sleep(0.05)
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
log(f" 精确匹配到 {len(phone_to_uid)} 个 UID")
|
||||
|
||||
# 构建 K 列写入数据 (只写需要更新的行)
|
||||
updates = []
|
||||
for idx, phone in need_uid:
|
||||
uid = phone_to_uid.get(phone, "")
|
||||
updates.append((idx, uid))
|
||||
|
||||
# 写入 (每行单独写K列)
|
||||
write_count = 0
|
||||
for idx, uid in updates:
|
||||
if uid:
|
||||
cell_range = f"K{idx}:K{idx}"
|
||||
if put_values(token, LEADS_SHEET_ID, cell_range, [[uid]]):
|
||||
write_count += 1
|
||||
time.sleep(0.1)
|
||||
|
||||
log(f" 写入 {write_count} 个 UID 到 K 列")
|
||||
|
||||
# 更新 leads 列表中的 UID
|
||||
uid_map = {idx: uid for idx, uid in updates}
|
||||
updated_leads = []
|
||||
for idx, phone, uid, cs, nick, et in leads:
|
||||
if idx in uid_map and uid_map[idx]:
|
||||
uid = uid_map[idx]
|
||||
updated_leads.append((idx, phone, uid, cs, nick, et))
|
||||
|
||||
return updated_leads
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ② 订单明细: clear → 全量回填 A-P
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
def fill_order_details(token, leads):
|
||||
"""查询所有线索UID的订单 → clear 订单明细 → 全量写入 A-P"""
|
||||
log("=" * 60)
|
||||
log("② 订单明细 DB 全量回填")
|
||||
log("=" * 60)
|
||||
|
||||
# 收集所有 UID
|
||||
uid_set = set()
|
||||
lead_by_uid = {} # uid → lead info
|
||||
for idx, phone, uid, cs, nick, et in leads:
|
||||
if uid and uid.isdigit() and int(uid) > 0:
|
||||
uid_int = int(uid)
|
||||
uid_set.add(uid_int)
|
||||
if uid_int not in lead_by_uid:
|
||||
lead_by_uid[uid_int] = (idx, phone, cs, nick, et)
|
||||
|
||||
uid_list = list(uid_set)
|
||||
log(f" 有效 UID: {len(uid_list)}")
|
||||
|
||||
if not uid_list:
|
||||
log(" 无 UID,跳过订单查询")
|
||||
return
|
||||
|
||||
# PG 查询订单
|
||||
conn = psycopg2.connect(
|
||||
host="bj-postgres-16pob4sg.sql.tencentcdb.com", port=28591,
|
||||
user="ai_member", password=get_secret("PG_ONLINE_PASSWORD"),
|
||||
dbname="vala_bi", connect_timeout=30
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# 查询订单
|
||||
log(" 查询订单...")
|
||||
orders = batch_in(cur,
|
||||
"SELECT account_id, trade_no, pay_success_date, key_from, goods_id, pay_amount_int, order_status "
|
||||
"FROM bi_vala_order WHERE account_id IN (%s) AND pay_success_date IS NOT NULL AND order_status IN (3,4) "
|
||||
"ORDER BY account_id, pay_success_date",
|
||||
uid_list
|
||||
)
|
||||
|
||||
log(f" 订单数: {len(orders)}")
|
||||
|
||||
# 查询退款
|
||||
trade_nos = [o[1] for o in orders if o[1]]
|
||||
refund_map = defaultdict(int)
|
||||
if trade_nos:
|
||||
refunds = batch_in(cur,
|
||||
"SELECT trade_no, refund_amount_int FROM bi_refund_order WHERE trade_no IN (%s) AND status=3",
|
||||
trade_nos
|
||||
)
|
||||
for tno, amt in refunds:
|
||||
refund_map[tno] += amt
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# 构建订单明细行 (A-P, 共16列)
|
||||
order_rows = []
|
||||
for o in orders:
|
||||
account_id, trade_no, pay_date, key_from, goods_id, pay_amount, order_status = o
|
||||
|
||||
lead = lead_by_uid.get(account_id)
|
||||
if not lead:
|
||||
continue
|
||||
|
||||
lead_idx, phone, lead_cs, nickname, entry_time = lead
|
||||
|
||||
# A=下单月
|
||||
order_month = ""
|
||||
if pay_date:
|
||||
order_month = f"{pay_date.month}月"
|
||||
|
||||
# B=主归属 (来自线索C列)
|
||||
master_cs = lead_cs
|
||||
|
||||
# C=订单号
|
||||
order_no = trade_no or ""
|
||||
|
||||
# D=用户ID
|
||||
user_id = str(account_id)
|
||||
|
||||
# E=手机号
|
||||
phone_str = phone
|
||||
|
||||
# F=昵称
|
||||
nick = nickname
|
||||
|
||||
# G=线索ID (挂主进线, 用线索行号)
|
||||
lead_ref = str(lead_idx)
|
||||
|
||||
# H=进线时间
|
||||
entry = entry_time
|
||||
|
||||
# I=下单时间
|
||||
order_time = pay_date.strftime("%Y-%m-%d") if pay_date else ""
|
||||
|
||||
# J=GMV (分→元)
|
||||
gmv = round(pay_amount / 100, 2) if pay_amount else 0
|
||||
|
||||
# K=退款
|
||||
refund = round(refund_map.get(trade_no, 0) / 100, 2)
|
||||
|
||||
# L=GSV
|
||||
gsv = round(gmv - refund, 2)
|
||||
|
||||
# M=keyfrom
|
||||
kf = key_from or ""
|
||||
|
||||
# N=渠道归属
|
||||
channel = classify_channel(key_from)
|
||||
|
||||
# O=时序有效 (先留空,由自动规则填充)
|
||||
timing_valid = ""
|
||||
|
||||
# P=备注
|
||||
remark = ""
|
||||
|
||||
order_rows.append([
|
||||
order_month, master_cs, order_no, user_id, phone_str,
|
||||
nick, lead_ref, entry, order_time, gmv, refund, gsv,
|
||||
kf, channel, timing_valid, remark
|
||||
])
|
||||
|
||||
log(f" 生成 {len(order_rows)} 行订单数据")
|
||||
|
||||
# Clear 订单明细 (A3:P5000)
|
||||
log(" 清空订单明细...")
|
||||
# 先清空大范围
|
||||
clear_range(token, ORDERS_SHEET_ID, "A3:P5000")
|
||||
time.sleep(0.5)
|
||||
|
||||
# 写入订单数据 (分批, 每批最多 400 行 × 16 列 = 6400 格, 留余量用 250 行/批)
|
||||
BATCH_ROWS = 250
|
||||
total_written = 0
|
||||
for i in range(0, len(order_rows), BATCH_ROWS):
|
||||
batch = order_rows[i:i+BATCH_ROWS]
|
||||
start_row = i + 3 # 从第3行开始
|
||||
end_row = start_row + len(batch) - 1
|
||||
range_str = f"A{start_row}:P{end_row}"
|
||||
if put_values(token, ORDERS_SHEET_ID, range_str, batch):
|
||||
total_written += len(batch)
|
||||
time.sleep(0.3)
|
||||
|
||||
log(f" 写入 {total_written} 行订单数据")
|
||||
|
||||
# db_info 时间戳
|
||||
db_info = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log(f" db_info: {db_info}")
|
||||
|
||||
return db_info
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Main
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
def main():
|
||||
log("大麦 v2_fill 开始")
|
||||
t0 = time.time()
|
||||
|
||||
token = get_fs_token()
|
||||
log(f"飞书 token 获取成功")
|
||||
|
||||
# ① 线索明细 F→K UID
|
||||
leads = fill_lead_uids(token)
|
||||
|
||||
# ② 订单明细 全量回填
|
||||
db_info = fill_order_details(token, leads)
|
||||
|
||||
elapsed = time.time() - t0
|
||||
log(f"v2_fill 完成 · db_info: {db_info} · 耗时 {elapsed:.1f}s")
|
||||
|
||||
# 输出摘要
|
||||
uid_count = sum(1 for _, _, uid, *_ in leads if uid and uid.isdigit() and int(uid) > 0)
|
||||
phone_count = sum(1 for _, phone, *_ in leads if phone and re.match(r'^\d{11}$', phone))
|
||||
print(f"\n{'='*60}")
|
||||
print(f"v2_fill 完成")
|
||||
print(f" 线索明细: {len(leads)} 行, {phone_count} 手机号, {uid_count} UID")
|
||||
print(f" db_info: {db_info}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
skills/wechat-article-dachen/SKILL.md
Normal file
52
skills/wechat-article-dachen/SKILL.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
name: wechat-article-dachen
|
||||
slug: wechat-article-dachen
|
||||
version: 1.0.0
|
||||
description: 按陈逸鸫(大陈的创业笔记)风格撰写公众号短文,口语化、有反转、有人味、不啰嗦
|
||||
---
|
||||
|
||||
## When to Use
|
||||
|
||||
陈逸鸫要求写公众号短文、小红书投放/种草相关文章、灵犀使用指南等短图文内容时触发。
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. 语气:像跟朋友聊天,不像在写文章
|
||||
|
||||
- 用口语词:我靠、纯tm、屁用没有、脸疼、愣了
|
||||
- 短句为主,偶尔长句叙事。不要排比句,不要"首先其次最后"
|
||||
- 可以自嘲、可以吐槽平台、可以骂行业乱象
|
||||
- 不要客套话,不要"大家好""今天我们来聊聊"
|
||||
|
||||
### 2. 结构:反转驱动,不是逻辑驱动
|
||||
|
||||
- 开头一句话钩子(打脸/反常识/数据冲击)
|
||||
- 中间必须有"我以前以为X → 后来发现Y"的转折
|
||||
- 转折要有"人味":不是"我发现了一个洞察",而是"我被逼着去看/数据甩我脸上/我愣住了"
|
||||
- 结尾预告下一篇,或给一个可行动的结论
|
||||
- 不要总结式结尾,不要"综上所述"
|
||||
|
||||
### 3. 数据用法:一个数字就够了
|
||||
|
||||
- 每篇只放 1-2 个关键数字,不堆数据表
|
||||
- 数字要让人"我靠":90.4%、差40倍、ROI从0.8到1.6
|
||||
- 数字后面必须跟一句大白话解释,让人秒懂
|
||||
|
||||
### 4. 篇幅:短文 300-600 字
|
||||
|
||||
- 公众号短图文消息的长度
|
||||
- 一个核心观点,讲透就停
|
||||
- 不要试图在一篇里塞两个观点
|
||||
|
||||
### 5. 结尾:预告或行动号召二选一
|
||||
|
||||
- 系列文章:预告下一篇,留钩子
|
||||
- 单篇:给一个可执行的动作或邀请互动
|
||||
- 不要"感谢阅读"
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| 内容 | 文件 |
|
||||
|------|------|
|
||||
| 完整风格示例 | `examples.md` |
|
||||
| 短文选题池 | `topics.md` |
|
||||
57
skills/wechat-article-dachen/examples.md
Normal file
57
skills/wechat-article-dachen/examples.md
Normal file
@ -0,0 +1,57 @@
|
||||
# 风格示例
|
||||
|
||||
## 定稿示例:打脸预热文
|
||||
|
||||
---
|
||||
|
||||
「去年我说种草没用,今年就被打脸了。」
|
||||
|
||||
去年我写过一句话:"种草要投吗?No,屁用没有。"
|
||||
|
||||
当时的逻辑很简单:平台内容还未饱和,很多品牌还没开始投放,与其种草,不如多出硬广效率更高。
|
||||
|
||||
直客总是唠叨的灵犀更是极少登陆,因为不仅功能复杂,概念繁多,跟聚光数据也很难聚合,纯tm浪费时间。
|
||||
|
||||
但今年小红书难做了很多,内容饱和已经是常态,同样的赛道、同样的人群,所有人都在抢。我出价高,别人比我更高。最后就是纯拼谁烧得起——线索拿到了,ROI打不平。
|
||||
|
||||
这时候我才被逼着去找新出路:收割卷不动了,那能不能在收割之前做点什么?
|
||||
|
||||
于是我打开了灵犀,做了一方人群回传,结果一出来我都愣了。
|
||||
|
||||
我知道TI是最接近线索的人群,但是没考虑过线索中多少来自TI,数据出来一看,我靠,90.4%的线索都来自TI人群!而更泛的A和I人群加一起都不到10%......
|
||||
|
||||
TI很重要 vs 90%的线索来自TI,分量完全不同好不好!
|
||||
|
||||
你说那些小红书大会,一开就是半天,各种概念玄之又玄,你们直接把这个结论放出来我们早就启用了好不好。
|
||||
|
||||
那么TI怎么尽快扩大?哪些内容的TI种草成本低?什么样的投放能带来低成本TI?TI不够会怎么样?品牌营销的费用到底带来了多少回报?
|
||||
|
||||
一旦有了数据支持,这些以前无法回答的种草效果问题就迎刃而解。
|
||||
|
||||
当然,这整个链路很复杂,我把【灵犀+聚光+企微+销售聊天记录+订单系统】用ai全部串起来之后,改了10版计算逻辑,才有了一个贯穿全局的洞察。
|
||||
|
||||
我只能说,种草的确有效果,但是跟大家以为的效果不太一样,非常有意思哈哈。
|
||||
|
||||
接下来我会写一系列"种草效果分析"的内容,把灵犀中的各个数据指标解释清楚,并一步一步揭露"每一块钱的种草能带多少gmv?"的最终结论。
|
||||
|
||||
如果你也遇到了投放成本越来越高,线索越来越少,roi越来越低的问题,
|
||||
|
||||
我只能说,敬请期待!
|
||||
|
||||
这个玩法远不限于小红书,只要是内容平台,一律适用!
|
||||
|
||||
当然如果你等不及,也可以直接找我约个咨询。
|
||||
|
||||
下一篇预告《一个策略,不花一分钱也能直接ROI翻倍| 种草效果分析》
|
||||
|
||||
---
|
||||
|
||||
## 风格要素拆解
|
||||
|
||||
1. **开头钩子**:一句话打脸,直接有力
|
||||
2. **反转线**:以前出价能解决问题 → 现在饱和了卷不动了 → 被迫回头看TI → 发现90.4%
|
||||
3. **人味细节**:直客唠叨灵犀、纯tm浪费时间、我靠、愣了、好不好
|
||||
4. **数据冲击**:90.4%,一句话就够了
|
||||
5. **吐槽大会**:小红书大会开半天,概念玄之又玄
|
||||
6. **预告钩子**:下一篇标题直接放出来
|
||||
7. **行动号召**:等不及可以约咨询
|
||||
52
skills/wechat-article-dachen/topics.md
Normal file
52
skills/wechat-article-dachen/topics.md
Normal file
@ -0,0 +1,52 @@
|
||||
# 短文选题池
|
||||
|
||||
## 一、打脸/反转系列
|
||||
1. 「去年我说种草没用,今年就被打脸了。」 ✅ 已定稿
|
||||
2. 「我以前觉得灵犀就是花里胡哨的数据看板——直到用它改了一组计划,ROI从0.8拉到1.6。」
|
||||
3. 「灵犀里有个被大多数人忽略的数字,我抓准了之后,线索成本降了30%。」
|
||||
4. 「我公开说过'种草不用投',现在我要告诉你为什么我改口了。」
|
||||
|
||||
## 二、反常识数据卡
|
||||
5. 「你90%的成交,来自一批你根本不知道自己在'种草'的人。」
|
||||
6. 「100万人看到你→最后掏钱的只有700个。中间漏在哪?」
|
||||
7. 「线索翻了一倍,ROI反而降了。老板问我钱花哪了,我沉默了。」
|
||||
8. 「你花30万投的达人,47万人只是'刷到过'然后就没了。」
|
||||
|
||||
## 三、触点/预算错配
|
||||
9. 「你的钱70%烧在了转化最差的口子上——信息流0.12%,视频流0.47%,差了4倍。」
|
||||
10. 「同样的预算,换个口子投,成交多22%。但大部分人都投错了。」
|
||||
11. 「没加一分钱预算,只是把30%的钱搬了个家,ROI翻倍。」
|
||||
|
||||
## 四、内容类型错配
|
||||
12. 「一篇笔记带了1000个留资,转化率只有1%。按你的标准,它该被砍掉。」
|
||||
13. 「种草稿和收割稿是两种生物,但你用同一把尺子量。」
|
||||
14. 「收割型内容成交率23%,泛种草10%。但泛种草一篇能带1000+留资。」
|
||||
|
||||
## 五、搜索词系列
|
||||
15. 「竞品词投2万换十几个咨询,决策词投500也换十几个。差40倍。」
|
||||
16. 「你在抢竞品客户?不,你在帮平台创收。」
|
||||
17. 「搜索投放最大的骗局:你以为在截流,其实在捐款。」
|
||||
|
||||
## 六、灵犀系列
|
||||
18. 「灵犀和聚光,一个告诉你胖了,一个让你跑步。但它们是两家公司。」
|
||||
19. 「灵犀里'I→TI流转率'这个数字,决定了你种草效率的天花板。」
|
||||
20. 「达人投了30万,灵犀一看——种草效率还不如企业号自营。」
|
||||
|
||||
## 七、ROI/转化系列
|
||||
21. 「种草能让你线索多,但不能让你赚钱。赚钱靠的是另一件事。」
|
||||
22. 「为什么线索越来越多,ROI越来越低?因为成单率在偷偷降。」
|
||||
23. 「企业号自营内容带来的线索,成交率是达人内容的1.5倍。」
|
||||
|
||||
## 八、吐槽/行业观察
|
||||
24. 「小红书为什么非要把灵犀和聚光拆成两个平台?」
|
||||
25. 「聚光投手的一天:打开聚光→调计划→打开灵犀→看数据→回聚光→调计划→回灵犀→……」
|
||||
26. 「2026年了,小红书还在让你在两个平台之间来回跳。」
|
||||
|
||||
## 九、咨询案例速览
|
||||
27. 「客户问我:为什么我投了三个月,线索越来越多,ROI越来越低?」
|
||||
28. 「一个客户把80%预算投了竞品词,我让他砍掉一半,线索量没变,成本降了40%。」
|
||||
29. 「客户说'我种草了',我打开灵犀一看——TI净增是负的。」
|
||||
|
||||
## 十、预告/互动
|
||||
30. 「我花了三周用AI拆了113万投放数据,发现了五个我自己都不敢信的结论。」
|
||||
31. 「你们投小红书最头疼的问题是什么?评论区告诉我,我拿数据跑。」
|
||||
Loading…
Reference in New Issue
Block a user