preference.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """
  2. 智能股票分析助手 — 用户偏好数据模型
  3. 定义用户投资偏好、风控参数、界面偏好等持久化存储结构。
  4. """
  5. from sqlalchemy import Column, Integer, String, Float, Text, DateTime, Boolean, func
  6. from app.models.database import Base
  7. class UserPreference(Base):
  8. """用户偏好模型"""
  9. __tablename__ = "user_preferences"
  10. # 主键
  11. id = Column(Integer, primary_key=True, autoincrement=True)
  12. # 关联用户(当前简化:单用户模式,后续可扩展多用户)
  13. user_id = Column(String(64), default="default", unique=True, nullable=False, comment="用户ID")
  14. # =========================================================================
  15. # 投资偏好
  16. # =========================================================================
  17. risk_tolerance = Column(
  18. String(20), default="moderate", nullable=False,
  19. comment="风险承受度: conservative / moderate / aggressive"
  20. )
  21. investment_style = Column(
  22. String(20), default="blend", nullable=False,
  23. comment="投资风格: value / growth / momentum / dividend / blend"
  24. )
  25. preferred_sectors = Column(
  26. Text, default="[]",
  27. comment="偏好行业 (JSON数组)"
  28. )
  29. excluded_sectors = Column(
  30. Text, default="[]",
  31. comment="排除行业 (JSON数组)"
  32. )
  33. investment_horizon = Column(
  34. String(20), default="medium",
  35. comment="投资期限: short / medium / long"
  36. )
  37. target_return_rate = Column(
  38. Float, default=10.0, nullable=False,
  39. comment="目标年化收益率(%)"
  40. )
  41. # =========================================================================
  42. # 风控参数
  43. # =========================================================================
  44. max_position_ratio = Column(
  45. Float, default=30.0, nullable=False,
  46. comment="单只股票最大仓位比例(%)"
  47. )
  48. max_drawdown_limit = Column(
  49. Float, default=-15.0, nullable=False,
  50. comment="最大回撤预警线(%)"
  51. )
  52. # =========================================================================
  53. # 通知设置
  54. # =========================================================================
  55. notification_enabled = Column(
  56. Boolean, default=True, nullable=False,
  57. comment="是否启用通知"
  58. )
  59. notification_channels = Column(
  60. Text, default='["push"]',
  61. comment="通知渠道 (JSON: email/sms/push)"
  62. )
  63. market_alert_threshold = Column(
  64. Float, default=5.0, nullable=False,
  65. comment="行情异动提醒阈值(%)"
  66. )
  67. # =========================================================================
  68. # 界面偏好
  69. # =========================================================================
  70. language = Column(
  71. String(10), default="zh", nullable=False,
  72. comment="界面语言: zh / en"
  73. )
  74. theme = Column(
  75. String(10), default="auto", nullable=False,
  76. comment="主题: light / dark / auto"
  77. )
  78. default_view = Column(
  79. String(20), default="dashboard", nullable=False,
  80. comment="默认首页: dashboard / watchlist"
  81. )
  82. # =========================================================================
  83. # 时间戳
  84. # =========================================================================
  85. created_at = Column(DateTime, server_default=func.now(), comment="创建时间")
  86. updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间")
  87. def to_dict(self) -> dict:
  88. """转为字典(用于API响应)"""
  89. import json
  90. return {
  91. "id": self.id,
  92. "user_id": self.user_id,
  93. "risk_tolerance": self.risk_tolerance,
  94. "investment_style": self.investment_style,
  95. "preferred_sectors": json.loads(self.preferred_sectors),
  96. "excluded_sectors": json.loads(self.excluded_sectors),
  97. "investment_horizon": self.investment_horizon,
  98. "target_return_rate": self.target_return_rate,
  99. "max_position_ratio": self.max_position_ratio,
  100. "max_drawdown_limit": self.max_drawdown_limit,
  101. "notification_enabled": self.notification_enabled,
  102. "notification_channels": json.loads(self.notification_channels),
  103. "market_alert_threshold": self.market_alert_threshold,
  104. "language": self.language,
  105. "theme": self.theme,
  106. "default_view": self.default_view,
  107. "created_at": self.created_at.isoformat() if self.created_at else None,
  108. "updated_at": self.updated_at.isoformat() if self.updated_at else None,
  109. }
  110. @classmethod
  111. def create_default(cls, user_id: str = "default") -> "UserPreference":
  112. """创建默认偏好实例"""
  113. return cls(
  114. user_id=user_id,
  115. risk_tolerance="moderate",
  116. investment_style="blend",
  117. preferred_sectors="[]",
  118. excluded_sectors="[]",
  119. investment_horizon="medium",
  120. target_return_rate=10.0,
  121. max_position_ratio=30.0,
  122. max_drawdown_limit=-15.0,
  123. notification_enabled=True,
  124. notification_channels='["push"]',
  125. market_alert_threshold=5.0,
  126. language="zh",
  127. theme="auto",
  128. default_view="dashboard",
  129. )