文章目录
- 四、oj_server模块
- 1. oj_server的功能路由
- 2. 建立文件版的题库
- 3. model模块
- 4.controller模块
- 5.judge模块(负载均衡)
- 6.view模块整体代码结构(前端的东西,不是重点)
- 五、最终效果
- 项目源码
前面部分请看这里C++ – 负载均衡式在线OJ (二)
四、oj_server模块
oj_server说白了就是一个网站。oj_server的功能如下
- 1.获取首页
- 2.获取题目列表
- 3.获取单道题目,并提供编辑功能
- 4.提交判题功能(背后依靠的就是提供编译运行服务的服务器)
我们想采用的是基于MVC的一种架构模式
- M model 与数据交互的模块
- V view 视图,指用户界面,就是用来与用户进行交互的,模块
- C controller 控制器,核心的业务逻辑都在这里实现,合理调配model和view模块。
oj_server承担的就是负载均衡式的去调用后端的一个个编译服务,然后展现给用户,所以oj_server更靠近用户
1. oj_server的功能路由
我们设计的oj_server一共能提供给用户的是3个功能路由
- 1.题目列表的功能路由
- 2.单道题目的功能路由
- 3.提交代码进行判题的功能路由
#include <iostream>
#include <signal.h>
#include "../comm/httplib.h"
#include "oj_control.hpp"
using namespace httplib;
using namespace ns_control;
static Control *ctrl_ptr = nullptr;
void Recovery(int signo)
{
ctrl_ptr->RecoveryMachine();
}
int main()
{
signal(SIGQUIT, Recovery);
//用户请求的服务路由功能
Server svr;
Control ctrl;// 当用户请求时就直接调用controller当中的方法,交互数据model也被controller包含在内
ctrl_ptr = &ctrl;
// 获取所有的题目列表
svr.Get("/all_questions", [&ctrl](const Request &req, Response &resp){
//返回一张包含有所有题目的html网页
std::string html;
ctrl.AllQuestions(&html);
//用户看到的是什么呢??网页数据 + 拼上了题目相关的数据
resp.set_content(html, "text/html; charset=utf-8");
});
// 用户要根据题目编号,获取题目的内容
// /question/100 -> 正则匹配
// R"()", 原始字符串raw string,保持字符串内容的原貌,不用做相关的转义
svr.Get(R"(/question/(\d+))", [&ctrl](const Request &req, Response &resp){
std::string number = req.matches[1];
std::string html;
ctrl.Question(number, &html);
resp.set_content(html, "text/html; charset=utf-8");
});
// 用户提交代码,使用我们的判题功能(1. 每道题的测试用例 2. compile_and_run)
svr.Post(R"(/judge/(\d+))", [&ctrl](const Request &req, Response &resp){
std::string number = req.matches[1];
std::string result_json;
ctrl.Judge(number, req.body, &result_json);
resp.set_content(result_json, "application/json;charset=utf-8");
// resp.set_content("指定题目的判题: " + number, "text/plain; charset=utf-8");
});
// 设置Web根目录
svr.set_base_dir("./wwwroot");
// 启动服务器
svr.listen("0.0.0.0", 8080);
return 0;
}
注意:
-
1.set_base_dir其实是提供给首页的,我们的url如果是http://101.42.249.66/的话就代表想要的资源是/,这个其实就代表的是访问的我们的web根目录(我们命名为wwwroot),而一般,这样的访问代表首页,我们会在web根目录下放置一个index.html供用户访问
-
2.R"()"上面以及说过了,就是row string,保持()中字符串原貌。
-
3.然后(\d+)代表的是正则表达式,+代表有多少就匹配多少,\d是匹配数字
-
4.上面使用到了Request当中的mathes对象,其实matches对象就是将我们的资源申请做了切分,比如说\question\100,question就放到了matches[0],100就放到了matches[1]当中。
我们提供了三个功能路由就分别对应三种资源申请
- http://110.42.249.66:8080/all_questions
- http://110.42.249.66:8080/question/1
2. 建立文件版的题库
首先,我们的题目需要的东西有
- 1.题号 number
- 2.标题 title
- 3.难度 star
- 4.描述 desc
- 5.时间要求 cpu_limit
- 6.空间要求 mem_limit
在oj_server目录下,我们需要一个questions目录对题目的所有东西进行存储。
而我们需要一个questions.list配置文件来读取所有题目(我们打算将题目构建成一个Question对象)
然后更具体的东西,比如题目的描述,预设给用户的代码,测试用例单独放在一个目录里
在questions.list配置文件中的存储方式
header.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution
{
public:
int Max(const vector<int> &v)
{
//将你的代码写在下面
return 0;
}
};
tail.cpp测试用例部分
所以我们需要给header.cpp中的代码进行合并,进行合并的代码就放在tail.cpp当中
所谓测试用例,其实就是把你在代码编辑框中的代码提交上来,然后和另外一个代码进行合并。这个代码里差的就是对你写的那部分函数。所以两个合在一起,才形成了完整的一个程序。
#ifndef COMPILER_ONLINE
#include "header.cpp"
#endif
void Test1()
{
vector<int> v = {1, 2, 3, 4, 5, 6};
int max = Solution().Max(v);
if (max == 6)
{
std::cout << "Test 1 .... OK" << std::endl;
}
else
{
std::cout << "Test 1 .... Failed" << std::endl;
}
}
void Test2()
{
vector<int> v = {-1, -2, -3, -4, -5, -6};
int max = Solution().Max(v);
if (max == -1)
{
std::cout << "Test 2 .... OK" << std::endl;
}
else
{
std::cout << "Test 2 .... Failed" << std::endl;
}
}
int main()
{
Test1();
Test2();
return 0;
}
注意:
- 条件编译的原因是:这部分代码因为缺少用户提交的那部分函数,所以我们在编译oj_server的时候,是会报错的,因为少了函数,跑不了可以理解。所以我们需要加一个条件编译,让这个.cc文件知道我们有该函数,不要报错。
- 这个条件编译到时候我们再通过给其他方式去掉,我们可以在调用g++的时候加选项,比如我们上面的宏是 COMPILER_ONLINE,那么到时候,我们直接 gcc … -D COMPILER_ONLIEN就可以去掉了。-D选项就是在命令行进行宏定义的方式
3. model模块
model模块主要是用来和数据交互的,对外提供访问数据的接口
我们在model模块当中,因为我们的数据就是题目,所以一上来我们就要把题目读出来。
我们会有一个Question类,用它来描述该题目的信息
// 根据题⽬list⽂件,加载所有的题⽬信息到内存中
// model: 主要⽤来和数据进⾏交互,对外提供访问数据的接⼝
namespace ns_model
{
using namespace std;
using namespace ns_log;
using namespace ns_util;
struct Question
{
std::string number; // 题⽬编号,唯⼀
std::string title; // 题⽬的标题
std::string star; // 难度: 简单 中等 困难
int cpu_limit; // 题⽬的时间要求(S)
int mem_limit; // 题⽬的空间要去(KB)
std::string desc; // 题⽬的描述
std::string header; // 题⽬预设给⽤⼾在线编辑器的代码
std::string tail; // 题⽬的测试⽤例,需要和header拼接,形成完整代码
};
}
选择用unordered_map<string,Question>的结构体来存储生成的Question,建立题目(字符串)与Question的映射。
使用boost准标准库当中的split进行字符串分割
class StringUtil
{
public:
/**
* str:输入性参数,要切分的字符串
* target:输出型参数,保存并返回切分完毕的结果
* sep:separator分隔符
*/
static void SplitString(const std::string &str,std::vector<std::string>* target,std::string sep)
{
//使用C++准标准库boost 当中的split进行字符串分割
boost::split((*target),str,boost::is_any_of(sep),boost::algorithm::token_compress_on);
//is_any_of代表sep分隔符字符串当中的任意一个字符都能用来分割
//token_compress_on代表我是否需要进行压缩
//调用这个接口就自动的帮我们完成了字符串切分
}
};
按行读取配置文件形成Question对象
- 1.用C++的文件流的方式创建ifstream对象,打开文件流
- 2.使用getline进行按行读取,getline的注意事项上面以及说过,不再重复
- 3.使用字符串工具类中封装好的函数进行字符串切割放入tokens数组
- 4.利用该数组进行Question结构体的创建
// 根据题⽬list⽂件,加载所有的题⽬信息到内存中
// model: 主要⽤来和数据进⾏交互,对外提供访问数据的接⼝
namespace ns_model
{
using namespace std;
using namespace ns_log;
using namespace ns_util;
const std::string questins_list = "./questions/questions.list";
const std::string questins_path = "./questions/";
class Model
{
private:
// 题号:题目细节
unordered_map<string, Question> questions;
public:
Model()
{
assert(LoadQuestionList(questins_list));
}
bool LoadQuestionList(const string &question_list)
{
// 加载配置⽂件: questions/questions.list + 题⽬编号⽂件
ifstream in(question_list);
if (!in.is_open())
{
LOG(FATAL) << " 加载题库失败,请检查是否存在题库⽂件" << "\n";
return false;
}
string line;
while (getline(in, line))
{
vector<string> tokens;
StringUtil::SplitString(line, &tokens, " ");
// 1 判断回⽂数 简单 1 30000
if (tokens.size() != 5)
{
LOG(WARNING) << "加载部分题⽬失败, 请检查⽂件格式" << "\n";
continue;
}
Question q;
q.number = tokens[0];
q.title = tokens[1];
q.star = tokens[2];
q.cpu_limit = atoi(tokens[3].c_str());
q.mem_limit = atoi(tokens[4].c_str());
string path = questins_path;
path += q.number;
path += "/";
FileUtil::ReadFile(path + "desc.txt", &(q.desc), true);
FileUtil::ReadFile(path + "header.cpp", &(q.header), true);
FileUtil::ReadFile(path + "tail.cpp", &(q.tail), true);
questions.insert({q.number, q});
}
LOG(INFO) << "加载题库...成功!" << "\n";
in.close();
}
bool GetAllQuestions(vector<Question> *out)
{
if (questions.size() == 0)
{
LOG(ERROR) << "⽤⼾获取题库失败" << "\n";
return false;
}
for (const auto &q : questions)
{
out->push_back(q.second); // first: key, second: value
}
return true;
}
bool GetOneQuestion(const std::string &number, Question *q)
{
const auto &iter = questions.find(number);
if (iter == questions.end())
{
LOG(ERROR) << "⽤⼾获取题⽬失败, 题⽬编号: " << number << "\n";
return false;
}
(*q) = iter->second;
return true;
}
~Model()
{
}
};
}
4.controller模块
controller模块整体结构
Controller模块是MVC架构模式当中的C,主要负责核心逻辑的编写。
比如model模块和view模块的调用将来都是在controller模块
我们以及有了功能路由,但是如果向访问到页面,就需要用到view模块(前端页面)和model模块(数据获取)。所以功能路由一定是通过创建controller对象去进行调用。(controller的类当中就会合理的调用model模块还要view模块,就会有一个渲染好的html显示给用户)
5.judge模块(负载均衡)
用户在编辑器中编写的代码提交给oj_server之后,oj_server是需要做负载均衡的,也就是选择负载最少的主机进行访问
那么我们就在controller增加一个判题的功能。当客户端把代码提交上来之后,judge模块就要进行主机的选择,然后序列化成compile_server需要的json串发过去。(不要忘记需要拼接测试用例)
现在看来,用户提交的json串,有三部分构成
- 1.首先需要题目的id,让我们可以进行测试用例的拼接
- 2.code,这个就是用户编辑的那部分代码
- 3.input,其实是可以有自测输入的,不过我们今天不支持,反正也不难
收到json串的code之后,judge模块就会根据读取配置文件建立好的unordered_map来找到对应的题目细节,然后拿到题目对应的测试用例,进行拼接。
那么有哪些主机可以供我们选择呢?我们又怎么去选择负载最低的呢?
所以我们就需要给一个配置文件,里面配置的就是主机的信息,比如IP,端口,然后我们还需要再oj_server当中维护对应主机的负载情况,以便我们进行选择。
Machine类
namespace ns_control
{
using namespace std;
using namespace ns_log;
using namespace ns_util;
using namespace ns_model;
using namespace ns_view;
using namespace httplib;
// 提供服务的主机
class Machine
{
public:
std::string ip; // 编译服务的ip
int port; // 编译服务的port
uint64_t load; // 编译服务的负载
std::mutex *mtx; // mutex禁止拷贝的,使用指针
public:
Machine() : ip(""), port(0), load(0), mtx(nullptr)
{
}
~Machine()
{
}
public:
// 提升主机负载
void IncLoad()
{
if (mtx)
mtx->lock();
++load;
if (mtx)
mtx->unlock();
}
// 减少主机负载
void DecLoad()
{
if (mtx)
mtx->lock();
--load;
if (mtx)
mtx->unlock();
}
void ResetLoad()
{
if (mtx)
mtx->lock();
load = 0;
if (mtx)
mtx->unlock();
}
// 获取主机负载,没有太大的意义,只是为了统一接口
uint64_t Load()
{
uint64_t _load = 0;
if (mtx)
mtx->lock();
_load = load;
if (mtx)
mtx->unlock();
return _load;
}
};
}
注意:
一旦连接我,拼接完之后就要对主机进行选择,所以这里是要加锁包的,为了负载均衡,我们维护的有load,我们要选择load最小的去进行服务。
负载均衡函数
namespace ns_control
{
using namespace std;
using namespace ns_log;
using namespace ns_util;
using namespace ns_model;
using namespace ns_view;
using namespace httplib;
const std::string service_machine = "./conf/service_machine.conf";
// 负载均衡模块
class LoadBlance
{
private:
// 可以给我们提供编译服务的所有的主机
// 每一台主机都有自己的下标,充当当前主机的id
std::vector<Machine> machines;
// 所有在线的主机id
std::vector<int> online;
// 所有离线的主机id
std::vector<int> offline;
// 保证LoadBlance它的数据安全
std::mutex mtx;
public:
LoadBlance()
{
assert(LoadConf(service_machine));
LOG(INFO) << "加载 " << service_machine << " 成功"
<< "\n";
}
~LoadBlance()
{
}
public:
bool LoadConf(const std::string &machine_conf)
{
std::ifstream in(machine_conf);
if (!in.is_open())
{
LOG(FATAL) << " 加载: " << machine_conf << " 失败"
<< "\n";
return false;
}
std::string line;
while (std::getline(in, line))
{
std::vector<std::string> tokens;
StringUtil::SplitString(line, &tokens, ":");
if (tokens.size() != 2)
{
LOG(WARNING) << " 切分 " << line << " 失败"
<< "\n";
continue;
}
Machine m;
m.ip = tokens[0];
m.port = atoi(tokens[1].c_str());
m.load = 0;
m.mtx = new std::mutex();
online.push_back(machines.size());
machines.push_back(m);
}
in.close();
return true;
}
// id: 输出型参数
// m : 输出型参数
bool SmartChoice(int *id, Machine **m)
{
// 1. 使用选择好的主机(更新该主机的负载)
// 2. 我们需要可能离线该主机
mtx.lock();
// 负载均衡的算法
// 1. 随机数+hash
// 2. 轮询+hash
int online_num = online.size();
if (online_num == 0)
{
mtx.unlock();
LOG(FATAL) << " 所有的后端编译主机已经离线, 请运维的同事尽快查看"
<< "\n";
return false;
}
// 通过遍历的方式,找到所有负载最小的机器
*id = online[0];
*m = &machines[online[0]];
uint64_t min_load = machines[online[0]].Load();
for (int i = 1; i < online_num; i++)
{
uint64_t curr_load = machines[online[i]].Load();
if (min_load > curr_load)
{
min_load = curr_load;
*id = online[i];
*m = &machines[online[i]];
}
}
mtx.unlock();
return true;
}
void OfflineMachine(int which)
{
mtx.lock();
for (auto iter = online.begin(); iter != online.end(); iter++)
{
if (*iter == which)
{
machines[which].ResetLoad();
// 要离线的主机已经找到啦
online.erase(iter);
offline.push_back(which);
break; // 因为break的存在,所有我们暂时不考虑迭代器失效的问题
}
}
mtx.unlock();
}
void OnlineMachine()
{
// 我们统一上线,后面统一解决
mtx.lock();
online.insert(online.end(), offline.begin(), offline.end());
offline.erase(offline.begin(), offline.end());
mtx.unlock();
LOG(INFO) << "所有的主机有上线啦!" << "\n";
}
// for test
void ShowMachines()
{
mtx.lock();
std::cout << "当前在线主机列表: ";
for (auto &id : online)
{
std::cout << id << " ";
}
std::cout << std::endl;
std::cout << "当前离线主机列表: ";
for (auto &id : offline)
{
std::cout << id << " ";
}
std::cout << std::endl;
mtx.unlock();
}
};
}
6.view模块整体代码结构(前端的东西,不是重点)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>这是我的个人OJ系统</title>
<style>
/* 起手式, 100%保证我们的样式设置可以不受默认影响 */
* {
/* 消除网页的默认外边距 */
margin: 0px;
/* 消除网页的默认内边距 */
padding: 0px;
}
html,
body {
width: 100%;
height: 100%;
}
.container .navbar {
width: 100%;
height: 50px;
background-color: black;
/* 给父级标签设置overflow,取消后续float带来的影响 */
overflow: hidden;
}
.container .navbar a {
/* 设置a标签是行内块元素,允许你设置宽度 */
display: inline-block;
/* 设置a标签的宽度,a标签默认行内元素,无法设置宽度 */
width: 80px;
/* 设置字体颜色 */
color: white;
/* 设置字体的大小 */
font-size: large;
/* 设置文字的高度和导航栏一样的高度 */
line-height: 50px;
/* 去掉a标签的下划线 */
text-decoration: none;
/* 设置a标签中的文字居中 */
text-align: center;
}
/* 设置鼠标事件 */
.container .navbar a:hover {
background-color: green;
}
.container .navbar .login {
float: right;
}
.container .content {
/* 设置标签的宽度 */
width: 800px;
/* 用来调试 */
/* background-color: #ccc; */
/* 整体居中 */
margin: 0px auto;
/* 设置文字居中 */
text-align: center;
/* 设置上外边距 */
margin-top: 200px;
}
.container .content .font_ {
/* 设置标签为块级元素,独占一行,可以设置高度宽度等属性 */
display: block;
/* 设置每个文字的上外边距 */
margin-top: 20px;
/* 去掉a标签的下划线 */
text-decoration: none;
/* 设置字体大小
font-size: larger; */
}
</style>
</head>
<body>
<div class="container">
<!-- 导航栏, 功能不实现-->
<div class="navbar">
<a href="/">首页</a>
<a href="/all_questions">题库</a>
<a href="#">竞赛</a>
<a href="#">讨论</a>
<a href="#">求职</a>
<a class="login" href="#">登录</a>
</div>
<!-- 网页的内容 -->
<div class="content">
<h1 class="font_">欢迎来到我的OnlineJudge平台</h1>
<p class="font_">这个我个人独立开发的一个在线OJ平台</p>
<a class="font_" href="/all_questions">点击我开始编程啦!</a>
</div>
</div>
</body>
</html>
one_question
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{number}}.{{title}}</title>
<!-- 引入ACE插件 -->
<!-- 官网链接:https://ace.c9.io/ -->
<!-- CDN链接:https://cdnjs.com/libraries/ace -->
<!-- 使用介绍:https://www.iteye.com/blog/ybc77107-2296261 -->
<!-- https://justcode.ikeepstudying.com/2016/05/ace-editor-%E5%9C%A8%E7%BA%BF%E4%BB%A3%E7%A0%81%E7%BC%96%E8%BE%91%E6%9E%81%E5%85%B6%E9%AB%98%E4%BA%AE/ -->
<!-- 引入ACE CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript"
charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ext-language_tools.js" type="text/javascript"
charset="utf-8"></script>
<!-- 引入jquery CDN -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
.container .navbar {
width: 100%;
height: 50px;
background-color: black;
/* 给父级标签设置overflow,取消后续float带来的影响 */
overflow: hidden;
}
.container .navbar a {
/* 设置a标签是行内块元素,允许你设置宽度 */
display: inline-block;
/* 设置a标签的宽度,a标签默认行内元素,无法设置宽度 */
width: 80px;
/* 设置字体颜色 */
color: white;
/* 设置字体的大小 */
font-size: large;
/* 设置文字的高度和导航栏一样的高度 */
line-height: 50px;
/* 去掉a标签的下划线 */
text-decoration: none;
/* 设置a标签中的文字居中 */
text-align: center;
}
/* 设置鼠标事件 */
.container .navbar a:hover {
background-color: green;
}
.container .navbar .login {
float: right;
}
.container .part1 {
width: 100%;
height: 600px;
overflow: hidden;
}
.container .part1 .left_desc {
width: 50%;
height: 600px;
float: left;
overflow: scroll;
}
.container .part1 .left_desc h3 {
padding-top: 10px;
padding-left: 10px;
}
.container .part1 .left_desc pre {
padding-top: 10px;
padding-left: 10px;
font-size: medium;
font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
.container .part1 .right_code {
width: 50%;
float: right;
}
.container .part1 .right_code .ace_editor {
height: 600px;
}
.container .part2 {
width: 100%;
overflow: hidden;
}
.container .part2 .result {
width: 300px;
float: left;
}
.container .part2 .btn-submit {
width: 120px;
height: 50px;
font-size: large;
float: right;
background-color: #26bb9c;
color: #FFF;
/* 给按钮带上圆角 */
/* border-radius: 1ch; */
border: 0px;
margin-top: 10px;
margin-right: 10px;
}
.container .part2 button:hover {
color:green;
}
.container .part2 .result {
margin-top: 15px;
margin-left: 15px;
}
.container .part2 .result pre {
font-size: large;
}
</style>
</head>
<body>
<div class="container">
<!-- 导航栏, 功能不实现-->
<div class="navbar">
<a href="/">首页</a>
<a href="/all_questions">题库</a>
<a href="#">竞赛</a>
<a href="#">讨论</a>
<a href="#">求职</a>
<a class="login" href="#">登录</a>
</div>
<!-- 左右呈现,题目描述和预设代码 -->
<div class="part1">
<div class="left_desc">
<h3><span id="number">{{number}}</span>.{{title}}_{{star}}</h3>
<pre>{{desc}}</pre>
</div>
<div class="right_code">
<pre id="code" class="ace_editor"><textarea class="ace_text-input">{{pre_code}}</textarea></pre>
</div>
</div>
<!-- 提交并且得到结果,并显示 -->
<div class="part2">
<div class="result"></div>
<button class="btn-submit" onclick="submit()">提交代码</button>
</div>
</div>
<script>
//初始化对象
editor = ace.edit("code");
//设置风格和语言(更多风格和语言,请到github上相应目录查看)
// 主题大全:http://www.manongjc.com/detail/25-cfpdrwkkivkikmk.html
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/c_cpp");
// 字体大小
editor.setFontSize(16);
// 设置默认制表符的大小:
editor.getSession().setTabSize(4);
// 设置只读(true时只读,用于展示代码)
editor.setReadOnly(false);
// 启用提示菜单
ace.require("ace/ext/language_tools");
editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
});
function submit(){
// alert("嘿嘿!");
// 1. 收集当前页面的有关数据, 1. 题号 2.代码
var code = editor.getSession().getValue();
// console.log(code);
var number = $(".container .part1 .left_desc h3 #number").text();
// console.log(number);
var judge_url = "/judge/" + number;
// console.log(judge_url);
// 2. 构建json,并通过ajax向后台发起基于http的json请求
$.ajax({
method: 'Post', // 向后端发起请求的方式
url: judge_url, // 向后端指定的url发起请求
dataType: 'json', // 告知server,我需要什么格式
contentType: 'application/json;charset=utf-8', // 告知server,我给你的是什么格式
data: JSON.stringify({
'code':code,
'input': ''
}),
success: function(data){
//成功得到结果
// console.log(data);
show_result(data);
}
});
// 3. 得到结果,解析并显示到 result中
function show_result(data)
{
// console.log(data.status);
// console.log(data.reason);
// 拿到result结果标签
var result_div = $(".container .part2 .result");
// 清空上一次的运行结果
result_div.empty();
// 首先拿到结果的状态码和原因结果
var _status = data.status;
var _reason = data.reason;
var reason_lable = $( "<p>",{
text: _reason
});
reason_lable.appendTo(result_div);
if(status == 0){
// 请求是成功的,编译运行过程没出问题,但是结果是否通过看测试用例的结果
var _stdout = data.stdout;
var _stderr = data.stderr;
var stdout_lable = $("<pre>", {
text: _stdout
});
var stderr_lable = $("<pre>", {
text: _stderr
})
stdout_lable.appendTo(result_div);
stderr_lable.appendTo(result_div);
}
else{
// 编译运行出错,do nothing
}
}
}
</script>
</body>
</html>
all_questions
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线OJ-题目列表</title>
<style>
/* 起手式, 100%保证我们的样式设置可以不受默认影响 */
* {
/* 消除网页的默认外边距 */
margin: 0px;
/* 消除网页的默认内边距 */
padding: 0px;
}
html,
body {
width: 100%;
height: 100%;
}
.container .navbar {
width: 100%;
height: 50px;
background-color: black;
/* 给父级标签设置overflow,取消后续float带来的影响 */
overflow: hidden;
}
.container .navbar a {
/* 设置a标签是行内块元素,允许你设置宽度 */
display: inline-block;
/* 设置a标签的宽度,a标签默认行内元素,无法设置宽度 */
width: 80px;
/* 设置字体颜色 */
color: white;
/* 设置字体的大小 */
font-size: large;
/* 设置文字的高度和导航栏一样的高度 */
line-height: 50px;
/* 去掉a标签的下划线 */
text-decoration: none;
/* 设置a标签中的文字居中 */
text-align: center;
}
/* 设置鼠标事件 */
.container .navbar a:hover {
background-color: green;
}
.container .navbar .login {
float: right;
}
.container .question_list {
padding-top: 50px;
width: 800px;
height: 100%;
margin: 0px auto;
/* background-color: #ccc; */
text-align: center;
}
.container .question_list table {
width: 100%;
font-size: large;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
margin-top: 50px;
background-color: rgb(243, 248, 246);
}
.container .question_list h1 {
color: green;
}
.container .question_list table .item {
width: 100px;
height: 40px;
font-size: large;
font-family:'Times New Roman', Times, serif;
}
.container .question_list table .item a {
text-decoration: none;
color: black;
}
.container .question_list table .item a:hover {
color: blue;
text-decoration:underline;
}
.container .footer {
width: 100%;
height: 50px;
text-align: center;
line-height: 50px;
color: #ccc;
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container">
<!-- 导航栏, 功能不实现-->
<div class="navbar">
<a href="/">首页</a>
<a href="/all_questions">题库</a>
<a href="#">竞赛</a>
<a href="#">讨论</a>
<a href="#">求职</a>
<a class="login" href="#">登录</a>
</div>
<div class="question_list">
<h1>OnlineJuge题目列表</h1>
<table>
<tr>
<th class="item">编号</th>
<th class="item">标题</th>
<th class="item">难度</th>
</tr>
{{#question_list}}
<tr>
<td class="item">{{number}}</td>
<td class="item"><a href="/question/{{number}}">{{title}}</a></td>
<td class="item">{{star}}</td>
</tr>
{{/question_list}}
</table>
</div>
<div class="footer">
<!-- <hr> -->
<h4>@比特就业课</h4>
</div>
</div>
</body>
</html>
五、最终效果
项目源码
Gitee:https://gitee.com/niu-zanqi/aries.c-warehouse.2/tree/master/OnlineJudge