news 2026/4/23 15:43:00

基于SpringAI的在线考试系统-企业级软件研发工程应用规范实现细节

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringAI的在线考试系统-企业级软件研发工程应用规范实现细节

从“考生参加考试”看DDD四层架构:把业务需求拆成可落地的代码逻辑

在企业级在线考试系统中,“考生参加考试并提交答卷”是核心场景之一。我们用DDD四层架构,把用户的自然语言需求,转化为“业务规则→流程→接口→技术实现”的完整链路。

一、先明确:用户的“自然语言业务需求”是什么?

这是领域层的输入源,必须先把用户的口语化需求,提炼成清晰的业务规则:

  1. 考生只能参加“已发布且未结束”的考试;
  2. 考试开始后,系统自动加载对应试卷,且试卷内容不可修改;
  3. 考生答题过程中,系统每5分钟自动保存一次答案;
  4. 考生主动交卷/考试时间到,系统自动计算客观题分数,主观题进入阅卷队列;
  5. 考试结束后,考生能立即看到客观题得分和错题提示。

二、领域层:把“自然语言规则”转化为业务代码

领域层是业务规则的载体,我们要把上面的需求,封装成实体、聚合根、领域服务:

1. 核心领域实体/聚合根设计

Exam(考试)为聚合根(因为考试是该场景的核心载体),包含关联的实体/值对象:

// 聚合根:Exam(考试)@AggregateRoot@DatapublicclassExam{@IdentifierprivateLongid;privateStringtitle;// 考试名称privateLongpaperId;// 关联试卷IDprivateLocalDateTimestartTime;// 开始时间privateLocalDateTimeendTime;// 结束时间privateExamStatusstatus;// 状态:DRAFT(草稿)/PUBLISHED(已发布)/FINISHED(已结束)// 值对象:封装“考试配置”的业务规则@EmbeddedprivateExamConfigconfig;// 业务规则1:判断考生是否能参加考试publicbooleanisJoinable(){LocalDateTimenow=LocalDateTime.now();returnthis.status==ExamStatus.PUBLISHED&&now.isAfter(this.startTime)&&now.isBefore(this.endTime);}// 业务规则2:考试开始后不能修改试卷publicbooleancanModifyPaper(){returnLocalDateTime.now().isBefore(this.startTime);}}// 值对象:ExamConfig(考试配置)@ValueObject@DatapublicclassExamConfig{privateIntegerautoSaveInterval;// 自动保存间隔(分钟)// 业务规则3:获取自动保存的时间间隔publicDurationgetAutoSaveDuration(){returnDuration.ofMinutes(this.autoSaveInterval);}}// 实体:AnswerSheet(考生答卷)@Entity@DatapublicclassAnswerSheet{@IdprivateLongid;privateLongexamId;// 关联考试IDprivateLonguserId;// 关联考生IDprivateMap<Long,String>answers;// 题目ID→考生答案privateLocalDateTimelastSaveTime;// 最后自动保存时间privateBigDecimalobjectiveScore;// 客观题得分// 业务规则4:判断是否需要自动保存publicbooleanneedAutoSave(ExamConfigexamConfig){LocalDateTimenextSaveTime=this.lastSaveTime.plus(examConfig.getAutoSaveDuration());returnLocalDateTime.now().isAfter(nextSaveTime);}}

2. 领域服务:跨聚合的业务规则封装

如果业务需要多个聚合协同(比如“计算客观题分数”需要AnswerSheet+Question聚合),用领域服务封装:

// 领域服务:AnswerSheetDomainService@ServicepublicclassAnswerSheetDomainService{// 依赖Question聚合的仓储(接口,由基础设施层实现)privatefinalQuestionRepositoryquestionRepository;// 业务规则4:计算客观题分数publicBigDecimalcalculateObjectiveScore(AnswerSheetanswerSheet){List<Question>questions=questionRepository.findByExamId(answerSheet.getExamId());BigDecimalscore=BigDecimal.ZERO;for(Questionquestion:questions){if(question.getType()==QuestionType.OBJECTIVE){// 只算客观题StringcorrectAnswer=question.getCorrectAnswer();StringuserAnswer=answerSheet.getAnswers().get(question.getId());if(correctAnswer.equals(userAnswer)){score=score.add(question.getScore());}}}answerSheet.setObjectiveScore(score);returnscore;}}

三、应用层:把“业务规则”编排成“用户流程”

应用层不写业务规则,只负责把领域层的能力串成用户需要的操作流程(对应“考生参加考试”的完整步骤):

// 应用服务:ExamApplicationService@ServicepublicclassExamApplicationService{// 依赖领域层的仓储和服务privatefinalExamRepositoryexamRepository;privatefinalAnswerSheetRepositoryanswerSheetRepository;privatefinalAnswerSheetDomainServiceanswerSheetDomainService;privatefinalMessageProducermessageProducer;// 消息发送(基础设施层提供)// 编排“考生参加考试并提交答卷”的流程publicExamResultDTOparticipateAndSubmit(LongexamId,LonguserId,Map<Long,String>answers){// 步骤1:校验考试是否可参加(调用领域层规则)Examexam=examRepository.findById(examId).orElseThrow(()->newBusinessException("考试不存在"));if(!exam.isJoinable()){thrownewBusinessException("当前考试不可参加");}// 步骤2:创建/更新答卷(调用领域层实体)AnswerSheetanswerSheet=answerSheetRepository.findByExamIdAndUserId(examId,userId).orElse(newAnswerSheet(examId,userId));answerSheet.setAnswers(answers);answerSheet.setLastSaveTime(LocalDateTime.now());// 步骤3:计算客观题分数(调用领域服务)BigDecimalobjectiveScore=answerSheetDomainService.calculateObjectiveScore(answerSheet);answerSheetRepository.save(answerSheet);// 步骤4:发送“主观题阅卷”消息(调用基础设施层能力)messageProducer.sendSubjectiveMarkingMessage(answerSheet.getId());// 步骤5:组装结果返回returnExamResultDTO.builder().examId(examId).userId(userId).objectiveScore(objectiveScore).build();}}

四、接口层:把“流程”包装成“对外服务”

接口层是系统的对外入口,负责把应用层的流程,暴露为前端/其他系统可调用的形式(比如RESTful API):

// 接口层:ExamController@RestController@RequestMapping("/api/exam")publicclassExamController{privatefinalExamApplicationServiceexamApplicationService;// 对外提供“参加考试并提交答卷”的API@PostMapping("/{examId}/submit")publicResult<ExamResultDTO>submitExam(@PathVariableLongexamId,@RequestParamLonguserId,@RequestBodyMap<Long,String>answers){ExamResultDTOresult=examApplicationService.participateAndSubmit(examId,userId,answers);returnResult.success(result);}}

五、基础设施层:把“业务逻辑”落地为“技术实现”

基础设施层是纯技术细节,负责实现领域层/应用层需要的技术能力(数据库、消息队列等):

1. 仓储的技术实现(比如MySQL)

// 基础设施层:ExamRepository的MySQL实现@RepositorypublicclassExamRepositoryImplimplementsExamRepository{privatefinalJdbcTemplatejdbcTemplate;@OverridepublicOptional<Exam>findById(Longid){Stringsql="SELECT * FROM exam WHERE id = ?";returnjdbcTemplate.query(sql,newObject[]{id},rs->{if(rs.next()){Examexam=newExam();exam.setId(rs.getLong("id"));exam.setTitle(rs.getString("title"));// 其他字段赋值...returnOptional.of(exam);}returnOptional.empty();});}@Overridepublicvoidsave(Examexam){// 实现MySQL的插入/更新逻辑}}

2. 消息队列的技术实现(比如RabbitMQ)

// 基础设施层:MessageProducer的RabbitMQ实现@ServicepublicclassRabbitMQMessageProducerimplementsMessageProducer{privatefinalRabbitTemplaterabbitTemplate;@OverridepublicvoidsendSubjectiveMarkingMessage(LonganswerSheetId){rabbitTemplate.convertAndSend("subjective-marking-exchange","subjective.marking",answerSheetId);}}

六、总结:四层架构的“职责边界”

通过“考生参加考试”场景,我们能清晰看到DDD四层的分工:

层级角色核心产出物
领域层业务规则的“翻译器”实体、聚合根、领域服务
应用层业务流程的“串线工”应用服务(流程编排)
接口层对外服务的“窗口”RESTful API/消息处理器
基础设施层技术实现的“工具箱”仓储实现、消息队列集成、外部接口适配

这样的分层设计,既能保证业务规则不被技术细节污染(领域层独立),又能让流程和技术实现灵活替换(比如把MySQL换成MongoDB,只改基础设施层),是企业级系统“易维护、易扩展”的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 10:49:07

低端游戏网站:在线玩260+经典DOS/Windows游戏,基于JSDOS技术

对于许多资深玩家而言&#xff0c;重温《红色警戒》、《仙剑奇侠传98柔情篇》、《大富翁4》等经典游戏往往面临兼容性差、安装繁琐的难题。传统的解决方案是使用DOSBox等本地模拟器进行复杂配置。低端游戏​ 平台则另辟蹊径&#xff0c;通过前沿的Web技术将模拟器环境“搬”到了…

作者头像 李华
网站建设 2026/4/23 10:47:42

被说“像 AI”的论文,最先该改的是哪里?

如果你已经被提醒过一句话&#xff1a;“这篇论文有点像 AI。”我想先帮你把一个误区拆掉&#xff1a;这不是让你把整篇论文推翻重写。绝大多数情况下&#xff0c; 只需要改对位置&#xff0c;就能明显缓解问题。关键在于—— 顺序不能错。一、最先要改的&#xff0c;不是正文核…

作者头像 李华
网站建设 2026/4/23 12:10:22

看完这篇,论文降AI不再是难事!总结了4个最强提示词组合和3个核心改写策略,让你的论文在AI率不再红!

写完论文以后你最担心什么。 肯定是查降ai结果啊。 AI率太高或太低都不符合学校要求。 这就意味着要反复修改。 不停调整用词。 花更多时间精力。 真的太累人。 其实我早就猜到大家会头疼降低ai率这件事。 所以提前试了很多方法。 总算找到几个有用又实在的办法。 今天…

作者头像 李华
网站建设 2026/4/23 12:11:38

(新卷,200分)- 返回矩阵中非1的元素个数(Java JS Python C)

(新卷,200分)- 返回矩阵中非1的元素个数&#xff08;Java & JS & Python & C&#xff09; 题目描述 存在一个m*n的二维数组&#xff0c;其成员取值范围为0&#xff0c;1&#xff0c;2。 其中值为1的元素具备同化特性&#xff0c;每经过1S&#xff0c;将上下左右…

作者头像 李华
网站建设 2026/4/23 12:19:08

优雅的使用Nexent创建与部署前端面试智能体

文章目录引言Nexent平台简介Nexent优点实战&#xff1a;创建和部署一个前端面试智能体什么是知识库记忆管理它带来了什么好处&#xff1f;本地化部署未来前景总结引言 在AI技术重塑各行各业工作模式的今天&#xff0c;一个共识日益清晰&#xff1a;AI不会淘汰开发者&#xff0…

作者头像 李华
网站建设 2026/4/23 12:19:16

AI大模型行业真相与学习路线,从月薪3万到年薪200万

AI行业呈现"冰火两重天"&#xff1a;算法工程师年薪可达50-200万&#xff0c;而传统程序员面临裁员风险。薪资呈金字塔结构&#xff0c;核心算法岗薪资最高。快速上手AI工具只能提供短期优势&#xff0c;而扎实的数学基础、编程思维和算法设计等基本功才是长期发展的…

作者头像 李华