feat:编写AI报告的接口、添加鼓励语句、修改举一反三从题库中获取相似题目的接口
This commit is contained in:
parent
068f16ffaa
commit
3e863506aa
@ -25,5 +25,9 @@
|
|||||||
<artifactId>common-validation</artifactId>
|
<artifactId>common-validation</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.seer.teach.common.enums.teacher;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum ReportTypeEnum {
|
||||||
|
|
||||||
|
DAILY(0, "今天"),
|
||||||
|
WEEKLY(1, "本周"),
|
||||||
|
MONTHLY(2, "本月");
|
||||||
|
|
||||||
|
private final Integer type;
|
||||||
|
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public static ReportTypeEnum fromType(Integer type) {
|
||||||
|
for (ReportTypeEnum e : values()) {
|
||||||
|
if (e.type.equals(type)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("非法报告类型type:" + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public Integer getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package com.seer.teach.common.utils;
|
package com.seer.teach.common.utils;
|
||||||
|
|
||||||
|
import com.seer.teach.common.enums.teacher.ReportTypeEnum;
|
||||||
|
|
||||||
import java.time.DayOfWeek;
|
import java.time.DayOfWeek;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -29,6 +31,7 @@ public class DateUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据创建时间计算距离当前时间相差多少天
|
* 根据创建时间计算距离当前时间相差多少天
|
||||||
|
*
|
||||||
* @param createTime 创建时间(LocalDateTime)
|
* @param createTime 创建时间(LocalDateTime)
|
||||||
* @return 相差的天数,例如:5
|
* @return 相差的天数,例如:5
|
||||||
*/
|
*/
|
||||||
@ -43,46 +46,54 @@ public class DateUtils {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ai报告获取开始时间
|
* 获取开始时间
|
||||||
* @param type 1-本周,2-本天
|
*
|
||||||
* @return 开始时间
|
* @param type 类型
|
||||||
|
* @return LocalDateTime对象
|
||||||
*/
|
*/
|
||||||
public static LocalDateTime getStartTime(Integer type) {
|
public static LocalDateTime getStartTime(ReportTypeEnum type) {
|
||||||
if (type == 1) {
|
if (type.getType().equals(0)) {
|
||||||
// 本天
|
// 今天
|
||||||
return LocalDate.now().atStartOfDay();
|
return LocalDate.now().atStartOfDay();
|
||||||
} else if(type == 0){
|
} else if (type.getType().equals(1)) {
|
||||||
// 本周
|
// 本周
|
||||||
return LocalDate.now().with(DayOfWeek.MONDAY).atStartOfDay();
|
return LocalDate.now().with(DayOfWeek.MONDAY).atStartOfDay();
|
||||||
|
} else if (type.getType().equals(2)) {
|
||||||
|
// 本月
|
||||||
|
return LocalDate.now().withDayOfMonth(1).atStartOfDay();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("type只能为0或1");
|
throw new IllegalArgumentException("type只能为0、1或2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将秒数转换为小时或者天
|
* 将秒数转换为小时或者天
|
||||||
|
*
|
||||||
* @param seconds 秒数
|
* @param seconds 秒数
|
||||||
* @param type 1-本周,2-本天
|
* @param type 0-今天,1-本周,2-本月
|
||||||
* @return 时间字符串
|
* @return 时间字符串
|
||||||
*/
|
*/
|
||||||
public static String formatSecondsToHourMinute(Integer seconds, Integer type) {
|
public static String formatSecondsToHourMinute(Integer seconds, ReportTypeEnum type) {
|
||||||
if (type == 1) {
|
if (type.getType().equals(0)) {
|
||||||
// 本天转换为小时
|
// 今天转换为小时和分钟
|
||||||
int hours = seconds / 3600;
|
return formatSecondsToHourAndMinute(seconds);
|
||||||
return hours + "小时";
|
} else if (type.getType().equals(1) || type.getType().equals(2)) {
|
||||||
} else if (type == 0){
|
// 本周和本月转换为天
|
||||||
// 本周转换为天
|
|
||||||
int days = seconds / 86400;
|
int days = seconds / 86400;
|
||||||
return days + "天";
|
return days + "天";
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("type只能为0或1");
|
throw new IllegalArgumentException("type只能为0、1或2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatSecondsToHourAndMinute(Integer seconds) {
|
public static String formatSecondsToHourAndMinute(Integer seconds) {
|
||||||
int hours = seconds / 3600;
|
int hours = seconds / 3600;
|
||||||
int minutes = (seconds % 3600) / 60;
|
int minutes = (seconds % 3600) / 60;
|
||||||
return hours + "小时" + minutes + "分";
|
if (hours > 0) {
|
||||||
|
return hours + "小时" + minutes + "分钟";
|
||||||
|
} else {
|
||||||
|
return minutes + "分钟";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.seer.teach.teacher.common.req;
|
package com.seer.teach.teacher.common.req;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import com.seer.teach.common.enums.teacher.ReportTypeEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -16,7 +16,7 @@ import jakarta.validation.constraints.NotNull;
|
|||||||
@Schema(description = "Ai报告请求实体类")
|
@Schema(description = "Ai报告请求实体类")
|
||||||
public class AiReportReq {
|
public class AiReportReq {
|
||||||
|
|
||||||
@Schema(description = "报告类型(0-本周,1-本天)")
|
@Schema(description ="报告类型(0-今天,1-本周,2-本月)")
|
||||||
@NotNull(message = "报告类型不能为空")
|
@NotNull(message = "报告类型不能为空")
|
||||||
private Integer type;
|
private ReportTypeEnum type;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package com.seer.teach.teacher.common.resp;
|
package com.seer.teach.teacher.common.resp;
|
||||||
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -32,6 +31,9 @@ public class AiReportResp {
|
|||||||
@Schema(description = "作业正确率")
|
@Schema(description = "作业正确率")
|
||||||
private String homeworkCorrectRate;
|
private String homeworkCorrectRate;
|
||||||
|
|
||||||
|
@Schema(description = "鼓励语句")
|
||||||
|
private String encourage;
|
||||||
|
|
||||||
@Schema(description = "科目正确率")
|
@Schema(description = "科目正确率")
|
||||||
private List<SubjectCorrectRateResp> subjectCorrectRate;
|
private List<SubjectCorrectRateResp> subjectCorrectRate;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.seer.teach.teacher.common.resp;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ToString
|
||||||
|
@Schema(description = "举一反三类似题目返回实体类")
|
||||||
|
public class SimilarQuestionResp {
|
||||||
|
|
||||||
|
@Schema(description = "题目Id")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "做题富文本数据")
|
||||||
|
private String questionHtml;
|
||||||
|
|
||||||
|
@Schema(description = "题型(1单选选择题 2多选选择题 4判断题 6填空题)")
|
||||||
|
private Integer type;
|
||||||
|
}
|
||||||
@ -7,8 +7,8 @@ import com.seer.teach.common.annotation.LogPrint;
|
|||||||
import com.seer.teach.teacher.common.req.AiReportReq;
|
import com.seer.teach.teacher.common.req.AiReportReq;
|
||||||
import com.seer.teach.teacher.common.resp.AiReportResp;
|
import com.seer.teach.teacher.common.resp.AiReportResp;
|
||||||
import com.seer.teach.teacher.service.ReportService;
|
import com.seer.teach.teacher.service.ReportService;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|||||||
@ -5,19 +5,22 @@ import com.seer.teach.common.ResultBean;
|
|||||||
import com.seer.teach.common.annotation.DecryptionAnnotation;
|
import com.seer.teach.common.annotation.DecryptionAnnotation;
|
||||||
import com.seer.teach.common.annotation.EncryptionAnnotation;
|
import com.seer.teach.common.annotation.EncryptionAnnotation;
|
||||||
import com.seer.teach.common.annotation.LogPrint;
|
import com.seer.teach.common.annotation.LogPrint;
|
||||||
import com.seer.teach.common.enums.AiScenarioCodeEnum;
|
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
||||||
|
import com.seer.teach.teacher.common.resp.SimilarQuestionResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
||||||
import com.seer.teach.teacher.module.entity.IdEntity;
|
import com.seer.teach.teacher.module.entity.IdEntity;
|
||||||
import com.seer.teach.teacher.service.WrongQuestionService;
|
import com.seer.teach.teacher.service.WrongQuestionService;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -37,7 +40,7 @@ public class WrongQuestionController {
|
|||||||
@LogPrint
|
@LogPrint
|
||||||
@Operation(summary = "获取各科目的错题数量")
|
@Operation(summary = "获取各科目的错题数量")
|
||||||
public ResultBean<WrongQuestionResp> getWrongQuestionBook(@RequestBody @Validated WrongQuestionBookReq params) {
|
public ResultBean<WrongQuestionResp> getWrongQuestionBook(@RequestBody @Validated WrongQuestionBookReq params) {
|
||||||
WrongQuestionResp wrongQuestion = wrongQuestionService.getWrongQuestion(params);
|
WrongQuestionResp wrongQuestion = wrongQuestionService.getWrongQuestionCount(params);
|
||||||
return ResultBean.success(wrongQuestion);
|
return ResultBean.success(wrongQuestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,13 +60,12 @@ public class WrongQuestionController {
|
|||||||
return ResultBean.success();
|
return ResultBean.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/generateQuestionsByQuestionId")
|
@PostMapping("/getSimilarQuestions")
|
||||||
@LogPrint
|
@LogPrint
|
||||||
@Operation(summary = "举一反三")
|
@Operation(summary = "举一反三获取相似题目")
|
||||||
public ResultBean<List<String>> generateSimilarQuestions(@RequestBody @Validated IdEntity params) {
|
public ResultBean<List<SimilarQuestionResp>> getSimilarQuestions(@RequestBody @Validated IdEntity params) {
|
||||||
List<String> questions = wrongQuestionService.generateSimilarQuestions(params.getId(),
|
List<SimilarQuestionResp> result = wrongQuestionService.getSimilarQuestionsByQuestionId(params.getId());
|
||||||
AiScenarioCodeEnum.LEARN_BY_ANALOGY_GENERATE_QUESTIONS, 3);
|
return ResultBean.success(result);
|
||||||
return ResultBean.success(questions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ spring:
|
|||||||
import:
|
import:
|
||||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
|
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
|
||||||
- optional:nacos:shared-database.yaml
|
- optional:nacos:shared-database.yaml
|
||||||
|
- optional:nacos:shared-minio.yaml
|
||||||
- optional:nacos:shared-redis.yaml
|
- optional:nacos:shared-redis.yaml
|
||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
|
|||||||
@ -12,5 +12,6 @@ public interface AiReportConvert {
|
|||||||
|
|
||||||
AiReportConvert INSTANCE = Mappers.getMapper(AiReportConvert.class);
|
AiReportConvert INSTANCE = Mappers.getMapper(AiReportConvert.class);
|
||||||
|
|
||||||
AiReportResp toAiReportResp(String studyTime, String readingTime, String practiceCount, String dailyStudyTime, String homeworkCorrectRate, List<SubjectCorrectRateResp> subjectCorrectRate);
|
AiReportResp toAiReportResp(String studyTime, String readingTime, String practiceCount, String encourage,
|
||||||
|
String dailyStudyTime, String homeworkCorrectRate, List<SubjectCorrectRateResp> subjectCorrectRate);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
package com.seer.teach.teacher.convert;
|
package com.seer.teach.teacher.convert;
|
||||||
|
|
||||||
|
import com.seer.teach.teacher.common.resp.SimilarQuestionResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
||||||
|
import com.seer.teach.teacher.module.entity.BankQuestionsEntity;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface WrongQuestionBookConvert {
|
public interface WrongQuestionBookConvert {
|
||||||
@ -13,4 +17,8 @@ public interface WrongQuestionBookConvert {
|
|||||||
|
|
||||||
@Mapping(source = "totalWrongCount",target = "count")
|
@Mapping(source = "totalWrongCount",target = "count")
|
||||||
WrongQuestionResp toWrongQuestion(String totalWrongCount);
|
WrongQuestionResp toWrongQuestion(String totalWrongCount);
|
||||||
|
|
||||||
|
SimilarQuestionResp toSimilarQuestion(BankQuestionsEntity bankQuestionsEntity);
|
||||||
|
|
||||||
|
List<SimilarQuestionResp> toSimilarQuestionList(List<BankQuestionsEntity> bankQuestionsEntity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.seer.teach.common.PageListBean;
|
|||||||
import com.seer.teach.common.enums.AiScenarioCodeEnum;
|
import com.seer.teach.common.enums.AiScenarioCodeEnum;
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
||||||
|
import com.seer.teach.teacher.common.resp.SimilarQuestionResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
||||||
import com.seer.teach.teacher.module.entity.IdEntity;
|
import com.seer.teach.teacher.module.entity.IdEntity;
|
||||||
@ -24,7 +25,7 @@ public interface WrongQuestionService {
|
|||||||
* @param params 请求参数
|
* @param params 请求参数
|
||||||
* @return 错题本
|
* @return 错题本
|
||||||
*/
|
*/
|
||||||
WrongQuestionResp getWrongQuestion(WrongQuestionBookReq params);
|
WrongQuestionResp getWrongQuestionCount(WrongQuestionBookReq params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取错题列表
|
* 获取错题列表
|
||||||
@ -51,4 +52,11 @@ public interface WrongQuestionService {
|
|||||||
* @return 举一反三
|
* @return 举一反三
|
||||||
*/
|
*/
|
||||||
List<String> generateSimilarQuestions(Integer id, AiScenarioCodeEnum scenarioCode, Integer count);
|
List<String> generateSimilarQuestions(Integer id, AiScenarioCodeEnum scenarioCode, Integer count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举一反三获取相似题目
|
||||||
|
* @param id 题目Id
|
||||||
|
* @return 相似题目
|
||||||
|
*/
|
||||||
|
List<SimilarQuestionResp> getSimilarQuestionsByQuestionId(Integer id);
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.seer.teach.common.entity.BaseEntity;
|
import com.seer.teach.common.entity.BaseEntity;
|
||||||
import com.seer.teach.common.enums.MakeQuestionTypeEnums;
|
import com.seer.teach.common.enums.MakeQuestionTypeEnums;
|
||||||
|
import com.seer.teach.common.enums.teacher.ReportTypeEnum;
|
||||||
import com.seer.teach.common.utils.DateUtils;
|
import com.seer.teach.common.utils.DateUtils;
|
||||||
import com.seer.teach.iot.api.UserDeviceServiceApi;
|
import com.seer.teach.iot.api.UserDeviceServiceApi;
|
||||||
import com.seer.teach.teacher.common.dto.ReportQuestionDTO;
|
import com.seer.teach.teacher.common.dto.ReportQuestionDTO;
|
||||||
@ -18,18 +19,18 @@ import com.seer.teach.teacher.convert.QuestionConvert;
|
|||||||
import com.seer.teach.teacher.convert.ReportConvert;
|
import com.seer.teach.teacher.convert.ReportConvert;
|
||||||
import com.seer.teach.teacher.module.entity.*;
|
import com.seer.teach.teacher.module.entity.*;
|
||||||
import com.seer.teach.teacher.service.*;
|
import com.seer.teach.teacher.service.*;
|
||||||
|
import com.seer.teach.teacher.util.RateUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.*;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -47,6 +48,20 @@ public class ReportServiceImpl implements ReportService {
|
|||||||
|
|
||||||
private static final String PERCENT_SIGN = "%";
|
private static final String PERCENT_SIGN = "%";
|
||||||
|
|
||||||
|
private static final String FIRST_STUDY = "你近期首次学习,期待你的持续进步!";
|
||||||
|
|
||||||
|
private static final String NOT_STUDY = "你%s还没有学习哦! 快去学习吧~";
|
||||||
|
|
||||||
|
private static final String VERY_GREAT = "很棒哦! %s你的作业正确率提升了%d%%,继续保持优秀表现~";
|
||||||
|
|
||||||
|
private static final String NOT_BAD = "不错哦! %s你的作业正确率提升了%d%%,请继续加油~";
|
||||||
|
|
||||||
|
private static final String COME_ON = "加油哦! %s你的作业正确率有提升,再接再厉~";
|
||||||
|
|
||||||
|
private static final String CONTINUE_STUDYING = "继续努力哦! %s你的作业正确率还有提升空间~";
|
||||||
|
|
||||||
|
private static final String DON_LOSE_HEART = "别灰心哦!%s你的作业正确率有所下降,坚持学习一定会进步的~";
|
||||||
|
|
||||||
private final EvaluationTasksService evaluationTasksService;
|
private final EvaluationTasksService evaluationTasksService;
|
||||||
|
|
||||||
private final UserDeviceServiceApi userDeviceServiceApi;
|
private final UserDeviceServiceApi userDeviceServiceApi;
|
||||||
@ -161,16 +176,144 @@ public class ReportServiceImpl implements ReportService {
|
|||||||
.reduce(0, Integer::sum);
|
.reduce(0, Integer::sum);
|
||||||
String readingTime = DateUtils.formatSecondsToHourAndMinute(readingSeconds);
|
String readingTime = DateUtils.formatSecondsToHourAndMinute(readingSeconds);
|
||||||
|
|
||||||
|
// 鼓励语句
|
||||||
|
String encourage = generateEncouragement(params.getType(), homeworkCorrectRate, userId);
|
||||||
|
|
||||||
// 日均学习时间
|
// 日均学习时间
|
||||||
long days = ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate()) + 1;
|
long days = ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate()) + 1;
|
||||||
String dailyStudyTime = (studySeconds / 60 / days) + MINUTE;
|
String dailyStudyTime = (studySeconds / 60 / days) + MINUTE;
|
||||||
|
|
||||||
// 返回最终AI报告结果
|
// 返回最终AI报告结果
|
||||||
return AiReportConvert.INSTANCE.toAiReportResp(
|
return AiReportConvert.INSTANCE.toAiReportResp(studyTime, readingTime, practiceCount, encourage,
|
||||||
studyTime, readingTime, practiceCount, dailyStudyTime, homeworkCorrectRate, subjectRates
|
dailyStudyTime, homeworkCorrectRate, subjectRates);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼓励语句
|
||||||
|
*
|
||||||
|
* @param type 时间类型
|
||||||
|
* @param correctRate 当前正确率
|
||||||
|
* @return 鼓励语
|
||||||
|
*/
|
||||||
|
private String generateEncouragement(ReportTypeEnum type, String correctRate, Integer userId) {
|
||||||
|
// 时间段
|
||||||
|
String timePeriod = type.getDesc();
|
||||||
|
// 当前正确率
|
||||||
|
BigDecimal currentRate = RateUtils.percentStrToRate(correctRate);
|
||||||
|
|
||||||
|
// 如果正确率为0%,说明没有做题,返回鼓励语
|
||||||
|
if (currentRate.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
return String.format(NOT_STUDY, timePeriod);
|
||||||
|
}
|
||||||
|
// 获取历史正确率进行对比
|
||||||
|
Optional<BigDecimal> historyRateOpt = getHistoricalCorrectRate(userId, type);
|
||||||
|
// 如果没有历史学习记录,返回提示语
|
||||||
|
if (!historyRateOpt.isPresent()) {
|
||||||
|
return FIRST_STUDY;
|
||||||
|
}
|
||||||
|
// 获取提升百分比
|
||||||
|
BigDecimal historyRate = historyRateOpt.get();
|
||||||
|
BigDecimal improvementPercent = RateUtils.calculateImprovementPercent(currentRate, historyRate);
|
||||||
|
int improve = improvementPercent.intValue();
|
||||||
|
if (improve >= 20) {
|
||||||
|
return String.format(VERY_GREAT, timePeriod, improve);
|
||||||
|
} else if (improve >= 10) {
|
||||||
|
return String.format(NOT_BAD, timePeriod, improve);
|
||||||
|
} else if (improve >= 5) {
|
||||||
|
return String.format(COME_ON, timePeriod);
|
||||||
|
} else if (improve > -5) {
|
||||||
|
return String.format(CONTINUE_STUDYING, timePeriod);
|
||||||
|
} else {
|
||||||
|
return String.format(DON_LOSE_HEART, timePeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史作业正确率
|
||||||
|
*
|
||||||
|
* @param userId 用户Id
|
||||||
|
* @param type 时间类型
|
||||||
|
* @return 历史作业正确率
|
||||||
|
*/
|
||||||
|
private Optional<BigDecimal> getHistoricalCorrectRate(Integer userId, ReportTypeEnum type) {
|
||||||
|
// 获取当前查询时间段的开始时间
|
||||||
|
LocalDateTime currentTimeStart = DateUtils.getStartTime(type);
|
||||||
|
|
||||||
|
// 查询当前时间段之前的学习记录,按时间倒序排列
|
||||||
|
List<LessonsEntity> previousLessons = lessonsService.lambdaQuery()
|
||||||
|
.eq(LessonsEntity::getUserId, userId)
|
||||||
|
.lt(LessonsEntity::getCreateTime, currentTimeStart)
|
||||||
|
.orderByDesc(LessonsEntity::getCreateTime)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
List<EvaluationTasksEntity> previousEvaluations = evaluationTasksService.lambdaQuery()
|
||||||
|
.eq(EvaluationTasksEntity::getUserId, userId)
|
||||||
|
.lt(EvaluationTasksEntity::getCreateTime, currentTimeStart)
|
||||||
|
.orderByDesc(EvaluationTasksEntity::getCreateTime)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 合并并按时间倒序排序,找出最近一次学习会话的时间范围
|
||||||
|
List<LocalDateTime> allCreateTimes = Stream.concat(
|
||||||
|
previousLessons.stream().map(LessonsEntity::getCreateTime),
|
||||||
|
previousEvaluations.stream().map(EvaluationTasksEntity::getCreateTime))
|
||||||
|
.sorted(Collections.reverseOrder()).collect(Collectors.toList());
|
||||||
|
if (allCreateTimes.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最近一次学习的时间
|
||||||
|
LocalDateTime latestLearningTime = allCreateTimes.get(0);
|
||||||
|
|
||||||
|
// 根据报告类型确定历史时间段
|
||||||
|
LocalDateTime periodStart, periodEnd;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case DAILY:
|
||||||
|
// 查找最近一次学习的那一天的所有学习记录
|
||||||
|
LocalDate learningDate = latestLearningTime.toLocalDate();
|
||||||
|
periodStart = learningDate.atStartOfDay();
|
||||||
|
periodEnd = learningDate.atTime(LocalTime.MAX);
|
||||||
|
break;
|
||||||
|
case WEEKLY:
|
||||||
|
// 查找最近一次学习的那周
|
||||||
|
LocalDate learningWeekStart = latestLearningTime.toLocalDate().with(DayOfWeek.MONDAY);
|
||||||
|
LocalDate learningWeekEnd = learningWeekStart.plusDays(6);
|
||||||
|
periodStart = learningWeekStart.atStartOfDay();
|
||||||
|
periodEnd = learningWeekEnd.atTime(LocalTime.MAX);
|
||||||
|
break;
|
||||||
|
case MONTHLY:
|
||||||
|
// 查找最近一次学习的那个月
|
||||||
|
YearMonth learningMonth = YearMonth.from(latestLearningTime);
|
||||||
|
periodStart = learningMonth.atDay(1).atStartOfDay();
|
||||||
|
periodEnd = learningMonth.atEndOfMonth().atTime(LocalTime.MAX);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取该历史时期的完整学习数据
|
||||||
|
List<LessonsEntity> lessons = lessonsService.lambdaQuery()
|
||||||
|
.eq(LessonsEntity::getUserId, userId)
|
||||||
|
.ge(LessonsEntity::getCreateTime, periodStart)
|
||||||
|
.le(LessonsEntity::getCreateTime, periodEnd)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
List<EvaluationTasksEntity> evaluations = evaluationTasksService.lambdaQuery()
|
||||||
|
.eq(EvaluationTasksEntity::getUserId, userId)
|
||||||
|
.ge(EvaluationTasksEntity::getCreateTime, periodStart)
|
||||||
|
.le(EvaluationTasksEntity::getCreateTime, periodEnd)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
int totalQ = getTotalQuestions(lessons) + getTotalQuestions(evaluations);
|
||||||
|
if (totalQ == 0) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalCorrect = calculateTotalCorrect(lessons).add(calculateTotalCorrect(evaluations));
|
||||||
|
return Optional.of(totalCorrect.divide(BigDecimal.valueOf(totalQ), 2, RoundingMode.HALF_UP));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定用户的全部题目数据(包括测评和计划中的题目)。
|
* 获取指定用户的全部题目数据(包括测评和计划中的题目)。
|
||||||
*
|
*
|
||||||
@ -535,6 +678,11 @@ public class ReportServiceImpl implements ReportService {
|
|||||||
* @return 科目ID到名称的映射
|
* @return 科目ID到名称的映射
|
||||||
*/
|
*/
|
||||||
private Map<Integer, String> getSubjectsByIds(Set<Integer> ids) {
|
private Map<Integer, String> getSubjectsByIds(Set<Integer> ids) {
|
||||||
|
log.info("获取科目名称,ID集合: {}", ids);
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
log.info("ID集合为空,返回空map");
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
Map<Integer, String> map = new HashMap<>();
|
Map<Integer, String> map = new HashMap<>();
|
||||||
for (SubjectsEntity s : subjectsService.listByIds(new ArrayList<>(ids))) {
|
for (SubjectsEntity s : subjectsService.listByIds(new ArrayList<>(ids))) {
|
||||||
map.put(s.getId(), s.getSubject());
|
map.put(s.getId(), s.getSubject());
|
||||||
|
|||||||
@ -7,11 +7,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.seer.teach.common.PageListBean;
|
import com.seer.teach.common.PageListBean;
|
||||||
import com.seer.teach.common.enums.*;
|
import com.seer.teach.common.enums.AiScenarioCodeEnum;
|
||||||
|
import com.seer.teach.common.enums.BankQuestionStatusEnum;
|
||||||
|
import com.seer.teach.common.enums.QuestionResultEnums;
|
||||||
|
import com.seer.teach.common.enums.ResultCodeEnum;
|
||||||
import com.seer.teach.common.utils.AssertUtils;
|
import com.seer.teach.common.utils.AssertUtils;
|
||||||
import com.seer.teach.common.utils.PageConverterUtils;
|
import com.seer.teach.common.utils.PageConverterUtils;
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
|
||||||
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
|
||||||
|
import com.seer.teach.teacher.common.resp.SimilarQuestionResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
|
||||||
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
|
||||||
import com.seer.teach.teacher.convert.WrongQuestionBookConvert;
|
import com.seer.teach.teacher.convert.WrongQuestionBookConvert;
|
||||||
@ -63,7 +67,7 @@ public class WrongQuestionServiceImpl implements WrongQuestionService {
|
|||||||
* @return 返回封装了总错题数的响应对象
|
* @return 返回封装了总错题数的响应对象
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public WrongQuestionResp getWrongQuestion(WrongQuestionBookReq params) {
|
public WrongQuestionResp getWrongQuestionCount(WrongQuestionBookReq params) {
|
||||||
Integer userId = StpUtil.getLoginIdAsInt();
|
Integer userId = StpUtil.getLoginIdAsInt();
|
||||||
log.info("开始获取用户[{}]测评错题本数据", userId);
|
log.info("开始获取用户[{}]测评错题本数据", userId);
|
||||||
|
|
||||||
@ -243,6 +247,40 @@ public class WrongQuestionServiceImpl implements WrongQuestionService {
|
|||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举一反三获取相似题目
|
||||||
|
*
|
||||||
|
* @param id 题目Id
|
||||||
|
* @return 相似题目列表
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SimilarQuestionResp> getSimilarQuestionsByQuestionId(Integer id) {
|
||||||
|
// 根据题目Id获取题目
|
||||||
|
BankQuestionsEntity questionBank = bankQuestionsService.getById(id);
|
||||||
|
AssertUtils.notNull(questionBank, ResultCodeEnum.QUESTION_NOT_EXIST);
|
||||||
|
// 获取知识点Id
|
||||||
|
Integer knowId = questionBank.getKnowId();
|
||||||
|
// 查询相同知识点的题目,排除原题目
|
||||||
|
List<BankQuestionsEntity> questionsList = bankQuestionsService.lambdaQuery()
|
||||||
|
.eq(BankQuestionsEntity::getKnowId, knowId)
|
||||||
|
.ne(BankQuestionsEntity::getId, id)
|
||||||
|
.list();
|
||||||
|
List<SimilarQuestionResp> similarQuestionList = WrongQuestionBookConvert.INSTANCE.toSimilarQuestionList(questionsList);
|
||||||
|
// 过滤掉questionHtml为空的题目
|
||||||
|
List<SimilarQuestionResp> result = similarQuestionList.stream()
|
||||||
|
.filter(question -> question.getQuestionHtml() != null && !question.getQuestionHtml().isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
// 将相似题目打乱顺序排序
|
||||||
|
Collections.shuffle(result);
|
||||||
|
// 返回10道知识点相同的题目
|
||||||
|
return result.stream()
|
||||||
|
.limit(10)
|
||||||
|
// 重新编号(序号1-10)
|
||||||
|
.peek((question) -> question.setId(result.indexOf(question) + 1))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步批量插入生成的相似题目到题库
|
* 异步批量插入生成的相似题目到题库
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.seer.teach.teacher.util;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class RateUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 百分比字符串转正确率("62%" -> 0.62)
|
||||||
|
*
|
||||||
|
* @param percent 百分比字符串
|
||||||
|
* @return 正确率
|
||||||
|
*/
|
||||||
|
public static BigDecimal percentStrToRate(String percent) {
|
||||||
|
if (percent == null || percent.trim().isEmpty() || "0%".equals(percent)) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return new BigDecimal(percent.replace("%", ""))
|
||||||
|
.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算提升百分比
|
||||||
|
*
|
||||||
|
* @param current 当前正确率
|
||||||
|
* @param history 历史正确率
|
||||||
|
* @return 提升百分比
|
||||||
|
*/
|
||||||
|
public static BigDecimal calculateImprovementPercent(BigDecimal current, BigDecimal history) {
|
||||||
|
if (history.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
// 历史为 0,认为是首次学习
|
||||||
|
return BigDecimal.valueOf(100);
|
||||||
|
}
|
||||||
|
BigDecimal improve = current.subtract(history)
|
||||||
|
.divide(history, 2, RoundingMode.HALF_UP)
|
||||||
|
.multiply(BigDecimal.valueOf(100))
|
||||||
|
.setScale(0, RoundingMode.HALF_UP);
|
||||||
|
// 限制范围 [-100, 100]
|
||||||
|
return improve.max(BigDecimal.valueOf(-100))
|
||||||
|
.min(BigDecimal.valueOf(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -188,11 +188,10 @@ public class WrongQuestionUtils {
|
|||||||
String processed = originalHtml
|
String processed = originalHtml
|
||||||
.replaceAll("<!--\\s*题目解析\\s*-->\\s*<p><b>解析:</b>", "<p>解析:")
|
.replaceAll("<!--\\s*题目解析\\s*-->\\s*<p><b>解析:</b>", "<p>解析:")
|
||||||
.replaceAll("<p>\\s*<b>答案:</b>\\s*", "<p>【正确答案】 ");
|
.replaceAll("<p>\\s*<b>答案:</b>\\s*", "<p>【正确答案】 ");
|
||||||
String redX = " ❌";
|
// String redX = " ❌";
|
||||||
// 构建学生答案段
|
// 构建学生答案段
|
||||||
String studentSegment = "<p>【你的答案】 "
|
String studentSegment = "<p>【你的答案】 "
|
||||||
+ (safeStudentAnswer.isEmpty() ? "" : safeStudentAnswer)
|
+ (safeStudentAnswer.isEmpty() ? "" : safeStudentAnswer)
|
||||||
+ redX
|
|
||||||
+ "</p>\n\n";
|
+ "</p>\n\n";
|
||||||
// 插入到正确答案之前
|
// 插入到正确答案之前
|
||||||
processed = processed.replaceFirst("<p>【正确答案】", studentSegment + "<p>【正确答案】");
|
processed = processed.replaceFirst("<p>【正确答案】", studentSegment + "<p>【正确答案】");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user