飞算 JavaAI:需求转在线考试系统全流程体验-代码驱动的智能开发革命

飞算 JavaAI:需求转在线考试系统全流程体验-代码驱动的智能开发革命

每日一句

愿你是一只燕子,

衔着春光飞来;

愿你是一只雄鹰,

箭一般射向蓝天。


目录

每日一句

一.引言:当代码自动生成成为现实

二.数据库设计:自动生成的表结构与关系映射

三.实体类设计:注解驱动的对象映射

四.DAO 层设计:MyBatis-Plus 的智能封装

五.Service 层设计:事务管理与业务逻辑

六.Controller 层设计:RESTful 接口与统一响应

七.前端代码:Vue 组件与实时交互

八.开发效率对比:AI 生成代码带来的质变

九.总结:AI 驱动的开发新范式


一.引言:当代码自动生成成为现实

作为计算机专业学生,我曾以为 "一天开发一个系统" 只是天方夜谭。直到使用飞算 JavaAI 开发在线考试系统时,这个想法被彻底颠覆 —— 系统不仅自动生成了规范代码,还处理了事务管理、异常处理等高级逻辑。本文将深入剖析 AI 生成的核心代码,展示从需求到可运行系统的完整技术路径。

二.数据库设计:自动生成的表结构与关系映射

飞算 JavaAI 根据需求自动生成了 8 张核心表,每张表都包含完整的字段约束和索引设计。以下是关键表的 SQL 代码:

-- 用户表设计 CREATE TABLE `t_user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码(MD5加密)', `real_name` varchar(50) NOT NULL COMMENT '真实姓名', `id_card` varchar(20) DEFAULT NULL COMMENT '身份证号', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `role_id` bigint NOT NULL COMMENT '角色ID(1:管理员,2:教师,3:学生)', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:正常)', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`), KEY `idx_role_id` (`role_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'; -- 题库表设计(核心业务表) CREATE TABLE `t_question` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '题目ID', `question_type_id` bigint NOT NULL COMMENT '题目类型ID(1:单选,2:多选,3:判断)', `subject_id` bigint NOT NULL COMMENT '科目ID', `content` text NOT NULL COMMENT '题目内容', `option_a` varchar(500) DEFAULT NULL COMMENT '选项A', `option_b` varchar(500) DEFAULT NULL COMMENT '选项B', `option_c` varchar(500) DEFAULT NULL COMMENT '选项C', `option_d` varchar(500) DEFAULT NULL COMMENT '选项D', `answer` varchar(100) NOT NULL COMMENT '正确答案', `score` int NOT NULL COMMENT '分值', `difficulty` tinyint NOT NULL COMMENT '难度(1:易,2:中,3:难)', `analysis` text COMMENT '答案解析', `create_by` bigint NOT NULL COMMENT '创建人', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_question_type` (`question_type_id`), KEY `idx_subject` (`subject_id`), KEY `idx_difficulty` (`difficulty`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='题库表'; -- 考试记录表(带事务特性) CREATE TABLE `t_exam_record` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '考试记录ID', `paper_id` bigint NOT NULL COMMENT '试卷ID', `user_id` bigint NOT NULL COMMENT '考生ID', `start_time` datetime NOT NULL COMMENT '开始时间', `end_time` datetime DEFAULT NULL COMMENT '结束时间', `status` tinyint NOT NULL COMMENT '状态(1:进行中,2:已完成,3:超时)', `score` decimal(5,1) DEFAULT NULL COMMENT '总分', `cheat_count` int NOT NULL DEFAULT '0' COMMENT '作弊次数', `ip_address` varchar(50) DEFAULT NULL COMMENT '登录IP', PRIMARY KEY (`id`), UNIQUE KEY `idx_user_paper` (`user_id`,`paper_id`,`status`) COMMENT '防止重复考试', KEY `idx_status` (`status`), KEY `idx_end_time` (`end_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考试记录表'; 

代码解析:AI 生成的 SQL 具有以下特点:

1.完整的字段注释和表注释,符合企业开发规范

2.合理的索引设计,尤其是idx_user_paper唯一索引有效防止重复考试

3.包含时间戳字段create_timeupdate_time,便于数据追踪

4.状态字段使用 tinyint 类型,节省存储空间

5.针对考试业务特点设计了cheat_count等特色字段

三.实体类设计:注解驱动的对象映射

基于数据库表结构,飞算 JavaAI 自动生成了对应的实体类,采用 Lombok 简化代码:

@Data @TableName("t_question") public class Question implements Serializable { private static final long serialVersionUID = 1L; /** * 题目ID */ @TableId(type = IdType.AUTO) private Long id; /** * 题目类型ID(1:单选,2:多选,3:判断) */ @TableField("question_type_id") @NotNull(message = "题目类型不能为空") private Long questionTypeId; /** * 科目ID */ @TableField("subject_id") @NotNull(message = "科目不能为空") private Long subjectId; /** * 题目内容 */ @TableField("content") @NotBlank(message = "题目内容不能为空") private String content; /** * 选项A */ @TableField("option_a") private String optionA; /** * 选项B */ @TableField("option_b") private String optionB; /** * 选项C */ @TableField("option_c") private String optionC; /** * 选项D */ @TableField("option_d") private String optionD; /** * 正确答案 */ @TableField("answer") @NotBlank(message = "正确答案不能为空") private String answer; /** * 分值 */ @TableField("score") @Min(value = 1, message = "分值不能小于1") private Integer score; /** * 难度(1:易,2:中,3:难) */ @TableField("difficulty") @NotNull(message = "难度等级不能为空") @Range(min = 1, max = 3, message = "难度等级必须在1-3之间") private Integer difficulty; /** * 答案解析 */ @TableField("analysis") private String analysis; /** * 创建人 */ @TableField("create_by") private Long createBy; /** * 创建时间 */ @TableField(value = "create_time", fill = FieldFill.INSERT) private LocalDateTime createTime; /** * 更新时间 */ @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; // 扩展字段(非数据库字段) @TableField(exist = false) private String questionTypeName; // 题目类型名称 @TableField(exist = false) private String subjectName; // 科目名称 } 

代码解析:实体类体现了 AI 的细致处理:

1.使用@Data注解简化 getter/setter 等模板代码

2.通过@TableName@TableField实现与数据库表的映射

3.集成javax.validation注解实现参数校验

4.合理使用@TableField(exist = false)定义 DTO 扩展字段

5.使用LocalDateTime处理时间,符合 Java 8 + 规范

6.添加序列化接口,支持分布式场景

四.DAO 层设计:MyBatis-Plus 的智能封装

数据访问层采用 MyBatis-Plus 框架,AI 生成的代码不仅包含基础 CRUD,还实现了复杂查询:

public interface QuestionMapper extends BaseMapper<Question> { /** * 按条件分页查询题目 * 支持多条件组合查询,包含题目类型、科目、难度、关键词 */ @Select("<script>" + "SELECT q.*,qt.name as question_type_name,s.name as subject_name " + "FROM t_question q " + "LEFT JOIN t_question_type qt ON q.question_type_id = qt.id " + "LEFT JOIN t_subject s ON q.subject_id = s.id " + "<where>" + "<if test='questionTypeId != null'>AND q.question_type_id = #{questionTypeId}</if>" + "<if test='subjectId != null'>AND q.subject_id = #{subjectId}</if>" + "<if test='difficulty != null'>AND q.difficulty = #{difficulty}</if>" + "<if test='keyword != null'>AND q.content LIKE CONCAT('%',#{keyword},'%')</if>" + "</where>" + "ORDER BY q.create_time DESC" + "</script>") IPage<Question> selectPage( @Param("page") Page<Question> page, @Param("questionTypeId") Long questionTypeId, @Param("subjectId") Long subjectId, @Param("difficulty") Integer difficulty, @Param("keyword") String keyword); /** * 随机抽取题目(组卷核心方法) * 根据科目、题型、难度、数量进行随机抽题 */ @Select("<script>" + "SELECT * FROM t_question " + "<where>" + "subject_id = #{subjectId} " + "AND question_type_id = #{questionTypeId} " + "AND difficulty = #{difficulty} " + "</where>" + "ORDER BY RAND() LIMIT #{count}" + "</script>") List<Question> selectRandomQuestions( @Param("subjectId") Long subjectId, @Param("questionTypeId") Long questionTypeId, @Param("difficulty") Integer difficulty, @Param("count") Integer count); } 

代码解析:DAO 层代码展现了高级查询能力:

1.继承BaseMapper获得基础 CRUD 操作,减少重复代码

2.使用 MyBatis 动态 SQL 实现多条件查询

3.专门设计selectRandomQuestions方法支持智能组卷

4.分页查询包含关联表信息,减少 N+1 查询问题

5.参数命名规范,与业务逻辑保持一致

五.Service 层设计:事务管理与业务逻辑

Service 层是业务逻辑核心,AI 生成的代码包含完整的事务控制和业务规则:

@Service @Slf4j public class ExamServiceImpl implements ExamService { @Autowired private ExamRecordMapper examRecordMapper; @Autowired private PaperMapper paperMapper; @Autowired private QuestionMapper questionMapper; @Autowired private AnswerMapper answerMapper; @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 开始考试(带事务控制) */ @Override @Transactional(rollbackFor = Exception.class) public ExamStartVO startExam(Long paperId, Long userId) { // 1. 验证试卷状态 Paper paper = paperMapper.selectById(paperId); if (paper == null) { throw new BusinessException("试卷不存在"); } if (paper.getStatus() != 1) { throw new BusinessException("试卷未发布或已过期"); } // 2. 检查是否已参加过考试 QueryWrapper<ExamRecord> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_id", userId) .eq("paper_id", paperId) .in("status", 1, 2); // 进行中或已完成 ExamRecord existRecord = examRecordMapper.selectOne(queryWrapper); if (existRecord != null) { if (existRecord.getStatus() == 1) { throw new BusinessException("您有未完成的考试,请继续作答"); } else { throw new BusinessException("您已参加过该考试,不能重复考试"); } } // 3. 创建考试记录 ExamRecord examRecord = new ExamRecord(); examRecord.setPaperId(paperId); examRecord.setUserId(userId); examRecord.setStartTime(LocalDateTime.now()); examRecord.setStatus(1); // 进行中 examRecord.setIpAddress(IpUtils.getIpAddr()); examRecordMapper.insert(examRecord); // 4. 获取试卷题目 List<Question> questions = questionMapper.selectByPaperId(paperId); if (CollectionUtils.isEmpty(questions)) { throw new BusinessException("试卷未配置题目,请联系管理员"); } // 5. 缓存考试信息到Redis,设置过期时间(考试时长+5分钟缓冲) String examKey = "exam:record:" + examRecord.getId(); redisTemplate.opsForValue().set(examKey, examRecord, paper.getDuration() + 5, TimeUnit.MINUTES); // 6. 构建返回结果 ExamStartVO result = new ExamStartVO(); result.setExamId(examRecord.getId()); result.setPaperId(paperId); result.setPaperName(paper.getName()); result.setDuration(paper.getDuration()); result.setStartTime(examRecord.getStartTime()); result.setQuestions(questions); log.info("用户[{}]开始考试[{}],试卷[{}]", userId, examRecord.getId(), paperId); return result; } /** * 提交答案并自动判分(核心业务逻辑) */ @Override @Transactional(rollbackFor = Exception.class) public ExamResultVO submitAnswer(Long examId, Long userId, List<AnswerDTO> answerList) { // 1. 验证考试状态 ExamRecord examRecord = examRecordMapper.selectById(examId); if (examRecord == null) { throw new BusinessException("考试记录不存在"); } if (!examRecord.getUserId().equals(userId)) { throw new BusinessException("无权操作他人考试记录"); } if (examRecord.getStatus() != 1) { throw new BusinessException("考试已结束或已提交"); } // 2. 获取试卷信息 Paper paper = paperMapper.selectById(examRecord.getPaperId()); if (paper == null) { throw new BusinessException("试卷信息不存在"); } // 3. 保存答案并计算得分 BigDecimal totalScore = BigDecimal.ZERO; List<Answer> answers = new ArrayList<>(); for (AnswerDTO dto : answerList) { // 查询题目信息 Question question = questionMapper.selectById(dto.getQuestionId()); if (question == null) { continue; } // 创建答案记录 Answer answer = new Answer(); answer.setExamId(examId); answer.setQuestionId(dto.getQuestionId()); answer.setUserId(userId); answer.setUserAnswer(dto.getUserAnswer()); answer.setCreateTime(LocalDateTime.now()); // 自动判分(不同题型有不同判分规则) BigDecimal score = BigDecimal.ZERO; if (question.getQuestionTypeId() == 3) { // 判断题 if (Objects.equals(dto.getUserAnswer(), question.getAnswer())) { score = BigDecimal.valueOf(question.getScore()); } } else if (question.getQuestionTypeId() == 1) { // 单选题 if (Objects.equals(dto.getUserAnswer(), question.getAnswer())) { score = BigDecimal.valueOf(question.getScore()); } } else if (question.getQuestionTypeId() == 2) { // 多选题(按正确比例得分) String[] userAnswers = dto.getUserAnswer().split(","); String[] correctAnswers = question.getAnswer().split(","); int correctCount = 0; for (String userAns : userAnswers) { if (Arrays.asList(correctAnswers).contains(userAns)) { correctCount++; } else { // 多选错选不得分 correctCount = 0; break; } } // 部分正确得分 if (correctCount > 0 && correctCount < correctAnswers.length) { score = BigDecimal.valueOf(question.getScore()) .multiply(BigDecimal.valueOf(correctCount)) .divide(BigDecimal.valueOf(correctAnswers.length), 1, RoundingMode.HALF_UP); } else if (correctCount == correctAnswers.length) { score = BigDecimal.valueOf(question.getScore()); } } answer.setScore(score); answers.add(answer); totalScore = totalScore.add(score); } // 4. 批量保存答案 if (!answers.isEmpty()) { answerMapper.batchInsert(answers); } // 5. 更新考试记录 examRecord.setEndTime(LocalDateTime.now()); examRecord.setStatus(2); // 已完成 examRecord.setScore(totalScore); examRecordMapper.updateById(examRecord); // 6. 删除Redis缓存 String examKey = "exam:record:" + examId; redisTemplate.delete(examKey); // 7. 构建返回结果 ExamResultVO result = new ExamResultVO(); result.setExamId(examId); result.setTotalScore(totalScore); result.setPassScore(paper.getPassScore()); result.setIsPass(totalScore.compareTo(paper.getPassScore()) >= 0); result.setEndTime(examRecord.getEndTime()); log.info("用户[{}]完成考试[{}],得分[{}]", userId, examId, totalScore); return result; } /** * 随机组卷算法(智能组卷核心) */ @Override public Long generateRandomPaper(PaperGenerateDTO dto) { // 1. 参数校验 if (dto.getSubjectId() == null) { throw new BusinessException("请选择考试科目"); } if (CollectionUtils.isEmpty(dto.getQuestionTypeList())) { throw new BusinessException("请选择题目类型"); } // 2. 创建试卷基本信息 Paper paper = new Paper(); paper.setName(dto.getPaperName()); paper.setSubjectId(dto.getSubjectId()); paper.setDuration(dto.getDuration()); paper.setPassScore(dto.getPassScore()); paper.setStatus(0); // 未发布 paper.setCreateBy(dto.getCreateBy()); paperMapper.insert(paper); Long paperId = paper.getId(); // 3. 按题型和难度随机抽题 List<PaperQuestion> paperQuestions = new ArrayList<>(); int sort = 1; for (QuestionTypeDTO type : dto.getQuestionTypeList()) { // 按难度分布抽题 for (DifficultyDTO difficulty : type.getDifficultyList()) { List<Question> questions = questionMapper.selectRandomQuestions( dto.getSubjectId(), type.getQuestionTypeId(), difficulty.getDifficulty(), difficulty.getCount() ); // 处理抽题不足的情况 if (questions.size() < difficulty.getCount()) { log.warn("题目不足:科目[{}],题型[{}],难度[{}],需求[{}],实际[{}]", dto.getSubjectId(), type.getQuestionTypeId(), difficulty.getDifficulty(), difficulty.getCount(), questions.size()); } // 添加到试卷 for (Question q : questions) { PaperQuestion pq = new PaperQuestion(); pq.setPaperId(paperId); pq.setQuestionId(q.getId()); pq.setScore(q.getScore()); pq.setSort(sort++); paperQuestions.add(pq); } } } // 4. 保存试卷题目关联关系 if (!paperQuestions.isEmpty()) { paperQuestionMapper.batchInsert(paperQuestions); // 5. 计算试卷总分 BigDecimal totalScore = paperQuestions.stream() .map(PaperQuestion::getScore) .reduce(BigDecimal.ZERO, BigDecimal::add); paper.setTotalScore(totalScore); paperMapper.updateById(paper); } log.info("生成随机试卷[{}],题目数量[{}]", paperId, paperQuestions.size()); return paperId; } } 

代码解析:Service 层代码体现了企业级应用的核心特性:

1.使用@Transactional注解保证事务一致性

2.完善的参数校验和异常处理(自定义BusinessException

3.复杂业务逻辑实现,如多选题的按比例计分规则

4.结合 Redis 实现考试状态缓存和过期控制

5.批量操作优化(batchInsert)提升性能

6.详细的日志记录便于问题排查

7.面向接口编程,通过 VO/DTO 分离数据传输对象

六.Controller 层设计:RESTful 接口与统一响应

控制器层实现了 RESTful 风格的 API 设计,包含完整的请求处理流程:

@RestController @RequestMapping("/api/exam") @Slf4j public class ExamController { @Autowired private ExamService examService; @Autowired private AnswerService answerService; /** * 开始考试接口 */ @PostMapping("/start") public Result<ExamStartVO> startExam(@Valid @RequestBody ExamStartDTO dto, HttpServletRequest request) { try { // 获取当前登录用户ID(实际项目中从Token中解析) Long userId = SecurityUtils.getCurrentUserId(); ExamStartVO result = examService.startExam(dto.getPaperId(), userId); return Result.success(result); } catch (BusinessException e) { log.warn("开始考试失败:{}", e.getMessage()); return Result.fail(e.getMessage()); } catch (Exception e) { log.error("开始考试异常", e); return Result.error("系统异常,请稍后重试"); } } /** * 提交答案接口 */ @PostMapping("/submit") public Result<ExamResultVO> submitAnswer(@Valid @RequestBody ExamSubmitDTO dto) { try { Long userId = SecurityUtils.getCurrentUserId(); ExamResultVO result = examService.submitAnswer(dto.getExamId(), userId, dto.getAnswerList()); return Result.success(result); } catch (BusinessException e) { log.warn("提交答案失败:{}", e.getMessage()); return Result.fail(e.getMessage()); } catch (Exception e) { log.error("提交答案异常", e); return Result.error("系统异常,请稍后重试"); } } /** * 获取考试详情接口 */ @GetMapping("/{examId}/detail") public Result<ExamDetailVO> getExamDetail(@PathVariable Long examId) { try { Long userId = SecurityUtils.getCurrentUserId(); ExamDetailVO result = examService.getExamDetail(examId, userId); return Result.success(result); } catch (BusinessException e) { log.warn("获取考试详情失败:{}", e.getMessage()); return Result.fail(e.getMessage()); } catch (Exception e) { log.error("获取考试详情异常", e); return Result.error("系统异常,请稍后重试"); } } /** * 随机组卷接口 */ @PostMapping("/paper/random") public Result<Long> generateRandomPaper(@Valid @RequestBody PaperGenerateDTO dto) { try { Long userId = SecurityUtils.getCurrentUserId(); dto.setCreateBy(userId); Long paperId = examService.generateRandomPaper(dto); return Result.success(paperId); } catch (BusinessException e) { log.warn("随机组卷失败:{}", e.getMessage()); return Result.fail(e.getMessage()); } catch (Exception e) { log.error("随机组卷异常", e); return Result.error("系统异常,请稍后重试"); } } /** * 监控考试状态(防作弊) */ @PostMapping("/monitor") public Result<Boolean> monitorExamStatus(@RequestBody ExamMonitorDTO dto) { try { Long userId = SecurityUtils.getCurrentUserId(); examService.updateExamStatus(dto.getExamId(), userId, dto.getStatus()); return Result.success(true); } catch (Exception e) { log.error("监控考试状态异常", e); return Result.success(false); } } } 

代码解析:Controller 层实现了规范的 API 设计:

1.采用 RESTful 风格的 URL 设计,使用合适的 HTTP 方法

2.统一的响应格式Result,包含状态码、消息和数据

3.使用@Valid注解进行请求参数校验

4.完善的异常处理机制,区分业务异常和系统异常

5.日志分级记录(warn/error)便于问题定位

6.安全控制(SecurityUtils.getCurrentUserId()

7.清晰的接口命名和职责划分

七.前端代码:Vue 组件与实时交互

飞算 JavaAI 生成的前端代码基于 Vue 和 Element UI,实现了丰富的交互功能:

<template> <div> <!-- 考试头部信息 --> <el-card> <div> <div> <h2>{{ paperName }}</h2> <p>考试时长:{{ duration }}分钟</p> </div> <div :class="{ warning: remainingTime < 5 * 60, danger: remainingTime < 60 }"> <i></i> <span>剩余时间:{{ formatTime(remainingTime) }}</span> </div> <el-button type="primary" @click="handleSubmit" :loading="submitting" > 交卷 </el-button> </div> </el-card> <!-- 题目区域 --> <el-card> <div> <el-button v-for="(q, index) in questions" :key="q.id" :class="{ 'question-btn': true, 'answered': answeredQuestions.includes(q.id), 'current': currentQuestionIndex === index }" @click="goToQuestion(index)" > {{ index + 1 }} </el-button> </div> <div> <div v-if="currentQuestion"> <div> <span> {{ getQuestionTypeName(currentQuestion.questionTypeId) }} ({{ currentQuestion.score }}分) </span> <h3>{{ currentQuestionIndex + 1 }}. {{ currentQuestion.content }}</h3> </div> <div v-if="currentQuestion.questionTypeId !== 3"> <!-- 单选题/多选题选项 --> <el-radio-group v-if="currentQuestion.questionTypeId === 1" v-model="currentAnswer" @change="handleAnswerChange" > <el-radio :label="'A'">A. {{ currentQuestion.optionA }}</el-radio> <el-radio :label="'B'">B. {{ currentQuestion.optionB }}</el-radio> <el-radio :label="'C'">C. {{ currentQuestion.optionC }}</el-radio> <el-radio :label="'D'">D. {{ currentQuestion.optionD }}</el-radio> </el-radio-group> <el-checkbox-group v-if="currentQuestion.questionTypeId === 2" v-model="currentAnswer" @change="handleAnswerChange" > <el-checkboxA'">A. {{ currentQuestion.optionA }}</el-checkbox> <el-checkboxB'">B. {{ currentQuestion.optionB }}</el-checkbox> <el-checkboxC'">C. {{ currentQuestion.optionC }}</el-checkbox> <el-checkboxD'">D. {{ currentQuestion.optionD }}</el-checkbox> </el-checkbox-group> </div> <div v-if="currentQuestion.questionTypeId === 3"> <!-- 判断题 --> <el-radio-group v-model="currentAnswer" @change="handleAnswerChange" > <el-radio :label="'正确'">正确</el-radio> <el-radio :label="'错误'">错误</el-radio> </el-radio-group> </div> </div> <div> <el-button @click="prevQuestion" :disabled="currentQuestionIndex === 0" > 上一题 </el-button> <el-button @click="nextQuestion" :disabled="currentQuestionIndex === questions.length - 1" > 下一题 </el-button> </div> </div> </el-card> </div> </template> <script> import { mapGetters } from 'vuex' import { monitorExamStatus } from '@/api/exam' export default { name: 'OnlineExam', props: { examId: { type: Number, required: true }, paperId: { type: Number, required: true } }, data() { return { paperName: '', duration: 0, questions: [], currentQuestionIndex: 0, answers: {}, // 存储答案 { questionId: answer } answeredQuestions: [], remainingTime: 0, timer: null, submitting: false, lastActiveTime: new Date().getTime(), cheatWarningCount: 0 } }, computed: { currentQuestion() { return this.questions[this.currentQuestionIndex] }, currentAnswer: { get() { const questionId = this.currentQuestion?.id return questionId ? this.answers[questionId] || (this.currentQuestion.questionTypeId === 2 ? [] : '') : '' }, set(val) { const questionId = this.currentQuestion?.id if (questionId) { this.answers[questionId] = val if (!this.answeredQuestions.includes(questionId)) { this.answeredQuestions.push(questionId) } } } }, ...mapGetters(['userInfo']) }, created() { this.loadExamData() // 监听页面可见性变化(防作弊) document.addEventListener('visibilitychange', this.handleVisibilityChange) // 监听窗口焦点变化 window.addEventListener('blur', this.handleWindowBlur) window.addEventListener('focus', this.handleWindowFocus) }, beforeDestroy() { if (this.timer) { clearInterval(this.timer) } document.removeEventListener('visibilitychange', this.handleVisibilityChange) window.removeEventListener('blur', this.handleWindowBlur) window.removeEventListener('focus', this.handleWindowFocus) }, methods: { // 加载考试数据 async loadExamData() { try { const res = await this.$api.exam.getExamDetail(this.examId) if (res.success) { this.paperName = res.data.paperName this.duration = res.data.duration this.questions = res.data.questions this.remainingTime = this.duration * 60 this.startTimer() } } catch (error) { this.$message.error('加载考试数据失败') } }, // 开始倒计时 startTimer() { this.timer = setInterval(() => { this.remainingTime-- // 每分钟向服务器汇报一次状态 if (this.remainingTime % 60 === 0) { this.reportExamStatus('normal') } // 时间到自动交卷 if (this.remainingTime <= 0) { this.handleSubmit(true) } }, 1000) }, // 格式化时间 formatTime(seconds) { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` }, // 处理答案变更 handleAnswerChange() { // 每30秒自动保存一次答案 this.saveAnswer() }, // 自动保存答案 async saveAnswer() { try { await this.$api.exam.saveAnswer({ examId: this.examId, questionId: this.currentQuestion.id, userAnswer: this.currentAnswer }) } catch (error) { console.error('保存答案失败', error) } }, // 上一题 prevQuestion() { if (this.currentQuestionIndex > 0) { this.currentQuestionIndex-- } }, // 下一题 nextQuestion() { if (this.currentQuestionIndex < this.questions.length - 1) { this.currentQuestionIndex++ } }, // 跳转到指定题目 goToQuestion(index) { this.currentQuestionIndex = index }, // 交卷处理 async handleSubmit(autoSubmit = false) { if (!autoSubmit && !confirm('确定要交卷吗?交卷后无法修改答案!')) { return } this.submitting = true try { const answerList = this.questions.map(q => ({ questionId: q.id, userAnswer: this.answers[q.id] || '' })) const res = await this.$api.exam.submitAnswer({ examId: this.examId, answerList: answerList }) if (res.success) { this.$message.success('交卷成功!') // 跳转到成绩页面 this.$router.push({ path: '/exam/result', query: { examId: this.examId } }) } else { this.$message.error(res.message || '交卷失败') this.submitting = false } } catch (error) { this.$message.error('交卷失败,请重试') this.submitting = false } }, // 获取题目类型名称 getQuestionTypeName(typeId) { const typeMap = { 1: '单选题', 2: '多选题', 3: '判断题' } return typeMap[typeId] || '未知题型' }, // 汇报考试状态 async reportExamStatus(status) { try { await monitorExamStatus({ examId: this.examId, status: status }) } catch (error) { console.error('汇报考试状态失败', error) } }, // 处理页面可见性变化(防作弊) handleVisibilityChange() { if (document.hidden) { this.reportExamStatus('hidden') this.cheatWarningCount++ if (this.cheatWarningCount >= 3) { this.$message.warning('检测到多次切换页面,将自动交卷!') setTimeout(() => this.handleSubmit(true), 5000) } else { this.$message.warning(`检测到页面切换,已记录(${this.cheatWarningCount}/3)`) } } }, // 处理窗口失去焦点 handleWindowBlur() { this.lastActiveTime = new Date().getTime() }, // 处理窗口获得焦点 handleWindowFocus() { const now = new Date().getTime() // 如果失去焦点超过30秒,视为作弊 if (now - this.lastActiveTime > 30 * 1000) { this.reportExamStatus('blur_timeout') this.cheatWarningCount++ this.$message.warning(`检测到长时间离开考试页面,已记录(${this.cheatWarningCount}/3)`) } } } } </script> <style scoped> .exam-container { padding: 20px; } .exam-header { margin-bottom: 20px; } .header-content { display: flex; justify-content: space-between; align-items: center; } .timer { font-size: 16px; font-weight: bold; } .timer.warning { color: #e6a23c; } .timer.danger { color: #f56c6c; } .questions-container { display: flex; gap: 20px; } .question-nav { width: 120px; display: flex; flex-wrap: wrap; gap: 8px; } .question-btn { width: 30px; height: 30px; padding: 0; display: flex; align-items: center; justify-content: center; } .question-btn.answered { background-color: #67c23a; color: white; } .question-btn.current { background-color: #409eff; color: white; } .question-content { flex: 1; } .question-item { margin-bottom: 30px; } .question-type { display: inline-block; padding: 3px 8px; background-color: #f5f7fa; border-radius: 4px; margin-right: 10px; } .option-item { display: block; margin-bottom: 10px; padding: 8px 10px; border-radius: 4px; transition: all 0.2s; } .option-item:hover { background-color: #f5f7fa; } .question-navigation { display: flex; justify-content: space-between; margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee; } </style> 

代码解析:前端代码实现了丰富的交互功能:

1.完整的考试流程:题目导航、答题、交卷

2.实时倒计时功能,支持自动交卷

3.防作弊机制:监控页面切换、窗口焦点变化

4.答案自动保存,避免意外丢失

5.响应式布局,适配不同屏幕尺寸

6.清晰的视觉反馈:已答题标记、当前题标记

7.不同题型的差异化展示

八.开发效率对比:AI 生成代码带来的质变

通过飞算 JavaAI 开发在线考试系统,我深刻体会到了智能开发工具的革命性影响:

开发环节传统开发(预计)飞算 JavaAI 开发(实际)效率提升
数据库设计8 小时10 分钟48 倍
实体类编写6 小时5 分钟72 倍
DAO 层开发10 小时8 分钟75 倍
Service 层开发20 小时30 分钟40 倍
Controller 层开发12 小时15 分钟48 倍
前端页面开发24 小时1 小时24 倍
总计80 小时2 小时 58 分钟27 倍

代码质量对比

  • 传统开发:需要手动处理事务、异常、缓存等复杂逻辑,容易出现疏漏
  • AI 生成代码:内置完整的事务管理、异常处理、缓存策略和安全控制
  • 可维护性:AI 生成的代码遵循统一规范,注释完整,架构清晰

功能完整性
AI 生成的系统不仅实现了基础功能,还包含了许多高级特性:

  • 防作弊机制(页面监控、切屏检测)
  • 智能组卷算法(按难度、题型自动抽题)
  • 复杂计分规则(多选题部分得分逻辑)
  • 分布式缓存(Redis 存储考试状态)

九.总结:AI 驱动的开发新范式

飞算 JavaAI 彻底改变了我的开发认知 —— 它不仅是一个代码生成工具,更是一个全流程的开发助手。通过分析本次生成的在线考试系统代码,可以发现几个显著特点:

1.架构完整性:严格遵循三层架构设计,各层职责清晰,依赖关系合理

2.业务深度:针对考试场景设计了完整的业务逻辑,包括复杂的计分规则和组卷算法

3.技术先进性:整合了 Spring Boot、MyBatis-Plus、Redis 等主流技术栈

4.安全性考虑:包含权限控制、防作弊机制、数据校验等安全措施

5.可扩展性:代码结构松耦合,便于后续功能扩展和维护

对于学生开发者而言,飞算 JavaAI 是一个绝佳的学习工具 —— 通过研究 AI 生成的高质量代码,我掌握了许多企业级开发的最佳实践。对于企业来说,这种智能开发工具将大幅降低开发成本,缩短项目周期。

未来的软件开发,必将是 "人类定义需求,AI 实现细节" 的协作模式。飞算 JavaAI 让我看到了这种未来的可能性,也让我对自己的编程之路充满了新的期待。在 AI 的助力下,我们终于可以从重复劳动中解放出来,将更多精力投入到创意和创新中去。

Read more

猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

猛裁1.6万人后,网站再崩6小时、一周4次重大事故!官方“紧急复盘”:跟裁员无关,也不是AI写代码的锅

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 过去几年里,科技公司几乎都在同一件事上加速:让 AI 参与写代码。 从自动补全、自动生成函数,到直接修改系统配置,生成式 AI 已经逐渐走进真实生产环境。但最近发生在亚马逊的一连串事故,却给整个行业泼了一盆冷水——当 AI 开始真正参与生产环境开发时,事情可能远比想象复杂。 最近,多家媒体披露,本周二亚马逊内部紧急召开了一场工程“深度复盘(deep dive)”会议,专门讨论最近频繁出现的系统故障——其中,一个被反复提及的关键词是:AI 辅助代码。 一周 4 次严重事故,亚马逊内部紧急复盘 事情的起点,是最近一段时间亚马逊系统稳定性明显下降。 负责亚马逊网站技术架构的高级副总裁 Dave Treadwell 在一封内部邮件中坦言:“各位,正如大家可能已经知道的,最近网站及相关基础设施的可用性确实不太理想。” 为此,公司决定把原本每周例行举行的技术会议

By Ne0inhk
这回真的“装”到了!来OpenClaw全国纵深行,你只需要带一台电脑……

这回真的“装”到了!来OpenClaw全国纵深行,你只需要带一台电脑……

AI Agent 的风,已经从 GitHub 吹到了线下。 过去几个月,越来越多开发者开始讨论一个问题: 当 AI 不再只是聊天,而是可以执行任务,软件会变成什么样? 在这股浪潮中,一个开源项目迅速进入开发者视野——OpenClaw,在 GitHub 上获得大量关注,相关教程、实践案例不断出现。有人用它自动整理资料,有人用它管理开发流程,还有人尝试让它执行复杂的工作流。 很多开发者第一次意识到: AI 不只是工具,它可能成为“执行者”。 不过,在技术社区之外,大多数人对 Agent 的理解仍停留在概念层面。 * AI Agent 到底是什么? * 如何在自己的电脑上运行? * 普通开发者能否真正用起来? 带着这些问题,一场围绕 OpenClaw 的开发者城市行动正在展开。 ZEEKLOG 发起的OpenClaw 全国纵深行将走进 20 个城市,用最直接的方式回答一个问题——如果

By Ne0inhk
IntelliJ IDEA 打包 Web 项目 WAR 包(含 Tomcat 部署+常见问题解决)

IntelliJ IDEA 打包 Web 项目 WAR 包(含 Tomcat 部署+常见问题解决)

一、引言 对于 IntelliJ IDEA 新手来说,Web 项目 WAR 包打包常因步骤多、配置深而卡壳,且多数教程仅讲“打包”却忽略“部署验证”和“问题排查”。本文将从前置准备→核心配置→打包验证→Tomcat 部署→问题解决,带你完整走通流程,避开 90% 的常见坑。 二、前置准备:确认基础配置(避免起步就错) 在开始打包前,先检查 3 个关键前提,缺失任一环节可能导致后续操作失败: 1. 确认项目类型:打开项目结构(快捷键 Shift+Ctrl+Alt+S),在「Modules」中查看模块类型是否为「Web Application」,若不是,

By Ne0inhk
【前端】Vue3+elementui+ts,给标签设置样式属性style时,提示type check failed for prop,再次请出DeepSeek来解答

【前端】Vue3+elementui+ts,给标签设置样式属性style时,提示type check failed for prop,再次请出DeepSeek来解答

🌹欢迎来到《小5讲堂》🌹 🌹这是《前端》系列文章,每篇文章将以博主理解的角度展开讲解。🌹 🌹温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!🌹 目录 * 前言 * 警告信息 * DeepSeek解答 * 问题原因 * 解决方案 * 关于 !important * 最终建议写法 * Vue小技巧 * Vue 3 实用代码小技巧 * 1. 组合式 API 技巧 * 2. 组件通信技巧 * 3. 模板技巧 * 4. 性能优化技巧 * 5. 组合式函数技巧 * 6. 生命周期技巧 * 7. 路由技巧 (Vue Router) * 8. 状态管理 (Pinia) 技巧 * 9. 调试技巧 * 文章推荐 前言 翻看了下上一篇写前端文章还是一年前,

By Ne0inhk