segment_mentions.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. """
  2. Segment Mentions API
  3. 片段@提及和通知API接口
  4. """
  5. from flask import request
  6. from flask_login import current_user
  7. from flask_restx import Resource, fields, marshal_with
  8. from werkzeug.exceptions import BadRequest, NotFound
  9. from controllers.console import api
  10. from controllers.console.wraps import (
  11. account_initialization_required,
  12. setup_required,
  13. )
  14. from libs.login import login_required
  15. from services.dataset_service import DatasetService, DocumentService
  16. from services.segment_mention_service import SegmentMentionService
  17. # Response fields
  18. comment_fields = {
  19. 'id': fields.String,
  20. 'content': fields.String,
  21. 'account_id': fields.String,
  22. 'account_name': fields.String,
  23. 'account_avatar': fields.String,
  24. 'mentioned_account_ids': fields.List(fields.String),
  25. 'created_at': fields.String,
  26. }
  27. comment_list_fields = {
  28. 'comments': fields.List(fields.Nested(comment_fields)),
  29. 'total': fields.Integer,
  30. }
  31. notification_fields = {
  32. 'id': fields.String,
  33. 'type': fields.String,
  34. 'title': fields.String,
  35. 'content': fields.String,
  36. 'sender_id': fields.String,
  37. 'sender_name': fields.String,
  38. 'dataset_id': fields.String,
  39. 'dataset_name': fields.String,
  40. 'segment_id': fields.String,
  41. 'segment_preview': fields.String,
  42. 'link_url': fields.String,
  43. 'is_read': fields.Boolean,
  44. 'created_at': fields.String,
  45. }
  46. notification_list_fields = {
  47. 'notifications': fields.List(fields.Nested(notification_fields)),
  48. 'unread_count': fields.Integer,
  49. 'total': fields.Integer,
  50. }
  51. class SegmentCommentsApi(Resource):
  52. """片段评论API"""
  53. @setup_required
  54. @login_required
  55. @account_initialization_required
  56. @marshal_with(comment_list_fields)
  57. def get(self, dataset_id: str, segment_id: str):
  58. """
  59. 获取片段的评论列表
  60. Args:
  61. dataset_id: 知识库ID
  62. segment_id: 片段ID
  63. """
  64. dataset_id_str = str(dataset_id)
  65. segment_id_str = str(segment_id)
  66. # 权限检查
  67. dataset = DatasetService.get_dataset(dataset_id_str)
  68. if not dataset:
  69. raise NotFound("Dataset not found.")
  70. DatasetService.check_dataset_permission(dataset, current_user)
  71. # 获取评论
  72. limit = request.args.get('limit', 50, type=int)
  73. comments = SegmentMentionService.get_segment_comments(
  74. dataset_id=dataset_id_str,
  75. segment_id=segment_id_str,
  76. limit=min(limit, 100) # 最多100条
  77. )
  78. return {
  79. 'comments': comments,
  80. 'total': len(comments)
  81. }
  82. @setup_required
  83. @login_required
  84. @account_initialization_required
  85. @marshal_with(comment_fields)
  86. def post(self, dataset_id: str, segment_id: str):
  87. """
  88. 创建评论
  89. Args:
  90. dataset_id: 知识库ID
  91. segment_id: 片段ID
  92. Request Body:
  93. - content: 评论内容
  94. - mentioned_account_ids: @提及的用户ID列表
  95. """
  96. dataset_id_str = str(dataset_id)
  97. segment_id_str = str(segment_id)
  98. # 权限检查
  99. dataset = DatasetService.get_dataset(dataset_id_str)
  100. if not dataset:
  101. raise NotFound("Dataset not found.")
  102. DatasetService.check_dataset_permission(dataset, current_user)
  103. # 获取请求数据
  104. data = request.get_json()
  105. content = data.get('content', '').strip()
  106. mentioned_account_ids = data.get('mentioned_account_ids', [])
  107. # 调试日志
  108. import logging
  109. logger = logging.getLogger(__name__)
  110. logger.info(f"Creating comment with mentioned_account_ids: {mentioned_account_ids}")
  111. if not content:
  112. raise BadRequest("Comment content cannot be empty.")
  113. if len(content) > 2000:
  114. raise BadRequest("Comment content is too long (max 2000 characters).")
  115. # 创建评论
  116. comment = SegmentMentionService.create_comment(
  117. tenant_id=str(dataset.tenant_id),
  118. dataset_id=dataset_id_str,
  119. segment_id=segment_id_str,
  120. content=content,
  121. account_id=str(current_user.id),
  122. mentioned_account_ids=mentioned_account_ids
  123. )
  124. return comment, 201
  125. class SegmentCommentApi(Resource):
  126. """单个评论API"""
  127. @setup_required
  128. @login_required
  129. @account_initialization_required
  130. def delete(self, dataset_id: str, segment_id: str, comment_id: str):
  131. """
  132. 删除评论
  133. Args:
  134. dataset_id: 知识库ID
  135. segment_id: 片段ID
  136. comment_id: 评论ID
  137. """
  138. dataset_id_str = str(dataset_id)
  139. comment_id_str = str(comment_id)
  140. # 权限检查
  141. dataset = DatasetService.get_dataset(dataset_id_str)
  142. if not dataset:
  143. raise NotFound("Dataset not found.")
  144. DatasetService.check_dataset_permission(dataset, current_user)
  145. # 删除评论
  146. try:
  147. SegmentMentionService.delete_comment(
  148. comment_id=comment_id_str,
  149. account_id=str(current_user.id)
  150. )
  151. except PermissionError as e:
  152. raise BadRequest(str(e))
  153. return {'result': 'success'}, 200
  154. class UserNotificationsApi(Resource):
  155. """用户通知API"""
  156. @setup_required
  157. @login_required
  158. @account_initialization_required
  159. @marshal_with(notification_list_fields)
  160. def get(self):
  161. """
  162. 获取当前用户的通知列表
  163. Query Parameters:
  164. - is_read: 是否已读(true/false,不传表示全部)
  165. - limit: 返回数量限制(默认50,最大100)
  166. """
  167. import logging
  168. logger = logging.getLogger(__name__)
  169. is_read_param = request.args.get('is_read')
  170. is_read = None
  171. if is_read_param is not None:
  172. is_read = is_read_param.lower() == 'true'
  173. limit = request.args.get('limit', 50, type=int)
  174. logger.info(f"[NOTIFY-API] Request params - is_read_param: {is_read_param}, parsed is_read: {is_read}, limit: {limit}, user_id: {current_user.id}")
  175. result = SegmentMentionService.get_user_notifications(
  176. account_id=str(current_user.id),
  177. is_read=is_read,
  178. limit=min(limit, 100)
  179. )
  180. logger.info(f"[NOTIFY-API] Response - notifications count: {len(result.get('notifications', []))}, unread_count: {result.get('unread_count', 0)}")
  181. return result
  182. @setup_required
  183. @login_required
  184. @account_initialization_required
  185. def post(self):
  186. """
  187. 标记通知为已读
  188. Request Body:
  189. - notification_ids: 通知ID列表(可选)
  190. - mark_all_as_read: 是否标记全部为已读(布尔值)
  191. """
  192. data = request.get_json()
  193. if data.get('mark_all_as_read'):
  194. SegmentMentionService.mark_all_as_read(
  195. account_id=str(current_user.id)
  196. )
  197. else:
  198. notification_ids = data.get('notification_ids', [])
  199. if notification_ids:
  200. SegmentMentionService.mark_as_read(
  201. account_id=str(current_user.id),
  202. notification_ids=notification_ids
  203. )
  204. return {'result': 'success'}, 200
  205. class UnreadNotificationsCountApi(Resource):
  206. """未读通知数量API"""
  207. @setup_required
  208. @login_required
  209. @account_initialization_required
  210. def get(self):
  211. """获取当前用户的未读通知数量"""
  212. unread_count = SegmentMentionService.get_unread_count(
  213. account_id=str(current_user.id)
  214. )
  215. return {'unread_count': unread_count}, 200
  216. # 注册路由
  217. api.add_resource(
  218. SegmentCommentsApi,
  219. '/datasets/<uuid:dataset_id>/segments/<uuid:segment_id>/comments'
  220. )
  221. api.add_resource(
  222. SegmentCommentApi,
  223. '/datasets/<uuid:dataset_id>/segments/<uuid:segment_id>/comments/<uuid:comment_id>'
  224. )
  225. api.add_resource(
  226. UserNotificationsApi,
  227. '/datasets/notifications'
  228. )
  229. api.add_resource(
  230. UnreadNotificationsCountApi,
  231. '/datasets/notifications/unread-count'
  232. )