基于SSM实现前后端分离在线考试管理系统

简介: 基于SSM实现前后端分离在线考试管理系统

项目编号:BS-XX-105

前言:


互联网的普及给人们带来的便利不需多说。因此如果把题库及试卷管理系统与互联网结合起来,利用My Eclipse编程软件建设题库及试卷管理系统,实现题库及试卷管理系统的网络化。则对于进一步提高教学发展定能起到不少的促进作用。


题库及试卷管理系统能够通过互联网得到广泛的、全面的宣传,让尽可能多的学校了解和熟知学校的题库及试卷管理系统服务等,不仅为学校提供了服务,而且也推广了自己,让更多的学生了解自己。对于学校而言,若拥有自己的题库及试卷管理系统,通过题库及试卷管理系统让学校的宣传、营销提上一个新台阶,同时提升了学校形象。


根据本系统的研究现状和发展趋势,系统从需求分析、结构设计、数据库设计,在到系统实现,分别为前端实现和后端实现。论文内容从系统描述、系统分析、系统设计、系统实现、系统测试来阐述系统的开发过程。本系统力求结合实际找出一种切实可行的开发方案,经过反复研究和学习,借助My Eclipse编程软件、SSM技术、MySQL数据库和Tomcat服务器来完成系统的所有功能,最后进行系统测试,来检测系统的权限和漏洞,从而将系统完善,达到符合标准。

一,项目简介


本项目主要实现SSM开发框架实现在线考试管理系统,采用前后端分离的方式开发实现,基于接口实现前后端开发对接,后端服务系统主要实现的管理功能有:试题管理、试卷管理、考试管理、用户管理、数据字典管理、系统设置管理。前端用户服务实现的功能有:试题练习、在线考试、会员中心、统计知识掌握情况、统计分析各种考试数据等功能。整体功能比较强大,也比较完整。


根据前面的各项设计分析,按照系统开发的基本理念对系统进行分解,从模块上主要可分为用户模块和管理员模块。


用户模块只要是让普通用户使用,包括题库及试卷管理,成绩查询等功能,管理员模块只要是让管理员使用,包括学生信息管理、教师信息管理、试卷管理等功能,可以对数据进行添加、删除、修改及查询等操作。


系统总体功能结构图如下图所示。


根据前面的各项设计分析,按照系统开发的基本理念对系统进行分解,从模块上主要可分为用户模块和管理员模块。


用户模块只要是让普通用户使用,包括题库及试卷管理,成绩查询等功能,管理员模块只要是让管理员使用,包括学生信息管理、教师信息管理、试卷管理等功能,可以对数据进行添加、删除、修改及查询等操作。


系统总体功能结构图如下图所示。

image.png

二,环境介绍


语言环境:Java:  jdk1.8

数据库:Mysql: mysql5.7

应用服务器:Tomcat:  tomcat8.5.31

开发工具:IDEA或eclipse

后台开发技术:SSM框架+SpringSecurity安全框架

前台开发技术:Bootstrap+Ajax

三,系统展示


后台管理界面:

管理首页

image.png

试题管理

image.png

试题添加

image.png

试卷管理

image.png

创建试卷

image.png

考试管理

image.png

用户管理

image.png

通用数据管理

image.png

系统设置

image.png

教师登陆系统

image.png

试卷管理

image.png

前端用户服务系统

image.png

试题练习

image.png

答题模式

image.png

背题模式

image.png

在线考试

image.png

在线考试

image.png

会员中心

image.png

个人设置

image.png

四,核心代码展示


package com.examstack.management.controller.action;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.examstack.common.domain.exam.ExamHistory;
import com.examstack.common.domain.exam.ExamPaper;
import com.examstack.common.domain.question.PointStatistic;
import com.examstack.common.domain.question.Question;
import com.examstack.common.domain.question.QuestionFilter;
import com.examstack.common.domain.user.User;
import com.examstack.common.util.Page;
import com.examstack.management.security.UserInfo;
import com.examstack.management.service.ExamPaperService;
import com.examstack.management.service.ExamService;
import com.examstack.management.service.QuestionService;
import com.examstack.management.service.UserService;
@Controller
public class DashBoardAction {
  @Autowired
  private QuestionService questionService;
  @Autowired
  private UserService userService;
  @Autowired
  private ExamPaperService examPaperService;
  @Autowired
  private ExamService examService;
  @RequestMapping(value = "/secure/dashboard/baseinfo", method = RequestMethod.GET)
  public @ResponseBody List<Integer> baseInfo(Model model) {
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext()
          .getAuthentication()
          .getPrincipal();
    Page<User> pageUser = new Page<User>();
    pageUser.setPageNo(1);
    pageUser.setPageSize(1);
    userService.getUserListByRoleId(userInfo.getRoleMap().get("ROLE_STUDENT").getRoleId(), pageUser);
    Page<Question> pageQuestion = new Page<Question>();
    pageQuestion.setPageNo(1);
    pageQuestion.setPageSize(1);
    QuestionFilter qf = new QuestionFilter();
    qf.setFieldId(0);
    qf.setKnowledge(0);
    qf.setQuestionType(0);
    qf.setTag(0);
    qf.setSearchParam("-1");
    questionService.getQuestionList(pageQuestion, qf);
    Page<ExamPaper> pageExamPaper = new Page<ExamPaper>();
    pageExamPaper.setPageNo(1);
    pageExamPaper.setPageSize(1);
    examPaperService.getEnabledExamPaperList(userInfo.getUsername(), pageExamPaper);
    List<Integer> l = new ArrayList<Integer>();
    l.add(pageQuestion.getTotalRecord());
    l.add(pageExamPaper.getTotalRecord());
    l.add(pageUser.getTotalRecord());
    return l;
  }
  @RequestMapping(value = "/secure/dashboard/studentApprovedList", method = RequestMethod.GET)
  public @ResponseBody List<ExamHistory> studentApprovedList(Model model) {
    Page<ExamHistory> page = new Page<ExamHistory>();
    page.setPageNo(1);
    page.setPageSize(4);
    List<ExamHistory> histList = examService.getUserExamHistList(page, 0);
    return histList;
  }
  @RequestMapping(value = "/secure/dashboard/StudentMarkList", method = RequestMethod.GET)
  public @ResponseBody List<ExamHistory> studentMarkList(Model model) {
    Page<ExamHistory> page = new Page<ExamHistory>();
    page.setPageNo(1);
    page.setPageSize(4);
    List<ExamHistory> histList = examService.getUserExamHistList(page, 2);
    return histList;
  }
  @RequestMapping(value = "/secure/dashboard/chartinfo/{fieldId}", method = RequestMethod.GET)
  public @ResponseBody List<FieldNumber> chartInfo(Model model,@PathVariable("fieldId") int fieldId) {
    List<PointStatistic> pointStatisticList = questionService.getPointCount(fieldId, null);
    List<FieldNumber> l = new ArrayList<FieldNumber>();
    for(PointStatistic ps : pointStatisticList){
      FieldNumber fieldNumber = new FieldNumber();
      fieldNumber.name = ps.getPointName();
      fieldNumber.amount = ps.getAmount();
      l.add(fieldNumber);
    }
    return l;
  }
  class FieldNumber{
    private String name;
    private int amount;
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getAmount() {
      return amount;
    }
    public void setAmount(int amount) {
      this.amount = amount;
    }
  }
}
package com.examstack.management.controller.action;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.examstack.common.domain.exam.AnswerSheet;
import com.examstack.common.domain.exam.AnswerSheetItem;
import com.examstack.common.domain.exam.ExamPaper;
import com.examstack.common.domain.exam.Message;
import com.examstack.management.service.ExamPaperService;
import com.examstack.management.service.ExamService;
import com.google.gson.Gson;
@Controller
public class ExamAction {
  @Autowired
  private ExamPaperService examPaperService;
  @Autowired
  private ExamService examService;
  @RequestMapping(value = "/api/exampaper/{id}", method = RequestMethod.GET)
  public @ResponseBody ExamPaper getExamPaper(@PathVariable("id") int id){
    ExamPaper paper = examPaperService.getExamPaperById(id);
    return paper;
  }
  @RequestMapping(value = "/api/answersheet", method = RequestMethod.POST)
  public @ResponseBody Message submitAnswerSheet(@RequestBody AnswerSheet answerSheet){
    List<AnswerSheetItem> itemList = answerSheet.getAnswerSheetItems();
    //全部是客观题,则状态更改为已阅卷
    int approved = 3;
    for(AnswerSheetItem item : itemList){
      if(item.getQuestionTypeId() != 1 && item.getQuestionTypeId() != 2 && item.getQuestionTypeId() != 3){
        approved = 2;
        break;
      }
    }
    Gson gson = new Gson();   
    examService.updateUserExamHist(answerSheet, gson.toJson(answerSheet),approved);
    return new Message();
  }
}
package com.examstack.management.controller.action;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.examstack.common.domain.exam.Message;
import com.examstack.common.domain.question.KnowledgePoint;
import com.examstack.common.domain.question.Question;
import com.examstack.common.domain.question.QuestionTag;
import com.examstack.common.util.file.FileUploadUtil;
import com.examstack.management.security.UserInfo;
import com.examstack.management.service.QuestionService;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
@Controller
public class QuestionAction {
  @Autowired
  private QuestionService questionService;
  /**
   * 添加试题
   * 
   * @param question
   * @return
   */
  @RequestMapping(value = "/secure/question/question-add", method = RequestMethod.POST)
  public @ResponseBody Message addQuestion(@RequestBody Question question) {
    UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    Message message = new Message();
    Gson gson = new Gson();
    question.setContent(gson.toJson(question.getQuestionContent()));
    question.setCreate_time(new Date());
    question.setCreator(userDetails.getUsername());
    try {
      questionService.addQuestion(question);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      message.setResult("error");
      message.setMessageInfo(e.getClass().getName());
      e.printStackTrace();
    }
    return message;
  }
  /**
   * 获取试题的标签列表
   * 
   * @param questionId
   * @return
   */
  @RequestMapping(value = "/secure/question/question-tag/{questionId}", method = RequestMethod.GET)
  public @ResponseBody Message getQuestionTag(@PathVariable("questionId") int questionId) {
    Message message = new Message();
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    List<QuestionTag> tagList = questionService.getQuestionTagByQuestionIdAndUserId(questionId,
        userInfo.getUserid(), null);
    message.setObject(tagList);
    return message;
  }
  /**
   * 为试题添加标签
   * 
   * @param questionId
   * @param questionTagList
   * @return
   */
  @RequestMapping(value = "/secure/question/add-question-tag", method = RequestMethod.POST)
  public @ResponseBody Message addQuestionTag(@RequestBody int questionId,
      @RequestBody List<QuestionTag> questionTagList) {
    Message message = new Message();
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    try {
      questionService.addQuestionTag(questionId, userInfo.getUserid(), questionTagList);
    } catch (Exception e) {
      e.printStackTrace();
      message.setResult(e.getClass().getName());
    }
    return message;
  }
  /**
   * 获取试题详细信息
   * @param questionId
   * @return
   */
  @RequestMapping(value = "/secure/question/question-detail/{questionId}", method = RequestMethod.GET)
  public @ResponseBody Message getQuestionDetail(@PathVariable("questionId") int questionId) {
    Message message = new Message();
    //UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    try {
      Question question = questionService.getQuestionDetail(questionId, 0);
      message.setObject(question);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      message.setResult(e.getCause().getMessage());
    }
    return message;
  }
  /**
   * 修改试题知识点
   * 
   * @param questionId
   * @param pointId
   * @param questionTagList
   * @return
   */
  @RequestMapping(value = "/secure/question/question-update/{questionId}/{pointId}", method = RequestMethod.POST)
  public @ResponseBody Message updateQuestionKnowledge(@PathVariable int questionId, @PathVariable int pointId,
      @RequestBody List<QuestionTag> questionTagList) {
    Message message = new Message();
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    Question question = new Question();
    question.setId(questionId);
    List<Integer> pointIdList = new ArrayList<Integer>();
    pointIdList.add(pointId);
    question.setPointList(pointIdList);
    try {
      questionService.updateQuestionPoint(question, userInfo.getUserid(), questionTagList);
    } catch (Exception e) {
      message.setResult(e.getClass().getName());
    }
    return message;
  }
  @RequestMapping(value = "/secure/question/question-update", method = RequestMethod.POST)
  public @ResponseBody Message updateQuestion(@RequestBody String jsonStr){
    Message msg = new Message();
    Gson gson = new Gson();
    JsonParser parser = new JsonParser();
    JsonElement element = parser.parse(jsonStr);
    List<QuestionTag> questionTagList = gson.fromJson(element.getAsJsonObject().get("tags"), new TypeToken<ArrayList<QuestionTag>>(){}.getType());
    Question question = gson.fromJson(element.getAsJsonObject().get("question"), Question.class);
    try {
      questionService.updateQuestion(question, questionTagList);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      msg.setResult(e.getCause().getMessage());
    }
    //TO-DO:需要提交到数据库,保证在事务中提交
    return msg;
  }
  @RequestMapping(value = "/secure/question/get-knowledge-point/{fieldId}", method = RequestMethod.GET)
  public @ResponseBody Message getQuestionPointByFieldId(@PathVariable int fieldId) {
    Message message = new Message();
    HashMap<Integer, String> pointMap = new HashMap<Integer, String>();
    List<KnowledgePoint> pointList = questionService.getKnowledgePointByFieldId(fieldId, null);
    for (KnowledgePoint point : pointList) {
      pointMap.put(point.getPointId(), point.getPointName());
    }
    message.setObject(pointMap);
    return message;
  }
  @RequestMapping(value = "/secure/question/delete-question/{questionId}", method = RequestMethod.GET)
  public @ResponseBody Message deleteQuestion(Model model, @PathVariable("questionId") int questionId) {
    // UserDetails userDetails = (UserDetails)
    // SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    Message message = new Message();
    try {
      questionService.deleteQuestionByQuestionId(questionId);
    } catch (Exception ex) {
      message.setResult(ex.getClass().getName());
    }
    return message;
  }
  @RequestMapping(value = "/secure/upload-uploadify-img", method = RequestMethod.POST)
  public @ResponseBody String uploadImg(HttpServletRequest request, HttpServletResponse response) {
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    List<String> filePathList = new ArrayList<String>();
    try {
      filePathList = FileUploadUtil.uploadImg(request, response, userInfo.getUsername());
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    if (filePathList.size() == 0) {
      return "系统错误";
    }
    return filePathList.get(0);
  }
  @RequestMapping(value = "/secure/upload-uploadify", method = RequestMethod.POST)
  public @ResponseBody String uploadFile(HttpServletRequest request, HttpServletResponse response) {
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    List<String> filePathList = new ArrayList<String>();
    try {
      filePathList = FileUploadUtil.uploadFile(request, response, userInfo.getUsername());
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    if (filePathList.size() == 0) {
      return "系统错误";
    }
    return filePathList.get(0);
  }
  @RequestMapping(value = "/secure/question-import/{id}", method = RequestMethod.POST)
  public @ResponseBody Message courseImport(@RequestBody String filePath, @PathVariable("id") int id) {
    Message message = new Message();
    UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext()
        .getAuthentication().getPrincipal();
    if(id == 0){
      message.setResult("error");
      message.setMessageInfo("请选择题库");
      return message;
    }
    try{
      questionService.uploadQuestions(filePath, userInfo.getUsername(),id);
    }catch(RuntimeException e){
      message.setResult(e.getClass().getName() + ":" + e.getMessage());
      message.setMessageInfo(e.getMessage());
    }
    return message;
  }
}

五,项目总结


相关文章
|
7月前
|
存储 Java 关系型数据库
ssm026校园美食交流系统(文档+源码)_kaic
本文介绍了基于Java语言和MySQL数据库的校园美食交流系统的设计与实现。该系统采用B/S架构和SSM框架,旨在提高校园美食信息管理的效率与便捷性。主要内容包括:系统的开发背景、目的及内容;对Java技术、MySQL数据库、B/S结构和SSM框架的介绍;系统分析部分涵盖可行性分析、性能分析和功能需求分析;最后详细描述了系统各功能模块的具体实现,如登录、管理员功能(美食分类管理、用户管理等)和前台首页功能。通过此系统,管理员可以高效管理美食信息,用户也能方便地获取和分享美食资讯,从而提升校园美食交流的管理水平和用户体验。
|
6月前
|
Java 关系型数据库 MySQL
weixin050高校体育场管理系统+ssm(文档+源码)_kaic
本文针对高校体育场管理系统的开发与实现进行详细介绍。随着经济快速发展,人们对手机软件需求增加,高校体育场管理系统应运而生。系统采用JAVA技术、Mysql数据库和SSM框架等成熟技术,通过分析功能需求、可行性及性能,设计出包含管理员、用户和学生角色的功能模块。系统实现用户注册登录、信息管理等功能,简化传统手工统计模式,提高管理效率,满足用户对信息获取的及时性与准确性需求。
weixin050高校体育场管理系统+ssm(文档+源码)_kaic
|
6月前
|
前端开发 Java 关系型数据库
基于ssm的社区物业管理系统,附源码+数据库+论文+任务书
社区物业管理系统采用B/S架构,基于Java语言开发,使用MySQL数据库。系统涵盖个人中心、用户管理、楼盘管理、收费管理、停车登记、报修与投诉管理等功能模块,方便管理员及用户操作。前端采用Vue、HTML、JavaScript等技术,后端使用SSM框架。系统支持远程安装调试,确保顺利运行。提供演示视频和详细文档截图,帮助用户快速上手。
219 17
|
6月前
|
前端开发 Java 关系型数据库
基于ssm的超市会员(积分)管理系统,附源码+数据库+论文,包安装调试
本项目为简单内容浏览和信息处理系统,具备管理员和员工权限。管理员可管理会员、员工、商品及积分记录,员工则负责积分、商品信息和兑换管理。技术框架采用Java编程语言,B/S架构,前端使用Vue+JSP+JavaScript+Css+LayUI,后端为SSM框架,数据库为MySQL。运行环境为Windows,JDK8+Tomcat8.5,非前后端分离的Maven项目。提供演示视频和详细文档,购买后支持免费远程安装调试。
281 19
|
6月前
|
前端开发 JavaScript Java
[Java计算机毕设]基于ssm的OA办公管理系统的设计与实现,附源码+数据库+论文+开题,包安装调试
OA办公管理系统是一款基于Java和SSM框架开发的B/S架构应用,适用于Windows系统。项目包含管理员、项目管理人员和普通用户三种角色,分别负责系统管理、请假审批、图书借阅等日常办公事务。系统使用Vue、HTML、JavaScript、CSS和LayUI构建前端,后端采用SSM框架,数据库为MySQL,共24张表。提供完整演示视频和详细文档截图,支持远程安装调试,确保顺利运行。
244 17
|
6月前
|
前端开发 Java 关系型数据库
基于ssm的网络直播带货管理系统,附源码+数据库+论文
该项目为网络直播带货网站,包含管理员和用户两个角色。管理员可进行主页、个人中心、用户管理、商品分类与信息管理、系统及订单管理;用户可浏览主页、管理个人中心、收藏和订单。系统基于Java开发,采用B/S架构,前端使用Vue、JSP等技术,后端为SSM框架,数据库为MySQL。项目运行环境为Windows,支持JDK8、Tomcat8.5。提供演示视频和详细文档截图。
153 10
|
6月前
|
前端开发 Java 关系型数据库
基于ssm的台球厅管理系统,附源码+数据库+论文
本项目为新锐台球厅管理系统,支持管理员和会员两种角色。管理员可进行会员管理、台球桌管理、订单管理等;会员可查看台球桌、预约、购买商品等。技术框架基于Java,采用B/S架构,前端使用Vue+HTML+JavaScript+CSS+LayUI,后端使用SSM框架,数据库为MySQL。运行环境为Windows,JDK8+MySQL5.7+Tomcat8.5。提供演示视频及详细文档截图。
|
7月前
|
存储 Java 关系型数据库
ssm064农产品仓库管理系统系统(文档+源码)_kaic
农产品仓库管理系统基于现代经济快速发展和信息化技术的升级,采用SSM框架、Java语言及Mysql数据库开发。系统旨在帮助管理者高效处理大量数据信息,提升事务处理效率,实现数据管理的科学化与规范化。该系统涵盖物资基础数据管理、出入库订单管理等功能,界面简洁美观,符合用户操作习惯,并提供数据安全解决方案,确保信息的安全性和可靠性。通过自动化和集中处理,系统显著提高了仓库管理的效率和准确性。
|
12月前
|
Java 应用服务中间件 数据库连接
ssm项目整合,简单的用户管理系统
文章介绍了一个使用SSM框架(Spring、SpringMVC、MyBatis)构建的简单用户管理系统的整合过程,包括项目搭建、数据库配置、各层代码实现以及视图展示。
ssm项目整合,简单的用户管理系统
|
12月前
|
XML Java 数据库连接
如何搭建SSM框架、图书商城系统
这是一份详尽的《Spring + SpringMVC + Mybatis 整合指南》,作者耗时良久整理出约五万字的内容,现已经全部笔记公开。此文档详细地介绍了如何搭建与整合SSM框架,具体步骤包括创建Maven项目、添加web骨架、配置pom文件以及整合Spring、SpringMVC和Mybatis等。无论是对初学者还是有一定基础的开发者来说,都是很好的学习资源。此外,作者还提供了项目源码的GitHub链接,方便读者实践。虽然当前主流推荐学习SpringBoot,但了解SSM框架仍然是不可或缺的基础。
144 0

热门文章

最新文章