# 多独立 AI 算法服务管理实施方案

## 1. 方案概述

本方案旨在解决多 AI 算法服务的端口冲突、统一启停/监控、配置管理和运维效率问题，同时保持每个服务的独立性，避免单个服务故障影响其他服务。方案支持两种部署方式：无 Docker 的实现方式（使用 Supervisor）和 Docker 容器化实现方式，通过「标准化 + 统一管理」的方法，实现多服务的高效管理。

本方案专门为 AI 算法工程展示平台设计，支持从代码上传、服务部署到算法能力展示的完整流程，满足用户对算法工程管理和展示的核心需求。

### 1.1 核心诉求

- **端口冲突**：为每个服务分配唯一端口，避免冲突
- **统一管理**：批量管理所有服务的启停、监控、自动重启
- **配置管理**：集中化管理公共配置，保留服务独立配置，包括 Gitea 访问配置
- **运维效率**：降低运维成本，提高服务可靠性
- **服务独立性**：单个服务故障不影响其他服务
- **代码管理**：集成 Gitea 进行代码管理和部署，支持算法工程代码上传
- **服务部署**：自动部署算法工程为 API 服务
- **算法展示**：支持使用演示数据和视频执行算法，展示算法能力
- **效果对比**：支持对比不同算法或参数的效果

### 1.2 技术栈

- **服务运行**：Python/Node.js + FastAPI/HTTP Server
- **统一管理**：Supervisor
- **可选**：Nginx（API 网关）
- **监控**：服务健康检查 + 日志管理
- **数据库**：PostgreSQL
- **缓存**：Redis
- **对象存储**：MinIO
- **代码管理**：Gitea
- **前端**：Vue 3 + TypeScript + Vite + Element Plus
- **后端**：FastAPI

### 1.3 网站用途与工作流程

本网站的核心用途是展示和管理多个 AI 算法工程，通过以下三个步骤实现：

**第一步：代码管理**
- 上传 AI 算法工程代码到平台
- 将代码管理在 Gitea 仓库中
- Gitea 访问配置保存在数据库中，确保配置持久化

**第二步：服务部署与管理**
- 部署上传的算法工程为可访问的 API 服务
- 支持多服务的统一管理（启动、停止、重启）
- 生成标准化的 API 接口
- 监控服务健康状态和运行情况

**第三步：算法能力展示**
- 使用演示数据和视频等输入执行算法
- 可视化展示算法执行结果
- 对比不同算法或参数的效果
- 向客户展示 AI 算法的能力和价值

## 2. 项目架构分析

### 2.1 系统架构

```
┌─────────────────────────────────────────────────────────┐
│                    前端层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Vue 3       │  │ TypeScript  │  │ Element Plus│     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    后端层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ FastAPI     │  │ Service     │  │ Gitea       │     │
│  │             │  │ Orchestrator│  │ Integration │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    服务层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Service-1   │  │ Service-2   │  │ Service-3   │     │
│  │ 端口: 8001  │  │ 端口: 8002  │  │ 端口: 8003  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    存储层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ PostgreSQL  │  │ Redis       │  │ MinIO       │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

### 2.2 现有系统功能

- ✅ 服务部署（支持 Docker 和本地进程）
- ✅ 服务启动/停止/重启/删除
- ✅ 服务状态查询
- ✅ 服务日志获取
- ✅ 服务健康检查
- ✅ Gitea 集成（代码管理和部署）
- ✅ 数据库配置管理
- ✅ 前端管理界面

### 2.3 现有实现的优缺点

**优点**：
- 支持两种部署模式，灵活性高
- 提供完整的服务生命周期管理
- 实现了服务健康检查和日志管理
- 集成了 Gitea 进行代码管理和部署
- 支持数据库配置管理
- 提供了完整的前端管理界面

**缺点**：
- 本地进程模式下，服务管理依赖于 `ServiceOrchestrator` 进程，进程退出后无法自动管理服务
- 缺乏批量管理能力，每次操作需要单独处理每个服务
- 服务配置分散，缺乏集中化管理
- 端口管理依赖手动配置，容易冲突

## 3. 方案设计

### 3.1 整体架构

```
┌─────────────────────────────────────────────────────────┐
│                    管理层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Supervisor  │  │   Nginx     │  │  配置管理   │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    服务层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Service-1   │  │ Service-2   │  │ Service-3   │     │
│  │ 端口: 8001  │  │ 端口: 8002  │  │ 端口: 8003  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    基础设施层                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 操作系统    │  │ 网络        │  │ 文件系统    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

### 3.2 核心设计原则

1. **服务标准化**：统一目录结构、接口规范、启动方式
2. **端口独占规划**：为每个服务分配唯一端口
3. **统一管理工具**：使用 Supervisor 批量管理服务
4. **配置集中化**：抽离公共配置，保留独立配置
5. **服务独立性**：每个服务独立运行，独立管理
6. **代码管理集成**：通过 Gitea 管理服务代码和部署

## 4. 分步实现

### 4.1 第一步：数据库配置管理

#### 4.1.1 配置存储架构

为了支持在界面上配置并存储到数据库中，我们需要设计一个统一的配置管理架构：

```
┌─────────────────────────────────────────────────────────┐
│                    配置管理架构                         │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 前端配置界面 │  │ 配置API层   │  │ 配置服务层  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 配置数据库  │  │ 缓存层      │  │ 配置文件    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

#### 4.1.2 配置数据库模型

创建统一的配置存储模型：

```python
# backend/app/models/models.py

class ServiceConfig(Base):
    """服务配置模型"""
    __tablename__ = "service_configs"
    
    id = Column(String, primary_key=True, index=True)
    config_key = Column(String, nullable=False, unique=True, index=True)  # 配置键
    config_value = Column(JSON, nullable=False)  # 配置值（JSON格式）
    config_type = Column(String, nullable=False)  # 配置类型：system, service, user
    service_id = Column(String, nullable=True, index=True)  # 服务ID（可为空，系统配置）
    description = Column(Text, default="")  # 配置描述
    status = Column(String, default="active")  # 状态
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())
```

#### 4.1.3 配置服务实现

```python
# backend/app/services/config_service.py

class ConfigService:
    """配置服务"""
    
    @staticmethod
    def get_config(db: Session, config_key: str) -> Optional[Dict[str, Any]]:
        """获取配置"""
        config = db.query(ServiceConfig).filter_by(
            config_key=config_key,
            status="active"
        ).first()
        
        if config:
            return config.config_value
        return None
    
    @staticmethod
    def set_config(db: Session, config_key: str, config_value: Dict[str, Any], 
                  config_type: str = "system", service_id: Optional[str] = None, 
                  description: str = "") -> bool:
        """设置配置"""
        try:
            # 检查是否存在
            existing_config = db.query(ServiceConfig).filter_by(
                config_key=config_key
            ).first()
            
            if existing_config:
                # 更新现有配置
                existing_config.config_value = config_value
                existing_config.config_type = config_type
                existing_config.service_id = service_id
                existing_config.description = description
                existing_config.status = "active"
            else:
                # 创建新配置
                new_config = ServiceConfig(
                    id=f"config-{uuid.uuid4()}",
                    config_key=config_key,
                    config_value=config_value,
                    config_type=config_type,
                    service_id=service_id,
                    description=description,
                    status="active"
                )
                db.add(new_config)
            
            db.commit()
            return True
        except Exception as e:
            logger.error(f"Failed to set config: {str(e)}")
            db.rollback()
            return False
    
    @staticmethod
    def get_service_configs(db: Session, service_id: str) -> List[Dict[str, Any]]:
        """获取服务的所有配置"""
        configs = db.query(ServiceConfig).filter_by(
            service_id=service_id,
            status="active"
        ).all()
        
        return [
            {
                "key": config.config_key,
                "value": config.config_value,
                "type": config.config_type,
                "description": config.description
            }
            for config in configs
        ]
```

#### 4.1.4 配置API接口

```python
# backend/app/routes/config.py

router = APIRouter(prefix="/config", tags=["config"])

@router.get("/{config_key}")
async def get_config(
    config_key: str,
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """获取配置"""
    config = ConfigService.get_config(db, config_key)
    if not config:
        raise HTTPException(status_code=404, detail="配置不存在")
    return {"key": config_key, "value": config}

@router.post("/{config_key}")
async def set_config(
    config_key: str,
    config_data: Dict[str, Any],
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """设置配置"""
    success = ConfigService.set_config(
        db=db,
        config_key=config_key,
        config_value=config_data.get("value"),
        config_type=config_data.get("type", "system"),
        service_id=config_data.get("service_id"),
        description=config_data.get("description", "")
    )
    if not success:
        raise HTTPException(status_code=400, detail="设置配置失败")
    return {"message": "设置配置成功"}

@router.get("/service/{service_id}")
async def get_service_configs(
    service_id: str,
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """获取服务配置"""
    configs = ConfigService.get_service_configs(db, service_id)
    return {"service_id": service_id, "configs": configs}
```

#### 4.1.5 前端配置界面

```vue
<!-- 前端配置管理组件 -->
<template>
  <div class="admin-config-container">
    <!-- 页面标题 -->
    <h1>配置管理</h1>
    
    <!-- 操作栏 -->
    <div class="action-bar">
      <el-button type="primary" @click="refreshConfigs">
        <el-icon><Refresh /></el-icon>
        刷新配置
      </el-button>
    </div>
    
    <!-- 系统配置 -->
    <el-card class="mb-4">
      <template #header>
        <div class="card-header">
          <span>系统配置</span>
        </div>
      </template>
      
      <el-form :model="systemConfigForm" label-width="120px">
        <el-form-item label="部署模式">
          <el-select v-model="systemConfigForm.deploymentMode" placeholder="选择部署模式">
            <el-option label="本地进程" value="local"></el-option>
            <el-option label="Docker" value="docker"></el-option>
            <el-option label="Supervisor" value="supervisor"></el-option>
          </el-select>
        </el-form-item>
        
        <el-form-item label="服务根目录">
          <el-input v-model="systemConfigForm.serviceRootDir" placeholder="服务根目录"></el-input>
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="saveSystemConfig">保存配置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    
    <!-- 服务配置 -->
    <el-card>
      <template #header>
        <div class="card-header">
          <span>服务配置</span>
          <el-select v-model="selectedServiceId" placeholder="选择服务">
            <el-option 
              v-for="service in services" 
              :key="service.id" 
              :label="service.name" 
              :value="service.id"
            ></el-option>
          </el-select>
        </div>
      </template>
      
      <el-form v-if="selectedServiceId" :model="serviceConfigForm" label-width="120px">
        <el-form-item label="服务端口">
          <el-input-number v-model="serviceConfigForm.port" :min="1000" :max="65535"></el-input-number>
        </el-form-item>
        
        <el-form-item label="服务主机">
          <el-input v-model="serviceConfigForm.host" placeholder="服务主机"></el-input>
        </el-form-item>
        
        <el-form-item label="超时时间">
          <el-input-number v-model="serviceConfigForm.timeout" :min="1" :max="300"></el-input-number>
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="saveServiceConfig">保存配置</el-button>
        </el-form-item>
      </el-form>
      <div v-else class="text-center text-gray-400 py-4">
        请选择一个服务
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'

// 状态管理
const services = ref<any[]>([])
const selectedServiceId = ref('')

// 系统配置表单
const systemConfigForm = ref({
  deploymentMode: 'local',
  serviceRootDir: '/opt/ai-services'
})

// 服务配置表单
const serviceConfigForm = ref({
  port: 8001,
  host: '0.0.0.0',
  timeout: 30
})

// 加载服务列表
const loadServices = async () => {
  try {
    const token = localStorage.getItem('token')
    if (!token) {
      ElMessage.error('未登录，请重新登录')
      return
    }
    
    const response = await fetch('http://0.0.0.0:8001/api/v1/services', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      }
    })
    
    if (!response.ok) {
      throw new Error('获取服务列表失败')
    }
    
    const data = await response.json()
    if (data.success) {
      services.value = data.services
    }
  } catch (error) {
    console.error('加载服务列表失败:', error)
    ElMessage.error('加载服务列表失败')
  }
}

// 加载系统配置
const loadSystemConfig = async () => {
  try {
    const token = localStorage.getItem('token')
    if (!token) {
      ElMessage.error('未登录，请重新登录')
      return
    }
    
    const response = await fetch('http://0.0.0.0:8001/api/v1/config/deployment.mode', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      }
    })
    
    if (response.ok) {
      const data = await response.json()
      if (data.value) {
        systemConfigForm.value.deploymentMode = data.value
      }
    }
    
    const response2 = await fetch('http://0.0.0.0:8001/api/v1/config/service.root.dir', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      }
    })
    
    if (response2.ok) {
      const data = await response2.json()
      if (data.value) {
        systemConfigForm.value.serviceRootDir = data.value
      }
    }
  } catch (error) {
    console.log('系统配置不存在，使用默认值')
  }
}

// 加载服务配置
const loadServiceConfig = async (serviceId: string) => {
  if (!serviceId) return
  
  try {
    const token = localStorage.getItem('token')
    if (!token) {
      ElMessage.error('未登录，请重新登录')
      return
    }
    
    const response = await fetch(`http://0.0.0.0:8001/api/v1/config/service/${serviceId}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      }
    })
    
    if (response.ok) {
      const data = await response.json()
      if (data.configs) {
        const configs = data.configs
        configs.forEach((config: any) => {
          if (config.key === 'service.port') {
            serviceConfigForm.value.port = config.value
          } else if (config.key === 'service.host') {
            serviceConfigForm.value.host = config.value
          } else if (config.key === 'service.timeout') {
            serviceConfigForm.value.timeout = config.value
          }
        })
      }
    }
  } catch (error) {
    console.error('加载服务配置失败:', error)
    ElMessage.error('加载服务配置失败')
  }
}

// 保存系统配置
const saveSystemConfig = async () => {
  try {
    const token = localStorage.getItem('token')
    if (!token) {
      ElMessage.error('未登录，请重新登录')
      return
    }
    
    await fetch('http://0.0.0.0:8001/api/v1/config/deployment.mode', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        value: systemConfigForm.value.deploymentMode,
        type: 'system',
        description: '部署模式'
      })
    })
    
    await fetch('http://0.0.0.0:8001/api/v1/config/service.root.dir', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        value: systemConfigForm.value.serviceRootDir,
        type: 'system',
        description: '服务根目录'
      })
    })
    
    ElMessage.success('保存系统配置成功')
  } catch (error) {
    console.error('保存系统配置失败:', error)
    ElMessage.error('保存系统配置失败')
  }
}

// 保存服务配置
const saveServiceConfig = async () => {
  if (!selectedServiceId.value) return
  
  try {
    const token = localStorage.getItem('token')
    if (!token) {
      ElMessage.error('未登录，请重新登录')
      return
    }
    
    await fetch('http://0.0.0.0:8001/api/v1/config/service.port', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        value: serviceConfigForm.value.port,
        type: 'service',
        service_id: selectedServiceId.value,
        description: '服务端口'
      })
    })
    
    await fetch('http://0.0.0.0:8001/api/v1/config/service.host', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        value: serviceConfigForm.value.host,
        type: 'service',
        service_id: selectedServiceId.value,
        description: '服务主机'
      })
    })
    
    await fetch('http://0.0.0.0:8001/api/v1/config/service.timeout', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        value: serviceConfigForm.value.timeout,
        type: 'service',
        service_id: selectedServiceId.value,
        description: '服务超时时间'
      })
    })
    
    ElMessage.success('保存服务配置成功')
  } catch (error) {
    console.error('保存服务配置失败:', error)
    ElMessage.error('保存服务配置失败')
  }
}

// 刷新配置
const refreshConfigs = async () => {
  await loadSystemConfig()
  if (selectedServiceId.value) {
    await loadServiceConfig(selectedServiceId.value)
  }
  ElMessage.success('配置已刷新')
}

// 监听服务选择变化
const handleServiceChange = () => {
  loadServiceConfig(selectedServiceId.value)
}

// 初始化
onMounted(() => {
  loadServices()
  loadSystemConfig()
})
</script>

<style scoped>
.admin-config-container {
  padding: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.mb-4 {
  margin-bottom: 20px;
}
</style>
```

### 4.2 第二步：多服务目录标准化

#### 4.2.1 统一服务根目录

```plaintext
/opt/ai-services/  # 所有AI服务的根目录
├── config/        # 全局配置
│   ├── common.py  # 公共配置
│   └── ports.py   # 端口分配配置
├── logs/          # 所有服务的日志总目录
│   ├── service-1.log
│   ├── service-2.log
│   └── service-3.log
├── service-1/     # 服务1：如文本分类
│   ├── ai_algorithm.py  # 服务1的AI算法逻辑
│   ├── main.py          # 服务1的FastAPI接口
│   ├── requirements.txt # 服务1的依赖
│   ├── config.py        # 服务1的独立配置
│   └── venv/            # 服务1的虚拟环境
├── service-2/     # 服务2：如图像识别
│   ├── ai_algorithm.py
│   ├── main.py
│   ├── requirements.txt
│   ├── config.py
│   └── venv/
└── service-3/     # 服务3：如语音转文字
    ├── ...
```

#### 4.2.2 统一接口规范

所有服务必须实现以下接口：

- `POST /predict`：算法预测接口
- `GET /health`：健康检查接口
- `GET /info`：服务信息接口

#### 4.2.3 统一启动方式

所有服务使用统一的启动脚本：

```bash
# /opt/ai-services/service-1/start.sh
#!/bin/bash
cd /opt/ai-services/service-1
source venv/bin/activate
python main.py
```

### 4.3 第三步：使用 Supervisor 统一管理服务

#### 4.3.1 安装 Supervisor

```bash
# Ubuntu/Debian
apt update && apt install supervisor

# CentOS/RHEL
yum install epel-release
yum install supervisor

# 启动 Supervisor 服务
systemctl start supervisor
systemctl enable supervisor
```

#### 4.3.2 配置 Supervisor

创建统一的配置文件：

```ini
# /etc/supervisor/conf.d/ai-services.conf

[supervisord]
logfile=/opt/ai-services/logs/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
nodaemon=false
minfds=1024
minprocs=200

# 服务1：文本分类（8001端口）
[program:ai-service-1]
name=ai-service-1
command=/opt/ai-services/service-1/start.sh
directory=/opt/ai-services/service-1
user=ubuntu
autostart=true
autorestart=true
startretries=3
stdout_logfile=/opt/ai-services/logs/service-1.log
stderr_logfile=/opt/ai-services/logs/service-1_error.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5

# 服务2：图像识别（8002端口）
[program:ai-service-2]
name=ai-service-2
command=/opt/ai-services/service-2/start.sh
directory=/opt/ai-services/service-2
user=ubuntu
autostart=true
autorestart=true
startretries=3
stdout_logfile=/opt/ai-services/logs/service-2.log
stderr_logfile=/opt/ai-services/logs/service-2_error.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5

# 服务3：语音转文字（8003端口）
[program:ai-service-3]
name=ai-service-3
command=/opt/ai-services/service-3/start.sh
directory=/opt/ai-services/service-3
user=ubuntu
autostart=true
autorestart=true
startretries=3
stdout_logfile=/opt/ai-services/logs/service-3.log
stderr_logfile=/opt/ai-services/logs/service-3_error.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
```

#### 4.3.3 Supervisor 管理命令

```bash
# 重新加载配置（新增/修改服务后执行）
supervisorctl reload

# 启动所有AI服务
supervisorctl start ai-service-*

# 启动单个服务
supervisorctl start ai-service-1

# 查看所有服务状态
supervisorctl status
# 输出示例：
# ai-service-1                    RUNNING   pid 1234, uptime 0:05:10
# ai-service-2                    RUNNING   pid 1235, uptime 0:05:08
# ai-service-3                    RUNNING   pid 1236, uptime 0:05:05

# 重启所有服务
supervisorctl restart ai-service-*

# 停止单个服务
supervisorctl stop ai-service-2

# 查看某个服务的日志（快速排查问题）
supervisorctl tail -f ai-service-1
```

### 4.4 第四步：配置集中化管理

#### 4.4.1 配置管理架构

采用三层配置管理架构：
1. **数据库配置**：存储在数据库中的动态配置，支持界面修改
2. **文件配置**：存储在文件中的静态配置，作为默认值
3. **环境变量**：优先级最高，用于覆盖其他配置

#### 4.4.2 配置加载流程

```python
# backend/app/config/settings.py

from pydantic_settings import BaseSettings
from typing import Optional, Dict, Any
from sqlalchemy.orm import Session
from app.models.database import SessionLocal
from app.models.models import ServiceConfig

class Settings(BaseSettings):
    """应用配置类"""
    # 应用基本配置
    APP_NAME: str = "智能算法展示平台"
    APP_VERSION: str = "1.0.0"
    DEBUG: bool = True
    
    # 数据库配置
    DATABASE_URL: str = "postgresql://admin:password@localhost:5432/algorithm_db"
    
    # 其他配置...
    
    # 服务管理配置
    SERVICE_MANAGEMENT: Dict[str, Any] = {
        "mode": "supervisor",  # 服务管理模式：local, docker, supervisor
        "service_root_dir": "/opt/ai-services",
        "supervisor_config_dir": "/etc/supervisor/conf.d",
    }
    
    class Config:
        env_file = ".env"
        case_sensitive = True
        extra = "allow"  # 允许额外的环境变量

    def get_config(self, config_key: str, default: Any = None) -> Any:
        """获取配置，优先级：环境变量 > 数据库 > 文件默认值"""
        # 1. 先从环境变量获取
        env_value = getattr(self, config_key.upper().replace('.', '_'), None)
        if env_value is not None:
            return env_value
        
        # 2. 从数据库获取
        db: Session = SessionLocal()
        try:
            config = db.query(ServiceConfig).filter_by(
                config_key=config_key,
                status="active"
            ).first()
            if config:
                return config.config_value
        finally:
            db.close()
        
        # 3. 返回默认值
        return default


# 创建全局配置实例
settings = Settings()
```

#### 4.4.3 配置服务集成

```python
# backend/app/services/service_orchestrator.py

class ServiceOrchestrator:
    """服务编排服务"""
    
    def __init__(self, deployment_mode="local"):
        """初始化服务编排器
        
        Args:
            deployment_mode: 部署模式，支持"docker"、"local"和"supervisor"
        """
        # 从数据库获取配置
        self.deployment_mode = settings.get_config("deployment.mode", deployment_mode)
        self.service_root_dir = settings.get_config("service.root.dir", "/opt/ai-services")
        # 其他初始化代码...
    
    def _get_service_config(self, service_id: str) -> Dict[str, Any]:
        """获取服务配置"""
        # 基础配置
        base_config = {
            "host": "0.0.0.0",
            "port": self._get_service_port(service_id),
            "timeout": 30,
            "health_check_interval": 10,
        }
        
        # 从数据库获取服务特定配置
        db: Session = SessionLocal()
        try:
            configs = db.query(ServiceConfig).filter_by(
                service_id=service_id,
                status="active"
            ).all()
            
            for config in configs:
                # 解析配置键，设置到相应的位置
                if config.config_key == "service.port":
                    base_config["port"] = config.config_value
                elif config.config_key == "service.host":
                    base_config["host"] = config.config_value
                elif config.config_key == "service.timeout":
                    base_config["timeout"] = config.config_value
        finally:
            db.close()
        
        return base_config
```

#### 4.4.4 配置管理最佳实践

1. **配置分类**：
   - `system`：系统级配置，影响所有服务
   - `service`：服务级配置，仅影响特定服务
   - `user`：用户级配置，与用户偏好相关

2. **配置命名规范**：
   - 使用点分隔的命名方式：`category.subcategory.setting`
   - 示例：`service.port`, `deployment.mode`, `logging.level`

3. **配置版本管理**：
   - 保留配置的历史版本
   - 支持配置回滚
   - 记录配置修改日志

4. **配置验证**：
   - 对配置值进行类型检查
   - 对配置值进行范围验证
   - 确保配置的完整性

5. **配置安全**：
   - 敏感配置加密存储
   - 配置访问权限控制
   - 配置变更审计

### 4.5 第五步：可选优化 - 统一 API 网关

#### 4.5.1 安装 Nginx

```bash
# Ubuntu/Debian
apt update && apt install nginx

# CentOS/RHEL
yum install nginx

# 启动 Nginx 服务
systemctl start nginx
systemctl enable nginx
```

#### 4.5.2 配置 Nginx 反向代理

```nginx
# /etc/nginx/conf.d/ai-services.conf
server {
    listen 80;
    server_name your-server-ip;  # 替换为你的服务器IP/域名

    # 服务1：文本分类（网关路径/ai/text-classify）
    location /ai/text-classify/ {
        proxy_pass http://127.0.0.1:8001/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_read_timeout 30s;
    }

    # 服务2：图像识别（网关路径/ai/image-recog）
    location /ai/image-recog/ {
        proxy_pass http://127.0.0.1:8002/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_read_timeout 30s;
    }

    # 服务3：语音转文字（网关路径/ai/speech-to-text）
    location /ai/speech-to-text/ {
        proxy_pass http://127.0.0.1:8003/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_read_timeout 30s;
    }
}
```

#### 4.5.3 重启 Nginx 并测试

```bash
# 检查配置是否正确
nginx -t

# 重启Nginx
systemctl restart nginx

# 测试网关调用（无需记端口，只需记路径）
curl -X POST "http://your-server-ip/ai/text-classify/predict" \
-H "Content-Type: application/json" \
-d '{"input_data": ["这是一个测试文本"], "batch_id": "test_001"}'
```

## 5. 与现有系统集成

### 5.1 集成 ServiceOrchestrator

修改现有的 `ServiceOrchestrator` 类，使其支持 Supervisor 管理模式和数据库配置：

```python
# backend/app/services/service_orchestrator.py

class ServiceOrchestrator:
    """服务编排服务"""
    
    def __init__(self, deployment_mode="local"):
        """初始化服务编排器
        
        Args:
            deployment_mode: 部署模式，支持"docker"、"local"和"supervisor"
        """
        # 从数据库获取配置
        self.deployment_mode = settings.get_config("deployment.mode", deployment_mode)
        self.service_root_dir = settings.get_config("service.root.dir", "/opt/ai-services")
        # 其他初始化代码...
    
    def deploy_service(self, service_id: str, service_config: Dict[str, Any], project_info: Dict[str, Any]) -> Dict[str, Any]:
        """部署服务"""
        try:
            if self.deployment_mode == "supervisor":
                # 使用 Supervisor 部署服务
                return self._deploy_with_supervisor(service_id, service_config, project_info)
            # 其他部署模式...
        except Exception as e:
            # 错误处理...
    
    def _deploy_with_supervisor(self, service_id: str, service_config: Dict[str, Any], project_info: Dict[str, Any]) -> Dict[str, Any]:
        """使用 Supervisor 部署服务"""
        # 1. 创建服务目录
        service_dir = self._create_service_directory(service_id)
        
        # 2. 生成服务配置和启动脚本
        self._generate_service_config(service_dir, service_id, service_config)
        self._generate_start_script(service_dir, service_id)
        
        # 3. 创建 Supervisor 配置
        self._create_supervisor_config(service_id, service_dir, service_config)
        
        # 4. 重新加载 Supervisor 配置
        self._reload_supervisor()
        
        # 5. 启动服务
        self._start_supervisor_service(service_id)
        
        # 6. 验证服务启动
        if not self._verify_service_startup(service_id, service_config):
            return {
                "success": False,
                "error": "服务启动验证失败",
                "service_id": service_id,
                "status": "error",
                "api_url": None
            }
        
        # 7. 构建 API URL
        api_url = f"http://{service_config.get('host', 'localhost')}:{service_config.get('port', 8000)}"
        
        # 8. 保存服务配置到数据库
        self._save_service_config_to_db(service_id, service_config)
        
        return {
            "success": True,
            "service_id": service_id,
            "status": "running",
            "api_url": api_url,
            "error": None
        }
    
    def _save_service_config_to_db(self, service_id: str, service_config: Dict[str, Any]):
        """保存服务配置到数据库"""
        db = SessionLocal()
        try:
            # 保存服务端口
            ConfigService.set_config(
                db=db,
                config_key="service.port",
                config_value=service_config.get("port"),
                config_type="service",
                service_id=service_id,
                description="服务端口"
            )
            
            # 保存服务主机
            ConfigService.set_config(
                db=db,
                config_key="service.host",
                config_value=service_config.get("host", "0.0.0.0"),
                config_type="service",
                service_id=service_id,
                description="服务主机"
            )
            
            # 保存服务超时
            ConfigService.set_config(
                db=db,
                config_key="service.timeout",
                config_value=service_config.get("timeout", 30),
                config_type="service",
                service_id=service_id,
                description="服务超时时间"
            )
        finally:
            db.close()
    
    # 其他方法...
```

### 5.2 集成配置管理

修改现有配置管理，使用数据库存储配置：

```python
# backend/app/config/settings.py

from pydantic_settings import BaseSettings
from typing import Optional, Dict, Any
from sqlalchemy.orm import Session
from app.models.database import SessionLocal
from app.models.models import ServiceConfig


class Settings(BaseSettings):
    """应用配置类"""
    # 应用基本配置
    APP_NAME: str = "智能算法展示平台"
    APP_VERSION: str = "1.0.0"
    DEBUG: bool = True
    
    # 数据库配置
    DATABASE_URL: str = "postgresql://admin:password@localhost:5432/algorithm_db"
    
    # 其他配置...
    
    # 服务管理配置
    SERVICE_MANAGEMENT: Dict[str, Any] = {
        "mode": "supervisor",  # 服务管理模式：local, docker, supervisor
        "service_root_dir": "/opt/ai-services",
        "supervisor_config_dir": "/etc/supervisor/conf.d",
    }
    
    class Config:
        env_file = ".env"
        case_sensitive = True
        extra = "allow"  # 允许额外的环境变量

    def get_config(self, config_key: str, default: Any = None) -> Any:
        """获取配置，优先级：环境变量 > 数据库 > 文件默认值"""
        # 1. 先从环境变量获取
        env_value = getattr(self, config_key.upper().replace('.', '_'), None)
        if env_value is not None:
            return env_value
        
        # 2. 从数据库获取
        db: Session = SessionLocal()
        try:
            config = db.query(ServiceConfig).filter_by(
                config_key=config_key,
                status="active"
            ).first()
            if config:
                return config.config_value
        finally:
            db.close()
        
        # 3. 返回默认值
        return default


# 创建全局配置实例
settings = Settings()
```

### 5.3 集成服务状态查询

修改服务状态查询接口，支持 Supervisor 管理的服务：

```python
# backend/app/routes/services.py

@router.get("/status/{service_id}")
async def get_service_status(
    service_id: str,
    orchestrator: ServiceOrchestrator = Depends(get_orchestrator)
):
    """获取服务状态"""
    try:
        # 获取服务状态
        status_info = orchestrator.get_service_status(service_id)
        
        if not status_info["success"]:
            raise HTTPException(status_code=404, detail=status_info.get("error", "服务不存在"))
        
        return {
            "service_id": service_id,
            "status": status_info["status"],
            "health": status_info["health"],
            "message": "获取服务状态成功"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
```

### 5.4 集成配置 API

添加配置管理 API 接口：

```python
# backend/app/routes/config.py

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import Dict, Any, List, Optional

from app.models.database import get_db
from app.services.config_service import ConfigService
from app.services.user import get_current_active_user

router = APIRouter(prefix="/config", tags=["config"])

@router.get("/{config_key}")
async def get_config(
    config_key: str,
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """获取配置"""
    config = ConfigService.get_config(db, config_key)
    if not config:
        raise HTTPException(status_code=404, detail="配置不存在")
    return {"key": config_key, "value": config}

@router.post("/{config_key}")
async def set_config(
    config_key: str,
    config_data: Dict[str, Any],
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """设置配置"""
    success = ConfigService.set_config(
        db=db,
        config_key=config_key,
        config_value=config_data.get("value"),
        config_type=config_data.get("type", "system"),
        service_id=config_data.get("service_id"),
        description=config_data.get("description", "")
    )
    if not success:
        raise HTTPException(status_code=400, detail="设置配置失败")
    return {"message": "设置配置成功"}

@router.get("/service/{service_id}")
async def get_service_configs(
    service_id: str,
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_active_user)
):
    """获取服务配置"""
    configs = ConfigService.get_service_configs(db, service_id)
    return {"service_id": service_id, "configs": configs}
```

## 6. Gitea 集成方案

### 6.1 Gitea 集成概述

针对所有工程都在 Gitea 上的场景，本方案提供了完整的 Gitea 集成能力，实现从代码仓库到服务部署的全流程管理。基于现有的 GiteaService 和 GiteaClient 实现，进一步完善了从代码管理到服务部署的自动化流程。

### 6.2 现有 Gitea 集成实现

当前系统已经实现了完整的 Gitea 集成功能：

- ✅ Gitea 连接配置管理
- ✅ 仓库创建、克隆、推送、拉取
- ✅ 代码上传（支持大文件处理）
- ✅ 从 Gitea 部署服务
- ✅ 错误处理和重试机制
- ✅ CI/CD 集成支持

### 6.3 Gitea 配置管理（gitea_configs）

#### 6.3.1 配置存储架构

系统使用 `gitea_configs` 表存储 Gitea 连接配置，实现了配置的持久化和版本管理：

```python
# backend/app/models/models.py

class GiteaConfig(Base):
    """Gitea配置模型"""
    __tablename__ = "gitea_configs"
    
    id = Column(String, primary_key=True, index=True)
    server_url = Column(String, nullable=False)  # Gitea服务器URL
    access_token = Column(String, nullable=False)  # 访问令牌
    default_owner = Column(String, nullable=False)  # 默认组织/用户
    repo_prefix = Column(String, default="")  # 仓库前缀
    status = Column(String, default="active")  # 状态
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())
```

#### 6.3.2 配置管理实现

Gitea 配置管理通过 `GiteaService` 实现，支持配置的加载、保存和使用：

```python
# backend/app/gitea/service.py

class GiteaService:
    def _load_config(self) -> Optional[Dict[str, Any]]:
        """加载Gitea配置"""
        try:
            db = SessionLocal()
            # 从数据库中获取配置（只取第一个配置）
            config = db.query(GiteaConfig).filter_by(status="active").first()
            db.close()
            
            if config:
                return {
                    'id': config.id,
                    'server_url': config.server_url,
                    'access_token': config.access_token,
                    'default_owner': config.default_owner,
                    'repo_prefix': config.repo_prefix,
                    'status': config.status
                }
            
            # 配置不存在时返回默认值
            return {
                'server_url': getattr(settings, 'GITEA_SERVER_URL', ''),
                'access_token': getattr(settings, 'GITEA_ACCESS_TOKEN', ''),
                'default_owner': getattr(settings, 'GITEA_DEFAULT_OWNER', ''),
                'repo_prefix': getattr(settings, 'GITEA_REPO_PREFIX', '')
            }
        except Exception as e:
            logger.error(f"Failed to load Gitea config from database: {str(e)}")
            # 出错时返回默认配置
            return {
                'server_url': getattr(settings, 'GITEA_SERVER_URL', ''),
                'access_token': getattr(settings, 'GITEA_ACCESS_TOKEN', ''),
                'default_owner': getattr(settings, 'GITEA_DEFAULT_OWNER', ''),
                'repo_prefix': getattr(settings, 'GITEA_REPO_PREFIX', '')
            }
    
    def save_config(self, config: Dict[str, Any]) -> bool:
        """保存Gitea配置"""
        try:
            db = SessionLocal()
            
            # 将所有现有配置设置为非活动状态
            db.query(GiteaConfig).update({GiteaConfig.status: "inactive"})
            
            # 检查是否已有配置
            existing_config = db.query(GiteaConfig).first()
            
            if existing_config:
                # 更新现有配置
                existing_config.server_url = config['server_url']
                existing_config.access_token = config['access_token']
                existing_config.default_owner = config['default_owner']
                existing_config.repo_prefix = config.get('repo_prefix', '')
                existing_config.status = "active"
            else:
                # 创建新配置
                new_config = GiteaConfig(
                    id=f"gitea-config-{uuid.uuid4()}",
                    server_url=config['server_url'],
                    access_token=config['access_token'],
                    default_owner=config['default_owner'],
                    repo_prefix=config.get('repo_prefix', ''),
                    status="active"
                )
                db.add(new_config)
            
            db.commit()
            db.close()
            
            # 更新内存中的配置
            self.config = config
            self.client = GiteaClient(
                config['server_url'],
                config['access_token']
            )
            
            logger.info("Gitea config saved to database successfully")
            return True
        except Exception as e:
            logger.error(f"Failed to save Gitea config to database: {str(e)}")
            return False
```

#### 6.3.3 配置使用流程

1. **配置加载**：系统启动时，`GiteaService` 会从数据库加载 Gitea 配置
2. **配置验证**：使用加载的配置初始化 `GiteaClient`，测试连接
3. **配置使用**：在仓库操作、代码上传等功能中使用配置
4. **配置更新**：通过 API 或界面更新配置，自动保存到数据库

#### 6.3.4 配置管理最佳实践

1. **配置版本控制**：系统会将旧配置标记为 `inactive`，保留配置历史
2. **配置验证**：保存配置前应测试连接，确保配置有效
3. **安全存储**：访问令牌应妥善保管，避免泄露
4. **配置备份**：定期备份 `gitea_configs` 表，防止配置丢失

#### 6.3.5 配置 API 接口

系统提供了 Gitea 配置管理的 API 接口：

- `GET /api/v1/gitea/config`：获取当前 Gitea 配置
- `POST /api/v1/gitea/config`：更新 Gitea 配置
- `GET /api/v1/gitea/test-connection`：测试 Gitea 连接状态

### 6.4 核心功能

- **配置管理**：获取、设置 Gitea 连接配置，支持环境变量和数据库存储
- **连接测试**：测试与 Gitea 服务器的连接状态
- **仓库管理**：创建、克隆、推送、拉取 Gitea 仓库
- **代码上传**：支持大量文件上传，解决 HTTP 413 错误
- **大文件处理**：支持大文件分阶段推送，优化推送性能
- **服务部署**：从 Gitea 仓库直接部署算法服务
- **算法注册**：从仓库注册算法服务到系统

### 6.5 集成架构

```
┌─────────────────────────────────────────────────────────┐
│                    管理层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Supervisor  │  │   Nginx     │  │ Gitea服务  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    服务层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Service-1   │  │ Service-2   │  │ Service-3   │     │
│  │ 端口: 8001  │  │ 端口: 8002  │  │ 端口: 8003  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    代码层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Gitea仓库1  │  │ Gitea仓库2  │  │ Gitea仓库3  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    基础设施层                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 操作系统    │  │ 网络        │  │ 文件系统    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

### 6.6 从 Gitea 部署服务的完整流程

#### 6.6.1 配置 Gitea 连接

1. **设置 Gitea 配置**：
   - 服务器 URL
   - 访问令牌
   - 默认所有者
   - 仓库前缀

2. **测试连接**：
   ```bash
   # 使用现有的 GiteaService 测试连接
   python -c "from app.gitea.service import gitea_service; print(gitea_service.test_connection())"
   ```

3. **API 接口**：
   ```bash
   # 获取 Gitea 配置
   curl -X GET "http://localhost:8001/api/v1/gitea/config" -H "Authorization: Bearer <token>"
   
   # 设置 Gitea 配置
   curl -X POST "http://localhost:8001/api/v1/gitea/config" -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{
     "server_url": "https://gitea.example.com",
     "access_token": "your-token",
     "default_owner": "owner",
     "repo_prefix": "AI-"
   }'
   ```

#### 6.6.2 仓库管理流程

1. **创建仓库**：
   ```python
   from app.gitea.service import gitea_service
   
   repo = gitea_service.create_repository(
       algorithm_id="text-classification",
       algorithm_name="文本分类算法",
       description="基于 BERT 的文本分类算法"
   )
   print(repo)
   ```

2. **克隆仓库**：
   ```python
   success = gitea_service.clone_repository(
       repo_url="https://gitea.example.com/owner/text-classification.git",
       algorithm_id="text-classification"
   )
   print(f"Clone success: {success}")
   ```

3. **推送代码**：
   ```python
   success = gitea_service.push_to_repository(
       algorithm_id="text-classification",
       message="Update algorithm code"
   )
   print(f"Push success: {success}")
   ```

4. **上传文件**：
   ```bash
   # 使用 API 上传文件
   curl -X POST "http://localhost:8001/api/v1/gitea/repos/upload" -H "Authorization: Bearer <token>" -F "algorithm_id=text-classification" -F "files=@algorithm.py" -F "files=@model.pth"
   ```

#### 6.6.3 从 Gitea 部署服务

1. **通过 API 部署**：
   ```bash
   # 从 Gitea 部署服务
   curl -X POST "http://localhost:8001/api/v1/services/deploy-from-gitea" -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{
     "algorithm_id": "text-classification",
     "repo_url": "https://gitea.example.com/owner/text-classification.git",
     "branch": "main",
     "service_config": {
       "name": "文本分类服务",
       "port": 8001,
       "host": "0.0.0.0",
       "timeout": 30
     }
   }'
   ```

2. **通过前端界面部署**：
   - 登录前端管理界面
   - 进入「服务管理」页面
   - 点击「从 Gitea 部署」按钮
   - 填写部署信息并提交

#### 6.6.4 服务管理和监控

1. **服务状态查询**：
   ```bash
   curl -X GET "http://localhost:8001/api/v1/services/status/text-classification" -H "Authorization: Bearer <token>"
   ```

2. **服务日志查询**：
   ```bash
   curl -X GET "http://localhost:8001/api/v1/services/logs/text-classification?lines=100" -H "Authorization: Bearer <token>"
   ```

3. **服务操作**：
   ```bash
   # 启动服务
   curl -X POST "http://localhost:8001/api/v1/services/start/text-classification" -H "Authorization: Bearer <token>"
   
   # 停止服务
   curl -X POST "http://localhost:8001/api/v1/services/stop/text-classification" -H "Authorization: Bearer <token>"
   
   # 重启服务
   curl -X POST "http://localhost:8001/api/v1/services/restart/text-classification" -H "Authorization: Bearer <token>"
   ```

## 7. 数据库设计与实现

### 7.1 数据库架构

系统使用 PostgreSQL 数据库，包含以下核心表：

1. **algorithms**：算法信息表，存储算法的基本信息
2. **algorithm_versions**：算法版本表，存储算法的不同版本
3. **roles**：角色表，存储用户角色信息
4. **users**：用户表，存储用户信息
5. **algorithm_calls**：算法调用记录表，存储算法调用的历史记录
6. **gitea_configs**：Gitea配置表，存储Gitea连接配置
7. **algorithm_repositories**：算法仓库表，存储算法代码仓库信息
8. **service_groups**：服务分组表，存储服务分组信息
9. **algorithm_services**：算法服务表，存储算法服务的部署信息
10. **service_configs**：服务配置表，存储服务的配置信息

### 7.2 数据关系

```
┌─────────────────┐      ┌─────────────────────┐
│   algorithms    │┼──────│algorithm_versions  │
└─────────────────┘      └─────────────────────┘
        │                      │
        │                      │
        │                      │
┌─────────────────┐      ┌─────────────────────┐
│algorithm_calls  │┼──────│   users            │
└─────────────────┘      └─────────────────────┘
                              │
                              │
┌─────────────────┐      ┌─────────────────────┐
│  gitea_configs  │      │      roles         │
└─────────────────┘      └─────────────────────┘
        │
        │
┌─────────────────┐      ┌─────────────────────┐
│algorithm_repos  │┼──────│algorithm_services  │
└─────────────────┘      └─────────────────────┘
                              │
                              │
┌─────────────────┐      ┌─────────────────────┐
│service_groups   │      │  service_configs   │
└─────────────────┘      └─────────────────────┘
```

### 7.3 数据库初始化

```python
# backend/init_db.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.models.database import Base
from app.models.models import Algorithm, AlgorithmVersion, Role, User, AlgorithmCall, GiteaConfig, AlgorithmRepository, ServiceGroup, AlgorithmService, ServiceConfig

# 创建数据库引擎
engine = create_engine("postgresql://admin:password@localhost:5432/algorithm_db")

# 创建所有表
Base.metadata.create_all(bind=engine)

# 创建会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()

# 初始化默认角色
try:
    # 检查是否已存在角色
    admin_role = db.query(Role).filter_by(name="admin").first()
    if not admin_role:
        admin_role = Role(
            id="role-admin",
            name="admin",
            description="管理员角色，拥有所有权限"
        )
        db.add(admin_role)
    
    user_role = db.query(Role).filter_by(name="user").first()
    if not user_role:
        user_role = Role(
            id="role-user",
            name="user",
            description="普通用户角色，拥有基本权限"
        )
        db.add(user_role)
    
    db.commit()
    print("默认角色初始化成功")
except Exception as e:
    db.rollback()
    print(f"初始化默认角色失败: {str(e)}")

# 关闭会话
db.close()

## 8. 总结与最佳实践

### 8.1 方案总结

本方案提供了一个完整的多独立 AI 算法服务管理解决方案，解决了端口冲突、统一管理、配置管理、运维效率和服务独立性等核心诉求。通过以下关键技术实现：

1. **服务标准化**：统一目录结构、接口规范和启动方式，提高服务的一致性和可维护性
2. **Supervisor 统一管理**：批量管理所有服务的启停、监控和自动重启，减少人工干预
3. **配置集中化**：采用数据库存储配置，支持界面修改，实现配置的动态管理
4. **Gitea 集成**：实现从代码管理到服务部署的全流程自动化，提高开发和部署效率
5. **数据库配置管理**：支持在界面上配置并存储到数据库中，实现配置的版本管理和历史追踪

### 8.2 最佳实践

1. **服务设计**：
   - 每个服务应保持独立，避免与其他服务产生强依赖
   - 实现标准化的健康检查和服务信息接口
   - 合理设置服务的超时时间和资源限制

2. **配置管理**：
   - 使用分层配置管理：环境变量 > 数据库配置 > 文件配置
   - 为配置设置合理的命名规范，便于管理和维护
   - 定期备份配置数据，防止配置丢失

3. **部署管理**：
   - 使用 Supervisor 管理服务，确保服务的可靠运行
   - 实现服务的自动重启机制，提高服务的可用性
   - 为每个服务分配唯一的端口，避免端口冲突

4. **监控与运维**：
   - 实现服务的健康检查和状态监控
   - 集中管理服务日志，便于问题排查
   - 建立服务的告警机制，及时发现和处理服务异常

5. **安全管理**：
   - 保护敏感配置信息，避免泄露
   - 实现服务的访问控制，确保服务的安全性
   - 定期更新服务依赖，修复安全漏洞

### 8.3 后续优化方向

1. **服务发现与注册**：实现服务的自动发现和注册机制，减少手动配置
2. **负载均衡**：为高流量服务添加负载均衡机制，提高服务的并发处理能力
3. **容器化部署**：结合 Docker 容器化技术，提高服务的可移植性和一致性
4. **CI/CD 集成**：实现代码提交到服务部署的自动化流程，提高开发效率
5. **监控系统**：集成 Prometheus + Grafana 等监控系统，提供更全面的服务监控

### 8.4 实施建议

1. **分阶段实施**：先在测试环境验证方案的可行性，再逐步推广到生产环境
2. **培训与文档**：为运维人员提供详细的培训和文档，确保方案的正确实施
3. **持续优化**：根据实际运行情况，持续优化服务管理方案
4. **风险评估**：在实施前进行充分的风险评估，制定应对措施

通过本方案的实施，您可以构建一个高效、可靠、易管理的多独立 AI 算法服务管理系统，为算法的开发、部署和运行提供有力的支持。