完善设备端体验与微信校验支持
This commit is contained in:
69
docs/architecture/01-system-overview.md
Normal file
69
docs/architecture/01-system-overview.md
Normal 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`
|
||||
85
docs/architecture/02-device-bootstrap.md
Normal file
85
docs/architecture/02-device-bootstrap.md
Normal 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`
|
||||
90
docs/architecture/03-binding-flow.md
Normal file
90
docs/architecture/03-binding-flow.md
Normal 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`
|
||||
74
docs/architecture/04-voice-and-im-flow.md
Normal file
74
docs/architecture/04-voice-and-im-flow.md
Normal 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`
|
||||
88
docs/architecture/05-location-flow.md
Normal file
88
docs/architecture/05-location-flow.md
Normal 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`
|
||||
75
docs/architecture/06-alarm-and-notification-flow.md
Normal file
75
docs/architecture/06-alarm-and-notification-flow.md
Normal 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`
|
||||
60
docs/architecture/07-deployment.md
Normal file
60
docs/architecture/07-deployment.md
Normal 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`
|
||||
20
docs/architecture/README.md
Normal file
20
docs/architecture/README.md
Normal 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.
|
||||
600
docs/architecture/index.html
Normal file
600
docs/architecture/index.html
Normal 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>
|
||||
Reference in New Issue
Block a user