Commit 2433d18b authored by 陈立彬's avatar 陈立彬

AI对练、AI问答扩展

parent ee350b41
......@@ -131,12 +131,18 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT 支持 -->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt</artifactId>-->
<!-- <version>0.9.1</version>-->
<!-- </dependency>-->
<!-- Quartz Scheduler -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
<build>
......
......@@ -4,9 +4,11 @@ import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
@EnableKnife4j
@EnableScheduling
public class ElleaiApplication {
public static void main(String[] args) {
......
package cn.breeze.elleai.application.dto.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
public class UserChatCompletionHistoryListMobileDto implements Serializable {
@Schema(description = "今日会话列表")
@JsonProperty("today_list")
private List<UserChatCompletionMobileDto> todayList;
@Schema(description = "更早会话列表")
@JsonProperty("early_list")
private List<UserChatCompletionMobileDto> earlyList;
}
......@@ -24,5 +24,5 @@ public class UserChatCompletionMobileDto implements Serializable {
*/
@Schema(description = "创建时间")
@JsonProperty("create_time")
private Date createTime;
private Long createTime;
}
......@@ -46,6 +46,13 @@ public class WikiMobileDto implements Serializable {
@JsonProperty("wiki_category_name")
private String wikiCategoryName;
/**
* 场景分类ID
*/
@Schema(description = "场景分类ID")
@JsonProperty("scene_category_id")
private String sceneCategoryId;
/**
* 场景分类名称
*/
......
......@@ -86,7 +86,7 @@ public class AiPlatformExtensionService {
* @param avgScore
* @param qaResultList
*/
public String run4TotalEvaluate(String sessionId, String userId, Float avgScore, List qaResultList) {
public String run4TotalEvaluate(String sessionId, String userId, Float avgScore, String qaResultList) {
Map<String, Object> inputs = new HashMap<>();
inputs.put("scene", "total_evaluate");
inputs.put("avg_score", avgScore);
......
package cn.breeze.elleai.application.service;
import cn.breeze.elleai.application.dto.ApiResponse;
import cn.breeze.elleai.application.dto.PageResult;
import cn.breeze.elleai.application.dto.inner.AiSingleEvaluateResultDto;
import cn.breeze.elleai.application.dto.inner.ExamineBusinessCacheDto;
......@@ -18,23 +17,19 @@ import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.mybatisflex.core.paginate.Page;
import io.micrometer.common.util.StringUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
......@@ -1183,8 +1178,29 @@ public class AppExamineService {
businessCache.setRecordId(recordId);
redisTemplate.opsForValue().set(examBusinessKey, JSONObject.toJSONString(businessCache), 1, TimeUnit.DAYS);
// 最后一题答题,执行AI总点评
// 最后一题答题,完成考试,执行AI总点评
if(!businessCache.isHasNext()) {
this.completeExamine(userPrincipal, examineId, businessNo);
}
}
/**
* 完成考试,更新最终考试结果
* @param userPrincipal
* @param examineId
* @param businessNo
*/
public void completeExamine(UserPrincipal userPrincipal, Integer examineId, String businessNo) {
String userId = userPrincipal.getUserId();
// 获取缓存考试会话信息
ExamineBusinessCacheDto businessCache = getExamBusinessCache(businessNo);
if(Objects.isNull(businessCache)) {
throw new InternalException(-1, "获取不到对练信息");
}
Integer recordId = businessCache.getRecordId();
// 获取答题结果
ExamineDetailRecordRequestModel requestModel = new ExamineDetailRecordRequestModel();
......@@ -1195,8 +1211,23 @@ public class AppExamineService {
double totalScore = detailRecordList.stream().filter(v -> Objects.nonNull(v.getScore())).mapToDouble(ExamineDetailRecordResponseModel::getScore).sum();
Double avgScore = totalScore / detailRecordList.size();
List<Integer> qaIdList = detailRecordList.stream().map(ExamineDetailRecordResponseModel::getQaId).collect(Collectors.toList());
List<ExamineQaResponseModel> examineQaList = examineService.examineQaList(qaIdList);
Map<Integer, ExamineQaResponseModel> qaMap = examineQaList.stream().collect(toMap(ExamineQaResponseModel::getId, Function.identity()));
String qaResultStr = "";
for(int i = 0; i < detailRecordList.size(); i++) {
ExamineDetailRecordResponseModel detail = detailRecordList.get(i);
ExamineQaResponseModel qa = qaMap.get(detail.getQaId());
qaResultStr += "问题 " + (i + 1) + ": " + qa.getQuestion() + "\n";
qaResultStr += "得分:" + detail.getScore() + "\n";
qaResultStr += "评分说明:" + detail.getEvaluation() + "\n";
qaResultStr += "----------------------------------------\n";
}
// 执行综合点评
String overallEvaluation = extensionService.run4TotalEvaluate(businessNo, userId, null, null);
String overallEvaluation = extensionService.run4TotalEvaluate(businessNo, userId, avgScore.floatValue(), qaResultStr);
// 更新答题数量
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setId(recordId);
......@@ -1206,64 +1237,47 @@ public class AppExamineService {
examineService.saveExamineRecord(saveModel);
}
}
}
/**
* 获取下一题
* 获取考试最终结果
* @param userPrincipal
* @param examineId 对练ID
* @param businessNo 考试流水号
* @return
*/
public ExamineFinalResultDto getExamineFinalResult(UserPrincipal userPrincipal, Integer examineId, String businessNo) {
String userId = userPrincipal.getUserId();
ExamineFinalResultDto result = new ExamineFinalResultDto();
public ExamineRecordMobileDto getExamineFinalResult(UserPrincipal userPrincipal, Integer examineId, String businessNo) {
// 获取考试记录信息
ExamineRecordResponseModel recordModel = examineService.examineRecordDetail(businessNo);
if(Objects.nonNull(recordModel)) {
Integer recordId = recordModel.getId();
return this.examineRecordMobileDetail(recordId);
}
return null;
}
/**
* 退出考试,清除考试记录
* @param userPrincipal
* @param examineId
* @param businessNo
* @return
*/
public void exitExamine(UserPrincipal userPrincipal, Integer examineId, String businessNo) {
// 获取缓存考试会话信息
String examBusinessKey = String.format(EXAM_BUSINESS_REDIS_KEY, businessNo);
ExamineBusinessCacheDto businessCache = getExamBusinessCache(businessNo);
if(Objects.isNull(businessCache)) {
throw new InternalException(-1, "获取不到考试流水信息");
}
// 当前出题
Integer currentQuestionId = businessCache.getCurrentQuestionId();
if(Objects.isNull(currentQuestionId)) {
throw new InternalException(-1, "获取上一题信息失败");
}
// 获取下一题
ExamineQaResponseModel nextQa = null;
boolean hasNext = false;
List<ExamineQaResponseModel> examineQaList = examineService.examineQaList(examineId);
if(CollectionUtil.isNotEmpty(examineQaList)) {
OptionalInt indexOpt = IntStream.range(0, examineQaList.size())
.filter(i -> examineQaList.get(i).getId() == currentQuestionId)
.findFirst();
if (indexOpt.isPresent()) {
int index = indexOpt.getAsInt();
nextQa = index + 1 < examineQaList.size() ? examineQaList.get(index + 1) : null;
int remainingNum = examineQaList.size() - index - 1;
hasNext = remainingNum > 1 ? true : false;
}
}
if(Objects.nonNull(nextQa)) {
// 返回结果
// result.setNext(hasNext);
// result.setQuestion(nextQa.getQuestion());
// result.setAnswer(nextQa.getAnswer());
// result.setQuestionId(nextQa.getId());
// 缓存
businessCache.setCurrentQuestionId(nextQa.getId());
businessCache.setHasNext(hasNext);
redisTemplate.opsForValue().set(examBusinessKey, JSONObject.toJSONString(businessCache), 1, TimeUnit.DAYS);
return result;
} else {
throw new InternalException(-1, "获取下一题信息有误");
if(Objects.nonNull(businessCache) && Objects.nonNull(businessCache.getRecordId())) {
// 删除考试记录
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setId(businessCache.getRecordId());
saveModel.setDeleted(1);
examineService.saveExamineRecord(saveModel);
}
// 删除缓存
redisTemplate.delete(examBusinessKey);
}
/**
......
package cn.breeze.elleai.config;
import cn.breeze.elleai.job.SingleJob;
import lombok.SneakyThrows;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
@Configuration
public class QuartzCronConfig {
@Bean
public JobDetail jobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(SingleJob.class); // 可以使用同一个任务类
factoryBean.setDurability(true);
factoryBean.setName("cronJob");
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
@SneakyThrows
@Bean
public Trigger cronTrigger(JobDetail jobDetail) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression("0 0/1 * * * ?"); // 每5分钟执行一次
factoryBean.setName("cronTrigger");
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}
......@@ -107,18 +107,36 @@ public class ExamineMobileController {
*/
@Operation(summary = "完成考试(触发最终结果更新)")
@PostMapping(value = "/complete_examine")
public ApiResponse<String> updateExamResult(@RequestBody ExamineUpdateResultDto request) {
public ApiResponse<String> updateExamResult(UserPrincipal userPrincipal, @RequestBody ExamineUpdateResultDto request) {
examineService.completeExamine(userPrincipal, request.getExamineId(), request.getBusinessNo());
return ApiResponse.ok("SUCCESS");
}
@Operation(summary = "前端轮询,获取最终考试结果")
@GetMapping(value = "/get_examine_final_result")
public ApiResponse<ExamineFinalResultDto> getExamineFinalResult(@Parameter(hidden = true) UserPrincipal userPrincipal,
public ApiResponse<ExamineRecordMobileDto> getExamineFinalResult(@Parameter(hidden = true) UserPrincipal userPrincipal,
@Schema(description = "对练ID") @RequestParam("examine_id") Integer examineId,
@Schema(description = "考试流水号") @RequestParam("business_no") String businessNo) {
ExamineFinalResultDto result = new ExamineFinalResultDto();
ExamineRecordMobileDto result = examineService.getExamineFinalResult(userPrincipal, examineId, businessNo);
return ApiResponse.ok(result);
}
/**
* 退出考试,清除考试记录
* @param userPrincipal
* @param examineId
* @param businessNo
* @return
*/
@Operation(summary = "退出考试,清除考试记录")
@PostMapping(value = "/exit_examine")
public ApiResponse<String> exitExam(@Parameter(hidden = true) UserPrincipal userPrincipal,
@Schema(description = "对练ID") @RequestParam("examine_id") Integer examineId,
@Schema(description = "考试流水号") @RequestParam("business_no") String businessNo) {
examineService.exitExamine(userPrincipal, examineId, businessNo);
return ApiResponse.ok("SUCCESS");
}
}
package cn.breeze.elleai.domain.sparring.model.request;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class ExamineEvaluateJobSaveModel implements Serializable {
private Integer id;
/**
* 业务ID(单题点评为详情记录ID,总点评为考试记录ID)
*/
private Integer businessId;
/**
* 考试流水号
*/
private String businessNo;
/**
* 类型(0单题点评 1总点评)
*/
private Integer type;
/**
* 状态(0待执行 1执行成功)
*/
private Integer status;
/**
* 执行失败原因
*/
private String failReason;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
......@@ -76,4 +76,9 @@ public class ExamineRecordSaveModel implements Serializable {
* 更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer deleted;
}
package cn.breeze.elleai.domain.sparring.model.response;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class ExamineEvaluateJobResponseModel implements Serializable {
private Integer id;
/**
* 业务ID(单题点评为详情记录ID,总点评为考试记录ID)
*/
private Integer businessId;
/**
* 考试流水号
*/
private String businessNo;
/**
* 类型(0单题点评 1总点评)
*/
private Integer type;
/**
* 用户ID
*/
private String userId;
/**
* 状态(0待执行 1执行成功)
*/
private Integer status;
/**
* 执行失败原因
*/
private String failReason;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
package cn.breeze.elleai.domain.sparring.model.response;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class ExamineRecordDetailResponseModel implements Serializable {
private Integer id;
/**
* 对练记录ID
*/
private Integer recordId;
/**
* 题目ID
*/
private Integer qaId;
/**
* 评分
*/
private Float score;
/**
* 评价
*/
private String evaluation;
/**
* 员工回答
*/
private String answer;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
package cn.breeze.elleai.domain.sparring.service;
import cn.breeze.elleai.domain.sparring.model.request.*;
import cn.breeze.elleai.domain.sparring.model.response.ExamineEvaluateJobResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.ProperNounResponseModel;
import com.mybatisflex.core.paginate.Page;
......@@ -21,4 +22,8 @@ public interface CommonService {
void deleteProperNoun(Integer id);
void saveProperNoun(ProperNounSaveModel dto);
void saveEvaluateJob(ExamineEvaluateJobSaveModel dto);
List<ExamineEvaluateJobResponseModel> pendingEvaluateJobList();
}
package cn.breeze.elleai.domain.sparring.service;
import cn.breeze.elleai.domain.sparring.model.request.ExamineEvaluateJobSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.ProperNounRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.ProperNounSaveModel;
import cn.breeze.elleai.domain.sparring.model.response.ExamineEvaluateJobResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.ProperNounResponseModel;
import cn.breeze.elleai.infra.entity.ExamineEvaluateJobEntity;
import cn.breeze.elleai.infra.entity.ProperNounEntity;
import cn.breeze.elleai.infra.mapper.ExamineEvaluateJobMapper;
import cn.breeze.elleai.infra.mapper.ProperNounMapper;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
......@@ -19,6 +23,7 @@ import java.util.List;
import java.util.Objects;
import static cn.breeze.elleai.infra.entity.table.ProperNounTableDef.PROPER_NOUN_ENTITY;
import static cn.breeze.elleai.infra.entity.table.ExamineEvaluateJobTableDef.EXAMINE_EVALUATE_JOB_ENTITY;
@Service
......@@ -27,6 +32,8 @@ public class CommonServiceImpl implements CommonService{
private final ProperNounMapper properNounMapper;
private final ExamineEvaluateJobMapper evaluateJobMapper;
@Override
public Page<ProperNounResponseModel> properNounPaginQuery(ProperNounRequestModel request) {
Integer pageNo = ObjectUtil.defaultIfNull(request.getPageNo(), 1);
......@@ -91,4 +98,24 @@ public class CommonServiceImpl implements CommonService{
}
properNounMapper.insertOrUpdateSelective(entity);
}
@Override
public void saveEvaluateJob(ExamineEvaluateJobSaveModel dto) {
ExamineEvaluateJobEntity entity = BeanUtil.toBean(dto, ExamineEvaluateJobEntity.class);
entity.setUpdateTime(new Date());
if(Objects.isNull(entity.getId())) {
entity.setStatus(0);
}
evaluateJobMapper.insertOrUpdateSelective(entity);
}
@Override
public List<ExamineEvaluateJobResponseModel> pendingEvaluateJobList() {
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.where(EXAMINE_EVALUATE_JOB_ENTITY.STATUS.eq(0));
queryWrapper.orderBy(EXAMINE_EVALUATE_JOB_ENTITY.CREATE_TIME, true);
return evaluateJobMapper.selectListByQueryAs(queryWrapper, ExamineEvaluateJobResponseModel.class);
}
}
......@@ -100,6 +100,8 @@ public interface ExamineService {
void saveExamineRecordDetail(ExamineRecordDetailSaveModel dto);
ExamineRecordDetailResponseModel singleExamineQaDetail(Integer id);
/***************************************** 资料库分类 *****************************************/
Page<WikiCategoryResponseModel> wikiCategoryPaginQuery(WikiCategoryRequestModel request);
......
......@@ -398,6 +398,7 @@ public class ExamineServiceImpl implements ExamineService {
if(Objects.nonNull(request.getEndTime())) {
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.CREATE_TIME.le(request.getEndTime()));
}
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.DELETED.eq(0));
queryWrapper.orderBy(USER_EXAMINE_RECORD_ENTITY.CREATE_TIME, false);
Page<ExamineRecordResponseModel> page = userExamineRecordMapper.paginateAs(pageNo, pageSize, queryWrapper, ExamineRecordResponseModel.class);
......@@ -420,6 +421,8 @@ public class ExamineServiceImpl implements ExamineService {
if(Objects.nonNull(request.getExamineId())) {
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.EXAMINE_ID.eq(request.getExamineId()));
}
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.DELETED.eq(0));
queryWrapper.orderBy(USER_EXAMINE_RECORD_ENTITY.CREATE_TIME, false);
Page<ExamineRecordResponseModel> page = userExamineRecordMapper.paginateAs(pageNo, pageSize, queryWrapper, ExamineRecordResponseModel.class);
......@@ -439,6 +442,7 @@ public class ExamineServiceImpl implements ExamineService {
if(Objects.nonNull(request.getExamineId())) {
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.EXAMINE_ID.eq(request.getExamineId()));
}
queryWrapper.where(USER_EXAMINE_RECORD_ENTITY.DELETED.eq(0));
return userExamineRecordMapper.selectListByQueryAs(queryWrapper, ExamineRecordResponseModel.class);
}
......@@ -484,6 +488,13 @@ public class ExamineServiceImpl implements ExamineService {
userExamineDetailRecordMapper.insertOrUpdateSelective(entity);
}
@Override
public ExamineRecordDetailResponseModel singleExamineQaDetail(Integer id) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(USER_EXAMINE_DETAIL_RECORD_ENTITY.ID.eq(id));
return userExamineDetailRecordMapper.selectOneByQueryAs(queryWrapper, ExamineRecordDetailResponseModel.class);
}
@Override
public Page<WikiCategoryResponseModel> wikiCategoryPaginQuery(WikiCategoryRequestModel request) {
Integer pageNo = ObjectUtil.defaultIfNull(request.getPageNo(), 1);
......
package cn.breeze.elleai.infra.entity;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 实体类。
*
* @author breeze
* @since 2024-09-18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("ai_examine_evaluate_job")
public class ExamineEvaluateJobEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id(keyType = KeyType.Auto)
private Integer id;
/**
* 业务ID(单题点评为详情记录ID,总点评为考试记录ID)
*/
private Integer businessId;
/**
* 考试流水号
*/
private String businessNo;
/**
* 类型(0单题点评 1总点评)
*/
private Integer type;
/**
* 用户ID
*/
private String userId;
/**
* 状态(0待执行 1执行成功)
*/
private Integer status;
/**
* 执行失败原因
*/
private String failReason;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
......@@ -97,4 +97,9 @@ public class UserExamineRecordEntity implements Serializable {
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer deleted;
}
package cn.breeze.elleai.infra.entity.table;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.table.TableDef;
import java.io.Serial;
/**
* 表定义层。
*
* @author breeze
* @since 2024-09-18
*/
public class ExamineEvaluateJobTableDef extends TableDef {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
public static final ExamineEvaluateJobTableDef EXAMINE_EVALUATE_JOB_ENTITY = new ExamineEvaluateJobTableDef();
public final QueryColumn ID = new QueryColumn(this, "id");
/**
* 业务ID(单题点评为详情记录ID,总点评为考试记录ID)
*/
public final QueryColumn BUSINESS_ID = new QueryColumn(this, "business_id");
/**
* 类型(0单题点评 1总点评)
*/
public final QueryColumn TYPE = new QueryColumn(this, "type");
/**
* 考试流水号
*/
public final QueryColumn BUSINESS_NO = new QueryColumn(this, "business_no");
/**
* 用户ID
*/
public final QueryColumn USER_ID = new QueryColumn(this, "user_id");
/**
* 状态(0待执行 1执行成功)
*/
public final QueryColumn STATUS = new QueryColumn(this, "status");
/**
* 执行失败原因
*/
public final QueryColumn FAIL_REASON = new QueryColumn(this, "fail_reason");
/**
* 创建时间
*/
public final QueryColumn CREATE_TIME = new QueryColumn(this, "create_time");
/**
* 更新时间
*/
public final QueryColumn UPDATE_TIME = new QueryColumn(this, "update_time");
/**
* 所有字段。
*/
public final QueryColumn ALL_COLUMNS = new QueryColumn(this, "*");
/**
* 默认字段,不包含逻辑删除或者 large 等字段。
*/
public final QueryColumn[] DEFAULT_COLUMNS = new QueryColumn[]{ID, BUSINESS_ID, TYPE, USER_ID, BUSINESS_NO, STATUS, FAIL_REASON, CREATE_TIME, UPDATE_TIME};
public ExamineEvaluateJobTableDef() {
super("", "ai_examine_evaluate_job");
}
private ExamineEvaluateJobTableDef(String schema, String name, String alisa) {
super(schema, name, alisa);
}
public ExamineEvaluateJobTableDef as(String alias) {
String key = getNameWithSchema() + "." + alias;
return getCache(key, k -> new ExamineEvaluateJobTableDef("", "ai_examine_evaluate_job", alias));
}
}
......@@ -90,6 +90,11 @@ public class UserExamineRecordTableDef extends TableDef {
*/
public final QueryColumn OVERALL_EVALUATION = new QueryColumn(this, "overall_evaluation");
/**
* 综合评价
*/
public final QueryColumn DELETED = new QueryColumn(this, "deleted");
/**
* 所有字段。
*/
......@@ -98,7 +103,7 @@ public class UserExamineRecordTableDef extends TableDef {
/**
* 默认字段,不包含逻辑删除或者 large 等字段。
*/
public final QueryColumn[] DEFAULT_COLUMNS = new QueryColumn[]{ID, USER_ID, USER_NAME, BUSINESS_NO, SHOP_ID, SHOP_NAME, SESSION_ID, EXAMINE_ID, EXAMINE_MODE, ANSWERED_NUM, OVERALL_SCORE, OVERALL_EVALUATION, CREATE_TIME, UPDATE_TIME};
public final QueryColumn[] DEFAULT_COLUMNS = new QueryColumn[]{ID, USER_ID, USER_NAME, BUSINESS_NO, SHOP_ID, SHOP_NAME, SESSION_ID, EXAMINE_ID, EXAMINE_MODE, ANSWERED_NUM, OVERALL_SCORE, OVERALL_EVALUATION, CREATE_TIME, UPDATE_TIME, DELETED};
public UserExamineRecordTableDef() {
super("", "ai_user_examine_record");
......
package cn.breeze.elleai.infra.mapper;
import cn.breeze.elleai.infra.entity.ExamineEvaluateJobEntity;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 映射层。
*
* @author breeze
* @since 2024-09-18
*/
@Mapper
public interface ExamineEvaluateJobMapper extends BaseMapper<ExamineEvaluateJobEntity> {
}
package cn.breeze.elleai.job;
import cn.breeze.elleai.application.dto.inner.AiSingleEvaluateResultDto;
import cn.breeze.elleai.application.service.AiPlatformExtensionService;
import cn.breeze.elleai.application.service.AppExamineService;
import cn.breeze.elleai.domain.sparring.model.request.ExamineDetailRecordRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.ExamineEvaluateJobSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.ExamineRecordDetailSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.ExamineRecordSaveModel;
import cn.breeze.elleai.domain.sparring.model.response.ExamineDetailRecordResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.ExamineEvaluateJobResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.ExamineQaResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.ExamineRecordDetailResponseModel;
import cn.breeze.elleai.domain.sparring.service.CommonService;
import cn.breeze.elleai.domain.sparring.service.ExamineService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
@Slf4j
@Component
@RequiredArgsConstructor
public class SingleJob extends QuartzJobBean {
private final AppExamineService appEamineService;
private final ExamineService examineService;
private final CommonService commonService;
private final AiPlatformExtensionService extensionService;
private final StringRedisTemplate redisTemplate;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("定时轮询AI点评异常任务开始.");
List<ExamineEvaluateJobResponseModel> jobList = commonService.pendingEvaluateJobList();
log.info("定时轮询AI点评异常任务,jobList size = {}", jobList.size());
if(CollectionUtil.isNotEmpty(jobList)) {
jobList.forEach(v -> {
executeEvaluateJob(v);
});
}
log.info("定时轮询AI点评异常任务结束.");
}
@Async
public void executeEvaluateJob(ExamineEvaluateJobResponseModel job) {
Integer id = job.getId();
Integer type = job.getType();
Integer businessId = job.getBusinessId();
String businessNo = job.getBusinessNo();
// 单题点评
if(Objects.equals(type, 0)) {
// 获取单题答题信息
ExamineRecordDetailResponseModel singleQaDetail = examineService.singleExamineQaDetail(businessId);
Integer qaId = singleQaDetail.getQaId();
ExamineQaResponseModel qaDetail = examineService.examineQaDetail(qaId);
AiSingleEvaluateResultDto evaluateResult = extensionService.run4SingleEvaluate(businessNo, job.getUserId(), qaDetail.getQuestion(), qaDetail.getAnswer(), singleQaDetail.getAnswer());
if(Objects.nonNull(evaluateResult)) {
// 更新答题点评信息
ExamineRecordDetailSaveModel detailSaveModel = new ExamineRecordDetailSaveModel();
detailSaveModel.setId(singleQaDetail.getId());
detailSaveModel.setScore(evaluateResult.getScore());
detailSaveModel.setEvaluation(evaluateResult.getEvaluation());
examineService.saveExamineRecordDetail(detailSaveModel);
// 更新任务执行状态
ExamineEvaluateJobSaveModel update = new ExamineEvaluateJobSaveModel();
update.setId(job.getId());
update.setStatus(1);
commonService.saveEvaluateJob(update);
//TODO 更新任务缓存信息
}
} else {
//TODO 判断所有题目是否已经单题点评完毕
// 获取答题结果
ExamineDetailRecordRequestModel requestModel = new ExamineDetailRecordRequestModel();
requestModel.setRecordId(businessId);
List<ExamineDetailRecordResponseModel> detailRecordList = examineService.examineDetailRecordList(requestModel);
if(CollectionUtil.isNotEmpty(detailRecordList)) {
// 计算综合评分
double totalScore = detailRecordList.stream().filter(v -> Objects.nonNull(v.getScore())).mapToDouble(ExamineDetailRecordResponseModel::getScore).sum();
Double avgScore = totalScore / detailRecordList.size();
List<Integer> qaIdList = detailRecordList.stream().map(ExamineDetailRecordResponseModel::getQaId).collect(Collectors.toList());
List<ExamineQaResponseModel> examineQaList = examineService.examineQaList(qaIdList);
Map<Integer, ExamineQaResponseModel> qaMap = examineQaList.stream().collect(toMap(ExamineQaResponseModel::getId, Function.identity()));
String qaResultStr = "";
for(int i = 0; i < detailRecordList.size(); i++) {
ExamineDetailRecordResponseModel detail = detailRecordList.get(i);
ExamineQaResponseModel qa = qaMap.get(detail.getQaId());
qaResultStr += "问题 " + (i + 1) + ": " + qa.getQuestion() + "\n";
qaResultStr += "得分:" + detail.getScore() + "\n";
qaResultStr += "评分说明:" + detail.getEvaluation() + "\n";
qaResultStr += "----------------------------------------\n";
}
// 执行综合点评
String overallEvaluation = extensionService.run4TotalEvaluate(businessNo, job.getUserId(), avgScore.floatValue(), qaResultStr);
if(StrUtil.isNotEmpty(overallEvaluation)) {
// 更新
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setId(businessId);
saveModel.setOverallEvaluation(overallEvaluation);
saveModel.setOverallScore(avgScore.floatValue());
examineService.saveExamineRecord(saveModel);
// 更新任务执行状态
ExamineEvaluateJobSaveModel update = new ExamineEvaluateJobSaveModel();
update.setId(job.getId());
update.setStatus(1);
commonService.saveEvaluateJob(update);
//TODO 更新任务缓存信息
}
}
}
}
}
......@@ -11,26 +11,12 @@ spring:
time-zone: GMT+8
default-property-inclusion: non_null
property-naming-strategy: SNAKE_CASE
#springfox:
# documentation:
# swagger-ui:
# enabled: true
#swagger:
# enable: true
springdoc:
swagger-ui:
path: /swagger-ui.html
dify:
api_base: https://app-api.tech.breezeai.cn/v1
api_key: app-mJFu7K2wl3qEYsILkQBgRAKO
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: fhgaf%^$#%cHDFDhUHLKnhkhj
# 令牌有效期(默认30分钟)
expireTime: 2628000
---
spring:
config:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment