每日总结更新 20260620
This commit is contained in:
parent
5bd587318a
commit
c1ef7c31b0
@ -639,3 +639,11 @@ To https://git.valavala.com/ai_member_only/ai_member_xiaoban
|
|||||||
3685359..9d6999a master -> master
|
3685359..9d6999a master -> master
|
||||||
[2026-06-18 08:10:03] 工作区备份成功:自动备份 2026-06-18 08:10:01
|
[2026-06-18 08:10:03] 工作区备份成功:自动备份 2026-06-18 08:10:01
|
||||||
[2026-06-19 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
|
||||||
|
|||||||
@ -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-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: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-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,
|
"version": 1,
|
||||||
"updatedAt": "2026-06-18T10:17:37.951Z",
|
"updatedAt": "2026-06-19T12:45:15.278Z",
|
||||||
"entries": {
|
"entries": {
|
||||||
"memory:memory/2026-05-24.md:1:30": {
|
"memory:memory/2026-05-24.md:1:30": {
|
||||||
"key": "memory:memory/2026-05-24.md:1:30",
|
"key": "memory:memory/2026-05-24.md:1:30",
|
||||||
@ -2375,19 +2375,21 @@
|
|||||||
"endLine": 30,
|
"endLine": 30,
|
||||||
"source": "memory",
|
"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 库中无订单表,无法直接对比交易系统后台",
|
"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,
|
"dailyCount": 0,
|
||||||
"groundedCount": 0,
|
"groundedCount": 0,
|
||||||
"totalScore": 2,
|
"totalScore": 3,
|
||||||
"maxScore": 1,
|
"maxScore": 1,
|
||||||
"firstRecalledAt": "2026-06-18T00:09:26.923Z",
|
"firstRecalledAt": "2026-06-18T00:09:26.923Z",
|
||||||
"lastRecalledAt": "2026-06-18T10:17:37.951Z",
|
"lastRecalledAt": "2026-06-19T12:45:15.278Z",
|
||||||
"queryHashes": [
|
"queryHashes": [
|
||||||
"ee468f64688e",
|
"ee468f64688e",
|
||||||
"d83437ba9cad"
|
"d83437ba9cad",
|
||||||
|
"290e7f4aaa74"
|
||||||
],
|
],
|
||||||
"recallDays": [
|
"recallDays": [
|
||||||
"2026-06-18"
|
"2026-06-18",
|
||||||
|
"2026-06-19"
|
||||||
],
|
],
|
||||||
"conceptTags": [
|
"conceptTags": [
|
||||||
"pick-valid-order",
|
"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. 「你们投小红书最头疼的问题是什么?评论区告诉我,我拿数据跑。」
|
||||||
3
tmp_daily_summary.md
Normal file
3
tmp_daily_summary.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
=== 每日总结 20260620 ===
|
||||||
|
## 昨日关键进展
|
||||||
|
- ① 线索明细 (7fdb4b): F列95个手机号 → XXTEA加密 → PG匹配 → K列UID 63个(新增12个)
|
||||||
Loading…
Reference in New Issue
Block a user