Files
hustoj/web/reinfo.php
klarkxy 6c462655b9 feat(ui): 重新设计判题结果页与提交页
- 判题结果页引入友好错误解释面板,按 verdict 类型给出提示与改进建议
- 新增 dedup_runtimeinfo() 函数将重复 RE 测试点折叠为区间汇总
- 区分管理员/源浏览者与普通用户视图,原始错误默认折叠
- 错误模式匹配扩展至 28 条,覆盖 glibc/Java/Python/Go 等常见运行时异常
- 提交页改用卡片式布局,编辑器工具栏与测试运行区交互优化
- 中英文语言文件补充相关文案与快捷操作标签
- 切换 LLM 点评 API 至 newapi.klarkxy.xyz,模型改为 minimax-m3
- 新增 .codegraph/.gitignore 忽略本地调试数据
2026-07-08 20:56:17 +08:00

274 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$cache_time = 10;
$OJ_CACHE_SHARE = false;
require_once('./include/cache_start.php');
require_once('./include/db_info.inc.php');
require_once('./include/setlang.php');
$view_title = "Welcome To Online Judge";
if(!isset($_SESSION[$OJ_NAME.'_'.'user_id'])){
header("location:loginpage.php");
exit(0);
}
require_once("./include/const.inc.php");
if(!isset($_GET['sid'])){
echo "No such code!\n";
require_once("oj-footer.php");
exit(0);
}
function is_valid($str2){
global $_SESSION,$OJ_NAME,$OJ_FRIENDLY_LEVEL;
if(isset($_SESSION[$OJ_NAME.'_'.'source_browser'])) return true;
if($OJ_FRIENDLY_LEVEL>3) return true;
// 如果希望能让任何人都查看对比和RE,放开行首注释,并设定$OJ_SHOW_DIFF=true;
// return true;
$n = strlen($str2);
$str = str_split($str2);
$m = 1;
for($i=0; $i<$n; $i++){
if(is_numeric($str[$i]))
$m++;
}
return $n/$m>3;
}
if(!isset($_SESSION[$OJ_NAME.'_'.'user_id'])){
$view_errors = $MSG_WARNING_ACCESS_DENIED ;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
$ok = false;
$id = strval(intval($_GET['sid']));
$sql = "SELECT * FROM `solution` WHERE `solution_id`=?";
$result = pdo_query($sql,$id);
$row = $result[0];
$lang = $row['language'];
$contest_id = intval($row['contest_id']);
$isRE = $row['result']==10;
$isAC = $row['result']==4 ;
$mark=$row['pass_rate']*100;
if($isAC) $mark=100;
if((isset($_SESSION[$OJ_NAME.'_'.'user_id']) && $row && ($row['user_id']==$_SESSION[$OJ_NAME.'_'.'user_id']))||isset($_SESSION[$OJ_NAME.'_'.'source_browser']))
{
$ok = true;
}
$spj=pdo_query("select spj from problem where problem_id=?",$row['problem_id']);
if(!empty($spj)&&$spj[0][0]==2 && $OJ_HIDE_RIGHT_ANSWER && !isset($_SESSION[$OJ_NAME.'_'.'source_browser']) ){
$view_errors = "<h1>$MSG_MARK:$mark</h1><br>";
$ok = false;
}
/**
* 把 N 个相同测试点的 RE 折叠为 "1.in~5.in: Aborted (共 5 个测试点)"
*/
function dedup_runtimeinfo($text, &$dedup_count) {
$lines = explode("\n", $text);
$buckets = array(); // reason => [testcase, ...]
$other = array();
foreach ($lines as $line) {
$line = trim($line);
if ($line === "") continue;
// 形如 "data/1001/1.in:Aborted" 或 "1.in: Segmentation fault"
if (preg_match('/^(?:data\/\d+\/)?([^\s:]+(?:[.:][^\s:]+)*?):\s*(.+)$/', $line, $m)) {
$tc = $m[1];
$reason = trim($m[2]);
$buckets[$reason][] = $tc;
} else {
$other[] = $line;
}
}
$out = array();
foreach ($other as $o) $out[] = $o;
foreach ($buckets as $reason => $tcs) {
$count = count($tcs);
if ($count == 1) {
$out[] = $tcs[0] . ": " . $reason;
} else {
$first = $tcs[0];
$last = $tcs[$count - 1];
$range = ($first === $last) ? $first : ($first . "~" . $last);
$out[] = $range . ": " . $reason . " (共 " . $count . " 个测试点)";
$dedup_count += ($count - 1);
}
}
return implode("\n", $out);
}
$view_reinfo = "";
$view_reinfo_raw = ""; // 原始(管理员/有权限者看)
$view_reinfo_summary = ""; // 折叠后(普通用户看)
$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){
if($row['user_id']!=$_SESSION[$OJ_NAME.'_'.'user_id']){
$view_mail_link= "<a href='mail.php?to_user=".htmlentities($row['user_id'],ENT_QUOTES,"UTF-8")."&title=$MSG_SUBMIT $id'>Mail the auther</a>";
}
$sql = "SELECT `error` FROM `runtimeinfo` WHERE `solution_id`=?";
$rt = pdo_query($sql,$id);
$raw_error = "";
if(isset($rt[0])){
$raw_error = str_replace("\n\r","\n",$rt[0]['error']);
}
// === 阶段 1基于 solution.result 给"主解释"(永远非空)===
$result_code = intval($row['result']);
$is_admin_session = isset($_SESSION[$OJ_NAME."_administrator"]);
$is_source_browser = isset($_SESSION[$OJ_NAME.'_'.'source_browser']);
// 颜色 + 图标
switch ($result_code) {
case 4: $verdict_color_class = "green"; $result_icon = "check circle"; break;
case 5: // PE
case 6: $verdict_color_class = "red"; $result_icon = "times circle"; break;
case 7: // TLE
case 8: // MLE
case 9: $verdict_color_class = "yellow";$result_icon = "hourglass end"; break;
case 10: $verdict_color_class = "red"; $result_icon = "bug"; break;
case 11: $verdict_color_class = "grey"; $result_icon = "code"; break;
case 13: $verdict_color_class = "blue"; $result_icon = "play circle"; break;
default: $verdict_color_class = "blue"; $result_icon = "info circle";
}
// 友好提示
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 = "正在评判,请稍候刷新";
}
// === 阶段 2syscall 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) === "") {
// TLE/MLE/OLE 时 runtimeinfo 通常为空
$view_reinfo_summary = "";
} else {
// 含 "PASS" 不再显示 "error3" 占位
// 含 "php" 不再显示 "error2" 占位(用 generic 提示)
if (strpos($raw_error, "php") !== false) {
$view_reinfo_summary = $MSG_RUNTIME_GENERIC_ERROR;
$view_reinfo_raw = $raw_error; // 管理员仍能看
}
// 含 "judge/" 区分:是否是 syscall 错误
elseif (strpos($raw_error, "judge/") !== false
&& strpos($raw_error, "CALLID") === false
&& strpos($raw_error, "Forbidden system call") === false) {
$view_reinfo_summary = "潜在的数组或指针越界,请检查代码。";
}
// 默认:原文 + 去重
else {
$view_reinfo_summary = $raw_error;
// 多测试点去重:按 "infile: error" 模式分组
$view_reinfo_summary = dedup_runtimeinfo($view_reinfo_summary, $view_reinfo_dedup_count);
}
}
$view_reinfo = $view_reinfo_summary;
// 管理员或 source_browser 永远看完整原文
$show_raw = $is_admin_session || $is_source_browser;
if ($show_raw) {
$view_reinfo = $view_reinfo_raw;
} else {
$view_reinfo = $view_reinfo_summary;
}
}
// 不是本人的提交,且不是 source_browser
else{
if($spj[0][0]!=2) $view_errors = $MSG_WARNING_ACCESS_DENIED;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
/////////////////////////Template
if(!isset($_SESSION[$OJ_NAME.'_'.'source_browser']) && $OJ_SHOW_DIFF==false){
$view_errors = $MSG_WARNING_ACCESS_DENIED;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
else{
require("template/".$OJ_TEMPLATE."/reinfo.php");
}
/////////////////////////Common foot
if(file_exists('./include/cache_end.php')){
require_once('./include/cache_end.php');
}
?>