Compare commits
8 Commits
6c462655b9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba36cfdfa2 | ||
| 5a27c07bba | |||
| 418674806b | |||
| 9b13c95e8a | |||
| 0e5cac3d4e | |||
| c22c788cfa | |||
| 90010113dd | |||
| 2b699a185b |
@@ -1,5 +1,7 @@
|
||||
# 调试说明
|
||||
|
||||
该项目为局域网本地项目。不需要考虑安全问题。
|
||||
|
||||
## 调试环境
|
||||
|
||||
- 目标网址:http://192.168.0.96/
|
||||
@@ -14,4 +16,12 @@
|
||||
2. 将提交 push 到服务器端对应仓库。
|
||||
3. 打开网页端提供的 git pull API 触发更新,使服务器拉取最新代码。
|
||||
|
||||
如需验证调试环境,优先使用上述账号登录目标网址,并通过网页端 git pull API 完成代码同步。
|
||||
如需验证调试环境,优先使用上述账号登录目标网址,并通过网页端 git pull API 完成代码同步。
|
||||
|
||||
## 数据库
|
||||
```
|
||||
static $DB_HOST="localhost"; //数据库服务器ip或域名
|
||||
static $DB_NAME="jol"; //数据库名
|
||||
static $DB_USER="debian-sys-maint"; //数据库账户
|
||||
static $DB_PASS="GJfFMuAmScKHHa32"; //数据库密码
|
||||
```
|
||||
@@ -1220,6 +1220,17 @@ void addreinfo(int solution_id)
|
||||
}
|
||||
}
|
||||
|
||||
void add_system_error_info(int solution_id, const char *message)
|
||||
{
|
||||
FILE *fp = fopen("error.out", "a");
|
||||
if (fp != NULL)
|
||||
{
|
||||
fprintf(fp, "system:%s\n", message);
|
||||
fclose(fp);
|
||||
}
|
||||
addreinfo(solution_id);
|
||||
}
|
||||
|
||||
void adddiffinfo(int solution_id)
|
||||
{
|
||||
|
||||
@@ -1670,7 +1681,7 @@ int check_mysql_conn(){
|
||||
#endif
|
||||
|
||||
#ifdef _mysql_h
|
||||
void _get_solution_mysql(int solution_id, char *work_dir, int lang)
|
||||
bool _get_solution_mysql(int solution_id, char *work_dir, int lang)
|
||||
{
|
||||
char sql[BUFFER_SIZE], src_pth[BUFFER_SIZE];
|
||||
// get the source code
|
||||
@@ -1678,7 +1689,8 @@ void _get_solution_mysql(int solution_id, char *work_dir, int lang)
|
||||
MYSQL_ROW row;
|
||||
sprintf(sql, "SELECT source FROM source_code WHERE solution_id=%d",
|
||||
solution_id);
|
||||
mysql_real_query(conn, sql, strlen(sql));
|
||||
if (mysql_real_query(conn, sql, strlen(sql)))
|
||||
return false;
|
||||
res = mysql_store_result(conn);
|
||||
|
||||
// create the src file
|
||||
@@ -1691,16 +1703,23 @@ void _get_solution_mysql(int solution_id, char *work_dir, int lang)
|
||||
if (DEBUG)
|
||||
printf("Main=%s", src_pth);
|
||||
FILE *fp_src = fopen(src_pth, "we");
|
||||
if (fp_src == NULL) {
|
||||
mysql_free_result(res);
|
||||
return false;
|
||||
}
|
||||
fprintf(fp_src, "%s", row[0]);
|
||||
mysql_free_result(res); // free the memory
|
||||
res = NULL;
|
||||
row = NULL;
|
||||
fclose(fp_src);
|
||||
return true;
|
||||
}
|
||||
mysql_free_result(res);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
void _get_solution_http(int solution_id, char *work_dir, int lang)
|
||||
bool _get_solution_http(int solution_id, char *work_dir, int lang)
|
||||
{
|
||||
char src_pth[BUFFER_SIZE];
|
||||
|
||||
@@ -1716,22 +1735,26 @@ void _get_solution_http(int solution_id, char *work_dir, int lang)
|
||||
FILE *pout = read_cmd_output(cmd2, solution_id, src_pth, http_baseurl, http_apipath);
|
||||
|
||||
pclose(pout);
|
||||
return access(src_pth, R_OK) == 0;
|
||||
}
|
||||
void get_solution(int solution_id, char *work_dir, int lang,int p_id)
|
||||
bool get_solution(int solution_id, char *work_dir, int lang,int p_id)
|
||||
{
|
||||
char src_pth[BUFFER_SIZE];
|
||||
bool ret = false;
|
||||
sprintf(src_pth, "Main.%s", lang_ext[lang]);
|
||||
if (http_judge)
|
||||
{
|
||||
_get_solution_http(solution_id, work_dir, lang);
|
||||
ret = _get_solution_http(solution_id, work_dir, lang);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#ifdef _mysql_h
|
||||
_get_solution_mysql(solution_id, work_dir, lang);
|
||||
ret = _get_solution_mysql(solution_id, work_dir, lang);
|
||||
#endif
|
||||
}
|
||||
if (!ret)
|
||||
return false;
|
||||
if(lang == LANG_PYTHON ){ // 从源码中搜索python2字样,失败的结果非零默认python3,成功的结果为0是python2
|
||||
py2 = execute_cmd("/bin/grep 'python2' %s/Main.py > /dev/null", work_dir);
|
||||
execute_cmd("sed -i 's/import.*os//g' %s/%s", work_dir, src_pth);
|
||||
@@ -1740,6 +1763,7 @@ void get_solution(int solution_id, char *work_dir, int lang,int p_id)
|
||||
execute_cmd("cp %s/../data/%d/sb3/%d.sb3 %s", work_dir,p_id,solution_id, src_pth);
|
||||
}
|
||||
execute_cmd("chown judge %s/%s", work_dir, src_pth);
|
||||
return access(src_pth, R_OK) == 0;
|
||||
}
|
||||
|
||||
#ifdef _mysql_h
|
||||
@@ -1804,7 +1828,7 @@ void get_custominput(int solution_id, char *work_dir)
|
||||
}
|
||||
|
||||
#ifdef _mysql_h
|
||||
void _get_solution_info_mysql(int solution_id, int & p_id, char * user_id,
|
||||
bool _get_solution_info_mysql(int solution_id, int & p_id, char * user_id,
|
||||
int & lang,int &cid) {
|
||||
|
||||
MYSQL_RES *res;
|
||||
@@ -1818,7 +1842,8 @@ void _get_solution_info_mysql(int solution_id, int & p_id, char * user_id,
|
||||
"insert into solution2 select * FROM solution where solution_id=%d",
|
||||
solution_id);
|
||||
//printf("%s\n",sql);
|
||||
mysql_real_query(conn, sql, strlen(sql));
|
||||
if (mysql_real_query(conn, sql, strlen(sql)))
|
||||
return false;
|
||||
sprintf(sql,
|
||||
"SELECT problem_id, user_id, language,contest_id FROM solution2 where solution_id=%d",
|
||||
solution_id);
|
||||
@@ -1831,9 +1856,16 @@ void _get_solution_info_mysql(int solution_id, int & p_id, char * user_id,
|
||||
solution_id);
|
||||
}
|
||||
//printf("%s\n",sql);
|
||||
mysql_real_query(conn, sql, strlen(sql));
|
||||
if (mysql_real_query(conn, sql, strlen(sql)))
|
||||
return false;
|
||||
res = mysql_store_result(conn);
|
||||
if (res == NULL)
|
||||
return false;
|
||||
row = mysql_fetch_row(res);
|
||||
if (row == NULL) {
|
||||
mysql_free_result(res);
|
||||
return false;
|
||||
}
|
||||
p_id = atoi(row[0]);
|
||||
strcpy(user_id, row[1]);
|
||||
lang = atoi(row[2]);
|
||||
@@ -1844,9 +1876,10 @@ void _get_solution_info_mysql(int solution_id, int & p_id, char * user_id,
|
||||
mysql_free_result(res); // free the memory
|
||||
res=NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
void _get_solution_info_http(int solution_id, int & p_id, char * user_id,
|
||||
bool _get_solution_info_http(int solution_id, int & p_id, char * user_id,
|
||||
int & lang,int & cid) {
|
||||
|
||||
login();
|
||||
@@ -1854,26 +1887,29 @@ void _get_solution_info_http(int solution_id, int & p_id, char * user_id,
|
||||
const char *cmd =
|
||||
"wget --post-data=\"getsolutioninfo=1&sid=%d\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s%s\"";
|
||||
FILE *pout = read_cmd_output(cmd, solution_id, http_baseurl, http_apipath);
|
||||
if(1!=fscanf(pout, "%d", &p_id)) printf("http problem_id read fail...\n");
|
||||
if(1!=fscanf(pout, "%s", user_id)) printf("http user_id read fail ... \n") ;
|
||||
if(1!=fscanf(pout, "%d", &lang)) printf("http language read fail ... \n") ;
|
||||
if(1!=fscanf(pout, "%d", &cid)) printf("http contest_id read fail ... \n") ;
|
||||
bool ret = true;
|
||||
if(1!=fscanf(pout, "%d", &p_id)) { printf("http problem_id read fail...\n"); ret = false; }
|
||||
if(1!=fscanf(pout, "%s", user_id)) { printf("http user_id read fail ... \n") ; ret = false; }
|
||||
if(1!=fscanf(pout, "%d", &lang)) { printf("http language read fail ... \n") ; ret = false; }
|
||||
if(1!=fscanf(pout, "%d", &cid)) { printf("http contest_id read fail ... \n") ; ret = false; }
|
||||
pclose(pout);
|
||||
return ret;
|
||||
}
|
||||
void get_solution_info(int solution_id, int & p_id, char * user_id,
|
||||
bool get_solution_info(int solution_id, int & p_id, char * user_id,
|
||||
int & lang,int & cid) {
|
||||
|
||||
if (http_judge) {
|
||||
_get_solution_info_http(solution_id, p_id, user_id, lang,cid);
|
||||
return _get_solution_info_http(solution_id, p_id, user_id, lang,cid);
|
||||
} else {
|
||||
#ifdef _mysql_h
|
||||
_get_solution_info_mysql(solution_id, p_id, user_id, lang,cid);
|
||||
return _get_solution_info_mysql(solution_id, p_id, user_id, lang,cid);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _mysql_h
|
||||
void _get_problem_info_mysql(int p_id, double &time_lmt, int &mem_lmt,
|
||||
bool _get_problem_info_mysql(int p_id, double &time_lmt, int &mem_lmt,
|
||||
int &spj)
|
||||
{
|
||||
// get the problem info from Table:problem
|
||||
@@ -1883,9 +1919,16 @@ void _get_problem_info_mysql(int p_id, double &time_lmt, int &mem_lmt,
|
||||
sprintf(sql,
|
||||
"SELECT time_limit,memory_limit,spj FROM problem where problem_id=%d",
|
||||
p_id);
|
||||
mysql_real_query(conn, sql, strlen(sql));
|
||||
if (mysql_real_query(conn, sql, strlen(sql)))
|
||||
return false;
|
||||
res = mysql_store_result(conn);
|
||||
if (res == NULL)
|
||||
return false;
|
||||
row = mysql_fetch_row(res);
|
||||
if (row == NULL) {
|
||||
mysql_free_result(res);
|
||||
return false;
|
||||
}
|
||||
time_lmt = atof(row[0]);
|
||||
mem_lmt = atoi(row[1]);
|
||||
spj = atoi(row[2]);
|
||||
@@ -1894,9 +1937,10 @@ void _get_problem_info_mysql(int p_id, double &time_lmt, int &mem_lmt,
|
||||
mysql_free_result(res); // free the memory
|
||||
res = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
void _get_problem_info_http(int p_id, double &time_lmt, int &mem_lmt,
|
||||
bool _get_problem_info_http(int p_id, double &time_lmt, int &mem_lmt,
|
||||
int &spj)
|
||||
{
|
||||
//login();
|
||||
@@ -1904,27 +1948,32 @@ void _get_problem_info_http(int p_id, double &time_lmt, int &mem_lmt,
|
||||
const char *cmd =
|
||||
"wget --post-data=\"getprobleminfo=1&pid=%d\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s%s\"";
|
||||
FILE *pout = read_cmd_output(cmd, p_id, http_baseurl, http_apipath);
|
||||
if(1!=fscanf(pout, "%lf", &time_lmt)) printf("http read time_limit fail...\n");
|
||||
if(1!=fscanf(pout, "%d", &mem_lmt) ) printf("http read memory_limit fail...\n");
|
||||
if(1!=fscanf(pout, "%d", &spj) ) printf("http read special judge fail...\n");
|
||||
bool ret = true;
|
||||
if(1!=fscanf(pout, "%lf", &time_lmt)) { printf("http read time_limit fail...\n"); ret = false; }
|
||||
if(1!=fscanf(pout, "%d", &mem_lmt) ) { printf("http read memory_limit fail...\n"); ret = false; }
|
||||
if(1!=fscanf(pout, "%d", &spj) ) { printf("http read special judge fail...\n"); ret = false; }
|
||||
pclose(pout);
|
||||
if(DEBUG) printf("time_lmt:%g\n",time_lmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void get_problem_info(int p_id, double &time_lmt, int &mem_lmt, int &spj)
|
||||
bool get_problem_info(int p_id, double &time_lmt, int &mem_lmt, int &spj)
|
||||
{
|
||||
if (http_judge)
|
||||
{
|
||||
_get_problem_info_http(p_id, time_lmt, mem_lmt, spj);
|
||||
if (!_get_problem_info_http(p_id, time_lmt, mem_lmt, spj))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _mysql_h
|
||||
_get_problem_info_mysql(p_id, time_lmt, mem_lmt, spj);
|
||||
if (!_get_problem_info_mysql(p_id, time_lmt, mem_lmt, spj))
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
if (time_lmt <= 0)
|
||||
time_lmt = 1;
|
||||
return true;
|
||||
}
|
||||
char *escape(char s[], char t[])
|
||||
{
|
||||
@@ -3455,6 +3504,8 @@ int main(int argc, char **argv)
|
||||
double time_lmt;
|
||||
char time_space_table[BUFFER_SIZE*100];
|
||||
int time_space_index=0;
|
||||
time_space_table[0]=0;
|
||||
user_id[0]=0;
|
||||
umask(0077);
|
||||
init_parameters(argc, argv, solution_id, runner_id);
|
||||
|
||||
@@ -3489,7 +3540,27 @@ int main(int argc, char **argv)
|
||||
|
||||
if (http_judge)
|
||||
if(!system("/bin/ln -s ../cookie ./")) printf("cookie link fail \n");
|
||||
get_solution_info(solution_id, p_id, user_id, lang,cid);
|
||||
if (!get_solution_info(solution_id, p_id, user_id, lang,cid)) {
|
||||
add_system_error_info(solution_id, "solution info not found");
|
||||
update_solution(solution_id, OJ_RE, 0, 0, 0, 0, 0.0);
|
||||
#ifdef _mysql_h
|
||||
if (!http_judge)
|
||||
mysql_close(conn);
|
||||
#endif
|
||||
clean_workdir(work_dir);
|
||||
exit(0);
|
||||
}
|
||||
if (lang < 0 || lang >= (int)(sizeof(lang_ext) / sizeof(lang_ext[0]))) {
|
||||
add_system_error_info(solution_id, "invalid language id");
|
||||
update_solution(solution_id, OJ_RE, 0, 0, 0, 0, 0.0);
|
||||
if(!turbo_mode)update_user(user_id);
|
||||
#ifdef _mysql_h
|
||||
if (!http_judge)
|
||||
mysql_close(conn);
|
||||
#endif
|
||||
clean_workdir(work_dir);
|
||||
exit(0);
|
||||
}
|
||||
//get the limit
|
||||
|
||||
if (p_id == 0)
|
||||
@@ -3500,11 +3571,33 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
get_problem_info(p_id, time_lmt, mem_lmt, spj);
|
||||
if (!get_problem_info(p_id, time_lmt, mem_lmt, spj)) {
|
||||
add_system_error_info(solution_id, "problem info not found");
|
||||
update_solution(solution_id, OJ_RE, 0, 0, 0, 0, 0.0);
|
||||
if(!turbo_mode)update_user(user_id);
|
||||
if(!turbo_mode)update_problem(p_id,cid);
|
||||
#ifdef _mysql_h
|
||||
if (!http_judge)
|
||||
mysql_close(conn);
|
||||
#endif
|
||||
clean_workdir(work_dir);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
//copy source file
|
||||
|
||||
get_solution(solution_id, work_dir, lang,p_id);
|
||||
if (!get_solution(solution_id, work_dir, lang,p_id)) {
|
||||
add_system_error_info(solution_id, "source code not found");
|
||||
update_solution(solution_id, OJ_RE, 0, 0, 0, 0, 0.0);
|
||||
if(!turbo_mode)update_user(user_id);
|
||||
if(!turbo_mode)update_problem(p_id,cid);
|
||||
#ifdef _mysql_h
|
||||
if (!http_judge)
|
||||
mysql_close(conn);
|
||||
#endif
|
||||
clean_workdir(work_dir);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//java and other VM language are lucky to have the global bonus in judge.conf
|
||||
if (lang >= LANG_JAVA && lang != LANG_OBJC && lang != LANG_CLANG && lang != LANG_CLANGPP && lang != LANG_GO )
|
||||
@@ -3581,11 +3674,18 @@ int main(int argc, char **argv)
|
||||
if(p_id > 0 && namelist_len == -1 ){
|
||||
|
||||
write_log("No such dir:%s!\n", fullpath);
|
||||
char error[BUFFER_SIZE];
|
||||
snprintf(error, BUFFER_SIZE - 1, "test data directory not found: %s", fullpath);
|
||||
add_system_error_info(solution_id, error);
|
||||
update_solution(solution_id, OJ_RE, 0, 0, 0, 0, 0.0);
|
||||
if(!turbo_mode)update_user(user_id);
|
||||
if(!turbo_mode)update_problem(p_id,cid);
|
||||
#ifdef _mysql_h
|
||||
if (!http_judge)
|
||||
mysql_close(conn);
|
||||
#endif
|
||||
exit(-1);
|
||||
clean_workdir(work_dir);
|
||||
exit(0);
|
||||
|
||||
}else{
|
||||
if(DEBUG){
|
||||
@@ -3702,6 +3802,7 @@ int main(int argc, char **argv)
|
||||
|
||||
}
|
||||
char last_name[BUFFER_SIZE];
|
||||
last_name[0]=0;
|
||||
int minus_mark=0;
|
||||
char path_buf[BUFFER_SIZE];
|
||||
sprintf(path_buf,"%s/data/%d/",oj_home,p_id);
|
||||
|
||||
@@ -81,7 +81,7 @@ static $OJ_BENCHMARK_MODE=false; //此选项仅供测试用,不是正常功
|
||||
static $OJ_CONTEST_RANK_FIX_HEADER=false; //比赛排名水平滚动时固定名单
|
||||
static $OJ_NOIP_KEYWORD="noip"; // 标题包含此关键词,激活noip模式,赛中不显示结果,仅保留最后一次提交。
|
||||
static $OJ_BEIAN=false; // 如果有备案号,填写备案号
|
||||
static $OJ_RANK_HIDDEN="'admin','super','szx','sen'"; // 管理员不显示在排名中
|
||||
static $OJ_RANK_HIDDEN="'admin','super','szx','sen','copilot'"; // 管理员不显示在排名中
|
||||
static $OJ_FRIENDLY_LEVEL=0; //系统友好级别,暂定0-9级,级别越高越傻瓜,系统易用度高的同时将降低安全性,仅供非专业用途,造成泄题、抄袭概不负责。
|
||||
static $OJ_FREE_PRACTICE=true; //自由练习,不受比赛作业用题限制
|
||||
static $OJ_SUBMIT_COOLDOWN_TIME=1; //提交冷却时间,连续两次提交的最小间隔,单位秒。
|
||||
|
||||
185
web/reinfo.php
185
web/reinfo.php
@@ -102,6 +102,145 @@ function dedup_runtimeinfo($text, &$dedup_count) {
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 runtimeinfo.error 解析为结构化的 diff blocks
|
||||
* 返回 [['name' => 'test1.out', 'expected' => [...], 'yours' => [...], 'full_mode' => bool], ...]
|
||||
* 解析失败返回空数组
|
||||
*/
|
||||
function parse_diff_blocks($text) {
|
||||
$blocks = array();
|
||||
if (empty($text)) return $blocks;
|
||||
|
||||
// 先按 "\n\n" 切分(simple mode 每个测试点之间有空行)
|
||||
$chunks = preg_split('/\n\s*\n/', $text);
|
||||
foreach ($chunks as $chunk) {
|
||||
$chunk = trim($chunk);
|
||||
if ($chunk === '') continue;
|
||||
|
||||
// 格式 1: 整段就是一个 =====[name]===== 块(包含表头+数据+结尾 ====)
|
||||
// 形如 "========[test1.out]========\nExpected | Yours\nFail | Fall\n=============================="
|
||||
if (preg_match('/^=+\[([^\]]+)\]\=+(.+?)\n=+\s*$/s', $chunk, $m)) {
|
||||
$name = trim($m[1]);
|
||||
$body = $m[2];
|
||||
// body 形如 "\nExpected | Yours\nFail | Fall"
|
||||
// 去掉首尾的换行符
|
||||
$body = trim($body, "\n\r");
|
||||
$lines_in_body = explode("\n", $body);
|
||||
$expected = array();
|
||||
$yours = array();
|
||||
$found_header = false;
|
||||
foreach ($lines_in_body as $bl) {
|
||||
$bl = rtrim($bl);
|
||||
if ($bl === '') continue;
|
||||
// 跳过表头
|
||||
if (!$found_header && preg_match('/^Expected.*\|.*Yours/i', $bl)) {
|
||||
$found_header = true;
|
||||
continue;
|
||||
}
|
||||
if (!$found_header && (preg_match('/^\|?--\s*\|/', $bl) || trim($bl) === '--')) {
|
||||
continue;
|
||||
}
|
||||
$parsed = parse_one_diff_line($bl);
|
||||
if ($parsed !== null) {
|
||||
$expected[] = $parsed[0];
|
||||
$yours[] = $parsed[1];
|
||||
$found_header = true;
|
||||
} else {
|
||||
$expected[] = $bl;
|
||||
$yours[] = '';
|
||||
$found_header = true;
|
||||
}
|
||||
}
|
||||
if (!empty($expected)) {
|
||||
$blocks[] = array(
|
||||
'name' => $name,
|
||||
'expected' => $expected,
|
||||
'yours' => $yours,
|
||||
'full_mode' => true,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 格式 2: simple mode "test1.out\n--\n|Expected|Yours\n|--|--\n|row1|row2"
|
||||
if (preg_match('/^([^\n=|]+)\n--\n([\s\S]+)$/', $chunk, $m)) {
|
||||
$name = trim($m[1]);
|
||||
$body = $m[2];
|
||||
$lines_in_body = explode("\n", $body);
|
||||
$expected = array();
|
||||
$yours = array();
|
||||
$found_header = false;
|
||||
foreach ($lines_in_body as $bl) {
|
||||
$bl = rtrim($bl);
|
||||
if ($bl === '') continue;
|
||||
if (!$found_header && preg_match('/Expected.*\|.*Yours/i', $bl)) {
|
||||
$found_header = true;
|
||||
continue;
|
||||
}
|
||||
if (!$found_header && (preg_match('/^\|?--\s*\|/', $bl) || trim($bl) === '--')) {
|
||||
continue;
|
||||
}
|
||||
$parsed = parse_one_diff_line($bl);
|
||||
if ($parsed !== null) {
|
||||
$expected[] = $parsed[0];
|
||||
$yours[] = $parsed[1];
|
||||
$found_header = true;
|
||||
} else {
|
||||
$expected[] = $bl;
|
||||
$yours[] = '';
|
||||
$found_header = true;
|
||||
}
|
||||
}
|
||||
if (!empty($expected)) {
|
||||
$blocks[] = array(
|
||||
'name' => $name,
|
||||
'expected' => $expected,
|
||||
'yours' => $yours,
|
||||
'full_mode' => false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单行 "expected | yours" 格式
|
||||
* 支持 "|a|b"、"a | b"、"a|b" 等
|
||||
* 返回 [left, right] 或 null(无法解析)
|
||||
*/
|
||||
function parse_one_diff_line($line) {
|
||||
$line = rtrim($line);
|
||||
if ($line === '') return null;
|
||||
|
||||
// 格式 A: "|a|b" (首尾带 |)
|
||||
if ($line[0] === '|') {
|
||||
$rest = substr($line, 1);
|
||||
$pos = strpos($rest, '|');
|
||||
if ($pos !== false) {
|
||||
return array(substr($rest, 0, $pos), substr($rest, $pos + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// 格式 B: "a | b" 或 "a | b"(带空格的 markdown 风格)
|
||||
// 注意:可能行内有空格,所以用 " | " 或 " | " 等宽松匹配
|
||||
if (preg_match('/^(.*?)\s+\|\s+(.*)$/', $line, $m)) {
|
||||
// 确保左右两边都非空
|
||||
$left = trim($m[1]);
|
||||
$right = trim($m[2]);
|
||||
if ($left !== '' || $right !== '') {
|
||||
return array($left, $right);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式 C: "a<b" 或 "a>b"(diff -y 风格)
|
||||
if (preg_match('/^(.*?)\s+([<>])\s+(.*)$/', $line, $m)) {
|
||||
return array($m[1], $m[3]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$view_reinfo = "";
|
||||
$view_reinfo_raw = ""; // 原始(管理员/有权限者看)
|
||||
$view_reinfo_summary = ""; // 折叠后(普通用户看)
|
||||
@@ -109,7 +248,6 @@ $view_reinfo_dedup_count = 0; // 折叠掉的测试点数
|
||||
$verdict_color_class = "blue"; // semantic ui color: green/red/orange/yellow/grey
|
||||
$result_icon = "info circle";
|
||||
$verdict_tip = "";
|
||||
$verdict_hints = array();
|
||||
|
||||
if($ok){
|
||||
|
||||
@@ -121,7 +259,9 @@ if($ok){
|
||||
$rt = pdo_query($sql,$id);
|
||||
$raw_error = "";
|
||||
if(isset($rt[0])){
|
||||
$raw_error = str_replace("\n\r","\n",$rt[0]['error']);
|
||||
$raw_error = $rt[0]['error'];
|
||||
// 统一换行符:先替换 \r\n,再替换单独的 \r
|
||||
$raw_error = str_replace(array("\r\n", "\r"), "\n", $raw_error);
|
||||
}
|
||||
|
||||
// === 阶段 1:基于 solution.result 给"主解释"(永远非空)===
|
||||
@@ -143,75 +283,39 @@ if($ok){
|
||||
default: $verdict_color_class = "blue"; $result_icon = "info circle";
|
||||
}
|
||||
|
||||
// 友好提示
|
||||
// 友好提示:仅保留标题性文字,具体修复建议交给 AI 点评
|
||||
switch ($result_code) {
|
||||
case 4: // AC
|
||||
$verdict_tip = "本次提交通过了所有测试点";
|
||||
$verdict_hints[] = "注意时间/内存是否接近限制,考虑是否有更优的算法";
|
||||
$verdict_hints[] = "可以继续挑战更高难度或查看其他人的标程";
|
||||
break;
|
||||
case 5: // PE
|
||||
$verdict_tip = "输出与预期基本一致但格式不符(Presentation Error)";
|
||||
$verdict_hints[] = "对照样例:每行末尾是否多/少换行";
|
||||
$verdict_hints[] = "检查空格、大小写、标点符号";
|
||||
$verdict_hints[] = "PE 通常是细微的格式问题,请逐字符对比";
|
||||
break;
|
||||
case 6: // WA
|
||||
$verdict_tip = "输出与预期不符(Wrong Answer)";
|
||||
$verdict_hints[] = "对照样例手动模拟程序执行过程";
|
||||
$verdict_hints[] = "检查边界条件:n=0、n=1、极值、空输入";
|
||||
$verdict_hints[] = "注意 long long 溢出、int 范围";
|
||||
break;
|
||||
case 7: // TLE
|
||||
$verdict_tip = $MSG_HINT_TLE;
|
||||
$verdict_hints[] = "检查是否有死循环(循环变量未更新)";
|
||||
$verdict_hints[] = "优化算法复杂度(如 O(n²) → O(n log n))";
|
||||
$verdict_hints[] = "递归层数过深?改为迭代或加深栈";
|
||||
break;
|
||||
case 8: // MLE
|
||||
$verdict_tip = $MSG_HINT_MLE;
|
||||
$verdict_hints[] = "大数组开在函数内(栈)?改为全局或 new";
|
||||
$verdict_hints[] = "Java: 减小内存使用或调整 JVM 参数";
|
||||
$verdict_hints[] = "递归太深未释放,考虑显式清理";
|
||||
break;
|
||||
case 9: // OLE
|
||||
$verdict_tip = $MSG_HINT_OLE;
|
||||
$verdict_hints[] = "是否有死循环输出";
|
||||
$verdict_hints[] = "调试用的 print/cout 是否忘记删除";
|
||||
break;
|
||||
case 10: // RE
|
||||
$verdict_tip = "程序运行中发生错误(Runtime Error)";
|
||||
$verdict_hints[] = "数组下标是否越界(i<n 还是 i<=n)";
|
||||
$verdict_hints[] = "除以零、空指针、栈溢出";
|
||||
$verdict_hints[] = "递归爆栈:转迭代或加尾递归";
|
||||
break;
|
||||
case 11: // CE
|
||||
$verdict_tip = "编译错误(Compile Error)。请查看编译信息页。";
|
||||
break;
|
||||
case 13: // TR (test run)
|
||||
$verdict_tip = "测试运行完成(Test Run)";
|
||||
$verdict_hints[] = "你输入的测试数据已与程序输出对比";
|
||||
break;
|
||||
default:
|
||||
$verdict_tip = "正在评判,请稍候刷新";
|
||||
}
|
||||
|
||||
// === 阶段 2:syscall vs 真正越界 的区分 ===
|
||||
if ($result_code === 10) {
|
||||
if (preg_match('/Forbidden system call.*CALLID:(\d+)/', $raw_error, $m)) {
|
||||
$callid = $m[1];
|
||||
$verdict_hints[] = sprintf("系统调用 ID=%d 被判题器禁止,常见于直接操作文件/网络/进程", $callid);
|
||||
}
|
||||
// Java / Python / Go 的细化提示
|
||||
if (preg_match('/java\.lang\.(\w+Exception|\w+Error)/', $raw_error, $m2)) {
|
||||
$verdict_hints[] = "Java 异常类型:" . $m2[1];
|
||||
} elseif (preg_match('/Traceback \(most recent call last\)/', $raw_error)) {
|
||||
$verdict_hints[] = "Python 抛出异常,下方 traceback 从下往上读就是异常源头";
|
||||
} elseif (preg_match('/panic:.*?(index out of range|invalid memory address|nil pointer)/i', $raw_error)) {
|
||||
$verdict_hints[] = "Go 运行时报错:通常是越界访问或 nil 指针";
|
||||
}
|
||||
}
|
||||
|
||||
// === 阶段 3:原始错误处理(syscall 提示 vs 占位符修复)===
|
||||
$view_reinfo_raw = $raw_error;
|
||||
if (trim($raw_error) === "") {
|
||||
@@ -240,6 +344,9 @@ if($ok){
|
||||
|
||||
$view_reinfo = $view_reinfo_summary;
|
||||
|
||||
// 尝试解析为结构化的 diff 数据(用于 WA/PE/AC 时的对比展示)
|
||||
$diff_blocks = parse_diff_blocks($raw_error);
|
||||
|
||||
// 管理员或 source_browser 永远看完整原文
|
||||
$show_raw = $is_admin_session || $is_source_browser;
|
||||
if ($show_raw) {
|
||||
|
||||
108
web/submit.php
108
web/submit.php
@@ -3,19 +3,37 @@ require_once "include/db_info.inc.php";
|
||||
require_once "include/my_func.inc.php";
|
||||
require_once "include/email.class.php";
|
||||
require_once "include/base64.php";
|
||||
require_once "include/const.inc.php";
|
||||
|
||||
$is_ajax_submit = isset($_GET['ajax']) || isset($_POST['ajax']);
|
||||
|
||||
function submit_error($message) {
|
||||
global $OJ_TEMPLATE, $is_ajax_submit, $view_errors;
|
||||
|
||||
if ($is_ajax_submit) {
|
||||
header("Content-Type: text/plain; charset=UTF-8");
|
||||
$text = trim(html_entity_decode(strip_tags($message), ENT_QUOTES, "UTF-8"));
|
||||
if ($text === "") {
|
||||
$text = "Submit failed.";
|
||||
}
|
||||
echo $text;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$view_errors = $message;
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(isset($OJ_CSRF) && $OJ_CSRF && $OJ_TEMPLATE=="syzoj" && !isset($_SESSION[$OJ_NAME.'_'.'http_judge']))
|
||||
require_once(dirname(__FILE__)."/include/csrf_check.php");
|
||||
|
||||
if (!isset($_SESSION[$OJ_NAME . '_' . 'user_id'])) {
|
||||
|
||||
$view_errors = "<a href=loginpage.php>$MSG_Login</a>";
|
||||
require("template/".$OJ_TEMPLATE."/error.php");
|
||||
exit(0);
|
||||
submit_error("<a href=loginpage.php>$MSG_Login</a>");
|
||||
}
|
||||
|
||||
require_once "include/memcache.php";
|
||||
require_once "include/const.inc.php";
|
||||
|
||||
$now = strftime("%Y-%m-%d %H:%M", time());
|
||||
$user_id = $_SESSION[$OJ_NAME.'_'.'user_id'];
|
||||
@@ -41,10 +59,8 @@ if (!$OJ_BENCHMARK_MODE) {
|
||||
$_SESSION[$OJ_NAME.'_'."vcode"] = null;
|
||||
$err_str = $err_str.$MSG_VCODE_WRONG."\n";
|
||||
$err_cnt++;
|
||||
$view_errors = $err_str;
|
||||
$_SESSION[ $OJ_NAME . '_' . "vfail" ]=true;
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
submit_error($err_str);
|
||||
}else{
|
||||
$_SESSION[ $OJ_NAME . '_' . "vfail" ]=false;
|
||||
}
|
||||
@@ -74,24 +90,13 @@ else {
|
||||
if(!$test_run){
|
||||
$res = mysql_query_cache($sql);
|
||||
if (isset($res) && count($res)<1 && !isset($_SESSION[$OJ_NAME.'_'.'administrator']) && !((isset($cid)&&$cid<=0) || (isset($id)&&$id<=0))) {
|
||||
$view_errors = $MSG_LINK_ERROR."<br>";
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_LINK_ERROR."<br>");
|
||||
}
|
||||
}
|
||||
if ($res[0][1]!='N' && !($test_run||isset($_SESSION[$OJ_NAME.'_'.'administrator']))) {
|
||||
if (!$test_run && isset($res[0]) && $res[0][1]!='N' && !isset($_SESSION[$OJ_NAME.'_'.'administrator'])) {
|
||||
// echo "res:$res,count:".count($res);
|
||||
// echo "$sql";
|
||||
$view_errors = $MSG_PROBLEM_RESERVED."<br>";
|
||||
|
||||
if (isset($_POST['ajax'])) {
|
||||
echo $view_errors.$res[0][1];
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
}
|
||||
exit(0);
|
||||
submit_error($MSG_PROBLEM_RESERVED."<br>".$res[0][1]);
|
||||
}
|
||||
|
||||
$title = "";
|
||||
@@ -118,10 +123,7 @@ else if (isset($_POST['pid']) && isset($_POST['cid']) && $_POST['cid']!=0) {
|
||||
$rows_cnt = count($result);
|
||||
|
||||
if ($rows_cnt != 1) {
|
||||
$view_errors .= $MSG_NOT_IN_CONTEST;
|
||||
|
||||
require "template/" . $OJ_TEMPLATE . "/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_NOT_IN_CONTEST);
|
||||
}
|
||||
else {
|
||||
$row = $result[0];
|
||||
@@ -137,9 +139,7 @@ else if (isset($_POST['pid']) && isset($_POST['cid']) && $_POST['cid']!=0) {
|
||||
$ccnt = intval($row[0]);
|
||||
|
||||
if ($ccnt==0 && !isset($_SESSION[$OJ_NAME.'_'.'administrator'])) {
|
||||
$view_errors = $MSG_NOT_INVITED."\n";
|
||||
require "template/" . $OJ_TEMPLATE . "/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_NOT_INVITED."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,9 +150,7 @@ else if (isset($_POST['pid']) && isset($_POST['cid']) && $_POST['cid']!=0) {
|
||||
$rows_cnt = count($result);
|
||||
|
||||
if ($rows_cnt != 1) {
|
||||
$view_errors = $MSG_NO_PROBLEM."\n";
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_NO_PROBLEM."\n");
|
||||
}
|
||||
else {
|
||||
$row = $result[0];
|
||||
@@ -182,12 +180,10 @@ if ($language > count($language_name) || $language < 0) {
|
||||
$language = strval($language);
|
||||
|
||||
if ($langmask&(1<<$language)) {
|
||||
$view_errors = $MSG_NO_PLS."\n[$language][$langmask][".($langmask&(1<<$language))."]";
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_NO_PLS."\n[$language][$langmask][".($langmask&(1<<$language))."]");
|
||||
}
|
||||
|
||||
$source = $_POST['source'];
|
||||
$source = isset($_POST['source']) ? $_POST['source'] : "";
|
||||
$input_text = "";
|
||||
|
||||
if (isset($_POST['input_text'])) {
|
||||
@@ -206,7 +202,7 @@ if ($test_run) {
|
||||
$id = -$id;
|
||||
}
|
||||
|
||||
$tempfile = $_FILES ["answer"] ["tmp_name"];
|
||||
$tempfile = (isset($_FILES["answer"]) && isset($_FILES["answer"]["tmp_name"])) ? $_FILES["answer"]["tmp_name"] : "";
|
||||
if($tempfile!=""){
|
||||
if($language!=23){
|
||||
|
||||
@@ -217,7 +213,7 @@ if($tempfile!=""){
|
||||
$source="Main.sb3";
|
||||
}
|
||||
}
|
||||
$origin_name=trim($_FILES ["answer"]['name']);
|
||||
$origin_name=(isset($_FILES["answer"]) && isset($_FILES["answer"]['name'])) ? trim($_FILES["answer"]['name']) : "";
|
||||
$solution_file = "$OJ_DATA/$id/solution.name";
|
||||
if(file_exists($solution_file)){
|
||||
$solution_name=trim(file_get_contents($solution_file));
|
||||
@@ -255,7 +251,7 @@ if ($test_run) {
|
||||
$id = 0;
|
||||
}
|
||||
|
||||
if($language!=23) $len = strlen($source);else $len = $_FILES['answer']['size'];
|
||||
if($language!=23) $len = strlen($source);else $len = (isset($_FILES['answer']) ? intval($_FILES['answer']['size']) : 0);
|
||||
//echo $source;
|
||||
|
||||
setcookie('lastlang', $language, time()+360000);
|
||||
@@ -270,15 +266,11 @@ if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
|
||||
|
||||
if ($len < 2) {
|
||||
$view_errors = $MSG_TOO_SHORT.$tempfile."<br>";
|
||||
require "template/".$OJ_TEMPLATE."/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_TOO_SHORT.$tempfile."<br>");
|
||||
}
|
||||
|
||||
if ($len > 65536) {
|
||||
$view_errors = $MSG_TOO_LONG."<br>";
|
||||
require "template/" . $OJ_TEMPLATE . "/error.php";
|
||||
exit(0);
|
||||
submit_error($MSG_TOO_LONG."<br>");
|
||||
}
|
||||
|
||||
if (!$OJ_BENCHMARK_MODE) {
|
||||
@@ -295,8 +287,8 @@ if (!$OJ_BENCHMARK_MODE) {
|
||||
exit(0);
|
||||
// 预防WAF抽风
|
||||
*/
|
||||
if(isset($_GET['ajax'])){
|
||||
echo -1;
|
||||
if($is_ajax_submit){
|
||||
submit_error(str_replace("10",$OJ_SUBMIT_COOLDOWN_TIME,$MSG_BREAK_TIME));
|
||||
}else{
|
||||
$statusURI = "status.php?user_id=".$_SESSION[$OJ_NAME.'_'.'user_id'];
|
||||
if (isset($cid)) {
|
||||
@@ -373,15 +365,17 @@ if (~$OJ_LANGMASK&(1<<$language)) {
|
||||
|
||||
////remote oj
|
||||
$result=0;
|
||||
$sql="select remote_oj from problem where problem_id=?";
|
||||
$remote_oj=pdo_query($sql,$id);
|
||||
if(isset($remote_oj[0])){
|
||||
$remote_oj=$remote_oj[0][0];
|
||||
if($remote_oj!=""){
|
||||
$result=16;
|
||||
$sql="update solution set result=16,remote_oj=? where solution_id=?";
|
||||
pdo_query($sql,$remote_oj,$insert_id);
|
||||
}
|
||||
if (!$test_run && isset($OJ_REMOTE_JUDGE) && $OJ_REMOTE_JUDGE) {
|
||||
$sql="select remote_oj from problem where problem_id=?";
|
||||
$remote_oj=pdo_query($sql,$id);
|
||||
if(isset($remote_oj[0])){
|
||||
$remote_oj=$remote_oj[0][0];
|
||||
if($remote_oj!=""){
|
||||
$result=16;
|
||||
$sql="update solution set result=16,remote_oj=? where solution_id=?";
|
||||
pdo_query($sql,$remote_oj,$insert_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////poison robot account,give system resources to the REAL people
|
||||
@@ -501,11 +495,11 @@ if (isset($cid)) {
|
||||
$statusURI .= "&cid=$cid&fixed=";
|
||||
}
|
||||
|
||||
if (!$test_run&&!isset($_GET['ajax'])) {
|
||||
if (!$test_run&&!$is_ajax_submit) {
|
||||
header("Location: $statusURI");
|
||||
}
|
||||
else {
|
||||
if (isset($_GET['ajax'])) {
|
||||
if ($is_ajax_submit) {
|
||||
echo $insert_id;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -51,25 +51,14 @@
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#friendly-explain {
|
||||
#verdict-tip {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
#friendly-explain .verdict-tip {
|
||||
font-size: 1.05em;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(33,150,243,0.08);
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid #2185d0;
|
||||
}
|
||||
#friendly-explain .verdict-hints {
|
||||
margin: 0;
|
||||
padding-left: 22px;
|
||||
}
|
||||
#friendly-explain .verdict-hints li {
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#raw-info-accordion {
|
||||
margin-bottom: 18px;
|
||||
@@ -112,25 +101,130 @@
|
||||
#llm-section .ui.header {
|
||||
margin-top: 0;
|
||||
}
|
||||
#llm-section .llm-section-header {
|
||||
transition: background-color 0.15s;
|
||||
padding: 4px 8px;
|
||||
margin-left: -8px;
|
||||
margin-right: -8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#llm-section .llm-section-header:hover {
|
||||
background-color: rgba(33,150,243,0.06);
|
||||
}
|
||||
#llm-section .llm-toggle-icon {
|
||||
transition: transform 0.2s;
|
||||
color: #888;
|
||||
}
|
||||
#llm-review-result {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 错误解释列表(由 JS explain() 填充) */
|
||||
#errexp-list {
|
||||
list-style: disc;
|
||||
padding-left: 22px;
|
||||
margin: 0;
|
||||
/* 错误解释列表(由 JS explain() 填充) - 已弃用,由 AI 点评替代 */
|
||||
|
||||
/* 自动刷新状态 */
|
||||
i.icon.spinning {
|
||||
animation: spin 1.2s linear infinite;
|
||||
}
|
||||
#errexp-list li {
|
||||
margin-bottom: 6px;
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
#auto-refresh-status {
|
||||
color: #2185d0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Diff 对比样式 */
|
||||
#diff-section {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.diff-card {
|
||||
margin-bottom: 14px;
|
||||
border-left: 4px solid #fbbd08;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.diff-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
.diff-name {
|
||||
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
.diff-table {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.diff-table { grid-template-columns: 1fr; }
|
||||
}
|
||||
.diff-side {
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
.diff-side-header {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.85em;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
.diff-expected .diff-side-header {
|
||||
background: #21ba45;
|
||||
}
|
||||
.diff-yours .diff-side-header {
|
||||
background: #db2828;
|
||||
}
|
||||
.diff-content {
|
||||
margin: 0 !important;
|
||||
padding: 8px 0 !important;
|
||||
background: #fff !important;
|
||||
font-family: 'Fira Mono', 'Cascadia Code', Consolas, monospace;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#errexp-list li code {
|
||||
background: rgba(0,0,0,0.05);
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
.diff-line {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
}
|
||||
.diff-line-mismatch {
|
||||
background: rgba(255, 230, 100, 0.25);
|
||||
}
|
||||
.diff-expected .diff-line-mismatch {
|
||||
background: rgba(33, 186, 69, 0.12);
|
||||
}
|
||||
.diff-yours .diff-line-mismatch {
|
||||
background: rgba(219, 40, 40, 0.10);
|
||||
}
|
||||
.diff-lineno {
|
||||
display: inline-block;
|
||||
min-width: 38px;
|
||||
padding: 0 8px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
background: #f5f5f5;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.diff-text {
|
||||
padding: 0 10px;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -142,6 +236,8 @@
|
||||
$verdict_memory = isset($row['memory']) ? intval($row['memory']) : 0;
|
||||
$verdict_time = isset($row['time']) ? intval($row['time']) : 0;
|
||||
$verdict_mark = isset($mark) ? $mark : (isset($row['pass_rate']) ? round($row['pass_rate']*100, 1) : 0);
|
||||
// 兜底:防止 PHP notice
|
||||
$diff_blocks = isset($diff_blocks) && is_array($diff_blocks) ? $diff_blocks : array();
|
||||
?>
|
||||
|
||||
<!-- 1. 顶部判题结果横幅 -->
|
||||
@@ -155,91 +251,73 @@
|
||||
<span class="stat"><i class="microchip icon"></i><?php echo $verdict_memory ?> KB</span>
|
||||
<span class="stat"><i class="clock icon"></i><?php echo $verdict_time ?> ms</span>
|
||||
<span class="stat"><i class="code icon"></i><?php echo htmlspecialchars($verdict_lang, ENT_QUOTES, 'UTF-8') ?></span>
|
||||
<span class="stat" id="auto-refresh-status" style="display:none;">
|
||||
<i class="refresh icon spinning"></i> <span id="auto-refresh-text"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- 2. 友好解释区(永远非空) -->
|
||||
<div class="ui message" id="friendly-explain">
|
||||
<div class="header">
|
||||
<i class="lightbulb outline icon"></i>
|
||||
<?php echo isset($MSG_FRIENDLY_EXPLAIN) ? $MSG_FRIENDLY_EXPLAIN : '错误解释'; ?>
|
||||
</div>
|
||||
<div class="verdict-tip">
|
||||
<i class="info circle icon"></i>
|
||||
<?php echo isset($verdict_tip) ? htmlspecialchars($verdict_tip, ENT_QUOTES, 'UTF-8') : ''; ?>
|
||||
</div>
|
||||
<?php if (!empty($verdict_hints) && is_array($verdict_hints)): ?>
|
||||
<ul class="verdict-hints">
|
||||
<?php foreach ($verdict_hints as $h): ?>
|
||||
<li><?php echo htmlspecialchars($h, ENT_QUOTES, 'UTF-8'); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<!-- JS explain() 也会把 pats[] 匹配到的具体错误模式追加在这里 -->
|
||||
<ul id="errexp-list"></ul>
|
||||
<!-- 2. 判题结果标题提示(仅一句话,具体修复建议交给 AI) -->
|
||||
<div class="ui info message" id="verdict-tip">
|
||||
<i class="info circle icon"></i>
|
||||
<?php echo isset($verdict_tip) ? htmlspecialchars($verdict_tip, ENT_QUOTES, 'UTF-8') : ''; ?>
|
||||
</div>
|
||||
|
||||
<!-- 3. 原始错误折叠(默认收起) -->
|
||||
<div class="ui styled accordion" id="raw-info-accordion">
|
||||
<div class="title">
|
||||
<i class="dropdown icon"></i>
|
||||
<?php echo isset($MSG_RAW_ERROR) ? $MSG_RAW_ERROR : '查看原始错误信息'; ?>
|
||||
<?php if (!empty($view_reinfo_dedup_count)): ?>
|
||||
<span class="ui tiny teal label dedup-label">
|
||||
<?php echo sprintf(isset($MSG_DEDUP_HINT) ? $MSG_DEDUP_HINT : '已折叠 %d 个相同测试点', intval($view_reinfo_dedup_count)); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (trim($view_reinfo) === ''): ?>
|
||||
<span class="dedup-label empty-notice">(无)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?php if (trim($view_reinfo) === ''): ?>
|
||||
<p class="empty-notice">当前判题结果没有原始错误信息(如 TLE/MLE 通常不会写 runtimeinfo)。</p>
|
||||
<?php else: ?>
|
||||
<pre id="errtxt"><?php
|
||||
// 普通用户:折叠后;管理员/源浏览者:原文
|
||||
if (isset($is_admin_session) && $is_admin_session) {
|
||||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||||
} elseif (isset($is_source_browser) && $is_source_browser) {
|
||||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||||
} else {
|
||||
echo htmlspecialchars($view_reinfo, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
?></pre>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. AI 点评区(直接显示按钮,不再折叠) -->
|
||||
<!-- 3. AI 点评区(标题可点击折叠/展开) -->
|
||||
<?php if (isset($OJ_LLM_ENABLED) && $OJ_LLM_ENABLED) { ?>
|
||||
<div class="ui raised segment" id="llm-section">
|
||||
<h3 class="ui header">
|
||||
<h3 class="ui header llm-section-header" id="llm-toggle" style="cursor: pointer; user-select: none;">
|
||||
<i class="magic icon"></i>
|
||||
<div class="content">
|
||||
AI 智能点评
|
||||
<div class="sub header"><?php echo isset($MSG_AI_REVIEW_DESC) ? $MSG_AI_REVIEW_DESC : '基于你的代码和判题结果分析'; ?></div>
|
||||
</div>
|
||||
<i class="dropdown icon llm-toggle-icon" style="margin-left: auto; font-size: 1em;"></i>
|
||||
</h3>
|
||||
<button class="ui labeled icon primary button" id="llm-review-btn" onclick="fetchLLMReview()">
|
||||
<i class="magic icon"></i> 获取 AI 点评
|
||||
</button>
|
||||
<div id="llm-review-loading" class="ui active inline text loader" style="display:none; margin-left: 1em;">
|
||||
AI 正在分析你的代码,请稍候...
|
||||
</div>
|
||||
<div id="llm-review-result" style="display:none; margin-top: 15px;">
|
||||
<div class="ui existing segment" id="llm-review-content"></div>
|
||||
</div>
|
||||
<div id="llm-review-error" style="display:none; margin-top: 15px;">
|
||||
<div class="ui negative message">
|
||||
<p id="llm-review-error-msg"></p>
|
||||
<div id="llm-section-body">
|
||||
<button class="ui labeled icon primary button" id="llm-review-btn" onclick="fetchLLMReview()">
|
||||
<i class="magic icon"></i> 获取 AI 点评
|
||||
</button>
|
||||
<div id="llm-review-loading" class="ui active inline text loader" style="display:none; margin-left: 1em;">
|
||||
AI 正在分析你的代码,请稍候...
|
||||
</div>
|
||||
<div id="llm-review-result" style="display:none; margin-top: 15px;">
|
||||
<div class="ui existing segment" id="llm-review-content"></div>
|
||||
</div>
|
||||
<div id="llm-review-error" style="display:none; margin-top: 15px;">
|
||||
<div class="ui negative message">
|
||||
<p id="llm-review-error-msg"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 折叠/展开 AI 点评区
|
||||
(function(){
|
||||
var header = document.getElementById('llm-toggle');
|
||||
var body = document.getElementById('llm-section-body');
|
||||
var icon = header ? header.querySelector('.llm-toggle-icon') : null;
|
||||
if (!header || !body) return;
|
||||
|
||||
header.addEventListener('click', function(e){
|
||||
// 如果用户点击的是按钮本身,不要触发展开(按钮事件自己处理)
|
||||
if (e.target.closest('button')) return;
|
||||
var collapsed = body.style.display === 'none';
|
||||
body.style.display = collapsed ? '' : 'none';
|
||||
if (icon) {
|
||||
icon.className = collapsed
|
||||
? 'dropdown icon llm-toggle-icon'
|
||||
: 'dropdown icon llm-toggle-icon active';
|
||||
// active class 让 Semantic UI 的箭头自动旋转 90°,但我们手写也行
|
||||
icon.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(-90deg)';
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
function fetchLLMReview(){
|
||||
var btn = $('#llm-review-btn');
|
||||
btn.prop('disabled', true).addClass('loading');
|
||||
@@ -347,6 +425,95 @@
|
||||
</script>
|
||||
<?php } ?>
|
||||
|
||||
<!-- 3.5. 对比详情(WA/PE 时显示,diff 视图) -->
|
||||
<?php if (!empty($diff_blocks) && is_array($diff_blocks)): ?>
|
||||
<div id="diff-section">
|
||||
<h3 class="ui header" style="margin-top: 0;">
|
||||
<i class="exchange icon"></i>
|
||||
<div class="content">
|
||||
输出对比
|
||||
<div class="sub header">逐测试点对比期望输出和你的输出</div>
|
||||
</div>
|
||||
</h3>
|
||||
<?php foreach ($diff_blocks as $idx => $block): ?>
|
||||
<div class="ui segment diff-card">
|
||||
<div class="diff-header">
|
||||
<i class="file outline icon"></i>
|
||||
<span class="diff-name"><?php echo htmlspecialchars($block['name'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
</div>
|
||||
<div class="diff-table">
|
||||
<div class="diff-side diff-expected">
|
||||
<div class="diff-side-header">
|
||||
<i class="check icon"></i> 期望输出
|
||||
</div>
|
||||
<pre class="diff-content"><?php
|
||||
$max = max(count($block['expected']), count($block['yours']));
|
||||
$lines_html = array();
|
||||
for ($li = 0; $li < $max; $li++) {
|
||||
$e = isset($block['expected'][$li]) ? $block['expected'][$li] : '';
|
||||
$y = isset($block['yours'][$li]) ? $block['yours'][$li] : '';
|
||||
$diff_class = ($e !== $y) ? ' diff-line-mismatch' : '';
|
||||
$line_no = $li + 1;
|
||||
$lines_html[] = '<div class="diff-line' . $diff_class . '" data-line="' . $line_no . '"><span class="diff-lineno">' . $line_no . '</span><span class="diff-text">' . htmlspecialchars($e, ENT_QUOTES, 'UTF-8') . '</span></div>';
|
||||
}
|
||||
echo implode('', $lines_html);
|
||||
?></pre>
|
||||
</div>
|
||||
<div class="diff-side diff-yours">
|
||||
<div class="diff-side-header">
|
||||
<i class="bug icon"></i> 你的输出
|
||||
</div>
|
||||
<pre class="diff-content"><?php
|
||||
$max = max(count($block['expected']), count($block['yours']));
|
||||
$lines_html = array();
|
||||
for ($li = 0; $li < $max; $li++) {
|
||||
$e = isset($block['expected'][$li]) ? $block['expected'][$li] : '';
|
||||
$y = isset($block['yours'][$li]) ? $block['yours'][$li] : '';
|
||||
$diff_class = ($e !== $y) ? ' diff-line-mismatch' : '';
|
||||
$line_no = $li + 1;
|
||||
$lines_html[] = '<div class="diff-line' . $diff_class . '" data-line="' . $line_no . '"><span class="diff-lineno">' . $line_no . '</span><span class="diff-text">' . htmlspecialchars($y, ENT_QUOTES, 'UTF-8') . '</span></div>';
|
||||
}
|
||||
echo implode('', $lines_html);
|
||||
?></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 4. 原始错误折叠(默认收起) -->
|
||||
<div class="ui styled accordion" id="raw-info-accordion">
|
||||
<div class="title">
|
||||
<i class="dropdown icon"></i>
|
||||
<?php echo isset($MSG_RAW_ERROR) ? $MSG_RAW_ERROR : '查看原始错误信息'; ?>
|
||||
<?php if (!empty($view_reinfo_dedup_count)): ?>
|
||||
<span class="ui tiny teal label dedup-label">
|
||||
<?php echo sprintf(isset($MSG_DEDUP_HINT) ? $MSG_DEDUP_HINT : '已折叠 %d 个相同测试点', intval($view_reinfo_dedup_count)); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (trim($view_reinfo) === ''): ?>
|
||||
<span class="dedup-label empty-notice">(无)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?php if (trim($view_reinfo) === ''): ?>
|
||||
<p class="empty-notice">当前判题结果没有原始错误信息(如 TLE/MLE 通常不会写 runtimeinfo)。</p>
|
||||
<?php else: ?>
|
||||
<pre id="errtxt"><?php
|
||||
// 普通用户:折叠后;管理员/源浏览者:原文
|
||||
if (isset($is_admin_session) && $is_admin_session) {
|
||||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||||
} elseif (isset($is_source_browser) && $is_source_browser) {
|
||||
echo htmlspecialchars($view_reinfo_raw, ENT_QUOTES, 'UTF-8');
|
||||
} else {
|
||||
echo htmlspecialchars($view_reinfo, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
?></pre>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. 快捷操作区 -->
|
||||
<div class="quick-actions">
|
||||
<div class="ui horizontal divider">
|
||||
@@ -362,132 +529,12 @@
|
||||
<a class="ui button" href="problem.php?id=<?php echo intval($row['problem_id']) ?>">
|
||||
<i class="info circle icon"></i> <?php echo isset($MSG_VIEW_PROBLEM) ? $MSG_VIEW_PROBLEM : '查看题目'; ?>
|
||||
</a>
|
||||
<?php if (isset($OJ_DOWNLOAD) && $OJ_DOWNLOAD && ($isRE || $row['result']==6)) { ?>
|
||||
<a class="ui orange button" href="download.php?sid=<?php echo intval($id) ?>">
|
||||
<i class="download icon"></i> <?php echo isset($MSG_DOWNLOAD_DATA) ? $MSG_DOWNLOAD_DATA : '下载测试数据'; ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 错误模式 → 解释(pats[]/exps[],共 28 条) -->
|
||||
<script>
|
||||
var pats = new Array();
|
||||
var exps = new Array();
|
||||
var patsSeen = {}; // 防止"段错误"和"stack smashing"双重提示
|
||||
|
||||
// === 原有 11 条 ===
|
||||
pats[0]=/A Not allowed system call.* /;
|
||||
exps[0]="<?php echo $MSG_A_NOT_ALLOWED_SYSTEM_CALL ?>";
|
||||
pats[1]=/Segmentation fault/;
|
||||
exps[1]="<?php echo $MSG_SEGMETATION_FAULT ?>";
|
||||
pats[2]=/Floating point exception/;
|
||||
exps[2]="<?php echo $MSG_FLOATING_POINT_EXCEPTION ?>";
|
||||
pats[3]=/buffer overflow detected/;
|
||||
exps[3]="<?php echo $MSG_BUFFER_OVERFLOW_DETECTED ?>";
|
||||
pats[4]=/Killed/;
|
||||
exps[4]="<?php echo $MSG_PROCESS_KILLED ?>";
|
||||
pats[5]=/Alarm clock/;
|
||||
exps[5]="<?php echo $MSG_ALARM_CLOCK ?>";
|
||||
pats[6]=/CALLID:20/;
|
||||
exps[6]="<?php echo $MSG_CALLID_20 ?>";
|
||||
pats[7]=/ArrayIndexOutOfBoundsException/;
|
||||
exps[7]="<?php echo $MSG_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION ?>";
|
||||
pats[8]=/StringIndexOutOfBoundsException/;
|
||||
exps[8]="<?php echo $MSG_STRING_INDEX_OUT_OF_BOUNDS_EXCEPTION ?>";
|
||||
pats[9]=/Binary files/;
|
||||
exps[9]="<?php echo $MSG_WRONG_OUTPUT_TYPE_EXCEPTION ?>";
|
||||
pats[10]=/non-zero return/;
|
||||
exps[10]="<?php echo $MSG_NON_ZERO_RETURN ?>";
|
||||
|
||||
// === 新增 17 条(覆盖 glibc / Java / Python / Go) ===
|
||||
pats[11]=/stack smashing detected/;
|
||||
exps[11]="<?php echo $MSG_STACK_SMASHING_DETECTED ?>";
|
||||
// 同时标 pats[3] 已被覆盖(避免重复)
|
||||
pats[12]=/terminate called after throwing/;
|
||||
exps[12]="<?php echo $MSG_TERMINATE_CALLED ?>";
|
||||
pats[13]=/NullPointerException/;
|
||||
exps[13]="<?php echo $MSG_NULL_POINTER_EXCEPTION ?>";
|
||||
pats[14]=/NumberFormatException/;
|
||||
exps[14]="<?php echo $MSG_NUMBER_FORMAT_EXCEPTION ?>";
|
||||
pats[15]=/ClassCastException/;
|
||||
exps[15]="<?php echo $MSG_CLASS_CAST_EXCEPTION ?>";
|
||||
pats[16]=/NegativeArraySizeException/;
|
||||
exps[16]="<?php echo $MSG_NEGATIVE_ARRAY_SIZE_EXCEPTION ?>";
|
||||
pats[17]=/Traceback \(most recent call last\)/;
|
||||
exps[17]="<?php echo $MSG_PYTHON_TRACEBACK ?>";
|
||||
pats[18]=/RecursionError/;
|
||||
exps[18]="<?php echo $MSG_PYTHON_RECURSION_ERROR ?>";
|
||||
pats[19]=/IndentationError/;
|
||||
exps[19]="<?php echo $MSG_PYTHON_INDENTATION_ERROR ?>";
|
||||
pats[20]=/ZeroDivisionError/;
|
||||
exps[20]="<?php echo $MSG_PYTHON_ZERO_DIVISION ?>";
|
||||
pats[21]=/NameError:/;
|
||||
exps[21]="<?php echo $MSG_PYTHON_NAME_ERROR ?>";
|
||||
pats[22]=/panic:.*runtime error/;
|
||||
exps[22]="<?php echo $MSG_GO_PANIC ?>";
|
||||
pats[23]=/Aborted \(core dumped\)/;
|
||||
exps[23]="<?php echo $MSG_ABORTED_CORE_DUMPED ?>";
|
||||
pats[24]=/CALLID:(\d+)/;
|
||||
exps[24]="使用了非法的系统调用(syscall),请检查是否访问了文件、网络、进程等受限资源。";
|
||||
pats[25]=/StackOverflowError/;
|
||||
exps[25]="<?php echo $MSG_STACK_OVERFLOW_ERROR ?? '栈溢出(StackOverflowError)。通常是递归层数过深或局部大数组占用过多栈空间。' ?>";
|
||||
pats[26]=/class not found|Could not find or load main class/;
|
||||
exps[26]="Java 类未找到。检查 public class 名称是否与文件名一致,是否有 package 声明。";
|
||||
pats[27]=/no test data/;
|
||||
exps[27]="没有找到测试数据,请联系管理员检查题目数据。";
|
||||
|
||||
/**
|
||||
* 把匹配到的错误模式追加到 <ul id="errexp-list">
|
||||
* 同一类错误(内存类/异常类/系统调用类)只显示一次
|
||||
*/
|
||||
function explain(){
|
||||
var errmsg = $("#errtxt").text();
|
||||
if (!errmsg || errmsg.trim() === '') return;
|
||||
|
||||
// 同族分组
|
||||
var familyMap = {
|
||||
'mem': [1, 3, 11, 25], // 段错误 / 缓冲溢出 / stack smashing / 栈溢出
|
||||
'sysc': [0, 6, 24], // 系统调用
|
||||
'time': [4, 5], // 时间/Killed
|
||||
'arith':[2, 20], // 除零
|
||||
'jexc': [7, 8, 13, 14, 15, 16, 25], // Java 异常
|
||||
'pyexc':[17, 18, 19, 20, 21], // Python
|
||||
'goexc':[22], // Go
|
||||
};
|
||||
|
||||
var shownFamilies = {};
|
||||
var shown = {};
|
||||
var items = [];
|
||||
|
||||
for (var i = 0; i < pats.length; i++) {
|
||||
if (!pats[i] || shown[i]) continue;
|
||||
var ret = pats[i].exec(errmsg);
|
||||
if (!ret) continue;
|
||||
|
||||
// 检查是否已被同族其它规则覆盖
|
||||
var family = null;
|
||||
for (var f in familyMap) {
|
||||
if (familyMap[f].indexOf(i) >= 0) { family = f; break; }
|
||||
}
|
||||
if (family && shownFamilies[family]) continue;
|
||||
if (family) shownFamilies[family] = true;
|
||||
shown[i] = true;
|
||||
|
||||
items.push("<li><code>" + ret[0] + "</code> → " + exps[i] + "</li>");
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
$("#errexp-list").html(items.join(""));
|
||||
} else {
|
||||
$("#errexp-list").html("<li class='empty-notice'>未匹配到具体错误模式,请参考原始错误信息或 AI 点评。</li>");
|
||||
}
|
||||
}
|
||||
explain();
|
||||
</script>
|
||||
|
||||
<?php if(isset($OJ_MARKDOWN) && $OJ_MARKDOWN){ ?>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
@@ -587,4 +634,99 @@
|
||||
</script>
|
||||
<?php } ?>
|
||||
|
||||
<!-- 自动刷新:判题中(result < 4 或 > 14)时定时拉取最新状态 -->
|
||||
<?php
|
||||
$result_code_js = isset($row['result']) ? intval($row['result']) : 4;
|
||||
$sid_js = isset($id) ? intval($id) : 0;
|
||||
// 0=Pending, 1=Pending Rejudging, 2=Compiling, 3=Running/Judging, 14=Manual, 15=Submitting, 16/17=Remote
|
||||
$is_pending = ($result_code_js < 4) || ($result_code_js > 14);
|
||||
?>
|
||||
<?php if ($sid_js > 0 && $is_pending): ?>
|
||||
<script>
|
||||
(function(){
|
||||
var sid = <?php echo $sid_js ?>;
|
||||
var currentResult = <?php echo $result_code_js ?>;
|
||||
var interval = 1500; // 初始 1.5s
|
||||
var maxInterval = 8000; // 最长 8s
|
||||
var countdown = 0;
|
||||
var stopped = false;
|
||||
var completed = false;
|
||||
|
||||
function showStatus(text){
|
||||
var el = document.getElementById('auto-refresh-status');
|
||||
var t = document.getElementById('auto-refresh-text');
|
||||
if (el && t) {
|
||||
el.style.display = '';
|
||||
t.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
function hideStatus(){
|
||||
var el = document.getElementById('auto-refresh-status');
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
|
||||
function tick(){
|
||||
if (stopped) return;
|
||||
countdown--;
|
||||
if (countdown > 0) {
|
||||
showStatus('将在 ' + countdown + ' 秒后自动刷新');
|
||||
setTimeout(tick, 1000);
|
||||
return;
|
||||
}
|
||||
fetch('status-ajax.php?solution_id=' + sid + '&t=json', {credentials: 'same-origin'})
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(data){
|
||||
if (!data || typeof data.result === 'undefined') {
|
||||
// 解析失败,下次重试
|
||||
interval = Math.min(interval * 1.5, maxInterval);
|
||||
countdown = Math.ceil(interval / 1000);
|
||||
tick();
|
||||
return;
|
||||
}
|
||||
var newResult = parseInt(data.result);
|
||||
if (newResult >= 4 && newResult <= 14) {
|
||||
// 判题完成,刷新页面拿到完整错误信息
|
||||
showStatus('判题完成,正在刷新...');
|
||||
setTimeout(function(){ location.reload(); }, 400);
|
||||
completed = true;
|
||||
stopped = true;
|
||||
return;
|
||||
}
|
||||
// 仍在评判中
|
||||
interval = Math.min(interval * 1.3, maxInterval);
|
||||
countdown = Math.ceil(interval / 1000);
|
||||
showStatus('判题中,将在 ' + countdown + ' 秒后再次查询');
|
||||
setTimeout(tick, 1000);
|
||||
})
|
||||
.catch(function(err){
|
||||
// 网络错误,重试
|
||||
console.warn('auto refresh failed:', err);
|
||||
interval = Math.min(interval * 1.5, maxInterval);
|
||||
countdown = Math.ceil(interval / 1000);
|
||||
showStatus('查询失败,' + countdown + ' 秒后重试');
|
||||
setTimeout(tick, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
// 用户切到后台标签时停止轮询,避免无意义请求
|
||||
document.addEventListener('visibilitychange', function(){
|
||||
if (document.hidden) {
|
||||
stopped = true;
|
||||
hideStatus();
|
||||
} else if (!completed) {
|
||||
stopped = false;
|
||||
countdown = 0;
|
||||
tick();
|
||||
}
|
||||
});
|
||||
|
||||
// 启动
|
||||
countdown = Math.ceil(interval / 1000);
|
||||
showStatus('判题中,将在 ' + countdown + ' 秒后自动刷新');
|
||||
setTimeout(tick, 1000);
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include("template/$OJ_TEMPLATE/footer.php");?>
|
||||
|
||||
@@ -215,6 +215,15 @@
|
||||
border-top: 1px solid rgba(0,0,5,0.08);
|
||||
}
|
||||
|
||||
.submit-top-actions {
|
||||
justify-content: flex-end;
|
||||
padding-top: 0;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid rgba(0,0,5,0.08);
|
||||
}
|
||||
|
||||
.submit-actions .actions-left {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@@ -388,7 +397,15 @@
|
||||
</div>
|
||||
|
||||
<script src="<?php echo $OJ_CDN_URL?>include/checksource.js"></script>
|
||||
<form id="frmSolution" action="submit.php<?php if (isset($_GET['spa'])) echo "?spa" ?>" method="post" onsubmit="do_submit()" enctype="multipart/form-data">
|
||||
<form id="frmSolution" action="submit.php<?php if (isset($_GET['spa'])) echo "?spa" ?>" method="post" onsubmit="do_submit();return false;" enctype="multipart/form-data">
|
||||
|
||||
<div class="submit-actions submit-top-actions">
|
||||
<div class="actions-left">
|
||||
<button id="Submit" type="button" class="btn btn-primary" onclick="do_submit();">
|
||||
<i class="paper plane icon"></i> <?php echo $MSG_SUBMIT?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="submit-toolbar">
|
||||
<?php if (!isset($_GET['spa']) || $solution_name ) {?>
|
||||
@@ -485,11 +502,9 @@
|
||||
<h2 class="solution-name-title">📎 <?php echo $solution_name?></h2>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ((isset($OJ_TEST_RUN) && $OJ_TEST_RUN && $spj<=1 && !$solution_name) || (isset($OJ_ENCODE_SUBMIT)&&$OJ_ENCODE_SUBMIT) || (isset($OJ_BLOCKLY)&&$OJ_BLOCKLY)){?>
|
||||
<div class="submit-actions">
|
||||
<div class="actions-left">
|
||||
<button id="Submit" type="button" class="btn btn-primary" onclick="do_submit();">
|
||||
<i class="paper plane icon"></i> <?php echo $MSG_SUBMIT?>
|
||||
</button>
|
||||
<?php if ( isset($OJ_TEST_RUN) && $OJ_TEST_RUN && $spj<=1 && !$solution_name ){?>
|
||||
<button id="TestRun" type="button" class="btn btn-info" onclick="do_test_run();">
|
||||
<i class="play icon"></i> <?php echo $MSG_TR?>
|
||||
@@ -508,13 +523,14 @@
|
||||
<input id="blockly_loader" type="button" class="btn btn-purple" onclick="openBlockly()" value="🧩 <?php echo $MSG_BLOCKLY_OPEN?>">
|
||||
<input id="transrun" type="button" class="btn btn-green" onclick="loadFromBlockly()" value="▶ <?php echo $MSG_BLOCKLY_TEST?>" style="display:none;">
|
||||
</div>
|
||||
<?php }?>
|
||||
<?php }?>
|
||||
</div>
|
||||
<?php }?>
|
||||
|
||||
<div id="blockly" class="center"></div>
|
||||
|
||||
<?php if (isset($id)){?>
|
||||
<input id="problem_id" type="hidden" value="<?php echo $id?>" name="problem_id">
|
||||
<input id="problem_id" type="hidden" value="<?php echo $id?>" name="id">
|
||||
<?php }else{?>
|
||||
<input id="cid" type="hidden" value="<?php echo $cid?>" name="cid">
|
||||
<input id="pid" type="hidden" value="<?php echo $pid?>" name="pid">
|
||||
@@ -618,6 +634,69 @@ return ret+"";
|
||||
}
|
||||
var count=0;
|
||||
|
||||
function htmlEscape(text){
|
||||
return $("<div>").text(text).html();
|
||||
}
|
||||
|
||||
function cleanSubmitMessage(data){
|
||||
var text=$("<div>").html(String(data)).text();
|
||||
text=$.trim(text.replace(/\s+/g," "));
|
||||
if(text=="") text="Submit failed.";
|
||||
return text;
|
||||
}
|
||||
|
||||
function showSubmitMessage(data){
|
||||
var tb=window.document.getElementById('result');
|
||||
var text=cleanSubmitMessage(data);
|
||||
if(tb!=null){
|
||||
tb.innerHTML="<span class='badge badge-danger'>"+htmlEscape(text)+"</span>";
|
||||
}else{
|
||||
alert(text);
|
||||
}
|
||||
if($("#vcode-img")!=null) $("#vcode-img").click();
|
||||
}
|
||||
|
||||
function setSubmitDisabled(disabled){
|
||||
$("#Submit").prop('disabled', disabled);
|
||||
$("#TestRun").prop('disabled', disabled);
|
||||
}
|
||||
|
||||
function startSubmitCooldown(){
|
||||
count=<?php echo isset($OJ_SUBMIT_COOLDOWN_TIME)?$OJ_SUBMIT_COOLDOWN_TIME:5 ?> * 2 ;
|
||||
handler_interval= window.setTimeout("resume();",1000);
|
||||
}
|
||||
|
||||
function submitSolutionAjax(isTestRun){
|
||||
var form=document.getElementById("frmSolution");
|
||||
var formData=new FormData(form);
|
||||
$.ajax({
|
||||
url:"submit.php?ajax<?php if(isset($_GET['spa'])) echo '&spa'; ?>",
|
||||
type:"POST",
|
||||
data:formData,
|
||||
processData:false,
|
||||
contentType:false,
|
||||
success:function(data){
|
||||
var text=$.trim(String(data));
|
||||
var sid=/^\d+$/.test(text)?parseInt(text,10):0;
|
||||
if(sid>0){
|
||||
if(isTestRun){
|
||||
fresh_result(sid);
|
||||
startSubmitCooldown();
|
||||
}else{
|
||||
window.location.href="reinfo.php?sid="+sid;
|
||||
}
|
||||
}else{
|
||||
showSubmitMessage(text);
|
||||
setSubmitDisabled(false);
|
||||
}
|
||||
},
|
||||
error:function(xhr){
|
||||
showSubmitMessage(xhr.responseText || "Submit failed.");
|
||||
setSubmitDisabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function encoded_submit(){
|
||||
|
||||
var mark="<?php echo isset($id)?'problem_id':'cid';?>";
|
||||
@@ -643,7 +722,7 @@ function encoded_submit(){
|
||||
}
|
||||
|
||||
function do_submit(){
|
||||
$("#Submit").attr("disabled","true");
|
||||
setSubmitDisabled(true);
|
||||
if(using_blockly)
|
||||
translate();
|
||||
if(typeof(editor) != "undefined"){
|
||||
@@ -656,37 +735,8 @@ function do_submit(){
|
||||
else
|
||||
problem_id.value='<?php if (isset($cid))echo $cid?>';
|
||||
document.getElementById("frmSolution").target="_self";
|
||||
|
||||
<?php if(isset($_GET['spa'])){?>
|
||||
<?php if($solution_name) { ?>document.getElementById("frmSolution").submit(); <?php } ?>
|
||||
$.post("submit.php?ajax",$("#frmSolution").serialize(),function(data){
|
||||
var sid=parseInt(data);
|
||||
if(sid>0){
|
||||
window.location.href="reinfo.php?sid="+sid;
|
||||
}else{
|
||||
fresh_result(data);
|
||||
}
|
||||
});
|
||||
$("#Submit").prop('disabled', true);
|
||||
$("#TestRun").prop('disabled', true);
|
||||
count=<?php echo $OJ_SUBMIT_COOLDOWN_TIME?> * 2 ;
|
||||
handler_interval= window.setTimeout("resume();",1000);
|
||||
<?php if(isset($OJ_REMOTE_JUDGE)&&$OJ_REMOTE_JUDGE) {?>$("#sk").attr("src","remote.php"); <?php } ?>
|
||||
<?php }else{?>
|
||||
$.post("submit.php?ajax",$("#frmSolution").serialize(),function(data){
|
||||
var sid=parseInt(data);
|
||||
if(sid>0){
|
||||
window.location.href="reinfo.php?sid="+sid;
|
||||
}else{
|
||||
fresh_result(data);
|
||||
}
|
||||
});
|
||||
$("#Submit").prop('disabled', true);
|
||||
$("#TestRun").prop('disabled', true);
|
||||
count=<?php echo $OJ_SUBMIT_COOLDOWN_TIME?> * 2 ;
|
||||
handler_interval= window.setTimeout("resume();",1000);
|
||||
<?php }?>
|
||||
|
||||
submitSolutionAjax(false);
|
||||
<?php if(isset($OJ_REMOTE_JUDGE)&&$OJ_REMOTE_JUDGE) {?>$("#sk").attr("src","remote.php"); <?php } ?>
|
||||
}
|
||||
var handler_interval;
|
||||
function do_test_run(){
|
||||
@@ -705,12 +755,9 @@ function do_test_run(){
|
||||
var problem_id=document.getElementById(mark);
|
||||
problem_id.value=-problem_id.value;
|
||||
document.getElementById("frmSolution").target="testRun";
|
||||
$.post("submit.php?ajax",$("#frmSolution").serialize(),function(data){fresh_result(data);});
|
||||
$("#Submit").prop('disabled', true);
|
||||
$("#TestRun").prop('disabled', true);
|
||||
setSubmitDisabled(true);
|
||||
submitSolutionAjax(true);
|
||||
problem_id.value=-problem_id.value;
|
||||
count=<?php echo isset($OJ_SUBMIT_COOLDOWN_TIME)?$OJ_SUBMIT_COOLDOWN_TIME:5 ?> * 2 ;
|
||||
handler_interval= window.setTimeout("resume();",1000);
|
||||
}
|
||||
function resume(){
|
||||
count--;
|
||||
|
||||
Reference in New Issue
Block a user