# 安全测试 ERP 任务创建 · 计划执行文档

> **文档状态**：初稿 · **版本**：v1.0 · **最后更新**：2026-08-17  
> **维护者**：czj · **所属模块**：安全测试  
> **对应 PRD**：`_PRD_安全测试ERP任务创建_需求文档.md`  
> **参考实现**：`ApiSecurityTest/utils/erp_client.py`、`task_creator.py`、`report_parser.py`

---

## 一、执行计划总览

### 阶段划分

| 阶段 | 内容 | 预估工时 | 产出物 |
|------|------|---------|--------|
| Phase 1 | 后端：ERPTaskClient（ERP 任务 API 客户端，复用 ErpService 模式） | 1h | `backend/app/services/erp_task_client.py` |
| Phase 2 | 后端：SecurityErpTaskService（报告解析 + 数据构建 + 任务创建） | 1.5h | `backend/app/services/security_erp_task_service.py` |
| Phase 3 | 后端：路由 + Schema + main.py 注册 | 0.5h | `backend/app/routers/security_erp_task.py` + `schemas/security_erp_task.py` |
| Phase 4 | 前端：API 封装 + Reports.vue 对话框 | 1h | `frontend/src/api/security.ts`（追加）+ `Reports.vue`（修改） |
| Phase 5 | 构建 + 联调验证 | 0.5h | `npm run build` 通过 + 后端路由验证 |

### 架构设计

```
前端 Reports.vue
  │  POST/GET /api/security/executions/{id}/task-preview
  │  POST/GET /api/security/executions/{id}/create-task
  ▼
security_erp_task.py (路由，薄层)
  ▼
security_erp_task_service.py (业务协调)
  ├── get_task_preview()   — 解析报告 + 获取基础数据 + 搜索需求
  └── create_task()        — 构建任务数据 + 调用 ERP
  ▼
erp_task_client.py (ERP API 客户端，含缓存/重试)
  ├── get_task_types()   GET /openclaw/tasktype
  ├── get_task_statuses() GET /openclaw/taskstatus
  ├── get_staff_list()   GET /openclaw/stuff
  ├── search_develop()   GET /openclaw/develop?search_str=
  └── create_task()      POST /openclaw/task
```

### 参考实现对照

| 参考文件 | 参考内容 | 本次实现 |
|---------|---------|---------|
| `UtilsTool/ApiSecurityTest/utils/erp_client.py` | ERP 客户端（缓存 + 重试） | `erp_task_client.py` |
| `UtilsTool/ApiSecurityTest/utils/task_creator.py` | 任务创建协调器 | `security_erp_task_service.py` |
| `UtilsTool/ApiSecurityTest/utils/report_parser.py` | Markdown 报告解析 | 复用 `SecurityReportService.get_report_content()` + 正则解析 |
| `UtilsTool/ApiSecurityTest/config.yaml` → `erp` 节点 | 默认配置 | 硬编码常量 + 前端可覆盖 |
| `backend/app/routers/functional_report.py` 的 ERP 上传实现 | 路由/Schema 风格 | `security_erp_task.py` + `schemas/security_erp_task.py` |

---

## 二、Phase 1：ERP 任务客户端

### 2.1 新增文件

**文件**: `backend/app/services/erp_task_client.py`（新建）

```python
"""
模块名称：erp_task_client.py
模块描述：ERP 任务 API 客户端，封装任务类型/状态/人员/需求/任务创建接口
参考实现：ApiSecurityTest/utils/erp_client.py
"""

ERPTaskClient 类：
- base_url / api_key / timeout / max_retries / retry_interval
- _cache / _cache_timestamps（1 小时缓存）
- get_task_types()   → GET /openclaw/tasktype
- get_task_statuses() → GET /openclaw/taskstatus
- get_staff_list()   → GET /openclaw/stuff
- search_develop(search_str, page_num=1, page_size=10) → GET /openclaw/develop
- create_task(task_data) → POST /openclaw/task
- _make_request(method, endpoint, ...) — 统一请求（重试 + 错误处理）
- _get_with_cache(key, fetch_func) — 缓存获取
```

### 2.2 配置获取

复用现有 `ErpConfig` 模型 + `settings` 环境变量：

```python
async def get_erp_task_client(db) -> ERPTaskClient:
    """从数据库读 ERP 配置，回退环境变量，创建客户端"""
    # 复用 /api/security/erp-config 相同的读取逻辑
    ...
```

### 2.3 关键说明

- 与 `erp_service.py` 共用配置来源（`erp_configs` 表 + `settings`）
- 缓存键：`task_types` / `task_statuses` / `staff_list`，缓存时长 3600s
- 请求头：`X-Api-Key`
- 错误处理：客户端错误（4xx）不重试，服务端错误（5xx）重试 3 次

---

## 三、Phase 2：任务创建服务

### 3.1 新增文件

**文件**: `backend/app/services/security_erp_task_service.py`（新建）

```python
"""
模块名称：security_erp_task_service.py
模块描述：安全测试 ERP 任务创建服务，解析报告、构建任务数据、创建 ERP 任务
参考实现：ApiSecurityTest/utils/task_creator.py + report_parser.py
"""

# 默认任务配置常量（对齐参考实现 config.yaml）
DEFAULT_TASK_CONFIG = {
    "type_id": 3,           # 后端开发
    "status_id": 1,         # 新建
    "level": 4,             # 常规处理
    "excepthour": 21,       # 期望工时
    "user_id": 101,         # 责任人 李立键
    "createuser_id": 441,   # 创建人 陈泽键
    "deadline_days": 7,     # 截止天数
    "develop_search_str": "【新统一平台】公司内部安全测试扫描及修复验证",
}

class SecurityErpTaskService:
    async def get_task_preview(execution_id, db) -> dict:
        """1. 生成报告 → 2. 解析摘要 → 3. 获取基础数据 → 4. 搜索需求 → 5. 返回预览"""

    async def create_task(execution_id, db, task_config) -> dict:
        """1. 解析报告 → 2. 构建任务数据 → 3. 创建任务 → 4. 返回结果"""
```

### 3.2 报告解析（对齐 report_parser.py）

```python
def parse_report_summary(markdown: str) -> dict:
    """从 Markdown 报告提取摘要"""
    summary = {target/server_ip/start_time/end_time/total/high/medium/low/info/safe}
    # 正则匹配，格式见 PRD 2.2.1
```

### 3.3 紧急程度计算（对齐 report_parser.py）

```python
def calculate_urgency_level(summary) -> int:
    high>0 → 5; medium>0 → 4; low>0 → 3; info>0 → 2; else → 1
```

### 3.4 任务名称生成（对齐 report_parser.py）

```python
def generate_task_name(server_ip) -> str:
    return f"{server_ip}接口安全测试及漏洞修复_{datetime.now():%Y%m%d}"
```

### 3.5 任务描述 HTML（对齐 report_parser.py）

```python
def generate_task_content(summary, high_risk_list) -> str:
    """生成 HTML 描述：测试概览/漏洞统计/高危漏洞/修复建议/紧急度说明"""
```

### 3.6 任务数据构建

```python
def build_task_data(summary, server_ip, high_risk_list, config) -> dict:
    """构建 POST /openclaw/task 的请求体"""
    task_data = {
        "name": generate_task_name(server_ip),
        "type_id": config.type_id,
        "status_id": config.status_id,
        "level": config.level (或自动计算),
        "excepthour": config.excepthour,
        "excepttime": datetime.now() + timedelta(days=config.deadline_days),
        "content": generate_task_content(...),
        "user_id": config.user_id,
        "createuser_id": config.createuser_id,
    }
    if config.develop_id:
        task_data["develop_id"] = config.develop_id
```

---

## 四、Phase 3：路由 + Schema

### 4.1 新增 Schema

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

```python
class TaskPreviewResponse(BaseModel):
    summary: dict
    task_name: str
    calculated_level: int
    task_types: list
    task_statuses: list
    staff_list: list
    requirement: Optional[dict]
    default_config: dict

class CreateTaskRequest(BaseModel):
    type_id: int
    status_id: int
    level: int
    excepthour: int
    user_id: int
    createuser_id: int
    deadline_days: int
    develop_id: Optional[int]

class CreateTaskResponse(BaseModel):
    success: bool
    task_id: Optional[int]
    task_name: str
    message: str
```

### 4.2 新增路由

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

```python
GET  /api/security/executions/{execution_id}/task-preview
POST /api/security/executions/{execution_id}/create-task
```

### 4.3 注册路由

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

```python
from app.routers import ..., security_erp_task

# 安全测试-ERP任务创建（需在 security_erp_upload 之后注册）
app.include_router(
    security_erp_task.router,
    prefix="/api/security",
    tags=["安全测试-ERP任务创建"]
)
```

---

## 五、Phase 4：前端实现

### 5.1 新增 API 封装

**文件**: `frontend/src/api/security.ts`（追加）

```typescript
/** 获取 ERP 任务创建预览 */
export function getSecurityTaskPreview(executionId: string) {
  return request.get(`/api/security/executions/${executionId}/task-preview`)
}

/** 创建 ERP 任务 */
export function createSecurityTask(
  executionId: string,
  data: {
    type_id: number
    status_id: number
    level: number
    excepthour: number
    user_id: number
    createuser_id: number
    deadline_days: number
    develop_id?: number
  }
) {
  return request.post(`/api/security/executions/${executionId}/create-task`, data)
}
```

### 5.2 修改 Reports.vue

在报告详情弹窗中，ERP 上传按钮旁新增「创建ERP任务」按钮：

```
[上传到ERP] [创建ERP任务]   ← 新增
```

新增对话框 `erpTaskDialog`：
- 报告摘要区（只读表格）：测试目标、测试时间、漏洞统计
- 任务配置区：
  - 任务名称（el-input，自动生成可编辑）
  - 任务类型（el-select，从 task_types）
  - 任务状态（el-select，从 task_statuses）
  - 责任人（el-select，过滤 staff_list）
  - 创建人（el-select，过滤 staff_list）
  - 紧急程度（el-select，1-5 自动计算）
  - 期望工时（el-input-number）
  - 截止天数（el-input-number）
  - 关联需求（el-input readonly，显示需求名称）
- 按钮：取消 / 确认创建

打开对话框时调用 `getSecurityTaskPreview(executionId)` 获取预览数据。

---

## 六、Phase 5：构建与验证

### 6.1 构建验证

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

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

### 6.2 后端验证

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

# 验证预览接口
curl http://localhost:8001/api/security/executions/{id}/task-preview

# 验证创建接口
curl -X POST http://localhost:8001/api/security/executions/{id}/create-task \
  -H "Content-Type: application/json" \
  -d '{"type_id":3,"status_id":1,"level":5,"excepthour":21,"user_id":101,"createuser_id":441,"deadline_days":7}'
```

### 6.3 功能验证清单

| 验证项 | 预期结果 | 状态 |
|--------|---------|------|
| 打开对话框获取预览 | 报告摘要 + 基础数据正确加载 | □ |
| 紧急程度自动计算 | 根据漏洞数显示 1-5 | □ |
| 任务名称自动生成 | `{IP}接口安全测试及漏洞修复_{日期}` | □ |
| 修改任务参数 | 提交时使用修改后的值 | □ |
| 确认创建 | ERP 返回任务 ID，前端显示成功 | □ |
| 创建失败 | 显示错误信息，不影响其他功能 | □ |
| 重复点击 | 按钮 loading 防重复提交 | □ |

---

## 七、文件清单

### 7.1 新增文件

| 文件 | 说明 |
|------|------|
| `backend/app/services/erp_task_client.py` | ERP 任务 API 客户端 |
| `backend/app/services/security_erp_task_service.py` | 任务创建服务 |
| `backend/app/schemas/security_erp_task.py` | 任务创建 Schema |
| `backend/app/routers/security_erp_task.py` | 任务创建路由 |
| `Docs/PRD/需求文档/安全测试/_PRD_安全测试ERP任务创建_需求文档.md` | 需求文档 |

### 7.2 修改文件

| 文件 | 修改内容 |
|------|---------|
| `backend/app/main.py` | 注册 `security_erp_task.router` |
| `frontend/src/api/security.ts` | 追加预览 + 创建 API |
| `frontend/src/views/Reports.vue` | 报告详情弹窗新增"创建ERP任务"按钮 + 对话框 |

---

## 八、风险评估

| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 参考目录 unicode 路径 | 导入失败 | 用绝对路径只读参考，不影响代码 |
| ERP 人员列表大 | 前端下拉卡顿 | 仅使用前 200 人 + filterable |
| 创建任务失败 | 用户困惑 | 明确错误提示 + 不阻断已有功能 |
| 报告正则不匹配 | 解析默认值 | 宽松匹配 + 默认值兜底 |

---

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