diff --git a/src/main/java/net/educoder/ecsonar/controller/GraduationProjectController.java b/src/main/java/net/educoder/ecsonar/controller/GraduationProjectController.java index c37e9e8..89e2822 100644 --- a/src/main/java/net/educoder/ecsonar/controller/GraduationProjectController.java +++ b/src/main/java/net/educoder/ecsonar/controller/GraduationProjectController.java @@ -1,13 +1,14 @@ package net.educoder.ecsonar.controller; +import net.educoder.ecsonar.model.RollPage; import net.educoder.ecsonar.model.api.QualityInspect; +import net.educoder.ecsonar.model.api.QualityInspectIsCompleted; +import net.educoder.ecsonar.model.api.QualityInspectResultData; import net.educoder.ecsonar.model.vo.GraduationProjectQualityInspectVO; +import net.educoder.ecsonar.model.vo.GraduationQualityInspectResultQueryVO; import net.educoder.ecsonar.services.GraduationProjectQualityInspectService; import net.educoder.ecsonar.utils.ResponseResult; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; @@ -15,7 +16,7 @@ import java.util.List; /** * @Author: youys * @Date: 2023/8/30 - * @Description: + * @Description: 毕设质量分析接口控制器 */ @RestController @@ -24,15 +25,38 @@ public class GraduationProjectController { @Resource - private GraduationProjectQualityInspectService qualityInspectService; + private GraduationProjectQualityInspectService graduationProjectQualityInspect; - @GetMapping("/qualityInspect") - public ResponseResult qualityInspect(@RequestBody List qualityInspectVOList){ - QualityInspect qualityInspect = qualityInspectService.graduationProjectQualityInspect(qualityInspectVOList); + /** + * 质量检测 + * @param qualityInspectVOList + * @return + */ + @PostMapping("/qualityInspect") + public ResponseResult graduationQualityInspect(@RequestBody List qualityInspectVOList){ + QualityInspect qualityInspect = graduationProjectQualityInspect.graduationProjectQualityInspect(qualityInspectVOList); return ResponseResult.success(qualityInspect); } + /** + * 质量检测结果查询 + * + * @param graduationQualityInspectResultQueryVO + * @return + */ + @RequestMapping(value = "qualityInspectResultQuery", method = RequestMethod.GET) + @ResponseBody + public ResponseResult graduationQualityInspectResultQuery(@RequestBody GraduationQualityInspectResultQueryVO graduationQualityInspectResultQueryVO) { + QualityInspectIsCompleted isCompleted = graduationProjectQualityInspect.qualityInspectIsCompleted(graduationQualityInspectResultQueryVO.getTaskId()); + if (isCompleted.getCompleted() != 1) { + return ResponseResult.error("质量检测正在处理中"); + } + + RollPage result = graduationProjectQualityInspect.qualityInspectResultQuery(graduationQualityInspectResultQueryVO); + return ResponseResult.success(result); + } + } diff --git a/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java b/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java index 6d1e32b..8f1dd16 100644 --- a/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java +++ b/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java @@ -5,7 +5,7 @@ import java.math.BigDecimal; /** * @Author: youys * @Date: 2022/1/17 - * @Description: + * @Description: 质量分析展示列表 */ public class QualityInspectResultData { diff --git a/src/main/java/net/educoder/ecsonar/model/vo/GraduationQualityInspectResultQueryVO.java b/src/main/java/net/educoder/ecsonar/model/vo/GraduationQualityInspectResultQueryVO.java new file mode 100644 index 0000000..af9a305 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/vo/GraduationQualityInspectResultQueryVO.java @@ -0,0 +1,38 @@ +package net.educoder.ecsonar.model.vo; + +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; + +/** + * @Author: youys + * @Date: 2023/9/1 + * @Description: 详细列表查询 + */ + +@Data +public class GraduationQualityInspectResultQueryVO { + + + private Integer pageSize; + @Min(value = 1, message = "起始页最少是1") + private Integer pageNumber; + + @NotBlank(message = "任务id不能为空") + private String taskId; + + @Min(value = 1, message = "最小值为1") + @Max(value = 100, message = "最大值为100") + private Integer bugRate; + + + @Min(value = 1, message = "最小值为1") + @Max(value = 100, message = "最大值为100") + private Integer vulnerabilityRate; + + @Min(value = 1, message = "最小值为1") + @Max(value = 100, message = "最大值为100") + private Integer codeSmallRate; +} diff --git a/src/main/java/net/educoder/ecsonar/services/DbOperateService.java b/src/main/java/net/educoder/ecsonar/services/DbOperateService.java index d8b7fd4..f699f13 100644 --- a/src/main/java/net/educoder/ecsonar/services/DbOperateService.java +++ b/src/main/java/net/educoder/ecsonar/services/DbOperateService.java @@ -4,10 +4,13 @@ import net.educoder.ecsonar.config.DynamicDataSourceConfig; import net.educoder.ecsonar.config.DynamicDataSourceContextHolder; import net.educoder.ecsonar.dao.*; import net.educoder.ecsonar.model.GameCodes; +import net.educoder.ecsonar.model.GraduationProjectTaskInfoDetail; import net.educoder.ecsonar.model.Project; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.annotation.Resource; + /** * @Author: youys * @Date: 2021/12/31 @@ -32,7 +35,8 @@ public class DbOperateService { @Autowired private CloudTaskInfoDetailDao cloudTaskInfoDetailDao; - private GraduationProjectTaskInfoDao graduationProjectTaskInfoDao; + @Resource + private GraduationProjectTaskInfoDetailDao graduationProjectTaskInfoDetailDao; /** * 根据id查询用户传的code @@ -67,7 +71,7 @@ public class DbOperateService { } public void updateGraduationProjectTaskInfoDetail(String id, Integer status) { - graduationProjectTaskInfoDao.updateTaskInfoStatus(id, status); + graduationProjectTaskInfoDetailDao.updateGraduationProjectTaskInfoDetailStatus(id, status); } public void updateCloudTaskInfoDetail(String id, Integer status) { diff --git a/src/main/java/net/educoder/ecsonar/services/GraduationProjectQualityInspectService.java b/src/main/java/net/educoder/ecsonar/services/GraduationProjectQualityInspectService.java index 7826f97..247bcaf 100644 --- a/src/main/java/net/educoder/ecsonar/services/GraduationProjectQualityInspectService.java +++ b/src/main/java/net/educoder/ecsonar/services/GraduationProjectQualityInspectService.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.RandomUtil; import net.educoder.ecsonar.dao.*; import net.educoder.ecsonar.exception.BusinessException; import net.educoder.ecsonar.model.*; +import net.educoder.ecsonar.model.api.Quality; import net.educoder.ecsonar.model.api.QualityInspect; import net.educoder.ecsonar.model.api.QualityInspectIsCompleted; import net.educoder.ecsonar.model.api.QualityInspectResultData; @@ -11,6 +12,7 @@ import net.educoder.ecsonar.model.dto.*; import net.educoder.ecsonar.model.vo.*; import net.educoder.ecsonar.task.GraduationProjectQualityInspectRunnable; import net.educoder.ecsonar.utils.IdUtils; +import net.educoder.ecsonar.utils.SystemUtil; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +21,8 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; @@ -33,10 +37,10 @@ public class GraduationProjectQualityInspectService { private static final Logger LOGGER = LoggerFactory.getLogger(GraduationProjectQualityInspectService.class); @Autowired - private GraduationProjectTaskInfoDao taskInfoDao; + private GraduationProjectTaskInfoDao graduationProjectTaskInfoDao; @Autowired - private GraduationProjectTaskInfoDetailDao taskInfoDetailDao; + private GraduationProjectTaskInfoDetailDao graduationProjectTaskInfoDetailDao; @Autowired private DbOperateService dbOperateService; @@ -77,7 +81,7 @@ public class GraduationProjectQualityInspectService { taskInfo.setId(IdUtils.nextStrId()); taskInfo.setPeopleNumber(qualityInspectVOList.size()); - taskInfoDao.insertTaskInfo(taskInfo); + graduationProjectTaskInfoDao.insertTaskInfo(taskInfo); LOGGER.info("taskId:{}, {}个人提交了代码", taskInfo.getId(), qualityInspectVOList.size()); for (GraduationProjectQualityInspectVO qualityInspectVO : qualityInspectVOList) { @@ -92,7 +96,7 @@ public class GraduationProjectQualityInspectService { graduationProjectTaskInfoDetail.setGitUrl(qualityInspectVO.getGitUrl()); graduationProjectTaskInfoDetail.setLanguage(qualityInspectVO.getLanguage()); - taskInfoDetailDao.insertTaskInfoDetail(graduationProjectTaskInfoDetail); + graduationProjectTaskInfoDetailDao.insertTaskInfoDetail(graduationProjectTaskInfoDetail); // 提交一个sonar扫描任务 GraduationProjectQualityInspectRunnable runnable = new GraduationProjectQualityInspectRunnable(taskInfo.getId(), @@ -108,31 +112,147 @@ public class GraduationProjectQualityInspectService { } - public RollPage qualityInspectResultQuery(Integer pageNum, Integer pageSize, String taskId) { - RollPage rollPage = new RollPage(); + public RollPage qualityInspectResultQuery(GraduationQualityInspectResultQueryVO queryVO) { + int pageSize = queryVO.getPageSize(); + int pageNum = queryVO.getPageNumber(); + + RollPage rollPage = new RollPage<>(); rollPage.setCurrentPage(pageNum); rollPage.setPageSize(pageSize); + int offset = (queryVO.getPageNumber() - 1) * queryVO.getPageSize(); + Integer totalCount = graduationProjectTaskInfoDetailDao. + selectGraduationProjectTaskInfoDetailPageCount(queryVO.getTaskId(), pageSize, offset); + rollPage.setRecordSum(totalCount); + + + if (totalCount == null || totalCount == 0) { + rollPage.setRecordList(new ArrayList<>()); + } else { + List taskInfoDetails = graduationProjectTaskInfoDetailDao. + selectGraduationProjectTaskInfoDetailPageList(queryVO.getTaskId(), pageSize, offset); + + List resultDataList = new ArrayList<>(taskInfoDetails.size()); + rollPage.setRecordList(resultDataList); + + // 组装数据 + for (GraduationProjectTaskInfoDetail taskInfoDetail : taskInfoDetails) { + QualityInspectResultData resultData = new QualityInspectResultData(); + resultData.setName(taskInfoDetail.getName()); + resultData.setStudentId(taskInfoDetail.getStudentId()); + resultData.setUserId(taskInfoDetail.getUserId()); + + Metrics metrics = reportService.getMetrics(taskInfoDetail.getProjectName()); + + resultData.setDiscern(!metrics.getBugs().equals("-")); + resultData.setBug(new Quality(metrics.getBlock_bugs(), metrics.getMajor_bugs(), metrics.getMinor_bugs(), metrics.getCritical_bugs(), metrics.getBugs())); + resultData.setVulnerability(new Quality(metrics.getBlock_violations(), metrics.getMajor_violations(), metrics.getMinor_violations(), metrics.getCritical_violations(), metrics.getViolations())); + resultData.setCodeSmall(new Quality(metrics.getBlock_code_smells(), metrics.getMajor_code_smells(), metrics.getMinor_code_smells(), metrics.getCritical_code_smells(), metrics.getCode_smells())); + + resultData.setComplexity(metrics.getComplexity()); + resultData.setTotalRowNumber(metrics.getLines()); + resultDataList.add(resultData); + } + + int maxBugNumber = resultDataList.stream().mapToInt((QualityInspectResultData resultData) -> { + Quality bug = resultData.getBug(); + return bug.getMinor() + bug.getBlocker() + bug.getMajor() + bug.getCritical(); + }).max().getAsInt(); + + int maxVulnerabilityNumber = resultDataList.stream().mapToInt((QualityInspectResultData resultData) -> { + Quality vulnerability = resultData.getVulnerability(); + return vulnerability.getMinor() + vulnerability.getBlocker() + vulnerability.getMajor() + vulnerability.getCritical(); + }).max().getAsInt(); + + int maxCodeSmallNumber = resultDataList.stream().mapToInt((QualityInspectResultData resultData) -> { + Quality codeSmall = resultData.getCodeSmall(); + return codeSmall.getMinor() + codeSmall.getBlocker() + codeSmall.getMajor() + codeSmall.getCritical(); + }).max().getAsInt(); + + + // 计算得分 + for (QualityInspectResultData resultData : resultDataList) { + BigDecimal finalScore = calcQualityScore(resultData, queryVO.getBugRate(), maxBugNumber, + queryVO.getVulnerabilityRate(), maxVulnerabilityNumber, + queryVO.getCodeSmallRate(), maxCodeSmallNumber); + resultData.setQualityScore(finalScore); + } + } return rollPage; } public QualityInspectIsCompleted qualityInspectIsCompleted(String taskId) { - return null; + GraduationProjectTaskInfo graduationProjectTaskInfo = graduationProjectTaskInfoDao.selectById(taskId); + if (graduationProjectTaskInfo == null) { + throw new BusinessException(2, "任务id不存在"); + } + + if (graduationProjectTaskInfo.getStatus() == 1) { + return new QualityInspectIsCompleted(taskId, graduationProjectTaskInfo.getStatus()); + } + + Integer taskNumber = graduationProjectTaskInfoDetailDao.selectCountByTaskId(graduationProjectTaskInfo.getId()); + int completed = graduationProjectTaskInfo.getPeopleNumber().equals(taskNumber) ? 1 : 0; + + // 数据库未更新状态,但sonar任务已经全部执行完,更新task_info状态 + if (graduationProjectTaskInfo.getStatus() == 0 && completed == 1) { + graduationProjectTaskInfoDao.updateTaskInfoStatus(graduationProjectTaskInfo.getId(), 1); + + // 并且删除文件 /tmp/$taskId + String command = String.format("rm -rf /tmp/%s", taskId); + SystemUtil.executeAndGetExitStatus(command); + } + + return new QualityInspectIsCompleted(taskId, completed); } /** - * 阻断 严重 主要 次要 - * 10 5 3 1 + * 代码质量分 =50*(x-a)/x + 30*(y-b)/y + 20*(z-c)/c + * 1、缺陷最大值为x,实际值为a。 + * 2、漏洞最大值为y,实际值为b。 + * 3、代码规范性最大值为z,实际值为c。 + * (具体占比需要根据老师配置的比例进行计算) * - * @param resultData - * @return + * @param resultData 用户数据 + * @param bugRate bug得分占比率 + * @param maxBugNumber 最大bug数 + * @param vulnerabilityRate 漏洞得分占比率 + * @param maxVulnerabilityNumber 最大漏洞数量 + * @param codeSmallRate 代码规范得分占比 + * @param maxCodeSmallNumber 最大代码规范数量 + * @return score */ - private BigDecimal calcQualityScore(QualityInspectResultData resultData) { + private BigDecimal calcQualityScore(QualityInspectResultData resultData, + int bugRate, + int maxBugNumber, + int vulnerabilityRate, + int maxVulnerabilityNumber, + int codeSmallRate, + int maxCodeSmallNumber) { + if (resultData.getTotalRowNumber() <= 0) { + return BigDecimal.ZERO; + } - return null; + Quality bug = resultData.getBug(); + BigDecimal bugScore = BigDecimal.valueOf(bugRate) + .multiply(BigDecimal.valueOf(maxBugNumber - (bug.getBlocker() + bug.getCritical() + bug.getMajor() + bug.getMinor()))) + .divide(BigDecimal.valueOf(maxBugNumber), 2, RoundingMode.HALF_UP); + + Quality vulnerability = resultData.getVulnerability(); + BigDecimal vulnerabilityScore = BigDecimal.valueOf(vulnerabilityRate) + .multiply(BigDecimal.valueOf(maxVulnerabilityNumber - (vulnerability.getBlocker() + vulnerability.getCritical() + vulnerability.getMajor() + vulnerability.getMinor()))) + .divide(BigDecimal.valueOf(maxVulnerabilityNumber), 2, RoundingMode.HALF_UP); + + Quality codeSmall = resultData.getCodeSmall(); + BigDecimal codeSmallScore = BigDecimal.valueOf(codeSmallRate) + .multiply(BigDecimal.valueOf(maxCodeSmallNumber - (codeSmall.getBlocker() + codeSmall.getCritical() + codeSmall.getMajor() + codeSmall.getMinor()))) + .divide(BigDecimal.valueOf(maxCodeSmallNumber), 2, RoundingMode.HALF_UP); + + BigDecimal score = bugScore.add(vulnerabilityScore).add(codeSmallScore); + return score; } diff --git a/src/main/java/net/educoder/ecsonar/task/GraduationProjectQualityInspectRunnable.java b/src/main/java/net/educoder/ecsonar/task/GraduationProjectQualityInspectRunnable.java index 636373b..4005be4 100644 --- a/src/main/java/net/educoder/ecsonar/task/GraduationProjectQualityInspectRunnable.java +++ b/src/main/java/net/educoder/ecsonar/task/GraduationProjectQualityInspectRunnable.java @@ -1,10 +1,13 @@ package net.educoder.ecsonar.task; +import com.alibaba.fastjson.JSONObject; import net.educoder.ecsonar.constant.Constant; import net.educoder.ecsonar.model.GraduationProjectTaskInfoDetail; import net.educoder.ecsonar.model.SonarScannerParam; import net.educoder.ecsonar.services.DbOperateService; import net.educoder.ecsonar.services.SonarService; +import net.educoder.ecsonar.utils.SystemUtil; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +53,13 @@ public class GraduationProjectQualityInspectRunnable implements Runnable { String projectName = taskInfoDetail.getProjectName(); String path = String.format("/tmp/%s/%s/", taskId, projectName); + // 需要先git clone 到某个目录下面 + SystemUtil.ExecuteResp executeResp = gitClone(path, taskInfoDetail.getGitUrl()); + if (executeResp.getStatus() != 0) { + LOGGER.warn("projectName:{} git clone失败", taskInfoDetail.getProjectName()); + return; + } + // 写完所有文件开始用sonar进行质量分析 SonarScannerParam param = new SonarScannerParam(projectName, path); if (Constant.C.equalsIgnoreCase(language) || Constant.CXX.equalsIgnoreCase(language)) { @@ -89,4 +99,22 @@ public class GraduationProjectQualityInspectRunnable implements Runnable { public void setSonarService(SonarService sonarService) { this.sonarService = sonarService; } + + /** + * 克隆代码 + * + * @param path + * @param gitUrl + * @return + */ + private SystemUtil.ExecuteResp gitClone(String path, String gitUrl) { + + String command = StringUtils.join("rm -rf ", path, " && mkdir -p ", path, " && cd ", path, " && git clone ", gitUrl); + + LOGGER.info("git clone:command:{}", command); + SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus(command); + + LOGGER.info("git clone结果:{}", JSONObject.toJSONString(executeResp)); + return executeResp; + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8a72a76..5a876a8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,9 +6,9 @@ spring.datasource.initSize=20 #spring.datasource.master.url=jdbc:postgresql://127.0.0.1:5432/sonar7.7 -spring.datasource.master.url=jdbc:postgresql://117.50.14.123:5432/sonar -spring.datasource.master.username=sonar -spring.datasource.master.password=sonar +spring.datasource.master.url=jdbc:postgresql://121.40.224.66:45432/sonar +spring.datasource.master.username=postgres +spring.datasource.master.password=Edu_postgresql spring.datasource.master.driverClassName=org.postgresql.Driver @@ -43,9 +43,9 @@ zip.save.path=/tmp/ #excel.template.path=/Users/guange/work/java/ecsonar/src/main/resources/template.xlsx excel.template.path=template.xlsx -sonar.url=http://127.0.0.1:9000 -#sonar.url=http://117.50.14.123:9000 -# token令牌 +#sonar.url=http://127.0.0.1:9000 +sonar.url=http://121.40.224.66:49000 +# token sonar.token=0253a518e824a976ea2f11aec17938cb0f8c0495 extract.path=/usr/local/bin/unar