Compare commits
2 Commits
809aab9e02
...
cb10c42d11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb10c42d11 | ||
|
|
758eee8ac6 |
@@ -127,6 +127,10 @@ class BailianService:
|
|||||||
evaluation.reasoning = response_content
|
evaluation.reasoning = response_content
|
||||||
|
|
||||||
evaluation.save()
|
evaluation.save()
|
||||||
|
|
||||||
|
# 同步结果到参赛项目 (如果关联了)
|
||||||
|
self._sync_evaluation_to_project(evaluation)
|
||||||
|
|
||||||
return evaluation
|
return evaluation
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -136,6 +140,103 @@ class BailianService:
|
|||||||
evaluation.save()
|
evaluation.save()
|
||||||
return evaluation
|
return evaluation
|
||||||
|
|
||||||
|
def _sync_evaluation_to_project(self, evaluation: AIEvaluation):
|
||||||
|
"""
|
||||||
|
将AI评估结果同步到关联的参赛项目(评分和评语)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
task = evaluation.task
|
||||||
|
if not task.project:
|
||||||
|
return
|
||||||
|
|
||||||
|
project = task.project
|
||||||
|
competition = project.competition
|
||||||
|
|
||||||
|
# 1. 确定评委身份 (Based on Template)
|
||||||
|
# 用户要求:评委显示的是模板名称
|
||||||
|
template_name = evaluation.template.name if evaluation.template else "AI智能评委"
|
||||||
|
# 使用固定前缀 + template_id 确保唯一性,这样同一个模板在不同项目里是同一个评委
|
||||||
|
openid = f"ai_judge_{evaluation.template.id}" if evaluation.template else "ai_judge_default"
|
||||||
|
|
||||||
|
# 延迟导入以避免循环依赖
|
||||||
|
from shop.models import WeChatUser
|
||||||
|
from competition.models import CompetitionEnrollment, Score, Comment, ScoreDimension
|
||||||
|
|
||||||
|
# 获取或创建虚拟评委用户
|
||||||
|
user, created = WeChatUser.objects.get_or_create(
|
||||||
|
openid=openid,
|
||||||
|
defaults={
|
||||||
|
'nickname': template_name,
|
||||||
|
'avatar_url': 'https://ui-avatars.com/api/?name=AI&background=random&color=fff'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果名字不匹配(比如模板改名了),更新它
|
||||||
|
if user.nickname != template_name:
|
||||||
|
user.nickname = template_name
|
||||||
|
user.save(update_fields=['nickname'])
|
||||||
|
|
||||||
|
# 2. 确保评委已报名 (Enrollment)
|
||||||
|
enrollment, _ = CompetitionEnrollment.objects.get_or_create(
|
||||||
|
competition=competition,
|
||||||
|
user=user,
|
||||||
|
defaults={
|
||||||
|
'role': 'judge',
|
||||||
|
'status': 'approved'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. 同步评分 (Score)
|
||||||
|
if evaluation.score is not None:
|
||||||
|
# 尝试找到匹配的维度
|
||||||
|
# 优先级:完全匹配模板名称 > 包含"AI"的维度 > 第一个维度
|
||||||
|
dimensions = competition.score_dimensions.all()
|
||||||
|
target_dimension = None
|
||||||
|
|
||||||
|
for dim in dimensions:
|
||||||
|
if dim.name == template_name:
|
||||||
|
target_dimension = dim
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_dimension:
|
||||||
|
for dim in dimensions:
|
||||||
|
if "AI" in dim.name.upper():
|
||||||
|
target_dimension = dim
|
||||||
|
break
|
||||||
|
|
||||||
|
# 如果还是没找到,尝试创建一个默认的 "AI评分" 维度?
|
||||||
|
# 或者使用第一个维度。考虑到用户说"对应的AI评分",如果没有对应的,可能需要创建一个?
|
||||||
|
# 为了安全起见,如果找不到明确的AI维度,且存在维度,就用第一个;否则不评分。
|
||||||
|
if not target_dimension and dimensions.exists():
|
||||||
|
target_dimension = dimensions.first()
|
||||||
|
|
||||||
|
if target_dimension:
|
||||||
|
Score.objects.update_or_create(
|
||||||
|
project=project,
|
||||||
|
judge=enrollment,
|
||||||
|
dimension=target_dimension,
|
||||||
|
defaults={'score': evaluation.score}
|
||||||
|
)
|
||||||
|
logger.info(f"Synced AI score {evaluation.score} to project {project.id} dimension {target_dimension.name}")
|
||||||
|
|
||||||
|
# 4. 同步评语 (Comment)
|
||||||
|
if evaluation.evaluation:
|
||||||
|
# 检查是否已存在该评委的评语,避免重复
|
||||||
|
comment = Comment.objects.filter(project=project, judge=enrollment).first()
|
||||||
|
if comment:
|
||||||
|
comment.content = evaluation.evaluation
|
||||||
|
comment.save()
|
||||||
|
else:
|
||||||
|
Comment.objects.create(
|
||||||
|
project=project,
|
||||||
|
judge=enrollment,
|
||||||
|
content=evaluation.evaluation
|
||||||
|
)
|
||||||
|
logger.info(f"Synced AI comment to project {project.id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to sync evaluation to project: {e}")
|
||||||
|
|
||||||
def summarize_task(self, task):
|
def summarize_task(self, task):
|
||||||
"""
|
"""
|
||||||
对转写任务进行总结
|
对转写任务进行总结
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-03-11 14:10
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ai_services', '0005_aievaluationtemplate_alter_aievaluation_options_and_more'),
|
||||||
|
('competition', '0003_competition_project_visibility'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transcriptiontask',
|
||||||
|
name='project',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='transcription_tasks', to='competition.project', verbose_name='关联参赛项目'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -23,6 +23,15 @@ class TranscriptionTask(models.Model):
|
|||||||
summary_data = models.JSONField(verbose_name=_('总结原始数据'), blank=True, null=True, help_text=_('阿里云返回的Summarization完整JSON'))
|
summary_data = models.JSONField(verbose_name=_('总结原始数据'), blank=True, null=True, help_text=_('阿里云返回的Summarization完整JSON'))
|
||||||
auto_chapters_data = models.JSONField(verbose_name=_('章节原始数据'), blank=True, null=True, help_text=_('阿里云返回的AutoChapters完整JSON'))
|
auto_chapters_data = models.JSONField(verbose_name=_('章节原始数据'), blank=True, null=True, help_text=_('阿里云返回的AutoChapters完整JSON'))
|
||||||
|
|
||||||
|
project = models.ForeignKey(
|
||||||
|
'competition.Project',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='transcription_tasks',
|
||||||
|
verbose_name=_('关联参赛项目')
|
||||||
|
)
|
||||||
|
|
||||||
transcription = models.TextField(verbose_name=_('逐字稿'), blank=True, null=True)
|
transcription = models.TextField(verbose_name=_('逐字稿'), blank=True, null=True)
|
||||||
summary = models.TextField(verbose_name=_('AI总结'), blank=True, null=True)
|
summary = models.TextField(verbose_name=_('AI总结'), blank=True, null=True)
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,14 @@ class AIEvaluationSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class TranscriptionTaskSerializer(serializers.ModelSerializer):
|
class TranscriptionTaskSerializer(serializers.ModelSerializer):
|
||||||
ai_evaluations = AIEvaluationSerializer(many=True, read_only=True)
|
ai_evaluations = AIEvaluationSerializer(many=True, read_only=True)
|
||||||
|
project_title = serializers.CharField(source='project.title', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TranscriptionTask
|
model = TranscriptionTask
|
||||||
fields = ['id', 'file_url', 'task_id', 'status', 'transcription', 'summary', 'error_message', 'created_at', 'updated_at', 'transcription_data', 'summary_data', 'auto_chapters_data', 'ai_evaluations']
|
fields = ['id', 'file_url', 'task_id', 'status', 'transcription', 'summary', 'error_message', 'created_at', 'updated_at', 'transcription_data', 'summary_data', 'auto_chapters_data', 'ai_evaluations', 'project', 'project_title']
|
||||||
read_only_fields = ['id', 'file_url', 'task_id', 'status', 'transcription', 'summary', 'error_message', 'created_at', 'updated_at', 'transcription_data', 'summary_data', 'auto_chapters_data', 'ai_evaluations']
|
read_only_fields = ['id', 'file_url', 'task_id', 'status', 'transcription', 'summary', 'error_message', 'created_at', 'updated_at', 'transcription_data', 'summary_data', 'auto_chapters_data', 'ai_evaluations', 'project_title']
|
||||||
|
|
||||||
class TranscriptionUploadSerializer(serializers.Serializer):
|
class TranscriptionUploadSerializer(serializers.Serializer):
|
||||||
file = serializers.FileField(help_text="上传的音频文件")
|
file = serializers.FileField(help_text="上传的音频文件", required=False)
|
||||||
|
file_url = serializers.URLField(help_text="音频文件的URL地址", required=False)
|
||||||
|
project_id = serializers.IntegerField(help_text="关联的参赛项目ID", required=False)
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class TranscriptionTaskViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
file_obj = request.FILES.get('file')
|
file_obj = request.FILES.get('file')
|
||||||
file_url = request.data.get('file_url')
|
file_url = request.data.get('file_url')
|
||||||
|
project_id = request.data.get('project_id')
|
||||||
|
|
||||||
if not file_obj and not file_url:
|
if not file_obj and not file_url:
|
||||||
return Response({'error': '请提供文件或文件URL'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': '请提供文件或文件URL'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -104,10 +105,17 @@ class TranscriptionTaskViewSet(viewsets.ModelViewSet):
|
|||||||
oss_url = file_url
|
oss_url = file_url
|
||||||
|
|
||||||
# 2. 创建数据库记录
|
# 2. 创建数据库记录
|
||||||
task_record = TranscriptionTask.objects.create(
|
task_data = {
|
||||||
file_url=oss_url,
|
'file_url': oss_url,
|
||||||
status=TranscriptionTask.Status.PENDING
|
'status': TranscriptionTask.Status.PENDING
|
||||||
)
|
}
|
||||||
|
if project_id:
|
||||||
|
try:
|
||||||
|
task_data['project_id'] = int(project_id)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass # Ignore invalid project_id
|
||||||
|
|
||||||
|
task_record = TranscriptionTask.objects.create(**task_data)
|
||||||
|
|
||||||
# 3. 调用听悟接口创建任务
|
# 3. 调用听悟接口创建任务
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ class CompetitionEnrollmentAdmin(ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Project)
|
@admin.register(Project)
|
||||||
class ProjectAdmin(ModelAdmin):
|
class ProjectAdmin(ModelAdmin):
|
||||||
list_display = ['title', 'competition', 'contestant', 'status', 'final_score', 'created_at']
|
list_display = ['id', 'title', 'competition', 'contestant', 'status', 'final_score', 'created_at']
|
||||||
list_filter = ['competition', 'status']
|
list_filter = ['competition', 'status']
|
||||||
search_fields = ['title', 'contestant__user__nickname']
|
search_fields = ['id', 'title', 'contestant__user__nickname']
|
||||||
inlines = [ProjectFileInline]
|
inlines = [ProjectFileInline]
|
||||||
readonly_fields = ['final_score']
|
readonly_fields = ['id', 'final_score']
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('基本信息', {
|
('基本信息', {
|
||||||
|
|||||||
Reference in New Issue
Block a user