schemas.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. """数据模型定义"""
  2. from typing import List, Optional, Union
  3. from pydantic import BaseModel, Field, field_validator
  4. from datetime import date
  5. # ============ 请求模型 ============
  6. class TripRequest(BaseModel):
  7. """旅行规划请求"""
  8. city: str = Field(..., description="目的地城市", example="北京")
  9. start_date: str = Field(..., description="开始日期 YYYY-MM-DD", example="2025-06-01")
  10. end_date: str = Field(..., description="结束日期 YYYY-MM-DD", example="2025-06-03")
  11. travel_days: int = Field(..., description="旅行天数", ge=1, le=30, example=3)
  12. transportation: str = Field(..., description="交通方式", example="公共交通")
  13. accommodation: str = Field(..., description="住宿偏好", example="经济型酒店")
  14. preferences: List[str] = Field(default=[], description="旅行偏好标签", example=["历史文化", "美食"])
  15. traveler_group: str = Field(default="", description="出行人群,如:独自旅行/情侣夫妻/朋友结伴/家庭亲子/公司团建/老年旅行/研学旅行", example="家庭亲子")
  16. free_text_input: Optional[str] = Field(default="", description="额外要求", example="希望多安排一些博物馆")
  17. class Config:
  18. json_schema_extra = {
  19. "example": {
  20. "city": "北京",
  21. "start_date": "2025-06-01",
  22. "end_date": "2025-06-03",
  23. "travel_days": 3,
  24. "transportation": "公共交通",
  25. "accommodation": "经济型酒店",
  26. "preferences": ["历史文化", "美食"],
  27. "traveler_group": "家庭亲子",
  28. "free_text_input": "希望多安排一些博物馆"
  29. }
  30. }
  31. class POISearchRequest(BaseModel):
  32. """POI搜索请求"""
  33. keywords: str = Field(..., description="搜索关键词", example="故宫")
  34. city: str = Field(..., description="城市", example="北京")
  35. citylimit: bool = Field(default=True, description="是否限制在城市范围内")
  36. class RouteRequest(BaseModel):
  37. """路线规划请求"""
  38. origin_address: str = Field(..., description="起点地址", example="北京市朝阳区阜通东大街6号")
  39. destination_address: str = Field(..., description="终点地址", example="北京市海淀区上地十街10号")
  40. origin_city: Optional[str] = Field(default=None, description="起点城市")
  41. destination_city: Optional[str] = Field(default=None, description="终点城市")
  42. route_type: str = Field(default="walking", description="路线类型: walking/driving/transit")
  43. # ============ 响应模型 ============
  44. class Location(BaseModel):
  45. """地理位置"""
  46. longitude: float = Field(..., description="经度")
  47. latitude: float = Field(..., description="纬度")
  48. class Attraction(BaseModel):
  49. """景点信息"""
  50. name: str = Field(..., description="景点名称")
  51. address: str = Field(..., description="地址")
  52. location: Location = Field(..., description="经纬度坐标")
  53. visit_duration: int = Field(..., description="建议游览时间(分钟)")
  54. description: str = Field(..., description="景点描述")
  55. category: Optional[str] = Field(default="景点", description="景点类别")
  56. rating: Optional[float] = Field(default=None, description="评分")
  57. photos: Optional[List[str]] = Field(default_factory=list, description="景点图片URL列表")
  58. poi_id: Optional[str] = Field(default="", description="POI ID")
  59. image_url: Optional[str] = Field(default=None, description="图片URL")
  60. ticket_price: int = Field(default=0, description="门票价格(元)")
  61. class Meal(BaseModel):
  62. """餐饮信息"""
  63. type: str = Field(..., description="餐饮类型: breakfast/lunch/dinner/snack")
  64. name: str = Field(..., description="餐饮名称")
  65. address: Optional[str] = Field(default=None, description="地址")
  66. location: Optional[Location] = Field(default=None, description="经纬度坐标")
  67. description: Optional[str] = Field(default=None, description="描述")
  68. estimated_cost: int = Field(default=0, description="预估费用(元)")
  69. class Hotel(BaseModel):
  70. """酒店信息"""
  71. name: str = Field(..., description="酒店名称")
  72. address: str = Field(default="", description="酒店地址")
  73. location: Optional[Location] = Field(default=None, description="酒店位置")
  74. price_range: str = Field(default="", description="价格范围")
  75. rating: str = Field(default="", description="评分")
  76. distance: str = Field(default="", description="距离景点距离")
  77. type: str = Field(default="", description="酒店类型")
  78. estimated_cost: int = Field(default=0, description="预估费用(元/晚)")
  79. class TransportSegment(BaseModel):
  80. """交通段详情"""
  81. type: str = Field(..., description="交通方式: 步行/公交/地铁/出租车/自驾")
  82. instruction: str = Field(..., description="详细交通说明,如'从天安门东站乘坐1号线到王府井站'")
  83. from_name: str = Field(..., description="起点名称,如'酒店'或上一个景点名")
  84. to_name: str = Field(..., description="终点名称,如景点名或餐厅名")
  85. departure_time: str = Field(default="", description="出发时间,如'09:00'")
  86. duration: int = Field(default=0, description="耗时(分钟)")
  87. distance: int = Field(default=0, description="距离(米)")
  88. route_detail: Optional[str] = Field(default=None, description="路线详情,如'经过5站·步行300米'")
  89. class DayPlan(BaseModel):
  90. """单日行程"""
  91. date: str = Field(..., description="日期 YYYY-MM-DD")
  92. day_index: int = Field(..., description="第几天(从0开始)")
  93. description: str = Field(..., description="当日行程描述")
  94. transportation: str = Field(..., description="交通方式")
  95. transportation_details: List[TransportSegment] = Field(default=[], description="详细交通分段信息,包含每一段的路线、时间、距离")
  96. accommodation: str = Field(..., description="住宿")
  97. hotel: Optional[Hotel] = Field(default=None, description="推荐酒店")
  98. attractions: List[Attraction] = Field(default=[], description="景点列表")
  99. meals: List[Meal] = Field(default=[], description="餐饮列表")
  100. class WeatherInfo(BaseModel):
  101. """天气信息"""
  102. date: str = Field(..., description="日期 YYYY-MM-DD")
  103. day_weather: str = Field(default="", description="白天天气")
  104. night_weather: str = Field(default="", description="夜间天气")
  105. day_temp: Union[int, str] = Field(default=0, description="白天温度")
  106. night_temp: Union[int, str] = Field(default=0, description="夜间温度")
  107. wind_direction: str = Field(default="", description="风向")
  108. wind_power: str = Field(default="", description="风力")
  109. @field_validator('day_temp', 'night_temp', mode='before')
  110. @classmethod
  111. def parse_temperature(cls, v):
  112. """解析温度,移除°C等单位"""
  113. if isinstance(v, str):
  114. # 移除°C, ℃等单位符号
  115. v = v.replace('°C', '').replace('℃', '').replace('°', '').strip()
  116. try:
  117. return int(v)
  118. except ValueError:
  119. return 0
  120. return v
  121. class Budget(BaseModel):
  122. """预算信息"""
  123. total_attractions: int = Field(default=0, description="景点门票总费用")
  124. total_hotels: int = Field(default=0, description="酒店总费用")
  125. total_meals: int = Field(default=0, description="餐饮总费用")
  126. total_transportation: int = Field(default=0, description="交通总费用")
  127. total: int = Field(default=0, description="总费用")
  128. class TripPlan(BaseModel):
  129. """旅行计划"""
  130. city: str = Field(..., description="目的地城市")
  131. start_date: str = Field(..., description="开始日期")
  132. end_date: str = Field(..., description="结束日期")
  133. days: List[DayPlan] = Field(..., description="每日行程")
  134. weather_info: List[WeatherInfo] = Field(default=[], description="天气信息")
  135. overall_suggestions: str = Field(..., description="总体建议")
  136. budget: Optional[Budget] = Field(default=None, description="预算信息")
  137. class TripPlanResponse(BaseModel):
  138. """旅行计划响应"""
  139. success: bool = Field(..., description="是否成功")
  140. message: str = Field(default="", description="消息")
  141. data: Optional[TripPlan] = Field(default=None, description="旅行计划数据")
  142. class POIInfo(BaseModel):
  143. """POI信息"""
  144. id: str = Field(..., description="POI ID")
  145. name: str = Field(..., description="名称")
  146. type: str = Field(..., description="类型")
  147. address: str = Field(..., description="地址")
  148. location: Location = Field(..., description="经纬度坐标")
  149. tel: Optional[str] = Field(default=None, description="电话")
  150. class POISearchResponse(BaseModel):
  151. """POI搜索响应"""
  152. success: bool = Field(..., description="是否成功")
  153. message: str = Field(default="", description="消息")
  154. data: List[POIInfo] = Field(default=[], description="POI列表")
  155. class RouteInfo(BaseModel):
  156. """路线信息"""
  157. distance: float = Field(..., description="距离(米)")
  158. duration: int = Field(..., description="时间(秒)")
  159. route_type: str = Field(..., description="路线类型")
  160. description: str = Field(..., description="路线描述")
  161. class RouteResponse(BaseModel):
  162. """路线规划响应"""
  163. success: bool = Field(..., description="是否成功")
  164. message: str = Field(default="", description="消息")
  165. data: Optional[RouteInfo] = Field(default=None, description="路线信息")
  166. class WeatherResponse(BaseModel):
  167. """天气查询响应"""
  168. success: bool = Field(..., description="是否成功")
  169. message: str = Field(default="", description="消息")
  170. data: List[WeatherInfo] = Field(default=[], description="天气信息")
  171. # ============ 错误响应 ============
  172. class ErrorResponse(BaseModel):
  173. """错误响应"""
  174. success: bool = Field(default=False, description="是否成功")
  175. message: str = Field(..., description="错误消息")
  176. error_code: Optional[str] = Field(default=None, description="错误代码")
  177. # ============ 认证模型 ============
  178. class RegisterRequest(BaseModel):
  179. """注册请求(密码经RSA公钥加密,Base64编码)"""
  180. username: str = Field(..., min_length=2, max_length=50, description="用户名")
  181. encrypted_password: str = Field(..., description="RSA-OAEP加密后的密码(Base64)")
  182. class LoginRequest(BaseModel):
  183. """登录请求(密码经RSA公钥加密,Base64编码)"""
  184. username: str = Field(..., description="用户名")
  185. encrypted_password: str = Field(..., description="RSA-OAEP加密后的密码(Base64)")
  186. # ============ 历史记录模型 ============
  187. class SaveHistoryRequest(BaseModel):
  188. """保存历史记录请求"""
  189. city: str = Field(..., description="城市")
  190. start_date: str = Field(..., description="开始日期")
  191. end_date: str = Field(..., description="结束日期")
  192. travel_days: int = Field(..., description="旅行天数")
  193. preferences: List[str] = Field(default=[], description="偏好标签")
  194. traveler_group: str = Field(default="", description="出行人群")
  195. plan_data: dict = Field(..., description="完整行程数据(JSON)")
  196. class HistoryItemResponse(BaseModel):
  197. """保存历史记录响应"""
  198. success: bool = Field(..., description="是否成功")
  199. message: str = Field(default="", description="消息")
  200. history_id: int = Field(default=0, description="历史记录ID")
  201. class HistoryListResponse(BaseModel):
  202. """历史记录列表响应"""
  203. success: bool = Field(..., description="是否成功")
  204. records: list = Field(default=[], description="历史记录列表")
  205. class HistoryDetailResponse(BaseModel):
  206. """历史记录详情响应"""
  207. success: bool = Field(..., description="是否成功")
  208. record: Optional[dict] = Field(default=None, description="历史记录详情")
  209. class DeleteResponse(BaseModel):
  210. """删除响应"""
  211. success: bool = Field(..., description="是否成功")
  212. message: str = Field(default="", description="消息")
  213. # ============ 聊天模型 ============
  214. class ChatMessageSchema(BaseModel):
  215. """聊天消息"""
  216. id: int = Field(..., description="消息ID")
  217. session_id: int = Field(..., description="会话ID")
  218. role: str = Field(..., description="角色: user/assistant")
  219. content: str = Field(..., description="消息内容")
  220. created_at: str = Field(..., description="创建时间")
  221. class ChatSessionSchema(BaseModel):
  222. """聊天会话"""
  223. id: int = Field(..., description="会话ID")
  224. user_id: int = Field(..., description="用户ID")
  225. title: str = Field(..., description="会话标题")
  226. created_at: str = Field(default="", description="创建时间")
  227. updated_at: str = Field(default="", description="更新时间")
  228. class ChatSessionResponse(BaseModel):
  229. """会话响应"""
  230. success: bool = Field(..., description="是否成功")
  231. session: Optional[ChatSessionSchema] = Field(default=None, description="会话信息")
  232. class ChatSessionListResponse(BaseModel):
  233. """会话列表响应"""
  234. success: bool = Field(..., description="是否成功")
  235. sessions: list = Field(default=[], description="会话列表")
  236. class ChatMessagesResponse(BaseModel):
  237. """消息列表响应"""
  238. success: bool = Field(..., description="是否成功")
  239. messages: list = Field(default=[], description="消息列表")
  240. class ChatSendMessageRequest(BaseModel):
  241. """发送消息请求"""
  242. content: str = Field(..., description="消息内容", min_length=1)
  243. class ChatDeleteResponse(BaseModel):
  244. """聊天删除响应"""
  245. success: bool = Field(..., description="是否成功")
  246. message: str = Field(..., description="消息")