config.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. from urllib.parse import quote_plus
  2. from pydantic_settings import BaseSettings, SettingsConfigDict
  3. class Settings(BaseSettings):
  4. app_name: str = "denamd-server"
  5. app_env: str = "dev"
  6. app_host: str = "0.0.0.0"
  7. app_port: int = 8000
  8. cors_allow_origins: str = "*"
  9. scheduler_heartbeat_seconds: int = 3600
  10. mysql_host: str = ""
  11. mysql_port: int = 3306
  12. mysql_user: str = "root"
  13. mysql_password: str = ""
  14. mysql_database: str = ""
  15. # 新热事件来源库(hot_content_* 表),与需求池库分离
  16. hot_content_mysql_host: str = ""
  17. hot_content_mysql_port: int = 3306
  18. hot_content_mysql_user: str = ""
  19. hot_content_mysql_password: str = ""
  20. hot_content_mysql_database: str = ""
  21. hot_content_mysql_charset: str = "utf8mb4"
  22. # 当下供需gap-分词来源库(demand_content 表)
  23. supply_mysql_host: str = ""
  24. supply_mysql_port: int = 3306
  25. supply_mysql_user: str = ""
  26. supply_mysql_password: str = ""
  27. supply_mysql_database: str = ""
  28. supply_mysql_charset: str = "utf8mb4"
  29. supply_demand_content_table: str = "demand_content"
  30. odps_access_id: str = "LTAI9EBa0bd5PrDa"
  31. odps_access_key: str = "vAalxds7YxhfOA2yVv8GziCg3Y87v5"
  32. odps_project: str = "loghubods"
  33. odps_endpoint: str = "http://service.odps.aliyun.com/api"
  34. demand_pool_source_table: str = "dwd_multi_demand_pool_di"
  35. demand_pool_secondary_source_table: str = "dwd_demand_pool_di"
  36. # 次源 dwd_demand_pool_di 同步开关;false 时仅同步主源 dwd_multi_demand_pool_di
  37. demand_pool_secondary_sync_enabled: bool = False
  38. demand_pool_secondary_strategy: str = "近期需求"
  39. demand_pool_secondary_default_ext_info: str = "{}"
  40. demand_pool_target_table: str = "multi_demand_pool_di"
  41. demand_pool_initial_partitions: str = "20260507,20260508,20260509"
  42. demand_pool_hourly_sync_enabled: bool = True
  43. demand_pool_hourly_sync_minute: int = 0
  44. demand_pool_daily_strategy_alert_enabled: bool = True
  45. demand_pool_daily_strategy_alert_hour: int = 10
  46. demand_pool_daily_strategy_alert_minute: int = 0
  47. feishu_webhook_url: str = ""
  48. feishu_webhook_timeout_seconds: int = 30
  49. # 默认不校验 HTTPS 证书,避免公司代理自签链导致发不出消息;生产若需严格校验可设为 true
  50. feishu_webhook_verify_ssl: bool = False
  51. hot_demand_pool_strategy: str = "新热事件"
  52. hot_content_wxindex_threshold: float = 1_000_000.0
  53. strategy_config_table: str = "strategy_config"
  54. strategy_staging_table: str = "strategy_staging"
  55. strategy_staging_hourly_generate_enabled: bool = True
  56. strategy_staging_hourly_generate_start_hour: int = 3
  57. strategy_staging_hourly_generate_end_hour: int = 23
  58. strategy_staging_hourly_generate_minute: int = 0
  59. substance_element_base_table: str = "substance_element_base"
  60. substance_element_effect_table: str = "substance_element_effect_di"
  61. substance_element_daily_sync_enabled: bool = True
  62. substance_element_daily_sync_hour: int = 9
  63. substance_element_daily_sync_minute: int = 30
  64. vertical_category_base_table: str = "vertical_category_base"
  65. vertical_category_effect_table: str = "vertical_category_effect_di"
  66. vertical_category_daily_sync_enabled: bool = True
  67. vertical_category_daily_sync_hour: int = 9
  68. vertical_category_daily_sync_minute: int = 30
  69. model_config = SettingsConfigDict(
  70. env_file=".env",
  71. env_file_encoding="utf-8",
  72. extra="ignore",
  73. )
  74. @property
  75. def mysql_dsn(self) -> str:
  76. return (
  77. "mysql+pymysql://"
  78. f"{quote_plus(self.mysql_user)}:{quote_plus(self.mysql_password)}"
  79. f"@{self.mysql_host}:{self.mysql_port}/{quote_plus(self.mysql_database)}"
  80. )
  81. @property
  82. def hot_content_mysql_configured(self) -> bool:
  83. return bool(self.hot_content_mysql_host.strip() and self.hot_content_mysql_database.strip())
  84. @property
  85. def hot_content_mysql_dsn(self) -> str:
  86. charset = quote_plus(self.hot_content_mysql_charset or "utf8mb4")
  87. return (
  88. "mysql+pymysql://"
  89. f"{quote_plus(self.hot_content_mysql_user)}:"
  90. f"{quote_plus(self.hot_content_mysql_password)}"
  91. f"@{self.hot_content_mysql_host}:{self.hot_content_mysql_port}/"
  92. f"{quote_plus(self.hot_content_mysql_database)}?charset={charset}"
  93. )
  94. @property
  95. def supply_mysql_configured(self) -> bool:
  96. return bool(self.supply_mysql_host.strip() and self.supply_mysql_database.strip())
  97. @property
  98. def supply_mysql_dsn(self) -> str:
  99. charset = quote_plus(self.supply_mysql_charset or "utf8mb4")
  100. return (
  101. "mysql+pymysql://"
  102. f"{quote_plus(self.supply_mysql_user)}:"
  103. f"{quote_plus(self.supply_mysql_password)}"
  104. f"@{self.supply_mysql_host}:{self.supply_mysql_port}/"
  105. f"{quote_plus(self.supply_mysql_database)}?charset={charset}"
  106. )
  107. @property
  108. def demand_pool_initial_partition_list(self) -> list[str]:
  109. return [
  110. partition.strip()
  111. for partition in self.demand_pool_initial_partitions.split(",")
  112. if partition.strip()
  113. ]
  114. @property
  115. def strategy_staging_hourly_cron_hours(self) -> str:
  116. """每小时触发,不跨午夜。默认 start=3、end=23 → '3-23'(末次 23:00,0 点不跑)。"""
  117. start = self.strategy_staging_hourly_generate_start_hour
  118. end = self.strategy_staging_hourly_generate_end_hour
  119. if not (0 <= start <= 23 and 0 <= end <= 23):
  120. raise ValueError("strategy staging hourly hours must be 0-23")
  121. if start > end:
  122. raise ValueError("start_hour cannot be greater than end_hour")
  123. if start == end:
  124. return str(start)
  125. return f"{start}-{end}"
  126. @property
  127. def cors_allow_origin_list(self) -> list[str]:
  128. if self.cors_allow_origins.strip() == "*":
  129. return ["*"]
  130. return [
  131. origin.strip()
  132. for origin in self.cors_allow_origins.split(",")
  133. if origin.strip()
  134. ]
  135. settings = Settings()