# Doodle Mini - 技术架构设计文档 > 版本:v2.0 > 最后更新:2026-03-24 > 配套文档:[现有功能清单](./现有功能清单.md) | [产品设计文档](./产品设计文档.md) --- ## 一、技术决策总览 ### 1.1 核心原则 | 原则 | 说明 | |------|------| | **成本优先** | 优先使用微信云开发,避免自建服务器的运维和费用 | | **渐进增强** | 先小程序跑通,后续再扩展 PC Web,不为未来过度设计 | | **前端为主** | Canvas 渲染、随机生成等核心逻辑保持前端执行,后端仅做必要的数据存储和服务 | | **可迁移** | 核心绘制逻辑与平台 API 解耦,为将来迁移 Web 做准备 | ### 1.2 后端方案决策 #### 需要后端的场景 vs 不需要的场景 ``` ┌─────────────────────────────────────────────────────────┐ │ 不需要后端(前端完成) │ ├─────────────────────────────────────────────────────────┤ │ ✅ Canvas 绘制所有题型 │ │ ✅ 随机题目生成算法 │ │ ✅ A4 排版与打印预览 │ │ ✅ PNG 导出 → 保存相册 │ │ ✅ 题型配置数据(可前端内置 JSON) │ │ ✅ 分享功能 │ │ ✅ 本地收藏/历史(wx.Storage) │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 需要后端(轻量级) │ ├─────────────────────────────────────────────────────────┤ │ 📦 内容配置远程下发(热更新题型/分类,无需发版) │ │ 📦 静态素材存储与 CDN 分发(涂色卡线稿、折纸模板等) │ │ 📦 用户数据同步(收藏、历史记录跨设备) │ │ 📦 下载统计 / 热度排名 │ │ 📦 PDF 多页合并导出 │ │ 📦 未来 PC Web 端的 API 服务 │ └─────────────────────────────────────────────────────────┘ ``` #### 方案对比与选择 | 维度 | 微信云开发 | 自建云服务器 | 决策 | |------|-----------|-------------|------| | 接入成本 | ⭐ 极低(已有基础) | ⭐⭐⭐ 需搭建部署 | 云开发 ✅ | | 运维成本 | ⭐ 免运维 | ⭐⭐⭐ 需监控/维护 | 云开发 ✅ | | 费用 | 免费额度足够早期(数据库 2GB/存储 5GB/云函数 10万次/月) | 最低约 ¥50-100/月 | 云开发 ✅ | | 小程序集成 | ⭐ 原生集成,鉴权零成本 | ⭐⭐ 需对接登录/鉴权 | 云开发 ✅ | | PC Web 支持 | ⭐⭐ 需通过 HTTP API 桥接 | ⭐ 天然支持 | 自建 ✅ | | 灵活性 | ⭐⭐ 受限于云开发 SDK | ⭐ 完全自由 | 自建 ✅ | | 数据迁移 | ⭐⭐ 可导出但不方便 | ⭐ 标准数据库 | 自建 ✅ | **最终决策:近期使用微信云开发,远期按需引入轻量后端** ``` Phase 1-3(当前~8周):100% 微信云开发 └── 云数据库 + 云存储 + 云函数,零服务器成本 Phase 4+(需要 PC Web 时):引入轻量后端 └── 方案 A(推荐):云开发 HTTP API + PC Web 直接调用 └── 方案 B:Cloudflare Workers / Vercel Serverless 做 API 代理层 └── 方案 C:轻量 Node.js 服务(仅在流量大时考虑) ``` --- ## 二、系统架构总图 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 用户端 │ │ │ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │ │ 微信小程序 (当前) │ │ PC Web (未来) │ │ │ │ │ │ │ │ │ │ ┌────────────────┐ │ │ ┌────────────────┐ │ │ │ │ │ UI 层 (WXML) │ │ │ │ UI 层 (React) │ │ │ │ │ │ Skyline 渲染 │ │ │ │ Tailwind CSS │ │ │ │ │ └───────┬────────┘ │ │ └───────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────▼────────┐ │ │ ┌───────▼────────┐ │ │ │ │ │ 业务逻辑层 (TS) │ │ │ │ 业务逻辑层 (TS) │ │ │ │ │ │ Page + Mixin │ │ │ │ Hooks + Store │ │ │ │ │ └───────┬────────┘ │ │ └───────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────▼────────┐ │ │ ┌───────▼────────┐ │ │ │ │ │ 共享核心层 │◄─┼─────────┼──► 共享核心层 │ │ │ │ │ │ @doodle/core │ │ │ │ @doodle/core │ │ │ │ │ │ ┌─────────────┐ │ │ │ │ (同一套代码) │ │ │ │ │ │ │ DrawService │ │ │ │ └───────┬────────┘ │ │ │ │ │ │ DataModels │ │ │ │ │ │ │ │ │ │ │ Generators │ │ │ │ │ │ │ │ │ │ │ Utils │ │ │ │ │ │ │ │ │ │ └─────────────┘ │ │ │ │ │ │ │ │ └───────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────▼────────┐ │ │ ┌───────▼────────┐ │ │ │ │ │ 平台适配层 │ │ │ │ 平台适配层 │ │ │ │ │ │ wx.* API │ │ │ │ Web API │ │ │ │ │ │ wx.cloud.* │ │ │ │ HTTP Client │ │ │ │ │ │ Canvas 2D │ │ │ │ Canvas 2D │ │ │ │ │ └───────┬────────┘ │ │ └───────┬────────┘ │ │ │ └──────────┼──────────┘ └──────────┼──────────┘ │ └──────────────┼───────────────────────────────┼──────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 后端服务层 │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 微信云开发 (Phase 1-3) │ │ │ │ │ │ │ │ ┌─────────────┐ ┌──────────┐ ┌────────────┐ │ │ │ │ │ 云数据库 │ │ 云存储 │ │ 云函数 │ │ │ │ │ │ (MongoDB) │ │ (COS) │ │ (Node.js) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • 用户数据 │ │ • 涂色卡 │ │ • getOpenId│ │ │ │ │ │ • 收藏记录 │ │ • 折纸图 │ │ • genPDF │ │ │ │ │ │ • 下载统计 │ │ • 手工图 │ │ • syncData │ │ │ │ │ │ • 内容配置 │ │ • 预览图 │ │ • stats │ │ │ │ │ │ • 反馈数据 │ │ • 字体 │ │ │ │ │ │ │ └─────────────┘ └──────────┘ └────────────┘ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 轻量 API 层 (Phase 4+, 仅 PC Web) │ │ │ │ │ │ │ │ 方案 A: 云开发 HTTP API (推荐,零额外成本) │ │ │ │ 方案 B: Cloudflare Workers (极低成本) │ │ │ │ 方案 C: Vercel Serverless Functions │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ CDN (腾讯云) │ │ │ │ 素材图片 / 字体文件 / 预览图 / 分享图 │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 三、前端架构详细设计 ### 3.1 现有架构问题 ``` 当前代码组织: miniprogram/ ├── pages/ ← 主包页面,各自直接引用 mixin ├── mathPages/ ← 数学分包,每个页面有自己的 Draw 服务 │ └── shared/ ← 数学共用(service、templates、assets) ├── focusPages/ ← 专注力分包,结构同上 │ └── shared/ ├── service/ ← 识字/练字的 Draw 服务 ├── base/ ← pageMixin、callCloud 等基础层 ├── components/ ← UI 组件 ├── constants/ ← 静态配置数据 └── utils/ ← 工具函数 ``` **问题分析:** | 问题 | 详情 | |------|------| | Mixin 模式笨重 | `pageMixin` 通过对象展开合并,无类型安全,难以追踪数据流 | | Draw 服务分散 | 每个分包下各有 `shared/service/`,大量重复的绘制工具方法 | | 平台耦合严重 | Draw 服务直接使用 `wx.createImage`、`canvas.createImage` 等 API | | 模板重复 | `mathPages` 和 `focusPages` 的 `canvas-page-template.wxml` 内容相同 | | 无共享核心 | 数据模型、生成算法与 UI 逻辑混在一起,无法跨平台复用 | | 状态管理原始 | 纯 `setData`,无统一的状态管理方案 | ### 3.2 目标架构 ``` miniprogram/ ├── core/ ← 🆕 共享核心层(平台无关) │ ├── models/ ← 数据模型定义 │ │ ├── worksheet.ts ← WorksheetType 统一模型 │ │ ├── category.ts ← 分类模型 │ │ ├── user.ts ← 用户数据模型 │ │ └── print-config.ts ← 打印配置模型 │ ├── generators/ ← 题目生成算法(纯逻辑) │ │ ├── math/ ← 数学题目生成器 │ │ │ ├── addition.ts │ │ │ ├── subtraction.ts │ │ │ ├── number-sequence.ts │ │ │ └── ... │ │ ├── focus/ ← 专注力题目生成器 │ │ ├── chinese/ ← 语文题目生成器 │ │ └── english/ ← 英语题目生成器 │ ├── draw/ ← 绘制服务(Canvas 2D API,平台无关) │ │ ├── base-draw.ts ← 基础绘制(纸面、页眉、网格等) │ │ ├── math-draw/ ← 数学绘制服务 │ │ ├── focus-draw/ ← 专注力绘制服务 │ │ ├── chinese-draw/ ← 语文绘制服务 │ │ ├── english-draw/ ← 英语绘制服务 │ │ └── craft-draw/ ← 手工/涂色绘制服务 │ ├── data/ ← 内容配置数据 │ │ ├── worksheets.ts ← 所有题型定义(本地兜底) │ │ ├── categories.ts ← 分类定义 │ │ ├── words.ts ← 词库 │ │ └── difficulty.ts ← 难度与年龄映射 │ └── utils/ ← 纯工具函数(无平台依赖) │ ├── random.ts │ ├── math-utils.ts │ └── format.ts │ ├── platform/ ← 🆕 平台适配层 │ ├── canvas-adapter.ts ← Canvas API 适配(wx ↔ Web) │ ├── storage-adapter.ts ← 存储适配(wxStorage ↔ localStorage) │ ├── cloud-adapter.ts ← 云服务适配(wx.cloud ↔ HTTP) │ ├── image-adapter.ts ← 图片加载适配 │ └── share-adapter.ts ← 分享能力适配 │ ├── services/ ← 🆕 业务服务层 │ ├── worksheet-service.ts ← 题型数据加载(本地 + 远程兜底) │ ├── user-service.ts ← 用户数据管理 │ ├── favorite-service.ts ← 收藏管理 │ ├── history-service.ts ← 下载历史管理 │ ├── print-service.ts ← 打印/导出统一入口 │ ├── stats-service.ts ← 统计上报 │ └── ad-service.ts ← 广告管理 │ ├── store/ ← 🆕 状态管理 │ ├── app-store.ts ← 全局状态(用户/配置/主题) │ └── page-store.ts ← 页面级状态工具 │ ├── pages/ ← 重构后的主包页面 │ ├── home/ ← 发现首页(合并原 4 个 Tab 入口) │ ├── category/ ← 分类详情页 │ ├── worksheet/ ← 统一的题型生成/预览页 │ ├── profile/ ← 我的 │ ├── guide/ ← 打印指南 │ ├── favorites/ ← 收藏列表 │ ├── history/ ← 下载历史 │ └── settings/ ← 设置 │ ├── subpackages/ ← 🆕 按品类分包 │ ├── math/ ← 数学题型页面 │ ├── chinese/ ← 语文题型页面 │ ├── english/ ← 英语题型页面 │ ├── puzzle/ ← 益智游戏页面 │ └── craft/ ← 创意手工页面 │ ├── components/ ← UI 组件 │ ├── shared/ ← 通用组件 │ │ ├── worksheet-card/ ← 题型卡片(带预览图) │ │ ├── category-tabs/ ← 分类标签栏 │ │ ├── age-filter/ ← 年龄筛选器 │ │ ├── difficulty-badge/ ← 难度标签 │ │ ├── canvas-preview/ ← Canvas 预览区 │ │ ├── action-bar/ ← 底部操作栏(保存/分享/换一批) │ │ ├── print-header/ ← 打印页眉配置 │ │ └── empty-state/ ← 空状态 │ └── business/ ← 业务组件 │ ├── worksheet-list/ ← 题型列表 │ ├── param-panel/ ← 参数设置面板 │ └── ... │ ├── config/ ← 配置 ├── assets/ ← 静态资源 └── style/ ← 全局样式 ``` ### 3.3 分层职责 ``` ┌──────────────────────────────────────────────────────┐ │ UI 层 │ │ pages/ + components/ (WXML + WXSS + 页面 TS) │ │ 职责:界面渲染、用户交互、调用 services │ ├──────────────────────────────────────────────────────┤ │ 业务服务层 │ │ services/ + store/ │ │ 职责:组合 core 能力,管理状态,对接平台 API │ ├──────────────────────────────────────────────────────┤ │ 平台适配层 │ │ platform/ │ │ 职责:抹平 wx.* 和 Web API 差异 │ ├──────────────────────────────────────────────────────┤ │ 共享核心层 │ │ core/ (纯 TypeScript,零平台依赖) │ │ 职责:绘制服务、生成算法、数据模型 │ │ 🔑 可直接在 Web 项目中 import 使用 │ └──────────────────────────────────────────────────────┘ ``` --- ## 四、共享核心层设计(@doodle/core) 这是未来跨平台复用的关键。所有与 `wx.*` 无关的逻辑都应该放在这一层。 ### 4.1 Canvas 适配器接口 ```typescript /** * 平台无关的 Canvas 上下文接口 * 微信小程序和 Web 的 Canvas 2D API 基本一致, * 但图片加载、字体加载等需要适配 */ interface ICanvasAdapter { getContext(): CanvasRenderingContext2D; setSize(width: number, height: number): void; loadImage(src: string): Promise; toDataURL(type?: string, quality?: number): Promise; toBlob(): Promise; // Web 专用 toTempFilePath(): Promise; // 小程序专用 } ``` ### 4.2 绘制服务基类(平台无关) ```typescript abstract class BaseDrawService { protected ctx: CanvasRenderingContext2D; protected paperWidth: number; protected paperHeight: number; // 这些方法仅使用标准 Canvas 2D API,不依赖任何平台 protected drawGrid(rows: number, cols: number): void { /* ... */ } protected drawText(text: string, x: number, y: number, options: TextOptions): void { /* ... */ } protected drawLine(x1: number, y1: number, x2: number, y2: number): void { /* ... */ } protected drawHeader(config: PrintConfig): void { /* ... */ } protected drawRoundedRect(x: number, y: number, w: number, h: number, r: number): void { /* ... */ } abstract draw(params: DrawParams): Promise; } ``` ### 4.3 题目生成器模式 ```typescript /** * 所有题目生成器实现此接口 * 输入配置参数,输出与平台无关的题目数据 * 绘制服务消费这些数据来渲染 Canvas */ interface IWorksheetGenerator { generate(config: TConfig): TData; getDefaultConfig(): TConfig; } // 示例:加法题生成器 class AdditionGenerator implements IWorksheetGenerator { generate(config: AdditionConfig): AdditionData { const questions = []; for (let i = 0; i < config.count; i++) { const a = randomInt(0, config.maxNumber); const b = randomInt(0, config.maxNumber - a); questions.push({ a, b, answer: a + b }); } return { questions, config }; } } ``` --- ## 五、后端(云开发)详细设计 ### 5.1 云数据库集合设计 ``` 云数据库 Collections: ┌─────────────────────────────────────────────────┐ │ │ │ worksheets (题型配置表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string │ │ │ │ category: string │ │ │ │ subcategory: string │ │ │ │ title: string │ │ │ │ desc: string │ │ │ │ ageRange: [number, number] │ │ │ │ difficulty: 1|2|3|4 │ │ │ │ previewImage: string │ │ │ │ page: string │ │ │ │ tags: string[] │ │ │ │ isNew: boolean │ │ │ │ isHot: boolean │ │ │ │ sortOrder: number │ │ │ │ downloadCount: number │ │ │ │ status: 'active'|'draft'|'hidden' │ │ │ │ createdAt: Date │ │ │ │ updatedAt: Date │ │ │ └─────────────────────────────────────┘ │ │ │ │ categories (分类表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string │ │ │ │ name: string │ │ │ │ icon: string │ │ │ │ color: string │ │ │ │ sortOrder: number │ │ │ │ parentId: string | null │ │ │ └─────────────────────────────────────┘ │ │ │ │ users (用户表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string (openid) │ │ │ │ uuid: string │ │ │ │ nickName: string │ │ │ │ avatarUrl: string │ │ │ │ totalDownloads: number │ │ │ │ createdAt: Date │ │ │ │ lastActiveAt: Date │ │ │ └─────────────────────────────────────┘ │ │ │ │ favorites (收藏表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string │ │ │ │ _openid: string │ │ │ │ worksheetId: string │ │ │ │ createdAt: Date │ │ │ └─────────────────────────────────────┘ │ │ │ │ download_logs (下载日志表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string │ │ │ │ _openid: string │ │ │ │ worksheetId: string │ │ │ │ params: object (生成参数快照) │ │ │ │ createdAt: Date │ │ │ └─────────────────────────────────────┘ │ │ │ │ feedback (用户反馈表) │ │ ┌─────────────────────────────────────┐ │ │ │ _id: string │ │ │ │ _openid: string │ │ │ │ content: string │ │ │ │ contact: string │ │ │ │ createdAt: Date │ │ │ └─────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────┘ ``` ### 5.2 云函数设计 ``` cloudfunctions/ └── doodle/ ├── index.js ← 统一入口(action 路由) ├── package.json ├── src/ │ ├── user/ │ │ ├── getOpenId.js ← ✅ 已有,保留 │ │ └── updateProfile.js ← 🆕 更新用户信息 │ ├── worksheet/ │ │ ├── getList.js ← 🆕 获取题型列表(支持分类/筛选) │ │ ├── getDetail.js ← 🆕 获取题型详情 │ │ └── incrementDownload.js← 🆕 下载计数+1 │ ├── favorite/ │ │ ├── add.js ← 🆕 添加收藏 │ │ ├── remove.js ← 🆕 取消收藏 │ │ └── list.js ← 🆕 我的收藏列表 │ ├── history/ │ │ └── list.js ← 🆕 下载历史 │ ├── feedback/ │ │ └── submit.js ← 🆕 提交反馈 │ └── pdf/ │ └── generate.js ← 🆕 PDF 生成(Phase 4) └── common/ ├── responseMiddleware.js ← ✅ 已有,保留 └── auth.js ← 🆕 鉴权中间件 ``` ### 5.3 云函数调用策略 ``` ┌────────────────────────────────────────────────────────────┐ │ 数据加载策略 │ │ │ │ 题型配置数据 (worksheets/categories) │ │ ┌────────────────────────────────────────┐ │ │ │ 优先级 1: 本地缓存(wx.Storage) │ │ │ │ 优先级 2: 云数据库查询 │ │ │ │ 优先级 3: 前端内置兜底数据 │ ← 保证离线可用 │ │ └────────────────────────────────────────┘ │ │ │ │ 缓存策略: │ │ • 首次启动:云端拉取 → 写入本地缓存 │ │ • 后续启动:先用缓存渲染 → 后台静默更新 │ │ • 缓存有效期:24 小时 │ │ • 无网络:使用本地缓存或内置兜底 │ │ │ │ 用户数据 (favorites/history) │ │ ┌────────────────────────────────────────┐ │ │ │ 本地优先写入 → 后台同步云端 │ │ │ │ 冲突策略:以云端为准(云端时间戳更新) │ │ │ └────────────────────────────────────────┘ │ │ │ │ 统计数据 (download count) │ │ ┌────────────────────────────────────────┐ │ │ │ 批量上报:本地累计 → 退出时/定时上报 │ │ │ │ 非关键路径,允许丢失 │ │ │ └────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────┘ ``` ### 5.4 云存储规划 ``` 云存储目录结构: cloud://doodle-xxx.xxxx/ ├── assets/ │ ├── previews/ ← 题型效果预览图 │ │ ├── math/ │ │ ├── chinese/ │ │ ├── english/ │ │ ├── puzzle/ │ │ └── craft/ │ ├── coloring/ ← 涂色卡线稿(SVG/PNG) │ │ ├── animals/ │ │ ├── vehicles/ │ │ ├── holidays/ │ │ └── ... │ ├── origami/ ← 折纸展开图 │ ├── stickers/ ← 贴纸素材 │ ├── maze-templates/ ← 迷宫模板数据(JSON) │ └── craft-templates/ ← 手工模板 ├── fonts/ ← 字体文件 │ ├── SimHei.ttf │ └── handwriting.ttf └── share/ ← 分享图 └── default-share.png ``` --- ## 六、关键技术方案 ### 6.1 Canvas 渲染架构(改进) **现有问题**:预览区尺寸初始化和 A4 导出尺寸是两阶段,`setPaper()` 会重设 Canvas 尺寸。 **改进方案**: ``` ┌─────────────────────────────────────────────────────┐ │ Canvas 渲染管线 │ │ │ │ ① 逻辑坐标系统(所有 Draw 服务统一使用) │ │ 固定使用 A4 逻辑坐标: 595 × 842 │ │ 所有绘制操作都在这个坐标系下 │ │ │ │ ② 预览渲染 │ │ Canvas 物理尺寸 = 容器宽度 × dpr │ │ ctx.scale(容器宽度/595 × dpr, ...) │ │ → 绘制服务使用 A4 坐标 → 自动缩放到预览区 │ │ │ │ ③ 导出渲染 │ │ Canvas 物理尺寸 = 1240 × 1754 (150DPI A4) │ │ ctx.scale(1240/595, 1754/842) │ │ → 同一套绘制代码 → 导出高清图 │ │ │ │ 好处: │ │ • Draw 服务完全不感知预览/导出差异 │ │ • 同一套代码在小程序和 Web 上直接可用 │ │ • 预览和导出结果 100% 一致 │ └─────────────────────────────────────────────────────┘ ``` ### 6.2 图片加载适配 ```typescript // platform/image-adapter.ts // 小程序实现 class WxImageAdapter implements IImageAdapter { async loadImage(canvas: WechatMiniprogram.Canvas, src: string) { const img = canvas.createImage(); return new Promise((resolve, reject) => { img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); } } // Web 实现(未来) class WebImageAdapter implements IImageAdapter { async loadImage(_canvas: HTMLCanvasElement, src: string) { const img = new Image(); img.crossOrigin = 'anonymous'; return new Promise((resolve, reject) => { img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); } } ``` ### 6.3 PDF 导出方案(Phase 4) ``` 方案对比: A. 云函数生成 PDF(推荐) 云函数接收绘制参数 JSON → 服务端用 pdfkit 生成 → 上传云存储 → 返回下载链接 优点:不受客户端限制,可生成多页 缺点:云函数有执行时间限制(60s),复杂图形渲染较慢 B. 客户端多页 Canvas 拼接 前端生成多张 PNG → 云函数用 pdfkit 合并为 PDF 优点:渲染在客户端,云函数只做拼接 缺点:需上传多张图片到云存储,流量开销大 C. Web 端直接使用 jsPDF 仅适用于 PC Web 版本 优点:零服务器成本 缺点:小程序不可用 推荐路径: 小程序:方案 B(客户端渲染 + 云端拼接) PC Web:方案 C(jsPDF 纯前端) ``` ### 6.4 涂色卡/迷宫等素材型内容方案 ``` ┌──────────────────────────────────────────────────────┐ │ 两类内容的技术方案差异 │ │ │ │ 类型 A:算法生成型(现有主力) │ │ ┌──────────────────────────────────────┐ │ │ │ 前端算法随机生成题目数据 │ │ │ │ → DrawService 渲染到 Canvas │ │ │ │ → 导出图片 │ │ │ │ │ │ │ │ 适用:数学题、专注力题、字母练习等 │ │ │ │ 优点:无限变化、零存储成本 │ │ │ │ 开发方式:写 Generator + DrawService │ │ │ └──────────────────────────────────────┘ │ │ │ │ 类型 B:素材模板型(需扩展) │ │ ┌──────────────────────────────────────┐ │ │ │ 设计师制作 SVG/PNG 线稿 │ │ │ │ → 上传到云存储 │ │ │ │ → 前端加载 + Canvas 排版到 A4 │ │ │ │ → 导出图片 │ │ │ │ │ │ │ │ 适用:涂色卡、折纸、手工、贴纸等 │ │ │ │ 优点:品质高、风格可控 │ │ │ │ 开发方式:素材管理 + 通用排版渲染器 │ │ │ └──────────────────────────────────────┘ │ │ │ │ 类型 C:混合型 │ │ ┌──────────────────────────────────────┐ │ │ │ 算法生成结构 + 素材填充 │ │ │ │ │ │ │ │ 适用:迷宫(算法生路径+主题皮肤) │ │ │ │ 找不同(模板+随机差异点) │ │ │ │ 闪卡(模板+词库数据) │ │ │ └──────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘ ``` --- ## 七、PC Web 端技术预案 ### 7.1 时机判断 **当前不急于开发 PC Web 的原因:** - 小程序用户群与目标用户(家长)高度重合,微信即触达 - PC Web 需要额外解决支付、登录、SEO 等问题 - 先把小程序做好,积累内容和用户 **何时启动 PC Web:** - 小程序 DAU 稳定 > 1000 - 用户反馈中"想在电脑上用"的需求频繁出现 - 内容积累到 100+ 种题型 ### 7.2 技术选型预案 ``` PC Web 技术栈(未来): ┌────────────────────────────────────┐ │ 框架:Next.js (App Router) │ │ UI:Tailwind CSS + Radix UI │ │ Canvas:HTML5 Canvas 2D │ │ PDF:jsPDF + html2canvas │ │ 状态:Zustand │ │ 部署:Vercel │ │ │ │ 核心复用: │ │ @doodle/core (共享核心层) │ │ └── 绘制服务、生成器、数据模型 │ │ → 直接 import 使用 │ │ │ │ 需新建: │ │ • Web UI 组件 │ │ • 平台适配层 (Web 实现) │ │ • 用户登录(微信扫码/手机号) │ │ • SEO 优化(SSR/SSG 题型页) │ └────────────────────────────────────┘ ``` ### 7.3 代码共享策略 ``` 仓库结构(Monorepo,未来演进): doodle/ ├── packages/ │ └── core/ ← 共享核心(npm 包) │ ├── src/ │ │ ├── draw/ │ │ ├── generators/ │ │ ├── models/ │ │ └── utils/ │ ├── package.json │ └── tsconfig.json ├── apps/ │ ├── mini/ ← 微信小程序 │ │ ├── miniprogram/ │ │ ├── cloudfunctions/ │ │ └── project.config.json │ └── web/ ← PC Web(未来) │ ├── src/ │ ├── package.json │ └── next.config.js ├── pnpm-workspace.yaml └── package.json 当前阶段不需要 Monorepo,只需在小程序项目中: 1. 将 core/ 作为目录组织代码 2. 确保 core/ 内不 import 任何 wx.* API 3. 未来迁移时,将 core/ 提取为独立 npm 包即可 ``` --- ## 八、前端重构路线图 ### Phase 1:基础重构(第 1-2 周) ``` 优先级:🔴 关键 目标:不改变现有功能,优化代码结构 1. 修复技术债务 ├── 统一云函数名称(callCloud 中 robotaxi → doodle) ├── 清理 cloudfunctions/doodle/index.js 中无效的模块引用 ├── 移除 debug 页中指向不存在页面的死链 └── 清理未使用的代码和资源 2. 建立 core/ 目录 ├── 将 constants/ 迁移到 core/data/ ├── 将纯工具函数迁移到 core/utils/ ├── 将 DrawService 基类提取到 core/draw/ └── 定义统一的 WorksheetType 数据模型 3. 建立 platform/ 目录 ├── 抽取 canvas-adapter(封装 wx Canvas API) ├── 抽取 storage-adapter(封装 wx.Storage) └── 抽取 image-adapter(封装图片加载) 4. 统一 WXML 模板 └── 合并 mathPages 和 focusPages 的重复 canvas-page-template ``` ### Phase 2:首页重构 + 内容配置化(第 3-4 周) ``` 优先级:🟡 重要 1. 数据模型升级 ├── WorksheetType 增加 category/ageRange/difficulty/tags 等字段 ├── 定义 Category 模型 └── 重构 MATH_FUNCTION_TYPES / FOCUS_FUNCTION_TYPES 为统一格式 2. 首页重构 ├── 新建 pages/home/ 替代原四个 Tab 入口页 ├── 实现分类标签栏 + 搜索 + 推荐区 ├── 实现年龄筛选、难度筛选 └── 使用新的 worksheet-card 组件(带预览图) 3. TabBar 重构 └── 发现 | 分龄 | 收藏 | 我的 4. 云数据库初始化 ├── 建表:worksheets、categories ├── 编写数据初始化脚本 └── 实现前端数据加载(缓存优先 + 云端更新) ``` ### Phase 3:新内容接入(第 5-6 周) ``` 优先级:🟡 重要 1. 开发通用素材渲染器 └── 通用的「加载图片 + A4 排版 + 导出」流程 2. 新增题型开发 ├── 数学:时钟练习、形状分类 ├── 语文:拼音练习、笔画练习 ├── 英语:字母描红(复用 numberFind 逻辑) ├── 益智:控笔练习 └── 创意:涂色卡(素材型) 3. 完善新分包 ├── english/ 分包 ├── puzzle/ 分包 └── craft/ 分包 ``` ### Phase 4:体验与功能升级(第 7-8 周) ``` 优先级:🟢 增强 1. 用户体系 ├── 微信登录 ├── 收藏功能(本地 + 云端同步) └── 下载历史 2. 打印体验 ├── 打印指南页面 ├── 批量生成(多题一页/多页) └── PDF 导出(云函数) 3. 运营能力 ├── 数据埋点完善 ├── 下载统计展示(热门排行) └── 用户反馈入口 ``` --- ## 九、成本预估 ### 9.1 微信云开发费用 ``` 免费额度(基础版 1): ┌────────────────────────────────────────┐ │ 云数据库:2 GB 存储 / 50 万次读写/天 │ │ 云存储:5 GB / 2 GB 下载/天 │ │ 云函数:10 万次调用/月 / 1000 GBs/月 │ │ CDN:5 GB/月 │ └────────────────────────────────────────┘ 预估用量(DAU 1000): ┌────────────────────────────────────────┐ │ 云数据库:~100 MB(足够) │ │ 云存储:~2 GB(素材渐增) │ │ 云函数:~3 万次/月 │ │ CDN:~3 GB/月 │ │ │ │ 结论:免费额度可覆盖到 DAU 3000 左右 │ │ 超出后升级到 19.9 元/月即可 │ └────────────────────────────────────────┘ ``` ### 9.2 开发资源 ``` 开发投入估算: ┌──────────────────────────────┐ │ Phase 1: ~20 人时 │ │ Phase 2: ~40 人时 │ │ Phase 3: ~40 人时 │ │ Phase 4: ~30 人时 │ │ ────────────────────── │ │ 总计: ~130 人时 │ │ (1 人兼职 ~8 周可完成) │ └──────────────────────────────┘ ``` --- ## 十、技术风险与应对 | 风险 | 影响 | 应对方案 | |------|------|---------| | 云开发免费额度不够 | 功能受限 | ① 优化查询减少调用 ② 本地缓存减少读取 ③ 升级付费版(19.9元/月起) | | Canvas 兼容性 | 低端机渲染异常 | ① 使用 Canvas 2D(已采用)② 控制单次绘制复杂度 ③ 降级方案 | | 小程序包体积 | 超 2MB 限制 | ① 素材用 CDN / 云存储 ② 合理分包 ③ 图片压缩 | | 素材制作瓶颈 | 涂色卡等需设计资源 | ① 使用开源 SVG 素材 ② AI 生成线稿 ③ 社区投稿 | | 跨平台迁移成本 | Web 端重复开发 | ① core/ 层保持平台无关 ② 适配器模式隔离差异 | | 云开发 HTTP API 限制 | PC Web 调用不便 | ① 使用云开发 HTTP API 触发器 ② 必要时引入轻量后端 | --- ## 附录 A:现有代码与目标架构映射 | 现有文件 | 目标位置 | 迁移动作 | |---------|---------|---------| | `constants/mathFunctions.ts` | `core/data/worksheets.ts` | 合并,增加字段 | | `constants/focusFunctions.ts` | `core/data/worksheets.ts` | 合并,增加字段 | | `constants/words.ts` | `core/data/words.ts` | 移动 | | `constants/colors.ts` | `core/data/colors.ts` | 移动 | | `constants/shapes.ts` | `core/data/shapes.ts` | 移动 | | `service/baseDraw.ts` | `core/draw/base-draw.ts` | 重构,去除 wx.* 依赖 | | `service/wordDrawService.ts` | `core/draw/chinese-draw/word-draw.ts` | 移动,去除 wx.* | | `service/drawServiceFactory.ts` | `core/draw/draw-factory.ts` | 扩展 | | `mathPages/shared/service/*.ts` | `core/draw/math-draw/*.ts` | 移动,去除 wx.* | | `focusPages/shared/service/*.ts` | `core/draw/focus-draw/*.ts` | 移动,去除 wx.* | | `base/pageMixin.ts` | `services/print-service.ts` + 页面代码 | 拆分职责 | | `base/callCloud.ts` | `platform/cloud-adapter.ts` | 重构 | | `utils/saveImage.ts` | `platform/canvas-adapter.ts` | 合并到适配器 | | `utils/downloadPrint.ts` | `services/print-service.ts` | 整合 | | `utils/tracker.ts` | `services/stats-service.ts` | 整合 | | `config/config.ts` | `core/models/print-config.ts` | 类型化 | ## 附录 B:技术选型决策记录 | 决策点 | 选项 | 决策 | 理由 | |--------|------|------|------| | 跨端框架 | Taro / uni-app / 原生 | **原生** | 项目已用原生开发,迁移成本高;Taro 等框架对 Canvas 2D 支持有限;core/ 层抽取足以实现代码复用 | | 状态管理 | MobX / 自定义 / 原生 setData | **轻量自定义** | 小程序场景简单,不需要 Redux 级方案;页面级 setData + 全局 Store 够用 | | UI 组件库 | Vant / 自研 | **Vant + 自定义组件** | 已用 Vant,保持;业务组件自研 | | 云开发 vs 自建 | 云开发 / 云服务器 | **云开发** | 零运维、免费额度充足、原生集成鉴权 | | PDF 方案 | pdfkit(云) / jsPDF(端) | **两者结合** | 小程序用云端 pdfkit,Web 用 jsPDF | | Monorepo | pnpm workspace / Turborepo | **暂不采用** | 当前只有小程序,过早引入增加复杂度;core/ 作为目录组织,未来再拆包 |