Files
doodle-mini/docs/小程序云开发方案和数据库设计.md
T
2026-05-06 17:28:51 +08:00

22 KiB
Raw Blame History

Doodle Mini — 小程序云开发方案

版本:v1.1 最后更新:2026-03-27 适用范围:与 技术架构设计文档 配套;当前阶段采用本文档所述微信云开发环境实现后端能力。 阶段截止:与总架构文档一致,有效期至 2026 年 9 月 15 日(到期前需复盘用量与成本,并决定是否迁移至 后端部署方案 所述自建服务)。


一、方案定位

微信云开发提供免运维的 Serverless 后端能力(云数据库、云存储、云函数),与小程序 wx.cloud 原生集成,接入与鉴权成本低,适合在阶段内快速落地题型配置下发、用户数据与素材存储。

与总架构文档的关系

  • 技术架构设计文档:描述前端分层、模板引擎、系统总览及后端分阶段策略(现阶段云开发 + 到期后可选 NestJS)。
  • 本文档:仅展开云开发侧的库表、云函数、存储与调用策略等技术细节。

到期后选项(不在本文档展开):若免费额度不足或需更强多端能力,可迁移至 后端部署方案(NestJS + 自有服务器);迁移时需做数据导出与 cloud-adapterhttp-adapter 的适配,详见技术架构文档中的「可迁移」原则。


二、与自建后端对比(决策参考)

维度 微信云开发 自建云服务器(NestJS 现阶段采用
接入成本 极低(原生集成) 需搭建部署 云开发
运维成本 免运维 需简单维护 云开发
费用 免费额度内 ¥0;超出按套餐计费 已有服务器可零增量 云开发(阶段内)
小程序集成 鉴权与 OpenID 一体化 需自建登录与 JWT 云开发
PC Web 支持 需 HTTP 云函数或桥接 RESTful 天然支持 远期看 NestJS
灵活性 受云开发 SDK 与配额约束 完全自主 远期看 NestJS
数据迁移 可导出,迁移需工作量 标准 MySQL,自主备份 远期看 NestJS

三、云数据库集合设计

本节与 后端部署方案 中的 Prisma SchemaMySQL 字段语义对齐,便于阶段结束后迁移;云数据库为 MongoDB 兼容模型(文档 = BSON),下列类型按 MongoDB 惯例书写。

3.1 设计约定

约定 说明
_id 每条文档主键。可由云开发自动生成,也可在写入时指定为字符串(如业务 ID、cuid)。不要求与微信 openid 相同。
Date 对应 BSON Date,云函数/控制台写入时使用 serverDate()new Date()
openid / unionid 显式业务字段;与下节「用户标识」一致。
_openid 仅当文档由小程序端直连数据库写入且开启用户校验时,云开发可自动注入当前用户 openid;若数据经云函数写入,通常自行维护 openid/userId 字段,不依赖 _openid
命名 集合名与 Prisma @@map 一致:worksheetscategoriesusersfavoritesdownload_logsfeedbacklearning_plans

3.2 用户标识:openidunionid_id

用户的 _id 只能是 openid 吗?——不是。

  • 云数据库文档的 _id 默认为云开发生成的唯一 ID;也可以在 add自定义为字符串(例如与 后端部署方案 一致的 cuid(),或直接等于 openid)。
  • 推荐users 集合使用 独立 openid 字段(唯一) + unionid 字段(可选、唯一)_id 可用自动生成或自定义;与 Prisma 中 User.idcuid+ openid / unionid 的建模方式一致,迁移时映射清晰。
  • unionid:同一微信开放平台下多应用用户唯一标识;需在小程序后台绑定开放平台,且用户已授权;未绑定时可能为空,字段应为可选并建稀疏唯一索引(仅非空值唯一)。

users 集合完整字段与索引:见 §3.5(与 Prisma User 一一对应)。

子表(收藏、下载日志、反馈)中的 userId 建议与 users._id 保持一致(若 _id 采用 cuid,则存 cuid;若 _idopenid,则存 openid),与 Prisma 外键语义一致。

unionid 的写入:在云函数中可通过 cloud.getWXContext() 读取 UNIONID(用户已绑定开放平台且当前会话可返回时才有值);首次登录 upsert users 时写入或更新 unionid 字段。若仅能在服务端用 codesession,则使用微信 code2Session 返回的 unionid 字段(同样可能为空)。不要在小程序端把 unionid 当可信主键直接展示给第三方,存储与鉴权仍以服务端为准。


3.3 集合:worksheets(题型配置)

字段 BSON 类型 必填 说明
_id string 主键;唯一的可读业务 ID。
title string 标题,建议 ≤8 字符。
subtitle string 副标题,建议 ≤12 字符。
category string 大类 对应categories 表中parentId=null 的分类
subcategory string 子类,建议 ≤50 字符。
ageMin int 适龄最小值
ageMax int 适龄最大值
grade int 年级(-4=托班,-3=小班 ... 0=幼小衔接,1=一年级..)
difficulty int 难度 1-4(1=入门,2=基础,3=进阶,4=挑战)
previewImg string 预览图 URL/云存储 fileID
tags array<string> 标签列表。
isNew bool 是否新品,默认 false
isHot bool 是否热门,默认 false
sortOrder int 排序权重,默认 0
downloads int 下载次数,默认 0
likes int 收藏此时,默认 0
status string active | draft,默认 draft
createdAt date 创建时间。
updatedAt date 更新时间。

后续可能需要使用的字段,现在先不用 | template | string | 是 | 渲染模板类型 TemplateType,建议 ≤30 字符。 | | generator | string | 是 | 生成器类型 GeneratorType,建议 ≤30 字符。 | | generatorConfig | object | 是 | 生成器参数(JSON 对象)。 | | layoutConfig | object | 是 | 排版参数(JSON 对象)。 | | userConfigurable | object | null | 否 | 用户可调整参数定义(JSON)。 | | legacyPage | string | null | 否 | 旧页面路径,迁移过渡用,建议 ≤200 字符。 | 索引建议

索引键 类型 说明
{ category: 1, status: 1, sortOrder: 1 } 复合 列表筛选与排序。
{ status: 1, sortOrder: 1 } 复合 全量上架列表。

3.4 集合:categories(分类)

对应 Prisma Category

字段 BSON 类型 必填 说明
_id string 主键。
name string 名称,建议 ≤50 字符。
icon string 图标 URL/fileID,建议 ≤200 字符。
color string 色值,建议 ≤10 字符。
sortOrder int 排序,默认 0
parentId string | null 父分类 ID。

索引建议{ parentId: 1, sortOrder: 1 }


3.5 集合:users(用户)

对应 Prisma User@@map("users"))。openid / unionid_id 的取舍、unionid 写入方式见 §3.2。

字段 BSON 类型 必填 说明
_id string 主键;可与 Prisma User.id 一样使用 cuid,也可自定义为其它字符串,团队内与用户档案查询方式统一即可。
openid string 当前小程序下微信用户标识;与 Prisma 一致建议 ≤100 字符;业务唯一,索引见下表。
unionid string | null 开放平台下跨应用用户标识;建议 ≤100 字符;未绑开放平台或未返回时为空。
nickName string | null 用户昵称;建议 ≤50 字符。
avatarUrl string | null 头像 URL 或云存储 fileID;建议 ≤500 字符。
totalDownloads int 累计下载次数,默认 0
createdAt date 首次创建时间。
lastActiveAt date 最近一次活跃时间(登录、打开小程序、写库等策略由实现约定)。

索引建议

索引键 类型 说明
openid 唯一 登录与 upsert 主查。
unionid 唯一(稀疏) 仅当存在开放平台绑定且需跨端关联时使用;空值不互斥。

3.6 集合:favorites(收藏日志)

对应 Prisma FavoriteLog

字段 BSON 类型 必填 说明
_id string 主键。
userId string 关联 users._id
worksheetId string 关联 worksheets._id
createdAt date 收藏时间。

索引建议

索引键 类型 说明
{ userId: 1, worksheetId: 1 } 唯一 同一用户同一题型仅一条收藏。
{ userId: 1, createdAt: -1 } 复合 「我的收藏」按时间倒序。

若小程序端直连写库且使用安全规则,可额外保留 _openiduserId 二选一做一致性校验;以云函数为主时以 userId + 云函数鉴权为准。


3.7 集合:download_logs(下载日志)

对应 Prisma DownloadLog

字段 BSON 类型 必填 说明
_id string 主键。
userId string 关联 users._id
worksheetId string 关联 worksheets._id
params object | null 生成参数快照(JSON)。 暂时不使用
createdAt date 下载时间。

索引建议{ userId: 1, createdAt: -1 }{ worksheetId: 1, createdAt: -1 }(统计/运营)。


3.8 集合:feedback(用户反馈)

对应 Prisma Feedback

字段 BSON 类型 必填 说明
_id string 主键。
userId string 关联 users._id
content string 反馈正文。
contact string | null 联系方式,建议 ≤100 字符。
createdAt date 提交时间。

索引建议{ userId: 1 }


3.9 集合:learning_plans(学习路线)

对应 Prisma LearningPlan(总架构中的适龄学习路线配置)。

字段 BSON 类型 必填 说明
_id string 主键。
ageMin int 适龄下限。
ageMax int 适龄上限。
ageLabel string 展示用年龄段文案,建议 ≤20 字符。
milestones array<string> 能力目标描述(JSON 数组,与 Prisma Json 一致)。
weeks array 周计划结构 WeekPlan[]JSON 数组,与 Prisma 一致)。

索引建议{ ageMin: 1, ageMax: 1 } 唯一,与 Prisma @@unique([ageMin, ageMax]) 一致。


3.10 ER 关系(逻辑)

users (1) ──< favorites >── (N) worksheets
users (1) ──< download_logs >── (N) worksheets
users (1) ──< feedback
categories ──(可选业务关联)── worksheets.category / subcategoryworksheet 内嵌字符串,非 DB 外键)
learning_plans:独立配置表,按 ageMin/ageMax 查询

四、云函数设计

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 生成(远期增值功能,会员专属)
    └── common/
        ├── responseMiddleware.js   ← ✅ 已有,保留
        └── auth.js                 ← 🆕 鉴权中间件

五、云函数调用与数据加载策略

┌────────────────────────────────────────────────────────────┐
│                    数据加载策略                              │
│                                                            │
│  题型配置数据 (worksheets/categories)                       │
│  ┌────────────────────────────────────────┐                │
│  │  优先级 1: 本地缓存(wx.Storage        │                │
│  │  优先级 2: 云数据库查询                  │                │
│  │  优先级 3: 前端内置兜底数据              │  ← 保证离线可用 │
│  └────────────────────────────────────────┘                │
│                                                            │
│  缓存策略:                                                │
│  • 首次启动:云端拉取 → 写入本地缓存                        │
│  • 后续启动:先用缓存渲染 → 后台静默更新                    │
│  • 缓存有效期:24 小时                                      │
│  • 无网络:使用本地缓存或内置兜底                            │
│                                                            │
│  用户数据 (favorites/history)                               │
│  ┌────────────────────────────────────────┐                │
│  │  本地优先写入 → 后台同步云端              │                │
│  │  冲突策略:以云端为准(云端时间戳更新)    │                │
│  └────────────────────────────────────────┘                │
│                                                            │
│  统计数据 (download count)                                  │
│  ┌────────────────────────────────────────┐                │
│  │  批量上报:本地累计 → 退出时/定时上报     │                │
│  │  非关键路径,允许丢失                    │                │
│  └────────────────────────────────────────┘                │
└────────────────────────────────────────────────────────────┘

六、云存储规划

云存储目录结构:
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

七、费用与额度(阶段内监控)

免费额度(基础版 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 元/月起)      │
└────────────────────────────────────────┘

建议在 2026-09-15 前:结合控制台用量与业务目标,决定续用云开发、升级套餐或启动迁移至 后端部署方案