关于研一上学期的课题项目总结(在线编程系统)
说明:本项目属于临时接手项目,接手后主要是针对项目中的功能完善和改进。
本文主要针对我在项目中的完成功能介绍以及实现的方式方法,和一些在工作中遇到的棘手问题的解决。
一、后台系统管理员导题模块--(绝非简单的插库操作)
总论:---讲一讲三表之间的插库顺序及判重问题的处理逻辑,后来出现的字符问题,大量改库泪奔。
1.1介绍
导题模块主要的操作对象是三张表(question,code,Parameter),将一张页面信息分别存放于三张表中。
存放顺序(原则上):question--->code--->parameter,但困难是:
①仅有question,单独录入code和parameter。
②仅有question,和code中java语言,录入code表C语言和parameter表
③仅有question,code,录入parameter表
④有question,code,parameter的一个或几个参数,只增加parameter表的一组参数。
⑤最开心的事:管理员录入题目时,三张表一次都不错的录入完毕,然而让他录入300道题目的情况下,这是不可能的。
上面的四种情况仅仅是集中典型的情况,另外操作中海油很多问题需要判重机制【每当插入一张表,都会留有flag,用于判断插入下一张表的判定】
question:存放试题id,名称,类型,难易程度,标题,内容以及通过率。
![](https://img.haomeiwen.com/i18653827/6f2d833c7e328b32.png)
code:对应的question的id,编程语言类型(目前仅仅支持java和C),函数头,main方法。
![](https://img.haomeiwen.com/i18653827/ac01131626482a66.png)
parameter:对应code的id,输入参数和输出参数。
![](https://img.haomeiwen.com/i18653827/e1b9d8e8deeb7706.png)
1.2 解决方案:
1.2.1 方案思路:
①question由title判重
0:插入question表,并r1为插入结果返回值。1: 啥都没干,此时r1没赋值。
②接下来,code表判重【 code表由title和codetype联合查询(具有唯一性)判重c表】
0: r1=1,if (r1)插入code表,r2为结果值。 1:啥都没干,r1没有赋值,(说明r1不赋值,就说明code表不会插入数据),
③接下来 parameter表判重
0: r2=1 if(r2)插入p表,结果返回值赋值给r3输出,1: r2不赋值。说明parameter表重复。
1.2.2 java代码
//试题导入controller
@RequestMapping(value = "/insert_question",method = {RequestMethod.POST},produces =MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Result<Integer> insertQuestion(@RequestBody Map<String,Object> map){
System.out.println("call /question/insert_question");
Integer r1=0,r2=0,r3=0,question_count=0;
String questionTypeId=map.get("questionTypeId").toString();
String difficultyId=map.get("difficultyId").toString();
String title=map.get("title").toString();
String content=map.get("content").toString();
Question question =new Question();
question.setQuestionTypeId(Integer.parseInt(questionTypeId));
question.setDifficultyId(Integer.parseInt(difficultyId));
question.setTitle(title);
question.setContent(content);
int codeTypeId=Integer.parseInt(map.get("codeTypeId").toString());
int codeTypeId_another=0;
int returnType=Integer.parseInt(map.get("returnType").toString());
//获取不定项输入参数
// System.out.println(map.get("inputNumber"));
ArrayList<LinkedHashMap<String,Object>> inputNumber = (ArrayList<LinkedHashMap<String,Object>>)map.get("inputNumber");
System.out.println(inputNumber.toString());
JSONArray jsonArray_input = JSONArray.fromObject(inputNumber);
System.out.println(jsonArray_input);
String inputNumber_type[] = new String[30];
String inputNumber_data[] = new String[30];
int inputSize=jsonArray_input.size();
if(inputSize>0){
for(int i=0;i<inputSize;i++){
JSONObject job= jsonArray_input.getJSONObject(i); // 遍历 jsonarray 数组,把每一个对象转成 json 对象
// System.out.println("`````````"+job.get("input_type_"+(i+1))) ; // 得到 每个对象中的属性值
// System.out.println("````````````"+job.get("input_data_"+(i+1)));
inputNumber_type[i]=job.get("input_type_"+(i+1)).toString();
inputNumber_data[i]=job.get("input_data_"+(i+1)).toString();
}
}
//验证数组
// for(int i=0;i<inputSize;i++){
// System.out.println(inputNumber_type[i]);
// System.out.println(inputNumber_data[i]);
// }
//获取不定项输出参数
ArrayList<LinkedHashMap<String,Object>> outputNumber = (ArrayList<LinkedHashMap<String,Object>>)map.get("outputNumber");
JSONArray jsonArray_output = JSONArray.fromObject(outputNumber);
System.out.println(jsonArray_output);
int outputSize =jsonArray_output.size();
String outputNumber_type[] = new String[30];
String outputNumber_data[] = new String[30];
if(outputSize>0){
for(int i=0;i<outputSize;i++){
JSONObject job1 = jsonArray_output.getJSONObject(i); // 遍历 jsonarray 数组,把每一个对象转成 json 对象
//System.out.println(""+job.get("input_type_"+(i+1))) ; // 得到 每个对象中的属性值
outputNumber_type[i]=job1.get("output_type_"+(i+1)).toString();
outputNumber_data[i]=job1.get("output_data_"+(i+1)).toString();
}
}
Result<Integer> result=new Result<>();
//此处需要根据parameterId和returnType查询对应的返回值类型
//判断编程语言类型 java:1,c:2,c++:3
ParameterType parameterType= questionService.selectParameterTypeById(returnType);
String returnType_name;
if(codeTypeId==1){
returnType_name=parameterType.getParameterTypeNameJava();
for(int i=0;i<inputSize;i++){
parameterType=questionService.selectParameterTypeById(Integer.parseInt(inputNumber_type[i]));
inputNumber_type[i]=parameterType.getParameterTypeNameJava();
}
for(int i=0;i<outputSize;i++){
parameterType=questionService.selectParameterTypeById(Integer.parseInt(outputNumber_type[i]));
outputNumber_type[i]=parameterType.getParameterTypeNameJava();
}
}else if(codeTypeId==2){
returnType_name=parameterType.getParameterTypeNameC();
for(int i=0;i<inputSize;i++){
parameterType=questionService.selectParameterTypeById(Integer.parseInt(inputNumber_type[i]));
inputNumber_type[i]=parameterType.getParameterTypeNameC();
}
for(int i=0;i<outputSize;i++){
parameterType=questionService.selectParameterTypeById(Integer.parseInt(outputNumber_type[i]));
outputNumber_type[i]=parameterType.getParameterTypeNameC();
}
}else{
//c++ 目前未写.先赋值为空串
returnType_name="";
}
//此处需要调用拼接函数
String testSolution =ProgrammingUtil.connectSolution(codeTypeId,returnType_name,inputNumber_type,inputSize);
String testMain=ProgrammingUtil.connectMain(codeTypeId,returnType_name,inputNumber_type,inputSize);
// //此处三项全部添加完毕才算成功
Integer i=questionService.question_count_current(title);
System.out.println(i);
if(questionService.question_count_current(title)==0) {
r1 = questionService.insertQuestion(question); //此处三项全部添加完毕才算成功
result.setResultStatus(ResultStatus.SUCCESS);
result.setMessage("question添加成功!code和parameter添加失败");
result.setData(r1);
}
//此处三项全部添加完毕才算成功
//判断重复code表
question_count=questionService.question_count_titltCode(title,codeTypeId);
if(question_count==0){
r1=1;
}
Code code = new Code();
//获得code表对应的question_id
Integer questionId=0;
if(question.getQuestionId()==0){
//写一个根据却title查询question表得到questionID的sql
questionId=questionService.getQuestionIdByTitle(title);
System.out.println(questionId);
code.setQuestionId(questionId);
}else{
questionId=question.getQuestionId();
code.setQuestionId(questionId);
}
// System.out.println(question.getQuestionId());
code.setCodeTypeId(codeTypeId);
/*以下方法使用拼接函数之后的方法*/
code.setSolutionFunction(testSolution);
code.setSolutionMain(testMain);
if(r1!=0){
r2=questionService.insertCode(code);
result.setResultStatus(ResultStatus.SUCCESS);
result.setMessage("code添加成功");
result.setData(r2);
}
Parameter parameter =new Parameter();
Integer codeCount=questionService.getCodeCount(questionId);
if(codeCount!=0){
//从数据库里获得,通过查询code表(根据questionID和codeTypeID)
Integer codeId=questionService.getQuestionIdByQuestionIdCodeTypeId(questionId,codeTypeId);
parameter.setCodeId(codeId);
}else{
parameter.setCodeId(code.getCodeId());
}
//对预插入的参数进行拼接---------
parameter.setInputParameter(ProgrammingUtil.connectParameter(inputNumber_type,inputNumber_data,inputSize));
parameter.setOutputParameter(ProgrammingUtil.connectParameter(outputNumber_type,outputNumber_data,outputSize));
//判断同一种codeID下的parameter重复问题(要求在parameter表中同一个codeID下,不能出现inputParameter和outPutParameter
// 一模一样的情况【为parameter重复】)
Integer parameterCount=questionService.isRepeat(parameter);
r2=(parameterCount==1?0:1);
if(r2!=0){
System.out.println(parameter);
r3=questionService.insertParameter(parameter);
// System.out.println(r3);
result.setResultStatus(ResultStatus.SUCCESS);
result.setMessage("parameter添加成功!");
result.setData(r3);
}else{
result.setMessage("parameter参数记录重复!");
}
return result;
}
二、开发独立模块--答题闯关
总论:讲一讲随机题库抽取以及用户已经抽取到闯关成功的题目不会再次出现,全部完成题库抽取题目的闯关即为全部通关。
技术点问题:指出频繁查库显示会影响显示速度,cookie设置但数据的结构化显示不会。请求指导。
功能实现要求:随机从题库中抽取三道题目用于闯关挑战
2.1 随机试题抽取
2.1.1 代码sql
<!--随机抽取3道题目用于闯关答题环节-->
<select id="select_quesiton_page_challenge" resultType="com.sunlight.model.Question">
select a.* from
( select question.question_id,question_type_id,question.difficulty_id,title,passing_rate
from question,difficulty,code
<where>
question.difficulty_id=difficulty.difficulty_id and code.question_id=question.question_id
<if test="title!='' and title!=null">
and title like '%${title}%'
</if>
</where>
group by question.question_id having count(code.question_id)=2
/*and question_id in (1,3,5,7,9,14,22,11,29,33,41,42,45,233,410)*/
and question_id not in (select distinct question_id from question_log where user_id=#{userId} and challenge_pass=1 )
order by rand() limit #{number}) as a order by a.question_id asc
</select>
代码介绍
在抽取试题的选择上,用户每次作答的题目记录都存于question_log表中,在最后的一个challenge_pass字段中用于标识用户是否已经成功闯关本题目(下方附图),闯关成功标识为“1”,
下次抽取题目时,不再出现。
另外抽取题目时只抽取三张表内容都完整的题目。
[![](https://img.haomeiwen.com/i18653827/07aad45b372a6472.png)
2.1.2 闯关成功的页面回显
![](https://img.haomeiwen.com/i18653827/1fadd323c79dd06f.png)
闯关出题页面
![](https://img.haomeiwen.com/i18653827/9739bd9d05531e8f.png)
答题页面
![](https://img.haomeiwen.com/i18653827/3d3ce4bfbdebbd36.png)
完成一道题目后
说一下上面的实现以及缺点:
①每次完成一道题目后的页面回显,我采用的是再次查库,在访问量低的情况下,这种方法可以使用,一旦访问量增加(比如1000人同时在线使用),在页面回显时会显示非常慢,
如果使用cookie机制,我能说自己太菜,不会从cookie中获取全部数据然后结构化的显示出来吗?请求指导。
②还有一点,其实页面题目的回显并没有真正的实现一 一绑定,我只是借用了id不唯一的特性,每次查询出题目都升序排列,再次回显也是升序,这样是否通过的显示标志就能和题目一 一对应上了(只是一种假对应罢了,至少在用户看来是真的)。
页面回显查库代码:
<!--//再次查询这三道题目-确定性查询当前正在挑战的题目-->
<select id="getChallengingQuestion" resultType="Question">
select * from
(select question_id,question_type_id,question.difficulty_id,title,passing_rate
from question where question_id in
<foreach collection="arrayObj" open="(" separator="," close=")" item="haha" index="index">
#{haha}
</foreach>) as a
order by a.question_id asc
</select>
<!--查询当前正在挑战的题目是否通过-->
<select id="getChallengingQuestionIsPass" resultType="Integer">
select count(*) from question_log where
user_id=#{userId}
and question_id =#{questionId} and challenge_pass=1
</select>
三、用户在系统界面的行为记录(登录时间,每道题目的停留时间)采集存入Excel文件以及插入数据库
--总论:插入Excel文件的代码展示,以及采用的Set 集合缓冲实现。
功能要求:用户在答题界面未答题的记录存入Excel文件,答题页面提交的题目记录存入question_log表。
重点介绍缓冲写入Excel文件中
其中引入缓冲机制:只有当要写入的文件的记录数大于100条或者系统仅剩最后一位用户时,启动一次写磁盘操作。
代码演示:
@RequestMapping(value = "/getUserLeaveBehaviour",method = {RequestMethod.POST},produces = MediaType.APPLICATION_JSON_UTF8_VALUE) //以json格式返回
@ResponseBody
public void getUserLeaveBehaviourInformation( HttpServletRequest request, @RequestBody Map<String,Object> myMap){
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
Map<String,Object> resultMap = new HashMap<String,Object>();
Integer questionId = (Integer)myMap.get("questionId");
int userId = Integer.parseInt(myMap.get("userId").toString());
//根据用户id获取用户姓名
User user = userService.getUserById(userId);
String username = user.getUserName();
String loginInTime = myMap.get("loginInTime").toString();
String leaveTime = myMap.get("leaveTime").toString();
DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date loginInTimeDate=new Date();
Date leaveTimeDate=new Date();
try {
loginInTimeDate = simpleDateFormat.parse(loginInTime);
leaveTimeDate = simpleDateFormat.parse(leaveTime);
}catch (ParseException e){
e.printStackTrace();
}
//计算时间间隔(单位为毫秒)
Long timeInterval=leaveTimeDate.getTime()-loginInTimeDate.getTime();
//时间处理
String timeIntervalDate=TimeProcessing.longTimeToDay(timeInterval);
//将数据写入excel文件
List<String> titleList = new ArrayList<String>();
//定义标题
titleList.add("用户ID");
titleList.add("用户姓名");
titleList.add("问题ID");
titleList.add("登录时间");
titleList.add("离开时间");
titleList.add("页面停留时间");
//定义内容
List<String> cellList = new ArrayList<String>();
cellList.add(String.valueOf(userId));
cellList.add(username);
cellList.add(questionId.toString());
cellList.add(loginInTime);
cellList.add(leaveTime);
cellList.add(timeIntervalDate);
contentList.add(cellList);
System.out.println("待下载的excel的列数为:"+contentList.size());
HttpSession session = request.getSession();
ServletContext application = session.getServletContext();
Set onlineUserSet = (Set)application.getAttribute("onlineUserSet");
if(contentList.size()>=100||onlineUserSet.size()==1) {
List<List<String>> contentListcopy = new ArrayList<List<String>>(300);
contentListcopy.addAll(contentList);
contentList.clear();
ExcelExportUtil exportUtil = new ExcelExportUtil();
exportUtil.exportExcel( titleList, contentListcopy);
contentListcopy.clear();
}
}
//util类
public class ExcelExportUtil
{
public ExcelExportUtil()
{
}
public void exportExcel(List titleList, List contentList)
{
String fileName = "myExcel.xlsx";
FileOutputStream out = null;
try
{
//File file = new File((new StringBuilder()).append("/www/backup/").append(fileName).toString());
File file = new File((new StringBuilder()).append("E:/output/").append(fileName).toString());
if (!file.exists())
{
file.createNewFile();
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet mySheet = wb.createSheet("mySheet1");
HSSFRow titleRow = mySheet.createRow(0);
for (int i = 0; i < titleList.size(); i++)
{
HSSFCell titleCell = titleRow.createCell(i);
titleCell.setCellValue((String)titleList.get(i));
}
for (int i = 0; i < contentList.size(); i++)
{
HSSFRow contentRow = mySheet.createRow(i + 1);
List fieldList = (List)contentList.get(i);
for (int j = 0; j < fieldList.size(); j++)
{
HSSFCell contentCell = contentRow.createCell(j);
contentCell.setCellValue((String)fieldList.get(j));
}
}
//out = new FileOutputStream((new StringBuilder()).append("/www/backup/").append(fileName).toString());
out = new FileOutputStream((new StringBuilder()).append("E:/output/").append(fileName).toString());
wb.write(out);
out.flush();
out.close();
wb.close();
} else
{
//FileInputStream fs = new FileInputStream((new StringBuilder()).append("/www/backup/").append(fileName).toString());
FileInputStream fs = new FileInputStream((new StringBuilder()).append("E:/output/").append(fileName).toString());
HSSFWorkbook wb = new HSSFWorkbook(fs);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFRow contentRow = sheet.getRow(0);
System.out.println((new StringBuilder()).append(sheet.getLastRowNum()).append(" ").append(contentRow.getLastCellNum()).toString());
for (int i = 0; i < contentList.size(); i++)
{
contentRow = sheet.createRow((short)(sheet.getLastRowNum() + 1));
List fieldList = (List)contentList.get(i);
for (int j = 0; j < fieldList.size(); j++)
{
HSSFCell contentCell = contentRow.createCell(j);
contentCell.setCellValue((String)fieldList.get(j));
}
}
//out = new FileOutputStream((new StringBuilder()).append("/www/backup/").append(fileName).toString());
out = new FileOutputStream((new StringBuilder()).append("E:/output/").append(fileName).toString());
wb.write(out);
out.flush();
out.close();
wb.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
重点说明:目前系统已经部署到外网,有感兴趣的朋友可以私聊我(qq:2572719054),给予地址.欢迎给予批评和指正。
另外,系统属于学生自己研究,不做商业行为,题目来源为LeetCode,已经获得授权。
网友评论