This commit is contained in:
Marsway 2026-01-13 16:01:05 +08:00
parent 25eb7fdace
commit a20c9ed91f
2 changed files with 83 additions and 34 deletions

34
Log.md
View File

@ -1,34 +0,0 @@
# Job 执行全量日志(落盘)
本项目在任务执行时,会将该次执行的 **全量日志** 落盘到 `data/logs`(容器内为 `/data/logs`)下,便于排障与留存。
## 1. 路径规则
- 根目录:`./data/logs/`
- 按任务分目录:`./data/logs/<job_id>/`
- 单次执行日志文件名:
- `YYYY-MM-DD_HH-mm-ss_log-<job_log_id>.log`
- 时间戳使用 **Asia/Shanghai**(与调度时区一致)
示例:
```text
./data/logs/sync_oa_to_didi.sync_legal_entity/2026-01-13_10-20-33_log-1234.log
```
> 注意:若 `job_id` 中包含路径分隔符(`/` 或 `\`),会被替换为 `_`,避免产生目录穿越或多级目录。
## 2. 与 Admin 的 JobLog.run_log 的区别
- **`JobLog.run_log`(入库)**
- 展示在 Admin 的 JobLog 详情页
- 有字节上限(会截断),适合快速浏览
- **`data/logs/.../*.log`(落盘全量)**
- 尽力写入,不做长度截断
- 适合完整排障、留存、归档
## 3. 运维建议
- 建议将 `./data/logs` 纳入备份或日志归档策略(按业务合规要求)。
- 如需自动清理(防止无限增长),推荐在宿主机使用 `logrotate` 或定时任务做保留策略(本项目不内置自动清理逻辑)。

83
docs/STRUCTURE.md Normal file
View File

@ -0,0 +1,83 @@
```text
.
├── app/ # 核心框架层:加载/调度/执行/后台/安全/适配器
│ ├── main.py # FastAPI 入口;挂载 SQLAdmin启动时确保 key 与 DB schema
│ ├── admin/ # SQLAdmin 管理后台
│ │ ├── views.py # Job/JobLog 的管理视图、校验、动作按钮、模板选择
│ │ ├── routes.py # Admin 自定义 POST 路由Retry/Run 等)
│ │ └── templates/ # 覆盖/扩展 SQLAdmin 的模板list/details/edit 等)
│ ├── core/ # 运行时基础设施(配置/日志/上下文/捕获)
│ │ ├── config.py # Settings从 .env 读取DB_URL/REDIS_URL/FERNET_KEY_PATH 等
│ │ ├── logging.py # 全局日志配置stdout + 可选文件)
│ │ ├── log_capture.py # “尽力捕获”本次执行日志到 JobLog.run_log不影响执行
│ │ └── log_context.py # 将 job_id/log_id 写入日志上下文(便于追踪)
│ ├── db/ # 数据层SQLAlchemy
│ │ ├── models.py # Job / JobLog 模型定义
│ │ ├── engine.py # create_engine + SessionLocal
│ │ ├── crud.py # 基础 CRUD读 Job、写 JobLog、清理日志等
│ │ └── schema.py # 轻量 schema 自升级create_all + 补列)
│ ├── security/ # 安全模块
│ │ └── fernet.py # Fernet key 管理 + JSON 加解密(兼容脏数据)
│ ├── plugins/ # 插件加载
│ │ └── manager.py # handler_path -> import -> BaseJob 子类校验 -> instantiate
│ ├── jobs/ # 插件规范
│ │ └── base.py # BaseJob 抽象基类run(params, secrets)
│ ├── integrations/ # 适配器/SDK业务 Job 禁止直接写 HTTP
│ │ ├── base.py # BaseClient超时/重试/日志;统一 request/get_json/post_json
│ │ ├── seeyon.py # SeeyonClient致远 OA token 认证 + 自动携带 token header
│ │ └── didi.py # (若存在)滴滴侧 SDK/适配器封装
│ └── tasks/ # Celery 任务引擎
│ ├── celery_app.py # Celery 配置broker/backend、timezone、beat_schedule 等)
│ ├── dispatcher.py # 定时调度:每分钟 tickcron_expr 命中则触发 execute_job
│ └── execute.py # 统一执行入口:读库->解密->加载插件->run->写 JobLog
├── extensions/ # 业务插件层(必须独立于框架层)
│ ├── __init__.py
│ └── sync_oa_to_didi/ # 示例插件:仅演示 token 获取日志
│ ├── __init__.py
│ └── job.py # SyncOAToDidiTokenJob调用 SeeyonClient.authenticate 并记录日志)
├── docker/ # 镜像构建
│ └── Dockerfile # Python 镜像 + APT 镜像源切换 + 安装依赖
├── docker-compose.yml # 生产基线backend/worker/beat/redis/postgres + ./data 挂载
├── docker-compose.dev.yml # 开发叠加:源码挂载 + backend reload + worker/beat watchfiles 重启
├── env.example # 环境变量示例(本地复制为 .env
├── connecthub.sh # 一键脚本build/start/restart/stop + dev-* + log
├── pyproject.toml # Python 依赖FastAPI/Celery/SQLAdmin/psycopg/cryptography 等)
├── README.md # “开发手册”主文档运行指南、Admin 使用、示例配置)
└── data/ # 运行数据目录(建议仅作为 volume不作为源码管理
├── logs/ # 应用日志落点
├── pgdata/ # PostgreSQL 数据目录volume
└── connecthub.db # 旧 SQLite 残留(若已切换 PG可视情况清理
```
---
## 核心执行链路(从 Job 到 JobLog
1. **Job 定义**在 DB 表 `jobs``id/cron_expr/handler_path/public_cfg/secret_cfg`
2. **Beat** 每分钟触发一次 `dispatcher.tick`:读取 `jobs`,用 `cron_expr` 判断是否到点
3. 到点后 `dispatcher` 调用 `execute_job.delay(job_id=...)`
4. **Worker** 执行 `execute_job`
- 读 Job -> 解密 `secret_cfg`Fernet仅内存
- PluginManager 动态加载 `handler_path` 指向的 `BaseJob` 子类
- 调用 `job.run(params, secrets)`
- 捕获/汇总日志run_log与异常traceback写入 `job_logs`Admin 可视化查看/重试)
---
## 框架层 vs 插件层(边界提示)
- **框架层app/**:只提供通用能力(调度、执行、加解密、日志、管理后台、适配器基类),不包含具体业务流程。
- **插件层extensions/**:只放“具体业务 Job”例如“同步员工/同步部门/同步 OA→滴滴”等必须继承 `BaseJob`
- **外部系统调用**:禁止在 Job 内直接写 HTTP必须通过 `app/integrations/*` 下的 Client。
---
## 运行数据与持久化data/
`data/` 是运行时 volume
- `data/pgdata/`PostgreSQL 持久化数据
- `data/logs/`:应用日志
- `data/fernet.key`Fernet key正式环境必须固定否则历史 secret_cfg 无法解密)
建议:`data/` 仅作为挂载目录,不当作源码文件纳入版本管理。