diff --git a/pom.xml b/pom.xml index a68dba9..acabb25 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,18 @@ druid-spring-boot-starter 1.1.9 + + cn.hutool + hutool-core + 5.7.19 + + + + com.alibaba + fastjson + 1.2.79 + + mysql diff --git a/src/main/java/net/educoder/ecsonar/EcsonarApplication.java b/src/main/java/net/educoder/ecsonar/EcsonarApplication.java index 2a74104..4554952 100644 --- a/src/main/java/net/educoder/ecsonar/EcsonarApplication.java +++ b/src/main/java/net/educoder/ecsonar/EcsonarApplication.java @@ -2,8 +2,9 @@ package net.educoder.ecsonar; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -@SpringBootApplication +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) public class EcsonarApplication { public static void main(String[] args) { diff --git a/src/main/java/net/educoder/ecsonar/config/CustomExceptionHandler.java b/src/main/java/net/educoder/ecsonar/config/CustomExceptionHandler.java new file mode 100644 index 0000000..ee50b5b --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/config/CustomExceptionHandler.java @@ -0,0 +1,109 @@ +package net.educoder.ecsonar.config; + +import net.educoder.ecsonar.exception.BusinessException; +import net.educoder.ecsonar.utils.ResponseResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: 统一异常处理 + */ +@ControllerAdvice +public class CustomExceptionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(CustomExceptionHandler.class); + /** + * 处理Exception异常 + * + * @param request + * @param e + * @return + */ + @ExceptionHandler(Exception.class) + @ResponseBody + public ResponseResult handleException(HttpServletRequest request, Exception e) { + LOGGER.error("接口:{},Exception异常:" + e.getMessage(), request.getRequestURI(), e); + return ResponseResult.error("系统出问题了"); + } + + /** + * 处理BindException异常 + * + * @param request + * @param be + * @return + */ + @ExceptionHandler(BindException.class) + @ResponseBody + public ResponseResult handleException(HttpServletRequest request, BindException be) { + LOGGER.error("接口:{},BindException异常:" + be.getMessage(), request.getRequestURI()); + return ResponseResult.error("参数错误"); + } + + /** + * 处理BusinessException异常 + * + * @param request + * @param e + * @return + */ + @ExceptionHandler(BusinessException.class) + @ResponseBody + public ResponseResult handleException(HttpServletRequest request, BusinessException e) { + LOGGER.error("接口:"+request.getRequestURI()+",BusinessException异常:",e); + return ResponseResult.error(e.getMessage()); + } + + /** + * 处理 MaxUploadSizeExceededException 异常 + * + * @param request + * @param e + * @return + */ + @ExceptionHandler(MaxUploadSizeExceededException.class) + @ResponseBody + public ResponseResult handleException(HttpServletRequest request, MaxUploadSizeExceededException e) { + LOGGER.error("接口:{},文件上传超过最大设置的值:" + e.getMessage(), request.getRequestURI()); + return ResponseResult.error("文件上传超过最大设置的值"); + } + + /** + * 405,请求方式错误 + * + * @param ex + * @param request + * @return + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + @ResponseBody + public ResponseResult handleHttpRequestMethodNotSupported(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) { + LOGGER.error("接口:{},请求方式错误405:" + ex.getMessage(), request.getRequestURI(), ex); + return ResponseResult.error("请求方式错误405"); + } + + /** + * 缺少必填参数错误 + * + * @param ex + * @param request + * @return + */ + @ExceptionHandler(MissingServletRequestParameterException.class) + @ResponseBody + public ResponseResult handleMissingServletRequestParameter(HttpServletRequest request, MissingServletRequestParameterException ex) { + LOGGER.error("接口:{},缺少必传参数400:" + ex.getMessage(), request.getRequestURI(), ex); + return ResponseResult.error("缺少必填参数"); + } +} diff --git a/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceConfig.java b/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceConfig.java index d8bb9cf..99e90a4 100644 --- a/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceConfig.java +++ b/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceConfig.java @@ -63,7 +63,8 @@ public class DynamicDataSourceConfig { masterDataSource.setUsername(masterUsername); masterDataSource.setPassword(masterPassword); masterDataSource.setInitialSize(initSize); - masterDataSource.setConnectionInitSqls(INIT_SQLS); + masterDataSource.setMaxActive(initSize); + masterDataSource.setMinIdle(initSize); return masterDataSource; } @@ -75,6 +76,8 @@ public class DynamicDataSourceConfig { readonlyDataSource.setUsername(readonlyUsername); readonlyDataSource.setPassword(readonlyPassword); readonlyDataSource.setInitialSize(initSize); + readonlyDataSource.setMaxActive(initSize); + readonlyDataSource.setMinIdle(initSize); readonlyDataSource.setConnectionInitSqls(INIT_SQLS); return readonlyDataSource; } diff --git a/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceContextHolder.java b/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceContextHolder.java index 0bd14eb..221b7a3 100644 --- a/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceContextHolder.java +++ b/src/main/java/net/educoder/ecsonar/config/DynamicDataSourceContextHolder.java @@ -22,7 +22,7 @@ public class DynamicDataSourceContextHolder { */ public static String getContextKey(){ String key = DATASOURCE_CONTEXT_KEY_HOLDER.get(); - return key == null? net.educode.detection.config.DynamicDataSourceConfig.MASTER: key; + return key == null? DynamicDataSourceConfig.MASTER: key; } /** diff --git a/src/main/java/net/educoder/ecsonar/config/ThreadPoolConfig.java b/src/main/java/net/educoder/ecsonar/config/ThreadPoolConfig.java index 9c4a9ae..dd993a8 100644 --- a/src/main/java/net/educoder/ecsonar/config/ThreadPoolConfig.java +++ b/src/main/java/net/educoder/ecsonar/config/ThreadPoolConfig.java @@ -2,16 +2,31 @@ package net.educoder.ecsonar.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.scheduling.concurrent.CustomizableThreadFactory; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; @Configuration public class ThreadPoolConfig { - @Bean - public ExecutorService getThreadPool(){ - return Executors.newFixedThreadPool(10); + @Bean("sonarScannerPool") + @Primary + public ExecutorService sonarScannerPool() { + ThreadFactory sonarScannerPool = new CustomizableThreadFactory("sonarScanner-pool-"); + return new ThreadPoolExecutor(10, 10, 60, + TimeUnit.SECONDS, new ArrayBlockingQueue<>(10000), sonarScannerPool, + new ThreadPoolExecutor.AbortPolicy()); + } + + + @Bean("sonarQueryResultPool") + public ExecutorService queryResultPool() { + ThreadFactory sonarQueryResultPool = new CustomizableThreadFactory("sonarScanner-pool-"); + return new ThreadPoolExecutor(5, 10, 60, + TimeUnit.SECONDS, new ArrayBlockingQueue<>(10000), + sonarQueryResultPool, + new ThreadPoolExecutor.AbortPolicy()); } } diff --git a/src/main/java/net/educoder/ecsonar/config/WebMvcConfiguration.java b/src/main/java/net/educoder/ecsonar/config/WebMvcConfiguration.java new file mode 100644 index 0000000..571e52f --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/config/WebMvcConfiguration.java @@ -0,0 +1,54 @@ +package net.educoder.ecsonar.config; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +@Configuration +public class WebMvcConfiguration implements WebMvcConfigurer { + + + @Override + public void configureMessageConverters(List> converters) { + FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); + + List supportedMediaTypes = new ArrayList<>(20); + supportedMediaTypes.add(MediaType.APPLICATION_JSON); + supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML); + supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED); + supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM); + supportedMediaTypes.add(MediaType.APPLICATION_PDF); + supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML); + supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML); + supportedMediaTypes.add(MediaType.APPLICATION_XML); + supportedMediaTypes.add(MediaType.IMAGE_GIF); + supportedMediaTypes.add(MediaType.IMAGE_JPEG); + supportedMediaTypes.add(MediaType.IMAGE_PNG); + supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM); + supportedMediaTypes.add(MediaType.TEXT_HTML); + supportedMediaTypes.add(MediaType.TEXT_MARKDOWN); + supportedMediaTypes.add(MediaType.TEXT_PLAIN); + supportedMediaTypes.add(MediaType.TEXT_XML); + fastJsonHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); + +// FastJsonConfig fastJsonConfig = new FastJsonConfig(); +// fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue); +// +// fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); + converters.add(0,fastJsonHttpMessageConverter); + } +} diff --git a/src/main/java/net/educoder/ecsonar/constant/Constant.java b/src/main/java/net/educoder/ecsonar/constant/Constant.java new file mode 100644 index 0000000..1a6294e --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/constant/Constant.java @@ -0,0 +1,20 @@ +package net.educoder.ecsonar.constant; + +import java.util.Arrays; +import java.util.List; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class Constant { + + + public static final String JAVA = "JAVA"; + public static final String C = "C"; + public static final String CXX = "C++"; + public static final String PYTHON = "PYTHON"; + + public static final List language = Arrays.asList(JAVA, C, CXX, PYTHON); +} diff --git a/src/main/java/net/educoder/ecsonar/controller/QualityInspectController.java b/src/main/java/net/educoder/ecsonar/controller/QualityInspectController.java index e6635fa..f6a3725 100644 --- a/src/main/java/net/educoder/ecsonar/controller/QualityInspectController.java +++ b/src/main/java/net/educoder/ecsonar/controller/QualityInspectController.java @@ -1,11 +1,15 @@ package net.educoder.ecsonar.controller; +import net.educoder.ecsonar.constant.Constant; +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.QualityInspectResult; +import net.educoder.ecsonar.model.api.QualityInspectResultData; import net.educoder.ecsonar.model.vo.QualityInspectVO; import net.educoder.ecsonar.services.QualityInspectService; import net.educoder.ecsonar.utils.ResponseResult; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** @@ -13,7 +17,7 @@ import org.springframework.web.bind.annotation.*; * @Date: 2022/1/17 * @Description: */ -@RestController +@Controller @RequestMapping("/api") public class QualityInspectController { @@ -27,9 +31,13 @@ public class QualityInspectController { * @return */ @RequestMapping(value = "qualityInspect", method = RequestMethod.POST) + @ResponseBody public ResponseResult qualityInspect(@RequestBody QualityInspectVO qualityInspectVO){ - String taskId = qualityInspectService.qualityInspect(qualityInspectVO); - return ResponseResult.success(taskId); + if (!Constant.language.contains(qualityInspectVO.getLanguage())) { + return ResponseResult.error("不支持的语言"); + } + QualityInspect qualityInspect = qualityInspectService.qualityInspect(qualityInspectVO); + return ResponseResult.success(qualityInspect); } @@ -39,6 +47,7 @@ public class QualityInspectController { * @return */ @RequestMapping(value = "qualityInspectIsCompleted", method = RequestMethod.GET) + @ResponseBody public ResponseResult qualityInspectIsCompleted(@RequestParam String taskId){ QualityInspectIsCompleted result = qualityInspectService.qualityInspectIsCompleted(taskId); return ResponseResult.success(result); @@ -51,8 +60,20 @@ public class QualityInspectController { * @return */ @RequestMapping(value = "qualityInspectResultQuery", method = RequestMethod.GET) - public ResponseResult qualityInspectResultQuery(@RequestParam String taskId){ - QualityInspectResult result = qualityInspectService.qualityInspectResultQuery(taskId); + @ResponseBody + public ResponseResult> qualityInspectResultQuery( + @RequestParam(defaultValue = "50") Integer pageSize, + @RequestParam(defaultValue = "1") Integer pageNum, + @RequestParam String taskId){ + if (pageNum <= 1) { + pageNum = 1; + } + QualityInspectIsCompleted isCompleted = qualityInspectService.qualityInspectIsCompleted(taskId); + if(isCompleted.getCompleted() != 1){ + return ResponseResult.error("质量检测正在处理中"); + } + + RollPage result = qualityInspectService.qualityInspectResultQuery(pageNum, pageSize,taskId); return ResponseResult.success(result); } diff --git a/src/main/java/net/educoder/ecsonar/dao/CeActivityDao.java b/src/main/java/net/educoder/ecsonar/dao/CeActivityDao.java new file mode 100644 index 0000000..b7cb563 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/dao/CeActivityDao.java @@ -0,0 +1,16 @@ +package net.educoder.ecsonar.dao; + +import org.apache.ibatis.annotations.Select; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public interface CeActivityDao { + + + @Select("select status from ce_activity where component_uuid=#{projectId} and is_last=true") + String queryActivityStatus(String projectId); + +} diff --git a/src/main/java/net/educoder/ecsonar/dao/GameCodesDao.java b/src/main/java/net/educoder/ecsonar/dao/GameCodesDao.java new file mode 100644 index 0000000..f75069f --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/dao/GameCodesDao.java @@ -0,0 +1,15 @@ +package net.educoder.ecsonar.dao; + +import net.educoder.ecsonar.model.GameCodes; +import org.apache.ibatis.annotations.Select; + +/** + * @Author: youys + * @Date: 2021/12/31 + * @Description: + */ +public interface GameCodesDao { + + @Select("select new_code code,path from game_codes where id=#{id}") + GameCodes queryOriginalCodeById(Long id); +} diff --git a/src/main/java/net/educoder/ecsonar/dao/TaskInfoDao.java b/src/main/java/net/educoder/ecsonar/dao/TaskInfoDao.java new file mode 100644 index 0000000..b1603cc --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/dao/TaskInfoDao.java @@ -0,0 +1,26 @@ +package net.educoder.ecsonar.dao; + +import net.educoder.ecsonar.model.TaskInfo; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public interface TaskInfoDao { + + @Insert("insert into task_info(id,language,homework_id,people_number) values(#{id},#{language},#{homeworkId},#{peopleNumber} )") + void insertTaskInfo(TaskInfo taskInfo); + + + @Update("update task_info set status=#{status},update_time=current_timestamp where id=#{id}") + void updateTaskInfoStatus(@Param("id") String id, @Param("status") Integer status); + + + @Select("select id,homework_id homeworkId,status,people_number peopleNumber,create_time createTime,update_time updateTime from task_info where id=#{id}") + TaskInfo selectById(String id); +} diff --git a/src/main/java/net/educoder/ecsonar/dao/TaskInfoDetailDao.java b/src/main/java/net/educoder/ecsonar/dao/TaskInfoDetailDao.java new file mode 100644 index 0000000..23c82aa --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/dao/TaskInfoDetailDao.java @@ -0,0 +1,74 @@ +package net.educoder.ecsonar.dao; + +import net.educoder.ecsonar.model.TaskInfoDetail; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.util.List; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public interface TaskInfoDetailDao { + + /** + * 插入数据 + * + * @param taskInfoDetail + */ + @Insert("insert into task_info_detail(id,task_id,project_name,student_id,name,code_ids) values(#{id},#{taskId},#{projectName},#{studentId},#{name},#{codeIds})") + void insertTaskInfoDetail(TaskInfoDetail taskInfoDetail); + + /** + * 更新状态 + * + * @param id + * @param status + */ + @Update("update task_info_detail set status=#{status},update_time=current_timestamp where id=#{id}") + void updateTaskInfoDetailStatus(@Param("id") String id, @Param("status") Integer status); + + /** + * 查询taskId执行完的sonar任务数 + * + * @param taskId + * @return + */ + @Select("select count(1) from task_info_detail where task_id=#{taskId} and status != 0") + Integer selectCountByTaskId(@Param("taskId") String taskId); + + /** + * 根据id查TaskInfoDetail + * + * @param id + * @return + */ + @Select("select id,task_id taskId,project_name projectName,student_id studentId,name,code_ids codeIds,status,create_time createTime,update_time updateTime from task_info_detail where id=#{id}") + TaskInfoDetail selectById(String id); + + /** + * count + * + * @param taskId + * @param pageSize + * @param offset + * @return + */ + @Select("select count(1) from task_info_detail where task_id=#{taskId} limit #{pageSize} offset #{offset}") + Integer selectTaskInfoDetailPageCount(@Param("taskId") String taskId, @Param("pageSize") Integer pageSize, @Param("offset") Integer offset); + + /** + * 分页查询 + * + * @param taskId + * @param pageSize + * @param position + * @return + */ + @Select("select id,task_id taskId,project_name projectName,student_id studentId,name,code_ids codeIds,status,create_time createTime,update_time updateTime from task_info_detail where task_id=#{taskId} limit #{pageSize} offset #{position}") + List selectTaskInfoDetailPageList(@Param("taskId") String taskId, @Param("pageSize") Integer pageSize, @Param("position") Integer position); +} diff --git a/src/main/java/net/educoder/ecsonar/exception/BusinessException.java b/src/main/java/net/educoder/ecsonar/exception/BusinessException.java new file mode 100644 index 0000000..f29a642 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/exception/BusinessException.java @@ -0,0 +1,17 @@ +package net.educoder.ecsonar.exception; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class BusinessException extends RuntimeException{ + private Integer code; + private String message; + + public BusinessException(Integer code, String message){ + super(message); + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/GameCodes.java b/src/main/java/net/educoder/ecsonar/model/GameCodes.java new file mode 100644 index 0000000..a8dbd96 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/GameCodes.java @@ -0,0 +1,28 @@ +package net.educoder.ecsonar.model; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class GameCodes { + + private String code; + private String path; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/RollPage.java b/src/main/java/net/educoder/ecsonar/model/RollPage.java new file mode 100644 index 0000000..88d14ea --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/RollPage.java @@ -0,0 +1,71 @@ +package net.educoder.ecsonar.model; + +import java.util.List; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class RollPage { + + private static final Integer DEFAULT_PAGE_SIZE = 50; + + private Integer pageSize; + + private Integer currentPage = 1; + + private Integer pageSum = 0; + + private Integer recordSum = 0; + + private List recordList; + + public RollPage() { + pageSize = DEFAULT_PAGE_SIZE; + } + + public Integer getPageSum() { + if (this.pageSum == 0) { + this.pageSum = (int) Math.ceil(this.recordSum / this.pageSize) + ((this.recordSum % this.pageSize == 0) ? 0 : 1); + } + return pageSum; + } + + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Integer getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(Integer currentPage) { + this.currentPage = currentPage; + } + + public void setPageSum(Integer pageSum) { + this.pageSum = pageSum; + } + + public Integer getRecordSum() { + return recordSum; + } + + public void setRecordSum(Integer recordSum) { + this.recordSum = recordSum; + } + + public List getRecordList() { + return recordList; + } + + public void setRecordList(List recordList) { + this.recordList = recordList; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/SonarScannerParam.java b/src/main/java/net/educoder/ecsonar/model/SonarScannerParam.java new file mode 100644 index 0000000..8167533 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/SonarScannerParam.java @@ -0,0 +1,53 @@ +package net.educoder.ecsonar.model; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class SonarScannerParam { + + /** + * sonar项目名 + */ + private String projectKey; + + /** + * 项目文件路径 + */ + private String projectPath; + + /** + * c/++ 需要设置cppcheck文件路径 + */ + private String cppCheckReportPath; + + public SonarScannerParam(String projectKey, String projectPath) { + this.projectKey = projectKey; + this.projectPath = projectPath; + } + + public String getProjectKey() { + return projectKey; + } + + public void setProjectKey(String projectKey) { + this.projectKey = projectKey; + } + + public String getProjectPath() { + return projectPath; + } + + public void setProjectPath(String projectPath) { + this.projectPath = projectPath; + } + + public String getCppCheckReportPath() { + return cppCheckReportPath; + } + + public void setCppCheckReportPath(String cppCheckReportPath) { + this.cppCheckReportPath = cppCheckReportPath; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/TaskInfo.java b/src/main/java/net/educoder/ecsonar/model/TaskInfo.java new file mode 100644 index 0000000..2d248fa --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/TaskInfo.java @@ -0,0 +1,78 @@ +package net.educoder.ecsonar.model; + +import java.util.Date; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class TaskInfo { + + private String id; + private String language; + private String homeworkId; + /** + * 状态 0:处理中 1成功 + */ + private Integer status; + private Integer peopleNumber; + private Date createTime; + private Date updateTime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getHomeworkId() { + return homeworkId; + } + + public void setHomeworkId(String homeworkId) { + this.homeworkId = homeworkId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getPeopleNumber() { + return peopleNumber; + } + + public void setPeopleNumber(Integer peopleNumber) { + this.peopleNumber = peopleNumber; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/TaskInfoDetail.java b/src/main/java/net/educoder/ecsonar/model/TaskInfoDetail.java new file mode 100644 index 0000000..9da4ed2 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/TaskInfoDetail.java @@ -0,0 +1,97 @@ +package net.educoder.ecsonar.model; + +import java.util.Date; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class TaskInfoDetail { + + private String id; + private String taskId; + private String projectName; + private String studentId; + private String name; + private String codeIds; + /** + * 0处理中 1处理成功 -1 处理失败 + */ + private Integer status; + private Date createTime; + private Date updateTime; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public String getStudentId() { + return studentId; + } + + public void setStudentId(String studentId) { + this.studentId = studentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCodeIds() { + return codeIds; + } + + public void setCodeIds(String codeIds) { + this.codeIds = codeIds; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } +} diff --git a/src/main/java/net/educoder/ecsonar/model/api/Quality.java b/src/main/java/net/educoder/ecsonar/model/api/Quality.java index e670ae2..ecc5404 100644 --- a/src/main/java/net/educoder/ecsonar/model/api/Quality.java +++ b/src/main/java/net/educoder/ecsonar/model/api/Quality.java @@ -28,6 +28,24 @@ public class Quality { */ private String grade; + public Quality() { + } + + /** + * @param blocker 阻断 + * @param major 主要 + * @param minor 次要 + * @param critical 严重 + * @param grade 等级 A-E + */ + public Quality(Integer blocker, Integer major, Integer minor, Integer critical, String grade) { + this.blocker = blocker; + this.major = major; + this.minor = minor; + this.critical = critical; + this.grade = grade; + } + public Integer getBlocker() { return blocker; } diff --git a/src/main/java/net/educoder/ecsonar/model/api/QualityInspect.java b/src/main/java/net/educoder/ecsonar/model/api/QualityInspect.java new file mode 100644 index 0000000..801c2b2 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/model/api/QualityInspect.java @@ -0,0 +1,20 @@ +package net.educoder.ecsonar.model.api; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class QualityInspect { + + private String taskId; + + public QualityInspect(String taskId){ + this.taskId = taskId; + } + + public String getTaskId() { + return taskId; + } + +} diff --git a/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResult.java b/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResult.java deleted file mode 100644 index f9d44f9..0000000 --- a/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResult.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.educoder.ecsonar.model.api; - -import java.util.List; - -/** - * @Author: youys - * @Date: 2022/1/17 - * @Description: 检测结果 - */ -public class QualityInspectResult { - - /** - * 任务id - */ - private String taskId; - - - /** - * 质量检测数据 - */ - private List dataList; - - - - public String getTaskId() { - return taskId; - } - - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - - - public List getDataList() { - return dataList; - } - - public void setDataList(List dataList) { - this.dataList = dataList; - } -} 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 b9ad64f..78d1d58 100644 --- a/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java +++ b/src/main/java/net/educoder/ecsonar/model/api/QualityInspectResultData.java @@ -39,7 +39,7 @@ public class QualityInspectResultData { /** * 复杂度 */ - private BigDecimal complexity; + private String complexity; /** * 总行数 @@ -99,11 +99,11 @@ public class QualityInspectResultData { this.codeSmall = codeSmall; } - public BigDecimal getComplexity() { + public String getComplexity() { return complexity; } - public void setComplexity(BigDecimal complexity) { + public void setComplexity(String complexity) { this.complexity = complexity; } diff --git a/src/main/java/net/educoder/ecsonar/model/vo/QualityInspectUserDataVO.java b/src/main/java/net/educoder/ecsonar/model/vo/QualityInspectUserDataVO.java index 67bc36f..069ed63 100644 --- a/src/main/java/net/educoder/ecsonar/model/vo/QualityInspectUserDataVO.java +++ b/src/main/java/net/educoder/ecsonar/model/vo/QualityInspectUserDataVO.java @@ -21,7 +21,7 @@ public class QualityInspectUserDataVO { /** * 学员代码id */ - private List codeIds; + private List codeIds; public String getStudentId() { return studentId; @@ -39,11 +39,11 @@ public class QualityInspectUserDataVO { this.name = name; } - public List getCodeIds() { + public List getCodeIds() { return codeIds; } - public void setCodeIds(List codeIds) { + public void setCodeIds(List codeIds) { this.codeIds = codeIds; } } diff --git a/src/main/java/net/educoder/ecsonar/services/DbOperateService.java b/src/main/java/net/educoder/ecsonar/services/DbOperateService.java new file mode 100644 index 0000000..f25f806 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/services/DbOperateService.java @@ -0,0 +1,67 @@ +package net.educoder.ecsonar.services; + +import net.educoder.ecsonar.config.DynamicDataSourceConfig; +import net.educoder.ecsonar.config.DynamicDataSourceContextHolder; +import net.educoder.ecsonar.dao.CeActivityDao; +import net.educoder.ecsonar.dao.GameCodesDao; +import net.educoder.ecsonar.dao.ProjectDao; +import net.educoder.ecsonar.dao.TaskInfoDetailDao; +import net.educoder.ecsonar.model.GameCodes; +import net.educoder.ecsonar.model.Project; +import net.educoder.ecsonar.model.TaskInfoDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @Author: youys + * @Date: 2021/12/31 + * @Description: + */ +@Service +public class DbOperateService { + + @Autowired + private GameCodesDao gameCodesDao; + + + @Autowired + private CeActivityDao ceActivityDao; + + @Autowired + private ProjectDao projectDao; + + @Autowired + private TaskInfoDetailDao taskInfoDetailDao; + + /** + * 根据id查询用户传的code + * @param id + * @return + */ + public GameCodes queryGameCodesById(Long id){ + DynamicDataSourceContextHolder.setContextKey(DynamicDataSourceConfig.READONLY); + GameCodes gameCode = gameCodesDao.queryOriginalCodeById(id); + DynamicDataSourceContextHolder.removeContextKey(); + return gameCode; + } + + /** + * 查询任务执行状态 + * @param projectName + * @return + */ + public String queryCeActivityStatus(String projectName){ + Project project = projectDao.findByName(projectName); + if(project == null ){ + return null; + } + return ceActivityDao.queryActivityStatus(project.getProject_uuid()); + } + + + public void updateTaskInfoDetail(String id, Integer status){ + taskInfoDetailDao.updateTaskInfoDetailStatus(id, status); + } + + +} diff --git a/src/main/java/net/educoder/ecsonar/services/QualityInspectService.java b/src/main/java/net/educoder/ecsonar/services/QualityInspectService.java index 8e7bffb..1879b4c 100644 --- a/src/main/java/net/educoder/ecsonar/services/QualityInspectService.java +++ b/src/main/java/net/educoder/ecsonar/services/QualityInspectService.java @@ -1,10 +1,34 @@ package net.educoder.ecsonar.services; +import net.educoder.ecsonar.exception.BusinessException; +import net.educoder.ecsonar.dao.TaskInfoDao; +import net.educoder.ecsonar.dao.TaskInfoDetailDao; +import net.educoder.ecsonar.model.Metrics; +import net.educoder.ecsonar.model.RollPage; +import net.educoder.ecsonar.model.TaskInfo; +import net.educoder.ecsonar.model.TaskInfoDetail; +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.QualityInspectResult; +import net.educoder.ecsonar.model.api.QualityInspectResultData; +import net.educoder.ecsonar.model.vo.QualityInspectUserDataVO; import net.educoder.ecsonar.model.vo.QualityInspectVO; +import net.educoder.ecsonar.task.QualityInspectRunnable; +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; +import org.springframework.beans.factory.annotation.Autowired; +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; + /** * @Author: youys * @Date: 2022/1/17 @@ -13,25 +37,170 @@ import org.springframework.stereotype.Service; @Service public class QualityInspectService { + private static final Logger LOGGER = LoggerFactory.getLogger(QualityInspectService.class); + + @Autowired + private TaskInfoDao taskInfoDao; + + @Autowired + private TaskInfoDetailDao taskInfoDetailDao; + + @Autowired + private DbOperateService dbOperateService; + + @Autowired + private SonarService sonarService; + + @Autowired + private ReportService reportService; + + + @Autowired + @Qualifier("sonarScannerPool") + private ExecutorService sonarScannerPool; + + @Autowired + @Qualifier("sonarQueryResultPool") + private ExecutorService sonarQueryResultPool; + - public String qualityInspect(QualityInspectVO qualityInspectVO) { + public QualityInspect qualityInspect(QualityInspectVO qualityInspectVO) { + List userDatas = qualityInspectVO.getUserDatas(); + LOGGER.info("homeworkId:{},{}个人提交了代码", qualityInspectVO.getHomeworkId(), userDatas.size()); - return null; + if (CollectionUtils.isEmpty(userDatas)) { + throw new BusinessException(2, "用户数据不能为空"); + } + + TaskInfo taskInfo = new TaskInfo(); + taskInfo.setId(IdUtils.nextStrId()); + taskInfo.setHomeworkId(qualityInspectVO.getHomeworkId()); + taskInfo.setLanguage(qualityInspectVO.getLanguage()); + taskInfo.setPeopleNumber(qualityInspectVO.getUserDatas().size()); + + taskInfoDao.insertTaskInfo(taskInfo); + + for (QualityInspectUserDataVO userData : userDatas) { + + TaskInfoDetail taskInfoDetail = new TaskInfoDetail(); + taskInfoDetail.setId(IdUtils.nextStrId()); + taskInfoDetail.setTaskId(taskInfo.getId()); + taskInfoDetail.setProjectName(String.format("%s-%s", qualityInspectVO.getHomeworkId(), userData.getStudentId())); + taskInfoDetail.setName(userData.getName()); + taskInfoDetail.setStudentId(userData.getStudentId()); + taskInfoDetail.setCodeIds(userData.getCodeIds().toString()); + + taskInfoDetailDao.insertTaskInfoDetail(taskInfoDetail); + + QualityInspectRunnable runnable = new QualityInspectRunnable(taskInfo.getId(), qualityInspectVO.getLanguage(), + qualityInspectVO.getHomeworkId(), taskInfoDetail, userData, sonarQueryResultPool); + runnable.setDbOperateService(dbOperateService); + runnable.setSonarService(sonarService); + + // 提交任务 + sonarScannerPool.execute(runnable); + } + // 提交之后返回taskId + return new QualityInspect(taskInfo.getId()); } - public QualityInspectResult qualityInspectResultQuery(String taskId) { + public RollPage qualityInspectResultQuery(Integer pageNum, Integer pageSize, String taskId) { + RollPage rollPage = new RollPage(); + rollPage.setCurrentPage(pageNum); + rollPage.setPageSize(pageSize); + + + int offset = (pageNum - 1) * pageSize; + Integer totalCount = taskInfoDetailDao.selectTaskInfoDetailPageCount(taskId, pageSize, offset); + rollPage.setRecordSum(totalCount); - QualityInspectResult result = new QualityInspectResult(); + if (totalCount == null || totalCount == 0) { + rollPage.setRecordList(new ArrayList<>()); + } else { + List taskInfoDetails = taskInfoDetailDao.selectTaskInfoDetailPageList(taskId, pageSize, offset); - return result; + List resultDataList = new ArrayList<>(taskInfoDetails.size()); + rollPage.setRecordList(resultDataList); + + // 组装数据 + for (TaskInfoDetail taskInfoDetail : taskInfoDetails) { + QualityInspectResultData resultData = new QualityInspectResultData(); + resultData.setName(taskInfoDetail.getName()); + resultData.setStudentId(taskInfoDetail.getStudentId()); + + Metrics metrics = reportService.getMetrics(taskInfoDetail.getProjectName()); + + 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()); + resultData.setQualityScore(calcQualityScore(resultData)); + + resultDataList.add(resultData); + } + } + + return rollPage; } public QualityInspectIsCompleted qualityInspectIsCompleted(String taskId) { - QualityInspectIsCompleted completed = new QualityInspectIsCompleted(taskId, 1); + TaskInfo taskInfo = taskInfoDao.selectById(taskId); + if (taskInfo == null) { + throw new BusinessException(2, "任务id不存在"); + } + + if (taskInfo.getStatus() == 1) { + return new QualityInspectIsCompleted(taskId, taskInfo.getStatus()); + } + + Integer taskNumber = taskInfoDetailDao.selectCountByTaskId(taskInfo.getId()); + int completed = taskInfo.getPeopleNumber().equals(taskNumber) ? 1 : 0; + + // 数据库未更新状态,但sonar任务已经全部执行完,更新task_info状态 + if (taskInfo.getStatus() == 0 && completed == 1) { + taskInfoDao.updateTaskInfoStatus(taskInfo.getId(), 1); + + // 并且删除文件 /tmp/taskId/* /tmp/homeworkId/* + String command = String.format("rm -rf /tmp/%s/* && rm -rf /tmp/%s/*", taskId, taskInfo.getHomeworkId()); + SystemUtil.executeAndGetExitStatus(command); + } + + QualityInspectIsCompleted qualityInspectIsCompleted = new QualityInspectIsCompleted(taskId, completed); + return qualityInspectIsCompleted; + } + + + /** + * 阻断 严重 主要 次要 + * 10 5 3 1 + * + * @param resultData + * @return + */ + private BigDecimal calcQualityScore(QualityInspectResultData resultData) { + + if (resultData.getTotalRowNumber() <= 0) { + return null; + } + + // 100 -(阻断 * 10 + 严重 * 5 + 主要* 3 + 次要 * 1)/ 总行数 * 100 + Quality bug = resultData.getBug(); + BigDecimal bugScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(bug.getBlocker() * 10 + bug.getCritical() * 5 + bug.getMajor() * 3 + bug.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100))); + + Quality vulnerability = resultData.getVulnerability(); + BigDecimal vulnerabilityScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(vulnerability.getBlocker() * 10 + vulnerability.getCritical() * 5 + vulnerability.getMajor() * 3 + vulnerability.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100))); + + Quality codeSmall = resultData.getCodeSmall(); + BigDecimal codeSmallScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(codeSmall.getBlocker() * 10 + codeSmall.getCritical() * 5 + codeSmall.getMajor() * 3 + codeSmall.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100))); + - return null; + // 总得分= bugScore * 0.5 + vulnerabilityScore * 0.3 + codeSmallScore * 0.2 + BigDecimal score = bugScore.multiply(BigDecimal.valueOf(0.5)).add(vulnerabilityScore.multiply(BigDecimal.valueOf(0.3))).add(codeSmallScore.multiply(BigDecimal.valueOf(0.2))).setScale(2, RoundingMode.HALF_UP); + return score; } } diff --git a/src/main/java/net/educoder/ecsonar/services/ReportService.java b/src/main/java/net/educoder/ecsonar/services/ReportService.java index 02eba48..31aaa7b 100644 --- a/src/main/java/net/educoder/ecsonar/services/ReportService.java +++ b/src/main/java/net/educoder/ecsonar/services/ReportService.java @@ -73,9 +73,9 @@ public class ReportService { metrics.setMinor_bugs(0); metrics.setMinor_code_smells(0); metrics.setMinor_violations(0); - metrics.setBugs("A"); - metrics.setViolations("A"); - metrics.setCode_smells("A"); + metrics.setBugs("-"); + metrics.setViolations("-"); + metrics.setCode_smells("-"); return metrics; } diff --git a/src/main/java/net/educoder/ecsonar/services/SonarService.java b/src/main/java/net/educoder/ecsonar/services/SonarService.java index f0d70cf..01564c5 100644 --- a/src/main/java/net/educoder/ecsonar/services/SonarService.java +++ b/src/main/java/net/educoder/ecsonar/services/SonarService.java @@ -1,6 +1,8 @@ package net.educoder.ecsonar.services; +import net.educoder.ecsonar.constant.Constant; +import net.educoder.ecsonar.model.SonarScannerParam; import net.educoder.ecsonar.model.api.Person; import net.educoder.ecsonar.model.api.SonarRequest; import net.educoder.ecsonar.task.ReadExcelRunnable; @@ -15,15 +17,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import javax.annotation.PostConstruct; import java.io.*; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -45,6 +51,55 @@ public class SonarService { @Autowired private ExecutorService executorService; + private ConcurrentHashMap> concurrentHashMap = new ConcurrentHashMap(8); + + @PostConstruct + public void init() { + concurrentHashMap.put(Constant.JAVA, param -> { + String command = "sonar-scanner " + + "-Dsonar.host.url=" + sonarUrl + " " + + "-Dsonar.sourceEncoding=utf-8 " + + "-Dsonar.projectKey=" + param.getProjectKey() + " " + + "-Dsonar.login=" + sonarToken + " " + + "-Dsonar.java.binaries=./ " + + "-Dsonar.projectBaseDir=" + param.getProjectPath(); + + SystemUtil.executeAndGetExitStatus(command); + }); + concurrentHashMap.put(Constant.PYTHON, param -> { + String command = "sonar-scanner " + + "-Dsonar.host.url=" + sonarUrl + " " + + "-Dsonar.sourceEncoding=utf-8 " + + "-Dsonar.projectKey=" + param.getProjectKey() + " " + + "-Dsonar.login=" + sonarToken + " " + + "-Dsonar.projectBaseDir=" + param.getProjectPath(); + + SystemUtil.executeAndGetExitStatus(command); + }); + concurrentHashMap.put(Constant.C, param -> { + String command = "cppcheck --xml --xml-version=2 --enable=all " + param.getProjectPath() + " 2> " + param.getCppCheckReportPath() + + "&& sonar-scanner " + + "-Dsonar.host.url=" + sonarUrl + " " + + "-Dsonar.sourceEncoding=utf-8 " + + "-Dsonar.projectKey=" + param.getProjectKey() + " " + + "-Dsonar.login=" + sonarToken + " " + + "-Dsonar.cxx.cppcheck.reportPath=" + param.getCppCheckReportPath() + " " + + "-Dsonar.projectBaseDir=" + param.getProjectPath(); + SystemUtil.executeAndGetExitStatus(command); + }); + concurrentHashMap.put(Constant.CXX, param -> { + String command = "cppcheck --xml --xml-version=2 --enable=all " + param.getProjectPath() + " 2> " + param.getCppCheckReportPath() + + "&& sonar-scanner " + + "-Dsonar.host.url=" + sonarUrl + " " + + "-Dsonar.sourceEncoding=utf-8 " + + "-Dsonar.projectKey=" + param.getProjectKey() + " " + + "-Dsonar.login=" + sonarToken + " " + + "-Dsonar.cxx.cppcheck.reportPath=" + param.getCppCheckReportPath() + " " + + "-Dsonar.projectBaseDir=" + param.getProjectPath(); + SystemUtil.executeAndGetExitStatus(command); + }); + } + /** * 提交一个任务 * @@ -59,13 +114,13 @@ public class SonarService { String savePath = zipSavePath + zipFilename; //1. 下载文件 - if (index==1) { + if (index == 1) { download(zipUrl, savePath); } //2. 解压目录 String extractPath = zipSavePath + key; - if (index==1) { + if (index == 1) { log.info("savePath: {}, extractPath: {}", savePath, extractPath); extract(savePath, extractPath); } @@ -82,7 +137,7 @@ public class SonarService { log.info("scan zipSavePath: {}, zipFilename: {}, savePath: {}, extractPath: {}, subPath: {}", zipSavePath, zipFilename, savePath, extractPath, subPath); - if (!new File(extractPath + subPath).exists()){ + if (!new File(extractPath + subPath).exists()) { throw new Exception("项目文件夹不存在"); } @@ -119,17 +174,24 @@ public class SonarService { } + public void sonar(String language, SonarScannerParam sonarScannerParam) { + Consumer consumer = concurrentHashMap.get(language); + if (consumer != null) { + log.info("语言:{},projectName:{},path:{}找到了consumer", language, sonarScannerParam.getProjectKey(),sonarScannerParam.getProjectPath()); + consumer.accept(sonarScannerParam); + } + } + + public void sonar(String projectPath, String key) { - String command = "cppcheck --xml --xml-version=2 --enable=all "+projectPath+" 2> /tmp/"+key+"-result.xml &&" + - "source ~/.bash_profile && sonar-scanner " + + String command = "sonar-scanner " + "-Dsonar.host.url=" + sonarUrl + " " + "-Dsonar.sourceEncoding=utf-8 " + "-Dsonar.projectKey=" + key + " " + "-Dsonar.login=" + sonarToken + " " + "-Dsonar.java.binaries=./ " + - "-Dsonar.cxx.cppcheck.reportPath=/tmp/"+ key+"-result.xml " + "-Dsonar.projectBaseDir=" + projectPath; - log.info("projectPath:{},key:{}, command: {}", projectPath,key, command); + log.info("projectPath:{},key:{}, command: {}", projectPath, key, command); SystemUtil.executeAndGetExitStatus(command); } @@ -140,14 +202,14 @@ public class SonarService { */ private void extract(String zipFile, String extractPath) throws Exception { File extractFile = new File(extractPath); - if (extractFile.exists()){ + if (extractFile.exists()) { extractFile.delete(); } extractFile.mkdir(); SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus("cd " + extractPath - + " && "+extractProgramPath+" -encoding GBK " + zipFile); + + " && " + extractProgramPath + " -encoding GBK " + zipFile); } /** @@ -181,9 +243,10 @@ public class SonarService { /** * 扫描excel文件 + * * @param excelPath */ - public void scanExcel(String excelPath){ + public void scanExcel(String excelPath) { int homeworkId = 20210101; @@ -192,7 +255,7 @@ public class SonarService { Map> collect = personList.stream().collect(Collectors.groupingBy(Person::getUid)); - collect.forEach((k,v)->{ + collect.forEach((k, v) -> { SonarRequest request = new SonarRequest(); request.setHomeworkId(homeworkId); request.setPersonList(v); diff --git a/src/main/java/net/educoder/ecsonar/task/QualityInspectRunnable.java b/src/main/java/net/educoder/ecsonar/task/QualityInspectRunnable.java new file mode 100644 index 0000000..be97833 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/task/QualityInspectRunnable.java @@ -0,0 +1,110 @@ +package net.educoder.ecsonar.task; + +import cn.hutool.core.io.FileUtil; +import net.educoder.ecsonar.constant.Constant; +import net.educoder.ecsonar.model.GameCodes; +import net.educoder.ecsonar.model.SonarScannerParam; +import net.educoder.ecsonar.model.TaskInfoDetail; +import net.educoder.ecsonar.model.vo.QualityInspectUserDataVO; +import net.educoder.ecsonar.services.DbOperateService; +import net.educoder.ecsonar.services.SonarService; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.concurrent.ExecutorService; + + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: 质量检测任务 + */ +public class QualityInspectRunnable implements Runnable { + + + private static final Logger LOGGER = LoggerFactory.getLogger(QualityInspectRunnable.class); + + private String taskId; + public String language; + public String homeworkId; + private QualityInspectUserDataVO userData; + + private ExecutorService queryResultPool; + + private TaskInfoDetail taskInfoDetail; + private DbOperateService dbOperateService; + private SonarService sonarService; + + public QualityInspectRunnable(String taskId, String language, String homeworkId, + TaskInfoDetail taskInfoDetail, + QualityInspectUserDataVO userData, + ExecutorService queryResultPool) { + this.taskId = taskId; + this.language = language; + this.homeworkId = homeworkId; + this.taskInfoDetail = taskInfoDetail; + this.userData = userData; + this.queryResultPool = queryResultPool; + } + + + @Override + public void run() { + String projectName = String.format("%s-%s", homeworkId, userData.getStudentId()); + String path = String.format("/tmp/%s/%s/", homeworkId, projectName); + + List codeIds = userData.getCodeIds(); + for (Long codeId : codeIds) { + File file = new File(path); + try { + FileUtils.forceMkdir(file); + } catch (IOException e) { + } + + GameCodes gameCodes = dbOperateService.queryGameCodesById(codeId); + FileUtil.writeString(gameCodes.getCode(), path + gameCodes.getPath(), Charset.forName("UTF-8")); + } + + // 写完所有文件开始用sonar进行质量分析 + SonarScannerParam param = new SonarScannerParam(projectName, path); + if (Constant.C.equalsIgnoreCase(language) || Constant.CXX.equalsIgnoreCase(language)) { + String resultPath = String.format("/tmp/%s", taskId); + File f = new File(resultPath); + if (!f.exists()) { + f.mkdirs(); + } + param.setCppCheckReportPath(resultPath + String.format("/%s-result.xml", projectName)); + + } + // 调用sonar服务 + LOGGER.info("projectName:{}, detailId:{} 开始调用sonar分析,语言:{}", projectName,taskInfoDetail.getId(),language); + sonarService.sonar(language, param); + + // 提交一个查结果的任务 + SonarQueryResultRunnable queryResultRunnable = new SonarQueryResultRunnable(projectName, taskInfoDetail.getId(), dbOperateService); + LOGGER.info("projectName:{}, detailId:{} 提交了查询结果的任务", projectName,taskInfoDetail.getId()); + queryResultPool.execute(queryResultRunnable); + } + + + public DbOperateService getDbOperateService() { + return dbOperateService; + } + + public void setDbOperateService(DbOperateService dbOperateService) { + this.dbOperateService = dbOperateService; + } + + public SonarService getSonarService() { + return sonarService; + } + + public void setSonarService(SonarService sonarService) { + this.sonarService = sonarService; + } +} diff --git a/src/main/java/net/educoder/ecsonar/task/SonarQueryResultRunnable.java b/src/main/java/net/educoder/ecsonar/task/SonarQueryResultRunnable.java new file mode 100644 index 0000000..715fc26 --- /dev/null +++ b/src/main/java/net/educoder/ecsonar/task/SonarQueryResultRunnable.java @@ -0,0 +1,46 @@ +package net.educoder.ecsonar.task; + +import net.educoder.ecsonar.services.DbOperateService; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @Author: youys + * @Date: 2022/1/18 + * @Description: + */ +public class SonarQueryResultRunnable implements Runnable { + + private static final Logger LOGGER = LoggerFactory.getLogger(SonarQueryResultRunnable.class); + + private static final String SUCCESS = "SUCCESS"; + + private String projectName; + private String detailId; + private DbOperateService dbOperateService; + + public SonarQueryResultRunnable(String projectName, String detailId, DbOperateService dbOperateService) { + this.projectName = projectName; + this.detailId = detailId; + this.dbOperateService = dbOperateService; + } + + @Override + public void run() { + + + String status; + while((status = dbOperateService.queryCeActivityStatus(projectName)) == null){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + LOGGER.info("detailId:[{}],sonar还未执行完", detailId); + } + // 查询 + LOGGER.info("detailId:[{}], 查询sonar执行状态[{}]", detailId, status); + // 更新状态 + dbOperateService.updateTaskInfoDetail(detailId, SUCCESS.equals(status) ? 1 : -1); + + } +} diff --git a/src/main/java/net/educoder/ecsonar/utils/IdUtils.java b/src/main/java/net/educoder/ecsonar/utils/IdUtils.java index a1d9a12..e94963e 100644 --- a/src/main/java/net/educoder/ecsonar/utils/IdUtils.java +++ b/src/main/java/net/educoder/ecsonar/utils/IdUtils.java @@ -12,4 +12,8 @@ public final class IdUtils { return idWorker.nextId(); } + public static String nextStrId() { + return String.valueOf(idWorker.nextId()); + } + } diff --git a/src/main/java/net/educoder/ecsonar/utils/ResponseResult.java b/src/main/java/net/educoder/ecsonar/utils/ResponseResult.java index 7ad3a2f..b94fa1f 100644 --- a/src/main/java/net/educoder/ecsonar/utils/ResponseResult.java +++ b/src/main/java/net/educoder/ecsonar/utils/ResponseResult.java @@ -1,11 +1,13 @@ package net.educoder.ecsonar.utils; +import java.io.Serializable; + /** * @Author: youys * @Date: 2022/1/17 * @Description: */ -public class ResponseResult { +public class ResponseResult implements Serializable { private Integer code; private String msg; @@ -21,6 +23,8 @@ public class ResponseResult { } public ResponseResult(T data){ + this.code = 1; + this.msg = ""; this.data = data; } @@ -29,11 +33,35 @@ public class ResponseResult { } - public static ResponseResult success(Object data){ + public static ResponseResult success(T data){ return new ResponseResult(data); } public static ResponseResult error(String msg){ - return new ResponseResult(2,msg); + return new ResponseResult(-1,msg); + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; } } diff --git a/src/main/java/net/educoder/ecsonar/utils/SnowflakeIdWorker.java b/src/main/java/net/educoder/ecsonar/utils/SnowflakeIdWorker.java index 174e4eb..461e45e 100644 --- a/src/main/java/net/educoder/ecsonar/utils/SnowflakeIdWorker.java +++ b/src/main/java/net/educoder/ecsonar/utils/SnowflakeIdWorker.java @@ -1,147 +1,133 @@ package net.educoder.ecsonar.utils; -/** - * Twitter_Snowflake
- * SnowFlake的结构如下(每部分用-分开):
- * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - - * 000000000000
- * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
- * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) - * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T - * = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
- * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
- * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
- * 加起来刚好64位,为一个Long型。
- * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 - */ public class SnowflakeIdWorker { + /** 开始时间截 (2019-08-06) */ + private final long twepoch = 1565020800000L; + + /** 机器id所占的位数 */ + private final long workerIdBits = 5L; + + /** 数据标识id所占的位数 */ + private final long datacenterIdBits = 5L; + + /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** 支持的最大数据标识id,结果是31 */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** 序列在id中占的位数 */ + private final long sequenceBits = 12L; + + /** 机器ID向左移12位 */ + private final long workerIdShift = sequenceBits; + + /** 数据标识id向左移17位(12+5) */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** 时间截向左移22位(5+5+12) */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** 工作机器ID(0~31) */ + private long workerId; + + /** 数据中心ID(0~31) */ + private long datacenterId; + + /** 毫秒内序列(0~4095) */ + private long sequence = 0L; + + /** 上次生成ID的时间截 */ + private long lastTimestamp = -1L; + + //==============================Constructors==================== + /** + * 构造函数 + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods================================= + /** + * 获得下一个ID (该方法是线程安全的) + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + + //==============================Test============================================= + /** 测试 */ + public static void main(String[] args) { + SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); + for (int i = 0; i < 10; i++) { + long id = idWorker.nextId(); + System.out.println(Long.toBinaryString(id)); + System.out.println(id); + } + } +} - // ==============================Fields=========================================== - /** 开始时间截 (2015-01-01) */ - private final long twepoch = 1420041600000L; - - /** 机器id所占的位数 */ - private final long workerIdBits = 5L; - - /** 数据标识id所占的位数 */ - private final long datacenterIdBits = 5L; - - /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ - private final long maxWorkerId = -1L ^ (-1L << workerIdBits); - - /** 支持的最大数据标识id,结果是31 */ - private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); - - /** 序列在id中占的位数 */ - private final long sequenceBits = 12L; - - /** 机器ID向左移12位 */ - private final long workerIdShift = sequenceBits; - - /** 数据标识id向左移17位(12+5) */ - private final long datacenterIdShift = sequenceBits + workerIdBits; - - /** 时间截向左移22位(5+5+12) */ - private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; - - /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ - private final long sequenceMask = -1L ^ (-1L << sequenceBits); - - /** 工作机器ID(0~31) */ - private long workerId; - - /** 数据中心ID(0~31) */ - private long datacenterId; - - /** 毫秒内序列(0~4095) */ - private long sequence = 0L; - - /** 上次生成ID的时间截 */ - private long lastTimestamp = -1L; - - // ==============================Constructors===================================== - /** - * 构造函数 - * - * @param workerId - * 工作ID (0~31) - * @param datacenterId - * 数据中心ID (0~31) - */ - public SnowflakeIdWorker(long workerId, long datacenterId) { - if (workerId > maxWorkerId || workerId < 0) { - throw new IllegalArgumentException( - String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); - } - if (datacenterId > maxDatacenterId || datacenterId < 0) { - throw new IllegalArgumentException( - String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); - } - this.workerId = workerId; - this.datacenterId = datacenterId; - } - - // ==============================Methods========================================== - /** - * 获得下一个ID (该方法是线程安全的) - * - * @return SnowflakeId - */ - public synchronized long nextId() { - long timestamp = timeGen(); - - // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 - if (timestamp < lastTimestamp) { - throw new RuntimeException(String.format( - "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); - } - - // 如果是同一时间生成的,则进行毫秒内序列 - if (lastTimestamp == timestamp) { - sequence = (sequence + 1) & sequenceMask; - // 毫秒内序列溢出 - if (sequence == 0) { - // 阻塞到下一个毫秒,获得新的时间戳 - timestamp = tilNextMillis(lastTimestamp); - } - } - // 时间戳改变,毫秒内序列重置 - else { - sequence = 0L; - } - - // 上次生成ID的时间截 - lastTimestamp = timestamp; - - // 移位并通过或运算拼到一起组成64位的ID - return ((timestamp - twepoch) << timestampLeftShift) // - | (datacenterId << datacenterIdShift) // - | (workerId << workerIdShift) // - | sequence; - } - - /** - * 阻塞到下一个毫秒,直到获得新的时间戳 - * - * @param lastTimestamp - * 上次生成ID的时间截 - * @return 当前时间戳 - */ - protected long tilNextMillis(long lastTimestamp) { - long timestamp = timeGen(); - while (timestamp <= lastTimestamp) { - timestamp = timeGen(); - } - return timestamp; - } - - /** - * 返回以毫秒为单位的当前时间 - * - * @return 当前时间(毫秒) - */ - protected long timeGen() { - return System.currentTimeMillis(); - } - -} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9042525..d6f7f95 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,12 +4,12 @@ #spring.datasource.password=root #spring.datasource.driver-class-name=org.postgresql.Driver - +spring.datasource.initSize=20 spring.datasource.master.url=jdbc:postgresql://127.0.0.1:5432/sonar7.7 #spring.datasource.url=jdbc:postgresql://117.50.14.123:5432/sonar spring.datasource.master.username=root spring.datasource.master.password=root -spring.datasource.master.driverClass=org.postgresql.Driver +spring.datasource.master.driverClassName=org.postgresql.Driver spring.datasource.readonly.driverClassName=com.mysql.jdbc.Driver