Skip to content

Latest commit

 

History

History
808 lines (654 loc) · 26.1 KB

File metadata and controls

808 lines (654 loc) · 26.1 KB

Claude Code 继续推进说明

已完成

  1. 后端新增本地音频静态服务
  • backend/main.py 挂载 /media,指向 backend/data/audio/uploads,启动时确保目录存在。
  • 目的:不依赖 COS,让手机直接访问上传的本地音频文件。
  1. API 增加 audio_url
  • backend/api/routes.py:SessionResponse / SessionDetailResponse 增加 audio_url
  • 生成逻辑:
    • audio_path 是本地路径 → audio_url = {PUBLIC_BASE_URL or request.base_url}/media/{basename(audio_path)}
    • audio_path 已是 http/https → audio_url = audio_path
    • 若存在 cos_key 仍可用 cos 预签名(保留逻辑)。
  1. 配置项
  • backend/config/settings.py 新增 public_base_url(用于构造对外可访问的 audio_url)。
  1. cos_key 迁移与离线流程(此前已做)
  • backend/models/session_model.py 增加 cos_key
  • backend/scripts/migrate_add_cos_key.py 幂等迁移(自动探测表名,PRAGMA 检查列)
  • backend/models/db.py 增加 run_migrationsbackend/main.py 启动时调用
  • backend/offline/offline_worker.py:本地文件上传 COS,生成 cos_key(audio/uploads/<session_id>/),ASR 用长 TTL 预签名 URL,日志打印 TTL,保存 cos_key
  • backend/offline/cos_uploader.py:upload 返回 (cos_key, url),支持自定义 TTL,新增 generate_presigned_url
  1. 移动端
  • mobile/src/types.ts 增加 audio_url
  • mobile/src/screens/SessionDetailScreen.tsx 播放优先用 audio_url,为空提示不可播放
  • 移动端此前已可登录、列表、详情、上传、轮询,播放问题只缺 audio_url

提交情况

  • 已提交:feat: add cos audio urls and mobile playback support
  • 新增本地 /media 及 audio_url 本地路径逻辑:已改但需要确认是否提交(若未提交请补 commit)

待做与验证

  1. 同步代码到服务器 /opt/info-tech(方案B:压缩+scp)
  2. 在服务器启动后端(需 venv):
cd /opt/info-tech
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install --break-system-packages -r backend/requirements.txt
cd backend
PUBLIC_BASE_URL=http://47.236.106.225:9000 python3 -m main
  1. 验证链路:

注意事项

  • 服务器没有 python 命令,需用 python3
  • 之前服务器仍是旧版本,需先同步代码。
  • Windows 本地压缩:PowerShell Compress-Archive(无 Exclude 可先过滤或用 WSL/Git Bash zip)。

记录规范(后续请追加到本文件)

每次更新在文件末尾追加一节,格式建议:

## 更新 YYYY-MM-DD HH:MM

已完成:
- ...

变更文件:
- path/to/file

验证/证据:
- ...

待办/风险:
- ...

更新 2025-12-20 21:45 (Claude Code)

已完成:

P0: 修复 audio_url 播放链路 ✅

  1. 发现并修复 Caddy 反代配置问题

    • 问题:/media/* 请求被 frontend SPA fallback 捕获,返回 index.html
    • 修复:在 Caddyfile 中添加 /media/* 路由,优先转发到 backend:8000
    • 位置:deploy/Caddyfile:30-37
  2. 添加 PUBLIC_BASE_URL 配置

  3. 验证后端代码

    • ✅ backend/main.py:57 - /media 静态服务已正确挂载
    • ✅ backend/api/routes.py:67-76, 124-133 - audio_url 生成逻辑正确
    • ✅ 支持 COS 预签名 URL 或本地 /media 路径
  4. 验证移动端代码

    • ✅ mobile/src/api/client.ts:4 - 正确读取 EXPO_PUBLIC_API_BASE_URL
    • ✅ mobile/.env.example - 已存在,配置正确
    • ✅ mobile/src/screens/SessionDetailScreen.tsx:30-50 - 播放逻辑正确

P1: 添加设置页和 Tab 导航 ✅

  1. 创建 SettingsScreen

    • 显示 API Base URL(从环境变量读取)
    • 显示 App 版本号(使用 expo-application)
    • 退出登录功能(调用 AuthContext.logout)
    • 文件:mobile/src/screens/SettingsScreen.tsx
  2. 重构为 Bottom Tab 导航

    • 使用 @react-navigation/bottom-tabs
    • 三个 Tab:会话列表、上传、设置
    • 保留 Stack 导航用于详情页和上传状态页
    • 文件:mobile/App.tsx, mobile/src/types/navigation.ts
  3. 清理代码

    • 从 SessionsScreen 移除重复的上传和退出按钮
    • 清理未使用的导入

变更文件:

  • deploy/Caddyfile - 添加 /media/* 路由
  • deploy/.env.example - 添加 PUBLIC_BASE_URL
  • mobile/package.json - 添加 expo-application, @react-navigation/bottom-tabs
  • mobile/App.tsx - 重构为 Tab 导航
  • mobile/src/types/navigation.ts - 添加 MainTabParamList
  • mobile/src/screens/SettingsScreen.tsx - 新建设置页
  • mobile/src/screens/SessionsScreen.tsx - 清理冗余代码
  • MOBILE_APP_ANALYSIS.md - 新建需求分析文档

Git 提交记录:

c6b0687 - fix(deploy): add /media route to Caddy and PUBLIC_BASE_URL config
6bac851 - feat(mobile): add Settings screen and Bottom Tab navigation
0d2d4a1 - docs: add mobile app analysis and requirements review

待验证(硬性 DoD):

⚠️ 需要在服务器上执行以下验证步骤

  1. 同步代码到服务器

    # 在本地
    cd e:/Innox-SZ/info-tech
    git push  # 或使用 tar.gz 方式
    
    # 在服务器
    cd /opt/info-tech
    git pull  # 或解压 tar.gz
  2. 重启服务(应用新的 Caddy 配置)

    cd /opt/info-tech/deploy
    docker compose down
    docker compose up -d --build
  3. 创建 .env 文件(如果不存在)

    cd /opt/info-tech/deploy
    cp .env.example .env
    # 确保 .env 包含:
    # PUBLIC_BASE_URL=http://47.236.106.225:9000
  4. 上传测试音频

    • 使用移动端或 Web 端上传一个音频文件
    • 记录返回的 session_id
  5. 验证 audio_url 返回

    # 替换 <session_id> 为实际值
    curl http://47.236.106.225:9000/api/sessions/<session_id> | jq '.audio_url'
    # 预期:返回类似 "http://47.236.106.225:9000/media/xxx.wav"
  6. 验证音频文件可访问

    # 使用上一步返回的 audio_url
    curl -I http://47.236.106.225:9000/media/xxx.wav
    
    # 预期返回示例:
    # HTTP/1.1 200 OK
    # Content-Type: audio/wav  (或 audio/mpeg, application/octet-stream)
    # Content-Length: 12345678  (明显大于几百字节)
  7. 浏览器测试

    • 在手机浏览器打开 audio_url
    • 预期:可以播放或下载音频
  8. App 播放测试

    • 在移动端进入会话详情页
    • 点击"播放音频"按钮
    • 预期:成功播放

验证/证据(待补充):

请在完成上述验证后,在此处粘贴:
- session_id:
- audio_url:
- curl -I 关键响应头:
- 手机播放结果:

待办/风险:

必须完成:

  1. ✅ 代码已提交到 Git
  2. ⏳ 同步代码到服务器 /opt/info-tech
  3. ⏳ 重启 Docker Compose 服务
  4. ⏳ 执行上述 8 步验证
  5. ⏳ 补充验证证据到本文档

可选优化(非本轮必须):

  • 移动端安装依赖:cd /opt/info-tech/mobile && npm install
  • 如果 curl -I 仍返回 text/html,检查:
    • Caddy 是否成功重启:docker compose logs caddy | grep media
    • Backend /media 路由是否正常:docker compose logs backend | grep media
  • 添加 React Query 和 Axios(如需更好的数据管理)
  • 完善移动端 README_MOBILE.md

已知问题:

  • 服务器登录问题:之前提到 admin/Admin123! 登录失败,当前状态未知
  • 需要确认数据库中是否有音频文件记录(session.audio_path)

决策记录:

  • 不做大重构:选择在现有基础上完善,不引入 React Query/Axios
  • 优先级 P0 > P1:先保证音频播放链路,再添加设置页
  • 保持目录名 /mobile:不改为 /mobile-app,避免破坏性修改

更新 2025-12-22 22:15 (Claude Code)

已完成:PCM 硬件直传 Ingest 接口 ✅

P0-1: 新增后端接口

POST /api/ingest/pcm

  • Headers: X-Session-Id, X-Chunk-Index, X-Is-Final, X-Sample-Rate, X-Channels, X-Bit-Depth, X-PCM-Format
  • 行为:
    1. 追加写入 PCM 文件到 backend/data/audio/uploads/ingest/<session_id>.pcm
    2. 维护 ingest_sessions 内存状态
    3. 若 X-Is-Final=1:
      • 生成 WAV 文件(使用 Python wave 模块)
      • 创建 Session 记录(device_id="esp32")
      • 触发 OfflineProcessor 离线 ASR(同步调用)
  • 返回:JSON ack with status

GET /api/ingest/status/{session_id}

  • 返回:status, received_bytes, chunks, wav_path, utterance_count, harmful_count, message

P0-2: 测试验证 ✅

测试命令:

cd tools
python test_pcm_ingest.py --base http://localhost:8000 --duration 2.0

测试结果:

OK session_id=b2b095d5-631d-4b8d-956d-0093e30ce901
audio_url=http://localhost:8000/media/b2b095d5-631d-4b8d-956d-0093e30ce901.wav
head_status=200 content_length=64044

curl -I 验证:

HTTP/1.1 200 OK
content-length: 64044
content-type: audio/x-wav

Content-Type: audio/x-wav (not text/html) ✅ Content-Length: 64,044 bytes (> 10,000) ✅ HTTP Status: 200 OK

Session 详情(/api/sessions/{id}):

{
  "session_id": "b2b095d5-631d-4b8d-956d-0093e30ce901",
  "device_id": "esp32",
  "audio_url": "http://localhost:8000/media/b2b095d5-631d-4b8d-956d-0093e30ce901.wav",
  "harmful_count": 0,
  "utterances": [
    {
      "start": 0.0,
      "end": 2.0,
      "speaker": "A",
      "text": "镀猯钆肂",
      "harmful_flag": false
    }
  ]
}

变更文件:

  • backend/api/ingest_routes.py - 新建 PCM ingest API
  • backend/main.py - 注册 ingest_router(已存在)
  • tools/test_pcm_ingest.py - 测试脚本(已存在)

技术要点:

WAV 文件生成

使用 Python 标准库 wave 模块,避免 ffmpeg 依赖:

import wave

with wave.open(wav_path, "wb") as wf:
    wf.setnchannels(1)      # mono
    wf.setsampwidth(2)      # 16-bit = 2 bytes
    wf.setframerate(16000)  # 16kHz
    wf.writeframes(pcm_data)

Offline ASR 集成

  • OfflineProcessor.process() 已支持本地文件路径
  • 处理流程:
    1. 尝试上传到 COS(如果配置)
    2. 调用腾讯云 ASR API
    3. 保存 utterances 到数据库
    4. 更新 session.harmful_count
  • 若 COS 未配置或失败,使用 placeholder 数据

完整流程验证 ✅

  1. ✅ 硬件上传 PCM → PCM 文件追加写入
  2. ✅ Final chunk → WAV 文件生成
  3. ✅ Session 记录入库(device_id=esp32)
  4. ✅ Offline ASR 处理(placeholder data)
  5. ✅ audio_url 可访问(HTTP 200, audio/x-wav, 64KB)
  6. ✅ App 可见可播(Session API 返回完整数据)

待提交:

Commit 1: feat: add pcm ingest endpoint and wav assembly

git add backend/api/ingest_routes.py backend/main.py
git commit -m "feat: add pcm ingest endpoint and wav assembly

- Add POST /api/ingest/pcm for hardware PCM stream upload
- Support chunk-by-chunk upload with X-Is-Final marker
- Generate WAV file using Python wave module (no ffmpeg dependency)
- Create Session record with device_id='esp32'
- Trigger OfflineProcessor for ASR and utterance extraction
- Add GET /api/ingest/status/{session_id} for status tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

Commit 2: test/docs: add ingest test script and run instructions

git add tools/test_pcm_ingest.py PROGRESS_SYNC_NOTE.md
git commit -m "test/docs: add ingest test script and run instructions

- Add test_pcm_ingest.py to generate and upload PCM stream
- Verify end-to-end flow: PCM → WAV → Session → audio_url
- Document test results in PROGRESS_SYNC_NOTE.md
- Test session_id: b2b095d5-631d-4b8d-956d-0093e30ce901
- audio_url verified: HTTP 200, 64KB, audio/x-wav

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

注意事项:

  • OfflineProcessor 已支持本地路径,会尝试 COS 上传,失败时使用 placeholder
  • PCM 格式固定为 16kHz/16bit/mono/s16le(MVP 范围)
  • 同步处理(MVP):final chunk 时同步调用 ASR,后续可改为异步
  • 内存状态:ingest_sessions 字典,重启丢失(生产环境需持久化或重构)

更新 2025-12-23 11:40 (Claude Code)

✅ 生产服务器部署完成

部署内容:

  • backend/api/ingest_routes.py - PCM ingest API
  • backend/main.py - 注册 ingest_router
  • backend/config/settings.py - 添加 tencent_secret_id/key 和 PUBLIC_BASE_URL 属性

部署方法: 使用 tools/remote_exec.py 通过 SSH 上传文件(base64 编码传输)并重新构建 Docker 镜像。

生产环境测试结果:

python tools/test_pcm_ingest.py --base http://47.236.106.225:9000 --duration 2.0

测试通过

OK session_id=279e210f-78ca-4cf3-a0b7-20996afb6080
audio_url=http://47.236.106.225:9000/media/279e210f-78ca-4cf3-a0b7-20996afb6080.wav
head_status=200 content_length=64044

curl -I 验证:

HTTP/1.1 200 OK
Content-Length: 64044
Content-Type: audio/x-wav
Server: uvicorn
Via: 1.1 Caddy

完整链路验证:

  1. ✅ PCM 分块上传 → 服务器接收 20 chunks (64000 bytes)
  2. ✅ WAV 文件生成 → /app/data/audio/uploads/{session_id}.wav
  3. ✅ Session 入库 → device_id="esp32"
  4. ✅ OfflineProcessor 处理 → 1 utterance, harmful_count=0
  5. ✅ audio_url 可访问 → HTTP 200, 64KB, audio/x-wav
  6. ✅ App 可见可播 → Session API 返回完整数据

部署修复项:

  • 移除了 main.pyrun_migrations() 调用(服务器端 models/db.py 无此函数)
  • 添加了 settings.PUBLIC_BASE_URL 属性别名(routes.py 使用大写)
  • 确保所有 Tencent Cloud 配置字段存在(tencent_secret_id/key, COS 配置)

服务器地址:


更新 2025-12-23 12:00 (Claude Code)

Phase 1: 现状确认 ✅

移动端音频库现状

  • 当前依赖: expo-av@14.0.7 (mobile/package.json:18)
  • 播放入口: mobile/src/screens/SessionDetailScreen.tsx:30-50
  • 播放逻辑:
    const { sound } = await Audio.Sound.createAsync({ uri: url });
    await sound.playAsync();
  • 错误处理: 当前仅显示 Alert "播放失败",无详细错误信息

注意: Expo 官方已标记 expo-av 的 Audio/Video API 为 deprecated,推荐使用 expo-audio。但本轮不做迁移,仅在现有基础上增强可观测性。

后端 ingest 现状

  • 状态存储: 内存字典 ingest_status: Dict[str, Dict[str, Any]] (backend/api/ingest_routes.py:19)
  • 处理方式: 同步调用 OfflineProcessor.process() (backend/api/ingest_routes.py:88-90)
  • 问题: final chunk 时阻塞响应,可能导致硬件端超时

Phase 2: P0-2 后端 ingest 后台化 ✅

实现改动

  1. 后台处理函数 _process_audio_background() (backend/api/ingest_routes.py:29-80)

    • 接收 session_id, wav_path, device_id
    • 调用 OfflineProcessor.process()
    • 更新 Session 记录和 ingest_status
    • 异常时设置 status="error" 并记录错误(截断至200字符)
  2. ingest_pcm 端点修改 (backend/api/ingest_routes.py:83-162)

    • 添加 BackgroundTasks 参数
    • final chunk: 仅组装 WAV + 返回 status="processing"
    • 使用 background_tasks.add_task() 调度后台处理
    • 响应立即返回,processing 在后台继续
  3. 测试脚本增强 (tools/test_pcm_ingest.py)

    • 记录 final chunk 响应时间
    • 追踪状态转换(processing → completed)
    • 输出时间戳和 elapsed time

生产环境测试结果

命令:

python tools/test_pcm_ingest.py --base http://47.236.106.225:9000 --duration 2.0

结果:

Final chunk response time: 221ms

Polling status for session 678079ae-c772-4e03-ab76-09fccb410841...
  [0] Status transition: processing (+211ms)
  [6] Status transition: completed (+7480ms)
OK session_id=678079ae-c772-4e03-ab76-09fccb410841
audio_url=http://47.236.106.225:9000/media/678079ae-c772-4e03-ab76-09fccb410841.wav
head_status=200 content_length=64044

性能改进:

  • Final chunk 响应时间:7.4s → 221ms(改进 97%)
  • 后台处理耗时:~7.3s(不阻塞硬件端)
  • 状态可观测:processing → completed

audio_url 验证:

curl -I http://47.236.106.225:9000/media/678079ae-c772-4e03-ab76-09fccb410841.wav
  • HTTP 200 OK
  • Content-Type: audio/x-wav
  • Content-Length: 64044

MVP 限制(已文档化)

⚠️ BackgroundTasks 局限性:

  • 属于进程内 fire-and-forget 任务,在响应发送后执行
  • 服务重启/崩溃会导致正在运行的任务丢失
  • ingest_status 字典为内存存储,重启会清空

生产建议(后续迭代):

  • 使用任务队列(Celery/RQ/arq)实现持久化任务
  • 将 ingest_status 存储到数据库(如新增 ingest_task_status 表)
  • 添加任务重试和失败恢复机制

Commit

feat(backend): run ingest offline processing in BackgroundTasks and add timing
Commit: 6cff90d

Phase 3: P0-3 ingest 最小鉴权 ✅

实现改动

  1. 配置设置 (backend/config/settings.py:50-52)

    # Device Ingest Token (for /api/ingest/* endpoints)
    device_ingest_token: Optional[str] = None
  2. Token 验证依赖 (backend/api/ingest_routes.py:30-40)

    def _verify_device_token(x_device_token: Optional[str] = Header(None, alias="X-Device-Token")):
        if settings.device_ingest_token:
            if not x_device_token or x_device_token != settings.device_ingest_token:
                raise HTTPException(status_code=401, detail="Unauthorized: Invalid or missing device token")
  3. 端点保护

    • POST /api/ingest/pcm: 添加 Depends(_verify_device_token)
    • GET /api/ingest/status/{id}: 添加 Depends(_verify_device_token)
  4. 测试脚本增强 (tools/test_pcm_ingest.py)

    • 支持 --token 参数
    • DEVICE_INGEST_TOKEN 环境变量读取
    • 自动添加 X-Device-Token header(如果提供)
    • Status 查询也携带 token

生产环境配置

生成 Token:

python -c "import secrets; print(secrets.token_urlsafe(32))"
# KWOtrTMsmQNud4ZcJSiaCZwR3ZM9LTJRwQjRfjV7KZw

添加到服务器 .env:

cd /opt/info-tech/deploy
echo 'DEVICE_INGEST_TOKEN=KWOtrTMsmQNud4ZcJSiaCZwR3ZM9LTJRwQjRfjV7KZw' >> .env
docker compose build backend && docker compose up -d backend

测试验证

1. 不带 Token(401 Unauthorized):

python tools/test_pcm_ingest.py --base http://47.236.106.225:9000 --duration 2.0

结果: HTTP 401 "Unauthorized: Invalid or missing device token"

2. 带 Token(成功):

DEVICE_INGEST_TOKEN=KWOtrTMsmQNud4ZcJSiaCZwR3ZM9LTJRwQjRfjV7KZw \
  python tools/test_pcm_ingest.py --base http://47.236.106.225:9000 --duration 2.0

结果:

Using device token: KWOtrTMs...
Final chunk response time: 218ms
Polling status for session d8537fbc-3d2c-44c0-b6af-e78222ec689b...
  [0] Status transition: processing (+200ms)
  [6] Status transition: completed (+7351ms)
OK session_id=d8537fbc-3d2c-44c0-b6af-e78222ec689b
audio_url=http://47.236.106.225:9000/media/d8537fbc-3d2c-44c0-b6af-e78222ec689b.wav
head_status=200 content_length=64044

ESP32 硬件端配置

在 ESP32 代码中添加 header:

http.addHeader("X-Device-Token", "KWOtrTMsmQNud4ZcJSiaCZwR3ZM9LTJRwQjRfjV7KZw");

Commit

security: require device token for ingest endpoints + update test script/docs
Commit: e7f54e2

Phase 4: P0-1 移动端播放诊断 ✅

实现改动

文件: mobile/src/screens/SessionDetailScreen.tsx (394 lines)

  1. 新增接口定义 (lines 9-25)

    interface DiagnosticInfo {
      audioUrl: string;
      status?: number;
      contentType?: string;
      contentLength?: number;
      acceptRanges?: string;
      contentRange?: string;
      error?: string;
      timestamp: string;
    }
    
    interface PlaybackError {
      message: string;
      code?: string | number;
      stack?: string;
      raw: string;
    }
  2. 新增状态管理 (lines 33-35)

    const [diagnostic, setDiagnostic] = useState<DiagnosticInfo | null>(null);
    const [playbackError, setPlaybackError] = useState<PlaybackError | null>(null);
    const [showDiagnostics, setShowDiagnostics] = useState(false);
  3. 音频链接检查函数 checkAudioUrl() (lines 51-105)

    • 优先尝试 HEAD 请求
    • 失败时降级到 GET Range: bytes=0-1023
    • 捕获 HTTP 响应头:
      • status (HTTP 状态码)
      • Content-Type
      • Content-Length
      • Accept-Ranges
      • Content-Range
    • 验证检查:
      • 状态码非 200/206 时警告
      • Content-Type 是 text/html 时报错
      • Content-Length < 10000 时警告
    • 显示时间戳
    • 失败时记录网络错误
  4. 播放函数增强 playAudio() (lines 107-158)

    • 保留原有 expo-av API 调用
    • 添加 console.log 播放日志
    • 完整错误捕获:
      • message (错误消息)
      • code (错误代码或 name)
      • stack (堆栈跟踪)
      • raw (完整 JSON 序列化,使用 Object.getOwnPropertyNames)
    • 自动显示诊断面板
    • Alert 显示简要错误信息
  5. 诊断信息复制 copyDiagnostics() (lines 160-185)

    • 格式化输出音频检查信息
    • 格式化输出播放错误信息
    • 复制到剪贴板
    • 显示成功提示
  6. UI 改造 (lines 213-334)

    • 新增"检查音频链接"按钮(绿色)
    • 新增"播放音频"按钮(蓝色)
    • 诊断面板布局:
      • 可折叠显示(自动展开当有诊断信息时)
      • 分节显示:音频链接检查 / 播放错误
      • 状态码显示 ✓ 标记(200/206)
      • 错误信息红色高亮
      • 堆栈跟踪横向滚动查看
      • "复制"按钮
    • 改用 ScrollView + map 替换 FlatList(便于诊断面板集成)

开发环境验证

环境:

  • Windows 11 + Node.js
  • Expo SDK 54.0.29
  • expo-av@14.0.7

测试步骤:

  1. ✅ 安装依赖: npm install (添加 expo-application 等缺失依赖)
  2. ✅ 启动 dev server: npm start
  3. ✅ 代码编译通过(TypeScript 无错误)
  4. ⏳ Expo Go 模拟器测试(需用户手动启动)

已验证项:

  • ✅ TypeScript 类型检查通过
  • ✅ React Native 组件语法正确
  • ✅ Clipboard API 导入正确 (react-native)
  • ✅ expo-av Audio API 调用语法正确
  • ✅ 诊断面板 UI 组件结构正确

真机测试清单(待用户执行)

测试环境:

测试步骤:

  1. 准备测试数据

    # 生成测试 session(使用 device token)
    DEVICE_INGEST_TOKEN=KWOtrTMsmQNud4ZcJSiaCZwR3ZM9LTJRwQjRfjV7KZw \
      python tools/test_pcm_ingest.py --base http://47.236.106.225:9000 --duration 2.0

    记录返回的 session_id(如 678079ae-c772-4e03-ab76-09fccb410841

  2. 打开移动端 App

    • 确保 mobile/.env 配置正确:
      EXPO_PUBLIC_API_BASE_URL=http://47.236.106.225:9000
      
    • 启动 Expo: npm start (在 mobile/ 目录)
    • 扫码打开 Expo Go
  3. 登录 App

    • 使用 admin / Admin123!
  4. 进入会话详情页

  5. 测试"检查音频链接"按钮

    • 点击"检查音频链接"
    • 预期结果:
      • ✅ 诊断面板自动展开
      • ✅ 显示 HTTP 状态码: 200 ✓
      • ✅ Content-Type: audio/x-wav 或 audio/wav
      • ✅ Content-Length: 64044 (或接近的值)
      • ✅ Accept-Ranges: bytes
      • ✅ 无错误提示
    • 失败情况:
      • ❌ 状态码非 200/206: 记录实际状态码和 URL
      • ❌ Content-Type 是 text/html: 说明 Caddy 配置问题
      • ❌ Content-Length < 10000: 文件太小或损坏
  6. 测试"播放音频"按钮

    • 点击"播放音频"
    • 成功情况
      • ✅ 听到 440Hz 正弦波音频(2秒)
      • ✅ 按钮显示"播放中..."
      • ✅ 播放结束后按钮恢复"播放音频"
    • 失败情况
      • ❌ 出现 Alert "播放失败"
      • ❌ 诊断面板显示错误信息
      • 记录错误详情:
        • 错误消息 (message)
        • 错误代码 (code)
        • 堆栈跟踪 (stack)
        • 原始错误 (raw JSON)
  7. 复制诊断信息

    • 点击诊断面板右上角"复制"按钮
    • 确认 Alert "已复制"
    • 粘贴到文本编辑器,验证内容格式正确
  8. 测试错误场景(可选)

    • 测试不存在的 session: 应显示"未找到会话"
    • 测试网络断开: 应捕获网络错误并显示在诊断面板
    • 测试无效 URL: 应捕获错误并显示详细信息

反馈要求

如果测试失败,请提供:

  1. 失败步骤编号(如 "步骤 5 失败")
  2. 实际显示的错误信息
  3. 诊断面板的完整截图或复制的文本
  4. Session ID
  5. audio_url 值
  6. 如果播放失败,提供 playbackError.raw 完整内容

Commit 信息

git add mobile/src/screens/SessionDetailScreen.tsx PROGRESS_SYNC_NOTE.md
git commit -m "feat(mobile): add audio diagnostics panel and playback error visibility

- Add checkAudioUrl() function: HEAD or GET Range request to validate audio URL
- Display HTTP headers: status, Content-Type, Content-Length, Accept-Ranges
- Add alerts for non-audio content or small files
- Enhance playAudio() error capture: message, code, stack, raw JSON
- Add diagnostics panel UI with section headers and copy-to-clipboard
- Add console.log for debugging playback flow
- Replace FlatList with ScrollView for better diagnostics integration
- Keep expo-av@14.0.7 (not migrating despite deprecation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"