Files
doodle-mini/docs/技术架构设计文档.md
T

50 KiB
Raw Blame History

Doodle Mini - 技术架构设计文档

版本:v2.0 最后更新:2026-03-24 配套文档:现有功能清单 | 产品设计文档


一、技术决策总览

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 直接调用
  └── 方案 BCloudflare 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.createImagecanvas.createImage 等 API
模板重复 mathPagesfocusPagescanvas-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 适配器接口

/**
 * 平台无关的 Canvas 上下文接口
 * 微信小程序和 Web 的 Canvas 2D API 基本一致,
 * 但图片加载、字体加载等需要适配
 */
interface ICanvasAdapter {
  getContext(): CanvasRenderingContext2D;
  setSize(width: number, height: number): void;
  loadImage(src: string): Promise<CanvasImageSource>;
  toDataURL(type?: string, quality?: number): Promise<string>;
  toBlob(): Promise<Blob>;               // Web 专用
  toTempFilePath(): Promise<string>;      // 小程序专用
}

4.2 绘制服务基类(平台无关)

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<void>;
}

4.3 题目生成器模式

/**
 * 所有题目生成器实现此接口
 * 输入配置参数,输出与平台无关的题目数据
 * 绘制服务消费这些数据来渲染 Canvas
 */
interface IWorksheetGenerator<TConfig, TData> {
  generate(config: TConfig): TData;
  getDefaultConfig(): TConfig;
}

// 示例:加法题生成器
class AdditionGenerator implements IWorksheetGenerator<AdditionConfig, AdditionData> {
  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 图片加载适配

// 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)        │
│  UITailwind CSS + Radix UI       │
│  CanvasHTML5 Canvas 2D           │
│  PDFjsPDF + 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/月   │
│  CDN5 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(端) 两者结合 小程序用云端 pdfkitWeb 用 jsPDF
Monorepo pnpm workspace / Turborepo 暂不采用 当前只有小程序,过早引入增加复杂度;core/ 作为目录组织,未来再拆包