feat: worksheet发布功能开发
This commit is contained in:
+95
-83
@@ -18,7 +18,7 @@
|
||||
→ ② 放入 node-tools/entranceInput/<分类>/
|
||||
→ ③ 运行 processEntrancePicture.js 裁剪缩放
|
||||
→ ④ 从 entranceOuput/ 拷贝到 miniprogram/assets/entrancePicture/
|
||||
→ ⑤ 手动编辑 category.data.ts 添加元数据(title、subtitle、path、img 等)
|
||||
→ ⑤ 手动编辑 category.data.ts 添加元数据(title、desc、path、previewImg 等)
|
||||
→ ⑥ 提交代码 → 审核 → 发版
|
||||
```
|
||||
|
||||
@@ -67,10 +67,11 @@
|
||||
│ │ 副标题: 四宫格每日字母打卡练习 │ │
|
||||
│ │ 分类: english │ │
|
||||
│ │ 路径: /englishPages/letterTracing/... │ │
|
||||
│ │ 难度: beginner │ │
|
||||
│ │ 难度: 1 │ │
|
||||
│ │ 年龄: 4-7岁 │ │
|
||||
│ │ 标签: [字母, 描红, 打卡] │ │
|
||||
│ │ [预览图缩略图] │ │
|
||||
│ │ 裁剪图标:头部和底部、仅头部、不裁剪 │ │
|
||||
│ │ │ │
|
||||
│ │ ─ 可编辑字段(允许微调) ─ │ │
|
||||
│ │ │ │
|
||||
@@ -132,26 +133,20 @@ WXML 中条件渲染:
|
||||
*/
|
||||
interface PublishMeta {
|
||||
// ─── 必填字段 ───
|
||||
id: string; // 唯一标识,如 'letter-tracing-daily-checkin'
|
||||
title: string; // 显示标题
|
||||
subtitle: string; // 显示副标题
|
||||
id: string; // 唯一标识,如 'letter-tracing-daily-checkin'
|
||||
title: string; // 显示标题
|
||||
desc: string; // 显示描述
|
||||
category: 'math' | 'chinese' | 'english' | 'puzzle' | 'craft';
|
||||
subcategory: string; // 子分类
|
||||
path: string; // 页面完整路径(含参数)
|
||||
subcategory: string; // 子分类
|
||||
path: string; // 页面完整路径(含参数)
|
||||
ageMin: number; // 适龄最小值
|
||||
ageMax: number; // 适龄最大值
|
||||
difficulty: 1 | 2 | 3 | 4; // 1-4:入门、基础、进阶、挑战
|
||||
|
||||
// ─── 选填字段(有默认值)───
|
||||
icon?: string; // Emoji 图标
|
||||
ageRange?: [number, number]; // 适用年龄,默认 [3, 8]
|
||||
difficulty?: 'beginner' | 'basic' | 'intermediate' | 'advanced';
|
||||
tags?: string[]; // 搜索标签
|
||||
sortOrder?: number; // 排序权重
|
||||
status?: 'draft' | 'active' | 'hidden'; // 默认 'draft'
|
||||
|
||||
// ─── 模板引擎相关(可选,未来扩展)───
|
||||
template?: string;
|
||||
generator?: string;
|
||||
generatorConfig?: Record<string, any>;
|
||||
layoutConfig?: Record<string, any>;
|
||||
previewImg?: string; // 上传入口图后回填的云存储 fileID
|
||||
tags?: string[]; // 搜索标签
|
||||
sortOrder?: number; // 排序权重
|
||||
}
|
||||
```
|
||||
|
||||
@@ -175,13 +170,13 @@ getPublishMeta(): PublishMeta {
|
||||
return {
|
||||
id: this.data.currentId,
|
||||
title: drawService.title,
|
||||
subtitle: drawService.subtitle,
|
||||
desc: drawService.desc,
|
||||
category: 'english',
|
||||
subcategory: 'letter-tracing',
|
||||
path: `/englishPages/letterTracing/letterTracing?id=${this.data.currentId}`,
|
||||
icon: '🔠',
|
||||
ageRange: [4, 7],
|
||||
difficulty: 'beginner',
|
||||
ageMin: 4,
|
||||
ageMax: 7,
|
||||
difficulty: 1,
|
||||
tags: ['字母', '描红', '英语'],
|
||||
};
|
||||
}
|
||||
@@ -194,7 +189,7 @@ getPublishMeta(): PublishMeta {
|
||||
```typescript
|
||||
/**
|
||||
* 从预览 Canvas 导出入口图
|
||||
*
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 从 preview-card 导出完整 A4 临时文件
|
||||
* 2. 用离屏 Canvas 裁剪掉页眉/页脚区域(与 processEntrancePicture.js 逻辑对齐)
|
||||
@@ -204,7 +199,12 @@ getPublishMeta(): PublishMeta {
|
||||
async function exportEntranceImage(
|
||||
canvas: WechatMiniprogram.Canvas,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
paperConfig: { headerHeight: number; footerHeight: number; width: number; height: number }
|
||||
paperConfig: {
|
||||
headerHeight: number;
|
||||
footerHeight: number;
|
||||
width: number;
|
||||
height: number;
|
||||
},
|
||||
): Promise<string> {
|
||||
// 裁剪参数 — 与 node-tools/processEntrancePicture.js 保持一致
|
||||
const cropTop = paperConfig.headerHeight;
|
||||
@@ -220,9 +220,15 @@ async function exportEntranceImage(
|
||||
canvas.width = ENTRANCE_WIDTH;
|
||||
canvas.height = targetH;
|
||||
ctx.drawImage(
|
||||
canvas, // 自身作为源(需先 toDataURL 再 loadImage,实际实现需用临时文件中转)
|
||||
0, cropTop, sourceW, sourceH, // 源区域:去掉页眉页脚
|
||||
0, 0, ENTRANCE_WIDTH, targetH // 目标区域:缩放到标准宽度
|
||||
canvas, // 自身作为源(需先 toDataURL 再 loadImage,实际实现需用临时文件中转)
|
||||
0,
|
||||
cropTop,
|
||||
sourceW,
|
||||
sourceH, // 源区域:去掉页眉页脚
|
||||
0,
|
||||
0,
|
||||
ENTRANCE_WIDTH,
|
||||
targetH, // 目标区域:缩放到标准宽度
|
||||
);
|
||||
|
||||
const tempPath = await canvasToTempFilePath(canvas, {
|
||||
@@ -246,21 +252,24 @@ async function exportEntranceImage(
|
||||
async function uploadEntranceImage(
|
||||
tempFilePath: string,
|
||||
category: string,
|
||||
id: 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
|
||||
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> {
|
||||
async function publishWorksheet(
|
||||
meta: PublishMeta,
|
||||
imageFileID: string,
|
||||
): Promise<void> {
|
||||
const db = wx.cloud.database();
|
||||
const collection = db.collection('worksheets');
|
||||
|
||||
@@ -271,7 +280,7 @@ async function publishWorksheet(meta: PublishMeta, imageFileID: string): Promise
|
||||
publishedAt: db.serverDate(),
|
||||
updatedAt: db.serverDate(),
|
||||
version: 1,
|
||||
publishedBy: 'debug', // 标记为 debug 发布
|
||||
publishedBy: 'debug', // 标记为 debug 发布
|
||||
};
|
||||
|
||||
// upsert:如果已存在则更新,不存在则创建
|
||||
@@ -312,12 +321,16 @@ async function onDebugPublish(this: any): Promise<void> {
|
||||
try {
|
||||
// 3. 导出入口图
|
||||
const tempPath = await exportEntranceImage(
|
||||
this.canvas, this.ctx, this.paperConfig
|
||||
this.canvas,
|
||||
this.ctx,
|
||||
this.paperConfig,
|
||||
);
|
||||
|
||||
// 4. 上传图片到云存储
|
||||
const fileID = await uploadEntranceImage(
|
||||
tempPath, meta.category, meta.id
|
||||
tempPath,
|
||||
meta.category,
|
||||
meta.id,
|
||||
);
|
||||
|
||||
// 5. 写入云数据库
|
||||
@@ -352,33 +365,32 @@ async function onDebugPublish(this: any): Promise<void> {
|
||||
|
||||
### 4.2 category.data.ts 的角色变化
|
||||
|
||||
| 阶段 | category.data.ts 的作用 |
|
||||
|------|------------------------|
|
||||
| **当前** | 唯一数据源(硬编码所有题型信息) |
|
||||
| **方案实施后** | 兜底数据源 + 离线保障(云端不可用时生效) |
|
||||
| **长期** | 通过 `syncFromCloud` 脚本自动同步,保持与云端一致 |
|
||||
| 阶段 | category.data.ts 的作用 |
|
||||
| -------------- | ------------------------------------------------- |
|
||||
| **当前** | 唯一数据源(硬编码所有题型信息) |
|
||||
| **方案实施后** | 兜底数据源 + 离线保障(云端不可用时生效) |
|
||||
| **长期** | 通过 `syncFromCloud` 脚本自动同步,保持与云端一致 |
|
||||
|
||||
### 4.3 worksheets 集合字段映射
|
||||
|
||||
debug 发布写入的字段,与 [小程序云开发方案](./小程序云开发方案.md) §3.3 `worksheets` 集合的字段**完全对齐**:
|
||||
|
||||
| PublishMeta 字段 | worksheets 集合字段 | 说明 |
|
||||
|-----------------|-------------------|------|
|
||||
| `id` | `id` | 业务唯一标识 |
|
||||
| `title` | `title` | 显示标题 |
|
||||
| `subtitle` | `desc` | 显示描述 |
|
||||
| `category` | `category` | 所属大类 |
|
||||
| `subcategory` | `subcategory` | 子分类 |
|
||||
| `path` | 新增字段 `pagePath` | 小程序页面路径 |
|
||||
| `icon` | 新增字段 `icon` | Emoji 图标 |
|
||||
| `ageRange` | `ageRange` | 适用年龄段 |
|
||||
| `difficulty` | `difficulty` | 难度级别 |
|
||||
| `tags` | `tags` | 搜索标签 |
|
||||
| `sortOrder` | `sortOrder` | 排列顺序 |
|
||||
| `status` | `status` | 上架状态 |
|
||||
| — | `previewImage` | 云存储 fileID |
|
||||
| — | `publishedAt` | 发布时间 |
|
||||
| — | `version` | 版本号(递增) |
|
||||
| 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` | 更新时间 |
|
||||
|
||||
---
|
||||
|
||||
@@ -414,9 +426,9 @@ cloud://doodle-xxx/
|
||||
```typescript
|
||||
// 三重保障
|
||||
const canPublish =
|
||||
isDebugPublishEnabled() // ① envVersion === 'develop'
|
||||
&& isDevBypassLimits() // ② 与下载绕过逻辑一致
|
||||
&& wx.getStorageSync('enableDebug'); // ③ debug 页面手动开启
|
||||
isDebugPublishEnabled() && // ① envVersion === 'develop'
|
||||
isDevBypassLimits() && // ② 与下载绕过逻辑一致
|
||||
wx.getStorageSync('enableDebug'); // ③ debug 页面手动开启
|
||||
```
|
||||
|
||||
### 6.2 云端安全规则
|
||||
@@ -450,12 +462,12 @@ exports.main = async (event, context) => {
|
||||
|
||||
### 6.3 数据保护
|
||||
|
||||
| 措施 | 说明 |
|
||||
|------|------|
|
||||
| 默认 draft 状态 | 发布后默认 `status: 'draft'`,需手动激活为 `active` |
|
||||
| 版本递增 | 每次更新 `version + 1`,可追踪变更历史 |
|
||||
| publishedBy 标记 | `publishedBy: 'debug'` 区分来源 |
|
||||
| 时间戳 | `publishedAt` / `updatedAt` 记录操作时间 |
|
||||
| 措施 | 说明 |
|
||||
| ---------------- | --------------------------------------------------- |
|
||||
| 默认 draft 状态 | 发布后默认 `status: 'draft'`,需手动激活为 `active` |
|
||||
| 版本递增 | 每次更新 `version + 1`,可追踪变更历史 |
|
||||
| publishedBy 标记 | `publishedBy: 'debug'` 区分来源 |
|
||||
| 时间戳 | `publishedAt` / `updatedAt` 记录操作时间 |
|
||||
|
||||
---
|
||||
|
||||
@@ -571,26 +583,26 @@ Debug 发布 → 云端数据 → 线上可见
|
||||
|
||||
## 十、风险与应对
|
||||
|
||||
| 风险 | 影响 | 应对 |
|
||||
|------|------|------|
|
||||
| **误操作发布测试数据** | 污染线上列表 | 默认 `status: 'draft'`;确认面板二次确认 |
|
||||
| **入口图质量不一致** | 不同设备/DPR 下渲染差异 | 统一使用开发者工具发布;导出时固定 `destWidth/destHeight` |
|
||||
| **云端数据丢失** | 已发布内容消失 | `category.data.ts` 兜底;`syncFromCloud` 定期同步 |
|
||||
| **安全:非授权上传** | 恶意写入数据 | 云函数白名单校验;客户端三重门控 |
|
||||
| **双数据源不一致** | 本地兜底与云端数据冲突 | 云端优先,发版前 `syncFromCloud` 对齐 |
|
||||
| **云存储额度** | 图片累积占用存储 | 入口图约 50~200KB/张,100 张仅 ~20MB,远低于免费额度 |
|
||||
| 风险 | 影响 | 应对 |
|
||||
| ---------------------- | ----------------------- | --------------------------------------------------------- |
|
||||
| **误操作发布测试数据** | 污染线上列表 | 默认 `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/` | 存放入口图 |
|
||||
| 现有模块 | 本方案的关联 |
|
||||
| ------------------------------------------ | ------------------------------------------------------ |
|
||||
| `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/` | 存放入口图 |
|
||||
|
||||
+37
-34
@@ -66,32 +66,35 @@
|
||||
|
||||
### 3.3 集合:`worksheets`(题型配置)
|
||||
|
||||
| 字段 | BSON 类型 | 必填 | 说明 |
|
||||
| ------------------ | --------------- | ---- | --------------------------------------------------------------- |
|
||||
| `_id` | string | 是 | 主键;建议与后端一致用 cuid 或可读业务 ID。 |
|
||||
| `title` | string | 是 | 标题,建议 ≤100 字符。 |
|
||||
| `desc` | string | 是 | 描述,建议 ≤500 字符。 |
|
||||
| `category` | string | 是 | 大类:`math` \| `chinese` \| `english` \| `puzzle` \| `craft`。 |
|
||||
| `subcategory` | string | 是 | 子类,建议 ≤50 字符。 |
|
||||
| `ageMin` | int | 是 | 适龄最小值(与 Prisma 一致;替代原 `ageRange` 数组)。 |
|
||||
| `ageMax` | int | 是 | 适龄最大值。 |
|
||||
| `difficulty` | int | 是 | 难度 1–4。 |
|
||||
| `previewImage` | string | 是 | 预览图 URL/云存储 fileID,建议 ≤500 字符。 |
|
||||
| `tags` | array\<string\> | 是 | 标签列表。 |
|
||||
| `isNew` | bool | 是 | 是否新品,默认 `false`。 |
|
||||
| `isHot` | bool | 是 | 是否热门,默认 `false`。 |
|
||||
| `sortOrder` | int | 是 | 排序权重,默认 `0`。 |
|
||||
| `downloadCount` | int | 是 | 下载次数,默认 `0`。 |
|
||||
| `status` | string | 是 | `active` \| `draft` \| `hidden`,默认 `active`。 |
|
||||
| `template` | string | 是 | 渲染模板类型 `TemplateType`,建议 ≤30 字符。 |
|
||||
| `generator` | string | 是 | 生成器类型 `GeneratorType`,建议 ≤30 字符。 |
|
||||
| `generatorConfig` | object | 是 | 生成器参数(JSON 对象)。 |
|
||||
| `layoutConfig` | object | 是 | 排版参数(JSON 对象)。 |
|
||||
| `userConfigurable` | object \| null | 否 | 用户可调整参数定义(JSON)。 |
|
||||
| `legacyPage` | string \| null | 否 | 旧页面路径,迁移过渡用,建议 ≤200 字符。 |
|
||||
| `createdAt` | date | 是 | 创建时间。 |
|
||||
| `updatedAt` | date | 是 | 更新时间。 |
|
||||
| 字段 | 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` \| `hidden`,默认 `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 字符。 |
|
||||
**索引建议**
|
||||
|
||||
| 索引键 | 类型 | 说明 |
|
||||
@@ -122,16 +125,16 @@
|
||||
|
||||
对应 Prisma `User`(`@@map("users")`)。**`openid` / `unionid` 与 `_id` 的取舍、`unionid` 写入方式**见 §3.2。
|
||||
|
||||
| 字段 | BSON 类型 | 必填 | 说明 |
|
||||
| ---------------- | -------------- | ---- | -------------------------------------------------------------------- |
|
||||
| 字段 | 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` | string | 是 | 当前小程序下微信用户标识;与 Prisma 一致建议 ≤100 字符;**业务唯一**,索引见下表。 |
|
||||
| `unionid` | string \| null | 否 | 开放平台下跨应用用户标识;建议 ≤100 字符;未绑开放平台或未返回时为空。 |
|
||||
| `nickName` | string \| null | 否 | 用户昵称;建议 ≤50 字符。 |
|
||||
| `avatarUrl` | string \| null | 否 | 头像 URL 或云存储 fileID;建议 ≤500 字符。 |
|
||||
| `totalDownloads` | int | 是 | 累计下载次数,默认 `0`。 |
|
||||
| `createdAt` | date | 是 | 首次创建时间。 |
|
||||
| `lastActiveAt` | date | 是 | 最近一次活跃时间(登录、打开小程序、写库等策略由实现约定)。 |
|
||||
|
||||
**索引建议**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user