Files
banban/mini-program/design.md

20 KiB
Raw Blame History

Banban 统一数据与接口设计文档Mini Program + talkingq-urlV4

1. 目标与边界

本设计用于统一 mini-programtalkingq-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*_idis_activestatuscreated_atupdated_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_authdevice_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_peer2-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_msmedia_mime_typemedia_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_snapshotreceiver_*_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_idchild_idcoord_typelatlngaccuracy_msourcedevice_timebattery_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-IDX-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_*_idsender_idreceiver_id)。
  4. 所有业务表统一包含 created_at/updated_at(历史流水类可无 updated_at)。
  5. status 用于业务态,is_active 用于启停态;两者不混用。
  6. 关键写路径(绑定确认、发消息、位置写入)必须事务化。
  7. 腾讯位置服务启用前提:必须配置合法 key,并完成域名/来源白名单与配额设置。
  8. 地图相关接口统一使用 gcj02lat,lng;禁止混用 wgs84/bd09 直接入库。
  9. 小程序端只上送一次性 code,不得自行调用微信换取 openid,更不得持有 AppSecret
  10. code 只用于调用微信 code2Session,不得入库为 openidunionid 或任何长期身份字段。
  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-programtalkingq-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 执行建表与变更。