diff --git a/.env b/.env new file mode 100644 index 0000000..f121ae1 --- /dev/null +++ b/.env @@ -0,0 +1,22 @@ +ENV=dev + +# MySQL(业务库) +DB_HOST=8.163.14.142 +DB_PORT=30006 +DB_USER=root +DB_PASSWORD=^CgDatabase2020 +DB_NAME=offerpie +MYSQL_POOL_SIZE=10 +MYSQL_MAX_OVERFLOW=10 + +# 连接池获取连接等待超时(秒) +DB_POOL_TIMEOUT=30 + +# AI 供应商 +# 火山引擎(OpenAI 兼容风格) +VOLCENGINE_API_KEY=fd065993-bee2-4f31-8bf2-56d5d3012c02 +VOLCENGINE_BASE_URL=https://ark.cn-beijing.volces.com/api/v3 + +# Claude(Anthropic 风格) +ANTHROPIC_API_KEY=sk-43ccdb29caa7e9ebe0db8ac0958c63f6d3a2d62e59064d3d26d94332055a9bc9 +ANTHROPIC_BASE_URL=https://code.warpdevloper.cloud diff --git a/.env.prod b/.env.prod new file mode 100644 index 0000000..7c05f6c --- /dev/null +++ b/.env.prod @@ -0,0 +1,22 @@ +ENV=prod + +# MySQL(业务库) +DB_HOST=8.163.14.142 +DB_PORT=30006 +DB_USER=root +DB_PASSWORD=^CgDatabase2020 +DB_NAME=offerpie +MYSQL_POOL_SIZE=10 +MYSQL_MAX_OVERFLOW=10 + +# 连接池获取连接等待超时(秒) +DB_POOL_TIMEOUT=30 + +# AI 供应商 +# 火山引擎(OpenAI 兼容风格) +VOLCENGINE_API_KEY=fd065993-bee2-4f31-8bf2-56d5d3012c02 +VOLCENGINE_BASE_URL=https://ark.cn-beijing.volces.com/api/v3 + +# Claude(Anthropic 风格) +ANTHROPIC_API_KEY=sk-43ccdb29caa7e9ebe0db8ac0958c63f6d3a2d62e59064d3d26d94332055a9bc9 +ANTHROPIC_BASE_URL=https://code.warpdevloper.cloud diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..ffd6e44 --- /dev/null +++ b/.env.test @@ -0,0 +1,22 @@ +ENV=test + +# MySQL(业务库) +DB_HOST=192.168.31.105 +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=123456 +DB_NAME=offerpie +MYSQL_POOL_SIZE=10 +MYSQL_MAX_OVERFLOW=10 + +# 连接池获取连接等待超时(秒) +DB_POOL_TIMEOUT=30 + +# AI 供应商 +# 火山引擎(OpenAI 兼容风格) +VOLCENGINE_API_KEY=fd065993-bee2-4f31-8bf2-56d5d3012c02 +VOLCENGINE_BASE_URL=https://ark.cn-beijing.volces.com/api/v3 + +# Claude(Anthropic 风格) +ANTHROPIC_API_KEY=sk-43ccdb29caa7e9ebe0db8ac0958c63f6d3a2d62e59064d3d26d94332055a9bc9 +ANTHROPIC_BASE_URL=https://code.warpdevloper.cloud diff --git a/.gitignore b/.gitignore index c3b5d5f..1dbc335 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ .venv/ -.env -.env.test -.env.prod .idea/ .vscode/ .history/ diff --git a/app/config/settings.py b/app/config/settings.py index 6b7271a..3fd374e 100644 --- a/app/config/settings.py +++ b/app/config/settings.py @@ -6,6 +6,18 @@ class Settings(BaseSettings): env: str = "dev" + # ──────────── MySQL(业务库)──────────── + db_host: str = "192.168.31.105" + db_port: int = 3306 + db_user: str = "root" + db_password: str = "123456" + db_name: str = "offerpie" + mysql_pool_size: int = 10 + mysql_max_overflow: int = 10 + + # 连接池获取连接的等待超时(秒) + db_pool_timeout: int = 30 + # 火山引擎(OpenAI 兼容风格) volcengine_api_key: str = "" volcengine_base_url: str = "https://ark.cn-beijing.volces.com/api/v3" @@ -14,6 +26,18 @@ class Settings(BaseSettings): anthropic_api_key: str = "" anthropic_base_url: str = "" + # ──────────── 日志 ──────────── + logging_level: str = "INFO" + log_file_name: str = "spider.log" + + @property + def mysql_url(self) -> str: + from urllib.parse import quote + return ( + f"mysql+pymysql://{self.db_user}:{quote(self.db_password, safe='')}" + f"@{self.db_host}:{self.db_port}/{self.db_name}" + ) + model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/database.py b/app/core/database.py new file mode 100644 index 0000000..13e3c74 --- /dev/null +++ b/app/core/database.py @@ -0,0 +1,50 @@ +"""MySQL 业务库数据源(同步)""" + +from typing import Optional + +from sqlalchemy import Engine, create_engine +from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker + +from app.config import settings +from app.core.logger import log + +# ──────────── 内部变量 ──────────── +_mysql_engine: Optional[Engine] = None +_mysql_session_factory: Optional[sessionmaker[Session]] = None + + +class MysqlBase(DeclarativeBase): + """MySQL ORM 声明基类""" + pass + + +def init_db() -> None: + """初始化 MySQL 数据源""" + global _mysql_engine, _mysql_session_factory + + _mysql_engine = create_engine( + settings.mysql_url, + pool_size=settings.mysql_pool_size, + max_overflow=settings.mysql_max_overflow, + pool_timeout=settings.db_pool_timeout, + pool_recycle=3600, + pool_pre_ping=True, + echo=False, + ) + _mysql_session_factory = sessionmaker(_mysql_engine, expire_on_commit=False) + + log.info("MySQL 数据源初始化完成: {}", settings.db_host) + + +def close_db() -> None: + """关闭 MySQL 数据源""" + if _mysql_engine: + _mysql_engine.dispose() + log.info("MySQL 数据源已关闭") + + +def MysqlSession() -> Session: + """获取 MySQL 会话(用作 with MysqlSession() as session)""" + if _mysql_session_factory is None: + raise RuntimeError("数据库未初始化,请先调用 init_db()") + return _mysql_session_factory() diff --git a/app/core/logger.py b/app/core/logger.py new file mode 100644 index 0000000..17d913e --- /dev/null +++ b/app/core/logger.py @@ -0,0 +1,34 @@ +"""日志配置""" + +import sys +from pathlib import Path + +from loguru import logger + +from app.config import settings + +# 日志目录 +_log_dir = Path("logs") +_log_dir.mkdir(exist_ok=True) + +# 移除默认 handler +logger.remove() + +# 控制台输出 +logger.add( + sys.stdout, + level=settings.logging_level, + format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{line} - {message}", +) + +# 文件输出(按天轮转,保留30天) +logger.add( + _log_dir / settings.log_file_name, + level=settings.logging_level, + rotation="00:00", + retention="30 days", + encoding="utf-8", + format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{line} - {message}", +) + +log = logger diff --git a/requirements.txt b/requirements.txt index c6284f1..ca5727b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,15 @@ onnxruntime>=1.17 opencv-python>=4.5 playwright>=1.49 pydantic-settings>=2.0 + +# 数据库 +sqlalchemy>=2.0 +pymysql>=1.1 + +# AI langchain-openai>=0.3 langchain-anthropic>=0.3 langchain-core>=0.3 + +# 工具 +loguru>=0.7