feat:完成首页、分类页内容管理功能开发

This commit is contained in:
R524809
2026-04-30 13:30:42 +08:00
parent fa39938e5a
commit c112c70bab
25 changed files with 1465 additions and 65 deletions
+165 -24
View File
@@ -36,15 +36,68 @@ function toDisplayItem(ws) {
};
}
function toHomeDisplayItem(ws) {
return {
id: ws._id,
title: ws.title || '',
subtitle: ws.subtitle || '',
category: ws.category || '',
ageBand: ageBand(ws.ageMin || 0, ws.ageMax || 0),
difficulty: DIFFICULTY_LABELS[ws.difficulty] || '基础',
icon: '',
img: ws.previewImg || '',
badge: ws.isNew ? 'NEW' : ws.isHot ? 'HOT' : '',
path: ws.path || '',
available: true,
};
}
const HOME_AGE_BANDS = [
{ key: '3-4', label: '3-4 岁', desc: '启蒙认知', path: '/pages/age/age' },
{ key: '4-5', label: '4-5 岁', desc: '基础练习', path: '/pages/age/age' },
{ key: '5-6', label: '5-6 岁', desc: '能力提升', path: '/pages/age/age' },
{ key: '6-7', label: '6-7 岁', desc: '幼小衔接', path: '/pages/age/age' },
{ key: '7-8', label: '7-8 岁', desc: '知识拓展', path: '/pages/age/age' },
];
async function readCurrentConfig() {
const db = cloud.database();
try {
const res = await cloud.downloadFile({ fileID: CONFIG_PATH });
return JSON.parse(res.fileContent.toString('utf-8'));
const { data } = await db
.collection('page_configs')
.doc('current')
.get();
return data;
} catch {
return { home: null, category: null, age: null, version: 0 };
}
}
async function saveConfig(config) {
const db = cloud.database();
// 1. Save to database (source of truth)
const toSave = { ...config };
delete toSave._id; // _id can't be in update data
try {
await db.collection('page_configs').doc('current').get();
await db.collection('page_configs').doc('current').update({
data: toSave,
});
} catch {
await db.collection('page_configs').doc('current').set({
data: toSave,
});
}
// 2. Upload to cloud storage (for mini-program pre-fetch)
const buffer = Buffer.from(JSON.stringify(config), 'utf-8');
await cloud.uploadFile({
cloudPath: CONFIG_PATH,
fileContent: buffer,
});
}
async function buildCategoryData(db) {
// Get all categories
const { data: categories } = await db
@@ -87,38 +140,126 @@ async function buildCategoryData(db) {
return { categoryData, stats };
}
async function buildHomeData(db, event) {
const featuredIds = Array.isArray(event.featuredIds) ? event.featuredIds : [];
const hotIds = Array.isArray(event.hotIds) ? event.hotIds : [];
const sectionConfigs = Array.isArray(event.sections) ? event.sections : [];
// Collect all referenced worksheet ids
const allIds = new Set([
...featuredIds,
...hotIds,
...sectionConfigs.flatMap((s) => s.worksheetIds || []),
]);
// Batch query all referenced worksheets
const wsMap = {};
if (allIds.size > 0) {
const _ = db.command;
const { data } = await db
.collection('worksheets')
.where({ _id: _.in([...allIds]) })
.limit(100)
.get();
for (const ws of data) {
wsMap[ws._id] = ws;
}
}
// Build categoryTabs from categories collection
const { data: categories } = await db
.collection('categories')
.orderBy('sortOrder', 'asc')
.get();
const categoryTabs = [
{ id: 'all', name: '全部' },
...categories.map((c) => ({ id: c._id, name: c.name || '' })),
];
// Build featured / hot arrays
const featured = featuredIds
.filter((id) => wsMap[id])
.map((id) => toHomeDisplayItem(wsMap[id]));
const hot = hotIds
.filter((id) => wsMap[id])
.map((id) => toHomeDisplayItem(wsMap[id]));
// Build sections
const sections = sectionConfigs
.filter((s) => s.id && Array.isArray(s.worksheetIds))
.map((s) => {
const cat = categories.find((c) => c._id === s.id);
return {
id: s.id,
title: cat ? cat.name : s.id,
subtitle: s.subtitle || '',
morePath: s.morePath || '',
items: (s.worksheetIds || [])
.filter((id) => wsMap[id])
.map((id) => toHomeDisplayItem(wsMap[id])),
};
});
const homeData = {
searchPlaceholder: '搜索你喜欢的练习册...',
categoryTabs,
ageBands: HOME_AGE_BANDS,
featured,
hot,
sections,
};
return { homeData };
}
exports.main = async (event) => {
try {
const db = cloud.database();
const page = String(event.page || '').trim();
if (page !== 'category') {
throw new Error('当前仅支持 page=category');
if (page !== 'category' && page !== 'home') {
throw new Error('page 参数不合法,支持 category / home');
}
const { categoryData, stats } = await buildCategoryData(db);
// Read current config, merge, bump version
const config = await readCurrentConfig();
config.category = categoryData;
config.version = (config.version || 0) + 1;
config.updatedAt = new Date().toISOString();
// Upload to cloud storage
const buffer = Buffer.from(JSON.stringify(config), 'utf-8');
await cloud.uploadFile({
cloudPath: CONFIG_PATH,
fileContent: buffer,
});
if (page === 'category') {
const { categoryData, stats } = await buildCategoryData(db);
config.category = categoryData;
config.version = (config.version || 0) + 1;
config.updatedAt = new Date().toISOString();
return {
success: true,
data: {
version: config.version,
updatedAt: config.updatedAt,
stats,
},
};
await saveConfig(config);
return {
success: true,
data: {
version: config.version,
updatedAt: config.updatedAt,
stats,
},
};
}
if (page === 'home') {
const { homeData } = await buildHomeData(db, event);
config.home = homeData;
config.version = (config.version || 0) + 1;
config.updatedAt = new Date().toISOString();
await saveConfig(config);
return {
success: true,
data: {
version: config.version,
updatedAt: config.updatedAt,
featured: homeData.featured.length,
hot: homeData.hot.length,
sections: homeData.sections.length,
},
};
}
} catch (error) {
return {
success: false,