Commit ee350b41 authored by 陈立彬's avatar 陈立彬

AI对练、AI问答扩展

parent c5c894d5
package cn.breeze.elleai.application.dto.inner;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* AI单题点评结果
*/
@Data
public class AiSingleEvaluateResultDto implements Serializable {
@Schema(description = "评分")
private Float score;
@Schema(description = "评价")
private String evaluation;
@Schema(description = "DIFY会话ID")
private String difySessionId;;
}
package cn.breeze.elleai.application.dto.inner;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 考试流水缓存
*/
@Data
public class ExamineBusinessCacheDto implements Serializable {
/**
* 考试ID
*/
@Schema(description = "考试ID")
private Integer examineId;
/**
* 对练模式(0练习 1对练)
*/
@Schema(description = "对练模式(0练习 1对练)")
private Integer examineMode;
/**
* 用户ID
*/
@Schema(description = "用户ID")
private String userId;
/**
* 考试记录ID
*/
@Schema(description = "考试记录ID")
private Integer recordId;
/**
* 当前题目ID
*/
@Schema(description = "当前题目ID")
private Integer currentQuestionId;
/**
* 是否有下一题
*/
@Schema(description = "是否有下一题")
private boolean hasNext;
}
package cn.breeze.elleai.application.service;
import cn.breeze.elleai.application.dto.inner.AiSingleEvaluateResultDto;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* AI平台扩展服务
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AiPlatformExtensionService {
@Value("${dify.api_base}")
private String difyBase;
@Value("${dify.api_key}")
private String apiKey;
private final RestTemplate restTemplate = new RestTemplate();
/**
* 单题评分+点评
* @param sessionId
* @param userId
* @param question
* @param answer
* @param userAnswer
*/
public AiSingleEvaluateResultDto run4SingleEvaluate(String sessionId, String userId, String question, String answer, String userAnswer) {
Map<String, String> inputs = new HashMap<>();
inputs.put("scene", "single_evaluate");
inputs.put("question", question);
inputs.put("answer", answer);
JSONObject param = new JSONObject();
param.put("query", userAnswer);
param.put("inputs", inputs);
param.put("response_mode", "blocking");
param.put("conversation_id", "");
param.put("user", userId);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
log.info("异步请求参数1,sessionId = {}", sessionId);
log.info("异步请求参数2,req = {}", JSONObject.toJSONString(param));
HttpEntity<String> postEntity = new HttpEntity<>(param.toJSONString(), headers);
ResponseEntity<String> response = restTemplate.postForEntity(difyBase + "/chat-messages", postEntity, String.class);
String body = response.getBody();
log.info("执行结果:{}", body);
if(Objects.equals(response.getStatusCode(), HttpStatus.OK)) {
JSONObject bodyObject = JSONObject.parseObject(body);
String conversationId = bodyObject.getString("conversation_id");
JSONObject answerObject = JSONObject.parseObject(bodyObject.getString("answer"));
Float score = answerObject.getFloat("score");
String evaluation = answerObject.getString("evaluation");
AiSingleEvaluateResultDto result = new AiSingleEvaluateResultDto();
result.setEvaluation(evaluation);
result.setScore(score);
result.setDifySessionId(conversationId);
return result;
}
return null;
}
/**
* 考试总点评
* @param sessionId
* @param userId
* @param avgScore
* @param qaResultList
*/
public String run4TotalEvaluate(String sessionId, String userId, Float avgScore, List qaResultList) {
Map<String, Object> inputs = new HashMap<>();
inputs.put("scene", "total_evaluate");
inputs.put("avg_score", avgScore);
inputs.put("qa_result_list", qaResultList);
JSONObject param = new JSONObject();
param.put("query", "Not_Null");
param.put("inputs", inputs);
param.put("response_mode", "blocking");
param.put("conversation_id", "");
param.put("user", userId);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
log.info("异步请求参数1,sessionId = {}", sessionId);
log.info("异步请求参数2,req = {}", JSONObject.toJSONString(param));
HttpEntity<String> postEntity = new HttpEntity<>(param.toJSONString(), headers);
ResponseEntity<String> response = restTemplate.postForEntity(difyBase + "/chat-messages", postEntity, String.class);
String body = response.getBody();
log.info("执行结果:{}", body);
if(Objects.equals(response.getStatusCode(), HttpStatus.OK)) {
JSONObject bodyObject = JSONObject.parseObject(body);
String conversationId = bodyObject.getString("conversation_id");
JSONObject answerObject = JSONObject.parseObject(bodyObject.getString("answer"));
String evaluation = answerObject.getString("evaluation");
return evaluation;
}
return null;
}
}
......@@ -5,7 +5,7 @@ import cn.breeze.elleai.application.dto.request.*;
import cn.breeze.elleai.application.dto.response.*;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaHistoryRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.UserChatCompletionSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaRequestModel;
import cn.breeze.elleai.domain.sparring.model.response.QaAssistantResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.UserChatCompletionHistoryResponseModel;
......@@ -14,6 +14,7 @@ import cn.breeze.elleai.domain.sparring.service.ChatCompletionService;
import cn.breeze.elleai.util.UserPrincipal;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.mybatisflex.core.paginate.Page;
......@@ -259,29 +260,77 @@ public class AppChatCompletionService {
}
/**
* AI问答会话分页查询
* 移动端历史会话明细
* @param userPrincipal
* @param chatCompletionId
* @param pageNo
* @param pageSize
* @return
*/
public PageResult<UserChatCompletionHistoryMobileDto> userQaHistoryMobilePaginQuery(UserPrincipal userPrincipal, Integer chatCompletionId, Integer pageNo, Integer pageSize) {
pageNo = Objects.isNull(pageNo) ? 1 : pageNo;
pageSize = Objects.isNull(pageSize) ? 10 : pageSize;
UserQaRequestDto req = new UserQaRequestDto();
req.setUserId(userPrincipal.getUserId());
Page<UserChatCompletionHistoryResponseModel> historyDetailPageResult = chatCompletionService.userQaHistoryDetailPaginQuery(chatCompletionId, pageNo, pageSize);
PageResult<UserChatCompletionHistoryMobileDto> pageResult = PageResult.of(pageNo, pageSize, (int) historyDetailPageResult.getTotalRow(), null);
if(Objects.nonNull(historyDetailPageResult) && CollectionUtil.isNotEmpty(historyDetailPageResult.getRecords())) {
List<UserChatCompletionHistoryMobileDto> items = historyDetailPageResult.getRecords().stream().map(v -> {
UserChatCompletionHistoryMobileDto dto = BeanUtil.copyProperties(v, UserChatCompletionHistoryMobileDto.class);
return dto;
}).collect(Collectors.toList());
pageResult.setItems(items);
}
return pageResult;
}
/**
* 用户提问
* @param request
* @return
*/
public UserAskResultMobileDto userAsk(UserPrincipal userPrincipal, UserQaMobileRequestDto request) {
//TODO 生成sessionId
UserAskResultMobileDto result = new UserAskResultMobileDto();
result.setChatCompletionId(1);
result.setReplyContent("moke ai reply.");
// 更新问答记录
// Integer recordId = chatCompletionService.saveUserQaSession(request.getUserId(), request.getSessionId());
//
// // 保存问答详情
// String content = request.getContent();
// chatCompletionService.saveUserQaRecord(recordId, 0, content);
//
// return ask4Knowledge(request.getSessionId(), request.getUserId(), content);
// 更新会话信息
UserChatCompletionSaveModel saveModel = new UserChatCompletionSaveModel();
saveModel.setUserId(userPrincipal.getUserId());
saveModel.setUserName(userPrincipal.getUserName());
saveModel.setId(request.getChatCompletionId());
saveModel.setLastQuestion(request.getContent());
saveModel.setShopId(userPrincipal.getShopId());
saveModel.setShopName(userPrincipal.getShopName());
// 首次提问
if(Objects.isNull(request.getChatCompletionId())) {
saveModel.setCreateTime(new Date());
saveModel.setFirstQuestion(request.getContent());
saveModel.setSessionId(UUID.randomUUID().toString());
}
Integer recordId = chatCompletionService.saveUserQaSession(saveModel);
// 保存问答详情
chatCompletionService.saveUserQaRecord(recordId, 0, request.getContent());
// 问一下AI
String replyContent = ask4Knowledge(saveModel.getSessionId(), saveModel.getUserId(), request.getContent());
// 保存AI问答详情
chatCompletionService.saveUserQaRecord(recordId, 1, replyContent);
result.setChatCompletionId(recordId);
result.setReplyContent(replyContent);
return result;
}
public String ask4Knowledge(String sessionId, String userId, String question) {
if(true) {
return "moke ai reply.";
}
......
......@@ -2,19 +2,24 @@ 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;
import cn.breeze.elleai.application.dto.request.*;
import cn.breeze.elleai.application.dto.response.*;
import cn.breeze.elleai.domain.sparring.model.request.*;
import cn.breeze.elleai.domain.sparring.model.response.*;
import cn.breeze.elleai.domain.sparring.service.ExamineService;
import cn.breeze.elleai.exception.InternalException;
import cn.breeze.elleai.util.UserPrincipal;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
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;
......@@ -28,6 +33,7 @@ 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.toMap;
......@@ -40,21 +46,17 @@ import static java.util.stream.Collectors.toMap;
@RequiredArgsConstructor
public class AppExamineService {
private static String EXAM_REDIS_QUESTION_KEY = "EXAM_MASTER_QUESTION:%s:%s";
private static String EXAM_BUSINESS_REDIS_KEY = "examine:business:%s"; // 考试流水缓存信息(businessNo)
private static String EXAM_REDIS_SESSION_KEY = "EXAM_MASTER_SESSION:%d:%d:%s";
private static String EXAM_AI_TASK_REDIS_KEY = "examine:ai_task:%s"; // 考试AI点评任务执行状态(businessNo)
private static String EXAM_REDIS_CAS_KEY = "EXAM_MASTER_CURRENT_AI_SCORE:%d:%s";
private static String USER_SESSION_CONTEXT_KEY = "USER_SESSION_CONTEXT:%d:%s:%s";
private static String USER_SESSION_CONTEXT_COUNT = "USER_SESSION_CONTEXT_COUNT";
private final ExamineService examineService;
private final StringRedisTemplate redisTemplate;
private final AiPlatformExtensionService extensionService;
/************************************************** 场景分类 **************************************************/
......@@ -394,14 +396,14 @@ public class AppExamineService {
/**
* QA列表查询
* @param request
* @param examineId
* @return
*/
public List<ExamineQaDto> examineQaList(ExamineQaRequestModel request) {
public List<ExamineQaDto> examineQaList(Integer examineId) {
List<ExamineQaDto> resultList = new ArrayList<>();
List<ExamineQaResponseModel> modelList = examineService.examineQaList(request);
List<ExamineQaResponseModel> modelList = examineService.examineQaList(examineId);
if(CollectionUtil.isNotEmpty(modelList)) {
resultList = modelList.stream().map(v -> {
......@@ -473,7 +475,7 @@ public class AppExamineService {
examTotalCount = recordList.size();
practiceCount = examTotalCount - examTotalCount;
maxOverallScore = recordList.stream().mapToDouble(ExamineRecordResponseModel::getOverallScore).max().getAsDouble();
maxOverallScore = recordList.stream().filter(v -> Objects.nonNull(v.getOverallScore())).mapToDouble(ExamineRecordResponseModel::getOverallScore).max().getAsDouble();
}
result.setExamCount(examCount);
result.setPracticeCount(practiceCount);
......@@ -995,92 +997,288 @@ public class AppExamineService {
/************************************************** 移动端对练 **************************************************/
public ExamineQaDifyDto getCurrentQuestion(Integer examineId, String sessionId, String userId) {
ExamineQaDifyDto result = new ExamineQaDifyDto();
/**
* 开始考试,并返回考试流水号&第一道题目
* @param userPrincipal
* @param request
* @return
*/
public ExamineStartResultDto startExamine(UserPrincipal userPrincipal, StartExamineMobileRequestDto request) {
String redisKey = String.format(EXAM_REDIS_QUESTION_KEY, sessionId, userId);
String userId = userPrincipal.getUserId();
String currentQuestion = redisTemplate.opsForValue().get(redisKey);
Integer examineId = request.getExamineId();
Integer examineMode = request.getExamineMode();
String businessNo = UUID.randomUUID().toString();
log.info("获取当前出题,rediskey = {}, value = {}", redisKey, currentQuestion);
if(StringUtils.isNotEmpty(currentQuestion)) {
ExamineStartResultDto result = new ExamineStartResultDto();
result.setBusinessNo(businessNo);
// 获取缓存考试会话信息
String examBusinessKey = String.format(EXAM_BUSINESS_REDIS_KEY, businessNo);
ExamineBusinessCacheDto businessCache = getExamBusinessCache(businessNo);
if(Objects.isNull(businessCache)) {
businessCache = new ExamineBusinessCacheDto();
businessCache.setExamineId(examineId);
businessCache.setExamineMode(examineMode);
businessCache.setUserId(userId);
}
ExamineQaResponseModel model = examineService.examineQaDetail(Integer.valueOf(currentQuestion));
if(Objects.nonNull(model)) {
ExamineSceneCategoryResponseModel category = examineService.sceneCategoryDetail(model.getCategoryId());
// 当前出题
Integer currentQuestionId = businessCache.getCurrentQuestionId();
if(Objects.isNull(currentQuestionId)) {
List<ExamineQaResponseModel> examineQaList = examineService.examineQaList(examineId);
if(CollectionUtil.isNotEmpty(examineQaList)) {
ExamineQaResponseModel model = examineQaList.get(0);
currentQuestionId = model.getId();
result.setQuestionId(currentQuestionId);
result.setQuestion(model.getQuestion());
result.setAnswer(model.getAnswer());
result.setProject(category.getName());
result.setCode(category.getName());
result.setQuestionId(model.getId());
result.setNext(true);
// 保存题目信息?
result.setNext(examineQaList.size() > 1 ? true : false);
businessCache.setCurrentQuestionId(currentQuestionId);
businessCache.setHasNext(result.isNext());
}
}
// 缓存
redisTemplate.opsForValue().set(examBusinessKey, JSONObject.toJSONString(businessCache), 1, TimeUnit.DAYS);
return result;
}
/**
* 开始考试,获取出
* @param examineId
* @param userId
* @param examineMode
* 获取下一
* @param userPrincipal
* @param examineId 对练ID
* @param businessNo 考试流水号
* @return
*/
public ExamineStartResultDto startExamine(UserPrincipal userPrincipal, StartExamineMobileRequestDto request) {
public ExamineStartResultDto getNextQuestion(UserPrincipal userPrincipal, Integer examineId, String businessNo) {
String userId = userPrincipal.getUserId();
ExamineStartResultDto result = new ExamineStartResultDto();
result.setBusinessNo(UUID.randomUUID().toString());
result.setQuestionId(1);
result.setQuestion("请问毛巾都是干净的嘛?");
result.setAnswer("是的");
result.setNext(true);
// // 获取缓存考试会话信息
// String examSessionKey = String.format(EXAM_REDIS_SESSION_KEY, examineId, examineMode, userId);
// String sessionId = redisTemplate.opsForValue().get(examSessionKey);
// if(StrUtil.isEmptyIfStr(sessionId)) {
// // 生成sessionId
// sessionId = UUID.randomUUID().toString();
// redisTemplate.opsForValue().set(examSessionKey, sessionId, 1, TimeUnit.DAYS);
// }
//
// // 获取缓存考题信息
// String questionKey = String.format(EXAM_REDIS_QUESTION_KEY, examineId, examineMode, userId, sessionId);
//
//
// ExamineQaRequestModel req = null;
// examineService.examineQaList(req);
// // 根据分类获取考题
// ExamineResponseModel model = examineService.examineDetail(examineId);
// if(Objects.nonNull(model)) {
// Long maxQuestionId = qaRecordMapper.selectSessionMaxQuestionId(tenant, sessionId, userId);
// maxQuestionId = Objects.nonNull(maxQuestionId) ? maxQuestionId : 0;
// ElehKnowledgeDo knowledgeDo = knowledgeMapper.selectQuestion(tenant, tagDo.getId(), maxQuestionId);
// if(Objects.nonNull(knowledgeDo)) {
// result.setQuestion(knowledgeDo.getQuestion());
// result.setAnswer(knowledgeDo.getAnswer());
// result.setProject(tagDo.getName());
// result.setCode(tagDo.getCode());
// result.setQuestionId(knowledgeDo.getId());
// result.setNext(true);
//
// // 存储当前会话考试信息
// String redisKey = String.format(EXAM_REDIS_QUESTION_KEY, tenant, sessionId, userId);
// redisTemplate.opsForValue().set(redisKey, result.getQuestionId()+"", 10, TimeUnit.MINUTES);
//
// log.info("出题结果:result = {}, rediskey = {}", JSONObject.toJSONString(result), redisKey);
// }
// }
result.setBusinessNo(businessNo);
return result;
// 获取缓存考试会话信息
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, "获取下一题信息有误");
}
}
/**
* 考试答题
* @param userPrincipal
* @param request
*/
public void submitAnswer(UserPrincipal userPrincipal, SubmitAnswerMobileRequestDto request) {
String userId = userPrincipal.getUserId();
Integer examineId = request.getExamineId();
String answer = request.getAnswer();
Integer questionId = request.getQuestionId();
String businessNo = request.getBusinessNo();
ExamineStartResultDto result = new ExamineStartResultDto();
result.setBusinessNo(businessNo);
// 获取缓存考试会话信息
String examBusinessKey = String.format(EXAM_BUSINESS_REDIS_KEY, businessNo);
ExamineBusinessCacheDto businessCache = getExamBusinessCache(businessNo);
if(Objects.isNull(businessCache)) {
throw new InternalException(-1, "获取不到对练信息");
}
// 获取题目信息
ExamineQaDto examineQaDto = qaDetail(questionId);
// 获取数据库考试信息
Integer recordId = null;
ExamineRecordResponseModel examineRecord = examineService.examineRecordDetail(businessNo);
if(Objects.isNull(examineRecord)) {
// 第一次答题,保存考试信息
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setUserId(userId);
saveModel.setUserName(userPrincipal.getUserName());
saveModel.setShopId(userPrincipal.getShopId());
saveModel.setShopName(userPrincipal.getShopName());
saveModel.setAnsweredNum(1);
saveModel.setExamineId(examineId);
saveModel.setExamineMode(businessCache.getExamineMode());
saveModel.setBusinessNo(businessNo);
examineService.saveExamineRecord(saveModel);
recordId = saveModel.getId();
} else {
recordId = examineRecord.getId();
// 更新答题数量
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setId(recordId);
saveModel.setAnsweredNum(examineRecord.getAnsweredNum() + 1);
examineService.saveExamineRecord(saveModel);
}
// 保存单题答题信息
ExamineRecordDetailSaveModel detailSaveModel = new ExamineRecordDetailSaveModel();
detailSaveModel.setRecordId(recordId);
detailSaveModel.setQaId(questionId);
detailSaveModel.setCreateTime(new Date());
detailSaveModel.setAnswer(answer);
// AI 点评
AiSingleEvaluateResultDto evaluateResultDto = extensionService.run4SingleEvaluate(businessNo, userId, examineQaDto.getQuestion(), examineQaDto.getAnswer(), answer);
if(Objects.nonNull(evaluateResultDto)) {
detailSaveModel.setScore(evaluateResultDto.getScore());
detailSaveModel.setEvaluation(evaluateResultDto.getEvaluation());
}
examineService.saveExamineRecordDetail(detailSaveModel);
// 更新缓存
businessCache.setRecordId(recordId);
redisTemplate.opsForValue().set(examBusinessKey, JSONObject.toJSONString(businessCache), 1, TimeUnit.DAYS);
// 最后一题答题,执行AI总点评
if(!businessCache.isHasNext()) {
// 获取答题结果
ExamineDetailRecordRequestModel requestModel = new ExamineDetailRecordRequestModel();
requestModel.setRecordId(recordId);
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();
// 执行综合点评
String overallEvaluation = extensionService.run4TotalEvaluate(businessNo, userId, null, null);
// 更新答题数量
ExamineRecordSaveModel saveModel = new ExamineRecordSaveModel();
saveModel.setId(recordId);
saveModel.setOverallEvaluation(overallEvaluation);
saveModel.setOverallScore(avgScore.floatValue());
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();
// 获取缓存考试会话信息
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, "获取下一题信息有误");
}
}
/**
* 获取考试流水缓存信息
* @param businessNo
* @return
*/
private ExamineBusinessCacheDto getExamBusinessCache(String businessNo) {
// 获取缓存考试会话信息
String examBusinessKey = String.format(EXAM_BUSINESS_REDIS_KEY, businessNo);
String examBusinessCache = redisTemplate.opsForValue().get(examBusinessKey);
if(StrUtil.isNotEmpty(examBusinessCache)) {
return JSONObject.parseObject(examBusinessCache, ExamineBusinessCacheDto.class);
}
return null;
}
}
package cn.breeze.elleai;
package cn.breeze.elleai.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......
package cn.breeze.elleai;
package cn.breeze.elleai.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
......
package cn.breeze.elleai;
package cn.breeze.elleai.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
......
package cn.breeze.elleai.controller.extension;
import cn.breeze.elleai.application.dto.ApiResponse;
import cn.breeze.elleai.application.dto.response.AppRoleDto;
import cn.breeze.elleai.application.service.AiPlatformExtensionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
/**
* @author yangyw
*/
......@@ -14,7 +22,28 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class AiPlatformExtensionController {
private final AiPlatformExtensionService extensionService;
@Operation(summary = "单题点评")
@GetMapping("/single_evaluate")
public ApiResponse<String> singleEvaluate() {
String userId = UUID.randomUUID().toString();
String sessionId = UUID.randomUUID().toString();
String question = "问题";
String answer = "答案";
String userAnswer = "用户答案";
extensionService.run4SingleEvaluate(userId, sessionId, question, answer, userAnswer);
return ApiResponse.ok("SUCCESS");
}
@Operation(summary = "单题点评")
@GetMapping("/total_evaluate")
public ApiResponse<String> totalEvaluate() {
String userId = UUID.randomUUID().toString();
String sessionId = UUID.randomUUID().toString();
extensionService.run4TotalEvaluate(sessionId, userId, null, null);
return ApiResponse.ok("SUCCESS");
}
}
......@@ -73,4 +73,15 @@ public class ChatCompletionMobileController {
result.setChatCompletionId(1);
return ApiResponse.ok(result);
}
@Operation(summary = "历史对话详情")
@GetMapping("/history/detail")
public ApiResponse<PageResult<UserChatCompletionHistoryMobileDto>> historyDetail(@Parameter(hidden = true) UserPrincipal userPrincipal,
@Schema(description = "历史会话ID") @RequestParam("chat_completion_id") Integer chatCompletionId,
@Schema(description = "页码") @RequestParam("page_no") Integer pageNo,
@Schema(description = "分页数量") @RequestParam("page_size") Integer pageSize) {
PageResult<UserChatCompletionHistoryMobileDto> pageResult = chatCompletionService.userQaHistoryMobilePaginQuery(userPrincipal, chatCompletionId, pageNo, pageSize);
return ApiResponse.ok(pageResult);
}
}
......@@ -85,6 +85,7 @@ public class ExamineMobileController {
@PostMapping(value = "submit_answer")
public ApiResponse<String> submitAnswer(@Parameter(hidden = true) UserPrincipal userPrincipal,
@RequestBody SubmitAnswerMobileRequestDto request) {
examineService.submitAnswer(userPrincipal, request);
return ApiResponse.ok("SUCCESS");
}
......@@ -95,7 +96,7 @@ public class ExamineMobileController {
@Schema(description = "对练ID") @RequestParam("examine_id") Integer examineId,
@Schema(description = "考试流水号") @RequestParam("business_no") String businessNo) {
ExamineStartResultDto result = new ExamineStartResultDto();
ExamineStartResultDto result = examineService.getNextQuestion(userPrincipal, examineId, businessNo);
return ApiResponse.ok(result);
}
......@@ -107,6 +108,7 @@ public class ExamineMobileController {
@Operation(summary = "完成考试(触发最终结果更新)")
@PostMapping(value = "/complete_examine")
public ApiResponse<String> updateExamResult(@RequestBody ExamineUpdateResultDto request) {
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 ExamineRecordDetailSaveModel 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.model.request;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class ExamineRecordSaveModel implements Serializable {
private Integer id;
/**
* 用户ID
*/
private String userId;
/**
* 用户名称
*/
private String userName;
/**
* 考试流水号
*/
private String businessNo;
/**
* 所属门店ID
*/
private String shopId;
/**
* 所属门店名称
*/
private String shopName;
/**
* 会话ID
*/
private String sessionId;
/**
* 对练ID
*/
private Integer examineId;
/**
* 对练模式(0练习 1对练)
*/
private Integer examineMode;
/**
* 已答题数量
*/
private Integer answeredNum;
/**
* 综合评分
*/
private Float overallScore;
/**
* 综合评价
*/
private String overallEvaluation;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
package cn.breeze.elleai.domain.sparring.model.request;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserChatCompletionSaveModel implements Serializable {
private Integer id;
/**
* 用户ID
*/
private String userId;
/**
* 用户名称
*/
private String userName;
/**
* 所属门店ID
*/
private String shopId;
/**
* 所属门店名称
*/
private String shopName;
/**
* 会话ID
*/
private String sessionId;
/**
* 首次提问时间
*/
private String firstQuestion;
/**
* 最后一次提问
*/
private String lastQuestion;
/**
* 创建时间
*/
private Date createTime;
}
......@@ -24,6 +24,11 @@ public class ExamineRecordResponseModel implements Serializable {
*/
private String userName;
/**
* 考试流水号
*/
private String businessNo;
/**
* 所属门店ID
*/
......
package cn.breeze.elleai.domain.sparring.service;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaHistoryRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.*;
import cn.breeze.elleai.domain.sparring.model.response.QaAssistantResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.UserChatCompletionHistoryResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.UserChatCompletionResponseModel;
......@@ -20,11 +17,13 @@ public interface ChatCompletionService {
List<UserChatCompletionResponseModel> userQaList(UserQaRequestModel request);
List<UserChatCompletionHistoryResponseModel> userQaHistoryDetail(Integer sessionId);
List<UserChatCompletionHistoryResponseModel> userQaHistoryDetail(Integer chatCompletionId);
Page<UserChatCompletionHistoryResponseModel> userQaHistoryDetailPaginQuery(Integer chatCompletionId, Integer pageNo, Integer pageSize);
UserChatCompletionResponseModel userQaDetail(UserQaRequestModel request);
Integer saveUserQaSession(String userId, String sessionId);
Integer saveUserQaSession(UserChatCompletionSaveModel dto);
void saveUserQaRecord(Integer chatCompletionId, Integer type, String content);
......
package cn.breeze.elleai.domain.sparring.service;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.QaAssistantSaveModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaHistoryRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.UserQaRequestModel;
import cn.breeze.elleai.domain.sparring.model.request.*;
import cn.breeze.elleai.domain.sparring.model.response.QaAssistantResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.UserChatCompletionHistoryResponseModel;
import cn.breeze.elleai.domain.sparring.model.response.UserChatCompletionResponseModel;
......@@ -91,14 +88,26 @@ public class ChatCompletionServiceImpl implements ChatCompletionService{
}
@Override
public List<UserChatCompletionHistoryResponseModel> userQaHistoryDetail(Integer sessionId) {
public List<UserChatCompletionHistoryResponseModel> userQaHistoryDetail(Integer chatCompletionId) {
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.where(USER_CHAT_COMPLETION_HISTORY_ENTITY.CHAT_COMPLETION_ID.eq(sessionId));
queryWrapper.where(USER_CHAT_COMPLETION_HISTORY_ENTITY.CHAT_COMPLETION_ID.eq(chatCompletionId));
queryWrapper.orderBy(USER_CHAT_COMPLETION_HISTORY_ENTITY.CREATE_TIME, true);
return chatCompletionHistoryMapper.selectListByQueryAs(queryWrapper, UserChatCompletionHistoryResponseModel.class);
}
@Override
public Page<UserChatCompletionHistoryResponseModel> userQaHistoryDetailPaginQuery(Integer chatCompletionId, Integer pageNo, Integer pageSize) {
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.where(USER_CHAT_COMPLETION_HISTORY_ENTITY.CHAT_COMPLETION_ID.eq(chatCompletionId));
queryWrapper.orderBy(USER_CHAT_COMPLETION_HISTORY_ENTITY.CREATE_TIME, true);
Page<UserChatCompletionHistoryResponseModel> page = chatCompletionHistoryMapper.paginateAs(pageNo, pageSize, queryWrapper, UserChatCompletionHistoryResponseModel.class);
return page;
}
@Override
public UserChatCompletionResponseModel userQaDetail(UserQaRequestModel request) {
QueryWrapper queryWrapper = QueryWrapper.create();
......@@ -109,10 +118,9 @@ public class ChatCompletionServiceImpl implements ChatCompletionService{
}
@Override
public Integer saveUserQaSession(String userId, String sessionId) {
public Integer saveUserQaSession(UserChatCompletionSaveModel dto) {
UserChatCompletionEntity entity = new UserChatCompletionEntity();
entity.setUserId(userId);
entity.setSessionId(sessionId);
BeanUtil.copyProperties(dto, entity);
entity.setUpdateTime(new Date());
chatCompletionMapper.insertOrUpdateSelective(entity);
return entity.getId();
......
......@@ -49,7 +49,7 @@ public interface ExamineService {
*/
Page<ExamineQaResponseModel> examineQaPaginQuery(ExamineQaRequestModel request);
List<ExamineQaResponseModel> examineQaList(ExamineQaRequestModel request);
List<ExamineQaResponseModel> examineQaList(Integer examineId);
List<ExamineQaResponseModel> examineQaList(List<Integer> qaIds);
......@@ -94,6 +94,12 @@ public interface ExamineService {
ExamineRecordResponseModel examineRecordDetail(Integer id);
ExamineRecordResponseModel examineRecordDetail(String businessNo);
void saveExamineRecord(ExamineRecordSaveModel dto);
void saveExamineRecordDetail(ExamineRecordDetailSaveModel dto);
/***************************************** 资料库分类 *****************************************/
Page<WikiCategoryResponseModel> wikiCategoryPaginQuery(WikiCategoryRequestModel request);
......
......@@ -213,13 +213,16 @@ public class ExamineServiceImpl implements ExamineService {
}
@Override
public List<ExamineQaResponseModel> examineQaList(ExamineQaRequestModel request) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(EXAMINE_QA_ENTITY.DELETED.eq(0));
if(StrUtil.isNotEmpty(request.getName())) {
queryWrapper.where(EXAMINE_QA_ENTITY.QUESTION.like("%"+request.getName()+"%"));
}
public List<ExamineQaResponseModel> examineQaList(Integer examineId) {
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.select(EXAMINE_QA_ENTITY.ALL_COLUMNS).from(EXAMINE_QA_ENTITY);
queryWrapper.leftJoin(EXAMINE_QA_XREF_ENTITY).on(EXAMINE_QA_ENTITY.ID.eq(EXAMINE_QA_XREF_ENTITY.QA_ID))
.and(EXAMINE_QA_XREF_ENTITY.DELETED.eq(0));
queryWrapper.where(EXAMINE_QA_ENTITY.DELETED.eq(0));
queryWrapper.where(EXAMINE_QA_XREF_ENTITY.EXAMINE_ID.eq(examineId));
queryWrapper.orderBy(EXAMINE_QA_XREF_ENTITY.CREATE_TIME, false);
System.out.println(queryWrapper.toSQL());
return qaMapper.selectListByQueryAs(queryWrapper, ExamineQaResponseModel.class);
}
......@@ -457,6 +460,30 @@ public class ExamineServiceImpl implements ExamineService {
return userExamineRecordMapper.selectOneByQueryAs(queryWrapper, ExamineRecordResponseModel.class);
}
@Override
public ExamineRecordResponseModel examineRecordDetail(String businessNo) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(USER_EXAMINE_RECORD_ENTITY.BUSINESS_NO.eq(businessNo));
return userExamineRecordMapper.selectOneByQueryAs(queryWrapper, ExamineRecordResponseModel.class);
}
@Override
public void saveExamineRecord(ExamineRecordSaveModel dto) {
UserExamineRecordEntity entity = BeanUtil.toBean(dto, UserExamineRecordEntity.class);
entity.setUpdateTime(new Date());
userExamineRecordMapper.insertOrUpdateSelective(entity);
dto.setId(entity.getId());
}
@Override
public void saveExamineRecordDetail(ExamineRecordDetailSaveModel dto) {
UserExamineDetailRecordEntity entity = BeanUtil.toBean(dto, UserExamineDetailRecordEntity.class);
entity.setUpdateTime(new Date());
userExamineDetailRecordMapper.insertOrUpdateSelective(entity);
}
@Override
public Page<WikiCategoryResponseModel> wikiCategoryPaginQuery(WikiCategoryRequestModel request) {
Integer pageNo = ObjectUtil.defaultIfNull(request.getPageNo(), 1);
......
......@@ -42,6 +42,11 @@ public class UserExamineRecordEntity implements Serializable {
*/
private String userName;
/**
* 考试流水号
*/
private String businessNo;
/**
* 所属门店ID
*/
......
......@@ -34,6 +34,12 @@ public class UserExamineRecordTableDef extends TableDef {
*/
public final QueryColumn USER_NAME = new QueryColumn(this, "user_name");
/**
* 考试流水号
*/
public final QueryColumn BUSINESS_NO = new QueryColumn(this, "business_no");
/**
* 所属门店ID
*/
......@@ -92,7 +98,7 @@ public class UserExamineRecordTableDef extends TableDef {
/**
* 默认字段,不包含逻辑删除或者 large 等字段。
*/
public final QueryColumn[] DEFAULT_COLUMNS = new QueryColumn[]{ID, USER_ID, USER_NAME, 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};
public UserExamineRecordTableDef() {
super("", "ai_user_examine_record");
......
......@@ -21,8 +21,8 @@ springdoc:
swagger-ui:
path: /swagger-ui.html
dify:
api_base: http://dify-api:5001/v1
api_key: app-ilgoEphXjLw0I7x7fGeCyKYk
api_base: https://app-api.tech.breezeai.cn/v1
api_key: app-mJFu7K2wl3qEYsILkQBgRAKO
# token配置
token:
# 令牌自定义标识
......
......@@ -21,9 +21,16 @@ springdoc:
swagger-ui:
path: /swagger-ui.html
dify:
api_base: http://dify-api:5001/v1
api_key: app-ilgoEphXjLw0I7x7fGeCyKYk
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