feat: 新增语音转文字(ASR)功能

- 后端:新增 /api/audio/transcribe 接口,调用 Ollama whisper 进行语音识别
- 前端:新增 AudioTranscribePage.vue 页面,支持 MP3/WAV/M4A/OGG/FLAC 等格式
- 注册路由、工具卡片、智能助手欢迎语更新

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
eaiadmin
2026-09-14 00:53:36 +08:00
co-authored by Claude Code
parent 752c7837ef
commit 0455f064ac
299 changed files with 1588 additions and 432 deletions
@@ -0,0 +1,75 @@
package api
import (
"github.com/gin-gonic/gin"
"eai_agentplatform/backend/internal/auth"
"eai_agentplatform/backend/internal/middleware"
"eai_agentplatform/backend/internal/model"
"eai_agentplatform/backend/internal/store"
"eai_agentplatform/backend/internal/web"
)
type loginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// Login 登录 → JWT
func Login(c *gin.Context) {
var req loginRequest
if err := c.ShouldBindJSON(&req); err != nil {
web.Fail(c, web.NewBadRequest("请求参数错误"))
return
}
var user model.User
if err := store.DB.Where("username = ?", req.Username).First(&user).Error; err != nil {
web.Fail(c, web.NewAuthError("用户名或密码错误"))
return
}
if user.Status != "active" {
web.Fail(c, web.NewAuthError("账号已禁用"))
return
}
if !auth.VerifyPassword(req.Password, user.PasswordHash) {
web.Fail(c, web.NewAuthError("用户名或密码错误"))
return
}
token, err := auth.CreateToken(user.Username, user.Role, Cfg.JWTSecret, Cfg.JWTExpireMin)
if err != nil {
web.Fail(c, web.NewBadRequest("令牌签发失败"))
return
}
web.OK(c, gin.H{
"token": token,
"expires_in": Cfg.JWTExpireMin * 60,
"user": gin.H{
"id": user.ID,
"username": user.Username,
"full_name": user.FullName,
"role": user.Role,
"ai_points": user.AiPoints,
},
})
}
// Me 当前用户信息
func Me(c *gin.Context) {
u := middleware.CurrentUser(c)
if u == nil {
web.Fail(c, web.NewAuthError("未登录"))
return
}
web.OK(c, gin.H{
"id": u.ID,
"username": u.Username,
"full_name": u.FullName,
"role": u.Role,
"status": u.Status,
"ai_points": u.AiPoints,
"learning_points": u.LearningPoints,
})
}