From a2d14487cb9a0c6b8a3ea57a1581afdd6e83b8a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=82=A5=E7=BE=8A?= <1048382248@qq.com>
Date: Mon, 19 Jan 2026 18:17:58 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20=E8=B0=83=E8=AF=95?=
=?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=811=E7=9A=84AI=E5=88=86=E6=9E=90?=
=?UTF-8?q?=E6=B5=81=E7=A8=8B(=E7=9F=A5=E8=AF=86=E7=82=B9=E6=9D=83?=
=?UTF-8?q?=E9=87=8D,=E6=9D=A5=E6=BA=90,AI=E5=A4=8D=E6=9F=A5=E5=A4=8D?=
=?UTF-8?q?=E6=A3=80...)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../AICore/GPT/DeepSeek/DeepSeekGPTClient.cs | 4 +-
.../AICore/GPT/Dto/QuestionRes.cs | 7 +-
VideoAnalysisCore/AICore/GPT/GPTClient.cs | 22 ++--
.../AICore/GPT/GTP_Analysis_1.cs | 110 +++++++++++-------
.../AICore/GPT/Gemini/GeminiGPTClient.cs | 2 +-
.../AICore/SherpaOnnx/SenseVoice.cs | 2 +-
.../Controllers/LJZK_Controller.cs | 54 ++++++---
.../Controllers/VideoTaskController.cs | 29 +++--
VideoAnalysisCore/Model/VideoKonwPoint.cs | 14 ++-
VideoAnalysisCore/Model/VideoTaskStage.cs | 10 +-
10 files changed, 168 insertions(+), 86 deletions(-)
diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekGPTClient.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekGPTClient.cs
index 4795118..4246464 100644
--- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekGPTClient.cs
+++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeekGPTClient.cs
@@ -42,7 +42,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
/// 最大token 不设置默认最大值 16000/8000
///
///
- public override async Task ChatAsync(string task, string postMessages, string title, string model =null, int max_tokens = 16000)
+ public override async Task ChatAsync(string task, string postMessages, string title, string model =null, int max_tokens = 32000)
{
Message[] messageArr = [
new Message(postMessages,"user"),
@@ -53,7 +53,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
taskId = task,
title = title,
model = model ?? ChatGPTType.Deepseek_Reasoner,
- max_tokens = model == ChatGPTType.Deepseek_Reasoner ? 16000 : max_tokens,
+ max_tokens = model == ChatGPTType.Deepseek_Reasoner ? 32000 : max_tokens,
stream = true,
temperature = 0.2f,
messages = messageArr
diff --git a/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs b/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs
index 5b892aa..c6f8bf9 100644
--- a/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs
+++ b/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs
@@ -34,9 +34,10 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
}
public class VideoKnowPointDto
{
+ public float KnowPointWeight { get; set; }
public string KnowPoint { get; set; }
public string KnowPointId { get; set; }
- public float KnowSourceTime { get; set; }
+ public string KnowSource { get; set; }
}
public class VideoKnowRes
{
@@ -64,6 +65,10 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
///
public virtual string? KnowPoint { get; set; }
///
+ /// 知识点权重
+ ///
+ public virtual float? KnowPointWeight { get; set; }
+ ///
/// 知识点ID
///
public virtual string? KnowPointId { get; set; }
diff --git a/VideoAnalysisCore/AICore/GPT/GPTClient.cs b/VideoAnalysisCore/AICore/GPT/GPTClient.cs
index 67c7f45..f176707 100644
--- a/VideoAnalysisCore/AICore/GPT/GPTClient.cs
+++ b/VideoAnalysisCore/AICore/GPT/GPTClient.cs
@@ -13,6 +13,8 @@ using System.IO;
using VideoAnalysisCore.AICore.GPT.ChatGPT;
using System.Threading.Tasks;
using System.Text.Json;
+using FFmpeg.NET.Services;
+using NetTaste;
namespace VideoAnalysisCore.AICore.GPT
{
@@ -76,7 +78,7 @@ namespace VideoAnalysisCore.AICore.GPT
{
throw new Exception("请求GPT服务器失败次数过多");
}
- goto PostJsonStream;
+ goto PostJsonStream;
}
using var stream = chatResp.Content.ReadAsStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
@@ -87,7 +89,7 @@ namespace VideoAnalysisCore.AICore.GPT
var splitCount = "data:".Length;
var maxLoop = 50 * 200;
int threshold = 0;
- var startTime= DateTime.Now;
+ var startTime = DateTime.Now;
var endTime = startTime.AddHours(1.5);
//最长分析分析时间1.5小时 或者重试读取 1w次
while (maxLoop > 0 && DateTime.Now < endTime)
@@ -156,7 +158,7 @@ namespace VideoAnalysisCore.AICore.GPT
/// 最大token 不设置默认最大值 16000/8000
///
///
- public async Task ChatAsync(ChatRequest chatRep)
+ public async Task ChatAsync(ChatRequest chatRep) where T:class,new()
{
var tryCount = 10;
while (tryCount-- > 0)
@@ -180,14 +182,20 @@ namespace VideoAnalysisCore.AICore.GPT
chatResContent = chatResContent?.Replace("```", "");
chatResContent = chatResContent?.Replace("}{", "},{");
chatResContent = chatResContent?.Replace("}|{", "},{");
- chatResContent = chatResContent?.Trim();
+ chatResContent = chatResContent?.Trim();
chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault();
if (string.IsNullOrEmpty(chatResContent))
throw new Exception($"GPT返回结果无有效JSON =>{chatResp?.res}");
- var startsStr = typeof(T).IsArray ? "[" : "{";
- var endStr = typeof(T).IsArray ? "]" : "}";
+ var startsStr = "{";
+ var endStr = "}";
+ var resT = new T();
+ if (resT is Array || resT is System.Collections.IList || resT is System.Collections.IList)
+ {
+ startsStr = "[";
+ endStr = "]";
+ }
if (!chatResContent.StartsWith(startsStr))
chatResContent = startsStr + chatResContent;
if (!chatResContent.EndsWith(endStr))
@@ -271,7 +279,7 @@ namespace VideoAnalysisCore.AICore.GPT
///
///
public virtual Task ChatAsync(string task, string postMessages, string title,
- string model = null, int max_tokens = 16000)
+ string model = null, int max_tokens = 16000) where T : class, new()
{
throw new Exception("需要实现");
}
diff --git a/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs b/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs
index f076cec..40fb88a 100644
--- a/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs
+++ b/VideoAnalysisCore/AICore/GPT/GTP_Analysis_1.cs
@@ -22,6 +22,7 @@ using System.Collections.Generic;
using UserCenter.Model.Enum;
using Dm.filter;
using System.Text.RegularExpressions;
+using System.Diagnostics;
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{
@@ -81,28 +82,37 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
questionRes = questionRes.Where(s => s != null)
.OrderBy(s => s.StartTime).ToList();
var thems = questionRes.Adapt().ToJson();
- var checkResFormat1 = """[{"StartTime":开始秒(number),"TextbookSource":教材来源(string),"KnowPoints":[{"KnowSourceTime":"知识点来源的开始秒(number)","KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]}]""";
+ var checkResFormat1 = """[{"StartTime":12.3,"TextbookSource":"课本","KnowPoints":[{"KnowPointWeight":0.5,"KnowSource":"开始时间(秒),匹配的原因","KnowPoint":"知识点名称","KnowPointId":"123"}]}]""";
var knowMessages =
- $"我针对{taskInfo.Subject}课堂授课视频分析出了视频的授课阶段片段。\n" +
- $"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点)。\n" +
- $"KnowSourceTime字段 需要提供出知识点被匹配的字幕内容最开始的时间来源。\n" +
- $"TextbookSource字段 需要你分析出单个片段内讲述内容所属的教材范围 例如 (范围限定在 课本/试卷/挹青苑/其他)。\n" +
- $"这是我的分段 {thems}。\n" +
- $"课堂内容与{sections}章节相关\n" +
- $"最后请确保分配的知识点是用户提供的,并且一定正确合理!\n" +
- $"返回的片段数量与传入片段数量一致!(硬性条件)\n" +
- $"输出内容只返回json格式({checkResFormat1})\n" +
- $" 格式 (方法点Id|方法点名称) \n" +
- $"提供的`知识点名称({knows})。\n";
+ $"""
+ 角色:你是一位{taskInfo.Subject}学科教研老师。
+ 任务:为每个【视频分段】分配对应的知识点(可多个),并补充来源信息。
+ 字段说明:
+ - TextbookSource:该分段讲授内容所属教材来源,仅允许取值:课本/试卷/挹青苑/其他
+ - KnowPoints:数组。每个元素代表一个知识点匹配结果
+ - KnowPoint:知识点名称(必须来自我提供的列表)
+ - KnowPointId:知识点ID(必须与 KnowPoint 对应)
+ - KnowPointWeight:知识点在本分段的占比权重(最高为1,单个分段内的知识点权重之和必须等于1)
+ - KnowSource:该知识点在字幕中被首次提及的“开始秒,为什么匹配的原因(最多50字)”
+ 强制约束(硬性条件):
+ 1) 输出数组长度必须与输入分段长度一致,且顺序一致;不得新增/删除/合并分段。
+ 2) 每个对象的 StartTime 必须与输入分段的 StartTime 完全一致(不得改动)。
+ 3) KnowPoints 允许为空数组(表示未能匹配任何知识点),但不要输出空字符串或用逗号拼接。
+ 4) 只输出 JSON(禁止 Markdown/解释/额外文本)。
+ 输入分段:{thems}
+ 章节:{sections}
+ 知识点列表(Id|Name):{knows}
+ 输出格式示例:{checkResFormat1}
+ """;
await redisManager.AddTaskLog(taskInfo.Id, "==>2.开始分析视频内容知识点");
- VideoKnowRes[] konwRes;
+ List konwRes;
var knowOK = false;
var chatClentArr = new GPTClient[] { chatGPTClient, geminiClient, deepSeekClient };
for (int i = 0; i < 3; i++)
{
- konwRes = await chatClentArr[i].ChatAsync(taskInfo.Id.ToString(), knowMessages, "知识点");
+ konwRes = await chatClentArr[i].ChatAsync>(taskInfo.Id.ToString(), knowMessages, "知识点");
// 分析结果的片段数量与预期不匹配
- if (questionRes.Count() != konwRes.Length) continue;
+ if (questionRes.Count() != konwRes.Count()) continue;
for (int xi = 0; xi < konwRes.Count(); xi++)
{
questionRes[xi].KnowPoints = konwRes[xi].KnowPoints;
@@ -132,7 +142,9 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
EndTime = s.EndTime,
StageId = StageId,
KnowPoint = x.KnowPoint,
- KnowSourceTime = x.KnowSourceTime,
+ KnowPointWeight=x.KnowPointWeight,
+ TextbookSource = s.TextbookSource,
+ KnowSource = x.KnowSource,
KnowPointId = knowDic[x.KnowPoint].ToString(),
TagId = taskInfo.TagId,
VideoTaskId = taskInfo.Id,
@@ -197,7 +209,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
var pptFormat = taskInfo.VideoType == AttachmentsInfoType.复习
? "这堂课是习题课,所讲解内容几乎都是试题。"
: string.Empty;
- var checkResFormat = """{"Score":85.5,"MinusScore":"简洁的扣分原因","Suggestion":"改进建议"}""";
+ var checkResFormat = """{"Score":65.5,"MinusScore":"简洁的扣分原因","Suggestion":"简洁的改进建议"}""";
var checkMessage =
$"""
请你担任一位专业的视频内容分析教研老师,擅长评估视频内容的结构和逻辑流暢度。
@@ -209,8 +221,9 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
知识点分配:检查分段内的知识点是否与分段Conten有关联,知识点分配给这个分段是否合理(硬性指标)。
逻辑过渡:评估分段之间的过渡是否自然流畅,后一段是否是前一段内容的合理延伸或转折。
综合评分:
- 请基于以上分析,提供一个0-100的综合得分(70分及格)。
- 详细说明打分理由,并逐条对应到上述评估维度。
+ 请基于以上分析,提供一个0-100的综合得分(70分及格,打分一定要严谨,总分一定要准确)。
+ MinusScore: 详细说明打分理由,并逐条对应到上述评估维度。
+ Suggestion: 基于扣分原因提出针对分段方案的改进意见(请忽略掉分段没有结束时间的问题我会自己处理)。
输入数据格式说明:
分段方案: {thems}
字幕文本: 格式为说话人:开始秒:结束秒:内容|下一段字幕。完整内容为:{captions.Captions}
@@ -257,8 +270,8 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
输出格式(仅 JSON):{resFormat}
""";
- var improved = await geminiClient.ChatAsync(taskInfo.Id.ToString(), message, "分段优化");
- if (improved is null || improved.Length != questionRes.Count())
+ var improved = await geminiClient.ChatAsync>(taskInfo.Id.ToString(), message, "分段优化");
+ if (improved is null || improved.Count() != questionRes.Count())
return null;
return improved.OrderBy(s => s.StartTime ?? 0).ToList();
@@ -271,7 +284,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
private async Task OptimizeSubtitles(VideoTask taskInfo,
SenseVoiceRes[] captionsArr, string sections)
{
- if (!string.IsNullOrEmpty(taskInfo.CaptionsAI))
+ if (!string.IsNullOrEmpty(taskInfo.CaptionsAI) && taskInfo.CaptionsAI!="[]")
return JsonSerializer.Deserialize(taskInfo.CaptionsAI);
var subject = taskInfo.Subject.ToString();
var newCaptionsList = new List(captionsArr.Length);
@@ -306,10 +319,10 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
$"待优化字幕内容:\n" +
$"{nowCaptionStr}\n" +
$"最终核对:请确保输出 JSON 中包含的字幕条数与输入的字幕条数完全对应。";
- string[]? resData = null;
+ List? resData = null;
for (int i = 0; i < 3; i++)
{
- resData = await chatClentArr[i].ChatAsync(taskInfo.Id.ToString(), postMessages, "优化字幕", ChatGPTType.Deepseek_Chat, 8000);
+ resData = await chatClentArr[i].ChatAsync>(taskInfo.Id.ToString(), postMessages, "优化字幕", ChatGPTType.Deepseek_Chat, 8000);
if (resData.Count() == cArr.Count())
break;
else
@@ -318,7 +331,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
if (resData.Count() != cArr.Count())
{
- resData = cStrArr.ToArray();
+ resData = cStrArr.ToList();
await redisManager.AddTaskLog(taskInfo.Id, $"==>字幕优化 分段{s} AI结果数量不匹配 采用原始值");
}
newCaptionsList.AddRange(resData.Select((text, i) => new SenseVoiceRes()
@@ -366,13 +379,12 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
$"完整的课堂标准流程包含以下5个阶段:课程引入/新知讲解/例题精讲/课堂练习/知识总结。\n" +
reviewStr +
$"讲解知识内容的阶段的细分程度到某个知识点的讲解/认识/例题/总结\n" +
+ $"不分析课堂作业相关的内容我已经预处理了\n" +
$"初步划分阶段:{keyFrameStr}\n" +
- $"\n" +
- $"内容分析:对每个时间段,提取主要讲解内容:识别关键词(如“例题”“证明”“练习”“总结”)和内容结构。\n" +
- $"判断阶段类型:如果内容以解题为主,归类为“例题精讲”;如果涉及新知识讲解,归类为“新知讲解”;以此类推。\n" +
- $"内容总结:简述该阶段的核心讲解内容40~150字, 必须完全基于字幕文本可推断的信息,禁止捏造不存在的内容(硬性条件)。\n" +
- $"阶段主题:基于内容总结,提炼一个恰当的主题(例如,“柯西不等式的基本应用”)。\n" +
- $"输出要求:确保阶段划分合理、无重叠\n" +
+ $"Stage:判断阶段类型如果内容以解题为主,归类为“例题精讲”;如果涉及新知识讲解,归类为“新知讲解”;以此类推。\n" +
+ $"Content:简述单个阶段的核心讲解内容40~150字(如“例题”“证明”“练习”“总结”...), 必须完全基于字幕文本可推断的信息,禁止捏造不存在的内容(硬性条件)。\n" +
+ $"Theme:理解Content,提炼一个精确的主题(例如,“柯西不等式的基本应用”)。\n" +
+ $"输出要求:确保阶段划分合理、无重叠、\n" +
$"作业布置阶段一般出现在末尾如果有" +
$"输出格式要求:内容只返回json格式({resFormat})\n" +
$"字幕格式(开始秒:内容|下一段字幕).以下是包含时间的视频字幕文本。\n" +
@@ -424,7 +436,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
输出格式(仅 JSON):{resFormat}
""";
- var res = await deepSeekClient.ChatAsync(taskInfo.Id.ToString(), message, "作业布置识别");
+ var res = await deepSeekClient.ChatAsync(taskInfo.Id.ToString(), message, "作业布置识别", ChatGPTType.Deepseek_Chat, 8000);
if (res is null)
return null;
if (!string.Equals(res.Stage, "作业布置", StringComparison.OrdinalIgnoreCase))
@@ -575,7 +587,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
$"输出内容只返回json格式为({resFormat})" +
$"以下是试题内容" +
$"`{sRes.Result.res.value}`";
- var resData = await deepSeekClient.ChatAsync(taskInfo.Id.ToString(), postMessages, "提取试题");
+ var resData = await deepSeekClient.ChatAsync>(taskInfo.Id.ToString(), postMessages, "提取试题");
//var resData = await chatClient.ChatAsync(taskInfo.Id.ToString(), postMessages, "提取试题");
if (resData is null || resData.Count() == 0)
break;
@@ -704,8 +716,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
if (questionRes is null) continue;
//处理分段 知识点
List insertData = await GetVideoKnow(questionRes, taskInfo, sections, knowledgeInfos);
- if (homework != null)
- questionRes.Add(homework);
+
//校验结果质量
var checkRes = await VerifySpanQuality(questionRes, taskInfo, captions, sections, Course_Id);
@@ -715,35 +726,44 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
// 质量复检
if (checkRes != null)
{
- var improved = await ImproveSpanBySuggestion(questionRes, taskInfo, captions, sections, checkRes.Suggestion);
+ var improved = await ImproveSpanBySuggestion(questionRes, taskInfo, captions, sections, "扣分原因 {checkRes.MinusScore} \n 改进意见 {checkRes.Suggestion}");
if (improved != null)
{
var improvedCheck = await VerifySpanQuality(improved, taskInfo, captions, sections, Course_Id);
await redisManager.AddTaskLog(taskInfo.Id, $"==>优化后复检得分=>{improvedCheck.Score}");
await redisManager.AddTaskLog(taskInfo.Id, $"==>优化后扣分原因 {improvedCheck.MinusScore}");
- if (improvedCheck != null && improvedCheck.Score >= 90)
+ if (improvedCheck != null && improvedCheck.Score >= 90 && improvedCheck.Score > checkRes.Score)
{
questionRes = improved;
+ if (homework != null && (!questionRes.Any(s => s.Stage == StageEnum.作业布置.ToString())))
+ questionRes.Add(homework);
+
insertData = await GetVideoKnow(questionRes, taskInfo, sections, knowledgeInfos);
await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id);
await videoTaskStageDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id);
var tStage = insertData.GroupBy(s => s.StageId).Select(s => new VideoTaskStage
{
- Id=s.Key,
+ Id = s.Key,
TagId = s.First().TagId,
- CloudSchoolId =s.First().CloudSchoolId,
- StartTime = s.Last().StartTime,
- EndTime =s.Last().EndTime,
- KnowSourceTime=s.Last().KnowSourceTime,
- Stage=s.First().Stage,
- Theme=s.First().Theme,
- VideoTaskId=taskInfo.Id,
+ CloudSchoolId = s.First().CloudSchoolId,
+ StartTime = s.First().StartTime,
+ EndTime = s.First().EndTime,
+ Content = s.First().Content,
+ TextbookSource = s.First().TextbookSource,
+ Stage = s.First().Stage,
+ Theme = s.First().Theme,
+ VideoTaskId = taskInfo.Id,
}).ToArray();
await videoTaskStageDB.InsertRangeAsync(tStage);
await videoKonwPointDB.InsertRangeAsync(insertData);
break;
}
+ else
+ {
+ await redisManager.AddTaskLog(taskInfo.Id, $"==>优化之后的得分降低/得分过低");
+ continue;
+ }
}
}
diff --git a/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs b/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs
index 0bcd8cd..36fdac4 100644
--- a/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs
+++ b/VideoAnalysisCore/AICore/GPT/Gemini/GeminiGPTClient.cs
@@ -58,7 +58,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
stream = true,
temperature = 0.2f,
messages = messageArr,
- max_completion_tokens=16000,
+ max_completion_tokens= 12288,
};
chatReq.modalities = null;
diff --git a/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs b/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
index 163a25a..99cc18c 100644
--- a/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
+++ b/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
@@ -143,7 +143,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
throw new Exception("task 音频路径未找到");
if (OR is null) Init();
serviceProvider.GetRequiredService()
- .TaskHandle(new WaveReader(filePath), null, SoundHandle, SherpaVadVersion.silero_vad_v5);
+ .TaskHandle(new WaveReader(filePath), task, SoundHandle, SherpaVadVersion.silero_vad_v5);
return Task.CompletedTask;
}
diff --git a/VideoAnalysisCore/Controllers/LJZK_Controller.cs b/VideoAnalysisCore/Controllers/LJZK_Controller.cs
index cf99215..156d155 100644
--- a/VideoAnalysisCore/Controllers/LJZK_Controller.cs
+++ b/VideoAnalysisCore/Controllers/LJZK_Controller.cs
@@ -35,6 +35,7 @@ namespace VideoAnalysisCore.Controllers
private readonly Repository videoTaskDB;
private readonly Repository courseInfoDB;
private readonly Repository videoKonwPointDB;
+ private readonly Repository videoTaskStageDB;
private readonly Repository nodePackageInfoDB;
private readonly Repository videoQuestionDB;
private readonly Repository videoQuestionKonwDB;
@@ -42,7 +43,7 @@ namespace VideoAnalysisCore.Controllers
public LJZK_Controller(IMapper mp, Repository nodesubscriptionDB,
Repository videoTaskDB = null, Repository videoKonwPointDB = null
- , Repository nodePackageInfoDB = null, Repository videoQuestionDB = null, Repository videoQuestionKonwDB = null, Repository courseInfoDB = null, RedisManager redisManager = null)
+ , Repository nodePackageInfoDB = null, Repository videoQuestionDB = null, Repository videoQuestionKonwDB = null, Repository courseInfoDB = null, RedisManager redisManager = null, Repository videoTaskStageDB = null)
{
this.mp = mp;
this.nodesubscriptionDB = nodesubscriptionDB;
@@ -53,6 +54,7 @@ namespace VideoAnalysisCore.Controllers
this.videoQuestionKonwDB = videoQuestionKonwDB;
this.courseInfoDB = courseInfoDB;
this.redisManager = redisManager;
+ this.videoTaskStageDB = videoTaskStageDB;
}
@@ -201,27 +203,49 @@ namespace VideoAnalysisCore.Controllers
.ToArrayAsync();
if (konwArr is null || konwArr.Length == 0)
return BadRequest("Чֶ");
+
+
+ var stageArr = await videoTaskStageDB.AsQueryable()
+ .Where(s => s.VideoTaskId == task.Id)
+ .ToArrayAsync();
+ var videoKnowDic = konwArr
+ .GroupBy(s => s.StageId)
+ .ToDictionary(s => s.Key);
+ var videoKnows = stageArr
+ .Select(s => new VideoKnowRes()
+ {
+ Content = s.Content,
+ StartTime = s.StartTime,
+ EndTime = s.EndTime,
+ Theme = s.Theme,
+ StageId = s.Id,
+ KnowPoint = videoKnowDic.ContainsKey(s.Id)
+ ? string.Join(',', videoKnowDic[s.Id].Select(x => x.KnowPoint))
+ : string.Empty
+ }).ToArray();
+
var res = new TaskKnowRes()
{
TagId = task.TagId,
Status = task.LastEnum,
VideoTaskId = task.Id,
- KnowBlockArr = konwArr
- .GroupBy(s => s.StartTime)
+ KnowBlockArr = stageArr
.Select(s => new TaskKnowBlock()
{
- Id = s.First().Id,
- Content = s.First().Content,
- StartTime = s.First().StartTime,
- StageId = s.First().StageId,
- EndTime = s.First().EndTime,
- Theme = s.First().Theme,
- Know = s.Select(x => new TaskKnowInfo()
- {
- Id = x.Id,
- KnowPoint = x.KnowPoint,
- KnowPointId = x.KnowPointId
- }).ToArray()
+ Id = s.Id,
+ Content = s.Content,
+ StartTime = s.StartTime,
+ StageId = s.Id,
+ EndTime = s.EndTime,
+ Theme = s.Theme,
+ Know = videoKnowDic.ContainsKey(s.Id)
+ ? videoKnowDic[s.Id]?.Select(x => new TaskKnowInfo()
+ {
+ Id = x.Id,
+ KnowPoint = x.KnowPoint,
+ KnowPointId = x.KnowPointId,
+ })?.ToArray()
+ : null
}).ToArray()
};
if (task.VideoType == AttachmentsInfoType.ϰ)
diff --git a/VideoAnalysisCore/Controllers/VideoTaskController.cs b/VideoAnalysisCore/Controllers/VideoTaskController.cs
index 614c2e5..ce4b059 100644
--- a/VideoAnalysisCore/Controllers/VideoTaskController.cs
+++ b/VideoAnalysisCore/Controllers/VideoTaskController.cs
@@ -33,9 +33,11 @@ namespace VideoAnalysisCore.Controllers
readonly Repository baseService;
readonly Repository videoQuestionDB;
readonly Repository videoKonwPointDB;
+ readonly Repository videoTaskStageDB;
readonly Repository videoQuestionKonwDB;
readonly Repository taskLogDB;
+
readonly RedisManager redisManager;
public readonly SenseVoice senseVoice;
public readonly FunASRNano funASRNano;
@@ -43,7 +45,7 @@ namespace VideoAnalysisCore.Controllers
private readonly IMapper mp;
public VideoTaskController(Repository baseService, RedisManager redisManager,
Repository videoQuestionDB,
- Repository videoQuestionKonwDB, Repository videoKonwPointDB, SenseVoice senseVoice, IMapper mp, Repository taskLogDB, FunASRNano funASRNano) : base(baseService)
+ Repository videoQuestionKonwDB, Repository videoKonwPointDB, SenseVoice senseVoice, IMapper mp, Repository taskLogDB, FunASRNano funASRNano, Repository videoTaskStageDB) : base(baseService)
{
this.baseService = baseService;
this.redisManager = redisManager;
@@ -54,6 +56,7 @@ namespace VideoAnalysisCore.Controllers
this.mp = mp;
this.taskLogDB = taskLogDB;
this.funASRNano = funASRNano;
+ this.videoTaskStageDB = videoTaskStageDB;
}
@@ -370,17 +373,23 @@ namespace VideoAnalysisCore.Controllers
var konwArr = await videoKonwPointDB.AsQueryable()
.Where(s => s.VideoTaskId == nowTask.Id)
.ToArrayAsync();
-
- var videoKnows = konwArr
- .GroupBy(s => s.StartTime)
+ var stageArr = await videoTaskStageDB.AsQueryable()
+ .Where(s => s.VideoTaskId == nowTask.Id)
+ .ToArrayAsync();
+ var videoKnowDic = konwArr
+ .GroupBy(s => s.StageId)
+ .ToDictionary(s => s.Key);
+ var videoKnows = stageArr
.Select(s => new VideoKnowRes()
{
- Content = s.First().Content,
- StartTime = s.First().StartTime,
- EndTime = s.First().EndTime,
- Theme = s.First().Theme,
- StageId = s.First().StageId,
- KnowPoint = string.Join(',', s.Select(x => x.KnowPoint))
+ Content = s.Content,
+ StartTime = s.StartTime,
+ EndTime = s.EndTime,
+ Theme = s.Theme,
+ StageId = s.Id,
+ KnowPoint = videoKnowDic.ContainsKey(s.Id)
+ ? string.Join(',', videoKnowDic[s.Id].Select(x => x.KnowPoint))
+ : string.Empty
}).ToArray();
if (nowTask.VideoType == AttachmentsInfoType.复习)
{
diff --git a/VideoAnalysisCore/Model/VideoKonwPoint.cs b/VideoAnalysisCore/Model/VideoKonwPoint.cs
index c675e46..b386bb5 100644
--- a/VideoAnalysisCore/Model/VideoKonwPoint.cs
+++ b/VideoAnalysisCore/Model/VideoKonwPoint.cs
@@ -65,9 +65,13 @@ namespace VideoAnalysisCore.Model
///
public string? KnowPointId { get; set; }
///
- /// 知识点来源 视频秒
+ /// 知识点占比权重
///
- public float KnowSourceTime { get; set; }
+ public float? KnowPointWeight { get; set; }
+ ///
+ /// 知识点来源
+ ///
+ public string KnowSource { get; set; }
///
/// 内容总结[不写入数据库]
///
@@ -84,5 +88,11 @@ namespace VideoAnalysisCore.Model
///
[SugarColumn(IsNullable = true)]
public long? CloudSchoolId { get; set; }
+ ///
+ /// 教材来源
+ /// 课本/试卷/挹青苑 ...
+ ///
+ [SugarColumn(IsIgnore = true)]
+ public virtual string? TextbookSource { get; set; }
}
}
diff --git a/VideoAnalysisCore/Model/VideoTaskStage.cs b/VideoAnalysisCore/Model/VideoTaskStage.cs
index 4bfb48f..8ed5c89 100644
--- a/VideoAnalysisCore/Model/VideoTaskStage.cs
+++ b/VideoAnalysisCore/Model/VideoTaskStage.cs
@@ -54,9 +54,9 @@ namespace VideoAnalysisCore.Model
///
public string? Theme { get; set; }
///
- /// 知识点来源 视频秒
+ /// 知识点来源 视频秒,来源原因
///
- public float KnowSourceTime { get; set; }
+ public string Content { get; set; }
///
/// 课程阶段
///
@@ -68,5 +68,11 @@ namespace VideoAnalysisCore.Model
///
[SugarColumn(IsNullable = true)]
public long? CloudSchoolId { get; set; }
+ ///
+ /// 教材来源
+ /// 课本/试卷/挹青苑 ...
+ ///
+ [SugarColumn(IsNullable = true)]
+ public virtual string? TextbookSource { get; set; }
}
}