Prechádzať zdrojové kódy

refactor: 优化接口测试控制器和服务实现,添加日志记录

weipengtao 6 mesiacov pred
rodič
commit
88e9d8d4fb

+ 24 - 23
web/src/main/java/cn/seecoder/web/controller/InterfaceTest/InterfaceTestController.java

@@ -2,69 +2,70 @@ package cn.seecoder.web.controller.InterfaceTest;
 
 import cn.seecoder.web.model.vo.InterfaceTest.InterfaceTestInfoVO;
 import cn.seecoder.web.model.vo.InterfaceTest.InterfaceTestResultVO;
+import cn.seecoder.web.model.vo.Response;
 import cn.seecoder.web.service.InterfaceTest.InterfaceTestService;
 import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiImplicitParam;
-import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
-import cn.seecoder.web.model.vo.Response;
 
 import java.io.IOException;
 import java.util.List;
 
+@Slf4j
 @RestController
 @RequestMapping("/interfaces")
+@RequiredArgsConstructor
 @Api(tags = "接口方法测试 API")
 public class InterfaceTestController {
 
     private final InterfaceTestService interfaceTestService;
 
-    @Autowired
-    public InterfaceTestController(InterfaceTestService interfaceTestService) {
-        this.interfaceTestService = interfaceTestService;
-    }
     /**
      * 创建一条接口方法测试
      */
     @PostMapping("/create")
     @ApiOperation(value = "创建一个方法测试", httpMethod = "POST")
-    public Response createInterfaceTest(@RequestBody InterfaceTestInfoVO interfaceTestInfoVO) {
+    public Response<Object> createInterfaceTest(@RequestBody InterfaceTestInfoVO interfaceTestInfoVO) {
+        log.info("创建接口测试, name={}, projectId={}, user={}",
+                interfaceTestInfoVO.getName(), interfaceTestInfoVO.getProjectId(), interfaceTestInfoVO.getUsername());
         interfaceTestService.createInterfaceTest(interfaceTestInfoVO);
         return Response.buildSuccess();
     }
+
     /**
      * 获取所有接口方法测试
      */
     @GetMapping("/list/{projectId}")
     @ApiOperation(value = "获取所有方法测试", httpMethod = "GET")
-    @ApiImplicitParam(name = "projectId", dataType = "int", paramType = "query")
     public Response<List<InterfaceTestInfoVO>> getInterfaceTestInfosByProjectId(@PathVariable("projectId") Integer projectId) {
-        return Response.buildSuccess(interfaceTestService.getInterfaceTestByProjectId(projectId));
+        log.info("查询项目接口测试列表, projectId={}", projectId);
+        List<InterfaceTestInfoVO> list = interfaceTestService.getInterfaceTestByProjectId(projectId);
+        log.debug("查询到接口测试 {} 条", list.size());
+        return Response.buildSuccess(list);
     }
+
     /**
      * 执行一条测试
      */
     @GetMapping("/execute/{id}/{username}/{type}")
     @ApiOperation(value = "执行一条方法测试", httpMethod = "GET")
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "id", dataType ="int", paramType = "query"),
-            @ApiImplicitParam(name = "username", dataType ="string", paramType = "query"),
-            @ApiImplicitParam(name = "type", dataType = "string", paramType = "query")
-    })
-    public Response executeInterfaceTestById(@PathVariable("id") Integer id, @PathVariable("username") String username, @PathVariable("type") String type) throws IOException {
+    public Response<Object> executeInterfaceTestById(@PathVariable("id") Integer id,
+                                                     @PathVariable("username") String username,
+                                                     @PathVariable("type") String type) throws IOException {
+        log.info("执行接口测试, id={}, username={}, type={}", id, username, type);
         interfaceTestService.executeInterfaceTestById(id, username, type);
         return Response.buildSuccess();
     }
+
     /**
      * 获取指定接口方法测试的所有执行结果(手动触发)
      */
-
     @GetMapping("/results/{id}")
     @ApiOperation(value = "获取一条方法测试的所有结果(手动触发)", httpMethod = "GET")
-    @ApiImplicitParam(name = "id", dataType = "int", paramType = "query")
     public Response<List<InterfaceTestResultVO>> getInterfaceResultsById(@PathVariable("id") Integer id) {
+        log.info("查询手动触发测试结果, testId={}", id);
         return Response.buildSuccess(interfaceTestService.getInterfaceTestResultsById(id));
     }
 
@@ -72,9 +73,9 @@ public class InterfaceTestController {
      * 删除一条接口方法测试
      */
     @GetMapping("/delete/{id}")
-    @ApiOperation(value = "删除指定方法测试", httpMethod = "DELETE")
-    @ApiImplicitParam(name = "id", dataType = "int", paramType = "query")
-    public Response deleteInterfaceInfoById(@PathVariable("id") Integer id) {
+    @ApiOperation(value = "删除指定方法测试", httpMethod = "GET")
+    public Response<Object> deleteInterfaceInfoById(@PathVariable("id") Integer id) {
+        log.info("删除接口测试, id={}", id);
         interfaceTestService.deleteInterfaceTestById(id);
         return Response.buildSuccess();
     }
@@ -84,8 +85,8 @@ public class InterfaceTestController {
      */
     @GetMapping("/results/auto/{id}")
     @ApiOperation(value = "获取一条方法测试的所有结果(自动化测试)", httpMethod = "GET")
-    @ApiImplicitParam(name = "id", dataType = "int", paramType = "query")
     public Response<List<InterfaceTestResultVO>> getAutoInterfaceResultsById(@PathVariable("id") Integer id) {
+        log.info("查询自动化测试结果, testId={}", id);
         return Response.buildSuccess(interfaceTestService.getAutoInterfaceTestResultsById(id));
     }
 

+ 23 - 12
web/src/main/java/cn/seecoder/web/dao/InterfaceTest/InterfaceTestMapper.java

@@ -3,42 +3,53 @@ package cn.seecoder.web.dao.InterfaceTest;
 import cn.seecoder.web.dao.provider.GeneralInsertUpdateSqlProvider;
 import cn.seecoder.web.model.po.InterfaceTest.InterfaceTestInfo;
 import cn.seecoder.web.model.po.InterfaceTest.InterfaceTestResult;
-import org.apache.ibatis.annotations.Delete;
-import org.apache.ibatis.annotations.InsertProvider;
-import org.apache.ibatis.annotations.Options;
-import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.*;
 import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
+/**
+ * 接口方法测试 Mapper
+ * 负责 interface_test_info 和 interface_test_result 两张表的数据访问
+ */
 @Repository
 public interface InterfaceTestMapper {
+
+    // ==================== interface_test_info ====================
+
+    /** 新增接口测试信息,返回自增主键 */
     @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
     @Options(useGeneratedKeys = true, keyProperty = "param1.id")
-//    boolean insert(InterfaceTestInfo interfaceTestInfo, String... ignoredCols);
     int insert(InterfaceTestInfo interfaceTestInfo, String... ignoredCols);
 
+    /** 根据项目ID查询所有接口测试 */
     @Select("select * from interface_test_info where project_id = #{id}")
     List<InterfaceTestInfo> selectByProjectId(Integer id);
 
+    /** 根据ID查询单条接口测试 */
+    @Select("select * from interface_test_info where id = #{id}")
+    InterfaceTestInfo selectById(Integer id);
+
+    /** 根据ID删除接口测试 */
     @Delete("delete from interface_test_info where id = #{id}")
     boolean deleteInterfaceTestById(Integer id);
 
-    @Select("select * from interface_test_info where id = #{id}")
-    InterfaceTestInfo selectById(Integer id);
+    // ==================== interface_test_result ====================
 
+    /** 新增接口测试执行结果 */
     @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
     @Options(useGeneratedKeys = true, keyProperty = "param1.id")
     boolean insertResult(InterfaceTestResult interfaceTestResult, String... ignoredCols);
 
-    // 获取某条测试手动触发测试的结果
+    /** 查询手动触发的测试结果(deployment_id = -1) */
     @Select("select * from interface_test_result where test_id = #{id} and deployment_id = -1")
     List<InterfaceTestResult> selectByTestId(Integer id);
 
-    @Delete("delete from interface_test_result where test_id = #{id}")
-    boolean deleteInterfaceResultsById(Integer id);
-
-    // 获取某条测试自动化测试的结果
+    /** 查询自动化测试的结果(deployment_id != -1) */
     @Select("select * from interface_test_result where test_id = #{id} and deployment_id != -1")
     List<InterfaceTestResult> selectAutoResultsByTestId(Integer id);
+
+    /** 根据测试ID删除所有执行结果 */
+    @Delete("delete from interface_test_result where test_id = #{id}")
+    boolean deleteInterfaceResultsById(Integer id);
 }

+ 241 - 157
web/src/main/java/cn/seecoder/web/service/impl/InterfaceTest/InterfaceTestServiceImpl.java

@@ -5,8 +5,6 @@ import cn.seecoder.common.util.OpType;
 import cn.seecoder.web.dao.InterfaceTest.InterfaceTestMapper;
 import cn.seecoder.web.dao.pipeline.PipelineMapper;
 import cn.seecoder.web.dao.pipeline.PipelineRecordMapper;
-import cn.seecoder.web.dao.user.UserMapper;
-import cn.seecoder.web.model.po.APITest.TestInfo;
 import cn.seecoder.web.model.po.InterfaceTest.InterfaceTestInfo;
 import cn.seecoder.web.model.po.InterfaceTest.InterfaceTestResult;
 import cn.seecoder.web.model.po.pipeline.PipelinePO;
@@ -16,6 +14,7 @@ import cn.seecoder.web.model.vo.InterfaceTest.InterfaceTestResultVO;
 import cn.seecoder.web.service.InterfaceTest.InterfaceTestService;
 import cn.seecoder.web.service.user.UserService;
 import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import org.apache.commons.lang.StringEscapeUtils;
@@ -29,20 +28,26 @@ import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 public class InterfaceTestServiceImpl implements InterfaceTestService {
 
-    private final InterfaceTestMapper interfaceTestMapper;
+    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    private static final int MANUAL_DEPLOYMENT_ID = -1;
+    private static final String TYPE_ARTIFICIAL = "artificial";
+    private static final String TYPE_COMPUTER = "computer";
+    private static final String METHOD_GET = "GET";
+    private static final String METHOD_POST = "POST";
 
+    private final InterfaceTestMapper interfaceTestMapper;
     private final PipelineMapper pipelineMapper;
-
     private final PipelineRecordMapper pipelineRecordMapper;
 
-    @Autowired
-    private UserMapper userMapper;
 
     @Autowired
-    public InterfaceTestServiceImpl(InterfaceTestMapper interfaceTestMapper, PipelineMapper pipelineMapper, PipelineRecordMapper pipelineRecordMapper) {
+    public InterfaceTestServiceImpl(InterfaceTestMapper interfaceTestMapper,
+                                    PipelineMapper pipelineMapper,
+                                    PipelineRecordMapper pipelineRecordMapper) {
         this.interfaceTestMapper = interfaceTestMapper;
         this.pipelineMapper = pipelineMapper;
         this.pipelineRecordMapper = pipelineRecordMapper;
@@ -50,9 +55,7 @@ public class InterfaceTestServiceImpl implements InterfaceTestService {
 
     @Override
     public void createInterfaceTest(InterfaceTestInfoVO interfaceTestInfoVO) {
-        Date d = new Date();
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        String createTime = sdf.format(d);
+        String createTime = formatNow();
         InterfaceTestInfo info = InterfaceTestInfo.builder()
                 .name(interfaceTestInfoVO.getName())
                 .projectId(interfaceTestInfoVO.getProjectId())
@@ -62,100 +65,79 @@ public class InterfaceTestServiceImpl implements InterfaceTestService {
                 .method(interfaceTestInfoVO.getMethod())
                 .params(JSONObject.fromObject(interfaceTestInfoVO.getParams()).toString())
                 .build();
-        int infoId = interfaceTestMapper.insert(info);
-        //ANA 日志需要打出测试信息和执行结果,具体信息在handleRequest方法内
-        try{
-            JSONObject object = new JSONObject();
-            object.put("test_name",interfaceTestInfoVO.getName());
-            object.put("project_id",interfaceTestInfoVO.getProjectId());
-            object.put("username",interfaceTestInfoVO.getUsername());
 
-            String data = com.alibaba.fastjson.JSONObject.toJSONString(object);
-            LogTrackingUtil.log(data,OpType.CREATE_TEST);
-        }catch (Exception e){
+        int infoId = interfaceTestMapper.insert(info);
+        log.info("创建接口测试成功, infoId={}, name={}, projectId={}",
+                infoId, info.getName(), info.getProjectId());
 
-        }
+        trackLog(interfaceTestInfoVO);
     }
 
     @Override
     public List<InterfaceTestInfoVO> getInterfaceTestByProjectId(Integer projectId) {
-        return interfaceTestMapper.selectByProjectId(projectId).stream()
-                .map(InterfaceTestInfoVO::new).collect(Collectors.toList());
+        List<InterfaceTestInfoVO> results = interfaceTestMapper.selectByProjectId(projectId).stream()
+                .map(InterfaceTestInfoVO::new)
+                .collect(Collectors.toList());
+        log.debug("查询项目[{}]接口测试, 共{}条", projectId, results.size());
+        return results;
     }
 
     @Override
     public void deleteInterfaceTestById(Integer id) {
+        log.info("删除接口测试, id={}", id);
         interfaceTestMapper.deleteInterfaceTestById(id);
         interfaceTestMapper.deleteInterfaceResultsById(id);
+        log.info("删除接口测试及其结果完成, id={}", id);
     }
 
     @Override
     public void executeInterfaceTestById(Integer id, String username, String type) throws IOException {
+        log.info("开始执行接口测试, id={}, username={}, type={}", id, username, type);
         InterfaceTestInfo info = interfaceTestMapper.selectById(id);
-        String url = info.getUrl();
-        Map<String, Object> params = parseJson2Map(info.getParams());
-        String method = info.getMethod();
-        Integer projectId = info.getProjectId();
-        handleRequest(url, params, id, method, username, type, projectId);
-        //ANA 日志需要打出测试信息和执行结果,具体信息在handleRequest方法内
+        if (info == null) {
+            log.warn("接口测试不存在, id={}", id);
+            return;
+        }
 
+        Map<String, Object> params = parseJson2Map(info.getParams());
+        handleRequest(info.getUrl(), params, id, info.getMethod(), username, type, info.getProjectId());
+        log.info("接口测试执行完成, id={}", id);
     }
 
     @Override
     public List<InterfaceTestResultVO> getInterfaceTestResultsById(Integer id) {
-        return interfaceTestMapper.selectByTestId(id).stream()
-                .map(InterfaceTestResultVO::new).collect(Collectors.toList());
+        List<InterfaceTestResultVO> results = interfaceTestMapper.selectByTestId(id).stream()
+                .map(InterfaceTestResultVO::new)
+                .collect(Collectors.toList());
+        log.debug("查询手动触发结果, testId={}, 共{}条", id, results.size());
+        return results;
     }
 
     @Override
     public List<InterfaceTestResultVO> getAutoInterfaceTestResultsById(Integer id) {
-        return interfaceTestMapper.selectAutoResultsByTestId(id).stream()
-                .map(InterfaceTestResultVO::new).collect(Collectors.toList());
+        List<InterfaceTestResultVO> results = interfaceTestMapper.selectAutoResultsByTestId(id).stream()
+                .map(InterfaceTestResultVO::new)
+                .collect(Collectors.toList());
+        log.debug("查询自动化测试结果, testId={}, 共{}条", id, results.size());
+        return results;
     }
 
-    public Map<String, Object> parseJson2Map(String str) {
-        Map<String, Object> map = new HashMap<>();
-        JSONObject json = JSONObject.fromObject(str);
-        for (Object k : json.keySet()) {
-            Object v = json.get(k);
-            if (v instanceof JSONArray) {
-                List<Map<String, Object>> list = new ArrayList<>();
-                for (JSONObject json2 : (Iterable<JSONObject>) v) {
-                    list.add(parseJson2Map(json2.toString()));
-                }
-                map.put(k.toString(), list);
-            } else {
-                map.put(k.toString(), v);
-            }
-        }
-        return map;
-    }
+    // ==================== 私有方法 ====================
+
+    /**
+     * 发送HTTP请求并保存结果
+     */
+    private void handleRequest(String url, Map<String, Object> params, int id,
+                               String method, String username, String type,
+                               Integer projectId) {
+        String[] res = executeHttpRequest(url, params, method);
+        log.debug("HTTP请求完成, url={}, method={}, code={}, cost={}ms", url, method, res[0], res[1]);
+
+        int deploymentId = resolveDeploymentId(type, projectId);
 
-    public void handleRequest(String url, Map<String, Object> params, int id, String method, String username, String type, Integer projectId) throws IOException {
-        String[] res = null;
-        if (method.equals("GET")) {
-            res = doGet(url, params);
-        } else if (method.equals("POST")) {
-            res = doPost(url, params);
-        }
-        for (String _res : res) {
-            System.out.println(_res);
-        }
-        Date d = new Date();
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        String executeTime = sdf.format(d);
-        int deploymentId = -111;
-        if (type.equals("artificial")) {
-            deploymentId = -1;
-        } else if (type.equals("computer")) {
-            // 获取最新的部署构建信息id
-            List<Integer> pipelineIds = pipelineMapper.selectByProjectId(projectId).stream().map(PipelinePO::getId).collect(Collectors.toList());
-            PipelineRecordPO latestRecord = pipelineRecordMapper.selectPipelinesLatestRecord(pipelineIds);
-            deploymentId = latestRecord.getId();
-        }
         InterfaceTestResult result = InterfaceTestResult.builder()
                 .testId(id)
-                .executeTime(executeTime)
+                .executeTime(formatNow())
                 .username(username)
                 .code(Integer.parseInt(res[0]))
                 .cost(Integer.parseInt(res[1]))
@@ -163,126 +145,228 @@ public class InterfaceTestServiceImpl implements InterfaceTestService {
                 .deploymentId(deploymentId)
                 .build();
         interfaceTestMapper.insertResult(result);
+        log.info("测试结果已保存, testId={}, code={}, cost={}ms, deploymentId={}", id, res[0], res[1], deploymentId);
 
-        //ANA 日志需要打出测试信息和执行结果
-        try{
-            JSONObject object = new JSONObject();
-            object.put("apiTest_id",id);
-            object.put("project_id",projectId);
-            object.put("user_id", UserService.loginUser().getId());
-            object.put("result",res[2]);
-//            object.put("failure",failure[0]);
-
-            String data = com.alibaba.fastjson.JSONObject.toJSONString(object);
-            LogTrackingUtil.log(data, OpType.INTERFACE_TEST);
+        trackTestExecutionLog(id, projectId, res[2]);
+    }
 
-        }catch(Exception e){}
+    /**
+     * 根据method分发HTTP请求
+     */
+    private String[] executeHttpRequest(String url, Map<String, Object> params, String method) {
+        if (METHOD_GET.equalsIgnoreCase(method)) {
+            return doGet(url, params);
+        } else if (METHOD_POST.equalsIgnoreCase(method)) {
+            return doPost(url, params);
+        }
+        log.warn("不支持的HTTP方法: {}", method);
+        return new String[]{"0", "0", "Unsupported method: " + method};
     }
 
-    public String[] doGet(String url, Map<String, Object> params) {
-        HttpURLConnection connection;
-        BufferedReader br = null;
-        StringBuilder sb = new StringBuilder();
-        StringBuilder result = new StringBuilder();
-        String path = "";
+    /**
+     * 解析部署ID:手动触发为-1,自动化则取最新流水线记录ID
+     */
+    private int resolveDeploymentId(String type, Integer projectId) {
+        if (TYPE_ARTIFICIAL.equals(type)) {
+            return MANUAL_DEPLOYMENT_ID;
+        }
+        if (TYPE_COMPUTER.equals(type)) {
+            List<Integer> pipelineIds = pipelineMapper.selectByProjectId(projectId).stream()
+                    .map(PipelinePO::getId)
+                    .collect(Collectors.toList());
+            PipelineRecordPO latestRecord = pipelineRecordMapper.selectPipelinesLatestRecord(pipelineIds);
+            if (latestRecord != null) {
+                return latestRecord.getId();
+            }
+            log.warn("未找到流水线最新记录, projectId={}", projectId);
+        }
+        return MANUAL_DEPLOYMENT_ID;
+    }
 
+    /**
+     * 发送GET请求
+     *
+     * @return [状态码, 耗时ms, 响应体]
+     */
+    private String[] doGet(String url, Map<String, Object> params) {
         String[] res = new String[3];
         long start = System.currentTimeMillis();
-        try {
-            if (params.size() == 1) {
-                for (String name : params.keySet()) {
-                    sb.append(name).append("=").append(URLEncoder.encode(String.valueOf(params.get(name)), "utf-8"));
-                }
-                path = sb.toString();
-            } else if (params.size() >= 2) {
-                for (String name : params.keySet()) {
-                    sb.append(name).append("=").append(URLEncoder.encode(String.valueOf(params.get(name)), "utf-8")).append("&");
-                }
-                path = sb.deleteCharAt(sb.length() - 1).toString();
-            }
-            String full_url = url;
-            if (!path.equals("")) {
-                full_url = url + "?" + path;
-            }
+        BufferedReader br = null;
 
-            URL connURL = new URL(full_url);
+        try {
+            String fullUrl = buildGetUrl(url, params);
+            log.debug("GET请求: {}", fullUrl);
 
-            connection = (HttpURLConnection) connURL.openConnection();
-            // 设置一些请求头
+            URL connURL = new URL(fullUrl);
+            HttpURLConnection connection = (HttpURLConnection) connURL.openConnection();
             connection.setRequestProperty("Accept", "*/*");
             connection.setRequestProperty("Connection", "Keep-Alive");
             connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
-
-            // 建立连接
             connection.connect();
-            res[0] = connection.getResponseCode() + "";
+
+            res[0] = String.valueOf(connection.getResponseCode());
+
             br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
+            StringBuilder result = new StringBuilder();
             String line;
             while ((line = br.readLine()) != null) {
                 result.append(line);
             }
+            if (result.length() > 0) {
+                res[2] = StringEscapeUtils.unescapeJava(result.toString());
+            }
         } catch (Exception e) {
-            res[2] = e + "";
-            e.printStackTrace();
+            log.error("GET请求异常, url={}", url, e);
+            res[2] = e.toString();
         } finally {
-            try {
-                if (br != null) {
-                    br.close();
-                }
-            } catch (IOException ex) {
-                ex.printStackTrace();
-            }
-        }
-        long end = System.currentTimeMillis();
-        res[1] = (end - start) + "";
-        if (result.length() != 0) {
-            res[2] = StringEscapeUtils.unescapeJava(result.toString());
+            closeQuietly(br);
         }
+
+        long cost = System.currentTimeMillis() - start;
+        res[1] = String.valueOf(cost);
         return res;
     }
 
-    public String[] doPost(String url, Map<String, Object> params) throws IOException {
-        Gson gson = new Gson();
-        String param = gson.toJson(params);
-        StringBuilder response = new StringBuilder();
-        long start = System.currentTimeMillis();
+    /**
+     * 发送POST请求
+     *
+     * @return [状态码, 耗时ms, 响应体]
+     */
+    private String[] doPost(String url, Map<String, Object> params) {
         String[] res = new String[3];
-        System.out.println(param);
+        String param = new Gson().toJson(params);
+        log.debug("POST请求: url={}, body={}", url, param);
+
+        long start = System.currentTimeMillis();
         try {
             URL realUrl = new URL(url);
-            // 打开和URL之间的连接
             HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
-            // 设置通用的请求属性
             connection.setDoOutput(true);
             connection.setDoInput(true);
             connection.setUseCaches(false);
             connection.setInstanceFollowRedirects(true);
-            connection.setRequestMethod("POST"); // 设置请求方式
-            connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
-            connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
+            connection.setRequestMethod("POST");
+            connection.setRequestProperty("Accept", "application/json");
+            connection.setRequestProperty("Content-Type", "application/json");
             connection.connect();
-            // 获取URLConnection对象对应的输出流
-            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); // utf-8编码
-            out.append(param);
-            // flush输出流的缓冲
-            out.flush();
-            out.close();
-            // 定义BufferedReader输入流来读取URL的响应
-            BufferedReader reader = null;
-            reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
-            String line;
-            while ((line = reader.readLine()) != null) {
-                response.append(line);
+
+            try (OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8)) {
+                out.write(param);
+                out.flush();
+            }
+
+            StringBuilder response = new StringBuilder();
+            try (BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    response.append(line);
+                }
             }
-            reader.close();
-            res[0] = connection.getResponseCode() + "";
+
+            res[0] = String.valueOf(connection.getResponseCode());
+            res[2] = StringEscapeUtils.unescapeJava(response.toString());
         } catch (Exception e) {
-            System.out.println("发送POST请求出现异常!" + e);
-            e.printStackTrace();
+            log.error("POST请求异常, url={}", url, e);
+            res[2] = e.toString();
         }
-        long end = System.currentTimeMillis();
-        res[1] = (end - start) + "";
-        res[2] = StringEscapeUtils.unescapeJava(response.toString());
+
+        long cost = System.currentTimeMillis() - start;
+        res[1] = String.valueOf(cost);
         return res;
     }
+
+    /**
+     * 拼接GET请求URL(含query参数)
+     */
+    private String buildGetUrl(String url, Map<String, Object> params) throws UnsupportedEncodingException {
+        if (params == null || params.isEmpty()) {
+            return url;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append(entry.getKey())
+              .append("=")
+              .append(URLEncoder.encode(String.valueOf(entry.getValue()), "utf-8"));
+        }
+        return url + "?" + sb;
+    }
+
+    /**
+     * 将JSON字符串递归解析为Map
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> parseJson2Map(String str) {
+        Map<String, Object> map = new HashMap<>();
+        JSONObject json = JSONObject.fromObject(str);
+        for (Object k : json.keySet()) {
+            Object v = json.get(k);
+            if (v instanceof JSONArray) {
+                List<Map<String, Object>> list = new ArrayList<>();
+                for (JSONObject json2 : (Iterable<JSONObject>) v) {
+                    list.add(parseJson2Map(json2.toString()));
+                }
+                map.put(k.toString(), list);
+            } else {
+                map.put(k.toString(), v);
+            }
+        }
+        return map;
+    }
+
+    /**
+     * 格式化当前时间
+     */
+    private String formatNow() {
+        return new SimpleDateFormat(DATE_FORMAT).format(new Date());
+    }
+
+    /**
+     * 安全关闭流
+     */
+    private void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                log.warn("关闭流异常", e);
+            }
+        }
+    }
+
+    /**
+     * 记录创建测试的追踪日志
+     */
+    private void trackLog(InterfaceTestInfoVO vo) {
+        try {
+            JSONObject object = new JSONObject();
+            object.put("test_name", vo.getName());
+            object.put("project_id", vo.getProjectId());
+            object.put("username", vo.getUsername());
+            String data = com.alibaba.fastjson.JSONObject.toJSONString(object);
+            LogTrackingUtil.log(data, OpType.CREATE_TEST);
+        } catch (Exception e) {
+            log.warn("记录创建测试追踪日志失败", e);
+        }
+    }
+
+    /**
+     * 记录执行测试的追踪日志
+     */
+    private void trackTestExecutionLog(int testId, Integer projectId, String result) {
+        try {
+            JSONObject object = new JSONObject();
+            object.put("apiTest_id", testId);
+            object.put("project_id", projectId);
+            object.put("user_id", UserService.loginUser().getId());
+            object.put("result", result);
+            String data = com.alibaba.fastjson.JSONObject.toJSONString(object);
+            LogTrackingUtil.log(data, OpType.INTERFACE_TEST);
+        } catch (Exception e) {
+            log.warn("记录执行测试追踪日志失败", e);
+        }
+    }
 }