# EasyJob **Repository Path**: duanzt/EasyJob ## Basic Information - **Project Name**: EasyJob - **Description**: EasyJob 让所有的Job都变得简单。 多种任务类型 — SQL 执行任务、ETL 数据同步、SQL 监控通知、飞书表格同步 可视化工作流引擎 — 基于 DAG 的拖拽式工作流设计器,支持 10 种节点类型(数据源/SQL执行/HTTP/C#/JavaScript/条件分支/循环/分片查询/通知/AI),支持分支跳过、级联跳过、多入边合并、失败策略(Continue/Abort/Fallbac - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 7 - **Created**: 2026-10-12 - **Last Updated**: 2026-10-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [English](README.en.md) | 中文
# EasyJob **基于 ASP.NET Core + Vue.js 的分布式任务调度与 ETL 数据同步平台** [![.NET](https://img.shields.io/badge/.NET-10.0-512BD4?logo=dotnet)](https://dotnet.microsoft.com/) [![Vue.js](https://img.shields.io/badge/Vue.js-3.5-4FC08D?logo=vuedotjs)](https://vuejs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.8-3178C6?logo=typescript)](https://www.typescriptlang.org/) [![Vite](https://img.shields.io/badge/Vite-6.3-646CFF?logo=vite)](https://vitejs.dev/) [![Element Plus](https://img.shields.io/badge/Element_Plus-2.9-409EFF?logo=element)](https://element-plus.org/) [![Quartz.NET](https://img.shields.io/badge/Quartz.NET-4.4-B71731?logo=quartz)](https://www.quartz-scheduler.net/) [![License](https://img.shields.io/badge/License-MIT-green)](LICENSE)
--- ## ✨ 功能特性 - **多种任务类型** — SQL 执行任务、ETL 数据同步、SQL 监控通知、飞书表格同步、HTTP 接口任务 - **可视化工作流引擎** — 基于 DAG 的拖拽式工作流设计器,支持 11 种节点类型(数据源/SQL执行/HTTP/C#/JavaScript/条件分支/循环/分片查询/通知/AI/结果输出),支持分支跳过、级联跳过、多入边合并、失败策略(Continue/Abort/Fallback) - **AI 节点集成** — 工作流中可直接调用 AI 大模型(OpenAI/Azure/Ollama/DeepSeek/通义千问/智谱/OneAPI/自定义),8 种预设配置 - **灵活的定时调度** — 基于 Quartz.NET,支持 Cron 表达式自定义调度,线程池并发可配(默认 50) - **三种 ETL 同步模式** — 全量同步、增量同步、聚合同步 - **多数据库支持** — SQL Server、MySQL、PostgreSQL、SQLite、Oracle - **多渠道通知** — 邮件通知、飞书通知、钉钉通知、企业微信通知 - **任务重试机制** — 支持任务超时设置和失败自动重试 - **分布式部署高可用** — 多副本部署下保证“同一任务同一时刻只被一台机器执行、且每个触发窗口恰好执行一次”(详见下文[分布式部署与高可用](#-分布式部署与高可用)) - **安全防护** — SQL 安全校验、C#/JS 代码节点沙箱隔离、登录限流防暴力破解(5 次/分钟/IP)、审计日志全操作可追溯 - **RBAC 权限控制** — 基于角色的访问控制,支持用户组管理 - **Windows 服务** — 原生支持以 Windows Service 方式运行 --- ![输入图片说明](demo_image/demo_00.png) ![输入图片说明](demo_image/demo_01.png) ![输入图片说明](demo_image/demo_02.png) ![输入图片说明](demo_image/demo_03.png) ![输入图片说明](demo_image/demo_04.png) ![输入图片说明](demo_image/demo_05.png) ## 🛠 技术栈 ### 后端 | 技术 | 版本 | 说明 | |------|------|------| | ASP.NET Core | 10.0 | Web 框架 | | Entity Framework Core | 10.0 | ORM(多数据库支持) | | Dapper | 2.1 | 高性能数据查询 | | Quartz.NET | 4.4 | 任务调度引擎(支持集群模式) | | StackExchange.Redis | 3.2 | Redis 分布式锁客户端 | | JWT | — | 身份认证 | | BCrypt | 4.2 | 密码加密 | | Serilog | 4.3 | 结构化日志 | | MailKit | 4.16 | 邮件发送 | | FeishuNetSdk | 4.1 | 飞书集成 | | ClosedXML | 0.105 | Excel 导出 | | Swashbuckle | 10.3 | Swagger UI(OpenAPI 文档由 .NET 10 内置生成) | ### 前端 | 技术 | 版本 | 说明 | |------|------|------| | Vue.js | 3.5 | 前端框架 | | TypeScript | 5.8 | 类型安全 | | Vite | 6.3 | 构建工具 | | Element Plus | 2.9 | UI 组件库 | | Pinia | 3.0 | 状态管理 | | Vue Router | 4.5 | 路由管理 | | Axios | 1.9 | HTTP 请求 | | Monaco Editor | 0.52 | SQL 编辑器 | | ECharts | 6.0 | 图表可视化 | ### 数据库 SQL Server | MySQL | PostgreSQL | SQLite(默认,零配置) | Oracle --- ## 🔧 工作流引擎 EasyJob 内置一套基于 DAG(有向无环图)的可视化工作流引擎,支持拖拽式编排复杂的数据处理流水线,节点间通过 `VariablePool` 自动传递数据。 ### 节点类型 | 节点 | 类型字符串 | 用途 | 输出契约 | |------|-----------|------|----------| | 数据源查询 | `DataSource` | 执行 SELECT 查询返回数据行 | `List>` | | SQL 执行 | `SqlExecute` | 执行 INSERT/UPDATE/DELETE,返回影响行数 | `{ affectedRows }` | | HTTP 请求 | `Http` | 调用外部 API,支持 GET/POST/PUT/DELETE 等 | `{ statusCode, body, headers }` | | C# 代码 | `CSharpCode` | 用 Roslyn 执行 C# 脚本,支持 NuGet 包引用 | 任意(脚本返回值) | | JavaScript 代码 | `JavaScript` | 用 Jint 执行 JS 脚本(沙箱) | 任意(run 函数返回值) | | 条件分支 | `Condition` | 按 cases 顺序匹配,首个命中决定 Branch | `{ branch, matchedCase, field, value }` | | 循环展开 | `Loop` | 将上游 List 展开为可迭代的 records 输出 | `{ count, records, itemVariableName }` | | 分片查询 | `ShardingDataSource` | 支持 Modulo/Time/Database 三种分片模式 | `List>` | | 通知发送 | `Notification` | 飞书/钉钉/企业微信/邮件多渠道并发 | `{ sent, channels[] }` | | AI 调用 | `AI` | 调用大模型生成文本,支持 8 种预设 | `{ response }` | | 结果输出 | `Output` | return / database / file 三种输出模式 | 取决于 mode | ### 数据流与变量插值 - **节点输出**:每个节点执行成功后,其 `Output` 写入 `VariablePool`,下游节点可按 `nodeId` 引用 - **模板插值**:配置中支持 `{{nodeId.fieldName}}` 语法,引擎自动从 VariablePool 取值并替换 - **工作流输入**:执行工作流时可通过 `InputJson` 传参,在节点配置中用 `input.fieldName` 引用(如 `input.branchSelector`) - **C# 节点 input**:单上游 → `input` 即上游输出;多上游 → `input` 是 `Dictionary` - **JS 节点 input**:始终 `input.{上游nodeId}.xxx` 访问(更直观的字典访问) ### 分支与跳过机制 - **Condition 节点**:按 cases 数组顺序匹配,首个命中的 case.label 作为返回的 Branch;都不匹配走 `defaultBranch` - **边分支绑定**:Condition 节点的出边可设置 `branch` 字段,仅当节点实际返回的 Branch 与边的 branch 一致时该边激活 - **级联跳过**:被跳过的节点不会写入 `VariablePool`,其无条件下游(branch=null 的入边)也会因上游无输出而级联跳过,避免空输入执行 - **多入边合并**:节点有多个入边时,只要任意一条入边激活即可执行;`final_out` 类合并节点天然支持三分支汇合 - **失败策略**:每个节点可配置 `FailureStrategy`: - `Abort`(默认)— 节点失败立即终止工作流 - `Continue` — 跳过失败节点,下游不含其输出 - `Fallback` — 注入 `{ error }` 作为输出,按 `FallbackBranch` 走指定分支 ### 安全保障 - **SQL 安全校验**:所有 SQL 节点强制过 `SqlSecurityValidator`,禁止 DDL(CREATE/DROP/ALTER/TRUNCATE),SELECT 强制只读 - **C# 沙箱**:C# 节点通过 `AssemblyLoadContext` 隔离 NuGet 包版本,超时控制,使用 `InvariantCulture` 避免中文乱码 - **JS 沙箱**:Jint 解释器无文件/网络 API,限制递归深度 1000,超时控制 - **标识符校验**:表名/列名严格正则校验,防 SQL 注入 --- ## 🌐 分布式部署与高可用 多副本部署(如 50 台服务器跑同一个定时任务)时,EasyJob 通过**四层机制**保证任务"不重复执行、且必须执行一次": | 防线 | 机制 | 拦截的异常场景 | |------|------|----------------| | ① 分布式锁互斥 | Redis `SET NX EX` 或数据库主键锁(二选一,`DistributedLock:Provider` 配置) | 多节点同一时刻并发触发 | | ② 锁持有者校验(CAS) | 续期/释放必须校验持有者身份(Redis Lua 原子脚本 / SQL 条件更新) | 长任务锁过期易主后误删他人锁,引发三节点级联并发 | | ③ 心跳中止 | 锁续期连续 3 次失败即主动中止本次执行,交由重试机制补跑 | 锁存储抖动/网络分区期间防止双节点同时执行 | | ④ 同窗口幂等复查 | 以 Quartz 触发时间为基准,5 秒容差内已执行过则跳过 | 毫秒级快任务释放锁后,同窗口延迟触发的节点串行重复执行 | 节点重启场景同样安全:启动时只回收**已过期**的锁,绝不误删其他节点正在持有的有效锁;调度注册采用幂等跳过,50 节点同时启动互不冲突。 ### 可选:Quartz 集群模式 开启后调度层由集群统一裁决触发权(单点触发 + 节点故障自动转移 + misfire 自动补跑),与业务层锁构成双保险: ```json "Quartz": { "EnableClustering": true, "ClusterCheckinIntervalSeconds": 10, "MisfireThresholdSeconds": 60, "ThreadPoolSize": 50 } ``` 启用步骤: 1. 所有节点的主库切换为 SQL Server / MySQL / PostgreSQL(SQLite 多节点共享文件不可靠,Oracle 需自行导入 Quartz 官方 QuRTZ 建表脚本,二者都会自动降级回单机模式并在日志中提示) 2. 在主库执行 `database/quartz_tables_sqlserver.sql` 创建 Quartz 集群表 3. 所有节点设置 `EnableClustering: true` 并连接同一主库 `ThreadPoolSize`(默认 50)控制本节点并行执行的任务数上限;集群模式下同时是本节点可从集群领取的触发器数——调小可把任务摊到更多节点,调大则提升单节点吞吐。启动日志 `Using thread pool ... size: N` 可确认实际生效值。 > 集群模式采用"先到先得"的竞争式任务分配(非负载均衡器):节点只会领取不超过其空闲线程数的触发器,忙节点自动让出、闲节点多接活,任务量大时长期统计趋近均衡;正确性(不重复、恰好一次)与分布无关。 --- ## 📁 项目结构 ``` EasyJob/ ├── backend/ # 后端 .NET 项目 │ ├── EasyJob.Api/ # API 层(控制器、启动配置、前端静态文件托管) │ ├── EasyJob.Core/ # 核心层(实体、DTO、接口) │ ├── EasyJob.Infrastructure/ # 基础设施层(数据访问、服务实现、调度) │ ├── EasyJob.ScriptHost/ # C# 代码节点独立进程沙箱宿主 │ └── EasyJob.Tests/ # 单元测试(含数据库迁移约定校验) ├── frontend/ # 前端 Vue.js 项目 │ └── src/ │ ├── api/ # API 请求封装 │ ├── components/ # 公共组件(含 workflow/ 工作流设计器组件) │ ├── layouts/ # 布局组件 │ ├── router/ # 路由配置 │ ├── stores/ # 状态管理 │ └── views/ # 页面视图 ├── database/ # 数据库初始化脚本(含 Quartz 集群建表 SQL、迁移备案 SQL) ├── docs/ # 技术文档 ├── docker-compose.yml # Docker 部署配置 └── start.bat # 一键启动脚本(开发模式) ``` > **前后端产物集成**:`frontend` 执行 `npm run build` 时,Vite 会直接把产物输出到 `backend/EasyJob.Api/Views`(见 `vite.config.ts` 的 `outDir`),由后端统一托管,无需 nginx、无需手工拷贝。 --- ## 🚀 快速开始 ### 环境要求 - .NET 10.0 SDK 或更高版本 - Node.js 18+ - 数据库:SQLite(默认,零配置)/ SQL Server / MySQL / PostgreSQL / Oracle ### 方式一:本地开发 ```bash # 启动后端(默认监听 http://localhost:8080,由 appsettings.json 的 Kestrel 配置决定) cd backend dotnet run --project EasyJob.Api # 启动前端(Vite 开发服务器,/api 自动代理到后端 8080) cd frontend npm install npm run dev ``` 也可以直接双击 `start.bat` 一键启动前后端。 ### 方式二:Docker 部署 ```bash # 1. 准备环境变量(三项均为必填,缺失时 compose 会拒绝启动,拒绝带着公开默认密钥跑生产) cp .env.example .env # 编辑 .env,填写 SA_PASSWORD / JWT_KEY / ADMIN_INITIAL_PASSWORD # 2. 启动 docker-compose up -d ``` 部署包含以下服务(前端产物由 ASP.NET Core 直接托管,无需独立 nginx 容器): | 服务 | 说明 | 端口 | |------|------|------| | easyjob-api | 后端 API + 前端静态资源 | 5000 | | sqlserver | SQL Server 2022 | 1433 | > ⚠️ `dataprotection_keys` 卷存储数据保护密钥(库内凭据加密依赖),**千万不能删除**,否则已加密的连接串/AppSecret/ApiKey 将无法解密。 ### 方式三:Windows 服务部署 后端已内置 `UseWindowsService()` 支持,标准部署流程: ```powershell # 1. 发布(前端产物已在 Views 目录时,直接发布后端即可) dotnet publish backend/EasyJob.Api -c Release -o C:\EasyJob # 2. 安装并启动服务(管理员 PowerShell) sc.exe create EasyJob binPath= "C:\EasyJob\EasyJob.Api.exe" start= auto sc.exe start EasyJob ``` 服务默认监听 `http://0.0.0.0:8080`(由 `appsettings.json` 的 `Kestrel:Endpoints:Http:Url` 配置,对 Windows 服务 / Docker / 直接运行均生效),修改配置后重启服务即可。 --- ## 🔑 默认账户 | 项目 | 值 | |------|------| | 用户名 | `admin` | | 密码 | `Admin@123` | > ⚠️ 该密码已在仓库中公开,部署后请**立即登录修改**,生产环境请配合强密码与 HTTPS 使用。 --- ## 🌐 访问地址 | 服务 | 地址 | |------|------| | 前端(开发模式) | http://localhost:5173 | | 前端 + API(发布模式 / Windows 服务) | http://localhost:8080 | | 前端 + API(Docker) | http://localhost:5000 | | Swagger 文档 | http://localhost:8080/swagger | > 说明:`dotnet run` 时 launchSettings.json 的 5271 会被 `appsettings.json` 的 Kestrel 端点配置覆盖(启动日志会打印 `Overriding address(es)`),实际以 8080 为准。 --- ## 📡 API 接口 | 模块 | 路径 | 说明 | |------|------|------| | 认证 | `/api/auth` | 登录、注册、刷新 Token | | 任务管理 | `/api/tasks` | 任务 CRUD、执行、调度 | | 工作流 | `/api/workflows` | 工作流 CRUD、手动执行、实例查询 | | 数据库连接 | `/api/dbconnections` | 数据库连接管理 | | 邮件发送者 | `/api/emailsenders` | 邮件发送账号管理 | | AI 供应商 | `/api/aiproviders` | AI 大模型供应商配置(8 种预设) | | 飞书通知 | `/api/feishunotify` | 飞书 Webhook 通知配置 | | 飞书应用 | `/api/feishuappconfig` | 飞书应用配置管理 | | 钉钉通知 | `/api/dingtalknotify` | 钉钉 Webhook 通知配置 | | 企业微信通知 | `/api/wecomnotify` | 企业微信 Webhook 通知配置 | | 用户管理 | `/api/users` | 用户 CRUD、角色分配 | | 用户组 | `/api/usergroups` | 用户组管理 | | 审计日志 | `/api/auditlogs` | 操作日志查询 | | 执行日志 | `/api/executionlogs` | 任务执行日志查询、清理 | | 仪表盘 | `/api/dashboard` | 统计数据、运行状态 | --- ## 🖥 功能页面 | 页面 | 路径 | 说明 | |------|------|------| | 仪表盘 | `/` | 任务运行统计、系统概览 | | 任务管理 | `/tasks` | 任务列表,支持筛选和搜索 | | 创建 SQL 任务 | `/tasks/create-sql` | SQL 执行任务配置 | | 创建 ETL 任务 | `/tasks/create-etl` | ETL 数据同步任务配置 | | 创建通知任务 | `/tasks/create-notification` | SQL 监控通知任务配置 | | 创建 HTTP 任务 | `/tasks/create-http` | HTTP 接口调用任务配置 | | 创建飞书表格任务 | `/tasks/create-feishu-sheet` | 飞书表格同步任务配置 | | 任务详情 | `/tasks/:id` | 任务执行记录和日志 | | 工作流列表 | `/workflows` | 工作流管理 | | 工作流设计器 | `/workflows/create`、`/workflows/:id/designer` | 拖拽式 DAG 编排 | | 工作流实例 | `/workflows/:id/instances` | 执行实例历史与详情 | | 数据库连接 | `/connections` | 管理目标数据库连接信息 | | 邮件发送者 | `/email-senders` | 邮件发送账号管理 | | AI 供应商 | `/ai-providers` | AI 大模型供应商配置 | | 飞书通知 | `/feishu-notify` | 飞书 Webhook 配置 | | 飞书应用 | `/feishu-apps` | 飞书应用凭证管理 | | 钉钉通知 | `/dingtalk-notify` | 钉钉 Webhook 配置 | | 企业微信通知 | `/wecom-notify` | 企业微信 Webhook 配置 | | 用户管理 | `/users` | 用户账户和角色管理 | | 用户组 | `/user-groups` | 用户分组管理 | | 审计日志 | `/audit-logs` | 系统操作记录查询 | | 执行日志 | `/execution-logs` | 任务执行日志查询与清理 | --- ## ⚙️ 配置说明 后端配置位于 `backend/EasyJob.Api/appsettings.json`: | 配置项 | 说明 | |--------|------| | `DatabaseProvider` | 数据库类型(Sqlite / SqlServer / MySql / PostgreSql / Oracle) | | `ConnectionStrings` | 各数据库连接字符串 | | `Kestrel:Endpoints:Http:Url` | 监听地址(默认 `http://0.0.0.0:8080`,对开发/服务/Docker 均生效,优先级高于 launchSettings) | | `Jwt:Key` | Token 签名密钥(生产环境用环境变量 `Jwt__Key` 注入随机值) | | `Jwt:Issuer` | Token 颁发者 | | `Jwt:Audience` | Token 受众 | | `DistributedLock:Provider` | 分布式锁提供者:`Database`(默认)/ `Redis`(多副本推荐) | | `DistributedLock:Redis:ConnectionString` | Redis 连接串(Provider=Redis 时必填),支持单机/哨兵 | | `Quartz:EnableClustering` | 是否启用 Quartz 集群调度(默认 false),详见[分布式部署与高可用](#-分布式部署与高可用) | | `Quartz:ThreadPoolSize` | 线程池并发上限(代码默认 50,当前配置 30) | | `Quartz:ClusterCheckinIntervalSeconds` | 集群节点报到间隔(默认 10 秒) | | `Quartz:MisfireThresholdSeconds` | 触发错过补跑阈值(默认 60 秒) | | `Workflow:AllowedNuGetPackages` | C# 代码节点允许引用的 NuGet 包白名单(默认空 = 全部拒绝) | | `Feishu:AppId` | 飞书应用 AppId | | `Feishu:AppSecret` | 飞书应用 AppSecret | | `Notification:Email` | 邮件发件账号(SMTP) | | `Notification:Alert` | 系统告警接收邮箱/飞书 Webhook | | `Oracle:Compatibility` | Oracle 兼容级别(19 默认 / 21 / 23),详见 [docs/Oracle-支持说明.md](docs/Oracle-支持说明.md) | | `Oracle:MaxIdentifierLength` | 可选,连 12.1 及更老 Oracle 时设 30 | --- ## 📄 License [MIT](LICENSE)