提交 387ab3eb authored 作者: 陈泽健's avatar 陈泽健

feat(ApiSecurityTest): 对接ERP系统实现安全测试任务创建功能

- 添加ERP对接配置项包括API密钥、基础URL和重试机制
- 实现获取任务类型、状态、人员和需求列表的基础数据功能
- 集成创建任务接口支持设置任务基本信息和关联需求
- 添加上传截图功能用于任务描述中的富文本内容
- 实现基础数据缓存机制避免重复请求提高性能
- 集成日志记录功能追踪API调用和任务创建状态
- 更新.gitignore添加API安全测试日志忽略规则
- 创建ERP对接需求文档和技术接口文档
上级 46442e56
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
# 新统一平台图片日志 # 新统一平台图片日志
/新统一平台/log/imgs/ /新统一平台/log/imgs/
# API安全测试日志
/AuxiliaryTool/ScriptTool/ApiSecurityTest/logs/
/AuxiliaryTool/ScriptTool/ApiSecurityTest/*.log
# --- Python 核心缓存与编译文件 (必须配置) --- # --- Python 核心缓存与编译文件 (必须配置) ---
# 忽略所有 __pycache__ 目录 (包含任意层级的子目录) # 忽略所有 __pycache__ 目录 (包含任意层级的子目录)
......
# ERP对接任务创建功能说明
## 功能概述
本功能在现有接口安全测试系统基础上增加了ERP任务创建功能,实现测试完成后自动创建ERP任务进行跟踪管理。
## 业务流程
```
步骤1:执行API安全测试(已有)
步骤2:生成安全测试报告(已有)
步骤3:上传报告到网盘(已有)
步骤4:创建ERP任务(🆕 新功能)
```
## 配置说明
`config.yaml` 中新增了 `erp` 配置节点:
```yaml
# ERP对接配置
erp:
# 是否启用ERP对接
enabled: true
# ERP API配置
base_url: "https://office.ubainsyun.com:5082/api/uerp"
api_key: "your_api_key_here" # 需要替换为实际密钥
timeout: 30
retry_interval: 2
max_retries: 3
# 任务配置
task:
type_id: 2 # 任务类型:2-测试任务
status_id: 1 # 任务状态:1-新建
level: 3 # 紧急程度:3-紧急处理
excepthour: 8 # 期望工时:8小时
user_id: 20 # 责任人ID
createuser_id: 5 # 创建人ID
deadline_days: 3 # 截止天数:当前时间+3天
# 需求关联
develop_search_str: "【新统一平台】公司内部安全测试扫描及修复验证"
# 缓存配置
cache_duration: 3600 # 基础数据缓存时长(秒)
# 上传配置
upload:
enabled: true # 是否上传截图
max_images: 5 # 最多上传图片数量
# 网盘配置
nas:
base_path: "\\\\192.168.9.9\\deploy\\18其它系统\\安全测试\\04新统一平台-安全报告"
```
## 使用方式
### 1. 启用ERP对接
`config.yaml` 中设置:
```yaml
erp:
enabled: true
api_key: "your_actual_api_key" # 替换为实际的API密钥
```
### 2. 配置任务参数
根据实际情况调整:
- `user_id`: 责任人ID(从ERP人员列表中选择)
- `createuser_id`: 创建人ID(从ERP人员列表中选择)
- `level`: 默认紧急程度(1-5)
- `excepthour`: 期望工时(小时)
- `deadline_days`: 截止天数
### 3. 执行测试
正常执行安全测试即可:
```bash
# 执行全部测试
python run_all.py
# 执行单个模块
python run_all.py api01
```
### 4. 查看结果
测试完成后会自动创建ERP任务,控制台输出:
```
==========================================================================
ERP任务创建阶段
==========================================================================
步骤 1/5: 解析测试报告...
✓ 测试目标: https://192.168.5.44
✓ 测试时间: 2026-06-24 15:00:00 ~ 15:30:00
✓ 漏洞统计: 高危=3, 中危=7, 低危=4
步骤 2/5: 获取ERP基础数据...
✓ 任务类型: 10种
✓ 任务状态: 5种
✓ 人员列表: 45人
步骤 3/5: 搜索关联需求...
✓ 找到需求: 【新统一平台】公司内部安全测试扫描及修复验证 (ID: 128)
步骤 4/5: 构建任务数据...
任务数据:
- 名称: 192.168.5.44接口安全测试及漏洞修复_20260624
- 类型: 测试任务
- 状态: 新建
- 紧急程度: 5
- 期望工时: 8小时
- 截止时间: 2026-06-27 18:00:00
- 责任人: 郑晓兵
- 创建人: 陈泽键
- 关联需求: 【新统一平台】公司内部安全测试扫描及修复验证
步骤 5/5: 创建ERP任务...
==========================================================================
✓ ERP任务创建成功!
- 任务ID: 1024
- 任务名称: 192.168.5.44接口安全测试及漏洞修复_20260624
- 任务链接: https://office.ubainsyun.com:5082/task/detail/1024
==========================================================================
```
## 新增模块
### 1. ERP客户端 (`utils/erp_client.py`)
提供ERP API调用功能:
- 获取任务类型、状态、人员列表
- 搜索需求列表
- 创建任务
- 上传图片
- 自动重试机制
- 数据缓存
### 2. 报告解析器 (`utils/report_parser.py`)
从Markdown报告中提取信息:
- 测试目标URL
- 测试时间
- 漏洞统计
- 漏洞列表
- 生成任务名称
- 生成任务描述HTML
### 3. 任务创建器 (`utils/task_creator.py`)
协调完成任务创建:
- 解析报告
- 获取ERP基础数据
- 搜索关联需求
- 构建任务数据
- 调用ERP接口
- 日志记录
## 任务名称规则
格式:`{目标IP}接口安全测试及漏洞修复_{日期}`
示例:`192.168.5.44接口安全测试及漏洞修复_20260624`
## 紧急程度规则
根据漏洞数量自动计算:
- **5** - 有高危漏洞(最紧急)
- **4** - 有中危漏洞
- **3** - 有低危漏洞
- **2** - 仅有信息类
- **1** - 无漏洞
## 错误处理
- ERP创建失败不影响测试报告生成和上传
- 详细的错误日志便于排查
- 网络超时自动重试(最多3次)
- 配置错误时有明确提示
## 注意事项
1. **API密钥**:需要在配置中提供正确的ERP API密钥
2. **人员ID**:user_id 和 createuser_id 需要从ERP系统中获取
3. **需求搜索**:develop_search_str 需要准确匹配ERP中的需求名称
4. **网络连接**:确保能够访问ERP服务器
5. **权限验证**:API密钥需要有创建任务的权限
## 禁用ERP对接
如果不需要ERP对接功能,在配置中设置:
```yaml
erp:
enabled: false
```
测试流程将正常执行,但不会创建ERP任务。
...@@ -64,6 +64,15 @@ report: ...@@ -64,6 +64,15 @@ report:
# 报告文件名前缀 # 报告文件名前缀
prefix: "api_security_report" prefix: "api_security_report"
# 日志配置
log:
# 日志输出目录
output_dir: "logs/"
# 是否启用文件日志记录
file_enabled: true
# 日志级别:DEBUG, INFO, WARNING, ERROR, CRITICAL
level: "INFO"
# Nginx 配置分析结果(来自 unified443.conf 分析) # Nginx 配置分析结果(来自 unified443.conf 分析)
nginx_analysis: nginx_analysis:
# 已知端口映射(从Nginx配置中提取的内部服务端口) # 已知端口映射(从Nginx配置中提取的内部服务端口)
...@@ -123,3 +132,53 @@ limits: ...@@ -123,3 +132,53 @@ limits:
batch_max: 50 batch_max: 50
# 请求间隔(秒),避免过快触发限流 # 请求间隔(秒),避免过快触发限流
request_interval: 0.5 request_interval: 0.5
# ERP对接配置
erp:
# 是否启用ERP对接
enabled: true
# ERP API基础URL
base_url: "https://office.ubainsyun.com:5082/api/uerp"
# ERP API密钥(需要替换为实际密钥)
api_key: "9adc1ce611544fae9bbbc5f6b1d6ce87"
# 请求超时时间(秒)
timeout: 30
# 重试间隔(秒)
retry_interval: 2
# 最大重试次数
max_retries: 3
# 任务配置
task:
# 默认任务类型:1-开发任务 2-测试任务 3-设计任务 4-部署任务 5-文档任务 6-维护任务 7-研究任务 8-会议沟通 9-售前支持 10-售后支持
type_id: 1
# 默认任务状态:1-新建 2-进行中 3-已解决 4-完成确认中 10-已完成
status_id: 1
# 默认紧急程度:1-每日观察 2-非常紧急 3-紧急处理 4-常规处理 5-迭代优化
level: 4
# 默认期望工时(小时)
excepthour: 14
# 默认责任人ID,陈泽键(441)
user_id: 441
# 默认创建人ID
createuser_id: 441
# 任务截止天数(当前时间+N天)
deadline_days: 7
# 需求关联配置
develop_search_str: "【新统一平台】公司内部安全测试扫描及修复验证"
# 缓存配置
cache_duration: 3600 # 基础数据缓存时长(秒),默认1小时
# 上传配置
upload:
# 是否上传截图
enabled: true
# 最多上传图片数量
max_images: 5
# 网盘配置(用于在任务描述中添加报告链接)
nas:
# 网盘基础路径
base_path: "\\\\192.168.9.9\\deploy\\18其它系统\\安全测试\\04新统一平台-安全报告"
# -*- coding: utf-8 -*-
"""
ERP对接诊断脚本
检查配置并测试ERP连接
"""
import sys
import yaml
from utils.logger import log
log.info("=" * 60)
log.info("ERP对接诊断工具")
log.info("=" * 60)
# 1. 检查配置文件
log.info("\n步骤 1/4: 检查配置文件...")
try:
with open('config.yaml', 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
erp_config = config.get('erp', {})
# 检查关键配置
log.info(f" - ERP启用状态: {erp_config.get('enabled', False)}")
log.info(f" - API基础URL: {erp_config.get('base_url', 'N/A')}")
log.info(f" - API密钥: {'*' * 10 if erp_config.get('api_key') and erp_config['api_key'] != 'your_api_key_here' else '未设置(需要配置)'}")
log.info(f" - 请求超时: {erp_config.get('timeout', 30)}秒")
log.info(f" - 重试次数: {erp_config.get('max_retries', 3)}")
task_config = erp_config.get('task', {})
log.info(f" - 责任人ID: {task_config.get('user_id', 'N/A')}")
log.info(f" - 创建人ID: {task_config.get('createuser_id', 'N/A')}")
if not erp_config.get('enabled', False):
log.warning(" ⚠ ERP对接未启用!请在config.yaml中设置 erp.enabled: true")
if not erp_config.get('api_key') or erp_config['api_key'] == 'your_api_key_here':
log.warning(" ⚠ API密钥未配置!请在config.yaml中设置 erp.api_key")
except Exception as e:
log.error(f" ✗ 配置文件读取失败: {e}")
sys.exit(1)
# 2. 测试ERP连接
log.info("\n步骤 2/4: 测试ERP连接...")
try:
from utils.erp_client import ERPClient
erp_client = ERPClient(config)
# 测试获取任务类型
log.info(" 正在测试API连接(获取任务类型)...")
task_types = erp_client.get_task_types()
if task_types:
log.info(f" ✓ 连接成功!获取到 {len(task_types)} 种任务类型")
for tt in task_types[:3]:
log.info(f" - {tt['id']}: {tt['name']}")
else:
log.error(" ✗ 连接失败:无法获取任务类型")
log.error(" 可能原因:")
log.error(" 1. API密钥无效")
log.error(" 2. 网络无法访问")
log.error(" 3. API端点地址错误")
except ImportError as e:
log.error(f" ✗ 模块导入失败: {e}")
except Exception as e:
log.error(f" ✗ 连接测试失败: {e}")
# 3. 测试获取人员列表
log.info("\n步骤 3/4: 测试获取人员列表...")
try:
staff_list = erp_client.get_staff_list()
if staff_list:
log.info(f" ✓ 获取到 {len(staff_list)} 人")
# 查找陈泽键
log.info(" 正在查找 '陈泽键'...")
chen_id = None
for staff in staff_list:
name = staff.get('name', '')
if '陈泽键' in name or name == '陈泽键':
chen_id = staff['id']
log.info(f" ✓ 找到陈泽键:ID = {chen_id}")
break
if chen_id is None:
log.warning(" ⚠ 未找到 '陈泽键'")
log.info(" 提示:可能是姓名不匹配,列出前10人供参考:")
for staff in staff_list[:10]:
log.info(f" ID: {staff['id']:3d} | 姓名: {staff['name']}")
else:
log.error(" ✗ 获取人员列表失败")
except Exception as e:
log.error(f" ✗ 获取人员列表失败: {e}")
# 4. 测试创建任务
log.info("\n步骤 4/4: 测试创建任务...")
try:
# 检查配置的ID是否有效
user_id = task_config.get('user_id')
createuser_id = task_config.get('createuser_id')
if not user_id or not createuser_id:
log.warning(" ⚠ 责任人ID或创建人ID未配置")
else:
# 验证ID是否存在
user_found = False
creator_found = False
for staff in staff_list:
if staff['id'] == user_id:
user_found = True
log.info(f" ✓ 责任人ID {user_id} 有效:{staff['name']}")
if staff['id'] == createuser_id:
creator_found = True
log.info(f" ✓ 创建人ID {createuser_id} 有效:{staff['name']}")
if not user_found:
log.warning(f" ⚠ 责任人ID {user_id} 在人员列表中不存在")
if not creator_found:
log.warning(f" ⚠ 创建人ID {createuser_id} 在人员列表中不存在")
except Exception as e:
log.error(f" ✗ 检查失败: {e}")
log.info("\n" + "=" * 60)
log.info("诊断完成")
log.info("=" * 60)
# 提供修复建议
log.info("\n修复建议:")
log.info("1. 如果API密钥未配置,请联系管理员获取有效的API密钥")
log.info("2. 如果未找到陈泽键,请确认姓名或手动设置ID")
log.info("3. 确保config.yaml中 erp.enabled 设置为 true")
# -*- coding: utf-8 -*-
"""
查询ERP人员列表脚本
用于查找人员ID
"""
import sys
import yaml
from utils.erp_client import ERPClient
from utils.logger import log
# 加载配置
with open('config.yaml', 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# 创建ERP客户端
erp_client = ERPClient(config)
# 获取人员列表
log.info("正在获取ERP人员列表...")
staff_list = erp_client.get_staff_list()
if staff_list:
log.info(f"✓ 共获取到 {len(staff_list)} 人")
# 打印所有人员
log.info("\n人员列表:")
log.info("-" * 60)
for staff in staff_list:
log.info(f"ID: {staff['id']:3d} | 姓名: {staff['name']:10s} | 部门ID: {staff.get('department_id', 'N/A')}")
log.info("-" * 60)
# 查找陈泽键
log.info("\n正在查找 '陈泽键'...")
found = False
for staff in staff_list:
if '陈泽键' in staff['name'] or staff['name'] == '陈泽键':
log.info(f"✓ 找到陈泽键:ID = {staff['id']}")
found = True
break
if not found:
log.warning("✗ 未找到 '陈泽键'")
log.info("提示:请检查人员姓名是否准确,可能需要使用部分匹配")
else:
log.error("✗ 获取人员列表失败")
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import os import os
import sys import sys
import shutil import shutil
import yaml
# 将项目根目录加入 Python 路径 # 将项目根目录加入 Python 路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
...@@ -18,6 +19,16 @@ from utils.report_generator import ReportGenerator ...@@ -18,6 +19,16 @@ from utils.report_generator import ReportGenerator
# 网盘固定路径:安全测试报告上传目录 # 网盘固定路径:安全测试报告上传目录
NAS_REPORT_DIR = r"\\192.168.9.9\deploy\18其它系统\安全测试\04新统一平台-安全报告" NAS_REPORT_DIR = r"\\192.168.9.9\deploy\18其它系统\安全测试\04新统一平台-安全报告"
# 加载配置文件
CONFIG = None
try:
with open('config.yaml', 'r', encoding='utf-8') as f:
CONFIG = yaml.safe_load(f)
log.info("配置文件加载成功")
except Exception as e:
log.warning(f"配置文件加载失败: {e},将使用默认配置")
CONFIG = {}
def upload_to_nas(report_path): def upload_to_nas(report_path):
""" """
...@@ -54,13 +65,50 @@ def upload_to_nas(report_path): ...@@ -54,13 +65,50 @@ def upload_to_nas(report_path):
def upload_to_erp(report_path): def upload_to_erp(report_path):
""" """
对接ERP创建任务跟踪(预留功能) 对接ERP创建任务跟踪
暂无对接,仅保留接口入口
参数: 参数:
report_path: 报告文件路径 report_path: 报告文件路径
返回:
bool: 是否成功创建任务
""" """
log.info("ERP对接功能暂未实现,跳过") try:
# 1. 检查是否启用ERP对接
if not CONFIG or not CONFIG.get('erp', {}).get('enabled', False):
log.info("ERP对接未启用,跳过任务创建")
return False
log.info("=" * 60)
log.info("ERP任务创建阶段")
log.info("=" * 60)
# 2. 导入ERP模块(延迟导入,避免影响主流程)
from utils.erp_client import ERPClient
from utils.task_creator import TaskCreator
# 3. 初始化ERP客户端
erp_client = ERPClient(CONFIG)
# 4. 创建任务创建器
task_creator = TaskCreator(erp_client, CONFIG)
# 5. 从报告创建任务
result = task_creator.create_task_from_report(report_path)
if result:
return True
else:
log.error("✗ ERP任务创建失败")
return False
except ImportError as e:
log.warning(f"ERP模块导入失败: {e}")
log.info("跳过ERP任务创建")
return False
except Exception as e:
log.error(f"✗ ERP任务创建异常: {e}")
return False
def run_all_tests(): def run_all_tests():
...@@ -137,10 +185,11 @@ def run_all_tests(): ...@@ -137,10 +185,11 @@ def run_all_tests():
log.info(f"总报告路径: {report_path}") log.info(f"总报告路径: {report_path}")
# 上传报告至网盘 # 上传报告至网盘
log.info("阶段 4/4: 上传报告至网盘...") log.info("阶段 4/5: 上传报告至网盘...")
upload_to_nas(report_path) upload_to_nas(report_path)
# ERP对接(预留) # ERP对接
log.info("阶段 5/5: 创建ERP任务...")
upload_to_erp(report_path) upload_to_erp(report_path)
return report return report
......
# -*- coding: utf-8 -*-
"""
测试ERP任务创建功能
使用现有报告快速测试ERP对接
"""
import os
import sys
import yaml
from utils.logger import log
from utils.erp_client import ERPClient
from utils.task_creator import TaskCreator
log.info("=" * 60)
log.info("ERP任务创建测试")
log.info("=" * 60)
# 加载配置
with open('config.yaml', 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# 检查ERP是否启用
if not config.get('erp', {}).get('enabled', False):
log.error("ERP对接未启用!请在config.yaml中设置 erp.enabled: true")
sys.exit(1)
# 查找最近的报告
reports_dir = "reports"
if not os.path.exists(reports_dir):
log.error(f"报告目录不存在: {reports_dir}")
log.info("请先运行安全测试生成报告:python run_all.py")
sys.exit(1)
# 获取最新的报告文件
report_files = [f for f in os.listdir(reports_dir) if f.endswith('.md')]
if not report_files:
log.error(f"没有找到报告文件: {reports_dir}/*.md")
log.info("请先运行安全测试生成报告:python run_all.py")
sys.exit(1)
# 按修改时间排序,获取最新的
report_files.sort(key=lambda x: os.path.getmtime(os.path.join(reports_dir, x)), reverse=True)
latest_report = os.path.join(reports_dir, report_files[0])
log.info(f"使用报告: {latest_report}")
log.info(f"报告时间: {report_files[0]}")
# 测试ERP任务创建
log.info("\n开始测试ERP任务创建...")
try:
# 初始化ERP客户端
erp_client = ERPClient(config)
# 创建任务创建器
task_creator = TaskCreator(erp_client, config)
# 从报告创建任务
result = task_creator.create_task_from_report(latest_report)
# 调试:打印 result 的类型和内容
log.info(f"[DEBUG] result type: {type(result)}")
log.info(f"[DEBUG] result keys: {result.keys() if isinstance(result, dict) else 'N/A'}")
if isinstance(result, dict):
log.info(f"[DEBUG] result.get('success'): {result.get('success')}")
if 'data' in result:
log.info(f"[DEBUG] result['data']: {result['data']}")
if result and isinstance(result, dict) and result.get('success') == 1:
# 从 data 字段中获取任务信息
data = result.get('data', {})
task_id = data.get('create')
task_name = data.get('name')
log.info("\n" + "=" * 60)
log.info("✅ 测试成功!ERP任务创建成功!")
log.info("=" * 60)
log.info(f" 任务ID: {task_id}")
log.info(f" 任务名称: {task_name}")
log.info(f" 查看链接: {config['erp']['base_url']}/task/detail/{task_id}")
log.info("=" * 60)
log.info("\n请登录ERP系统验证任务是否创建成功!")
else:
log.error("\n❌ 测试失败!ERP任务创建失败")
if result:
log.error(f" result内容: {result}")
else:
log.error(" result is None or empty")
except Exception as e:
log.error(f"\n❌ 测试异常: {e}")
import traceback
traceback.print_exc()
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
""" """
import os import os
import logging import logging
import yaml
from datetime import datetime from datetime import datetime
from pathlib import Path
# 尝试导入 colorama,不存在则降级处理 # 尝试导入 colorama,不存在则降级处理
try: try:
...@@ -15,6 +17,15 @@ except ImportError: ...@@ -15,6 +17,15 @@ except ImportError:
HAS_COLORAMA = False HAS_COLORAMA = False
def load_config():
"""加载配置文件"""
config_file = Path(__file__).parent.parent / 'config.yaml'
if config_file.exists():
with open(config_file, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
return {}
class ColoredFormatter(logging.Formatter): class ColoredFormatter(logging.Formatter):
"""带颜色的日志格式化器""" """带颜色的日志格式化器"""
...@@ -40,17 +51,28 @@ class ColoredFormatter(logging.Formatter): ...@@ -40,17 +51,28 @@ class ColoredFormatter(logging.Formatter):
return formatted return formatted
def setup_logger(name="ApiSecurityTest", log_dir=None): def setup_logger(name="ApiSecurityTest", log_dir=None, config=None):
""" """
创建并配置日志器 创建并配置日志器
参数: 参数:
name: 日志器名称 name: 日志器名称
log_dir: 日志文件输出目录,为None则不输出文件 log_dir: 日志文件输出目录,为None则从配置读取
config: 配置字典,为None则自动加载
返回: 返回:
logging.Logger: 配置好的日志器 logging.Logger: 配置好的日志器
""" """
# 加载配置
if config is None:
config = load_config()
# 从配置获取日志目录
if log_dir is None:
log_config = config.get('log', {})
if log_config.get('file_enabled', False):
log_dir = log_config.get('output_dir', 'logs')
logger = logging.getLogger(name) logger = logging.getLogger(name)
# 防止重复添加处理器 # 防止重复添加处理器
...@@ -80,6 +102,7 @@ def setup_logger(name="ApiSecurityTest", log_dir=None): ...@@ -80,6 +102,7 @@ def setup_logger(name="ApiSecurityTest", log_dir=None):
file_handler.setLevel(logging.DEBUG) file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(fmt, date_fmt)) file_handler.setFormatter(logging.Formatter(fmt, date_fmt))
logger.addHandler(file_handler) logger.addHandler(file_handler)
logger.info(f"日志文件: {log_file}")
return logger return logger
......
# ERP创建任务对接需求文档
## 代码路径
- 代码路径:[AuxiliaryTool/ERPTaskCreation]
## ERP对接文档
- 需求文档路径:[Docs/PRD/ERP对接/PRD_测试列表_任务接口_例子说明.md]
## 功能需求
### 功能目标
**目标:** 对接ERP任务管理接口,实现通过程序创建开发任务,支持设置任务基本信息、指派人员、关联需求、上传截图等功能。
### 需求描述
#### 1. 获取基础数据
在创建任务前,需要获取以下基础数据供用户选择:
- **获取任务类型**
- 接口:`GET /openclaw/tasktype`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/tasktype`
- X-Api-Key: 保持原有的key传入
- 返回数据示例:
```json
{
"success": 1,
"data": [
{"id": 1, "name": "开发任务"},
{"id": 2, "name": "测试任务"},
{"id": 3, "name": "设计任务"},
{"id": 4, "name": "部署任务"},
{"id": 5, "name": "文档任务"},
{"id": 6, "name": "维护任务"},
{"id": 7, "name": "研究任务"},
{"id": 8, "name": "会议沟通"},
{"id": 9, "name": "售前支持"},
{"id": 10, "name": "售后支持"}
]
}
```
- 用途:用户选择任务类型
- **获取任务状态**
- 接口:`GET /openclaw/taskstatus`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/taskstatus`
- X-Api-Key: 保持原有的key传入
- 返回数据示例:
```json
{
"success": 1,
"data": [
{"id": 1, "name": "新建"},
{"id": 2, "name": "进行中"},
{"id": 3, "name": "已解决"},
{"id": 4, "name": "完成确认中"},
{"id": 10, "name": "已完成"}
]
}
```
- 用途:用户选择任务初始状态(通常选择"新建"
- **获取人员列表**
- 接口:`GET /openclaw/stuff`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/stuff`
- X-Api-Key: 保持原有的key传入
- 返回数据示例:
```json
{
"success": 1,
"data": [
{"id": 1, "name": "超管员", "department_id": null},
{"id": 20, "name": "郑晓兵", "department_id": 94020884},
{"id": 24, "name": "黄史恭", "department_id": 596543063},
{"id": 31, "name": "欧阳友平", "department_id": 363531662}
]
}
```
- 用途:用户选择任务责任人(user_id)和下单人员(createuser_id)
- **获取需求列表**
- 接口:`GET /openclaw/develop`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/develop`
- X-Api-Key: 保持原有的key传入
- 查询参数:
- `pageNum`: 页码,默认 1
- `pageSize`: 每页数量,默认 10
- `search_str`: 全局搜索(需求名/描述/项目名/创建人/责任人)
- 返回数据示例:
```json
{
"success": 1,
"pageSize": 10,
"pageNum": 1,
"totalRows": 183,
"data": [
{
"id": 215,
"name": "测试需求",
"project_id": 1702,
"project_name": "演示测试项目2",
"dutyuser_id": 20,
"dutyuser_name": "郑晓兵"
}
]
}
```
- 用途:用户选择要关联的需求(develop_id 为必填)
#### 2. 上传截图(可选)
- 上传任务截图接口:
- 接口:`POST /openclaw/upload/richtext`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/upload/richtext`
- X-Api-Key: 保持原有的key传入
- Content-Type: `multipart/form-data`
- 请求示例:
```bash
curl -X POST "https://office.ubainsyun.com:5082/api/uerp/openclaw/upload/richtext" \
-H "X-Api-Key: your_api_key" \
-F "file=@/path/to/screenshot.png" \
-F "name=task_screenshot.png"
```
- 返回数据示例:
```json
{
"success": 1,
"data": {
"id": 12444,
"name": "task_screenshot.png",
"url": "https://office.ubainsyun.com:5015/upload/20260317223125747.png"
}
}
```
- 用途:上传截图后获取 URL,用于任务描述中的富文本内容
#### 3. 创建任务(核心功能)
- 创建任务接口:
- 接口:`POST /openclaw/task`
- 接口调用地址:`https://office.ubainsyun.com:5082/api/uerp/openclaw/task`
- X-Api-Key: 保持原有的key传入
- Content-Type: `application/json`
**必填参数(通过用户输入或选择):**
| 参数 | 类型 | 说明 | 数据来源 |
|------|------|------|----------|
| name | string | 任务名称(最长128字符) | 用户输入 |
| type_id | int | 任务类型 ID | 从任务类型列表中选择 |
| status_id | int | 任务状态 ID | 从任务状态列表中选择(默认1-新建) |
| level | int | 紧急程度(1-5 | 用户选择或输入 |
| excepthour | int | 期望工时(小时) | 用户输入 |
| excepttime | string | 截止时间 `YYYY-MM-DD HH:mm:ss` | 用户选择或输入 |
| content | string | 任务描述(HTML 格式) | 用户输入,支持富文本 |
| user_id | int | 主责任人 ID | 从人员列表中选择 |
| createuser_id | int | 下单人员 ID(创建人) | 从人员列表中选择 |
| develop_id | int | 关联的需求 ID | 从需求列表中选择 |
**可选参数:**
| 参数 | 类型 | 说明 | 数据来源 |
|------|------|------|----------|
| planhour | int | 预计工时(小时) | 用户输入,status_id > 1 时必填 |
| starttime | string | 开始时间 `YYYY-MM-DD HH:mm:ss` | 用户选择,status_id > 1 时必填 |
| endtime | string | 结束时间 `YYYY-MM-DD HH:mm:ss` | 用户选择,status_id > 1 时必填 |
| actualhour | int | 实际工时(小时) | 用户输入,默认 0 |
| project_id | int | 关联的项目 ID | 用户选择 |
| descript | string | 备注说明 | 用户输入 |
| process | int | 当前进度(0-100 | 用户输入,默认 0 |
| sort | int | 排序值 | 用户输入,默认 0 |
| copyuserList | array | 抄送人员 ID 列表 | 从人员列表中多选 |
**紧急程度(level)对照表:**
| level | 名称 | 说明 |
|-------|------|------|
| 1 | 每日观察 | 每日跟踪进度(最低优先级) |
| 2 | 非常紧急 | 需要立即处理 |
| 3 | 紧急处理 | 优先处理 |
| 4 | 常规处理 | 正常排期处理 |
| 5 | 迭代优化 | 后续版本处理(最高优先级) |
**请求示例:**
```bash
curl -X POST "https://office.ubainsyun.com:5082/api/uerp/openclaw/task" \
-H "X-Api-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "用户登录模块接口开发",
"type_id": 1,
"status_id": 1,
"level": 3,
"excepthour": 8,
"excepttime": "2026-06-25 18:00:00",
"content": "<h4>任务描述</h4><p>完成用户登录模块的后端接口开发</p><h4>技术要求</h4><ul><li>使用 RESTful API 规范</li><li>JWT token 认证</li><li>接口文档同步更新</li></ul><h4>参考截图</h4><p><img src=\"https://office.ubainsyun.com:5015/upload/20260617223125747.png\" alt=\"接口设计图\"/></p>",
"user_id": 20,
"createuser_id": 5,
"develop_id": 128,
"planhour": 6,
"project_id": 42,
"descript": "优先级较高的开发任务"
}'
```
**返回数据示例:**
```json
{
"success": 1,
"data": {
"create": 1024,
"name": "用户登录模块接口开发"
}
}
```
### 实现要求
1. **基础数据缓存**
- 获取任务类型、任务状态、人员列表后应进行缓存
- 避免每次创建任务都重复请求基础数据
- 建议缓存时长:1小时
2. **需求列表搜索**
- 支持通过 `search_str` 参数进行全局搜索
- 支持分页查询,默认每页显示20条
- 建议提供模糊搜索功能,提升用户体验
3. **用户交互流程**
- 第一步:获取并显示基础数据(任务类型、状态、人员)
- 第二步:用户搜索并选择关联需求
- 第三步:(可选)上传截图,获取图片 URL
- 第四步:填写任务信息并创建
4. **参数校验**
- 必填参数不能为空
- 时间格式必须为 `YYYY-MM-DD HH:mm:ss`
- 紧急程度必须在 1-5 之间
- 工时必须为正整数
5. **错误处理**
- API 调用失败时,显示明确的错误信息
- 网络异常时提供重试机制
- 参数错误时提示具体字段
6. **日志记录**
- 记录所有 API 调用日志
- 记录任务创建成功/失败信息
- 记录错误堆栈,便于问题排查
## 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论