message.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. from pydantic import BaseModel, Field, model_validator
  2. from typing import Optional, Union, Any
  3. from datetime import datetime
  4. from enum import Enum
  5. class MessageType(str, Enum):
  6. MESSAGE = "MESSAGE"
  7. NOTIFICATION = "NOTIFICATION"
  8. class ContentType(str, Enum):
  9. TEXT = "TEXT"
  10. IMAGE = "IMAGE"
  11. VIDEO = "VIDEO"
  12. FILE = "FILE"
  13. # 用户在应用内发起的“申请通知”,用于在私信界面展示通知样式(携带 action_url/action_text)
  14. USER_NOTIFICATION = "USER_NOTIFICATION"
  15. class MessageBase(BaseModel):
  16. title: str = Field(..., max_length=255)
  17. content: Union[str, dict, Any] = Field(..., description="内容")
  18. content_type: ContentType = ContentType.TEXT
  19. type: MessageType = MessageType.MESSAGE
  20. app_id: Optional[str] = None # 改为字符串类型,支持开发者保存的字符串 app_id
  21. action_url: Optional[str] = None
  22. action_text: Optional[str] = None
  23. # SSO 扩展
  24. target_url: Optional[str] = None
  25. auto_sso: bool = False
  26. class MessageCreate(MessageBase):
  27. receiver_id: Optional[int] = None
  28. app_user_id: Optional[str] = None
  29. sender_app_user_id: Optional[str] = None # 应用发信时可选:帐内发起人,解析后写入 sender_id,用于审计和会话聚合
  30. is_broadcast: bool = False
  31. @model_validator(mode='after')
  32. def check_receiver(self):
  33. if self.is_broadcast:
  34. return self
  35. if not self.receiver_id and not (self.app_user_id and self.app_id):
  36. raise ValueError("非广播消息必须提供 receiver_id,或者同时提供 app_user_id 和 app_id")
  37. return self
  38. class MessageUpdate(BaseModel):
  39. is_read: Optional[bool] = None
  40. class MessageResponse(BaseModel):
  41. id: int
  42. sender_id: Optional[int]
  43. receiver_id: int
  44. app_id: Optional[int]
  45. app_name: Optional[str] = None # 应用名称,用于前端显示
  46. type: MessageType
  47. content_type: ContentType
  48. title: str
  49. content: str # DB 中存的是字符串
  50. action_url: Optional[str]
  51. action_text: Optional[str]
  52. is_read: bool
  53. created_at: datetime
  54. read_at: Optional[datetime]
  55. class Config:
  56. from_attributes = True
  57. class ConversationResponse(BaseModel):
  58. user_id: int
  59. username: str
  60. full_name: Optional[str]
  61. unread_count: int
  62. last_message: str
  63. last_message_type: ContentType
  64. updated_at: datetime
  65. # 系统会话标记及应用信息(用于拆分不同平台的系统通知会话)
  66. is_system: bool = False
  67. app_id: Optional[int] = None
  68. app_name: Optional[str] = None
  69. class Config:
  70. from_attributes = True
  71. # Device Schema
  72. class DeviceRegister(BaseModel):
  73. device_token: str
  74. platform: str
  75. device_name: Optional[str] = None