Skip to content

Commit 851f84a

Browse files
committed
feat: add endpoint for retrieving portal historical conversations with pagination
1 parent 85421b9 commit 851f84a

7 files changed

Lines changed: 157 additions & 48 deletions

File tree

‎apps/locales/en_US/LC_MESSAGES/django.po‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9602,3 +9602,6 @@ msgstr "Set chat user quota"
96029602

96039603
msgid "The token quota for the current period has been exhausted. Please contact the administrator."
96049604
msgstr ""
9605+
9606+
msgid "Get portal historical conversation by page"
9607+
msgstr ""

‎apps/locales/zh_CN/LC_MESSAGES/django.po‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9742,6 +9742,9 @@ msgstr "设置对话用户配额"
97429742
msgid "The token quota for the current period has been exhausted. Please contact the administrator."
97439743
msgstr "当前周期 Tokens 配额已用尽,请联系管理员。"
97449744

9745+
msgid "Get portal historical conversation by page"
9746+
msgstr "分页获取门户历史会话"
9747+
97459748
#: apps/xpack/views/system_chat_user.py:101
97469749
msgid "Batch set chat user quota"
97479750
msgstr "批量设置对话用户配额"

‎apps/locales/zh_Hant/LC_MESSAGES/django.po‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9740,4 +9740,7 @@ msgid "Set chat user quota"
97409740
msgstr "設置對話用戶配額"
97419741

97429742
msgid "The token quota for the current period has been exhausted. Please contact the administrator."
9743-
msgstr "當前週期 Tokens 配額已用盡,請聯繫管理員。"
9743+
msgstr "當前週期 Tokens 配額已用盡,請聯繫管理員。"
9744+
9745+
msgid "Get portal historical conversation by page"
9746+
msgstr "分頁獲取門戶歷史會話"

‎apps/portal/api/portal.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,34 @@ class Logout(APIMixin):
9595
@staticmethod
9696
def get_response():
9797
return DefaultResultSerializer
98+
99+
class Conversation(APIMixin):
100+
@staticmethod
101+
def get_parameters():
102+
return [
103+
OpenApiParameter(
104+
name='current_page',
105+
description='当前页码',
106+
type=OpenApiTypes.INT,
107+
location='path',
108+
required=True,
109+
),
110+
OpenApiParameter(
111+
name='page_size',
112+
description='每页数量',
113+
type=OpenApiTypes.INT,
114+
location='path',
115+
required=True,
116+
),
117+
OpenApiParameter(
118+
name='name',
119+
description='应用名称搜索',
120+
type=OpenApiTypes.STR,
121+
location='query',
122+
required=False,
123+
),
124+
]
125+
126+
@staticmethod
127+
def get_response():
128+
return DefaultResultSerializer

‎apps/portal/serializers/portal.py‎

Lines changed: 86 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
import uuid_utils.compat as uuid
1111
from django.core import signing
1212
from django.core.cache import cache
13-
from django.db.models import Exists, OuterRef, QuerySet
13+
from django.db.models import Exists, OuterRef
1414
from django.utils.translation import gettext_lazy as _
1515
from rest_framework import serializers
1616

17-
from application.models import Application
17+
from application.models import Application, Chat
1818
from application.models.application_access_token import ApplicationAccessToken
1919
from common.auth.common import FileToken
2020
from common.constants.authentication_type import AuthenticationType
@@ -80,7 +80,7 @@ def _handle_file_field(self, portal, field_name, value):
8080
old_url = getattr(portal, field_name)
8181
if old_url:
8282
old_file_id = old_url.split("/")[-1]
83-
QuerySet(File).filter(id=old_file_id).delete()
83+
File.objects.filter(id=old_file_id).delete()
8484
new_url = self._upload_file(value)
8585
setattr(portal, field_name, new_url)
8686
else:
@@ -103,6 +103,42 @@ def edit(self, instance, with_valid=True):
103103
return PortalSerializer.Model(portal).data
104104

105105

106+
class PortalApplicationAuthMixin:
107+
"""门户应用授权过滤公共逻辑"""
108+
109+
@staticmethod
110+
def get_authorized_application_queryset(user_id):
111+
public_apps = ApplicationAccessToken.objects.filter(
112+
application_id=OuterRef('id'),
113+
authentication=False
114+
)
115+
if not ChatUser.objects.filter(id=user_id).exists():
116+
return Application.objects.filter(Exists(public_apps))
117+
authed_token_exists = ApplicationAccessToken.objects.filter(
118+
application_id=OuterRef('id'),
119+
authentication=True
120+
)
121+
direct_auth = ResourceChatUserAuthorize.objects.filter(
122+
resource_id=OuterRef('id'),
123+
resource_type=ResourceType.APPLICATION.value,
124+
is_auth=True,
125+
user_id=user_id
126+
)
127+
user_groups = UserGroupRelation.objects.filter(
128+
user_id=user_id
129+
).values_list('group_id', flat=True)
130+
group_auth = ResourceChatUserGroupAuthorize.objects.filter(
131+
resource_id=OuterRef('id'),
132+
resource_type=ResourceType.APPLICATION.value,
133+
is_auth=True,
134+
user_group_id__in=user_groups
135+
)
136+
return Application.objects.filter(
137+
Exists(public_apps) |
138+
(Exists(authed_token_exists) & (Exists(direct_auth) | Exists(group_auth)))
139+
)
140+
141+
106142
class ApplicationResponseSerializer(serializers.Serializer):
107143
id = serializers.CharField(required=True)
108144
name = serializers.CharField(required=True)
@@ -116,62 +152,72 @@ class ApplicationResponseSerializer(serializers.Serializer):
116152

117153
class PortalApplicationSerializer(serializers.Serializer):
118154

119-
class Query(serializers.Serializer):
155+
class Query(PortalApplicationAuthMixin, serializers.Serializer):
120156
name = serializers.CharField(required=False, allow_blank=True, label=_('Application Name'),
121157
help_text=_('Application name'))
122158

123159
def get_query_set(self):
124-
queryset = Application.objects.filter(is_publish=True).only(
125-
'id', 'name', 'desc', 'icon', 'type',
126-
'dialogue_number', 'prologue', 'is_publish', 'create_time'
127-
)
160+
queryset = Application.objects.filter(is_publish=True)
128161
name = self.data.get('name')
129162
if name:
130163
queryset = queryset.filter(name__icontains=name)
131164
return queryset.order_by('-create_time')
132165

133-
def _apply_auth_filter(self, queryset, user_id):
134-
chat_user_exists = ChatUser.objects.filter(id=user_id).exists()
135-
public_apps = ApplicationAccessToken.objects.filter(
136-
application_id=OuterRef('id'),
137-
authentication=False
138-
)
139-
if not chat_user_exists:
140-
return queryset.filter(Exists(public_apps))
141-
authed_token_exists = ApplicationAccessToken.objects.filter(
142-
application_id=OuterRef('id'),
143-
authentication=True
144-
)
145-
direct_auth = ResourceChatUserAuthorize.objects.filter(
146-
resource_id=OuterRef('id'),
147-
resource_type=ResourceType.APPLICATION.value,
148-
is_auth=True,
149-
user_id=user_id
166+
def page(self, current_page, page_size, user_id, with_valid=True):
167+
if with_valid:
168+
self.is_valid(raise_exception=True)
169+
queryset = self.get_query_set()
170+
queryset = queryset.filter(
171+
id__in=self.get_authorized_application_queryset(user_id).values('id')
150172
)
151-
user_groups = UserGroupRelation.objects.filter(
152-
user_id=user_id
153-
).values_list('group_id', flat=True)
154-
group_auth = ResourceChatUserGroupAuthorize.objects.filter(
155-
resource_id=OuterRef('id'),
156-
resource_type=ResourceType.APPLICATION.value,
157-
is_auth=True,
158-
user_group_id__in=user_groups
173+
return page_search(
174+
current_page,
175+
page_size,
176+
queryset,
177+
post_records_handler=lambda app: ApplicationResponseSerializer(app).data,
159178
)
160-
return queryset.filter(
161-
Exists(public_apps) |
162-
(Exists(authed_token_exists) & (Exists(direct_auth) | Exists(group_auth)))
179+
180+
181+
class PortalHistoricalConversationResponseSerializer(serializers.Serializer):
182+
id = serializers.CharField(required=True)
183+
abstract = serializers.CharField(required=True)
184+
create_time = serializers.CharField(required=True)
185+
update_time = serializers.CharField(required=True)
186+
application = serializers.SerializerMethodField()
187+
188+
def get_application(self, chat):
189+
return {
190+
'id': str(chat.application_id),
191+
'name': chat.application.name,
192+
'icon': chat.application.icon,
193+
}
194+
195+
196+
class PortalHistoricalConversationSerializer(serializers.Serializer):
197+
198+
class Query(PortalApplicationAuthMixin, serializers.Serializer):
199+
name = serializers.CharField(required=False, allow_blank=True, label=_('Application Name'),
200+
help_text=_('Application name'))
201+
202+
def get_query_set(self, user_id):
203+
queryset = Chat.objects.filter(
204+
chat_user_id=user_id,
205+
is_deleted=False,
206+
application_id__in=self.get_authorized_application_queryset(user_id).values('id')
163207
)
208+
name = self.data.get('name')
209+
if name:
210+
queryset = queryset.filter(application__name__icontains=name)
211+
return queryset.select_related('application').order_by('-update_time', 'id')
164212

165213
def page(self, current_page, page_size, user_id, with_valid=True):
166214
if with_valid:
167215
self.is_valid(raise_exception=True)
168-
queryset = self.get_query_set()
169-
queryset = self._apply_auth_filter(queryset, user_id)
170216
return page_search(
171217
current_page,
172218
page_size,
173-
queryset,
174-
post_records_handler=lambda app: ApplicationResponseSerializer(app).data,
219+
self.get_query_set(user_id),
220+
post_records_handler=lambda chat: PortalHistoricalConversationResponseSerializer(chat).data,
175221
)
176222

177223

‎apps/portal/urls.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
from . import views
44

5-
app_name = 'portal'
5+
app_name = "portal"
66

77
urlpatterns = [
8-
path('portal', views.PortalView.as_view()),
9-
path('portal/info', views.PortalInfoView.as_view()),
10-
path('portal/login', views.PortalLoginView.as_view()),
11-
path('portal/logout', views.PortalLogoutView.as_view()),
12-
path('portal/application/<int:current_page>/<int:page_size>', views.PortalApplicationView.as_view()),
8+
path("portal", views.PortalView.as_view()),
9+
path("portal/info", views.PortalInfoView.as_view()),
10+
path("portal/login", views.PortalLoginView.as_view()),
11+
path("portal/logout", views.PortalLogoutView.as_view()),
12+
path("portal/application/<int:current_page>/<int:page_size>", views.PortalApplicationView.as_view()),
13+
path(
14+
"portal/historical_conversation/<int:current_page>/<int:page_size>",
15+
views.PortalHistoricalConversationView.as_view(),
16+
),
1317
]

‎apps/portal/views/portal.py‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
from common.utils.common import query_params_to_single_dict
2323
from django.core.cache import cache
2424
from portal.api.portal import PortalAPI
25-
from portal.serializers.portal import PortalSerializer, PortalApplicationSerializer, PortalLoginSerializer
25+
from portal.serializers.portal import PortalSerializer, PortalApplicationSerializer, PortalLoginSerializer, \
26+
PortalHistoricalConversationSerializer
2627

2728

2829
class PortalView(APIView):
@@ -77,6 +78,24 @@ def get(self, request: Request, current_page: int, page_size: int):
7778
).page(current_page, page_size, str(request.user.id)))
7879

7980

81+
class PortalHistoricalConversationView(APIView):
82+
authentication_classes = [TokenAuth]
83+
84+
@extend_schema(
85+
methods=['GET'],
86+
description=_('Get portal historical conversation by page'),
87+
summary=_('Get portal historical conversation by page'),
88+
operation_id=_('Get portal historical conversation by page'),
89+
parameters=PortalAPI.Conversation.get_parameters(),
90+
responses=PortalAPI.Conversation.get_response(),
91+
tags=[_('Portal')]
92+
)
93+
def get(self, request: Request, current_page: int, page_size: int):
94+
return result.success(PortalHistoricalConversationSerializer.Query(
95+
data={**query_params_to_single_dict(request.query_params)}
96+
).page(current_page, page_size, str(request.user.id)))
97+
98+
8099
class PortalLoginView(APIView):
81100

82101
@extend_schema(

0 commit comments

Comments
 (0)