feat(dashboard): 增加首页工作台并调整聊天入口
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Count, Q
|
||||
from django.db.models import Count, Q, Sum
|
||||
import json
|
||||
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse, StreamingHttpResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils.http import urlencode
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from .services import (
|
||||
@@ -28,6 +29,29 @@ from .models import KnowledgeBaseDocument
|
||||
from .regulatory_review.services.info_extract import ensure_regulatory_condition_candidates
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def home_dashboard(request: HttpRequest) -> HttpResponse:
|
||||
"""Renders the data-first home dashboard for the current user."""
|
||||
|
||||
if request.GET.get("conversation"):
|
||||
query = {"conversation": request.GET["conversation"]}
|
||||
search = (request.GET.get("q") or "").strip()
|
||||
if search:
|
||||
query["q"] = search
|
||||
return redirect(f"/chat/?{urlencode(query)}")
|
||||
|
||||
context = build_home_dashboard_context(request.user)
|
||||
return render(
|
||||
request,
|
||||
"workbench.html",
|
||||
{
|
||||
"page_title": "首页",
|
||||
"dashboard": context,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def workspace(request: HttpRequest) -> HttpResponse:
|
||||
@@ -39,7 +63,7 @@ def workspace(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
if action == "new_conversation":
|
||||
conversation = create_conversation(request.user)
|
||||
return redirect(f"/?conversation={conversation.pk}")
|
||||
return redirect(f"/chat/?conversation={conversation.pk}")
|
||||
|
||||
if action == "send_message":
|
||||
content = (request.POST.get("prompt") or "").strip()
|
||||
@@ -47,7 +71,7 @@ def workspace(request: HttpRequest) -> HttpResponse:
|
||||
conversation = create_conversation(request.user)
|
||||
if content:
|
||||
send_message(conversation, content)
|
||||
return redirect(f"/?conversation={conversation.pk}")
|
||||
return redirect(f"/chat/?conversation={conversation.pk}")
|
||||
|
||||
search = (request.GET.get("q") or "").strip()
|
||||
conversations = list_conversations(request.user, search)
|
||||
@@ -325,3 +349,139 @@ def _format_form_fill_label(batch: ApplicationFormFillBatch) -> str:
|
||||
if batch.risk_notes:
|
||||
parts.append(f"提示 {len(batch.risk_notes)}")
|
||||
return " · ".join(parts)
|
||||
|
||||
|
||||
def build_home_dashboard_context(user) -> dict[str, object]:
|
||||
conversations = Conversation.objects.filter(user=user)
|
||||
active_attachments = FileAttachment.objects.filter(user=user).exclude(
|
||||
upload_status=FileAttachment.UploadStatus.DELETED
|
||||
)
|
||||
active_knowledge_documents = KnowledgeBaseDocument.objects.filter(user=user).exclude(
|
||||
status=KnowledgeBaseDocument.Status.DELETED
|
||||
)
|
||||
knowledge_context = build_knowledge_base_context_for_user(user)
|
||||
builtin_source_count = int(knowledge_context.get("source_count") or 0)
|
||||
collection_chunk_count = int((knowledge_context.get("collection") or {}).get("count") or 0)
|
||||
managed_document_count = active_knowledge_documents.count()
|
||||
file_batches = FileSummaryBatch.objects.filter(user=user).select_related("conversation")
|
||||
regulatory_batches = RegulatoryReviewBatch.objects.filter(user=user).select_related("conversation")
|
||||
form_fill_batches = ApplicationFormFillBatch.objects.filter(user=user, is_deleted=False).select_related("conversation")
|
||||
|
||||
batch_status_counts = _build_batch_status_counts(file_batches, regulatory_batches, form_fill_batches)
|
||||
total_batches = file_batches.count() + regulatory_batches.count() + form_fill_batches.count()
|
||||
successful_batches = batch_status_counts["success"]
|
||||
handled_batches = successful_batches + batch_status_counts["failed"]
|
||||
recent_records = _build_recent_dashboard_records(
|
||||
conversations.order_by("-updated_at", "-id")[:8],
|
||||
file_batches.order_by("-created_at", "-id")[:8],
|
||||
regulatory_batches.order_by("-created_at", "-id")[:8],
|
||||
form_fill_batches.order_by("-created_at", "-id")[:8],
|
||||
)
|
||||
|
||||
return {
|
||||
"metrics": {
|
||||
"conversation_count": conversations.count(),
|
||||
"recent_conversation_count": conversations.filter(messages__isnull=False).distinct().count(),
|
||||
"attachment_count": active_attachments.count(),
|
||||
"active_attachment_count": active_attachments.filter(is_active=True).count(),
|
||||
"knowledge_document_count": managed_document_count + builtin_source_count,
|
||||
"running_batch_count": batch_status_counts["running"],
|
||||
"handled_batch_count": handled_batches,
|
||||
"success_batch_count": successful_batches,
|
||||
"waiting_batch_count": batch_status_counts["waiting"],
|
||||
"failed_batch_count": batch_status_counts["failed"],
|
||||
"total_batch_count": total_batches,
|
||||
},
|
||||
"knowledge": {
|
||||
"document_count": managed_document_count,
|
||||
"builtin_source_count": builtin_source_count,
|
||||
"total_material_count": managed_document_count + builtin_source_count,
|
||||
"active_document_count": active_knowledge_documents.filter(is_active=True).count(),
|
||||
"indexed_document_count": active_knowledge_documents.filter(indexed_chunk_count__gt=0).count(),
|
||||
"managed_chunk_count": active_knowledge_documents.aggregate(total=Sum("indexed_chunk_count"))["total"] or 0,
|
||||
"chunk_count": collection_chunk_count,
|
||||
},
|
||||
"attachments": {
|
||||
"attachment_count": active_attachments.count(),
|
||||
"active_attachment_count": active_attachments.filter(is_active=True).count(),
|
||||
"recent_attachment_count": active_attachments.order_by("-created_at", "-id")[:5].count(),
|
||||
"conversation_count": active_attachments.values("conversation_id").distinct().count(),
|
||||
},
|
||||
"workflow": {
|
||||
"file_summary_count": file_batches.count(),
|
||||
"regulatory_review_count": regulatory_batches.count(),
|
||||
"application_form_fill_count": form_fill_batches.count(),
|
||||
**batch_status_counts,
|
||||
},
|
||||
"recent_records": recent_records,
|
||||
}
|
||||
|
||||
|
||||
def _build_batch_status_counts(file_batches, regulatory_batches, form_fill_batches) -> dict[str, int]:
|
||||
running_statuses = {
|
||||
FileSummaryBatch.Status.PENDING,
|
||||
FileSummaryBatch.Status.RUNNING,
|
||||
ApplicationFormFillBatch.Status.PENDING,
|
||||
ApplicationFormFillBatch.Status.RUNNING,
|
||||
RegulatoryReviewBatch.Status.PENDING,
|
||||
RegulatoryReviewBatch.Status.RUNNING,
|
||||
}
|
||||
waiting_statuses = {
|
||||
ApplicationFormFillBatch.Status.WAITING_USER,
|
||||
RegulatoryReviewBatch.Status.WAITING_USER,
|
||||
}
|
||||
success_statuses = {
|
||||
FileSummaryBatch.Status.SUCCESS,
|
||||
RegulatoryReviewBatch.Status.SUCCESS,
|
||||
ApplicationFormFillBatch.Status.SUCCESS,
|
||||
ApplicationFormFillBatch.Status.PARTIAL_SUCCESS,
|
||||
}
|
||||
failed_statuses = {
|
||||
FileSummaryBatch.Status.FAILED,
|
||||
RegulatoryReviewBatch.Status.FAILED,
|
||||
ApplicationFormFillBatch.Status.FAILED,
|
||||
}
|
||||
statuses = [
|
||||
*file_batches.values_list("status", flat=True),
|
||||
*regulatory_batches.values_list("status", flat=True),
|
||||
*form_fill_batches.values_list("status", flat=True),
|
||||
]
|
||||
return {
|
||||
"running": sum(1 for status in statuses if status in running_statuses),
|
||||
"waiting": sum(1 for status in statuses if status in waiting_statuses),
|
||||
"success": sum(1 for status in statuses if status in success_statuses),
|
||||
"failed": sum(1 for status in statuses if status in failed_statuses),
|
||||
}
|
||||
|
||||
|
||||
def _build_recent_dashboard_records(conversations, file_batches, regulatory_batches, form_fill_batches) -> list[dict[str, object]]:
|
||||
records = []
|
||||
for conversation in conversations:
|
||||
records.append(
|
||||
{
|
||||
"type": "对话",
|
||||
"title": conversation.title or "新对话",
|
||||
"status": "已更新",
|
||||
"updated_at": conversation.updated_at,
|
||||
"url": f"/chat/?conversation={conversation.pk}",
|
||||
}
|
||||
)
|
||||
for batch in file_batches:
|
||||
records.append(_batch_record(batch, "文件汇总"))
|
||||
for batch in regulatory_batches:
|
||||
status = batch.status
|
||||
risk_label = _format_risk_label(batch.risk_summary or {})
|
||||
records.append(_batch_record(batch, "法规核查", status_label=risk_label or status))
|
||||
for batch in form_fill_batches:
|
||||
records.append(_batch_record(batch, "申报填表"))
|
||||
return sorted(records, key=lambda item: item["updated_at"], reverse=True)[:8]
|
||||
|
||||
|
||||
def _batch_record(batch, record_type: str, status_label: str | None = None) -> dict[str, object]:
|
||||
return {
|
||||
"type": record_type,
|
||||
"title": batch.batch_no,
|
||||
"status": status_label or batch.status,
|
||||
"updated_at": batch.created_at,
|
||||
"url": f"/chat/?conversation={batch.conversation_id}",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user