完善设备端体验与微信校验支持

This commit is contained in:
stu2not
2026-07-07 16:58:05 +08:00
parent b4fef6d2e0
commit 937e3023fc
38 changed files with 3377 additions and 51 deletions

View File

@@ -0,0 +1,69 @@
# System Overview
## Runtime Architecture
```mermaid
flowchart LR
subgraph WeChat["WeChat Ecosystem"]
Mini["banban-mini\nTaro WeChat mini-program"]
WxLogin["WeChat login / phone APIs"]
WxMP["WeChat Official Account\nnotifications and bind"]
end
subgraph Edge["Public Edge"]
Domain["banban.api.talkingq.com"]
Nginx["Nginx / TLS"]
end
subgraph Backend["talkingq-url FastAPI backend"]
Main["main.py\nFastAPI app + lifespan"]
AuthMW["banban auth middleware"]
BanbanRouter["banban routers\nchildren, devices, binding, chat,\nlocation, alarms, firmware, roles,\nwechat-mp"]
MqttSvc["TalkingQMQTTService"]
Scheduler["TaskScheduler\ncleanup and periodic tasks"]
StaticAssets["/assets static files"]
end
subgraph Data["Stateful Services"]
MySQL["MySQL\nshared banban schema"]
COS["Tencent COS\nmessage audio files"]
MQTT["MQTT broker"]
end
subgraph DeviceSide["Device Side"]
Device["TalkingQ device"]
NFC["NFC card"]
end
subgraph External["External Providers"]
MapSvc["Map / reverse geocoding provider"]
SMS["SMS provider"]
end
Mini -->|HTTPS /banban/*| Domain --> Nginx --> Main
Main --> AuthMW --> BanbanRouter
Main --> StaticAssets
Main --> Scheduler
Main --> MqttSvc
BanbanRouter --> MySQL
MqttSvc --> MQTT
Device -->|device/{id}/event,response| MQTT
MQTT -->|subscribed messages| MqttSvc
MqttSvc --> MySQL
Device --> NFC
BanbanRouter --> COS
MqttSvc --> COS
BanbanRouter --> WxLogin
BanbanRouter --> WxMP
BanbanRouter --> SMS
MqttSvc --> MapSvc
```
## Source Anchors
- Mini-program API base and services: `banban-mini/src/services/*`
- FastAPI startup and router registration: `talkingq-url/main.py`
- Banban route aggregation: `talkingq-url/banban/routers/*`
- MQTT service and device message handlers: `talkingq-url/handlers/mqtt_handler.py`
- Shared database schema: `database/talkingq_shared_schema.sql`
- Runtime database initialization: `talkingq-url/database/init_db.py`

View File

@@ -0,0 +1,85 @@
# Device Bootstrap Flow
## Purpose
Device bootstrap maps a factory IMEI to Banban's internal `device_id` and
`serial_number`. Devices do not need to know those IDs before first bootstrap.
## Flow
```mermaid
sequenceDiagram
autonumber
participant Factory as Factory Excel / import CSV
participant Mapping as device_imei_mapping
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Init as device_identity_initializer
participant Auth as device_auth
Factory->>Mapping: Insert imei, device_id, serial_number, status=pending
Device->>MQTT: Publish device/{imei}/event msg_id=012
MQTT->>Handler: Deliver identity init event
Handler->>Init: initialize_by_imei(imei)
Init->>Mapping: SELECT mapping by imei
alt mapping missing or incomplete
Init-->>Handler: DeviceIdentityInitializationError
Handler-->>MQTT: device/{imei}/event_resp status=failed
else mapping found
Init->>Auth: SELECT device_auth by device_id
alt no auth row
Init->>Auth: INSERT device_id, serial_number, is_active=1
else auth row exists and serial matches
Init->>Auth: UPDATE is_active=1
else serial mismatch
Init-->>Handler: device_auth serial_number conflict
Handler-->>MQTT: device/{imei}/event_resp status=failed
end
Init->>Mapping: UPDATE status=activated, activated_at=now
Handler-->>MQTT: device/{imei}/event_resp with device_id and device_sn
MQTT-->>Device: Receive assigned identity
end
```
## Data Model
```mermaid
erDiagram
device_imei_mapping {
int id PK
varchar imei UK
varchar device_id UK
varchar serial_number
varchar status
datetime activated_at
datetime created_at
datetime updated_at
}
device_auth {
int id PK
varchar device_id UK
varchar serial_number
varchar batch_id
tinyint is_active
datetime created_at
datetime updated_at
}
device_imei_mapping ||--o| device_auth : activates
```
## Current ID Style
```text
device_id: TalkingQ_ZHWK00002001
serial_number: TQ_ZHWK000000002001
```
## Source Anchors
- Bootstrap handler: `talkingq-url/handlers/mqtt_handler.py`
- Identity initializer: `talkingq-url/services/device_identity_initializer.py`
- Import script: `talkingq-url/scripts/import_device_imei_mapping.py`
- Tables: `database/talkingq_shared_schema.sql`

View File

@@ -0,0 +1,90 @@
# Binding Flow
## Device Binding
The mini-program binding flow validates an active `device_auth` row before it
creates a pending bind session. Final binding is completed after the device
returns an NFC card UUID.
```mermaid
sequenceDiagram
autonumber
participant Parent as Parent in mini-program
participant Mini as banban-mini bind page
participant API as /banban/bind APIs
participant Binding as BindingService
participant DAO as BindingDAO
participant Auth as device_auth
participant MQTT as TalkingQMQTTService
participant Device as TalkingQ device
participant Card as cards
participant Bindings as device_bindings
participant History as device_bind_history
Parent->>Mini: Scan QR or open bind URL
Mini->>API: start_bind(device_id, serial_number, child_id)
API->>Binding: start_bind(user_id, device_id, serial_number, child_id)
Binding->>Auth: get_device_auth(device_id)
alt device missing, inactive, or serial mismatch
Binding-->>API: BindingError
API-->>Mini: 4xx error
else device is bindable
Binding->>DAO: ensure no active device binding
Binding->>DAO: create device_bind_session
Binding->>MQTT: send_bind_nfc_command(msg_id=006)
MQTT-->>Device: device/{device_id}/command
API-->>Mini: bind_token, expires_at
end
Device->>MQTT: NFC response / card UUID
MQTT->>Binding: finalize_nfc_bind(device_id, card_uuid)
Binding->>DAO: load latest pending bind session
Binding->>Card: activate_card(card_uuid, device_id)
Binding->>Bindings: create active binding
Binding->>History: write bind audit history
Binding->>DAO: mark session completed
```
## Related Tables
```mermaid
erDiagram
parents ||--o{ children : owns
parents ||--o{ device_bindings : owns
children ||--o{ device_bindings : current_child
device_auth ||--o{ device_bindings : device
device_auth ||--o{ device_bind_sessions : pending_bind
device_auth ||--o{ cards : card_owner_device
device_bindings ||--o{ device_bind_history : audit
device_auth {
varchar device_id UK
varchar serial_number
tinyint is_active
}
device_bindings {
int binding_id PK
varchar device_id FK
int owner_user_id FK
int child_id FK
int status
}
device_bind_sessions {
int id PK
varchar bind_token UK
varchar device_id FK
int initiator_user_id
int target_child_id
int status
datetime expires_at
}
```
## Source Anchors
- Mini-program bind page: `banban-mini/src/pages/bind/index.tsx`
- Binding service: `talkingq-url/banban/service/binding.py`
- Binding DAO: `talkingq-url/banban/dao/binding.py`
- MQTT bind command handling: `talkingq-url/handlers/mqtt_handler.py`

View File

@@ -0,0 +1,74 @@
# Voice And IM Flow
## Parent Voice Message To Device
```mermaid
sequenceDiagram
autonumber
participant Parent as Parent
participant Mini as Chat detail page
participant API as /banban chat APIs
participant Audio as Upload / audio handling
participant COS as Tencent COS
participant IM as IM service / DAO
participant DB as im_conversations + im_messages
participant Pending as device_pending_voice_messages
participant MQTT as MQTT broker
participant Device as TalkingQ device
Parent->>Mini: Long press "press to leave message"
Mini->>Mini: Taro.getRecorderManager records mp3/aac
Mini->>API: upload voice file + metadata
API->>Audio: validate and store audio
Audio->>COS: upload message audio
COS-->>Audio: media_file_key / public URL
API->>IM: create parent voice IM message
IM->>DB: upsert conversation and insert message
IM->>Pending: create pending voice delivery row
API-->>Mini: message item with media URL
Device->>MQTT: device/{device_id}/event msg_id=005 NFC listen
MQTT->>API: MQTT handler receives event
API->>Pending: find pending voice for card/device
API-->>MQTT: device/{device_id}/event_resp with audio URL(s)
MQTT-->>Device: Play pending audio
API->>Pending: mark delivered / increment delivery count
```
## Device / Child Voice Into IM
```mermaid
flowchart TD
Device["Device short press / voice upload\nmsg_id=011 or compatibility path"]
MQTT["TalkingQMQTTService"]
Upload["Audio upload / archive service"]
Resolve["Resolve bound owner and child\nfrom device_bindings"]
IM["IM service"]
Tables["im_conversations\nim_messages"]
Mini["banban-mini chat list/detail"]
Device --> MQTT --> Upload --> Resolve --> IM --> Tables
Tables -->|GET chat APIs| Mini
```
## Audio Domain Notes
```mermaid
flowchart LR
Message["im_messages.media_file_key"]
Presenter["present_message_item / chat service"]
URL["mediaUrl returned to mini-program"]
Player["Taro InnerAudioContext"]
COS["COS downloadFile legal domain"]
Message --> Presenter --> URL --> Player
URL --> COS
```
## Source Anchors
- Chat detail recording/playback: `banban-mini/src/pages/chat/detail/index.tsx`
- Chat service client: `banban-mini/src/services/chat.ts`
- Backend IM service and DAO: `talkingq-url/banban/service/im.py`, `talkingq-url/banban/dao/im.py`
- Audio upload and COS: `talkingq-url/handlers/audio_file_handler.py`, `talkingq-url/banban/service/device_audio_cache.py`
- Pending voice table: `database/talkingq_shared_schema.sql`

View File

@@ -0,0 +1,88 @@
# Location Flow
## Device-Sourced Location
The mini-program location page displays the bound device's reported location.
It is not the same as reading the parent's phone location.
```mermaid
sequenceDiagram
autonumber
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Location as Location service
participant Current as child_location_current
participant History as child_location_history
participant Mini as banban-mini location page
participant API as /banban/devices/{device_id}/location
Device->>MQTT: msg_id=000 with latitude/longitude or GPS response
MQTT->>Handler: deliver device event/response
Handler->>Location: report_mqtt_device_location(...)
Location->>Location: resolve active child binding
Location->>Current: upsert latest location
Location->>History: insert location history point
Handler-->>MQTT: success event_resp
Mini->>API: GET current location
API->>Current: read current row for bound child/device
API-->>Mini: current location, accuracy, battery, address fields
Mini->>API: GET trajectory
API->>History: read recent/today points
API-->>Mini: trajectory points
```
## Data Model
```mermaid
erDiagram
device_auth ||--o{ child_location_current : device
children ||--o{ child_location_current : child
device_auth ||--o{ child_location_history : device
children ||--o{ child_location_history : child
device_bindings ||--o{ child_location_current : active_binding_source
child_location_current {
int id PK
int child_id FK
varchar device_id FK
decimal lat
decimal lng
varchar coord_type
decimal accuracy_m
int battery_pct
datetime device_time
datetime server_time
varchar address
}
child_location_history {
int id PK
int child_id FK
varchar device_id FK
decimal lat
decimal lng
varchar coord_type
datetime device_time
datetime server_time
}
```
## Review / Audit Wording
```text
Location is sourced from the bound child device and shown to the parent for
device tracking, trajectory, and alarm context. It is not used to get the
parent phone's current location unless a separate phone-location feature is
added later.
```
## Source Anchors
- Mini-program location page: `banban-mini/src/pages/location/index.tsx`
- Location API client: `banban-mini/src/services/location.ts`
- Device location routes: `talkingq-url/banban/routers/devices.py`
- Location service: `talkingq-url/banban/service/location.py`
- MQTT persistence: `talkingq-url/handlers/mqtt_handler.py`

View File

@@ -0,0 +1,75 @@
# Alarm And Notification Flow
## Device Alarm To User Notification
```mermaid
sequenceDiagram
autonumber
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Alarm as Alarm service
participant Location as Location snapshot
participant DB as device_alarm_events
participant Banner as system banner polling
participant Mini as banban-mini
participant WxMP as WeChat Official Account
participant SMS as SMS provider
Device->>MQTT: alarm event msg_id=010
MQTT->>Handler: deliver alarm payload
Handler->>Alarm: create alarm event
Alarm->>Location: attach current location snapshot if available
Alarm->>DB: insert device_alarm_events
Alarm->>WxMP: schedule/send template notification if bound
Alarm->>SMS: send SMS if phone notification is enabled
Banner->>DB: poll latest/new alarm
Mini->>Banner: show banner / alarm history on device home
Handler-->>MQTT: alarm event_resp
```
## Supporting Notification Bind
```mermaid
flowchart TD
Mini["banban-mini management page"]
Phone["WeChat phone authorization"]
WxBind["WeChat MP bind start/status"]
Backend["talkingq-url wechat-mp routers/services"]
Parent["parents / parent_wechat_* tables"]
Notify["wechat_mp_notification_service"]
Mini --> Phone --> Backend --> Parent
Mini --> WxBind --> Backend --> Parent
Parent --> Notify
```
## Key Tables
```mermaid
erDiagram
device_auth ||--o{ device_alarm_events : reports
children ||--o{ device_alarm_events : alarm_owner
parents ||--o{ parent_wechat_accounts : mp_account
parents ||--o{ parent_wechat_identities : wx_identity
parents ||--o{ wechat_mp_bind_states : bind_state
device_alarm_events {
int id PK
varchar device_id FK
int child_id FK
varchar source_msg_id
decimal location_lat
decimal location_lng
datetime location_updated_at
datetime created_at
}
```
## Source Anchors
- Alarm schema: `database/talkingq_shared_schema.sql`
- Alarm location refresh tests: `talkingq-url/tests/test_alarm_gps_location_refresh.py`
- System banner client: `banban-mini/src/services/system-banner.ts`
- Device home alarm UI: `banban-mini/src/pages/device/index.tsx`
- WeChat MP notification service: `talkingq-url/banban/service/wechat_mp_notification.py`

View File

@@ -0,0 +1,60 @@
# Deployment
## Public Deployment Shape
```mermaid
flowchart TD
User["WeChat mini-program user"]
Domain["banban.api.talkingq.com"]
Nginx["Nginx container\nTLS + reverse proxy + static verify files"]
FastAPI["talkingq-url FastAPI process\n/home/ubuntu/banban-backend"]
MySQL["1Panel MySQL container\nMySQL 8.x"]
MQTT["Configured MQTT broker"]
COS["Tencent COS"]
Certbot["certbot container\nLet's Encrypt renewals"]
User -->|HTTPS| Domain --> Nginx --> FastAPI
FastAPI --> MySQL
FastAPI --> MQTT
FastAPI --> COS
Certbot --> Nginx
```
## Startup Responsibilities
```mermaid
sequenceDiagram
autonumber
participant Proc as FastAPI process
participant Init as init_db
participant Scheduler as TaskScheduler
participant MQTT as TalkingQMQTTService
participant Roles as role_manager
participant Firmware as firmware_scanner
Proc->>Init: initialize and migrate database tables
Proc->>Scheduler: start periodic tasks
Proc->>MQTT: connect to configured broker
Proc->>Roles: initialize role definitions
Proc->>Firmware: scan firmware files
Proc-->>Proc: serve API routes and assets
```
## Deployment Notes
- The mini-program production API base should resolve to
`https://banban.api.talkingq.com`.
- WeChat legal domains must include the API host and any audio/COS download
domains used by `mediaUrl`.
- MySQL table shape should be checked against the live database before direct
data work, because deployment state can drift from local schema files.
- Device identity data should normally enter through `device_imei_mapping`;
`device_auth` is activated by device bootstrap unless a deliberate pre-activation
path is required.
## Source Anchors
- Docker/Nginx assets: `talkingq-url/docker-compose.yml`, `talkingq-url/nginx.conf`
- Runtime startup: `talkingq-url/main.py`
- Database initialization: `talkingq-url/database/init_db.py`
- Mini-program API config: `banban-mini/config/prod.js`, `banban-mini/src/config/env.ts`

View File

@@ -0,0 +1,20 @@
# Banban Architecture Diagrams
This folder contains repo-backed Mermaid diagrams for the Banban mini-program,
backend, device, database, and notification flows.
## Diagrams
- [01 System Overview](./01-system-overview.md)
- [02 Device Bootstrap](./02-device-bootstrap.md)
- [03 Binding Flow](./03-binding-flow.md)
- [04 Voice And IM Flow](./04-voice-and-im-flow.md)
- [05 Location Flow](./05-location-flow.md)
- [06 Alarm And Notification Flow](./06-alarm-and-notification-flow.md)
- [07 Deployment](./07-deployment.md)
## Maintenance Notes
- Keep diagrams tied to current code paths and table names.
- Prefer several narrow diagrams over one overloaded diagram.
- Update the source anchors when moving routers, services, handlers, or tables.

View File

@@ -0,0 +1,600 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Banban Architecture Diagrams</title>
<style>
:root {
color-scheme: light;
--bg: #f6f7f9;
--surface: #ffffff;
--text: #1f2933;
--muted: #6b7280;
--border: #d9dee7;
--accent: #2563eb;
--accent-soft: #e8f0ff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background: var(--bg);
color: var(--text);
}
header {
position: sticky;
top: 0;
z-index: 10;
border-bottom: 1px solid var(--border);
background: rgba(255, 255, 255, 0.94);
backdrop-filter: blur(10px);
}
.header-inner {
max-width: 1280px;
margin: 0 auto;
padding: 16px 24px 12px;
}
h1 {
margin: 0 0 10px;
font-size: 22px;
line-height: 1.25;
}
.subtitle {
margin: 0;
color: var(--muted);
font-size: 13px;
}
nav {
display: flex;
gap: 8px;
margin-top: 14px;
overflow-x: auto;
padding-bottom: 2px;
}
nav a {
flex: 0 0 auto;
border: 1px solid var(--border);
border-radius: 6px;
padding: 7px 10px;
color: var(--text);
background: var(--surface);
font-size: 13px;
text-decoration: none;
}
nav a:hover {
border-color: var(--accent);
color: var(--accent);
}
main {
max-width: 1280px;
margin: 0 auto;
padding: 20px 24px 48px;
}
section {
margin: 0 0 24px;
padding: 18px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
}
h2 {
margin: 0 0 6px;
font-size: 18px;
}
h3 {
margin: 22px 0 10px;
color: var(--muted);
font-size: 14px;
font-weight: 650;
}
.section-note {
margin: 0 0 12px;
color: var(--muted);
font-size: 13px;
}
.diagram {
overflow: auto;
width: 100%;
margin: 0;
padding: 14px;
border: 1px solid var(--border);
border-radius: 8px;
background: #fbfcfe;
}
.diagram + .diagram {
margin-top: 16px;
}
.source {
margin-top: 14px;
padding: 10px 12px;
border-radius: 6px;
background: var(--accent-soft);
color: #1e3a8a;
font-size: 13px;
line-height: 1.7;
}
.source code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
}
.fallback {
margin: 12px 0 0;
color: var(--muted);
font-size: 13px;
}
@media (max-width: 760px) {
.header-inner,
main {
padding-left: 14px;
padding-right: 14px;
}
section {
padding: 14px;
}
}
</style>
</head>
<body>
<header>
<div class="header-inner">
<h1>Banban Architecture Diagrams</h1>
<p class="subtitle">
Rendered Mermaid viewer for the architecture docs. Requires internet
access for the Mermaid CDN when opened as a local file.
</p>
<nav aria-label="Architecture sections">
<a href="#overview">System Overview</a>
<a href="#bootstrap">Device Bootstrap</a>
<a href="#binding">Binding</a>
<a href="#voice">Voice / IM</a>
<a href="#location">Location</a>
<a href="#alarm">Alarm / Notification</a>
<a href="#deployment">Deployment</a>
</nav>
</div>
</header>
<main>
<section id="overview">
<h2>01 System Overview</h2>
<p class="section-note">Public edge, mini-program, backend, database, MQTT, device, and external providers.</p>
<h3>Runtime Architecture</h3>
<pre class="mermaid diagram">
flowchart LR
subgraph WeChat["WeChat Ecosystem"]
Mini["banban-mini\nTaro WeChat mini-program"]
WxLogin["WeChat login / phone APIs"]
WxMP["WeChat Official Account\nnotifications and bind"]
end
subgraph Edge["Public Edge"]
Domain["banban.api.talkingq.com"]
Nginx["Nginx / TLS"]
end
subgraph Backend["talkingq-url FastAPI backend"]
Main["main.py\nFastAPI app + lifespan"]
AuthMW["banban auth middleware"]
BanbanRouter["banban routers\nchildren, devices, binding, chat,\nlocation, alarms, firmware, roles,\nwechat-mp"]
MqttSvc["TalkingQMQTTService"]
Scheduler["TaskScheduler\ncleanup and periodic tasks"]
StaticAssets["/assets static files"]
end
subgraph Data["Stateful Services"]
MySQL["MySQL\nshared banban schema"]
COS["Tencent COS\nmessage audio files"]
MQTT["MQTT broker"]
end
subgraph DeviceSide["Device Side"]
Device["TalkingQ device"]
NFC["NFC card"]
end
subgraph External["External Providers"]
MapSvc["Map / reverse geocoding provider"]
SMS["SMS provider"]
end
Mini -->|HTTPS /banban/*| Domain --> Nginx --> Main
Main --> AuthMW --> BanbanRouter
Main --> StaticAssets
Main --> Scheduler
Main --> MqttSvc
BanbanRouter --> MySQL
MqttSvc --> MQTT
Device -->|device/{id}/event,response| MQTT
MQTT -->|subscribed messages| MqttSvc
MqttSvc --> MySQL
Device --> NFC
BanbanRouter --> COS
MqttSvc --> COS
BanbanRouter --> WxLogin
BanbanRouter --> WxMP
BanbanRouter --> SMS
MqttSvc --> MapSvc
</pre>
<div class="source">Source: <code>talkingq-url/main.py</code>, <code>banban-mini/src/services/*</code>, <code>database/talkingq_shared_schema.sql</code></div>
</section>
<section id="bootstrap">
<h2>02 Device Bootstrap</h2>
<p class="section-note">How IMEI mappings become active Banban device identities.</p>
<h3>Bootstrap Flow</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Factory as Factory Excel / import CSV
participant Mapping as device_imei_mapping
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Init as device_identity_initializer
participant Auth as device_auth
Factory->>Mapping: Insert imei, device_id, serial_number, status=pending
Device->>MQTT: Publish device/{imei}/event msg_id=012
MQTT->>Handler: Deliver identity init event
Handler->>Init: initialize_by_imei(imei)
Init->>Mapping: SELECT mapping by imei
alt mapping missing or incomplete
Init-->>Handler: DeviceIdentityInitializationError
Handler-->>MQTT: device/{imei}/event_resp status=failed
else mapping found
Init->>Auth: SELECT device_auth by device_id
alt no auth row
Init->>Auth: INSERT device_id, serial_number, is_active=1
else auth row exists and serial matches
Init->>Auth: UPDATE is_active=1
else serial mismatch
Init-->>Handler: device_auth serial_number conflict
Handler-->>MQTT: device/{imei}/event_resp status=failed
end
Init->>Mapping: UPDATE status=activated, activated_at=now
Handler-->>MQTT: device/{imei}/event_resp with device_id and device_sn
MQTT-->>Device: Receive assigned identity
end
</pre>
<h3>Data Model</h3>
<pre class="mermaid diagram">
erDiagram
device_imei_mapping {
int id PK
varchar imei UK
varchar device_id UK
varchar serial_number
varchar status
datetime activated_at
datetime created_at
datetime updated_at
}
device_auth {
int id PK
varchar device_id UK
varchar serial_number
varchar batch_id
tinyint is_active
datetime created_at
datetime updated_at
}
device_imei_mapping ||--o| device_auth : activates
</pre>
<div class="source">Source: <code>talkingq-url/services/device_identity_initializer.py</code>, <code>talkingq-url/handlers/mqtt_handler.py</code></div>
</section>
<section id="binding">
<h2>03 Binding Flow</h2>
<p class="section-note">How a parent binds an authenticated device to a child through NFC confirmation.</p>
<h3>Device Binding</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Parent as Parent in mini-program
participant Mini as banban-mini bind page
participant API as /banban/bind APIs
participant Binding as BindingService
participant DAO as BindingDAO
participant Auth as device_auth
participant MQTT as TalkingQMQTTService
participant Device as TalkingQ device
participant Card as cards
participant Bindings as device_bindings
participant History as device_bind_history
Parent->>Mini: Scan QR or open bind URL
Mini->>API: start_bind(device_id, serial_number, child_id)
API->>Binding: start_bind(user_id, device_id, serial_number, child_id)
Binding->>Auth: get_device_auth(device_id)
alt device missing, inactive, or serial mismatch
Binding-->>API: BindingError
API-->>Mini: 4xx error
else device is bindable
Binding->>DAO: ensure no active device binding
Binding->>DAO: create device_bind_session
Binding->>MQTT: send_bind_nfc_command(msg_id=006)
MQTT-->>Device: device/{device_id}/command
API-->>Mini: bind_token, expires_at
end
Device->>MQTT: NFC response / card UUID
MQTT->>Binding: finalize_nfc_bind(device_id, card_uuid)
Binding->>DAO: load latest pending bind session
Binding->>Card: activate_card(card_uuid, device_id)
Binding->>Bindings: create active binding
Binding->>History: write bind audit history
Binding->>DAO: mark session completed
</pre>
<h3>Related Tables</h3>
<pre class="mermaid diagram">
erDiagram
parents ||--o{ children : owns
parents ||--o{ device_bindings : owns
children ||--o{ device_bindings : current_child
device_auth ||--o{ device_bindings : device
device_auth ||--o{ device_bind_sessions : pending_bind
device_auth ||--o{ cards : card_owner_device
device_bindings ||--o{ device_bind_history : audit
</pre>
<div class="source">Source: <code>banban-mini/src/pages/bind/index.tsx</code>, <code>talkingq-url/banban/service/binding.py</code></div>
</section>
<section id="voice">
<h2>04 Voice And IM Flow</h2>
<p class="section-note">Parent voice upload, IM persistence, COS audio, and device delivery.</p>
<h3>Parent Voice Message To Device</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Parent as Parent
participant Mini as Chat detail page
participant API as /banban chat APIs
participant Audio as Upload / audio handling
participant COS as Tencent COS
participant IM as IM service / DAO
participant DB as im_conversations + im_messages
participant Pending as device_pending_voice_messages
participant MQTT as MQTT broker
participant Device as TalkingQ device
Parent->>Mini: Long press "press to leave message"
Mini->>Mini: Taro.getRecorderManager records mp3/aac
Mini->>API: upload voice file + metadata
API->>Audio: validate and store audio
Audio->>COS: upload message audio
COS-->>Audio: media_file_key / public URL
API->>IM: create parent voice IM message
IM->>DB: upsert conversation and insert message
IM->>Pending: create pending voice delivery row
API-->>Mini: message item with media URL
Device->>MQTT: device/{device_id}/event msg_id=005 NFC listen
MQTT->>API: MQTT handler receives event
API->>Pending: find pending voice for card/device
API-->>MQTT: device/{device_id}/event_resp with audio URL(s)
MQTT-->>Device: Play pending audio
API->>Pending: mark delivered / increment delivery count
</pre>
<h3>Device / Child Voice Into IM</h3>
<pre class="mermaid diagram">
flowchart TD
Device["Device short press / voice upload\nmsg_id=011 or compatibility path"]
MQTT["TalkingQMQTTService"]
Upload["Audio upload / archive service"]
Resolve["Resolve bound owner and child\nfrom device_bindings"]
IM["IM service"]
Tables["im_conversations\nim_messages"]
Mini["banban-mini chat list/detail"]
Device --> MQTT --> Upload --> Resolve --> IM --> Tables
Tables -->|GET chat APIs| Mini
</pre>
<h3>Audio Domain</h3>
<pre class="mermaid diagram">
flowchart LR
Message["im_messages.media_file_key"]
Presenter["present_message_item / chat service"]
URL["mediaUrl returned to mini-program"]
Player["Taro InnerAudioContext"]
COS["COS downloadFile legal domain"]
Message --> Presenter --> URL --> Player
URL --> COS
</pre>
<div class="source">Source: <code>banban-mini/src/pages/chat/detail/index.tsx</code>, <code>talkingq-url/banban/service/im.py</code></div>
</section>
<section id="location">
<h2>05 Location Flow</h2>
<p class="section-note">Device-sourced current location and trajectory shown in the mini-program.</p>
<h3>Device-Sourced Location</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Location as Location service
participant Current as child_location_current
participant History as child_location_history
participant Mini as banban-mini location page
participant API as /banban/devices/{device_id}/location
Device->>MQTT: msg_id=000 with latitude/longitude or GPS response
MQTT->>Handler: deliver device event/response
Handler->>Location: report_mqtt_device_location(...)
Location->>Location: resolve active child binding
Location->>Current: upsert latest location
Location->>History: insert location history point
Handler-->>MQTT: success event_resp
Mini->>API: GET current location
API->>Current: read current row for bound child/device
API-->>Mini: current location, accuracy, battery, address fields
Mini->>API: GET trajectory
API->>History: read recent/today points
API-->>Mini: trajectory points
</pre>
<h3>Data Model</h3>
<pre class="mermaid diagram">
erDiagram
device_auth ||--o{ child_location_current : device
children ||--o{ child_location_current : child
device_auth ||--o{ child_location_history : device
children ||--o{ child_location_history : child
device_bindings ||--o{ child_location_current : active_binding_source
</pre>
<div class="source">Source: <code>banban-mini/src/pages/location/index.tsx</code>, <code>talkingq-url/banban/service/location.py</code></div>
</section>
<section id="alarm">
<h2>06 Alarm And Notification Flow</h2>
<p class="section-note">Device alarm persistence and user-visible notifications.</p>
<h3>Device Alarm To User Notification</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Device as TalkingQ device
participant MQTT as MQTT broker
participant Handler as TalkingQMQTTService
participant Alarm as Alarm service
participant Location as Location snapshot
participant DB as device_alarm_events
participant Banner as system banner polling
participant Mini as banban-mini
participant WxMP as WeChat Official Account
participant SMS as SMS provider
Device->>MQTT: alarm event msg_id=010
MQTT->>Handler: deliver alarm payload
Handler->>Alarm: create alarm event
Alarm->>Location: attach current location snapshot if available
Alarm->>DB: insert device_alarm_events
Alarm->>WxMP: schedule/send template notification if bound
Alarm->>SMS: send SMS if phone notification is enabled
Banner->>DB: poll latest/new alarm
Mini->>Banner: show banner / alarm history on device home
Handler-->>MQTT: alarm event_resp
</pre>
<h3>Supporting Notification Bind</h3>
<pre class="mermaid diagram">
flowchart TD
Mini["banban-mini management page"]
Phone["WeChat phone authorization"]
WxBind["WeChat MP bind start/status"]
Backend["talkingq-url wechat-mp routers/services"]
Parent["parents / parent_wechat_* tables"]
Notify["wechat_mp_notification_service"]
Mini --> Phone --> Backend --> Parent
Mini --> WxBind --> Backend --> Parent
Parent --> Notify
</pre>
<h3>Key Tables</h3>
<pre class="mermaid diagram">
erDiagram
device_auth ||--o{ device_alarm_events : reports
children ||--o{ device_alarm_events : alarm_owner
parents ||--o{ parent_wechat_accounts : mp_account
parents ||--o{ parent_wechat_identities : wx_identity
parents ||--o{ wechat_mp_bind_states : bind_state
</pre>
<div class="source">Source: <code>banban-mini/src/services/system-banner.ts</code>, <code>talkingq-url/banban/service/wechat_mp_notification.py</code></div>
</section>
<section id="deployment">
<h2>07 Deployment</h2>
<p class="section-note">Public API host, reverse proxy, backend process, database, MQTT, COS, and certificate renewal.</p>
<h3>Public Deployment Shape</h3>
<pre class="mermaid diagram">
flowchart TD
User["WeChat mini-program user"]
Domain["banban.api.talkingq.com"]
Nginx["Nginx container\nTLS + reverse proxy + static verify files"]
FastAPI["talkingq-url FastAPI process\n/home/ubuntu/banban-backend"]
MySQL["1Panel MySQL container\nMySQL 8.x"]
MQTT["Configured MQTT broker"]
COS["Tencent COS"]
Certbot["certbot container\nLet's Encrypt renewals"]
User -->|HTTPS| Domain --> Nginx --> FastAPI
FastAPI --> MySQL
FastAPI --> MQTT
FastAPI --> COS
Certbot --> Nginx
</pre>
<h3>Startup Responsibilities</h3>
<pre class="mermaid diagram">
sequenceDiagram
autonumber
participant Proc as FastAPI process
participant Init as init_db
participant Scheduler as TaskScheduler
participant MQTT as TalkingQMQTTService
participant Roles as role_manager
participant Firmware as firmware_scanner
Proc->>Init: initialize and migrate database tables
Proc->>Scheduler: start periodic tasks
Proc->>MQTT: connect to configured broker
Proc->>Roles: initialize role definitions
Proc->>Firmware: scan firmware files
Proc-->>Proc: serve API routes and assets
</pre>
<div class="source">Source: <code>talkingq-url/docker-compose.yml</code>, <code>talkingq-url/nginx.conf</code>, <code>talkingq-url/main.py</code></div>
</section>
<p class="fallback">
If diagrams do not render, open this file with network access enabled or
render the Markdown files in a Mermaid-capable viewer such as GitHub,
GitLab, VS Code with Mermaid support, or Mermaid Live Editor.
</p>
</main>
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({
startOnLoad: true,
securityLevel: "loose",
theme: "default",
flowchart: {
htmlLabels: true,
curve: "basis"
},
sequence: {
mirrorActors: false
}
});
</script>
</body>
</html>