You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
5.8 KiB
174 lines
5.8 KiB
package echomeet
|
|
|
|
import (
|
|
"yunyan/comm"
|
|
"yunyan/pb"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// @Summary 启动会议转写任务
|
|
// @Description 扣除积分并启动指定会议记录的转写/翻译任务,根据源语言自动路由到字节或阿里云
|
|
// @Tags Echomeet
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param user body pb.EchomeetStartTaskReq true "请求数据"
|
|
// @Success 200 {object} comm.HttpResult{data=pb.EchomeetStartTaskResp} "响应数据"
|
|
// @Router /api/home/echomeet_starttask [post]
|
|
func (this *apiComp) StartTask(session comm.IUserSession, req *pb.EchomeetStartTaskReq) (resp *pb.EchomeetStartTaskResp, errdata *pb.ErrorData) {
|
|
var (
|
|
user *pb.DBUser
|
|
model *pb.DBEchoMeetRecord
|
|
err error
|
|
)
|
|
|
|
user, err = this.module.model.getuser(session.GetUserId())
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
model, err = this.module.model.getrecord(req.Id)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
if model.State > pb.DBEchoMeetRecordState_Unknow && model.State < pb.DBEchoMeetRecordState_Completed {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_TaskStateError,
|
|
Message: "任务状态错误",
|
|
}
|
|
return
|
|
}
|
|
if user.Meetintegral < int64(model.Seconds) {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_MeetIntegralNotEnough,
|
|
Message: "积分不足",
|
|
}
|
|
return
|
|
}
|
|
|
|
user.Meetintegral -= int64(model.Seconds)
|
|
err = this.module.model.updateuser(user)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
// 会议时长消耗埋点
|
|
if this.module.analyze != nil {
|
|
this.module.analyze.Report(&comm.StatEvent{
|
|
Type: comm.StatEventMeeting,
|
|
ProductId: user.Lastbindproductid,
|
|
ChannelId: user.Lastbindchannelid,
|
|
Uid: user.Uid,
|
|
Second: int64(model.Seconds),
|
|
})
|
|
}
|
|
|
|
// 同步更新累计统计(排行榜依赖此表)
|
|
if statistics, sErr := this.module.model.getStatistics(session.GetUserId()); sErr == nil {
|
|
statistics.Meetnum += 1
|
|
statistics.Meettime += int64(model.Seconds)
|
|
if uErr := this.module.model.updateStatistics(statistics); uErr != nil {
|
|
this.module.Warnf("StartTask id:%d update userstatistics failed: %v", model.Id, uErr)
|
|
}
|
|
}
|
|
// 写消费日志(供历史重建扫描)
|
|
_ = this.module.model.addUseLog(&pb.DBUserUseLog{
|
|
Uid: session.GetUserId(),
|
|
Logtype: pb.UserLogType_UserConsume,
|
|
Ts: time.Now().Unix(),
|
|
Addmeetsecond: -1 * int64(model.Seconds),
|
|
})
|
|
|
|
tmpl, err := this.module.cache.ResolveTemplate(req.Tid, req.Templateid, req.Tolanguage)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_TemplateNotFound,
|
|
Message: "模板不存在或不支持该语言",
|
|
}
|
|
this.module.Errorf("StartTask id:%d req:%+v 解析模板失败 err:%v", model.Id, req, err)
|
|
return
|
|
}
|
|
model.State = pb.DBEchoMeetRecordState_AwaitTranscribing
|
|
model.Starttime = time.Now().Unix()
|
|
model.Processduration = 0
|
|
model.Templateid = tmpl.Id
|
|
model.Isdistinguishspeaker = req.Isdistinguishspeaker
|
|
model.Formlanguage = req.Formlanguage
|
|
model.Tolanguage = req.Tolanguage
|
|
|
|
// 三段选路(后台编排,fail-closed:未配置直接报错提示去后台配置):
|
|
// - 识别/翻译:按后台优先级 + 语言支持过滤,客户端不可指定;
|
|
// - 总结:后台默认模型;开关允许时客户端可用 summary_svc_id 指定。
|
|
llmSvcId, _, err := this.module.providers.PickLLM(req.SummarySvcId)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_SystemError, Message: err.Error()}
|
|
return
|
|
}
|
|
model.LlmSvcId = llmSvcId
|
|
// 翻译:源=目标语言时无需翻译,选路失败不阻断(后续改语言重译时再补选)。
|
|
if mtSvcId, _, merr := this.module.providers.PickMT(model.Formlanguage); merr == nil {
|
|
model.MtSvcId = mtSvcId
|
|
} else if model.Formlanguage != model.Tolanguage {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_SystemError, Message: merr.Error()}
|
|
return
|
|
}
|
|
|
|
if model.Rtype == "VOICETRANSLAT" {
|
|
// 语音翻译:已有转写文本,只走翻译+总结,无需识别选路。
|
|
this.module.model.saverecord(model)
|
|
this.module.Infof("StartTask id:%d → 语音翻译 mt:%s llm:%s", model.Id, model.MtSvcId, model.LlmSvcId)
|
|
var contexts []*pb.ContextStruct
|
|
if err = json.Unmarshal([]byte(model.Original), &contexts); err != nil {
|
|
this.module.Errorf("Summary id:%d 解析 Original 失败 err:%v", model.Id, err)
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_SystemError,
|
|
Message: "json.Unmarshal Original 失败",
|
|
}
|
|
return
|
|
} else {
|
|
this.module.tasks.TranslateProcess(model, contexts)
|
|
_ = this.module.tasks.SubmitAITask(model)
|
|
}
|
|
return
|
|
}
|
|
|
|
if model.Audiourl == "" {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AudioUrlEmpty,
|
|
Message: "音频URL为空",
|
|
}
|
|
return
|
|
}
|
|
asrSvcId, _, _, err := this.module.providers.PickASR(model.Formlanguage)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_SystemError, Message: err.Error()}
|
|
return
|
|
}
|
|
model.AsrSvcId = asrSvcId
|
|
this.module.model.saverecord(model)
|
|
|
|
this.module.Infof("StartTask id:%d uid:%s formlang:%s tolang:%s seconds:%d size:%d distinguishSpeaker:%v asr:%s mt:%s llm:%s",
|
|
model.Id, model.Uid, model.Formlanguage, model.Tolanguage, model.Seconds, model.Size, model.Isdistinguishspeaker,
|
|
model.AsrSvcId, model.MtSvcId, model.LlmSvcId)
|
|
|
|
// 字节短音频走专用限流队列(flash 同步通道);其余统一走通用异步提交。
|
|
if this.module.providers.ASRProvider(asrSvcId) == "bytedance" &&
|
|
model.Seconds <= byteDanceFlashMaxSeconds && model.Size <= byteDanceFlashMaxBytes {
|
|
_ = this.module.tasks.SubmitShortAudioTask(model)
|
|
} else {
|
|
_ = this.module.tasks.SubmitTranscribeTask(model)
|
|
}
|
|
resp = &pb.EchomeetStartTaskResp{}
|
|
return
|
|
}
|
|
|