From 8dac4cf2a1efca2c55aa879abc19b70b966fe66f Mon Sep 17 00:00:00 2001
From: hhp <272302281@qq.com>
Date: Fri, 10 Apr 2026 09:09:14 +0800
Subject: [PATCH 001/153] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20GitHub=20Act?=
=?UTF-8?q?ions=20-=20=E8=87=AA=E5=8A=A8=E6=9E=84=E5=BB=BA=E5=B9=B6?=
=?UTF-8?q?=E6=8E=A8=E9=80=81=20Docker=20=E9=95=9C=E5=83=8F=E5=88=B0=20Doc?=
=?UTF-8?q?ker=20Hub=EF=BC=88tag=E8=A7=A6=E5=8F=91=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/workflows/docker-build-push.yml | 51 +++++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 .github/workflows/docker-build-push.yml
diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml
new file mode 100644
index 0000000..540da69
--- /dev/null
+++ b/.github/workflows/docker-build-push.yml
@@ -0,0 +1,51 @@
+name: Build and Push Docker Image
+
+on:
+ push:
+ tags:
+ - 'v*' # 推送任何 v 开头的 tag 时触发,如 v1.0, v1.1
+ workflow_dispatch: # 支持手动触发
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Extract version from tag
+ id: version
+ run: |
+ VERSION=${GITHUB_REF_NAME}
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "Building version: $VERSION"
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: ./Dockerfile
+ push: true
+ tags: |
+ pythonhuang/juggle-net8:${{ steps.version.outputs.version }}
+ pythonhuang/juggle-net8:latest
+ # 多平台支持(可选,移除 linux/arm64 可加快构建速度)
+ platforms: linux/amd64
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ build-args: |
+ BUILD_DATE=${{ github.event.repository.updated_at }}
+ GIT_COMMIT=${{ github.sha }}
+
+ - name: Image digest
+ run: echo "Image pushed with digest ${{ steps.docker_build.outputs.digest }}"
From 9fe4a410ef57320a5e9c6ab9a13f3b9e47403576 Mon Sep 17 00:00:00 2001
From: hhp <272302281@qq.com>
Date: Fri, 10 Apr 2026 09:34:19 +0800
Subject: [PATCH 002/153] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20README=20?=
=?UTF-8?q?=E4=B8=BA=E4=B8=AD=E8=8B=B1=E6=96=87=E5=8F=8C=E8=AF=AD=E7=89=88?=
=?UTF-8?q?=E6=9C=AC=EF=BC=8C=E9=BB=98=E8=AE=A4=E4=B8=AD=E6=96=87=E5=B1=95?=
=?UTF-8?q?=E7=A4=BA=EF=BC=8C=E6=94=AF=E6=8C=81=E9=94=9A=E7=82=B9=E5=88=87?=
=?UTF-8?q?=E6=8D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 336 ++++++++++++++++++++++++++++++------------------------
1 file changed, 186 insertions(+), 150 deletions(-)
diff --git a/README.md b/README.md
index cf22af6..c8fb403 100644
--- a/README.md
+++ b/README.md
@@ -1,215 +1,251 @@
-# Juggle 接口编排平台 (.NET 8 + Vue3)
+
-> 图形化微服务接口编排低代码工具 —— 像积木一样灵活,像魔法一样强大
+# 🎪 Juggle 接口编排平台
----
-
-## 项目简介
+
+
+
+
+
+
-**Juggle** 是一个可视化的接口编排平台,通过拖拽式流程设计器将多个已有的 HTTP API 接口编排为一个复合接口,直接供前端或业务系统调用,极大提升开发效率,无需重复编码。
+**中文** | [English](#english)
-本项目为 Java/Spring Boot 原版的 **.NET 8 + SQLite + Vue3 重构版**,实现了完整的 DDD 四层架构。
+
---
-## 核心功能
-
-| 功能模块 | 说明 |
-|---------|------|
-| 🎨 可视化流程设计器 | 拖拽节点、连线建立数据流、自动布局(拓扑排序)、MiniMap 缩略图 |
-| 🔧 多种节点类型 | START / END / METHOD / CONDITION / ASSIGN / CODE / MYSQL / MERGE / **SUB_FLOW** |
-| ▶️ 流程调试 | 在设计界面直接调试,节点高亮显示(成功绿/失败红)、查看输出结果和节点日志 |
-| 📦 套件 & 接口管理 | 组织管理基础 HTTP API,支持导入/导出 JSON 分享移植 |
-| 🗄️ 多数据库支持 | SQLite / MySQL / PostgreSQL / SQL Server |
-| 📊 执行日志 | 流程执行主日志 + 节点明细日志(含变量快照时间轴) |
-| 🔑 Token 管理 | 开放接口访问令牌管理 |
-| 📐 全局静态变量 | 跨流程共享变量,支持读写与重置默认值 |
-| 📤 导入/导出 | 流程定义和套件支持 JSON 导出/导入,方便分享移植 |
-| 🔗 子流程编排 | SUB_FLOW 节点可调用已发布的其他流程,支持入参/出参变量映射 |
-| 🔐 JWT 认证 | 控制台登录认证 |
+## 📖 简介
----
+Juggle 中文有"积木、魔法"之意,寓意像积木一样灵活,像魔法一样强大,满足灵活多变的业务需求,助力业务快速落地!
+
+Juggle 是一个**图形化微服务编排工具**,通过简单的流程编排,快速完成接口开发,大大提高开发效率。
-## 技术栈
+### ✨ 核心能力
-| 层次 | 技术 |
+| 场景 | 说明 |
|------|------|
-| 后端框架 | ASP.NET Core 8 Web API |
-| 架构模式 | DDD 四层架构(Api / Application / Domain / Infrastructure) |
-| 数据库 | SQLite(EF Core 8) |
-| 多数据库 | MySqlConnector / Npgsql / Microsoft.Data.SqlClient |
-| 认证 | JWT Bearer Token |
-| 文档 | Swagger / OpenAPI |
-| 前端框架 | Vue 3 (Composition API + TypeScript) |
-| UI 组件库 | Element Plus |
-| 流程画布 | @vue-flow/core |
-| 状态管理 | Pinia |
-| 路由 | Vue Router 4 |
-| 构建工具 | Vite 8 |
+| 🧩 微服务编排 | 根据已有基础接口快速编排开发新接口 |
+| 🔗 系统集成 | 快速打通第三方系统平台,消除系统壁垒 |
+| 📦 BFF 层 | 面向前端提供聚合/适配层(Backend for Frontend)|
+| 🎨 定制开发 | 私有化标准功能定制,避免污染标准代码 |
---
-## 目录结构
+## 🚀 快速开始
+### Docker 一键启动
+
+```bash
+docker run -d \
+ --name juggle \
+ -p 9127:9127 \
+ -v juggle_data:/data \
+ pythonhuang/juggle-net8:v1.0
```
-JuggleNet6/
-├── Juggle.Api/ # 入口层:14个 Controller + Program.cs
-│ ├── Controllers/
-│ │ ├── Api/ # 业务 Controller(流程、套件、系统等)
-│ │ └── Open/ # 开放接口(外部触发流程)
-│ ├── wwwroot/ # 前端构建产物(生产模式集成)
-│ ├── juggle.db # SQLite 数据库文件
-│ └── Program.cs
-│
-├── Juggle.Application/ # 应用层:Service + DTO
-│ ├── Services/
-│ │ ├── Flow/ # 流程执行服务
-│ │ └── Impl/ # DataSourceService、JwtService
-│ └── Models/
-│ ├── Request/ # 请求 DTO
-│ └── Response/ # ApiResult 统一响应
-│
-├── Juggle.Domain/ # 领域层:实体 + 流程引擎
-│ ├── Entities/ # 15个领域实体
-│ └── Engine/ # FlowEngine + 9个节点执行器
-│
-├── Juggle.Infrastructure/ # 基础设施层
-│ ├── Persistence/ # JuggleDbContext(EF Core)
-│ └── Common/ # JsonHelper、Md5Helper
-│
-├── JuggleNet6.Frontend/ # Vue3 前端
-│ └── src/views/ # 功能页面(flow/suite/system/object)
-│
-├── JuggleNet6.Backend/ # 原单体后端(保留参考,已废弃)
-├── Architecture.md # 系统架构文档
-├── DesignDoc.md # 系统详细设计文档
-└── Juggle.sln # Visual Studio 解决方案
+
+或使用 docker-compose:
+
+```bash
+docker-compose up -d
```
+- 访问地址:http://localhost:9127
+- 默认账号:`juggle` / `juggle`
+
---
-## 快速开始
+## 🛠️ 技术栈
-### 环境要求
+| 层级 | 技术 |
+|------|------|
+| 后端 | ASP.NET Core 8 / EF Core 8 / SQLite |
+| 前端 | Vue3 / Vite / Element Plus / Pinia |
+| 容器 | Docker (multi-stage build) |
+| 认证 | JWT + RBAC 角色权限 |
-- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
-- [Node.js 18+](https://nodejs.org/)
+---
-### 启动后端
+## 📦 功能特性(30+ 项)
+
+### 核心流程
+- ✅ 可视化流程设计器(节点画布)
+- ✅ **13 种节点**:START / END / METHOD / CONDITION / MERGE / ASSIGN / CODE / DB / SUB_FLOW / LOOP / DELAY / PARALLEL / NOTIFY
+- ✅ 节点超时 & 重试策略
+- ✅ 流程版本管理 & 版本对比
+- ✅ 流程克隆 / 导入 / 导出(含 Word 文档)
+- ✅ 流程分组管理
+
+### 触发方式
+- ✅ 同步触发 `GET/POST /open/flow/trigger/{key}`
+- ✅ 异步触发 + 结果查询
+- ✅ Webhook 触发(含签名验证)
+- ✅ 定时任务调度
+
+### 套件 & 接口
+- ✅ 套件 / 接口 / 对象 / 参数管理
+- ✅ 接口 Mock 功能
+
+### 监控 & 测试
+- ✅ 监控仪表盘
+- ✅ 执行日志(含节点级日志)
+- ✅ 流程测试用例(断言 + 批量执行)
+- ✅ Monaco Editor 代码编辑(JS / SQL 高亮 + 自动补全)
+
+### 系统管理
+- ✅ 用户管理 / 角色管理 / 菜单权限(RBAC)
+- ✅ 多租户数据隔离(JWT Claims 驱动)
+- ✅ 审计日志 / Token 权限管理
+- ✅ 系统配置中心 / 全局异常告警
+
+### 数据库支持
+- **系统数据库**:SQLite / MySQL / PostgreSQL / SQLServer
+- **业务数据源**:SQLite / MySQL / PostgreSQL / SQLServer / Oracle / 达梦
-```powershell
-cd JuggleNet6\Juggle.Api
-dotnet run
-```
+---
-服务启动后访问:
-- 应用首页:http://localhost:9127/
-- Swagger 文档:http://localhost:9127/swagger
+## 📚 文档
-### 启动前端(开发模式)
+- [原项目文档](https://juggle.plus/docs/guide/introduce/introduce.html)(Java 版本参考)
+- [Release Notes](./RELEASE.md)
-```powershell
-cd JuggleNet6\JuggleNet6.Frontend
-npm install
-npm run dev
-```
+---
-前端开发服务:http://localhost:5173/
+## 🙏 致谢
-### 构建前端并集成到后端(生产模式)
+本项目基于 [Juggle](https://github.com/somta/Juggle) 原始 Java 版本的设计思路与架构进行重构实现。
-```powershell
-cd JuggleNet6\JuggleNet6.Frontend
-npm run build
-Copy-Item dist\* ..\Juggle.Api\wwwroot\ -Recurse -Force
-```
+感谢 [@somta](https://github.com/somta) 及原项目团队!
---
-## 默认账号
+
-| 用户名 | 密码 |
-|--------|------|
-| juggle | juggle |
+
---
-## 流程节点类型
+
-| 节点 | 说明 |
-|------|------|
-| START | 开始节点,流程入口 |
-| END | 结束节点,流程出口 |
-| METHOD | 方法节点,调用外部 HTTP API |
-| CONDITION | 条件节点,多分支判断 |
-| ASSIGN | 赋值节点,变量赋值/类型转换,支持读写全局静态变量 |
-| CODE | 代码节点,执行 JavaScript 脚本(`$var`/`$static` 对象) |
-| MYSQL | 数据库节点,执行 SQL 查询(支持 `${varName}` 模板替换) |
-| MERGE | 聚合节点,多分支汇聚 |
-| **SUB_FLOW** | **子流程节点,调用已发布的其他流程,支持入参/出参变量映射** |
+# 🎪 Juggle - API Orchestration Platform
+
+
+
+
+
+
+
+
+[中文](#-简介) | **English**
+
+
---
-## API 接口规范
+## 📖 Introduction
-| 前缀 | 用途 | 认证方式 |
-|------|------|---------|
-| `/api/` | 控制台管理接口 | JWT Token |
-| `/open/` | 开放接口(外部触发流程) | API Token |
+Juggle means "building blocks" and "magic" in Chinese, symbolizing flexibility like building blocks and power like magic — meeting flexible business needs and helping you deliver quickly!
-统一响应格式:
-```json
-{
- "code": 200,
- "message": "success",
- "data": { ... }
-}
-```
+Juggle is a **graphical microservice orchestration tool** that enables rapid API development through simple visual workflow design.
+
+### ✨ Core Capabilities
+
+| Scenario | Description |
+|----------|-------------|
+| 🧩 Microservice Orchestration | Quickly build new APIs by orchestrating existing base APIs |
+| 🔗 System Integration | Rapidly integrate with third-party platforms, breaking down system barriers |
+| 📦 BFF Layer | Provide aggregation/adaptation layer for frontend (Backend for Frontend) |
+| 🎨 Custom Development | Privatized standard function customization without polluting core code |
---
-## 开放接口访问方式
+## 🚀 Quick Start
-```http
-# 带版本号触发(指定某一版本)
-POST /open/flow/trigger/v1/sync_abcdefghij
-X-Access-Token: your-api-token
+### Docker One-Click Start
-# 不带版本号触发(自动取最新已发布版本)
-POST /open/flow/trigger/sync_abcdefghij
-X-Access-Token: your-api-token
+```bash
+docker run -d \
+ --name juggle \
+ -p 9127:9127 \
+ -v juggle_data:/data \
+ pythonhuang/juggle-net8:v1.0
```
+Or use docker-compose:
+
+```bash
+docker-compose up -d
+```
+
+- Access: http://localhost:9127
+- Default credentials: `juggle` / `juggle`
+
---
-## 导入/导出功能
+## 🛠️ Tech Stack
-流程定义和套件均支持 JSON 格式的导入/导出,方便在不同环境间分享和移植:
+| Layer | Technology |
+|-------|------------|
+| Backend | ASP.NET Core 8 / EF Core 8 / SQLite |
+| Frontend | Vue3 / Vite / Element Plus / Pinia |
+| Container | Docker (multi-stage build) |
+| Auth | JWT + RBAC Role-Based Access Control |
-| 操作 | 接口 |
-|------|------|
-| 导出流程定义 | `GET /api/flow/definition/export/{id}` |
-| 导入流程定义 | `POST /api/flow/definition/import` |
-| 导出套件(含接口+参数) | `GET /api/suite/export/{id}` |
-| 导入套件 | `POST /api/suite/import` |
+---
-> 导入时自动生成新的唯一标识(flowKey / suiteCode),不会覆盖已有数据。
+## 📦 Features (30+)
+
+### Core Workflow
+- ✅ Visual workflow designer (node canvas)
+- ✅ **13 Node Types**: START / END / METHOD / CONDITION / MERGE / ASSIGN / CODE / DB / SUB_FLOW / LOOP / DELAY / PARALLEL / NOTIFY
+- ✅ Node timeout & retry policies
+- ✅ Workflow version management & comparison
+- ✅ Clone / Import / Export (with Word docs)
+- ✅ Workflow grouping
+
+### Trigger Methods
+- ✅ Synchronous trigger `GET/POST /open/flow/trigger/{key}`
+- ✅ Asynchronous trigger + result query
+- ✅ Webhook trigger (with signature verification)
+- ✅ Scheduled task scheduling
+
+### Suite & API Management
+- ✅ Suite / API / Object / Parameter management
+- ✅ API Mock functionality
+
+### Monitoring & Testing
+- ✅ Monitoring dashboard
+- ✅ Execution logs (with node-level logs)
+- ✅ Workflow test cases (assertions + batch execution)
+- ✅ Monaco Editor code editing (JS / SQL highlighting + autocomplete)
+
+### System Management
+- ✅ User management / Role management / Menu permissions (RBAC)
+- ✅ Multi-tenant data isolation (JWT Claims driven)
+- ✅ Audit logs / Token permission management
+- ✅ System config center / Global exception alerting
+
+### Database Support
+- **System Database**: SQLite / MySQL / PostgreSQL / SQLServer
+- **Business Data Sources**: SQLite / MySQL / PostgreSQL / SQLServer / Oracle / Dameng
---
-## 相关文档
+## 📚 Documentation
-- [架构文档](./Architecture.md) - 系统总体架构设计
-- [设计文档](./DesignDoc.md) - 系统详细设计说明
-- [后端 README](./Juggle.Api/README.md) - 后端项目说明
-- [前端 README](./JuggleNet6.Frontend/README.md) - 前端项目说明
+- [Original Project Docs](https://juggle.plus/docs/guide/introduce/introduce.html) (Java version reference)
+- [Release Notes](./RELEASE.md)
---
-## 致谢
+## 🙏 Acknowledgments
-本项目基于 [Juggle](https://github.com/somta/Juggle) 原始 Java 版本的设计思路与架构,使用 .NET 8 + Vue3 + SQLite 进行重构实现。
+This project is a .NET 8 reimplementation based on the design and architecture of the original [Juggle](https://github.com/somta/Juggle) Java version.
+
+Thanks to [@somta](https://github.com/somta) and the original team!
+
+---
-感谢 [@somta](https://github.com/somta) 及原项目团队提供的思路与支持!
+## 📄 License
-> 原项目:https://github.com/somta/Juggle
+[MIT License](./LICENSE)
From 5f70f0a0d49ad0e3e3ac2f474cc647b49daa61e6 Mon Sep 17 00:00:00 2001
From: hhp <272302281@qq.com>
Date: Tue, 28 Apr 2026 16:34:44 +0800
Subject: [PATCH 003/153] =?UTF-8?q?docs:=20=E7=94=9F=E6=88=90=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E5=BA=93=E8=A1=A8=E7=BB=93=E6=9E=84=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增表列表总览文档 (docs/表结构/表列表.md)
- 新增25张表的详细结构文档 (docs/表结构/表/*.md)
- 包含表字段说明、索引、设计理念和示例数据
- 按功能模块分类:基础模块、流程编排、套件接口、安全集成、触发调度
---
.../\350\241\250/t_api.md" | 64 +++++++
.../\350\241\250/t_audit_log.md" | 65 +++++++
.../\350\241\250/t_base.md" | 59 +++++++
.../\350\241\250/t_data_source.md" | 63 +++++++
.../\350\241\250/t_flow_definition.md" | 62 +++++++
.../\350\241\250/t_flow_info.md" | 54 ++++++
.../\350\241\250/t_flow_log.md" | 71 ++++++++
.../\350\241\250/t_flow_node_log.md" | 85 +++++++++
.../\350\241\250/t_flow_test_case.md" | 72 ++++++++
.../\350\241\250/t_flow_version.md" | 56 ++++++
.../\350\241\250/t_login_log.md" | 59 +++++++
.../\350\241\250/t_object.md" | 68 ++++++++
.../\350\241\250/t_parameter.md" | 89 ++++++++++
.../\350\241\250/t_role.md" | 51 ++++++
.../\350\241\250/t_role_menu.md" | 58 +++++++
.../\350\241\250/t_schedule_task.md" | 81 +++++++++
.../\350\241\250/t_static_variable.md" | 61 +++++++
.../\350\241\250/t_suite.md" | 62 +++++++
.../\350\241\250/t_system_config.md" | 71 ++++++++
.../\350\241\250/t_tenant.md" | 57 ++++++
.../\350\241\250/t_token.md" | 53 ++++++
.../\350\241\250/t_token_permission.md" | 63 +++++++
.../\350\241\250/t_user.md" | 52 ++++++
.../\350\241\250/t_variable_info.md" | 64 +++++++
.../\350\241\250/t_webhook.md" | 82 +++++++++
.../\350\241\250\345\210\227\350\241\250.md" | 162 ++++++++++++++++++
26 files changed, 1784 insertions(+)
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_api.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_audit_log.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_base.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_data_source.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_definition.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_info.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_log.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_node_log.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_test_case.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_version.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_login_log.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_object.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_parameter.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role_menu.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_schedule_task.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_static_variable.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_suite.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_system_config.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_tenant.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token_permission.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_user.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_variable_info.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_webhook.md"
create mode 100644 "docs/\350\241\250\347\273\223\346\236\204/\350\241\250\345\210\227\350\241\250.md"
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_api.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_api.md"
new file mode 100644
index 0000000..2ac551a
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_api.md"
@@ -0,0 +1,64 @@
+# t_api - API 接口表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| suite_code | TEXT | | 所属套件编码 |
+| method_code | TEXT | | 接口方法编码 |
+| method_name | TEXT | | 接口方法名称 |
+| method_desc | TEXT | | 接口方法描述 |
+| url | TEXT | | 请求地址 |
+| request_type | TEXT | | 请求类型:GET / POST / PUT / DELETE |
+| content_type | TEXT | | 内容类型:JSON / FORM |
+| mock_json | TEXT | | Mock 数据 JSON |
+| method_type | TEXT | DEFAULT 'HTTP' | 接口类型:HTTP / WEBSERVICE |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| suite_code | 关联所属套件 |
+| method_code | 接口唯一编码 |
+| url | 完整请求地址或相对路径 |
+| request_type | HTTP方法类型 |
+| mock_json | 启用Mock时的返回数据 |
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_method_code | method_code | UNIQUE | 方法编码唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "suite_code": "user_suite",
+ "method_code": "getUserInfo",
+ "method_name": "获取用户信息",
+ "method_desc": "根据用户ID获取用户详细信息",
+ "url": "https://api.example.com/user/info",
+ "request_type": "GET",
+ "content_type": "JSON",
+ "method_type": "HTTP",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_audit_log.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_audit_log.md"
new file mode 100644
index 0000000..7c512e8
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_audit_log.md"
@@ -0,0 +1,65 @@
+# t_audit_log - 审计日志表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| module | TEXT | NOT NULL | 操作模块:tenant / role / user / flow 等 |
+| action_type | TEXT | NOT NULL | 操作类型:add / update / delete |
+| target_id | INTEGER | NOT NULL | 操作目标ID |
+| change_content | TEXT | DEFAULT '{}' | 变更内容摘要(JSON格式) |
+| operator_name | TEXT | | 操作人用户名 |
+| operator_id | INTEGER | | 操作人用户ID |
+| operator_tenant_id | INTEGER | | 操作人租户ID |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| module | 操作所属模块,便于分类查询和统计 |
+| action_type | 操作类型枚举 |
+| target_id | 被操作记录的主键ID |
+| change_content | JSON格式记录变更前后对比,如 `{"before":{"name":"A"},"after":{"name":"B"}}` |
+
+---
+
+## 操作类型枚举
+
+| action_type | 说明 |
+|-------------|------|
+| add | 新增 |
+| update | 修改 |
+| delete | 删除 |
+| enable | 启用 |
+| disable | 禁用 |
+| deploy | 部署 |
+| trigger | 触发 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "module": "user",
+ "action_type": "update",
+ "target_id": 5,
+ "change_content": "{\"before\":{\"role_id\":2},\"after\":{\"role_id\":1}}",
+ "operator_name": "admin",
+ "operator_id": 1,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_base.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_base.md"
new file mode 100644
index 0000000..c16dd84
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_base.md"
@@ -0,0 +1,59 @@
+# t_base - 基础实体基类
+
+> 所有业务实体表均包含以下公共字段
+
+---
+
+## 公共字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | INTEGER | 主键,自增 |
+| deleted | INTEGER | 软删除标记:0=正常, 1=已删除 |
+| tenant_id | INTEGER | 所属租户ID,null=全局数据(不隔离) |
+| created_at | TEXT | 创建时间(ISO 8601 格式) |
+| created_by | INTEGER | 创建人用户ID |
+| updated_at | TEXT | 更新时间(ISO 8601 格式) |
+| updated_by | INTEGER | 更新人用户ID |
+
+---
+
+## 说明
+
+`t_base` 不是独立的数据表,而是**所有业务表的公共基类字段**。
+
+### 设计理念
+
+1. **软删除**:使用 `deleted` 字段实现逻辑删除,数据不物理删除,方便数据恢复和审计
+2. **租户隔离**:`tenant_id` 字段支持多租户数据隔离,null 表示全局数据
+3. **审计字段**:`created_at/updated_at` 记录数据生命周期,`created_by/updated_by` 记录操作人
+
+### 继承关系
+
+```
+BaseEntity (抽象基类)
+ ├── UserEntity (用户)
+ ├── TenantEntity (租户)
+ ├── RoleEntity (角色)
+ ├── RoleMenuEntity (角色菜单)
+ ├── SuiteEntity (套件)
+ ├── ApiEntity (API接口)
+ ├── ObjectEntity (自定义对象)
+ ├── ParameterEntity (参数)
+ ├── FlowDefinitionEntity (流程定义)
+ ├── FlowInfoEntity (流程信息)
+ ├── FlowVersionEntity (流程版本)
+ ├── FlowLogEntity (流程日志)
+ ├── FlowNodeLogEntity (节点日志)
+ ├── FlowTestCaseEntity (测试用例)
+ ├── VariableInfoEntity (变量)
+ ├── StaticVariableEntity (静态变量)
+ ├── TokenEntity (Token)
+ ├── TokenPermissionEntity (Token权限)
+ ├── DataSourceEntity (数据源)
+ ├── SystemConfigEntity (系统配置)
+ ├── WebhookEntity (Webhook)
+ ├── ScheduleTaskEntity (定时任务)
+ ├── LoginLogEntity (登录日志)
+ └── AuditLogEntity (审计日志)
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_data_source.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_data_source.md"
new file mode 100644
index 0000000..11628a5
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_data_source.md"
@@ -0,0 +1,63 @@
+# t_data_source - 数据源配置表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| ds_name | TEXT | | 数据源名称 |
+| ds_type | TEXT | | 数据源类型:mysql / postgres / sqlserver / oracle 等 |
+| host | TEXT | | 主机地址 |
+| port | INTEGER | | 端口号 |
+| db_name | TEXT | | 数据库名称 |
+| username | TEXT | | 用户名 |
+| password | TEXT | | 密码(加密存储) |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| ds_name | 数据源显示名称 |
+| ds_type | 支持多种数据库类型 |
+| password | 敏感信息,应加密存储 |
+
+---
+
+## 支持的数据库类型
+
+| ds_type | 说明 |
+|---------|------|
+| mysql | MySQL 数据库 |
+| postgres | PostgreSQL 数据库 |
+| sqlserver | SQL Server 数据库 |
+| oracle | Oracle 数据库 |
+| dm | 达梦数据库 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "ds_name": "业务数据库",
+ "ds_type": "mysql",
+ "host": "192.168.1.100",
+ "port": 3306,
+ "db_name": "business_db",
+ "username": "juggle",
+ "password": "encrypted_password",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_definition.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_definition.md"
new file mode 100644
index 0000000..df8a9a1
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_definition.md"
@@ -0,0 +1,62 @@
+# t_flow_definition - 流程定义表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_key | TEXT | NOT NULL | 流程唯一标识Key |
+| flow_name | TEXT | | 流程名称 |
+| flow_desc | TEXT | | 流程描述 |
+| flow_content | TEXT | | 流程节点JSON配置 |
+| flow_type | TEXT | | 流程类型:sync(同步)/ async(异步) |
+| group_name | TEXT | | 分组名称(便于分类管理) |
+| status | INTEGER | DEFAULT 0 | 状态:0=草稿, 1=已部署 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| flow_key | 全局唯一,用于API调用,如 `/open/flow/trigger/demo_flow` |
+| flow_name | 流程显示名称 |
+| flow_content | JSON格式的节点配置,部署时会复制到 t_flow_version |
+| flow_type | sync=同步执行等待结果,async=异步执行立即返回实例ID |
+| status | 草稿状态可编辑,部署后变为只读 |
+| group_name | 支持流程分组,如"订单流程"、"用户流程" |
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_flow_key | flow_key | UNIQUE | 流程Key唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_key": "order_process",
+ "flow_name": "订单处理流程",
+ "flow_desc": "处理订单创建、支付、发货完整流程",
+ "flow_content": "[{\"key\":\"start_1\",\"elementType\":\"START\",...}]",
+ "flow_type": "sync",
+ "group_name": "订单流程",
+ "status": 0,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_info.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_info.md"
new file mode 100644
index 0000000..1b41dd4
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_info.md"
@@ -0,0 +1,54 @@
+# t_flow_info - 流程信息表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_key | TEXT | NOT NULL | 流程唯一标识Key(关联定义) |
+| flow_name | TEXT | | 流程名称 |
+| flow_desc | TEXT | | 流程描述 |
+| flow_type | TEXT | | 流程类型:sync / async |
+| status | INTEGER | DEFAULT 1 | 状态:1=启用, 0=禁用 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| flow_key | 与 t_flow_definition.flow_key 一一对应 |
+| status | 禁用后该流程无法被触发,但历史执行记录保留 |
+
+---
+
+## 设计说明
+
+- **部署流程**时,从 t_flow_definition 复制基本信息到 t_flow_info
+- t_flow_info 作为**运行时引用表**,记录已发布流程
+- 多个版本共存时,flow_key 相同但 t_flow_version 不同
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_key": "order_process",
+ "flow_name": "订单处理流程",
+ "flow_desc": "处理订单创建、支付、发货完整流程",
+ "flow_type": "sync",
+ "status": 1,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_log.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_log.md"
new file mode 100644
index 0000000..62c5630
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_log.md"
@@ -0,0 +1,71 @@
+# t_flow_log - 流程执行日志表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_key | TEXT | | 流程Key |
+| flow_name | TEXT | | 流程名称(冗余) |
+| version | TEXT | | 版本号 |
+| trigger_type | TEXT | | 触发方式:debug / open / schedule |
+| status | TEXT | | 执行状态:SUCCESS / FAILED |
+| start_time | TEXT | | 开始时间 |
+| end_time | TEXT | | 结束时间 |
+| cost_ms | INTEGER | | 耗时(毫秒) |
+| error_message | TEXT | | 错误信息 |
+| input_json | TEXT | | 输入参数 JSON |
+| output_json | TEXT | | 输出结果 JSON |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| trigger_type | 区分触发来源,便于统计和分析 |
+| status | 执行结果状态 |
+| cost_ms | 性能监控指标 |
+| input_json/output_json | 完整的输入输出数据,便于问题排查 |
+
+---
+
+## 触发类型枚举
+
+| trigger_type | 说明 |
+|--------------|------|
+| debug | 调试模式触发 |
+| open | 开放接口触发(/open/flow/trigger) |
+| schedule | 定时任务触发 |
+| webhook | Webhook触发 |
+| test_case | 测试用例执行 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_key": "order_process",
+ "flow_name": "订单处理流程",
+ "version": "v1",
+ "trigger_type": "open",
+ "status": "SUCCESS",
+ "start_time": "2026-03-24T10:00:00",
+ "end_time": "2026-03-24T10:00:05",
+ "cost_ms": 5000,
+ "input_json": "{\"orderId\": 12345}",
+ "output_json": "{\"code\": 200, \"message\": \"success\"}",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_node_log.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_node_log.md"
new file mode 100644
index 0000000..10a290c
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_node_log.md"
@@ -0,0 +1,85 @@
+# t_flow_node_log - 流程节点执行明细日志表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_log_id | INTEGER | NOT NULL | 关联流程日志ID |
+| node_key | TEXT | | 节点Key |
+| node_label | TEXT | | 节点标签/名称 |
+| node_type | TEXT | | 节点类型 |
+| seq_no | INTEGER | | 执行顺序(从1开始) |
+| status | TEXT | | 执行状态:SUCCESS / FAILED / SKIPPED |
+| start_time | TEXT | | 开始时间 |
+| end_time | TEXT | | 结束时间 |
+| cost_ms | INTEGER | | 耗时(毫秒) |
+| input_snapshot | TEXT | | 节点输入(变量快照 JSON) |
+| output_snapshot | TEXT | | 节点输出(变量快照 JSON) |
+| detail | TEXT | | 日志详情(SQL/HTTP请求等) |
+| error_message | TEXT | | 错误信息 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| flow_log_id | 关联 t_flow_log 表,标识属于哪次流程执行 |
+| node_type | 节点类型:START/END/METHOD/CONDITION/ASSIGN/CODE/MYSQL等 |
+| seq_no | 执行顺序,用于还原执行路径 |
+| input_snapshot | 进入节点时的变量状态快照 |
+| output_snapshot | 离开节点时的变量状态快照 |
+| detail | 详细信息,如HTTP请求体、SQL语句等 |
+
+---
+
+## 节点类型枚举
+
+| node_type | 说明 |
+|-----------|------|
+| START | 开始节点 |
+| END | 结束节点 |
+| METHOD | HTTP API调用节点 |
+| CONDITION | 条件分支节点 |
+| ASSIGN | 变量赋值节点 |
+| CODE | 代码执行节点 |
+| MYSQL | 数据库操作节点 |
+| MERGE | 聚合节点 |
+| DELAY | 延时节点 |
+| PARALLEL | 并行执行节点 |
+| LOOP | 循环节点 |
+| SUB_FLOW | 子流程调用节点 |
+| NOTIFY | 通知节点 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_log_id": 1,
+ "node_key": "method_order",
+ "node_label": "查询订单",
+ "node_type": "METHOD",
+ "seq_no": 2,
+ "status": "SUCCESS",
+ "start_time": "2026-03-24T10:00:01",
+ "end_time": "2026-03-24T10:00:03",
+ "cost_ms": 2000,
+ "input_snapshot": "{\"orderId\": 12345}",
+ "output_snapshot": "{\"orderId\": 12345, \"orderStatus\": \"paid\"}",
+ "detail": "HTTP POST https://api.example.com/order/get",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:01"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_test_case.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_test_case.md"
new file mode 100644
index 0000000..1e4c9a9
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_test_case.md"
@@ -0,0 +1,72 @@
+# t_flow_test_case - 流程测试用例表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_key | TEXT | NOT NULL | 关联流程Key |
+| case_name | TEXT | NOT NULL | 用例名称 |
+| input_json | TEXT | | 入参 JSON |
+| assert_json | TEXT | | 断言 JSON |
+| last_run_status | TEXT | | 最近执行状态 |
+| last_run_time | TEXT | | 最近执行时间 |
+| last_run_result | TEXT | | 最近执行结果摘要 |
+| remark | TEXT | | 备注说明 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| input_json | 测试入参,格式如 `{"userId": 123}` |
+| assert_json | 断言配置,格式如 `{"result.code": 200, "data.token": "notNull"}` |
+| last_run_status | SUCCESS / FAILED / PENDING |
+| last_run_result | 最近一次执行的实际输出摘要 |
+
+---
+
+## 断言配置示例
+
+```json
+{
+ "result.code": 200,
+ "data.success": true,
+ "data.token": "notNull",
+ "data.userId": 12345
+}
+```
+
+断言类型:
+- 精确匹配:`"field": "expectedValue"`
+- 非空验证:`"field": "notNull"`
+- 正则匹配:`"field": "/^\\d+$/"`
+- 包含关系:`"field": "contains:value"`
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_key": "user_login",
+ "case_name": "正常登录测试",
+ "input_json": "{\"username\": \"test\", \"password\": \"123456\"}",
+ "assert_json": "{\"code\": 200, \"data.token\": \"notNull\"}",
+ "last_run_status": "SUCCESS",
+ "last_run_time": "2026-03-24T10:00:00",
+ "last_run_result": "{\"code\": 200, \"data\": {\"token\": \"abc123\"}}",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_version.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_version.md"
new file mode 100644
index 0000000..ac8b5cc
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_flow_version.md"
@@ -0,0 +1,56 @@
+# t_flow_version - 流程版本表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_info_id | INTEGER | NOT NULL | 关联流程信息ID |
+| flow_key | TEXT | NOT NULL | 流程唯一标识Key |
+| version | TEXT | NOT NULL | 版本号,如 v1, v2 |
+| flow_content | TEXT | | 部署时的流程JSON快照 |
+| status | INTEGER | DEFAULT 1 | 状态:0=禁用, 1=启用 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| flow_info_id | 关联 t_flow_info 表,记录属于哪个流程 |
+| version | 版本号,每次部署自增,支持回滚 |
+| flow_content | 部署时的快照,修改流程定义不影响已部署版本 |
+| status | 同一 flow_key 下只能有一个版本为启用状态 |
+
+---
+
+## 设计说明
+
+- **版本管理**:每次部署生成新版本,支持版本回滚
+- **快照机制**:flow_content 存储部署时的完整配置,与定义解耦
+- **灰度发布**:可同时部署多个版本,通过切换状态实现灰度
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_info_id": 1,
+ "flow_key": "order_process",
+ "version": "v1",
+ "flow_content": "[{\"key\":\"start_1\",\"elementType\":\"START\",...}]",
+ "status": 1,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_login_log.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_login_log.md"
new file mode 100644
index 0000000..18e30b2
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_login_log.md"
@@ -0,0 +1,59 @@
+# t_login_log - 登录日志表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| user_id | INTEGER | | 登录用户ID |
+| user_name | TEXT | | 登录用户名 |
+| login_type | TEXT | DEFAULT 'login' | 登录类型:login / logout |
+| result | TEXT | DEFAULT 'success' | 登录结果:success / fail |
+| ip_address | TEXT | | 登录IP地址 |
+| user_agent | TEXT | | 浏览器User-Agent |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| login_type | 区分登录和登出行为 |
+| result | 记录登录成功/失败,便于安全审计 |
+| ip_address | 记录登录来源IP,支持异常登录告警 |
+| user_agent | 记录浏览器信息,支持设备识别 |
+
+---
+
+## 使用场景
+
+- 用户登录审计
+- 异常登录检测(如异地登录)
+- 用户活跃度统计
+- 安全合规要求
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "user_id": 1,
+ "user_name": "admin",
+ "login_type": "login",
+ "result": "success",
+ "ip_address": "192.168.1.100",
+ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_object.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_object.md"
new file mode 100644
index 0000000..de43b3a
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_object.md"
@@ -0,0 +1,68 @@
+# t_object - 自定义对象表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| object_code | TEXT | | 对象编码(唯一) |
+| object_name | TEXT | | 对象名称 |
+| object_desc | TEXT | | 对象描述 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| object_code | 对象唯一编码,用于参数引用 |
+| object_name | 中文名称 |
+| object_desc | 详细描述说明 |
+
+---
+
+## 设计说明
+
+- 自定义对象用于定义复杂的数据结构
+- 对象属性通过 t_parameter 表的 `param_type=3` 记录
+- 支持嵌套对象(param_type=object)
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_object_code | object_code | UNIQUE | 对象编码唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "object_code": "user_info",
+ "object_name": "用户信息",
+ "object_desc": "包含用户基本信息的数据结构",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
+
+关联的参数(t_parameter):
+```json
+[
+ {"param_code": "userId", "param_name": "用户ID", "data_type": "int"},
+ {"param_code": "userName", "param_name": "用户名", "data_type": "string"},
+ {"param_code": "email", "param_name": "邮箱", "data_type": "string"}
+]
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_parameter.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_parameter.md"
new file mode 100644
index 0000000..059c515
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_parameter.md"
@@ -0,0 +1,89 @@
+# t_parameter - 参数表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| owner_id | INTEGER | | 归属者ID(API ID / Object ID 等) |
+| owner_code | TEXT | | 归属者编码 |
+| param_type | INTEGER | NOT NULL | 参数类型 |
+| param_code | TEXT | | 参数编码 |
+| param_name | TEXT | | 参数名称 |
+| data_type | TEXT | | 数据类型 |
+| object_code | TEXT | | 引用对象编码(DataType=object时) |
+| required | INTEGER | DEFAULT 0 | 是否必填:0=否, 1=是 |
+| default_value | TEXT | | 默认值 |
+| description | TEXT | | 参数描述 |
+| sort_num | INTEGER | DEFAULT 0 | 排序号 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 参数类型枚举
+
+| param_type | 说明 | 归属者 |
+|------------|------|--------|
+| 1 | API入参 | t_api |
+| 2 | API出参 | t_api |
+| 3 | 对象属性 | t_object |
+| 4 | API Header | t_api |
+| 5 | 流程入参 | t_flow_definition |
+| 6 | 流程出参 | t_flow_definition |
+
+---
+
+## 数据类型枚举
+
+| data_type | 说明 |
+|-----------|------|
+| string | 字符串 |
+| int | 整数 |
+| double | 浮点数 |
+| boolean | 布尔值 |
+| date | 日期 |
+| datetime | 日期时间 |
+| array | 数组 |
+| object | 对象(引用 t_object) |
+| json | JSON字符串 |
+
+---
+
+## 示例数据
+
+API入参示例:
+```json
+{
+ "id": 1,
+ "owner_id": 1,
+ "owner_code": "user_suite.getUserInfo",
+ "param_type": 1,
+ "param_code": "userId",
+ "param_name": "用户ID",
+ "data_type": "int",
+ "required": 1,
+ "description": "用户唯一标识",
+ "sort_num": 1
+}
+```
+
+流程入参示例:
+```json
+{
+ "id": 10,
+ "owner_id": 1,
+ "owner_code": "order_process",
+ "param_type": 5,
+ "param_code": "orderId",
+ "param_name": "订单ID",
+ "data_type": "int",
+ "required": 1
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role.md"
new file mode 100644
index 0000000..c73d406
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role.md"
@@ -0,0 +1,51 @@
+# t_role - 角色表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| role_name | TEXT | NOT NULL | 角色名称 |
+| role_code | TEXT | | 角色编码(唯一) |
+| remark | TEXT | | 备注说明 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID(null=全局角色,跨租户共享) |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| role_name | 角色显示名称,如"管理员"、"普通用户" |
+| role_code | 角色唯一标识,如"admin"、"user" |
+| tenant_id | null = 全局角色(所有租户共享);有值 = 租户私有角色 |
+
+---
+
+## 设计说明
+
+- **全局角色**:tenant_id 为 null,如系统内置的"超级管理员"角色
+- **租户角色**:tenant_id 有值,仅该租户可见和使用
+- 角色通过 t_role_menu 表关联菜单权限
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "role_name": "超级管理员",
+ "role_code": "super_admin",
+ "tenant_id": null,
+ "created_at": "2026-03-24T10:00:00",
+ "deleted": 0
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role_menu.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role_menu.md"
new file mode 100644
index 0000000..7237ed5
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_role_menu.md"
@@ -0,0 +1,58 @@
+# t_role_menu - 角色菜单权限表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| role_id | INTEGER | NOT NULL | 关联角色ID |
+| menu_key | TEXT | NOT NULL | 菜单标识,如 "flow/define" |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| role_id | 关联 t_role 表的角色ID |
+| menu_key | 菜单唯一标识,支持通配符如 "suite/*" 表示所有套件菜单 |
+
+---
+
+## 设计说明
+
+- 角色与菜单权限的**多对多中间表**
+- 支持细粒度菜单级别权限控制
+- menu_key 使用通配符可批量授权,如:
+ - `flow/*` - 所有流程相关菜单
+ - `suite/*` - 所有套件相关菜单
+ - `system/*` - 所有系统管理菜单
+
+---
+
+## 示例数据
+
+```json
+[
+ {
+ "id": 1,
+ "role_id": 1,
+ "menu_key": "flow/define",
+ "tenant_id": 1
+ },
+ {
+ "id": 2,
+ "role_id": 1,
+ "menu_key": "suite/*",
+ "tenant_id": 1
+ }
+]
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_schedule_task.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_schedule_task.md"
new file mode 100644
index 0000000..3ca59e5
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_schedule_task.md"
@@ -0,0 +1,81 @@
+# t_schedule_task - 定时任务调度表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_key | TEXT | NOT NULL | 关联流程Key |
+| flow_name | TEXT | | 流程名称(冗余) |
+| cron_expression | TEXT | | Cron表达式 |
+| input_json | TEXT | | 触发时的固定入参 JSON |
+| status | INTEGER | DEFAULT 0 | 状态:0=暂停, 1=启用 |
+| last_run_time | TEXT | | 上次执行时间 |
+| last_run_status | TEXT | | 上次执行状态 |
+| next_run_time | TEXT | | 下次执行时间(预计算) |
+| run_count | INTEGER | DEFAULT 0 | 累计执行次数 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| flow_key | 关联 t_flow_definition |
+| cron_expression | 标准6位Cron表达式 |
+| input_json | 固定入参,触发时作为流程入参 |
+| next_run_time | 后台预计算,用于前端展示下次执行时间 |
+
+---
+
+## Cron 表达式说明
+
+| 位置 | 含义 | 取值范围 |
+|------|------|---------|
+| 第1位 | 秒 | 0-59 |
+| 第2位 | 分钟 | 0-59 |
+| 第3位 | 小时 | 0-23 |
+| 第4位 | 日期 | 1-31 |
+| 第5位 | 月份 | 1-12 |
+| 第6位 | 星期 | 0-6 (0=周日) |
+
+---
+
+## 常用 Cron 表达式示例
+
+| 表达式 | 含义 |
+|--------|------|
+| `0 */5 * * * *` | 每5分钟执行一次 |
+| `0 0 * * * *` | 每小时整点执行 |
+| `0 0 0 * * *` | 每天零点执行 |
+| `0 0 9 * * 1-5` | 工作日9点执行 |
+| `0 0 8 * * *` | 每天8点执行 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_key": "data_sync",
+ "flow_name": "数据同步流程",
+ "cron_expression": "0 */30 * * * *",
+ "input_json": "{\"syncType\": \"full\"}",
+ "status": 1,
+ "last_run_time": "2026-03-24T10:30:00",
+ "last_run_status": "SUCCESS",
+ "next_run_time": "2026-03-24T11:00:00",
+ "run_count": 128,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_static_variable.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_static_variable.md"
new file mode 100644
index 0000000..44324c2
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_static_variable.md"
@@ -0,0 +1,61 @@
+# t_static_variable - 静态(全局)变量表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| var_code | TEXT | | 变量编码(唯一键) |
+| var_name | TEXT | | 变量名称(中文描述) |
+| data_type | TEXT | | 数据类型:string / integer / double / boolean / date / json |
+| value | TEXT | | 当前值(字符串形式存储) |
+| default_value | TEXT | | 默认值 |
+| description | TEXT | | 描述说明 |
+| group_name | TEXT | | 分组名称 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| var_code | 变量唯一编码,流程中用 `$static.xxx` 引用 |
+| value | 当前值,存储为字符串,使用时转换类型 |
+| group_name | 可按业务分组,便于管理 |
+
+---
+
+## 使用方式
+
+在流程中通过表达式引用:
+```
+$static.redis_host // 获取静态变量值
+$static.max_retry_count
+```
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "var_code": "redis_host",
+ "var_name": "Redis服务器地址",
+ "data_type": "string",
+ "value": "192.168.1.100:6379",
+ "default_value": "localhost:6379",
+ "description": "缓存服务器地址",
+ "group_name": "缓存配置",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_suite.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_suite.md"
new file mode 100644
index 0000000..bc40e95
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_suite.md"
@@ -0,0 +1,62 @@
+# t_suite - 套件表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| suite_code | TEXT | | 套件编码(唯一) |
+| suite_name | TEXT | | 套件名称 |
+| suite_classify_id | INTEGER | | 套件分类ID |
+| suite_image | TEXT | | 套件图标/封面 |
+| suite_version | TEXT | | 套件版本号 |
+| suite_desc | TEXT | | 套件描述 |
+| suite_help_doc_json | TEXT | | 帮助文档 JSON |
+| suite_flag | INTEGER | DEFAULT 0 | 标识:0=自定义, 1=官方 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID(null=全局/官方) |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| suite_code | 套件唯一编码,用于API调用 |
+| suite_classify_id | 关联套件分类,便于分类管理 |
+| suite_image | 套件展示图标URL |
+| suite_version | 遵循语义化版本,如 1.0.0 |
+| suite_flag | 区分官方套件和用户自定义套件 |
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_suite_code | suite_code | UNIQUE | 套件编码唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "suite_code": "user_suite",
+ "suite_name": "用户管理套件",
+ "suite_classify_id": 1,
+ "suite_image": "https://example.com/user.png",
+ "suite_version": "1.0.0",
+ "suite_desc": "提供用户注册、登录、权限管理等功能",
+ "suite_flag": 1,
+ "tenant_id": null,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_system_config.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_system_config.md"
new file mode 100644
index 0000000..3306efe
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_system_config.md"
@@ -0,0 +1,71 @@
+# t_system_config - 系统配置表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| config_key | TEXT | NOT NULL | 配置键(唯一) |
+| config_value | TEXT | | 配置值 |
+| config_name | TEXT | | 配置名称 |
+| config_group | TEXT | | 配置分组 |
+| remark | TEXT | | 备注说明 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID(null=全局配置) |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| config_key | 全局唯一配置键 |
+| config_value | 配置值,字符串形式存储 |
+| config_group | 分组标识,便于分类管理 |
+
+---
+
+## 配置分组示例
+
+| config_group | 说明 |
+|--------------|------|
+| system | 系统配置 |
+| email | 邮件配置 |
+| alert | 告警配置 |
+| sms | 短信配置 |
+
+---
+
+## 示例数据
+
+```json
+[
+ {
+ "id": 1,
+ "config_key": "system.name",
+ "config_value": "Juggle 接口编排平台",
+ "config_name": "系统名称",
+ "config_group": "system"
+ },
+ {
+ "id": 2,
+ "config_key": "alert.email.enabled",
+ "config_value": "true",
+ "config_name": "邮件告警启用",
+ "config_group": "alert"
+ },
+ {
+ "id": 3,
+ "config_key": "alert.email.smtp_host",
+ "config_value": "smtp.example.com",
+ "config_name": "SMTP服务器",
+ "config_group": "alert"
+ }
+]
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_tenant.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_tenant.md"
new file mode 100644
index 0000000..55c63ea
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_tenant.md"
@@ -0,0 +1,57 @@
+# t_tenant - 租户表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| tenant_name | TEXT | NOT NULL | 租户名称 |
+| tenant_code | TEXT | | 租户编码(唯一) |
+| remark | TEXT | | 备注说明 |
+| status | INTEGER | DEFAULT 1 | 状态:1=启用, 0=禁用 |
+| expired_at | TEXT | | 过期时间,null=永不过期 |
+| menu_keys | TEXT | DEFAULT '[]' | 租户菜单权限(JSON数组) |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID(null表示全局) |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| tenant_code | 租户唯一编码,用于系统标识 |
+| status | 启用后可正常使用,禁用后该租户用户无法登录 |
+| expired_at | 到期后自动禁用,支持订阅制商业模式 |
+| menu_keys | JSON数组格式,如 `["flow/define","flow/log"]`,控制菜单可见性 |
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_tenant_code | tenant_code | UNIQUE | 租户编码唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "tenant_name": "演示租户",
+ "tenant_code": "demo",
+ "status": 1,
+ "expired_at": null,
+ "menu_keys": "[\"flow/define\",\"flow/list\",\"flow/version\",\"suite/*\",\"object/*\",\"system/*\"]",
+ "created_at": "2026-03-24T10:00:00",
+ "deleted": 0
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token.md"
new file mode 100644
index 0000000..56c4151
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token.md"
@@ -0,0 +1,53 @@
+# t_token - 访问令牌表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| token_value | TEXT | | Token值(唯一) |
+| token_name | TEXT | | Token名称 |
+| expired_at | TEXT | | 过期时间,null=永不过期 |
+| status | INTEGER | DEFAULT 1 | 状态:1=启用, 0=禁用 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| token_value | API访问凭证,用于 /open/* 接口认证 |
+| token_name | Token显示名称,便于识别用途 |
+| expired_at | 过期时间,到期自动失效 |
+
+---
+
+## 使用场景
+
+- 开放接口(/open/*)的身份认证
+- 第三方系统接入凭证
+- Webhook触发验证
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "token_value": "sk-a1b2c3d4e5f6...",
+ "token_name": "第三方系统接入Token",
+ "expired_at": "2027-12-31T23:59:59",
+ "status": 1,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token_permission.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token_permission.md"
new file mode 100644
index 0000000..de16d4c
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_token_permission.md"
@@ -0,0 +1,63 @@
+# t_token_permission - Token 权限配置表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| token_id | INTEGER | NOT NULL | 关联Token ID |
+| permission_type | TEXT | NOT NULL | 权限类型:FLOW / API |
+| resource_key | TEXT | NOT NULL | 资源Key(flowKey 或 methodCode) |
+| resource_name | TEXT | | 资源名称(冗余存储) |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| token_id | 关联 t_token 表 |
+| permission_type | FLOW=可调用流程,API=可调用套件接口 |
+| resource_key | 具体资源标识 |
+
+---
+
+## 权限类型说明
+
+| permission_type | 说明 | resource_key 示例 |
+|-----------------|------|-------------------|
+| FLOW | 流程调用权限 | `order_process` |
+| API | 套件接口权限 | `user_suite.getUserInfo` |
+
+---
+
+## 示例数据
+
+```json
+[
+ {
+ "id": 1,
+ "token_id": 1,
+ "permission_type": "FLOW",
+ "resource_key": "order_process",
+ "resource_name": "订单处理流程",
+ "tenant_id": 1
+ },
+ {
+ "id": 2,
+ "token_id": 1,
+ "permission_type": "API",
+ "resource_key": "user_suite.getUserInfo",
+ "resource_name": "获取用户信息",
+ "tenant_id": 1
+ }
+]
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_user.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_user.md"
new file mode 100644
index 0000000..06f98ed
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_user.md"
@@ -0,0 +1,52 @@
+# t_user - 用户表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| user_name | TEXT | NOT NULL | 用户名(登录账号) |
+| password | TEXT | | 密码(加密存储) |
+| role_id | INTEGER | | 关联角色ID |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| user_name | 用户登录账号,通常为唯一标识 |
+| password | 密码经过加密存储(如 MD5/SHA256) |
+| role_id | 关联 t_role 表,支持 RBAC 权限控制 |
+
+---
+
+## 索引
+
+| 索引名 | 字段 | 类型 | 说明 |
+|--------|------|------|------|
+| idx_user_name | user_name | UNIQUE | 用户名唯一索引 |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "user_name": "admin",
+ "password": "e10adc3949ba59abbe56e057f20f883e",
+ "role_id": 1,
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00",
+ "deleted": 0
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_variable_info.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_variable_info.md"
new file mode 100644
index 0000000..67add46
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_variable_info.md"
@@ -0,0 +1,64 @@
+# t_variable_info - 变量信息表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| flow_definition_id | INTEGER | | 关联流程定义ID |
+| flow_key | TEXT | | 流程Key |
+| variable_code | TEXT | | 变量编码 |
+| variable_name | TEXT | | 变量名称 |
+| data_type | TEXT | | 数据类型:string / int / boolean / object / array |
+| variable_type | TEXT | | 变量类型:input / output / env |
+| default_value | TEXT | | 默认值 |
+| description | TEXT | | 变量描述 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| variable_code | 变量唯一标识,流程中通过 `input_xxx` / `output_xxx` / `env_xxx` 引用 |
+| variable_name | 中文名称,用于展示 |
+| variable_type | input=入参, output=出参, env=内部环境变量 |
+| data_type | 决定数据类型和校验规则 |
+
+---
+
+## 变量类型说明
+
+| variable_type | 说明 | 引用方式 |
+|--------------|------|---------|
+| input | 流程输入参数 | input_xxx |
+| output | 流程输出结果 | output_xxx |
+| env | 内部环境变量 | env_xxx |
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "flow_definition_id": 1,
+ "flow_key": "user_login",
+ "variable_code": "input_userName",
+ "variable_name": "用户名",
+ "data_type": "string",
+ "variable_type": "input",
+ "default_value": "",
+ "description": "登录用户名",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_webhook.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_webhook.md"
new file mode 100644
index 0000000..bb1b946
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250/t_webhook.md"
@@ -0,0 +1,82 @@
+# t_webhook - Webhook 触发配置表
+
+---
+
+## 表结构
+
+| 字段名 | 类型 | 约束 | 说明 |
+|--------|------|------|------|
+| id | INTEGER | PK, AUTOINCREMENT | 主键 |
+| webhook_key | TEXT | NOT NULL | Webhook唯一标识Key |
+| webhook_name | TEXT | NOT NULL | Webhook名称 |
+| flow_key | TEXT | NOT NULL | 关联流程Key |
+| flow_name | TEXT | | 流程名称(冗余) |
+| secret | TEXT | | 签名密钥(HMAC-SHA256) |
+| allowed_method | TEXT | DEFAULT 'POST' | 允许的HTTP方法 |
+| async_mode | INTEGER | DEFAULT 0 | 异步模式:0=同步, 1=异步 |
+| status | INTEGER | DEFAULT 1 | 状态:1=启用, 0=禁用 |
+| trigger_count | INTEGER | DEFAULT 0 | 触发次数统计 |
+| last_trigger_time | TEXT | | 最后触发时间 |
+| remark | TEXT | | 备注说明 |
+| deleted | INTEGER | DEFAULT 0 | 软删除标记 |
+| tenant_id | INTEGER | | 所属租户ID |
+| created_at | TEXT | | 创建时间 |
+| created_by | INTEGER | | 创建人ID |
+| updated_at | TEXT | | 更新时间 |
+| updated_by | INTEGER | | 更新人ID |
+
+---
+
+## 字段说明
+
+| 字段 | 说明 |
+|------|------|
+| webhook_key | 访问URL:`/open/webhook/{webhook_key}` |
+| secret | 设置后请求需携带签名头 X-Webhook-Signature |
+| async_mode | 同步模式等待执行完成,异步模式立即返回logId |
+
+---
+
+## 触发方式
+
+**同步触发**:`POST /open/webhook/{webhookKey}`
+```json
+{
+ "data": { ... },
+ "timestamp": 1711251200000,
+ "signature": "sha256=xxx"
+}
+```
+
+**异步触发**:同上,设置 `async_mode=1`
+
+---
+
+## 签名验证
+
+如果配置了 secret,请求头需包含签名:
+```
+X-Webhook-Signature: sha256={HMAC-SHA256(requestBody, secret)}
+```
+
+---
+
+## 示例数据
+
+```json
+{
+ "id": 1,
+ "webhook_key": "order_notify",
+ "webhook_name": "订单通知Webhook",
+ "flow_key": "order_process",
+ "flow_name": "订单处理流程",
+ "secret": "my-secret-key",
+ "allowed_method": "POST",
+ "async_mode": 0,
+ "status": 1,
+ "trigger_count": 156,
+ "last_trigger_time": "2026-03-24T10:00:00",
+ "tenant_id": 1,
+ "created_at": "2026-03-24T10:00:00"
+}
+```
diff --git "a/docs/\350\241\250\347\273\223\346\236\204/\350\241\250\345\210\227\350\241\250.md" "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250\345\210\227\350\241\250.md"
new file mode 100644
index 0000000..1f5a0aa
--- /dev/null
+++ "b/docs/\350\241\250\347\273\223\346\236\204/\350\241\250\345\210\227\350\241\250.md"
@@ -0,0 +1,162 @@
+# Juggle 接口编排平台 - 数据库表结构
+
+> 版本:v1.0
+> 日期:2026-03-24
+> 数据库:SQLite
+
+---
+
+## 一、表列表总览
+
+本系统共 **23** 张数据表,按功能模块分类如下:
+
+### 1. 基础模块
+
+| 序号 | 表名 | 说明 |
+|:---:|------|------|
+| 1 | t_base | 基础实体基类(所有表包含) |
+| 2 | t_user | 用户表 |
+| 3 | t_tenant | 租户表 |
+| 4 | t_role | 角色表 |
+| 5 | t_role_menu | 角色菜单权限表 |
+| 6 | t_login_log | 登录日志表 |
+| 7 | t_audit_log | 审计日志表 |
+
+### 2. 流程编排模块
+
+| 序号 | 表名 | 说明 |
+|:---:|------|------|
+| 8 | t_flow_definition | 流程定义表(草稿状态) |
+| 9 | t_flow_info | 流程信息表(已部署) |
+| 10 | t_flow_version | 流程版本表 |
+| 11 | t_flow_log | 流程执行日志表 |
+| 12 | t_flow_node_log | 流程节点执行明细日志表 |
+| 13 | t_flow_test_case | 流程测试用例表 |
+| 14 | t_variable_info | 变量信息表 |
+| 15 | t_static_variable | 静态(全局)变量表 |
+
+### 3. 套件与接口模块
+
+| 序号 | 表名 | 说明 |
+|:---:|------|------|
+| 16 | t_suite | 套件表 |
+| 17 | t_api | API 接口表 |
+| 18 | t_object | 自定义对象表 |
+| 19 | t_parameter | 参数表(入参/出参/Header/属性) |
+
+### 4. 安全与集成模块
+
+| 序号 | 表名 | 说明 |
+|:---:|------|------|
+| 20 | t_token | 访问令牌表 |
+| 21 | t_token_permission | Token 权限配置表 |
+| 22 | t_data_source | 数据源配置表 |
+| 23 | t_system_config | 系统配置表 |
+
+### 5. 触发与调度模块
+
+| 序号 | 表名 | 说明 |
+|:---:|------|------|
+| 24 | t_webhook | Webhook 触发配置表 |
+| 25 | t_schedule_task | 定时任务调度表 |
+
+---
+
+## 二、ER 关系图
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ t_tenant │
+│ (租户 - 多租户隔离) │
+└─────────────────────────────────────────────────────────────────────────┘
+ │ │ │
+ │ 1:N │ 1:N │ 1:N
+ ▼ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ t_user │ │ t_role │ │ t_flow_* │
+│ (用户) │ │ (角色) │ │ (流程相关) │
+└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
+ │ │
+ │ N:M │ N:M
+ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ t_role_menu │ │ t_suite │
+│ (角色菜单权限) │ │ (套件) │
+└─────────────────┘ └────────┬────────┘
+ │
+ │ 1:N
+ ▼
+ ┌─────────────────┐
+ │ t_api │
+ │ (API接口) │
+ └────────┬────────┘
+ │ 1:N
+ ▼
+ ┌─────────────────┐
+ │ t_parameter │
+ │ (参数配置) │
+ └─────────────────┘
+```
+
+---
+
+## 三、各表详情
+
+| 文件 | 说明 |
+|------|------|
+| [t_base.md](./表/t_base.md) | 基础实体基类(公共字段说明) |
+| [t_user.md](./表/t_user.md) | 用户表 |
+| [t_tenant.md](./表/t_tenant.md) | 租户表 |
+| [t_role.md](./表/t_role.md) | 角色表 |
+| [t_role_menu.md](./表/t_role_menu.md) | 角色菜单权限表 |
+| [t_login_log.md](./表/t_login_log.md) | 登录日志表 |
+| [t_audit_log.md](./表/t_audit_log.md) | 审计日志表 |
+| [t_flow_definition.md](./表/t_flow_definition.md) | 流程定义表 |
+| [t_flow_info.md](./表/t_flow_info.md) | 流程信息表 |
+| [t_flow_version.md](./表/t_flow_version.md) | 流程版本表 |
+| [t_flow_log.md](./表/t_flow_log.md) | 流程执行日志表 |
+| [t_flow_node_log.md](./表/t_flow_node_log.md) | 流程节点执行明细日志表 |
+| [t_flow_test_case.md](./表/t_flow_test_case.md) | 流程测试用例表 |
+| [t_variable_info.md](./表/t_variable_info.md) | 变量信息表 |
+| [t_static_variable.md](./表/t_static_variable.md) | 静态变量表 |
+| [t_suite.md](./表/t_suite.md) | 套件表 |
+| [t_api.md](./表/t_api.md) | API 接口表 |
+| [t_object.md](./表/t_object.md) | 自定义对象表 |
+| [t_parameter.md](./表/t_parameter.md) | 参数表 |
+| [t_token.md](./表/t_token.md) | 访问令牌表 |
+| [t_token_permission.md](./表/t_token_permission.md) | Token 权限配置表 |
+| [t_data_source.md](./表/t_data_source.md) | 数据源配置表 |
+| [t_system_config.md](./表/t_system_config.md) | 系统配置表 |
+| [t_webhook.md](./表/t_webhook.md) | Webhook 触发配置表 |
+| [t_schedule_task.md](./表/t_schedule_task.md) | 定时任务调度表 |
+
+---
+
+## 四、字段命名规范
+
+| 字段类型 | 前缀/后缀 | 示例 |
+|---------|----------|------|
+| 主键 | Id | UserId, FlowId |
+| 外键 | xxxId | TenantId, FlowInfoId |
+| 名称 | Name | FlowName, SuiteName |
+| 编码 | Code | FlowKey, SuiteCode |
+| 描述 | Desc | FlowDesc, ObjectDesc |
+| 状态 | Status | Status, LoginStatus |
+| 时间 | At/Time | CreatedAt, UpdatedAt, LastRunTime |
+| 数量 | Count | TriggerCount, RunCount |
+| JSON数据 | Json | FlowContent, InputJson |
+| 布尔值 | Is/Flag | IsDeleted (用 Deleted 代替) |
+
+---
+
+## 五、软删除与租户隔离
+
+### 5.1 软删除
+- 所有业务表均包含 `deleted` 字段
+- `deleted = 0` 表示正常数据
+- `deleted = 1` 表示已删除(逻辑删除)
+
+### 5.2 租户隔离
+- 所有业务表均包含 `tenant_id` 字段(继承自 BaseEntity)
+- `tenant_id = null` 表示全局数据(不隔离)
+- `tenant_id` 有值时,查询自动按租户过滤
From daf62e1f5a291f04c90cc74f8a153f8787e5d2b3 Mon Sep 17 00:00:00 2001
From: hhp <272302281@qq.com>
Date: Tue, 28 Apr 2026 16:35:36 +0800
Subject: [PATCH 004/153] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Windows?=
=?UTF-8?q?=E4=B8=80=E9=94=AE=E5=90=AF=E5=8A=A8=E8=84=9A=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增启动Juggle.bat脚本
- 支持Docker启动、直接运行.NET、构建镜像、清理资源、查看日志
- 包含版本检查和错误提示
- 提供交互式菜单界面
---
"\345\220\257\345\212\250Juggle.bat" | 188 +++++++++++++++++++++++++++
1 file changed, 188 insertions(+)
create mode 100644 "\345\220\257\345\212\250Juggle.bat"
diff --git "a/\345\220\257\345\212\250Juggle.bat" "b/\345\220\257\345\212\250Juggle.bat"
new file mode 100644
index 0000000..9153b79
--- /dev/null
+++ "b/\345\220\257\345\212\250Juggle.bat"
@@ -0,0 +1,188 @@
+@echo off
+chcp 65001 >nul
+:: ============================================================
+:: Juggle 接口编排平台 - Windows 一键启动脚本
+:: Version: 1.0
+:: ============================================================
+
+echo.
+echo ╔═══════════════════════════════════════════════════════╗
+echo ║ Juggle 接口编排平台 - 一键启动 (Windows) ║
+echo ╚═══════════════════════════════════════════════════════╝
+echo.
+
+:: 获取脚本所在目录
+set SCRIPT_DIR=%~dp0
+cd /d "%SCRIPT_DIR%"
+
+:menu
+echo 请选择启动方式:
+echo.
+echo [1] Docker 启动 (推荐,需要 Docker Desktop)
+echo [2] 直接运行 .NET 项目 (需要 .NET 8 SDK)
+echo [3] 仅构建 Docker 镜像
+echo [4] 清理 Docker 容器和镜像
+echo [5] 查看日志 (Docker模式)
+echo [0] 退出
+echo.
+set /p choice=请输入选项 [1-5, 0退出]:
+
+if "%choice%"=="1" goto docker_start
+if "%choice%"=="2" goto dotnet_run
+if "%choice%"=="3" goto docker_build
+if "%choice%"=="4" goto docker_clean
+if "%choice%"=="5" goto docker_logs
+if "%choice%"=="0" goto end
+
+echo.
+echo [错误] 无效选项,请重新选择!
+echo.
+goto menu
+
+:docker_start
+echo.
+echo [1/3] 检查 Docker 状态...
+docker info >nul 2>&1
+if errorlevel 1 (
+ echo [错误] Docker 未运行,请先启动 Docker Desktop
+ echo.
+ pause
+ goto menu
+)
+echo [OK] Docker 运行正常
+
+echo.
+echo [2/3] 检查 Docker 镜像是否存在...
+docker images pythonhuang/juggle-net8:v1.0 -q >nul 2>&1
+if errorlevel 1 (
+ echo [提示] 本地未找到镜像,正在尝试拉取...
+ docker pull pythonhuang/juggle-net8:v1.0
+ if errorlevel 1 (
+ echo [提示] 拉取失败,尝试本地构建...
+ goto docker_build
+ )
+)
+
+echo.
+echo [3/3] 启动 Juggle 容器...
+docker run -d ^
+ --name juggle ^
+ --restart unless-stopped ^
+ -p 9127:9127 ^
+ -v juggle_data:/data ^
+ -e ASPNETCORE_ENVIRONMENT=Production ^
+ -e ASPNETCORE_URLS=http://+:9127 ^
+ -e DB_PATH=/data/juggle.db ^
+ pythonhuang/juggle-net8:v1.0
+
+if errorlevel 1 (
+ echo.
+ echo [错误] 容器启动失败,可能端口已被占用
+ echo.
+ goto menu
+)
+
+echo.
+echo ╔══════════════════════════════════════════════════════════╗
+echo ║ Juggle 启动成功! ║
+echo ╠══════════════════════════════════════════════════════════╣
+echo ║ 访问地址: http://localhost:9127 ║
+echo ║ 默认账号: juggle / juggle ║
+echo ╚══════════════════════════════════════════════════════════╝
+echo.
+echo 提示: 使用选项 [5] 可查看运行日志
+echo.
+pause
+goto end
+
+:docker_build
+echo.
+echo [1/2] 停止并移除旧容器 (如果存在)...
+docker stop juggle >nul 2>&1
+docker rm juggle >nul 2>&1
+
+echo.
+echo [2/2] 构建 Docker 镜像 (这可能需要几分钟)...
+docker build -t pythonhuang/juggle-net8:v1.0 .
+if errorlevel 1 (
+ echo.
+ echo [错误] Docker 构建失败
+ echo.
+ pause
+ goto menu
+)
+
+echo.
+echo [OK] 镜像构建完成!
+echo.
+set /p start_now=是否立即启动? [Y/N]:
+if /i "%start_now%"=="Y" goto docker_start
+goto menu
+
+:docker_clean
+echo.
+echo [警告] 即将清理 Docker 资源...
+echo.
+docker stop juggle >nul 2>&1
+docker rm juggle >nul 2>&1
+docker volume rm juggle_data >nul 2>&1
+echo [OK] 清理完成
+echo.
+pause
+goto menu
+
+:docker_logs
+docker logs -f juggle
+goto end
+
+:dotnet_run
+echo.
+echo [1/4] 检查 .NET SDK 版本...
+dotnet --version >nul 2>&1
+if errorlevel 1 (
+ echo [错误] 未安装 .NET 8 SDK,请先安装:
+ echo https://dotnet.microsoft.com/download/dotnet/8.0
+ echo.
+ pause
+ goto menu
+)
+for /f "delims=" %%v in ('dotnet --version') do set DOTNET_VER=%%v
+echo [OK] .NET 版本: %DOTNET_VER%
+
+echo.
+echo [2/4] 还原 NuGet 包...
+dotnet restore Juggle.sln
+if errorlevel 1 (
+ echo [错误] 包还原失败
+ echo.
+ pause
+ goto menu
+)
+
+echo.
+echo [3/4] 构建项目...
+dotnet build Juggle.sln -c Release
+if errorlevel 1 (
+ echo [错误] 项目构建失败
+ echo.
+ pause
+ goto menu
+)
+
+echo.
+echo [4/4] 启动服务...
+echo.
+echo ╔══════════════════════════════════════════════════════════╗
+echo ║ Juggle 启动成功! ║
+echo ╠══════════════════════════════════════════════════════════╣
+echo ║ 访问地址: http://localhost:9127 ║
+echo ║ 默认账号: juggle / juggle ║
+echo ║ 按 Ctrl+C 停止服务 ║
+echo ╚══════════════════════════════════════════════════════════╝
+echo.
+dotnet run --project Juggle.Api/Juggle.Api.csproj --no-build
+goto end
+
+:end
+echo.
+echo再见!
From 9f64ee422b6e6b06a79d4f235fe1e9a3c1b4e52f Mon Sep 17 00:00:00 2001
From: hhp <272302281@qq.com>
Date: Tue, 28 Apr 2026 16:36:41 +0800
Subject: [PATCH 005/153] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E7=95=8C?=
=?UTF-8?q?=E9=9D=A2=E6=88=AA=E5=9B=BE=E5=88=B0README.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增images文件夹存放截图
- 复制flowdesign流程设计器截图
- 在README功能特性后添加界面预览板块
- 展示流程设计器和流程设计示例
---
README.md | 12 ++++++++++++
images/flowdesign.png | Bin 0 -> 207671 bytes
images/flowdesign01.png | Bin 0 -> 269309 bytes
3 files changed, 12 insertions(+)
create mode 100644 images/flowdesign.png
create mode 100644 images/flowdesign01.png
diff --git a/README.md b/README.md
index c8fb403..4ad6faf 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,18 @@ docker-compose up -d
---
+## 📸 界面预览
+
+### 流程设计器
+
+
+
+### 流程设计示例
+
+
+
+---
+
## 📚 文档
- [原项目文档](https://juggle.plus/docs/guide/introduce/introduce.html)(Java 版本参考)
diff --git a/images/flowdesign.png b/images/flowdesign.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8c0f94d57dfbc8c9050225bdb1c3a7a667bc04f
GIT binary patch
literal 207671
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%0AQ(!Ca|@xXH!u76Yo!4lyo|mm<WEmqwr9)T?
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