From 8fafc57de938601d5db89a8fc22f509745fcd4da Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Tue, 8 Sep 2026 17:04:12 +0800 Subject: [PATCH] refactor: enhance portal application settings retrieval and improve chat history mapping --- apps/chat/serializers/portal.py | 123 +++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 26 deletions(-) diff --git a/apps/chat/serializers/portal.py b/apps/chat/serializers/portal.py index c58d3450e6e..c5a2cd7648b 100644 --- a/apps/chat/serializers/portal.py +++ b/apps/chat/serializers/portal.py @@ -7,12 +7,15 @@ @desc: 门户配置序列化器 """ +from django.core.cache import cache from django.db.models import Exists, OuterRef from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from application.models import Application, Chat from application.models.application_access_token import ApplicationAccessToken +from common.constants.cache_version import Cache_Version +from common.database_model_manage.database_model_manage import DatabaseModelManage from common.db.search import page_search from system_manage.models.chat_user import ( ChatUser, @@ -23,14 +26,61 @@ ) +def build_application_setting_dict(setting, show_source): + return { + "show_source": show_source, + "show_history": setting.show_history, + "draggable": setting.draggable, + "show_guide": setting.show_guide, + "avatar": setting.avatar, + "show_avatar": setting.show_avatar, + "float_icon": setting.float_icon, + "disclaimer": setting.disclaimer, + "disclaimer_value": setting.disclaimer_value, + "custom_theme": setting.custom_theme or {"theme_color": "", "header_font_color": ""}, + "user_avatar": setting.user_avatar, + "show_user_avatar": setting.show_user_avatar, + "show_share": setting.show_share, + "float_location": setting.float_location or {"x": {"type": "", "value": ""}, "y": {"type": "", "value": ""}}, + "chat_background": setting.chat_background, + } + + +def get_application_settings_map(application_ids): + """批量返回 application_id -> 门户设置信息;license 无效或模型缺失时返回空 dict""" + application_setting_model = DatabaseModelManage.get_model("application_setting") + if application_setting_model is None or not application_ids: + return {} + license_is_valid = cache.get( + Cache_Version.SYSTEM.get_key(key="license_is_valid"), version=Cache_Version.SYSTEM.get_version() + ) + if not license_is_valid: + return {} + settings = application_setting_model.objects.filter(application_id__in=application_ids) + access_tokens = ApplicationAccessToken.objects.filter(application_id__in=application_ids).values_list( + "application_id", "show_source" + ) + token_map = {str(application_id): show_source for application_id, show_source in access_tokens} + return { + str(setting.application_id): build_application_setting_dict( + setting, token_map.get(str(setting.application_id), False) + ) + for setting in settings + } + + class PortalApplicationAuthMixin: """门户应用授权过滤公共逻辑""" @staticmethod - def get_authorized_application_queryset(user_id): + def get_authorized_application_ids(user_id): public_apps = ApplicationAccessToken.objects.filter(application_id=OuterRef("id"), authentication=False) if not ChatUser.objects.filter(id=user_id).exists(): - return Application.objects.filter(is_publish=True, is_portal=True).filter(Exists(public_apps)) + return ( + Application.objects.filter(is_publish=True, is_portal=True) + .filter(Exists(public_apps)) + .values_list("id", flat=True) + ) authed_token_exists = ApplicationAccessToken.objects.filter(application_id=OuterRef("id"), authentication=True) direct_auth = ResourceChatUserAuthorize.objects.filter( resource_id=OuterRef("id"), resource_type=ResourceType.APPLICATION.value, is_auth=True, user_id=user_id @@ -42,8 +92,10 @@ def get_authorized_application_queryset(user_id): is_auth=True, user_group_id__in=user_groups, ) - return Application.objects.filter(is_publish=True, is_portal=True).filter( - Exists(public_apps) | (Exists(authed_token_exists) & (Exists(direct_auth) | Exists(group_auth))) + return ( + Application.objects.filter(is_publish=True, is_portal=True) + .filter(Exists(public_apps) | (Exists(authed_token_exists) & (Exists(direct_auth) | Exists(group_auth)))) + .values_list("id", flat=True) ) @@ -76,7 +128,7 @@ def page(self, current_page, page_size, user_id, with_valid=True): if with_valid: self.is_valid(raise_exception=True) queryset = self.get_query_set() - queryset = queryset.filter(id__in=self.get_authorized_application_queryset(user_id).values("id")) + queryset = queryset.filter(id__in=self.get_authorized_application_ids(user_id)) return page_search( current_page, page_size, @@ -85,19 +137,25 @@ def page(self, current_page, page_size, user_id, with_valid=True): ) -class PortalHistoricalConversationResponseSerializer(serializers.Serializer): - id = serializers.CharField(required=True) - abstract = serializers.CharField(required=True) - create_time = serializers.CharField(required=True) - update_time = serializers.CharField(required=True) - application = serializers.SerializerMethodField() - - def get_application(self, chat): - return { - "id": str(chat.application_id), - "name": chat.application.name, - "icon": chat.application.icon, - } +def get_recent_chats_map(user_id, application_ids, limit=5): + """批量返回 application_id -> 该应用最近的 limit 条历史会话;show_history=false 的应用不在此表里""" + chats = Chat.objects.filter(chat_user_id=user_id, is_deleted=False, application_id__in=application_ids).order_by( + "application_id", "-update_time", "id" + ) + result = {} + for chat in chats: + key = str(chat.application_id) + if len(result.get(key, [])) >= limit: + continue + result.setdefault(key, []).append( + { + "id": str(chat.id), + "abstract": chat.abstract, + "create_time": str(chat.create_time), + "update_time": str(chat.update_time), + } + ) + return result class PortalHistoricalConversationSerializer(serializers.Serializer): @@ -107,22 +165,35 @@ class Query(PortalApplicationAuthMixin, serializers.Serializer): ) def get_query_set(self, user_id): - queryset = Chat.objects.filter( - chat_user_id=user_id, - is_deleted=False, - application_id__in=self.get_authorized_application_queryset(user_id).values("id"), + # 主表是应用:返回用户有权限访问的已发布门户应用 + queryset = Application.objects.filter( + is_publish=True, + is_portal=True, + id__in=self.get_authorized_application_ids(user_id), ) name = self.data.get("name") if name: - queryset = queryset.filter(application__name__icontains=name) - return queryset.select_related("application").order_by("-update_time", "id") + queryset = queryset.filter(name__icontains=name) + return queryset.order_by("-create_time") def page(self, current_page, page_size, user_id, with_valid=True): if with_valid: self.is_valid(raise_exception=True) - return page_search( + result = page_search( current_page, page_size, self.get_query_set(user_id), - post_records_handler=lambda chat: PortalHistoricalConversationResponseSerializer(chat).data, + post_records_handler=lambda app: { + "id": str(app.id), + "name": app.name, + "icon": app.icon, + }, ) + app_ids = [record["id"] for record in result["records"]] + settings_map = get_application_settings_map(app_ids) + show_history_ids = [aid for aid in app_ids if settings_map.get(aid, {}).get("show_history")] + chat_map = get_recent_chats_map(user_id, show_history_ids) if show_history_ids else {} + for record in result["records"]: + record.update(settings_map.get(record["id"], {})) + record["conversations"] = chat_map.get(record["id"], []) + return result