484 lines
20 KiB
Markdown
484 lines
20 KiB
Markdown
# Banban 统一数据与接口设计文档(Mini Program + talkingq-url,V4)
|
||
|
||
## 1. 目标与边界
|
||
|
||
本设计用于统一 `mini-program` 与 `talkingq-url` 的数据库模型与接口语义,目标是:
|
||
|
||
1. 两个服务使用同一套设备身份(`device_id`)。
|
||
2. 保留 `talkingq-url/mysql/init/01-init.sql` 现有表结构不变。
|
||
3. 在不改动基础表的前提下,补齐家长/小孩/绑定/聊天/定位能力。
|
||
4. 家长账号体系统一采用微信小程序登录,服务端基于 `code2Session` 换取真实 `openid/unionid`。
|
||
|
||
硬性边界:
|
||
|
||
1. `01-init.sql` 中现有表不改表名、不改字段、不改约束。
|
||
2. 所有新增表仅做增量扩展。
|
||
3. 新增表字段命名风格对齐现有风格:`snake_case`、`*_id`、`is_active`、`status`、`created_at`、`updated_at`。
|
||
4. 微信 `AppSecret` 只能保存在服务端配置中,小程序端只持有一次性 `code`。
|
||
|
||
## 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. `avatar_file_key` VARCHAR(255) NULL
|
||
7. `phone` VARCHAR(20) NULL
|
||
8. `status` TINYINT NOT NULL DEFAULT 1
|
||
9. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
10. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
说明:
|
||
|
||
1. `avatar_file_key` 作为头像主存储值,保存 COS 对象 key。
|
||
2. `avatar_url` 作为兼容或展示字段使用,不作为唯一主依据。
|
||
3. `openid` 必须存储微信服务端 `code2Session` 返回的真实 OpenID;禁止将前端 `code`、测试用户名或设备标识写入该字段。
|
||
4. `unionid` 仅在微信返回时写入;历史记录为空时,后续登录可补写,不作为首次登录成功前置条件。
|
||
5. `nickname/avatar_url` 为展示资料,可由小程序端在登录时上传并在后续更新,但不参与身份认证。
|
||
6. `phone` 为业务补充字段,与微信登录态解耦;如后续接入手机号授权,应基于服务端掌握的 `session_key` 解密后再写入。
|
||
|
||
索引建议:
|
||
|
||
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. `owner_user_id` BIGINT UNSIGNED NOT NULL
|
||
4. `child_id` BIGINT UNSIGNED NULL
|
||
5. `status` TINYINT NOT NULL DEFAULT 1
|
||
6. `bound_at` DATETIME NOT NULL
|
||
7. `unbound_at` DATETIME NULL
|
||
8. `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
9. `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
|
||
说明:
|
||
|
||
1. 设备可以先归属到家长,再在后续流程中补充关联 `child_id`。
|
||
2. `child_id` 为空表示当前设备已归属,但尚未绑定到具体小孩。
|
||
|
||
约束建议:
|
||
|
||
1. `UNIQUE(device_id)`
|
||
2. `UNIQUE(child_id)`
|
||
3. `INDEX idx_owner_user_id(owner_user_id)`
|
||
4. `FOREIGN KEY (device_id) REFERENCES device_auth(device_id)`
|
||
5. `FOREIGN KEY (owner_user_id) REFERENCES parents(user_id)`
|
||
6. `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. 小程序调用 `wx.login` / `Taro.login` 获取一次性 `code`。
|
||
2. 小程序调用后端 `POST /auth/login`,上传 `code`,并可附带 `nickname/avatar_url` 作为资料字段。
|
||
3. 后端使用服务端配置的 `wechat_app_id + wechat_app_secret` 调用微信 `code2Session`。
|
||
4. 后端从微信响应中获取真实 `openid`、可选 `unionid`,以及仅供服务端使用的 `session_key`。
|
||
5. 后端以 `openid` 为唯一身份键 upsert `parents`;若本次拿到 `unionid` 且库中为空,则补写 `unionid`。
|
||
6. 后端签发本地 `access_token(JWT)` 返回给小程序,后续业务接口仍统一使用本地 Bearer Token。
|
||
7. 若微信换码失败、`openid` 缺失、`code` 失效或被重复使用,登录直接失败,不创建本地用户。
|
||
8. `session_key` 仅用于后续需要的微信敏感数据解密能力(如手机号);当前阶段不作为登录态主键,也不写入 `parents` 主表。
|
||
|
||
### 5.2 设备注册与鉴权
|
||
|
||
1. 生产或后台调用 `talkingq-url` 现有注册接口,写入 `device_auth`。
|
||
2. 手表请求携带 `X-Device-ID` 与 `X-Device-Serial`。
|
||
3. 后端基于 `device_auth` 鉴权通过后继续处理聊天/定位/绑定。
|
||
|
||
### 5.3 绑定流程(扫码 + 确认码)
|
||
|
||
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.4 聊天流程
|
||
|
||
1. 设备 AI 对话仍走 `conversation_histories + conversation_messages`。
|
||
2. 家长-小孩、小孩-小孩消息走 `im_conversations + im_messages`。
|
||
3. IM 消息统一使用 `sender_type/sender_id + receiver_type/receiver_id`,不按场景拆专用发送者字段。
|
||
4. `mini-program` 侧不再新增独立的 `chat_*` 消息表定义,统一以 `im_*` 为 IM 正表。
|
||
5. 发送接口必须使用 `client_msg_id` 幂等。
|
||
6. 查询前必须校验 `parent_child_relations` 权限。
|
||
7. 列表展示优先读取消息快照字段,降低跨表查询成本。
|
||
|
||
### 5.5 位置流程
|
||
|
||
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` 直接入库。
|
||
9. 小程序端只上送一次性 `code`,不得自行调用微信换取 `openid`,更不得持有 `AppSecret`。
|
||
10. `code` 只用于调用微信 `code2Session`,不得入库为 `openid`、`unionid` 或任何长期身份字段。
|
||
11. `session_key` 不返回前端,不写入 `parents` 主表;如需支持手机号等敏感数据解密,应服务端短期缓存并设置明确过期时间。
|
||
12. `nickname/avatar_url` 仅作资料字段,不能作为登录态、账号归并或权限判断依据。
|
||
|
||
## 7. 分阶段落地
|
||
|
||
1. 第一阶段:落地微信登录链路,打通 `Taro.login -> /auth/login -> code2Session -> parents -> JWT`。
|
||
2. 第二阶段:落地 `parents/children/parent_child_relations/device_bind_*`,打通绑定链路。
|
||
3. 第三阶段:落地 `im_conversations/im_messages`,打通家长与小孩 IM。
|
||
4. 第四阶段:落地 `child_location_current/history`,打通定位链路。
|
||
5. 第五阶段:补充审计、告警、实时推送和运营能力。
|
||
|
||
## 8. 验收标准
|
||
|
||
1. 家长可通过真实微信登录完成换码,并获取本地 Bearer Token。
|
||
2. `parents.openid` 存储值来自微信返回的真实 OpenID,而不是前端 `code` 或测试标识。
|
||
3. 不修改 `01-init.sql` 任一现有表,服务可正常启动。
|
||
4. 设备鉴权、角色配置、OTA能力保持可用。
|
||
5. 家长可完成扫码绑定、解绑、重绑,且历史保留。
|
||
6. 家长可查看自己小孩与其他小孩完整双向消息。
|
||
7. 小程序可查看小孩最新位置与历史轨迹。
|
||
|
||
## 9. 共享库落地规范
|
||
|
||
### 9.1 唯一数据库与唯一 Schema
|
||
|
||
1. `mini-program` 与 `talkingq-url` 必须共用同一个 MySQL 数据库:`talkingq`。
|
||
2. `database/talkingq_shared_schema.sql` 是唯一 schema 真相源。
|
||
3. `mini-program/database/init.sql` 仅保留给历史上的“单服务独立开发”场景,不再作为共享部署的建库依据。
|
||
|
||
### 9.2 表归属(Table Ownership)
|
||
|
||
`talkingq-url` 拥有以下表的写入责任:
|
||
|
||
1. `device_auth`
|
||
2. `device_configs`
|
||
3. `conversation_histories`
|
||
4. `conversation_messages`
|
||
5. `roles`
|
||
6. `role_languages`
|
||
7. `device_firmware_update`
|
||
8. `system_config`
|
||
|
||
`mini-program` 拥有以下表的写入责任:
|
||
|
||
1. `parents`
|
||
2. `children`
|
||
3. `parent_child_relations`
|
||
4. `device_bindings`
|
||
5. `device_bind_sessions`
|
||
6. `device_bind_history`
|
||
7. `device_settings`
|
||
8. `cards`
|
||
9. `im_conversations`
|
||
10. `im_messages`
|
||
11. `child_location_current`
|
||
12. `child_location_history`
|
||
|
||
约束:
|
||
|
||
1. 两个服务允许跨服务读取共享依赖表,但不允许跨服务写对方负责的主表。
|
||
2. `mini-program` 只读 `device_auth`,不直接承担设备注册主流程。
|
||
3. `talkingq-url` 不写 `parents/children/device_bindings/im_* /child_location_*`。
|
||
|
||
### 9.3 共享字段与真相源
|
||
|
||
1. 设备主身份统一以 `device_auth.device_id` 为准。
|
||
2. 设备与 AI 的历史消息统一以 `conversation_histories + conversation_messages` 为准。
|
||
3. 家长/儿童 IM 统一以 `im_conversations + im_messages` 为准。
|
||
4. 设备音量如需双端共享,以 `device_configs.volume` 为真相源;`device_settings.volume` 仅作小程序展示和兼容字段。
|
||
|
||
### 9.4 迁移原则
|
||
|
||
1. 目标库固定为 `talkingq`。
|
||
2. 迁移时以 `talkingq-url` 当前使用的 `talkingq` 数据为准。
|
||
3. 历史 `mini_program` 库中的业务数据不并入 `talkingq`,也不作为冲突解决依据。
|
||
4. 迁移动作只做两件事:
|
||
1. 在 `talkingq` 上补齐 `mini-program` 所需扩展表。
|
||
2. 将 `mini-program` 服务配置切换到 `talkingq`。
|
||
5. `mini_program` 库可在切换验证完成后归档或删除,但不再作为运行时数据库。
|
||
|
||
### 9.5 启动与建表策略
|
||
|
||
1. 共享部署下,服务启动默认只检查数据库连通性,不自动执行 ORM `create_all`。
|
||
2. 只有显式开启开发开关时,`mini-program` 才允许自动建表。
|
||
3. 生产、联调、共享测试环境统一通过 `database/talkingq_shared_schema.sql` 或正式 migration 执行建表与变更。
|
||
|