config.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. from pydantic import Field
  2. from pydantic_settings import BaseSettings
  3. from typing import Optional
  4. from pathlib import Path
  5. import os
  6. class Settings(BaseSettings):
  7. """应用配置"""
  8. # 应用基础配置
  9. APP_NAME: str = "shanxia2.0 管理骨架"
  10. DEBUG: bool = True
  11. API_V1_PREFIX: str = "/api"
  12. # 独立移动段快照:先执行配套 SQL 迁移再启用;关闭时旧 Web 不查询新表。
  13. PL_MOBILE_PLAN_PRODUCT_META_ENABLED: bool = False
  14. # 数据库配置
  15. DATABASE_URL: str = "mysql+pymysql://root:password@localhost:3306/sanqiang"
  16. # pymysql 连接/读写超时(秒);远程库或休眠唤醒后避免 SELECT 无限阻塞
  17. DB_CONNECT_TIMEOUT_SEC: int = 10
  18. DB_READ_TIMEOUT_SEC: int = 10
  19. DB_WRITE_TIMEOUT_SEC: int = 10
  20. # 连接池回收周期(秒),宜小于 MySQL wait_timeout / 防火墙 idle 超时
  21. DB_POOL_RECYCLE_SEC: int = 600
  22. DB_POOL_SIZE: int = 5
  23. DB_MAX_OVERFLOW: int = 10
  24. # 后台保活间隔(秒),远程 MySQL 空闲 TCP 被回收时主动 SELECT 1
  25. DB_KEEPALIVE_INTERVAL_SEC: int = 240
  26. # JWT配置
  27. SECRET_KEY: str = "your-secret-key-change-in-production-please-use-random-string"
  28. ALGORITHM: str = "HS256"
  29. ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 * 24 # 30天
  30. # CORS配置
  31. BACKEND_CORS_ORIGINS: list = [
  32. "http://localhost:5173", # art-design-pro 开发服务器
  33. "http://localhost:8080",
  34. "http://localhost:8081", # uni-app H5 端口被占用时常用
  35. "http://localhost:8082",
  36. "http://127.0.0.1:5173",
  37. "http://127.0.0.1:8080",
  38. "http://127.0.0.1:8081",
  39. "http://127.0.0.1:8082",
  40. "http://localhost:3000", # Web开发
  41. "http://localhost:3006", # Web开发
  42. "http://127.0.0.1:3000",
  43. "http://127.0.0.1:3006",
  44. ]
  45. # 文件存储配置
  46. STORAGE_TYPE: str = "local" # local, qiniu, oss, minio
  47. # 本地存储配置
  48. LOCAL_UPLOAD_DIR: str = "uploads"
  49. LOCAL_STORAGE_URL: str = "/uploads"
  50. # Redis 配置(用于登录会话与 refreshToken 吊销)
  51. # 未配置 REDIS_HOST 时,后端会保持兼容:仍然只校验 JWT,不强制会话吊销。
  52. REDIS_HOST: str = ""
  53. REDIS_PORT: int = 6379
  54. REDIS_DB: int = 0
  55. REDIS_PASSWORD: str = ""
  56. REDIS_TIMEOUT_SEC: int = 5
  57. REDIS_CONNECT_TIMEOUT_SEC: int = 5
  58. REDIS_HEALTH_CHECK_INTERVAL_SEC: int = 30
  59. # asyncio 层 Redis 操作超时(秒),与会话校验/续期一致
  60. REDIS_OP_TIMEOUT_SEC: int = 3
  61. REDIS_SSL_ENABLED: bool = False
  62. # 七牛云存储配置 (生产环境推荐)
  63. QINIU_ACCESS_KEY: str = ""
  64. QINIU_SECRET_KEY: str = ""
  65. QINIU_BUCKET: str = ""
  66. QINIU_DOMAIN: str = ""
  67. # 阿里云OSS配置 (可选)
  68. OSS_ACCESS_KEY_ID: str = ""
  69. OSS_ACCESS_KEY_SECRET: str = ""
  70. OSS_BUCKET: str = ""
  71. OSS_ENDPOINT: str = ""
  72. OSS_DOMAIN: str = ""
  73. # 阿里云 OCR(身份证 / 驾驶证识别)
  74. ALIYUN_OCR_ACCESS_KEY_ID: str = ""
  75. ALIYUN_OCR_ACCESS_KEY_SECRET: str = ""
  76. ALIYUN_OCR_ENDPOINT: str = "ocr-api.cn-hangzhou.aliyuncs.com"
  77. ALIYUN_OCR_MONTHLY_LIMIT: int = 170
  78. # MinIO配置
  79. MINIO_ENDPOINT: str = ""
  80. MINIO_ACCESS_KEY: str = ""
  81. MINIO_SECRET_KEY: str = ""
  82. MINIO_BUCKET: str = ""
  83. MINIO_SECURE: bool = False
  84. # 浏览器可访问的公开前缀(建议 HTTPS 同域反代,如 https://w.shanxiachem.com/ruoyi)。
  85. # 配置后:普通 get_url 用此前缀;预签名预览/下载按该主机签发(避免 HTTPS 页 Mixed Content)。
  86. MINIO_BASE_URL: str = ""
  87. # 微信支付配置 (生产环境必需)
  88. WXPAY_MCHID: str = "" # 商户号
  89. WXPAY_APPID: str = "" # 公众号AppID
  90. WXPAY_MINI_APPID: str = "" # 小程序AppID
  91. WXPAY_API_KEY: str = "" # API密钥 (V2)
  92. WXPAY_SANDBOX: bool = False # 是否使用沙箱环境
  93. WXPAY_NOTIFY_URL: str = "https://your-domain.com/api/payment/notify" # 支付回调地址
  94. WXPAY_REFUND_NOTIFY_URL: str = "https://your-domain.com/api/payment/refund-notify" # 退款回调地址
  95. # V3 API 证书配置
  96. WXPAY_CERT_PATH: str = "" # API证书(apiclient_cert.pem)路径
  97. WXPAY_PRIVATE_KEY_PATH: str = "" # API私钥(apiclient_key.pem)路径
  98. WXPAY_SERIAL_NO: str = "" # 证书序列号
  99. # SSL证书配置 (退款必需)
  100. WXPAY_SSL_CERT: str = "" # SSL证书路径
  101. WXPAY_SSL_KEY: str = "" # SSL私钥路径
  102. # 微信小程序配置
  103. WXPAY_SECRET: str = "" # 小程序Secret
  104. # 门户微信小程序登录(与 WXPAY_* 支付凭证分离)
  105. WECHAT_MP_APPID: str = "" # 门户小程序 AppID,须与 miniprogram manifest 一致
  106. WECHAT_MP_SECRET: str = "" # 门户小程序 AppSecret(仅后端)
  107. # web-admin 微信扫码登录(小程序码)
  108. WX_QR_SESSION_TTL_SEC: int = 180
  109. WX_QR_ENV_VERSION: str = "release" # release | trial | develop
  110. WX_QR_PAGE: str = "pages/login/web-qr/index"
  111. # 服务号审批通知:独立于小程序/支付,执行迁移并配置后台后再启用。
  112. WECHAT_OA_ENABLED: bool = False
  113. WECHAT_OA_APPID: str = "wxf63dfb83af901d43"
  114. WECHAT_OA_SECRET: str = ""
  115. WECHAT_OA_PUBLIC_BASE_URL: str = "" # 门户公网 HTTPS 地址(不含 /api)
  116. WECHAT_OA_MINI_APPID: str = "" # 与服务号关联的门户小程序
  117. WECHAT_OA_TEMPLATE_PENDING: str = "ukQRYoczEXH1fZe9TdL8uX6LmuJJTlPidboxqtq_7N0"
  118. WECHAT_OA_TEMPLATE_RESULT: str = "vA_z15BdP-8LltRmS__1MmUcNKQBy6b6rvAXKEYzoLY"
  119. WECHAT_OA_TEMPLATE_COMPLETION: str = "rnPOIVknPqfgwVzKbchj6pCMCWpoDe3h3dkrFbUEsD4"
  120. WECHAT_OA_TEMPLATE_CERT_REMINDER: str = "aYzLZ_EqI8hq7RXrlf73BTAfNvzgJHeg_xWKar01Md0"
  121. WECHAT_OA_CERT_REMINDER_ENABLED: bool = True # 同时受服务号总开关控制
  122. WECHAT_OA_CERT_NOTICE_ROLE_NAME: str = "证件到期通知接收人"
  123. WECHAT_OA_CERT_REMINDER_HOUR: int = Field(default=10, ge=0, le=23)
  124. WECHAT_OA_CERT_REMINDER_MINUTE: int = Field(default=0, ge=0, le=59)
  125. # 微信模板消息ID配置
  126. WX_TEMPLATE_ORDER_STATUS: str = "" # 订单状态通知模板ID
  127. WX_TEMPLATE_DELIVERY_NOTICE: str = "" # 配送通知模板ID
  128. WX_TEMPLATE_PAYMENT_SUCCESS: str = "" # 支付成功通知模板ID
  129. WX_TEMPLATE_REFUND_RESULT: str = "" # 退款结果通知模板ID
  130. WX_TEMPLATE_DELIVERY_WARNING: str = "" # 配送时效预警模板ID
  131. # 与 docs/sql/shanxia2.0.sql 基线一致:登录按租户 + sys_user.user_no
  132. DEFAULT_TENANT_NO: str = "shanxia2.0"
  133. # 无 user_id 等异常链路的 Web 角色兜底;未绑定 sys_user_role 的用户在 resolve_web_role_for_user 中固定为 admin
  134. WEB_AUTH_DEFAULT_ROLE: str = "admin"
  135. # 登录 RSA:私钥 PEM(多实例/负载均衡须配置同一私钥;留空则进程启动时临时生成,仅适合单机开发)
  136. LOGIN_RSA_PRIVATE_KEY_PEM: str = ""
  137. # 是否允许明文 username/password 登录(仅建议本地排障临时开启;生产务必 false)
  138. LOGIN_ALLOW_PLAINTEXT_LOGIN: bool = False
  139. # 加密载荷内时间戳允许偏差(秒),防简单重放
  140. LOGIN_ENCRYPT_TS_SKEW_SEC: int = 300
  141. # 临时文件目录
  142. TEMP_FILE_DIR: str = "temp_files"
  143. # 司机免登录 H5 页面 Origin(用于拼接 driverUrl)
  144. PUBLIC_WEB_ORIGIN: str = "http://localhost:3006"
  145. # 文档库(Cloudreve)。门户用户按 id 一一对应到文档库账号。
  146. # 私有文档库;专用桶不得配置匿名读策略。
  147. DOCLIB_MINIO_BUCKET: str = "shanxia-doclib-private"
  148. DOCLIB_MAX_UPLOAD_BYTES: int = 100 * 1024 * 1024
  149. DOCLIB_BASE_URL: str = "http://127.0.0.1:5212"
  150. DOCLIB_PUBLIC_URL: str = "http://127.0.0.1:5212"
  151. DOCLIB_PORTAL_ORIGIN: str = "http://127.0.0.1:8000"
  152. DOCLIB_ADMIN_EMAIL: str = "test@shanxia.local"
  153. DOCLIB_ADMIN_PASSWORD: str = "Test123456"
  154. # 全量 API 请求日志(中间件写入 sys_api_request_log)
  155. API_REQUEST_LOG_ENABLED: bool = True
  156. API_REQUEST_LOG_MAX_BODY: int = 65536
  157. API_REQUEST_LOG_MAX_BODY_BYTES: int = 131072
  158. API_REQUEST_LOG_EXCLUDE_PATHS: str = ""
  159. # Web API 请求/响应加密配置
  160. API_ENCRYPT_ENABLED: bool = False
  161. API_ENCRYPT_FAIL_OPEN: bool = True
  162. API_ENCRYPT_FIXED_SECRET: str = ""
  163. API_ENCRYPT_TS_SKEW_SEC: int = 300
  164. API_ENCRYPT_NONCE_TTL_SEC: int = 300
  165. API_ENCRYPT_EXCLUDE_PATHS: str = (
  166. "/,"
  167. "/health,"
  168. "/docs,"
  169. "/openapi.json,"
  170. "/api/web/auth/login-rsa-public-key,"
  171. "/api/web/auth/login,"
  172. "/api/mini/auth/wechat-login,"
  173. "/api/web/auth/wx-qr/create,"
  174. "/api/web/auth/wx-qr/status,"
  175. "/api/mini/auth/web-qr-scanned,"
  176. "/api/mini/auth/web-qr-confirm,"
  177. "/api/web/auth/refresh-token,"
  178. "/api/web/auth/mp-webview-exchange,"
  179. "/api/public/wechat-notices/oauth/callback,"
  180. "/api/public/wechat-notices/oauth/launch,"
  181. "/api/web/admin/attachment/upload,"
  182. "/api/web/doclib/upload,"
  183. "/api/web/admin/products/upload-image,"
  184. "/api/web/admin/categories/upload-image,"
  185. "/api/web/admin/users/upload-avatar,"
  186. "/api/invoice/download,"
  187. "/api/bx/health,"
  188. "/api/db/health,"
  189. "/api/expense-items,"
  190. "/api/workflows,"
  191. "/api/advances,"
  192. "/api/reimburses,"
  193. "/api/payments,"
  194. "/api/attachments,"
  195. "/api/public/pl/transport,"
  196. "/api/public/pl/transport-site,"
  197. "/api/public/pl/site,"
  198. "/api/public/pl/inbound,"
  199. "/api/public/pl/outbound,"
  200. "/api/transport/site-plan,"
  201. "/api/transport/driver-plan,"
  202. "/api/public/db/inventory,"
  203. "/api/public/db/vehicle-plates,"
  204. "/api/public/duty"
  205. )
  206. class Config:
  207. # 支持通过 ENV_FILE 指定环境文件;未指定时回退到 backend/.env
  208. _default_env_file = Path(__file__).resolve().parents[2] / ".env"
  209. env_file = os.getenv("ENV_FILE", str(_default_env_file))
  210. case_sensitive = True
  211. settings = Settings()