22 KiB
页面内容管理方案
版本:v2.0 | 最后更新:2026-04-30 配套文档:Worksheet 发布方案 | 小程序云开发方案
一、背景与目标
worksheet 已可通过 Debug 发布进入云数据库,但三个展示页仍使用本地静态数据:
| 页面 | 当前数据来源 | 问题 |
|---|---|---|
| 首页 | pages/home/home.data.ts |
featured、hot、分区靠代码手动维护 |
| 分类页 | pages/category/category.data.ts |
新 worksheet 发布后不会自动出现 |
| 分龄页 | pages/age/age.ts 内 Mock |
推荐内容和周路线无法运营配置 |
目标: 通过 Debug 管理页配置三个页面的内容,生成统一配置写入云数据库并同步至云存储,小程序启动时通过数据预拉取获取并渲染。
二、整体架构
2.1 数据存储与同步
数据源(Source of Truth): 云数据库 page_configs 集合,文档 _id = 'current'
数据交付(Delivery): 云存储 content/page-config.json(供小程序预拉取使用)
每次更新时,云函数先写数据库,再同步上传至云存储,保证两者一致。各管理页更新时只修改配置中自己负责的字段,不影响其他页面数据。
// page_configs/current 文档结构
type PageConfig = {
home: HomePageData; // 首页配置
category: CategoryPageData; // 分类页配置
age: AgePageData; // 分龄页配置
version: number; // 版本号,每次更新递增
updatedAt: string; // 最后更新时间
};
读写流程:
readCurrentConfig() → 从 page_configs/current 读取当前完整配置
config[page] = data → 只覆盖对应页面字段(home / category / age)
saveConfig(config) → 写回数据库 → 上传云存储 content/page-config.json
2.2 数据流
┌──────────────────────────────────────────────────────────────────┐
│ Debug 内容管理页(独立页面) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 首页内容管理 │ │ 分类页内容管理 │ │ 分龄页内容管理 │ │
│ │ homeContent │ │ categoryContent│ │ ageContent │ │
│ │ Manage │ │ Manage │ │ Manage │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └────────┬─────────┘─────────┬─────────┘ │
│ ▼ ▼ │
│ 点击「更新」按钮 → 调用云函数 pageContentBuild │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. 从 page_configs/current 读取当前配置 │ │
│ │ 2. 只更新对应页面字段(home/category/age) │ │
│ │ 3. version++ → 写回数据库 │ │
│ │ 4. 同步上传至云存储 content/page-config.json│ │
│ └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ 小程序启动 │
│ │
│ app.onLaunch │
│ └─ wx.getBackgroundFetchData('pre') 获取预拉取结果 │
│ ├─ 成功 → 存入 globalData + storage 缓存 │
│ └─ 失败 → 读取 storage 缓存 │
│ ├─ 命中 → 存入 globalData │
│ └─ 未命中 → 调用 pageConfigFetch 云函数兜底 │
│ └─ 成功 → 存入 globalData + storage │
│ │
│ TabBar 页面 onLoad(首页 / 分类页 / 分龄页) │
│ ├─ 配置已就绪 → 从 globalData.pageConfig 读取对应字段 → 渲染 │
│ └─ 配置未就绪 → 显示加载态 → 监听配置就绪回调 → 渲染 │
│ │
│ TabBar 页面 onShow(后续切换) │
│ └─ 直接从 globalData.pageConfig 读取,零延迟 │
└──────────────────────────────────────────────────────────────────┘
2.3 数据预拉取
小程序数据预拉取通过云函数 pageConfigFetch 实现:
- 在
app.json中配置fetchDataUrl指向pageConfigFetch云函数 - 小程序冷启动时由微信客户端自动调用,不占用页面加载时间
- 云函数从
page_configs/current读取完整配置并返回
// cloudfunctions/pageConfigFetch/index.js
exports.main = async () => {
const db = cloud.database();
const { data } = await db.collection('page_configs').doc('current').get();
return { success: true, data };
};
2.4 页面加载策略
三个展示页(首页、分类页、分龄页)均为 TabBar 页面,不再使用本地 .data.ts 静态数据渲染,改为统一从内存读取预拉取配置,并通过 storage 缓存保证离线可用:
app.onLaunch
│
├─ Step 1: wx.getBackgroundFetchData('pre') 获取预拉取结果
│ 成功 → 存入 globalData.pageConfig
│ → wx.setStorageSync('pageConfig', data) 写入缓存
│ → 通知页面就绪
│
├─ Step 2: 预拉取失败 → wx.getStorageSync('pageConfig') 读取缓存
│ 命中 → 存入 globalData.pageConfig → 通知页面就绪
│
└─ Step 3: 缓存也为空 → 调用 pageConfigFetch 云函数兜底
成功 → 存入 globalData.pageConfig
→ wx.setStorageSync('pageConfig', data) 写入缓存
→ 通知页面就绪
TabBar 页面 onLoad(仅首次进入触发)
│
├─ globalData.pageConfig 已就绪
│ → 读取对应字段(home / category / age)→ setData 渲染
│
└─ globalData.pageConfig 未就绪
→ 显示加载态(骨架屏 / loading)
→ 注册回调,配置就绪后自动渲染
TabBar 页面 onShow(后续每次切换触发)
│
└─ 直接从 globalData.pageConfig 读取,零延迟,无网络请求
三级降级策略: 预拉取 → storage 缓存 → 云函数调用
| 层级 | 数据来源 | 耗时 | 适用场景 |
|---|---|---|---|
| L1 | wx.getBackgroundFetchData('pre') |
0ms | 正常冷启动,预拉取已完成 |
| L2 | wx.getStorageSync('pageConfig') |
~1ms | 预拉取失败,但之前成功过 |
| L3 | pageConfigFetch 云函数 |
200-500ms | 首次使用或缓存被清理 |
设计要点:
- 配置在
app.onLaunch时加载一次,存入globalData.pageConfig,三个页面共享同一份内存数据 - 每次预拉取或云函数获取成功后,同步写入 storage 作为下次启动的缓存兜底
- TabBar 页面
onLoad只执行一次,onShow每次切换都触发,天然适合「启动时加载一次,后续复用」的模式 - 不再维护
home.data.ts、category.data.ts等本地静态数据文件,消除双数据源不一致的问题 - storage 缓存的 key 为
pageConfig,使用wx.setStorageSync/wx.getStorageSync同步读写
2.5 云函数总览
| 云函数 | 职责 |
|---|---|
pageContentBuild |
生成指定页面配置(home / category),写入数据库 + 云存储 |
pageConfigFetch |
数据预拉取接口,从数据库读取完整配置返回给客户端 |
homeAutoRefresh |
定时任务,每日 22:00 自动更新 featured 和 hot |
homeTimerControl |
暂停/启动首页定时刷新任务(更新 settings 集合标记位) |
worksheetsQuery |
查询 worksheet 列表(支持按分类、状态筛选) |
worksheetsUpdateStatus |
更新单个 worksheet 的状态 |
三、首页内容管理
3.1 管理页入口
supportPages/homeContentManage/homeContentManage
进入页面时并行调用两个云函数加载数据:
onLoad
→ Promise.all([
worksheetsQuery({ status: 'active' }), // 获取所有 active worksheet
pageConfigFetch() // 获取当前已保存的配置
])
→ featured: 始终按 downloads desc 取前 5 自动填充
→ hot: 始终按 likes desc 取前 5 自动填充
→ sections: 从 config.home.sections 回显已保存的分类分区数据
3.2 管理 UI
| 区块 | 对应字段 | 数据来源 |
|---|---|---|
| 定时任务卡片 | — | 显示定时任务状态(运行中/已暂停),可切换 |
| 今日推荐 | featured |
自动:downloads 最多的 5 个 active worksheet |
| 热门推荐 | hot |
自动:likes 最多的 5 个 active worksheet |
| 分类分区 | sections |
从已保存配置回显,支持手动添加/移除 worksheet |
3.3 数据回显逻辑
featured / hot(自动填充,不回显历史配置):
每次进入管理页,始终从当前 active worksheets 中实时计算:
- featured = 按
downloads降序取前 5 - hot = 按
likes降序取前 5
用户可在自动填充基础上手动添加/移除,点击「更新首页数据」后生效。
sections(从配置回显):
从 pageConfigFetch 返回的 home.sections 中恢复已保存的分区数据:
CATEGORY_LIST.map(category => {
saved = savedSectionMap.get(category.id)
items = saved.items → 通过 wsMap 还原为完整 worksheet 对象
morePath = category.path // 从 CATEGORY_LIST 获取,如 /pages/category/category?id=math
})
name/icon始终从本地CATEGORY_LIST取(保证最新)morePath从CATEGORY_LIST的path字段获取,指向对应分类页并自动选中该分类items通过 worksheet id 从 active worksheets 中匹配还原,已下架的 worksheet 自动过滤
3.4 自动刷新与定时任务
今日推荐和热门推荐采用自动化 + 手动微调的方式:
- 定时任务:云函数
homeAutoRefresh配置定时触发器,每日 22:00 自动执行,查询最新 top5 数据更新page-config.json中的home.featured和home.hot - 开关控制:通过
settings集合中homeAutoRefresh文档的enabled字段控制。管理页提供暂停/启动按钮,调用homeTimerControl云函数切换状态。homeAutoRefresh执行时先检查此标记,enabled === false则跳过 - 手动更新:用户可在管理页手动添加/移除 worksheet,点击「更新首页数据」调用
pageContentBuild({ page: 'home', ... })生成
定时任务(每日 22:00)
→ homeAutoRefresh 云函数
→ 检查 settings/homeAutoRefresh.enabled
→ enabled === false → 跳过执行
→ 查询 top5 downloads → featured
→ 查询 top5 likes → hot
→ readCurrentConfig() → 只更新 home.featured 和 home.hot → saveConfig()
3.5 更新首页数据流程
点击「更新首页数据」
→ pageContentBuild({
page: 'home',
featuredIds: [...],
hotIds: [...],
sections: [
{ id: 'math', subtitle: '', morePath: '/pages/category/category?id=math', worksheetIds: [...] },
{ id: 'english', subtitle: '', morePath: '/pages/category/category?id=english', worksheetIds: [...] },
...
]
})
→ 云函数批量查询所有引用的 worksheet
→ 构建 home 配置(categoryTabs + ageBands + featured + hot + sections)
→ readCurrentConfig() → config.home = homeData → saveConfig()
3.6 数据结构
type HomePageData = {
searchPlaceholder: string;
categoryTabs: Array<{ id: string; name: string; path?: string }>;
ageBands: Array<{ key: string; label: string; desc: string; path: string }>;
featured: HomeDisplayItem[];
hot: HomeDisplayItem[];
sections: HomeDisplaySection[];
};
type HomeDisplaySection = {
id: string; // 分类 id
title: string; // 分类名称
subtitle: string;
morePath: string; // 跳转路径,如 /pages/category/category?id=math
items: HomeDisplayItem[];
};
| 模块 | 生成规则 |
|---|---|
categoryTabs |
由 CATEGORY_LIST_WITH_ALL 生成 |
ageBands |
由 AGE_BANDS 生成 |
featured |
自动:downloads 最多的 5 个 active worksheet |
hot |
自动:likes 最多的 5 个 active worksheet |
sections |
管理页配置的 worksheetIds,morePath 来自 CATEGORY_LIST |
四、分类页内容管理
4.1 管理页入口
supportPages/categoryContentManage/categoryContentManage
4.2 管理 UI
分类页管理顶部展示分类选择器(来源 CATEGORY_LIST),默认选中 math。
分类头部统计:
| 指标 | 查询规则 |
|---|---|
| 总数 | worksheets.category == 当前分类 |
| 草稿 | status == draft |
| 线上 | status == active |
| 已隐藏 | status == hidden |
列表展示当前分类下所有 worksheet(按 draft → hidden → active 排序),每项显示预览图、标题、ID、年龄/难度/标签、状态、操作按钮。
操作按钮:
| 当前状态 | 按钮样式 | 可执行操作 |
|---|---|---|
draft |
绿色按钮 | 激活上线 |
active |
灰色按钮 | 下架为 hidden |
hidden |
黄色按钮 | 恢复为 draft 或直接激活 |
4.3 分类页支持 id 参数
分类页 /pages/category/category 支持通过 URL 参数 id 指定默认选中的分类:
/pages/category/category?id=english → 默认选中「英语启蒙」
/pages/category/category?id=math → 默认选中「数感启蒙」
/pages/category/category → 默认选中「全部」
首页 sections 的「查看更多」链接使用此参数跳转到对应分类。
4.4 数据结构
type CategoryPageData = {
searchPlaceholder: string;
categories: Array<{
id: string;
name: string;
icon: string;
items: CategoryItem[];
}>;
};
生成时从 worksheets 集合查询 status == active 的内容,按分类分组、按 sortOrder 排序,转换为 CategoryItem。
4.5 更新流程
分类页管理 → 管理 worksheet 状态和排序
→ 点击「更新分类数据」
→ 查询所有 active worksheets,按 category 分组
→ 调用 pageContentBuild({ page: 'category' })
→ 云函数更新 page_configs/current 中的 category 字段 + 同步云存储
五、分龄页内容管理
详细方案已独立为 分龄页内容管理方案
分龄页内容分为四部分,通过 age.config.ts 统一管理:
| 部分 | 名称 | 管理方式 |
|---|---|---|
| 第一部分 | 年龄段设置 | 写死在 age.config.ts,几乎不变 |
| 第二部分 | 能力目标 | AI Skill generate-age-abilities 生成 |
| 第三部分 | 4 周学习路线 | AI Skill generate-age-weekly-plans 生成 |
| 第四部分 | 为你推荐 | 运行时动态计算(取收藏/下载最高的 6 个 worksheet) |
AI Skill 位于 skills/ 目录,在开发时通过 Cursor 运行,以早教专家视角结合最新 worksheet 数据生成内容。
六、Debug 页入口结构
supportPages/debug/debug
├─ 分类基础数据管理 (已有:同步 CATEGORY_LIST → categories)
├─ Worksheet 内容池 (已有:状态、分类、上下架、预览)
└─ 页面内容管理 (新增)
├─ 📋 分类页内容管理 → supportPages/categoryContentManage
├─ 🏠 首页内容管理 → supportPages/homeContentManage
└─ 📅 分龄页内容管理 → supportPages/ageContentManage(待开发)
七、实施计划
Phase 1:基础设施 ✅
- 实现
pageContentBuild云函数(读写数据库 + 同步云存储) - 实现
pageConfigFetch云函数(数据预拉取接口) - 实现
worksheetsQuery/worksheetsUpdateStatus云函数
Phase 2:分类页管理 ✅
- 实现分类页管理页(worksheet 列表、状态管理)
- 实现「更新分类数据」→ 生成 category 配置
- 分类页支持
?id=xxx参数默认选中分类
Phase 3:首页管理 ✅
- 实现首页管理页(featured / hot 自动填充 + sections 配置回显)
- 实现 worksheet 选择器弹窗
- 实现定时任务(homeAutoRefresh + homeTimerControl)
- 实现「更新首页数据」→ 生成 home 配置
Phase 4:分龄页管理(待开发)
详见 分龄页内容管理方案
- 新增
age.config.ts配置文件,将静态配置与 AI 生成数据分离 - 开发 AI Skill(
generate-age-abilities+generate-age-weekly-plans) - 分龄页接入 pageConfig 数据源,实现「为你推荐」动态计算
八、风险与约束
| 风险 | 处理方式 |
|---|---|
| 预拉取失败 | 依次降级:storage 缓存 → pageConfigFetch 云函数兜底 |
| 配置生成失败 | 云函数返回错误,管理页提示重试,线上数据不受影响 |
| 首页配置引用了 hidden 内容 | sections 回显时通过 wsMap 匹配,已下架的自动过滤 |
| 更新某页数据清空其他页 | 云函数从数据库读取完整配置,只覆盖对应字段,其他页面数据不受影响 |
| 分龄内容不适龄 | 选择器按年龄段默认过滤 |
| 并发更新冲突 | 云函数使用 version 乐观锁,冲突时提示重新加载后重试 |
九、关键文件索引
| 文件 / 模块 | 职责 |
|---|---|
supportPages/homeContentManage/ |
首页内容管理页 |
supportPages/categoryContentManage/ |
分类页内容管理页 |
cloudfunctions/pageContentBuild/ |
生成 page-config 中指定页面的配置 |
cloudfunctions/pageConfigFetch/ |
数据预拉取接口,返回完整页面配置 |
cloudfunctions/homeAutoRefresh/ |
定时任务,每日自动更新首页推荐位数据 |
cloudfunctions/homeTimerControl/ |
暂停/启动首页定时刷新任务 |
cloudfunctions/worksheetsQuery/ |
查询 worksheet 列表 |
cloudfunctions/worksheetsUpdateStatus/ |
更新 worksheet 状态 |
pages/category/category.ts |
分类页,支持 ?id=xxx 参数选中分类 |
pages/age/age.config.ts |
分龄页配置(详见 分龄页方案) |
skills/generate-age-abilities/SKILL.md |
AI Skill:生成分龄页能力目标 |
skills/generate-age-weekly-plans/SKILL.md |
AI Skill:生成分龄页学习路线 |
pages/home/home.data.ts |
首页兜底数据 |
pages/category/category.data.ts |
分类页兜底数据 |
core/data/categories.ts |
CATEGORY_LIST 分类定义(id/name/icon/path) |
content/page-config.json(云存储) |
三个页面的统一配置 JSON |
page_configs/current(云数据库) |
配置数据源(Source of Truth) |
settings/homeAutoRefresh(云数据库) |
定时任务开关标记 |