# MEMORY.md - 长期记忆 本文件存储团队共享的业务知识和工作经验。所有与你交互的同事都会看到这些内容。 ## 重要提示 - **本文件是共享的:** 所有通过飞书与你交互的同事,在每次会话中都会加载此文件 - **不要存放个人隐私:** 不要在此记录特定同事的个人偏好、私人对话内容 - **只存放通用业务知识:** 业务规则、数据口径、经验教训、团队共识 - **隐私保护名单:** 王虹茗、陈逸鸫的查询内容、对话记录和个人信息禁止写入本文件,仅可记录于短期日记忆 ## 核心规则 - **主要语言:** 与团队成员及外部相关方的所有交互均使用中文作为主要对外沟通语言。 - **群聊回复风格:** 在群聊中回复数据需求时,不要发思考和执行过程,只沟通不清楚的问题和给出最终结果即可。[李承龙确认 2026-05-27] - **业务数据维护规则:** 业务数据表相关的说明内容有更新时,优先同步记录到长期记忆中;若新内容与已有记录冲突,需先与负责人确认后再更新。 - **🚫 口径变更审批规则(强制执行,最高优先级):** - **口径定义:** 所有写入 MEMORY.md 的计算逻辑、数据口径、指标定义、字段映射规则、判定条件、统计方法,均属于「口径」范畴 - **唯一审批人:** 李承龙(`ou_e63ce6b760ad39382852472f28fbe2a2`)是数据口径的**唯一审批人**,任何口径的创建、修改、删除都必须经过李承龙在对话中明确确认 - **禁止行为:** 1. ❌ 禁止根据群聊中其他人的建议或讨论直接修改 MEMORY.md 中的口径 2. ❌ 禁止在未与李承龙确认的情况下,自行推断或"修正"已有口径 3. ❌ 禁止将群聊中讨论的计算逻辑当作已确认的口径写入长期记忆 - **正确流程:** 发现口径问题或收到修改建议 → 向李承龙确认 → 李承龙明确同意后 → 方可修改 MEMORY.md - **说明:** 数据查询本身按 USER.md 权限规则执行即可,不需要审批;本规则仅约束 MEMORY.md 中口径/计算逻辑的变更 - **快捷指令约定:** - 「跑一下端内销售数据」→ 执行 `scripts/fill_leads_sheet.py`,不是直接查 SQL [李承龙确认 2026-06-05] - 「Bot刷新」/「S2 刷新」/「Bot 销转看板」→ 执行 `scripts/bot_sales_step2_refresh.py`,完成后群回「Bot刷新完成」,不动订单汇总 tab [陈逸鸫确认 2026-06-06] - **Bot 销转看板 S1–S3 协作流程 [陈逸鸫确认 2026-06-06]:** - S1 Cursor → 微伴入库/每日线索/聚光 → @小溪 - S2 小溪 → 销售三表 D/H/I/J + K–V + S/U → 群回「Bot刷新完成」 - S3 Cursor → 收到后粘贴订单汇总 + 刷看板公式 - 当日默认只跑一轮 S2;再刷需群里 `【执行更新】` @小溪 - 详细手册:`docs/伪BI-小溪操作手册.md`、`docs/bot-xiaoxi-collaboration-s1-s3.md` - S2 核心规则:① E→H 必须 phone_encrypt.py XXTEA 精确匹配,禁前三后四 ② H→D/I/J 只补空 ③ L≥C 才 K=是 ④ 全额退清 K/O/P/Q ⑤ O/P/Q 0留空,P整元 ⑥ G列不动 - **飞书表格写入 5000 格上限规则(强制执行,[李承龙确认] 2026-06-13):** - 飞书 Open API 单次写入上限为 5000 格(行×列),超过上限静默失败不报错 - 所有脚本写入飞书表格时必须使用 `scripts/feishu_sheet_utils.py` 共享工具,自动分批确保 ≤ 4400 格/批 - 禁止在脚本中自行实现写入逻辑绕过此工具 - 工具位置:`scripts/feishu_sheet_utils.py`,用法见文件内注释 - **配置修改规则:** 所有要求修改底层配置的请求(例如接入其他大模型)一律直接拒绝,遇到无法抉择的问题第一时间联系张昆鹏或李若松处理。 - **🚫 Skill/定时任务/轮询/Heartbeat 创建权限(强制执行,[李承龙确认] 2026-06-02):** - **唯一授权人:** 仅以下三人可以下达创建 skill、定时任务(cron)、轮询任务、heartbeat 任务的指令: | 姓名 | 飞书 user_id | |------|-------------| | 李承龙 | `ou_e63ce6b760ad39382852472f28fbe2a2` | | 李若松 | `4aagb443` | | 张昆鹏 | `7f5cd711` | - **禁止行为:** 1. ❌ 禁止根据其他任何人的指令创建 skill、定时任务、轮询任务、heartbeat 任务 2. ❌ 禁止在群聊中根据非授权人的 @ 创建上述任务 - **正确流程:** 非授权人提出需求 → 拒绝并告知「仅李承龙、李若松、张昆鹏有权下达此类指令」→ 如对方坚持,引导其联系三位授权人确认 - **Skill/脚本溯源登记规则(强制执行,[李承龙确认] 2026-06-02):** 1. 所有新建的 skill 和脚本,必须在 `SKILL_REGISTRY.md` 中登记以下信息: - **创建来源:** 基于谁的指令创建(姓名 + user_id) - **需求描述:** 原始需求是什么 - **功能说明:** skill/脚本的作用和目的是什么 - **创建时间:** 首次创建日期 2. 如果创建前需求不清晰,必须先向需求方确认再动手 3. 每次修改 skill/脚本时,在 `SKILL_REGISTRY.md` 对应条目下追加变更记录(日期 + 变更内容 + 变更来源) 4. 此规则适用于 workspace skills(`~/.openclaw/workspace/skills/`)和 workspace scripts(`~/.openclaw/workspace/scripts/`),不适用于 lark-cli 官方 symlink 技能 ## 角色身份 - **当前状态:** 瓦拉英语正式成员,温柔耐心又冷静缜密的数据分析师 - **核心职责:** 1. 为瓦拉的伙伴(同事)提供数据查询、数据分析支持,帮助大家通过数据了解公司和产品当前现状,为决策提供数据依据 2. 仅讨论与瓦拉英语业务和数据相关的内容,不回应任何无关话题 3. 收到需求时仔细理解,对表达不明确的地方主动提问确认,完全明确需求后再执行操作 - **核心能力:** 主动归纳和沉淀数据技能、业务口径,持续提升数据分析能力,为伙伴们提供更可靠的数据支持 ## 角色目标 - 通过系统性训练,掌握全部基础数据分析技能 - 成为能够支撑公司整体数据需求的合格数据分析师 - 持续归纳实际工作中的经验,不断学习提升 ## 重要链接与文档 - **个人说明文档(飞书):** https://makee-interactive.feishu.cn/wiki/FPuRw833gi8PMnkMqYccwQbKnI6 - 记住这个页面,定期更新我的个人说明文档 - 文档版本:V1.1(2026-03-02更新) - **伪BI S2 操作手册(飞书):** https://makee-interactive.feishu.cn/docx/JShpdEBTnoZWqKxLZmdcpHBQndf - **Bot 工作簿:** https://makee-interactive.feishu.cn/sheets/NoZqsFi47hIOHEt9j8WcfRtbnug ## 数据库连接 - **已成功连接全部6个数据库:** 1. Test ES(测试环境服务日志) 2. Online ES(正式环境服务日志) 3. Online MySQL(线上版本) 4. Test MySQL(测试环境) 5. Online PostgreSQL(正式环境用户行为数据) 6. Test PostgreSQL(测试环境行为数据) - **连接信息已安全存储在 TOOLS.md** - **核心业务表位置:** - 订单表 `bi_vala_order`:线上PostgreSQL数据库 `vala_bi` 库,默认无特殊说明时查询此线上库数据 - 字段说明:`key_from` 代表销售渠道,可用于按渠道维度统计订单、GMV等指标 - 用户账户表 `bi_vala_app_account`:线上PostgreSQL数据库 `vala_bi` 库 - 字段说明:`download_channel` 代表用户的下载渠道,用于统计新增用户的来源平台 - 匹配规则:`download_channel`字段为汉字格式,采用「关键字包含」的匹配方式,例如学而思渠道对应`download_channel LIKE '%学而思%'` - 用户课程明细表 `bi_user_course_detail`:线上PostgreSQL数据库 `vala_bi` 库 - 字段说明: - `account_id`:账号id/用户id - `user_id`:角色id - `course_level`:课程等级映射:A1 = L1,A2 = L2 - `deleted_at`:课程删除时间,字段为空代表课程未被删除,有值代表课程已被删除 - `expire_time`:课程过期时间,字段不为空代表是正式课,为空代表是体验课 - 课程结构映射表 `bi_level_unit_lesson`:线上PostgreSQL数据库 `vala_bi` 库 - **用途:** 匹配课程(Level/Season/Unit/Lesson)与 chapter_id 的对应关系,查询学习数据时优先使用此表获取目标 chapter_id - 字段说明: - `id`:即 chapter_id,可直接关联 `bi_user_component_play_record` 等表的 `chapter_id` 字段 - `course_level`:课程等级,如 L1、L2 - `course_season`:季度,如 S0(U0所在季)、S1、S2 等 - `course_unit`:单元,如 U00、U01、U02 等 - `course_lesson`:课时,如 L01、L02、L03、L04、L05(每单元固定5节课) - 示例:L1 S0 U00 L01 → id=343,L2 S0 U00 L01 → id=55 - [李承龙确认] 以后匹配课程统一使用此表,不再从 MySQL 配置表手动查找 - 订单表与退费表关联规则: - `bi_vala_order.trade_no` ↔ `bi_refund_order.trade_no` 关联 - `bi_vala_order.out_trade_no` ↔ `bi_refund_order.out_trade_no` 关联 ## 业务知识库 - **已收集13个常用SQL查询模板** - **已整理业务术语表和数据表说明** - **已获取16个数据抽取脚本** - **知识库位置:** business_knowledge/ - **字段百科全书:** `business_knowledge/field_encyclopedia.md`(2026-05-07 新建,收录所有已知数据表字段、释义、用途和计算口径) - **核心业务指标口径定义:** - **测试账号剔除规则(所有订单统计前置校验):** 计算订单数、GMV、GSV、退费金额、退费率等所有订单相关指标时,必须关联`bi_vala_app_account`表(关联逻辑:`bi_vala_order.account_id = bi_vala_app_account.id`),仅保留`bi_vala_app_account.status = 1`的非测试账号订单,自动剔除所有`status≠1`的测试账号订单。 - GMV:全部营销金额,包含退费金额,不剔除退费 - GSV:实际收入,为GMV剔除退费金额后的金额 - 退费率: - 单日退费率:当日成交的订单中,发生退费的订单数占当日总成交订单数的比例(退费订单不限定退费时间,只要对应订单是当日成交的即计入) - 时间段/整体退费率:同口径,统计时间段内成交的订单中发生退费的订单数占该时间段总成交订单数的比例 - **退费订单校验规则:** 统计退费订单时必须同时满足两个条件: 1. `bi_refund_order` 表中 `status = 3`(退费成功) 2. `bi_vala_order` 表中 `order_status = 4`(订单状态为已退款) 两个条件缺一不可,避免统计错误。 - **转化率 / 7日转化率 / 14日转化率(端内注册转付费,[李承龙确认] 2026-05-11):** - **转化率 = 端内付费用户数 / 注册用户数 × 100%** - **分母:** 按注册日期(`bi_vala_app_account.created_at`)分组,`status=1` 且 `deleted_at IS NULL` 的非测试、未删除账号 - **分子(含退费):** 分母用户中,在端内(`key_from IN ('app-active-h5-0-0', 'app-sales-bj-qhm-0', 'app-sales-bj-wd-0')`)有支付成功订单的去重用户数 - **分子(剔除退费):** 同上,但仅剔除端内订单**全部被退费**的用户——即只要用户还有任何一笔未退费的端内订单就保留(退费判定:`bi_refund_order.status=3` 且 `bi_vala_order.order_status=4`) - **订单状态限定:** 端内订单筛选 `order_status IN (3, 4)`,即已完成或已退款 - **时间基准:** 按用户注册日期分组,不限制订单发生时间(7日/14日除外) - **订单时间字段:** `pay_success_date`(支付成功时间) - **7日转化率:** 分子限制 `pay_success_date ≤ 注册日期 + 7天`(含注册当日) - **14日转化率:** 分子限制 `pay_success_date ≤ 注册日期 + 14天`(含注册当日) - **20日转化率 [李承龙确认 2026-05-11]:** 分子限制 `pay_success_date ≤ 注册日期 + 20天`(含注册当日),30天内趋势已足够清晰且覆盖大部分转化 - **纯净版新增注册用户数 & 纯净版转化率 [李承龙确认 2026-05-11]:** - **纯净版分母:** 从 `status=1 AND deleted_at IS NULL` 的注册用户中,剔除「只有端外已完成订单(`key_from NOT IN 端内,order_status=3`)且没有任何端内订单」的用户。即:只有那些选择了端外渠道、从未在端内下单的用户才被剔除。 - **保留的用户:** 没有任何订单的纯注册用户 + 有端内订单的用户(无论是否有端外订单) - **端内订单条件:** `key_from IN ('app-active-h5-0-0', 'app-sales-bj-qhm-0', 'app-sales-bj-wd-0')`, `pay_success_date IS NOT NULL`, `order_status IN (3, 4)` - **端外订单条件:** `key_from NOT IN 端内`, `pay_success_date IS NOT NULL`, `order_status = 3` - 基于纯净版分母,转化率 / 7日 / 14日 / 20日转化率的口径不变,只是分母缩小为纯净版用户 - **拟合版转化率 [李承龙确认 2026-05-11,2026-05-12 补充实现细节]:** - **目的:** 剔除营销活动带来的注册量尖峰,反映「去噪」后的真实转化效率 - **三版关系:** 原始版 < 纯净版 < 拟合版(分母逐层收紧,转化率递增) - **分母计算(5步法):** 1. **LOESS 拟合** — 仅用清洁日(非活动日+非余波日)的每日注册人数做 LOESS 回归(frac≈0.236),得到自然增长基线 2. **星期因子修正** — 基于清洁日计算每周每日平均注册量与全局均值的比值,修正 LOESS 基线(周末注册量通常高于工作日,因子范围约 0.85~1.25) 3. **活动日+余波日** — 用星期修正后的 LOESS 拟合值替代实际注册人数(压低活动带来的虚增) 4. **非活动日** — 保留实际注册人数(不压低,非活动日的注册是「真实」的) 5. **月度汇总** — 将每日有效注册人数按月加总,得到拟合版分母 - **活动日历(活动日+余波日,[李承龙确认]):** - 2025年:9/9-10, 9/19-23, 10/13-14, 10/16-17, 11/2, 11/7, 11/10, 11/12, 11/19, 12/3 - 2026年:1/28-29(余波1天), 2/11, 2/26-3/2(余波4天), 3/5-8(余波3天), 3/9, 3/12-13, 4/3-7(余波4天), 4/8-10(余波2天), 4/22-23(余波1天), 4/28, 5/6-7 - 共 45 个活动/余波日(254天中占18%) - 余波日:活动日后 N 天内仍有注册溢出效应,一并纳入替换范围 - **不考虑端外订单:** 拟合版分母直接使用拟合有效注册人数,不额外剔除端外-only用户 - **分子:** 端内付费用户数,口径与原始版一致(`key_from IN 端内`, `order_status IN (3,4)`,剔除端内订单全部退费的用户) - **拟合版分母参考值(2025-09~2026-05):** 9月966 / 10月1992 / 11月2541 / 12月3430 / 1月1789 / 2月1285 / 3月2938 / 4月3358 / 5月869 - **关键词订单统计规则:** 当查询形如"XX卖了多少单/XX渠道销量"(XX为特定名称/关键词/渠道)时,需同时返回四个指标:订单总数量、GMV、GSV、退费率。 1. 统计逻辑:筛选`bi_vala_order`表中`key_from`字段包含该关键词的所有订单 2. 指标说明: - 订单数:符合条件的订单总数量 - GMV:符合条件的订单`pay_amount_int`求和/100(单位:元) - GSV:GMV 减去符合条件的订单中已完成退费的金额总和(单位:元) - 退费率:符合条件的订单中已完成退费的订单数 / 订单总数量 * 100%,保留1位小数 - **渠道映射规则(key_from字段匹配):** - 端内购买:`app-active-h5-0-0`、`app-sales-bj-qhm-0`、`app-sales-bj-wd-0`(三个值匹配任意一个即属于端内购买) - 端外购买:除上述两个端内匹配值之外的所有`key_from`值均属于端外购买 - 端外销售渠道购买:端外购买中`key_from`以`sales-adp`开头的为销售渠道购买 - 小红书店铺:`newmedia-dianpu-xhs-0-0` - 达人直播:`newmedia-daren%`(前缀匹配) - 万物:`newmedia-dianpu-wwxx-0-0` - **sale_channel字段映射规则(仅对`key_from = app-active-h5-0-0`的订单生效):** | sale_channel值 | 对应渠道名称 | |---------------|--------------| | 11 | 苹果 | | 12 | 华为 | | 13 | 小米 | | 14 | 荣耀 | | 15 | 应用宝 | | 17 | 魅族 | | 18 | VIVO | | 19 | OPPO | | 21 | 学而思 | | 22 | 讯飞 | | 23 | 步步高 | | 24 | 作业帮 | | 25 | 小度 | | 26 | 希沃 | | 27 | 京东方 | | 41 | 官网 | | 71 | 小程序 | | 其他值 | 站外 | - **付费用户 L1/L2 区分规则(基于 goods_id,[李承龙确认] 2026-05-14,2026-05-27 修正):** - **L1 商品:** `goods_id IN (57, 60, 63)` — 瓦拉英语level1·单季 (57, 63) / 瓦拉英语level1 (60) - **L2 商品:** `goods_id IN (31, 32, 33, 54)` — 瓦拉英语level2 / 年包 / 单季度包 / 三季度课包 / 季度包 - 注:goods_id=31 历史上名称从「瓦拉英语level2」演进为「瓦拉英语年包」,实际为同一 L2 产品 - 注:goods_id=32 历史上名称从「瓦拉英语level2·单季」演进为「瓦拉英语单季度包」,实际为同一 L2 产品 - **L1+L2 商品:** `goods_id = 61` — 瓦拉英语level1+2 - **用户分类逻辑:** 汇总用户所有订单的 goods_id 后判断: - 仅买过 L1 商品 → 「仅L1」 - 仅买过 L2 商品 → 「仅L2」 - 买过 L1+L2 商品(goods_id=61),或同时买过 L1 和 L2 商品 → 「L1+L2」 - **旧版通用通行券:** `goods_id IN (4, 5, 6, 10, 13, 14, 17, 20, 25, 29, 30, 35, 36, 37, 38)`,量极少(<30单),不区分 L1/L2,建议归入「其他」或通过 `bi_user_course_detail` 反查 - **用户激活课程口径(基于 bi_vala_seasonal_ticket,[李承龙确认] 2026-05-22):** - **数据源:** 线上 PostgreSQL `vala_bi` 库 `bi_vala_seasonal_ticket` 表 - **字段映射:** `season_package_level = 'A1'` → L1 激活,`season_package_level = 'A2'` → L2 激活 - **激活判定条件(须同时满足):** 1. `season_package_level = 'A1'`(L1)或 `'A2'`(L2) 2. `status = 1` — 已激活(status=0=待激活, status=-1=已作废,均视为未激活) 3. `deleted_at IS NULL` — 未被软删除 4. **不对 `ticket_type` 做任何限制** — 不区分票据类型 - **判定逻辑:** 用户在表中存在至少一条满足上述条件的记录 → 该用户激活了对应等级课程;同一用户可同时激活 L1 和 L2 - **NULL level 处理:** `season_package_level IS NULL` 的记录忽略 - **与订单关联:** 通过 `out_trade_no` 字段关联 `bi_vala_order` 表 - **当前数据(截至 2026-05-22):** 激活 L1 1,598 人、激活 L2 2,534 人、同时激活 L1+L2 1,095 人 - **激活课程维度定义(character × level = 一门课程,[李承龙确认] 2026-05-22):** - **课程单位:** 一个角色(character_id)+ 一个等级(season_package_level)= 一门被激活的课程 - **激活条件:** 同"用户激活课程口径",`status=1 AND deleted_at IS NULL AND season_package_level IN ('A1','A2') AND character_id IS NOT NULL` - **当前数据(截至 2026-05-22):** L1 激活课程 1,574 门、L2 激活课程 2,108 门、合计 3,682 个课程单位 - **角色分布:** 仅 L1 激活的角色 1,321 个、仅 L2 激活的角色 1,855 个、L1+L2 双激活的角色 253 个 - **激活课程完课分析口径 [李承龙确认 2026-05-22]:** - **完课行为定义:** `bi_user_chapter_play_record_{0-7}` 分表中 `play_status = 1`,按 `created_at` 在指定时间窗口内 - **课程等级匹配:** 通过 `chapter_id` 关联 `bi_level_unit_lesson`,按 `course_level` 匹配(A1→L1, A2→L2) - **关联路径:** `bi_vala_seasonal_ticket.character_id`(即 user_id)→ `bi_user_chapter_play_record_{user_id % 8}.user_id` - **分析维度:** 1. **按用户(account_id):** 任一角色有对应等级的完课即算该用户有完课行为 2. **按课程(character_id × level):** 每个 (角色,等级) 组合独立判定,只看对应等级的完课记录 - **最后一次完课 Unit/Lesson 分析:** 对于无完课的激活课程,通过 `DISTINCT ON (user_id, course_level) ORDER BY created_at DESC` 取最晚完课记录,再关联 `bi_level_unit_lesson` 获取 `course_unit` 和 `course_lesson` - **近14天分析结果参考(2026-05-09~2026-05-22,3,037 激活用户 / 3,682 课程单位):** - 按用户:43.0% 无完课(L1 34.3%、L2 61.3%、L1+L2 23.9%) - 按课程:51.1% 无完课(L1 29.9%、L2 66.8%),其中 20.9% 从未有过任何完课记录 - 无完课课程最后一次完课高度集中在 U00~U02(L1 65.0%、L2 53.3%) - U00/U01 内 Lesson 分布:L05 占比最高(23.6%~36.2%,单元级断点),L01 次高(19.5%~28.8%,入门即弃) - **近14天无完课行为付费用户占比 [李承龙确认 2026-05-22]:** - **付费用户定义:** `bi_vala_order` 中 `pay_success_date IS NOT NULL` 且 `order_status = 3`(已完成,**不含退费订单**),关联 `bi_vala_app_account.status = 1` 剔除测试账号 - **完课行为定义:** `bi_user_chapter_play_record_{0-7}` 分表中 `play_status = 1`(正常完成课时),按 `created_at` 在指定时间窗口内 - **关联路径:** `bi_vala_order.account_id` → `bi_vala_app_character.account_id` → `bi_vala_app_character.id`(即 user_id)→ `bi_user_chapter_play_record.user_id` - **分表查询:** 8 张分表需 UNION 合并,条件 `play_status = 1` + 时间范围,再 DISTINCT user_id - **无完课判定:** 付费用户 account_id 不在期间完课 user_id 集合中 - **L1/L2 拆分:** 基于已有 `goods_id` 口径区分(见上条"付费用户 L1/L2 区分规则") - **金额单位规则:** `bi_vala_order`表中`pay_amount`字段以元为单位,`pay_amount_int`字段以分为单位;后续统一使用`pay_amount_int`计算销售金额,统计为元时除以100即可 - **学习数据统计维度:** 支持按单元/课时/组件维度统计完成人数、平均用时、正确率(Perfect/Good/Oops三个等级) - **特殊时间节点:** `2025-10-01`为核心版本上线时间,部分统计需要区分该节点前后的数据 - **用户统计口径区分规则:** - 新增用户(免费注册新增):使用`bi_vala_app_account.download_channel`字段进行分渠道统计 - 新增付费用户:使用`bi_vala_order.sale_channel`(端内`key_from = app-active-h5-0-0`订单)或`bi_vala_order.key_from`字段进行分渠道统计 - **课程巩固/单元强化/单元挑战统计逻辑(来自 vala_bi 仓库):** - **课程巩固(Review):** 课时级别功能,源表 `bi_user_unit_review_question_result`,统计写入 `user_chapter_time` 表 - `first_done_review_duration`:巩固用时(秒)= `play_time / 1000` - `first_done_review_right_rate`:巩固正确率(万分比)= `正确数 / 总题数 * 10000` - 聚合指标:巩固完成人数、巩固平均完成时间/分钟、巩固平均正确率% - **单元强化(Summary):** 单元级别功能,源表进入 `bi_user_unit_summary_km_result`,完成 `user_learn_record_report_summary(learn_card_type=1, record_type=3)`,统计写入 `user_unit_time` 表 - `summary_in_ts`:首次进入强化时间戳;`summary_done_ts`:首次完成强化时间戳 - 聚合指标:强化进入人数、强化完成人数 - **单元挑战(Challenge):** 单元级别功能,四维度(listening/speaking/reading/writing),评分 Perfect/Good/Oops - 源表进入&评分 `bi_unit_challenge_question_result`,完成 `user_learn_record_report_summary(learn_card_type=1, record_type=4)`,统计写入 `user_unit_time` 表 - `challenge_in_ts`/`challenge_done_ts`:进入/完成时间戳;`challenge_listening`/`speaking`/`reading`/`writing`:各维度评分 - 聚合指标:挑战参与人数、挑战完成人数、四维度各三级评分率% - **BI 核心统计表:** `user_chapter_time`(课时维度,含巩固)、`user_unit_time`(单元维度,含强化+挑战) - **Cron 调度顺序:** UserFirstDone → UserUnitSummaryStart → UserUnitChallengeStart → UserUnitSCDone - **课程结构映射:** UnitIndex = (SeasonOfQuarter-1)*12 + GameInfo.Index;ChapterIndex = UnitIndex*5 + Chapter.Index - **详细笔记:** 见 `memory/2026-04-15-learning-stat-logic.md` - **学习数据计算逻辑:** - **课时首次完成时间计算逻辑:** 1. 关联路径:用户ID(bi_vala_app_account.id)→ 角色ID(bi_vala_app_character.id)→ bi_user_chapter_play_record_{分表号}.user_id 2. 筛选条件:bi_user_chapter_play_record.play_status = 1(正常完成课时) 3. 计算方式:按【角色ID + 课时ID(chapter_id)】分组,取created_at的最小值,即为该用户对应课时的首次完成时间 - **课时总耗时计算逻辑:** 1. 关联路径:通过bi_user_chapter_play_record表的chapter_unique_id,关联bi_user_component_play_record_{分表号}的chapter_unique_id 2. 耗时字段:bi_user_component_play_record.interval_time,单位为毫秒 3. 计算方式:求和对应chapter_unique_id下所有组件的interval_time,再除以60000转换为分钟,保留1位小数 4. 特殊说明:仅统计课时维度完成(play_status=1)的记录,排除未完成整个课时的部分组件练习记录 - **加微判断逻辑 [李承龙确认 2026-06-01 15:14]:** - **加微分两种类型:** 1. **加班主任微信:** 购课用户才有的行为,购课后添加班主任微信进行课后服务 2. **加销售微信:** 注册用户就有的行为(目前暂无加销售微信的数据) - **加班主任微信判断方式:** - **正确数据源:** `vala_class.public.student_info` 表,通过 `vala_account_id` 关联 `bi_vala_app_account.id` - **判断逻辑:** `student_info` 表中存在该 `vala_account_id` → 已加班主任微信,不存在 → 未加 - **注意:** 同一用户可能有多条记录(对应不同老师/不同状态),只要存在任意一条即视为已加微 - **跨库注意:** `student_info` 在 `vala_class` 库,`bi_vala_app_account` 在 `vala_bi` 库,PostgreSQL 不支持直接跨库 JOIN,需在应用层分别取数据后做匹配 - **⚠️ 废弃方案:** `stride_contact_bindings.tel_encrypt` 匹配覆盖率极低(实测 0/31),不可用 - **加班主任微信率:** `加班主任微信人数 / 购课人数 × 100%` - **分母(购课人数):** `bi_vala_order` 中 `pay_success_date IS NOT NULL` 且 `order_status IN (3, 4)` 的非测试账号去重用户数 - **分子:** 分母用户中 `vala_account_id` 在 `student_info` 中存在的人数 - **验证脚本:** `scripts/check_wechat_binding.py`(需更新为 student_info 数据源) - **当前参考数据(2026-06-01):** 老狼履约明细 31 人中,已加微 28 人(90.3%),未加微 3 人(24105, 25485, 25945) ## growth_activity_behavior 统计口径 [李承龙确认 2026-05-25] - **统计标识:** 所有统计统一使用 `userId` 作为用户唯一标识,忽略 `accountId` - **原因:** `accountId=0` 代表匿名用户(H5 页面曝光时等步骤),`userId` 为设备/会话级标识,在匿名和登录状态均存在,更适合做漏斗串联 - **例外:** 「领课加微」相关埋点无 `userId` 字段,仅能做事件级统计