mbti性格测试应用项目后端开发(基础)

目录

一、需求分析

a.项目功能梳理

b.核心业务流程

c.需求优先级

二、库表设计

a.用户表

b.应用表

c.题目表

d.评分结果表

测评类应用

得分类应用

e.用户答题记录表

三、后端项目初始化

a.打开后端模板项目

b.准备依赖

c.执行模板的初始化 SQL 脚本,运行模板

d.模板改造

四、后端基础开发-增删改查

a. 数据访问层代码生成

b. 业务逻辑代码生成

c.数据模型开发

包装类

枚举类

VO视图

d. 对库表的增删改查

dto

五、后端基础开发-接口开发

六、后端基础开发-服务开发

上传图片

七、后端核心业务开发

应用审核功能

评分模块实现

全局执行器

回答模块

控制应用可见范围


一、需求分析

目标是明确要做的需求,并且给需求设置优先级,从而明确开发计划。

a.项目功能梳理

按照模块梳理功能:

• 用户模块

    • 注册
    • 登录
    • 管理用户 - 增删改查(仅管理员可用)

• 应用模块

    • 创建应用
    • 修改应用
    • 删除应用
    • 查看应用列表
    • 查看应用详情
    • 查看自己创建的应用
    • 管理应用 - 增删改查(仅管理员可用)
    • 审核发布和下架应用(仅管理员可用)
    • 应用分享(扫码查看)

• 题目模块

    • 创建题目(包括题目选项得分设置)
    • 修改题目
    • 删除题目
    • 管理题目 - 增删改查(仅管理员可用)
    • AI 生成题目

• 评分模块

    • 创建评分结果
    • 修改评分结果
    • 删除评分结果
    • 根据回答计算评分结果(多种评分策略)
      • 自定义规则评分 - 测评类
      • 自定义规则评分 - 打分类
      • AI 评分
    • 管理评分结果 - 增删改查(仅管理员可用)

• 回答模块

    • 提交回答(创建)
    • 查看某次回答的评分结果
    • 查看自己提交的回答列表
    • 管理回答 - 增删改查(仅管理员可用)

• 统计分析模块

    • 应用评分结果分析和查看

b.核心业务流程

流程图:

文字描述:

  1. 用户注册 => 用户登录
  2. 用户创建应用 => 创建题目(包括题目选项得分)=> 创建评分规则(评分策略和评分结果)
  3. 管理员管理应用,审核发布(或下架)应用1808505663815548929_0.618888251920501
  4. 用户查看和检索应用列表,进入应用详情页,在线答题并提交回答
  5. 经过评分模块计算后,用户可查看本次评分结果

c.需求优先级

根据核心业务业务流程,明确需求开发的优先级。

  • P0 为核心,非做不可
  • P1 为重点功能,最好做
  • P2 为实用功能,有空就做
  • P3 可做可不做,时间充裕再做

排好优先级的需求列表如下,其实用表格的形式整理更好:

  • 用户模块
    • 注册 P0
    • 登录 P0
    • 管理用户 - 增删改查(仅管理员可用)P1

应用模块

    • 创建应用 P0
    • 修改应用 P1
    • 删除应用 P1
    • 查看应用列表 P0
    • 查看应用详情 P0
    • 查看自己创建的应用 P1
    • 管理应用 - 增删改查(仅管理员可用)P0
    • 审核发布和下架应用(仅管理员可用)P0
    • 应用分享(扫码查看)P2
  • 题目模块
    • 创建题目(包括题目选项得分设置)P0
    • 修改题目 P1
    • 删除题目 P1
    • 管理题目 - 增删改查(仅管理员可用)P1
    • AI 生成题目 P1
  • 评分模块
    • 创建评分结果 P0
    • 修改评分结果 P1
    • 删除评分结果 P1
    • 根据回答计算评分结果(多种评分策略)
      • 自定义规则评分 - 测评类 P0
      • 自定义规则评分 - 打分类 P0
      • AI 评分 P1
    • 管理评分结果 - 增删改查(仅管理员可用)P1
  • 回答模块
    • 提交回答(创建)P0
    • 查看某次回答的评分结果 P0
    • 查看自己提交的回答列表 P1
    • 管理回答 - 增删改查(仅管理员可用)P1
  • 统计分析模块
    • 应用评分结果分析和查看 P2

排好优先级后,就可以根据优先级去设计接口和页面了。

企业中一般使用专业的系统或者表格来管理需求:

二、库表设计

对应需求分析中的功能梳理的模块,应该有 6 张表(统计分析表本节先不做)。

库名:yudada

数据库初始化文件:

  • 创建库表:create_table.sql
  • 初始数据:init_data.sql

创建时间、更新时间、是否删除等字段表中一定要有。

a.用户表

b.应用表

审核字段 4 件套:

reviewStatus    int      default  0  not null comment '审核状态:0-待审核, 1-通过, 2-拒绝',
reviewMessage   varchar(512)   null comment '审核信息',
reviewerId      bigint         null comment '审核人 id',
reviewTime      datetime       null comment '审核时间',

c.题目表

每个应用对应一个题目表的记录,使用 questionContent 这一 JSON 字段,整体更新和维护该应用的题目列表、选项信息。(方便后期排序题目等功能完善)

questionContent 结构,注意区分 result 和 score,分别用于统计两种不同应用类型的结果。

[{"options": [{"result": "I", // 如果是测评类,则用 reslut 来保存答案属性"score": 1,    // 如果是得分类,则用 score 来设置本题分数"value": "A选项", //选项内容"key": "A"   //选项 key},{"result": "E", // 如果是测评类,则用 reslut 来保存答案属性"score": 0,"value": "B选项","key": "B"}],"title": "题目"}
]

d.评分结果表

用户提交答案后,会获得一定的回答评定,例如 ISTJ 之类的,评分结果表就是存储这些数据的表。

**不同类型的应用使用不同的字段。**测评类用 resultProp,得分类用 resultScoreRange。

测评类应用

resultProp 的结构,结果属性集合 JSON:

["I", "S", "T", "J"]

得分类应用

默认 resultScoreRange 字段的使用规则是“大于等于设定的分数则命中对应的 result”。

举一个得分类应用的例子:

  • resultName:知识大师
  • resultDesc:你真棒棒哦,知识掌握地非常出色!
  • resultIcon:预留字段,如果想界面好看点,可以给 result 设定图片
  • resultScoreRange:9

示例数据如下:

-- 得分 评分结果初始化
INSERT INTO demo.scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime, updateTime, isDelete, appId, userId) VALUES (17, '知识大师', '你真棒棒哦,知识掌握地非常出色!', null, null, 9, '2024-04-25 15:05:44', '2024-05-09 12:28:21', 0, 2, 1);
INSERT INTO demo.scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime, updateTime, isDelete, appId, userId) VALUES (18, '地理小能手!', '你对于地理知识了解得相当不错,但还有一些小地方需要加强哦!', null, null, 7, '2024-04-25 15:05:44', '2024-05-09 12:28:21', 0, 2, 1);
INSERT INTO demo.scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime, updateTime, isDelete, appId, userId) VALUES (19, '继续加油!', '还需努力哦', null, null, 0, '2024-04-25 15:05:44', '2024-05-09 12:28:21', 0, 2, 1);

e.用户答题记录表

其中字段存在冗余设计(某些字段在其他表中也有),通过冗余设计可以避免多表联查,提高查询性能。

为什么要有冗余字段?因为回答记录一旦设置,几乎不会更改,便于查询,不用联表,节约开发成本。

还有可能通过异步的方式、或者题目答案没提交(只答一半的时候),先临时保存回答记录。

resultId 可能为空,是因为 AI 分析策略不会从结果表中选取结果,没有 resultId。

choices 是 JSON 结构,题目选项的 key 数组:

["A", "B", "C"]

只存储选项的优点是,可以节约存储空间;但缺点是应用的题目如果发生修改,就对应不上了。

如果更严谨一些,可能要对应题目的 id(或者题目编号、题目的 key),比如:

{1: "A",2: "B"
}

三、后端项目初始化

a.打开后端模板项目

b.准备依赖

c.执行模板的初始化 SQL 脚本,运行模板

d.模板改造

  1. 改模块名(全局替换)

ctrl+shift+R修改(注意当前目录级别)

  1. 改包名(重构 + 全局替换)

  1. 移除不必要的模块(Elasticsearch、微信开发、表格处理、定时任务相关代码)。不移除也不影响项目运行。

在pop.xml中删除依赖,哪里报错删哪里;

将显而易见的相关代码删掉;

本项目去掉es搜索和wx

  1. 执行真实业务的库表初始化脚本,更改数据库等配置为自己的,尝试重新运行项目。

四、后端基础开发-增删改查

对着项目功能梳理,就是 5 张表最基础的增删改查,先不包含任何复杂的业务逻辑。

每个模块都要遵循如下的开发流程:

a. 数据访问层代码生成

MyBatisX 插件生成 mapper 和数据库实体类,生成后移动到项目对应位置。

首先(user表不选,因为模板已经实现)

然后

然后

最后

生成的代码移动到对应的模块目录(service层不移动,后面要生成业务逻辑);

b. 业务逻辑代码生成

使用万用模板的代码生成器工具(CodeGenerator)生成代码,包括:Controller、Service 接口和实现类、数据模型包装类和枚举类

首先修改指定生成参数和路径

根据路径会在项目的根目录下生成一个generator

如法炮制将其他表生成业务逻辑代码

获得代码

最后将代码移动到各个controler等层中

c.数据模型开发

编写数据模型包装类(请求类和视图类)、JSON 结构对应的类、枚举类。

JSON结构对应的类是为了保存答案属性、本题分数等信息;

枚举类是为了展示给用户可以选择创建哪些类试题;

VO层面;

包装类

包装类需要根据前端实际传递的请求参数或需要的响应结果自行修改。

例如对库表的增删改查中的question表中的字段,前端传递过来JSON格式的数据,后端需要转换为 (便于进行校验等操作),进入数据库前再转换为JSON格式。

JSON 结构对应的类可以利用 CodeGeex AI 编程助手生成,prompt 如下:

帮我把下列 json 结构转为使用 lombok 的 java 对象:{"options": [{"result": "I", // 如果是测评类,则用 reslut 来保存答案属性"score": 1,    // 如果是得分类,则用 score 来设置本题分数"value": "A选项", //选项内容"key": "A"   //选项 key},{"result": "E", // 如果是测评类,则用 reslut 来保存答案属性"score": 0,"value": "B选项","key": "B"}],"title": "题目"}

生成的结果如下:

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;import java.util.List;@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Question {private String title;private List<Option> options;@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic static class Option {private String result;private int score;private String value;private String key;}
}

修改加入对库表增删改查的包中

枚举类

关于枚举类,需要编写的枚举字段:

appType         tinyint  default 0                 not null comment '应用类型(0-得分类,1-测评类)',
scoringStrategy tinyint  default 0                 not null comment '评分策略(0-自定义,1-AI)',
reviewStatus    int      default 0                 not null comment '审核状态:0-待审核, 1-通过, 2-拒绝',

作为示例,提供审核状态枚举类代码:

package com.yupi.yudada.model.enums;import cn.hutool.core.util.ObjectUtil;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;/*** 审核状态枚举** @author <a href="https://github.com/liyupi">程序员鱼皮</a>* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>*/
public enum ReviewStatusEnum {REVIEWING("待审核", 0),PASS("通过", 1),REJECT("拒绝", 2);private final String text;private final int value;ReviewStatusEnum(String text, int value) {this.text = text;this.value = value;}/*** 根据 value 获取枚举** @param value* @return*/public static ReviewStatusEnum getEnumByValue(Integer value) {if (ObjectUtil.isEmpty(value)) {return null;}for (ReviewStatusEnum anEnum : ReviewStatusEnum.values()) {if (anEnum.value == value) {return anEnum;}}return null;}/*** 获取值列表** @return*/public static List<Integer> getValues() {return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());}public int getValue() {return value;}public String getText() {return text;}
}	

据此修改为具体的枚举类代码

package com.yupi.springbootinit.model.enums;import cn.hutool.core.util.ObjectUtil;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;/*** App应用类型枚举** @author <a href="https://github.com/liyupi">程序员鱼皮</a>* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>*/
public enum AppTypeEnum {SCORE("得分类", 0),TEST("测评类", 1);private final String text;private final int value;AppTypeEnum(String text, int value) {this.text = text;this.value = value;}/*** 根据 value 获取枚举** @param value* @return*/public static AppTypeEnum getEnumByValue(Integer value) {if (ObjectUtil.isEmpty(value)) {return null;}for (AppTypeEnum anEnum : AppTypeEnum.values()) {if (anEnum.value == value) {return anEnum;}}return null;}/*** 获取值列表** @return*/public static List<Integer> getValues() {return Arrays.stream(AppTypeEnum.values()).map(item -> item.value).collect(Collectors.toList());}public int getValue() {return value;}public String getText() {return text;}
}

VO视图

展示给用户

根据库表写VO

d. 对库表的增删改查

entity中是库表的字段映射出来的属性;

dto是对库表的增删改查;

对于前端确定哪些字段是必填的(必填的就从entity中取出);

dto

对于app应用表,AppAddRequest(创建应用请求)为:

package com.yupi.springbootinit.model.dto.app;
import lombok.Data;
import java.io.Serializable;/*** 创建应用请求** @author <a href="https://github.com/liyupi">程序员鱼皮</a>* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>*/
@Data
public class AppAddRequest implements Serializable {/*** 应用名*/private String appName;/*** 应用描述*/private String appDesc;/*** 应用图标*/private String appIcon;/*** 应用类型(0-得分类,1-测评类)*/private Integer appType;/*** 评分策略(0-自定义,1-AI)*/private Integer scoringStrategy;private static final long serialVersionUID = 1L;
}

其余的表也按这种方式进行。

五、后端基础开发-接口开发

六、后端基础开发-服务开发

编写 Service 接口和实现类,完善业务逻辑,比如数据校验之类的。

上传图片

七、后端核心业务开发

核心业务流程:

  1. 用户注册 => 用户登录
  2. 用户创建应用 => 创建题目(包括题目选项得分)=> 创建评分规则(评分方式和评分结果)
  3. 管理员管理应用,审核发布(或下架)应用
  4. 用户查看和检索应用列表,进入应用详情页,在线答题并提交回答
  5. 经过评分模块计算后,用户可查看本次评分结果

需要额外开发的能力:

  • 应用模块
    • 审核发布和下架应用(仅管理员可用)P0
  • 评分模块
    • 根据回答计算评分结果(多种评分策略)
      • 自定义规则评分 - 测评类 P0
      • 自定义规则评分 - 打分类 P0
  • 回答模块
    • 提交回答(创建)P0。需要额外调整,提交回答后就可以调用评分模块并更新回答表。

然后通过接口文档来完整测试一遍业务流程即可。

应用审核功能

审核是一个通用能力,每个表都可以按照如下方式开发。

首先因为审核是一个通用能力,因此将其功能抽取出来放在common包下的ReviewRequest;

其次模拟前端发送请求,这个请求中包含哪些参数,需要后端接收;

package com.yupi.springbootinit.common;import lombok.Data;import java.io.Serializable;/*** 审核请求*/
@Data
public class ReviewRequest implements Serializable {/*** id*/private Long id;/*** 状态(0-待审核,1-通过,2-拒绝)*/private Integer reviewStatus;/*** 审核信息*/private String reviewMessage;private static final long serialVersionUID = 1L;
}

然后因为是每个表都有审核功能因此以App表为例,在AppController写一个接口;

    /*** 应用审核** @param reviewRequest* @param request* @return*/@PostMapping("/review")@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)public BaseResponse<Boolean> doAppReview(@RequestBody ReviewRequest reviewRequest, HttpServletRequest request) {ThrowUtils.throwIf(reviewRequest == null, ErrorCode.PARAMS_ERROR);Long id = reviewRequest.getId();Integer reviewStatus = reviewRequest.getReviewStatus();// 校验ReviewStatusEnum reviewStatusEnum = ReviewStatusEnum.getEnumByValue(reviewStatus);if (id == null || reviewStatusEnum == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR);}// 判断是否存在App oldApp = appService.getById(id);ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);// 已是该状态if (oldApp.getReviewStatus().equals(reviewStatus)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "请勿重复审核");}// 更新审核状态User loginUser = userService.getLoginUser(request);App app = new App();app.setId(id);app.setReviewStatus(reviewStatus);app.setReviewMessage(reviewRequest.getReviewMessage());app.setReviewerId(loginUser.getId());app.setReviewTime(new Date());boolean result = appService.updateById(app);ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);return ResultUtils.success(true);}

逻辑并不复杂,所以就直接在 Controller 编写了。有时间的话,可以再移动到 Service 中。

评分模块实现

需求:针对不同的应用类别和评分策略,编写不同的实现逻辑。

核心实现方式:策略模式

策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装到独立的类中,使得它们可以相互替换。

在本项目的场景中,输入的参数是一致的(应用和用户的答案列表),并且每种实现逻辑区别较大,很适合使用策略模式。

所有代码放到 scoring 包中,模块化。

首先在根目录创建scoring包,模块化;

其次需要一个接口来定义需要传递什么参数和准备写实现类;

import com.yupi.springbootinit.model.entity.App;
import com.yupi.springbootinit.model.entity.UserAnswer;
import java.util.List;/*** 评分策略*/
public interface ScoringStrategy {/*** 执行评分评分* @param choice 用户答案* @param app 应用* @return 评分结果*/UserAnswer doScore(List<String> choice, App app)throws Exception;
}

因此其实现类(需要一个测评类评分和自定义评分)(也就是写评分算法逻辑);


/*** 自定义测评类应用评分策略*/
@ScoringStrategyConfig(appType = 1, scoringStrategy = 0)
public class CustomTestScoringStrategy implements ScoringStrategy {@Resourceprivate QuestionService questionService;@Resourceprivate ScoringResultService scoringResultService;@Overridepublic UserAnswer doScore(List<String> choices, App app) throws Exception {Long appId = app.getId();// 1. 根据 id 查询到题目和题目结果信息Question question = questionService.getOne(Wrappers.lambdaQuery(Question.class).eq(Question::getAppId, appId));List<ScoringResult> scoringResultList = scoringResultService.list(Wrappers.lambdaQuery(ScoringResult.class).eq(ScoringResult::getAppId, appId));// 2. 统计用户每个选择对应的属性个数,如 I = 10 个,E = 5 个// 初始化一个Map,用于存储每个选项的计数Map<String, Integer> optionCount = new HashMap<>();QuestionVO questionVO = QuestionVO.objToVo(question);List<QuestionContentDTO> questionContent = questionVO.getQuestionContent();// 遍历题目列表for (QuestionContentDTO questionContentDTO : questionContent) {// 遍历答案列表for (String answer : choices) {// 遍历题目中的选项for (QuestionContentDTO.Option option : questionContentDTO.getOptions()) {// 如果答案和选项的key匹配if (option.getKey().equals(answer)) {// 获取选项的result属性String result = option.getResult();// 如果result属性不在optionCount中,初始化为0if (!optionCount.containsKey(result)) {optionCount.put(result, 0);}// 在optionCount中增加计数optionCount.put(result, optionCount.get(result) + 1);}}}}// 3. 遍历每种评分结果,计算哪个结果的得分更高// 初始化最高分数和最高分数对应的评分结果int maxScore = 0;ScoringResult maxScoringResult = scoringResultList.get(0);// 遍历评分结果列表for (ScoringResult scoringResult : scoringResultList) {List<String> resultProp = JSONUtil.toList(scoringResult.getResultProp(), String.class);// 计算当前评分结果的分数,[I, E] => [10, 5] => 15int score = resultProp.stream().mapToInt(prop -> optionCount.getOrDefault(prop, 0)).sum();// 如果分数高于当前最高分数,更新最高分数和最高分数对应的评分结果if (score > maxScore) {maxScore = score;maxScoringResult = scoringResult;}}// 4. 构造返回值,填充答案对象的属性UserAnswer userAnswer = new UserAnswer();userAnswer.setAppId(appId);userAnswer.setAppType(app.getAppType());userAnswer.setScoringStrategy(app.getScoringStrategy());userAnswer.setChoices(JSONUtil.toJsonStr(choices));userAnswer.setResultId(maxScoringResult.getId());userAnswer.setResultName(maxScoringResult.getResultName());userAnswer.setResultDesc(maxScoringResult.getResultDesc());userAnswer.setResultPicture(maxScoringResult.getResultPicture());return userAnswer;}
}

全局执行器

为了简化外部调用,需要根据不同的应用类别和评分策略,选择对应的策略执行,因此需要一个全局执行器。

2 种实现方式:

1)编程式,在内部计算选用何种策略:

@Service
@Deprecated
public class ScoringStrategyContext {@Resourceprivate CustomScoreScoringStrategy customScoreScoringStrategy;@Resourceprivate CustomTestScoringStrategy customTestScoringStrategy;/*** 评分** @param choiceList* @param app* @return* @throws Exception*/public UserAnswer doScore(List<String> choiceList, App app) throws Exception {AppTypeEnum appTypeEnum = AppTypeEnum.getEnumByValue(app.getAppType());AppScoringStrategyEnum appScoringStrategyEnum = AppScoringStrategyEnum.getEnumByValue(app.getScoringStrategy());if (appTypeEnum == null || appScoringStrategyEnum == null) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}// 根据不同的应用类别和评分策略,选择对应的策略执行switch (appTypeEnum) {case SCORE:switch (appScoringStrategyEnum) {case CUSTOM:return customScoreScoringStrategy.doScore(choiceList, app);case AI:break;}break;case TEST:switch (appScoringStrategyEnum) {case CUSTOM:return customTestScoringStrategy.doScore(choiceList, app);case AI:break;}break;}throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}
}

优点是直观清晰,缺点是不利于扩展和维护。

2)声明式,在每个策略类中通过接口声明对应的生效条件,适合比较规律的策略选取场景。

接口:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ScoringStrategyConfig {int appType();int scoringStrategy();
}

给策略实现类补充注解:

@ScoringStrategyConfig(appType = 0, scoringStrategy = 0)

全局执行器:

@Service
public class ScoringStrategyExecutor {// 策略列表@Resourceprivate List<ScoringStrategy> scoringStrategyList;/*** 评分** @param choiceList* @param app* @return* @throws Exception*/public UserAnswer doScore(List<String> choiceList, App app) throws Exception {Integer appType = app.getAppType();Integer appScoringStrategy = app.getScoringStrategy();if (appType == null || appScoringStrategy == null) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}// 根据注解获取策略for (ScoringStrategy strategy : scoringStrategyList) {if (strategy.getClass().isAnnotationPresent(ScoringStrategyConfig.class)) {ScoringStrategyConfig scoringStrategyConfig = strategy.getClass().getAnnotation(ScoringStrategyConfig.class);if (scoringStrategyConfig.appType() == appType && scoringStrategyConfig.scoringStrategy() == appScoringStrategy) {return strategy.doScore(choiceList, app);}}}throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");}
}

因为用了 ScoringStrategyConfig 注解,所以这个实现类被加上了 component 注解,因此可以被spring 管理扫描到。 然后 @Resoure 注入的时候,会通过 ScoringStrategy 类型找到所有实现 ScoringStrategy 接口的实现类

回答模块

在 addUserAnswer 创建回答方法中补充 “调用评分模块并更新回答表”的逻辑即可。

控制应用可见范围

如果应用未过审,用户无法答题、并且无法通过列表查看到未过审的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/872071.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对话谢秀鹏:创元数字化内功是怎样练成的?

当今市场环境复杂多变&#xff0c;在日益激烈的竞争和快速变化的消费者需求面前产品生命周期越来越短&#xff0c;企业产品开发对市场需求的响应能力、开发效率及成本&#xff0c;成为决定其市场竞争力的关键因素之一。集成产品开发&#xff08;Integrated Product Development…

顺序表<数据结构 C 版>

目录 线性表 顺序表 动态顺序表类型 初始化 销毁 打印 检查空间是否充足&#xff08;扩容&#xff09; 尾部插入 头部插入 尾部删除 头部删除 指定位置插入 指定位置删除 查找数据 线性表 线性表是n个相同特性的数据元素组成的有限序列&#xff0c;其是一种广泛运…

基于jeecgboot-vue3的Flowable流程同时支持bpmn流程设计器与仿钉钉流程设计器(全网首创)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、新建流程的时候可以选择使用不同的流程设计器 2、选择bpmn流程设计器 3、选择仿钉钉流程设计器

EMQX开源版安装

一、EMQX是什么 EMQX 是一款开源的大规模分布式 MQTT 消息服务器&#xff0c;功能丰富&#xff0c;专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条&#xff0c;单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息&#xff0c;同时保证毫秒…

线程安全(六)AQS 的工作原理

目录 一、AQS 概述1.1 什么是 AQS?1.2 AQS 与 synchronized 区别:1.3 AQS 常见的实现类:二、AQS 的工作原理2.1 state 的用途:2.2 AQS 双向链表:2.3 ConditionObject 双向队列:2.4 总结:画图说明三、AQS 资源同步3.1 AQS 资源同步方式3.2 自定义同步器3.3 常见同步工具类…

顶顶通呼叫中心中间件-被叫路由、目的地绑定(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-被叫路由、目的地绑定(mod_cti基于FreeSWITCH) 1、配置分机 点击分机 -> 找到你需要设置的分机 ->呼叫路由设置为external 2、配置拨号方案 点击拨号方案 -> 输入目的地绑定 -> 点击添加 -> 点击brother conditi ->根据图中配置 co…

【实战系列】PostgreSQL 专栏,基于 PostgreSQL 16 版本

我的 PostgreSQL 专栏介绍及进度 20240715&#xff1a;目前整体进度已完成 85%&#xff0c;完成 16 万字&#xff0c;还有近 5 万字就截稿了。 (venv312) ➜ mypostgres git:(dev) sh scripts/word_statistics_pg_style.sh Filename …

无人机航电系统技术详解

一、系统概述 无人机航电系统&#xff08;Avionics System&#xff09;是无人机飞行与任务执行的核心部分&#xff0c;它集成了飞控系统、传感器、导航设备、通信设备等&#xff0c;为无人机提供了必要的飞行控制和任务执行能力。航电系统的设计和性能直接影响到无人机的安全性…

【JVM基础03】——组成-详细介绍下Java中的堆

目录 1- 引言&#xff1a;堆1-1 堆是什么&#xff1f;(What)1-2 为什么用堆&#xff1f;堆的作用 (Why) 2- ⭐核心&#xff1a;堆的原理&#xff08;How&#xff09;2-1 堆的划分2-2 Java 7 与 Java 8 的堆区别 3- 小结&#xff1a;3-1 详细介绍下Java的堆&#xff1f;3-2 JVM …

基于springboot与vue的旅游推荐系统与门票售卖

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

iPhone数据恢复:如何从iPhone恢复误删除的短信

来自iPhone的意外删除的短信可能很关键。它们可能是来自您常用应用程序、银行交易、付款收据的重要通知&#xff0c;也可能是来自朋友的重要文本、孩子的学校通知等。 如果您也从iPhone丢失了此类消息&#xff0c;我们在这里分享如何在没有备份以及有备份的情况下在iPhone上恢…

SQL Server详细使用教程(包含启动SQL server服务、建立数据库、建表的详细操作) 非常适合初学者

SQL Server详细使用教程(包含启动SQL server服务、建立数据库、建表的详细操作) 非常适合初学者 文章目录 目录 前言 一、启动SQL server服务的三种方法 1.不启动SQL server服务的影响 2.方法一&#xff1a;利用cmd启动SQL server服务 3.方法二&#xff1a;利用SQL Serv…

人工智能算法工程师(中级)课程14-神经网络的优化与设计之拟合问题及优化与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程14-神经网络的优化与设计之拟合问题及优化与代码详解。在机器学习和深度学习领域&#xff0c;模型的训练目标是找到一组参数&#xff0c;使得模型能够从训练数据中学习到有用的模式&am…

2023年高教杯数学建模2023B题解析(仅从代码角度出发)

前言 最近博主正在和队友准备九月的数学建模,在做往年的题目&#xff0c;博主主要是负责数据处理&#xff0c;运算以及可视化&#xff0c;这里分享一下自己部分的工作,相关题目以及下面所涉及的代码后续我会作为资源上传 问题求解 第一题 第一题的思路主要如下&#xff1a;…

【SpringBoot】SpringCache轻松启用Redis缓存

目录&#xff1a; 1.前言 2.常用注解 3.启用缓存 1.前言 Spring Cache是Spring提供的一种缓存抽象机制&#xff0c;旨在通过简化缓存操作来提高系统性能和响应速度。Spring Cache可以将方法的返回值缓存起来&#xff0c;当下次调用方法时如果从缓存中查询到了数据&#xf…

基于 jenkins 部署接口自动化测试项目!

引言 在现代软件开发过程中&#xff0c;自动化测试是保证代码质量的关键环节。通过自动化测试&#xff0c;可以快速发现和修复代码中的问题&#xff0c;从而提高开发效率和产品质量。而 Jenkins 作为一款开源的持续集成工具&#xff0c;可以帮助我们实现自动化测试的自动化部署…

自动化(二正)

Java接口自动化用到的技术栈 技术栈汇总&#xff1a; ①Java基础&#xff08;封装、反射、泛型、jdbc&#xff09; ②配置文件解析(properties) ③httpclient&#xff08;发送http请求&#xff09; ④fastjson、jsonpath处理数据的 ⑤testng自动化测试框架重点 ⑥allure测试报…

JMeter CSV 参数文件的使用教程

在 JMeter 测试过程中&#xff0c;合理地使用参数化技术是提高测试逼真度的关键步骤。本文将介绍如何通过 CSV 文件实现 JMeter 中的参数化。 设定 CSV 文件 首先&#xff0c;构建一个包含需要参数化数据的 CSV 文件。打开任何文本编辑器&#xff0c;输入希望模拟的用户数据&…

IGBT参数学习

IGBT&#xff08;绝缘栅双极晶体管(Insulated Gate Bipolar Transistor)&#xff09;的内部架构如下所示&#xff1a; IGBT是个单向的器件&#xff0c;电流只能朝一个方向流动&#xff0c;通常IGBT会并联一个续流二极管 IGBT型号&#xff1a;IKW40N120T2 IKW40N120T2 电路符号…

【代码规范】.train(False)和.eval()的相似性和区别

【代码规范】.train(False)和.eval()的相似性和区别 文章目录 一、.train(False) 和 .eval() 的功能二、.train(False) 和 .eval() 的区别2.1 .eval()2.2 .train(False)2.3 总结 三、.eval()更加规范 一、.train(False) 和 .eval() 的功能 .train(False) 和 .eval() 在功能上非…