diff --git a/.gitignore b/.gitignore index 5f2f374..86010b8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ local.properties /第三方/安卓demo和说明/ptt.rar .idea/ .idea/ +/第三方/安卓demo和说明/POC_Manual.txt +/第三方/安卓demo和说明/POC_SingleCall.md +/第三方/安卓demo和说明/POC_SOS.md diff --git a/PTT_API_DOC.md b/PTT_API_DOC.md new file mode 100644 index 0000000..5127fec --- /dev/null +++ b/PTT_API_DOC.md @@ -0,0 +1,175 @@ +# PTT 平台外部接口详细文档 + +**基础路径**: `/api/external/ptt` +**说明**: 该控制器是与第三方 PTT 语音平台对接的核心入口,涵盖了频道管理、成员状态、录音存取及语音识别等全量功能。 + +--- + +## 1. 频道与会话管理 + +### 三、创建频道 +* **接口**: `POST /group` +* **描述**: 在平台创建一个新的普通频道,支持邀请账号列表或用户 UID 列表。 +* **参数**: + * `gname` (Query, 必填): 群组/频道名称。 + * `accounts` (Body, 可选): 参与成员账号列表 (`List`)。 + * `uids` (Query, 可选): 参与成员 UID 列表 (`List`)。 +* **返回**: `Result` + +### 四、删除频道 +* **接口**: `DELETE /group/{gid}` +* **描述**: 删除指定的普通频道。 +* **参数**: + * `gid` (Path, 必填): 频道 ID。 +* **返回**: `Result` + +### 五、修改频道 +* **接口**: `PUT /group/{gid}` +* **描述**: 修改频道的名称信息。 +* **参数**: + * `gid` (Path, 必填): 频道 ID。 + * `newName` (Query, 必填): 新的频道名称。 +* **返回**: `Result` + +### 六、拉成员入频道 +* **接口**: `POST /group/{gid}/members` +* **描述**: 将指定的账号列表加入到特定的频道中。 +* **参数**: + * `gid` (Path, 必填): 频道 ID。 + * `accounts` (Body, 必填): 待加入的成员账号列表 (`List`)。 +* **返回**: `Result` + +### 七、成员踢出频道 +* **接口**: `DELETE /group/{gid}/members` +* **描述**: 将指定的账号列表从频道中移除。 +* **参数**: + * `gid` (Path, 必填): 频道 ID。 + * `accounts` (Body, 必填): 待移除的成员账号列表 (`List`)。 +* **返回**: `Result` + +### 二十、创建临时会话 +* **接口**: `POST /temp-group` +* **描述**: 发起一个新的临时群组通话。 +* **参数**: + * `gname` (Query, 必填): 群组名称。 + * `accounts` (Body, 必填): 参与成员账号列表 (`List`)。 + * `creatorid` (Query, 必填): 创建者 UID。 +* **返回**: `Result` + +### 二十一、删除临时会话 +* **接口**: `DELETE /temp-group/{gid}` +* **描述**: 注销/结束一个临时群组。 +* **参数**: + * `gid` (Path, 必填): 临时群组 ID。 +* **返回**: `Result` + +--- + +## 2. 列表与成员查询 + +### 八、频道成员列表 +* **接口**: `GET /group/{gid}/members` +* **描述**: 查询指定普通频道内的成员列表,支持搜索和分页。 +* **参数**: + * `gid` (Path, 必填): 频道 ID。 + * `page`, `limit` (Query, 可选): 分页参数。 + * `search` (Query, 可选): 搜索关键词(用户名/账号)。 +* **返回**: `Result>>` + +### 九、频道列表查询 +* **接口**: `GET /groups` +* **描述**: 获取系统内所有的普通频道列表。 +* **参数**: `page`, `limit` (Query, 可选)。 +* **返回**: `Result>>` + +### 十、临时会话列表查询 +* **接口**: `GET /temp-groups` +* **描述**: 获取所有的临时群组列表。 +* **参数**: `page`, `limit` (Query, 可选)。 +* **返回**: `Result>>` + +### 十一、成员可见频道列表查询 +* **接口**: `GET /user/groups` +* **描述**: 查询指定成员有权限查看的频道。 +* **参数**: `account`, `uid`, `search` (Query, 可选)。 +* **返回**: `Result>` + +### 二十九、查询某个临时群组下的成员 +* **接口**: `GET /temp-group/{tempgid}/members` +* **描述**: 获取指定临时群组内的所有成员详情。 +* **参数**: `tempgid` (Path, 必填)。 +* **返回**: `Result>` + +### 三十一、分页查询群组成员 +* **接口**: `GET /group/{gid}/members-paged` +* **描述**: 按在线状态分页查询群组成员。 +* **参数**: + * `gid` (Path, 必填)。 + * `condition` (Query, 可选): 在线状态筛选 (1:在线, 2:离线, 3:在线不在组)。 + * `page`, `limit` (Query, 可选)。 +* **返回**: `Result>>` + +--- + +## 3. 录音记录与处理 (核心功能) + +### 十二、查询频道内成员ptt录音 +* **接口**: `POST /group/{gid}/records` +* **描述**: 获取普通频道内指定成员在特定时间段的 PTT 录音记录。 +* **参数**: + * `gid` (Path, 必填)。 + * `uids` (Body, 可选): 筛选的成员 UID 列表。 + * `start`, `end` (Query, 可选): 时间范围 (yyyy-MM-dd HH:mm:ss)。 + * `page`, `limit` (Query, 可选)。 +* **返回**: `Result>>` + +### 十三、查询临时会话下的录音 +* **接口**: `POST /temp-group/{tempgid}/records` +* **描述**: 获取临时会话下特定时间段的 PTT 录音记录。 +* **参数**: 同上,针对临时群组 ID。 +* **返回**: `Result>>` + +### 获取录音转文字 (新增集成) +* **接口**: `GET /record/text` +* **描述**: 根据录音地址获取并转为文字。 +* **特性**: + 1. 内部自动处理音频下载与本地缓存。 + 2. 已转义过的记录将直接从数据库返回,避免重复调用 ASR。 +* **参数**: `path` (Query, 必填): 录音文件远程相对路径。 +* **返回**: `Result` (识别后的文本内容)。 + +### 播放录音 (流式处理) +* **接口**: `GET /record/play` +* **描述**: 在线播放录音文件,首次播放会自动缓存到服务器本地。 +* **特性**: + 1. **流式输出**: 直接写入 Response 输出流,无内存占用压力。 + 2. **拖动支持**: 支持 HTTP Range 请求,可在浏览器进度条中任意拖动。 + 3. **优先播放**: 默认 `inline` 模式,谷歌浏览器直接调用内置播放器。 +* **参数**: `path` (Query, 必填): 录音文件远程相对路径。 +* **返回**: 二进制音频流 (`audio/wav`)。 + +--- + +## 4. 系统工具 + +### 手动执行平台登录 +* **接口**: `POST /login` +* **描述**: 刷新 PTT 平台的会话 Token 并重新登录。用于故障恢复或手动同步。 +* **返回**: `Result` + +### 二、退出登录 +* **接口**: `POST /logout` +* **描述**: 注销当前 PTT 会话。 +* **返回**: `Result` + +--- + +## 响应规范 +除 `playRecord` 这种流式接口外,所有接口均遵循统一响应体: +```json +{ + "code": 200, // 状态码 (200: 成功) + "msg": "操作成功", // 提示信息 + "data": { ... } // 业务载荷 +} +``` diff --git a/app/src/main/assets/print_demo.html b/app/src/main/assets/print_demo.html index aae5db1..fe4ebec 100644 --- a/app/src/main/assets/print_demo.html +++ b/app/src/main/assets/print_demo.html @@ -44,6 +44,12 @@ .log-time { color: #94a3b8; margin-right: 5px; } .log-msg { word-break: break-all; } + /* PTT Module Styles */ + .ptt-card { border-top: 2px solid #10B981 !important; margin-top: 20px; } + .ptt-label { font-size: 11px; color: #64748b; margin-top: 5px; display: block; } + .ptt-btn-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; } + .ptt-btn { margin: 0 !important; padding: 8px !important; font-size: 12px !important; } + /* Custom Modal Styles */ .modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; @@ -112,6 +118,55 @@ + +
+
+ PTT 业务接口测试 +
+ +
+
+
+ POC 账号: + +
+
+ 频道 ID: + +
+
+
+
+ 频道/群组名称: + +
+
+ 录音相对路径: + +
+
+
+ +
+ + + + + + + + + + + + +
+ +
+
PTT 接口响应将在此展示...
+
+
+
测试签收单

@@ -463,6 +518,113 @@ } setTimeout(loadHistory, 500); + + // === PTT 接口测试逻辑 === + const PTT_BASE = "http://127.0.0.1:8080/api/external/ptt"; + + function showPttResult(msg) { + const container = document.getElementById('ptt-result'); + const time = new Date().toLocaleTimeString(); + const item = document.createElement('div'); + item.className = 'log-item'; + let displayMsg = typeof msg === 'string' ? msg : JSON.stringify(msg, null, 2); + item.innerHTML = `[${time}]${displayMsg}`; + container.insertBefore(item, container.firstChild); + } + + async function pttRequest(path, method = 'GET', query = {}, body = null) { + let url = PTT_BASE + path; + const qParams = new URLSearchParams(query).toString(); + if (qParams) url += "?" + qParams; + + const options = { + method: method, + headers: { 'Content-Type': 'application/json' } + }; + if (body) options.body = JSON.stringify(body); + + try { + const response = await fetch(url, options); + if (path === '/record/play') { + showPttResult("流式播放接口已调用,请查看网络请求或直接点击预览链接"); + return; + } + const data = await response.json(); + showPttResult(data); + return data; + } catch (e) { + showPttResult("请求失败: " + e.message); + console.error(e); + } + } + + function pttCreateGroup() { + const name = document.getElementById('ptt-name').value; + const account = document.getElementById('ptt-account').value; + pttRequest('/group', 'POST', { gname: name }, [account]); + } + + function pttDeleteGroup() { + const gid = document.getElementById('ptt-gid').value; + pttRequest('/group/' + gid, 'DELETE'); + } + + function pttAddMembers() { + const gid = document.getElementById('ptt-gid').value; + const account = document.getElementById('ptt-account').value; + pttRequest('/group/' + gid + '/members', 'POST', {}, [account]); + } + + function pttKickMembers() { + const gid = document.getElementById('ptt-gid').value; + const account = document.getElementById('ptt-account').value; + pttRequest('/group/' + gid + '/members', 'DELETE', {}, [account]); + } + + function pttGetGroups() { + pttRequest('/groups', 'GET', { page: 1, limit: 10 }); + } + + function pttGetTempGroups() { + pttRequest('/temp-groups', 'GET', { page: 1, limit: 10 }); + } + + function pttGetRecords() { + const gid = document.getElementById('ptt-gid').value; + const end = new Date().toISOString().replace('T', ' ').substring(0, 19); + const start = new Date(Date.now() - 24 * 3600000).toISOString().replace('T', ' ').substring(0, 19); + pttRequest('/group/' + gid + '/records', 'POST', { start, end, page: 1, limit: 5 }, []); + } + + function pttGetRecordText() { + const path = document.getElementById('ptt-record-path').value; + if (!path) return alert("请输入录音路径"); + pttRequest('/record/text', 'GET', { path: path }); + } + + function pttPlayRecord() { + const path = document.getElementById('ptt-record-path').value; + if (!path) return alert("请输入录音路径"); + const url = PTT_BASE + '/record/play?path=' + encodeURIComponent(path); + window.open(url, '_blank'); + showPttResult("已尝试打开播放链接: " + url); + } + + function pttCreateTempGroup() { + const name = document.getElementById('ptt-name').value; + const account = document.getElementById('ptt-account').value; + pttRequest('/temp-group', 'POST', { gname: name, creatorid: 1001 }, [account]); + } + + function pttDeleteTempGroup() { + const gid = document.getElementById('ptt-gid').value; + pttRequest('/temp-group/' + gid, 'DELETE'); + } + + function pttGetTempMembers() { + const gid = document.getElementById('ptt-gid').value; + pttRequest('/temp-group/' + gid + '/members', 'GET'); + }