# UpdateDB 定时从微信公众号平台获取视频与文章数据,同步到远端 PostgreSQL 数据库。 ## 技术栈 - Python 3.11 - httpx (HTTP 客户端) - psycopg2-binary (PostgreSQL 驱动) - APScheduler (定时调度) - Docker 部署 ## 项目结构 ``` UpdateDB/ main.py # 入口:启动调度器,支持 --once 手动触发 config.py # 加载 .env 配置 wechat.py # 微信 API 客户端(token 管理、素材获取) db.py # 数据库连接、建表、UPSERT sync.py # 同步编排逻辑 Dockerfile docker-compose.yml .env_example # 环境变量模板 api_docs/ # 微信 API 文档 ``` ## 数据库变更总结 ### 新建表:`videos` 程序启动时自动创建(`CREATE TABLE IF NOT EXISTS`),**不会影响已有表**。 ```sql CREATE TABLE IF NOT EXISTS videos ( id SERIAL PRIMARY KEY, -- 自增主键 media_id VARCHAR(128) UNIQUE, -- 微信素材 ID(唯一约束) name TEXT, -- 视频文件名(来自列表接口) url TEXT, -- 视频播放 URL(来自列表接口) title TEXT, -- 视频标题(来自详情接口) description TEXT, -- 视频描述(来自详情接口) down_url TEXT, -- 视频下载地址(来自详情接口,可能过期) wechat_update_time TIMESTAMP, -- 微信端更新时间 created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` **后端读取时注意**: - `url` 和 `down_url` 都可能过期失效,建议每次使用前检查可用性,或定期触发同步刷新 - `title` 和 `description` 可能为 NULL(首次同步时详情接口调用失败) - `url` 是播放地址,`down_url` 是下载地址,两者不同 ### 未修改的表 #### `articles`(原有表,未改结构) 程序往这些**已有字段**写入数据,未新增/修改任何列: | 数据库字段 | 写入内容 | 微信 API 来源 | 注意事项 | |---|---|---|---| | `title` | 文章标题 | `news_item.title` | 截断到 200 字符 | | `content` | 文章 HTML 内容 | `news_item.content` | 原始 HTML,微信已去除 JS | | `cover_url` | 封面图 URL | `news_item.thumb_url` | 截断到 500 字符,可能为 NULL | | `author` | 作者 | `news_item.author` | 截断到 100 字符,可能为 NULL | | `publish_date` | 发布日期 | `item.update_time` 转 date | 仅日期,不含时分秒 | | `source_url` | 文章原文链接 | `news_item.url` | 截断到 1000 字符,可能为 NULL | | `wechat_article_id` | 去重标识 | `{media_id}_{index}` | 格式见下方说明 | **未写入的字段**(保持原有值/NULL):`category`、`tags`(微信 API 无此数据) #### `sync_states`(原有表,未改结构) 使用已有的 key-value 结构记录同步状态: | key | value 示例 | |---|---| | `wechat_news_sync` | `{"status":"idle","count":42,"last_sync":"2026-04-09T..."}` | | `wechat_video_sync` | `{"status":"idle","count":5,"last_sync":"2026-04-09T..."}` | value 是 JSON 字符串,后端可用 `json.loads()` 解析。 ### 同步删除机制 每次同步完成后,程序会对比微信端和数据库的 `media_id`: - 数据库中有、微信端没有的记录 → **自动删除** - 涉及 `articles` 和 `videos` 两张表 - articles 的删除依据:`wechat_article_id` 中提取的 `media_id`(按最后一个 `_` 分割) ### `wechat_article_id` 格式说明 ``` 格式:{media_id}_{article_index} 示例:abc123def456_0 (单图文) abc123def456_1 (多图文中的第 2 篇) ``` - `media_id`:微信素材 ID,一个 media_id 对应一条图文素材 - `article_index`:图文内的文章序号(从 0 开始),多图文时会有 0、1、2... - 后端查询某篇具体文章:`WHERE wechat_article_id = '{media_id}_{idx}'` - 后端查询某条图文素材的所有文章:`WHERE wechat_article_id LIKE '{media_id}_%'` ### 数据写入策略 - **articles**:`SELECT` 检查 `wechat_article_id` 是否存在 → 存在则 `UPDATE`,不存在则 `INSERT` - **videos**:`INSERT ... ON CONFLICT (media_id) DO UPDATE`,仅在 `wechat_update_time` 更新时覆盖 - **sync_states**:`INSERT ... ON CONFLICT (key) DO UPDATE`,每次同步更新状态 ## 快速开始 ### 本地开发 1. 安装依赖: ```bash pip install -e . ``` 2. 复制配置文件并填写: ```bash cp .env_example .env # 编辑 .env,填入 app_secret 和数据库信息 ``` 3. 运行: ```bash # 启动定时同步(默认每 48 小时) python main.py # 只运行一次 python main.py --once ``` ### Docker 部署 1. 在 VPS 上克隆仓库,创建 `.env` 文件: ```bash cp .env_example .env # 编辑 .env ``` 2. 构建并启动: ```bash docker compose up -d --build ``` 3. 查看日志: ```bash docker compose logs -f ``` ## 环境变量 | 变量 | 必填 | 说明 | |------|------|------| | `app_id` | 是 | 微信公众号 AppID | | `app_secret` | 是 | 微信公众号 AppSecret | | `db_host` | 是 | PostgreSQL 主机 | | `db_port` | 是 | PostgreSQL 端口 | | `db_name` | 是 | 数据库名 | | `db_user` | 是 | 数据库用户 | | `db_pw` | 是 | 数据库密码 | | `sync_interval_hours` | 否 | 同步间隔(小时),默认 48 | ## 同步逻辑 1. 调用微信稳定版 Token 接口获取 access_token(自动缓存和刷新) 2. 分页获取所有 news(图文)和 video(视频)素材 3. 对视频素材,额外调用详情接口获取下载地址 4. 使用 UPSERT 写入数据库(INSERT ON CONFLICT DO UPDATE) 5. news 和 video 独立同步,一个失败不影响另一个