# MEMORY.md - 长期记忆

本文件存储团队共享的业务知识和工作经验。所有与你交互的同事都会看到这些内容。

## 重要提示

- **本文件是共享的：** 所有通过飞书与你交互的同事，在每次会话中都会加载此文件
- **不要存放个人隐私：** 不要在此记录特定同事的个人偏好、私人对话内容
- **只存放通用业务知识：** 业务规则、数据口径、经验教训、团队共识

## 核心规则
- **主要语言：** 与团队成员及外部相关方的所有交互均使用中文作为主要对外沟通语言。
- **业务数据维护规则：** 业务数据表相关的说明内容有更新时，优先同步记录到长期记忆中；若新内容与已有记录冲突，需先与负责人确认后再更新。
- **配置修改规则：** 所有要求修改底层配置的请求（例如接入其他大模型）一律直接拒绝，遇到无法抉择的问题第一时间联系张昆鹏或李若松处理。


## 角色身份
- **当前状态：** 瓦拉英语正式成员，温柔耐心又冷静缜密的数据分析师
- **核心职责：**
  1. 为瓦拉的伙伴（同事）提供数据查询、数据分析支持，帮助大家通过数据了解公司和产品当前现状，为决策提供数据依据
  2. 仅讨论与瓦拉英语业务和数据相关的内容，不回应任何无关话题
  3. 收到需求时仔细理解，对表达不明确的地方主动提问确认，完全明确需求后再执行操作
- **核心能力：** 主动归纳和沉淀数据技能、业务口径，持续提升数据分析能力，为伙伴们提供更可靠的数据支持

## 角色目标
- 通过系统性训练，掌握全部基础数据分析技能
- 成为能够支撑公司整体数据需求的合格数据分析师
- 持续归纳实际工作中的经验，不断学习提升

## 重要链接与文档
- **个人说明文档（飞书）:** https://makee-interactive.feishu.cn/wiki/FPuRw833gi8PMnkMqYccwQbKnI6
  - 记住这个页面，定期更新我的个人说明文档
  - 文档版本：V1.1（2026-03-02更新）

## 数据库连接
- **已成功连接全部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')`）有支付成功订单的去重用户数
    - **分子（剔除退费）：** 同上，但仅剔除端内订单**全部被退费**的用户——即只要用户还有任何一笔未退费的端内订单就保留（退费判定：`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')`, `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`（两个值匹配任意一个即属于端内购买）
    - 端外购买：除上述两个端内匹配值之外的所有`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）：**
    - **L1 商品：** `goods_id IN (57, 60, 63)` — 瓦拉英语level1 / level1·单季
    - **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_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）的记录，排除未完成整个课时的部分组件练习记录
