From 1cdd0bcab15666785cf0d7c8ceab45f6bf77310b Mon Sep 17 00:00:00 2001 From: v6ole Date: Sat, 9 May 2026 13:30:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=20FastAPI=20?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E9=AA=A8=E6=9E=B6=EF=BC=88config=20+=20main?= =?UTF-8?q?=20+=20=E4=BE=9D=E8=B5=96=E7=AE=A1=E7=90=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 30 ++++++++++++++++++++++++++++++ .gitignore | 10 ++++++++++ app/__init__.py | 0 app/config.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ app/main.py | 27 +++++++++++++++++++++++++++ pyproject.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 app/__init__.py create mode 100644 app/config.py create mode 100644 app/main.py create mode 100644 pyproject.toml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..db88074 --- /dev/null +++ b/.env.example @@ -0,0 +1,30 @@ +# 应用 +DEBUG=false +LOG_LEVEL=INFO + +# 数据库 +DATABASE_URL=postgresql+asyncpg://gx-gp-notify:password@10.10.10.14:5432/gx-gp-notify + +# 爬虫 +CRAWLER_BASE_URL=https://zfcg.gxzf.gov.cn +CRAWLER_KEYWORDS=["大化"] +CRAWLER_MAX_PAGES=10 +CRAWLER_TIMEOUT=30 + +# 企业微信 +WECHAT_ENABLED=true +WECHAT_CORP_ID=ww69e8e44636f47780 +WECHAT_AGENT_ID=1000007 +WECHAT_SECRET= +WECHAT_TOKEN= +WECHAT_ENCODING_AES_KEY= +WECHAT_PORT=18001 +WECHAT_HOST=0.0.0.0 + +# 定时任务 +SCHEDULER_ENABLED=true +SCHEDULER_CRON=0 8,14,18 * * * + +# Markdown +MARKDOWN_ENABLED=true +MARKDOWN_OUTPUT_FILE=onu.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0758ee2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.env +logs/ +*.log +__pycache__/ +*.pyc +.venv/ +.ruff_cache/ +.pytest_cache/ +*.egg-info/ +dist/ diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..da09e3e --- /dev/null +++ b/app/config.py @@ -0,0 +1,44 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict +from typing import List + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") + + # 应用 + debug: bool = False + log_level: str = "INFO" + + # 数据库 + database_url: str = "postgresql+asyncpg://gx-gp-notify:password@localhost:5432/gx-gp-notify" + + # 爬虫 + crawler_base_url: str = "https://zfcg.gxzf.gov.cn" + crawler_keywords: List[str] = ["大化"] + crawler_max_pages: int = 10 + crawler_timeout: int = 30 + crawler_page_size: int = 100 + + # 企业微信 + wechat_enabled: bool = True + wechat_corp_id: str = "" + wechat_agent_id: str = "" + wechat_secret: str = "" + wechat_token: str = "" + wechat_encoding_aes_key: str = "" + wechat_port: int = 18001 + wechat_host: str = "0.0.0.0" + + # 定时任务 + scheduler_enabled: bool = True + scheduler_cron: str = "0 8,14,18 * * *" + + # Markdown + markdown_enabled: bool = True + markdown_output_file: str = "onu.md" + + # 公告来源(JSON 字符串,从环境变量读取) + announcement_sources: str = '{"ZcyAnnouncement1":{"category_id":66485,"name":"采购公告","type":"purchase"},"ZcyAnnouncement2":{"category_id":66485,"name":"结果公告","type":"result"},"ZcyAnnouncement3":{"category_id":66485,"name":"合同公告","type":"contract"},"ZcyAnnouncement4":{"category_id":66485,"name":"更正公告","type":"correction"},"ZcyAnnouncement5":{"category_id":66485,"name":"招标文件预公示","type":"pre_announcement"},"ZcyAnnouncement6":{"category_id":66485,"name":"单一来源公示","type":"single_source"},"ZcyAnnouncement7":{"category_id":66485,"name":"电子卖场公示","type":"electronic_market"},"ZcyAnnouncement10":{"category_id":66485,"name":"履约验收公示","type":"acceptance"},"ZcyAnnouncement11":{"category_id":66485,"name":"工程类公告","type":"engineering"},"ZcyAnnouncement20":{"category_id":66485,"name":"框架协议征集公告","type":"framework_agreement"},"ZcyAnnouncement21":{"category_id":66485,"name":"框架协议入围结果公告","type":"framework_result"},"ZcyAnnouncement23":{"category_id":66485,"name":"框架协议成交结果汇总公告","type":"framework_summary"},"61-266648":{"category_id":66485,"name":"采购意向公开","type":"intention"}}' + + +settings = Settings() diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..a72191f --- /dev/null +++ b/app/main.py @@ -0,0 +1,27 @@ +import logging +from contextlib import asynccontextmanager +from fastapi import FastAPI +from app.config import settings + + +@asynccontextmanager +async def lifespan(app: FastAPI): + logging.basicConfig( + level=getattr(logging, settings.log_level), + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", + ) + yield + + +app = FastAPI( + title="广西政府采购网公告监控系统", + version="2.0.0", + lifespan=lifespan, + docs_url="/docs" if settings.debug else None, + redoc_url=None, +) + + +@app.get("/health") +async def health(): + return {"status": "ok"} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9c1f846 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[project] +name = "gx-gp-notify" +version = "2.0.0" +description = "广西政府采购网公告监控系统" +requires-python = ">=3.12" +dependencies = [ + "fastapi>=0.115.0", + "uvicorn[standard]>=0.30.0", + "sqlalchemy[asyncio]>=2.0.30", + "asyncpg>=0.29.0", + "alembic>=1.13.0", + "httpx>=0.27.0", + "apscheduler>=3.10.0", + "pydantic-settings>=2.3.0", + "beautifulsoup4>=4.12.0", + "lxml>=5.2.0", + "pycryptodome>=3.20.0", + "python-dateutil>=2.9.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.2.0", + "pytest-asyncio>=0.23.0", + "pytest-cov>=5.0.0", + "ruff>=0.4.0", +] + +[tool.setuptools.packages.find] +include = ["app*"] + +[tool.ruff] +target-version = "py312" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "W", "UP"] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"]