# 需求文档:小程序数据获取与展示效率优化 ## 简介 对小程序三个核心页面(Performance 绩效页、Task-List 任务列表页、Task-Detail 任务详情页)进行数据获取和展示效率优化。优化覆盖两个维度:页面加载速度(减少 SQL 查询次数和连接开销)和页面间跳转体验(减少重复请求)。通过后端批量查询、SQL 层过滤、前端内存缓存 + 版本校验等手段,提升整体用户体验。 ## 术语表 - **小程序(Miniprogram)**:微信小程序客户端,面向门店助教使用 - **Performance_页**:绩效概览页面,展示助教当月绩效、收入、TOP 客户、服务记录等 - **Task_List_页**:任务列表页面,展示助教待办任务卡片列表及绩效进度条 - **Task_Detail_页**:任务详情页面,展示单个任务的客户信息、维客线索、服务记录、AI 分析等 - **ETL_库**:数据仓库(`test_etl_feiqiu`),通过 `_fdw_context` 设置 RLS 参数后访问 `app.v_*` 视图 - **业务库**:应用数据库(`test_zqyy_app`),存储 `biz.coach_tasks`、`biz.notes`、`biz.ai_cache` 等业务表 - **N_Plus_1_问题**:循环中逐条执行 SQL 查询的反模式,导致查询次数随数据量线性增长 - **Batch_Query**:`fdw_queries.batch_query_for_task_list()`,单连接内批量执行多条 ETL 查询的封装函数 - **RS_指数**:关系指数(`rs_display`),来自 `v_dws_member_assistant_relation_index`,衡量助教与客户的关系紧密度 - **updated_at**:数据版本时间戳,用于前端缓存校验后端数据是否已变更 - **Cache_Store**:前端内存缓存模块,存储跨页面共享的 ETL 数据(会员信息、余额、RS 指数等) - **salary_calc**:`v_dws_assistant_salary_calc` 视图,按 `assistant_id + salary_month` 唯一,包含工资计算参数 - **relation_index**:`v_dws_member_assistant_relation_index` 视图,按 `assistant_id + member_id` 唯一 ## 需求 ### 需求 1:Performance 页 N+1 查询消除 **用户故事:** 作为助教,我希望绩效页加载更快,以便在繁忙时段快速查看当月绩效数据。 #### 验收标准 1. WHEN `get_coach_detail` 被调用, THE Coach_Service SHALL 通过单条批量 SQL 查询获取所有 TOP 客户的 RS 指数,替代当前按 `member_id` 逐条循环调用 `get_relation_index` 的模式 2. THE FDW_Queries 模块 SHALL 提供 `get_relation_index_batch(conn, site_id, assistant_id, member_ids)` 函数,接受 `member_ids` 列表参数,返回 `dict[int, float]`(member_id → rs_display 映射) 3. WHEN `member_ids` 列表为空, THE `get_relation_index_batch` 函数 SHALL 返回空字典,不执行任何 SQL 查询 4. WHEN 批量查询执行后, THE 返回结果 SHALL 与逐条查询 `get_relation_index` 对相同 `member_ids` 的结果在 RS 指数值上完全一致 5. THE `_build_top_customers` 函数 SHALL 调用 `get_relation_index_batch` 一次性获取所有客户的 RS 指数,替代当前的 `for mid in member_ids` 循环 ### 需求 2:Task-List 页服务端过滤优化 **用户故事:** 作为助教,我希望任务列表加载更快且数据准确,以便高效管理待办任务。 #### 验收标准 1. WHEN `get_task_list_v2` 查询 `biz.coach_tasks` 时, THE Task_Manager SHALL 在 SQL WHERE 子句中直接排除不满足展示条件的任务,替代拉取全量 200 条后在内存中过滤的模式 2. THE SQL 查询 SHALL 在 WHERE 子句中包含 `relationship_building` 任务的 RS 范围过滤条件(排除 RS 指数不在 `[rs_min, rs_max]` 范围内的 `member_id`) 3. WHEN RS 排除列表查询失败时, THE Task_Manager SHALL 降级为不排除任何任务(当前已有的容错行为保持不变) 4. THE 分页 `total` 计数 SHALL 与实际返回的过滤后结果集一致,跨页翻页时 `total` 值保持准确 5. WHILE 前端请求 `pageSize=200` 一次性加载时, THE 后端 SHALL 仅返回满足展示条件的任务,减少无效数据传输量 ### 需求 3:前端内存缓存与版本校验 **用户故事:** 作为助教,我希望在页面间跳转时不重复等待相同数据加载,以获得流畅的操作体验。 #### 验收标准 1. THE Cache_Store SHALL 缓存以下跨页面共享的 ETL 数据:会员信息(`member_info`)、会员余额(`balance`)、RS 指数(`relation_index`) 2. THE 后端接口 SHALL 在每条返回数据中包含 `updated_at` 时间戳字段,表示该数据在后端的最后更新时间 3. WHEN 前端请求数据时, THE Cache_Store SHALL 先检查本地缓存是否存在且 `updated_at` 未过期;若缓存有效则直接使用,若缓存过期或不存在则发起网络请求 4. WHEN 后端数据发生变更(`updated_at` 更新)时, THE Cache_Store SHALL 在下次请求时检测到版本变化并重新获取最新数据 5. IF 缓存校验逻辑发生异常, THEN THE Cache_Store SHALL 降级为直接发起网络请求,确保功能可用性不受影响 6. THE Cache_Store SHALL 在小程序 `onHide`(切后台)或用户主动下拉刷新时清空全部缓存,确保回到前台时获取最新数据 ### 需求 4:跳转传参优化 **用户故事:** 作为助教,我希望从任务列表点击进入任务详情时,已加载的客户信息能直接展示,减少等待时间。 #### 验收标准 1. WHEN 用户从 Task_List_页 点击任务卡片跳转到 Task_Detail_页 时, THE Task_List_页 SHALL 将已加载的任务数据(`customer_name`、`heart_score`、`balance`、`task_type`、`task_type_label`)通过全局 Cache_Store 传递给 Task_Detail_页 2. WHEN Task_Detail_页 加载时, THE Task_Detail_页 SHALL 优先从 Cache_Store 读取已有数据立即渲染骨架内容,同时异步请求完整详情数据 3. WHEN 完整详情数据返回后, THE Task_Detail_页 SHALL 用完整数据覆盖骨架内容,实现无缝过渡 4. IF Cache_Store 中无对应任务的预加载数据, THEN THE Task_Detail_页 SHALL 回退为当前的全量加载模式(显示 loading → 请求 → 渲染) 5. WHEN 用户从 Performance_页 点击客户卡片跳转到 Task_Detail_页 时, THE Performance_页 SHALL 同样将已有的客户数据(`customer_name`、`heart_score`)通过 Cache_Store 传递 ### 需求 5:Task-Detail 页 ETL 查询合并 **用户故事:** 作为助教,我希望任务详情页加载更快,以便快速查看客户信息并采取行动。 #### 验收标准 1. WHEN `get_task_detail` 查询 ETL 数据时, THE Task_Manager SHALL 将 `member_info`、`balance`、`relation_index` 三个独立 ETL 连接合并为单连接批量查询 2. THE 合并后的查询 SHALL 复用 `_fdw_context` 单次连接,在同一个事务中依次执行会员信息、余额、RS 指数查询 3. WHEN 批量 ETL 查询失败时, THE Task_Manager SHALL 对每个子查询独立降级(返回默认值),不影响其他子查询的结果 4. THE 合并后的查询结果 SHALL 与当前三个独立查询的结果在数据内容上完全一致 ### 需求 6:后端 updated_at 版本字段支持 **用户故事:** 作为开发者,我希望后端接口返回数据版本时间戳,以便前端缓存能准确判断数据是否过期。 #### 验收标准 1. THE 后端 SHALL 在 `get_task_list_v2` 返回的每个任务项中包含 `updated_at` 字段(来自 `biz.coach_tasks.updated_at`) 2. THE 后端 SHALL 在 `get_coach_detail` 返回的 `top_customers` 列表中,为每个客户附加 `data_updated_at` 字段(取 ETL 查询时的服务器时间戳) 3. THE 后端 SHALL 在 `get_task_detail` 返回中包含 `updated_at` 字段 4. IF `biz.coach_tasks` 表当前缺少 `updated_at` 列, THEN THE 数据库迁移 SHALL 添加该列,默认值为 `now()`,并在任务状态变更时自动更新 5. THE DDL 变更 SHALL 合并到主 DDL 迁移文件中,并通过审计流程记录 ### 需求 7:Task-Detail 页折前/折后小时数条件显示 **用户故事:** 作为助教,我希望只在折前和折后小时数有差异时才看到折前小时数,以减少信息干扰。 #### 验收标准 1. WHEN 服务记录的折前小时数(`service_hours_raw`)与折后小时数(`service_hours`)不相等时, THE Task_Detail_页 SHALL 在折后小时数旁显示"折前 XX.Xh"标注 2. WHEN 折前小时数与折后小时数相等时, THE Task_Detail_页 SHALL 仅显示折后小时数,不显示折前标注 3. IF 折前小时数(`duration_raw`)为 `null` 或未返回, THEN THE Task_Detail_页 SHALL 视为与折后小时数相等,不显示折前标注 4. THE 折前小时数 SHALL 格式化为一位小数(如"折前 2.5h"),与折后小时数的格式保持一致 ### 需求 8:LEFT JOIN LATERAL 酒水聚合优化(低优先级) **用户故事:** 作为开发者,我希望服务记录的酒水明细聚合查询在数据量增长时仍保持高效。 #### 验收标准 1. WHILE 单次查询涉及的服务记录超过 50 条时, THE FDW_Queries 模块 SHALL 使用 CTE 预聚合方式替代当前的 `LEFT JOIN LATERAL` 子查询聚合酒水商品明细 2. WHEN 使用 CTE 预聚合后, THE 查询结果 SHALL 与 `LEFT JOIN LATERAL` 方式返回的 `drinks` 字段内容完全一致 3. WHILE 单次查询涉及的服务记录不超过 50 条时, THE FDW_Queries 模块 SHALL 保持当前的 `LEFT JOIN LATERAL` 方式(小数据量下性能已足够) ### 需求 9:数据基线快照与回归验证 **用户故事:** 作为开发者,我希望每次效率优化后能自动验证数据正确性,确保优化不引入数据偏差。 #### 验收标准 1. BEFORE 任何优化任务开始实施前, THE 验证脚本 SHALL 使用小燕(assistant_id: `2964673443302213`、site_id: `2790685415443269`)的真实数据,对三个核心接口(`get_coach_detail`、`get_task_list_v2`、`get_task_detail`)各执行一次,将完整返回值序列化为 JSON 基线快照 2. AFTER 每个优化任务完成后, THE 验证脚本 SHALL 使用相同参数再次调用接口,逐字段 diff 对比优化前后的返回值 3. THE diff 对比 SHALL 覆盖以下关键校验点:到手金额(hours × net_rate)、RS 指数、会员余额、服务记录条数和排序、酒水商品明细、TOP 客户列表 4. IF diff 发现任何数据值不一致(排除 `updated_at`、`data_updated_at` 等时间戳字段), THEN THE 验证脚本 SHALL 报告失败并输出差异详情 5. THE 验证脚本 SHALL 以 pytest 形式编写,放置在 `tests/` 目录下,可通过 `pytest tests/test_perf_optimization_baseline.py -v` 执行 6. THE 基线快照 JSON 文件 SHALL 存储在 `tests/fixtures/perf_optimization/` 目录下,纳入版本控制 ### 需求 10:AI 分析数据校对预留(后期) **用户故事:** 作为开发者,我希望 AI 分析相关的数据字段在优化过程中保持接口兼容,以便后期校对和调整。 #### 验收标准 1. THE 优化过程 SHALL 不改变 `aiAnalysis`(`app4_analysis`)和 `talkingPoints`(`app5_talking_points`)字段的数据来源、查询逻辑和返回结构 2. THE `biz.ai_cache` 表的查询方式(按 `target_id + site_id + cache_type` 查询,`ORDER BY created_at DESC`)SHALL 在优化后保持不变 3. IF 后续需要对 AI 分析数据进行校对或调整, THEN THE 接口返回结构 SHALL 保持向后兼容(新增字段可以,删除或改名已有字段不可以) 4. THE 数据基线快照(需求 9)SHALL 包含 `aiAnalysis` 和 `talkingPoints` 字段的完整内容,作为后期 AI 校对的参考基准