const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); const CONFIG_PATH = 'content/page-config.json'; const DIFFICULTY_LABELS = { 1: '入门', 2: '基础', 3: '进阶', 4: '挑战', }; function ageBand(min, max) { return `${min}-${max}岁`; } function toDisplayItem(ws) { return { id: ws._id, title: ws.title || '', subtitle: ws.subtitle || '', previewImg: ws.previewImg || '', ageBand: ageBand(ws.ageMin || 0, ws.ageMax || 0), ageMin: ws.ageMin || 0, ageMax: ws.ageMax || 0, difficulty: ws.difficulty || 2, difficultyLabel: DIFFICULTY_LABELS[ws.difficulty] || '基础', path: ws.path || '', available: true, likes: ws.likes || 0, downloads: ws.downloads || 0, date: ws.updatedAt ? new Date(ws.updatedAt).toISOString().slice(0, 10) : new Date().toISOString().slice(0, 10), }; } 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 { 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 .collection('categories') .orderBy('sortOrder', 'asc') .get(); // Get all active worksheets const { data: worksheets } = await db .collection('worksheets') .where({ status: 'active' }) .orderBy('sortOrder', 'asc') .orderBy('updatedAt', 'desc') .limit(100) .get(); // Group by category const grouped = {}; for (const ws of worksheets) { const cat = ws.category || 'unknown'; if (!grouped[cat]) grouped[cat] = []; grouped[cat].push(toDisplayItem(ws)); } const categoryData = { searchPlaceholder: '搜索练习纸...', categories: categories.map((cat) => ({ id: cat._id, name: cat.name || '', icon: cat.icon || '', items: grouped[cat._id] || [], })), }; const stats = {}; for (const cat of categoryData.categories) { stats[cat.id] = cat.items.length; } 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' && page !== 'home') { throw new Error('page 参数不合法,支持 category / home'); } const config = await readCurrentConfig(); if (page === 'category') { const { categoryData, stats } = await buildCategoryData(db); config.category = categoryData; config.version = (config.version || 0) + 1; config.updatedAt = new Date().toISOString(); 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, message: error instanceof Error ? error.message : '生成分类页数据失败', }; } };