# 安全测试ERP配置 · 计划执行文档

> **文档状态**：初稿 · **版本**：v1.0 · **最后更新**：2026-08-13  
> **维护者**：czj · **所属模块**：安全测试  
> **对应 PRD**：`_PRD_安全测试ERP配置_需求文档.md`

---

## 一、执行计划总览

### 阶段划分

| 阶段 | 内容 | 预估工时 | 产出物 |
|------|------|---------|--------|
| Phase 1 | 后端 API 路由（新增 ERP 配置接口，复用 `erp_configs` 表 + `ErpConfig` 模型） | 0.5h | `backend/app/routers/security_erp.py` + `schemas/security_erp.py` + main.py 注册 |
| Phase 2 | 前端 API 封装 + ERP 配置页面 + 路由 + 菜单 | 0.5h | `frontend/src/api/securityErp.ts` + `views/security/ErpConfig.vue` + router 修改 + App.vue 修改 |
| Phase 3 | 构建 + 联调验证 | 0.5h | `npm run build` 通过 + 后端 API 验证 |

### 参考实现

本次开发直接参考功能测试报告模块的 ERP 配置实现：

| 参考文件 | 参考内容 | 本次实现 |
|---------|---------|---------|
| `backend/app/routers/functional_report.py` (L520-L602) | `GET/PUT /api/functional-report/config` | `GET/PUT /api/security/erp-config` |
| `backend/app/schemas/functional_report.py` (L109-L124) | `ErpConfigResponse` / `ErpConfigUpdateRequest` | 新建 `schemas/security_erp.py` |
| `frontend/src/api/functionalReport.ts` (L141-L163) | `getErpConfig()` / `updateErpConfig()` | 新建 `api/securityErp.ts` |
| `frontend/src/views/functional-report/ErpConfig.vue` | ERP 配置表单页面 | 复制到 `views/security/ErpConfig.vue`，替换 API 引用 |
| `frontend/src/router/index.ts` (L57-L77) | 功能测试报告子路由结构 | 安全测试子路由新增 ERP 配置 |
| `frontend/src/App.vue` (L115-L117) | 功能测试报告 ERP 配置菜单 | 安全测试菜单新增 ERP 配置 |

---

## 二、Phase 1：后端 API

### 2.1 新增 Pydantic Schema

**文件**: `backend/app/schemas/security_erp.py`（新建）

```python
class SecurityErpConfigResponse(BaseModel):
    """安全测试ERP配置响应"""
    api_key: str = Field("", description="API密钥")
    base_url: str = Field("", description="基础URL")
    developtesting_id: int = Field(0, description="默认测试单ID")
    type_id: int = Field(2, description="报告类型ID")
    image_url_prefix: str = Field("", description="图片URL前缀")


class SecurityErpConfigUpdateRequest(BaseModel):
    """安全测试ERP配置更新请求"""
    api_key: Optional[str] = Field(None, description="API密钥")
    base_url: Optional[str] = Field(None, description="基础URL")
    developtesting_id: Optional[int] = Field(None, description="默认测试单ID")
    type_id: Optional[int] = Field(None, description="报告类型ID")
    image_url_prefix: Optional[str] = Field(None, description="图片URL前缀")
```

### 2.2 新增 API 路由

**文件**: `backend/app/routers/security_erp.py`（新建）

路由逻辑**完全对齐**功能测试报告的 ERP 配置接口：

#### GET /api/security/erp-config

```python
@router.get("/erp-config", response_model=SecurityErpConfigResponse)
async def get_erp_config(db: AsyncSession = Depends(get_db)):
    """获取ERP配置（优先数据库，回退环境变量）"""
    result = await db.execute(select(ErpConfig).where(ErpConfig.id == 1))
    config = result.scalar_one_or_none()
    if config:
        return SecurityErpConfigResponse(
            api_key=config.api_key or settings.ERP_API_KEY or "",
            base_url=config.base_url or settings.ERP_BASE_URL or "",
            developtesting_id=config.developtesting_id or settings.ERP_DEVELOPTESTING_ID or 0,
            type_id=config.type_id or settings.ERP_TYPE_ID or 2,
            image_url_prefix=config.image_url_prefix or settings.ERP_IMAGE_URL_PREFIX or "",
        )
    return SecurityErpConfigResponse(
        api_key=settings.ERP_API_KEY or "",
        base_url=settings.ERP_BASE_URL or "",
        developtesting_id=settings.ERP_DEVELOPTESTING_ID or 0,
        type_id=settings.ERP_TYPE_ID or 2,
        image_url_prefix=settings.ERP_IMAGE_URL_PREFIX or "",
    )
```

#### PUT /api/security/erp-config

```python
@router.put("/erp-config", response_model=SecurityErpConfigResponse)
async def update_erp_config(request: SecurityErpConfigUpdateRequest, db: AsyncSession = Depends(get_db)):
    """更新ERP配置（持久化到数据库，同时更新运行时环境变量）"""
    result = await db.execute(select(ErpConfig).where(ErpConfig.id == 1))
    config = result.scalar_one_or_none()
    if not config:
        config = ErpConfig(id=1, ...)
        db.add(config)
    # 更新字段 + 同步 settings
    ...
    await db.commit()
    await db.refresh(config)
    return SecurityErpConfigResponse(...)
```

### 2.3 注册路由到 main.py

**文件**: `backend/app/main.py`

在 `security.router` 注册后新增：
```python
from app.routers import security_erp

app.include_router(
    security_erp.router,
    prefix="/api/security",
    tags=["安全测试-ERP配置"]
)
```

### 2.4 关键说明

- **复用 `ErpConfig` 模型**：`from app.models.erp_config import ErpConfig`，两个模块共享 `erp_configs` 表
- **复用 `settings` 环境变量**：`from app.config import settings`，共享 `ERP_API_KEY` / `ERP_BASE_URL` 等配置
- **不新增数据表**：安全测试和功能测试报告共用同一套 ERP 配置

---

## 三、Phase 2：前端实现

### 3.1 新增 API 封装

**文件**: `frontend/src/api/securityErp.ts`（新建）

```typescript
import request from '@/utils/request'

export const securityErpApi = {
  async getConfig(): Promise<{
    api_key: string
    base_url: string
    developtesting_id: number
    type_id: number
    image_url_prefix: string
  }> {
    const response = await request.get('/api/security/erp-config')
    return response as any
  },

  async updateConfig(config: {
    api_key?: string
    base_url?: string
    developtesting_id?: number
    type_id?: number
    image_url_prefix?: string
  }): Promise<any> {
    const response = await request.put('/api/security/erp-config', config)
    return response as any
  }
}
```

### 3.2 新增 ERP 配置页面

**文件**: `frontend/src/views/security/ErpConfig.vue`（新建）

直接复制 `frontend/src/views/functional-report/ErpConfig.vue`，修改点：
- 组件名称注释：`安全测试 ERP 配置管理页面`
- API 引用：`import { securityErpApi } from '@/api/securityErp'`
- 方法调用：`securityErpApi.getConfig()` / `securityErpApi.updateConfig()`

表单字段、校验规则、布局样式完全一致。

### 3.3 路由配置

**文件**: `frontend/src/router/index.ts`

在安全测试路由（`/security`）下新增子路由：
```typescript
{
  path: '/security',
  name: 'Security',
  component: () => import('@/views/security/index.vue'),
  redirect: '/security/cases',
  meta: { title: '安全测试' },
  children: [
    // 原有子路由...
    {
      path: 'erp-config',
      name: 'SecurityErpConfig',
      component: () => import('@/views/security/ErpConfig.vue'),
      meta: { title: 'ERP配置' }
    }
  ]
}
```

### 3.4 菜单配置

**文件**: `frontend/src/App.vue`

安全测试子菜单新增 ERP 配置：
```html
<el-sub-menu index="/security">
  <template #title>
    <el-icon><Lock /></el-icon>
    <span>安全测试</span>
  </template>
  <el-menu-item index="/cases/security">用例管理</el-menu-item>
  <el-menu-item index="/execution/security">执行中心</el-menu-item>
  <el-menu-item index="/reports/security">报告中心</el-menu-item>
  <el-menu-item index="/security/erp-config">ERP 配置</el-menu-item>  <!-- 新增 -->
</el-sub-menu>
```

### 3.5 安全测试父容器组件

如果 `frontend/src/views/security/index.vue` 不存在，需要创建（参考 `functional-report/index.vue`），仅渲染 `<router-view />`。

---

## 四、Phase 3：构建与验证

### 4.1 构建验证

```bash
cd frontend
npm run build
```

预期结果：构建成功，无 TypeScript 类型错误。

### 4.2 后端验证

```bash
cd backend
uvicorn app.main:app --port 8001

# 验证 GET 接口
curl http://localhost:8001/api/security/erp-config
# 预期返回：{"api_key":"","base_url":"","developtesting_id":0,"type_id":2,"image_url_prefix":""}

# 验证 PUT 接口
curl -X PUT http://localhost:8001/api/security/erp-config \
  -H "Content-Type: application/json" \
  -d '{"api_key":"test-key","base_url":"https://erp.example.com"}'
# 预期返回：更新后的配置
```

### 4.3 功能验证

| 验证项 | 预期结果 | 状态 |
|--------|---------|------|
| 页面加载时自动获取配置 | 表单字段自动填充 | □ |
| 修改字段后保存 | 成功提示，数据库持久化 | □ |
| 重置按钮 | 恢复加载时的原始值 | □ |
| 必填校验（API Key 为空） | 表单校验失败提示 | □ |
| 必填校验（Base URL 为空） | 表单校验失败提示 | □ |
| 保存时按钮 loading | 按钮显示"保存中..." | □ |
| 刷新页面后配置保留 | 上次保存的值仍显示 | □ |

---

## 五、文件清单

### 5.1 新增文件

| 文件 | 说明 |
|------|------|
| `backend/app/schemas/security_erp.py` | ERP 配置 Pydantic Schema |
| `backend/app/routers/security_erp.py` | ERP 配置 API 路由 |
| `frontend/src/api/securityErp.ts` | ERP 配置 API 前端封装 |
| `frontend/src/views/security/ErpConfig.vue` | ERP 配置管理页面 |
| `frontend/src/views/security/index.vue` | 安全测试父容器（若不存在） |

### 5.2 修改文件

| 文件 | 修改内容 |
|------|---------|
| `backend/app/main.py` | 注册 `security_erp.router` |
| `frontend/src/router/index.ts` | 安全测试路由新增 `erp-config` 子路由 |
| `frontend/src/App.vue` | 安全测试菜单新增 ERP 配置子项 |

---

## 六、风险评估

| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 与功能测试报告共用 ERP 配置 | 配置修改互相影响 | 预期行为，两个模块共用同一套 ERP 配置，降低维护成本 |
| 后端已有 `erp_config` 路由冲突 | 404 或路由覆盖 | 前缀用 `/api/security/erp-config`，与 `/api/functional-report/config` 路径不同，不冲突 |
| 安全测试父容器不存在 | 路由无法渲染子页面 | 检查 `security/index.vue` 是否存在，不存在则创建 |

---

*本文档为安全测试ERP配置功能的计划执行文档，供开发阶段参考。*