Files
Pet3/docs/02-数据模型.md
2026-06-25 18:24:29 +08:00

137 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 02 · 数据模型(云数据库)
云数据库为文档型(类 MongoDB。共 4 个集合。约定:`_openid` 由云开发自动写入作为归属与权限依据时间统一存毫秒时间戳Number
---
## 1. `users` — 用户
| 字段 | 类型 | 说明 |
|------|------|------|
| `_id` | String | 自动 |
| `_openid` | String | 微信 openid唯一 |
| `nickname` | String | 昵称,默认「铲屎官」 |
| `avatar` | String | 云存储 fileID可空 |
| `walkCount` | Number | 累计遛狗次数(冗余,展示用) |
| `joinedAt` | Number | 加入时间戳 |
| `createdAt` | Number | 创建时间 |
> 「加入 92 天」由 `joinedAt` 客户端算;「遛狗 87 次」读 `walkCount`。
---
## 2. `dogs` — 狗狗
| 字段 | 类型 | 说明 |
|------|------|------|
| `_id` | String | 狗狗 ID |
| `_openid` | String | 归属用户 |
| `name` | String | **必填**,名字 |
| `breed` | String | **必填**,犬种(如「柯基」) |
| `sizeLevel` | String | 体型档位:`xs`/`s`/`m`/`l`/`xl`,由犬种映射;未知犬种由用户手动选择 |
| `baseStride` | Number | 档位基础步幅,单位米/步 |
| `stride` | Number | 最终步幅,填体重时在 `baseStride` 上校准,封顶 ±15% |
| `gender` | String | `male` / `female` / null |
| `ageStage` | String | `puppy` / `adult` / `senior` / null |
| `ageText` | String | 展示文案,如「成犬 3 岁」,可空 |
| `weight` | Number | kg可空二次校准狗步 |
| `avatar` | String | 云存储 fileID可空 |
| `stats` | Object | 累计统计(见下,冗余加速主页) |
| `createdAt` | Number | 建档时间 |
**`stats` 内嵌对象**(每次遛狗结算时在云函数原子更新):
```js
stats: {
totalWalks: 87, // 总次数
totalDistance: 142.0, // 总公里
totalSteps: 0, // 总狗步(可选)
currentStreak: 7, // 连续天数
lastWalkDate: '2026-06-24', // 最近一次有效遛狗的日期(yyyy-mm-dd),算连续用
}
```
> 仅「名字 + 犬种」必填——降低首次门槛。`sizeLevel/baseStride/stride` 由 `utils/dogStep.js` 按「犬种 → 体型档位 → 步幅」计算;中华田园犬、串串、找不到的犬种必须让用户手动选体型档位。
---
## 3. `walks` — 遛狗记录
| 字段 | 类型 | 说明 |
|------|------|------|
| `_id` | String | 记录 ID |
| `_openid` | String | 归属用户 |
| `dogIds` | Array<String> | 本次遛的狗(支持多狗一起遛) |
| `startAt` | Number | 开始时间戳 |
| `endAt` | Number | 结束时间戳 |
| `duration` | Number | 净遛狗秒数(已扣暂停) |
| `distance` | Number | 公里,未授权定位时为 null |
| `dogStepsByDog` | Object | `{ [dogId]: steps }`,按每只狗的 `stride` 分别换算;未授权定位时为 null |
| `pooped` | Boolean | 便便打卡 |
| `weather` | String | `sunny`/`cloudy`/`rainy`,可空 |
| `mood` | String | 状态标签,可空 |
| `note` | String | 备注,可空 |
| `photos` | Array<String> | 云存储 fileID可空 |
| `source` | String | `realtime` / `manual`,对应 PRD 的数据来源 |
| `isManual` | Boolean | 是否手动补记;兼容展示判断,等价于 `source === 'manual'` |
| `countsForRanking` | Boolean | 是否计入排行榜;补记恒为 `false` |
| `walkDate` | String | `yyyy-mm-dd`,按本地时区,连续/分组用 |
| `createdAt` | Number | 入库时间 |
> 成果卡所有补充字段photos/weather/mood/note全部选填跳过也能保存。`dogStepsByDog` 仅用于拟人化展示;成就与未来排行榜只使用距离、时长、连续天数等客观量。
---
## 4. `dog_achievements` — 成就解锁记录
成就**按狗维度**(原型「成就墙 · 豆豆」)。成就定义不入库,固化在代码 `utils/achievements.js`(含 key、名称、描述、判定规则、图标。本集合只存「某狗解锁了某成就」。
| 字段 | 类型 | 说明 |
|------|------|------|
| `_id` | String | 自动 |
| `_openid` | String | 归属用户 |
| `dogId` | String | 狗狗 ID |
| `achievementKey` | String | 成就标识,如 `week_streak` |
| `unlockedAt` | Number | 解锁时间戳 |
唯一性:`(dogId, achievementKey)` 不重复解锁(云函数判定前先查重)。
**成就 key 参考**(来自原型):
| key | 名称 | 解锁条件 |
|-----|------|---------|
| `first_walk` | 首遛 | 第一次记录遛狗 |
| `week_streak` | 一周不断 | 连续遛狗满 7 天 |
| `month_streak` | 满月坚持 | 连续遛狗满 30 天 |
| `rainy_warrior` | 雨天战士 | 雨天也坚持遛狗 |
| `hundred_km` | 百里同行 | 累计满 100 公里 |
| `early_bird` | 早起鸟 | 7 点前遛狗 10 次 |
---
## 5. 索引
| 集合 | 索引字段 | 说明 |
|------|---------|------|
| `users` | `_openid`(唯一) | 登录查重 |
| `dogs` | `_openid` | 列出我的狗 |
| `walks` | `_openid` + `walkDate`(降序) | 历史列表、连续判定 |
| `walks` | `dogIds`(多键) | 按狗查记录 |
| `dog_achievements` | `dogId` + `achievementKey`(联合唯一) | 查重与成就墙 |
## 6. 数据库权限(安全规则)
全部集合设为 **「仅创建者可读写」**,写操作再经云函数二次校验。客户端只允许读自己的数据;统计累加、成就解锁等只能由云函数(管理员权限)写,防止刷量。
```json
// 示例walks 集合权限
{
"read": "doc._openid == auth.openid",
"write": false // 客户端禁写,统一走云函数
}
```