196 lines
6.3 KiB
Markdown
196 lines
6.3 KiB
Markdown
# 03 · 云函数设计
|
||
|
||
约定:所有云函数用 `wx-server-sdk`,统一返回 `{ ok: true, data }` 或 `{ ok: false, code, msg }`。`openid` 一律从 `cloud.getWXContext().OPENID` 取,**绝不信任客户端传入的 openid**。
|
||
|
||
---
|
||
|
||
## 1. `login` — 登录 / 首次建用户
|
||
|
||
**职责**:拿 openid,若用户不存在则建档,返回用户信息 + 是否已有狗狗(决定跳建档还是首页)。
|
||
|
||
```
|
||
入参: {}
|
||
出参: {
|
||
ok, data: {
|
||
userInfo: { openid, nickname, avatar, walkCount, joinedAt },
|
||
hasDog: Boolean // false → 前端跳 0b 建档;true → 跳首页
|
||
}
|
||
}
|
||
```
|
||
|
||
逻辑:`users` 查 `_openid`,无则插入默认用户;再 `dogs` count 判断 `hasDog`。
|
||
|
||
---
|
||
|
||
## 2. `dogManage` — 狗狗增 / 改 / 查
|
||
|
||
**职责**:建档、编辑、列表(建档与编辑复用,由 `action` 区分)。
|
||
|
||
```
|
||
入参: {
|
||
action: 'create' | 'update' | 'list',
|
||
dog?: { _id?, name, breed, sizeLevel?, gender, ageStage, ageText, weight, avatar }
|
||
}
|
||
出参: { ok, data: dog | dogs[] }
|
||
```
|
||
|
||
要点:
|
||
- 服务端校验 `name`、`breed` 非空。
|
||
- 由 `breed` 查表得到体型档位 `sizeLevel`;中华田园犬、串串、找不到的犬种必须由前端让用户手动选择 `sizeLevel` 后再提交。
|
||
- 由 `sizeLevel` 得到 `baseStride`,填了 `weight` 时计算最终 `stride`:`baseStride * (1 + (weight - 档位标准体重) * 0.005)`,修正幅度封顶 ±15%。
|
||
- `create` 时初始化 `stats` 全 0。
|
||
- `update` 校验该 dog 的 `_openid` == 调用者,防越权改他人狗。
|
||
---
|
||
|
||
## 3. `walkSave` — 保存遛狗(核心)
|
||
|
||
**职责**:写入一条 `walks`,**事务式**更新每只狗 `stats`,触发成就判定;并支持成果卡页回写选填字段。这是 V1 最重的云函数。
|
||
|
||
### 3.1 结束遛狗:`action='finish'`
|
||
|
||
```
|
||
入参: {
|
||
action: 'finish',
|
||
dogIds: [String],
|
||
startAt, endAt, duration,
|
||
distance?,
|
||
pooped
|
||
}
|
||
出参: {
|
||
ok, data: {
|
||
walkId,
|
||
dogStepsByDog,
|
||
newAchievements: [{ key, name, desc }] // 本次新解锁,成果卡展示
|
||
}
|
||
}
|
||
```
|
||
|
||
执行步骤:
|
||
1. 校验 `dogIds` 均归属当前 openid;未建档任何狗时拒绝保存。
|
||
2. 计算 `walkDate`(按用户本地时区,前端传时区偏移或统一 +8)。
|
||
3. 插入 `walks`,`source='realtime'`、`isManual=false`、`countsForRanking=true`。
|
||
4. 获得定位授权时写入 `distance`,并按每只狗的 `stride` 在服务端计算 `dogStepsByDog`;未授权时 `distance` 与 `dogStepsByDog` 为 `null`,只保存时长。
|
||
5. 对每个 dogId:
|
||
- `totalWalks += 1`,`totalDistance += distance||0`,`totalSteps += dogStepsByDog[dogId]||0`。
|
||
- **连续天数**:比较 `lastWalkDate` 与 `walkDate`——同日不变;昨日则 `currentStreak += 1`;更早则重置为 1。更新 `lastWalkDate`。
|
||
- 用 `db.command.inc()` 做原子自增,避免并发覆盖。
|
||
6. `users.walkCount += 1`。
|
||
7. 调成就判定(见 `achievementCheck`),收集新解锁返回。
|
||
|
||
### 3.2 成果卡补充字段:`action='attach'`
|
||
|
||
```
|
||
入参: {
|
||
action: 'attach',
|
||
walkId,
|
||
weather?, mood?, note?, photos?
|
||
}
|
||
出参: { ok, data: { walkId, newAchievements?: [{ key, name, desc }] } }
|
||
```
|
||
|
||
要点:
|
||
- `weather/mood/note/photos` 全部选填,用户跳过也不影响 `finish` 已保存的核心记录。
|
||
- 只允许回写当前 openid 自己的 walk。
|
||
- 若 `weather='rainy'`,可在 attach 后补触发「雨天战士」等场景成就,并返回新增成就给成果卡刷新。
|
||
- 旧记录回看不复用 attach;后续单次记录详情页若支持编辑,应单独定义记录编辑能力。
|
||
---
|
||
|
||
## 4. `walkManual` — 手动补记
|
||
|
||
**职责**:补记一次遛狗。规则与 `walkSave` 不同。
|
||
|
||
```
|
||
入参: { dogId, startAt, duration, distance? }
|
||
出参: { ok, data: { walkId, newAchievements?: [{ key, name, desc }] } }
|
||
```
|
||
|
||
关键差异(**业务规则,必落地**):
|
||
- `source='manual'`、`isManual=true`、`countsForRanking=false`(不计排行榜,防刷量)。
|
||
- **可维持连续打卡**:同样参与 `currentStreak` 计算(不断签)。
|
||
- 不触发「分享/成果卡」类成就,但可触发「连续」类成就。
|
||
- 列表展示带「补记」标识(前端按 `isManual` 渲染)。
|
||
|
||
---
|
||
|
||
## 5. `getDogDetail` — 狗狗主页聚合
|
||
|
||
**职责**:一次返回狗狗主页所需全部数据,减少往返。
|
||
|
||
```
|
||
入参: { dogId }
|
||
出参: {
|
||
ok, data: {
|
||
dog: {...}, // 含 stats
|
||
achievements: { // 成就墙
|
||
unlocked: [{key,name,unlockedAt}],
|
||
total: 12
|
||
},
|
||
recentWalks: [walk, ...] // 近期记录(取最近 5~10 条)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. `getHistory` — 历史 / 记录 Tab 聚合
|
||
|
||
**职责**:返回当前用户全部狗狗的遛狗记录列表,供历史页和记录 Tab 复用,减少前端跨集合拼装。
|
||
|
||
```
|
||
入参: { dogId?, page?, pageSize? }
|
||
出参: {
|
||
ok, data: {
|
||
list: [{ walk, dogNames, manualLabel }],
|
||
hasMore: Boolean
|
||
}
|
||
}
|
||
```
|
||
|
||
要点:
|
||
- 默认查当前 openid 下全部记录;传 `dogId` 时只查某只狗。
|
||
- 按 `walkDate/startAt` 倒序返回,可由前端按周或日期分组。
|
||
- 返回项带狗名、是否补记标识,避免列表层再多次请求 `dogs`。
|
||
- 只读,不写库。
|
||
---
|
||
|
||
## 7. 内部模块:`achievementCheck`(被 walkSave/walkManual 复用)
|
||
|
||
不是独立云函数,是云函数内共享的判定模块(也可做成被 `cloud.callFunction` 内部调用的函数)。
|
||
|
||
```
|
||
输入: dogId, 该狗最新 stats, 本次 walk(含 weather/时间)
|
||
流程:
|
||
for 每个成就定义:
|
||
if 已解锁(查 dog_achievements) → skip
|
||
if 满足条件(基于 stats / walk):
|
||
插入 dog_achievements
|
||
收集进 newAchievements
|
||
返回: newAchievements[]
|
||
```
|
||
|
||
判定基于 **连续 / 累计 / 场景**,不基于「最多 / 最快」。
|
||
|
||
---
|
||
|
||
## 8. 云函数清单速查
|
||
|
||
| 云函数 | 触发时机 | 写库 |
|
||
|--------|---------|------|
|
||
| `login` | App 启动 | users |
|
||
| `dogManage` | 建档/编辑/进首页 | dogs |
|
||
| `walkSave` | 结束遛狗、成果卡补充字段回写 | walks, dogs, users, dog_achievements |
|
||
| `walkManual` | 历史页补记保存 | walks, dogs, dog_achievements |
|
||
| `getDogDetail` | 进狗狗主页 | 只读 |
|
||
| `getHistory` | 进历史页 / 记录 Tab | 只读(跨全部狗狗,附狗名) |
|
||
|
||
> 后续若上排行榜(pending),新增定时触发云函数做聚合,按 `countsForRanking=true` 统计。
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|