Browse Source

生成部署脚本和部署指南。
修复n8n_client.py类型错误。

maxiaolong 4 ngày trước cách đây
mục cha
commit
7fbf9538c2

+ 2 - 2
app/api/data_factory/routes.py

@@ -54,8 +54,8 @@ def get_workflows():
         result = N8nService.get_workflows(
             page=page,
             page_size=page_size,
-            active=active if active is not None else False,
-            tags=tags if tags is not None else [],
+            active=active,  # None 表示不过滤
+            tags=tags,
             search=search,
         )
 

+ 105 - 79
app/config/config.py

@@ -1,6 +1,7 @@
 import os
 import platform
 
+
 def get_environment():
     """
     获取当前运行环境
@@ -9,151 +10,176 @@ def get_environment():
     2. 根据操作系统自动判断(Windows -> development, Linux -> production)
     """
     # 首先检查环境变量
-    env = os.environ.get('FLASK_ENV')
+    env = os.environ.get("FLASK_ENV")
     if env:
         return env.lower()
-    
+
     # 根据操作系统判断
     system = platform.system().lower()
-    if system == 'windows':
-        return 'development'
-    elif system == 'linux':
-        return 'production'
+    if system == "windows":
+        return "development"
+    elif system == "linux":
+        return "production"
     else:
-        return 'development'  # 其他系统默认使用开发环境
+        return "development"  # 其他系统默认使用开发环境
+
 
 class BaseConfig:
     """基础配置类,包含所有环境共享的配置"""
-    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
+
+    SECRET_KEY = os.environ.get("SECRET_KEY") or "you-will-never-guess"
     JSON_AS_ASCII = False
     JSONIFY_PRETTYPRINT_REGULAR = True
     JSON_SORT_KEYS = False
-    
+
     # 平台特定配置
     PLATFORM = platform.system().lower()
-    
+
     # 文件上传配置
-    ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'xlsx', 'xls', 'csv', 'sql', 'dll'}
-    
+    ALLOWED_EXTENSIONS = {
+        "txt",
+        "pdf",
+        "png",
+        "jpg",
+        "jpeg",
+        "gif",
+        "xlsx",
+        "xls",
+        "csv",
+        "sql",
+        "dll",
+    }
+
     # 数据抽取配置
     DATA_EXTRACT_BATCH_SIZE = 1000  # 每批处理的记录数
-    
+
     # PostgreSQL 基础配置
     SQLALCHEMY_ENGINE_OPTIONS = {
-        'pool_pre_ping': True,
-        'pool_recycle': 300,
-        'pool_size': 10,
-        'max_overflow': 20
+        "pool_pre_ping": True,
+        "pool_recycle": 300,
+        "pool_size": 10,
+        "max_overflow": 20,
     }
-    
+
     # LLM基础配置
     LLM_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
     LLM_MODEL_NAME = "qwen-turbo"
     # LLM_API_KEY = os.environ.get('LLM_API_KEY', "sk-86d4622141d74e9a8d7c38ee873c4d91")
-    LLM_API_KEY = os.environ.get('LLM_API_KEY', "sk-db68e37f00974031935395315bfe07f0")
-    
+    LLM_API_KEY = os.environ.get("LLM_API_KEY", "sk-db68e37f00974031935395315bfe07f0")
+
     # Qwen VL模型配置 - 用于图片解析
-    QWEN_API_KEY = os.environ.get('QWEN_API_KEY', "sk-db68e37f00974031935395315bfe07f0")
+    QWEN_API_KEY = os.environ.get("QWEN_API_KEY", "sk-db68e37f00974031935395315bfe07f0")
     QWEN_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
-    
+
     # Qwen API配置 - 用于文本生成(替代Deepseek)
-    QWEN_TEXT_API_KEY = os.environ.get('QWEN_TEXT_API_KEY', "sk-db68e37f00974031935395315bfe07f0")
+    QWEN_TEXT_API_KEY = os.environ.get(
+        "QWEN_TEXT_API_KEY", "sk-db68e37f00974031935395315bfe07f0"
+    )
     QWEN_TEXT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
     QWEN_TEXT_MODEL = "qwen-turbo"
-    
+
     # 日志基础配置
-    LOG_FORMAT = '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s'
-    LOG_ENCODING = 'UTF-8'
+    LOG_FORMAT = "%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s"
+    LOG_ENCODING = "UTF-8"
     LOG_ENABLED = True
-    
+
     # n8n 工作流引擎配置
-    N8N_API_URL = os.environ.get('N8N_API_URL', 'https://n8n.citupro.com')
-    N8N_API_KEY = os.environ.get('N8N_API_KEY', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MTcyNzlmMC1jNTQwLTQyMTEtYjczYy1mNjU4OTI5NTZhMmUiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzY2NTcyMDg0fQ.QgiUa5tEM1IGZSxhqFaWtdKvwk1SvoRmqdRovTT254M')
-    N8N_API_TIMEOUT = int(os.environ.get('N8N_API_TIMEOUT', '30'))
-    
+    N8N_API_URL = os.environ.get("N8N_API_URL", "https://n8n.citupro.com")
+    N8N_API_KEY = os.environ.get(
+        "N8N_API_KEY",
+        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MTcyNzlmMC1jNTQwLTQyMTEtYjczYy1mNjU4OTI5NTZhMmUiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzY2NTcyMDg0fQ.QgiUa5tEM1IGZSxhqFaWtdKvwk1SvoRmqdRovTT254M",
+    )
+    N8N_API_TIMEOUT = int(os.environ.get("N8N_API_TIMEOUT", "30"))
+
 
 class DevelopmentConfig(BaseConfig):
     """Windows 开发环境配置"""
-    FLASK_ENV = 'development'
+
+    FLASK_ENV = "development"
     DEBUG = True
     PORT = 5500
-    
+
     # 开发环境 MinIO 配置
-    MINIO_HOST = 'localhost:9000'
-    MINIO_USER = 'citu-test'
-    MINIO_PASSWORD = 'citu-test'
+    MINIO_HOST = "localhost:9000"
+    MINIO_USER = "citu-test"
+    MINIO_PASSWORD = "citu-test"
     MINIO_SECURE = False
-    MINIO_BUCKET = 'dataops-bucket'
-    PREFIX = ''
-    
+    MINIO_BUCKET = "dataops-bucket"
+    PREFIX = ""
+
     # 开发环境 PostgreSQL 配置
-    SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:postgres@localhost:5432/dataops'
-    
+    SQLALCHEMY_DATABASE_URI = "postgresql://postgres:postgres@localhost:5432/dataops"
+
     # 开发环境 Neo4j 配置
     NEO4J_URI = "bolt://localhost:7687"
     NEO4J_HTTP_URI = "http://localhost:7474"
     NEO4J_USER = "neo4j"
     NEO4J_PASSWORD = "Passw0rd"
     NEO4J_ENCRYPTED = False
-    
+
     # 开发环境文件路径配置
-    UPLOAD_BASE_PATH = 'C:\\tmp\\upload'
-    ARCHIVE_BASE_PATH = 'C:\\tmp\\archive'
-    
+    UPLOAD_BASE_PATH = "C:\\tmp\\upload"
+    ARCHIVE_BASE_PATH = "C:\\tmp\\archive"
+
     # 开发环境日志配置
-    LOG_LEVEL = 'DEBUG'
-    LOG_FILE = 'flask_development.log'
+    LOG_LEVEL = "DEBUG"
+    LOG_FILE = "flask_development.log"
     LOG_TO_CONSOLE = True
-    
+
     # 开发环境 Airflow 配置
-    AIRFLOW_BASE_URL = 'http://localhost:8080'
-    AIRFLOW_AUTH_USER = 'admin'
-    AIRFLOW_AUTH_PASSWORD = 'admin'
+    AIRFLOW_BASE_URL = "http://localhost:8080"
+    AIRFLOW_AUTH_USER = "admin"
+    AIRFLOW_AUTH_PASSWORD = "admin"
+
 
 class ProductionConfig(BaseConfig):
     """Linux 生产环境配置"""
-    FLASK_ENV = 'production'
+
+    FLASK_ENV = "production"
     DEBUG = False
     PORT = 80
-    
+
     # 生产环境 MinIO 配置
-    MINIO_HOST = '192.168.3.143:9000'
-    MINIO_USER = 'citu-dataops-acc-key'
-    MINIO_PASSWORD = 'citu-dataops-secret-key'
+    MINIO_HOST = "192.168.3.143:9000"
+    MINIO_USER = "citu-dataops-acc-key"
+    MINIO_PASSWORD = "citu-dataops-secret-key"
     MINIO_SECURE = False
-    MINIO_BUCKET = 'dataops-bucket'
-    PREFIX = ''
-    
+    MINIO_BUCKET = "dataops-bucket"
+    PREFIX = ""
+
     # 生产环境 PostgreSQL 配置
-    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'postgresql://postgres:dataOps@192.168.3.143:5432/dataops')
-    
+    SQLALCHEMY_DATABASE_URI = os.environ.get(
+        "DATABASE_URL", "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
+    )
+
     # 生产环境 Neo4j 配置
-    NEO4J_URI = os.environ.get('NEO4J_URI', "bolt://192.168.3.143:7687")
-    NEO4J_HTTP_URI = os.environ.get('NEO4J_HTTP_URI', "http://192.168.3.143:7474")
-    NEO4J_USER = os.environ.get('NEO4J_USER', "neo4j")
-    NEO4J_PASSWORD = os.environ.get('NEO4J_PASSWORD', "cituneo4j")
+    NEO4J_URI = os.environ.get("NEO4J_URI", "bolt://192.168.3.143:7687")
+    NEO4J_HTTP_URI = os.environ.get("NEO4J_HTTP_URI", "http://192.168.3.143:7474")
+    NEO4J_USER = os.environ.get("NEO4J_USER", "neo4j")
+    NEO4J_PASSWORD = os.environ.get("NEO4J_PASSWORD", "cituneo4j")
     NEO4J_ENCRYPTED = False
-    
+
     # 生产环境文件路径配置
-    UPLOAD_BASE_PATH = '/data/upload'
-    ARCHIVE_BASE_PATH = '/data/archive'
-    
+    UPLOAD_BASE_PATH = "/data/upload"
+    ARCHIVE_BASE_PATH = "/data/archive"
+
     # 生产环境日志配置
-    LOG_LEVEL = 'INFO'
-    LOG_FILE = 'flask_production.log'
+    LOG_LEVEL = "INFO"
+    LOG_FILE = "flask_production.log"
     LOG_TO_CONSOLE = False
-    
+
     # 生产环境 Airflow 配置
-    AIRFLOW_BASE_URL = os.environ.get('AIRFLOW_BASE_URL', 'http://192.168.3.143:8080')
-    AIRFLOW_AUTH_USER = os.environ.get('AIRFLOW_AUTH_USER', 'admin')
-    AIRFLOW_AUTH_PASSWORD = os.environ.get('AIRFLOW_AUTH_PASSWORD', 'admin')
+    AIRFLOW_BASE_URL = os.environ.get("AIRFLOW_BASE_URL", "http://192.168.3.143:8080")
+    AIRFLOW_AUTH_USER = os.environ.get("AIRFLOW_AUTH_USER", "admin")
+    AIRFLOW_AUTH_PASSWORD = os.environ.get("AIRFLOW_AUTH_PASSWORD", "admin")
+
 
 # 配置字典
 config = {
-    'development': DevelopmentConfig,
-    'production': ProductionConfig,
-    'default': DevelopmentConfig
+    "development": DevelopmentConfig,
+    "production": ProductionConfig,
+    "default": DevelopmentConfig,
 }
 
 # 获取当前环境
-current_env = get_environment() 
+current_env = get_environment()

+ 11 - 11
app/core/data_factory/n8n_client.py

@@ -165,14 +165,14 @@ class N8nClient:
         Returns:
             工作流列表数据
         """
-        params = {"limit": min(limit, 100)}
+        params: Dict[str, Any] = {"limit": min(limit, 100)}
 
         if active is not None:
-            params["active"] = int(active)
+            params["active"] = "true" if active else "false"
         if tags:
-            params["tags"] = ",".join(tags)  # type: ignore
+            params["tags"] = ",".join(tags)
         if cursor:
-            params["cursor"] = cursor  # type: ignore
+            params["cursor"] = cursor
 
         return self._request("GET", "workflows", params=params)
 
@@ -235,16 +235,16 @@ class N8nClient:
         Returns:
             执行记录列表数据
         """
-        params = {"limit": min(limit, 100)}
+        params: Dict[str, Any] = {"limit": min(limit, 100)}
 
         if workflow_id:
-            params["workflowId"] = int(workflow_id)
+            params["workflowId"] = workflow_id
         if status:
-            params["status"] = int(status)
+            params["status"] = status
         if cursor:
-            params["cursor"] = cursor  # type: ignore
+            params["cursor"] = cursor
         if include_data:
-            params["includeData"] = int(include_data)
+            params["includeData"] = "true"
 
         return self._request("GET", "executions", params=params)
 
@@ -261,9 +261,9 @@ class N8nClient:
         Returns:
             执行详情数据
         """
-        params = {}
+        params: Dict[str, Any] = {}
         if include_data:
-            params["includeData"] = int(include_data)
+            params["includeData"] = "true"
 
         return self._request("GET", f"executions/{execution_id}", params=params)
 

+ 585 - 0
docs/PRODUCTION_DEPLOYMENT_GUIDE.md

@@ -0,0 +1,585 @@
+# DataOps Platform 生产环境部署与运维手册
+
+## 目录
+
+1. [系统概述](#1-系统概述)
+2. [服务器环境](#2-服务器环境)
+3. [目录结构](#3-目录结构)
+4. [配置参数](#4-配置参数)
+5. [虚拟环境管理](#5-虚拟环境管理)
+6. [服务管理](#6-服务管理)
+7. [日志管理](#7-日志管理)
+8. [常见问题排查](#8-常见问题排查)
+9. [备份与恢复](#9-备份与恢复)
+10. [API 接口文档](#10-api-接口文档)
+
+---
+
+## 1. 系统概述
+
+### 1.1 项目简介
+
+DataOps Platform 是一个基于 Flask 的数据运营平台,提供数据管理、处理和分析功能。平台集成了 Neo4j 图数据库进行关系管理,并支持 n8n 工作流自动化。
+
+### 1.2 技术栈
+
+| 组件 | 技术 | 版本 |
+|------|------|------|
+| Web 框架 | Flask | 2.3.3 |
+| ORM | SQLAlchemy | 2.0.23 |
+| 关系数据库 | PostgreSQL | 14+ |
+| 图数据库 | Neo4j | 5.x |
+| 对象存储 | MinIO | - |
+| 工作流引擎 | n8n | 1.116.2 |
+| WSGI 服务器 | Gunicorn | 21.2.0 |
+| 进程管理 | Supervisor | - |
+| 反向代理 | Nginx | - |
+
+### 1.3 访问地址
+
+| 服务 | 地址 |
+|------|------|
+| **DataOps API** | `https://company.citupro.com:18183` |
+| **n8n 工作流** | `https://n8n.citupro.com` |
+| **Neo4j Browser** | `http://192.168.3.143:7474` |
+| **MinIO Console** | `http://192.168.3.143:9000` |
+
+---
+
+## 2. 服务器环境
+
+### 2.1 服务器信息
+
+| 项目 | 值 |
+|------|------|
+| 操作系统 | Ubuntu 20.04/22.04 LTS |
+| 用户 | ubuntu |
+| Python 版本 | 3.8+ |
+| 服务器 IP | 192.168.3.143 |
+
+### 2.2 依赖服务
+
+确保以下服务已安装并运行:
+
+```bash
+# 检查 Python 版本
+python3 --version
+
+# 检查 Supervisor
+sudo systemctl status supervisor
+
+# 检查 Nginx
+sudo systemctl status nginx
+
+# 检查 PostgreSQL 连接
+psql -h 192.168.3.143 -U postgres -d dataops -c "SELECT 1"
+
+# 检查 Neo4j 连接
+curl http://192.168.3.143:7474
+```
+
+---
+
+## 3. 目录结构
+
+### 3.1 安装路径
+
+```
+/opt/dataops-platform/           # 应用根目录
+├── app/                         # 应用代码
+│   ├── api/                     # API 路由
+│   │   ├── business_domain/     # 业务领域 API
+│   │   ├── data_factory/        # n8n 工作流 API
+│   │   ├── data_flow/           # 数据流 API
+│   │   ├── data_interface/      # 数据接口 API
+│   │   ├── data_source/         # 数据源 API
+│   │   ├── graph/               # 图数据 API
+│   │   ├── meta_data/           # 元数据 API
+│   │   └── system/              # 系统 API
+│   ├── config/                  # 配置文件
+│   │   ├── config.py            # 主配置
+│   │   └── cors.py              # CORS 配置
+│   ├── core/                    # 核心业务逻辑
+│   ├── models/                  # 数据模型
+│   ├── scripts/                 # 数据库脚本
+│   └── services/                # 共享服务
+├── database/                    # SQL 脚本
+├── docs/                        # 文档
+├── logs/                        # 应用日志
+├── scripts/                     # 运维脚本
+│   ├── deploy_dataops.sh        # 部署脚本
+│   ├── start_dataops.sh         # 启动脚本
+│   ├── stop_dataops.sh          # 停止脚本
+│   └── restart_dataops.sh       # 重启脚本
+├── tests/                       # 测试文件
+├── venv/                        # Python 虚拟环境
+├── application.py               # 应用入口(开发用)
+├── wsgi.py                      # WSGI 入口(生产用)
+├── requirements.txt             # Python 依赖
+└── gunicorn.conf.py             # Gunicorn 配置(可选)
+```
+
+### 3.2 日志路径
+
+| 日志类型 | 路径 |
+|----------|------|
+| 应用日志 | `/opt/dataops-platform/flask_production.log` |
+| Supervisor 日志 | `/var/log/supervisor/dataops-platform.log` |
+| Nginx 访问日志 | `/var/log/nginx/access.log` |
+| Nginx 错误日志 | `/var/log/nginx/error.log` |
+
+### 3.3 配置文件路径
+
+| 配置 | 路径 |
+|------|------|
+| Supervisor | `/etc/supervisor/conf.d/dataops-platform.conf` |
+| Nginx | `/etc/nginx/sites-enabled/dataops-platform.conf` |
+| 应用配置 | `/opt/dataops-platform/app/config/config.py` |
+
+---
+
+## 4. 配置参数
+
+### 4.1 应用配置 (`app/config/config.py`)
+
+#### 生产环境配置 (ProductionConfig)
+
+```python
+# Flask 配置
+FLASK_ENV = "production"
+DEBUG = False
+PORT = 80
+
+# PostgreSQL 数据库
+SQLALCHEMY_DATABASE_URI = "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
+
+# Neo4j 图数据库
+NEO4J_URI = "bolt://192.168.3.143:7687"
+NEO4J_HTTP_URI = "http://192.168.3.143:7474"
+NEO4J_USER = "neo4j"
+NEO4J_PASSWORD = "cituneo4j"
+
+# MinIO 对象存储
+MINIO_HOST = "192.168.3.143:9000"
+MINIO_USER = "citu-dataops-acc-key"
+MINIO_PASSWORD = "citu-dataops-secret-key"
+MINIO_BUCKET = "dataops-bucket"
+
+# n8n 工作流引擎
+N8N_API_URL = "https://n8n.citupro.com"
+N8N_API_KEY = "<JWT_TOKEN>"
+N8N_API_TIMEOUT = 30
+
+# LLM 配置 (阿里云通义千问)
+LLM_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
+LLM_MODEL_NAME = "qwen-turbo"
+LLM_API_KEY = "<API_KEY>"
+
+# 文件路径
+UPLOAD_BASE_PATH = "/data/upload"
+ARCHIVE_BASE_PATH = "/data/archive"
+
+# 日志配置
+LOG_LEVEL = "INFO"
+LOG_FILE = "flask_production.log"
+LOG_TO_CONSOLE = False
+```
+
+### 4.2 Supervisor 配置
+
+**文件**: `/etc/supervisor/conf.d/dataops-platform.conf`
+
+```ini
+[program:dataops-platform]
+command=/opt/dataops-platform/venv/bin/gunicorn --bind 0.0.0.0:5500 --workers 4 --timeout 120 wsgi:application
+directory=/opt/dataops-platform
+user=ubuntu
+autostart=true
+autorestart=true
+redirect_stderr=true
+stdout_logfile=/var/log/supervisor/dataops-platform.log
+stdout_logfile_maxbytes=50MB
+stdout_logfile_backups=5
+environment=FLASK_ENV=production,PATH="/opt/dataops-platform/venv/bin"
+```
+
+**配置说明**:
+
+| 参数 | 值 | 说明 |
+|------|------|------|
+| `--bind` | `0.0.0.0:5500` | 监听地址和端口 |
+| `--workers` | `4` | Worker 进程数 |
+| `--timeout` | `120` | 请求超时时间(秒) |
+| `autostart` | `true` | 系统启动时自动启动 |
+| `autorestart` | `true` | 进程异常退出时自动重启 |
+
+### 4.3 环境变量(可选)
+
+可以通过环境变量覆盖默认配置:
+
+```bash
+export FLASK_ENV=production
+export DATABASE_URL=postgresql://user:pass@host:5432/db
+export NEO4J_URI=bolt://host:7687
+export NEO4J_PASSWORD=password
+export N8N_API_KEY=your_api_key
+export LLM_API_KEY=your_llm_api_key
+```
+
+---
+
+## 5. 虚拟环境管理
+
+### 5.1 虚拟环境路径
+
+```
+/opt/dataops-platform/venv/
+```
+
+### 5.2 激活虚拟环境
+
+```bash
+cd /opt/dataops-platform
+source venv/bin/activate
+```
+
+### 5.3 查看已安装的包
+
+```bash
+source venv/bin/activate
+pip list
+```
+
+### 5.4 安装/更新依赖
+
+```bash
+source venv/bin/activate
+pip install -r requirements.txt
+```
+
+### 5.5 重建虚拟环境
+
+如果虚拟环境损坏,可以重建:
+
+```bash
+cd /opt/dataops-platform
+
+# 删除旧的虚拟环境
+rm -rf venv
+
+# 创建新的虚拟环境
+python3 -m venv venv
+
+# 激活并安装依赖
+source venv/bin/activate
+pip install --upgrade pip
+pip install -r requirements.txt
+
+# 验证安装
+python -c "from app import create_app; app = create_app(); print('成功')"
+```
+
+### 5.6 主要依赖包
+
+| 包名 | 版本 | 用途 |
+|------|------|------|
+| Flask | 2.3.3 | Web 框架 |
+| Flask-SQLAlchemy | 3.1.1 | ORM 扩展 |
+| SQLAlchemy | 2.0.23 | ORM |
+| psycopg2-binary | 2.9.9 | PostgreSQL 驱动 |
+| neo4j | 5.26.0 | Neo4j 驱动 |
+| minio | 7.2.10 | MinIO 客户端 |
+| openai | 1.58.1 | LLM 客户端 |
+| gunicorn | 21.2.0 | WSGI 服务器 |
+| pandas | 2.0.3 | 数据处理 |
+| loguru | 0.7.2 | 日志 |
+
+---
+
+## 6. 服务管理
+
+### 6.1 使用运维脚本
+
+```bash
+cd /opt/dataops-platform
+
+# 启动服务
+sudo ./scripts/start_dataops.sh
+
+# 停止服务
+sudo ./scripts/stop_dataops.sh
+
+# 重启服务
+sudo ./scripts/restart_dataops.sh
+```
+
+### 6.2 使用 Supervisor 命令
+
+```bash
+# 查看状态
+sudo supervisorctl status dataops-platform
+
+# 启动服务
+sudo supervisorctl start dataops-platform
+
+# 停止服务
+sudo supervisorctl stop dataops-platform
+
+# 重启服务
+sudo supervisorctl restart dataops-platform
+
+# 重新加载配置
+sudo supervisorctl reread
+sudo supervisorctl update
+
+# 查看实时日志
+sudo supervisorctl tail -f dataops-platform
+```
+
+### 6.3 健康检查
+
+```bash
+# 本地检查
+curl http://127.0.0.1:5500/api/system/health
+
+# 外部检查
+curl https://company.citupro.com:18183/api/system/health
+```
+
+**正常响应**:
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "status": "healthy",
+    "components": {
+      "database": true,
+      "neo4j": true,
+      "environment": "production",
+      "platform": "linux"
+    }
+  }
+}
+```
+
+### 6.4 代码更新流程
+
+```bash
+cd /opt/dataops-platform
+
+# 1. 拉取最新代码(如果使用 Git)
+git pull origin main
+
+# 2. 激活虚拟环境并更新依赖(如有变更)
+source venv/bin/activate
+pip install -r requirements.txt
+
+# 3. 重启服务
+sudo supervisorctl restart dataops-platform
+
+# 4. 验证服务状态
+sudo supervisorctl status dataops-platform
+curl http://127.0.0.1:5500/api/system/health
+```
+
+---
+
+## 7. 日志管理
+
+### 7.1 查看日志
+
+```bash
+# 查看应用日志
+tail -f /opt/dataops-platform/flask_production.log
+
+# 查看 Supervisor 日志
+tail -f /var/log/supervisor/dataops-platform.log
+
+# 查看最近 100 行日志
+tail -100 /var/log/supervisor/dataops-platform.log
+
+# 搜索错误日志
+grep -i "error" /var/log/supervisor/dataops-platform.log
+```
+
+### 7.2 日志级别
+
+生产环境默认日志级别为 `INFO`,如需调试可临时修改为 `DEBUG`:
+
+```python
+# app/config/config.py
+LOG_LEVEL = "DEBUG"  # 改为 DEBUG
+```
+
+修改后重启服务生效。
+
+### 7.3 日志轮转
+
+Supervisor 自动管理日志轮转:
+- 最大文件大小:50MB
+- 保留备份数:5
+
+---
+
+## 8. 常见问题排查
+
+### 8.1 服务无法启动
+
+**检查步骤**:
+
+```bash
+# 1. 检查 Supervisor 状态
+sudo supervisorctl status dataops-platform
+
+# 2. 查看错误日志
+tail -50 /var/log/supervisor/dataops-platform.log
+
+# 3. 手动测试启动
+cd /opt/dataops-platform
+source venv/bin/activate
+python -c "from app import create_app; app = create_app(); print('成功')"
+```
+
+**常见原因**:
+- 虚拟环境路径错误
+- 依赖包缺失
+- 配置文件错误
+- 端口被占用
+
+### 8.2 数据库连接失败
+
+```bash
+# 测试 PostgreSQL 连接
+psql -h 192.168.3.143 -U postgres -d dataops -c "SELECT 1"
+
+# 测试 Neo4j 连接
+curl http://192.168.3.143:7474
+```
+
+### 8.3 n8n API 连接失败
+
+```bash
+# 测试 n8n API
+curl -H "X-N8N-API-KEY: <YOUR_API_KEY>" https://n8n.citupro.com/api/v1/workflows
+```
+
+### 8.4 依赖包缺失
+
+```bash
+source venv/bin/activate
+pip install <package_name>
+sudo supervisorctl restart dataops-platform
+```
+
+### 8.5 权限问题
+
+```bash
+# 修复文件权限
+sudo chown -R ubuntu:ubuntu /opt/dataops-platform
+chmod +x /opt/dataops-platform/scripts/*.sh
+```
+
+---
+
+## 9. 备份与恢复
+
+### 9.1 备份内容
+
+| 内容 | 路径/命令 |
+|------|------|
+| 应用代码 | `/opt/dataops-platform/` |
+| PostgreSQL 数据库 | `pg_dump -h 192.168.3.143 -U postgres dataops > backup.sql` |
+| Neo4j 数据库 | Neo4j Admin 导出 |
+| MinIO 文件 | `mc mirror minio/dataops-bucket ./backup/` |
+
+### 9.2 备份脚本示例
+
+```bash
+#!/bin/bash
+BACKUP_DIR="/data/backup/$(date +%Y%m%d)"
+mkdir -p $BACKUP_DIR
+
+# 备份代码
+tar -czf $BACKUP_DIR/dataops-code.tar.gz /opt/dataops-platform/app
+
+# 备份数据库
+pg_dump -h 192.168.3.143 -U postgres dataops > $BACKUP_DIR/dataops-db.sql
+
+echo "备份完成: $BACKUP_DIR"
+```
+
+### 9.3 恢复数据库
+
+```bash
+psql -h 192.168.3.143 -U postgres dataops < backup.sql
+```
+
+---
+
+## 10. API 接口文档
+
+### 10.1 系统接口
+
+| 接口 | 方法 | 路径 | 说明 |
+|------|------|------|------|
+| 健康检查 | GET | `/api/system/health` | 系统健康状态 |
+| 系统信息 | GET | `/api/system/info` | 系统配置信息 |
+
+### 10.2 工作流接口 (Data Factory)
+
+| 接口 | 方法 | 路径 | 说明 |
+|------|------|------|------|
+| n8n 健康检查 | GET | `/api/datafactory/health` | n8n 连接状态 |
+| 工作流列表 | GET | `/api/datafactory/workflows` | 获取工作流列表 |
+| 工作流详情 | GET | `/api/datafactory/workflows/{id}` | 获取工作流详情 |
+| 工作流状态 | GET | `/api/datafactory/workflows/{id}/status` | 获取工作流状态 |
+| 激活工作流 | POST | `/api/datafactory/workflows/{id}/activate` | 激活工作流 |
+| 停用工作流 | POST | `/api/datafactory/workflows/{id}/deactivate` | 停用工作流 |
+| 执行记录列表 | GET | `/api/datafactory/executions` | 获取执行记录 |
+| 执行详情 | GET | `/api/datafactory/executions/{id}` | 获取执行详情 |
+| 触发工作流 | POST | `/api/datafactory/workflows/{id}/execute` | 触发工作流执行 |
+
+### 10.3 其他 API 模块
+
+| 模块 | 前缀 | 说明 |
+|------|------|------|
+| 元数据 | `/api/meta/` | 元数据管理 |
+| 图数据 | `/api/graph/` | Neo4j 图数据操作 |
+| 数据接口 | `/api/interface/` | 数据接口管理 |
+| 数据源 | `/api/datasource/` | 数据源管理 |
+| 数据流 | `/api/dataflow/` | 数据流管理 |
+| 业务领域 | `/api/bd/` | 业务领域管理 |
+
+---
+
+## 附录
+
+### A. 联系方式
+
+如有问题,请联系运维团队。
+
+### B. 更新记录
+
+| 日期 | 版本 | 更新内容 |
+|------|------|------|
+| 2025-12-25 | 1.0.0 | 初始版本,包含完整部署说明 |
+
+### C. 快速命令参考
+
+```bash
+# 服务管理
+sudo supervisorctl status dataops-platform
+sudo supervisorctl restart dataops-platform
+
+# 日志查看
+tail -f /var/log/supervisor/dataops-platform.log
+
+# 健康检查
+curl http://127.0.0.1:5500/api/system/health
+
+# 虚拟环境
+source /opt/dataops-platform/venv/bin/activate
+```
+

+ 20 - 7
requirements.txt

@@ -9,26 +9,39 @@ Flask-Migrate==4.0.5
 SQLAlchemy==2.0.23
 alembic==1.13.0
 psycopg2-binary==2.9.9
+neo4j==5.26.0
+
+# 对象存储
+minio==7.2.10
+
+# AI/LLM 相关
+openai==1.58.1
 
 # 工具库
 python-dotenv==1.0.0
 requests==2.31.0
-pandas==2.1.4
-numpy==1.25.2
+pandas==2.0.3
+numpy==1.24.3
 
-# 开发工具
-pytest==7.4.3
-ruff==0.8.6
-pyright==1.1.391
+# 系统监控
+psutil==5.9.8
 
 # 日志和配置
 loguru==0.7.2
 python-multipart==0.0.6
 
+# 生产环境部署
+gunicorn==21.2.0
+
 # 其他必要依赖
 Werkzeug==2.3.7
 Jinja2==3.1.2
 MarkupSafe==2.1.3
 itsdangerous==2.1.2
 click==8.1.7
-blinker==1.6.3
+blinker==1.6.3
+
+# 开发工具(生产环境可选)
+# pytest==7.4.3
+# ruff==0.8.6
+# pyright==1.1.391

+ 355 - 0
scripts/deploy_dataops.sh

@@ -0,0 +1,355 @@
+#!/bin/bash
+#
+# DataOps Platform 部署脚本
+# 用于初始化或重建虚拟环境并配置 supervisor
+#
+
+set -e
+
+# 配置变量
+APP_NAME="dataops-platform"
+APP_DIR="/opt/dataops-platform"
+VENV_DIR="${APP_DIR}/venv"
+LOG_DIR="${APP_DIR}/logs"
+PYTHON_VERSION="python3"
+SUPERVISOR_CONF="/etc/supervisor/conf.d/${APP_NAME}.conf"
+
+# Gunicorn 配置
+GUNICORN_WORKERS=4
+GUNICORN_BIND="0.0.0.0:80"
+GUNICORN_TIMEOUT=120
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+echo_info() {
+    echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+echo_warn() {
+    echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+echo_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+echo_step() {
+    echo -e "${BLUE}[STEP]${NC} $1"
+}
+
+# 检查是否以 root 或 sudo 运行
+check_permissions() {
+    if [ "$EUID" -ne 0 ]; then
+        echo_error "请使用 sudo 运行此脚本"
+        exit 1
+    fi
+}
+
+# 检查 Python 版本
+check_python() {
+    echo_step "检查 Python 版本..."
+    
+    if ! command -v ${PYTHON_VERSION} &> /dev/null; then
+        echo_error "Python3 未安装"
+        exit 1
+    fi
+    
+    python_ver=$(${PYTHON_VERSION} --version 2>&1 | awk '{print $2}')
+    echo_info "Python 版本: ${python_ver}"
+    
+    # 检查 python3-venv
+    if ! dpkg -l | grep -q python3-venv; then
+        echo_warn "python3-venv 未安装,正在安装..."
+        apt-get update && apt-get install -y python3-venv python3-pip
+    fi
+}
+
+# 检查 supervisor
+check_supervisor() {
+    echo_step "检查 Supervisor..."
+    
+    if ! command -v supervisord &> /dev/null; then
+        echo_warn "Supervisor 未安装,正在安装..."
+        apt-get update && apt-get install -y supervisor
+        systemctl enable supervisor
+        systemctl start supervisor
+    fi
+    
+    echo_info "Supervisor 已安装"
+}
+
+# 创建目录结构
+create_directories() {
+    echo_step "创建目录结构..."
+    
+    mkdir -p ${LOG_DIR}
+    chown -R ubuntu:ubuntu ${LOG_DIR}
+    
+    echo_info "日志目录: ${LOG_DIR}"
+}
+
+# 创建虚拟环境
+create_venv() {
+    echo_step "创建虚拟环境..."
+    
+    # 如果虚拟环境存在,先备份再删除
+    if [ -d "${VENV_DIR}" ]; then
+        echo_warn "发现已存在的虚拟环境,正在删除..."
+        rm -rf ${VENV_DIR}
+    fi
+    
+    # 创建新的虚拟环境
+    ${PYTHON_VERSION} -m venv ${VENV_DIR}
+    
+    # 更新 pip
+    ${VENV_DIR}/bin/pip install --upgrade pip
+    
+    echo_info "虚拟环境创建完成: ${VENV_DIR}"
+}
+
+# 安装依赖
+install_dependencies() {
+    echo_step "安装 Python 依赖..."
+    
+    if [ ! -f "${APP_DIR}/requirements.txt" ]; then
+        echo_error "requirements.txt 不存在"
+        exit 1
+    fi
+    
+    # 安装依赖
+    ${VENV_DIR}/bin/pip install -r ${APP_DIR}/requirements.txt
+    
+    # 确保 gunicorn 已安装
+    ${VENV_DIR}/bin/pip install gunicorn
+    
+    echo_info "依赖安装完成"
+    
+    # 显示已安装的关键包
+    echo_info "已安装的关键包:"
+    ${VENV_DIR}/bin/pip list | grep -E "Flask|gunicorn|neo4j|SQLAlchemy|psycopg2"
+}
+
+# 验证安装
+verify_installation() {
+    echo_step "验证安装..."
+    
+    # 测试导入
+    cd ${APP_DIR}
+    ${VENV_DIR}/bin/python -c "
+from app import create_app
+app = create_app()
+print('Flask 应用创建成功')
+print(f'已注册的蓝图: {list(app.blueprints.keys())}')
+" || {
+        echo_error "应用验证失败"
+        exit 1
+    }
+    
+    echo_info "应用验证通过"
+}
+
+# 创建 Gunicorn 配置文件
+create_gunicorn_config() {
+    echo_step "创建 Gunicorn 配置..."
+    
+    cat > ${APP_DIR}/gunicorn.conf.py << EOF
+# Gunicorn 配置文件
+import multiprocessing
+
+# 绑定地址
+bind = "${GUNICORN_BIND}"
+
+# Worker 进程数
+workers = ${GUNICORN_WORKERS}
+
+# Worker 类型
+worker_class = "sync"
+
+# 超时时间
+timeout = ${GUNICORN_TIMEOUT}
+
+# 优雅重启超时
+graceful_timeout = 30
+
+# 保持连接时间
+keepalive = 5
+
+# 最大请求数(防止内存泄漏)
+max_requests = 1000
+max_requests_jitter = 50
+
+# 日志配置
+accesslog = "${LOG_DIR}/gunicorn_access.log"
+errorlog = "${LOG_DIR}/gunicorn_error.log"
+loglevel = "info"
+
+# 进程名
+proc_name = "${APP_NAME}"
+
+# 工作目录
+chdir = "${APP_DIR}"
+
+# 预加载应用
+preload_app = True
+
+# 环境变量
+raw_env = [
+    "FLASK_ENV=production",
+]
+EOF
+
+    chown ubuntu:ubuntu ${APP_DIR}/gunicorn.conf.py
+    echo_info "Gunicorn 配置文件已创建: ${APP_DIR}/gunicorn.conf.py"
+}
+
+# 创建 WSGI 入口文件
+create_wsgi() {
+    echo_step "创建 WSGI 入口文件..."
+    
+    cat > ${APP_DIR}/wsgi.py << 'EOF'
+"""
+WSGI 入口文件
+用于 Gunicorn 启动 Flask 应用
+"""
+from app import create_app
+
+application = create_app()
+
+if __name__ == "__main__":
+    application.run()
+EOF
+
+    chown ubuntu:ubuntu ${APP_DIR}/wsgi.py
+    echo_info "WSGI 入口文件已创建: ${APP_DIR}/wsgi.py"
+}
+
+# 配置 Supervisor
+configure_supervisor() {
+    echo_step "配置 Supervisor..."
+    
+    cat > ${SUPERVISOR_CONF} << EOF
+[program:${APP_NAME}]
+command=${VENV_DIR}/bin/gunicorn -c ${APP_DIR}/gunicorn.conf.py wsgi:application
+directory=${APP_DIR}
+user=ubuntu
+autostart=true
+autorestart=true
+stopasgroup=true
+killasgroup=true
+redirect_stderr=true
+stdout_logfile=${LOG_DIR}/supervisor_stdout.log
+stderr_logfile=${LOG_DIR}/supervisor_stderr.log
+environment=FLASK_ENV="production",PATH="${VENV_DIR}/bin:%(ENV_PATH)s"
+EOF
+
+    echo_info "Supervisor 配置已创建: ${SUPERVISOR_CONF}"
+    
+    # 重新加载 supervisor 配置
+    supervisorctl reread
+    supervisorctl update
+    
+    echo_info "Supervisor 配置已更新"
+}
+
+# 设置文件权限
+set_permissions() {
+    echo_step "设置文件权限..."
+    
+    chown -R ubuntu:ubuntu ${APP_DIR}
+    chmod +x ${APP_DIR}/scripts/*.sh 2>/dev/null || true
+    
+    echo_info "文件权限设置完成"
+}
+
+# 启动应用
+start_application() {
+    echo_step "启动应用..."
+    
+    supervisorctl start ${APP_NAME}
+    
+    sleep 3
+    
+    status=$(supervisorctl status ${APP_NAME} | awk '{print $2}')
+    if [ "$status" = "RUNNING" ]; then
+        echo_info "应用启动成功!"
+        supervisorctl status ${APP_NAME}
+    else
+        echo_error "应用启动失败,请检查日志"
+        echo_info "查看日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+        exit 1
+    fi
+}
+
+# 健康检查
+health_check() {
+    echo_step "执行健康检查..."
+    sleep 3
+    
+    response=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:80/api/system/health 2>/dev/null || echo "000")
+    
+    if [ "$response" = "200" ]; then
+        echo_info "健康检查通过! HTTP 状态码: ${response}"
+    else
+        echo_warn "健康检查返回状态码: ${response}"
+        echo_info "服务可能需要更多时间启动"
+    fi
+}
+
+# 显示部署信息
+show_summary() {
+    echo ""
+    echo "=========================================="
+    echo -e "${GREEN}  部署完成!${NC}"
+    echo "=========================================="
+    echo ""
+    echo "应用信息:"
+    echo "  - 应用名称: ${APP_NAME}"
+    echo "  - 应用目录: ${APP_DIR}"
+    echo "  - 虚拟环境: ${VENV_DIR}"
+    echo "  - 日志目录: ${LOG_DIR}"
+    echo "  - 监听地址: ${GUNICORN_BIND}"
+    echo ""
+    echo "常用命令:"
+    echo "  - 启动: sudo ${APP_DIR}/scripts/start_dataops.sh"
+    echo "  - 停止: sudo ${APP_DIR}/scripts/stop_dataops.sh"
+    echo "  - 重启: sudo ${APP_DIR}/scripts/restart_dataops.sh"
+    echo "  - 状态: sudo supervisorctl status ${APP_NAME}"
+    echo "  - 日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+    echo ""
+    echo "Supervisor 命令:"
+    echo "  - sudo supervisorctl status"
+    echo "  - sudo supervisorctl restart ${APP_NAME}"
+    echo "  - sudo supervisorctl tail -f ${APP_NAME}"
+    echo ""
+}
+
+# 主函数
+main() {
+    echo "=========================================="
+    echo "  DataOps Platform 部署脚本"
+    echo "=========================================="
+    echo ""
+    
+    check_permissions
+    check_python
+    check_supervisor
+    create_directories
+    create_venv
+    install_dependencies
+    verify_installation
+    create_gunicorn_config
+    create_wsgi
+    configure_supervisor
+    set_permissions
+    start_application
+    health_check
+    show_summary
+}
+
+main "$@"
+

+ 105 - 0
scripts/restart_dataops.sh

@@ -0,0 +1,105 @@
+#!/bin/bash
+#
+# DataOps Platform 重启脚本
+# 使用 supervisorctl 重启 gunicorn 服务
+#
+
+set -e
+
+# 配置变量
+APP_NAME="dataops-platform"
+APP_DIR="/opt/dataops-platform"
+VENV_DIR="${APP_DIR}/venv"
+LOG_DIR="${APP_DIR}/logs"
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo_info() {
+    echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+echo_warn() {
+    echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+echo_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# 检查虚拟环境是否存在
+check_venv() {
+    if [ ! -d "${VENV_DIR}" ]; then
+        echo_error "虚拟环境不存在: ${VENV_DIR}"
+        echo_info "请先运行部署脚本创建虚拟环境"
+        exit 1
+    fi
+}
+
+# 检查 supervisor 是否运行
+check_supervisor() {
+    if ! pgrep -x "supervisord" > /dev/null; then
+        echo_warn "supervisord 未运行,正在启动..."
+        sudo supervisord -c /etc/supervisor/supervisord.conf
+        sleep 2
+    fi
+}
+
+# 重启应用
+restart_app() {
+    echo_info "正在重启 ${APP_NAME}..."
+    
+    sudo supervisorctl restart ${APP_NAME}
+    
+    # 等待重启
+    sleep 3
+    
+    # 检查状态
+    status=$(sudo supervisorctl status ${APP_NAME} | awk '{print $2}')
+    if [ "$status" = "RUNNING" ]; then
+        echo_info "${APP_NAME} 重启成功!"
+        sudo supervisorctl status ${APP_NAME}
+    else
+        echo_error "${APP_NAME} 重启失败!"
+        echo_info "查看日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+        exit 1
+    fi
+}
+
+# 健康检查
+health_check() {
+    echo_info "正在进行健康检查..."
+    sleep 2
+    
+    response=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:80/api/system/health 2>/dev/null || echo "000")
+    
+    if [ "$response" = "200" ]; then
+        echo_info "健康检查通过! HTTP 状态码: ${response}"
+    else
+        echo_warn "健康检查返回状态码: ${response}"
+        echo_info "服务可能需要更多时间启动,请稍后手动检查"
+    fi
+}
+
+# 主函数
+main() {
+    echo "=========================================="
+    echo "  DataOps Platform 重启脚本"
+    echo "=========================================="
+    
+    check_venv
+    check_supervisor
+    restart_app
+    health_check
+    
+    echo ""
+    echo_info "重启完成!"
+    echo_info "访问地址: http://localhost:80"
+    echo_info "查看日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+}
+
+main "$@"
+

+ 116 - 0
scripts/start_dataops.sh

@@ -0,0 +1,116 @@
+#!/bin/bash
+#
+# DataOps Platform 启动脚本
+# 使用 supervisorctl 启动 gunicorn 服务
+#
+
+set -e
+
+# 配置变量
+APP_NAME="dataops-platform"
+APP_DIR="/opt/dataops-platform"
+VENV_DIR="${APP_DIR}/venv"
+LOG_DIR="${APP_DIR}/logs"
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo_info() {
+    echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+echo_warn() {
+    echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+echo_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# 检查虚拟环境是否存在
+check_venv() {
+    if [ ! -d "${VENV_DIR}" ]; then
+        echo_error "虚拟环境不存在: ${VENV_DIR}"
+        echo_info "请先运行部署脚本创建虚拟环境"
+        exit 1
+    fi
+}
+
+# 检查 supervisor 是否运行
+check_supervisor() {
+    if ! pgrep -x "supervisord" > /dev/null; then
+        echo_warn "supervisord 未运行,正在启动..."
+        sudo supervisord -c /etc/supervisor/supervisord.conf
+        sleep 2
+    fi
+}
+
+# 启动应用
+start_app() {
+    echo_info "正在启动 ${APP_NAME}..."
+    
+    # 检查应用状态
+    status=$(sudo supervisorctl status ${APP_NAME} 2>/dev/null | awk '{print $2}' || echo "UNKNOWN")
+    
+    if [ "$status" = "RUNNING" ]; then
+        echo_warn "${APP_NAME} 已经在运行中"
+        sudo supervisorctl status ${APP_NAME}
+        return 0
+    fi
+    
+    # 启动应用
+    sudo supervisorctl start ${APP_NAME}
+    
+    # 等待启动
+    sleep 3
+    
+    # 检查启动状态
+    status=$(sudo supervisorctl status ${APP_NAME} | awk '{print $2}')
+    if [ "$status" = "RUNNING" ]; then
+        echo_info "${APP_NAME} 启动成功!"
+        sudo supervisorctl status ${APP_NAME}
+    else
+        echo_error "${APP_NAME} 启动失败!"
+        echo_info "查看日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+        exit 1
+    fi
+}
+
+# 健康检查
+health_check() {
+    echo_info "正在进行健康检查..."
+    sleep 2
+    
+    # 尝试访问健康检查接口
+    response=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:80/api/system/health 2>/dev/null || echo "000")
+    
+    if [ "$response" = "200" ]; then
+        echo_info "健康检查通过! HTTP 状态码: ${response}"
+    else
+        echo_warn "健康检查返回状态码: ${response}"
+        echo_info "服务可能需要更多时间启动,请稍后手动检查"
+    fi
+}
+
+# 主函数
+main() {
+    echo "=========================================="
+    echo "  DataOps Platform 启动脚本"
+    echo "=========================================="
+    
+    check_venv
+    check_supervisor
+    start_app
+    health_check
+    
+    echo ""
+    echo_info "启动完成!"
+    echo_info "访问地址: http://localhost:80"
+    echo_info "查看日志: tail -f ${LOG_DIR}/gunicorn_error.log"
+}
+
+main "$@"
+

+ 88 - 0
scripts/stop_dataops.sh

@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# DataOps Platform 停止脚本
+# 使用 supervisorctl 停止 gunicorn 服务
+#
+
+set -e
+
+# 配置变量
+APP_NAME="dataops-platform"
+APP_DIR="/opt/dataops-platform"
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo_info() {
+    echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+echo_warn() {
+    echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+echo_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# 停止应用
+stop_app() {
+    echo_info "正在停止 ${APP_NAME}..."
+    
+    # 检查 supervisor 是否运行
+    if ! pgrep -x "supervisord" > /dev/null; then
+        echo_warn "supervisord 未运行"
+        
+        # 尝试直接杀死 gunicorn 进程
+        if pgrep -f "gunicorn.*${APP_NAME}" > /dev/null; then
+            echo_info "发现 gunicorn 进程,正在终止..."
+            pkill -f "gunicorn.*${APP_NAME}" || true
+            echo_info "gunicorn 进程已终止"
+        else
+            echo_info "${APP_NAME} 未在运行"
+        fi
+        return 0
+    fi
+    
+    # 检查应用状态
+    status=$(sudo supervisorctl status ${APP_NAME} 2>/dev/null | awk '{print $2}' || echo "UNKNOWN")
+    
+    if [ "$status" = "STOPPED" ] || [ "$status" = "UNKNOWN" ]; then
+        echo_info "${APP_NAME} 已经停止"
+        return 0
+    fi
+    
+    # 停止应用
+    sudo supervisorctl stop ${APP_NAME}
+    
+    # 等待停止
+    sleep 2
+    
+    # 检查停止状态
+    status=$(sudo supervisorctl status ${APP_NAME} | awk '{print $2}')
+    if [ "$status" = "STOPPED" ]; then
+        echo_info "${APP_NAME} 已停止"
+    else
+        echo_warn "状态: ${status}"
+    fi
+    
+    sudo supervisorctl status ${APP_NAME}
+}
+
+# 主函数
+main() {
+    echo "=========================================="
+    echo "  DataOps Platform 停止脚本"
+    echo "=========================================="
+    
+    stop_app
+    
+    echo ""
+    echo_info "停止完成!"
+}
+
+main "$@"
+