基于Java实现简单的在线 OJ 系统

项目名称:在线 oj 系统

项目背景:

每个人都有自己的梦想,一个程序猿要实现自己的梦想,平时的练习就必不可少。平时的刷题是必不可少的,力扣网和牛客网是我平时刷题的两个网站。里边的功能深深的吸引了我,所以我尝试去实现一个类似于力扣网的在线OJ系统。

实现功能:

根据我们在力扣网上的参考,想实现让用户在一个网页上进行读题,做题,提交,最终反馈给用户结果、在操作过程中还要实现对题库进行增删改查这几个功能。

模块划分:

1.编译运行模块(compile)

此模块主要用来能让用户进行题目的编译和运行的操作。本模块里边有大致划分为四个模块。

1).执行命令模块(CommandUitl)
这个类里边完成的功能就是能进行编译和运行的指令。要进行编译和运行我们就要创建一个新的进程(子进程)。用到的技术有单例设计模式、多进程。里边用run方法来表示编译运行模块。要想进行编译和运行,指令必不可少,执行完指令之后,结果会有以下几种:第一是编译运行都成功,第二是编译出错,第三是编译成功但是运行出错。因此我们在设计参数的时候也就考虑到这三个参数。第一个参数是指令,第二个参数是标准输出文件,要是编译运行都成功则一定有输出的内容,我们把编译和运行都成功的内容放在一个标准输出文件中(stdoutFile中),第三个参数是标准错误文件,不管是编译出错还是运行出错到时也会有错误的内容,我们出错内容放在标准错误文件中(stderrFile中)。
注意

  • 进程的创建,用的是Runtime中提供的方法,它符合“单例设计模式”。

  • 子进程创建后,父进程要结束的话,要等待子进程结束后才能最终结束所有进程。所以要进行等待子进程的结束,用的是 .waitFor()方法。

  • 不管是标准输出还是标准错误里边的内容,我们都采用重定向,即将两个里边的内容放在我们重新创建的文件中,方便后边对文件的读写操作。

2)用户编译模块(Question):
用户在点进网页后,先会展示题目列表页和题目详情页,当点入具体的题目后,会出现编辑框让用户编辑代码。因此这个模块完成的功能是用户代码的输入。我们用question来表示,只需要定义一个code 属性。get,set 一下即可。
3)运行结果模块(Answer):
这个模块用来反馈运行结果给用户,只要是程序跑起来起来就会出现成功或者失败两种情况。本模块属性包含返回码、标准错误信息、输出信息。
4)编译运行整个过程(Task):
这个模块用来表示编译和运行全过程。我们可以想一下,在编译和运行过程中一定会产生好多的临时文件。例如:要编译运行的临时文件,编译出错的临时文件、运行出错的标准错误文件、标准输出的临时文件等等。我们将这些临时的文件放在一个目录下,方便后边进程间通信。此模块里边又分为以下4步。

  • 考虑到会有多个人进行此系统的操作。因此要保证每一个用户生成的临时文件都有唯一的目录。用UUID来生成唯一目录。
  • 把要进行编译的源代码,就是用户写好的代码放在一个文件中。
  • 有了源代码之后,首先进行的是 javac编译指令的构建并执行。编译之后无外乎两种情况:一是编译出错,也就是编译错误文件中有内容,设置一个1来表示编译出错;二是编译成功,也就是编译错误的文件中没有内容。
  • 要是编译成功之后,执行java运行指令,就是运行刚编译好的.class文件。结果也无外乎两种:一是运行成功,即也就是标准错误文件中没有内容;二是运行失败,设置个2表示运行失败,也就是标准错误中有内容。

2.保存题目模块(problem):

此模块实现的功能进行题目的插入、删除、题目列表页的显示和题目详情页的显示。题目列表页就像力扣一样,只显示了题目的序号、标题、难度级别这三个。而题目详情页就是用户带进去之后显示题目的详情信息和代码编辑框,模板代码等内容。这个里边我们又分为两个类。
1)题目实体类:
这个类是一个实体类,表示的是数据库中的一条记录就对应的是网页上边显示的每一个实例。里边的属性有:题目ID,标题,难度,描述,模板代码,测试代码。
2)数据访问类:
这个类里边主要进行题目的增加、删除、题目列表页显示、题目详情页显示。这里不涉及修改,因为在力扣中,题目不是能让用户随便改变的。这个类里边又大致分为以下几个小的方法。

  • 给数据库中增添一条记录(insert):主要用的是JDBC来操作数据库,这里表示的是插入不牵扯返回结果集,基本步骤是:和数据库建立连接、拼装sql、执行sql、断开连接。
  • 从数据库中删除一条记录(delete):同上,还是JDBC操作数据库,只是删除一条记录不牵扯返回结果集,基本步骤:和数据库建立连接、拼装sql、执行sql、断开连接。
  • 题目列表页的展示(selectAll):实现的功能为显示所有的题目,这个里边是大致的显示列表:包含所有题目(每一行里边只显示题目序号、标题、难度级别)。这个还是通过操作数据库来给页面展示效果,它在JDBC操作的时候和上边两个不同的是它要返回结果集,将结果集渲染在页面上。所以其JDBC操作基本步骤是:建立连接、拼装sql、执行sql、遍历结果集、断开连接。
  • 题目详情展示页(selectOne):主要实现的功能是展示一个题目的所有信息(题目序号、标题、难度级别、题目描述、模板代码、测试代码)。还是要给用户打开页面进行结果集的展示JDBC操作步骤:建立连接、拼装sql、执行sql、遍历结果集、断开连接。

3. API模块:

次模块用来实现前后端的交互,就是规定一种前后端都认可的HTTP接口设计。里边大体分为四个小的方法。
1)编译请求(CompileRequest)
在编译好代码之后要运行,所以要将编译好的代码整个模块所有的数据发送给服务端。编译好的代码只需要发送题目对应的id还有里边的code即可。所以这个类里边表示的是给服务端发送请求时的题目id和具体的代码。
2)编译响应(CompileResponse)
在给服务端发送请求之后,服务端进行数据解析。最终返回响应,响应里边包含返回码(不管是编译错误、运行错误、编译运行成功都会有返回码)、错误原因(要是编译错误或者运行错误则返回错误信息,也就是打印出出错的堆栈信息)、标准输出(要是编译和运行都没有出错,则会有标准输出内容)。
3)编译Servlet(CompileServlet)
这个模块主要是要完成数据格式之间的转换,完成客户端和服务端之间的相互解析响应全过程。
(1)先从request中读取body的内容。
(2)再将json数据转化成请求对象。
(3)根据请求中的id在数据库中查询对应的测试代码。
(4)拼接好用户提交代码和数据库中的测试代码,再进行整套的编译运行操作。
(5)最终把运行结果包装成响应数据,再返回给页面即可。
4)问题Servlet(ProblemServlet)
这个模块是用来给前端的页面展示题目列表页和题目详情页的,里边还包含给数据库中插入题目和从数据库中删除一条记录的操作。里边包含以下几个主要方法。
(1)doGet方法:通过创建problemDao请求对象,向服务端发起请求,在数据库中查找结果并转化成Json格式最终返回给客户端界面。完成的功能是展示题目列表页和题目详情页。
(2)doPost方法:主要完成给数据库中新增一条记录并返回给客户端插入结果。插入的时候先要获取body中的内容,然后将内容转换成Problem对象,最后在插入到数据库中并返回客户端一个结果(就是状态码,提示信息等)。
(3)doDelete方法:主要完成从数据库中删除一条记录并返回给客户端删除结果。删除的时候是根据题目id来删除的,所以要先获取请求的id,在通过写的删除操作删除即可。最终还是要返回给客户端一个删除结果(状态码,提示信息等)。

4.辅助模块:

这个模块主要负责的事情就是对我们以上一个模块的重复操作进行提取,就是我们所说的封装。比如JDBC操作中的建立连接和关闭连接、还有问价的读取操作等。我们进行封装的主要有以下三个部分。
(1)DBUtil:在进行JDBC操作的时候每一次我们都要,先获取数据资源、建立连接、关闭连接这些重复的操作,所以对这些操作进行提取。用的时候直接调用即可。
(2)HTTPUtil:上边也说了,我们在进行客户端和服务端交互的时候经常要进行body内容的获取操作。因此我们对读body的内容进行了封装,用的时候直接调用。
(3)FileUtil:在编译运行的模块牵扯到不止一次的读写文件,同理还是进行封装,用的时候直接调用即可。

5.成果展示

1)题目列表页展示:

2)题目详情页展示:
基于Java实现简单的在线 OJ 系统_第1张图片
3)编译运行都成功展示:
基于Java实现简单的在线 OJ 系统_第2张图片
这也符合我们的预期结果(规定编译运行都成功放回码是0,并提示用例通过)。
4)编译出错展示(让println书写错误):
基于Java实现简单的在线 OJ 系统_第3张图片
符合预期(编译出错会返回错误码1,提示编译出错信息)。这里出现乱码,因为用的服务器是Tomcat会出现中文乱码。

5)编译成功运行出错展示(直接构造一个空指针异常)
基于Java实现简单的在线 OJ 系统_第4张图片
结果符合预期(返回码是2,并打印出异常信息)。

6.源码链接:

在线 OJ 链接

你可能感兴趣的:(项目,tomcat,java)