master
youys 3 years ago
parent f106919d74
commit 4f2e5d13d9

18
.gitignore vendored

@ -0,0 +1,18 @@
/.classpath
/.project
/.settings/
/target/
/.idea
*.iml
.idea
*.project
*.classpath
.metadata
.settings
**/**/target
*.factorypath
out/
logs/
*.DS_Store
logs/
log/

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.educoder</groupId>
<artifactId>automated_evaluation</artifactId>
<version>1.0-SNAPSHOT</version>
<description>自动化评测接口异常通知</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
</parent>
<properties>
<hutool.version>5.8.0.M2</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,29 @@
package net.educoder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@RestController
@SpringBootApplication
@EnableScheduling
@EnableAsync
public class AutomatedEvaluationApplication {
public static void main(String[] args) {
SpringApplication.run(AutomatedEvaluationApplication.class, args);
}
@GetMapping("/")
public String hello() {
return "hello";
}
}

@ -0,0 +1,74 @@
package net.educoder.common;
import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description: .
*/
@Slf4j
public class DingTalk {
private String token;
private String secret;
public DingTalk(String token, String secret) {
this.token = token;
this.secret = secret;
}
/**
* .
*
* @param message
* @return string
*/
public String sendMessage(String message) {
String url = getUrl();
JSONObject jsonObject = new JSONObject();
//固定参数
jsonObject.put("msgtype", "text");
JSONObject content = new JSONObject();
//此处message是你想要发送到钉钉的信息
content.put("content", message);
jsonObject.put("text", content);
try {
return HttpUtil.post(url, jsonObject.toJSONString());
} catch (Exception e) {
log.error("发送钉钉消息异常", e);
return null;
}
}
private String getUrl() {
String baseUrl = "https://oapi.dingtalk.com/robot/send?access_token=";
long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + secret;
// MAC加密算法
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return baseUrl + token + "&timestamp=" + timestamp + "&sign=" +
URLEncoder.encode(Base64.encode(signData), "UTF-8");
} catch (Exception e) {
return null;
}
}
}

@ -0,0 +1,53 @@
package net.educoder.config;
import net.educoder.common.DingTalk;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@Configuration
public class AppConfig {
@Autowired
private PropertiesConfig propertiesConfig;
@Bean("dingTalkEvaluation")
public DingTalk dingTalkEvaluation() {
return new DingTalk(propertiesConfig.getToken(), propertiesConfig.getSecret());
}
/**
* 线
* @return
*/
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(50);
// 设置最大线程数
executor.setMaxPoolSize(50);
// 设置队列容量
executor.setQueueCapacity(200);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(10);
// 设置默认线程名称
executor.setThreadNamePrefix("task-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}

@ -0,0 +1,29 @@
package net.educoder.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@Configuration
public class PropertiesConfig {
@Value("${evaluation.token}")
private String token;
@Value("${evaluation.secret}")
private String secret;
public String getToken() {
return token;
}
public String getSecret() {
return secret;
}
}

@ -0,0 +1,79 @@
package net.educoder.controller;
import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.educoder.common.DingTalk;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@Slf4j
@RestController
@RequestMapping("/callback")
public class CallbackController {
@Autowired
private DingTalk dingTalk;
private static final String RESULT = "success";
private final List<String> ERROR_MSG_LIST = Arrays.asList("系统繁忙,请稍后重试",
"程序执行失败导致评测提前终止,请稍后重试或联系系统管理员",
"本次评测网络延迟较高资源无法正常加载请10s后重试。",
"实验环境存在问题或已被回收,请保存数据再重置实训重新评测。",
"当前实验环境正在更新中,请稍后重试或联系系统管理员!",
"当前网络较差,代码下载超时,请稍后重试!",
"评测脚本设置异常,建议您在实训的配置页面重新选择或修改评测脚本"
);
@PostMapping("/evaluation")
public String evaluation(@RequestBody Map<String, Object> params) {
log.info("回调请求参数:{}", params);
if (params == null) {
return RESULT;
}
JSONObject jsonTestDetails = JSONObject.parseObject(params.get("jsonTestDetails").toString());
String outPut = jsonTestDetails.getString("outPut");
String tpiID = jsonTestDetails.getString("tpiID");
String decodeOutPut = Base64.decodeStr(outPut);
log.info("tpiID:{}, decodeOutPut:{}", tpiID, decodeOutPut);
if (match(decodeOutPut)) {
log.info("tpiID:{},需要发送钉钉通知", tpiID);
// 钉钉通知
StringBuilder sb = new StringBuilder();
sb.append("【自动化评测】tpiID:").append(tpiID).append(" 错误:消息").append(decodeOutPut);
String result = dingTalk.sendMessage(sb.toString());
log.info("发送钉钉通知结果:{}", result);
}
return RESULT;
}
/**
* .
*
* @param output
* @return
*/
private boolean match(String output) {
for (String s : ERROR_MSG_LIST) {
if (output.contains(s)) {
return true;
}
}
return false;
}
}

@ -0,0 +1,12 @@
package net.educoder.dao;
import net.educoder.model.AutoEvaParamConfig;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
public interface AutoEvaParamConfigRepository extends JpaRepository<AutoEvaParamConfig,Long> {
}

@ -0,0 +1,109 @@
package net.educoder.model;
import org.hibernate.annotations.Table;
import javax.annotation.Generated;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;
@Entity
public class AutoEvaParamConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer type;
private String tpiId;
private String tpiGitUrl;
private String buildId;
private String testCases;
private String extras;
private Date createTime;
private String codeFileContent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getTpiId() {
return tpiId;
}
public void setTpiId(String tpiId) {
this.tpiId = tpiId == null ? null : tpiId.trim();
}
public String getTpiGitUrl() {
return tpiGitUrl;
}
public void setTpiGitUrl(String tpiGitUrl) {
this.tpiGitUrl = tpiGitUrl == null ? null : tpiGitUrl.trim();
}
public String getBuildId() {
return buildId;
}
public void setBuildId(String buildId) {
this.buildId = buildId == null ? null : buildId.trim();
}
public String getTestCases() {
return testCases;
}
public void setTestCases(String testCases) {
this.testCases = testCases == null ? null : testCases.trim();
}
public String getExtras() {
return extras;
}
public void setExtras(String extras) {
this.extras = extras == null ? null : extras.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getCodeFileContent() {
return codeFileContent;
}
public void setCodeFileContent(String codeFileContent) {
this.codeFileContent = codeFileContent == null ? null : codeFileContent.trim();
}
public static final Integer SX_EVA = 0;
public static final Integer OJ_EVA = 1;
}

@ -0,0 +1,26 @@
package net.educoder.service;
import net.educoder.dao.AutoEvaParamConfigRepository;
import net.educoder.model.AutoEvaParamConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@Service
public class AutoEvaParamConfigService {
@Autowired
private AutoEvaParamConfigRepository autoEvaParamConfigRepository;
public List<AutoEvaParamConfig> findAll() {
return autoEvaParamConfigRepository.findAll();
}
}

@ -0,0 +1,110 @@
package net.educoder.shedule;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.educoder.model.AutoEvaParamConfig;
import net.educoder.service.AutoEvaParamConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @Author: youys
* @Date: 2022/4/2
* @Description:
*/
@Slf4j
@Component
public class EvaCheckWarningTask {
@Value("${bridge.evaluation}")
private String gameEvaluationUrl;
@Value("${bridge.ojEvaluation}")
private String ojEvaluationUrl;
@Value("${bridge.callbackUrl}")
private String callbackUrl;
@Autowired
private AutoEvaParamConfigService autoEvaParamConfigService;
@Async
@Scheduled(initialDelay = 5 * 1000, fixedRate = 30 * 1000)
public void check() {
log.info("自动化评测接口告警检查---------start ");
List<AutoEvaParamConfig> autoEvaParamConfigList = autoEvaParamConfigService.findAll();
log.info(JSONObject.toJSONString(autoEvaParamConfigList));
for (AutoEvaParamConfig autoEvaParamConfig : autoEvaParamConfigList) {
try {
if (autoEvaParamConfig.getType() == 0) {
String extras = autoEvaParamConfig.getExtras();
if (!StringUtils.hasLength(extras)) {
continue;
}
postEvaluation(autoEvaParamConfig);
} else if (autoEvaParamConfig.getType() == 1) {
if (!StringUtils.hasLength(autoEvaParamConfig.getCodeFileContent())) {
continue;
}
postOjEvaluation(autoEvaParamConfig);
}
} catch (Exception e) {
log.error("自动化评测接口告警检查任务异常", e);
}
}
log.info("自动化评测接口告警检查 ---------end ");
}
private void postEvaluation(AutoEvaParamConfig autoEvaParamConfig) {
JSONObject extrasObject = JSONObject.parseObject(autoEvaParamConfig.getExtras());
Map<String, Object> param = new HashMap<>();
param.put("isPublished", extrasObject.getInteger("isPublished"));
param.put("trimBlank", extrasObject.getInteger("trimBlank"));
param.put("containers", extrasObject.getString("containers"));
param.put("instanceChallenge", extrasObject.getString("instanceChallenge"));
param.put("tpmScript", extrasObject.getString("tpmScript"));
param.put("timeLimit", extrasObject.getInteger("timeLimit"));
param.put("times", extrasObject.getInteger("times"));
param.put("resubmit", extrasObject.getInteger("resubmit"));
param.put("podType", extrasObject.getInteger("podType"));
param.put("content_modified", extrasObject.getInteger("content_modified"));
param.put("tpiID", autoEvaParamConfig.getTpiId());
param.put("testCases", autoEvaParamConfig.getTestCases());
param.put("tpiGitURL", autoEvaParamConfig.getTpiGitUrl());
param.put("buildID", autoEvaParamConfig.getBuildId());
param.put("sec_key", UUID.randomUUID().toString());
param.put("callBackUrl", callbackUrl);
String result = HttpUtil.post(gameEvaluationUrl, param);
JSONObject jsonResult = JSONObject.parseObject(result);
log.info("实训评测接口返回:{},tpiId:{}", jsonResult, autoEvaParamConfig.getTpiId());
}
private void postOjEvaluation(AutoEvaParamConfig autoEvaParamConfig) {
Map<String, Object> param = new HashMap<>(8);
param.put("tpiID", autoEvaParamConfig.getTpiId());
param.put("testCases", autoEvaParamConfig.getTestCases());
param.put("codeFileContent", autoEvaParamConfig.getCodeFileContent());
param.put("sec_key", UUID.randomUUID().toString());
param.put("callBackUrl", callbackUrl);
String result = HttpUtil.post(ojEvaluationUrl, param);
JSONObject jsonResult = JSONObject.parseObject(result);
log.info("Oj评测接口返回:{},tpiId:{}", jsonResult, autoEvaParamConfig.getTpiId());
}
}

@ -0,0 +1,9 @@
## 评测钉钉机器人配置
evaluation:
token: 1069c45d4dfab187bb95599faeaf3687b630f79eda0d6fff914a18ee9e25db6f
secret: SEC7431097248ee9c19b9d7a2c61883e29082bea79c88c3e8f02e5fa121c2837dbc
bridge:
evaluation: http://pre-bridge.educoder.net/bridge/game/gameEvaluate
ojEvaluation: http://pre-bridge.educoder.net/bridge/ojs/evaluate
callbackUrl: http://1c32-120-228-142-52.ngrok.io/callback/evaluation

@ -0,0 +1,9 @@
## 评测钉钉机器人配置
evaluation:
token: 1069c45d4dfab187bb95599faeaf3687b630f79eda0d6fff914a18ee9e25db6f
secret: SEC7431097248ee9c19b9d7a2c61883e29082bea79c88c3e8f02e5fa121c2837dbc
bridge:
evaluation: https://www.educoder.net/bridge/game/gameEvaluate
ojEvaluation: https://www.educoder.net/bridge/ojs/evaluate
callbackUrl: https://1c32-120-228-142-52.ngrok.io/callback/evaluation

@ -0,0 +1,25 @@
server:
port: 8002
servlet:
context-path: /
spring:
profiles:
active: dev
# 数据库配置
datasource:
url: jdbc:mysql://rm-bp13v5020p7828r5rso.mysql.rds.aliyuncs.com:3306/testbridge?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
username: testeducoder
password: TEST@123
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 50
minIdle: 50
maxActive: 100
validationQuery: SELECT 1
# redis配置
redis:
host: 127.0.0.1
port: 6379
password:
Loading…
Cancel
Save