# 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 执行建表与变更。