380 lines
15 KiB
Markdown
380 lines
15 KiB
Markdown
# Banban 统一数据与接口设计文档(Mini Program + talkingq-url,V3)
|
||
|
||
## 1. 目标与边界
|
||
|
||
本设计用于统一 `mini-program` 与 `talkingq-url` 的数据库模型与接口语义,目标是:
|
||
|
||
1. 两个服务使用同一套设备身份(`device_id`)。
|
||
2. 保留 `talkingq-url/mysql/init/01-init.sql` 现有表结构不变。
|
||
3. 在不改动基础表的前提下,补齐家长/小孩/绑定/聊天/定位能力。
|
||
|
||
硬性边界:
|
||
|
||
1. `01-init.sql` 中现有表不改表名、不改字段、不改约束。
|
||
2. 所有新增表仅做增量扩展。
|
||
3. 新增表字段命名风格对齐现有风格:`snake_case`、`*_id`、`is_active`、`status`、`created_at`、`updated_at`。
|
||
|
||
## 2. 现有冻结表(来自 01-init.sql)
|
||
|
||
以下表为设备能力基础层,保持不变:
|
||
|
||
1. `device_auth`:设备身份白名单(`device_id + serial_number`)。
|
||
2. `device_configs`:设备角色与基础配置(含 `volume`)。
|
||
3. `conversation_histories`:设备与角色会话头。
|
||
4. `conversation_messages`:设备与角色会话消息。
|
||
5. `roles` / `role_languages`:角色与多语言配置。
|
||
6. `device_firmware_update`:设备固件升级状态。
|
||
7. `system_config`:系统配置项。
|
||
|
||
职责说明:
|
||
|
||
1. `conversation_histories/conversation_messages` 继续只服务“设备 <-> AI 角色”的对话,不承担家长-小孩 IM。
|
||
2. 设备鉴权统一走 `device_auth`,`device_id` 为全局设备业务标识。
|
||
|
||
## 3. 新增主体模型
|
||
|
||
### 3.1 家长主体表 `parents`
|
||
|
||
建议字段:
|
||
|
||
1. `user_id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `openid` VARCHAR(64) UNIQUE NOT NULL
|
||
3. `unionid` VARCHAR(64) NULL
|
||
4. `nickname` VARCHAR(64) NULL
|
||
5. `avatar_url` VARCHAR(255) NULL
|
||
6. `phone` VARCHAR(20) NULL
|
||
7. `status` TINYINT NOT NULL DEFAULT 1
|
||
8. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
9. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
索引建议:
|
||
|
||
1. `uk_openid(openid)`
|
||
2. `idx_unionid(unionid)`
|
||
3. `idx_phone(phone)`
|
||
|
||
### 3.2 小孩主体表 `children`
|
||
|
||
建议字段:
|
||
|
||
1. `child_id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `child_name` VARCHAR(32) NOT NULL
|
||
3. `child_gender` TINYINT NOT NULL DEFAULT 2
|
||
4. `child_birthday` DATE NULL
|
||
5. `status` TINYINT NOT NULL DEFAULT 1
|
||
6. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
7. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
说明:
|
||
|
||
1. 不在库内存储 `child_age` 计算列,年龄由查询层按时间实时计算,避免跨年缓存误差。
|
||
|
||
### 3.3 家长-小孩关系表 `parent_child_relations`
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `user_id` BIGINT UNSIGNED NOT NULL
|
||
3. `child_id` BIGINT UNSIGNED NOT NULL
|
||
4. `relation_type` TINYINT NOT NULL DEFAULT 9
|
||
5. `is_primary` TINYINT(1) NOT NULL DEFAULT 0
|
||
6. `status` TINYINT NOT NULL DEFAULT 1
|
||
7. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
8. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
约束建议:
|
||
|
||
1. `UNIQUE(user_id, child_id)`
|
||
2. `FOREIGN KEY (user_id) REFERENCES parents(user_id)`
|
||
3. `FOREIGN KEY (child_id) REFERENCES children(child_id)`
|
||
|
||
### 3.4 设备归属表 `device_bindings`
|
||
|
||
用途:表达“当前哪块设备绑定到哪个小孩”,并与 `device_auth` 对齐。
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `device_id` VARCHAR(64) NOT NULL
|
||
3. `child_id` BIGINT UNSIGNED NOT NULL
|
||
4. `status` TINYINT NOT NULL DEFAULT 1
|
||
5. `bound_at` DATETIME NOT NULL
|
||
6. `unbound_at` DATETIME NULL
|
||
7. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
8. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
约束建议:
|
||
|
||
1. `UNIQUE(device_id)`
|
||
2. `UNIQUE(child_id)`
|
||
3. `FOREIGN KEY (device_id) REFERENCES device_auth(device_id)`
|
||
4. `FOREIGN KEY (child_id) REFERENCES children(child_id)`
|
||
|
||
### 3.5 绑定会话表 `device_bind_sessions`
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `bind_token` CHAR(36) UNIQUE NOT NULL
|
||
3. `device_id` VARCHAR(64) NOT NULL
|
||
4. `initiator_user_id` BIGINT UNSIGNED NOT NULL
|
||
5. `target_child_id` BIGINT UNSIGNED NULL
|
||
6. `challenge_code_hash` CHAR(64) NULL
|
||
7. `challenge_set_at` DATETIME NULL
|
||
8. `expires_at` DATETIME NOT NULL
|
||
9. `max_attempt_count` TINYINT UNSIGNED NOT NULL DEFAULT 5
|
||
10. `attempt_count` TINYINT UNSIGNED NOT NULL DEFAULT 0
|
||
11. `status` TINYINT NOT NULL DEFAULT 1
|
||
12. `confirmed_at` DATETIME NULL
|
||
13. `consumed_at` DATETIME NULL
|
||
14. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
15. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
约束建议:
|
||
|
||
1. `FOREIGN KEY (device_id) REFERENCES device_auth(device_id)`
|
||
2. `FOREIGN KEY (initiator_user_id) REFERENCES parents(user_id)`
|
||
3. `FOREIGN KEY (target_child_id) REFERENCES children(child_id)`
|
||
|
||
### 3.6 绑定历史表 `device_bind_history`
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `device_id` VARCHAR(64) NOT NULL
|
||
3. `child_id` BIGINT UNSIGNED NOT NULL
|
||
4. `bound_by_user_id` BIGINT UNSIGNED NOT NULL
|
||
5. `unbound_by_user_id` BIGINT UNSIGNED NULL
|
||
6. `bind_source` TINYINT NOT NULL DEFAULT 1
|
||
7. `bound_at` DATETIME NOT NULL
|
||
8. `unbound_at` DATETIME NULL
|
||
9. `unbind_reason` VARCHAR(191) NULL
|
||
10. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
|
||
约束建议:
|
||
|
||
1. `FOREIGN KEY (device_id) REFERENCES device_auth(device_id)`
|
||
2. `FOREIGN KEY (child_id) REFERENCES children(child_id)`
|
||
3. `FOREIGN KEY (bound_by_user_id) REFERENCES parents(user_id)`
|
||
4. `FOREIGN KEY (unbound_by_user_id) REFERENCES parents(user_id)`
|
||
|
||
### 3.7 卡片表 `cards`
|
||
|
||
建议字段:
|
||
|
||
1. `card_id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `card_uuid` VARCHAR(64) UNIQUE NOT NULL
|
||
3. `device_id` VARCHAR(64) UNIQUE NULL
|
||
4. `card_name` VARCHAR(64) NULL
|
||
5. `status` TINYINT NOT NULL DEFAULT 0
|
||
6. `total_swaps` INT NOT NULL DEFAULT 0
|
||
7. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
8. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
约束建议:
|
||
|
||
1. `FOREIGN KEY (device_id) REFERENCES device_auth(device_id)`
|
||
|
||
### 3.8 设备设置表 `device_settings`
|
||
|
||
建议字段:
|
||
|
||
1. `setting_id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `device_id` VARCHAR(64) NOT NULL UNIQUE
|
||
3. `sleep_mode` TINYINT NOT NULL DEFAULT 0
|
||
4. `disable_time_start` TIME NULL
|
||
5. `disable_time_end` TIME NULL
|
||
6. `timezone` VARCHAR(32) NOT NULL DEFAULT 'Asia/Shanghai'
|
||
7. `volume` TINYINT UNSIGNED NULL
|
||
8. `brightness` TINYINT UNSIGNED NULL
|
||
9. `disable_weekdays` VARCHAR(32) NULL
|
||
10. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
11. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
兼容规则:
|
||
|
||
1. `volume` 同步写入 `device_configs.volume`,防止两服务读到不同值。
|
||
2. 若只允许单一音量来源,则以 `device_configs.volume` 为准,`device_settings.volume` 可后续下线。
|
||
3. `disable_time_start/disable_time_end` 与历史命名 `sleep_start/sleep_end` 一一映射。
|
||
4. 禁用时段按 `timezone` 解释;跨天时段(如 22:00-07:00)按“跨日窗口”处理。
|
||
|
||
## 4. 聊天与定位模型(新增,不改 01 现有会话表)
|
||
|
||
### 4.1 IM 会话表 `im_conversations`
|
||
|
||
用途:仅服务家长-小孩、小孩-小孩 1v1,不与 `conversation_histories` 混用。
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `conversation_type` TINYINT UNSIGNED NOT NULL
|
||
3. `participant_a_type` TINYINT UNSIGNED NOT NULL
|
||
4. `participant_a_id` VARCHAR(64) NOT NULL
|
||
5. `participant_b_type` TINYINT UNSIGNED NOT NULL
|
||
6. `participant_b_id` VARCHAR(64) NOT NULL
|
||
7. `pair_key` VARCHAR(191) NOT NULL
|
||
8. `status` TINYINT NOT NULL DEFAULT 1
|
||
9. `last_seq` BIGINT UNSIGNED NOT NULL DEFAULT 0
|
||
10. `message_count` BIGINT UNSIGNED NOT NULL DEFAULT 0
|
||
11. `last_message_preview` VARCHAR(255) NULL
|
||
12. `last_message_at` DATETIME NULL
|
||
13. `ext_json` JSON NULL
|
||
14. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
15. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
说明:
|
||
|
||
1. 会话核心字段保持通用,不使用 `child_low_id/child_high_id` 这类场景耦合命名。
|
||
2. `conversation_type` 使用类型码,可扩展;新增会话类型优先通过“类型码 + 业务校验 + `ext_json`”扩展,无需改核心表。
|
||
3. 当前建议启用类型:`1-child_peer`、`2-parent_child`。
|
||
|
||
约束建议:
|
||
|
||
1. `UNIQUE(conversation_type, pair_key)`
|
||
2. `INDEX idx_participant_a (participant_a_type, participant_a_id)`
|
||
3. `INDEX idx_participant_b (participant_b_type, participant_b_id)`
|
||
4. `INDEX idx_last_message_at (last_message_at)`
|
||
|
||
### 4.2 IM 消息表 `im_messages`
|
||
|
||
建议字段:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `conversation_id` BIGINT UNSIGNED NOT NULL
|
||
3. `seq` BIGINT UNSIGNED NOT NULL
|
||
4. `sender_type` TINYINT UNSIGNED NOT NULL
|
||
5. `sender_id` VARCHAR(64) NOT NULL
|
||
6. `receiver_type` TINYINT UNSIGNED NOT NULL
|
||
7. `receiver_id` VARCHAR(64) NOT NULL
|
||
8. `content_type` TINYINT UNSIGNED NOT NULL
|
||
9. `content_text` MEDIUMTEXT NULL
|
||
10. `content_json` JSON NULL
|
||
11. `media_file_key` VARCHAR(255) NULL
|
||
12. `media_duration_ms` INT UNSIGNED NULL
|
||
13. `media_mime_type` VARCHAR(64) NULL
|
||
14. `media_size_bytes` BIGINT UNSIGNED NULL
|
||
15. `media_transcript_text` TEXT NULL
|
||
16. `client_msg_id` VARCHAR(64) NULL
|
||
17. `sender_name_snapshot` VARCHAR(64) NULL
|
||
18. `sender_avatar_snapshot` VARCHAR(255) NULL
|
||
19. `receiver_name_snapshot` VARCHAR(64) NULL
|
||
20. `receiver_avatar_snapshot` VARCHAR(255) NULL
|
||
21. `ext_json` JSON NULL
|
||
22. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
23. `deleted_at` DATETIME NULL
|
||
|
||
约束建议:
|
||
|
||
1. `UNIQUE(conversation_id, seq)`
|
||
2. `UNIQUE(conversation_id, client_msg_id)`
|
||
3. `FOREIGN KEY (conversation_id) REFERENCES im_conversations(id) ON DELETE CASCADE`
|
||
4. `INDEX idx_sender (sender_type, sender_id, created_at)`
|
||
5. `INDEX idx_receiver (receiver_type, receiver_id, created_at)`
|
||
|
||
#### 4.2.1 文本与语音消息规则
|
||
|
||
1. `content_type=1(text)`:`content_text` 必填。
|
||
2. `content_type=2(audio)`:`media_file_key` 必填,建议同时写入 `media_duration_ms`、`media_mime_type`、`media_size_bytes`。
|
||
3. `content_type=3(image)`:`media_file_key` 必填。
|
||
4. `content_type=4(json)`:`content_json` 必填。
|
||
5. 语音转写内容可写入 `media_transcript_text`,用于检索与展示。
|
||
6. 展示层优先使用 `sender_name_snapshot/sender_avatar_snapshot` 与 `receiver_*_snapshot`,减少联表查询。
|
||
|
||
### 4.3 位置表
|
||
|
||
`child_location_current`:
|
||
|
||
1. `child_id` BIGINT UNSIGNED PK
|
||
2. `device_id` VARCHAR(64) NOT NULL
|
||
3. `coord_type` VARCHAR(16) NOT NULL DEFAULT 'gcj02'
|
||
4. `lat` DECIMAL(10,7) NOT NULL
|
||
5. `lng` DECIMAL(10,7) NOT NULL
|
||
6. `accuracy_m` INT UNSIGNED NULL
|
||
7. `altitude_m` DECIMAL(8,2) NULL
|
||
8. `speed_mps` DECIMAL(8,2) NULL
|
||
9. `heading_deg` SMALLINT UNSIGNED NULL
|
||
10. `source` TINYINT UNSIGNED NOT NULL
|
||
11. `battery_pct` TINYINT UNSIGNED NULL
|
||
12. `device_time` DATETIME NOT NULL
|
||
13. `server_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
14. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
`child_location_history`:
|
||
|
||
1. `id` BIGINT UNSIGNED PK AUTO_INCREMENT
|
||
2. `child_id` BIGINT UNSIGNED NOT NULL
|
||
3. `device_id` VARCHAR(64) NOT NULL
|
||
4. `coord_type` VARCHAR(16) NOT NULL DEFAULT 'gcj02'
|
||
5. 其余定位字段与 `child_location_current` 对齐
|
||
6. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
|
||
#### 4.3.1 位置上报协议(统一格式)
|
||
|
||
1. 上报字段:`device_id`、`child_id`、`coord_type`、`lat`、`lng`、`accuracy_m`、`source`、`device_time`、`battery_pct`。
|
||
2. 坐标系标准:统一使用 `gcj02`。
|
||
3. 坐标格式:统一按 `lat,lng`(纬度在前,经度在后)传输和存储。
|
||
4. 若设备侧提供 `wgs84`,服务端需先转换到 `gcj02` 后再入库;原始坐标可选写入 `ext_json` 备查。
|
||
5. 写入规则:同事务写 `child_location_history` 并覆盖 `child_location_current`。
|
||
|
||
## 5. 核心业务流程
|
||
|
||
### 5.1 设备注册与鉴权
|
||
|
||
1. 生产或后台调用 `talkingq-url` 现有注册接口,写入 `device_auth`。
|
||
2. 手表请求携带 `X-Device-ID` 与 `X-Device-Serial`。
|
||
3. 后端基于 `device_auth` 鉴权通过后继续处理聊天/定位/绑定。
|
||
|
||
### 5.2 绑定流程(扫码 + 确认码)
|
||
|
||
1. 家长登录后发起 `bind/start`,创建 `device_bind_sessions`。
|
||
2. 手表上报确认码,更新会话挑战信息。
|
||
3. 家长提交确认码。
|
||
4. 后端校验通过后开启事务。
|
||
5. upsert `parent_child_relations`。
|
||
6. upsert `device_bindings`(保证一设备一行、一小孩一行)。
|
||
7. 追加 `device_bind_history`。
|
||
8. 更新会话状态为 `confirmed/consumed` 并提交事务。
|
||
|
||
### 5.3 聊天流程
|
||
|
||
1. 设备 AI 对话仍走 `conversation_histories + conversation_messages`。
|
||
2. 家长-小孩、小孩-小孩消息走 `im_conversations + im_messages`。
|
||
3. IM 消息统一使用 `sender_type/sender_id + receiver_type/receiver_id`,不按场景拆专用发送者字段。
|
||
4. 发送接口必须使用 `client_msg_id` 幂等。
|
||
5. 查询前必须校验 `parent_child_relations` 权限。
|
||
6. 列表展示优先读取消息快照字段,降低跨表查询成本。
|
||
|
||
### 5.4 位置流程
|
||
|
||
1. 手表上报定位写入 `child_location_history`。
|
||
2. 同事务更新 `child_location_current`。
|
||
3. 小程序查询当前定位读 `child_location_current`,轨迹读 `child_location_history`。
|
||
4. 地址解析使用腾讯位置服务;若解析失败,前端降级展示经纬度。
|
||
|
||
## 6. 统一约束与实现规则
|
||
|
||
1. 所有设备引用字段统一使用 `device_id VARCHAR(64)`,不再新增 `watch_device_id BIGINT`。
|
||
2. 设备主身份只认 `device_auth.device_id`。
|
||
3. 跨主体通用 ID 字段统一采用字符串(如 `participant_*_id`、`sender_id`、`receiver_id`)。
|
||
4. 所有业务表统一包含 `created_at/updated_at`(历史流水类可无 `updated_at`)。
|
||
5. `status` 用于业务态,`is_active` 用于启停态;两者不混用。
|
||
6. 关键写路径(绑定确认、发消息、位置写入)必须事务化。
|
||
7. 腾讯位置服务启用前提:必须配置合法 `key`,并完成域名/来源白名单与配额设置。
|
||
8. 地图相关接口统一使用 `gcj02` 和 `lat,lng`;禁止混用 `wgs84/bd09` 直接入库。
|
||
|
||
## 7. 分阶段落地
|
||
|
||
1. 第一阶段:落地 `parents/children/parent_child_relations/device_bind_*`,打通绑定链路。
|
||
2. 第二阶段:落地 `im_conversations/im_messages`,打通家长与小孩 IM。
|
||
3. 第三阶段:落地 `child_location_current/history`,打通定位链路。
|
||
4. 第四阶段:补充审计、告警、实时推送和运营能力。
|
||
|
||
## 8. 验收标准
|
||
|
||
1. 不修改 `01-init.sql` 任一现有表,服务可正常启动。
|
||
2. 设备鉴权、角色配置、OTA能力保持可用。
|
||
3. 家长可完成扫码绑定、解绑、重绑,且历史保留。
|
||
4. 家长可查看自己小孩与其他小孩完整双向消息。
|
||
5. 小程序可查看小孩最新位置与历史轨迹。
|
||
|