20 KiB
20 KiB
Banban 统一数据与接口设计文档(Mini Program + talkingq-url,V4)
1. 目标与边界
本设计用于统一 mini-program 与 talkingq-url 的数据库模型与接口语义,目标是:
- 两个服务使用同一套设备身份(
device_id)。 - 保留
talkingq-url/mysql/init/01-init.sql现有表结构不变。 - 在不改动基础表的前提下,补齐家长/小孩/绑定/聊天/定位能力。
- 家长账号体系统一采用微信小程序登录,服务端基于
code2Session换取真实openid/unionid。
硬性边界:
01-init.sql中现有表不改表名、不改字段、不改约束。- 所有新增表仅做增量扩展。
- 新增表字段命名风格对齐现有风格:
snake_case、*_id、is_active、status、created_at、updated_at。 - 微信
AppSecret只能保存在服务端配置中,小程序端只持有一次性code。
2. 现有冻结表(来自 01-init.sql)
以下表为设备能力基础层,保持不变:
device_auth:设备身份白名单(device_id + serial_number)。device_configs:设备角色与基础配置(含volume)。conversation_histories:设备与角色会话头。conversation_messages:设备与角色会话消息。roles/role_languages:角色与多语言配置。device_firmware_update:设备固件升级状态。system_config:系统配置项。
职责说明:
conversation_histories/conversation_messages继续只服务“设备 <-> AI 角色”的对话,不承担家长-小孩 IM。- 设备鉴权统一走
device_auth,device_id为全局设备业务标识。
3. 新增主体模型
3.1 家长主体表 parents
建议字段:
user_idBIGINT UNSIGNED PK AUTO_INCREMENTopenidVARCHAR(64) UNIQUE NOT NULLunionidVARCHAR(64) NULLnicknameVARCHAR(64) NULLavatar_urlVARCHAR(255) NULLavatar_file_keyVARCHAR(255) NULLphoneVARCHAR(20) NULLstatusTINYINT NOT NULL DEFAULT 1created_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
说明:
avatar_file_key作为头像主存储值,保存 COS 对象 key。avatar_url作为兼容或展示字段使用,不作为唯一主依据。openid必须存储微信服务端code2Session返回的真实 OpenID;禁止将前端code、测试用户名或设备标识写入该字段。unionid仅在微信返回时写入;历史记录为空时,后续登录可补写,不作为首次登录成功前置条件。nickname/avatar_url为展示资料,可由小程序端在登录时上传并在后续更新,但不参与身份认证。phone为业务补充字段,与微信登录态解耦;如后续接入手机号授权,应基于服务端掌握的session_key解密后再写入。
索引建议:
uk_openid(openid)idx_unionid(unionid)idx_phone(phone)
3.2 小孩主体表 children
建议字段:
child_idBIGINT UNSIGNED PK AUTO_INCREMENTchild_nameVARCHAR(32) NOT NULLchild_genderTINYINT NOT NULL DEFAULT 2child_birthdayDATE NULLstatusTINYINT NOT NULL DEFAULT 1created_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
说明:
- 不在库内存储
child_age计算列,年龄由查询层按时间实时计算,避免跨年缓存误差。
3.3 家长-小孩关系表 parent_child_relations
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTuser_idBIGINT UNSIGNED NOT NULLchild_idBIGINT UNSIGNED NOT NULLrelation_typeTINYINT NOT NULL DEFAULT 9is_primaryTINYINT(1) NOT NULL DEFAULT 0statusTINYINT NOT NULL DEFAULT 1created_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
约束建议:
UNIQUE(user_id, child_id)FOREIGN KEY (user_id) REFERENCES parents(user_id)FOREIGN KEY (child_id) REFERENCES children(child_id)
3.4 设备归属表 device_bindings
用途:表达“当前哪块设备归属到哪个家长,并可选关联到哪个小孩”,并与 device_auth 对齐。
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTdevice_idVARCHAR(64) NOT NULLowner_user_idBIGINT UNSIGNED NOT NULLchild_idBIGINT UNSIGNED NULLstatusTINYINT NOT NULL DEFAULT 1bound_atDATETIME NOT NULLunbound_atDATETIME NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
说明:
- 设备可以先归属到家长,再在后续流程中补充关联
child_id。 child_id为空表示当前设备已归属,但尚未绑定到具体小孩。
约束建议:
UNIQUE(device_id)UNIQUE(child_id)INDEX idx_owner_user_id(owner_user_id)FOREIGN KEY (device_id) REFERENCES device_auth(device_id)FOREIGN KEY (owner_user_id) REFERENCES parents(user_id)FOREIGN KEY (child_id) REFERENCES children(child_id)
3.5 绑定会话表 device_bind_sessions
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTbind_tokenCHAR(36) UNIQUE NOT NULLdevice_idVARCHAR(64) NOT NULLinitiator_user_idBIGINT UNSIGNED NOT NULLtarget_child_idBIGINT UNSIGNED NULLchallenge_code_hashCHAR(64) NULLchallenge_set_atDATETIME NULLexpires_atDATETIME NOT NULLmax_attempt_countTINYINT UNSIGNED NOT NULL DEFAULT 5attempt_countTINYINT UNSIGNED NOT NULL DEFAULT 0statusTINYINT NOT NULL DEFAULT 1confirmed_atDATETIME NULLconsumed_atDATETIME NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
约束建议:
FOREIGN KEY (device_id) REFERENCES device_auth(device_id)FOREIGN KEY (initiator_user_id) REFERENCES parents(user_id)FOREIGN KEY (target_child_id) REFERENCES children(child_id)
3.6 绑定历史表 device_bind_history
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTdevice_idVARCHAR(64) NOT NULLchild_idBIGINT UNSIGNED NOT NULLbound_by_user_idBIGINT UNSIGNED NOT NULLunbound_by_user_idBIGINT UNSIGNED NULLbind_sourceTINYINT NOT NULL DEFAULT 1bound_atDATETIME NOT NULLunbound_atDATETIME NULLunbind_reasonVARCHAR(191) NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
约束建议:
FOREIGN KEY (device_id) REFERENCES device_auth(device_id)FOREIGN KEY (child_id) REFERENCES children(child_id)FOREIGN KEY (bound_by_user_id) REFERENCES parents(user_id)FOREIGN KEY (unbound_by_user_id) REFERENCES parents(user_id)
3.7 卡片表 cards
建议字段:
card_idBIGINT UNSIGNED PK AUTO_INCREMENTcard_uuidVARCHAR(64) UNIQUE NOT NULLdevice_idVARCHAR(64) UNIQUE NULLcard_nameVARCHAR(64) NULLstatusTINYINT NOT NULL DEFAULT 0total_swapsINT NOT NULL DEFAULT 0created_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
约束建议:
FOREIGN KEY (device_id) REFERENCES device_auth(device_id)
3.8 设备设置表 device_settings
建议字段:
setting_idBIGINT UNSIGNED PK AUTO_INCREMENTdevice_idVARCHAR(64) NOT NULL UNIQUEsleep_modeTINYINT NOT NULL DEFAULT 0disable_time_startTIME NULLdisable_time_endTIME NULLtimezoneVARCHAR(32) NOT NULL DEFAULT 'Asia/Shanghai'volumeTINYINT UNSIGNED NULLbrightnessTINYINT UNSIGNED NULLdisable_weekdaysVARCHAR(32) NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
兼容规则:
volume同步写入device_configs.volume,防止两服务读到不同值。- 若只允许单一音量来源,则以
device_configs.volume为准,device_settings.volume可后续下线。 disable_time_start/disable_time_end与历史命名sleep_start/sleep_end一一映射。- 禁用时段按
timezone解释;跨天时段(如 22:00-07:00)按“跨日窗口”处理。
4. 聊天与定位模型(新增,不改 01 现有会话表)
4.1 IM 会话表 im_conversations
用途:仅服务家长-小孩、小孩-小孩 1v1,不与 conversation_histories 混用。
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTconversation_typeTINYINT UNSIGNED NOT NULLparticipant_a_typeTINYINT UNSIGNED NOT NULLparticipant_a_idVARCHAR(64) NOT NULLparticipant_b_typeTINYINT UNSIGNED NOT NULLparticipant_b_idVARCHAR(64) NOT NULLpair_keyVARCHAR(191) NOT NULLstatusTINYINT NOT NULL DEFAULT 1last_seqBIGINT UNSIGNED NOT NULL DEFAULT 0message_countBIGINT UNSIGNED NOT NULL DEFAULT 0last_message_previewVARCHAR(255) NULLlast_message_atDATETIME NULLext_jsonJSON NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
说明:
- 会话核心字段保持通用,不使用
child_low_id/child_high_id这类场景耦合命名。 conversation_type使用类型码,可扩展;新增会话类型优先通过“类型码 + 业务校验 +ext_json”扩展,无需改核心表。- 当前建议启用类型:
1-child_peer、2-parent_child。
约束建议:
UNIQUE(conversation_type, pair_key)INDEX idx_participant_a (participant_a_type, participant_a_id)INDEX idx_participant_b (participant_b_type, participant_b_id)INDEX idx_last_message_at (last_message_at)
4.2 IM 消息表 im_messages
建议字段:
idBIGINT UNSIGNED PK AUTO_INCREMENTconversation_idBIGINT UNSIGNED NOT NULLseqBIGINT UNSIGNED NOT NULLsender_typeTINYINT UNSIGNED NOT NULLsender_idVARCHAR(64) NOT NULLreceiver_typeTINYINT UNSIGNED NOT NULLreceiver_idVARCHAR(64) NOT NULLcontent_typeTINYINT UNSIGNED NOT NULLcontent_textMEDIUMTEXT NULLcontent_jsonJSON NULLmedia_file_keyVARCHAR(255) NULLmedia_duration_msINT UNSIGNED NULLmedia_mime_typeVARCHAR(64) NULLmedia_size_bytesBIGINT UNSIGNED NULLmedia_transcript_textTEXT NULLclient_msg_idVARCHAR(64) NULLsender_name_snapshotVARCHAR(64) NULLsender_avatar_snapshotVARCHAR(255) NULLreceiver_name_snapshotVARCHAR(64) NULLreceiver_avatar_snapshotVARCHAR(255) NULLext_jsonJSON NULLcreated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPdeleted_atDATETIME NULL
约束建议:
UNIQUE(conversation_id, seq)UNIQUE(conversation_id, client_msg_id)FOREIGN KEY (conversation_id) REFERENCES im_conversations(id) ON DELETE CASCADEINDEX idx_sender (sender_type, sender_id, created_at)INDEX idx_receiver (receiver_type, receiver_id, created_at)
4.2.1 文本与语音消息规则
content_type=1(text):content_text必填。content_type=2(audio):media_file_key必填,建议同时写入media_duration_ms、media_mime_type、media_size_bytes。content_type=3(image):media_file_key必填。content_type=4(json):content_json必填。- 语音转写内容可写入
media_transcript_text,用于检索与展示。 - 展示层优先使用
sender_name_snapshot/sender_avatar_snapshot与receiver_*_snapshot,减少联表查询。
4.3 位置表
child_location_current:
child_idBIGINT UNSIGNED PKdevice_idVARCHAR(64) NOT NULLcoord_typeVARCHAR(16) NOT NULL DEFAULT 'gcj02'latDECIMAL(10,7) NOT NULLlngDECIMAL(10,7) NOT NULLaccuracy_mINT UNSIGNED NULLaltitude_mDECIMAL(8,2) NULLspeed_mpsDECIMAL(8,2) NULLheading_degSMALLINT UNSIGNED NULLsourceTINYINT UNSIGNED NOT NULLbattery_pctTINYINT UNSIGNED NULLdevice_timeDATETIME NOT NULLserver_timeDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
child_location_history:
idBIGINT UNSIGNED PK AUTO_INCREMENTchild_idBIGINT UNSIGNED NOT NULLdevice_idVARCHAR(64) NOT NULLcoord_typeVARCHAR(16) NOT NULL DEFAULT 'gcj02'- 其余定位字段与
child_location_current对齐 created_atDATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
4.3.1 位置上报协议(统一格式)
- 上报字段:
device_id、child_id、coord_type、lat、lng、accuracy_m、source、device_time、battery_pct。 - 坐标系标准:统一使用
gcj02。 - 坐标格式:统一按
lat,lng(纬度在前,经度在后)传输和存储。 - 若设备侧提供
wgs84,服务端需先转换到gcj02后再入库;原始坐标可选写入ext_json备查。 - 写入规则:同事务写
child_location_history并覆盖child_location_current。
5. 核心业务流程
5.1 家长登录流程(微信小程序)
- 小程序调用
wx.login/Taro.login获取一次性code。 - 小程序调用后端
POST /auth/login,上传code,并可附带nickname/avatar_url作为资料字段。 - 后端使用服务端配置的
wechat_app_id + wechat_app_secret调用微信code2Session。 - 后端从微信响应中获取真实
openid、可选unionid,以及仅供服务端使用的session_key。 - 后端以
openid为唯一身份键 upsertparents;若本次拿到unionid且库中为空,则补写unionid。 - 后端签发本地
access_token(JWT)返回给小程序,后续业务接口仍统一使用本地 Bearer Token。 - 若微信换码失败、
openid缺失、code失效或被重复使用,登录直接失败,不创建本地用户。 session_key仅用于后续需要的微信敏感数据解密能力(如手机号);当前阶段不作为登录态主键,也不写入parents主表。
5.2 设备注册与鉴权
- 生产或后台调用
talkingq-url现有注册接口,写入device_auth。 - 手表请求携带
X-Device-ID与X-Device-Serial。 - 后端基于
device_auth鉴权通过后继续处理聊天/定位/绑定。
5.3 绑定流程(扫码 + 确认码)
- 家长登录后发起
bind/start,创建device_bind_sessions。 - 手表上报确认码,更新会话挑战信息。
- 家长提交确认码。
- 后端校验通过后开启事务。
- upsert
parent_child_relations。 - upsert
device_bindings(保证一设备一行、一小孩一行)。 - 追加
device_bind_history。 - 更新会话状态为
confirmed/consumed并提交事务。
5.4 聊天流程
- 设备 AI 对话仍走
conversation_histories + conversation_messages。 - 家长-小孩、小孩-小孩消息走
im_conversations + im_messages。 - IM 消息统一使用
sender_type/sender_id + receiver_type/receiver_id,不按场景拆专用发送者字段。 mini-program侧不再新增独立的chat_*消息表定义,统一以im_*为 IM 正表。- 发送接口必须使用
client_msg_id幂等。 - 查询前必须校验
parent_child_relations权限。 - 列表展示优先读取消息快照字段,降低跨表查询成本。
5.5 位置流程
- 手表上报定位写入
child_location_history。 - 同事务更新
child_location_current。 - 小程序查询当前定位读
child_location_current,轨迹读child_location_history。 - 地址解析使用腾讯位置服务;若解析失败,前端降级展示经纬度。
6. 统一约束与实现规则
- 所有设备引用字段统一使用
device_id VARCHAR(64),不再新增watch_device_id BIGINT。 - 设备主身份只认
device_auth.device_id。 - 跨主体通用 ID 字段统一采用字符串(如
participant_*_id、sender_id、receiver_id)。 - 所有业务表统一包含
created_at/updated_at(历史流水类可无updated_at)。 status用于业务态,is_active用于启停态;两者不混用。- 关键写路径(绑定确认、发消息、位置写入)必须事务化。
- 腾讯位置服务启用前提:必须配置合法
key,并完成域名/来源白名单与配额设置。 - 地图相关接口统一使用
gcj02和lat,lng;禁止混用wgs84/bd09直接入库。 - 小程序端只上送一次性
code,不得自行调用微信换取openid,更不得持有AppSecret。 code只用于调用微信code2Session,不得入库为openid、unionid或任何长期身份字段。session_key不返回前端,不写入parents主表;如需支持手机号等敏感数据解密,应服务端短期缓存并设置明确过期时间。nickname/avatar_url仅作资料字段,不能作为登录态、账号归并或权限判断依据。
7. 分阶段落地
- 第一阶段:落地微信登录链路,打通
Taro.login -> /auth/login -> code2Session -> parents -> JWT。 - 第二阶段:落地
parents/children/parent_child_relations/device_bind_*,打通绑定链路。 - 第三阶段:落地
im_conversations/im_messages,打通家长与小孩 IM。 - 第四阶段:落地
child_location_current/history,打通定位链路。 - 第五阶段:补充审计、告警、实时推送和运营能力。
8. 验收标准
- 家长可通过真实微信登录完成换码,并获取本地 Bearer Token。
parents.openid存储值来自微信返回的真实 OpenID,而不是前端code或测试标识。- 不修改
01-init.sql任一现有表,服务可正常启动。 - 设备鉴权、角色配置、OTA能力保持可用。
- 家长可完成扫码绑定、解绑、重绑,且历史保留。
- 家长可查看自己小孩与其他小孩完整双向消息。
- 小程序可查看小孩最新位置与历史轨迹。
9. 共享库落地规范
9.1 唯一数据库与唯一 Schema
mini-program与talkingq-url必须共用同一个 MySQL 数据库:talkingq。database/talkingq_shared_schema.sql是唯一 schema 真相源。mini-program/database/init.sql仅保留给历史上的“单服务独立开发”场景,不再作为共享部署的建库依据。
9.2 表归属(Table Ownership)
talkingq-url 拥有以下表的写入责任:
device_authdevice_configsconversation_historiesconversation_messagesrolesrole_languagesdevice_firmware_updatesystem_config
mini-program 拥有以下表的写入责任:
parentschildrenparent_child_relationsdevice_bindingsdevice_bind_sessionsdevice_bind_historydevice_settingscardsim_conversationsim_messageschild_location_currentchild_location_history
约束:
- 两个服务允许跨服务读取共享依赖表,但不允许跨服务写对方负责的主表。
mini-program只读device_auth,不直接承担设备注册主流程。talkingq-url不写parents/children/device_bindings/im_* /child_location_*。
9.3 共享字段与真相源
- 设备主身份统一以
device_auth.device_id为准。 - 设备与 AI 的历史消息统一以
conversation_histories + conversation_messages为准。 - 家长/儿童 IM 统一以
im_conversations + im_messages为准。 - 设备音量如需双端共享,以
device_configs.volume为真相源;device_settings.volume仅作小程序展示和兼容字段。
9.4 迁移原则
- 目标库固定为
talkingq。 - 迁移时以
talkingq-url当前使用的talkingq数据为准。 - 历史
mini_program库中的业务数据不并入talkingq,也不作为冲突解决依据。 - 迁移动作只做两件事:
- 在
talkingq上补齐mini-program所需扩展表。 - 将
mini-program服务配置切换到talkingq。
- 在
mini_program库可在切换验证完成后归档或删除,但不再作为运行时数据库。
9.5 启动与建表策略
- 共享部署下,服务启动默认只检查数据库连通性,不自动执行 ORM
create_all。 - 只有显式开启开发开关时,
mini-program才允许自动建表。 - 生产、联调、共享测试环境统一通过
database/talkingq_shared_schema.sql或正式 migration 执行建表与变更。