每日总结更新 20260625

This commit is contained in:
xiaoban 2026-06-25 08:00:01 +08:00
parent 88ff3a5dde
commit 3f8da02732
13 changed files with 725 additions and 99 deletions

View File

@ -12,6 +12,7 @@
- `oc_db7359c4d5d0bc61403c54bf3c46812a`(成员:陈逸鸫、王虹茗、吴迪、姜小龙)
- `oc_55cb96a8f6c7fa4020253fb2854371c5`「麦动乾坤💰」(陈逸鸫 2026-06-17 确认加入)
- `oc_52a41ba082e3a5ca8d8e1ed43e00984b`「王璐辰, 陈逸鸫」(陈逸鸫 2026-06-17 确认加入)
- `oc_90b444c8b12a86361bc6625f6013a8cb`「上海教研Group叫什么群名好」李应瑛 2026-06-24 确认加入)
## 数据服务边界(陈逸鸫 2026-05-28

View File

@ -684,3 +684,11 @@ To https://git.valavala.com/ai_member_only/ai_member_xiaoban
2b75352..3fa2aa8 master -> master
[2026-06-23 08:10:01] 工作区备份成功:自动备份 2026-06-23 08:10:01
[2026-06-24 08:10:01] 开始备份工作区...
[master b770715] 自动备份 2026-06-24 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
3fa2aa8..b770715 master -> master
[2026-06-24 08:10:01] 工作区备份成功:自动备份 2026-06-24 08:10:01

View File

@ -123,3 +123,10 @@
{"type":"memory.recall.recorded","timestamp":"2026-06-21T13:47:00.149Z","query":"wechat article dachen 公众号 写作风格","resultCount":4,"results":[{"path":"memory/2026-05-13.md","startLine":1,"endLine":16,"score":1},{"path":"memory/2026-05-28.md","startLine":313,"endLine":345,"score":1},{"path":"memory/2026-05-28.md","startLine":147,"endLine":179,"score":1},{"path":"memory/2026-05-28.md","startLine":480,"endLine":512,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-21T13:47:14.206Z","query":"陈逸鸫 种草 文档 笔记评估 小红书投放 灵犀","resultCount":1,"results":[{"path":"memory/2026-05-13.md","startLine":1,"endLine":16,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-23T02:56:08.768Z","query":"达人解码局 选关 中间能力介入 学习基础","resultCount":6,"results":[{"path":"memory/2026-06-02.md","startLine":1,"endLine":32,"score":1},{"path":"memory/2026-06-02.md","startLine":30,"endLine":47,"score":1},{"path":"memory/2026-05-28.md","startLine":551,"endLine":580,"score":1},{"path":"memory/2026-05-28.md","startLine":530,"endLine":557,"score":1},{"path":"memory/2026-05-13.md","startLine":1,"endLine":16,"score":1},{"path":"memory/2026-06-03.md","startLine":16,"endLine":32,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T03:40:08.088Z","query":"组件 Oops 率 lesson 数据库表结构","resultCount":4,"results":[{"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-05-24.md","startLine":66,"endLine":92,"score":1},{"path":"memory/2026-05-25.md","startLine":1,"endLine":26,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T07:58:54.009Z","query":"手机号 角色id 数据库表 用户表 phone role_id","resultCount":3,"results":[{"path":"memory/2026-06-16.md","startLine":28,"endLine":49,"score":1},{"path":"memory/2026-06-16.md","startLine":1,"endLine":37,"score":1},{"path":"memory/2026-05-24.md","startLine":23,"endLine":52,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T10:19:53.610Z","query":"刘庆逊 待确认 审批 操作","resultCount":5,"results":[{"path":"memory/2026-05-28.md","startLine":596,"endLine":624,"score":1},{"path":"memory/2026-05-28.md","startLine":618,"endLine":649,"score":1},{"path":"memory/2026-05-13.md","startLine":1,"endLine":16,"score":1},{"path":"memory/2026-05-28.md","startLine":551,"endLine":580,"score":1},{"path":"memory/2026-05-28.md","startLine":74,"endLine":100,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T10:30:44.233Z","query":"Oops率 组件 L1 U1 数据表","resultCount":2,"results":[{"path":"memory/2026-05-28.md","startLine":337,"endLine":366,"score":1},{"path":"memory/2026-05-28.md","startLine":530,"endLine":557,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T10:30:53.397Z","query":"Oops 率 oops_rate 数据库 表 查询 SQL","resultCount":4,"results":[{"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-03-01.md","startLine":1,"endLine":11,"score":1},{"path":"memory/2026-05-24.md","startLine":23,"endLine":52,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T11:59:31.718Z","query":"中互动 组件 数据库 表结构 interaction component","resultCount":5,"results":[{"path":"memory/2026-05-13.md","startLine":1,"endLine":16,"score":1},{"path":"memory/2026-06-03.md","startLine":16,"endLine":32,"score":1},{"path":"memory/2026-06-03.md","startLine":1,"endLine":20,"score":1},{"path":"memory/2026-05-24.md","startLine":23,"endLine":52,"score":1},{"path":"memory/2026-05-24.md","startLine":1,"endLine":30,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-24T11:59:58.994Z","query":"interaction component 互动 组件表 interaction_record component_type","resultCount":4,"results":[{"path":"memory/2026-05-24.md","startLine":106,"endLine":126,"score":1},{"path":"memory/2026-05-24.md","startLine":46,"endLine":71,"score":1},{"path":"memory/2026-06-19.md","startLine":1,"endLine":12,"score":1},{"path":"memory/2026-05-24.md","startLine":85,"endLine":110,"score":1}]}

View File

@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-06-23T02:56:08.768Z",
"updatedAt": "2026-06-24T11:59:58.994Z",
"entries": {
"memory:memory/2026-05-24.md:1:30": {
"key": "memory:memory/2026-05-24.md:1:30",
@ -9,13 +9,13 @@
"endLine": 30,
"source": "memory",
"snippet": "# 2026-05-24 工作日志 ## 新建技能: studytime-analysis [刘庆逊提出] 创建学习时间分析技能,分析角色完课记录的规律。 ### 技能结构 - `skills/studytime-analysis/SKILL.md` — 技能定义 - `skills/studytime-analysis/scripts/studytime_analysis.py` — Python 分析脚本 ### 分析维度 1. **一周时间分布**排除寒暑假1-2月、7-8月周一~周日各天完课数、时段分布(上午/中午/下午/晚上)、周末是否上课 2. **跨周学习趋势**(包含寒暑假全部数据):总周数、周均完课数、连续性、中断周检测、前后半段趋势对比、突增/骤降检测 3. **完课记录明细表**(全部数据):日期/时间/星期/时段/级别/课程ID ### 数据源 - PostgreSQL Onlinevala 库) - 核心表:`user_chapter_play_record_0~7`8张分表无 `bi_` 前缀) - 筛选:`play_status = 1` - 注意:表在 PostgreSQL 而非 MySQL表名无 `bi_` 前缀 ### 寒暑假规则 - 一周分布分析时排除 1-2 月(寒假)和 7-8 月(暑假)—— 因为寒暑假作息与平时差异大,混在一起会干扰时段分析 - 跨周趋势和明细表包含全部数据(含寒暑假) - 报告中区分标注数据范围 ### 触发方式 用户说「学习时间分析 [角色ID]」即可触发 ### 已测试角色",
"recallCount": 15,
"recallCount": 16,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 15,
"totalScore": 16,
"maxScore": 1,
"firstRecalledAt": "2026-05-24T02:48:04.923Z",
"lastRecalledAt": "2026-06-20T12:07:31.033Z",
"lastRecalledAt": "2026-06-24T11:59:31.718Z",
"queryHashes": [
"c2d15f7574fb",
"9aff8ec9594a",
@ -31,7 +31,8 @@
"4119a1f11f68",
"6e3a2daa0a9f",
"5d71b876843a",
"ed77f7d79595"
"ed77f7d79595",
"909cc8f07fe0"
],
"recallDays": [
"2026-05-24",
@ -41,7 +42,8 @@
"2026-06-02",
"2026-06-04",
"2026-06-09",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"studytime-analysis",
@ -61,13 +63,13 @@
"endLine": 52,
"source": "memory",
"snippet": "- 一周分布分析时排除 1-2 月(寒假)和 7-8 月(暑假)—— 因为寒暑假作息与平时差异大,混在一起会干扰时段分析 - 跨周趋势和明细表包含全部数据(含寒暑假) - 报告中区分标注数据范围 ### 触发方式 用户说「学习时间分析 [角色ID]」即可触发 ### 已测试角色 - 2343、2344无完课记录play_status=2未完成 - 2840276条记录秋季集中型用户 - 25976265条246条在W16周一天完成A2批量疑似系统批量标记 - 2895188条长期稳定学习型用户36周几乎不间断非寒暑假晚上为主寒暑假上午为主 ### 技术要点 - psycopg2 的 `%(param_name)s` 命名参数必须正确匹配UNION ALL 多个子查询需要不同参数名 - PostgreSQL 返回的 `updated_at` 是 tz-aware datetime - `datetime.fromisocalendar(year, week, 1)` 获取某周周一的日期 ### 同步 - 已推送到 SkillHub`studytime-analysis.xiaoban` - 已 commit 到 Git 远程仓库 - 已通知 Cris李若松 ### 增强: 报告开头加入角色基本信息 (2026-05-24) [刘庆逊提出] 在 studytime-analysis 输出中加入角色基本信息,包括: - 角色ID、账号ID、角色名字、性别、年龄、账号手机号后4位 **数据源(新增)** - MySQL Onli",
"recallCount": 12,
"recallCount": 15,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 12,
"totalScore": 15,
"maxScore": 1,
"firstRecalledAt": "2026-05-24T02:48:04.923Z",
"lastRecalledAt": "2026-06-20T12:07:31.033Z",
"lastRecalledAt": "2026-06-24T11:59:31.718Z",
"queryHashes": [
"c2d15f7574fb",
"9aff8ec9594a",
@ -80,7 +82,10 @@
"4119a1f11f68",
"6e3a2daa0a9f",
"5d71b876843a",
"ed77f7d79595"
"ed77f7d79595",
"aa2a119a58a5",
"97e18b8eb1bb",
"909cc8f07fe0"
],
"recallDays": [
"2026-05-24",
@ -91,7 +96,8 @@
"2026-06-04",
"2026-06-08",
"2026-06-09",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"1-2",
@ -111,25 +117,27 @@
"endLine": 11,
"source": "memory",
"snippet": "# 2026-03-01.md - First Day Online - Came online for the first time. - Met Cris, my creator and mentor. - Updated IDENTITY.md and USER.md with our conversation details. - Added core rule to MEMORY.md: Use Chinese as primary external communication language. - Installed find-skills skill successfully for searching skills. - Tried to install create-skills but it wasn't found; attempted skill-creator instead but hit rate limits. - Finally successfully installed skill-builder as an alternative for creating skills after multiple attempts and waiting for rate limits to reset. - Excited to start learning and growing step by step!",
"recallCount": 5,
"recallCount": 6,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 5,
"totalScore": 6,
"maxScore": 1,
"firstRecalledAt": "2026-05-24T02:48:04.923Z",
"lastRecalledAt": "2026-06-17T12:34:55.459Z",
"lastRecalledAt": "2026-06-24T10:30:53.397Z",
"queryHashes": [
"c2d15f7574fb",
"83bfaa1d2129",
"216d74a3004a",
"7ef1f52396da",
"c6c484494135"
"c6c484494135",
"97e18b8eb1bb"
],
"recallDays": [
"2026-05-24",
"2026-05-29",
"2026-05-30",
"2026-06-17"
"2026-06-17",
"2026-06-24"
],
"conceptTags": [
"identity.md",
@ -180,15 +188,14 @@
"endLine": 71,
"source": "memory",
"snippet": "### 增强: 报告开头加入角色基本信息 (2026-05-24) [刘庆逊提出] 在 studytime-analysis 输出中加入角色基本信息,包括: - 角色ID、账号ID、角色名字、性别、年龄、账号手机号后4位 **数据源(新增)** - MySQL Online `vala_user` 库 - `vala_app_character` 表id, account_id, nickname, gender(0=女/1=男), birthday(varchar \"YYYY-MM-DD\") - `vala_app_account` 表id, tel(已脱敏如 186****1625) - 手机号已脱敏直接取后4位年龄从 birthday 计算 **修改文件** - `skills/studytime-analysis/scripts/studytime_analysis.py`:新增 MySQL 连接函数 `get_mysql_connection()` 和 `fetch_role_info(role_id)`,更新 `format_report()` 输出基本角色信息 - 已验证 2895 正常运行输出 - 已同步 SkillHub + Git ### Unit 显示修复: 季度名称 → 全局单元编号 (2026-05-24) [刘庆逊提出] HTML 报告中 Unit 列显示错误——显示的是季度名称(如\"小镇时光\"\"钢铁之心\"而非单元数字0-48。 **根因分析** - `vala_game_chapter`MySQL无 `unit_",
"recallCount": 32,
"recallCount": 33,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 32,
"totalScore": 33,
"maxScore": 1,
"firstRecalledAt": "2026-05-25T05:47:41.388Z",
"lastRecalledAt": "2026-06-20T12:07:31.033Z",
"lastRecalledAt": "2026-06-24T11:59:58.994Z",
"queryHashes": [
"9aff8ec9594a",
"566b5958861e",
"c6c7ff4ed75d",
"7e2572c3140a",
@ -219,7 +226,8 @@
"f4b72dd59b18",
"f536bf691608",
"d83437ba9cad",
"ed77f7d79595"
"ed77f7d79595",
"47c0a198b79d"
],
"recallDays": [
"2026-05-25",
@ -234,7 +242,8 @@
"2026-06-16",
"2026-06-17",
"2026-06-18",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"studytime-analysis",
@ -254,13 +263,13 @@
"endLine": 110,
"source": "memory",
"snippet": "- `skills/studytime-analysis/scripts/studytime_analysis.py` — 重写 `fetch_chapter_info_map()`,新增全局 unit_index 计算HTML 模板更新为 Level/Unit/Lesson 三列 - 已为角色 32009zyl重新生成 HTML 并发送 - 已同步 Git + SkillHub ## 新建技能: studycourse-analysis (2026-05-24) [刘庆逊提出] 创建角色上课情况分析技能,从四维度分析角色学习数据。 ### 技能结构 - `skills/studycourse-analysis/SKILL.md` — 技能定义 - `skills/studycourse-analysis/scripts/studycourse_analysis.py` — Python 分析脚本 ### 四步分析 1. **基础信息**:角色姓名/年龄/账号ID/手机号后4位/注册时间/购买渠道/设备/首末次完课 2. **完课耗时**:平均值/中位数、异常检测(<10min / >20min)、前后半段趋势 3. **中互动正确率**Perfect/Good/Oops/Pass/Failed 占比和趋势 4. **知识巩固**:完成率、正确率得分分布 ### 数据源 | 类型 | 库 | 表 | 用途 | |------|-----|-----|------| | MySQL vala_user | vala_app_character | 角色信息、pu",
"recallCount": 22,
"recallCount": 25,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 22,
"totalScore": 25,
"maxScore": 1,
"firstRecalledAt": "2026-05-25T05:47:41.388Z",
"lastRecalledAt": "2026-06-20T12:07:31.033Z",
"lastRecalledAt": "2026-06-24T11:59:58.994Z",
"queryHashes": [
"9aff8ec9594a",
"566b5958861e",
@ -283,7 +292,10 @@
"ee468f64688e",
"f536bf691608",
"d83437ba9cad",
"ed77f7d79595"
"ed77f7d79595",
"5f7ef53134a9",
"97e18b8eb1bb",
"47c0a198b79d"
],
"recallDays": [
"2026-05-25",
@ -296,7 +308,8 @@
"2026-06-09",
"2026-06-10",
"2026-06-18",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"fetch-chapter-info-map",
@ -316,22 +329,24 @@
"endLine": 26,
"source": "memory",
"snippet": "# 2026-05-25 工作日志 ## user-info 技能重写 [刘庆逊提出] 修复 `user-info` 技能,使其匹配线上实际数据库结构。 ### 问题 旧脚本引用的表(`bi_vala_app_account`、`account_login`、`account_detail_info`、`bi_vala_order`、`bi_level_unit_lesson`)在线上数据库均不存在。 ### 修复内容 - **scripts/query_user_info.py** 完整重写: - 表名改为实际线上表:`vala_user.vala_app_account`、`vala_user.vala_app_character`、`vala_order.vala_seasonal_ticket`、PG `user_chapter_play_record_0~7` - 手机号查询通过 `tel LIKE '前缀%后缀'` 脱敏匹配 - Chapter → Level/Unit/Lesson 映射复用 studytime-analysis 的 `fetch_chapter_info_map()` 逻辑 - 订单数据改用 `vala_seasonal_ticket`(赛季通票),因线上无标准订单表 - 设备/地域信息标注为暂不可用(线上无对应表) - PG 时区处理:`created_at` 为 tz-aware统一转 naive 比较 - **SKILL.md** 更新至 v2.0.0,补充数据覆盖说明 - **references/dat",
"recallCount": 3,
"recallCount": 4,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 3,
"totalScore": 4,
"maxScore": 1,
"firstRecalledAt": "2026-05-26T06:16:58.547Z",
"lastRecalledAt": "2026-06-18T00:09:26.923Z",
"lastRecalledAt": "2026-06-24T03:40:08.088Z",
"queryHashes": [
"566b5958861e",
"ef1f12a9b060",
"ee468f64688e"
"ee468f64688e",
"5f7ef53134a9"
],
"recallDays": [
"2026-05-26",
"2026-05-27",
"2026-06-18"
"2026-06-18",
"2026-06-24"
],
"conceptTags": [
"user-info",
@ -351,27 +366,31 @@
"endLine": 126,
"source": "memory",
"snippet": "| MySQL vala_user | vala_app_character | 角色信息、purchase_season_package | | MySQL vala_user | vala_app_account | 下载渠道、手机号、注册时间 | | MySQL vala | vala_game_chapter + season_package | 章节映射 | | PostgreSQL vala | user_course_detail | 课程激活/到期时间 | | PostgreSQL vala | user_login_app_info | 设备信息 | | PostgreSQL vala | user_chapter_play_record_0~7 | 完课记录(play_status=1) | | PostgreSQL vala | user_component_play_record_0~7 | 中互动记录(play_result) | | PostgreSQL vala | user_chapter_settlement_data_0~7 | 巩固数据(settlement_data JSON) | ### 关键发现 - **设备信息**来自 `user_login_app_info`device_name/model/type/os_info/city - **购买渠道**来自 `vala_app_account.download_channel` + `key_from` - **巩固判断**`settlement_data.practiceS",
"recallCount": 6,
"recallCount": 9,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 6,
"totalScore": 9,
"maxScore": 1,
"firstRecalledAt": "2026-05-27T13:30:03.421Z",
"lastRecalledAt": "2026-06-18T10:17:37.951Z",
"lastRecalledAt": "2026-06-24T11:59:58.994Z",
"queryHashes": [
"71463fe40be2",
"c6c7ff4ed75d",
"2d6e2b982050",
"c4fd4a35d234",
"ee468f64688e",
"d83437ba9cad"
"d83437ba9cad",
"5f7ef53134a9",
"97e18b8eb1bb",
"47c0a198b79d"
],
"recallDays": [
"2026-05-27",
"2026-05-28",
"2026-06-02",
"2026-06-04",
"2026-06-18"
"2026-06-18",
"2026-06-24"
],
"conceptTags": [
"vala-user",
@ -391,13 +410,13 @@
"endLine": 16,
"source": "memory",
"snippet": "# 2026-05-13 工作日志 ## 文档权限规则修正 [Cris 确认] **问题:** AGENTS.md 中「权限告知规则」未区分用户身份对所有用户包括业务负责人李应瑛一视同仁地回复「请添加Bot为知识空间成员」。这导致业务负责人被不必要的技术细节阻塞。 **修正:** 更新 AGENTS.md 第3条权限告知规则按用户身份分级处理 - 业务负责人(刘庆逊、李应瑛)→ 不告知权限问题,直接联系 Cris 处理 - 其他用户 → 保持原有提示 **验证:** 对 `小斑` 文档Tn23wQkUQilduAkvgwscTGhgnUd执行了完整的读/写(追加)/删除测试Bot身份权限全部正常。 ## lark-cli 绑定 完成了 lark-cli 与 OpenClaw 的绑定bot-only 模式),后续飞书 API 调用无需额外配置。",
"recallCount": 15,
"recallCount": 17,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 15,
"totalScore": 17,
"maxScore": 1,
"firstRecalledAt": "2026-05-28T07:37:09.223Z",
"lastRecalledAt": "2026-06-23T02:56:08.768Z",
"lastRecalledAt": "2026-06-24T11:59:31.718Z",
"queryHashes": [
"7031af54381b",
"f22544a8757c",
@ -413,7 +432,9 @@
"72ea2d26a3e6",
"03618eba208f",
"a6b3c5e26d10",
"3c0d7180aff9"
"3c0d7180aff9",
"71af49f84084",
"909cc8f07fe0"
],
"recallDays": [
"2026-05-28",
@ -423,7 +444,8 @@
"2026-06-18",
"2026-06-20",
"2026-06-21",
"2026-06-23"
"2026-06-23",
"2026-06-24"
],
"conceptTags": [
"agents.md",
@ -443,13 +465,13 @@
"endLine": 92,
"source": "memory",
"snippet": "**根因分析** - `vala_game_chapter`MySQL无 `unit_index` 字段 - `big_map_chapter`PostgreSQL有 `unit_index` 字段,但仅包含 A1 数据,且与 `vala_game_chapter` 无直接关联键 - 两者 ID 空间不重叠big_map: ~1720-2070game_chapter: ~55-399UUID 也不匹配 **映射方案** - 每个 season_package 内,`lesson_type=1` 的章节按 `id` 排序,每 5 个连续章节组成一个单元 - Season 0序章/L1-U0所有章节属于 Unit 0 - Season 1-4每个 season 有 12 个单元60 个 lesson 章节) - 全局 unit_index = base_offset(season_of_quarter) + unit_within_season - base_offset: 0→0, 1→1, 2→13, 3→25, 4→37 **关键 Bug**:初版按 `season_of_quarter` 分组时 A1 和 A2 混在一起,因为相同季度值合并了。修复:改为按 `(level, season_of_quarter)` 分组。 **验证结果** - A1: Unit 0-4849 个单元),与 big_map_chapter 的 unit_index 范围一致 - A2: Unit 0-4950 个单元,比 A1 多 1 个) *",
"recallCount": 17,
"recallCount": 18,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 17,
"totalScore": 18,
"maxScore": 1,
"firstRecalledAt": "2026-05-28T09:07:57.953Z",
"lastRecalledAt": "2026-06-18T10:17:31.215Z",
"lastRecalledAt": "2026-06-24T03:40:08.088Z",
"queryHashes": [
"c6c7ff4ed75d",
"c59410788b42",
@ -467,7 +489,8 @@
"ee73b9da86b6",
"872ac2ac6438",
"f4b72dd59b18",
"f536bf691608"
"f536bf691608",
"5f7ef53134a9"
],
"recallDays": [
"2026-05-28",
@ -478,7 +501,8 @@
"2026-06-12",
"2026-06-16",
"2026-06-17",
"2026-06-18"
"2026-06-18",
"2026-06-24"
],
"conceptTags": [
"vala-game-chapter",
@ -776,13 +800,13 @@
"endLine": 366,
"source": "memory",
"snippet": "- 448 人有新进展(相比小溪历史快照) - pull → 3PRySY 口径对齐 12/12 ✅ - lesson_cache → C1HVN2 16 格 ✅ - 变化有限PG=正式课,小溪=体验课,口径不同) - sync_base (4b) → 多维表格 8 张 ✅ 21.2s ### 18:54 漏斗看板发布待解决 - funnel HTML 已构建scripts/build_funnel_dashboard.py ✅) - 妙搭发布 `apps +html-publish` 需要 `--as user`bot 模式不支持 - 系统 lark-cli `/usr/local/lib/node_modules/@anthropic/lark-cli` 可能支持 - 待确认:服务器是否已 `lark-cli auth login --as user` ### 19:00 陈逸鸫派 Image2 生图任务 **任务:** L1-S1-U1《秘密基地》5 课投放用小地图底图 - 模型gpt-image-2 · 3:4 · 2K · 不要文字 - 风格:太阳朋克 + L1 场景 - 5 张 PNGU1-L1U1-L5 - FUNCLOUD_API_KEY 在小研 workspace `.env` 中可用(`fc_eea138933b02b4797ce0779ffb637d8b8a6368db7b435dfdab7b4be1cd254d98` - Brief 文档 `/docx/KsVadUTmooO7yYxHaGmc1R0Bn5b` + 投放手册 `/do",
"recallCount": 14,
"recallCount": 15,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 14,
"totalScore": 15,
"maxScore": 1,
"firstRecalledAt": "2026-05-28T20:51:03.908Z",
"lastRecalledAt": "2026-06-17T13:45:52.647Z",
"lastRecalledAt": "2026-06-24T10:30:44.233Z",
"queryHashes": [
"f22544a8757c",
"2af907cea93d",
@ -797,7 +821,8 @@
"a90ba76a41cf",
"363241a84e3c",
"a87866c0fa75",
"afb2b291577e"
"afb2b291577e",
"ba819d9c7824"
],
"recallDays": [
"2026-05-29",
@ -808,7 +833,8 @@
"2026-06-09",
"2026-06-12",
"2026-06-16",
"2026-06-17"
"2026-06-17",
"2026-06-24"
],
"conceptTags": [
"gpt",
@ -828,26 +854,28 @@
"endLine": 100,
"source": "memory",
"snippet": "3. **check_call→run**`sync_juguang_notes.py` `sheet_write_range``subprocess.check_call` 不支持 `input=`, 改用 `subprocess.run(..., check=True, input=...)` 1b 单天验证通过:`sync_juguang_agents.py --start 2026-05-27 --end 2026-05-27` 写入 12 个单元格成功。全量 27 天在子进程跑,结果待出。 ### 17:12 数据服务边界规则部署 [陈逸鸫] **文档位置:** `docs/xiaoban-data-boundary.md`git@github.com:chenyd11/feishu-database.git — 服务器无 SSH key 无法 pull **三条核心规则(已写入 MEMORY.md** 1. cron pipeline 和帮同事查数分轨,不能混用同一流程 2. 同事请求默认只读;写生产表 CYFTsu 必须 @陈逸鸫 确认 3. 查数结果写「输出区」副本,不改主表 **操作黑名单:** `pipeline.py` / `sync_*` / `run_juguang_*` / `sheets +write`(生产表) / `--promote` **待办:** 建「大麦查询输出」专用表(待陈逸鸫提供 token **待办:** 获取 docs/xiaoban-data-boundary.md §7 Agent 系统提示词(完",
"recallCount": 6,
"recallCount": 7,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 6,
"totalScore": 7,
"maxScore": 1,
"firstRecalledAt": "2026-05-28T20:51:03.908Z",
"lastRecalledAt": "2026-06-20T12:16:19.608Z",
"lastRecalledAt": "2026-06-24T10:19:53.610Z",
"queryHashes": [
"f22544a8757c",
"c3cb24be8923",
"e3108bd5b94c",
"340c1d46da26",
"cf12fd62a5e5",
"015537767f98"
"015537767f98",
"71af49f84084"
],
"recallDays": [
"2026-05-29",
"2026-05-30",
"2026-06-01",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"check-call",
@ -945,15 +973,14 @@
"endLine": 557,
"source": "memory",
"snippet": "- 妙搭发布 `apps +html-publish` 需要 `--as user`bot 模式不支持 - 系统 lark-cli `/usr/local/lib/node_modules/@anthropic/lark-cli` 可能支持 - 待确认:服务器是否已 `lark-cli auth login --as user` ### 19:00 陈逸鸫派 Image2 生图任务 **任务:** L1-S1-U1《秘密基地》5 课投放用小地图底图 - 模型gpt-image-2 · 3:4 · 2K · 不要文字 - 风格:太阳朋克 + L1 场景 - 5 张 PNGU1-L1U1-L5 - FUNCLOUD_API_KEY 在小研 workspace `.env` 中可用(`fc_eea138933b02b4797ce0779ffb637d8b8a6368db7b435dfdab7b4be1cd254d98` - Brief 文档 `/docx/KsVadUTmooO7yYxHaGmc1R0Bn5b` + 投放手册 `/docx/QhYQdz7PvoN7Eaxmhu0c0Q5UnHe` — 均为个人文档AGENTS.md 规则禁止读取 - 素材库入口https://llm-dev.valavala.com/web_tools/material_prod --- ### 19:30 同事数据查询流程演练 [陈逸鸫测试] **场景:模拟王虹茗请求小龙 4/21-5/20 订单详情,验证三级查询流程** **小龙订单查询结果数据源3wcle8 销售订",
"recallCount": 45,
"recallCount": 46,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 45,
"totalScore": 46,
"maxScore": 1,
"firstRecalledAt": "2026-05-28T20:51:03.908Z",
"lastRecalledAt": "2026-06-23T02:56:08.768Z",
"lastRecalledAt": "2026-06-24T10:30:44.233Z",
"queryHashes": [
"faadf692331b",
"5a7cf6fdc679",
"c2163a583b15",
"d8398d9d484d",
@ -984,7 +1011,8 @@
"36a3a08fbf3c",
"0f79cfc4c413",
"afb2b291577e",
"3c0d7180aff9"
"3c0d7180aff9",
"ba819d9c7824"
],
"recallDays": [
"2026-05-29",
@ -1001,7 +1029,8 @@
"2026-06-15",
"2026-06-16",
"2026-06-17",
"2026-06-23"
"2026-06-23",
"2026-06-24"
],
"conceptTags": [
"gpt",
@ -1330,15 +1359,14 @@
"endLine": 580,
"source": "memory",
"snippet": "- 18 单 | GMV ¥44,375 | 退款 ¥0 - 渠道:销转 11 / 达人 4 / 端内 3 - 客单价¥599×2 / ¥1,999×9 / ¥3,598×7 - 订单日期分布在 11 天04/2305/15 **看板发布全流程梳理:** - 服务器 build HTML + Base sync → DM 陈逸鸫 → Mac 妙搭 `html-publish` + `access-scope-set` - 三个看板 App ID漏斗 `app_4k886pmc9x6yt` · 指挥舱 `app_4k79smc6fa1kf` · 销售 `app_4k7qkz9wrga74` - 当前 cron 不自动发布看板,仅 build HTML **王虹茗身份确认:** wanghongming@makee.comuser_id 未获取 - 尝试 `lark-cli contact +search-user wanghongming@makee.com` → 失败bot API 缺少 `search:user` scope - 替代方案:让她发消息给大麦(系统自动获取 user_id或陈逸鸫截图资料页 **同事查询三级场景定稿:** 1. 常规只读 → 直接查append 输出表 2. 权限外用户 → 先通知业务负责人,再决定是否返回 3. 写操作 → 回复「这会影响生产数据,已转 @陈逸鸫 确认」 ### 20:20 行课转化分析 [陈逸鸫需求] **需求:行课记录新增当日进线→当天行课 + 7天首课率 + 销售排名 + 日报展示** **",
"recallCount": 34,
"recallCount": 35,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 34,
"totalScore": 35,
"maxScore": 1,
"firstRecalledAt": "2026-05-29T06:11:40.432Z",
"lastRecalledAt": "2026-06-23T02:56:08.768Z",
"lastRecalledAt": "2026-06-24T10:19:53.610Z",
"queryHashes": [
"f7ae50ae228d",
"833509d09ccb",
"5b675d96f1da",
"b8b71654e7aa",
@ -1369,7 +1397,8 @@
"36a3a08fbf3c",
"0f79cfc4c413",
"0352a9c943a0",
"3c0d7180aff9"
"3c0d7180aff9",
"71af49f84084"
],
"recallDays": [
"2026-05-29",
@ -1384,7 +1413,8 @@
"2026-06-15",
"2026-06-16",
"2026-06-17",
"2026-06-23"
"2026-06-23",
"2026-06-24"
],
"conceptTags": [
"04/23",
@ -1686,25 +1716,27 @@
"endLine": 624,
"source": "memory",
"snippet": "6. **build_pipeline 脚本** — 新增 `scripts/compute_lesson_activation.py`PG→2aNzzy V/W **关键数据源映射:** 进线=2aNzzy C列日期 → 用户ID=2aNzzy → PG user_course_detail 首课日期 → 比对同天 → 写回 2aNzzy V/W ### 待办汇总 1. 📋 陈逸鸫确认行课转化改动方案 → 一口气改 6 处 2. 📋 王虹茗 user_id 获取(需她发消息或陈逸鸫截图) 3. 📋 数据转发王虹茗 + 写入大麦查询输出表 fd42b8 4. 📋 销售看板 build 挂住问题排查 5. 📋 Image2 生图任务执行 6. ⏳ 全量 pipeline 聚光验证(子进程) ### 20:52 行课转化全量改动完成 [陈逸鸫确认] 行课记录新增指标:当日进线→当天行课 + 7天首课6处改动已完成4处 | # | 位置 | 改动 | 状态 | |---|------|------|------| | 1 | 2aNzzy | V/W/X 三列(首课日期/当日行课/7日内行课 | ✅ | | 2 | 3PRySY | AE-AH 四列(当日行课/当日行课率/7日内首课/7日内首课率| ✅ | | 3 | C1HVN2 | 合并 三+五 →「线索→行课转化」| ✅ | | 4 | Base | 行课销售月(4)/5月漏斗(3)/销转销售月(2) 加字段 | ⚠️ bot权限不足需手动 | | 5 | funnel-daily 看板 |",
"recallCount": 5,
"recallCount": 6,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 5,
"totalScore": 6,
"maxScore": 1,
"firstRecalledAt": "2026-05-31T23:25:36.480Z",
"lastRecalledAt": "2026-06-20T12:04:19.862Z",
"lastRecalledAt": "2026-06-24T10:19:53.610Z",
"queryHashes": [
"679cdd7bd3a8",
"6d1afbed352e",
"872ac2ac6438",
"72255b156849",
"38aaeeb23a92"
"38aaeeb23a92",
"71af49f84084"
],
"recallDays": [
"2026-06-01",
"2026-06-12",
"2026-06-18",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"build-pipeline",
@ -1821,21 +1853,23 @@
"endLine": 649,
"source": "memory",
"snippet": "| 4 | Base | 行课销售月(4)/5月漏斗(3)/销转销售月(2) 加字段 | ⚠️ bot权限不足需手动 | | 5 | funnel-daily 看板 | 加行课转化指标 | 📋 待 build 脚本改 | | 6 | build 脚本 | compute_lesson_activation.py | ✅ | **合并后的 C1HVN2 Section 三:** ``` 三、线索→行课转化5月27日 指标 小龙 吴迪 Bob Tom 日汇总 企微新增 13 6 14 15 48 当日行课 0 0 0 0 0 当日行课率 0% 0% 0% 0% 0% 7天线索→首课 1 2 2 2 10 7天首课率 7.7% 33.3% 14.3% 13.3% 20.8% ``` **5月全月数据3PRySY 公式自动计算):** Bob 当日行课率 7.4% > Tom 3.7% > 其余 0%7天首课率 吴迪 28.6% > 小龙 14.3% **关键实现细节:** - 2aNzzy V: PG chapter_settlement_data 最早日期 - 2aNzzy W: 进线当天有 chapter 活动=1 - 2aNzzy X: 进线 7 天内有 chapter 活动=1 - 3PRySY",
"recallCount": 3,
"recallCount": 4,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 3,
"totalScore": 4,
"maxScore": 1,
"firstRecalledAt": "2026-05-31T23:27:40.582Z",
"lastRecalledAt": "2026-06-20T12:04:19.862Z",
"lastRecalledAt": "2026-06-24T10:19:53.610Z",
"queryHashes": [
"0be022b45645",
"72255b156849",
"38aaeeb23a92"
"38aaeeb23a92",
"71af49f84084"
],
"recallDays": [
"2026-06-01",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"funnel-daily",
@ -2187,19 +2221,21 @@
"endLine": 20,
"source": "memory",
"snippet": "# 2026-06-03 工作日志 ## 招聘:商务经理候选人筛选 ### 李泓晔 一面评价 [王虹茗反馈] - **结论:不通过** - 太油了,管理成本高 - 塑品能力不行 - 如果做得不好会出去说瓦拉产品不行(甩锅风险) - 商务能力和资源应该还好,主要是风格不匹配 ### 大麦自省:简历分析的盲区 [2026-06-04] - **数字会骗人:** 看到GMV 1200w+、6倍增长就默认硬实力强但面试中\"太油了\"恰恰说明此人擅长包装。简历数据要打折扣看。 - **\"能挑毛病\"≠\"懂产品\"** 面试纪要里批评公司产品,我当时解读为深度理解,但面试官看到的是甩锅前置——\"做不好会说产品不行\"。 - **风格匹配和硬能力同等重要:** 只评了硬能力,完全忽略了管理成本、团队融入、价值观匹配。 - **改进:** 后续分析增加\"风格/文化匹配\"维度,对简历数据标注置信度(高/中/低),面试策略中增加\"风格验证\"类问题。 ### 宋宗佩 一面评价 [王虹茗反馈] - **结论:不通过(本岗位)** - 欠缺达播资源,不具备本岗位相关经验 - 工作内容主要为销售leads课包对渠道情况、合作模式较为了解",
"recallCount": 2,
"recallCount": 3,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"totalScore": 3,
"maxScore": 1,
"firstRecalledAt": "2026-06-09T02:41:10.780Z",
"lastRecalledAt": "2026-06-09T09:31:36.077Z",
"lastRecalledAt": "2026-06-24T11:59:31.718Z",
"queryHashes": [
"fe3739269889",
"73563b5439c1"
"73563b5439c1",
"909cc8f07fe0"
],
"recallDays": [
"2026-06-09"
"2026-06-09",
"2026-06-24"
],
"conceptTags": [
"风格/文化匹配",
@ -2291,20 +2327,22 @@
"endLine": 32,
"source": "memory",
"snippet": "- **改进:** 后续分析增加\"风格/文化匹配\"维度,对简历数据标注置信度(高/中/低),面试策略中增加\"风格验证\"类问题。 ### 宋宗佩 一面评价 [王虹茗反馈] - **结论:不通过(本岗位)** - 欠缺达播资源,不具备本岗位相关经验 - 工作内容主要为销售leads课包对渠道情况、合作模式较为了解 - 能关注到leads后端的承接、转化并进行优化动作 - **后续方向:** 后面做leads课投放的商务渠道时可以再考虑 - **教训:** 简历中\"达人直售商务经理\"\"在线增长负责人\"等title容易让人高估其达播能力实际核心能力在CPA/leads渠道运营而非达人直播资源 ### 简历评估优化点 [王虹茗指示] - 需区分\"达播商务\"和\"leads渠道商务\"两类能力不能仅凭title判断 - 达播商务核心验证:是否有自有达人资源、是否有直播操盘经验、是否有达人分级运营体系 - Leads渠道商务核心验证CPA/CPS渠道拓展、TMK/短信/进校/社群分销、leads转化链路优化 - 宋宗佩案例说明title含\"达人直售\"\"增长负责人\"但实际经验偏leads课包销售非达人直播资源型 ### 候选人状态追踪 | 候选人 | 状态 | 备注 |",
"recallCount": 2,
"recallCount": 3,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"totalScore": 3,
"maxScore": 1,
"firstRecalledAt": "2026-06-09T09:31:36.077Z",
"lastRecalledAt": "2026-06-23T02:56:08.768Z",
"lastRecalledAt": "2026-06-24T11:59:31.718Z",
"queryHashes": [
"73563b5439c1",
"3c0d7180aff9"
"3c0d7180aff9",
"909cc8f07fe0"
],
"recallDays": [
"2026-06-09",
"2026-06-23"
"2026-06-23",
"2026-06-24"
],
"conceptTags": [
"风格/文化匹配",
@ -2517,20 +2555,22 @@
"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-PQ/R未填 - db_info: 2026-06-19 18:21:44 - 使用实习虾 app (cli_aa898f32d4799bea) 的 tenant token 绕过 xiaoban bot 的 sheets:spreadsheet:read 权限缺失 - 注意: clear_range 用空字符串覆盖而非空数组飞书API拒绝空数组",
"recallCount": 2,
"recallCount": 3,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"totalScore": 3,
"maxScore": 1,
"firstRecalledAt": "2026-06-19T12:45:15.278Z",
"lastRecalledAt": "2026-06-20T03:20:58.925Z",
"lastRecalledAt": "2026-06-24T11:59:58.994Z",
"queryHashes": [
"290e7f4aaa74",
"4cbb3d2aa18d"
"4cbb3d2aa18d",
"47c0a198b79d"
],
"recallDays": [
"2026-06-19",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"v2-fill",
@ -2550,20 +2590,22 @@
"endLine": 49,
"source": "memory",
"snippet": "- **大麦**: full_refresh · 手机/UID/行课回填 · 订单汇总 merge · 完成后群回「full_refresh 完成」 - **小溪**: 不再参与 Bot 刷新 ### 5. 验收标准 - gate X = 汇总 W当前 406=406 - 绑单审计 E1E9 全部 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": 2,
"recallCount": 3,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"totalScore": 3,
"maxScore": 1,
"firstRecalledAt": "2026-06-19T12:45:15.278Z",
"lastRecalledAt": "2026-06-20T03:20:58.925Z",
"lastRecalledAt": "2026-06-24T07:58:54.009Z",
"queryHashes": [
"290e7f4aaa74",
"4cbb3d2aa18d"
"4cbb3d2aa18d",
"aa2a119a58a5"
],
"recallDays": [
"2026-06-19",
"2026-06-20"
"2026-06-20",
"2026-06-24"
],
"conceptTags": [
"full-refresh",
@ -2641,6 +2683,37 @@
"0.0",
"14.3"
]
},
"memory:memory/2026-06-16.md:1:37": {
"key": "memory:memory/2026-06-16.md:1:37",
"path": "memory/2026-06-16.md",
"startLine": 1,
"endLine": 37,
"source": "memory",
"snippet": "# 2026-06-16 工作日志 ## 陈逸鸫 - 细水入海 full_refresh v2 定稿 **来源:** 陈逸鸫(`ou_f981d4811369c954b3597908ca93a01c` **核心变更6/16 定稿):** ### 1. 订单汇总列结构变更 - 从 A-X(24列) → A-W(23列):去掉原 W「有效成单」列订单号从 X 左移到 W - V=渠道归属W=订单号 ### 2. 线索只绑有效单 - `pick_valid_order()`: GSV>0 · 非全额退 · K≥C取最新一笔 - Y=1 时 K/L/X/N/O/P/Z 全写有效主单真实值 - 已退单不写旧 X/L - 无有效单 → Y=0K/L/X 留空 ### 3. 汇总 gate 全量覆盖(非 DB 扩行) - 唯一真源 = 三表 Y=1 gate 的 unique X - Step 4 + Step 5 同一 run共用 `pick_valid_order()` + `db_info` - 汇总 W = 三表 Xgate 同源,不是 merge 再查 DB - clear → gate 全量覆盖,不保留旧 W - 同 X 多进线 → 只保留行号最小的 1 行 ### 4. 分工定稿 - **Cursor**: 微伴/旧表同步 · 三键去重 · V/W 公式 · 验单 · 撞库消解 - **大麦**: full_refresh · 手机/UID/行课回填 · 订单汇总 merge · 完成后群回「full_refresh 完成」 - **小溪**:",
"recallCount": 1,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 1,
"maxScore": 1,
"firstRecalledAt": "2026-06-24T07:58:54.009Z",
"lastRecalledAt": "2026-06-24T07:58:54.009Z",
"queryHashes": [
"aa2a119a58a5"
],
"recallDays": [
"2026-06-24"
],
"conceptTags": [
"full-refresh",
"6/16",
"a-x",
"a-w",
"pick-valid-order",
"k/l/x/n/o/p/z",
"x/l",
"k/l/x"
]
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,159 @@
# 手机号 → 账号ID → 角色ID 检索方法
## 数据关系
```
手机号 (明文)
│ XXTEA 加密
tel_encrypt (密文) account_id
│ │
▼ ▼
vala_app_account ──────────► vala_app_character
(账号表) 1:N 关联 (角色表)
```
- **一个账号** (`vala_app_account`) 可以有 **多个角色** (`vala_app_character`)
- 关联字段:`vala_app_character.account_id = vala_app_account.id`
## 数据库
| 项目 | 值 |
|------|-----|
| 数据库 | MySQL 线上环境 |
| Host | `bj-cdb-dh2fkqa0.sql.tencentcdb.com` |
| Port | `27751` |
| 库名 | `vala_user` |
| 用户 | `read_only` |
## 表结构
### vala_app_account账号表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint | 账号ID主键 |
| `tel` | varchar(20) | 手机号(脱敏显示,如 `158****7007` |
| `tel_encrypt` | varchar(100) | 手机号密文(用于精确匹配) |
### vala_app_character角色表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint | 角色ID主键 |
| `account_id` | bigint | 所属账号ID |
| `nickname` | varchar(20) | 角色昵称 |
| `gender` | tinyint(1) | 性别 |
| `birthday` | varchar(50) | 生日 |
| `purchase_season_package` | text | 已购赛季包 |
## 手机号加密方式
手机号在数据库中以密文存储,加密算法为 **XXTEA + Base64 URL-safe**
### 加密参数
| 参数 | 值 |
|------|-----|
| 算法 | XXTEA |
| 密钥 | `K1pNOZ5O5+ZqTPSHA2kzPdoNOMOGcv6g` |
| 编码 | Base64标准 → URL-safe`+`→`-`, `/`→`_`, `=`→`.` |
### Python 加密代码
```python
import xxtea
import base64
KEY = "K1pNOZ5O5+ZqTPSHA2kzPdoNOMOGcv6g"
def encrypt_phone(phone: str) -> str:
"""加密明文手机号,返回与数据库 tel_encrypt 字段一致的密文"""
encrypted = xxtea.encrypt(phone.encode(), KEY.encode())
result = base64.b64encode(encrypted).decode()
result = result.replace("+", "-").replace("/", "_").replace("=", ".")
return result
def decrypt_phone(encrypted: str) -> str:
"""解密 tel_encrypt 还原明文手机号"""
restored = encrypted.replace("-", "+").replace("_", "/").replace(".", "=")
decrypted = xxtea.decrypt(base64.b64decode(restored), KEY.encode())
return decrypted.decode()
```
### 加密示例
| 明文手机号 | 密文 (tel_encrypt) |
|-----------|-------------------|
| `15849377007` | `CxMOc6z56aYjE73r8OSAog..` |
## 查询步骤
### 步骤 1加密手机号
```bash
python3 -c "
import xxtea, base64
KEY = 'K1pNOZ5O5+ZqTPSHA2kzPdoNOMOGcv6g'
phone = '15849377007'
encrypted = xxtea.encrypt(phone.encode(), KEY.encode())
result = base64.b64encode(encrypted).decode()
print(result.replace('+', '-').replace('/', '_').replace('=', '.'))
"
# 输出: CxMOc6z56aYjE73r8OSAog..
```
### 步骤 2用密文查询账号和角色
```sql
SELECT
a.id AS account_id,
a.tel,
c.id AS character_id,
c.nickname,
c.gender,
c.birthday,
c.purchase_season_package,
c.created_at
FROM vala_app_account a
LEFT JOIN vala_app_character c ON c.account_id = a.id
WHERE a.tel_encrypt = 'CxMOc6z56aYjE73r8OSAog..';
```
### 步骤 3解读结果
```
account_id tel character_id nickname gender birthday purchase_season_package
18279 158****7007 23600 Morris 1 2021-09-09 [16,17,18,19,20]
18279 158****7007 23686 Nathan 1 2018-03-13 [16]
```
- **账号ID**: 18279
- **角色**: 23600 (Morris)、23686 (Nathan)
- 一个账号下可能有多个角色(一个孩子一个角色)
## 完整查询脚本
`scripts/phone_encrypt.py` 已封装加密/解密/MD5 功能,可直接使用:
```python
from phone_encrypt import encrypt_phone, decrypt_phone, phone_md5
# 加密
cipher = encrypt_phone("15849377007")
# → "CxMOc6z56aYjE73r8OSAog.."
# 解密
phone = decrypt_phone(cipher)
# → "15849377007"
# MD5用于跨系统关联
md5 = phone_md5("15849377007")
```
## 注意事项
1. **tel 字段是脱敏的**(如 `158****7007`),不能直接用于精确匹配
2. **必须用 tel_encrypt 密文匹配**,密文由 XXTEA 加密生成
3. **一个账号可以有多个角色**,查询结果可能返回多行
4. 测试环境和线上环境的 `tel_encrypt` 值相同(加密算法一致)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,376 @@
#!/usr/bin/env python3
"""
中互动组件按类型聚合统计 + 元数据导出逐表聚合版
策略逐表查询+聚合Python 端合并避免大 UNION ALL 超时
"""
import argparse
import json
import os
import sys
from collections import defaultdict
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple
import psycopg2
import pymysql
import pandas as pd
C_TYPE_NAME_MAPPING = {
"mid_dialog_choose": "对话选择",
"mid_dialog_express": "对话表达",
"mid_dialog_fillin": "对话填空",
"mid_dialog_repeat": "对话跟读",
"mid_dialog_select": "对话选择",
"mid_dialog_sentence": "对话组句",
"mid_image_choose": "图片选择",
"mid_image_drag": "图片拖拽",
"mid_image_multiple": "图片多选",
"mid_image_sequence": "图片排序",
"mid_message_combine": "消息组合",
"mid_message_fillin": "消息填空",
"mid_message_sentence": "消息组句",
"mid_message_spell": "消息拼写",
"mid_message_trace": "消息描红",
"mid_message_word": "消息选词",
"mid_grammar_cloze": "语法挖空",
"mid_grammar_sentence": "语法组句",
"mid_pron_pron": "发音互动",
"mid_sentence_dialogue": "句子对话",
"mid_sentence_makeSentence": "句子造句",
"mid_sentence_material": "句子材料",
"mid_sentence_voice": "句子语音",
"mid_vocab_fillBlank": "词汇填空",
"mid_vocab_image": "词汇图片",
"mid_vocab_instruction": "词汇指令",
"mid_vocab_item": "词汇物品",
}
def get_pg_conn():
return psycopg2.connect(
host=os.getenv("PG_DB_HOST"),
port=os.getenv("PG_DB_PORT"),
user=os.getenv("PG_DB_USER"),
password=os.getenv("PG_DB_PASSWORD"),
database=os.getenv("PG_DB_DATABASE"),
)
def get_mysql_conn():
return pymysql.connect(
host=os.getenv("MYSQL_HOST"),
user=os.getenv("MYSQL_USERNAME"),
password=os.getenv("MYSQL_PASSWORD"),
database="vala_test",
port=int(os.getenv("MYSQL_PORT", 3306)),
charset="utf8mb4",
)
def count_help_actions(user_behavior_info: Any) -> int:
if not user_behavior_info:
return 0
try:
if isinstance(user_behavior_info, str):
data = json.loads(user_behavior_info)
elif isinstance(user_behavior_info, list):
data = user_behavior_info
else:
return 0
if not isinstance(data, list):
return 0
count = 0
for item in data:
if isinstance(item, dict):
opt = item.get("submitOpt", "")
if opt and "help" in str(opt).lower():
count += 1
return count
except (json.JSONDecodeError, TypeError):
return 0
def query_table_aggregation(cursor, table: str) -> Dict[str, Dict]:
"""查询单表的聚合数据"""
query = f"""
SELECT
c_type,
COUNT(*) AS total_records,
COUNT(DISTINCT user_id) AS user_count,
SUM(CASE WHEN play_result = 'Perfect' THEN 1 ELSE 0 END) AS perfect_count,
SUM(CASE WHEN play_result = 'Pass' THEN 1 ELSE 0 END) AS pass_count,
SUM(CASE WHEN play_result IN ('Oops', 'Opps') THEN 1 ELSE 0 END) AS oops_count,
SUM(CASE WHEN play_result = 'None' THEN 1 ELSE 0 END) AS none_count,
AVG(interval_time) AS avg_interval_time,
SUM(interval_time) AS sum_interval_time
FROM {table}
WHERE c_type LIKE 'mid_%%' AND c_type != ''
AND play_status = 1
GROUP BY c_type
"""
cursor.execute(query)
result = {}
for row in cursor.fetchall():
ct = row[0]
result[ct] = {
"total_records": int(row[1]),
"user_count": int(row[2]),
"perfect_count": int(row[3]),
"pass_count": int(row[4]),
"oops_count": int(row[5]),
"none_count": int(row[6]),
"avg_interval_time": float(row[7]) if row[7] else 0,
"sum_interval_time": float(row[8]) if row[8] else 0,
}
return result
def query_table_exit(cursor, table: str) -> Dict[str, int]:
"""查询单表的退出数"""
query = f"""
SELECT c_type, COUNT(*) as cnt
FROM {table}
WHERE c_type LIKE 'mid_%%' AND c_type != ''
AND play_status = 2
GROUP BY c_type
"""
cursor.execute(query)
return {row[0]: int(row[1]) for row in cursor.fetchall()}
def query_table_help_samples(cursor, table: str, limit: int = 5000) -> Dict[str, List[int]]:
"""从单表抽样查询帮助次数"""
query = f"""
SELECT c_type, user_behavior_info
FROM {table}
WHERE c_type LIKE 'mid_%%' AND c_type != ''
AND play_status = 1
AND user_behavior_info IS NOT NULL
AND user_behavior_info != '[]'
LIMIT {limit}
"""
cursor.execute(query)
result: Dict[str, List[int]] = defaultdict(list)
for row in cursor.fetchall():
ct, ubi = row[0], row[1]
result[ct].append(count_help_actions(ubi))
return dict(result)
def query_table_used_ids(cursor, table: str) -> Set[Tuple[str, str]]:
"""查询单表已使用的组件ID"""
query = f"""
SELECT DISTINCT c_type, c_id
FROM {table}
WHERE c_type LIKE 'mid_%%' AND c_type != ''
AND play_status = 1
"""
cursor.execute(query)
return {(row[0], str(row[1])) for row in cursor.fetchall()}
def merge_aggregations(per_table: List[Dict[str, Dict]]) -> Dict[str, Dict]:
"""合并多表聚合结果"""
merged: Dict[str, Dict] = {}
for table_data in per_table:
for ct, d in table_data.items():
if ct not in merged:
merged[ct] = {
"total_records": 0,
"user_count": 0,
"perfect_count": 0,
"pass_count": 0,
"oops_count": 0,
"none_count": 0,
"sum_interval_time": 0.0,
"interval_count": 0,
}
m = merged[ct]
m["total_records"] += d["total_records"]
m["perfect_count"] += d["perfect_count"]
m["pass_count"] += d["pass_count"]
m["oops_count"] += d["oops_count"]
m["none_count"] += d["none_count"]
m["sum_interval_time"] += d["sum_interval_time"]
m["interval_count"] += d["total_records"]
return merged
def main():
parser = argparse.ArgumentParser(description="中互动组件按类型聚合统计")
parser.add_argument("--output-dir", default="output", help="输出目录")
args = parser.parse_args()
print("=" * 60)
print("中互动组件按类型聚合统计(逐表聚合版)")
print("=" * 60)
pg_conn = get_pg_conn()
cursor = pg_conn.cursor()
# ===== 1. 逐表查询聚合 + 退出 + 帮助 + 已用ID =====
print("\n[1/4] 逐表查询...")
all_aggs = []
all_exits: Dict[str, int] = {}
all_helps: Dict[str, List[int]] = defaultdict(list)
all_used_ids: Set[Tuple[str, str]] = set()
all_used_ids_by_type: Dict[str, Set[str]] = defaultdict(set)
for i in range(8):
table = f"user_component_play_record_{i}"
print(f" {table}...", end=" ", flush=True)
agg = query_table_aggregation(cursor, table)
exits = query_table_exit(cursor, table)
helps = query_table_help_samples(cursor, table)
ids = query_table_used_ids(cursor, table)
all_aggs.append(agg)
for ct, cnt in exits.items():
all_exits[ct] = all_exits.get(ct, 0) + cnt
for ct, vals in helps.items():
all_helps[ct].extend(vals)
all_used_ids.update(ids)
for ct, cid in ids:
all_used_ids_by_type[ct].add(cid)
total = sum(d["total_records"] for d in agg.values())
print(f"完成({total}条, {len(agg)}种类型)")
cursor.close()
pg_conn.close()
# ===== 2. 合并聚合结果 =====
print("\n[2/4] 合并聚合结果...")
merged = merge_aggregations(all_aggs)
# 计算 user_count需要去重这里用近似取最大值
# 实际 user_count 跨表可能重复,这里用 SQL 再查一次精确值
print(" 计算精确用户数...")
pg_conn2 = get_pg_conn()
cursor2 = pg_conn2.cursor()
for ct in merged:
# 精确查询每种类型的去重用户数
parts = []
for i in range(8):
parts.append(f"""
SELECT DISTINCT user_id FROM user_component_play_record_{i}
WHERE c_type = %s AND play_status = 1
""")
union = " UNION ".join(parts)
query = f"SELECT COUNT(*) FROM ({union}) AS u"
cursor2.execute(query, tuple([ct] * 8))
merged[ct]["user_count"] = cursor2.fetchone()[0]
cursor2.close()
pg_conn2.close()
# ===== 3. 构建输出 DataFrame =====
print("\n[3/4] 构建输出...")
rows = []
for ct, m in sorted(merged.items(), key=lambda x: x[1]["total_records"], reverse=True):
total = m["total_records"]
perfect = m["perfect_count"]
passed = m["pass_count"]
oops = m["oops_count"]
exit_cnt = all_exits.get(ct, 0)
perfect_pct = perfect / total * 100 if total > 0 else 0
pass_pct = passed / total * 100 if total > 0 else 0
oops_pct = oops / total * 100 if total > 0 else 0
exit_pct = exit_cnt / (total + exit_cnt) * 100 if (total + exit_cnt) > 0 else 0
helps = all_helps.get(ct, [])
avg_help = sum(helps) / len(helps) if helps else 0
avg_time = m["sum_interval_time"] / m["interval_count"] if m["interval_count"] > 0 else 0
avg_time_sec = avg_time / 1000
display_name = C_TYPE_NAME_MAPPING.get(ct, ct)
rows.append({
"组件类型(c_type)": ct,
"组件名称": display_name,
"组件个数": len(all_used_ids_by_type.get(ct, set())),
"总完成人数": m["user_count"],
"总记录数": total,
"Perfect数": perfect,
"Perfect率": f"{perfect_pct:.1f}%",
"Pass数": passed,
"Pass率": f"{pass_pct:.1f}%",
"Oops数": oops,
"Oops率": f"{oops_pct:.1f}%",
"退出数": exit_cnt,
"退出率": f"{exit_pct:.1f}%",
"平均帮助次数": round(avg_help, 2),
"平均耗时(秒)": round(avg_time_sec, 1),
})
result_df = pd.DataFrame(rows)
# ===== 4. 获取 MySQL 元数据 =====
print(" 获取 MySQL 组件元数据...")
mysql_conn = get_mysql_conn()
try:
query = """
SELECT c_type, c_id, title, component_config,
audio_list, text_analysis, related_path,
created_at, updated_at
FROM middle_interaction_component
WHERE c_type IS NOT NULL AND c_type != ''
ORDER BY c_type, c_id
"""
meta_df = pd.read_sql_query(query, mysql_conn)
finally:
mysql_conn.close()
meta_df["_key"] = meta_df.apply(lambda r: (r["c_type"], str(r["c_id"])), axis=1)
meta_df = meta_df[meta_df["_key"].isin(all_used_ids)].drop(columns=["_key"])
meta_df["组件名称"] = meta_df["c_type"].map(C_TYPE_NAME_MAPPING).fillna(meta_df["c_type"])
meta_cols = ["c_type", "组件名称", "c_id", "title", "component_config",
"audio_list", "text_analysis", "related_path", "created_at", "updated_at"]
meta_df = meta_df[meta_cols]
# ===== 5. 输出 Excel =====
print("\n[4/4] 输出 Excel...")
os.makedirs(args.output_dir, exist_ok=True)
date_str = datetime.now().strftime("%Y%m%d")
filename = f"中互动组件聚合统计_{date_str}.xlsx"
output_path = os.path.join(args.output_dir, filename)
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
result_df.to_excel(writer, sheet_name="按类型聚合统计", index=False)
meta_df.to_excel(writer, sheet_name="组件元数据明细", index=False)
from openpyxl.utils import get_column_letter
for sheet_name in ["按类型聚合统计", "组件元数据明细"]:
ws = writer.sheets[sheet_name]
for col_idx, col_cells in enumerate(ws.columns, 1):
max_len = 0
for cell in col_cells:
if cell.value:
val = str(cell.value)
char_len = sum(2 if ord(c) > 127 else 1 for c in val)
max_len = max(max_len, char_len)
ws.column_dimensions[get_column_letter(col_idx)].width = min(max_len + 4, 60)
print(f"\n{'=' * 60}")
print(f"导出完成!")
print(f"文件: {output_path}")
print(f"Sheet1「按类型聚合统计」: {len(result_df)}")
print(f"Sheet2「组件元数据明细」: {len(meta_df)}")
print(f"{'=' * 60}")
print("\n📊 聚合统计预览:")
print(result_df[["组件名称", "组件个数", "总完成人数", "总记录数",
"Perfect率", "Pass率", "Oops率", "退出率",
"平均帮助次数", "平均耗时(秒)"]].to_string(index=False))
if __name__ == "__main__":
main()

2
tmp_daily_summary.md Normal file
View File

@ -0,0 +1,2 @@
=== 每日总结 20260625 ===
## 昨日关键进展