ST广播系统 — 开发说明文档(对外版)

API接口文档

最后更新: 2026-08-11 · 适用版本: v2.28.0 (Electron) / v2.0.33 (Web/Android 应用内)

本文档面向开发者,涵盖:

  1. 整体架构与平台差异
  2. HTTP API 接口完整清单(认证、设备、分组、任务、媒体、报警、播放日志、TTS、电台、计费等)
  3. 实时喊话 WebSocket 协议(含握手、音频帧格式、关闭流程)
  4. 客户端本地存储与平台检测
  5. 调试与常见问题

一、整体架构

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│   Web 浏览器    │ │  Electron 客户端 │ │  Android APK    │
│ (Vue3 + Vite)  │ │ (Vue3 + Vite +  │ │ (Vue3 + Vite +  │
│                 │ │  Capacitor)      │ │  Capacitor)     │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
         │                   │                   │
   HTTP / WS           HTTP / WS          HTTP / WS
   (via /api 代理)     (直连)            (直连, Capacitor HTTP)
         │                   │                   │
         └───────────────────┼───────────────────┘
                             │
                  ┌──────────▼──────────┐
                  │  后端 Node.js 服务  │
                  │  127.0.0.1:12080    │
                  │  (仓库不包含)        │
                  └─────────────────────┘

平台差异

能力 Web Electron Android
HTTP API
WebSocket 喊话
UDP 设备局域网扫描 ❌ 浏览器无 UDP ✅ Electron IPC
本地存储 localStorage localStorage + 主进程 JSON Capacitor Preferences
客户端登录(输入服务器地址)
单声道/立体声协议 单声道 单声道 单声道
麦克风采集 Web Audio + ScriptProcessor 同左(WebView 内核) 同左

音频参数(3 个构建版本,3 端通用)

版本 采样率 码率 声道
web-16k-stereo-16k / 16K 16K 16kHz 16 kbps 单声道
web-16k-stereo-32k / 16K 32K 16kHz 32 kbps 单声道
web-44.1k-stereo-64k / 44.1K 64K 44.1kHz 64 kbps 单声道

重要:协议必须单声道。服务器、原始 demo、uni-app2 老项目都是单声道。立体声编码服务器不认,会导致推空数据。


二、HTTP API

2.1 基础约定

  • 后端地址http://127.0.0.1:12080(开发)/ http://192.168.0.222:12080(仓库示例)
  • 方法:基本全为 POST
  • 请求格式multipart/form-data(FormData)
  • 数据格式:UTF-8
  • Token 双重传递(必须同时):
    1. Header: Authorization: Bearer {token}
    2. FormData: token={token}
  • 自动注入:请求拦截器自动追加 id(用户ID)和 token 到 FormData
  • 基础 URL 切换(客户端模式):
    • Web:Vite 代理 /user/api → 后端
    • Electron/Android:先调用 localStorage.getItem('serverHost') 作为基础 URL,空则走相对路径

2.2 通用返回格式

// 通用
{ res: boolean, info?: string, msg?: string }
// 列表型(带 data 包装)
{ res: boolean, data: [...], list: [...], results: [...] }
// 登录型
{ res: true, data: { id, user_name, nickname, token, ... } }

res: true 成功;res: false 失败,查看 infomsg


2.3 认证模块

接口 方法 备注
/user/fnkukei/gtoken POST 登录(username + passwd)
/user/checkin POST 登录验证
/user/logout POST 登出
/user/info GET 当前用户信息

登录接口(/user/fnkukei/gtoken)特殊:返回 data 字段包含 idtokenuser_namenicknamemaskmaster_idplay_leveluser_level 等,前端存到 Pinia useUserStore + localStorage。


2.4 设备管理

接口 方法 参数 用途
/user/listdev POST 设备列表(字段:id, devname, device_seed, status, vol, voltage, debug, updatetime, changetime
/user/fnckukei/listmydevice POST 我的设备列表(results 字段)
/user/olist POST 设备列表(list 字段)
/user/deviceadd POST name, sn 添加设备(sn = 序列号|密钥)
/user/deldevs POST snid 删除设备
/user/redevname POST devid, newname 重命名
/user/resn POST devid, newsn 修改 SN(32 位)
/user/sndebug POST sn, dsv (1=开/2=关) 设备调试/提示音
/user/editvols POST vol(1-63), snlist(` `分隔)
/user/fnkukei/batchsetvolume POST vol, snlist 批量调音量

关键返回字段/user/fnckukei/listmydevice):

{
  "id": 35,
  "devname": "123123",
  "device_name": "MTIzMTIz",        // base64 编码(注意)
  "device_seed": "b14a70bf...",     // 设备 SN/MAC
  "status": 1,                       // 1=在线 2=播放中 0=离线 -1=从未上线 -2=过期
  "vol": 30,
  "voltage": 85,
  "debug": 0,
  "updatetime": "...",
  "changetime": "...",               // ★ 最后更新时间(注意是 changetime)
  "kind": 0,
  "device_type": 0,
  "iswarning": 0
}

device_name 可能是 base64 编码(decodeURIComponent(escape(atob(s)) 解码),前端已封装 decodeDeviceName


2.5 分组管理

接口 方法 参数 用途
/user/addgname POST gname, snlist 添加分组
/user/listdetailgroups POST 获取分组列表
/user/fnckukei/getonegdevs POST gname 获取分组设备
/user/editgdevs POST gname, snlist 编辑分组设备
/user/delgroup POST gname 删除分组

2.6 任务/定时播放

接口 方法 参数 用途
/user/tmpstartpl POST taskary 开始播放(任务数组)
/user/tmpstoppl POST taskid 停止任务
/user/tmplistpl POST 获取播放列表
/user/fnckukei/addziuser POST ziusername, zipasswd, nickname, beizhu, level, mask 添加子账号(mask 40 位)
/user/fnckukei/listziuser POST 子账号列表
/user/fnckukei/info_edit_listziuser POST 子账号详情/编辑
/user/fnckukei/delziuser POST id 删除子账号
/user/fnckukei/changezimask POST userid, zdex, zcase 修改子账号权限位
/user/fnckukei/changezidevices POST userid, snlist 修改子账号可见设备
/user/fnckukei/newpasswdnick POST id, newpass 子账号改密码

mask 权限位(40 位字符串,每位 0/1):

  • 位 0 = 可创建子账户
  • 位 1 = 可添加/删除设备
  • 位 2 = 可上传文件

2.7 媒体管理

接口 方法 参数 用途
/user/listfile POST 文件列表
/user/fnckukei/listmydevice (复用) / / 设备列表
/user/swapupload POST file, sn 交换/上传文件
TTS 购买 POST 文字转语音购买支付(5次/2元)

2.8 报警系统

接口 方法 参数 用途
/user/fnckukei/listmydevice POST 报警设备列表
/user/fnkukei/listzrdevs POST 报警注册设备列表
/warningsetauto.php POST 自动报警设置
/Warningeableauto.php POST enable (1/0) 启用/禁用自动报警
/user/fnckukei/wfilelistrg POST 报警文件分组列表
/user/fnckukei/wplantadd POST 报警方案新增
/user/fnckukei/getwplantlist POST 报警方案列表
/user/fnckukei/rmwplant POST id 删除报警方案
/user/fnckukei/wpdevicehistory POST sn 报警设备历史

2.9 播放日志

接口 方法 参数 用途
/user/fnckukei/queryLogs POST index, count, findex/bindex 分页查询日志
/user/fnckukei/clearLogs POST 清空日志

2.10 计费资料

接口 方法 参数 用途
/user/fnckukei/getBilling POST 获取计费资料
/user/fnckukei/saveBilling POST 表单数据 保存计费资料

2.11 电台/TTS

接口 方法 参数 用途
/user/fnckukei/tts POST text 文字转语音
/user/fnckukei/radio POST url 电台广播

2.12 客户端登录(Electron/Android 特有)

Electron/Android 不和 Web 一样有 Vite 代理,需要用户手动输入服务器地址

  1. 用户在 ClientLogin 页输入 serverHost(如 http://192.168.0.222:12080
  2. 存入 localStorage.serverHost
  3. axios baseURL 优先读 serverHost,空则走相对路径

三、实时喊话 WebSocket 协议

协议基于原始 demo(WEBsocketDemo/4GYX/1.html + recordmp3.js),单声道 PCM → lame.js MP3 → base64 → WebSocket。

3.1 流程图

┌──────────┐                              ┌──────────┐
│  浏览器  │                              │  服务器  │
│ Web Audio│                              │ WS 23020 │
└────┬─────┘                              └────┬─────┘
     │ 1. WS connect ws://server:23020          │
     │─────────────────────────────────────────>│
     │ 2. WS open (连接成功)                    │
     │<─────────────────────────────────────────│
     │ 3. 发送握手报文 (JSON, 长度前缀)         │
     │─────────────────────────────────────────>│
     │ 4. 等待服务器返回 'WELL' (校验成功)      │
     │<─────────────────────────────────────────│
     │ 5. 麦克风采集 → PCM → Worker → MP3       │
     │    → base64 → ws.send() (循环)          │
     │─────────────────────────────────────────>│
     │ 6. 停止推流 → ws.send('CLOSE')          │
     │─────────────────────────────────────────>│
     │ 7. ws.close()                            │
     │─────────────────────────────────────────>│

3.2 握手协议(步骤 3)

格式{json长度}\n{json内容}

示例:208\n{"Meport":6002,...}

{
  "Meport": 6002,        // 跨服端口号
  "Umagic": 89686,        // 快服句柄随机数(10-99999)
  "userid": 6,            // 用户 ID(也作为 "id" 字段)
  "id": 6,                // 同 userid
  "token": "aece294b...", // 用户登录 token
  "Umask": "84d3def4...",  // 随机 32 字符串(快服填)
  "plevel": 9,            // 播放等级(1~9,9 最高)
  "ulevel": 600,          // 用户间等级(1~600),冲突时高优先级切低
  "snlist": ["7f31ad..."], // 目标设备 SN 数组
  "cmd": "PLAYLIST",      // 固定值
  "Meip": "127.0.0.1"     // 节点服务器 IP
}

前端发送实现(useBroadcast.ts:183):

const headerStr = JSON.stringify(headerObj)
ws.send(headerStr.length + '\n' + headerStr)

3.3 服务器响应(步骤 4)

校验成功返回 WELL(4 字节文本),前端 ws.onmessage 监听:

if (data.trim() === 'WELL') {
  // 握手成功,开始推流
} else {
  // 握手失败
}

10 秒超时未收到 WELL 则判定失败。

3.4 音频帧协议(步骤 5)

3.4.1 音频参数

参数 16K 16K 版本 16K 32K 版本 44.1K 64K 版本
采样率 16000 Hz 16000 Hz 44100 Hz
码率 16 kbps 32 kbps 64 kbps
声道 1(单声道) 1(单声道) 1(单声道)
MP3 编码器 Mp3Encoder(1, ...) Mp3Encoder(1, ...) Mp3Encoder(1, ...)

3.4.2 采集 → 编码 → 发送管线

麦克风 → MediaStream
       → AudioContext (sampleRate = audioContext.sampleRate, 自适应)
       → MediaStreamSource
       → GainNode (采集音量 0~200)
       → ScriptProcessor(16384, 1, 1) ← 单声道输入 1 通道
       → Worker (lame.js Mp3Encoder(1, sampleRate, bitRate))
       → Int16Array PCM → 编码为 Uint8Array MP3 帧
       → base64 编码
       → ws.send(base64)

3.4.3 Worker 协议

消息方向 cmd 数据
主 → Worker init { config: { inSampleRate, bitRate } } 初始化编码器
主 → Worker open { buf: Float32Array } 一段单声道 PCM
主 → Worker close 刷编码器剩余数据
Worker → 主 init 初始化完成
Worker → 主 chunk { buf: Uint8Array[] } 编码好的 MP3 帧列表
Worker → 主 end flush 完成

前端实现(useBroadcast.ts:277):

scriptProcessor = audioContext.createScriptProcessor(16384, 1, 1)
scriptProcessor.onaudioprocess = (event) => {
  const inputData = event.inputBuffer.getChannelData(0)  // 单声道
  worker.postMessage({ cmd: 'open', buf: inputData })
}

worker 实现(public/js/worker-realtime.js):

mp3Encoder = new lame.Mp3Encoder(1, inSampleRate, bitRate)  // 单声道
// 每 1152 样本编码一个 MP3 帧,立即 postMessage

3.5 客户端音频图(关键)

Web Audio 是 destination 驱动渲染的,音频图必须连到 destination,否则 onaudioprocessinputBuffer 拿到的全是静音:

Microphone → MediaStreamSource → Gain → ScriptProcessor
                                                  │
                          ┌───────────────────────┴────────────────┐
                          │ ① ScriptProcessor → AnalyserNode (音量分析)
                          │ ② ScriptProcessor → monitorGain(0) → destination
                          │    (0 增益静音节点连接 destination,避免扬声器回声)

⚠️ 常见错误:以为”连 destination 就会从扬声器播出来”。错!只要中间节点 gain=0,最终到 destination 听不到任何声音,但渲染触发了,onaudioprocess 正常触发并获取真实音频。

3.6 关闭协议(步骤 6-7)

  1. 客户端 ws.send('CLOSE')(4 字节字符串)
  2. 客户端 ws.close() 断开连接

3.7 完整代码位置

文件 职责
src-new/composables/useBroadcast.ts 采集 + 编码 + WebSocket 客户端(430 行)
public/js/worker-realtime.js lame.js MP3 编码 worker(81 行)
src-new/pages/Broadcast/index.vue UI 和调用入口

四、本地存储与平台检测

4.1 localStorage 键

键名 用途
token 用户登录 token string
userInfo 用户信息 JSON JSON
userId 用户 ID string
theme 主题模式 'diablo' / 'light'
serverHost 客户端模式服务器地址 URL string
serverPort 客户端模式端口 string
device_display_order 设备自定义排序 JSON array
device_view_mode 设备列表视图模式 'list' / 'card'
broadcast_selected_sn 喊话选中的设备 SN array

4.2 平台检测

const isNativeApp = !!(window as any).Capacitor?.isNativePlatform?.()
const isElectron = !!(window as any).electronAPI?.isElectron
const isClient = isElectron || isNativeApp
  • Web:都 false
  • ElectronisElectron = true(由 preload.js 通过 contextBridge 注入 window.electronAPI
  • Android (Capacitor)isNativeApp = true

4.3 Capacitor 配置

// capacitor.config.ts
{
  appId: 'com.st.broadcast',
  appName: 'ST广播系统',
  webDir: 'dist',
  server: { android:Uri: 'http', cleartext: true },
  plugins: { CapacitorHttp: { enabled: true } }
}

4.4 Electron preload 注入

electron/preload.js 通过 contextBridge.exposeInMainWorld('electronAPI', {...}) 暴露:

方法 用途
storeGet/storeSet/storeDelete 主进程 JSON 文件存储
udpStartListener/udpStopListener/udpScan/udpConfigure/udpClear UDP 设备扫描
udpVolume/udpAmixerGet/udpAmixer 音量/amixer 控制

五、调试与常见问题

5.1 开发调试

# 启动开发服务器(带热更新)
npm run dev
# 访问 http://localhost:8080

Vite 自动代理 /user/apihttp://127.0.0.1:12080

5.2 一键打包三端

npm run build:web       # Web 3 份 → release-main/
npm run build:electron  # Electron 3 份 → release/
npm run build:android   # Android 3 份 → release/

5.3 常见问题

Q: 喊话推流推出去是空数据/假数据

原因:协议必须是单声道,不是立体声。
检查

  • useBroadcast.tscreateScriptProcessor(16384, 1, 1) —— 必须是 (16384, 1, 1)
  • worker-realtime.jsMp3Encoder(1, ...) —— 必须是 Mp3Encoder(1, ...)
  • onaudioprocessgetChannelData(0) 单声道

Q: 喊话声音采不到(inputBuffer 全 0)

原因:音频图没有连接到 destination,Web Audio 不渲染。
修复:用 0 增益 静音节点把图连到 destination:

const monitorGain = audioContext.createGain()
monitorGain.gain.value = 0
scriptProcessor.connect(monitorGain)
monitorGain.connect(audioContext.destination)

Q: 表格在手机上操作列被裁掉

修复:用 .scroll-x 类包表格,加 min-width

<div class="scroll-x">
  <table class="data-table" style="min-width: 900px">...</table>
</div>

Q: 接口拼写 fnckukei vs fnkukei

仓库里两种都有(uni-app2 用无 c 的 fnkukei)。建议跟后端确认统一为一种

Q: Android 上扫不到 UDP 设备

正常,浏览器无 UDP socket,UDP 扫描只支持 Electron。

Q: Capacitor 后端请求发不出去

原因:相对路径 /user/swapupload 在 Capacitor 下 origin 是本地(capacitor://localhost),请求不会发到 serverHost。
修复:使用 getStaticBase() 函数从 localStorage.serverHost 取服务器地址前缀:

function getStaticBase() {
  return (localStorage.getItem('serverHost') || '').replace(/\/+$/, '')
}
fetch(`${getStaticBase()}/user/swapupload`, { ... })

六、版本与构建

6.1 版本号管理

  • package.json version:Electron 安装包命名(当前 2.28.0,必须是合法 semver)
  • src-new/version.json:应用内版本(Web/Android 使用,当前 2.0.32,每次 vite 构建自动 +1)

6.2 3 个音频参数版本

3 个版本通过修改 src-new/composables/useBroadcast.ts 的:

  • new AudioContext({ sampleRate }) —— 采样率
  • bitRate —— 码率

实现自动化(scripts/build-*-all.mjs)。一键构建自动切换参数、构建、恢复源码。

6.3 平台差异总结

平台 入口 打包
Web index.htmlsrc-new/main.ts npm run builddist/
Electron electron/main.js + src-new/main.ts npm run electron:build:winrelease/Setup.exe
Android android/ + src-new/ npm run build + npx cap sync android + gradlew assembleDebugrelease/*.apk

七、相关文件清单

src-new/
├── main.ts                          # Web 入口
├── pages/
│   ├── Login/                      # 管理员登录
│   ├── ClientLogin/                # 客户端登录(Electron/Android)
│   ├── Dashboard/                  # 仪表盘
│   ├── DeviceManage/               # 设备管理
│   ├── GroupManage/                # 分组管理
│   ├── TaskManage/                 # 定时任务
│   ├── MediaManage/                # 媒体管理
│   ├── Broadcast/                  # 实时喊话(WS)
│   ├── Alarm/                      # 报警系统
│   ├── Account/                    # 账户管理
│   ├── Log/                        # 播放日志
│   └── Admin/                      # 后台管理(管理员)—— 不在本对外版文档范围内
├── composables/
│   └── useBroadcast.ts             # 喊话核心(WS + 采集 + 编码)
├── stores/
│   ├── user.ts                     # 用户状态
│   ├── device.ts                   # 设备状态
│   └── app.ts                      # 应用状态(主题等)
├── api/
│   ├── request.ts                  # axios 实例 + 拦截器
│   ├── auth.ts                     # 认证 API
│   └── device.ts                   # 设备 API
└── assets/styles/
    └── diablo-theme.css            # 主题变量(玻璃 / 暗黑 / 亮色)

public/js/
├── worker-realtime.js              # 喊话 MP3 编码 worker(lame.js)
└── lame.min.js                     # MP3 编码库

electron/
├── main.js                         # Electron 主进程
└── preload.js                      # 上下文桥(注入 electronAPI)

android/
├── app/src/main/AndroidManifest.xml
├── app/build.gradle                 # 应用包配置
└── app/src/main/assets/             # Capacitor 同步的 web 资源

scripts/
├── build-web-all.mjs                # 一键构建 Web 三版本
├── build-electron-all.mjs           # 一键构建 Electron 三版本
└── build-android-all.mjs            # 一键构建 Android 三版本

文档版本:v1.0(2026-08-11)
适用代码版本:main 分支(包含单声道推流修复、Android 移动端布局修复、一键构建脚本)

作者:oxiaom  创建时间:2026-08-28 11:50
最后编辑:oxiaom  更新时间:2026-08-30 18:51