/* Verwandlung Online Judge - A cross-platform judge online system * Copyright (C) 2018 Haozhe Xie <[email protected]> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * _ooOoo_ * o8888888o * 88" . "88 * (| -_- |) * O\ = /O * ____/`---'\____ * .' \\| |// `. * / \\||| : |||// \ * / _||||| -:- |||||- \ * | | \\\ - /// | | * | \_| ''\---/'' | | * \ .-\__ `-` ___/-. / * ___`. .' /--.--\ `. . __ * ."" '< `.___\_<|>_/___.' >'"". * | | : `- \`.;`\ _ /`;.`/ - ` : | | * \ \ `-. \_ __\ /__ _/ .-` / / * ======`-.____`-.___\_____/___.-`____.-'====== * `=---=' * * HERE BE BUDDHA * */ package org.verwandlung.voj.web.controller; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; 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.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON; import org.verwandlung.voj.web.exception.ResourceNotFoundException; import org.verwandlung.voj.web.messenger.ApplicationEventListener; import org.verwandlung.voj.web.model.Checkpoint; import org.verwandlung.voj.web.model.Language; import org.verwandlung.voj.web.model.Option; import org.verwandlung.voj.web.model.Problem; import org.verwandlung.voj.web.model.ProblemCategory; import org.verwandlung.voj.web.model.ProblemTag; import org.verwandlung.voj.web.model.Submission; import org.verwandlung.voj.web.model.User; import org.verwandlung.voj.web.model.UserGroup; import org.verwandlung.voj.web.service.LanguageService; import org.verwandlung.voj.web.service.OptionService; import org.verwandlung.voj.web.service.ProblemService; import org.verwandlung.voj.web.service.SubmissionService; import org.verwandlung.voj.web.service.UserService; import org.verwandlung.voj.web.util.CsrfProtector; import org.verwandlung.voj.web.util.DateUtils; import org.verwandlung.voj.web.util.HttpRequestParser; import org.verwandlung.voj.web.util.SessionListener; /** * 用于处理系统管理的请求. * * @author Haozhe Xie */ @Controller @RequestMapping(value="/administration") public class AdministrationController { /** * 加载系统管理首页. * @param request - HttpRequest对象 * @param response - HttpResponse对象 * @return 包含系统管理页面信息的ModelAndView对象 */ @RequestMapping(value="", method=RequestMethod.GET) public ModelAndView indexView( HttpServletRequest request, HttpServletResponse response) { ModelAndView view = new ModelAndView("administration/index"); view.addObject("totalUsers", getTotalUsers()); view.addObject("newUsersToday", getNumberOfUserRegisteredToday()); view.addObject("onlineUsers", getOnlineUsers()); view.addObject("totalProblems", getTotalProblems()); view.addObject("numberOfCheckpoints", getNumberOfCheckpoints()); view.addObject("privateProblems", getPrivateProblems()); view.addObject("submissionsToday", getSubmissionsToday()); view.addObject("memoryUsage", getCurrentMemoryUsage()); view.addObject("onlineJudgers", getOnlineJudgers()); return view; } /** * 获取系统中注册用户的总数. * @return 系统中注册用户的总数 */ private long getTotalUsers() { UserGroup userGroup = userService.getUserGroupUsingSlug("users"); return userService.getNumberOfUsers(userGroup); } /** * 获取今日注册的用户数量. * @return 今日注册的用户数量 */ public long getNumberOfUserRegisteredToday() { return userService.getNumberOfUserRegisteredToday(); } /** * 获取在线用户的数量. * @return 在线用户的数量 */ private long getOnlineUsers() { return SessionListener.getTotalSessions(); } /** * 获取全部试题的总数量. * @return 全部试题的总数量 */ private long getTotalProblems() { return problemService.getNumberOfProblems(); } /** * 获取私有试题的数量. * @return 私有试题的数量 */ private long getPrivateProblems() { long numberOfTotalProblems = getTotalProblems(); long numberOfPublicProblems = problemService.getNumberOfProblemsUsingFilters(null, "", true); return numberOfTotalProblems - numberOfPublicProblems; } /** * 获取全部试题测试点的数量(包括私有试题). * @return 全部试题测试点的数量 */ private long getNumberOfCheckpoints() { return problemService.getNumberOfCheckpoints(); } /** * 获取今日的提交数量. * @return 今日的提交数量 */ private long getSubmissionsToday() { Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int date = calendar.get(Calendar.DAY_OF_MONTH); calendar.set(year, month, date, 0, 0, 0); Date startTime = calendar.getTime(); calendar.set(year, month, date + 1, 0, 0, 0); Date endTime = calendar.getTime(); return submissionService.getNumberOfSubmissionsUsingDate(startTime, endTime); } /** * 获取Web应用当前内存占用情况. * @return Web应用当前内存占用(MB) */ private long getCurrentMemoryUsage() { long totalMemory = Runtime.getRuntime().totalMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); return (totalMemory - freeMemory) / 1048576; } /** * 获取在线的评测机数量. * 通过获取监听消息队列的Consumer数量. * @return 在线的评测机数量 */ private long getOnlineJudgers() { return eventListener.getOnlineJudgers(); } /** * 获取系统一段时间内的提交次数. * @param period - 时间间隔的天数 * @param request - HttpServletRequest对象 * @return 包含提交次数与时间的 Map 对象 */ @RequestMapping(value="/getNumberOfSubmissions.action", method=RequestMethod.GET) public @ResponseBody Map<String, Object> getNumberOfSubmissionsAction( @RequestParam(value="period") int period, HttpServletRequest request) { Map<String, Object> submissions = new HashMap<>(2, 1); Date today = new Date(); Date previousDate = DateUtils.getPreviousDate(period); Map<String, Long> totalSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate, today, 0, false); Map<String, Long> acceptedSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate, today, 0, true); submissions.put("totalSubmissions", totalSubmissions); submissions.put("acceptedSubmissions", acceptedSubmissions); return submissions; } /** * 加载用户列表页面. * @param request - HttpRequest对象 * @param response - HttpResponse对象 * @return 包含用户列表页面信息的ModelAndView对象 */ @RequestMapping(value="/all-users", method=RequestMethod.GET) public ModelAndView allUsersView( @RequestParam(value="userGroup", required=false, defaultValue="") String userGroupSlug, @RequestParam(value="username", required=false, defaultValue="") String username, @RequestParam(value="page", required=false, defaultValue="1") long pageNumber, HttpServletRequest request, HttpServletResponse response) { final int NUMBER_OF_USERS_PER_PAGE = 100; List<UserGroup> userGroups = userService.getUserGroups(); UserGroup userGroup = userService.getUserGroupUsingSlug(userGroupSlug); long totalUsers = userService.getNumberOfUsersUsingUserGroupAndUsername(userGroup, username); long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_USERS_PER_PAGE; List<User> users = userService.getUserUsingUserGroupAndUsername(userGroup, username, offset, NUMBER_OF_USERS_PER_PAGE); ModelAndView view = new ModelAndView("administration/all-users"); view.addObject("userGroups", userGroups); view.addObject("selectedUserGroup", userGroupSlug); view.addObject("username", username); view.addObject("currentPage", pageNumber); view.addObject("totalPages", (long) Math.ceil(totalUsers * 1.0 / NUMBER_OF_USERS_PER_PAGE)); view.addObject("users", users); return view; } /** * 删除选定的用户. * @param users - 用户ID的集合, 以逗号(, )分隔 * @param request - HttpServletRequest对象 * @return 提交记录的删除结果 */ @RequestMapping(value="/deleteUsers.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> deleteUsersAction( @RequestParam(value="users") String users, HttpServletRequest request) { Map<String, Boolean> result = new HashMap<>(2, 1); List<Long> userList = JSON.parseArray(users, Long.class); for ( Long userId : userList ) { userService.deleteUser(userId); } result.put("isSuccessful", true); return result; } /** * 加载编辑用户信息的页面. * @param userId - 用户的唯一标识符 * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含编辑用户信息的ModelAndView对象 */ @RequestMapping(value="/edit-user/{userId}", method=RequestMethod.GET) public ModelAndView editUserView( @PathVariable(value = "userId") long userId, HttpServletRequest request, HttpServletResponse response) { User user = userService.getUserUsingUid(userId); Map<String, Object> userMeta = userService.getUserMetaUsingUid(user); if ( user == null ) { throw new ResourceNotFoundException(); } List<UserGroup> userGroups = userService.getUserGroups(); List<Language> languages = languageService.getAllLanguages(); ModelAndView view = new ModelAndView("administration/edit-user"); view.addObject("user", user); view.addAllObjects(userMeta); view.addObject("userGroups", userGroups); view.addObject("languages", languages); return view; } /** * 编辑用户个人信息. * @param uid - 用户的唯一标识符. * @param password - 用户的密码(未经MD5加密) * @param email - 用户的电子邮件地址 * @param userGroupSlug - 用户组的别名 * @param preferLanguageSlug - 用户的偏好语言的别名 * @param location - 用户的所在地区 * @param website - 用户的个人主页 * @param socialLinks - 用户的社交网络信息 * @param aboutMe - 用户的个人简介 * @param request - HttpServletRequest对象 * @return 一个包含个人资料修改结果的Map<String, Boolean>对象 */ @RequestMapping(value="/editUser.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> editUserAction( @RequestParam(value="uid") long uid, @RequestParam(value="password") String password, @RequestParam(value="email") String email, @RequestParam(value="userGroup") String userGroupSlug, @RequestParam(value="preferLanguage") String preferLanguageSlug, @RequestParam(value="location") String location, @RequestParam(value="website") String website, @RequestParam(value="socialLinks") String socialLinks, @RequestParam(value="aboutMe") String aboutMe, HttpServletRequest request) { User user = userService.getUserUsingUid(uid); Map<String, Boolean> result = new HashMap<>(12, 1); result.put("isSuccessful", false); result.put("isUserExists", false); if ( user != null ) { Map<String, Boolean> updateProfileResult = userService.updateProfile(user, password, userGroupSlug, preferLanguageSlug); Map<String, Boolean> updateUserMetaResult = userService.updateProfile(user, email, location, website, socialLinks, aboutMe); boolean isUpdateProfileSuccessful = updateProfileResult.get("isSuccessful"); boolean isUpdateUserMetaSuccessful = updateUserMetaResult.get("isSuccessful"); result.putAll(updateProfileResult); result.putAll(updateUserMetaResult); result.put("isUserExists", true); result.put("isSuccessful", isUpdateProfileSuccessful && isUpdateUserMetaSuccessful); } return result; } /** * 加载创建用户页面. * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含创建用户页面信息的ModelAndView对象 */ @RequestMapping(value="/new-user", method=RequestMethod.GET) public ModelAndView newUserView( HttpServletRequest request, HttpServletResponse response) { List<UserGroup> userGroups = userService.getUserGroups(); List<Language> languages = languageService.getAllLanguages(); ModelAndView view = new ModelAndView("administration/new-user"); view.addObject("userGroups", userGroups); view.addObject("languages", languages); return view; } /** * 创建新用户. * @param username - 用户名 * @param password - 密码 * @param email - 电子邮件地址 * @param userGroupSlug - 用户组的别名 * @param preferLanguageSlug - 偏好语言的别名 * @param request - HttpServletRequest对象 * @return 一个包含账户创建结果的Map<String, Boolean>对象 */ @RequestMapping(value="/newUser.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> newUserAction( @RequestParam(value="username") String username, @RequestParam(value="password") String password, @RequestParam(value="email") String email, @RequestParam(value="userGroup") String userGroupSlug, @RequestParam(value="preferLanguage") String preferLanguageSlug, HttpServletRequest request) { Map<String, Boolean> result = userService.createUser(username, password, email, userGroupSlug, preferLanguageSlug); if ( result.get("isSuccessful") ) { String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("User: [Username=%s] was created by administrator at %s.", new Object[] {username, ipAddress})); } return result; } /** * 加载试题列表页面. * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含提交列表页面信息的ModelAndView对象 */ @RequestMapping(value="/all-problems", method=RequestMethod.GET) public ModelAndView allProblemsView( @RequestParam(value="keyword", required=false, defaultValue="") String keyword, @RequestParam(value="problemCategory", required=false, defaultValue="") String problemCategorySlug, @RequestParam(value="problemTag", required=false, defaultValue="") String problemTagSlug, @RequestParam(value="page", required=false, defaultValue="1") long pageNumber, HttpServletRequest request, HttpServletResponse response) { final int NUMBER_OF_PROBLEMS_PER_PAGE = 100; List<ProblemCategory> problemCategories = problemService.getProblemCategories(); long totalProblems = problemService.getNumberOfProblemsUsingFilters(keyword, problemCategorySlug, false); long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_PROBLEMS_PER_PAGE; long problemIdLowerBound = problemService.getFirstIndexOfProblems() + offset; long problemIdUpperBound = problemIdLowerBound + NUMBER_OF_PROBLEMS_PER_PAGE - 1; List<Problem> problems = problemService.getProblemsUsingFilters(problemIdLowerBound, keyword, problemCategorySlug, problemTagSlug, false, NUMBER_OF_PROBLEMS_PER_PAGE); Map<Long, List<ProblemCategory>> problemCategoryRelationships = problemService.getProblemCategoriesOfProblems(problemIdLowerBound, problemIdUpperBound); Map<Long, List<ProblemTag>> problemTagRelationships = problemService.getProblemTagsOfProblems(problemIdLowerBound, problemIdUpperBound); ModelAndView view = new ModelAndView("administration/all-problems"); view.addObject("problemCategories", problemCategories); view.addObject("selectedProblemCategory", problemCategorySlug); view.addObject("keyword", keyword); view.addObject("currentPage", pageNumber); view.addObject("totalPages", (long) Math.ceil(totalProblems * 1.0 / NUMBER_OF_PROBLEMS_PER_PAGE)); view.addObject("problems", problems); view.addObject("problemCategoryRelationships", problemCategoryRelationships); view.addObject("problemTagRelationships", problemTagRelationships); return view; } /** * 删除选定的试题. * @param problems - 试题ID的集合, 以逗号(, )分隔 * @param request - HttpServletRequest对象 * @return 试题的删除结果 */ @RequestMapping(value="/deleteProblems.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> deleteProblemsAction( @RequestParam(value="problems") String problems, HttpServletRequest request) { Map<String, Boolean> result = new HashMap<>(2, 1); List<Long> problemList = JSON.parseArray(problems, Long.class); for ( Long problemId : problemList ) { problemService.deleteProblem(problemId); String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("Problem: [ProblemId=%s] was deleted by administrator at %s.", new Object[] {problemId, ipAddress})); } result.put("isSuccessful", true); return result; } /** * 加载创建试题页面. * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含创建试题页面信息的ModelAndView对象 */ @RequestMapping(value="/new-problem", method=RequestMethod.GET) public ModelAndView newProblemView( HttpServletRequest request, HttpServletResponse response) { Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService.getProblemCategoriesWithHierarchy(); ModelAndView view = new ModelAndView("administration/new-problem"); view.addObject("problemCategories", problemCategories); return view; } /** * 处理用户创建试题的请求. * @param problemName - 试题名称 * @param timeLimit - 时间限制 * @param memoryLimit - 内存占用限制 * @param description - 试题描述 * @param hint - 试题提示 * @param inputFormat - 输入格式 * @param outputFormat - 输出格式 * @param inputSample - 输入样例 * @param outputSample - 输出样例 * @param testCases - 测试用例(JSON 格式) * @param problemCategories - 试题分类(JSON 格式) * @param problemTags - 试题标签((JSON 格式) * @param isPublic - 试题是否公开 * @param isExactlyMatch - 测试点是否精确匹配 * @param request - HttpServletRequest对象 * @return 包含试题创建结果的 Map<String, Boolean>对象 */ @RequestMapping(value="/createProblem.action", method=RequestMethod.POST) public @ResponseBody Map<String, Object> createProblemAction( @RequestParam(value="problemName") String problemName, @RequestParam(value="timeLimit") String timeLimit, @RequestParam(value="memoryLimit") String memoryLimit, @RequestParam(value="description") String description, @RequestParam(value="hint") String hint, @RequestParam(value="inputFormat") String inputFormat, @RequestParam(value="outputFormat") String outputFormat, @RequestParam(value="inputSample") String inputSample, @RequestParam(value="outputSample") String outputSample, @RequestParam(value="testCases") String testCases, @RequestParam(value="problemCategories") String problemCategories, @RequestParam(value="problemTags") String problemTags, @RequestParam(value="isPublic") boolean isPublic, @RequestParam(value="isExactlyMatch") boolean isExactlyMatch, HttpServletRequest request) { if ( timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit) ) { timeLimit = "-1"; } if ( memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit) ) { memoryLimit = "-1"; } Map<String, Object> result = problemService.createProblem(problemName, Integer.parseInt(timeLimit), Integer.parseInt(memoryLimit), description, hint, inputFormat, outputFormat, inputSample, outputSample, testCases, problemCategories, problemTags, isPublic, isExactlyMatch); if ( (boolean) result.get("isSuccessful") ) { long problemId = (Long) result.get("problemId"); String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("Problem: [ProblemId=%s] was created by administrator at %s.", new Object[] {problemId, ipAddress})); } return result; } /** * 加载编辑试题页面. * @param problemId - 试题的唯一标识符 * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含提交列表页面信息的ModelAndView对象 */ @RequestMapping(value="/edit-problem/{problemId}", method=RequestMethod.GET) public ModelAndView editProblemsView( @PathVariable(value = "problemId") long problemId, HttpServletRequest request, HttpServletResponse response) { Problem problem = problemService.getProblem(problemId); if ( problem == null ) { throw new ResourceNotFoundException(); } List<Checkpoint> checkpoints = problemService.getCheckpointsUsingProblemId(problemId); List<ProblemCategory> selectedProblemCategories = problemService.getProblemCategoriesUsingProblemId(problemId); Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService.getProblemCategoriesWithHierarchy(); List<ProblemTag> problemTags = problemService.getProblemTagsUsingProblemId(problemId); ModelAndView view = new ModelAndView("administration/edit-problem"); view.addObject("problem", problem); view.addObject("checkpoints", checkpoints); view.addObject("problemCategories", problemCategories); view.addObject("selectedProblemCategories", selectedProblemCategories); view.addObject("problemTags", problemTags); return view; } /** * 处理用户编辑试题的请求. * @param problemName - 试题名称 * @param timeLimit - 时间限制 * @param memoryLimit - 内存占用限制 * @param description - 试题描述 * @param hint - 试题提示 * @param inputFormat - 输入格式 * @param outputFormat - 输出格式 * @param inputSample - 输入样例 * @param outputSample - 输出样例 * @param testCases - 测试用例(JSON 格式) * @param problemCategories - 试题分类(JSON 格式) * @param problemTags - 试题标签((JSON 格式) * @param isPublic - 试题是否公开 * @param isExactlyMatch - 测试点是否精确匹配 * @param request - HttpServletRequest对象 * @return 包含试题编辑结果的 Map<String, Boolean>对象 */ @RequestMapping(value="/editProblem.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> editProblemAction( @RequestParam(value="problemId") long problemId, @RequestParam(value="problemName") String problemName, @RequestParam(value="timeLimit") String timeLimit, @RequestParam(value="memoryLimit") String memoryLimit, @RequestParam(value="description") String description, @RequestParam(value="hint") String hint, @RequestParam(value="inputFormat") String inputFormat, @RequestParam(value="outputFormat") String outputFormat, @RequestParam(value="inputSample") String inputSample, @RequestParam(value="outputSample") String outputSample, @RequestParam(value="testCases") String testCases, @RequestParam(value="problemCategories") String problemCategories, @RequestParam(value="problemTags") String problemTags, @RequestParam(value="isPublic") boolean isPublic, @RequestParam(value="isExactlyMatch") boolean isExactlyMatch, HttpServletRequest request) { if ( timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit) ) { timeLimit = "-1"; } if ( memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit) ) { memoryLimit = "-1"; } Map<String, Boolean> result = problemService.editProblem(problemId, problemName, Integer.parseInt(timeLimit), Integer.parseInt(memoryLimit), description, hint, inputFormat, outputFormat, inputSample, outputSample, testCases, problemCategories, problemTags, isPublic, isExactlyMatch); if ( result.get("isSuccessful") ) { String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("Problem: [ProblemId=%s] was edited by administrator at %s.", new Object[] {problemId, ipAddress})); } return result; } /** * 加载试题分类页面. * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含试题分类页面信息的ModelAndView对象. */ @RequestMapping(value="/problem-categories", method=RequestMethod.GET) public ModelAndView problemCategoriesView( HttpServletRequest request, HttpServletResponse response) { List<ProblemCategory> problemCategories = problemService.getProblemCategories(); ModelAndView view = new ModelAndView("administration/problem-categories"); view.addObject("problemCategories", problemCategories); return view; } /** * 创建试题分类. * @param problemCategorySlug - 试题分类的别名 * @param problemCategoryName - 试题分类的名称 * @param parentProblemCategorySlug - 父级试题分类的别名 * @param request - HttpServletRequest对象 * @return 包含试题分类的创建结果的Map<String, Object>对象 */ @RequestMapping(value="/createProblemCategory.action", method=RequestMethod.POST) public @ResponseBody Map<String, Object> createProblemCategoryAction( @RequestParam(value="problemCategorySlug") String problemCategorySlug, @RequestParam(value="problemCategoryName") String problemCategoryName, @RequestParam(value="parentProblemCategory") String parentProblemCategorySlug, HttpServletRequest request) { Map<String, Object> result = problemService.createProblemCategory( problemCategorySlug, problemCategoryName, parentProblemCategorySlug); if ( (boolean) result.get("isSuccessful") ) { long problemCategoryId = (Long) result.get("problemCategoryId"); String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was created by administrator at %s.", new Object[] {problemCategoryId, ipAddress})); } return result; } /** * 编辑试题分类. * @param problemCategoryId - 试题分类的唯一标识符 * @param problemCategorySlug - 试题分类的别名 * @param problemCategoryName - 试题分类的名称 * @param parentProblemCategorySlug - 父级试题分类的别名 * @param request - HttpServletRequest对象 * @return 包含试题分类的编辑结果的Map<String, Boolean>对象 */ @RequestMapping(value="/editProblemCategory.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> editProblemCategoryAction( @RequestParam(value="problemCategoryId") String problemCategoryId, @RequestParam(value="problemCategorySlug") String problemCategorySlug, @RequestParam(value="problemCategoryName") String problemCategoryName, @RequestParam(value="parentProblemCategory") String parentProblemCategorySlug, HttpServletRequest request) { Map<String, Boolean> result = problemService.editProblemCategory( Integer.parseInt(problemCategoryId), problemCategorySlug, problemCategoryName, parentProblemCategorySlug); if ( result.get("isSuccessful") ) { String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was edited by administrator at %s.", new Object[] {problemCategoryId, ipAddress})); } return result; } /** * 删除试题分类. * @param problemCategories - 试题分类的唯一标识符集合 * @param request - HttpServletRequest对象 * @return 包含试题分类的删除结果的Map<String, Boolean>对象 */ @RequestMapping(value="/deleteProblemCategories.action", method=RequestMethod.POST) public @ResponseBody Map<String, Object> deleteProblemCategoryAction( @RequestParam(value="problemCategories") String problemCategories, HttpServletRequest request) { Map<String, Object> result = new HashMap<>(3, 1); List<Integer> problemCategoryList = JSON.parseArray(problemCategories, Integer.class); List<Integer> deletedProblemCategories = new ArrayList<>(); for ( int problemCategoryId : problemCategoryList ) { if ( problemService.deleteProblemCategory(problemCategoryId) ) { deletedProblemCategories.add(problemCategoryId); } String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was deleted by administrator at %s.", new Object[] {problemCategoryId, ipAddress})); } result.put("isSuccessful", true); result.put("deletedProblemCategories", deletedProblemCategories); return result; } /** * 加载提交列表页面. * @param problemId - 提交对应试题的唯一标识符 * @param username - 提交者的用户名 * @param pageNumber - 当前页面的页码 * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含提交列表页面信息的ModelAndView对象 */ @RequestMapping(value="/all-submissions", method=RequestMethod.GET) public ModelAndView allSubmissionsView( @RequestParam(value="problemId", required=false, defaultValue="0") long problemId, @RequestParam(value="username", required=false, defaultValue="") String username, @RequestParam(value="page", required=false, defaultValue="1") long pageNumber, HttpServletRequest request, HttpServletResponse response) { final int NUMBER_OF_SUBMISSIONS_PER_PAGE = 100; long totalSubmissions = submissionService.getNumberOfSubmissionsUsingProblemIdAndUsername(problemId, username); long latestSubmissionId = submissionService.getLatestSubmissionId(); long offset = latestSubmissionId - (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_SUBMISSIONS_PER_PAGE; List<Submission> submissions = submissionService.getSubmissions(problemId, username, offset, NUMBER_OF_SUBMISSIONS_PER_PAGE); ModelAndView view = new ModelAndView("administration/all-submissions"); view.addObject("problemId", problemId); view.addObject("username", username); view.addObject("currentPage", pageNumber); view.addObject("totalPages", (long) Math.ceil(totalSubmissions * 1.0 / NUMBER_OF_SUBMISSIONS_PER_PAGE)); view.addObject("submissions", submissions); return view; } /** * 删除选定的提交记录. * @param submissions - 提交记录ID的集合, 以逗号(, )分隔 * @param request - HttpServletRequest对象 * @return 提交记录的删除结果 */ @RequestMapping(value="/deleteSubmissions.action", method=RequestMethod.POST) public @ResponseBody Map<String, Object> deleteSubmissionsAction( @RequestParam(value="submissions") String submissions, HttpServletRequest request) { Map<String, Object> result = new HashMap<>(3, 1); List<Long> submissionList = JSON.parseArray(submissions, Long.class); List<Long> deletedSubmissions = new ArrayList<>(); for ( Long submissionId : submissionList ) { if ( submissionService.deleteSubmission(submissionId) ) { deletedSubmissions.add(submissionId); } String ipAddress = HttpRequestParser.getRemoteAddr(request); LOGGER.info(String.format("Submission: [SubmissionId=%s] deleted by administrator at %s.", new Object[] {submissionId, ipAddress})); } result.put("isSuccessful", true); result.put("deletedSubmissions", deletedSubmissions); return result; } /** * 重新评测选定的提交记录. * @param submissions - 提交记录ID的集合, 以逗号(, )分隔 * @param request - HttpServletRequest对象 * @return 重新评测请求的执行结果 */ @RequestMapping(value="/restartSubmissions.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> restartSubmissionsAction( @RequestParam(value="submissions") String submissions, HttpServletRequest request) { Map<String, Boolean> result = new HashMap<>(2, 1); List<Long> submissionList = JSON.parseArray(submissions, Long.class); for ( Long submissionId : submissionList ) { submissionService.createSubmissionTask(submissionId); } result.put("isSuccessful", true); return result; } /** * 查看提交记录. * @param submissionId - 提交记录的唯一标识符 * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含提交记录信息的ModelAndView对象 */ @RequestMapping(value="/edit-submission/{submissionId}", method=RequestMethod.GET) public ModelAndView editSubmissionView( @PathVariable(value = "submissionId") long submissionId, HttpServletRequest request, HttpServletResponse response) { Submission submission = submissionService.getSubmission(submissionId); if ( submission == null ) { throw new ResourceNotFoundException(); } ModelAndView view = new ModelAndView("administration/edit-submission"); view.addObject("submission", submission); view.addObject("csrfToken", CsrfProtector.getCsrfToken(request.getSession())); return view; } /** * 加载常规选项页面. * @param request - HttpRequest对象 * @param response - HttpResponse对象 * @return 包含常规选项页面信息的ModelAndView对象 */ @RequestMapping(value="/general-settings", method=RequestMethod.GET) public ModelAndView generalSettingsView( HttpServletRequest request, HttpServletResponse response) { ModelAndView view = new ModelAndView("administration/general-settings"); view.addObject("options", getOptions()); return view; } /** * 获取系统全部的选项, 以键值对的形式返回. * @return 键值对形式的系统选项 */ private Map<String, String> getOptions() { Map<String, String> optionMap = new HashMap<>(); List<Option> options = optionService.getOptions(); for ( Option option : options ) { optionMap.put(option.getOptionName(), option.getOptionValue()); } return optionMap; } /** * 更新网站常规选项. * @param websiteName - 网站名称 * @param websiteDescription - 网站描述 * @param copyright - 网站版权信息 * @param allowUserRegister - 是否允许用户注册 * @param icpNumber - 网站备案号 * @param policeIcpNumber - 公安备案号 * @param googleAnalyticsCode - Google Analytics代码 * @param offensiveWords - 敏感词列表 * @param request - HttpServletRequest对象 * @return 网站常规选项的更新结果 */ @RequestMapping(value="/updateGeneralSettings.action", method=RequestMethod.POST) public @ResponseBody Map<String, Boolean> updateGeneralSettingsAction( @RequestParam(value="websiteName") String websiteName, @RequestParam(value="websiteDescription") String websiteDescription, @RequestParam(value="copyright") String copyright, @RequestParam(value="allowUserRegister") boolean allowUserRegister, @RequestParam(value="icpNumber") String icpNumber, @RequestParam(value="policeIcpNumber") String policeIcpNumber, @RequestParam(value="googleAnalyticsCode") String googleAnalyticsCode, @RequestParam(value="offensiveWords") String offensiveWords, HttpServletRequest request) { Map<String, Boolean> result = optionService.updateOptions(websiteName, websiteDescription, copyright, allowUserRegister, icpNumber, policeIcpNumber, googleAnalyticsCode, offensiveWords); return result; } /** * 加载编程语言设置页面. * @param request - HttpServletRequest对象 * @param response - HttpServletResponse对象 * @return 包含编程语言设置信息的ModelAndView对象 */ @RequestMapping(value="/language-settings", method=RequestMethod.GET) public ModelAndView languageSettingsView( HttpServletRequest request, HttpServletResponse response) { ModelAndView view = new ModelAndView("administration/language-settings"); view.addObject("languages", languageService.getAllLanguages()); return view; } /** * 更新网站编程语言选项. * @param languages - 包含编程语言设置的数组 * @param request - HttpServletRequest对象 * @return 编程语言选项的更新结果 */ @RequestMapping(value="/updateLanguageSettings.action", method=RequestMethod.POST) public @ResponseBody Map<String, Object> updateLanguageSettingsAction( @RequestParam(value="languages") String languages, HttpServletRequest request) { List<Language> languagesList = JSON.parseArray(languages, Language.class); Map<String, Object> result = languageService.updateLanguageSettings(languagesList); return result; } /** * 自动注入的UserService对象. */ @Autowired private UserService userService; /** * 自动注入的ProblemService对象. * 用于获取试题记录信息. */ @Autowired private ProblemService problemService; /** * 自动注入的SubmissionService对象. * 用于获取提交记录信息. */ @Autowired private SubmissionService submissionService; /** * 自动注入的OptionService对象. * 用于获取系统中的设置选项. */ @Autowired private OptionService optionService; /** * 自动注入的LanguageService对象. * 用于获取系统中的编程语言选项. */ @Autowired private LanguageService languageService; /** * 自动注入的ApplicationEventListener对象. * 用于获取在线评测机的数量. */ @Autowired private ApplicationEventListener eventListener; /** * 日志记录器. */ private static final Logger LOGGER = LogManager.getLogger(AdministrationController.class); }