视频会议保存
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
// Package controller 提供 transcribe 模块的 admin HTTP 接口
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/echochat/backend/app/dto"
|
||||
"github.com/echochat/backend/app/transcribe/dao"
|
||||
"github.com/echochat/backend/app/transcribe/model"
|
||||
"github.com/echochat/backend/app/transcribe/service"
|
||||
"github.com/echochat/backend/pkg/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TranscribeController admin 端转写控制器
|
||||
type TranscribeController struct {
|
||||
svc *service.TranscribeService
|
||||
}
|
||||
|
||||
// NewTranscribeController 创建实例
|
||||
func NewTranscribeController(svc *service.TranscribeService) *TranscribeController {
|
||||
return &TranscribeController{svc: svc}
|
||||
}
|
||||
|
||||
// GetTranscript GET /admin/recordings/:id/transcript
|
||||
//
|
||||
// 返回值:
|
||||
// - 200 + transcript:存在时返回最新状态
|
||||
// - 200 + null:尚未发起转写(前端据此显示"开始转写"按钮)
|
||||
func (ctl *TranscribeController) GetTranscript(c *gin.Context) {
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
utils.ResponseBadRequest(c, "无效的录制 ID")
|
||||
return
|
||||
}
|
||||
|
||||
t, err := ctl.svc.GetByRecording(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
utils.ResponseError(c, "查询转写失败")
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, toDTO(t))
|
||||
}
|
||||
|
||||
// SubmitTranscribe POST /admin/recordings/:id/transcribe
|
||||
//
|
||||
// body: { force: bool, language: string }
|
||||
//
|
||||
// 行为:异步启动 STT,返回 running 状态快照
|
||||
func (ctl *TranscribeController) SubmitTranscribe(c *gin.Context) {
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
utils.ResponseBadRequest(c, "无效的录制 ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.AdminTranscribeRequest
|
||||
// body 可选;POST 不带 body 也允许(默认 force=false language="")
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
t, err := ctl.svc.Submit(c.Request.Context(), id, req.Force, req.Language)
|
||||
if err != nil {
|
||||
ctl.handleError(c, err, t)
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, toDTO(t))
|
||||
}
|
||||
|
||||
// handleError 统一业务错误映射
|
||||
func (ctl *TranscribeController) handleError(c *gin.Context, err error, t *model.Transcript) {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrRecordingNotFound):
|
||||
utils.ResponseNotFound(c, err.Error())
|
||||
case errors.Is(err, service.ErrRecordingNotReady):
|
||||
utils.ResponseBadRequest(c, err.Error())
|
||||
case errors.Is(err, service.ErrSTTNotConfigured), errors.Is(err, dao.ErrLLMSourceDisabled):
|
||||
// 503 表示服务暂不可用,前端可据此提示"请联系管理员配置 STT"
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||
"code": 503,
|
||||
"message": "语音转写服务未配置或不可用",
|
||||
"data": nil,
|
||||
})
|
||||
case errors.Is(err, service.ErrTranscribeRunning):
|
||||
// 已在跑:返回当前进行中行,前端可继续轮询
|
||||
c.JSON(http.StatusAccepted, gin.H{
|
||||
"code": 0,
|
||||
"message": "已有转写任务进行中",
|
||||
"data": toDTO(t),
|
||||
})
|
||||
case errors.Is(err, dao.ErrNoActiveSTT):
|
||||
utils.ResponseBadRequest(c, "未找到可用的 STT 模型或 API Key,请检查 LLM 配置中心")
|
||||
default:
|
||||
utils.ResponseError(c, "发起转写失败: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// toDTO 模型转 DTO,segments JSON 解码 + 状态中文标签
|
||||
func toDTO(t *model.Transcript) *dto.AdminTranscriptDTO {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
segs := []dto.AdminTranscriptSeg{}
|
||||
if t.Segments != "" {
|
||||
_ = json.Unmarshal([]byte(t.Segments), &segs)
|
||||
}
|
||||
return &dto.AdminTranscriptDTO{
|
||||
ID: t.ID,
|
||||
RecordingID: t.RecordingID,
|
||||
RoomID: t.RoomID,
|
||||
Status: t.Status,
|
||||
StatusLabel: statusLabel(t.Status),
|
||||
Text: t.Text,
|
||||
Segments: segs,
|
||||
Language: t.Language,
|
||||
DurationSec: t.DurationSec,
|
||||
ProviderCode: t.ProviderCode,
|
||||
ModelCode: t.ModelCode,
|
||||
ErrorMsg: t.ErrorMsg,
|
||||
StartedAt: dto.FormatTranscriptTimePtr(t.StartedAt),
|
||||
FinishedAt: dto.FormatTranscriptTimePtr(t.FinishedAt),
|
||||
CreatedAt: dto.FormatTranscriptTime(t.CreatedAt),
|
||||
UpdatedAt: dto.FormatTranscriptTime(t.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func statusLabel(s string) string {
|
||||
switch s {
|
||||
case model.TranscriptStatusPending:
|
||||
return "待处理"
|
||||
case model.TranscriptStatusRunning:
|
||||
return "转写中"
|
||||
case model.TranscriptStatusReady:
|
||||
return "已就绪"
|
||||
case model.TranscriptStatusFailed:
|
||||
return "失败"
|
||||
default:
|
||||
return s
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user