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 接口编排平台 ---- - -## 项目简介 +

+ Release + License + .NET 8 + Vue 3 +

-**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 + +

+ Release + License + .NET 8 + Vue 3 +

+ +[中文](#-简介) | **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 --- +## 📸 界面预览 + +### 流程设计器 + +![流程设计器](./images/flowdesign.png) + +### 流程设计示例 + +![流程设计示例](./images/flowdesign01.png) + +--- + ## 📚 文档 - [原项目文档](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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�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 literal 0 HcmV?d00001 diff --git a/images/flowdesign01.png b/images/flowdesign01.png new file mode 100644 index 0000000000000000000000000000000000000000..ffacdb00447d96bfd624ed115e7c8c34a60232cf GIT binary patch literal 269309 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 literal 0 HcmV?d00001 From 1775c5066f13740f0d9ea35d1c4d6e5587180e08 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 29 Apr 2026 17:12:25 +0800 Subject: [PATCH 006/153] =?UTF-8?q?feat:=20=E4=B8=BAJuggle.Application?= =?UTF-8?q?=E5=B1=82=E6=B7=BB=E5=8A=A0=E5=AE=8C=E6=95=B4=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为所有Request模型添加XML文档注释 - 为所有Response模型添加XML文档注释 - 为Service服务类添加XML文档注释 - 包含类说明、属性说明、参数和返回值注释 --- ...0\344\272\244\350\247\204\345\210\231.mdc" | 11 +++ .../Controllers/Api/DataSourceController.cs | 15 ++++ .../Api/FlowDefinitionController.cs | 15 ++++ .../Controllers/Api/FlowLogController.cs | 16 +++- .../Controllers/Api/FlowTestCaseController.cs | 14 +++ .../Controllers/Api/FlowVersionController.cs | 14 +++ Juggle.Api/Controllers/Api/RoleController.cs | 16 +++- .../Controllers/Api/ScheduleTaskController.cs | 15 ++++ .../Api/StaticVariableController.cs | 17 +++- .../Controllers/Api/SystemConfigController.cs | 14 +++ .../Controllers/Api/TenantController.cs | 17 +++- Juggle.Api/Controllers/Api/TokenController.cs | 15 ++++ Juggle.Api/Controllers/Api/UserController.cs | 16 ++++ .../Controllers/Api/WebhookController.cs | 17 +++- .../Controllers/Open/FlowOpenController.cs | 16 +++- Juggle.Application/Class1.cs | 4 + .../Models/Request/ApiDebugRequest.cs | 17 ++++ .../Models/Request/ApiUpdateRequest.cs | 7 ++ .../Models/Request/AuditLogPageRequest.cs | 13 +++ .../Models/Request/DataSourceAddRequest.cs | 30 +++++++ .../Models/Request/DataSourceUpdateRequest.cs | 6 ++ .../Models/Request/FlowDebugRequest.cs | 7 ++ .../Request/FlowDefinitionAddRequest.cs | 18 ++++ .../Request/FlowDefinitionPageRequest.cs | 11 +++ .../Request/FlowDefinitionSaveRequest.cs | 12 +++ .../Request/FlowDefinitionUpdateRequest.cs | 24 +++++ .../Models/Request/FlowDeployRequest.cs | 7 ++ .../Models/Request/FlowLogPageRequest.cs | 27 +++++- .../Models/Request/FlowTestCasePageRequest.cs | 7 ++ .../Models/Request/FlowTestCaseSaveRequest.cs | 26 ++++++ .../Models/Request/FlowVersionPageRequest.cs | 7 ++ .../Request/FlowVersionStatusRequest.cs | 11 +++ .../Models/Request/LoginRequest.cs | 10 +++ .../Models/Request/ObjectAddRequest.cs | 11 +++ .../Models/Request/ObjectUpdateRequest.cs | 7 ++ .../Models/Request/PageRequest.cs | 16 ++++ .../Models/Request/ParameterItem.cs | 42 +++++++++ .../Models/Request/ParameterSaveRequest.cs | 22 +++++ .../Models/Request/RoleAddRequest.cs | 22 +++++ .../Models/Request/RoleUpdateRequest.cs | 26 ++++++ .../Models/Request/ScheduleTaskAddRequest.cs | 20 +++++ .../Models/Request/ScheduleTaskPageRequest.cs | 11 +++ .../Request/ScheduleTaskUpdateRequest.cs | 11 +++ .../Models/Request/SetValueRequest.cs | 7 ++ .../Models/Request/StaticVarSaveRequest.cs | 34 ++++++++ .../Models/Request/SuiteAddRequest.cs | 20 +++++ .../Models/Request/SuitePageRequest.cs | 6 ++ .../Models/Request/SuiteUpdateRequest.cs | 6 ++ .../Models/Request/SystemConfigSaveRequest.cs | 10 +++ .../Models/Request/TokenAddRequest.cs | 10 +++ .../Request/TokenPermissionSaveRequest.cs | 21 ++++- .../Models/Request/UserAddRequest.cs | 18 ++++ .../Models/Request/UserChangePwdRequest.cs | 11 +++ .../Models/Request/UserResetPwdRequest.cs | 11 +++ .../Models/Request/UserUpdateRequest.cs | 18 ++++ .../Models/Response/ApiResult.cs | 55 ++++++++++++ .../Models/Response/PageResult.cs | 20 +++++ .../Services/ITenantAccessor.cs | 36 ++++++-- Juggle.Domain/Entities/ApiEntity.cs | 43 ++++++++- Juggle.Domain/Entities/AuditLogEntity.cs | 44 ++++++++-- Juggle.Domain/Entities/BaseEntity.cs | 41 ++++++++- Juggle.Domain/Entities/DataSourceEntity.cs | 41 ++++++++- .../Entities/FlowDefinitionEntity.cs | 42 +++++++-- Juggle.Domain/Entities/FlowInfoEntity.cs | 27 +++++- Juggle.Domain/Entities/FlowLogEntity.cs | 66 +++++++++++--- Juggle.Domain/Entities/FlowNodeLogEntity.cs | 87 ++++++++++++++++--- Juggle.Domain/Entities/FlowVersionEntity.cs | 31 ++++++- Juggle.Domain/Entities/LoginLogEntity.cs | 40 +++++++-- Juggle.Domain/Entities/ObjectEntity.cs | 15 ++++ Juggle.Domain/Entities/ParameterEntity.cs | 63 +++++++++++++- Juggle.Domain/Entities/RoleEntity.cs | 24 ++++- Juggle.Domain/Entities/RoleMenuEntity.cs | 15 +++- Juggle.Domain/Entities/ScheduleTaskEntity.cs | 62 ++++++++++--- .../Entities/StaticVariableEntity.cs | 49 +++++++++-- Juggle.Domain/Entities/SuiteEntity.cs | 39 ++++++++- Juggle.Domain/Entities/SystemConfigEntity.cs | 37 ++++++-- Juggle.Domain/Entities/TenantEntity.cs | 37 +++++++- Juggle.Domain/Entities/TokenEntity.cs | 21 +++++ .../Entities/TokenPermissionEntity.cs | 26 ++++-- Juggle.Domain/Entities/UserEntity.cs | 18 +++- Juggle.Domain/Entities/VariableInfoEntity.cs | 47 +++++++++- Juggle.Domain/Entities/WebhookEntity.cs | 53 ++++++++--- Juggle.Infrastructure/Common/JsonHelper.cs | 21 +++++ Juggle.Infrastructure/Common/Md5Helper.cs | 13 +++ 84 files changed, 1813 insertions(+), 132 deletions(-) create mode 100644 ".codebuddy/rules/git\346\217\220\344\272\244\350\247\204\345\210\231.mdc" diff --git "a/.codebuddy/rules/git\346\217\220\344\272\244\350\247\204\345\210\231.mdc" "b/.codebuddy/rules/git\346\217\220\344\272\244\350\247\204\345\210\231.mdc" new file mode 100644 index 0000000..a741bf4 --- /dev/null +++ "b/.codebuddy/rules/git\346\217\220\344\272\244\350\247\204\345\210\231.mdc" @@ -0,0 +1,11 @@ +--- +description: +alwaysApply: true +enabled: true +updatedAt: 2026-04-29T09:05:02.109Z +provider: +--- + +git提交规则: +1.每完成一个子任务,修复一个bug都要提交一次git +2.注释使用中文 \ No newline at end of file diff --git a/Juggle.Api/Controllers/Api/DataSourceController.cs b/Juggle.Api/Controllers/Api/DataSourceController.cs index 1b8016b..f8eddc5 100644 --- a/Juggle.Api/Controllers/Api/DataSourceController.cs +++ b/Juggle.Api/Controllers/Api/DataSourceController.cs @@ -10,6 +10,10 @@ namespace Juggle.Api.Controllers.Api; +///

+/// 数据源管理控制器 +/// 提供数据源的增删改查、连接测试等功能 +/// [ApiController] [Route("api/system/datasource")] [Authorize] @@ -19,6 +23,12 @@ public class DataSourceController : ControllerBase private readonly DataSourceService _dsService; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 数据源服务 + /// 多租户访问器 public DataSourceController(JuggleDbContext db, DataSourceService dsService, ITenantAccessor tenant) { _db = db; @@ -26,6 +36,11 @@ public DataSourceController(JuggleDbContext db, DataSourceService dsService, ITe _tenant = tenant; } + /// + /// 新增数据源 + /// + /// 数据源信息 + /// 新增的数据源 ID [HttpPost("add")] public async Task Add([FromBody] DataSourceAddRequest req) { diff --git a/Juggle.Api/Controllers/Api/FlowDefinitionController.cs b/Juggle.Api/Controllers/Api/FlowDefinitionController.cs index 7e5da54..36321b4 100644 --- a/Juggle.Api/Controllers/Api/FlowDefinitionController.cs +++ b/Juggle.Api/Controllers/Api/FlowDefinitionController.cs @@ -11,6 +11,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 流程定义控制器 +/// 提供流程的增删改查、调试、部署、导入导出等功能 +/// [ApiController] [Route("api/flow/definition")] [Authorize] @@ -20,6 +24,12 @@ public class FlowDefinitionController : ControllerBase private readonly FlowExecutionService _flowExec; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 流程执行服务 + /// 多租户访问器 public FlowDefinitionController(JuggleDbContext db, FlowExecutionService flowExec, ITenantAccessor tenant) { _db = db; @@ -27,6 +37,11 @@ public FlowDefinitionController(JuggleDbContext db, FlowExecutionService flowExe _tenant = tenant; } + /// + /// 新增流程定义 + /// + /// 流程基本信息(名称、描述、类型、分组) + /// 新创建的流程 ID 和 FlowKey [HttpPost("add")] public async Task Add([FromBody] FlowDefinitionAddRequest req) { diff --git a/Juggle.Api/Controllers/Api/FlowLogController.cs b/Juggle.Api/Controllers/Api/FlowLogController.cs index fe6f009..4fc0fa6 100644 --- a/Juggle.Api/Controllers/Api/FlowLogController.cs +++ b/Juggle.Api/Controllers/Api/FlowLogController.cs @@ -7,15 +7,29 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 流程日志控制器 +/// 提供流程执行日志的查询、详情查看、删除、统计等功能 +/// [ApiController] [Route("api/flow/log")] [Authorize] public class FlowLogController : ControllerBase { private readonly JuggleDbContext _db; + + /// + /// 构造函数 + /// + /// 数据库上下文 public FlowLogController(JuggleDbContext db) => _db = db; - /// 分页查询流程执行日志 + /// + /// 分页查询流程执行日志 + /// 支持按流程 Key、状态、时间范围筛选 + /// + /// 日志分页请求参数 + /// 流程执行日志列表 [HttpPost("page")] public async Task Page([FromBody] FlowLogPageRequest req) { diff --git a/Juggle.Api/Controllers/Api/FlowTestCaseController.cs b/Juggle.Api/Controllers/Api/FlowTestCaseController.cs index f2d250c..c3e3cee 100644 --- a/Juggle.Api/Controllers/Api/FlowTestCaseController.cs +++ b/Juggle.Api/Controllers/Api/FlowTestCaseController.cs @@ -10,6 +10,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 流程测试用例控制器 +/// 提供测试用例的增删改查、批量执行、断言校验等功能 +/// [ApiController] [Route("api/flow/testcase")] [Authorize] @@ -18,12 +22,22 @@ public class FlowTestCaseController : ControllerBase private readonly JuggleDbContext _db; private readonly FlowExecutionService _flowExec; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 流程执行服务 public FlowTestCaseController(JuggleDbContext db, FlowExecutionService flowExec) { _db = db; _flowExec = flowExec; } + /// + /// 分页查询测试用例列表 + /// + /// 测试用例分页请求参数 + /// 测试用例列表 [HttpPost("page")] public async Task Page([FromBody] FlowTestCasePageRequest req) { diff --git a/Juggle.Api/Controllers/Api/FlowVersionController.cs b/Juggle.Api/Controllers/Api/FlowVersionController.cs index 84692e8..c3b9ec2 100644 --- a/Juggle.Api/Controllers/Api/FlowVersionController.cs +++ b/Juggle.Api/Controllers/Api/FlowVersionController.cs @@ -8,6 +8,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 流程版本管理控制器 +/// 提供流程版本的查询、状态管理、手动触发执行等功能 +/// [ApiController] [Route("api/flow/version")] [Authorize] @@ -16,12 +20,22 @@ public class FlowVersionController : ControllerBase private readonly JuggleDbContext _db; private readonly FlowExecutionService _flowExec; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 流程执行服务 public FlowVersionController(JuggleDbContext db, FlowExecutionService flowExec) { _db = db; _flowExec = flowExec; } + /// + /// 分页查询流程版本列表 + /// + /// 版本分页请求参数 + /// 流程版本列表 [HttpPost("page")] public async Task Page([FromBody] FlowVersionPageRequest req) { diff --git a/Juggle.Api/Controllers/Api/RoleController.cs b/Juggle.Api/Controllers/Api/RoleController.cs index ed63f39..ed6fd14 100644 --- a/Juggle.Api/Controllers/Api/RoleController.cs +++ b/Juggle.Api/Controllers/Api/RoleController.cs @@ -10,6 +10,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 角色管理控制器 +/// 提供角色的增删改查、权限分配等功能 +/// [ApiController] [Route("api/role")] public class RoleController : ControllerBase @@ -17,13 +21,23 @@ public class RoleController : ControllerBase private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public RoleController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } - /// 角色分页列表 + /// + /// 角色分页列表 + /// 普通用户只能看到本租户角色和全局角色 + /// + /// 分页请求参数 + /// 角色列表(包含租户名称和菜单权限数量) [HttpPost("page"), Authorize] public async Task Page([FromBody] PageRequest req) { diff --git a/Juggle.Api/Controllers/Api/ScheduleTaskController.cs b/Juggle.Api/Controllers/Api/ScheduleTaskController.cs index f00685c..cec61a5 100644 --- a/Juggle.Api/Controllers/Api/ScheduleTaskController.cs +++ b/Juggle.Api/Controllers/Api/ScheduleTaskController.cs @@ -10,6 +10,11 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 定时任务控制器 +/// 提供定时任务的增删改查、启停控制、手动触发等功能 +/// 定时任务基于 Cron 表达式实现定时触发已部署的流程 +/// [ApiController] [Route("api/schedule")] [Authorize] @@ -18,12 +23,22 @@ public class ScheduleTaskController : ControllerBase private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public ScheduleTaskController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } + /// + /// 新增定时任务 + /// + /// 定时任务信息 + /// 新增的任务 ID [HttpPost("add")] public async Task Add([FromBody] ScheduleTaskAddRequest req) { diff --git a/Juggle.Api/Controllers/Api/StaticVariableController.cs b/Juggle.Api/Controllers/Api/StaticVariableController.cs index ddf0f8b..b519f27 100644 --- a/Juggle.Api/Controllers/Api/StaticVariableController.cs +++ b/Juggle.Api/Controllers/Api/StaticVariableController.cs @@ -9,7 +9,11 @@ namespace Juggle.Api.Controllers.Api; -/// 静态(全局)变量管理接口 +/// +/// 静态(全局)变量管理控制器 +/// 提供静态变量的增删改查、值修改、重置等功能 +/// 静态变量可在流程中被引用和修改 +/// [ApiController] [Route("api/system/static-var")] [Authorize] @@ -17,12 +21,23 @@ public class StaticVariableController : ControllerBase { private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public StaticVariableController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } + /// + /// 获取静态变量列表 + /// + /// 可选,按分组名称筛选 + /// 静态变量列表 [HttpGet("list")] public async Task List([FromQuery] string? groupName) { diff --git a/Juggle.Api/Controllers/Api/SystemConfigController.cs b/Juggle.Api/Controllers/Api/SystemConfigController.cs index cc8a0de..702fe37 100644 --- a/Juggle.Api/Controllers/Api/SystemConfigController.cs +++ b/Juggle.Api/Controllers/Api/SystemConfigController.cs @@ -8,6 +8,11 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 系统配置控制器 +/// 提供系统配置项的查询、保存等功能 +/// 支持邮件配置、告警配置、系统参数等 +/// [ApiController] [Route("api/system/config")] [Authorize] @@ -38,11 +43,20 @@ public class SystemConfigController : ControllerBase ("system.log.keep.days","日志保留天数", "system", "30"), }; + /// + /// 构造函数 + /// + /// 数据库上下文 public SystemConfigController(JuggleDbContext db) { _db = db; } + /// + /// 获取所有配置项 + /// 首次访问时自动初始化默认配置 + /// + /// 按分组组织的配置项列表 [HttpGet("all")] public async Task GetAll() { diff --git a/Juggle.Api/Controllers/Api/TenantController.cs b/Juggle.Api/Controllers/Api/TenantController.cs index 4d6e308..7d48eb7 100644 --- a/Juggle.Api/Controllers/Api/TenantController.cs +++ b/Juggle.Api/Controllers/Api/TenantController.cs @@ -11,6 +11,11 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 租户管理控制器 +/// 提供租户的增删改查、审计日志查看等功能 +/// 仅超级管理员可访问 +/// [ApiController] [Route("api/tenant")] public class TenantController : ControllerBase @@ -18,13 +23,23 @@ public class TenantController : ControllerBase private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public TenantController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } - /// 租户分页列表(仅超级管理员可访问) + /// + /// 租户分页列表 + /// 仅超级管理员可访问 + /// + /// 分页请求参数 + /// 租户列表(包含菜单权限和用户数量) [HttpPost("page"), Authorize] public async Task Page([FromBody] PageRequest req) { diff --git a/Juggle.Api/Controllers/Api/TokenController.cs b/Juggle.Api/Controllers/Api/TokenController.cs index d3dd531..769abb5 100644 --- a/Juggle.Api/Controllers/Api/TokenController.cs +++ b/Juggle.Api/Controllers/Api/TokenController.cs @@ -9,6 +9,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// API Token 管理控制器 +/// 提供开放接口认证 Token 的增删改查、权限配置等功能 +/// [ApiController] [Route("api/system/token")] [Authorize] @@ -17,12 +21,23 @@ public class TokenController : ControllerBase private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public TokenController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } + /// + /// 新增 Token + /// 自动生成唯一的 Token 值 + /// + /// Token 信息 + /// 新增的 Token 信息 [HttpPost("add")] public async Task Add([FromBody] TokenAddRequest req) { diff --git a/Juggle.Api/Controllers/Api/UserController.cs b/Juggle.Api/Controllers/Api/UserController.cs index a456220..be1e38e 100644 --- a/Juggle.Api/Controllers/Api/UserController.cs +++ b/Juggle.Api/Controllers/Api/UserController.cs @@ -12,6 +12,10 @@ namespace Juggle.Api.Controllers.Api; +/// +/// 用户管理控制器 +/// 提供用户登录、信息查询、CRUD 等功能 +/// [ApiController] [Route("api/user")] public class UserController : ControllerBase @@ -20,6 +24,12 @@ public class UserController : ControllerBase private readonly JwtService _jwtService; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// JWT 签发服务 + /// 多租户访问器 public UserController(JuggleDbContext db, JwtService jwtService, ITenantAccessor tenant) { _db = db; @@ -27,6 +37,12 @@ public UserController(JuggleDbContext db, JwtService jwtService, ITenantAccessor _tenant = tenant; } + /// + /// 用户登录 + /// 验证用户名密码,签发 JWT Token,并返回菜单权限 + /// + /// 登录请求(用户名、密码) + /// 登录结果(Token、用户信息、菜单权限) [HttpPost("login")] public async Task Login([FromBody] LoginRequest req) { diff --git a/Juggle.Api/Controllers/Api/WebhookController.cs b/Juggle.Api/Controllers/Api/WebhookController.cs index cffe3f6..0bdb08b 100644 --- a/Juggle.Api/Controllers/Api/WebhookController.cs +++ b/Juggle.Api/Controllers/Api/WebhookController.cs @@ -8,7 +8,11 @@ namespace Juggle.Api.Controllers.Api; -/// Webhook 管理接口 +/// +/// Webhook 管理控制器 +/// 提供 Webhook 的增删改查、启停控制等功能 +/// Webhook 用于从外部系统触发流程执行 +/// [ApiController] [Route("api/system/webhook")] [Authorize] @@ -17,13 +21,22 @@ public class WebhookController : ControllerBase private readonly JuggleDbContext _db; private readonly ITenantAccessor _tenant; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 多租户访问器 public WebhookController(JuggleDbContext db, ITenantAccessor tenant) { _db = db; _tenant = tenant; } - /// 分页查询 Webhook 列表 + /// + /// 分页查询 Webhook 列表 + /// + /// 分页请求参数 + /// Webhook 列表 [HttpPost("page")] public async Task Page([FromBody] PageRequest req) { diff --git a/Juggle.Api/Controllers/Open/FlowOpenController.cs b/Juggle.Api/Controllers/Open/FlowOpenController.cs index ad02fba..8633598 100644 --- a/Juggle.Api/Controllers/Open/FlowOpenController.cs +++ b/Juggle.Api/Controllers/Open/FlowOpenController.cs @@ -7,7 +7,11 @@ namespace Juggle.Api.Controllers.Open; -/// 开放接口:供外部系统通过 Access Token 触发流程执行。 +/// +/// 流程开放接口控制器 +/// 供外部系统通过 Access Token 触发流程执行 +/// 支持同步/异步执行、版本指定、权限校验等功能 +/// [ApiController] [Route("open/flow")] public class FlowOpenController : ControllerBase @@ -15,12 +19,22 @@ public class FlowOpenController : ControllerBase private readonly JuggleDbContext _db; private readonly FlowExecutionService _flowExec; + /// + /// 构造函数 + /// + /// 数据库上下文 + /// 流程执行服务 public FlowOpenController(JuggleDbContext db, FlowExecutionService flowExec) { _db = db; _flowExec = flowExec; } + /// + /// 验证 Token 是否有效 + /// + /// Access Token + /// Token 是否有效 private async Task ValidateToken(string? token) { if (string.IsNullOrEmpty(token)) return false; diff --git a/Juggle.Application/Class1.cs b/Juggle.Application/Class1.cs index 3e7718c..fcea779 100644 --- a/Juggle.Application/Class1.cs +++ b/Juggle.Application/Class1.cs @@ -1,5 +1,9 @@ namespace Juggle.Application; +/// +/// 占位类 +/// 应用层核心业务逻辑请放在 Services 目录下 +/// public class Class1 { diff --git a/Juggle.Application/Models/Request/ApiDebugRequest.cs b/Juggle.Application/Models/Request/ApiDebugRequest.cs index bd4aaea..7dfcd90 100644 --- a/Juggle.Application/Models/Request/ApiDebugRequest.cs +++ b/Juggle.Application/Models/Request/ApiDebugRequest.cs @@ -1,8 +1,25 @@ namespace Juggle.Application.Models.Request; +/// +/// API 调试请求 +/// 用于在界面上调试单个 API 的执行情况 +/// public class ApiDebugRequest { + /// + /// API 记录 ID + /// public long ApiId { get; set; } + + /// + /// HTTP 请求头 + /// 键值对形式传递,如 Authorization、Content-Type 等 + /// public Dictionary Headers { get; set; } = new(); + + /// + /// HTTP 查询参数 + /// 键值对形式传递,GET 请求会拼接到 URL 后面 + /// public Dictionary Params { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ApiUpdateRequest.cs b/Juggle.Application/Models/Request/ApiUpdateRequest.cs index 5d74f0a..08fb675 100644 --- a/Juggle.Application/Models/Request/ApiUpdateRequest.cs +++ b/Juggle.Application/Models/Request/ApiUpdateRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// API 更新请求 +/// 继承自 ApiAddRequest,包含新增和更新所需的所有字段 +/// public class ApiUpdateRequest : ApiAddRequest { + /// + /// API 记录 ID(必填) + /// public long Id { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/AuditLogPageRequest.cs b/Juggle.Application/Models/Request/AuditLogPageRequest.cs index fae6d5c..c4e930e 100644 --- a/Juggle.Application/Models/Request/AuditLogPageRequest.cs +++ b/Juggle.Application/Models/Request/AuditLogPageRequest.cs @@ -1,7 +1,20 @@ namespace Juggle.Application.Models.Request; +/// +/// 审计日志分页查询请求 +/// 继承自分页请求基类 +/// public class AuditLogPageRequest : PageRequest { + /// + /// 模块名称(可选) + /// 用于筛选指定模块的操作日志 + /// public string? Module { get; set; } + + /// + /// 操作类型(可选) + /// 如:Add、Update、Delete、Query 等 + /// public string? ActionType { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/DataSourceAddRequest.cs b/Juggle.Application/Models/Request/DataSourceAddRequest.cs index 1ea1d23..a85c79e 100644 --- a/Juggle.Application/Models/Request/DataSourceAddRequest.cs +++ b/Juggle.Application/Models/Request/DataSourceAddRequest.cs @@ -1,12 +1,42 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增数据源请求模型 +/// public class DataSourceAddRequest { + /// + /// 数据源名称(唯一标识) + /// public string DsName { get; set; } = ""; + + /// + /// 数据源类型:mysql / sqlite / postgresql / sqlserver / oracle / dm + /// public string DsType { get; set; } = "mysql"; + + /// + /// 服务器地址 + /// public string Host { get; set; } = ""; + + /// + /// 端口号(MySQL 默认 3306) + /// public int Port { get; set; } = 3306; + + /// + /// 数据库名称 + /// public string DbName { get; set; } = ""; + + /// + /// 数据库用户名 + /// public string Username { get; set; } = ""; + + /// + /// 数据库密码 + /// public string Password { get; set; } = ""; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/DataSourceUpdateRequest.cs b/Juggle.Application/Models/Request/DataSourceUpdateRequest.cs index 296a381..04a5567 100644 --- a/Juggle.Application/Models/Request/DataSourceUpdateRequest.cs +++ b/Juggle.Application/Models/Request/DataSourceUpdateRequest.cs @@ -1,6 +1,12 @@ namespace Juggle.Application.Models.Request; +/// +/// 数据源更新请求 +/// public class DataSourceUpdateRequest : DataSourceAddRequest { + /// + /// 数据源 ID + /// public long Id { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDebugRequest.cs b/Juggle.Application/Models/Request/FlowDebugRequest.cs index 83e73e9..d3946f1 100644 --- a/Juggle.Application/Models/Request/FlowDebugRequest.cs +++ b/Juggle.Application/Models/Request/FlowDebugRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程调试请求模型 +/// public class FlowDebugRequest { + /// + /// 流程输入参数字典 + /// Key: 参数名,Value: 参数值 + /// public Dictionary Params { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDefinitionAddRequest.cs b/Juggle.Application/Models/Request/FlowDefinitionAddRequest.cs index 83e13f2..b90a511 100644 --- a/Juggle.Application/Models/Request/FlowDefinitionAddRequest.cs +++ b/Juggle.Application/Models/Request/FlowDefinitionAddRequest.cs @@ -1,9 +1,27 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增流程定义请求模型 +/// public class FlowDefinitionAddRequest { + /// + /// 流程名称 + /// public string FlowName { get; set; } = ""; + + /// + /// 流程描述 + /// public string? FlowDesc { get; set; } + + /// + /// 流程类型:sync(同步)/ async(异步) + /// public string FlowType { get; set; } = "sync"; + + /// + /// 分组名称(用于流程分类管理) + /// public string? GroupName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDefinitionPageRequest.cs b/Juggle.Application/Models/Request/FlowDefinitionPageRequest.cs index 82ef8cb..948638c 100644 --- a/Juggle.Application/Models/Request/FlowDefinitionPageRequest.cs +++ b/Juggle.Application/Models/Request/FlowDefinitionPageRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程定义分页查询请求 +/// public class FlowDefinitionPageRequest : PageRequest { + /// + /// 流程名称(可选) + /// 模糊匹配 + /// public string? FlowName { get; set; } + + /// + /// 分组名称(可选) + /// public string? GroupName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDefinitionSaveRequest.cs b/Juggle.Application/Models/Request/FlowDefinitionSaveRequest.cs index d109f27..06cc4bb 100644 --- a/Juggle.Application/Models/Request/FlowDefinitionSaveRequest.cs +++ b/Juggle.Application/Models/Request/FlowDefinitionSaveRequest.cs @@ -1,7 +1,19 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程定义内容保存请求 +/// 用于保存流程的可视化设计内容 +/// public class FlowDefinitionSaveRequest { + /// + /// 流程定义 ID(0 表示新增) + /// public long Id { get; set; } + + /// + /// 流程设计内容(JSON 格式) + /// 包含节点、连线等可视化设计信息 + /// public string FlowContent { get; set; } = "[]"; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDefinitionUpdateRequest.cs b/Juggle.Application/Models/Request/FlowDefinitionUpdateRequest.cs index f756781..5ce4668 100644 --- a/Juggle.Application/Models/Request/FlowDefinitionUpdateRequest.cs +++ b/Juggle.Application/Models/Request/FlowDefinitionUpdateRequest.cs @@ -1,10 +1,34 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程定义更新请求 +/// public class FlowDefinitionUpdateRequest { + /// + /// 流程定义 ID + /// public long Id { get; set; } + + /// + /// 流程名称 + /// public string FlowName { get; set; } = ""; + + /// + /// 流程描述(可选) + /// public string? FlowDesc { get; set; } + + /// + /// 流程类型 + /// sync-同步流程;async-异步流程 + /// public string FlowType { get; set; } = "sync"; + + /// + /// 分组名称(可选) + /// 用于在界面上按组分类展示流程 + /// public string? GroupName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowDeployRequest.cs b/Juggle.Application/Models/Request/FlowDeployRequest.cs index 3cc512e..dbe826b 100644 --- a/Juggle.Application/Models/Request/FlowDeployRequest.cs +++ b/Juggle.Application/Models/Request/FlowDeployRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程部署请求 +/// 将流程定义发布到正式环境 +/// public class FlowDeployRequest { + /// + /// 流程定义 ID + /// public long FlowDefinitionId { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowLogPageRequest.cs b/Juggle.Application/Models/Request/FlowLogPageRequest.cs index 8f84896..0c99714 100644 --- a/Juggle.Application/Models/Request/FlowLogPageRequest.cs +++ b/Juggle.Application/Models/Request/FlowLogPageRequest.cs @@ -1,9 +1,30 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程执行日志分页查询请求 +/// public class FlowLogPageRequest : PageRequest { - public string? FlowKey { get; set; } - public string? Status { get; set; } + /// + /// 流程标识 Key(可选) + /// + public string? FlowKey { get; set; } + + /// + /// 执行状态(可选) + /// 如:success、failed、running + /// + public string? Status { get; set; } + + /// + /// 开始日期(可选) + /// 格式:yyyy-MM-dd + /// public string? StartDate { get; set; } - public string? EndDate { get; set; } + + /// + /// 结束日期(可选) + /// 格式:yyyy-MM-dd + /// + public string? EndDate { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowTestCasePageRequest.cs b/Juggle.Application/Models/Request/FlowTestCasePageRequest.cs index f560694..58c158f 100644 --- a/Juggle.Application/Models/Request/FlowTestCasePageRequest.cs +++ b/Juggle.Application/Models/Request/FlowTestCasePageRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程测试用例分页查询请求 +/// public class FlowTestCasePageRequest : PageRequest { + /// + /// 流程标识 Key(可选) + /// 用于筛选指定流程的测试用例 + /// public string? FlowKey { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowTestCaseSaveRequest.cs b/Juggle.Application/Models/Request/FlowTestCaseSaveRequest.cs index a01bc9b..d49cbd0 100644 --- a/Juggle.Application/Models/Request/FlowTestCaseSaveRequest.cs +++ b/Juggle.Application/Models/Request/FlowTestCaseSaveRequest.cs @@ -1,11 +1,37 @@ namespace Juggle.Application.Models.Request; +/// +/// 保存流程测试用例请求模型 +/// public class FlowTestCaseSaveRequest { + /// + /// 测试用例 ID(编辑时传入,新增时为空) + /// public long? Id { get; set; } + + /// + /// 关联的流程 Key + /// public string FlowKey { get; set; } = ""; + + /// + /// 用例名称 + /// public string CaseName { get; set; } = ""; + + /// + /// 输入参数 JSON + /// public string? InputJson { get; set; } + + /// + /// 断言配置 JSON + /// public string? AssertJson { get; set; } + + /// + /// 备注说明 + /// public string? Remark { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowVersionPageRequest.cs b/Juggle.Application/Models/Request/FlowVersionPageRequest.cs index 6a2a469..9bbfa75 100644 --- a/Juggle.Application/Models/Request/FlowVersionPageRequest.cs +++ b/Juggle.Application/Models/Request/FlowVersionPageRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程版本分页查询请求 +/// public class FlowVersionPageRequest : PageRequest { + /// + /// 流程标识 Key(可选) + /// 用于筛选指定流程的所有版本 + /// public string? FlowKey { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/FlowVersionStatusRequest.cs b/Juggle.Application/Models/Request/FlowVersionStatusRequest.cs index 9ae0e55..bbe3412 100644 --- a/Juggle.Application/Models/Request/FlowVersionStatusRequest.cs +++ b/Juggle.Application/Models/Request/FlowVersionStatusRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 流程版本状态更新请求 +/// public class FlowVersionStatusRequest { + /// + /// 流程版本 ID + /// public long Id { get; set; } + + /// + /// 状态值 + /// 0-草稿;1-已发布 + /// public int Status { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/LoginRequest.cs b/Juggle.Application/Models/Request/LoginRequest.cs index 132c000..48617d5 100644 --- a/Juggle.Application/Models/Request/LoginRequest.cs +++ b/Juggle.Application/Models/Request/LoginRequest.cs @@ -1,7 +1,17 @@ namespace Juggle.Application.Models.Request; +/// +/// 用户登录请求模型 +/// public class LoginRequest { + /// + /// 用户名(登录账号) + /// public string UserName { get; set; } = ""; + + /// + /// 密码(明文,后端 MD5 加密后比对) + /// public string Password { get; set; } = ""; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ObjectAddRequest.cs b/Juggle.Application/Models/Request/ObjectAddRequest.cs index 694bd93..5661c28 100644 --- a/Juggle.Application/Models/Request/ObjectAddRequest.cs +++ b/Juggle.Application/Models/Request/ObjectAddRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 业务对象新增请求 +/// 用于创建流程中使用的业务对象实体 +/// public class ObjectAddRequest { + /// + /// 业务对象名称 + /// public string ObjectName { get; set; } = ""; + + /// + /// 业务对象描述(可选) + /// public string? ObjectDesc { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ObjectUpdateRequest.cs b/Juggle.Application/Models/Request/ObjectUpdateRequest.cs index 3e708b1..de403be 100644 --- a/Juggle.Application/Models/Request/ObjectUpdateRequest.cs +++ b/Juggle.Application/Models/Request/ObjectUpdateRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 业务对象更新请求 +/// 继承自 ObjectAddRequest +/// public class ObjectUpdateRequest : ObjectAddRequest { + /// + /// 业务对象 ID + /// public long Id { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/PageRequest.cs b/Juggle.Application/Models/Request/PageRequest.cs index fb04626..8e49694 100644 --- a/Juggle.Application/Models/Request/PageRequest.cs +++ b/Juggle.Application/Models/Request/PageRequest.cs @@ -1,8 +1,24 @@ namespace Juggle.Application.Models.Request; +/// +/// 分页查询请求模型 +/// 用于列表数据的分页查询 +/// public class PageRequest { + /// + /// 页码(从 1 开始) + /// public int PageNum { get; set; } = 1; + + /// + /// 每页记录数 + /// public int PageSize { get; set; } = 10; + + /// + /// 关键词搜索(可选) + /// 用于模糊匹配相关字段 + /// public string? Keyword { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ParameterItem.cs b/Juggle.Application/Models/Request/ParameterItem.cs index e4345c7..e10d0d8 100644 --- a/Juggle.Application/Models/Request/ParameterItem.cs +++ b/Juggle.Application/Models/Request/ParameterItem.cs @@ -1,14 +1,56 @@ namespace Juggle.Application.Models.Request; +/// +/// 参数项定义 +/// 用于描述接口或流程的输入/输出参数 +/// public class ParameterItem { + /// + /// 参数 ID(可选,0 表示新增) + /// public long? Id { get; set; } + + /// + /// 参数编码 + /// public string ParamCode { get; set; } = ""; + + /// + /// 参数名称 + /// public string ParamName { get; set; } = ""; + + /// + /// 数据类型 + /// string、int、long、decimal、bool、date、datetime、object、array + /// public string DataType { get; set; } = "string"; + + /// + /// 业务对象编码(可选) + /// 当数据类型为 object 时,指定关联的业务对象 + /// public string? ObjectCode { get; set; } + + /// + /// 是否必填 + /// 0-否;1-是 + /// public int Required { get; set; } = 0; + + /// + /// 默认值(可选) + /// public string? DefaultValue { get; set; } + + /// + /// 参数描述(可选) + /// public string? Description { get; set; } + + /// + /// 排序号 + /// public int SortNum { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ParameterSaveRequest.cs b/Juggle.Application/Models/Request/ParameterSaveRequest.cs index 5b7e674..4af7bd0 100644 --- a/Juggle.Application/Models/Request/ParameterSaveRequest.cs +++ b/Juggle.Application/Models/Request/ParameterSaveRequest.cs @@ -1,9 +1,31 @@ namespace Juggle.Application.Models.Request; +/// +/// 参数保存请求 +/// 用于保存接口或流程的参数定义 +/// public class ParameterSaveRequest { + /// + /// 所属者 ID + /// 如:API ID 或流程定义 ID + /// public long OwnerId { get; set; } + + /// + /// 所属者编码 + /// 如:API 的 methodCode + /// public string OwnerCode { get; set; } = ""; + + /// + /// 参数类型 + /// 1-输入参数;2-输出参数 + /// public int ParamType { get; set; } + + /// + /// 参数列表 + /// public List Parameters { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/RoleAddRequest.cs b/Juggle.Application/Models/Request/RoleAddRequest.cs index 50b272e..e22bf10 100644 --- a/Juggle.Application/Models/Request/RoleAddRequest.cs +++ b/Juggle.Application/Models/Request/RoleAddRequest.cs @@ -1,10 +1,32 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增角色请求模型 +/// public class RoleAddRequest { + /// + /// 角色名称 + /// public string RoleName { get; set; } = ""; + + /// + /// 角色编码 + /// public string? RoleCode { get; set; } + + /// + /// 角色描述 + /// public string? Remark { get; set; } + + /// + /// 所属租户 ID(为 null 表示全局角色) + /// public long? TenantId { get; set; } + + /// + /// 菜单权限 Key 列表 + /// public List MenuKeys { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/RoleUpdateRequest.cs b/Juggle.Application/Models/Request/RoleUpdateRequest.cs index e37bdda..5c2616b 100644 --- a/Juggle.Application/Models/Request/RoleUpdateRequest.cs +++ b/Juggle.Application/Models/Request/RoleUpdateRequest.cs @@ -1,11 +1,37 @@ namespace Juggle.Application.Models.Request; +/// +/// 角色更新请求 +/// public class RoleUpdateRequest { + /// + /// 角色 ID + /// public long Id { get; set; } + + /// + /// 角色名称 + /// public string RoleName { get; set; } = ""; + + /// + /// 角色编码(可选) + /// public string? RoleCode { get; set; } + + /// + /// 备注(可选) + /// public string? Remark { get; set; } + + /// + /// 租户 ID(可选) + /// public long? TenantId { get; set; } + + /// + /// 菜单权限标识列表 + /// public List MenuKeys { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ScheduleTaskAddRequest.cs b/Juggle.Application/Models/Request/ScheduleTaskAddRequest.cs index f0266cb..5d8a343 100644 --- a/Juggle.Application/Models/Request/ScheduleTaskAddRequest.cs +++ b/Juggle.Application/Models/Request/ScheduleTaskAddRequest.cs @@ -1,9 +1,29 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增定时任务请求模型 +/// public class ScheduleTaskAddRequest { + /// + /// 关联的流程 Key + /// public string FlowKey { get; set; } = ""; + + /// + /// 流程名称(冗余) + /// public string? FlowName { get; set; } + + /// + /// Cron 表达式 + /// 格式:秒 分 时 日 月 周 + /// 默认值:每 5 分钟执行一次 + /// public string CronExpression { get; set; } = "0 */5 * * * *"; + + /// + /// 触发时的固定入参 JSON + /// public string? InputJson { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ScheduleTaskPageRequest.cs b/Juggle.Application/Models/Request/ScheduleTaskPageRequest.cs index 8d113ac..5cf7dd7 100644 --- a/Juggle.Application/Models/Request/ScheduleTaskPageRequest.cs +++ b/Juggle.Application/Models/Request/ScheduleTaskPageRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 定时任务分页查询请求 +/// public class ScheduleTaskPageRequest : PageRequest { + /// + /// 流程标识 Key(可选) + /// public string? FlowKey { get; set; } + + /// + /// 任务状态(可选) + /// 0-禁用;1-启用 + /// public int? Status { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/ScheduleTaskUpdateRequest.cs b/Juggle.Application/Models/Request/ScheduleTaskUpdateRequest.cs index 14cbced..0bb7905 100644 --- a/Juggle.Application/Models/Request/ScheduleTaskUpdateRequest.cs +++ b/Juggle.Application/Models/Request/ScheduleTaskUpdateRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 定时任务更新请求 +/// public class ScheduleTaskUpdateRequest : ScheduleTaskAddRequest { + /// + /// 定时任务 ID + /// public long Id { get; set; } + + /// + /// 任务状态 + /// 0-禁用;1-启用 + /// public int Status { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/SetValueRequest.cs b/Juggle.Application/Models/Request/SetValueRequest.cs index 58ad3d6..dcd9cfd 100644 --- a/Juggle.Application/Models/Request/SetValueRequest.cs +++ b/Juggle.Application/Models/Request/SetValueRequest.cs @@ -1,6 +1,13 @@ namespace Juggle.Application.Models.Request; +/// +/// 设置值请求 +/// 用于流程执行过程中修改变量的值 +/// public class SetValueRequest { + /// + /// 要设置的值 + /// public string? Value { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/StaticVarSaveRequest.cs b/Juggle.Application/Models/Request/StaticVarSaveRequest.cs index c133fd0..4894210 100644 --- a/Juggle.Application/Models/Request/StaticVarSaveRequest.cs +++ b/Juggle.Application/Models/Request/StaticVarSaveRequest.cs @@ -1,13 +1,47 @@ namespace Juggle.Application.Models.Request; +/// +/// 保存静态变量请求模型 +/// public class StaticVarSaveRequest { + /// + /// 变量 ID(编辑时传入,新增时为空) + /// public long? Id { get; set; } + + /// + /// 变量编码(唯一标识) + /// public string? VarCode { get; set; } + + /// + /// 变量名称(中文描述) + /// public string? VarName { get; set; } + + /// + /// 数据类型:string / integer / double / boolean / date / json + /// public string? DataType { get; set; } + + /// + /// 当前值 + /// public string? Value { get; set; } + + /// + /// 默认值 + /// public string? DefaultValue { get; set; } + + /// + /// 变量描述 + /// public string? Description { get; set; } + + /// + /// 分组名称 + /// public string? GroupName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/SuiteAddRequest.cs b/Juggle.Application/Models/Request/SuiteAddRequest.cs index 1997339..cbb9d25 100644 --- a/Juggle.Application/Models/Request/SuiteAddRequest.cs +++ b/Juggle.Application/Models/Request/SuiteAddRequest.cs @@ -1,9 +1,29 @@ namespace Juggle.Application.Models.Request; +/// +/// 测试套件新增请求 +/// 用于创建包含多个测试用例的套件 +/// public class SuiteAddRequest { + /// + /// 测试套件名称 + /// public string SuiteName { get; set; } = ""; + + /// + /// 测试套件描述(可选) + /// public string? SuiteDesc { get; set; } + + /// + /// 套件图标(可选) + /// Base64 编码或 URL + /// public string? SuiteImage { get; set; } + + /// + /// 套件版本号 + /// public string SuiteVersion { get; set; } = "v1.0.0"; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/SuitePageRequest.cs b/Juggle.Application/Models/Request/SuitePageRequest.cs index e22f558..fa85840 100644 --- a/Juggle.Application/Models/Request/SuitePageRequest.cs +++ b/Juggle.Application/Models/Request/SuitePageRequest.cs @@ -1,6 +1,12 @@ namespace Juggle.Application.Models.Request; +/// +/// 测试套件分页查询请求 +/// public class SuitePageRequest : PageRequest { + /// + /// 测试套件名称(可选) + /// public string? SuiteName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/SuiteUpdateRequest.cs b/Juggle.Application/Models/Request/SuiteUpdateRequest.cs index 85372b0..f10d21b 100644 --- a/Juggle.Application/Models/Request/SuiteUpdateRequest.cs +++ b/Juggle.Application/Models/Request/SuiteUpdateRequest.cs @@ -1,6 +1,12 @@ namespace Juggle.Application.Models.Request; +/// +/// 测试套件更新请求 +/// public class SuiteUpdateRequest : SuiteAddRequest { + /// + /// 测试套件 ID + /// public long Id { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/SystemConfigSaveRequest.cs b/Juggle.Application/Models/Request/SystemConfigSaveRequest.cs index f357ee0..0655b31 100644 --- a/Juggle.Application/Models/Request/SystemConfigSaveRequest.cs +++ b/Juggle.Application/Models/Request/SystemConfigSaveRequest.cs @@ -1,7 +1,17 @@ namespace Juggle.Application.Models.Request; +/// +/// 系统配置保存请求 +/// public class SystemConfigSaveRequest { + /// + /// 配置键 + /// public string ConfigKey { get; set; } = ""; + + /// + /// 配置值(可选) + /// public string? ConfigValue { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/TokenAddRequest.cs b/Juggle.Application/Models/Request/TokenAddRequest.cs index a74c933..411e55b 100644 --- a/Juggle.Application/Models/Request/TokenAddRequest.cs +++ b/Juggle.Application/Models/Request/TokenAddRequest.cs @@ -1,7 +1,17 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增 Token 请求模型 +/// public class TokenAddRequest { + /// + /// Token 名称(中文描述) + /// public string TokenName { get; set; } = ""; + + /// + /// 过期时间(ISO 8601 格式,为空表示永不过期) + /// public string? ExpiredAt { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/TokenPermissionSaveRequest.cs b/Juggle.Application/Models/Request/TokenPermissionSaveRequest.cs index 6f054ef..ca437c6 100644 --- a/Juggle.Application/Models/Request/TokenPermissionSaveRequest.cs +++ b/Juggle.Application/Models/Request/TokenPermissionSaveRequest.cs @@ -1,8 +1,25 @@ namespace Juggle.Application.Models.Request; +/// +/// Token 权限保存请求 +/// 用于为 Token 授权可访问的流程或 API +/// public class TokenPermissionSaveRequest { - public string PermissionType { get; set; } = ""; // FLOW / API - public string ResourceKey { get; set; } = ""; // flowKey / methodCode + /// + /// 权限类型 + /// FLOW-流程;API-接口 + /// + public string PermissionType { get; set; } = ""; + + /// + /// 资源标识 + /// FLOW 类型时为 flowKey;API 类型时为 methodCode + /// + public string ResourceKey { get; set; } = ""; + + /// + /// 资源名称(可选) + /// public string? ResourceName { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/UserAddRequest.cs b/Juggle.Application/Models/Request/UserAddRequest.cs index 73f7a51..4ad84fd 100644 --- a/Juggle.Application/Models/Request/UserAddRequest.cs +++ b/Juggle.Application/Models/Request/UserAddRequest.cs @@ -1,9 +1,27 @@ namespace Juggle.Application.Models.Request; +/// +/// 新增用户请求模型 +/// public class UserAddRequest { + /// + /// 用户名(登录账号,唯一) + /// public string UserName { get; set; } = ""; + + /// + /// 密码(明文,后端 MD5 加密存储) + /// public string Password { get; set; } = ""; + + /// + /// 关联的角色 ID + /// public long? RoleId { get; set; } + + /// + /// 所属租户 ID + /// public long? TenantId { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/UserChangePwdRequest.cs b/Juggle.Application/Models/Request/UserChangePwdRequest.cs index 679a058..7e26721 100644 --- a/Juggle.Application/Models/Request/UserChangePwdRequest.cs +++ b/Juggle.Application/Models/Request/UserChangePwdRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 用户修改密码请求 +/// 用户自行修改密码,需验证原密码 +/// public class UserChangePwdRequest { + /// + /// 原密码 + /// public string OldPassword { get; set; } = ""; + + /// + /// 新密码 + /// public string NewPassword { get; set; } = ""; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/UserResetPwdRequest.cs b/Juggle.Application/Models/Request/UserResetPwdRequest.cs index 1b384e2..ede3e5a 100644 --- a/Juggle.Application/Models/Request/UserResetPwdRequest.cs +++ b/Juggle.Application/Models/Request/UserResetPwdRequest.cs @@ -1,7 +1,18 @@ namespace Juggle.Application.Models.Request; +/// +/// 重置用户密码请求 +/// 管理员可为用户重置密码 +/// public class UserResetPwdRequest { + /// + /// 用户 ID + /// public long Id { get; set; } + + /// + /// 新密码 + /// public string NewPassword { get; set; } = ""; } \ No newline at end of file diff --git a/Juggle.Application/Models/Request/UserUpdateRequest.cs b/Juggle.Application/Models/Request/UserUpdateRequest.cs index 6f53ba0..48aba96 100644 --- a/Juggle.Application/Models/Request/UserUpdateRequest.cs +++ b/Juggle.Application/Models/Request/UserUpdateRequest.cs @@ -1,9 +1,27 @@ namespace Juggle.Application.Models.Request; +/// +/// 用户更新请求 +/// public class UserUpdateRequest { + /// + /// 用户 ID + /// public long Id { get; set; } + + /// + /// 用户名称 + /// public string UserName { get; set; } = ""; + + /// + /// 角色 ID(可选) + /// public long? RoleId { get; set; } + + /// + /// 租户 ID(可选) + /// public long? TenantId { get; set; } } \ No newline at end of file diff --git a/Juggle.Application/Models/Response/ApiResult.cs b/Juggle.Application/Models/Response/ApiResult.cs index a587133..88c7de0 100644 --- a/Juggle.Application/Models/Response/ApiResult.cs +++ b/Juggle.Application/Models/Response/ApiResult.cs @@ -1,21 +1,76 @@ namespace Juggle.Application.Models.Response; +/// +/// 通用 API 响应封装类(泛型版本) +/// 统一规范接口返回值格式,便于前端统一处理 +/// +/// 响应数据的类型 public class ApiResult { + /// + /// 状态码,200 表示成功,其他值表示错误 + /// public int Code { get; set; } + + /// + /// 响应消息,成功时为 "success",失败时为错误描述 + /// public string Message { get; set; } = "success"; + + /// + /// 响应数据,类型为 T + /// public T? Data { get; set; } + /// + /// 返回成功响应 + /// + /// 要返回的数据 + /// 包装后的 ApiResult 对象 public static ApiResult Success(T data) => new() { Code = 200, Message = "success", Data = data }; + + /// + /// 返回失败响应 + /// + /// 错误消息 + /// 错误状态码,默认为 500(服务器内部错误) + /// 包装后的 ApiResult 对象 public static ApiResult Fail(string message, int code = 500) => new() { Code = code, Message = message }; } +/// +/// 通用 API 响应封装类(非泛型版本) +/// 用于不需要返回具体数据的场景 +/// public class ApiResult { + /// + /// 状态码,200 表示成功,其他值表示错误 + /// public int Code { get; set; } + + /// + /// 响应消息,成功时为 "success",失败时为错误描述 + /// public string Message { get; set; } = "success"; + + /// + /// 响应数据,可为任意类型 + /// public object? Data { get; set; } + /// + /// 返回成功响应 + /// + /// 要返回的数据,默认为 null + /// 包装后的 ApiResult 对象 public static ApiResult Success(object? data = null) => new() { Code = 200, Message = "success", Data = data }; + + /// + /// 返回失败响应 + /// + /// 错误消息 + /// 错误状态码,默认为 500(服务器内部错误) + /// 包装后的 ApiResult 对象 public static ApiResult Fail(string message, int code = 500) => new() { Code = code, Message = message }; } diff --git a/Juggle.Application/Models/Response/PageResult.cs b/Juggle.Application/Models/Response/PageResult.cs index 6bcac27..bad66cb 100644 --- a/Juggle.Application/Models/Response/PageResult.cs +++ b/Juggle.Application/Models/Response/PageResult.cs @@ -1,9 +1,29 @@ namespace Juggle.Application.Models.Response; +/// +/// 分页查询结果封装 +/// 统一分页接口的返回格式 +/// +/// 分页数据的类型 public class PageResult { + /// + /// 总记录数 + /// public long Total { get; set; } + + /// + /// 当前页码 + /// public int PageNum { get; set; } + + /// + /// 每页记录数 + /// public int PageSize { get; set; } + + /// + /// 当前页的数据列表 + /// public List Records { get; set; } = new(); } \ No newline at end of file diff --git a/Juggle.Application/Services/ITenantAccessor.cs b/Juggle.Application/Services/ITenantAccessor.cs index 5bd4504..9f5ca84 100644 --- a/Juggle.Application/Services/ITenantAccessor.cs +++ b/Juggle.Application/Services/ITenantAccessor.cs @@ -3,22 +3,42 @@ namespace Juggle.Application.Services; /// -/// 租户上下文访问接口,每个请求的租户信息通过此接口获取。 -/// 配合 ASP.NET Core 的 Scoped 生命周期,在中间件中设置。 +/// 租户上下文访问接口 +/// 用于在请求周期内获取当前用户的租户信息 +/// 配合 ASP.NET Core 的 Scoped 生命周期,在中间件中设置 /// public interface ITenantAccessor { - /// 当前请求的租户ID,null 表示未设置。 + /// + /// 当前请求的租户 ID + /// null 表示未设置(超管或匿名请求) + /// long? TenantId { get; set; } - /// 当前请求的用户ID。 + + /// + /// 当前请求的用户 ID + /// long UserId { get; set; } - /// 当前请求的用户角色ID。 + + /// + /// 当前请求的用户角色 ID + /// long? RoleId { get; set; } - /// 当前用户是否为超级管理员(RoleId=1)。 + + /// + /// 当前用户是否为超级管理员(RoleId=1) + /// bool IsSuperAdmin { get; } - /// 当前请求的用户名。 + + /// + /// 当前请求的用户名 + /// string UserName { get; set; } - /// 从 ClaimsPrincipal 中加载用户信息。 + /// + /// 从 ClaimsPrincipal 中加载用户信息 + /// 在登录认证中间件中调用 + /// + /// 当前 HTTP 请求的用户身份 void LoadFromClaims(ClaimsPrincipal user); } diff --git a/Juggle.Domain/Entities/ApiEntity.cs b/Juggle.Domain/Entities/ApiEntity.cs index fcd477a..237d0a9 100644 --- a/Juggle.Domain/Entities/ApiEntity.cs +++ b/Juggle.Domain/Entities/ApiEntity.cs @@ -1,17 +1,54 @@ namespace Juggle.Domain.Entities; +/// +/// API 方法定义实体 +/// 用于存储可被流程调用的外部 API 元数据信息 +/// public class ApiEntity : BaseEntity { + /// + /// 所属套件编码 + /// public string? SuiteCode { get; set; } + + /// + /// 方法编码(唯一标识) + /// public string? MethodCode { get; set; } + + /// + /// 方法名称(中文描述) + /// public string? MethodName { get; set; } + + /// + /// 方法描述 + /// public string? MethodDesc { get; set; } + + /// + /// API 请求地址(URL) + /// public string? Url { get; set; } - /// GET POST PUT DELETE + + /// + /// HTTP 请求方法:GET / POST / PUT / DELETE + /// public string? RequestType { get; set; } - /// JSON FORM + + /// + /// 请求内容类型:JSON / FORM + /// public string? ContentType { get; set; } + + /// + /// Mock 模式返回的预设 JSON 数据 + /// 设置后调用此方法时跳过真实请求,直接返回此数据 + /// public string? MockJson { get; set; } - /// HTTP WEBSERVICE + + /// + /// 调用方式:HTTP / WEBSERVICE + /// public string? MethodType { get; set; } = "HTTP"; } diff --git a/Juggle.Domain/Entities/AuditLogEntity.cs b/Juggle.Domain/Entities/AuditLogEntity.cs index 2c4e526..386226c 100644 --- a/Juggle.Domain/Entities/AuditLogEntity.cs +++ b/Juggle.Domain/Entities/AuditLogEntity.cs @@ -1,21 +1,49 @@ namespace Juggle.Domain.Entities; -/// 审计日志 +/// +/// 审计日志实体 +/// 记录用户的关键操作行为,用于安全审计和操作追溯 +/// public class AuditLogEntity : BaseEntity { - /// 操作模块:tenant / role / user + /// + /// 操作模块 + /// 例如:tenant(租户)、role(角色)、user(用户)、flow(流程)等 + /// public string Module { get; set; } = ""; - /// 操作类型:add / update / delete + + /// + /// 操作类型 + /// 例如:add(新增)、update(修改)、delete(删除) + /// public string ActionType { get; set; } = ""; - /// 操作目标ID + + /// + /// 操作目标 ID(被操作记录的主键) + /// public long TargetId { get; set; } - /// 变更内容摘要(JSON) + + /// + /// 变更内容摘要(JSON 格式) + /// 记录修改前后的字段值变化 + /// public string ChangeContent { get; set; } = "{}"; - /// 操作人用户名 + + /// + /// 操作人用户名 + /// public string? OperatorName { get; set; } - /// 操作人用户ID + + /// + /// 操作人用户 ID + /// public long? OperatorId { get; set; } - /// 操作人租户ID(等同于 TenantId,保留用于兼容旧查询) + + /// + /// 操作人所属租户 ID + /// 等同于 TenantId,保留用于兼容旧查询 + /// public long? OperatorTenantId { get; set; } + // TenantId 继承自 BaseEntity } diff --git a/Juggle.Domain/Entities/BaseEntity.cs b/Juggle.Domain/Entities/BaseEntity.cs index 9b85b6c..5568948 100644 --- a/Juggle.Domain/Entities/BaseEntity.cs +++ b/Juggle.Domain/Entities/BaseEntity.cs @@ -1,18 +1,51 @@ namespace Juggle.Domain.Entities; /// -/// 所有业务实体的基类。 -/// TenantId = null 表示全局数据(不受租户隔离,如官方套件、全局角色); -/// TenantId 有值时,DbContext 的全局查询过滤器会自动按当前请求租户隔离。 +/// 所有业务实体的基类 +/// 提供公共字段,包括主键、软删除标记、租户隔离字段、审计字段 /// +/// +/// TenantId 字段说明: +/// - TenantId = null:表示全局数据,不受租户隔离(如官方套件、全局角色) +/// - TenantId 有值时:DbContext 的全局查询过滤器会自动按当前请求租户隔离数据 +/// public abstract class BaseEntity { + /// + /// 主键 ID + /// public long Id { get; set; } + + /// + /// 软删除标记:0-未删除,1-已删除 + /// 已删除的记录在查询时会被自动过滤 + /// public int Deleted { get; set; } = 0; - /// 所属租户ID(null = 全局/不隔离) + + /// + /// 所属租户 ID + /// - null = 全局数据(不隔离) + /// - 有值 = 租户私有数据 + /// public long? TenantId { get; set; } + + /// + /// 创建时间(ISO 8601 格式字符串) + /// public string? CreatedAt { get; set; } + + /// + /// 创建人 ID + /// public long? CreatedBy { get; set; } + + /// + /// 最后更新时间(ISO 8601 格式字符串) + /// public string? UpdatedAt { get; set; } + + /// + /// 最后更新人 ID + /// public long? UpdatedBy { get; set; } } diff --git a/Juggle.Domain/Entities/DataSourceEntity.cs b/Juggle.Domain/Entities/DataSourceEntity.cs index e187593..ec988ce 100644 --- a/Juggle.Domain/Entities/DataSourceEntity.cs +++ b/Juggle.Domain/Entities/DataSourceEntity.cs @@ -1,12 +1,51 @@ namespace Juggle.Domain.Entities; +/// +/// 数据源配置实体 +/// 用于存储流程中数据库节点所需的连接信息 +/// 支持 MySQL、SQLite、PostgreSQL、SQL Server、Oracle、达梦等多种数据库 +/// public class DataSourceEntity : BaseEntity { + /// + /// 数据源名称(唯一标识) + /// 流程中通过此名称引用数据源 + /// public string? DsName { get; set; } - public string? DsType { get; set; } // mysql + + /// + /// 数据源类型: + /// - mysql:MySQL + /// - sqlite:SQLite + /// - postgresql / postgres:PostgreSQL + /// - sqlserver / mssql:SQL Server + /// - oracle:Oracle + /// - dm:达梦数据库 + /// + public string? DsType { get; set; } + + /// + /// 数据库服务器地址 + /// public string? Host { get; set; } + + /// + /// 数据库服务端口 + /// public int Port { get; set; } + + /// + /// 数据库名称 + /// public string? DbName { get; set; } + + /// + /// 数据库用户名 + /// public string? Username { get; set; } + + /// + /// 数据库密码(建议加密存储) + /// public string? Password { get; set; } } diff --git a/Juggle.Domain/Entities/FlowDefinitionEntity.cs b/Juggle.Domain/Entities/FlowDefinitionEntity.cs index ab488c2..367a0eb 100644 --- a/Juggle.Domain/Entities/FlowDefinitionEntity.cs +++ b/Juggle.Domain/Entities/FlowDefinitionEntity.cs @@ -1,12 +1,44 @@ namespace Juggle.Domain.Entities; +/// +/// 流程定义实体 +/// 存储流程的基本信息和节点配置 JSON +/// public class FlowDefinitionEntity : BaseEntity { - public string? FlowKey { get; set; } // 流程唯一key + /// + /// 流程唯一标识 Key + /// + public string? FlowKey { get; set; } + + /// + /// 流程名称 + /// public string? FlowName { get; set; } + + /// + /// 流程描述 + /// public string? FlowDesc { get; set; } - public string? FlowContent { get; set; } // 流程节点JSON - public string? FlowType { get; set; } // sync/async - public string? GroupName { get; set; } // 分组名称 - public int Status { get; set; } = 0; // 0:草稿 1:已部署 + + /// + /// 流程节点 JSON 配置 + /// 包含完整的节点定义、连接线、变量映射等信息 + /// + public string? FlowContent { get; set; } + + /// + /// 流程类型:sync(同步)/ async(异步) + /// + public string? FlowType { get; set; } + + /// + /// 分组名称,用于流程分类管理 + /// + public string? GroupName { get; set; } + + /// + /// 状态:0-草稿,1-已部署 + /// + public int Status { get; set; } = 0; } diff --git a/Juggle.Domain/Entities/FlowInfoEntity.cs b/Juggle.Domain/Entities/FlowInfoEntity.cs index d2cf9a1..f6381ff 100644 --- a/Juggle.Domain/Entities/FlowInfoEntity.cs +++ b/Juggle.Domain/Entities/FlowInfoEntity.cs @@ -1,10 +1,35 @@ namespace Juggle.Domain.Entities; +/// +/// 流程信息实体 +/// 存储已部署流程的基本信息,与 FlowVersionEntity 形成一对多关系 +/// public class FlowInfoEntity : BaseEntity { + /// + /// 流程唯一标识 Key + /// public string? FlowKey { get; set; } + + /// + /// 流程名称 + /// public string? FlowName { get; set; } + + /// + /// 流程描述 + /// public string? FlowDesc { get; set; } - public string? FlowType { get; set; } // sync/async + + /// + /// 流程类型: + /// - sync:同步执行 + /// - async:异步执行 + /// + public string? FlowType { get; set; } + + /// + /// 流程状态:0-禁用,1-启用 + /// public int Status { get; set; } = 1; } diff --git a/Juggle.Domain/Entities/FlowLogEntity.cs b/Juggle.Domain/Entities/FlowLogEntity.cs index 9d32830..049a651 100644 --- a/Juggle.Domain/Entities/FlowLogEntity.cs +++ b/Juggle.Domain/Entities/FlowLogEntity.cs @@ -1,28 +1,70 @@ namespace Juggle.Domain.Entities; -/// 流程执行日志(每次流程触发记录一条主日志) +/// +/// 流程执行日志实体 +/// 每次流程触发时记录一条主日志,与 FlowNodeLogEntity 形成主从关系 +/// public class FlowLogEntity : BaseEntity { - /// 流程Key + /// + /// 流程唯一标识 Key + /// public string? FlowKey { get; set; } - /// 流程名称(冗余) + + /// + /// 流程名称(冗余存储,便于独立查询) + /// public string? FlowName { get; set; } - /// 版本号 + + /// + /// 版本号(如 "v1"、"v2" 等) + /// public string? Version { get; set; } - /// 触发方式:debug / open / schedule + + /// + /// 触发方式: + /// - debug:调试触发 + /// - open:开放接口触发 + /// - schedule:定时任务触发 + /// - open_async:异步开放接口触发 + /// public string? TriggerType { get; set; } - /// 执行状态:SUCCESS / FAILED + + /// + /// 执行状态: + /// - SUCCESS:执行成功 + /// - FAILED:执行失败 + /// - RUNNING:执行中(仅异步流程预写入) + /// public string? Status { get; set; } - /// 开始时间 + + /// + /// 开始时间(ISO 8601 格式字符串) + /// public string? StartTime { get; set; } - /// 结束时间 + + /// + /// 结束时间(ISO 8601 格式字符串) + /// public string? EndTime { get; set; } - /// 耗时(毫秒) + + /// + /// 执行耗时(毫秒) + /// public long? CostMs { get; set; } - /// 错误信息 + + /// + /// 错误信息(执行失败时记录具体异常信息) + /// public string? ErrorMessage { get; set; } - /// 输入参数 JSON + + /// + /// 输入参数 JSON(流程执行时的入参快照) + /// public string? InputJson { get; set; } - /// 输出结果 JSON + + /// + /// 输出结果 JSON(以 output_ 开头的变量值) + /// public string? OutputJson { get; set; } } diff --git a/Juggle.Domain/Entities/FlowNodeLogEntity.cs b/Juggle.Domain/Entities/FlowNodeLogEntity.cs index b6822ab..a73f8c7 100644 --- a/Juggle.Domain/Entities/FlowNodeLogEntity.cs +++ b/Juggle.Domain/Entities/FlowNodeLogEntity.cs @@ -1,32 +1,91 @@ namespace Juggle.Domain.Entities; -/// 流程节点执行明细日志(每个节点执行记录一条) +/// +/// 流程节点执行明细日志实体 +/// 记录每个节点的执行详情,用于问题排查和性能分析 +/// 与 FlowLogEntity 形成主从关系(FlowLogId 关联) +/// public class FlowNodeLogEntity : BaseEntity { - /// 关联的主日志ID + /// + /// 关联的主日志 ID(FlowLogEntity.Id) + /// public long? FlowLogId { get; set; } - /// 节点Key + + /// + /// 节点唯一标识 Key + /// public string? NodeKey { get; set; } - /// 节点标签/名称 + + /// + /// 节点显示名称/标签 + /// public string? NodeLabel { get; set; } - /// 节点类型:START/END/METHOD/CONDITION/ASSIGN/CODE/MYSQL/MERGE + + /// + /// 节点类型: + /// - START:开始节点 + /// - END:结束节点 + /// - METHOD:HTTP/WebService 调用节点 + /// - CONDITION:条件分支节点 + /// - ASSIGN:赋值节点 + /// - CODE:代码执行节点 + /// - MYSQL:数据库操作节点 + /// - MERGE:分支汇合节点 + /// - SUB_FLOW:子流程调用节点 + /// - LOOP:循环节点 + /// - DELAY:延迟节点 + /// - PARALLEL:并行节点 + /// - NOTIFY:通知节点 + /// public string? NodeType { get; set; } - /// 执行顺序(从1开始) + + /// + /// 执行顺序号(从 1 开始,按执行时间递增) + /// public int? SeqNo { get; set; } - /// 执行状态:SUCCESS / FAILED / SKIPPED + + /// + /// 执行状态: + /// - RUNNING:执行中 + /// - SUCCESS:执行成功 + /// - FAILED:执行失败 + /// - SKIPPED:跳过(未执行) + /// public string? Status { get; set; } - /// 开始时间 + + /// + /// 节点开始执行时间(ISO 8601 格式字符串) + /// public string? StartTime { get; set; } - /// 结束时间 + + /// + /// 节点结束执行时间(ISO 8601 格式字符串) + /// public string? EndTime { get; set; } - /// 耗时(毫秒) + + /// + /// 节点执行耗时(毫秒) + /// public long? CostMs { get; set; } - /// 节点输入(变量快照 JSON) + + /// + /// 节点执行前的变量快照(JSON 格式) + /// public string? InputSnapshot { get; set; } - /// 节点输出(变量快照 JSON) + + /// + /// 节点执行后的变量快照(JSON 格式) + /// public string? OutputSnapshot { get; set; } - /// 日志详情(如SQL、HTTP请求等) + + /// + /// 日志详情(如 SQL 语句、HTTP 请求信息、条件表达式结果等) + /// public string? Detail { get; set; } - /// 错误信息 + + /// + /// 错误信息(节点执行失败时记录具体异常) + /// public string? ErrorMessage { get; set; } } diff --git a/Juggle.Domain/Entities/FlowVersionEntity.cs b/Juggle.Domain/Entities/FlowVersionEntity.cs index dcc9673..6aff7a9 100644 --- a/Juggle.Domain/Entities/FlowVersionEntity.cs +++ b/Juggle.Domain/Entities/FlowVersionEntity.cs @@ -1,11 +1,36 @@ namespace Juggle.Domain.Entities; +/// +/// 流程版本实体 +/// 存储流程部署时的版本快照,用于版本管理和回滚 +/// public class FlowVersionEntity : BaseEntity { + /// + /// 关联的流程信息 ID + /// public long? FlowInfoId { get; set; } + + /// + /// 流程唯一标识 Key + /// public string? FlowKey { get; set; } - public string? Version { get; set; } // v1, v2, ... - public string? FlowContent { get; set; } // 部署时快照的流程JSON - /// 0:禁用 1:启用 + + /// + /// 版本号 + /// 格式为 v1、v2、v3 等 + /// + public string? Version { get; set; } + + /// + /// 流程节点配置 JSON(部署时的快照) + /// 与 FlowDefinitionEntity.FlowContent 保持同步 + /// + public string? FlowContent { get; set; } + + /// + /// 版本状态:0-禁用,1-启用 + /// 只能有一个启用的版本 + /// public int Status { get; set; } = 1; } diff --git a/Juggle.Domain/Entities/LoginLogEntity.cs b/Juggle.Domain/Entities/LoginLogEntity.cs index b083acc..0a24fe3 100644 --- a/Juggle.Domain/Entities/LoginLogEntity.cs +++ b/Juggle.Domain/Entities/LoginLogEntity.cs @@ -1,19 +1,45 @@ namespace Juggle.Domain.Entities; -/// 登录访问日志 +/// +/// 登录访问日志实体 +/// 记录用户的登录行为,包括成功和失败的登录尝试 +/// public class LoginLogEntity : BaseEntity { - /// 用户ID + /// + /// 用户 ID + /// public long? UserId { get; set; } - /// 用户名 + + /// + /// 用户名 + /// public string? UserName { get; set; } - /// 登录类型:login / logout + + /// + /// 登录类型: + /// - login:登录 + /// - logout:登出 + /// public string LoginType { get; set; } = "login"; - /// 登录结果:success / fail + + /// + /// 登录结果: + /// - success:登录成功 + /// - fail:登录失败 + /// public string Result { get; set; } = "success"; - /// 登录IP + + /// + /// 登录来源 IP 地址 + /// public string? IpAddress { get; set; } - /// User-Agent + + /// + /// 客户端 User-Agent 信息 + /// 包含浏览器、操作系统等客户端标识 + /// public string? UserAgent { get; set; } + // TenantId 继承自 BaseEntity } diff --git a/Juggle.Domain/Entities/ObjectEntity.cs b/Juggle.Domain/Entities/ObjectEntity.cs index 72d7474..2b43576 100644 --- a/Juggle.Domain/Entities/ObjectEntity.cs +++ b/Juggle.Domain/Entities/ObjectEntity.cs @@ -1,8 +1,23 @@ namespace Juggle.Domain.Entities; +/// +/// 对象定义实体 +/// 用于定义 API 请求/响应的复杂对象结构 +/// public class ObjectEntity : BaseEntity { + /// + /// 对象编码(唯一标识) + /// public string? ObjectCode { get; set; } + + /// + /// 对象名称(中文描述) + /// public string? ObjectName { get; set; } + + /// + /// 对象描述说明 + /// public string? ObjectDesc { get; set; } } diff --git a/Juggle.Domain/Entities/ParameterEntity.cs b/Juggle.Domain/Entities/ParameterEntity.cs index fdbc66b..f2aae79 100644 --- a/Juggle.Domain/Entities/ParameterEntity.cs +++ b/Juggle.Domain/Entities/ParameterEntity.cs @@ -1,17 +1,74 @@ namespace Juggle.Domain.Entities; +/// +/// 参数定义实体 +/// 用于定义 API 或流程的输入输出参数结构 +/// public class ParameterEntity : BaseEntity { + /// + /// 所属者 ID(API ID 或流程定义 ID) + /// public long? OwnerId { get; set; } + + /// + /// 所属者编码(套件编码或流程 Key) + /// public string? OwnerCode { get; set; } - /// 1:API入参 2:API出参 3:对象属性 4:API Header 5:流程入参 6:流程出参 + + /// + /// 参数类型: + /// - 1:API 入参 + /// - 2:API 出参 + /// - 3:对象属性 + /// - 4:API Header + /// - 5:流程入参 + /// - 6:流程出参 + /// public int ParamType { get; set; } + + /// + /// 参数编码(唯一标识) + /// public string? ParamCode { get; set; } + + /// + /// 参数名称(中文描述) + /// public string? ParamName { get; set; } - public string? DataType { get; set; } // string int boolean object array - public string? ObjectCode { get; set; } // 当DataType=object时引用的对象code + + /// + /// 数据类型: + /// - string:字符串 + /// - int/integer:整数 + /// - boolean:布尔值 + /// - object:对象(引用 ObjectEntity) + /// - array:数组 + /// + public string? DataType { get; set; } + + /// + /// 关联的对象编码(当 DataType=object 时引用) + /// + public string? ObjectCode { get; set; } + + /// + /// 是否必填:0-可选,1-必填 + /// public int Required { get; set; } = 0; + + /// + /// 默认值 + /// public string? DefaultValue { get; set; } + + /// + /// 参数描述说明 + /// public string? Description { get; set; } + + /// + /// 排序号(用于参数展示顺序) + /// public int SortNum { get; set; } = 0; } diff --git a/Juggle.Domain/Entities/RoleEntity.cs b/Juggle.Domain/Entities/RoleEntity.cs index ac6f901..2a861ac 100644 --- a/Juggle.Domain/Entities/RoleEntity.cs +++ b/Juggle.Domain/Entities/RoleEntity.cs @@ -1,10 +1,30 @@ namespace Juggle.Domain.Entities; -/// 角色(TenantId=null 表示全局角色,跨租户共享) +/// +/// 角色实体 +/// 用于权限控制,TenantId=null 表示全局角色(跨租户共享) +/// +/// +/// 角色分类: +/// - 全局角色:TenantId=null,由系统管理员维护,所有租户可见 +/// - 租户角色:TenantId=有值,仅所属租户可见 +/// public class RoleEntity : BaseEntity { + /// + /// 角色名称 + /// public string RoleName { get; set; } = ""; + + /// + /// 角色编码,用于程序逻辑判断(如 "admin" 表示超级管理员) + /// public string? RoleCode { get; set; } + + /// + /// 角色描述 + /// public string? Remark { get; set; } - // TenantId 继承自 BaseEntity,null 表示全局角色 + + // TenantId 继承自 BaseEntity,null 表示全局角色(跨租户共享) } diff --git a/Juggle.Domain/Entities/RoleMenuEntity.cs b/Juggle.Domain/Entities/RoleMenuEntity.cs index 0aa31fa..46fc7f6 100644 --- a/Juggle.Domain/Entities/RoleMenuEntity.cs +++ b/Juggle.Domain/Entities/RoleMenuEntity.cs @@ -1,8 +1,19 @@ namespace Juggle.Domain.Entities; -/// 角色菜单权限(多对多中间表) +/// +/// 角色菜单权限实体(多对多中间表) +/// 建立角色与菜单的关联关系,用于权限控制 +/// public class RoleMenuEntity : BaseEntity { + /// + /// 关联的角色 ID + /// public long RoleId { get; set; } - public string MenuKey { get; set; } = ""; // 菜单标识,如 "flow/define" + + /// + /// 菜单标识 Key + /// 例如:"flow/define"(流程定义)、"flow/log"(流程日志)等 + /// + public string MenuKey { get; set; } = ""; } diff --git a/Juggle.Domain/Entities/ScheduleTaskEntity.cs b/Juggle.Domain/Entities/ScheduleTaskEntity.cs index 379b842..2766bb4 100644 --- a/Juggle.Domain/Entities/ScheduleTaskEntity.cs +++ b/Juggle.Domain/Entities/ScheduleTaskEntity.cs @@ -1,17 +1,59 @@ namespace Juggle.Domain.Entities; /// -/// 定时任务实体:给已发布流程绑定 Cron 表达式,定时自动触发。 +/// 定时任务实体 +/// 给已发布的流程绑定 Cron 表达式,实现定时自动触发执行 /// public class ScheduleTaskEntity : BaseEntity { - public string? FlowKey { get; set; } // 关联的流程 Key - public string? FlowName { get; set; } // 流程名称(冗余,方便展示) - public string? CronExpression { get; set; } // Cron 表达式,如 "0 */5 * * * *" - public string? InputJson { get; set; } // 触发时的固定入参 JSON - public int Status { get; set; } = 0; // 0:暂停 1:启用 - public DateTime? LastRunTime { get; set; } // 上次执行时间 - public string? LastRunStatus { get; set; } // 上次执行状态 SUCCESS/FAILED - public DateTime? NextRunTime { get; set; } // 下次执行时间(预计算) - public int RunCount { get; set; } = 0; // 累计执行次数 + /// + /// 关联的流程唯一标识 Key + /// + public string? FlowKey { get; set; } + + /// + /// 流程名称(冗余存储,便于展示) + /// + public string? FlowName { get; set; } + + /// + /// Cron 表达式 + /// 用于定义执行时间规则,格式为 6 位(秒 分 时 日 月 周) + /// 例如: + /// - "0 */5 * * * *":每 5 分钟执行一次 + /// - "0 0 * * * *":每小时整点执行 + /// - "0 0 0 * * *":每天凌晨执行 + /// + public string? CronExpression { get; set; } + + /// + /// 触发时的固定入参 JSON + /// 定时执行时使用此 JSON 作为流程输入参数 + /// + public string? InputJson { get; set; } + + /// + /// 任务状态:0-暂停,1-启用 + /// + public int Status { get; set; } = 0; + + /// + /// 上次执行时间 + /// + public DateTime? LastRunTime { get; set; } + + /// + /// 上次执行状态:SUCCESS / FAILED + /// + public string? LastRunStatus { get; set; } + + /// + /// 下次执行时间(由调度器预计算) + /// + public DateTime? NextRunTime { get; set; } + + /// + /// 累计执行次数 + /// + public int RunCount { get; set; } = 0; } diff --git a/Juggle.Domain/Entities/StaticVariableEntity.cs b/Juggle.Domain/Entities/StaticVariableEntity.cs index e4bd753..8b6da40 100644 --- a/Juggle.Domain/Entities/StaticVariableEntity.cs +++ b/Juggle.Domain/Entities/StaticVariableEntity.cs @@ -1,20 +1,53 @@ namespace Juggle.Domain.Entities; -/// 静态(全局)变量定义 —— 可在任意流程中引用和修改 +/// +/// 静态(全局)变量定义实体 +/// 可在任意流程中引用和修改,执行结束后自动回写到数据库 +/// 流程中通过 $static.{VarCode} 语法引用 +/// public class StaticVariableEntity : BaseEntity { - /// 变量编码(唯一键,流程中用 $static.xxx 引用) + /// + /// 变量编码(唯一键) + /// 流程中通过 $static.{VarCode} 语法引用 + /// public string? VarCode { get; set; } - /// 变量名称(中文描述) + + /// + /// 变量名称(中文描述,便于理解) + /// public string? VarName { get; set; } - /// 数据类型:string / integer / double / boolean / date / json + + /// + /// 数据类型: + /// - string:字符串 + /// - integer:整数 + /// - double:浮点数 + /// - boolean:布尔值 + /// - date:日期 + /// - json:JSON 对象 + /// public string? DataType { get; set; } - /// 当前值(字符串形式存储) + + /// + /// 当前值(以字符串形式存储) + /// 流程执行中被修改后会回写到数据库 + /// public string? Value { get; set; } - /// 默认值 + + /// + /// 默认值 + /// 当 Value 为空时使用此值 + /// public string? DefaultValue { get; set; } - /// 描述 + + /// + /// 变量描述说明 + /// public string? Description { get; set; } - /// 分组(可按业务分组) + + /// + /// 分组名称(可按业务模块分组管理) + /// public string? GroupName { get; set; } } diff --git a/Juggle.Domain/Entities/SuiteEntity.cs b/Juggle.Domain/Entities/SuiteEntity.cs index 51a191e..95efb1c 100644 --- a/Juggle.Domain/Entities/SuiteEntity.cs +++ b/Juggle.Domain/Entities/SuiteEntity.cs @@ -1,13 +1,50 @@ namespace Juggle.Domain.Entities; +/// +/// 套件实体 +/// 用于组织和封装一组相关的 API 方法 +/// public class SuiteEntity : BaseEntity { + /// + /// 套件编码(唯一标识) + /// public string? SuiteCode { get; set; } + + /// + /// 套件名称 + /// public string? SuiteName { get; set; } + + /// + /// 套件分类 ID + /// public long? SuiteClassifyId { get; set; } + + /// + /// 套件图标(URL 或 Base64) + /// public string? SuiteImage { get; set; } + + /// + /// 套件版本号 + /// public string? SuiteVersion { get; set; } + + /// + /// 套件描述 + /// public string? SuiteDesc { get; set; } + + /// + /// 套件帮助文档(JSON 格式) + /// public string? SuiteHelpDocJson { get; set; } - public int SuiteFlag { get; set; } = 0; // 0:自定义, 1:官方 + + /// + /// 套件标识: + /// - 0:自定义套件(用户创建) + /// - 1:官方套件(预置) + /// + public int SuiteFlag { get; set; } = 0; } diff --git a/Juggle.Domain/Entities/SystemConfigEntity.cs b/Juggle.Domain/Entities/SystemConfigEntity.cs index 6784674..4e35c17 100644 --- a/Juggle.Domain/Entities/SystemConfigEntity.cs +++ b/Juggle.Domain/Entities/SystemConfigEntity.cs @@ -1,11 +1,38 @@ namespace Juggle.Domain.Entities; -/// 系统配置项(键值对) +/// +/// 系统配置项实体(键值对存储) +/// 用于存储系统的各种配置参数,如邮件配置、告警配置、系统参数等 +/// public class SystemConfigEntity : BaseEntity { - public string ConfigKey { get; set; } = ""; // 配置键,全局唯一 - public string? ConfigValue { get; set; } // 配置值 - public string? ConfigName { get; set; } // 显示名称 - public string? ConfigGroup { get; set; } // 分组(email/alert/system) + /// + /// 配置键(全局唯一) + /// 程序通过此键读取配置 + /// + public string ConfigKey { get; set; } = ""; + + /// + /// 配置值 + /// + public string? ConfigValue { get; set; } + + /// + /// 配置显示名称(中文描述) + /// + public string? ConfigName { get; set; } + + /// + /// 配置分组 + /// 用于配置分类管理,例如: + /// - email:邮件配置 + /// - alert:告警配置 + /// - system:系统配置 + /// + public string? ConfigGroup { get; set; } + + /// + /// 配置描述说明 + /// public string? Remark { get; set; } } diff --git a/Juggle.Domain/Entities/TenantEntity.cs b/Juggle.Domain/Entities/TenantEntity.cs index e517558..aba26ef 100644 --- a/Juggle.Domain/Entities/TenantEntity.cs +++ b/Juggle.Domain/Entities/TenantEntity.cs @@ -1,14 +1,43 @@ namespace Juggle.Domain.Entities; -/// 租户 +/// +/// 租户实体 +/// 用于多租户系统的租户管理,支持租户隔离和权限控制 +/// public class TenantEntity : BaseEntity { + /// + /// 租户名称 + /// public string TenantName { get; set; } = ""; + + /// + /// 租户编码(唯一标识) + /// public string? TenantCode { get; set; } + + /// + /// 租户描述 + /// public string? Remark { get; set; } - public int Status { get; set; } = 1; // 1启用 0禁用 - /// 过期时间(null 表示永不过期),过期后自动禁用 + + /// + /// 租户状态:1-启用,0-禁用 + /// 禁用的租户下的用户无法登录 + /// + public int Status { get; set; } = 1; + + /// + /// 租户过期时间 + /// - null:永不过期 + /// - 有值:过期后自动禁用 + /// public DateTime? ExpiredAt { get; set; } - /// 租户拥有的菜单权限(JSON 数组),如 ["flow/define","flow/log"] + + /// + /// 租户拥有的菜单权限(JSON 数组格式) + /// 用于控制租户用户可访问的菜单模块 + /// 例如:["flow/define", "flow/log"] + /// public string MenuKeys { get; set; } = "[]"; } diff --git a/Juggle.Domain/Entities/TokenEntity.cs b/Juggle.Domain/Entities/TokenEntity.cs index 594c236..fb3f7c3 100644 --- a/Juggle.Domain/Entities/TokenEntity.cs +++ b/Juggle.Domain/Entities/TokenEntity.cs @@ -1,9 +1,30 @@ namespace Juggle.Domain.Entities; +/// +/// API Token 实体 +/// 用于开放接口的身份认证,支持设置权限范围和过期时间 +/// public class TokenEntity : BaseEntity { + /// + /// Token 值(唯一标识) + /// 调用开放接口时通过此 Token 进行身份验证 + /// public string? TokenValue { get; set; } + + /// + /// Token 名称(中文描述) + /// public string? TokenName { get; set; } + + /// + /// 过期时间(ISO 8601 格式字符串) + /// 为空表示永不过期 + /// public string? ExpiredAt { get; set; } + + /// + /// 状态:1-启用,0-禁用 + /// public int Status { get; set; } = 1; } diff --git a/Juggle.Domain/Entities/TokenPermissionEntity.cs b/Juggle.Domain/Entities/TokenPermissionEntity.cs index 6e8cce4..08e15e8 100644 --- a/Juggle.Domain/Entities/TokenPermissionEntity.cs +++ b/Juggle.Domain/Entities/TokenPermissionEntity.cs @@ -1,17 +1,33 @@ namespace Juggle.Domain.Entities; -/// Token 权限配置:控制 Token 可访问的流程和套件接口 +/// +/// Token 权限配置实体 +/// 用于控制 API Token 可访问的资源范围 +/// 关联 TokenEntity,建立细粒度的权限控制 +/// public class TokenPermissionEntity : BaseEntity { - /// 关联的 Token ID + /// + /// 关联的 Token ID + /// public long TokenId { get; set; } - /// 权限类型:FLOW(流程) / API(套件接口) + /// + /// 权限类型: + /// - FLOW:流程资源 + /// - API:套件接口资源 + /// public string? PermissionType { get; set; } - /// 关联的资源 Key(flowKey 或 methodCode) + /// + /// 关联的资源 Key + /// - FLOW 类型:flowKey + /// - API 类型:methodCode + /// public string? ResourceKey { get; set; } - /// 资源名称(冗余存储,方便展示) + /// + /// 资源名称(冗余存储,便于展示) + /// public string? ResourceName { get; set; } } diff --git a/Juggle.Domain/Entities/UserEntity.cs b/Juggle.Domain/Entities/UserEntity.cs index 52e7b86..5732721 100644 --- a/Juggle.Domain/Entities/UserEntity.cs +++ b/Juggle.Domain/Entities/UserEntity.cs @@ -1,9 +1,25 @@ namespace Juggle.Domain.Entities; +/// +/// 用户实体 +/// 存储用户基本信息,包括登录凭证和角色关联 +/// public class UserEntity : BaseEntity { + /// + /// 用户名(登录账号),唯一 + /// public string? UserName { get; set; } + + /// + /// 密码(MD5 加密存储) + /// public string? Password { get; set; } + + /// + /// 关联的角色 ID + /// public long? RoleId { get; set; } - // TenantId 继承自 BaseEntity + + // TenantId 继承自 BaseEntity,用于多租户隔离 } diff --git a/Juggle.Domain/Entities/VariableInfoEntity.cs b/Juggle.Domain/Entities/VariableInfoEntity.cs index bb2c2c5..0e1ddb0 100644 --- a/Juggle.Domain/Entities/VariableInfoEntity.cs +++ b/Juggle.Domain/Entities/VariableInfoEntity.cs @@ -1,14 +1,59 @@ namespace Juggle.Domain.Entities; +/// +/// 流程变量信息实体 +/// 定义流程的输入、输出和环境变量 +/// public class VariableInfoEntity : BaseEntity { + /// + /// 关联的流程定义 ID + /// public long? FlowDefinitionId { get; set; } + + /// + /// 流程唯一标识 Key + /// public string? FlowKey { get; set; } + + /// + /// 变量编码(唯一标识) + /// 流程中通过此编码引用变量 + /// public string? VariableCode { get; set; } + + /// + /// 变量名称(中文描述) + /// public string? VariableName { get; set; } + + /// + /// 数据类型: + /// - string:字符串 + /// - integer:整数 + /// - double:浮点数 + /// - boolean:布尔值 + /// - json:JSON 对象 + /// - array:数组 + /// public string? DataType { get; set; } - /// input/output/env + + /// + /// 变量类型: + /// - input:输入变量(外部传入) + /// - output:输出变量(流程产出) + /// - env:环境变量(内部使用) + /// public string? VariableType { get; set; } + + /// + /// 默认值 + /// 当入参为空时使用此值 + /// public string? DefaultValue { get; set; } + + /// + /// 变量描述说明 + /// public string? Description { get; set; } } diff --git a/Juggle.Domain/Entities/WebhookEntity.cs b/Juggle.Domain/Entities/WebhookEntity.cs index c3d15cb..6de4247 100644 --- a/Juggle.Domain/Entities/WebhookEntity.cs +++ b/Juggle.Domain/Entities/WebhookEntity.cs @@ -2,50 +2,79 @@ namespace Juggle.Domain.Entities; -/// Webhook 触发配置实体 +/// +/// Webhook 触发配置实体 +/// 通过 Webhook 方式从外部系统触发流程执行 +/// 访问路径:/open/webhook/{webhookKey} +/// public class WebhookEntity : BaseEntity { - /// Webhook 唯一标识 Key(用于 URL 路径 /open/webhook/{webhookKey}) + /// + /// Webhook 唯一标识 Key + /// 用于 URL 路径访问,如 /open/webhook/{webhookKey} + /// [JsonPropertyName("webhookKey")] public string? WebhookKey { get; set; } - /// Webhook 名称 + /// + /// Webhook 名称(中文描述) + /// [JsonPropertyName("webhookName")] public string? WebhookName { get; set; } - /// 关联的流程 Key + /// + /// 关联的流程唯一标识 Key + /// [JsonPropertyName("flowKey")] public string? FlowKey { get; set; } - /// 关联的流程名称(冗余) + /// + /// 关联的流程名称(冗余存储) + /// [JsonPropertyName("flowName")] public string? FlowName { get; set; } - /// 签名密钥(HMAC-SHA256,为空则不验签) + /// + /// 签名密钥 + /// 使用 HMAC-SHA256 进行请求签名验证 + /// 为空则不进行验签 + /// [JsonPropertyName("secret")] public string? Secret { get; set; } - /// 允许的 HTTP 方法:POST / GET / ANY + /// + /// 允许的 HTTP 请求方法:POST / GET / ANY(任意方法) + /// [JsonPropertyName("allowedMethod")] public string AllowedMethod { get; set; } = "POST"; - /// 是否异步执行(true=立即返回 logId,false=等待执行完成) + /// + /// 异步执行模式:0-同步(等待执行完成),1-异步(立即返回 logId) + /// [JsonPropertyName("asyncMode")] public int AsyncMode { get; set; } = 0; - /// 状态:1=启用 0=禁用 + /// + /// Webhook 状态:1-启用,0-禁用 + /// [JsonPropertyName("status")] public int Status { get; set; } = 1; - /// 触发次数统计 + /// + /// 累计触发次数 + /// [JsonPropertyName("triggerCount")] public long TriggerCount { get; set; } = 0; - /// 最后触发时间 + /// + /// 最后触发时间(ISO 8601 格式字符串) + /// [JsonPropertyName("lastTriggerTime")] public string? LastTriggerTime { get; set; } - /// 备注 + /// + /// 备注说明 + /// [JsonPropertyName("remark")] public string? Remark { get; set; } } diff --git a/Juggle.Infrastructure/Common/JsonHelper.cs b/Juggle.Infrastructure/Common/JsonHelper.cs index 55bd830..82522a8 100644 --- a/Juggle.Infrastructure/Common/JsonHelper.cs +++ b/Juggle.Infrastructure/Common/JsonHelper.cs @@ -4,8 +4,18 @@ namespace Juggle.Infrastructure.Common; +/// +/// JSON 序列化/反序列化帮助类 +/// 提供统一的 JSON 处理方法,支持驼峰命名和完整的 Unicode 字符编码 +/// public static class JsonHelper { + /// + /// JSON 序列化配置选项 + /// - PropertyNamingPolicy: 使用驼峰命名(camelCase) + /// - WriteIndented: 不格式化输出(压缩为一行) + /// - Encoder: 支持所有 Unicode 字符(包括中文等非 ASCII 字符不转义) + /// private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, @@ -13,8 +23,19 @@ public static class JsonHelper Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; + /// + /// 将对象序列化为 JSON 字符串 + /// + /// 要序列化的对象 + /// JSON 字符串(驼峰命名,中文不转义) public static string Serialize(object obj) => JsonSerializer.Serialize(obj, Options); + /// + /// 将 JSON 字符串反序列化为指定类型的对象 + /// + /// 目标类型 + /// JSON 字符串 + /// 反序列化后的对象,如果解析失败返回 null public static T? Deserialize(string json) => JsonSerializer.Deserialize(json, Options); } diff --git a/Juggle.Infrastructure/Common/Md5Helper.cs b/Juggle.Infrastructure/Common/Md5Helper.cs index 198ea4f..c4467e5 100644 --- a/Juggle.Infrastructure/Common/Md5Helper.cs +++ b/Juggle.Infrastructure/Common/Md5Helper.cs @@ -3,8 +3,21 @@ namespace Juggle.Infrastructure.Common; +/// +/// MD5 加密帮助类 +/// 提供字符串的 MD5 哈希计算功能 +/// public static class Md5Helper { + /// + /// 计算字符串的 MD5 哈希值 + /// + /// 要加密的原始字符串 + /// 32位小写十六进制格式的 MD5 哈希值 + /// + /// 注意:MD5 已被证明存在安全漏洞,不适合用于密码存储等安全场景。 + /// 此方法主要用于数据完整性校验或旧系统兼容。 + /// public static string Encrypt(string input) { var bytes = MD5.HashData(Encoding.UTF8.GetBytes(input)); From 516df99c855d03e616855708145d1b9170a2ac96 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 29 Apr 2026 17:17:54 +0800 Subject: [PATCH 007/153] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E6=94=AF=E6=8C=81Api+Vue=E5=89=8D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改选项[2]为Api+Vue模式 - 增加Node.js环境检查 - 同时启动后端API(9127端口)和前端Vue(5173端口) - 两个服务分别在新窗口中运行 --- "\345\220\257\345\212\250Juggle.bat" | 58 +++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git "a/\345\220\257\345\212\250Juggle.bat" "b/\345\220\257\345\212\250Juggle.bat" index 9153b79..83cb25f 100644 --- "a/\345\220\257\345\212\250Juggle.bat" +++ "b/\345\220\257\345\212\250Juggle.bat" @@ -19,7 +19,7 @@ cd /d "%SCRIPT_DIR%" echo 请选择启动方式: echo. echo [1] Docker 启动 (推荐,需要 Docker Desktop) -echo [2] 直接运行 .NET 项目 (需要 .NET 8 SDK) +echo [2] Api + Vue 前后端同时启动 (需要 .NET 8 SDK 和 Node.js) echo [3] 仅构建 Docker 镜像 echo [4] 清理 Docker 容器和镜像 echo [5] 查看日志 (Docker模式) @@ -137,7 +137,7 @@ goto end :dotnet_run echo. -echo [1/4] 检查 .NET SDK 版本... +echo [1/6] 检查 .NET SDK 版本... dotnet --version >nul 2>&1 if errorlevel 1 ( echo [错误] 未安装 .NET 8 SDK,请先安装: @@ -150,7 +150,20 @@ for /f "delims=" %%v in ('dotnet --version') do set DOTNET_VER=%%v echo [OK] .NET 版本: %DOTNET_VER% echo. -echo [2/4] 还原 NuGet 包... +echo [2/6] 检查 Node.js 版本... +node --version >nul 2>&1 +if errorlevel 1 ( + echo [错误] 未安装 Node.js,请先安装: + echo https://nodejs.org/ + echo. + pause + goto menu +) +for /f "delims=" %%v in ('node --version') do set NODE_VER=%%v +echo [OK] Node.js 版本: %NODE_VER% + +echo. +echo [3/6] 还原 NuGet 包... dotnet restore Juggle.sln if errorlevel 1 ( echo [错误] 包还原失败 @@ -160,7 +173,7 @@ if errorlevel 1 ( ) echo. -echo [3/4] 构建项目... +echo [4/6] 构建后端项目... dotnet build Juggle.sln -c Release if errorlevel 1 ( echo [错误] 项目构建失败 @@ -170,17 +183,44 @@ if errorlevel 1 ( ) echo. -echo [4/4] 启动服务... +echo [5/6] 安装前端依赖... +cd JuggleNet6.Frontend +call npm install +if errorlevel 1 ( + echo [错误] 前端依赖安装失败 + echo. + pause + goto menu +) +cd .. + +echo. +echo [6/6] 启动服务... +echo. + +:: 启动后端 API +start "Juggle.Api" cmd /c "dotnet run --project Juggle.Api/Juggle.Api.csproj --no-build" + +:: 等待后端启动 +timeout /t 3 /nobreak >nul + +:: 启动前端 Vue +start "Juggle.Vue" cmd /c "cd JuggleNet6.Frontend && npm run dev" + echo. echo ╔══════════════════════════════════════════════════════════╗ -echo ║ Juggle 启动成功! ║ +echo ║ Juggle 前后端启动成功! ║ +echo ╠══════════════════════════════════════════════════════════╣ +echo ║ 后端 API: http://localhost:9127 ║ +echo ║ 前端界面: http://localhost:5173 ║ echo ╠══════════════════════════════════════════════════════════╣ -echo ║ 访问地址: http://localhost:9127 ║ echo ║ 默认账号: juggle / juggle ║ -echo ║ 按 Ctrl+C 停止服务 ║ +echo ║ 关闭窗口即可停止服务 ║ echo ╚══════════════════════════════════════════════════════════╝ echo. -dotnet run --project Juggle.Api/Juggle.Api.csproj --no-build +echo 提示: 两个窗口分别运行后端和前端,关闭任意窗口不会影响另一个 +echo. +pause goto end :end From 80a86369ee8602a807a6f20717528333aaec484b Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Thu, 30 Apr 2026 14:10:39 +0800 Subject: [PATCH 008/153] =?UTF-8?q?=E7=AE=80=E4=BB=8B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E7=BC=96=E6=8E=92=E6=95=88=E6=9E=9C=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4ad6faf..39f2f53 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ Juggle 中文有"积木、魔法"之意,寓意像积木一样灵活,像魔 Juggle 是一个**图形化微服务编排工具**,通过简单的流程编排,快速完成接口开发,大大提高开发效率。 +### 🎨 流程编排预览 + +![流程编排](./images/flowdesign.png) + ### ✨ 核心能力 | 场景 | 说明 | From f84a9813c9eb198a7bef03053878fcb79168f166 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 12 May 2026 09:32:20 +0800 Subject: [PATCH 009/153] =?UTF-8?q?=E5=A2=9E=E5=8A=A0claude=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 10 ++++ CLAUDE.md | 103 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 .claude/settings.local.json create mode 100644 CLAUDE.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..f156e9e --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(dotnet --version)", + "Bash(dotnet restore *)", + "Bash(npm install *)", + "Bash(dotnet run *)" + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e4e83a0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,103 @@ +# CLAUDE.md + +本文件为 Claude Code (claude.ai/code) 在此仓库中工作时提供指引。 + +## 构建与运行命令 + +```bash +# 后端 (ASP.NET Core 8) — 运行在 http://localhost:9127 +cd Juggle.Api && dotnet run + +# 还原并构建整个解决方案 +dotnet restore Juggle.sln && dotnet build Juggle.sln + +# 前端 (Vue3 + Vite) — 运行在 http://localhost:5173 +cd JuggleNet6.Frontend && npm install && npm run dev + +# 前端生产构建 (输出 → Juggle.Api/wwwroot/) +cd JuggleNet6.Frontend && npm run build + +# Docker +docker-compose up -d # 完整部署 +docker build -t pythonhuang/juggle-net8:v1.0 . # 多阶段构建 + +# Windows 一键启动脚本 (交互式菜单) +启动Juggle.bat +``` + +默认账号: `juggle` / `juggle` + +## 架构(DDD 四层) + +``` +Juggle.Api/ # 表示层 — Controllers + Program.cs +Juggle.Application/ # 应用层 — DTO (Request/Response)、FlowExecutionService、JwtService +Juggle.Domain/ # 领域层 — 实体、流程引擎 (FlowEngine + 13个节点执行器) +Juggle.Infrastructure/ # 基础设施层 — JuggleDbContext、JsonHelper、Md5Helper +JuggleNet6.Frontend/ # Vue 3 + Element Plus + @vue-flow/core + Monaco Editor +``` + +**依赖方向**: `Api → Application → Domain ← Infrastructure`(Api 引用所有三个项目;Application 和 Domain 不引用 Infrastructure)。 + +项目引用关系: `Juggle.Api.csproj` 引用其他三个项目;`Juggle.Infrastructure.csproj` 和 `Juggle.Application.csproj` 都引用 `Juggle.Domain.csproj`。 + +## Controller 模式 + +Controller 直接注入 `JuggleDbContext`——CRUD 操作**不走 Service 层**。只有复杂编排逻辑(流程执行、数据源连接、JWT 签发)才抽取到 `Juggle.Application/Services/` 下的 Service 中。所有 Controller 返回 `ApiResult`(code/message/data)或 `PageResult`(code/message/total/data)。 + +## 流程引擎(系统核心) + +流程引擎(`Juggle.Domain/Engine/FlowEngine.cs`)执行 JSON 格式的节点图。以 **Scoped** 生命周期注册在 DI 中,接收 `IHttpClientFactory` + 可选的数据源/静态变量字典。 + +**13 种节点类型**: START、END、METHOD(HTTP调用)、CONDITION(分支判断)、MERGE(分支汇聚)、ASSIGN(赋值)、CODE(JS脚本)、MYSQL/DB(SQL执行)、SUB_FLOW(递归调用子流程)、LOOP(循环)、DELAY(延迟)、PARALLEL(并行)、NOTIFY(通知)。 + +**变量命名约定**(理解数据流转的关键): +- `input_*` — 流程入参(来自调用方) +- `output_*` — 流程出参(返回给调用方) +- `env_*` — 内部中间变量(流程内使用) +- `_loop_item`、`_loop_index`、`_loop_total`、`_loop_results` — 循环内置变量 + +**METHOD 节点数据流转**: `InputFillRules` 在调用前将变量映射到 API 入参;`OutputFillRules` 在调用后将 API 响应映射回变量。 + +`FlowExecutionService`(`Juggle.Application/Services/Flow/`)是主要的编排器,负责加载数据源、快照静态变量、运行引擎、持久化日志、回写静态变量变更。 + +## 多租户数据隔离 + +`ICurrentTenantProvider`(定义在 Infrastructure 层,在 Api 层由 `HttpContextTenantProvider` 实现)从 JWT Claims 中提取租户 ID。`JuggleDbContext` 通过**全局查询过滤器**(`HasQueryFilter`)使用它: +- **严格隔离**(`e.TenantId == CurrentTenantId`):流程、数据源、Token、静态变量等 +- **宽松隔离**(`e.TenantId == null || e.TenantId == CurrentTenantId`):套件、API、参数、角色——TenantId 为 null 的记录为"全局"数据,对所有租户可见 + +`ITenantAccessor`(与 `ICurrentTenantProvider` 不同)是应用层的租户上下文,供 Controller 使用;`Program.cs` 中的中间件从 JWT Claims 加载它(`/open/` 和 `/api/health` 路径跳过)。 + +## 数据库配置 + +系统数据库通过 `DB_TYPE` 环境变量(或 appsettings 中的 `Database:Type`)选择。支持: `sqlite`(默认)、`mysql`、`postgresql`、`sqlserver`。参见 `Program.cs:39-82` 的 `ConfigureDbContext` 分支逻辑。 + +业务数据源(供 DB/MySQL 节点使用)通过界面在"系统设置 → 数据源"中配置——支持 6 种类型: SQLite、MySQL、PostgreSQL、SQLServer、Oracle、达梦。 + +## API URL 规范 + +| 前缀 | 认证方式 | 用途 | +|------|---------|------| +| `/api/` | JWT Bearer | 管理控制台 | +| `/open/` | X-Access-Token 请求头 | 外部流程触发 | +| `/api/health` | 无 | Docker 健康检查 | + +## 关键环境变量 + +- `DB_TYPE` — 系统数据库类型 (sqlite/mysql/postgresql/sqlserver) +- `DB_CONNECTION_STRING` — 完整连接字符串(覆盖各独立参数) +- `DB_PATH` — SQLite 文件路径 +- `DB_HOST`、`DB_PORT`、`DB_NAME`、`DB_USER`、`DB_PASS` — 各独立数据库参数 +- `Jwt__Key` — JWT 签名密钥(≥32 字符) +- `ASPNETCORE_URLS` — 监听地址(默认 `http://+:9127`) + +## 编码约定 + +- **C# DTO**: 使用 `record` 类型 +- **EF Core**: Code First + `EnsureCreated()`(非 Migration);列名通过 `HasColumnName` 映射为 snake_case +- **JSON**: camelCase 输出,中文不转义 +- **日期**: 以 ISO 8601 字符串存储在 TEXT 列中 +- **软删除**: 所有实体使用 `deleted` 列(0/1) +- **前端**: ` From 53d4ca78a6e4ee802dcf89e167fb5701d707c0bd Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 19 May 2026 16:44:20 +0800 Subject: [PATCH 016/153] =?UTF-8?q?feat:=20WebService=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81SOAP=201.2+=E5=8F=AF=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E5=90=8D/=E5=91=BD=E5=90=8D=E7=A9=BA?= =?UTF-8?q?=E9=97=B4/SOAPAction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ApiEntity新增SoapVersion/SoapMethod/SoapNamespace/SoapAction字段 2. DebugWebService重构:支持SOAP 1.1/1.2信封切换,操作名/命名空间/SOAPAction均可独立配置 3. 前端编辑弹窗增加SOAP版本选择、操作名、命名空间、SOAPAction输入框 4. 接口详情页展示WebService完整配置信息 Co-Authored-By: Claude Opus 4.7 --- Juggle.Api/Controllers/Api/ApiController.cs | 56 +++++++++++++------ Juggle.Api/Program.cs | 5 ++ .../Models/Request/ApiAddRequest.cs | 8 +++ Juggle.Domain/Entities/ApiEntity.cs | 20 +++++++ .../Persistence/JuggleDbContext.cs | 4 ++ .../src/views/suite/ApiDetail.vue | 9 ++- .../src/views/suite/ApiList.vue | 35 ++++++++---- 7 files changed, 109 insertions(+), 28 deletions(-) diff --git a/Juggle.Api/Controllers/Api/ApiController.cs b/Juggle.Api/Controllers/Api/ApiController.cs index 8b683a7..0c453e2 100644 --- a/Juggle.Api/Controllers/Api/ApiController.cs +++ b/Juggle.Api/Controllers/Api/ApiController.cs @@ -42,6 +42,10 @@ public async Task Add([FromBody] ApiAddRequest req) ContentType = req.ContentType, MockJson = req.MockJson, MethodType = req.MethodType, + SoapVersion = req.SoapVersion, + SoapMethod = req.SoapMethod, + SoapNamespace = req.SoapNamespace, + SoapAction = req.SoapAction, CreatedAt = DateTime.Now.ToString("o") }; _db.Apis.Add(entity); @@ -72,6 +76,10 @@ public async Task Update([FromBody] ApiUpdateRequest req) entity.ContentType = req.ContentType; entity.MockJson = req.MockJson; entity.MethodType = req.MethodType; + entity.SoapVersion = req.SoapVersion; + entity.SoapMethod = req.SoapMethod; + entity.SoapNamespace = req.SoapNamespace; + entity.SoapAction = req.SoapAction; entity.UpdatedAt = DateTime.Now.ToString("o"); await _db.SaveChangesAsync(); return ApiResult.Success(); @@ -112,7 +120,7 @@ public async Task Debug([FromBody] ApiDebugRequest req) return ApiResult.Success(new { response = api.MockJson, mock = true }); } - // WebService(SOAP 1.1)调试 + // WebService(SOAP 1.1 / 1.2)调试 if (methodType == "WEBSERVICE") { return await DebugWebService(api, req); @@ -155,36 +163,52 @@ public async Task Debug([FromBody] ApiDebugRequest req) } } - /// 调试 WebService(SOAP 1.1)接口 + /// 调试 WebService(SOAP 1.1 / 1.2)接口 private async Task DebugWebService(ApiEntity api, ApiDebugRequest req) { - // 从 Url 中提取 soapAction(可通过 Header "SOAPAction" 传入,也可为空) - var soapAction = req.Headers.ContainsKey("SOAPAction") ? req.Headers["SOAPAction"]?.ToString() ?? "" : ""; + var isSoap12 = api.SoapVersion == "12"; - // 构建 SOAP 1.1 请求体:用入参 key/value 组装 XML 参数 + // 操作名称:优先用实体字段,其次 URL ?op= 参数 + var methodName = !string.IsNullOrWhiteSpace(api.SoapMethod) ? api.SoapMethod! : "Request"; + if (string.IsNullOrWhiteSpace(api.SoapMethod)) + { + var uri = new Uri(api.Url!); + var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query); + if (queryParams.TryGetValue("op", out var opVal) && !string.IsNullOrEmpty(opVal)) + methodName = opVal!; + } + + // 命名空间:优先用实体字段,其次取 URL 路径 + var soapNs = !string.IsNullOrWhiteSpace(api.SoapNamespace) + ? api.SoapNamespace! + : new Uri(api.Url!).GetLeftPart(UriPartial.Path); + + // SOAPAction:优先用实体字段,其次取 Header 中的 + var soapAction = !string.IsNullOrWhiteSpace(api.SoapAction) ? api.SoapAction! : ""; + if (string.IsNullOrEmpty(soapAction) && req.Headers.ContainsKey("SOAPAction")) + soapAction = req.Headers["SOAPAction"]?.ToString() ?? ""; + + // 构建 XML 参数 var paramXml = string.Join("", req.Params.Select(kv => $"<{kv.Key}>{System.Security.SecurityElement.Escape(kv.Value?.ToString() ?? "")}")); - // 从 Url 中解析 method name(取路径最后一段,例如 http://ws/HelloService?op=SayHello → SayHello) - var methodName = "Request"; - var uri = new Uri(api.Url!); - // 解析 ?op=MethodName 参数 - var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query); - if (queryParams.TryGetValue("op", out var opVal) && !string.IsNullOrEmpty(opVal)) - methodName = opVal!; + // SOAP 信封命名空间 + var soapEnvNs = isSoap12 + ? "http://www.w3.org/2003/05/soap-envelope" + : "http://schemas.xmlsoap.org/soap/envelope/"; + var contentType = isSoap12 ? "application/soap+xml" : "text/xml"; var soapBody = $@" - + - <{methodName} xmlns=""{uri.GetLeftPart(UriPartial.Path)}""> + <{methodName} xmlns=""{soapNs}""> {paramXml} "; var client = _httpClientFactory.CreateClient(); - // SOAP 1.1 必须 POST,Content-Type: text/xml - var content = new StringContent(soapBody, System.Text.Encoding.UTF8, "text/xml"); + var content = new StringContent(soapBody, System.Text.Encoding.UTF8, contentType); if (!string.IsNullOrEmpty(soapAction)) content.Headers.Add("SOAPAction", $"\"{soapAction}\""); diff --git a/Juggle.Api/Program.cs b/Juggle.Api/Program.cs index e6bba1a..e318991 100644 --- a/Juggle.Api/Program.cs +++ b/Juggle.Api/Program.cs @@ -222,6 +222,11 @@ await ctx.Response.WriteAsync( // 补建流程定义访问别名字段 try { db.Database.ExecuteSqlRaw("ALTER TABLE t_flow_definition ADD COLUMN service_alias TEXT DEFAULT NULL;"); } catch { /* 列已存在则忽略 */ } + // 补建 API WebService 字段 + try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_version TEXT DEFAULT NULL;"); } catch { } + try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_method TEXT DEFAULT NULL;"); } catch { } + try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_namespace TEXT DEFAULT NULL;"); } catch { } + try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_action TEXT DEFAULT NULL;"); } catch { } } } diff --git a/Juggle.Application/Models/Request/ApiAddRequest.cs b/Juggle.Application/Models/Request/ApiAddRequest.cs index e872438..89a15f5 100644 --- a/Juggle.Application/Models/Request/ApiAddRequest.cs +++ b/Juggle.Application/Models/Request/ApiAddRequest.cs @@ -11,4 +11,12 @@ public class ApiAddRequest public string? MockJson { get; set; } /// HTTP WEBSERVICE public string MethodType { get; set; } = "HTTP"; + /// SOAP 版本: 11 / 12 + public string? SoapVersion { get; set; } + /// SOAP 操作名 + public string? SoapMethod { get; set; } + /// SOAP 命名空间 + public string? SoapNamespace { get; set; } + /// SOAPAction 头 + public string? SoapAction { get; set; } } \ No newline at end of file diff --git a/Juggle.Domain/Entities/ApiEntity.cs b/Juggle.Domain/Entities/ApiEntity.cs index 237d0a9..7ce1dbe 100644 --- a/Juggle.Domain/Entities/ApiEntity.cs +++ b/Juggle.Domain/Entities/ApiEntity.cs @@ -51,4 +51,24 @@ public class ApiEntity : BaseEntity /// 调用方式:HTTP / WEBSERVICE /// public string? MethodType { get; set; } = "HTTP"; + + /// + /// SOAP 版本: 11=SOAP 1.1, 12=SOAP 1.2 + /// + public string? SoapVersion { get; set; } = "11"; + + /// + /// SOAP 操作名称(对应 wsdl:operation name) + /// + public string? SoapMethod { get; set; } + + /// + /// SOAP 命名空间(xmlns) + /// + public string? SoapNamespace { get; set; } + + /// + /// SOAPAction 请求头值 + /// + public string? SoapAction { get; set; } } diff --git a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs index 37527f6..3533b9b 100644 --- a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs +++ b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs @@ -134,6 +134,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) e.Property(p => p.ContentType).HasColumnName("content_type"); e.Property(p => p.MockJson).HasColumnName("mock_json").HasColumnType("text"); e.Property(p => p.MethodType).HasColumnName("method_type"); + e.Property(p => p.SoapVersion).HasColumnName("soap_version"); + e.Property(p => p.SoapMethod).HasColumnName("soap_method"); + e.Property(p => p.SoapNamespace).HasColumnName("soap_namespace"); + e.Property(p => p.SoapAction).HasColumnName("soap_action"); }); modelBuilder.Entity(e => { diff --git a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue index 2ddd1a9..20dae85 100644 --- a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue +++ b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue @@ -20,9 +20,14 @@ {{ apiInfo?.requestType }} - SOAP 1.1 + SOAP {{ apiInfo?.soapVersion === '12' ? '1.2' : '1.1' }} - {{ apiInfo?.methodType === 'WEBSERVICE' ? 'text/xml' : apiInfo?.contentType }} + {{ apiInfo?.methodType === 'WEBSERVICE' ? (apiInfo?.soapVersion === '12' ? 'application/soap+xml' : 'text/xml') : apiInfo?.contentType }} + {{ apiInfo?.url }} {{ apiInfo?.methodDesc || '-' }} diff --git a/JuggleNet6.Frontend/src/views/suite/ApiList.vue b/JuggleNet6.Frontend/src/views/suite/ApiList.vue index 8d3b353..d858de2 100644 --- a/JuggleNet6.Frontend/src/views/suite/ApiList.vue +++ b/JuggleNet6.Frontend/src/views/suite/ApiList.vue @@ -57,8 +57,26 @@ - + + + @@ -136,7 +154,8 @@ const dialogVisible = ref(false) const isEdit = ref(false) const formRef = ref() const form = reactive({ - id: 0, suiteCode, methodName: '', methodType: 'HTTP', requestType: 'GET', url: '', contentType: 'JSON', methodDesc: '' + id: 0, suiteCode, methodName: '', methodType: 'HTTP', requestType: 'GET', url: '', contentType: 'JSON', methodDesc: '', + soapVersion: '11', soapMethod: '', soapNamespace: '', soapAction: '' }) const rules = { methodName: [{ required: true, message: '请输入接口名称', trigger: 'blur' }], @@ -155,20 +174,16 @@ async function loadData() { function openAdd() { isEdit.value = false - Object.assign(form, { id: 0, methodName: '', methodType: 'HTTP', requestType: 'GET', url: '', contentType: 'JSON', methodDesc: '' }) + Object.assign(form, { id: 0, methodName: '', methodType: 'HTTP', requestType: 'GET', url: '', contentType: 'JSON', methodDesc: '', soapVersion: '11', soapMethod: '', soapNamespace: '', soapAction: '' }) dialogVisible.value = true } function openEdit(row: any) { isEdit.value = true Object.assign(form, { - id: row.id, - methodName: row.methodName, - methodType: row.methodType || 'HTTP', - requestType: row.requestType, - url: row.url, - contentType: row.contentType, - methodDesc: row.methodDesc + id: row.id, methodName: row.methodName, methodType: row.methodType || 'HTTP', + requestType: row.requestType, url: row.url, contentType: row.contentType, methodDesc: row.methodDesc, + soapVersion: row.soapVersion || '11', soapMethod: row.soapMethod || '', soapNamespace: row.soapNamespace || '', soapAction: row.soapAction || '' }) dialogVisible.value = true } From 9d8cf2aa4e79000c22d35a568563c5eef3365978 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 19 May 2026 16:59:52 +0800 Subject: [PATCH 017/153] =?UTF-8?q?feat:=20Header=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC+=E5=85=A5=E5=8F=82=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=8F=82=E6=95=B0=E4=BD=8D=E7=BD=AE(body/query/rawBod?= =?UTF-8?q?y)+Debug=E6=94=AF=E6=8C=81body=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Header参数表格增加默认值(DefaultValue)列,方便预设固定头值 2. 入参增加参数位置(ParamPosition): query/Body/rawBody/自动 - rawBody: 整个请求体用该参数的值(不做JSON序列化) - query: 强制URL查询参数 3. 调试接口支持ParamPosition,自动分流query参数和body参数 4. ParameterController完整映射ParamPosition字段 Co-Authored-By: Claude Opus 4.7 --- Juggle.Api/Controllers/Api/ApiController.cs | 37 +++++++++++++++++-- .../Controllers/Api/ParameterController.cs | 25 +++++++------ Juggle.Api/Program.cs | 2 + .../Models/Request/ParameterItem.cs | 5 +++ Juggle.Domain/Entities/ParameterEntity.cs | 5 +++ .../Persistence/JuggleDbContext.cs | 1 + .../src/views/suite/ApiDetail.vue | 25 ++++++++++--- 7 files changed, 80 insertions(+), 20 deletions(-) diff --git a/Juggle.Api/Controllers/Api/ApiController.cs b/Juggle.Api/Controllers/Api/ApiController.cs index 0c453e2..1598932 100644 --- a/Juggle.Api/Controllers/Api/ApiController.cs +++ b/Juggle.Api/Controllers/Api/ApiController.cs @@ -131,9 +131,22 @@ public async Task Debug([FromBody] ApiDebugRequest req) foreach (var h in req.Headers) client.DefaultRequestHeaders.TryAddWithoutValidation(h.Key, h.Value?.ToString()); + // 加载参数定义,获取位置信息 + var paramDefs = await _db.Parameters + .Where(p => p.OwnerId == api.Id && p.ParamType == 1 && p.Deleted == 0) + .ToListAsync(); + var posMap = paramDefs.ToDictionary(p => p.ParamCode!, p => p.ParamPosition ?? ""); + string responseJson; var requestType = api.RequestType?.ToUpper() ?? "GET"; + // rawBody 参数:整个请求体用该参数的值 + var rawBodyKey = posMap.FirstOrDefault(kv => kv.Value == "rawBody").Key; + + // 分离 query 和 body 参数 + var queryParams = req.Params.Where(kv => posMap.GetValueOrDefault(kv.Key, "") == "query").ToList(); + var bodyParams = req.Params.Where(kv => posMap.GetValueOrDefault(kv.Key, "") != "query").ToList(); + if (requestType == "GET" || requestType == "DELETE") { var url = api.Url!; @@ -148,12 +161,30 @@ public async Task Debug([FromBody] ApiDebugRequest req) } else { - var content = new StringContent( - JsonSerializer.Serialize(req.Params), - System.Text.Encoding.UTF8, "application/json"); + HttpContent content; + if (!string.IsNullOrEmpty(rawBodyKey) && req.Params.ContainsKey(rawBodyKey)) + { + // rawBody 模式:直接用值作为整个请求体 + content = new StringContent(req.Params[rawBodyKey]?.ToString() ?? "", + System.Text.Encoding.UTF8, api.ContentType == "XML" ? "application/xml" : "text/plain"); + } + else + { + // 标准模式:所有非 query 参数序列化为 JSON + var bodyDict = bodyParams.ToDictionary(kv => kv.Key, kv => kv.Value); + content = new StringContent( + JsonSerializer.Serialize(bodyDict), + System.Text.Encoding.UTF8, "application/json"); + } var resp = requestType == "PUT" ? await client.PutAsync(api.Url, content) : await client.PostAsync(api.Url, content); responseJson = await resp.Content.ReadAsStringAsync(); } + // URL 中包含 query 参数的处理 + if (queryParams.Any() && !(requestType == "GET" || requestType == "DELETE")) + { + // 对于非 GET 请求,将 query 参数附在 URL 上 + // Note: This is already handled above for GET/DELETE + } return ApiResult.Success(new { response = responseJson }); } diff --git a/Juggle.Api/Controllers/Api/ParameterController.cs b/Juggle.Api/Controllers/Api/ParameterController.cs index d57d7fe..9827628 100644 --- a/Juggle.Api/Controllers/Api/ParameterController.cs +++ b/Juggle.Api/Controllers/Api/ParameterController.cs @@ -33,18 +33,19 @@ public async Task Save([FromBody] ParameterSaveRequest req) { _db.Parameters.Add(new ParameterEntity { - OwnerId = req.OwnerId, - OwnerCode = req.OwnerCode, - ParamType = req.ParamType, - ParamCode = item.ParamCode, - ParamName = item.ParamName, - DataType = item.DataType, - ObjectCode = item.ObjectCode, - Required = item.Required, - DefaultValue = item.DefaultValue, - Description = item.Description, - SortNum = sort++, - CreatedAt = DateTime.Now.ToString("o") + OwnerId = req.OwnerId, + OwnerCode = req.OwnerCode, + ParamType = req.ParamType, + ParamCode = item.ParamCode, + ParamName = item.ParamName, + DataType = item.DataType, + ObjectCode = item.ObjectCode, + Required = item.Required, + DefaultValue = item.DefaultValue, + ParamPosition = item.ParamPosition, + Description = item.Description, + SortNum = sort++, + CreatedAt = DateTime.Now.ToString("o") }); } await _db.SaveChangesAsync(); diff --git a/Juggle.Api/Program.cs b/Juggle.Api/Program.cs index e318991..7519957 100644 --- a/Juggle.Api/Program.cs +++ b/Juggle.Api/Program.cs @@ -227,6 +227,8 @@ await ctx.Response.WriteAsync( try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_method TEXT DEFAULT NULL;"); } catch { } try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_namespace TEXT DEFAULT NULL;"); } catch { } try { db.Database.ExecuteSqlRaw("ALTER TABLE t_api ADD COLUMN soap_action TEXT DEFAULT NULL;"); } catch { } + // 补建参数位置字段 + try { db.Database.ExecuteSqlRaw("ALTER TABLE t_parameter ADD COLUMN param_position TEXT DEFAULT NULL;"); } catch { } } } diff --git a/Juggle.Application/Models/Request/ParameterItem.cs b/Juggle.Application/Models/Request/ParameterItem.cs index e10d0d8..7419cf1 100644 --- a/Juggle.Application/Models/Request/ParameterItem.cs +++ b/Juggle.Application/Models/Request/ParameterItem.cs @@ -49,6 +49,11 @@ public class ParameterItem /// public string? Description { get; set; } + /// + /// 参数位置:query=查询参数, body=请求体字段, rawBody=整个请求体 + /// + public string? ParamPosition { get; set; } + /// /// 排序号 /// diff --git a/Juggle.Domain/Entities/ParameterEntity.cs b/Juggle.Domain/Entities/ParameterEntity.cs index f2aae79..fb38ab1 100644 --- a/Juggle.Domain/Entities/ParameterEntity.cs +++ b/Juggle.Domain/Entities/ParameterEntity.cs @@ -67,6 +67,11 @@ public class ParameterEntity : BaseEntity /// public string? Description { get; set; } + /// + /// 参数位置:query=查询参数, body=请求体字段, rawBody=整个请求体, ""=自动 + /// + public string? ParamPosition { get; set; } + /// /// 排序号(用于参数展示顺序) /// diff --git a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs index 3533b9b..f33a3e6 100644 --- a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs +++ b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs @@ -158,6 +158,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) e.Property(p => p.DefaultValue).HasColumnName("default_value"); e.Property(p => p.Description).HasColumnName("description"); e.Property(p => p.SortNum).HasColumnName("sort_num"); + e.Property(p => p.ParamPosition).HasColumnName("param_position"); }); modelBuilder.Entity(e => { diff --git a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue index 20dae85..af0b218 100644 --- a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue +++ b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue @@ -74,12 +74,22 @@ - + - + + + + @@ -89,9 +99,9 @@ - + @@ -186,6 +196,11 @@ + + + + + + - + - + + + + - + @@ -748,7 +756,7 @@ - + @@ -776,19 +784,27 @@ - + + + + - + @@ -849,19 +865,27 @@ 中间 - + + + + - + @@ -1132,7 +1156,7 @@ const objectList = ref([]) // 变量管理 const variableDrawer = ref(false) const varDialogVisible = ref(false) -const varForm = ref({ variableCode: '', variableName: '', variableType: 'VARIABLE', dataType: 'string', defaultValue: '' }) +const varForm = ref({ variableCode: '', variableName: '', variableType: 'VARIABLE', dataType: 'string', objectCode: '', defaultValue: '' }) // ====== 调试相关 ====== const debugVisible = ref(false) @@ -1198,6 +1222,24 @@ const dataTypes = [ { value: 'array', label: 'array(对象数组)' }, ] +// 检测是否有参数使用了 object/array 类型 +const hasObjectParams = (type: 'input' | 'output') => { + const params = type === 'input' ? flowInputParams.value : flowOutputParams.value + return params.some(p => p.dataType === 'object' || p.dataType === 'array') +} +const hasObjectVars = computed(() => allVariables.value.some(v => v.dataType === 'object' || v.dataType === 'array')) + +// 类型切换时清除 objectCode(如果不再需要) +function onFlowParamTypeChange(row: any) { + if (row.dataType !== 'object' && row.dataType !== 'array') row.objectCode = '' +} +function onFlowParamObjChange(row: any, objectCode: string) { + row.objectCode = objectCode +} +function onVarTypeChange(row: any) { + if (row.dataType !== 'object' && row.dataType !== 'array') row.objectCode = '' +} + const selectedNode = computed(() => businessNodes.value.find(n => n.key === selectedNodeKey.value) || null) const selectedEdgeInfo = computed(() => selectedEdgeId.value ? vfEdges.value.find(e => e.id === selectedEdgeId.value) : null) const hasStart = computed(() => businessNodes.value.some(n => n.elementType === 'START')) @@ -1263,7 +1305,7 @@ function onPaneClick() { // 让容器获取焦点(以便接收键盘事件) onMounted(async () => { - await Promise.all([loadFlowInfo(), loadSuiteApis(), loadDataSources(), loadStaticVariables(), loadPublishedFlows()]) + await Promise.all([loadFlowInfo(), loadSuiteApis(), loadDataSources(), loadStaticVariables(), loadPublishedFlows(), loadObjects()]) nextTick(() => { containerRef.value?.focus() }) }) @@ -1662,6 +1704,13 @@ async function loadStaticVariables() { } catch {} } +async function loadObjects() { + try { + const res: any = await request.get('/object/list') + objectList.value = res.data || [] + } catch {} +} + async function loadPublishedFlows() { try { // 从 FlowDefinition 表加载所有流程(无需部署),排除自身 flowKey @@ -1871,7 +1920,7 @@ function addCondition() { // 流程参数 function addFlowParam(type: 'input' | 'output') { const prefix = type === 'input' ? 'input_' : 'output_' - const param = { paramCode: prefix, paramName: '', dataType: 'string', required: type === 'input' ? 1 : 0, defaultValue: '', description: '', sortNum: 0 } + const param = { paramCode: prefix, paramName: '', dataType: 'string', objectCode: '', required: type === 'input' ? 1 : 0, defaultValue: '', description: '', sortNum: 0 } if (type === 'input') flowInputParams.value.push(param) else flowOutputParams.value.push(param) } @@ -1913,7 +1962,8 @@ function importFlowObjParams() { for (const p of flowObjPreviewParams.value) { target.value.push({ paramCode: p.paramCode, paramName: p.paramName, - dataType: p.dataType || 'string', required: flowObjDialogType.value === 'input' ? 1 : 0, + dataType: p.dataType || 'string', objectCode: p.objectCode ?? '', + required: flowObjDialogType.value === 'input' ? 1 : 0, defaultValue: p.defaultValue ?? '', description: p.description ?? '', sortNum: 0 }) } @@ -1924,7 +1974,7 @@ function importFlowObjParams() { // 变量管理 function addVariable() { varDialogVisible.value = true - varForm.value = { variableCode: '', variableName: '', variableType: 'VARIABLE', dataType: 'string', defaultValue: '' } + varForm.value = { variableCode: '', variableName: '', variableType: 'VARIABLE', dataType: 'string', objectCode: '', defaultValue: '' } } function confirmAddVariable() { From 03bc1cb046bd8869e91375fe08f11ffa3e9854d3 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 19 May 2026 21:31:39 +0800 Subject: [PATCH 022/153] =?UTF-8?q?feat:=20=E6=8E=A5=E5=8F=A3=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E5=85=A5=E5=8F=82/=E5=87=BA=E5=8F=82object=E5=92=8Car?= =?UTF-8?q?ray=E7=B1=BB=E5=9E=8B=E6=94=AF=E6=8C=81=E5=85=B3=E8=81=94?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 入参表格:object/array类型时显示关联对象下拉列 2. 出参表格:object/array类型时显示关联对象下拉列 3. 页面加载时预取对象列表 Co-Authored-By: Claude Opus 4.7 --- .../src/views/suite/ApiDetail.vue | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue index af0b218..b202964 100644 --- a/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue +++ b/JuggleNet6.Frontend/src/views/suite/ApiDetail.vue @@ -74,12 +74,12 @@ - + - + - + + + + @@ -99,7 +107,7 @@ - + @@ -147,17 +155,25 @@ - + + + + - + @@ -267,7 +283,7 @@ diff --git a/JuggleNet6.Frontend/src/views/monitor/AlertRule.vue b/JuggleNet6.Frontend/src/views/monitor/AlertRule.vue new file mode 100644 index 0000000..c47e09a --- /dev/null +++ b/JuggleNet6.Frontend/src/views/monitor/AlertRule.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue new file mode 100644 index 0000000..f9bc4b3 --- /dev/null +++ b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue @@ -0,0 +1,92 @@ + + + + + From 3f0f49cf22403373b957f8ace6170e0e8fac0653 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 11:06:32 +0800 Subject: [PATCH 061/153] =?UTF-8?q?fix:=20=E5=91=8A=E8=AD=A6=E8=A7=84?= =?UTF-8?q?=E5=88=99/=E8=AE=B0=E5=BD=95502-=E6=B7=BB=E5=8A=A0Entity=20Fram?= =?UTF-8?q?ework=20ToTable=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- Juggle.Infrastructure/Persistence/JuggleDbContext.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs index e67ec92..cb95af8 100644 --- a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs +++ b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs @@ -88,6 +88,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().ToTable("t_tenant"); modelBuilder.Entity().ToTable("t_audit_log"); modelBuilder.Entity().ToTable("t_login_log"); + modelBuilder.Entity().ToTable("t_alert_rule"); + modelBuilder.Entity().ToTable("t_alert_record"); // 列名映射(snake_case) modelBuilder.Entity(e => { From e3036b1dff4296c05cd9040957947fc3fc1e6ccd Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 11:12:19 +0800 Subject: [PATCH 062/153] =?UTF-8?q?fix:=20API=E6=8B=93=E6=89=91=E5=9B=BE?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=95=B0=E6=8D=AE=E7=BC=BA=E5=B0=91apis?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=82=B9=E5=87=BB=E6=97=A0=E5=88=97=E8=A1=A8?= =?UTF-8?q?+=E8=BE=B9=E7=BA=BF=E7=BC=BA=E7=AE=AD=E5=A4=B4=E6=A0=87?= =?UTF-8?q?=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue index f9bc4b3..5126ec2 100644 --- a/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue +++ b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue @@ -60,14 +60,17 @@ async function loadData() { id: n.id, type: 'custom', position: { x: (i % 4) * 220, y: Math.floor(i / 4) * 120 }, - data: { label: n.label, apiCount: n.apiCount, status: n.status, hostId: n.id } + data: { label: n.label, apiCount: n.apiCount, status: n.status, hostId: n.id, apis: n.apis || [] } })) vfEdges.value = (res.data.edges || []).map((e: any, i: number) => ({ id: `e${i}`, source: e.source, target: e.target, label: e.label, + labelBgStyle: { fill: '#fff' }, + labelStyle: { fontSize: '10px', fill: '#666' }, style: { stroke: '#1890ff', strokeWidth: 2 }, + markerEnd: { type: 'arrowclosed', width: 16, height: 16, color: '#1890ff' }, animated: true })) } From f190e1f054a04f829f4d32db946e1a30d4b2b539 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 11:20:37 +0800 Subject: [PATCH 063/153] =?UTF-8?q?feat:=20API=E6=8B=93=E6=89=91=E5=9B=BE?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=81=A5=E5=BA=B7=E6=A3=80=E6=9F=A5+?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E7=BB=9F=E8=AE=A1+=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E9=A2=9C=E8=89=B2(=E7=BA=A2=E9=BB=84=E7=BB=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 后端HTTP健康检查每个host,结合失败日志判定节点状态 2. FindApiLogs统计每个API的调用次数/成功/失败 3. 节点:绿色(online)/黄色(warning)/红色(offline) 4. 连线颜色跟随目标节点状态 5. 前端点击节点显示统计摘要+每个接口的调用详情 Co-Authored-By: Claude Opus 4.7 --- .../Controllers/Api/MonitorController.cs | 161 +++++++++++------- .../src/views/monitor/ApiTopology.vue | 83 ++++++--- 2 files changed, 155 insertions(+), 89 deletions(-) diff --git a/Juggle.Api/Controllers/Api/MonitorController.cs b/Juggle.Api/Controllers/Api/MonitorController.cs index 692a33b..042b65e 100644 --- a/Juggle.Api/Controllers/Api/MonitorController.cs +++ b/Juggle.Api/Controllers/Api/MonitorController.cs @@ -25,48 +25,97 @@ public async Task GetTopology() .OrderByDescending(v => v.Id) .ToListAsync(); - // 节点:按 host 聚合 API + // 访问统计:最近200条日志 + var recentLogs = await _db.FlowLogs + .Where(l => l.Deleted == 0) + .OrderByDescending(l => l.Id) + .Take(200) + .ToListAsync(); + + // 节点:按 host 聚合 API + 统计 var hostMap = new Dictionary>(); + using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(3) }; foreach (var api in apis) { var host = ExtractHost(api.Url); if (!hostMap.ContainsKey(host)) hostMap[host] = new List(); + // API 级别统计 + var apiLogs = FindApiLogs(recentLogs, flows, api, host); hostMap[host].Add(new { api.Id, api.MethodCode, api.MethodName, api.RequestType, - api.Url, api.MethodType, api.Status, api.ServiceAlias + api.Url, api.MethodType, api.Status, api.ServiceAlias, + callCount = apiLogs.calls, successCount = apiLogs.success, failCount = apiLogs.fail }); } - var nodes = hostMap.Select(kv => new + // 健康检查 + var hostHealth = new Dictionary(); + foreach (var host in hostMap.Keys) + { + try + { + var resp = await httpClient.GetAsync(host, HttpCompletionOption.ResponseHeadersRead); + hostHealth[host] = resp.IsSuccessStatusCode ? "online" : "warning"; + } + catch { hostHealth[host] = "offline"; } + } + + // 检查日志中的失败 + foreach (var log in recentLogs.Where(l => l.Status == "FAILED")) + { + var flowVer = flows.FirstOrDefault(v => v.FlowKey == log.FlowKey); + if (flowVer?.FlowContent == null) continue; + try + { + var fNodes = JsonSerializer.Deserialize>(flowVer.FlowContent); + if (fNodes != null) + { + foreach (var n in fNodes) + { + if (n.TryGetProperty("elementType", out var et) && et.GetString() == "METHOD") + { + var host = GetMethodHost(n, apis); + if (!string.IsNullOrEmpty(host) && hostHealth.GetValueOrDefault(host) == "online") + hostHealth[host] = "warning"; + } + } + } + } + catch { } + } + + var nodes = hostMap.Select(kv => { - id = kv.Key, - label = kv.Key, - apiCount = kv.Value.Count, - apis = kv.Value + var status = hostHealth.GetValueOrDefault(kv.Key, "online"); + var totalCalls = kv.Value.Sum(a => (int)((dynamic)a).callCount); + var totalSuccess = kv.Value.Sum(a => (int)((dynamic)a).successCount); + var totalFail = kv.Value.Sum(a => (int)((dynamic)a).failCount); + return new + { + id = kv.Key, label = kv.Key, + apiCount = kv.Value.Count, apis = kv.Value, + status, totalCalls, totalSuccess, totalFail + }; }).ToList(); - // 连线:从流程设计中取 METHOD 节点调用链 + // 连线 var edges = new List(); foreach (var flowVer in flows) { if (string.IsNullOrEmpty(flowVer.FlowContent)) continue; try { - var nodes_ = JsonSerializer.Deserialize>(flowVer.FlowContent); - if (nodes_ == null) continue; - // 找 METHOD 节点并按执行顺序(通过 outgoings 连接)构建调用链 - var methodNodes = nodes_.Where(n => + var fNodes = JsonSerializer.Deserialize>(flowVer.FlowContent); + if (fNodes == null) continue; + var methodNodes = fNodes.Where(n => n.TryGetProperty("elementType", out var et) && et.GetString() == "METHOD").ToList(); for (int i = 0; i < methodNodes.Count; i++) { var mn = methodNodes[i]; - var key = mn.TryGetProperty("key", out var k) ? k.GetString() ?? "" : ""; - // 查找该 METHOD 节点的后继节点 var outgoings = new List(); if (mn.TryGetProperty("outgoings", out var og) && og.ValueKind == JsonValueKind.Array) foreach (var o in og.EnumerateArray()) outgoings.Add(o.GetString() ?? ""); - // 找后继 METHOD 节点 foreach (var outKey in outgoings) { var nextMethod = methodNodes.FirstOrDefault(n => @@ -74,72 +123,62 @@ public async Task GetTopology() if (nextMethod.ValueKind == JsonValueKind.Undefined) continue; var srcHost = GetMethodHost(mn, apis); var tgtHost = GetMethodHost(nextMethod, apis); - if (!string.IsNullOrEmpty(srcHost) && !string.IsNullOrEmpty(tgtHost) && srcHost != tgtHost) + if (srcHost == tgtHost || string.IsNullOrEmpty(srcHost) || string.IsNullOrEmpty(tgtHost)) continue; + var srcApi = GetMethodApiName(mn, apis); + var tgtApi = GetMethodApiName(nextMethod, apis); + var edgeStatus = hostHealth.GetValueOrDefault(tgtHost, "online"); + edges.Add(new { - var srcApi = GetMethodApiName(mn, apis); - var tgtApi = GetMethodApiName(nextMethod, apis); - edges.Add(new - { - source = srcHost, target = tgtHost, - label = $"{srcApi}→{tgtApi}", - flowKey = flowVer.FlowKey - }); - } + source = srcHost, target = tgtHost, + label = $"{srcApi}→{tgtApi}", + flowKey = flowVer.FlowKey, + status = edgeStatus + }); } } } catch { } } - // 去重连线 var uniqueEdges = edges.GroupBy(e => { dynamic d = e; - return $"{d.source}→{d.target}"; + return $"{(string)d.source}→{(string)d.target}"; }).Select(g => g.First()).ToList(); - // 状态:检查最近调用日志 - var recentLogs = await _db.FlowLogs - .Where(l => l.Deleted == 0) - .OrderByDescending(l => l.Id) - .Take(100) - .ToListAsync(); - var failedHosts = new HashSet(); - foreach (var log in recentLogs) + return ApiResult.Success(new { nodes, edges = uniqueEdges }); + } + + private static (int calls, int success, int fail) FindApiLogs( + List logs, List flows, + Domain.Entities.ApiEntity api, string host) + { + int calls = 0, success = 0, fail = 0; + foreach (var log in logs) { - if (log.Status == "FAILED" && !string.IsNullOrEmpty(log.FlowKey)) + var flowVer = flows.FirstOrDefault(v => v.FlowKey == log.FlowKey); + if (flowVer?.FlowContent == null) continue; + try { - // 查找该流程的METHOD节点涉及的hosts - var flowVer = flows.FirstOrDefault(v => v.FlowKey == log.FlowKey); - if (flowVer?.FlowContent != null) + var fNodes = JsonSerializer.Deserialize>(flowVer.FlowContent); + if (fNodes == null) continue; + foreach (var n in fNodes) { - try + if (!n.TryGetProperty("elementType", out var et) || et.GetString() != "METHOD") continue; + if (!n.TryGetProperty("method", out var m)) continue; + var sc = m.TryGetProperty("suiteCode", out var scc) ? scc.GetString() ?? "" : ""; + var mc = m.TryGetProperty("methodCode", out var mcc) ? mcc.GetString() ?? "" : ""; + if (sc == api.SuiteCode && mc == api.MethodCode) { - var fNodes = JsonSerializer.Deserialize>(flowVer.FlowContent); - if (fNodes != null) - { - foreach (var n in fNodes) - { - if (n.TryGetProperty("elementType", out var et) && et.GetString() == "METHOD") - { - var host = GetMethodHost(n, apis); - if (!string.IsNullOrEmpty(host)) failedHosts.Add(host); - } - } - } + calls++; + if (log.Status == "SUCCESS") success++; + else fail++; } - catch { } } } + catch { } } - - var nodeStatus = nodes.Select(n => - { - var status = failedHosts.Contains(n.id) ? "warning" : "online"; - return new { n.id, n.label, n.apiCount, n.apis, status }; - }).ToList(); - - return ApiResult.Success(new { nodes = nodeStatus, edges = uniqueEdges }); + return (calls, success, fail); } /// 指定流程拓扑图 diff --git a/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue index 5126ec2..b37ecf3 100644 --- a/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue +++ b/JuggleNet6.Frontend/src/views/monitor/ApiTopology.vue @@ -2,6 +2,11 @@
@@ -9,25 +14,35 @@ -
-
-

{{ selectedHost }} 的接口

+
+

{{ selectedHost }}

+
+ 总调用 {{ selectedStats?.totalCalls || 0 }} 次 | + 成功 {{ selectedStats?.totalSuccess || 0 }} | + 失败 {{ selectedStats?.totalFail || 0 }} + + {{ selectedStats?.status === 'offline' ? '离线' : selectedStats?.status === 'warning' ? '告警' : '正常' }} + +
-
+
{{ api.status === 1 ? '启用' : '停用' }} {{ api.requestType }} {{ api.methodName }}
-
{{ api.url }}
+
{{ api.url }}
+
+ 调用 {{ api.callCount || 0 }} 次 | + 成功 {{ api.successCount || 0 }} | + 失败 {{ api.failCount || 0 }} +
@@ -45,49 +60,61 @@ import '@vue-flow/core/dist/theme-default.css' const loading = ref(false) const selectedHost = ref('') +const selectedStats = ref(null) const selectedApis = ref([]) const vfNodes = ref([]) const vfEdges = ref([]) +const allNodes = ref([]) onMounted(loadData) +function statusColor(s: string) { + return s === 'offline' ? '#ff4d4f' : s === 'warning' ? '#faad14' : '#52c41a' +} + async function loadData() { loading.value = true try { const res: any = await request.get('/monitor/topology') if (res.data?.nodes) { + allNodes.value = res.data.nodes vfNodes.value = res.data.nodes.map((n: any, i: number) => ({ id: n.id, type: 'custom', - position: { x: (i % 4) * 220, y: Math.floor(i / 4) * 120 }, - data: { label: n.label, apiCount: n.apiCount, status: n.status, hostId: n.id, apis: n.apis || [] } - })) - vfEdges.value = (res.data.edges || []).map((e: any, i: number) => ({ - id: `e${i}`, - source: e.source, - target: e.target, - label: e.label, - labelBgStyle: { fill: '#fff' }, - labelStyle: { fontSize: '10px', fill: '#666' }, - style: { stroke: '#1890ff', strokeWidth: 2 }, - markerEnd: { type: 'arrowclosed', width: 16, height: 16, color: '#1890ff' }, - animated: true + position: { x: (i % 4) * 240, y: Math.floor(i / 4) * 140 }, + data: { label: n.label, apiCount: n.apiCount, status: n.status, hostId: n.id, apis: n.apis || [], totalCalls: n.totalCalls || 0, totalSuccess: n.totalSuccess || 0, totalFail: n.totalFail || 0 } })) + vfEdges.value = (res.data.edges || []).map((e: any, i: number) => { + const color = statusColor(e.status || 'online') + return { + id: `e${i}`, + source: e.source, + target: e.target, + label: e.label, + labelBgStyle: { fill: '#fff' }, + labelStyle: { fontSize: '10px', fill: '#666' }, + style: { stroke: color, strokeWidth: 2 }, + markerEnd: { type: 'arrowclosed' as any, width: 14, height: 14, color }, + animated: true + } + }) } } finally { loading.value = false } } -function selectHost(hostId: string) { - selectedHost.value = hostId - const node = vfNodes.value.find(n => n.id === hostId) - selectedApis.value = node?.data?.apis || [] +function selectHost(data: any) { + selectedHost.value = data.hostId + selectedStats.value = data + selectedApis.value = data.apis || [] } "); + + // 执行所有数据集 + var datasets = new Dictionary(); + if (root.TryGetProperty("datasets", out var dsArr)) + { + foreach (var ds in dsArr.EnumerateArray()) + { + var id = ds.TryGetProperty("id", out var idEl) ? idEl.GetString() ?? "" : ""; + var sourceType = ds.TryGetProperty("sourceType", out var stEl) ? stEl.GetString() ?? "" : ""; + var sourceRef = ds.TryGetProperty("sourceRef", out var srEl) ? srEl.GetString() ?? "" : ""; + var customSql = ds.TryGetProperty("customSql", out var csEl) ? csEl.GetString() ?? "" : ""; + + var dt = await ResolveSource(sourceType, sourceRef, customSql, queryParams); + if (dt != null) datasets[id] = dt; + } + } + + // 渲染表格 + sb.AppendLine(""); + var rows = root.TryGetProperty("rows", out var rowsEl) ? rowsEl : default; + var cols = root.TryGetProperty("cols", out var colsEl) ? colsEl : default; + var cells = root.TryGetProperty("cells", out var cellsEl) ? cellsEl : default; + + // 构建 cells lookup + var cellMap = new Dictionary<(int r, int c), JsonElement>(); + if (cells.ValueKind == JsonValueKind.Array) + foreach (var c in cells.EnumerateArray()) + { + var r = c.TryGetProperty("r", out var re) ? re.GetInt32() : 0; + var cc = c.TryGetProperty("c", out var ce) ? ce.GetInt32() : 0; + cellMap[(r, cc)] = c; + } + + // 简单渲染:遍历 rows → 渲染每行 + int maxRows = rows.ValueKind == JsonValueKind.Array ? rows.GetArrayLength() : 0; + int maxCols = cols.ValueKind == JsonValueKind.Array ? cols.GetArrayLength() : 0; + var usedCells = new HashSet<(int, int)>(); + + for (int ri = 0; ri < maxRows; ri++) + { + var rowEl = rows[ri]; + var rowType = rowEl.TryGetProperty("type", out var rtEl) ? rtEl.GetString() ?? "data" : "data"; + var dataset = rowEl.TryGetProperty("dataset", out var dsRefEl) ? dsRefEl.GetString() ?? "" : ""; + var expand = rowEl.TryGetProperty("expand", out var expEl) ? expEl.GetString() ?? "" : ""; + + DataTable? dt = string.IsNullOrEmpty(dataset) || !datasets.ContainsKey(dataset) ? null : datasets[dataset]; + + if (expand == "auto" && dt != null) + { + // 数据扩展行:为每行数据渲染一次 + for (int di = 0; di < dt.Rows.Count; di++) + { + sb.Append(""); + for (int ci = 0; ci < maxCols; ci++) + { + if (usedCells.Contains((ri, ci))) continue; + RenderCell(sb, ri, ci, cellMap, dt, di, datasets); + } + sb.AppendLine(""); + } + } + else if (dt != null && dt.Rows.Count > 0) + { + sb.Append(""); + for (int ci = 0; ci < maxCols; ci++) + { + if (usedCells.Contains((ri, ci))) continue; + RenderCell(sb, ri, ci, cellMap, dt, 0, datasets); + } + sb.AppendLine(""); + } + else + { + // 静态行 + sb.Append(""); + for (int ci = 0; ci < maxCols; ci++) + { + if (usedCells.Contains((ri, ci))) continue; + RenderCell(sb, ri, ci, cellMap, null, 0, datasets); + } + sb.AppendLine(""); + } + + // 标记已处理的合并单元格 + foreach (var mk in cellMap.Keys.Where(k => k.r == ri)) + { + var cell = cellMap[mk]; + var colspan = cell.TryGetProperty("colspan", out var csp) ? csp.GetInt32() : 1; + var rowspan = cell.TryGetProperty("rowspan", out var rsp) ? rsp.GetInt32() : 1; + for (int dr = 0; dr < rowspan; dr++) + for (int dc = 0; dc < colspan; dc++) + if (dr > 0 || dc > 0) + usedCells.Add((ri + dr, mk.c + dc)); + } + } + + sb.AppendLine("
"); + return sb.ToString(); + } + + private static void RenderCell(StringBuilder sb, int ri, int ci, + Dictionary<(int, int), JsonElement> cells, DataTable? dt, int dataRow, + Dictionary datasets) + { + var style = "border:1px solid #ccc;"; + string value = ""; + int colspan = 1, rowspan = 1; + + if (cells.TryGetValue((ri, ci), out var cell)) + { + value = cell.TryGetProperty("value", out var vEl) ? vEl.GetString() ?? "" : ""; + colspan = cell.TryGetProperty("colspan", out var csp) ? csp.GetInt32() : 1; + rowspan = cell.TryGetProperty("rowspan", out var rsp) ? rsp.GetInt32() : 1; + + if (cell.TryGetProperty("style", out var sEl)) + { + if (sEl.TryGetProperty("bold", out var b) && b.ValueKind == JsonValueKind.True) style += "font-weight:bold;"; + if (sEl.TryGetProperty("italic", out var it) && it.ValueKind == JsonValueKind.True) style += "font-style:italic;"; + if (sEl.TryGetProperty("fontSize", out var fs)) style += $"font-size:{fs.GetInt32()}px;"; + if (sEl.TryGetProperty("color", out var cl)) style += $"color:{cl.GetString()};"; + if (sEl.TryGetProperty("bgColor", out var bg)) style += $"background-color:{bg.GetString()};"; + if (sEl.TryGetProperty("align", out var al)) style += $"text-align:{al.GetString()};"; + if (sEl.TryGetProperty("border", out var bd) && bd.ValueKind == JsonValueKind.False) style = style.Replace("border:1px solid #ccc;", ""); + } + + // ${field} 替换 + if (dt != null && value.Contains("${")) + { + foreach (System.Data.DataColumn col in dt.Columns) + { + var key = $"${{{col.ColumnName}}}"; + if (value.Contains(key)) + { + var replacement = dt.Rows[dataRow][col]?.ToString() ?? ""; + value = value.Replace(key, replacement); + } + } + value = value.Replace("${rowIndex}", (dataRow + 1).ToString()); + } + + // 公式 =xxx + if (value.StartsWith("=") && dt != null) + { + try + { + var cellVals = new Dictionary(); + for (int c = 0; c < dt.Columns.Count; c++) + cellVals[$"${dt.Columns[c].ColumnName}"] = dt.Rows[dataRow][c]; + cellVals["_rowIndex"] = dataRow; + value = FormulaEngine.Eval(value, cellVals, cellVals)?.ToString() ?? ""; + } + catch { value = "#ERR"; } + } + } + + var tag = "td"; + if (colspan > 1) sb.Append($"<{tag} colspan='{colspan}'"); + else sb.Append($"<{tag}"); + if (rowspan > 1) sb.Append($" rowspan='{rowspan}'"); + sb.Append($" style='{style}'>"); + sb.Append(value); + sb.AppendLine($""); + } + + /// 导出 Excel (.xlsx) + public byte[] ExportExcel(string layoutJson, Dictionary? queryParams = null) + { + // Simplified: generate HTML and return as bytes for browser download + // Full implementation would use ClosedXML + var html = RenderToHtml(layoutJson, queryParams).GetAwaiter().GetResult(); + return Encoding.UTF8.GetBytes(html); + } + + /// 导出 PDF + public byte[] ExportPdf(string layoutJson, Dictionary? queryParams = null) + { + // Return HTML for now — browser print handles PDF + var html = RenderToHtml(layoutJson, queryParams).GetAwaiter().GetResult(); + return Encoding.UTF8.GetBytes(html); + } + + // ===== Data Source Helpers ===== + + private async Task ResolveSource(string sourceType, string sourceRef, string customSql, + Dictionary? queryParams) + { + switch (sourceType) + { + case "dataview": + if (long.TryParse(sourceRef, out var dvId)) + { + var dv = await _db.Set().FindAsync(dvId); + if (dv != null && !string.IsNullOrEmpty(dv.Sql)) + return await ExecuteQuery(dv.DataSourceId, dv.Sql, queryParams); + } + break; + case "sql": + if (!string.IsNullOrEmpty(customSql)) + { + // 使用第一个数据源执行自定义SQL + var firstDs = await _db.DataSources.FirstOrDefaultAsync(d => d.Deleted == 0); + if (firstDs != null) + return await ExecuteQuery(firstDs.Id, customSql, queryParams); + } + break; + } + return null; + } + + // ===== Connection Factory (reuses MysqlNodeExecutor pattern) ===== + + private static IDbConnection CreateConnection(string dsType, string connStr) => dsType switch + { + "sqlite" => new SqliteConnection(connStr), + "mysql" => new MySqlConnection(connStr), + "postgresql" or "postgres" => new NpgsqlConnection(connStr), + "sqlserver" or "mssql" => new Microsoft.Data.SqlClient.SqlConnection(connStr), + _ => throw new Exception($"Unsupported DB: {dsType}") + }; + + private static IDbDataParameter CreateParameter(string name, object? value, string dsType) + { + return dsType switch + { + "sqlite" => new SqliteParameter($"@{name}", value ?? DBNull.Value), + "mysql" => new MySqlParameter($"@{name}", value ?? DBNull.Value), + "postgresql" or "postgres" => new NpgsqlParameter($"@{name}", value ?? DBNull.Value), + "sqlserver" or "mssql" => new Microsoft.Data.SqlClient.SqlParameter($"@{name}", value ?? DBNull.Value), + _ => new SqliteParameter($"@{name}", value ?? DBNull.Value) + }; + } + + private static string BuildConnectionString(Domain.Entities.DataSourceEntity ds) => ds.DsType?.ToLower() switch + { + "sqlite" => $"Data Source={ds.DbName ?? "juggle.db"}", + "mysql" => $"Server={ds.Host};Port={ds.Port};Database={ds.DbName};User={ds.Username};Password={ds.Password};CharSet=utf8mb4;", + "postgresql" or "postgres" => $"Host={ds.Host};Port={ds.Port};Database={ds.DbName};Username={ds.Username};Password={ds.Password};", + "sqlserver" or "mssql" => $"Server={ds.Host},{ds.Port};Database={ds.DbName};User Id={ds.Username};Password={ds.Password};TrustServerCertificate=True;", + _ => throw new Exception($"Unsupported DB: {ds.DsType}") + }; +} diff --git a/Juggle.Domain/Engine/FormulaEngine.cs b/Juggle.Domain/Engine/FormulaEngine.cs new file mode 100644 index 0000000..f53b678 --- /dev/null +++ b/Juggle.Domain/Engine/FormulaEngine.cs @@ -0,0 +1,452 @@ +namespace Juggle.Domain.Engine; + +/// +/// 报表公式引擎 — 递归下降解析 + 求值 +/// 支持: 单元格引用(A1)、范围引用(A1:A10)、字段引用(${field})、 +/// 算术(+ - * / %)、比较(== != > < >= <=)、逻辑(&& || !)、 +/// 三元(?:)、内置函数(SUM/AVG/MIN/MAX/COUNT/IF/SUBSTR等) +/// +public class FormulaEngine +{ + private readonly string _expr; + private int _pos; + private readonly Dictionary _cellValues; // "A1" → value + private readonly Dictionary _fields; // "${field}" → value + private readonly Func> _rangeResolver; // (start, end) → values + + public FormulaEngine(string expr, + Dictionary cellValues, + Dictionary fields, + Func> rangeResolver) + { + _expr = expr; + _pos = 0; + _cellValues = cellValues; + _fields = fields; + _rangeResolver = rangeResolver; + } + + public object? Evaluate() + { + if (string.IsNullOrEmpty(_expr)) return ""; + var val = ParseTernary(); + SkipWhitespace(); + if (_pos < _expr.Length) + return $"#ERR: unexpected '{_expr[_pos]}'"; + return val; + } + + // ===== Recursive Descent Parser ===== + + private object? ParseTernary() + { + var cond = ParseOr(); + SkipWhitespace(); + if (Peek() == '?') + { + _pos++; + var trueVal = ParseTernary(); + SkipWhitespace(); + if (Peek() == ':') + { + _pos++; + var falseVal = ParseTernary(); + return IsTruthy(cond) ? trueVal : falseVal; + } + return "#ERR: missing ':'"; + } + return cond; + } + + private object? ParseOr() + { + var left = ParseAnd(); + SkipWhitespace(); + while (Peek() == '|' && Peek(1) == '|') + { + _pos += 2; + var right = ParseAnd(); + left = IsTruthy(left) ? left : right; // short-circuit + } + return left; + } + + private object? ParseAnd() + { + var left = ParseCompare(); + SkipWhitespace(); + while (Peek() == '&' && Peek(1) == '&') + { + _pos += 2; + var right = ParseCompare(); + left = IsTruthy(left) ? right : false; // short-circuit + } + return left; + } + + private object? ParseCompare() + { + var left = ParseAddSub(); + SkipWhitespace(); + var op = ""; + if (Peek() == '=' && Peek(1) == '=') { _pos += 2; op = "=="; } + else if (Peek() == '!' && Peek(1) == '=') { _pos += 2; op = "!="; } + else if (Peek() == '>' && Peek(1) == '=') { _pos += 2; op = ">="; } + else if (Peek() == '<' && Peek(1) == '=') { _pos += 2; op = "<="; } + else if (Peek() == '>') { _pos++; op = ">"; } + else if (Peek() == '<') { _pos++; op = "<"; } + else return left; + + var right = ParseAddSub(); + return Compare(left, right, op); + } + + private object? ParseAddSub() + { + var left = ParseMulDiv(); + SkipWhitespace(); + while (Peek() == '+' || Peek() == '-') + { + var op = _expr[_pos++]; + var right = ParseMulDiv(); + left = Arithmetic(left, right, op); + } + return left; + } + + private object? ParseMulDiv() + { + var left = ParseUnary(); + SkipWhitespace(); + while (Peek() == '*' || Peek() == '/' || Peek() == '%') + { + var op = _expr[_pos++]; + var right = ParseUnary(); + left = Arithmetic(left, right, op); + } + return left; + } + + private object? ParseUnary() + { + SkipWhitespace(); + if (Peek() == '-') { _pos++; var v = ParseUnary(); return Negate(v); } + if (Peek() == '!') { _pos++; return !IsTruthy(ParseUnary()); } + return ParsePrimary(); + } + + private object? ParsePrimary() + { + SkipWhitespace(); + if (_pos >= _expr.Length) return "#ERR: unexpected end"; + + // 数字 + if (char.IsDigit(_expr[_pos]) || (_expr[_pos] == '.' && _pos + 1 < _expr.Length && char.IsDigit(_expr[_pos + 1]))) + { + var start = _pos; + while (_pos < _expr.Length && (char.IsDigit(_expr[_pos]) || _expr[_pos] == '.')) _pos++; + var numStr = _expr[start.._pos]; + if (double.TryParse(numStr, out var d)) + return numStr.Contains('.') ? d : (object)(long)d; + return d; + } + + // 字符串 + if (_expr[_pos] == '\'' || _expr[_pos] == '"') + { + var quote = _expr[_pos++]; + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != quote) _pos++; + var str = _expr[start.._pos]; + if (_pos < _expr.Length) _pos++; + return str; + } + + // 字段引用 ${field} + if (_expr[_pos] == '$' && _pos + 1 < _expr.Length && _expr[_pos + 1] == '{') + { + _pos += 2; + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != '}') _pos++; + var field = _expr[start.._pos]; + if (_pos < _expr.Length) _pos++; + return _fields.TryGetValue($"${{{field}}}", out var fv) ? fv : _fields.TryGetValue(field, out fv) ? fv : null; + } + + // 括号 + if (_expr[_pos] == '(') + { + _pos++; + var val = ParseTernary(); + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ')') _pos++; + return val; + } + + // 函数调用 / 单元格引用 + var ident = ParseIdentifier(); + if (string.IsNullOrEmpty(ident)) return "#ERR: expected value"; + + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == '(') + return CallFunction(ident); + + // 单元格引用 A1 或 ds1!A1 + if (char.IsLetter(ident[0])) + return ResolveCellRef(ident); + + return ident; + } + + private string ParseIdentifier() + { + var start = _pos; + while (_pos < _expr.Length && (char.IsLetterOrDigit(_expr[_pos]) || _expr[_pos] == '_' || _expr[_pos] == '!' || _expr[_pos] == ':')) + { + if (_expr[_pos] == '!' || _expr[_pos] == ':') + { + // 范围引用: A1:A10 — 停止标识符解析,交给 ResolveCellRef + if (_expr[_pos] == ':') break; + _pos++; // skip '!' + continue; + } + _pos++; + } + return _expr[start.._pos]; + } + + private object? CallFunction(string name) + { + _pos++; // skip '(' + var args = new List(); + var depth = 1; + var argStart = _pos; + while (_pos < _expr.Length && depth > 0) + { + if (_expr[_pos] == '(') depth++; + else if (_expr[_pos] == ')') depth--; + else if (_expr[_pos] == ',' && depth == 1) + { + args.Add(EvaluateArg(_expr[argStart.._pos])); + argStart = _pos + 1; + } + _pos++; + } + if (argStart < _pos - 1) + args.Add(EvaluateArg(_expr[argStart..(_pos - 1)])); + + return ExecuteFunction(name.ToUpper(), args); + } + + private object? EvaluateArg(string argExpr) + { + if (string.IsNullOrWhiteSpace(argExpr)) return null; + // 范围引用: A1:A10 + if (argExpr.Contains(':') && char.IsLetter(argExpr.Trim()[0])) + { + var parts = argExpr.Trim().Split(':'); + if (parts.Length == 2) + { + var values = _rangeResolver(parts[0].Trim(), parts[1].Trim()); + return values; + } + } + // 单元格引用 + if (char.IsLetter(argExpr.Trim()[0]) && !argExpr.StartsWith("$")) + { + return ResolveCellRef(argExpr.Trim()); + } + // 嵌套公式求值 + var sub = new FormulaEngine(argExpr.Trim(), _cellValues, _fields, _rangeResolver); + return sub.Evaluate(); + } + + private object? ExecuteFunction(string name, List args) + { + try + { + switch (name) + { + case "SUM": return SumRange(args); + case "AVG": return AvgRange(args); + case "MIN": return MinRange(args); + case "MAX": return MaxRange(args); + case "COUNT": return CountRange(args); + case "IF": return args.Count >= 3 ? (IsTruthy(args[0]) ? args[1] : args[2]) : "#ERR: IF requires 3 args"; + case "SUBSTR": return args.Count >= 3 ? SubStr(args) : "#ERR"; + case "LEN": return args.Count >= 1 ? (args[0]?.ToString()?.Length ?? 0) : 0; + case "UPPER": return args.Count >= 1 ? args[0]?.ToString()?.ToUpper() : ""; + case "LOWER": return args.Count >= 1 ? args[0]?.ToString()?.ToLower() : ""; + case "TRIM": return args.Count >= 1 ? args[0]?.ToString()?.Trim() : ""; + case "CONCAT": return string.Concat(args.Select(a => a?.ToString() ?? "")); + case "ROUND": return args.Count >= 2 ? Math.Round(ToDouble(args[0]), ToInt(args[1])) : args[0]; + case "ABS": return args.Count >= 1 ? Math.Abs(ToDouble(args[0])) : 0; + case "NOW": return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + case "TODAY": return DateTime.Now.ToString("yyyy-MM-dd"); + case "ROW": return _cellValues.TryGetValue("_rowIndex", out var ri) ? (ToInt(ri) + 1) : 1; + default: return $"#NAME? {name}"; + } + } + catch { return "#ERR"; } + } + + // ===== Range Operations ===== + + private object SumRange(List args) + { + double sum = 0; + foreach (var a in args) + { + if (a is List list) sum += list.Sum(v => ToDouble(v)); + else sum += ToDouble(a); + } + return sum; + } + + private object AvgRange(List args) + { + var all = FlattenRange(args); + return all.Count > 0 ? all.Average(ToDouble) : 0; + } + + private object MinRange(List args) + { + var all = FlattenRange(args); + return all.Count > 0 ? all.Min(ToDouble) : 0; + } + + private object MaxRange(List args) + { + var all = FlattenRange(args); + return all.Count > 0 ? all.Max(ToDouble) : 0; + } + + private object CountRange(List args) + { + return FlattenRange(args).Count; + } + + private object SubStr(List args) + { + var s = args[0]?.ToString() ?? ""; + var start = ToInt(args[1]); + var len = args.Count > 2 ? ToInt(args[2]) : s.Length - start; + if (start < 0) start = s.Length + start; + if (start < 0 || start >= s.Length) return ""; + if (start + len > s.Length) len = s.Length - start; + return s.Substring(start, len); + } + + private List FlattenRange(List args) + { + var result = new List(); + foreach (var a in args) + { + if (a is List list) result.AddRange(list); + else result.Add(a); + } + return result; + } + + // ===== Cell Reference Resolution ===== + + private object? ResolveCellRef(string refStr) + { + // dsName!A1 format + string? dataset = null; + var cellPart = refStr; + if (refStr.Contains('!')) + { + var parts = refStr.Split('!'); + dataset = parts[0]; + cellPart = parts[1]; + } + + if (!string.IsNullOrEmpty(dataset)) + cellPart = $"{dataset}!{cellPart}"; + + if (_cellValues.TryGetValue(cellPart, out var val)) + return val; + if (_cellValues.TryGetValue(refStr, out val)) + return val; + return null; + } + + // ===== Arithmetic & Comparison ===== + + private static object? Arithmetic(object? left, object? right, char op) + { + var l = ToDouble(left); + var r = ToDouble(right); + return op switch { '+' => l + r, '-' => l - r, '*' => l * r, '/' => r != 0 ? l / r : "#DIV/0!", '%' => l % r, _ => 0 }; + } + + private static object? Negate(object? v) => -ToDouble(v); + + private static object Compare(object? left, object? right, string op) + { + if (left is double || right is double || left is long || right is long || left is int || right is int) + { + var l = ToDouble(left); var r = ToDouble(right); + return op switch { "==" => l == r, "!=" => l != r, ">" => l > r, "<" => l < r, ">=" => l >= r, "<=" => l <= r, _ => false }; + } + var ls = left?.ToString() ?? ""; var rs = right?.ToString() ?? ""; + return op switch { "==" => ls == rs, "!=" => ls != rs, ">" => ls.CompareTo(rs) > 0, "<" => ls.CompareTo(rs) < 0, ">=" => ls.CompareTo(rs) >= 0, "<=" => ls.CompareTo(rs) <= 0, _ => false }; + } + + // ===== Helpers ===== + + private static double ToDouble(object? v) + { + if (v == null) return 0; + if (v is double d) return d; + if (v is long l) return l; + if (v is int i) return i; + if (v is float f) return f; + if (v is decimal m) return (double)m; + if (double.TryParse(v.ToString(), out var r)) return r; + return 0; + } + + private static int ToInt(object? v) + { + if (v == null) return 0; + if (v is int i) return i; + if (v is long l) return (int)l; + if (v is double d) return (int)d; + if (int.TryParse(v.ToString(), out var r)) return r; + return 0; + } + + private static bool IsTruthy(object? v) + { + if (v == null) return false; + if (v is bool b) return b; + if (v is double d) return d != 0; + if (v is long l) return l != 0; + if (v is int i) return i != 0; + if (v is string s) return !string.IsNullOrEmpty(s) && s != "false" && s != "0"; + return true; + } + + private char Peek(int ahead = 0) => _pos + ahead < _expr.Length ? _expr[_pos + ahead] : '\0'; + + private void SkipWhitespace() + { + while (_pos < _expr.Length && char.IsWhiteSpace(_expr[_pos])) _pos++; + } + + // ===== Static Entry Point ===== + public static object? Eval(string formula, + Dictionary? cellValues = null, + Dictionary? fields = null, + Func>? rangeResolver = null) + { + var expr = formula.StartsWith("=") ? formula[1..] : formula; + var engine = new FormulaEngine(expr, + cellValues ?? new(), + fields ?? new(), + rangeResolver ?? ((s, e) => new())); + return engine.Evaluate(); + } +} diff --git a/Juggle.Domain/Entities/DataViewEntity.cs b/Juggle.Domain/Entities/DataViewEntity.cs new file mode 100644 index 0000000..015fe37 --- /dev/null +++ b/Juggle.Domain/Entities/DataViewEntity.cs @@ -0,0 +1,12 @@ +namespace Juggle.Domain.Entities; + +public class DataViewEntity : BaseEntity +{ + public string? GroupName { get; set; } + public string? Name { get; set; } + public long DataSourceId { get; set; } + public string? Sql { get; set; } + public string? Parameters { get; set; } // JSON: [{name,type,default,label}] + public string? Remark { get; set; } + public int Status { get; set; } = 1; +} diff --git a/Juggle.Domain/Entities/ReportEntity.cs b/Juggle.Domain/Entities/ReportEntity.cs new file mode 100644 index 0000000..18d59c5 --- /dev/null +++ b/Juggle.Domain/Entities/ReportEntity.cs @@ -0,0 +1,13 @@ +namespace Juggle.Domain.Entities; + +public class ReportEntity : BaseEntity +{ + public string? Name { get; set; } + public string? GroupName { get; set; } + public string? SourceType { get; set; } // flow / api / dataview / sql + public string? SourceRef { get; set; } // flowKey / apiCode / dataViewId + public string? CustomSql { get; set; } // SourceType=sql 时使用 + public string? ParamsConfig { get; set; } // 查询参数 JSON + public string? LayoutJson { get; set; } // 报表布局 JSON + public int Status { get; set; } = 1; +} diff --git a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs index 1224224..5237bca 100644 --- a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs +++ b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs @@ -90,6 +90,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().ToTable("t_login_log"); modelBuilder.Entity().ToTable("t_alert_rule"); modelBuilder.Entity().ToTable("t_alert_record"); + modelBuilder.Entity().ToTable("t_data_view"); + modelBuilder.Entity().ToTable("t_report"); // 列名映射(snake_case)— AlertRule modelBuilder.Entity(e => { diff --git a/JuggleNet6.Frontend/src/router/index.ts b/JuggleNet6.Frontend/src/router/index.ts index b38dcbf..ab7054a 100644 --- a/JuggleNet6.Frontend/src/router/index.ts +++ b/JuggleNet6.Frontend/src/router/index.ts @@ -35,7 +35,10 @@ const routes = [ { path: 'system/audit-log', component: () => import('../views/system/AuditLog.vue') }, { path: 'flow/log', component: () => import('../views/flow/FlowLog.vue') }, { path: 'flow/async-result', component: () => import('../views/flow/AsyncFlowResult.vue') }, - { path: 'flow/testcase', component: () => import('../views/flow/FlowTestCase.vue') } + { path: 'flow/testcase', component: () => import('../views/flow/FlowTestCase.vue') }, + { path: 'report/dataview', component: () => import('../views/report/DataViewList.vue') }, + { path: 'report/design', component: () => import('../views/report/ReportList.vue') }, + { path: 'report/designer/:id', component: () => import('../views/report/ReportDesigner.vue') } ] }, { diff --git a/JuggleNet6.Frontend/src/views/Layout.vue b/JuggleNet6.Frontend/src/views/Layout.vue index f23cfec..7160b89 100644 --- a/JuggleNet6.Frontend/src/views/Layout.vue +++ b/JuggleNet6.Frontend/src/views/Layout.vue @@ -39,6 +39,14 @@ 对象管理 + + + 数据视图 + 报表管理 + @@ -62,6 +66,9 @@ const previewHtml = ref('') const previewParams = ref([]) const previewValues = ref>({}) const previewId = ref(0) +const previewPage = ref(1) +const previewPageSize = ref(20) +const previewTotal = ref(0) onMounted(loadData) @@ -94,8 +101,9 @@ function openPreview(row: any) { } async function doPreview() { - const res = await request.post('/report/preview', { id: previewId.value, params: previewValues.value }) + const res = await request.post('/report/preview', { id: previewId.value, params: previewValues.value, page: previewPage.value, pageSize: previewPageSize.value }) previewHtml.value = res.data?.html || '' + previewTotal.value = res.data?.total || 0 } async function doExportPdf(row: any) { From 5fe8322cde08c7ec2a87c7a2efcd2a46d32dfe4b Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 15:42:08 +0800 Subject: [PATCH 079/153] =?UTF-8?q?fix:=20=E6=8A=A5=E8=A1=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=A7=86=E5=9B=BE/=E7=AE=A1=E7=90=86500-=E8=A1=A5?= =?UTF-8?q?=E5=85=A8DataView+Report=20snake=5Fcase=E5=88=97=E6=98=A0?= =?UTF-8?q?=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../Persistence/JuggleDbContext.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs index 5237bca..07b7e9b 100644 --- a/Juggle.Infrastructure/Persistence/JuggleDbContext.cs +++ b/Juggle.Infrastructure/Persistence/JuggleDbContext.cs @@ -91,7 +91,40 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().ToTable("t_alert_rule"); modelBuilder.Entity().ToTable("t_alert_record"); modelBuilder.Entity().ToTable("t_data_view"); + modelBuilder.Entity(e => { + e.Property(p => p.Id).HasColumnName("id"); + e.Property(p => p.Deleted).HasColumnName("deleted"); + e.Property(p => p.CreatedAt).HasColumnName("created_at"); + e.Property(p => p.CreatedBy).HasColumnName("created_by"); + e.Property(p => p.UpdatedAt).HasColumnName("updated_at"); + e.Property(p => p.UpdatedBy).HasColumnName("updated_by"); + e.Property(p => p.TenantId).HasColumnName("tenant_id"); + e.Property(p => p.GroupName).HasColumnName("group_name"); + e.Property(p => p.Name).HasColumnName("name"); + e.Property(p => p.DataSourceId).HasColumnName("data_source_id"); + e.Property(p => p.Sql).HasColumnName("sql"); + e.Property(p => p.Parameters).HasColumnName("parameters"); + e.Property(p => p.Remark).HasColumnName("remark"); + e.Property(p => p.Status).HasColumnName("status"); + }); modelBuilder.Entity().ToTable("t_report"); + modelBuilder.Entity(e => { + e.Property(p => p.Id).HasColumnName("id"); + e.Property(p => p.Deleted).HasColumnName("deleted"); + e.Property(p => p.CreatedAt).HasColumnName("created_at"); + e.Property(p => p.CreatedBy).HasColumnName("created_by"); + e.Property(p => p.UpdatedAt).HasColumnName("updated_at"); + e.Property(p => p.UpdatedBy).HasColumnName("updated_by"); + e.Property(p => p.TenantId).HasColumnName("tenant_id"); + e.Property(p => p.Name).HasColumnName("name"); + e.Property(p => p.GroupName).HasColumnName("group_name"); + e.Property(p => p.SourceType).HasColumnName("source_type"); + e.Property(p => p.SourceRef).HasColumnName("source_ref"); + e.Property(p => p.CustomSql).HasColumnName("custom_sql"); + e.Property(p => p.ParamsConfig).HasColumnName("params_config"); + e.Property(p => p.LayoutJson).HasColumnName("layout_json"); + e.Property(p => p.Status).HasColumnName("status"); + }); // 列名映射(snake_case)— AlertRule modelBuilder.Entity(e => { From 16f24f79e216cf9840e243f78e817a8f381a5138 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 16:09:23 +0800 Subject: [PATCH 080/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E5=85=A8=E9=9D=A2=E5=8D=87=E7=BA=A7(?= =?UTF-8?q?=E5=88=97=E5=A4=B4/=E8=A1=8C=E5=A4=B4/=E6=8B=96=E6=8B=BD/?= =?UTF-8?q?=E5=A4=9A=E9=80=89/=E9=A1=B5=E9=9D=A2=E8=AE=BE=E7=BD=AE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 工具栏增加报表名称+分组的直接编辑 2. Excel风格列头(A,B,C)和行头(1,2,3)显示 3. 拖拽列头分割线调整列宽,拖拽行头分割线调整行高 4. 点击行头选中整行,点击列头选中整列 5. Ctrl+点击多选单元格,批量设置样式/合并/边框 6. 页面设置对话框:纸张/方向/页边距/页眉/页脚 7. 布局JSON新增page.header/footer字段 Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 501 ++++++++++-------- 1 file changed, 281 insertions(+), 220 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 2670e2d..23905ac 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -1,10 +1,13 @@ From 676dc57b332629840c90dd37759fe2c1d53fae9f Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 16:16:08 +0800 Subject: [PATCH 081/153] =?UTF-8?q?fix:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E5=8D=95=E5=85=83=E6=A0=BC=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1-=E5=88=87=E6=8D=A2=E5=89=8D=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E4=BF=9D=E5=AD=98+=E8=BE=93=E5=85=A5=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 23905ac..eae30bf 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -101,7 +101,7 @@

位置: {{ hasSelection ? colLetter(selC) + (selR+1) : '' }} {{ selectedCells.length>1 ? `(+${selectedCells.length-1}格)` : '' }}

值:

- +

类型:

@@ -238,6 +238,8 @@ function getCellClasses(r:number,c:number) { } function onCellMouseDown(_e:MouseEvent, r:number, c:number) { + // 先保存上一个单元格的编辑内容 + commitEdit() if (ctrlDown.value) { if (selectedCells.value.some(([sr,sc])=>sr===r&&sc===c)) selectedCells.value = selectedCells.value.filter(([sr,sc])=>!(sr===r&&sc===c)) else selectedCells.value.push([r,c]) @@ -255,15 +257,25 @@ function onCellMouseDown(_e:MouseEvent, r:number, c:number) { selColor.value = cell?.style?.color||''; selBgColor.value = cell?.style?.bgColor||'' } +function commitEdit() { + if (selR.value<0||selC.value<0) return + const key=`${selR.value},${selC.value}`, existing=cells.value[key]||{} + if (existing.value !== cellValue.value) { + cells.value[key]={...existing, value: cellValue.value} + } +} + function onCellDoubleClick(r:number,c:number) { selR.value=r; selC.value=c; cellValue.value=getCell(r,c)?.value||'' } function selectRow(r:number, _e:MouseEvent) { + commitEdit() if (ctrlDown.value) { if (selectedRows.value.has(r)) selectedRows.value.delete(r); else selectedRows.value.add(r) } else { selectedRows.value = new Set([r]); selectedCols.value.clear(); selectedCells.value=[]; selR.value=-1; selC.value=-1 } for (let c=0;c Date: Tue, 26 May 2026 17:15:37 +0800 Subject: [PATCH 082/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=95=B0=E6=8D=AE=E9=9B=86=E7=AE=A1=E7=90=86?= =?UTF-8?q?(4=E7=A7=8D=E6=95=B0=E6=8D=AE=E6=BA=90+=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=A0=91+=E7=82=B9=E5=87=BB=E5=A1=AB=E5=85=A5=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=A0=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 左侧面板改为数据集列表树,支持展开/折叠/删除 2. 新增数据集对话框:数据视图/自定义SQL/流程/接口 3. 点击数据集字段自动填入当前选中单元格(带数据集前缀) 4. 拖拽字段填入单元格,支持${dsName.field}格式 5. 布局JSON新增datasets数组持久化存储 Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 176 +++++++++++++++--- 1 file changed, 145 insertions(+), 31 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index eae30bf..80041dd 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -7,16 +7,7 @@
- - - - - - - - - - 字段 + {{ datasets.length }} 个数据集 页面 保存 预览 @@ -24,9 +15,29 @@
-

数据字段

-
选择数据源后加载字段
-
{{ f }}
+
+

数据集

+ +
+
点击 + 添加数据集
+
+
+ + {{ ds.expanded?'▼':'▶' }} {{ ds.name }} + + {{ sourceLabel(ds.sourceType) }} + × +
+
+
加载中...
+
{{ f }}
+
点击加载字段
+ ↻ 加载字段 +
+
@@ -130,6 +141,43 @@ + + + + + + + 数据视图 + 自定义SQL + 流程 + 接口 + + + + + + + + + +
@@ -153,8 +201,13 @@ const router = useRouter() const rptId = Number(route.params.id) || 0 const form = reactive({ id:0, name:'', groupName:'', sourceType:'dataview', sourceRef:'', customSql:'', paramsConfig:'[]', layoutJson:'{}', status:1 }) -const dvList = ref([]) -const fields = ref([]) +const dvList = ref([]), dsList = ref([]), flowList = ref([]), apiList = ref([]) + +// 数据集管理 +interface Dataset { id: string; name: string; sourceType: string; sourceRef: string; customSql: string; dataSourceId: string|number; fields: string[]; expanded: boolean; loading: boolean } +const datasets = ref([]) +const dsDialogVisible = ref(false) +const dsForm = reactive({ name:'', sourceType:'dataview', sourceRef:'', customSql:'', dataSourceId:'' }) const maxRows = ref(10), maxCols = ref(6) const rowHeights = ref<(number|string)[]>([]) const colWidths = ref<(number|string)[]>([]) @@ -180,18 +233,88 @@ const hasSelection = computed(() => selR.value >= 0 && selC.value >= 0) onMounted(() => { document.addEventListener('keydown', (e) => { if (e.key==='Control') ctrlDown.value = true }) document.addEventListener('keyup', (e) => { if (e.key==='Control') ctrlDown.value = false }) - loadDvList(); if (rptId>0) loadReport() + loadDvList(); loadDsList(); loadFlowList(); loadApiList() + if (rptId>0) loadReport() resizeGrid() }) async function loadDvList() { try { const res = await request.post('/report/dataview/page', { pageNum:1, pageSize:200 }); dvList.value = res.data?.list||[] } catch {} } +async function loadDsList() { + try { const res = await request.get('/system/datasource/list'); dsList.value = res.data||[] } catch {} +} +async function loadFlowList() { + try { const res = await request.get('/flow/definition/list'); flowList.value = res.data||[] } catch {} +} +async function loadApiList() { + try { const res = await request.post('/suite/api/list', { suiteCode: '' }); apiList.value = res.data||[] } catch {} +} + +function sourceLabel(t: string) { const m: Record={dataview:'视图',sql:'SQL',flow:'流程',api:'接口'}; return m[t]||t } +function flowSelChange() {} + +function openDsDialog() { + Object.assign(dsForm, { name:'', sourceType:'dataview', sourceRef:'', customSql:'', dataSourceId:'' }) + dsDialogVisible.value = true +} + +function addDataset() { + if (!dsForm.name) { dsForm.name = dsForm.sourceType + '_' + (datasets.value.length+1) } + const ds: Dataset = { id: Date.now().toString(), name: dsForm.name, sourceType: dsForm.sourceType, sourceRef: dsForm.sourceRef, customSql: dsForm.customSql, dataSourceId: dsForm.dataSourceId, fields: [], expanded: true, loading: false } + datasets.value.push(ds) + dsDialogVisible.value = false + loadDsFields(datasets.value.length - 1) +} + +async function loadDsFields(di: number) { + const ds = datasets.value[di] + ds.loading = true + try { + switch (ds.sourceType) { + case 'dataview': + const dv = dvList.value.find(d => String(d.id) === ds.sourceRef) + if (dv) { + try { const res = await request.post('/report/dataview/preview', { id: Number(ds.sourceRef), params: {} }); ds.fields = res.data?.columns||[] } catch {} + const params = JSON.parse(dv.parameters||'[]') + previewParams.value = [...previewParams.value, ...params.filter((p:any)=>!previewParams.value.find((q:any)=>q.name===p.name))] + } + break + case 'sql': + if (ds.dataSourceId && ds.customSql) { + try { const res = await request.post('/report/dataview/preview', { id: 0, sql: ds.customSql, dataSourceId: Number(ds.dataSourceId), params: {} }); ds.fields = res.data?.columns||[] } catch {} + } + break + case 'flow': + if (ds.sourceRef) { + try { const res = await request.get(`/flow/definition/output-params/${ds.sourceRef}`); ds.fields = res.data||[] } catch { ds.fields = [] } + } + break + case 'api': + if (ds.sourceRef) { + try { const res = await request.get(`/suite/api/output-params/${ds.sourceRef}`); ds.fields = res.data||[] } catch { ds.fields = [] } + } + break + } + } finally { ds.loading = false } +} + +function insertField(dsName: string, field: string) { + if (selR.value<0||selC.value<0) return + const key=`${selR.value},${selC.value}`, existing=cells.value[key]||{} + cells.value[key] = {...existing, value: `\${${dsName}.${field}}`} + cellValue.value = `\${${dsName}.${field}}` +} + +function onDragField(_e:DragEvent, field:string, dsName?:string) { + const val = dsName ? `\${${dsName}.${field}}` : `\${${field}}` + _e.dataTransfer?.setData('field', val) +} async function loadReport() { try { const res = await request.post('/report/page', { pageNum:1, pageSize:200 }) const rpt = (res.data?.list||[]).find((r:any)=>r.id===rptId) - if (rpt) { Object.assign(form, rpt); loadLayoutJson(); loadFields() } + if (rpt) { Object.assign(form, rpt); loadLayoutJson() } } catch {} } @@ -206,6 +329,7 @@ function loadLayoutJson() { pageHeader.value = layout.page?.header||''; pageFooter.value = layout.page?.footer||'' cells.value = {}; if (layout.cells) for (const c of layout.cells) cells.value[`${c.r},${c.c}`] = c previewParams.value = layout.params||[] + if (layout.datasets) datasets.value = layout.datasets.map((d:any)=>({...d,expanded:true,loading:false})) } catch {} } @@ -343,29 +467,19 @@ function startRowResize(e:MouseEvent, ri:number) { function autoFitCol(c:number) { colWidths.value[c]=200 } function autoFitRow(r:number) { rowHeights.value[r]=30 } -function onDragField(e:DragEvent, field:string) { e.dataTransfer?.setData('field',field) } -function onCellDrop(e:DragEvent, r:number, c:number) { const field = e.dataTransfer?.getData('field'); if (field) { const key=`${r},${c}`,ex=cells.value[key]||{}; cells.value[key]={...ex,value:`\${${field}}`} } } +function onCellDrop(e:DragEvent, r:number, c:number) { const field = e.dataTransfer?.getData('field'); if (field) { const key=`${r},${c}`,ex=cells.value[key]||{}; cells.value[key]={...ex,value:field} } } function onGridScroll() {} -async function loadFields() { - if (form.sourceType==='dataview' && form.sourceRef) { - try { const dv=dvList.value.find(d=>String(d.id)===form.sourceRef) - if (dv) { try { previewParams.value=JSON.parse(dv.parameters||'[]') } catch { previewParams.value=[] } } - const res = await request.post('/report/dataview/preview',{id:Number(form.sourceRef),params:{}}); fields.value=res.data?.columns||[] } - catch { fields.value=[] } - } -} - -function onSourceTypeChange() { form.sourceRef=''; fields.value=[] } - function saveLayoutJson() { const rows=[]; for (let r=0;r({r:Number(k.split(',')[0]),c:Number(k.split(',')[1]),...v})) + const dsArr = datasets.value.map(d=>({id:d.id,name:d.name,sourceType:d.sourceType,sourceRef:d.sourceRef,customSql:d.customSql,dataSourceId:d.dataSourceId,fields:d.fields})) form.layoutJson = JSON.stringify({ page:{size:pageSize.value,orientation:pageOrientation.value,margin:{...pageMargin},header:pageHeader.value,footer:pageFooter.value}, - params:previewParams.value, rows, cols, cells:cellArr + params:previewParams.value, rows, cols, cells:cellArr, + datasets: dsArr }) } From f98790221fccec9d0f7caec9a439d5ee1ec971aa Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 18:10:54 +0800 Subject: [PATCH 083/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=95=B0=E6=8D=AE=E9=9B=86=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E4=BF=AE=E6=94=B9+=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 每个数据集卡片增加编辑/预览数据/刷新字段/删除按钮 2. 编辑弹窗可修改数据集名称/数据源选择/SQL 3. 数据预览弹窗显示数据集实际查询结果表格 Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 95 ++++++++++++++++--- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 80041dd..67a464e 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -20,22 +20,21 @@
点击 + 添加数据集
-
-
- - {{ ds.expanded?'▼':'▶' }} {{ ds.name }} - - {{ sourceLabel(ds.sourceType) }} - × +
+
+ {{ ds.expanded?'▼':'▶' }} {{ ds.name }} + {{ sourceLabel(ds.sourceType) }} + + + + ×
-
+
加载中...
{{ f }}
-
点击加载字段
- ↻ 加载字段 + @click="insertField(ds.name, f)">{{ f }}
+
点击 ↻ 刷新加载字段
@@ -178,6 +177,39 @@ + + + + + + + + + + + + + + + +
+
+
共 {{ dsPreviewRows.length }} 条
+ + + +
+
+
@@ -194,6 +226,7 @@ import { ref, reactive, onMounted, computed } from 'vue' import { useRoute, useRouter } from 'vue-router' import { ElMessage } from 'element-plus' +import { Edit, View, Refresh, Loading } from '@element-plus/icons-vue' import request from '../../utils/request' const route = useRoute() @@ -206,8 +239,11 @@ const dvList = ref([]), dsList = ref([]), flowList = ref([] // 数据集管理 interface Dataset { id: string; name: string; sourceType: string; sourceRef: string; customSql: string; dataSourceId: string|number; fields: string[]; expanded: boolean; loading: boolean } const datasets = ref([]) -const dsDialogVisible = ref(false) +const dsDialogVisible = ref(false), dsEditVisible = ref(false), dsEditIdx = ref(-1) const dsForm = reactive({ name:'', sourceType:'dataview', sourceRef:'', customSql:'', dataSourceId:'' }) +const dsEditForm = reactive({ name:'', sourceType:'', sourceRef:'', customSql:'', dataSourceId:'' }) +const dsPreviewVisible = ref(false), dsPreviewLoading = ref(false) +const dsPreviewCols = ref([]), dsPreviewRows = ref([]) const maxRows = ref(10), maxCols = ref(6) const rowHeights = ref<(number|string)[]>([]) const colWidths = ref<(number|string)[]>([]) @@ -254,6 +290,39 @@ async function loadApiList() { function sourceLabel(t: string) { const m: Record={dataview:'视图',sql:'SQL',flow:'流程',api:'接口'}; return m[t]||t } function flowSelChange() {} +function openDsEdit(di: number) { + dsEditIdx.value = di + const ds = datasets.value[di] + Object.assign(dsEditForm, { name: ds.name, sourceType: ds.sourceType, sourceRef: ds.sourceRef, customSql: ds.customSql, dataSourceId: ds.dataSourceId }) + dsEditVisible.value = true +} + +function saveDsEdit() { + if (dsEditIdx.value < 0) return + const ds = datasets.value[dsEditIdx.value] + ds.name = dsEditForm.name; ds.sourceRef = dsEditForm.sourceRef; ds.customSql = dsEditForm.customSql; ds.dataSourceId = dsEditForm.dataSourceId + dsEditVisible.value = false + loadDsFields(dsEditIdx.value) +} + +async function previewDsData(di: number) { + dsPreviewVisible.value = true; dsPreviewLoading.value = true; dsPreviewCols.value = []; dsPreviewRows.value = [] + const ds = datasets.value[di] + try { + switch (ds.sourceType) { + case 'dataview': + const res = await request.post('/report/dataview/preview', { id: Number(ds.sourceRef), params: {} }) + dsPreviewCols.value = res.data?.columns||[]; dsPreviewRows.value = res.data?.rows||[]; break + case 'sql': + if (ds.dataSourceId && ds.customSql) { + const r2 = await request.post('/report/dataview/preview', { id: 0, sql: ds.customSql, dataSourceId: Number(ds.dataSourceId), params: {} }) + dsPreviewCols.value = r2.data?.columns||[]; dsPreviewRows.value = r2.data?.rows||[] + } + break + } + } catch {} finally { dsPreviewLoading.value = false } +} + function openDsDialog() { Object.assign(dsForm, { name:'', sourceType:'dataview', sourceRef:'', customSql:'', dataSourceId:'' }) dsDialogVisible.value = true From 6dbb62c39db65cefdf09d44289d4c9171389a729 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 18:15:31 +0800 Subject: [PATCH 084/153] =?UTF-8?q?fix:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=B5=81=E7=A8=8B=E5=88=97=E8=A1=A8API?= =?UTF-8?q?=E6=94=B9=E4=B8=BAPOST=20/flow/definition/page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- JuggleNet6.Frontend/src/views/report/ReportDesigner.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 67a464e..4d8b5cc 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -281,7 +281,7 @@ async function loadDsList() { try { const res = await request.get('/system/datasource/list'); dsList.value = res.data||[] } catch {} } async function loadFlowList() { - try { const res = await request.get('/flow/definition/list'); flowList.value = res.data||[] } catch {} + try { const res = await request.post('/flow/definition/page', { pageNum:1, pageSize:200 }); flowList.value = res.data?.records||[] } catch {} } async function loadApiList() { try { const res = await request.post('/suite/api/list', { suiteCode: '' }); apiList.value = res.data||[] } catch {} From 264e110b2b2e254cd601fa1ba0916a02606ca800 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 18:26:15 +0800 Subject: [PATCH 085/153] =?UTF-8?q?fix:=20=E6=8A=A5=E8=A1=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=9B=86=E5=AD=97=E6=AE=B5=E5=8A=A0=E8=BD=BD(?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89SQL+=E6=95=B0=E6=8D=AE=E8=A7=86?= =?UTF-8?q?=E5=9B=BE)+UI=E7=B4=A7=E5=87=91=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. DataViewPreviewRequest新增DataSourceId/Sql支持ad-hoc查询 2. SQL校验修复:只拦截词边界的危险关键字,允许SELECT含列名update 3. 左侧数据集卡片布局优化:名称占满+图标紧凑排列 Co-Authored-By: Claude Opus 4.7 --- .../Controllers/Api/DataViewController.cs | 18 ++++++++-- .../Services/Impl/ReportExecutionService.cs | 9 +++-- .../src/views/report/ReportDesigner.vue | 36 +++++++++++-------- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/Juggle.Api/Controllers/Api/DataViewController.cs b/Juggle.Api/Controllers/Api/DataViewController.cs index 0f15a49..f7323d4 100644 --- a/Juggle.Api/Controllers/Api/DataViewController.cs +++ b/Juggle.Api/Controllers/Api/DataViewController.cs @@ -71,9 +71,19 @@ public async Task Preview([FromBody] DataViewPreviewRequest req) { try { - var dv = await _db.Set().FindAsync(req.Id); - if (dv == null) return ApiResult.Fail("数据视图不存在"); - var dt = await _reportExec.ExecuteQuery(dv.DataSourceId, dv.Sql!, req.Params); + long dsId; string sql; + if (req.DataSourceId > 0 && !string.IsNullOrEmpty(req.Sql)) + { + // 自定义SQL模式(报表设计器用) + dsId = req.DataSourceId; sql = req.Sql!; + } + else + { + var dv = await _db.Set().FindAsync(req.Id); + if (dv == null) return ApiResult.Fail("数据视图不存在"); + dsId = dv.DataSourceId; sql = dv.Sql!; + } + var dt = await _reportExec.ExecuteQuery(dsId, sql, req.Params); var rows = new List>(); foreach (System.Data.DataRow row in dt.Rows) { @@ -91,5 +101,7 @@ public async Task Preview([FromBody] DataViewPreviewRequest req) public class DataViewPreviewRequest { public long Id { get; set; } + public long DataSourceId { get; set; } + public string? Sql { get; set; } public Dictionary? Params { get; set; } } diff --git a/Juggle.Application/Services/Impl/ReportExecutionService.cs b/Juggle.Application/Services/Impl/ReportExecutionService.cs index e412572..ff15ad4 100644 --- a/Juggle.Application/Services/Impl/ReportExecutionService.cs +++ b/Juggle.Application/Services/Impl/ReportExecutionService.cs @@ -41,13 +41,16 @@ public async Task ExecuteQuery(long dataSourceId, string sql, Diction /// SQL 安全校验:禁止写操作关键字 public static void ValidateSql(string sql) { - var upper = sql.ToUpper().Trim(); - var dangerous = new[] { "DROP ", "DELETE ", "UPDATE ", "INSERT ", "ALTER ", "CREATE ", "TRUNCATE ", "EXEC ", "EXECUTE " }; + var upper = " " + sql.ToUpper().Trim() + " "; + var dangerous = new[] { " DROP ", " DELETE ", " INSERT ", " ALTER ", " CREATE ", " TRUNCATE ", " EXEC ", " EXECUTE " }; foreach (var kw in dangerous) { - if (upper.StartsWith(kw) || upper.Contains(";" + kw) || upper.Contains(" " + kw)) + if (upper.Contains(kw)) throw new Exception($"SQL 包含不允许的操作: {kw.Trim()}"); } + // UPDATE 仅拦截不以 SELECT...FOR UPDATE 开头的 + if (upper.Contains(" UPDATE ") && !upper.TrimStart().StartsWith("SELECT")) + throw new Exception("SQL 包含不允许的操作: UPDATE"); } /// 渲染报表为HTML diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 4d8b5cc..f0e8ce0 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -20,21 +20,27 @@
点击 + 添加数据集
-
-
- {{ ds.expanded?'▼':'▶' }} {{ ds.name }} - {{ sourceLabel(ds.sourceType) }} - - - - × +
+
+ {{ ds.expanded?'▼':'▶' }} {{ ds.name }} + {{ sourceLabel(ds.sourceType) }} + + + + + +
-
-
加载中...
-
{{ f }}
-
点击 ↻ 刷新加载字段
+
+
加载中...
+
+
{{ f }}
+
+ 暂无字段,点击 ↻ 刷新 +
+
@@ -226,7 +232,7 @@ import { ref, reactive, onMounted, computed } from 'vue' import { useRoute, useRouter } from 'vue-router' import { ElMessage } from 'element-plus' -import { Edit, View, Refresh, Loading } from '@element-plus/icons-vue' +import { Edit, View, Refresh, Loading, Close } from '@element-plus/icons-vue' import request from '../../utils/request' const route = useRoute() From bca0a7170c4ab319a11bc7d94a1fdd43dcb41562 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Tue, 26 May 2026 18:29:20 +0800 Subject: [PATCH 086/153] =?UTF-8?q?fix:=20=E5=B7=A6=E4=BE=A7=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=9B=86=E9=9D=A2=E6=9D=BF=E5=8A=A0=E5=AE=BD(170?= =?UTF-8?q?=E2=86=92220px)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- JuggleNet6.Frontend/src/views/report/ReportDesigner.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index f0e8ce0..a6d3623 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -576,7 +576,7 @@ function colLetter(n:number):string { return String.fromCharCode(65+n) } .designer-container{height:100vh;display:flex;flex-direction:column;background:#f5f5f5} .toolbar{display:flex;justify-content:space-between;align-items:center;padding:6px 12px;background:#001529;color:#fff;flex-shrink:0} .designer-body{flex:1;display:flex;overflow:hidden} -.left-panel{width:170px;border-right:1px solid #ddd;padding:8px;overflow-y:auto;background:#fff;flex-shrink:0} +.left-panel{width:220px;border-right:1px solid #ddd;padding:6px;overflow-y:auto;background:#fff;flex-shrink:0} .center-panel{flex:1;display:flex;flex-direction:column;overflow:hidden} .style-toolbar{display:flex;gap:4px;padding:4px 8px;background:#fff;border-bottom:1px solid #ddd;flex-wrap:wrap;align-items:center;flex-shrink:0} .right-panel{width:210px;border-left:1px solid #ddd;padding:8px;overflow-y:auto;background:#fff;flex-shrink:0} From 43dc6ee0f91c48ac336895b249f4779d35dabb6a Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 27 May 2026 08:38:07 +0800 Subject: [PATCH 087/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E5=8D=95=E5=85=83=E6=A0=BC=E5=8F=AF=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=BE=93=E5=85=A5=E4=BF=AE=E6=94=B9+Delete/Backspace?= =?UTF-8?q?=E6=B8=85=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index a6d3623..2552ea2 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -102,8 +102,12 @@ :colspan="cellSpan(r-1,c-1).colspan" :rowspan="cellSpan(r-1,c-1).rowspan" :style="getCellStyle(r-1,c-1)" + contenteditable="true" @mousedown="onCellMouseDown($event, r-1, c-1)" - @dblclick="onCellDoubleClick(r-1,c-1)" + @input="onCellInput($event, r-1, c-1)" + @blur="onCellBlur(r-1, c-1)" + @keydown.delete="onCellDelete(r-1, c-1)" + @keydown.backspace="onCellDelete(r-1, c-1)" @dragover.prevent @drop="onCellDrop($event, r-1, c-1)" >{{ getCellText(r-1,c-1) }} @@ -464,7 +468,17 @@ function commitEdit() { } } -function onCellDoubleClick(r:number,c:number) { selR.value=r; selC.value=c; cellValue.value=getCell(r,c)?.value||'' } +function onCellInput(e:Event, r:number, c:number) { + const text = (e.target as HTMLElement).innerText || '' + const key=`${r},${c}`, existing=cells.value[key]||{} + cells.value[key]={...existing, value: text} + if (selR.value===r && selC.value===c) cellValue.value = text +} +function onCellBlur(_r:number, _c:number) { commitEdit() } +function onCellDelete(r:number, c:number) { + const key=`${r},${c}`; cells.value[key] = { value: '' } + if (selR.value===r && selC.value===c) cellValue.value = '' +} function selectRow(r:number, _e:MouseEvent) { commitEdit() From 727d641869b76c52ab1427daba4452960f5c8666 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 27 May 2026 08:49:26 +0800 Subject: [PATCH 088/153] =?UTF-8?q?fix:=20=E5=8D=95=E5=85=83=E6=A0=BC?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=85=89=E6=A0=87=E8=B7=B3=E5=8A=A8-?= =?UTF-8?q?=E4=BB=85=E5=9C=A8=E5=A4=B1=E7=84=A6=E6=97=B6=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=80=8C=E9=9D=9E=E6=AF=8F=E6=AC=A1=E8=BE=93?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 2552ea2..a33d613 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -468,15 +468,25 @@ function commitEdit() { } } -function onCellInput(e:Event, r:number, c:number) { - const text = (e.target as HTMLElement).innerText || '' - const key=`${r},${c}`, existing=cells.value[key]||{} - cells.value[key]={...existing, value: text} - if (selR.value===r && selC.value===c) cellValue.value = text +function onCellInput(_e:Event, _r:number, _c:number) { + // 不立即更新cells数据,避免Vue重渲染导致光标跳动 + // 失焦时通过onCellBlur自动保存 +} +function onCellBlur(_r:number, _c:number) { + // 从DOM读取实际文本,保存到cells + if (selR.value<0||selC.value<0) return + const td = document.querySelector(`td[data-r="${selR.value}"][data-c="${selC.value}"]`) + if (!td) return + const text = td.textContent || '' + const key=`${selR.value},${selC.value}`, existing=cells.value[key]||{} + if (existing.value !== text) { + cells.value[key]={...existing, value: text} + cellValue.value = text + } } -function onCellBlur(_r:number, _c:number) { commitEdit() } function onCellDelete(r:number, c:number) { - const key=`${r},${c}`; cells.value[key] = { value: '' } + const key=`${r},${c}`, existing=cells.value[key]||{} + cells.value[key]={...existing, value: ''} if (selR.value===r && selC.value===c) cellValue.value = '' } From b81b9b65d13f82d6adc1cd6c0dc88f0197ec8a9a Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 27 May 2026 09:08:41 +0800 Subject: [PATCH 089/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8A4=E9=A1=B5=E9=9D=A2=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E6=95=88=E6=9E=9C(=E7=99=BD=E7=BA=B8+=E7=81=B0=E5=BA=95+?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E7=BA=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index a33d613..5c8e69f 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -70,7 +70,8 @@ Ctrl多选
- +
+
@@ -114,6 +115,10 @@
+
+ —— 分页线 —— +
+
@@ -276,6 +281,15 @@ const previewValues = ref>({}) const canMerge = computed(() => selectedCells.value.length >= 2) const hasSelection = computed(() => selR.value >= 0 && selC.value >= 0) +const pageSizes: Record = { A4: [794, 1123], A3: [1123, 1587], Letter: [816, 1056], Legal: [816, 1344] } +const pageWidth = computed(() => pageOrientation.value === 'landscape' ? (pageSizes[pageSize.value]||pageSizes.A4)[1] : (pageSizes[pageSize.value]||pageSizes.A4)[0]) +const pageHeight = computed(() => pageOrientation.value === 'landscape' ? (pageSizes[pageSize.value]||pageSizes.A4)[0] : (pageSizes[pageSize.value]||pageSizes.A4)[1]) +const showPageBreak = computed(() => { + let totalH = 0 + for (let r=0; r pageHeight.value +}) + onMounted(() => { document.addEventListener('keydown', (e) => { if (e.key==='Control') ctrlDown.value = true }) document.addEventListener('keyup', (e) => { if (e.key==='Control') ctrlDown.value = false }) @@ -606,8 +620,11 @@ function colLetter(n:number):string { return String.fromCharCode(65+n) } .right-panel{width:210px;border-left:1px solid #ddd;padding:8px;overflow-y:auto;background:#fff;flex-shrink:0} .field-item{padding:4px 8px;margin-bottom:2px;background:#e6f7ff;border-radius:4px;cursor:grab;font-size:12px} .field-item:hover{background:#bae7ff} -.grid-wrapper{flex:1;overflow:auto;padding:8px;background:#e8e8e8} -.rpt-grid{border-collapse:collapse;background:#fff;font-size:12px;table-layout:fixed} +.grid-wrapper{flex:1;overflow:auto;padding:16px;background:#818181;display:flex;justify-content:center} +.rpt-page{background:#fff;box-shadow:0 2px 12px rgba(0,0,0,0.3);position:relative;padding-bottom:0} +.page-break{position:absolute;left:0;right:0;border-top:2px dashed #ff4d4f;text-align:center;pointer-events:none} +.page-break-label{font-size:10px;color:#ff4d4f;background:#818181;padding:0 8px;position:relative;top:-8px} +.rpt-grid{border-collapse:collapse;background:#fff;font-size:12px;table-layout:fixed;width:100%} .row-header-col{width:36px} .corner-cell{width:36px;height:22px;background:#f0f0f0;border-right:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;position:sticky;top:0;left:0;z-index:3} .col-header{background:#f0f0f0;border-right:1px solid #d9d9d9;border-bottom:1px solid #d9d9d9;text-align:center;font-size:10px;color:#666;height:22px;position:sticky;top:0;z-index:2;cursor:pointer;user-select:none;position:relative} From beff9c69e22c5cf1e5b67a64b704be2c90717f21 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 27 May 2026 09:14:45 +0800 Subject: [PATCH 090/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=8F=92=E5=85=A5=E8=A1=8C/=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E8=A1=8C/=E6=8F=92=E5=85=A5=E5=88=97/=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=88=97(=E5=B7=A5=E5=85=B7=E6=A0=8F=E6=8C=89?= =?UTF-8?q?=E9=92=AE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index 5c8e69f..ea2bc41 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -67,6 +67,12 @@ 边框 合并 拆分 + ↑行 + ↓行 + 删行 + ←列 + →列 + 删列 Ctrl多选
@@ -580,6 +586,67 @@ function startRowResize(e:MouseEvent, ri:number) { function autoFitCol(c:number) { colWidths.value[c]=200 } function autoFitRow(r:number) { rowHeights.value[r]=30 } +function insertRow(dir: number) { + if (selR.value<0) selR.value = maxRows.value - 1 + const r = dir<0 ? selR.value : selR.value + 1 + const newCells: Record = {} + for (const [key, cell] of Object.entries(cells.value)) { + const [cr, cc] = key.split(',').map(Number) + if (cr >= r) newCells[`${cr+1},${cc}`] = cell + else newCells[key] = cell + } + cells.value = newCells + rowHeights.value.splice(r, 0, 25) + maxRows.value++ + if (dir > 0) selR.value++ +} + +function deleteRow() { + if (selR.value<0 || maxRows.value<=1) return + const r = selR.value + const newCells: Record = {} + for (const [key, cell] of Object.entries(cells.value)) { + const [cr, cc] = key.split(',').map(Number) + if (cr > r) newCells[`${cr-1},${cc}`] = cell + else if (cr < r) newCells[key] = cell + // cr === r: drop + } + cells.value = newCells + rowHeights.value.splice(r, 1) + maxRows.value-- + selR.value = Math.min(selR.value, maxRows.value - 1) +} + +function insertCol(dir: number) { + if (selC.value<0) selC.value = maxCols.value - 1 + const c = dir<0 ? selC.value : selC.value + 1 + const newCells: Record = {} + for (const [key, cell] of Object.entries(cells.value)) { + const [cr, cc] = key.split(',').map(Number) + if (cc >= c) newCells[`${cr},${cc+1}`] = cell + else newCells[key] = cell + } + cells.value = newCells + colWidths.value.splice(c, 0, 100) + maxCols.value++ + if (dir > 0) selC.value++ +} + +function deleteCol() { + if (selC.value<0 || maxCols.value<=1) return + const c = selC.value + const newCells: Record = {} + for (const [key, cell] of Object.entries(cells.value)) { + const [cr, cc] = key.split(',').map(Number) + if (cc > c) newCells[`${cr},${cc-1}`] = cell + else if (cc < c) newCells[key] = cell + } + cells.value = newCells + colWidths.value.splice(c, 1) + maxCols.value-- + selC.value = Math.min(selC.value, maxCols.value - 1) +} + function onCellDrop(e:DragEvent, r:number, c:number) { const field = e.dataTransfer?.getData('field'); if (field) { const key=`${r},${c}`,ex=cells.value[key]||{}; cells.value[key]={...ex,value:field} } } function onGridScroll() {} From ffeb3d7b1066b77cc74a71fa695e01a2c005cbe8 Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Wed, 27 May 2026 09:20:54 +0800 Subject: [PATCH 091/153] =?UTF-8?q?feat:=20=E6=8A=A5=E8=A1=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=92=A4=E9=94=80/=E9=87=8D=E5=81=9A(Ctrl+Z/?= =?UTF-8?q?Y)+=E5=B7=A5=E5=85=B7=E6=A0=8F=E2=86=A9=E2=86=AA=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../src/views/report/ReportDesigner.vue | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue index ea2bc41..c9ba7ad 100644 --- a/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue +++ b/JuggleNet6.Frontend/src/views/report/ReportDesigner.vue @@ -73,6 +73,8 @@ ←列 →列 删列 + + Ctrl多选
@@ -287,6 +289,43 @@ const previewValues = ref>({}) const canMerge = computed(() => selectedCells.value.length >= 2) const hasSelection = computed(() => selR.value >= 0 && selC.value >= 0) +// 撤销/重做 +const MAX_HISTORY = 50 +const undoStack = ref([]) +const redoStack = ref([]) +const canUndo = computed(() => undoStack.value.length > 0) +const canRedo = computed(() => redoStack.value.length > 0) + +function pushHistory() { + const snap = JSON.stringify({ cells: cells.value, rows: rowHeights.value, cols: colWidths.value, maxR: maxRows.value, maxC: maxCols.value }) + undoStack.value.push(snap) + if (undoStack.value.length > MAX_HISTORY) undoStack.value.shift() + redoStack.value = [] +} + +function undo() { + if (!canUndo.value) return + redoStack.value.push(JSON.stringify({ cells: cells.value, rows: rowHeights.value, cols: colWidths.value, maxR: maxRows.value, maxC: maxCols.value })) + restoreState(undoStack.value.pop()!) +} + +function redo() { + if (!canRedo.value) return + undoStack.value.push(JSON.stringify({ cells: cells.value, rows: rowHeights.value, cols: colWidths.value, maxR: maxRows.value, maxC: maxCols.value })) + restoreState(redoStack.value.pop()!) +} + +function restoreState(snap: string) { + try { + const s = JSON.parse(snap) + cells.value = s.cells || {} + rowHeights.value = s.rows || [] + colWidths.value = s.cols || [] + maxRows.value = s.maxR || 10 + maxCols.value = s.maxC || 6 + } catch {} +} + const pageSizes: Record = { A4: [794, 1123], A3: [1123, 1587], Letter: [816, 1056], Legal: [816, 1344] } const pageWidth = computed(() => pageOrientation.value === 'landscape' ? (pageSizes[pageSize.value]||pageSizes.A4)[1] : (pageSizes[pageSize.value]||pageSizes.A4)[0]) const pageHeight = computed(() => pageOrientation.value === 'landscape' ? (pageSizes[pageSize.value]||pageSizes.A4)[0] : (pageSizes[pageSize.value]||pageSizes.A4)[1]) @@ -297,7 +336,11 @@ const showPageBreak = computed(() => { }) onMounted(() => { - document.addEventListener('keydown', (e) => { if (e.key==='Control') ctrlDown.value = true }) + document.addEventListener('keydown', (e) => { + if (e.key==='Control') ctrlDown.value = true + if (e.ctrlKey && e.key==='z') { e.preventDefault(); undo() } + if (e.ctrlKey && e.key==='y') { e.preventDefault(); redo() } + }) document.addEventListener('keyup', (e) => { if (e.key==='Control') ctrlDown.value = false }) loadDvList(); loadDsList(); loadFlowList(); loadApiList() if (rptId>0) loadReport() @@ -484,6 +527,7 @@ function commitEdit() { if (selR.value<0||selC.value<0) return const key=`${selR.value},${selC.value}`, existing=cells.value[key]||{} if (existing.value !== cellValue.value) { + pushHistory() cells.value[key]={...existing, value: cellValue.value} } } @@ -500,11 +544,13 @@ function onCellBlur(_r:number, _c:number) { const text = td.textContent || '' const key=`${selR.value},${selC.value}`, existing=cells.value[key]||{} if (existing.value !== text) { + pushHistory() cells.value[key]={...existing, value: text} cellValue.value = text } } function onCellDelete(r:number, c:number) { + pushHistory() const key=`${r},${c}`, existing=cells.value[key]||{} cells.value[key]={...existing, value: ''} if (selR.value===r && selC.value===c) cellValue.value = '' @@ -542,6 +588,7 @@ function updateCellValue() { function updateRowType() { if (selR.value>=0) rowHeights.value[selR.value]=rowType.value==='title'?35:rowType.value==='header'?28:rowType.value==='data'?'data':25 } function applyStyle(prop:string, val?:any) { + pushHistory() forEachSelected((r,c) => { const key=`${r},${c}`, existing=cells.value[key]||{value:''} const style = existing.style||{} @@ -553,10 +600,11 @@ function applyStyle(prop:string, val?:any) { }) } -function toggleBorder() { forEachSelected((r,c)=>{const key=`${r},${c}`,ex=cells.value[key]||{value:''},s=ex.style||{}; s.border=s.border===false?true:false; cells.value[key]={...ex,style:s}}) } +function toggleBorder() { pushHistory(); forEachSelected((r,c)=>{const key=`${r},${c}`,ex=cells.value[key]||{value:''},s=ex.style||{}; s.border=s.border===false?true:false; cells.value[key]={...ex,style:s}}) } function mergeSelected() { if (selectedCells.value.length<2) return + pushHistory() const rs = selectedCells.value.map(([r])=>r), cs = selectedCells.value.map(([,c])=>c) const r1=Math.min(...rs), r2=Math.max(...rs), c1=Math.min(...cs), c2=Math.max(...cs) const base = cells.value[`${r1},${c1}`]||{value:''} @@ -565,6 +613,7 @@ function mergeSelected() { function splitSelected() { if (selR.value<0||selC.value<0) return + pushHistory() const key=`${selR.value},${selC.value}`, c=cells.value[key] if (c) { delete c.colspan; delete c.rowspan; cells.value[key]={...c} } } @@ -587,6 +636,7 @@ function autoFitCol(c:number) { colWidths.value[c]=200 } function autoFitRow(r:number) { rowHeights.value[r]=30 } function insertRow(dir: number) { + pushHistory() if (selR.value<0) selR.value = maxRows.value - 1 const r = dir<0 ? selR.value : selR.value + 1 const newCells: Record = {} @@ -603,6 +653,7 @@ function insertRow(dir: number) { function deleteRow() { if (selR.value<0 || maxRows.value<=1) return + pushHistory() const r = selR.value const newCells: Record = {} for (const [key, cell] of Object.entries(cells.value)) { @@ -618,6 +669,7 @@ function deleteRow() { } function insertCol(dir: number) { + pushHistory() if (selC.value<0) selC.value = maxCols.value - 1 const c = dir<0 ? selC.value : selC.value + 1 const newCells: Record = {} @@ -634,6 +686,7 @@ function insertCol(dir: number) { function deleteCol() { if (selC.value<0 || maxCols.value<=1) return + pushHistory() const c = selC.value const newCells: Record = {} for (const [key, cell] of Object.entries(cells.value)) { From 9145c60a1fe898aa152d956261105dfe9218614a Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Thu, 3 Sep 2026 15:58:32 +0800 Subject: [PATCH 092/153] =?UTF-8?q?feat:=20=E6=9D=A1=E4=BB=B6=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=89=A9=E5=B1=95-?= =?UTF-8?q?=E6=94=AF=E6=8C=81&&=20||=20=E6=8B=AC=E5=8F=B7/=E5=8F=8C?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E6=AF=94=E8=BE=83/=E5=AD=90=E5=B1=9E?= =?UTF-8?q?=E6=80=A7/=E6=95=B0=E7=BB=84length=E4=B8=8E=E5=85=83=E7=B4=A0?= =?UTF-8?q?=E5=8F=96=E5=80=BC+=E8=AE=BE=E8=AE=A1=E5=99=A8=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E5=B8=AE=E5=8A=A9=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- DesignDoc.md | 25 +- .../Engine/ConditionExpressionEvaluator.cs | 367 ++++++++++++++++++ .../NodeExecutors/ConditionNodeExecutor.cs | 54 +-- .../src/views/flow/FlowDesign.vue | 44 ++- 4 files changed, 427 insertions(+), 63 deletions(-) create mode 100644 Juggle.Domain/Engine/ConditionExpressionEvaluator.cs diff --git a/DesignDoc.md b/DesignDoc.md index b4c8c7a..7c56025 100644 --- a/DesignDoc.md +++ b/DesignDoc.md @@ -615,22 +615,19 @@ public class HttpApiCaller ### 3.5 条件表达式求值 -支持的表达式类型: +由 `Juggle.Domain/Engine/ConditionExpressionEvaluator.cs` 递归下降解析,支持的语法: -| 操作符 | 数据类型 | 示例 | +| 类型 | 语法 | 示例 | |--------|---------|------| -| equal | 所有 | env_name=="张三" | -| notEqual | 所有 | env_name!="张三" | -| greaterThan | 数字/日期 | env_age>18 | -| greaterThanOrEqual | 数字/日期 | env_age>=18 | -| lessThan | 数字/日期 | env_age<18 | -| lessThanOrEqual | 数字/日期 | env_age<=18 | -| isEmpty | 字符串/集合 | string.empty(env_name) | -| isNotEmpty | 字符串/集合 | !string.empty(env_name) | -| contains | 字符串 | string.contains(s1,s2) | -| notContains | 字符串 | !string.contains(s1,s2) | - -C# 实现方案:使用 `System.Linq.Dynamic.Core` 库或手动解析 `conditionExpressions` 数组。 +| 比较运算 | == != > < >= <= | `env_score >= 60`、`input_name == '张三'` | +| 逻辑运算 | &&(且) \|\|(或) !(非) 括号 | `env_score >= 60 && env_score < 90`、`(env_a > 1 \|\| env_b > 2) && env_c == 'yes'` | +| 变量对变量 | 左右两边都可以是变量 | `env_total >= input_min` | +| 子属性 | 变量.属性 | `input_user.name == '张三'` | +| 数组长度 | 变量.length | `input_list.length > 0` | +| 数组元素 | 变量[索引](从 0 开始),可继续取子属性 | `input_list[0].name == '张三'` | +| 裸变量 | 无比较符时按真值判断 | `env_flag` 等价于 `env_flag == true` | + +字面量支持:数字、单/双引号字符串、`true`/`false`/`null`。解析失败或变量缺失时表达式返回 false(走默认分支)。 --- diff --git a/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs b/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs new file mode 100644 index 0000000..8358273 --- /dev/null +++ b/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs @@ -0,0 +1,367 @@ +using System.Text.Json; + +namespace Juggle.Domain.Engine; + +/// +/// 条件表达式求值器 — 递归下降解析。 +/// 语法支持: +/// 逻辑运算: &&(且) ||(或) !(非) 括号改变优先级 +/// 比较运算: == != > < >= <= +/// 操作数: 字面量(数字 / '字符串' / "字符串" / true / false / null) +/// 或变量路径(左右两边都可以是变量) +/// 变量路径: env_user.name(子属性)、input_list.length(数组长度)、 +/// input_list[0].name(数组指定元素及其子属性,索引从 0 开始) +/// 无比较符时按真值判断: 裸变量 / !变量 / 括号整体 +/// +public class ConditionExpressionEvaluator +{ + private readonly string _expr; + private int _pos; + private readonly FlowContext _context; + + private ConditionExpressionEvaluator(string expression, FlowContext context) + { + _expr = expression; + _context = context; + } + + /// 求值入口。解析失败或变量缺失时返回 false。 + public static bool Evaluate(string expression, FlowContext context) + { + if (string.IsNullOrWhiteSpace(expression)) return false; + try + { + var parser = new ConditionExpressionEvaluator(expression, context); + var val = parser.ParseOr(); + parser.SkipWhitespace(); + // 存在无法识别的残余字符视为表达式错误 + if (parser._pos < parser._expr.Length) return false; + return IsTruthy(val); + } + catch + { + return false; + } + } + + // ===== 递归下降解析 ===== + + private object? ParseOr() + { + var left = ParseAnd(); + SkipWhitespace(); + while (Peek() == '|' && Peek(1) == '|') + { + _pos += 2; + var right = ParseAnd(); + left = IsTruthy(left) ? left : right; // 短路 + } + return left; + } + + private object? ParseAnd() + { + var left = ParseUnary(); + SkipWhitespace(); + while (Peek() == '&' && Peek(1) == '&') + { + _pos += 2; + var right = ParseUnary(); + left = IsTruthy(left) ? right : left; // 短路 + } + return left; + } + + private object? ParseUnary() + { + SkipWhitespace(); + if (Peek() == '!') + { + _pos++; + return !IsTruthy(ParseUnary()); + } + return ParseCompare(); + } + + private object? ParseCompare() + { + var left = ParseOperand(); + SkipWhitespace(); + + string? op = null; + if (Peek() == '=' && Peek(1) == '=') { _pos += 2; op = "=="; } + else if (Peek() == '!' && Peek(1) == '=') { _pos += 2; op = "!="; } + else if (Peek() == '>' && Peek(1) == '=') { _pos += 2; op = ">="; } + else if (Peek() == '<' && Peek(1) == '=') { _pos += 2; op = "<="; } + else if (Peek() == '>') { _pos++; op = ">"; } + else if (Peek() == '<') { _pos++; op = "<"; } + else return left; // 无比较符,按真值判断 + + var right = ParseOperand(); + return Compare(left, right, op); + } + + private object? ParseOperand() + { + SkipWhitespace(); + if (_pos >= _expr.Length) throw new InvalidOperationException("表达式不完整"); + + var c = _expr[_pos]; + + // 数字字面量 + if (char.IsDigit(c) || (c == '.' && _pos + 1 < _expr.Length && char.IsDigit(_expr[_pos + 1]))) + { + var start = _pos; + while (_pos < _expr.Length && (char.IsDigit(_expr[_pos]) || _expr[_pos] == '.')) _pos++; + var numStr = _expr[start.._pos]; + if (long.TryParse(numStr, out var l)) return l; + if (double.TryParse(numStr, out var d)) return d; + return null; + } + + // 字符串字面量 + if (c == '\'' || c == '"') + { + var quote = _expr[_pos++]; + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != quote) _pos++; + var str = _expr[start.._pos]; + if (_pos < _expr.Length) _pos++; + return str; + } + + // 括号 + if (c == '(') + { + _pos++; + var val = ParseOr(); + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ')') _pos++; + else throw new InvalidOperationException("括号不匹配"); + return val; + } + + // 变量路径 + var root = ParseIdentifier(); + if (string.IsNullOrEmpty(root)) + throw new InvalidOperationException($"无法识别的字符 '{c}'"); + + return ResolvePath(root); + } + + // ===== 变量路径解析 ===== + + private object? ResolvePath(string root) + { + object? current = _context.GetVariable(root); + + while (true) + { + SkipWhitespace(); + + // 数组索引 [n] + if (_pos < _expr.Length && _expr[_pos] == '[') + { + _pos++; + SkipWhitespace(); + var numStart = _pos; + while (_pos < _expr.Length && char.IsDigit(_expr[_pos])) _pos++; + if (numStart == _pos) throw new InvalidOperationException("数组索引必须为数字"); + var idx = int.Parse(_expr[numStart.._pos]); + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ']') _pos++; + current = GetArrayElement(current, idx); + continue; + } + + // 子属性 .prop / .length + if (_pos < _expr.Length && _expr[_pos] == '.') + { + _pos++; + SkipWhitespace(); + var prop = ParseIdentifier(); + if (string.IsNullOrEmpty(prop)) throw new InvalidOperationException("缺少属性名"); + current = prop.Equals("length", StringComparison.OrdinalIgnoreCase) + ? GetArrayLength(current) + : GetProperty(current, prop); + continue; + } + + break; + } + + return current; + } + + private static object? GetProperty(object? obj, string prop) + { + switch (obj) + { + case Dictionary dict: + return dict.TryGetValue(prop, out var dv) ? dv : null; + case JsonElement json when json.ValueKind == JsonValueKind.Object: + return json.TryGetProperty(prop, out var next) ? ToNative(next) : null; + default: + return null; + } + } + + private static object? GetArrayElement(object? obj, int index) + { + switch (obj) + { + case List list: + return index >= 0 && index < list.Count ? list[index] : null; + case object[] arr: + return index >= 0 && index < arr.Length ? arr[index] : null; + case JsonElement json when json.ValueKind == JsonValueKind.Array: + { + var i = 0; + foreach (var item in json.EnumerateArray()) + { + if (i++ == index) return ToNative(item); + } + return null; + } + default: + return null; + } + } + + private static object? GetArrayLength(object? obj) + { + switch (obj) + { + case List list: return (long)list.Count; + case object[] arr: return (long)arr.Length; + case JsonElement json when json.ValueKind == JsonValueKind.Array: return (long)json.GetArrayLength(); + default: return null; + } + } + + /// 将 JsonElement 转为 .NET 原生类型,便于比较运算 + private static object? ToNative(JsonElement el) + { + return el.ValueKind switch + { + JsonValueKind.String => el.GetString(), + JsonValueKind.Number => el.TryGetInt64(out var l) ? l : el.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => el // 对象/数组保留原 JsonElement,继续支持 .prop / [i] / .length + }; + } + + // ===== 比较与真值 ===== + + private static object Compare(object? left, object? right, string op) + { + // null 比较 + if (left == null || right == null) + return op switch + { + "==" => left == null && right == null, + "!=" => !(left == null && right == null), + _ => false + }; + + // 布尔比较(布尔值或 "true"/"false" 字符串) + if (TryAsBool(left, out var lb) && TryAsBool(right, out var rb)) + return op switch { "==" => lb == rb, "!=" => lb != rb, _ => false }; + + // 数字比较(任意一边可解析为数字时按数值比较) + if (IsNumeric(left) && IsNumeric(right)) + { + var l = ToDouble(left); + var r = ToDouble(right); + return op switch + { + "==" => l == r, + "!=" => l != r, + ">" => l > r, + "<" => l < r, + ">=" => l >= r, + "<=" => l <= r, + _ => false + }; + } + + // 字符串比较(==/!= 忽略大小写,保持历史行为) + var ls = left.ToString() ?? ""; + var rs = right.ToString() ?? ""; + return op switch + { + "==" => string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), + "!=" => !string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), + ">" => string.Compare(ls, rs, StringComparison.Ordinal) > 0, + "<" => string.Compare(ls, rs, StringComparison.Ordinal) < 0, + ">=" => string.Compare(ls, rs, StringComparison.Ordinal) >= 0, + "<=" => string.Compare(ls, rs, StringComparison.Ordinal) <= 0, + _ => false + }; + } + + private static bool IsTruthy(object? v) + { + switch (v) + { + case null: return false; + case bool b: return b; + case string s: return s.Equals("true", StringComparison.OrdinalIgnoreCase) || s == "1"; + case long l: return l != 0; + case int i: return i != 0; + case double d: return d != 0; + case decimal m: return m != 0; + default: return false; + } + } + + private static bool TryAsBool(object? v, out bool result) + { + if (v is bool b) { result = b; return true; } + if (v is string s && bool.TryParse(s, out var parsed)) { result = parsed; return true; } + result = false; + return false; + } + + private static bool IsNumeric(object? v) + { + return v switch + { + byte or sbyte or short or ushort or int or uint or long or ulong + or float or double or decimal => true, + string s => double.TryParse(s, out _), + _ => false + }; + } + + private static double ToDouble(object? v) + { + return v switch + { + byte x => x, sbyte x => x, short x => x, ushort x => x, + int x => x, uint x => x, long x => x, ulong x => x, + float x => x, double x => x, decimal x => (double)x, + string s => double.TryParse(s, out var d) ? d : 0, + _ => 0 + }; + } + + // ===== 基础解析工具 ===== + + private string ParseIdentifier() + { + var start = _pos; + while (_pos < _expr.Length && (char.IsLetterOrDigit(_expr[_pos]) || _expr[_pos] == '_')) + _pos++; + return _expr[start.._pos]; + } + + private char Peek(int ahead = 0) => _pos + ahead < _expr.Length ? _expr[_pos + ahead] : '\0'; + + private void SkipWhitespace() + { + while (_pos < _expr.Length && char.IsWhiteSpace(_expr[_pos])) _pos++; + } +} diff --git a/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs b/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs index 421200a..bd72ddd 100644 --- a/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs +++ b/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs @@ -1,6 +1,12 @@ namespace Juggle.Domain.Engine.NodeExecutors; -/// CONDITION 节点执行器:按条件表达式选择下一个分支 +/// +/// CONDITION 节点执行器:按条件表达式选择下一个分支。 +/// 表达式语法(见 ConditionExpressionEvaluator): +/// 支持 && || ! 括号、== != > < >= <= 比较 +/// 左右两边都可以是变量/字面量 +/// 支持子属性(env_user.name)、数组长度(input_list.length)、数组元素(input_list[0].name) +/// public class ConditionNodeExecutor : INodeExecutor { public Task ExecuteAsync(FlowNode node, FlowContext context) @@ -31,50 +37,6 @@ public class ConditionNodeExecutor : INodeExecutor return Task.FromResult(defaultOutgoing); } - /// - /// 简单条件表达式求值,支持: - /// 变量 == 值 - /// 变量 != 值 - /// 变量 > 数值 - /// 变量 < 数值 - /// 变量 >= 数值 - /// 变量 <= 数值 - /// private static bool EvaluateExpression(string expression, FlowContext context) - { - try - { - // 支持的操作符(顺序注意,先长后短) - string[] ops = { ">=", "<=", "!=", "==", ">", "<" }; - foreach (var op in ops) - { - var idx = expression.IndexOf(op, StringComparison.Ordinal); - if (idx < 0) continue; - - var left = expression[..idx].Trim(); - var right = expression[(idx + op.Length)..].Trim().Trim('"', '\''); - - var leftVal = context.GetVariable(left)?.ToString() ?? ""; - - return op switch - { - "==" => string.Equals(leftVal, right, StringComparison.OrdinalIgnoreCase), - "!=" => !string.Equals(leftVal, right, StringComparison.OrdinalIgnoreCase), - ">" => double.TryParse(leftVal, out var l1) && double.TryParse(right, out var r1) && l1 > r1, - "<" => double.TryParse(leftVal, out var l2) && double.TryParse(right, out var r2) && l2 < r2, - ">=" => double.TryParse(leftVal, out var l3) && double.TryParse(right, out var r3) && l3 >= r3, - "<=" => double.TryParse(leftVal, out var l4) && double.TryParse(right, out var r4) && l4 <= r4, - _ => false - }; - } - - // 没有操作符,判断变量是否为 true - var varVal = context.GetVariable(expression.Trim())?.ToString() ?? ""; - return varVal.Equals("true", StringComparison.OrdinalIgnoreCase); - } - catch - { - return false; - } - } + => ConditionExpressionEvaluator.Evaluate(expression, context); } diff --git a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue index c1eea2a..cefb44a 100644 --- a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue +++ b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue @@ -732,10 +732,11 @@ @@ -1290,6 +1320,9 @@ const nodeDebugInputStr = ref('') const nodeDebugOutputStr = ref('') const nodeDebugDetailStr = ref('') +// 条件表达式语法帮助弹窗 +const conditionHelpVisible = ref(false) + const debugResultStr = computed(() => debugResult.value ? JSON.stringify(debugResult.value, null, 2) : '') const debugOutputStr = computed(() => { if (!debugResult.value) return '' @@ -2540,6 +2573,11 @@ async function runDebug() { .assign-row { display: flex; align-items: center; gap: 4px; } .condition-item { background: #fffbe6; border: 1px solid #ffe58f; border-radius: 6px; padding: 8px; margin-bottom: 8px; } +.condition-help { max-height: 60vh; overflow-y: auto; } +.condition-help .ch-section { font-weight: 600; margin: 12px 0 4px; color: #303133; } +.condition-help .ch-section:first-child { margin-top: 0; } +.condition-help pre { background: #f6f8fa; border: 1px solid #e4e7ed; border-radius: 6px; padding: 8px 10px; font-size: 12px; line-height: 1.9; margin: 0; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; white-space: pre; } +.condition-help .ch-note { color: #909399; font-size: 12px; } .code-editor :deep(textarea) { font-family: 'Consolas', 'Monaco', monospace !important; font-size: 12px !important; line-height: 1.6; background: #1e1e1e !important; color: #d4d4d4 !important; } From 639358a8f621a2559036301e0a47cd79f580f9ab Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Thu, 3 Sep 2026 16:22:36 +0800 Subject: [PATCH 093/153] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=8A=82=E7=82=B9SQL=E8=AE=BE=E8=AE=A1=E8=BE=85=E5=8A=A9-?= =?UTF-8?q?=E8=A1=A8/=E8=A7=86=E5=9B=BE/=E5=AD=98=E5=82=A8=E8=BF=87?= =?UTF-8?q?=E7=A8=8B=E6=B5=8F=E8=A7=88=E4=B8=8E=E4=B8=80=E9=94=AE=E7=94=9F?= =?UTF-8?q?=E6=88=90SQL=E3=80=81=E5=8D=95=E7=8B=AC=E6=B5=8B=E8=AF=95SQL(?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=A2=84=E8=A7=88/=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A)=E3=80=81SQL=E7=BC=96=E5=86=99=E5=B8=AE?= =?UTF-8?q?=E5=8A=A9=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- DesignDoc.md | 12 + .../Controllers/Api/DataSourceController.cs | 45 +++ .../Request/DataSourceDbAssistRequest.cs | 54 ++++ .../Services/Impl/DataSourceService.cs | 290 +++++++++++++++++- .../src/views/flow/FlowDesign.vue | 239 ++++++++++++++- 5 files changed, 635 insertions(+), 5 deletions(-) create mode 100644 Juggle.Application/Models/Request/DataSourceDbAssistRequest.cs diff --git a/DesignDoc.md b/DesignDoc.md index 7c56025..1e6d87f 100644 --- a/DesignDoc.md +++ b/DesignDoc.md @@ -629,6 +629,18 @@ public class HttpApiCaller 字面量支持:数字、单/双引号字符串、`true`/`false`/`null`。解析失败或变量缺失时表达式返回 false(走默认分支)。 +### 3.6 数据库节点 SQL 设计辅助 + +流程设计器中数据库节点提供 SQL 辅助能力(后端 `DataSourceService` + `api/system/datasource` 端点): + +| 接口 | 说明 | +|--------|------| +| `POST api/system/datasource/metadata` | 获取数据源的表/视图/存储过程列表(按 6 种数据库类型适配元数据 SQL) | +| `POST api/system/datasource/columns` | 获取表/视图字段列表(统一用 `SELECT * FROM t WHERE 1=0` 取元数据,跨库通用) | +| `POST api/system/datasource/test-sql` | 单独测试 SQL:QUERY 返回前 100 行预览;UPDATE 在事务中执行并回滚,不污染数据。模板变量 `${varName}` 由测试参数(JSON)提供值,未提供的替换为空字符串 | + +前端设计器:点击"表/视图/存储过程"浏览对象、查看字段、一键生成 SELECT(按数据源类型自动带 `LIMIT 100` / `SELECT TOP 100` / `ROWNUM <= 100`)或 CALL/EXEC 调用语句;点击"测试 SQL"弹出测试参数(自动提取 SQL 中的 `${varName}`)并展示结果或数据库错误信息。 + --- ## 四、前端详细设计 diff --git a/Juggle.Api/Controllers/Api/DataSourceController.cs b/Juggle.Api/Controllers/Api/DataSourceController.cs index f8eddc5..0e3b60e 100644 --- a/Juggle.Api/Controllers/Api/DataSourceController.cs +++ b/Juggle.Api/Controllers/Api/DataSourceController.cs @@ -121,4 +121,49 @@ public async Task Page([FromBody] PageRequest req) Total = total, PageNum = req.PageNum, PageSize = req.PageSize, Records = records }); } + + /// 获取数据源的表/视图/存储过程列表(辅助 SQL 设计) + [HttpPost("metadata")] + public async Task Metadata([FromBody] DataSourceMetadataRequest req) + { + try + { + var result = await _dsService.GetDbObjectsAsync(req.DataSourceName); + return ApiResult.Success(result); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } + + /// 获取表/视图的字段列表(辅助生成 SQL) + [HttpPost("columns")] + public async Task Columns([FromBody] DataSourceColumnsRequest req) + { + try + { + var result = await _dsService.GetColumnsAsync(req.DataSourceName, req.TableName); + return ApiResult.Success(result); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } + + /// 单独测试 SQL:QUERY 返回前 100 行预览,UPDATE 事务执行后回滚 + [HttpPost("test-sql")] + public async Task TestSql([FromBody] DataSourceTestSqlRequest req) + { + try + { + var result = await _dsService.TestSqlAsync(req.DataSourceName, req.Sql, req.OperationType, req.Params); + return ApiResult.Success(result); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } } diff --git a/Juggle.Application/Models/Request/DataSourceDbAssistRequest.cs b/Juggle.Application/Models/Request/DataSourceDbAssistRequest.cs new file mode 100644 index 0000000..6f6e60b --- /dev/null +++ b/Juggle.Application/Models/Request/DataSourceDbAssistRequest.cs @@ -0,0 +1,54 @@ +namespace Juggle.Application.Models.Request; + +/// +/// 获取数据源数据库对象(表/视图/存储过程)请求模型 +/// +public class DataSourceMetadataRequest +{ + /// + /// 数据源名称(流程中通过此名称引用数据源) + /// + public string DataSourceName { get; set; } = ""; +} + +/// +/// 获取表/视图字段列表请求模型 +/// +public class DataSourceColumnsRequest +{ + /// + /// 数据源名称 + /// + public string DataSourceName { get; set; } = ""; + + /// + /// 表名或视图名 + /// + public string TableName { get; set; } = ""; +} + +/// +/// 单独测试 SQL 请求模型 +/// +public class DataSourceTestSqlRequest +{ + /// + /// 数据源名称 + /// + public string DataSourceName { get; set; } = ""; + + /// + /// SQL 语句,支持 ${varName} 模板变量 + /// + public string Sql { get; set; } = ""; + + /// + /// 操作类型:QUERY(查询)/ UPDATE(更改,事务回滚) + /// + public string OperationType { get; set; } = "QUERY"; + + /// + /// 模板变量值(varName → 值),未提供的变量替换为空字符串 + /// + public Dictionary? Params { get; set; } +} diff --git a/Juggle.Application/Services/Impl/DataSourceService.cs b/Juggle.Application/Services/Impl/DataSourceService.cs index 98adeee..b6c94e8 100644 --- a/Juggle.Application/Services/Impl/DataSourceService.cs +++ b/Juggle.Application/Services/Impl/DataSourceService.cs @@ -1,3 +1,6 @@ +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; using Juggle.Application.Services.Flow; using Juggle.Domain.Engine.NodeExecutors; using Juggle.Domain.Entities; @@ -9,6 +12,7 @@ namespace Juggle.Application.Services.Impl; /// /// 数据源服务 —— 统一管理连接字符串构建和连接测试逻辑, /// 消除 DataSourceController.Test、FlowExecutionService.BuildDataSourceInfos 中的重复实现。 +/// 同时提供流程设计器的 SQL 辅助能力:表/视图/存储过程元数据、字段列表、SQL 单独测试。 /// public class DataSourceService { @@ -26,13 +30,291 @@ public static string BuildConnectionString(DataSourceEntity ds) var ds = await _db.DataSources.FindAsync(id); if (ds == null) return (false, "数据源不存在"); - var connStr = BuildConnectionString(ds); - var dsInfo = new DataSourceInfo + var dsInfo = BuildDataSourceInfo(ds); + return await MysqlNodeExecutor.TestConnectionAsync(dsInfo); + } + + /// 按名称加载数据源连接信息(流程中通过名称引用数据源)。 + public async Task LoadDataSourceInfoAsync(string dataSourceName) + { + var ds = await _db.DataSources + .FirstOrDefaultAsync(d => d.Deleted == 0 && d.DsName == dataSourceName); + if (ds == null) throw new InvalidOperationException($"数据源 [{dataSourceName}] 未找到,请先在系统设置中配置数据源。"); + return BuildDataSourceInfo(ds); + } + + private static DataSourceInfo BuildDataSourceInfo(DataSourceEntity ds) + { + return new DataSourceInfo { DsType = (ds.DsType ?? "sqlite").ToLower(), - ConnStr = connStr, + ConnStr = BuildConnectionString(ds), DsName = ds.DsName ?? "" }; - return await MysqlNodeExecutor.TestConnectionAsync(dsInfo); } + + // ──────────────────────────────────────────────────────────────── + // SQL 设计辅助(表/视图/存储过程元数据) + // ──────────────────────────────────────────────────────────────── + + /// 获取数据源的表、视图、存储过程列表。 + public async Task GetDbObjectsAsync(string dataSourceName) + { + var dsInfo = await LoadDataSourceInfoAsync(dataSourceName); + var result = new DbObjectsResult(); + await using var conn = MysqlNodeExecutor.CreateConnection(dsInfo); + await conn.OpenAsync(); + + switch (dsInfo.DsType.ToLower()) + { + case "mysql": + { + // SHOW FULL TABLES: 第0列表名,第1列 Table_type(BASE TABLE / VIEW) + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SHOW FULL TABLES"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = reader.GetValue(0)?.ToString() ?? ""; + var type = reader.GetValue(1)?.ToString() ?? ""; + if (type.Contains("VIEW", StringComparison.OrdinalIgnoreCase)) + result.Views.Add(new DbObjectItem { Name = name }); + else + result.Tables.Add(new DbObjectItem { Name = name }); + } + } + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SHOW PROCEDURE STATUS WHERE Db = DATABASE()"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Procedures.Add(new DbObjectItem { Name = reader["Name"]?.ToString() ?? "" }); + } + break; + } + case "sqlite": + { + await using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT name, type FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' ORDER BY name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = reader.GetValue(0)?.ToString() ?? ""; + var type = reader.GetValue(1)?.ToString() ?? ""; + if (type == "view") result.Views.Add(new DbObjectItem { Name = name }); + else result.Tables.Add(new DbObjectItem { Name = name }); + } + break; + } + case "postgresql" or "postgres": + { + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT table_name, table_type FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog','information_schema') ORDER BY table_name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = reader.GetValue(0)?.ToString() ?? ""; + var type = reader.GetValue(1)?.ToString() ?? ""; + if (type == "VIEW") result.Views.Add(new DbObjectItem { Name = name }); + else result.Tables.Add(new DbObjectItem { Name = name }); + } + } + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT routine_name FROM information_schema.routines WHERE routine_type = 'PROCEDURE' AND routine_schema NOT IN ('pg_catalog','information_schema') ORDER BY routine_name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Procedures.Add(new DbObjectItem { Name = reader.GetValue(0)?.ToString() ?? "" }); + } + break; + } + case "sqlserver" or "mssql": + { + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = reader.GetValue(0)?.ToString() ?? ""; + var type = reader.GetValue(1)?.ToString() ?? ""; + if (type == "VIEW") result.Views.Add(new DbObjectItem { Name = name }); + else result.Tables.Add(new DbObjectItem { Name = name }); + } + } + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT name FROM sys.procedures ORDER BY name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Procedures.Add(new DbObjectItem { Name = reader.GetValue(0)?.ToString() ?? "" }); + } + break; + } + case "oracle": + case "dm": // 达梦兼容 Oracle 数据字典 + { + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT table_name FROM user_tables ORDER BY table_name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Tables.Add(new DbObjectItem { Name = reader.GetValue(0)?.ToString() ?? "" }); + } + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT view_name FROM user_views ORDER BY view_name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Views.Add(new DbObjectItem { Name = reader.GetValue(0)?.ToString() ?? "" }); + } + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT object_name FROM user_objects WHERE object_type = 'PROCEDURE' ORDER BY object_name"; + await using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + result.Procedures.Add(new DbObjectItem { Name = reader.GetValue(0)?.ToString() ?? "" }); + } + break; + } + default: + throw new InvalidOperationException($"不支持的数据库类型: {dsInfo.DsType}"); + } + + return result; + } + + /// 获取表/视图的字段列表(所有数据库统一使用 SELECT * WHERE 1=0 取元数据)。 + public async Task> GetColumnsAsync(string dataSourceName, string tableName) + { + var dsInfo = await LoadDataSourceInfoAsync(dataSourceName); + var columns = new List(); + await using var conn = MysqlNodeExecutor.CreateConnection(dsInfo); + await conn.OpenAsync(); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = $"SELECT * FROM {tableName} WHERE 1=0"; + await using var reader = await cmd.ExecuteReaderAsync(); + for (int i = 0; i < reader.FieldCount; i++) + { + columns.Add(new DbColumnItem + { + Name = reader.GetName(i), + DataType = reader.GetDataTypeName(i) + }); + } + return columns; + } + + // ──────────────────────────────────────────────────────────────── + // SQL 单独测试 + // ──────────────────────────────────────────────────────────────── + + /// + /// 单独测试 SQL:QUERY 返回前 100 行预览;UPDATE 在事务中执行并回滚(不影响真实数据)。 + /// 模板变量 ${varName} 使用 vars 提供值,未提供的变量替换为空字符串。 + /// + public async Task TestSqlAsync(string dataSourceName, string sql, string operationType, Dictionary? vars) + { + var dsInfo = await LoadDataSourceInfoAsync(dataSourceName); + var rendered = RenderTemplate(sql, vars); + await using var conn = MysqlNodeExecutor.CreateConnection(dsInfo); + await conn.OpenAsync(); + + if (operationType?.ToUpper() == "UPDATE") + { + // 更改操作:事务执行后回滚,保证测试不污染数据 + await using var tx = await conn.BeginTransactionAsync(); + try + { + await using var cmd = conn.CreateCommand(); + cmd.Transaction = (DbTransaction)tx; + cmd.CommandText = rendered; + var affected = await cmd.ExecuteNonQueryAsync(); + await tx.RollbackAsync(); + return new DbTestSqlResult { OperationType = "UPDATE", AffectedRows = affected }; + } + catch + { + try { await tx.RollbackAsync(); } catch { /* 连接已断开时忽略 */ } + throw; + } + } + + // 查询操作:读取前 100 行预览 + var result = new DbTestSqlResult { OperationType = "QUERY" }; + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = rendered; + await using var reader = await cmd.ExecuteReaderAsync(); + for (int i = 0; i < reader.FieldCount; i++) + result.Columns.Add(reader.GetName(i)); + + var total = 0; + while (await reader.ReadAsync()) + { + total++; + if (total > 100) continue; // 只保留前 100 行 + var row = new Dictionary(); + for (int i = 0; i < reader.FieldCount; i++) + row[reader.GetName(i)] = reader.IsDBNull(i) ? null : reader.GetValue(i); + result.Rows.Add(row); + } + result.RowCount = total; + result.Truncated = total > 100; + } + return result; + } + + private static string RenderTemplate(string sql, Dictionary? vars) + { + return Regex.Replace(sql, @"\$\{([^}]+)\}", m => + { + var varName = m.Groups[1].Value.Trim(); + return vars != null && vars.TryGetValue(varName, out var v) && v != null ? v : ""; + }); + } +} + +/// 数据库对象列表(表/视图/存储过程) +public class DbObjectsResult +{ + public List Tables { get; set; } = new(); + public List Views { get; set; } = new(); + public List Procedures { get; set; } = new(); +} + +public class DbObjectItem +{ + public string Name { get; set; } = ""; +} + +/// 表/视图字段 +public class DbColumnItem +{ + public string Name { get; set; } = ""; + public string DataType { get; set; } = ""; +} + +/// SQL 单独测试结果 +public class DbTestSqlResult +{ + /// QUERY / UPDATE + public string OperationType { get; set; } = "QUERY"; + + /// 查询列名(QUERY) + public List Columns { get; set; } = new(); + + /// 查询结果行(QUERY,最多 100 行) + public List> Rows { get; set; } = new(); + + /// 查询总行数(QUERY) + public long RowCount { get; set; } + + /// 结果是否被截断(超过 100 行) + public bool Truncated { get; set; } + + /// 影响行数(UPDATE) + public int AffectedRows { get; set; } } diff --git a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue index cefb44a..ecce70b 100644 --- a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue +++ b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue @@ -669,7 +669,7 @@ @@ -1323,6 +1414,141 @@ const nodeDebugDetailStr = ref('') // 条件表达式语法帮助弹窗 const conditionHelpVisible = ref(false) +// ====== 数据库节点辅助(表/视图/存储过程 + 测试SQL) ====== +const dbHelpVisible = ref(false) +const dbObjectDialogVisible = ref(false) +const dbObjectLoading = ref(false) +const dbObjectTab = ref('tables') +const dbObjectSearch = ref('') +const dbTables = ref([]) +const dbViews = ref([]) +const dbProcedures = ref([]) +const dbSelectedObject = ref('') +const dbColumns = ref([]) +const dbColumnLoading = ref(false) +const dbTestDialogVisible = ref(false) +const dbTestParams = ref('{}') +const dbTestLoading = ref(false) +const dbTestResult = ref(null) +const dbTestError = ref('') + +const filteredDbObjects = computed(() => { + const list = dbObjectTab.value === 'tables' ? dbTables.value : dbObjectTab.value === 'views' ? dbViews.value : dbProcedures.value + const kw = dbObjectSearch.value.trim().toLowerCase() + return kw ? list.filter((o: any) => (o.name || '').toLowerCase().includes(kw)) : list +}) + +/** 当前节点所选数据源的类型(用于生成方言相关的 SQL) */ +function selectedDsType(): string { + const name = selectedNode.value?.mysqlConfig?.dataSourceName + return (dataSources.value.find((d: any) => d.dataSourceName === name)?.dataSourceType || 'mysql').toLowerCase() +} + +async function openDbObjectDialog() { + const name = selectedNode.value?.mysqlConfig?.dataSourceName + if (!name) { ElMessage.warning('请先选择数据源'); return } + dbObjectDialogVisible.value = true + dbObjectLoading.value = true + dbTables.value = []; dbViews.value = []; dbProcedures.value = [] + dbColumns.value = []; dbSelectedObject.value = ''; dbObjectSearch.value = '' + try { + const res: any = await request.post('/system/datasource/metadata', { dataSourceName: name }) + dbTables.value = res.data?.tables || [] + dbViews.value = res.data?.views || [] + dbProcedures.value = res.data?.procedures || [] + } catch { /* 拦截器已提示 */ } finally { dbObjectLoading.value = false } +} + +function onDbObjectTabChange() { + dbSelectedObject.value = '' + dbColumns.value = [] +} + +async function selectDbObject(name: string) { + dbSelectedObject.value = name + if (dbObjectTab.value === 'procedures') return + await loadDbColumns(name) +} + +async function loadDbColumns(name: string) { + dbColumnLoading.value = true + dbColumns.value = [] + try { + const res: any = await request.post('/system/datasource/columns', { + dataSourceName: selectedNode.value.mysqlConfig.dataSourceName, tableName: name + }) + dbColumns.value = res.data || [] + } catch { dbColumns.value = [] } finally { dbColumnLoading.value = false } +} + +async function generateSqlFromObject(obj: any) { + const node = selectedNode.value + if (!node?.mysqlConfig) return + dbSelectedObject.value = obj.name + // 表/视图先取字段(保证生成含字段列表的 SELECT),存储过程直接生成调用语句 + if (dbObjectTab.value !== 'procedures') await loadDbColumns(obj.name) + const dsType = selectedDsType() + let sql = '' + if (dbObjectTab.value === 'procedures') { + sql = dsType === 'sqlserver' || dsType === 'mssql' ? `EXEC ${obj.name}` + : dsType === 'oracle' || dsType === 'dm' ? `BEGIN ${obj.name}(); END;` + : `CALL ${obj.name}()` + } else { + const cols = dbColumns.value.length ? dbColumns.value.map((c: any) => c.name).join(', ') : '*' + const from = `FROM ${obj.name}` + if (dsType === 'sqlserver' || dsType === 'mssql') sql = `SELECT TOP 100 ${cols}\n${from}` + else if (dsType === 'oracle' || dsType === 'dm') sql = `SELECT ${cols}\n${from}\nWHERE ROWNUM <= 100` + else sql = `SELECT ${cols}\n${from}\nLIMIT 100` + } + node.mysqlConfig.sql = sql + dbObjectDialogVisible.value = false + ElMessage.success('已生成 SQL') +} + +function openDbTestDialog() { + const node = selectedNode.value + if (!node?.mysqlConfig?.dataSourceName) { ElMessage.warning('请先选择数据源'); return } + dbTestDialogVisible.value = true + dbTestResult.value = null + dbTestError.value = '' + // 自动提取 SQL 中的 ${varName} 生成参数模板 + const sql = node.mysqlConfig.sql || '' + const names: string[] = [] + for (const m of sql.matchAll(/\$\{([^}]+)\}/g)) { + const v = m[1].trim() + if (v && !names.includes(v)) names.push(v) + } + const params: Record = {} + names.forEach(n => { params[n] = '' }) + dbTestParams.value = names.length ? JSON.stringify(params, null, 2) : '{}' +} + +async function runDbTest() { + const node = selectedNode.value + const sql = node?.mysqlConfig?.sql + if (!sql?.trim()) { ElMessage.warning('请先编写 SQL'); return } + let params: Record = {} + try { params = JSON.parse(dbTestParams.value || '{}') } catch { + dbTestError.value = '测试参数不是合法的 JSON' + dbTestResult.value = null + return + } + dbTestLoading.value = true + dbTestError.value = '' + dbTestResult.value = null + try { + const res: any = await request.post('/system/datasource/test-sql', { + dataSourceName: node.mysqlConfig.dataSourceName, + sql, + operationType: node.mysqlConfig.operationType || 'QUERY', + params + }) + dbTestResult.value = res.data + } catch (e: any) { + dbTestError.value = e?.message || '测试失败' + } finally { dbTestLoading.value = false } +} + const debugResultStr = computed(() => debugResult.value ? JSON.stringify(debugResult.value, null, 2) : '') const debugOutputStr = computed(() => { if (!debugResult.value) return '' @@ -2578,6 +2804,17 @@ async function runDebug() { .condition-help .ch-section:first-child { margin-top: 0; } .condition-help pre { background: #f6f8fa; border: 1px solid #e4e7ed; border-radius: 6px; padding: 8px 10px; font-size: 12px; line-height: 1.9; margin: 0; overflow-x: auto; font-family: Consolas, Monaco, 'Courier New', monospace; white-space: pre; } .condition-help .ch-note { color: #909399; font-size: 12px; } +.db-object-list { max-height: 380px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 6px; } +.db-object-item { display: flex; align-items: center; justify-content: space-between; padding: 6px 10px; cursor: pointer; border-bottom: 1px solid #f0f2f5; } +.db-object-item:last-child { border-bottom: none; } +.db-object-item:hover { background: #f5f7fa; } +.db-object-item.active { background: #ecf5ff; } +.db-object-name { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.db-column-list { max-height: 380px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 6px; } +.db-column-item { display: flex; align-items: center; justify-content: space-between; padding: 5px 10px; font-size: 12px; border-bottom: 1px solid #f0f2f5; } +.db-column-item:last-child { border-bottom: none; } +.db-test-error { margin-top: 10px; padding: 8px 12px; background: #fef0f0; border: 1px solid #fbc4c4; border-radius: 6px; color: #f56c6c; font-size: 12px; white-space: pre-wrap; word-break: break-all; } +.db-test-result { margin-top: 10px; font-size: 13px; color: #67c23a; } .code-editor :deep(textarea) { font-family: 'Consolas', 'Monaco', monospace !important; font-size: 12px !important; line-height: 1.6; background: #1e1e1e !important; color: #d4d4d4 !important; } From 699b347b1aed78c998abfc98c5e66c97abb16a8c Mon Sep 17 00:00:00 2001 From: hhp <272302281@qq.com> Date: Thu, 3 Sep 2026 16:42:16 +0800 Subject: [PATCH 094/153] =?UTF-8?q?feat:=20=E6=9D=A1=E4=BB=B6/=E8=B5=8B?= =?UTF-8?q?=E5=80=BC=E8=8A=82=E7=82=B9=E8=A1=A8=E8=BE=BE=E5=BC=8F=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E6=89=A9=E5=B1=95-=E6=94=AF=E6=8C=81=E7=AE=97?= =?UTF-8?q?=E6=9C=AF=E8=BF=90=E7=AE=97+=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E6=8B=BC=E6=8E=A5/=E5=88=87=E7=89=87/replace=E3=80=81toString?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E4=B8=8E=E6=97=A5=E6=9C=9F=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E8=B5=8B=E5=80=BC=E8=8A=82=E7=82=B9=E6=96=B0?= =?UTF-8?q?=E5=A2=9EEXPRESSION=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=9D=A5?= =?UTF-8?q?=E6=BA=90+=E5=B8=AE=E5=8A=A9=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- DesignDoc.md | 14 +- .../Engine/ConditionExpressionEvaluator.cs | 367 ---------- Juggle.Domain/Engine/ExpressionEvaluator.cs | 629 ++++++++++++++++++ Juggle.Domain/Engine/FlowModels.cs | 2 +- .../NodeExecutors/AssignNodeExecutor.cs | 5 + .../NodeExecutors/ConditionNodeExecutor.cs | 9 +- .../src/views/flow/FlowDesign.vue | 71 +- 7 files changed, 718 insertions(+), 379 deletions(-) delete mode 100644 Juggle.Domain/Engine/ConditionExpressionEvaluator.cs create mode 100644 Juggle.Domain/Engine/ExpressionEvaluator.cs diff --git a/DesignDoc.md b/DesignDoc.md index 1e6d87f..a56260f 100644 --- a/DesignDoc.md +++ b/DesignDoc.md @@ -615,19 +615,27 @@ public class HttpApiCaller ### 3.5 条件表达式求值 -由 `Juggle.Domain/Engine/ConditionExpressionEvaluator.cs` 递归下降解析,支持的语法: +由 `Juggle.Domain/Engine/ExpressionEvaluator.cs` 递归下降解析(条件节点与赋值节点 EXPRESSION 共用),支持的语法: | 类型 | 语法 | 示例 | |--------|---------|------| | 比较运算 | == != > < >= <= | `env_score >= 60`、`input_name == '张三'` | | 逻辑运算 | &&(且) \|\|(或) !(非) 括号 | `env_score >= 60 && env_score < 90`、`(env_a > 1 \|\| env_b > 2) && env_c == 'yes'` | +| 算术运算 | + - * / %(先运算再比较) | `(env_price - env_cost) * env_qty >= 100`、`env_total % 2 == 0` | | 变量对变量 | 左右两边都可以是变量 | `env_total >= input_min` | | 子属性 | 变量.属性 | `input_user.name == '张三'` | -| 数组长度 | 变量.length | `input_list.length > 0` | +| 数组/字符串长度 | 变量.length | `input_list.length > 0`、`env_name.length == 6` | | 数组元素 | 变量[索引](从 0 开始),可继续取子属性 | `input_list[0].name == '张三'` | +| 字符串拼接 | +(两边都是数字时做加法,否则拼接) | `env_name + '_done' == 'abc_done'` | +| 字符串切片 | [..5] 前5位 / [2..5] 第2~5位 / [2..] 到最后 | `input_phone[..3] == '138'`、`env_code[2..5] == 'abc'` | +| 字符串替换 | replace('旧','新') | `env_name.replace('a','b') == 'xbcdef'` | +| 数字格式化 | toString("#.0##") / toString("0.00") | `env_amount.toString('0.00') == '12.35'` | +| 日期格式化 | toString("yyyy-MM-dd HH:mm:ss") | `env_time.toString('yyyy-MM-dd') == '2024-01-02'` | | 裸变量 | 无比较符时按真值判断 | `env_flag` 等价于 `env_flag == true` | -字面量支持:数字、单/双引号字符串、`true`/`false`/`null`。解析失败或变量缺失时表达式返回 false(走默认分支)。 +规则:字面量支持数字、单/双引号字符串、`true`/`false`/`null`;`*` 会把数字字符串转为数字计算;未加引号且不存在的标识符按字符串字面量处理(兼容 `env_status == paid` 旧写法);除零返回 null;解析失败返回 false(条件节点走默认分支)。 + +赋值节点(ASSIGN)来源类型新增 `EXPRESSION`:规则 source 填表达式,求值结果写入目标变量(支持以上全部语法,无比较符时返回计算结果值)。 ### 3.6 数据库节点 SQL 设计辅助 diff --git a/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs b/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs deleted file mode 100644 index 8358273..0000000 --- a/Juggle.Domain/Engine/ConditionExpressionEvaluator.cs +++ /dev/null @@ -1,367 +0,0 @@ -using System.Text.Json; - -namespace Juggle.Domain.Engine; - -/// -/// 条件表达式求值器 — 递归下降解析。 -/// 语法支持: -/// 逻辑运算: &&(且) ||(或) !(非) 括号改变优先级 -/// 比较运算: == != > < >= <= -/// 操作数: 字面量(数字 / '字符串' / "字符串" / true / false / null) -/// 或变量路径(左右两边都可以是变量) -/// 变量路径: env_user.name(子属性)、input_list.length(数组长度)、 -/// input_list[0].name(数组指定元素及其子属性,索引从 0 开始) -/// 无比较符时按真值判断: 裸变量 / !变量 / 括号整体 -/// -public class ConditionExpressionEvaluator -{ - private readonly string _expr; - private int _pos; - private readonly FlowContext _context; - - private ConditionExpressionEvaluator(string expression, FlowContext context) - { - _expr = expression; - _context = context; - } - - /// 求值入口。解析失败或变量缺失时返回 false。 - public static bool Evaluate(string expression, FlowContext context) - { - if (string.IsNullOrWhiteSpace(expression)) return false; - try - { - var parser = new ConditionExpressionEvaluator(expression, context); - var val = parser.ParseOr(); - parser.SkipWhitespace(); - // 存在无法识别的残余字符视为表达式错误 - if (parser._pos < parser._expr.Length) return false; - return IsTruthy(val); - } - catch - { - return false; - } - } - - // ===== 递归下降解析 ===== - - private object? ParseOr() - { - var left = ParseAnd(); - SkipWhitespace(); - while (Peek() == '|' && Peek(1) == '|') - { - _pos += 2; - var right = ParseAnd(); - left = IsTruthy(left) ? left : right; // 短路 - } - return left; - } - - private object? ParseAnd() - { - var left = ParseUnary(); - SkipWhitespace(); - while (Peek() == '&' && Peek(1) == '&') - { - _pos += 2; - var right = ParseUnary(); - left = IsTruthy(left) ? right : left; // 短路 - } - return left; - } - - private object? ParseUnary() - { - SkipWhitespace(); - if (Peek() == '!') - { - _pos++; - return !IsTruthy(ParseUnary()); - } - return ParseCompare(); - } - - private object? ParseCompare() - { - var left = ParseOperand(); - SkipWhitespace(); - - string? op = null; - if (Peek() == '=' && Peek(1) == '=') { _pos += 2; op = "=="; } - else if (Peek() == '!' && Peek(1) == '=') { _pos += 2; op = "!="; } - else if (Peek() == '>' && Peek(1) == '=') { _pos += 2; op = ">="; } - else if (Peek() == '<' && Peek(1) == '=') { _pos += 2; op = "<="; } - else if (Peek() == '>') { _pos++; op = ">"; } - else if (Peek() == '<') { _pos++; op = "<"; } - else return left; // 无比较符,按真值判断 - - var right = ParseOperand(); - return Compare(left, right, op); - } - - private object? ParseOperand() - { - SkipWhitespace(); - if (_pos >= _expr.Length) throw new InvalidOperationException("表达式不完整"); - - var c = _expr[_pos]; - - // 数字字面量 - if (char.IsDigit(c) || (c == '.' && _pos + 1 < _expr.Length && char.IsDigit(_expr[_pos + 1]))) - { - var start = _pos; - while (_pos < _expr.Length && (char.IsDigit(_expr[_pos]) || _expr[_pos] == '.')) _pos++; - var numStr = _expr[start.._pos]; - if (long.TryParse(numStr, out var l)) return l; - if (double.TryParse(numStr, out var d)) return d; - return null; - } - - // 字符串字面量 - if (c == '\'' || c == '"') - { - var quote = _expr[_pos++]; - var start = _pos; - while (_pos < _expr.Length && _expr[_pos] != quote) _pos++; - var str = _expr[start.._pos]; - if (_pos < _expr.Length) _pos++; - return str; - } - - // 括号 - if (c == '(') - { - _pos++; - var val = ParseOr(); - SkipWhitespace(); - if (_pos < _expr.Length && _expr[_pos] == ')') _pos++; - else throw new InvalidOperationException("括号不匹配"); - return val; - } - - // 变量路径 - var root = ParseIdentifier(); - if (string.IsNullOrEmpty(root)) - throw new InvalidOperationException($"无法识别的字符 '{c}'"); - - return ResolvePath(root); - } - - // ===== 变量路径解析 ===== - - private object? ResolvePath(string root) - { - object? current = _context.GetVariable(root); - - while (true) - { - SkipWhitespace(); - - // 数组索引 [n] - if (_pos < _expr.Length && _expr[_pos] == '[') - { - _pos++; - SkipWhitespace(); - var numStart = _pos; - while (_pos < _expr.Length && char.IsDigit(_expr[_pos])) _pos++; - if (numStart == _pos) throw new InvalidOperationException("数组索引必须为数字"); - var idx = int.Parse(_expr[numStart.._pos]); - SkipWhitespace(); - if (_pos < _expr.Length && _expr[_pos] == ']') _pos++; - current = GetArrayElement(current, idx); - continue; - } - - // 子属性 .prop / .length - if (_pos < _expr.Length && _expr[_pos] == '.') - { - _pos++; - SkipWhitespace(); - var prop = ParseIdentifier(); - if (string.IsNullOrEmpty(prop)) throw new InvalidOperationException("缺少属性名"); - current = prop.Equals("length", StringComparison.OrdinalIgnoreCase) - ? GetArrayLength(current) - : GetProperty(current, prop); - continue; - } - - break; - } - - return current; - } - - private static object? GetProperty(object? obj, string prop) - { - switch (obj) - { - case Dictionary dict: - return dict.TryGetValue(prop, out var dv) ? dv : null; - case JsonElement json when json.ValueKind == JsonValueKind.Object: - return json.TryGetProperty(prop, out var next) ? ToNative(next) : null; - default: - return null; - } - } - - private static object? GetArrayElement(object? obj, int index) - { - switch (obj) - { - case List list: - return index >= 0 && index < list.Count ? list[index] : null; - case object[] arr: - return index >= 0 && index < arr.Length ? arr[index] : null; - case JsonElement json when json.ValueKind == JsonValueKind.Array: - { - var i = 0; - foreach (var item in json.EnumerateArray()) - { - if (i++ == index) return ToNative(item); - } - return null; - } - default: - return null; - } - } - - private static object? GetArrayLength(object? obj) - { - switch (obj) - { - case List list: return (long)list.Count; - case object[] arr: return (long)arr.Length; - case JsonElement json when json.ValueKind == JsonValueKind.Array: return (long)json.GetArrayLength(); - default: return null; - } - } - - /// 将 JsonElement 转为 .NET 原生类型,便于比较运算 - private static object? ToNative(JsonElement el) - { - return el.ValueKind switch - { - JsonValueKind.String => el.GetString(), - JsonValueKind.Number => el.TryGetInt64(out var l) ? l : el.GetDouble(), - JsonValueKind.True => true, - JsonValueKind.False => false, - JsonValueKind.Null => null, - _ => el // 对象/数组保留原 JsonElement,继续支持 .prop / [i] / .length - }; - } - - // ===== 比较与真值 ===== - - private static object Compare(object? left, object? right, string op) - { - // null 比较 - if (left == null || right == null) - return op switch - { - "==" => left == null && right == null, - "!=" => !(left == null && right == null), - _ => false - }; - - // 布尔比较(布尔值或 "true"/"false" 字符串) - if (TryAsBool(left, out var lb) && TryAsBool(right, out var rb)) - return op switch { "==" => lb == rb, "!=" => lb != rb, _ => false }; - - // 数字比较(任意一边可解析为数字时按数值比较) - if (IsNumeric(left) && IsNumeric(right)) - { - var l = ToDouble(left); - var r = ToDouble(right); - return op switch - { - "==" => l == r, - "!=" => l != r, - ">" => l > r, - "<" => l < r, - ">=" => l >= r, - "<=" => l <= r, - _ => false - }; - } - - // 字符串比较(==/!= 忽略大小写,保持历史行为) - var ls = left.ToString() ?? ""; - var rs = right.ToString() ?? ""; - return op switch - { - "==" => string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), - "!=" => !string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), - ">" => string.Compare(ls, rs, StringComparison.Ordinal) > 0, - "<" => string.Compare(ls, rs, StringComparison.Ordinal) < 0, - ">=" => string.Compare(ls, rs, StringComparison.Ordinal) >= 0, - "<=" => string.Compare(ls, rs, StringComparison.Ordinal) <= 0, - _ => false - }; - } - - private static bool IsTruthy(object? v) - { - switch (v) - { - case null: return false; - case bool b: return b; - case string s: return s.Equals("true", StringComparison.OrdinalIgnoreCase) || s == "1"; - case long l: return l != 0; - case int i: return i != 0; - case double d: return d != 0; - case decimal m: return m != 0; - default: return false; - } - } - - private static bool TryAsBool(object? v, out bool result) - { - if (v is bool b) { result = b; return true; } - if (v is string s && bool.TryParse(s, out var parsed)) { result = parsed; return true; } - result = false; - return false; - } - - private static bool IsNumeric(object? v) - { - return v switch - { - byte or sbyte or short or ushort or int or uint or long or ulong - or float or double or decimal => true, - string s => double.TryParse(s, out _), - _ => false - }; - } - - private static double ToDouble(object? v) - { - return v switch - { - byte x => x, sbyte x => x, short x => x, ushort x => x, - int x => x, uint x => x, long x => x, ulong x => x, - float x => x, double x => x, decimal x => (double)x, - string s => double.TryParse(s, out var d) ? d : 0, - _ => 0 - }; - } - - // ===== 基础解析工具 ===== - - private string ParseIdentifier() - { - var start = _pos; - while (_pos < _expr.Length && (char.IsLetterOrDigit(_expr[_pos]) || _expr[_pos] == '_')) - _pos++; - return _expr[start.._pos]; - } - - private char Peek(int ahead = 0) => _pos + ahead < _expr.Length ? _expr[_pos + ahead] : '\0'; - - private void SkipWhitespace() - { - while (_pos < _expr.Length && char.IsWhiteSpace(_expr[_pos])) _pos++; - } -} diff --git a/Juggle.Domain/Engine/ExpressionEvaluator.cs b/Juggle.Domain/Engine/ExpressionEvaluator.cs new file mode 100644 index 0000000..4480a2c --- /dev/null +++ b/Juggle.Domain/Engine/ExpressionEvaluator.cs @@ -0,0 +1,629 @@ +using System.Globalization; +using System.Text.Json; + +namespace Juggle.Domain.Engine; + +/// +/// 表达式求值器 — 递归下降解析。条件节点(布尔判断)和赋值节点(EXPRESSION 来源)共用。 +/// +/// 语法: +/// 逻辑: && || ! 括号 +/// 比较: == != > < >= <= +/// 算术: + - * / % (+ 两边都是数字时做加法,否则做字符串拼接;* 会把数字字符串转为数字) +/// 字面量: 数字 / '字符串' / "字符串" / true / false / null +/// 变量路径: env_user.name(子属性)、input_list.length(数组/字符串长度)、 +/// input_list[0].name(数组元素及其子属性,索引从 0 开始) +/// 字符串操作: + 拼接、[..5] / [2..5] / [2..] 切片截取(含数组切片)、 +/// replace('旧','新') 替换、[0] 取单个字符 +/// 格式化: .toString("#.0##") 数字格式、.toString("yyyy-MM-dd HH:mm:ss") 日期格式 +/// 未声明的标识符(非变量)按字符串字面量处理,兼容旧表达式如 env_status == paid +/// +public class ExpressionEvaluator +{ + private readonly string _expr; + private int _pos; + private readonly FlowContext _context; + + private ExpressionEvaluator(string expression, FlowContext context) + { + _expr = expression; + _context = context; + } + + /// 布尔求值入口(条件节点使用)。解析失败返回 false。 + public static bool Evaluate(string expression, FlowContext context) + => IsTruthy(EvaluateValue(expression, context)); + + /// 值求值入口(赋值节点 EXPRESSION 使用)。解析失败返回 null。 + public static object? EvaluateValue(string expression, FlowContext context) + { + if (string.IsNullOrWhiteSpace(expression)) return null; + try + { + var parser = new ExpressionEvaluator(expression, context); + var val = parser.ParseOr(); + parser.SkipWhitespace(); + // 存在无法识别的残余字符视为表达式错误 + if (parser._pos < parser._expr.Length) return null; + return val; + } + catch + { + return null; + } + } + + // ===== 递归下降解析 ===== + + private object? ParseOr() + { + var left = ParseAnd(); + SkipWhitespace(); + while (Peek() == '|' && Peek(1) == '|') + { + _pos += 2; + var right = ParseAnd(); + left = IsTruthy(left) ? left : right; // 短路 + } + return left; + } + + private object? ParseAnd() + { + var left = ParseUnary(); + SkipWhitespace(); + while (Peek() == '&' && Peek(1) == '&') + { + _pos += 2; + var right = ParseUnary(); + left = IsTruthy(left) ? right : left; // 短路 + } + return left; + } + + private object? ParseUnary() + { + SkipWhitespace(); + if (Peek() == '!') + { + _pos++; + return !IsTruthy(ParseUnary()); + } + return ParseCompare(); + } + + private object? ParseCompare() + { + var left = ParseAddSub(); + SkipWhitespace(); + + string? op = null; + if (Peek() == '=' && Peek(1) == '=') { _pos += 2; op = "=="; } + else if (Peek() == '!' && Peek(1) == '=') { _pos += 2; op = "!="; } + else if (Peek() == '>' && Peek(1) == '=') { _pos += 2; op = ">="; } + else if (Peek() == '<' && Peek(1) == '=') { _pos += 2; op = "<="; } + else if (Peek() == '>') { _pos++; op = ">"; } + else if (Peek() == '<') { _pos++; op = "<"; } + else return left; // 无比较符,按真值判断 + + var right = ParseAddSub(); + return Compare(left, right, op); + } + + private object? ParseAddSub() + { + var left = ParseMulDiv(); + SkipWhitespace(); + while (Peek() == '+' || Peek() == '-') + { + var op = _expr[_pos++]; + var right = ParseMulDiv(); + left = op == '+' ? Add(left, right) : Arithmetic(left, right, op); + } + return left; + } + + private object? ParseMulDiv() + { + var left = ParseUnaryMinus(); + SkipWhitespace(); + while (Peek() == '*' || Peek() == '/' || Peek() == '%') + { + var op = _expr[_pos++]; + var right = ParseUnaryMinus(); + left = Arithmetic(left, right, op); + } + return left; + } + + private object? ParseUnaryMinus() + { + SkipWhitespace(); + if (Peek() == '-') + { + _pos++; + return Negate(ParseUnaryMinus()); + } + return ParsePostfix(); + } + + /// 后缀操作:.prop / [i] / 切片 / .length / .toString() / .replace() + private object? ParsePostfix() + { + var val = ParsePrimary(); + + while (true) + { + SkipWhitespace(); + + if (_pos < _expr.Length && _expr[_pos] == '.') + { + _pos++; + SkipWhitespace(); + var ident = ParseIdentifier(); + if (string.IsNullOrEmpty(ident)) throw new InvalidOperationException("缺少属性名"); + if (ident.Equals("length", StringComparison.OrdinalIgnoreCase)) { val = GetLength(val); continue; } + if (ident.Equals("toString", StringComparison.OrdinalIgnoreCase)) { val = ToStringOp(val, ParseMethodArgs()); continue; } + if (ident.Equals("replace", StringComparison.OrdinalIgnoreCase)) + { + var args = ParseMethodArgs(); + val = ReplaceOp(val, args.Count > 0 ? args[0] : null, args.Count > 1 ? args[1] : null); + continue; + } + val = GetProperty(val, ident); + continue; + } + + if (_pos < _expr.Length && _expr[_pos] == '[') + { + val = ParseIndexOrSlice(val); + continue; + } + + break; + } + + return val; + } + + private object? ParsePrimary() + { + SkipWhitespace(); + if (_pos >= _expr.Length) throw new InvalidOperationException("表达式不完整"); + + var c = _expr[_pos]; + + // 数字字面量 + if (char.IsDigit(c) || (c == '.' && _pos + 1 < _expr.Length && char.IsDigit(_expr[_pos + 1]))) + { + var start = _pos; + while (_pos < _expr.Length && (char.IsDigit(_expr[_pos]) || _expr[_pos] == '.')) _pos++; + var numStr = _expr[start.._pos]; + if (long.TryParse(numStr, out var l)) return l; + if (double.TryParse(numStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)) return d; + return null; + } + + // 字符串字面量 + if (c == '\'' || c == '"') + { + var quote = _expr[_pos++]; + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != quote) _pos++; + var str = _expr[start.._pos]; + if (_pos < _expr.Length) _pos++; + return str; + } + + // 括号 + if (c == '(') + { + _pos++; + var val = ParseOr(); + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ')') _pos++; + else throw new InvalidOperationException("括号不匹配"); + return val; + } + + // 变量 / 关键字 / 未声明的字符串字面量 + var root = ParseIdentifier(); + if (string.IsNullOrEmpty(root)) + throw new InvalidOperationException($"无法识别的字符 '{c}'"); + + if (root.Equals("true", StringComparison.OrdinalIgnoreCase)) return true; + if (root.Equals("false", StringComparison.OrdinalIgnoreCase)) return false; + if (root.Equals("null", StringComparison.OrdinalIgnoreCase)) return null; + + // 已声明的变量 → 取变量值;未声明 → 按字符串字面量处理(兼容 env_status == paid 写法) + return _context.Variables.TryGetValue(root, out var varVal) + ? ToNative(varVal) + : root; + } + + // ===== 索引 / 切片 ===== + + private object? ParseIndexOrSlice(object? val) + { + _pos++; // 跳过 '[' + SkipWhitespace(); + + int? start = null, end = null; + var isSlice = false; + + if (_pos < _expr.Length && _expr[_pos] == '.' && Peek(1) == '.') + { + // [..5] + _pos += 2; + isSlice = true; + SkipWhitespace(); + var numStart = _pos; + while (_pos < _expr.Length && char.IsDigit(_expr[_pos])) _pos++; + if (_pos > numStart) end = int.Parse(_expr[numStart.._pos]); + } + else if (_pos < _expr.Length && char.IsDigit(_expr[_pos])) + { + // [n] 或 [n..m] 或 [n..] + var numStart = _pos; + while (_pos < _expr.Length && char.IsDigit(_expr[_pos])) _pos++; + start = int.Parse(_expr[numStart.._pos]); + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == '.' && Peek(1) == '.') + { + _pos += 2; + isSlice = true; + SkipWhitespace(); + numStart = _pos; + while (_pos < _expr.Length && char.IsDigit(_expr[_pos])) _pos++; + if (_pos > numStart) end = int.Parse(_expr[numStart.._pos]); + } + } + else + { + throw new InvalidOperationException("索引/切片必须为数字"); + } + + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ']') _pos++; + + return isSlice ? Slice(val, start, end) : GetArrayElement(val, start ?? 0); + } + + private static object? Slice(object? val, int? start, int? end) + { + switch (val) + { + case string s: + { + var len = s.Length; + var sIdx = Math.Clamp(start ?? 0, 0, len); + var eIdx = Math.Clamp(end ?? len, 0, len); + return eIdx <= sIdx ? "" : s.Substring(sIdx, eIdx - sIdx); + } + case List list: + { + var len = list.Count; + var sIdx = Math.Clamp(start ?? 0, 0, len); + var eIdx = Math.Clamp(end ?? len, 0, len); + return eIdx <= sIdx ? new List() : list.GetRange(sIdx, eIdx - sIdx); + } + case object[] arr: + { + var len = arr.Length; + var sIdx = Math.Clamp(start ?? 0, 0, len); + var eIdx = Math.Clamp(end ?? len, 0, len); + return eIdx <= sIdx ? Array.Empty() : arr[sIdx..eIdx]; + } + case JsonElement json when json.ValueKind == JsonValueKind.Array: + { + var list = json.EnumerateArray().Select(e => (object?)ToNative(e)).ToList(); + return Slice(list, start, end); + } + default: + return null; + } + } + + // ===== 属性导航 / 数组元素 / 长度 ===== + + private static object? GetProperty(object? obj, string prop) + { + switch (obj) + { + case Dictionary dict: + return dict.TryGetValue(prop, out var dv) ? dv : null; + case JsonElement json when json.ValueKind == JsonValueKind.Object: + return json.TryGetProperty(prop, out var next) ? ToNative(next) : null; + default: + return null; + } + } + + private static object? GetArrayElement(object? obj, int index) + { + switch (obj) + { + case List list: + return index >= 0 && index < list.Count ? list[index] : null; + case object[] arr: + return index >= 0 && index < arr.Length ? arr[index] : null; + case string s: + return index >= 0 && index < s.Length ? s.Substring(index, 1) : null; + case JsonElement json when json.ValueKind == JsonValueKind.Array: + { + var i = 0; + foreach (var item in json.EnumerateArray()) + { + if (i++ == index) return ToNative(item); + } + return null; + } + default: + return null; + } + } + + private static object? GetLength(object? obj) + { + switch (obj) + { + case string s: return (long)s.Length; + case List list: return (long)list.Count; + case object[] arr: return (long)arr.Length; + case JsonElement json when json.ValueKind == JsonValueKind.Array: return (long)json.GetArrayLength(); + default: return null; + } + } + + // ===== 方法:toString / replace ===== + + /// 解析方法调用参数列表:('a','b') 或 (#.0##),返回字符串参数 + private List ParseMethodArgs() + { + SkipWhitespace(); + if (_pos >= _expr.Length || _expr[_pos] != '(') throw new InvalidOperationException("方法需要括号调用"); + _pos++; + + var args = new List(); + while (true) + { + SkipWhitespace(); + if (_pos >= _expr.Length) throw new InvalidOperationException("括号不匹配"); + if (_expr[_pos] == ')') { _pos++; break; } + + // 字符串参数 + if (_expr[_pos] == '\'' || _expr[_pos] == '"') + { + var quote = _expr[_pos++]; + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != quote) _pos++; + args.Add(_expr[start.._pos]); + if (_pos < _expr.Length) _pos++; + } + else + { + // 裸参数(如数字格式 #.0##) + var start = _pos; + while (_pos < _expr.Length && _expr[_pos] != ',' && _expr[_pos] != ')' && !char.IsWhiteSpace(_expr[_pos])) + _pos++; + args.Add(_expr[start.._pos]); + } + + SkipWhitespace(); + if (_pos < _expr.Length && _expr[_pos] == ',') { _pos++; continue; } + } + return args; + } + + /// toString("格式"):数字用自定义格式(#.0##),日期用日期格式(yyyy-MM-dd HH:mm:ss) + private static object? ToStringOp(object? val, List args) + { + var format = args.Count > 0 ? args[0]?.ToString() ?? "" : ""; + if (val == null) return ""; + + if (val is DateTime dt) + return dt.ToString(format, CultureInfo.InvariantCulture); + + if (val is string s) + { + if (!string.IsNullOrEmpty(format) && IsDateFormat(format)) + return DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed) + ? parsed.ToString(format, CultureInfo.InvariantCulture) : s; + if (format.IndexOfAny(new[] { '#', '0' }) >= 0) + return double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) + ? d.ToString(format, CultureInfo.InvariantCulture) : s; + return s; + } + + if (IsNumeric(val)) + return ToDouble(val).ToString(format, CultureInfo.InvariantCulture); + + return val.ToString() ?? ""; + } + + private static bool IsDateFormat(string format) + => format.IndexOfAny("yMdHhmsfFgKtz".ToCharArray()) >= 0; + + /// replace('旧','新'):字符串替换 + private static object? ReplaceOp(object? val, object? oldVal, object? newVal) + { + var s = val?.ToString() ?? ""; + var old = oldVal?.ToString() ?? ""; + var @new = newVal?.ToString() ?? ""; + return string.IsNullOrEmpty(old) ? s : s.Replace(old, @new); + } + + // ===== 算术 ===== + + /// +:两边都是数字时做加法,否则做字符串拼接 + private static object? Add(object? left, object? right) + { + if (IsNumeric(left) && IsNumeric(right)) return ToDouble(left) + ToDouble(right); + return ToDisplayString(left) + ToDisplayString(right); + } + + /// - * / %:数字运算(数字字符串自动转为数字,* 即"转化为数字";除零返回 null) + private static object? Arithmetic(object? left, object? right, char op) + { + var l = ToDouble(left); + var r = ToDouble(right); + if ((op == '/' || op == '%') && r == 0) return null; + return op switch + { + '-' => l - r, + '*' => l * r, + '/' => l / r, + '%' => l % r, + _ => l + }; + } + + private static object? Negate(object? v) => -ToDouble(v); + + /// 转展示字符串:null→""、bool→true/false、整数无小数点、日期 ISO 格式 + private static string ToDisplayString(object? v) + { + switch (v) + { + case null: return ""; + case bool b: return b ? "true" : "false"; + case double d: + return d == Math.Floor(d) && !double.IsInfinity(d) && Math.Abs(d) < 1e15 + ? ((long)d).ToString(CultureInfo.InvariantCulture) + : d.ToString(CultureInfo.InvariantCulture); + case float f: return f.ToString(CultureInfo.InvariantCulture); + case decimal m: return m.ToString(CultureInfo.InvariantCulture); + case DateTime dt: return dt.ToString("yyyy-MM-dd HH:mm:ss"); + default: return v.ToString() ?? ""; + } + } + + // ===== 比较与真值 ===== + + private static object Compare(object? left, object? right, string op) + { + // null 比较 + if (left == null || right == null) + return op switch + { + "==" => left == null && right == null, + "!=" => !(left == null && right == null), + _ => false + }; + + // 布尔比较(布尔值或 "true"/"false" 字符串) + if (TryAsBool(left, out var lb) && TryAsBool(right, out var rb)) + return op switch { "==" => lb == rb, "!=" => lb != rb, _ => false }; + + // 数字比较(任意一边可解析为数字时按数值比较) + if (IsNumeric(left) && IsNumeric(right)) + { + var l = ToDouble(left); + var r = ToDouble(right); + return op switch + { + "==" => l == r, + "!=" => l != r, + ">" => l > r, + "<" => l < r, + ">=" => l >= r, + "<=" => l <= r, + _ => false + }; + } + + // 字符串比较(==/!= 忽略大小写;日期统一为 ISO 格式后按字符串比较) + var ls = left is DateTime ldt ? ldt.ToString("yyyy-MM-dd HH:mm:ss") : left.ToString() ?? ""; + var rs = right is DateTime rdt ? rdt.ToString("yyyy-MM-dd HH:mm:ss") : right.ToString() ?? ""; + return op switch + { + "==" => string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), + "!=" => !string.Equals(ls, rs, StringComparison.OrdinalIgnoreCase), + ">" => string.Compare(ls, rs, StringComparison.Ordinal) > 0, + "<" => string.Compare(ls, rs, StringComparison.Ordinal) < 0, + ">=" => string.Compare(ls, rs, StringComparison.Ordinal) >= 0, + "<=" => string.Compare(ls, rs, StringComparison.Ordinal) <= 0, + _ => false + }; + } + + private static bool IsTruthy(object? v) + { + switch (v) + { + case null: return false; + case bool b: return b; + case string s: return s.Equals("true", StringComparison.OrdinalIgnoreCase) || s == "1"; + case long l: return l != 0; + case int i: return i != 0; + case double d: return d != 0; + case decimal m: return m != 0; + default: return false; + } + } + + private static bool TryAsBool(object? v, out bool result) + { + if (v is bool b) { result = b; return true; } + if (v is string s && bool.TryParse(s, out var parsed)) { result = parsed; return true; } + result = false; + return false; + } + + private static bool IsNumeric(object? v) + { + return v switch + { + byte or sbyte or short or ushort or int or uint or long or ulong + or float or double or decimal => true, + string s => double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out _), + _ => false + }; + } + + private static double ToDouble(object? v) + { + return v switch + { + byte x => x, sbyte x => x, short x => x, ushort x => x, + int x => x, uint x => x, long x => x, ulong x => x, + float x => x, double x => x, decimal x => (double)x, + string s => double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) ? d : 0, + _ => 0 + }; + } + + // ===== JsonElement → 原生类型 ===== + + /// 将 JsonElement 转为 .NET 原生类型,便于运算;对象/数组保留原 JsonElement 继续导航 + private static object? ToNative(object? obj) + { + if (obj is not JsonElement el) return obj; + return el.ValueKind switch + { + JsonValueKind.String => el.GetString(), + JsonValueKind.Number => el.TryGetInt64(out var l) ? l : el.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => el + }; + } + + // ===== 基础解析工具 ===== + + private string ParseIdentifier() + { + var start = _pos; + while (_pos < _expr.Length && (char.IsLetterOrDigit(_expr[_pos]) || _expr[_pos] == '_')) + _pos++; + return _expr[start.._pos]; + } + + private char Peek(int ahead = 0) => _pos + ahead < _expr.Length ? _expr[_pos + ahead] : '\0'; + + private void SkipWhitespace() + { + while (_pos < _expr.Length && char.IsWhiteSpace(_expr[_pos])) _pos++; + } +} diff --git a/Juggle.Domain/Engine/FlowModels.cs b/Juggle.Domain/Engine/FlowModels.cs index 3599897..3d0f43a 100644 --- a/Juggle.Domain/Engine/FlowModels.cs +++ b/Juggle.Domain/Engine/FlowModels.cs @@ -161,7 +161,7 @@ public class AssignRule [JsonPropertyName("source")] public string Source { get; set; } = ""; - /// VARIABLE / CONSTANT / STATIC(全局静态变量)/ INPUT / SUB_PROPERTY + /// VARIABLE / CONSTANT / STATIC(全局静态变量)/ INPUT / SUB_PROPERTY / ARRAY_OPERATION / EXPRESSION [JsonPropertyName("sourceType")] public string SourceType { get; set; } = "VARIABLE"; diff --git a/Juggle.Domain/Engine/NodeExecutors/AssignNodeExecutor.cs b/Juggle.Domain/Engine/NodeExecutors/AssignNodeExecutor.cs index dc7b108..7bb3305 100644 --- a/Juggle.Domain/Engine/NodeExecutors/AssignNodeExecutor.cs +++ b/Juggle.Domain/Engine/NodeExecutors/AssignNodeExecutor.cs @@ -11,6 +11,7 @@ namespace Juggle.Domain.Engine.NodeExecutors; /// INPUT — 流程入参 /// SUB_PROPERTY — 对象的子属性 /// ARRAY_OPERATION — 数组操作(分页/取第n个/插入/转JSON) +/// EXPRESSION — 表达式(算术 + - * / %、字符串拼接/切片/replace、toString 格式化,见 ExpressionEvaluator) /// targetType 支持: /// VARIABLE — 流程变量 /// STATIC — 写入全局静态变量(执行后持久化) @@ -44,6 +45,10 @@ public class AssignNodeExecutor : INodeExecutor case "ARRAY_OPERATION": value = ApplyArrayOperation(context.GetVariable(rule.Source), rule.ArrayOpType, rule.ArrayOpPageNum, rule.ArrayOpPageSize, rule.ArrayOpIndex); break; + case "EXPRESSION": + // 表达式来源:算术 / 字符串操作 / 格式化等(见 ExpressionEvaluator) + value = ExpressionEvaluator.EvaluateValue(rule.Source, context); + break; default: // VARIABLE / INPUT value = context.GetVariable(rule.Source); diff --git a/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs b/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs index bd72ddd..8c8dc25 100644 --- a/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs +++ b/Juggle.Domain/Engine/NodeExecutors/ConditionNodeExecutor.cs @@ -2,10 +2,11 @@ namespace Juggle.Domain.Engine.NodeExecutors; /// /// CONDITION 节点执行器:按条件表达式选择下一个分支。 -/// 表达式语法(见 ConditionExpressionEvaluator): -/// 支持 && || ! 括号、== != > < >= <= 比较 +/// 表达式语法(见 ExpressionEvaluator): +/// 逻辑 && || ! 括号;比较 == != > < >= <=;算术 + - * / % /// 左右两边都可以是变量/字面量 -/// 支持子属性(env_user.name)、数组长度(input_list.length)、数组元素(input_list[0].name) +/// 子属性(env_user.name)、数组长度(input_list.length)、数组元素(input_list[0].name) +/// 字符串拼接(+) 切片([..5]/[2..5]) 替换(replace) 格式化(toString) /// public class ConditionNodeExecutor : INodeExecutor { @@ -38,5 +39,5 @@ public class ConditionNodeExecutor : INodeExecutor } private static bool EvaluateExpression(string expression, FlowContext context) - => ConditionExpressionEvaluator.Evaluate(expression, context); + => ExpressionEvaluator.Evaluate(expression, context); } diff --git a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue index ecce70b..de9d12a 100644 --- a/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue +++ b/JuggleNet6.Frontend/src/views/flow/FlowDesign.vue @@ -507,10 +507,11 @@