refactor: 认证系统重构 + DevTools CLI 重写 + 文档全面更新

- auth: Login 简化为管理员始终通过 .env 验证,GetProfile 修正 admin DB 查询
- devtools: .sh/.bat 同步重写为完整 CLI (start/stop/status/logs/build/db:*)
- docs: 新增 devtools.md,重写 Deploy.md (三种方式+Windows说明),更新 README/gateway-api
- voice-service: DashScope 实时流式 STT 支持
- gateway: Phase 6 多模型配置 + 多端客户端管理 + WebSocket 增强

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 14:55:47 +08:00
parent 83e94d9e97
commit 7eb5e984c2
18 changed files with 2405 additions and 677 deletions
@@ -16,21 +16,27 @@ import (
var SupportedLanguages = []string{"zh", "en", "ja", "ko", "auto"}
// STTService 语音转文字服务。
// 优先使用 DashScope Gummy API,不可用时回退到本地 Whisper。
// 优先使用 DashScope API,不可用时回退到本地 Whisper。
type STTService struct {
whisperBinary string
whisperModel string
language string
dashscope *DashScopeSTT
dashscope *DashScopeSTT // 实时 ASR (qwen3-asr-flash-realtime)
}
// NewSTTService 创建 STT 服务。
func NewSTTService(cfg *config.Config) *STTService {
// 实时模型用于所有 WebSocket ASR 请求(支持 one-shot 和 streaming
// 离线模型 (qwen3-asr-flash-2026-02-10) 是 HTTP REST API,暂未实现
model := cfg.DashScopeSTTRealtime
if model == "" {
model = cfg.DashScopeModel
}
return &STTService{
whisperBinary: cfg.WhisperBinary,
whisperModel: cfg.WhisperModel,
language: cfg.WhisperLanguage,
dashscope: NewDashScopeSTT(cfg.DashScopeAPIKey, cfg.DashScopeModel),
dashscope: NewDashScopeSTT(cfg.DashScopeAPIKey, model),
}
}
@@ -58,15 +64,30 @@ func (s *STTService) Transcribe(audioData []byte, format string, language string
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
text, err := s.dashscope.Transcribe(ctx, audioData, format, language)
if err == nil && text != "" {
if err == nil {
return text, nil
}
// DashScope 失败,返回具体错误而不是回退到 Whisper
return "", fmt.Errorf("语音识别失败: %w", err)
}
// 回退到本地 Whisper
return s.transcribeWhisper(audioData, format, language)
}
// StartStreaming 创建持久的流式语音识别会话。
func (s *STTService) StartStreaming(format, language string) (*StreamingSession, error) {
if !s.dashscope.IsAvailable() {
return nil, fmt.Errorf("流式识别需要 DashScope,请配置 DASHSCOPE_API_KEY")
}
if language == "" {
language = s.language
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
return s.dashscope.StartStreaming(ctx, format, language)
}
// transcribeWhisper 使用本地 Whisper 引擎转录。
func (s *STTService) transcribeWhisper(audioData []byte, format string, language string) (string, error) {
if _, err := os.Stat(s.whisperBinary); err != nil {