609 lines
24 KiB
Markdown
609 lines
24 KiB
Markdown
# Doodle Mini — Debug 模式内容发布方案
|
||
|
||
> 版本:v1.0
|
||
> 最后更新:2026-04-23
|
||
> 配套文档:[技术架构设计文档](./技术架构设计文档.md) | [小程序云开发方案](./小程序云开发方案.md) | [产品设计文档](./产品设计文档.md)
|
||
|
||
---
|
||
|
||
## 一、方案定位与动机
|
||
|
||
### 1.1 当前痛点
|
||
|
||
每新增一种可打印资料,从开发完成到上线需要经历以下**手动步骤**:
|
||
|
||
```
|
||
开发页面 + Draw 逻辑
|
||
→ ① 在预览中截图或导出入口图原图
|
||
→ ② 放入 node-tools/entranceInput/<分类>/
|
||
→ ③ 运行 processEntrancePicture.js 裁剪缩放
|
||
→ ④ 从 entranceOuput/ 拷贝到 miniprogram/assets/entrancePicture/
|
||
→ ⑤ 手动编辑 category.data.ts 添加元数据(title、desc、path、previewImg 等)
|
||
→ ⑥ 提交代码 → 审核 → 发版
|
||
```
|
||
|
||
其中 ①~⑤ 是重复性机械劳动,且容易出错(路径拼错、忘记更新数据文件、图片尺寸不一致等)。
|
||
|
||
### 1.2 核心洞察
|
||
|
||
**当一个页面开发调试完成时,所有需要的信息已经就绪**:
|
||
|
||
- **页面路径** — 就是当前正在调试的页面 URL
|
||
- **标题 / 副标题** — 已写在绘制逻辑或配置中
|
||
- **入口图片** — 预览 Canvas 上正在渲染的就是
|
||
- **分类 / 难度 / 年龄段 / 标签** — 已在页面配置中定义
|
||
|
||
在这个时刻一键上传,是**成本最低、数据最准确**的做法。
|
||
|
||
### 1.3 方案目标
|
||
|
||
在 **develop / devtools 环境**的预览页中增加「发布到云端」按钮,点击后自动:
|
||
|
||
1. 从 Canvas 导出入口图 → 裁剪 → 压缩
|
||
2. 上传图片到云存储
|
||
3. 组装页面元数据 → 写入云数据库 `worksheets` 集合
|
||
4. 线上小程序通过缓存策略拉取云端数据,新内容**无需发版即可上线**
|
||
|
||
**不需要开发独立后台系统,不需要手动录入数据。**
|
||
|
||
---
|
||
|
||
## 二、整体流程
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ Debug 模式发布流程 │
|
||
│ │
|
||
│ ① 开发者在 develop / devtools 环境下完成页面开发 │
|
||
│ (绘制逻辑调试完毕,预览效果满意) │
|
||
│ │
|
||
│ ② 预览区底部出现 [📤 发布到云端] 按钮 │
|
||
│ (仅 develop / devtools 环境可见,线上版本不显示) │
|
||
│ │
|
||
│ ③ 点击后弹出确认面板,预览即将上传的数据: │
|
||
│ ┌─────────────────────────────────────────┐ │
|
||
│ │ ID: letter-tracing-daily-checkin │ │
|
||
│ │ 标题: 每日打卡 │ │
|
||
│ │ 副标题: 四宫格每日字母打卡练习 │ │
|
||
│ │ 分类: english │ │
|
||
│ │ 路径: /englishPages/letterTracing/... │ │
|
||
│ │ 难度: 1 │ │
|
||
│ │ 年龄: 4-7岁 │ │
|
||
│ │ 标签: [字母, 描红, 打卡] │ │
|
||
│ │ [预览图缩略图] │ │
|
||
│ │ 裁剪图标:头部和底部、仅头部、不裁剪 │ │
|
||
│ │ │ │
|
||
│ │ ─ 可编辑字段(允许微调) ─ │ │
|
||
│ │ │ │
|
||
│ │ 状态: ○ draft(默认) ○ active │ │
|
||
│ │ │ │
|
||
│ │ [取消] [确认发布] │ │
|
||
│ └─────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ④ 确认发布后,自动执行: │
|
||
│ a. Canvas 导出 → 裁剪页眉页脚 → 压缩至 ≤200KB │
|
||
│ b. 上传图片到云存储 │
|
||
│ cloud://xxx/assets/previews/<category>/<id>.jpg │
|
||
│ c. 组装 WorksheetConfig → upsert 到云数据库 worksheets 集合 │
|
||
│ d. 显示「发布成功 ✅」 │
|
||
│ │
|
||
│ ⑤ 线上小程序按缓存策略拉取新数据,内容自动出现 │
|
||
│ (或在 debug 管理页手动将 status 从 draft 切为 active) │
|
||
└──────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 三、技术设计
|
||
|
||
### 3.1 环境门控
|
||
|
||
发布功能**仅在开发环境**可见,**绝不暴露给普通用户**。
|
||
|
||
```typescript
|
||
/**
|
||
* 判断当前是否为开发环境,可执行 debug 发布操作。
|
||
* 复用 downloadPrint.ts 中 isDevBypassLimits 的判断模式。
|
||
*/
|
||
function isDebugPublishEnabled(): boolean {
|
||
const accountInfo = wx.getAccountInfoSync();
|
||
const { envVersion } = accountInfo.miniProgram;
|
||
// develop: 开发版;trial: 体验版;release: 正式版
|
||
return envVersion === 'develop';
|
||
}
|
||
```
|
||
|
||
WXML 中条件渲染:
|
||
|
||
```xml
|
||
<!-- 仅开发环境显示发布按钮 -->
|
||
<view wx:if="{{isDevEnv}}" class="debug-publish-bar">
|
||
<button bind:tap="onDebugPublish">📤 发布到云端</button>
|
||
</view>
|
||
```
|
||
|
||
### 3.2 页面元数据约定
|
||
|
||
每个 draw 页面需提供 `getPublishMeta()` 方法,返回标准化的发布元数据。
|
||
|
||
```typescript
|
||
/**
|
||
* 发布元数据接口
|
||
* 各 draw 页面实现此接口,提供自身的配置信息
|
||
*/
|
||
interface PublishMeta {
|
||
// ─── 必填字段 ───
|
||
id: string; // 唯一标识,如 'letter-tracing-daily-checkin'
|
||
title: string; // 显示标题
|
||
desc: string; // 显示描述
|
||
category: 'math' | 'chinese' | 'english' | 'puzzle' | 'craft';
|
||
subcategory: string; // 子分类
|
||
path: string; // 页面完整路径(含参数)
|
||
ageMin: number; // 适龄最小值
|
||
ageMax: number; // 适龄最大值
|
||
difficulty: 1 | 2 | 3 | 4; // 1-4:入门、基础、进阶、挑战
|
||
|
||
// ─── 选填字段(有默认值)───
|
||
previewImg?: string; // 上传入口图后回填的云存储 fileID
|
||
tags?: string[]; // 搜索标签
|
||
sortOrder?: number; // 排序权重
|
||
}
|
||
```
|
||
|
||
在 `pageMixin` 中约定调用方式:
|
||
|
||
```typescript
|
||
// pageMixin 中新增
|
||
debugPublishMixin: {
|
||
getPublishMeta(): PublishMeta {
|
||
// 子类覆写此方法
|
||
throw new Error('页面未实现 getPublishMeta()');
|
||
}
|
||
}
|
||
```
|
||
|
||
各页面实现示例(letterTracing):
|
||
|
||
```typescript
|
||
getPublishMeta(): PublishMeta {
|
||
const drawService = this.drawService;
|
||
return {
|
||
id: this.data.currentId,
|
||
title: drawService.title,
|
||
desc: drawService.desc,
|
||
category: 'english',
|
||
subcategory: 'letter-tracing',
|
||
path: `/englishPages/letterTracing/letterTracing?id=${this.data.currentId}`,
|
||
ageMin: 4,
|
||
ageMax: 7,
|
||
difficulty: 1,
|
||
tags: ['字母', '描红', '英语'],
|
||
};
|
||
}
|
||
```
|
||
|
||
### 3.3 入口图导出与处理
|
||
|
||
复用已有的 `preview-card` 组件的 `exportToTempFile` 能力,再做裁剪处理。
|
||
|
||
```typescript
|
||
/**
|
||
* 从预览 Canvas 导出入口图
|
||
*
|
||
* 处理流程:
|
||
* 1. 从 preview-card 导出完整 A4 临时文件
|
||
* 2. 用离屏 Canvas 裁剪掉页眉/页脚区域(与 processEntrancePicture.js 逻辑对齐)
|
||
* 3. 缩放至入口图标准宽度(600px)
|
||
* 4. 导出为 JPEG(quality: 0.85)
|
||
*/
|
||
async function exportEntranceImage(
|
||
canvas: WechatMiniprogram.Canvas,
|
||
ctx: CanvasRenderingContext2D,
|
||
paperConfig: {
|
||
headerHeight: number;
|
||
footerHeight: number;
|
||
width: number;
|
||
height: number;
|
||
},
|
||
): Promise<string> {
|
||
// 裁剪参数 — 与 node-tools/processEntrancePicture.js 保持一致
|
||
const cropTop = paperConfig.headerHeight;
|
||
const cropBottom = paperConfig.footerHeight;
|
||
const sourceW = paperConfig.width;
|
||
const sourceH = paperConfig.height - cropTop - cropBottom;
|
||
|
||
const ENTRANCE_WIDTH = 600;
|
||
const scale = ENTRANCE_WIDTH / sourceW;
|
||
const targetH = Math.round(sourceH * scale);
|
||
|
||
// 调整 Canvas 尺寸用于裁剪输出
|
||
canvas.width = ENTRANCE_WIDTH;
|
||
canvas.height = targetH;
|
||
ctx.drawImage(
|
||
canvas, // 自身作为源(需先 toDataURL 再 loadImage,实际实现需用临时文件中转)
|
||
0,
|
||
cropTop,
|
||
sourceW,
|
||
sourceH, // 源区域:去掉页眉页脚
|
||
0,
|
||
0,
|
||
ENTRANCE_WIDTH,
|
||
targetH, // 目标区域:缩放到标准宽度
|
||
);
|
||
|
||
const tempPath = await canvasToTempFilePath(canvas, {
|
||
fileType: 'jpg',
|
||
quality: 0.85,
|
||
destWidth: ENTRANCE_WIDTH,
|
||
destHeight: targetH,
|
||
});
|
||
|
||
return tempPath;
|
||
}
|
||
```
|
||
|
||
> **实际实现说明**:小程序 Canvas 不能直接自引用 `drawImage`,需要先导出为临时文件(`canvasToTempFilePath`),再用 `canvas.createImage()` 加载临时文件,然后在清空的 Canvas 上绘制裁剪区域。具体实现参考 `preview-card` 已有的 `exportToTempFile` 方法。
|
||
|
||
### 3.4 云端上传
|
||
|
||
#### 3.4.1 图片上传到云存储
|
||
|
||
```typescript
|
||
async function uploadEntranceImage(
|
||
tempFilePath: string,
|
||
category: string,
|
||
id: string,
|
||
): Promise<string> {
|
||
const cloudPath = `assets/previews/${category}/${id}.jpg`;
|
||
const res = await wx.cloud.uploadFile({
|
||
cloudPath,
|
||
filePath: tempFilePath,
|
||
});
|
||
return res.fileID; // cloud://doodle-xxx/assets/previews/english/letter-tracing-daily-checkin.jpg
|
||
}
|
||
```
|
||
|
||
#### 3.4.2 元数据写入云数据库
|
||
|
||
```typescript
|
||
async function publishWorksheet(
|
||
meta: PublishMeta,
|
||
imageFileID: string,
|
||
): Promise<void> {
|
||
const db = wx.cloud.database();
|
||
const collection = db.collection('worksheets');
|
||
|
||
const doc = {
|
||
...meta,
|
||
previewImage: imageFileID,
|
||
status: meta.status || 'draft',
|
||
publishedAt: db.serverDate(),
|
||
updatedAt: db.serverDate(),
|
||
version: 1,
|
||
publishedBy: 'debug', // 标记为 debug 发布
|
||
};
|
||
|
||
// upsert:如果已存在则更新,不存在则创建
|
||
const existing = await collection.where({ id: meta.id }).get();
|
||
if (existing.data.length > 0) {
|
||
const oldDoc = existing.data[0];
|
||
await collection.doc(oldDoc._id).update({
|
||
data: {
|
||
...doc,
|
||
version: (oldDoc.version || 0) + 1,
|
||
updatedAt: db.serverDate(),
|
||
},
|
||
});
|
||
} else {
|
||
await collection.add({ data: doc });
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.5 发布流程整合
|
||
|
||
```typescript
|
||
/**
|
||
* Debug 发布入口 — 挂载在 pageMixin 上
|
||
* 由预览页的「发布到云端」按钮触发
|
||
*/
|
||
async function onDebugPublish(this: any): Promise<void> {
|
||
if (!isDebugPublishEnabled()) return;
|
||
|
||
// 1. 获取页面元数据
|
||
const meta: PublishMeta = this.getPublishMeta();
|
||
|
||
// 2. 弹出确认面板(展示即将发布的数据,允许微调)
|
||
const confirmed = await showPublishConfirmDialog(meta);
|
||
if (!confirmed) return;
|
||
|
||
wx.showLoading({ title: '发布中...' });
|
||
try {
|
||
// 3. 导出入口图
|
||
const tempPath = await exportEntranceImage(
|
||
this.canvas,
|
||
this.ctx,
|
||
this.paperConfig,
|
||
);
|
||
|
||
// 4. 上传图片到云存储
|
||
const fileID = await uploadEntranceImage(
|
||
tempPath,
|
||
meta.category,
|
||
meta.id,
|
||
);
|
||
|
||
// 5. 写入云数据库
|
||
await publishWorksheet(meta, fileID);
|
||
|
||
wx.hideLoading();
|
||
wx.showToast({ title: '发布成功 ✅', icon: 'success' });
|
||
} catch (err) {
|
||
wx.hideLoading();
|
||
wx.showModal({
|
||
title: '发布失败',
|
||
content: JSON.stringify(err),
|
||
showCancel: false,
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、云端数据与现有架构的衔接
|
||
|
||
### 4.1 数据加载优先级(保持不变)
|
||
|
||
与 [技术架构设计文档](./技术架构设计文档.md) 第 6.3 节、[小程序云开发方案](./小程序云开发方案.md) 第五节一致:
|
||
|
||
```
|
||
优先级 1: 本地缓存(wx.Storage)
|
||
优先级 2: 云端拉取(worksheets 集合) ← debug 发布的数据在此
|
||
优先级 3: 前端内置兜底(category.data.ts)← 保持稳定兜底
|
||
```
|
||
|
||
### 4.2 category.data.ts 的角色变化
|
||
|
||
| 阶段 | category.data.ts 的作用 |
|
||
| -------------- | ------------------------------------------------- |
|
||
| **当前** | 唯一数据源(硬编码所有题型信息) |
|
||
| **方案实施后** | 兜底数据源 + 离线保障(云端不可用时生效) |
|
||
| **长期** | 通过 `syncFromCloud` 脚本自动同步,保持与云端一致 |
|
||
|
||
### 4.3 worksheets 集合字段映射
|
||
|
||
debug 发布写入的字段,与 [小程序云开发方案](./小程序云开发方案.md) §3.3 `worksheets` 集合的字段**完全对齐**:
|
||
|
||
| PublishMeta 字段 | worksheets 集合字段 | 说明 |
|
||
| ---------------- | ------------------- | ------------------------ |
|
||
| `id` | `_id` | 业务唯一标识 |
|
||
| `title` | `title` | 显示标题 |
|
||
| `desc` | `desc` | 副标题 |
|
||
| `category` | `category` | 所属大类 |
|
||
| `subcategory` | `subcategory` | 子分类,当前默认为空 |
|
||
| `path` | `path` | 小程序页面路径 |
|
||
| `previewImg` | `previewImg` | 云存储 fileID |
|
||
| `ageMin` | `ageMin` | 适龄最小值 |
|
||
| `ageMax` | `ageMax` | 适龄最大值 |
|
||
| `difficulty` | `difficulty` | 难度 1–4(入门、基础、) |
|
||
| `tags` | `tags` | 搜索标签 |
|
||
| `sortOrder` | `sortOrder` | 排列顺序 |
|
||
| — | `createdAt` | 创建时间 |
|
||
| — | `updatedAt` | 更新时间 |
|
||
|
||
---
|
||
|
||
## 五、云存储目录规划
|
||
|
||
与 [小程序云开发方案](./小程序云开发方案.md) §6 一致:
|
||
|
||
```
|
||
cloud://doodle-xxx/
|
||
├── assets/
|
||
│ └── previews/ ← debug 发布的入口图存放于此
|
||
│ ├── math/
|
||
│ │ ├── number-find.jpg
|
||
│ │ ├── addition-10.jpg
|
||
│ │ └── ...
|
||
│ ├── english/
|
||
│ │ ├── letter-tracing-single.jpg
|
||
│ │ ├── letter-tracing-daily-checkin.jpg
|
||
│ │ └── ...
|
||
│ ├── puzzle/
|
||
│ ├── chinese/
|
||
│ └── craft/
|
||
```
|
||
|
||
命名规则:`<id>.jpg`,与 `PublishMeta.id` 一致,便于查找和管理。
|
||
|
||
---
|
||
|
||
## 六、安全与权限控制
|
||
|
||
### 6.1 客户端门控
|
||
|
||
```typescript
|
||
// 三重保障
|
||
const canPublish =
|
||
isDebugPublishEnabled() && // ① envVersion === 'develop'
|
||
isDevBypassLimits() && // ② 与下载绕过逻辑一致
|
||
wx.getStorageSync('enableDebug'); // ③ debug 页面手动开启
|
||
```
|
||
|
||
### 6.2 云端安全规则
|
||
|
||
在云数据库安全规则中,`worksheets` 集合限制写入权限:
|
||
|
||
```json
|
||
{
|
||
"worksheets": {
|
||
".write": false,
|
||
".read": true
|
||
}
|
||
}
|
||
```
|
||
|
||
debug 发布通过**云函数**中转,云函数内校验 `openId` 白名单:
|
||
|
||
```javascript
|
||
// 云函数 debugPublish
|
||
exports.main = async (event, context) => {
|
||
const { OPENID } = cloud.getWXContext();
|
||
const ADMIN_OPENIDS = ['开发者的openid'];
|
||
|
||
if (!ADMIN_OPENIDS.includes(OPENID)) {
|
||
throw new Error('无权限执行此操作');
|
||
}
|
||
|
||
// 执行 upsert 操作...
|
||
};
|
||
```
|
||
|
||
### 6.3 数据保护
|
||
|
||
| 措施 | 说明 |
|
||
| ---------------- | --------------------------------------------------- |
|
||
| 默认 draft 状态 | 发布后默认 `status: 'draft'`,需手动激活为 `active` |
|
||
| 版本递增 | 每次更新 `version + 1`,可追踪变更历史 |
|
||
| publishedBy 标记 | `publishedBy: 'debug'` 区分来源 |
|
||
| 时间戳 | `publishedAt` / `updatedAt` 记录操作时间 |
|
||
|
||
---
|
||
|
||
## 七、辅助工具
|
||
|
||
### 7.1 syncFromCloud 脚本
|
||
|
||
在 `node-tools/` 中新增脚本,发版前从云数据库拉取最新数据,同步回 `category.data.ts` 作为兜底数据。
|
||
|
||
```
|
||
Debug 发布 → 云端数据 → 线上可见
|
||
↓
|
||
syncFromCloud.js ← 发版前运行
|
||
↓
|
||
category.data.ts 更新 ← 兜底数据同步
|
||
↓
|
||
下次发版包含
|
||
```
|
||
|
||
```javascript
|
||
// node-tools/src/syncFromCloud.js(伪代码)
|
||
// 通过云开发 HTTP API 或管理端 SDK 拉取 worksheets 集合
|
||
// 按 category 分组 → 生成 TypeScript 代码 → 写入 category.data.ts
|
||
```
|
||
|
||
### 7.2 Debug 管理页扩展
|
||
|
||
在已有的 `supportPages/debug/debug` 页面中增加 tab,提供简易内容管理:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Debug 工具页 │
|
||
│ │
|
||
│ [调试配置] [已发布内容] [系统信息] │
|
||
│ │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ 已发布内容列表 │ │
|
||
│ │ │ │
|
||
│ │ ● letter-tracing-single │ │
|
||
│ │ 状态: active 版本: 3 英语 │ │
|
||
│ │ [编辑] [下架] │ │
|
||
│ │ │ │
|
||
│ │ ● letter-tracing-daily-checkin │ │
|
||
│ │ 状态: draft 版本: 1 英语 │ │
|
||
│ │ [激活] [编辑] [删除] │ │
|
||
│ │ │ │
|
||
│ │ ● addition-10 │ │
|
||
│ │ 状态: active 版本: 5 数学 │ │
|
||
│ │ [编辑] [下架] │ │
|
||
│ └─────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 八、实施计划
|
||
|
||
### Phase 1:基础发布能力(约 0.5~1 天)
|
||
|
||
- [ ] `pageMixin` 中新增 `getPublishMeta()` 接口约定
|
||
- [ ] 实现 `isDebugPublishEnabled()` 环境判断
|
||
- [ ] 实现入口图导出 + 裁剪逻辑
|
||
- [ ] 实现云存储上传 + 云数据库写入
|
||
- [ ] 在 `preview-card` 或 `preview-footer-actions` 中添加发布按钮(条件渲染)
|
||
|
||
### Phase 2:确认面板与安全(约 0.5 天)
|
||
|
||
- [ ] 发布前确认弹窗(数据预览 + 可编辑字段)
|
||
- [ ] 云函数安全校验(openId 白名单)
|
||
- [ ] 默认 draft 状态 + 版本管理
|
||
|
||
### Phase 3:数据消费侧适配(约 0.5~1 天)
|
||
|
||
- [ ] `category.ts`(或 `worksheet-service`)增加从云 DB 拉取逻辑
|
||
- [ ] 实现缓存策略(本地缓存 → 云端 → 内置兜底)
|
||
- [ ] 入口图从云存储 fileID 获取临时 URL 展示
|
||
|
||
### Phase 4:辅助工具(约 0.5 天)
|
||
|
||
- [ ] `syncFromCloud.js` 脚本
|
||
- [ ] Debug 管理页扩展(列表 + 状态管理)
|
||
|
||
**总计预估:2~3 天**
|
||
|
||
---
|
||
|
||
## 九、与模板引擎的衔接(远期)
|
||
|
||
当 [技术架构设计文档](./技术架构设计文档.md) 第五章所述模板引擎落地后,debug 发布方案可进一步升级:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 模板引擎 + Debug 发布(远期形态) │
|
||
│ │
|
||
│ ① 在通用 worksheet 页面中: │
|
||
│ 选择模板 + 配置生成器参数 → 实时预览 │
|
||
│ │
|
||
│ ② 效果满意后点击「发布」: │
|
||
│ 自动上传: │
|
||
│ • JSON 配置(template + generator + generatorConfig + layout)│
|
||
│ • 入口预览图 │
|
||
│ • 元数据(title、tags、ageRange 等) │
|
||
│ │
|
||
│ ③ 新题型无需任何前端代码改动即可上线 │
|
||
│ (前提:使用已有的 TemplateRenderer + Generator 组合) │
|
||
│ │
|
||
│ ④ 配合 80% 新题型可动态上线的目标 │
|
||
│ debug 发布成为主要的内容上线方式 │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 十、风险与应对
|
||
|
||
| 风险 | 影响 | 应对 |
|
||
| ---------------------- | ----------------------- | --------------------------------------------------------- |
|
||
| **误操作发布测试数据** | 污染线上列表 | 默认 `status: 'draft'`;确认面板二次确认 |
|
||
| **入口图质量不一致** | 不同设备/DPR 下渲染差异 | 统一使用开发者工具发布;导出时固定 `destWidth/destHeight` |
|
||
| **云端数据丢失** | 已发布内容消失 | `category.data.ts` 兜底;`syncFromCloud` 定期同步 |
|
||
| **安全:非授权上传** | 恶意写入数据 | 云函数白名单校验;客户端三重门控 |
|
||
| **双数据源不一致** | 本地兜底与云端数据冲突 | 云端优先,发版前 `syncFromCloud` 对齐 |
|
||
| **云存储额度** | 图片累积占用存储 | 入口图约 50~200KB/张,100 张仅 ~20MB,远低于免费额度 |
|
||
|
||
---
|
||
|
||
## 附录 A:与现有代码的关系
|
||
|
||
| 现有模块 | 本方案的关联 |
|
||
| ------------------------------------------ | ------------------------------------------------------ |
|
||
| `preview-card` 组件 | 复用 `exportToTempFile`,新增裁剪逻辑 |
|
||
| `pageMixin.ts` | 新增 `getPublishMeta()` 约定和 `onDebugPublish()` 方法 |
|
||
| `downloadPrint.ts` / `isDevBypassLimits()` | 复用环境判断模式 |
|
||
| `category.data.ts` | 角色从「唯一数据源」变为「兜底数据源」 |
|
||
| `supportPages/debug/debug` | 扩展内容管理 tab |
|
||
| `node-tools/processEntrancePicture.js` | 裁剪参数对齐;流程被 debug 发布替代 |
|
||
| 云数据库 `worksheets` 集合 | 写入发布数据(字段与云开发方案对齐) |
|
||
| 云存储 `assets/previews/` | 存放入口图 |
|