feat: 首页、分类页加载数据逻辑重构

This commit is contained in:
R524809
2026-04-30 16:44:45 +08:00
parent c112c70bab
commit 7126d3a941
63 changed files with 805 additions and 433 deletions
+275 -271
View File
@@ -1,7 +1,7 @@
# 页面内容管理方案
> 版本:v1.0  |  最后更新:2026-04-29
> 配套文档:[Worksheet 发布方案](./Worksheet发布方案.md)  |  [小程序云开发方案](./小程序云开发方案.md)
> 版本:v2.0 | 最后更新:2026-04-30
> 配套文档:[Worksheet 发布方案](./Worksheet发布方案.md) | [小程序云开发方案](./小程序云开发方案.md)
---
@@ -9,36 +9,68 @@
worksheet 已可通过 Debug 发布进入云数据库,但三个展示页仍使用本地静态数据:
| 页面 | 当前数据来源 | 问题 |
| --- | --------------------------------- | ---------------------- |
| 首页 | `pages/home/home.data.ts` | featured、hot、分区靠代码手动维护 |
| 分类页 | `pages/category/category.data.ts` | 新 worksheet 发布后不会自动出现 |
| 分龄页 | `pages/age/age.ts` 内 Mock | 推荐内容和周路线无法运营配置 |
**目标:** 通过 Debug 管理页配置三个页面的内容,生成统一 JSON 上传云存储,小程序启动时通过**数据预拉取**获取并渲染。
**目标:** 通过 Debug 管理页配置三个页面的内容,生成统一配置写入云数据库并同步至云存储,小程序启动时通过**数据预拉取**获取并渲染。
---
## 二、整体架构
### 2.1 数据
### 2.1 数据存储与同步
**数据源(Source of Truth):** 云数据库 `page_configs` 集合,文档 `_id = 'current'`
**数据交付(Delivery):** 云存储 `content/page-config.json`(供小程序预拉取使用)
每次更新时,云函数先写数据库,再同步上传至云存储,保证两者一致。各管理页更新时只修改配置中自己负责的字段,不影响其他页面数据。
```ts
// 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 内容管理页(三个独立 tab
│ Debug 内容管理页(独立页面)
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ │ 首页管理 │ │ 分类页管理 │ │ 分龄页管理 │
└────┬────┘ └────┬────┘ └────┬────┘
└──────┬────────────┬──────┘
点击「更新」按钮 → 调用云函数 pageContentUpdate
更新云存储 content/page-config.json 中对应页面的配置
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 首页内容管理 │ │ 分类页内容管理 │ │ 分龄页内容管理 │ │
│ homeContent │ │ categoryContent│ │ ageContent
│ Manage│ Manage │ Manage
└──────┬───────┘ └──────┬───────┘ └──────┬──────┘ │
└────────┬─────────┘─────────┬─────────┘
点击「更新」按钮 → 调用云函数 pageContentBuild
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. 从 page_configs/current 读取当前配置 │ │
│ │ 2. 只更新对应页面字段(home/category/age │ │
│ │ 3. version++ → 写回数据库 │ │
│ │ 4. 同步上传至云存储 content/page-config.json│ │
│ └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
@@ -47,122 +79,194 @@ worksheet 已可通过 Debug 发布进入云数据库,但三个展示页仍使
│ 小程序启动 │
│ │
│ app.onLaunch │
│ └─ 数据预拉取 → 获取 content/page-config.json
│ └─ wx.getBackgroundFetchData('pre') 获取预拉取结果
│ ├─ 成功 → 存入 globalData + storage 缓存 │
│ └─ 失败 → 读取 storage 缓存 │
│ ├─ 命中 → 存入 globalData │
│ └─ 未命中 → 调用 pageConfigFetch 云函数兜底 │
│ └─ 成功 → 存入 globalData + storage │
│ │
│ 页面 onLoad
│ ├─ 1. 使用本地 *.data.ts / age.config.ts 渲染兜底首屏
2. 读取预拉取结果中对应页面的配置
└─ 3. 成功则 setData 更新页面
TabBar 页面 onLoad(首页 / 分类页 / 分龄页)
│ ├─ 配置已就绪 → 从 globalData.pageConfig 读取对应字段 → 渲染
配置未就绪 → 显示加载态 → 监听配置就绪回调 → 渲染
│ TabBar 页面 onShow(后续切换) │
│ └─ 直接从 globalData.pageConfig 读取,零延迟 │
└──────────────────────────────────────────────────────────────────┘
```
### 2.2 统一 JSON 格式
### 2.3 数据预拉取
云存储路径:`content/page-config.json`
小程序数据预拉取通过云函数 `pageConfigFetch` 实现:
```ts
// content/page-config.json 结构
type PageConfig = {
home: HomePageData; // 首页配置
category: CategoryPageData; // 分类页配置
age: AgePageData; // 分龄页配置
version: number; // 版本号,每次更新递增
updatedAt: string; // 最后更新时间
-`app.json` 中配置 `fetchDataUrl` 指向 `pageConfigFetch` 云函数
- 小程序冷启动时由微信客户端自动调用,不占用页面加载时间
- 云函数从 `page_configs/current` 读取完整配置并返回
```js
// 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 };
};
```
每个管理页更新时,只修改 JSON 中自己负责的字段,通过云函数读取当前 JSON → 合并更新 → 写回云存储。
### 2.4 页面加载策略
### 2.3 页面加载策略
三个页面统一采用**数据预拉取 + 本地兜底**的加载策略:
三个展示页(首页、分类页、分龄页)均为 TabBar 页面,不再使用本地 `.data.ts` 静态数据渲染,改为**统一从内存读取预拉取配置**,并通过 storage 缓存保证离线可用:
```
页面 onLoad
app.onLaunch
├─ Step 1: 使用本地静态数据渲染首屏(零延迟)
首页home.data.ts
分类页 → category.data.ts
分龄页 → age.config.ts
├─ Step 1: wx.getBackgroundFetchData('pre') 获取预拉取结果
成功存入 globalData.pageConfig
→ wx.setStorageSync('pageConfig', data) 写入缓存
→ 通知页面就绪
├─ Step 2: 读取数据预拉取结果
wx.getBackgroundFetchData('pre') 获取 page-config.json
│ 取出当前页面对应的配置(home / category / age
├─ Step 2: 预拉取失败 → wx.getStorageSync('pageConfig') 读取缓存
命中 → 存入 globalData.pageConfig → 通知页面就绪
└─ Step 3: 成功 → setData 更新页面内容
失败 → 保持本地兜底数据,用户无感知
└─ Step 3: 缓存也为空 → 调用 pageConfigFetch 云函数兜底
成功 → 存入 globalData.pageConfig
→ wx.setStorageSync('pageConfig', data) 写入缓存
→ 通知页面就绪
TabBar 页面 onLoad(仅首次进入触发)
├─ globalData.pageConfig 已就绪
│ → 读取对应字段(home / category / age)→ setData 渲染
└─ globalData.pageConfig 未就绪
→ 显示加载态(骨架屏 / loading)
→ 注册回调,配置就绪后自动渲染
TabBar 页面 onShow(后续每次切换触发)
└─ 直接从 globalData.pageConfig 读取,零延迟,无网络请求
```
> **数据预拉取**在 `app.json` 中配置 `fetchDataUrl`,小程序冷启动时由微信客户端自动发起,不占用页面加载时间。预拉取的数据指向云存储中的 `content/page-config.json`。
**三级降级策略:** 预拉取 → storage 缓存 → 云函数调用
### 2.4 云函数设计
| 层级 | 数据来源 | 耗时 | 适用场景 |
| --- | ----------------------------------- | ------- | -------------------- |
| L1 | `wx.getBackgroundFetchData('pre')` | 0ms | 正常冷启动,预拉取已完成 |
| L2 | `wx.getStorageSync('pageConfig')` | ~1ms | 预拉取失败,但之前成功过 |
| L3 | `pageConfigFetch` 云函数 | 200-500ms | 首次使用或缓存被清理 |
新增一个统一云函数 `pageContentUpdate`
**设计要点:**
```ts
// cloudfunctions/pageContentUpdate/index.js
type PageContentUpdatePayload = {
page: 'home' | 'category' | 'age'; // 要更新的页面
data: HomePageData | CategoryPageData | AgePageData; // 该页面的完整配置
};
```
- 配置在 `app.onLaunch` 时加载一次,存入 `globalData.pageConfig`,三个页面共享同一份内存数据
- 每次预拉取或云函数获取成功后,同步写入 storage 作为下次启动的缓存兜底
- TabBar 页面 `onLoad` 只执行一次,`onShow` 每次切换都触发,天然适合「启动时加载一次,后续复用」的模式
- 不再维护 `home.data.ts``category.data.ts` 等本地静态数据文件,消除双数据源不一致的问题
- storage 缓存的 key 为 `pageConfig`,使用 `wx.setStorageSync` / `wx.getStorageSync` 同步读写
执行逻辑:
### 2.5 云函数总览
```
接收参数 { page, data }
→ 从云存储读取当前 content/page-config.json
→ 合并:config[page] = data
→ config.version++
→ config.updatedAt = new Date()
→ 写回云存储 content/page-config.json
→ 返回 { success, version }
```
| 云函数 | 职责 |
| ---------------------- | ------------------------------------------------- |
| `pageContentBuild` | 生成指定页面配置(home / category),写入数据库 + 云存储 |
| `pageConfigFetch` | 数据预拉取接口,从数据库读取完整配置返回给客户端 |
| `homeAutoRefresh` | 定时任务,每日 22:00 自动更新 featured 和 hot |
| `homeTimerControl` | 暂停/启动首页定时刷新任务(更新 settings 集合标记位) |
| `worksheetsQuery` | 查询 worksheet 列表(支持按分类、状态筛选) |
| `worksheetsUpdateStatus` | 更新单个 worksheet 的状态 |
---
## 三、首页内容管理
### 3.1 管理 UI
### 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
| 区块 | 对应字段 | 数据来源 |
| ------ | -------------- | ----------------------------------- |
| 分类 tab | `categoryTabs` | 跟随 `CATEGORY_LIST_WITH_ALL`,一般不单独管理 |
| 年龄入口 | `ageBands` | 跟随 `AGE_BANDS`,可管理描述文案 |
| 今日推荐 | `featured` | 自动取 downloads 最多的 5 个 active worksheet,支持手动微调 |
| 热门推荐 | `hot` | 自动取 likes 最多的 5 个 active worksheet,支持手动微调 |
| 分类分区 | `sections` | 每个分类选择若干 active worksheet,支持排序 |
| 定时任务卡片 | — | 显示定时任务状态(运行中/已暂停),可切换 |
| 今日推荐 | `featured` | 自动:downloads 最多的 5 个 active worksheet |
| 热门推荐 | `hot` | 自动:likes 最多的 5 个 active worksheet |
| 分类分区 | `sections` | 从已保存配置回显,支持手动添加/移除 worksheet |
### 3.3 数据回显逻辑
### 3.2 自动刷新与定时任务
**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 自动刷新与定时任务
今日推荐和热门推荐采用**自动化 + 手动微调**的方式:
- **默认数据**:页面加载时自动查询 active worksheets,按 `downloads desc` 取前 5 填入 featured,按 `likes desc` 取前 5 填入 hot
- **定时任务**:云函数 `homeAutoRefresh` 配置定时触发器,每日 22:00 自动执行,查询最新 top5 数据更新 `page-config.json` 中的 `home.featured``home.hot`
- **开关控制**:通过 `settings` 集合中 `homeAutoRefresh` 文档的 `enabled` 字段控制。管理页提供暂停/启动按钮,调用 `homeTimerControl` 云函数切换状态。`homeAutoRefresh` 执行时先检查此标记,`enabled === false` 则跳过
- **手动微调**:用户可在自动填充的基础上手动添加/移除 worksheet,点击「更新首页数据」手动生成
- **手动更新**:用户可在管理页手动添加/移除 worksheet,点击「更新首页数据」调用 `pageContentBuild({ page: 'home', ... })` 生成
```
管理页 onLoad
→ 查询 active worksheets
→ 按 downloads desc 取前 5 → featured
→ 按 likes desc 取前 5 → hot
→ 用户可手动微调
→ 点击「更新首页数据」→ pageContentBuild({ page: 'home', ... })
定时任务(每日 22:00
→ homeAutoRefresh 云函数
→ 检查 settings/homeAutoRefresh.enabled
→ enabled === false → 跳过执行
→ 查询 top5 downloads → featured
→ 查询 top5 likes → hot
更新 page-config.json 中 home.featured 和 home.hot
readCurrentConfig() → 只更新 home.featured 和 home.hot → saveConfig()
```
### 3.3 数据结构
### 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 数据结构
```ts
type HomePageData = {
@@ -173,41 +277,38 @@ type HomePageData = {
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不足时从该分类 active 内容补齐 |
### 3.4 相关云函数
| 云函数 | 职责 |
| ------------------- | ------------------------------------------- |
| `pageContentBuild` | 手动更新首页全量数据(featured + hot + sections |
| `homeAutoRefresh` | 定时任务,每日 22:00 自动更新 featured 和 hot |
| `homeTimerControl` | 暂停/启动定时任务(更新 settings 集合标记位) |
| `sections` | 管理页配置的 worksheetIdsmorePath 来自 CATEGORY_LIST |
---
## 四、分类页内容管理
### 4.1 管理 UI
### 4.1 管理页入口
分类页管理 tab 顶部展示分类选择器(来源 `CATEGORY_LIST`),默认选中 `math`
`supportPages/categoryContentManage/categoryContentManage`
### 4.2 管理 UI
分类页管理顶部展示分类选择器(来源 `CATEGORY_LIST`),默认选中 `math`
分类头部统计:
| 指标 | 查询规则 |
| --- | ----------------------------- |
| 总数 | `worksheets.category == 当前分类` |
@@ -215,21 +316,29 @@ type HomePageData = {
| 线上 | `status == active` |
| 已隐藏 | `status == hidden` |
列表展示当前分类下所有 worksheet(按 `status``sortOrder``updatedAt` 排序),每项显示预览图、标题、ID、年龄/难度/标签、状态、排序权重。
列表展示当前分类下所有 worksheet(按 `draft → hidden → active` 排序),每项显示预览图、标题、ID、年龄/难度/标签、状态、操作按钮。
操作按钮:
| 当前状态 | 按钮样式 | 可执行操作 |
| -------- | ------- | --------------- |
| `draft` | 绿色按钮 | 激活上线 |
| `active` | 灰色按钮 | 下架为 hidden |
| `hidden` | 黄色按钮 | 恢复为 draft 或直接激活 |
| 当前状态 | 可执行操作 |
| -------- | --------------- |
| `draft` | 激活上线 |
| `active` | 下架为 hidden |
| `hidden` | 恢复为 draft 或直接激活 |
| 任意 | 调整排序、预览跳转、刷新 |
### 4.3 分类页支持 id 参数
分类页 `/pages/category/category` 支持通过 URL 参数 `id` 指定默认选中的分类:
### 4.2 数据结构
```
/pages/category/category?id=english → 默认选中「英语启蒙」
/pages/category/category?id=math → 默认选中「数感启蒙」
/pages/category/category → 默认选中「全部」
```
首页 sections 的「查看更多」链接使用此参数跳转到对应分类。
### 4.4 数据结构
```ts
type CategoryPageData = {
@@ -245,14 +354,14 @@ type CategoryPageData = {
生成时从 `worksheets` 集合查询 `status == active` 的内容,按分类分组、按 `sortOrder` 排序,转换为 `CategoryItem`
### 4.3 更新流程
### 4.5 更新流程
```
分类页管理 tab → 管理 worksheet 状态和排序
分类页管理 → 管理 worksheet 状态和排序
→ 点击「更新分类数据」
→ 查询所有 active worksheets,按 category 分组
→ 调用 pageContentUpdate({ page: 'category', data: ... })
→ 云函数更新 page-config.json 中的 category 字段
→ 调用 pageContentBuild({ page: 'category' })
→ 云函数更新 page_configs/current 中的 category 字段 + 同步云存储
```
---
@@ -264,23 +373,6 @@ type CategoryPageData = {
`miniprogram/pages/age/` 下新增 `age.config.ts`,将年龄段划分、能力目标、默认 worksheet 写死在配置中,作为兜底数据和管理页的基础结构。
```ts
// miniprogram/pages/age/age.config.ts
import { AGE_BANDS, type AgeBandKey } from '../../core/data/difficulty';
export type AbilityItem = {
icon: string;
title: string;
desc: string;
};
export type WeekPlan = {
week: number;
theme: string;
worksheetIds: string[]; // worksheet 业务 ID
worksheetTitles: string[]; // 兜底展示标题
};
export type AgeBandConfig = {
key: AgeBandKey;
label: string;
@@ -288,61 +380,13 @@ export type AgeBandConfig = {
abilities: AbilityItem[];
weeks: WeekPlan[];
};
export const AGE_CONFIG: AgeBandConfig[] = [
{
key: '3-4',
label: '3-4 岁',
subLabel: '启蒙认知',
abilities: [
{ icon: '🔢', title: '数感', desc: '认读 1-5、点数对应' },
{ icon: '✏️', title: '书写', desc: '涂鸦线条、简单描红' },
{ icon: '🧩', title: '思维', desc: '找相同、简单配对' },
],
weeks: [
{ week: 1, theme: '数感启蒙',
worksheetIds: [], worksheetTitles: ['找数字涂一涂', '5以内加法', '数数连一连'] },
{ week: 2, theme: '形状与连线',
worksheetIds: [], worksheetTitles: ['识别形状', '线条识别', '数字点连线'] },
{ week: 3, theme: '趣味专注',
worksheetIds: [], worksheetTitles: ['颜色找规律', '方格推理', '格子仿画'] },
{ week: 4, theme: '综合练习',
worksheetIds: [], worksheetTitles: ['10以内加减法', '识字卡', '连连看'] },
],
},
{
key: '4-5',
label: '4-5 岁',
subLabel: '基础练习',
abilities: [
{ icon: '🔢', title: '数感', desc: '10 以内数数、比大小' },
{ icon: '✏️', title: '书写', desc: '笔画模仿、图形描边' },
{ icon: '🧩', title: '思维', desc: '规律排序、图形分类' },
],
weeks: [
{ week: 1, theme: '数感启蒙',
worksheetIds: [], worksheetTitles: ['找数字涂一涂', '5以内加法', '数数连一连'] },
{ week: 2, theme: '形状与连线',
worksheetIds: [], worksheetTitles: ['识别形状', '线条识别', '数字点连线'] },
{ week: 3, theme: '趣味专注',
worksheetIds: [], worksheetTitles: ['颜色找规律', '方格推理', '格子仿画'] },
{ week: 4, theme: '综合练习',
worksheetIds: [], worksheetTitles: ['10以内加减法', '识字卡', '连连看'] },
],
},
// 5-6、6-7、7-8 结构相同,abilities 和 weeks 内容不同
// ... 完整配置见 age.config.ts 文件
];
```
> `worksheetIds` 初始为空数组,由分龄管理页选择后填入。`worksheetTitles` 作为兜底展示。
### 5.2 管理 UI
分龄管理 tab 按年龄段切换:`3-4 岁 | 4-5 岁 | 5-6 岁 | 6-7 岁 | 7-8 岁`
每个年龄段下的管理内容:
分龄管理按年龄段切换:`3-4 岁 | 4-5 岁 | 5-6 岁 | 6-7 岁 | 7-8 岁`
| 模块 | 操作 | 说明 |
| ---- | ------------ | -------------------------------- |
@@ -350,59 +394,14 @@ export const AGE_CONFIG: AgeBandConfig[] = [
| 四周路线 | 选择 worksheet | 每周选择 4 个 worksheet,主题来自 config |
| 推荐内容 | 选择 worksheet | 动态数据,展示 likes 数量最高的 6 个worksheet |
worksheet 选择器默认筛选条件:
### 5.3 更新流程
```
status == active
ageMin <= 当前年龄段 maxAge
ageMax >= 当前年龄段 minAge
```
**核心简化:** 分龄管理页不需要配置能力目标和周主题(这些写死在 `age.config.ts` 中),只需要为不同年龄段、不同周选择对应的 worksheet。
### 5.3 数据结构
```ts
type AgePageData = {
ageTabs: Array<{
key: AgeBandKey;
rangeText: string;
subLabel: string;
}>;
bands: Record<AgeBandKey, {
goalTitle: string;
abilityItems: AbilityItem[];
weekPlans: Array<{
week: number;
theme: string;
exercises: Array<{
id: string;
title: string;
path: string;
previewImg?: string;
}>;
}>;
recommendedItems: Array<{
id: string;
title: string;
image?: string;
path: string;
}>;
}>;
};
```
### 5.4 更新流程
```
分龄管理 tab → 选择年龄段
→ 为每周选择 worksheet
→ 选择推荐内容
分龄管理 → 选择年龄段 → 为每周选择 worksheet → 选择推荐内容
→ 点击「更新分龄数据」
→ 合并 age.config.ts 的能力目标 + 管理页选择的 worksheet 详情
→ 调用 pageContentUpdate({ page: 'age', data: ... })
→ 云函数更新 page-config.json 中的 age 字段
→ 调用 pageContentBuild({ page: 'age', data: ... })
→ 云函数更新 page_configs/current 中的 age 字段 + 同步云存储
```
---
@@ -411,70 +410,75 @@ type AgePageData = {
```
supportPages/debug/debug
├─ 分类基础数据管理 (已有:同步 CATEGORY_LIST → categories
├─ Worksheet 内容池 (已有:状态、分类、上下架、预览)
└─ 页面内容管理 (新增)
├─ Tab: 首页管理 → 编排 featured / hot / sections
├─ Tab: 分类页管理 → 管理 worksheet 状态和排序
└─ Tab: 分龄页管理 → 按年龄段选择周 worksheet
├─ 分类基础数据管理 (已有:同步 CATEGORY_LIST → categories
├─ Worksheet 内容池 (已有:状态、分类、上下架、预览)
└─ 页面内容管理 (新增)
├─ 📋 分类页内容管理 → supportPages/categoryContentManage
├─ 🏠 首页内容管理 → supportPages/homeContentManage
└─ 📅 分龄页内容管理 → supportPages/ageContentManage(待开发)
```
---
## 七、实施计划
### Phase 1:基础设施
### Phase 1:基础设施
1. 配置 `app.json` 数据预拉取,指向 `content/page-config.json`
2. 实现 `pageContentUpdate` 云函数
3. 上传初始 `page-config.json`(从现有 `*.data.ts` 转换)
4. 三个页面统一接入预拉取加载逻辑
1. 实现 `pageContentBuild` 云函数(读写数据库 + 同步云存储)
2. 实现 `pageConfigFetch` 云函数(数据预拉取接口)
3. 实现 `worksheetsQuery` / `worksheetsUpdateStatus` 云函数
### Phase 2:分类页管理
### Phase 2:分类页管理
1. 实现分类页管理 tabworksheet 列表、状态管理、排序
2. 实现「更新分类数据」→ 生成 category 配置 → 调用云函数更新
1. 实现分类页管理worksheet 列表、状态管理)
2. 实现「更新分类数据」→ 生成 category 配置
3. 分类页支持 `?id=xxx` 参数默认选中分类
### Phase 3:首页管理
### Phase 3:首页管理
1. 实现首页管理 tabfeatured / hot / sections 编排
1. 实现首页管理featured / hot 自动填充 + sections 配置回显
2. 实现 worksheet 选择器弹窗
3. 实现「更新首页数据」→ 生成 home 配置 → 调用云函数更新
3. 实现定时任务(homeAutoRefresh + homeTimerControl
4. 实现「更新首页数据」→ 生成 home 配置
### Phase 4:分龄页管理
### Phase 4:分龄页管理(待开发)
1. 新增 `age.config.ts` 配置文件
2. 实现分龄管理 tab(年龄段切换、周 worksheet 选择)
3. 实现「更新分龄数据」→ 合并 config + worksheet 详情 → 调用云函数更新
2. 实现分龄管理(年龄段切换、周 worksheet 选择)
3. 实现「更新分龄数据」→ 合并 config + worksheet 详情
---
## 八、风险与约束
| 风险 | 处理方式 |
| ----------------- | ----------------------------------------- |
| 预拉取失败 | 本地 `*.data.ts` / `age.config.ts` 兜底,用户无感知 |
| JSON 生成失败 | 云函数返回错误,管理页提示重试,线上数据不受影响 |
| 首页配置引用了 hidden 内容 | 生成时只允许 active 内容,不足时返回警告 |
| 预拉取失败 | 依次降级:storage 缓存 → pageConfigFetch 云函数兜底 |
| 配置生成失败 | 云函数返回错误,管理页提示重试,线上数据不受影响 |
| 首页配置引用了 hidden 内容 | sections 回显时通过 wsMap 匹配,已下架的自动过滤 |
| 更新某页数据清空其他页 | 云函数从数据库读取完整配置,只覆盖对应字段,其他页面数据不受影响 |
| 分龄内容不适龄 | 选择器按年龄段默认过滤 |
| 并发更新冲突 | 云函数使用 version 乐观锁,冲突时提示重新加载后重试 |
---
## 九、关键文件索引
| 文件 / 模块 | 职责 |
| ----------------------------------- | ------------------------------ |
| `supportPages/contentManage/` | 三 tab 内容管理页 |
| `cloudfunctions/pageContentBuild/` | 生成 `page-config.json` 中指定页面的配置 |
| `cloudfunctions/homeAutoRefresh/` | 定时任务,每日自动更新首页推荐位数据 |
| `cloudfunctions/homeTimerControl/` | 暂停/启动首页定时刷新任务 |
| `pages/age/age.config.ts` | 分龄页兜底配置(年龄段、能力目标、默认 worksheet) |
| `pages/home/home.data.ts` | 首页兜底数据 |
| `pages/category/category.data.ts` | 分类页兜底数据 |
| `content/page-config.json`(云存储) | 三个页面的统一配置 JSON |
| 文件 / 模块 | 职责 |
| -------------------------------------------- | ------------------------------ |
| `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` | 分龄页兜底配置 |
| `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`(云数据库) | 定时任务开关标记 |