|
@@ -27,7 +27,9 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.qmth.boot.core.ai.client.OcrApiClient;
|
|
|
+import com.qmth.boot.core.ai.model.llm.AutoScoreRequest;
|
|
|
import com.qmth.boot.core.ai.model.ocr.OcrType;
|
|
|
+import com.qmth.boot.core.ai.service.AiService;
|
|
|
import com.qmth.boot.core.exception.StatusException;
|
|
|
import com.qmth.boot.core.retrofit.exception.RetrofitResponseError;
|
|
|
import com.qmth.boot.core.retrofit.utils.SignatureInfo;
|
|
@@ -39,6 +41,7 @@ import com.qmth.boot.tools.excel.enums.ExcelType;
|
|
|
import com.qmth.boot.tools.excel.model.DataMap;
|
|
|
import com.qmth.boot.tools.models.ByteArray;
|
|
|
|
|
|
+import cn.com.qmth.am.bean.AiMarkingDto;
|
|
|
import cn.com.qmth.am.bean.AnswerImageDto;
|
|
|
import cn.com.qmth.am.bean.ImageSlice;
|
|
|
import cn.com.qmth.am.bean.ImportResult;
|
|
@@ -50,8 +53,10 @@ import cn.com.qmth.am.entity.QuestionEntity;
|
|
|
import cn.com.qmth.am.entity.StudentEntity;
|
|
|
import cn.com.qmth.am.entity.StudentScoreEntity;
|
|
|
import cn.com.qmth.am.enums.DataStatus;
|
|
|
+import cn.com.qmth.am.service.QuestionService;
|
|
|
import cn.com.qmth.am.service.StudentScoreService;
|
|
|
import cn.com.qmth.am.service.StudentService;
|
|
|
+import cn.com.qmth.am.utils.Calculator;
|
|
|
import cn.com.qmth.am.utils.ImageUtil;
|
|
|
|
|
|
@Service
|
|
@@ -68,6 +73,10 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
private StudentService studentService;
|
|
|
@Autowired
|
|
|
private OcrApiClient ocrApiClient;
|
|
|
+ @Autowired
|
|
|
+ private AiService aiService;
|
|
|
+ @Autowired
|
|
|
+ private QuestionService questionService;
|
|
|
static {
|
|
|
int threadCount = Runtime.getRuntime().availableProcessors();
|
|
|
queue = new ArrayBlockingQueue<>(threadCount * 2);
|
|
@@ -343,7 +352,7 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
|
|
|
@Override
|
|
|
public void createSlice(StudentScoreEntity score, QuestionEntity q, Map<Integer, AnswerImageDto> answerImages) {
|
|
|
- StudentScoreImageDto dto=new StudentScoreImageDto();
|
|
|
+ StudentScoreImageDto dto = new StudentScoreImageDto();
|
|
|
dto.setStudentId(score.getStudentId());
|
|
|
dto.setStudentScoreId(score.getId());
|
|
|
dto.setImage(getSlice(score, q, answerImages));
|
|
@@ -355,13 +364,13 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
String suff = null;
|
|
|
for (ImageSlice s : q.getImageSlice()) {
|
|
|
AnswerImageDto sheet = getSheet(score, q, s.getI(), answerImages);
|
|
|
- suff=sheet.getSuff();
|
|
|
+ suff = sheet.getSuff();
|
|
|
ret.add(ImageUtil.cutImage(sheet.getImage(), sheet.getSuff(), s.getX().intValue(), s.getY().intValue(),
|
|
|
s.getW().intValue(), s.getH().intValue()));
|
|
|
}
|
|
|
- if(ret.size()>1) {
|
|
|
+ if (ret.size() > 1) {
|
|
|
return ImageUtil.joinImages(ret, suff);
|
|
|
- }else {
|
|
|
+ } else {
|
|
|
return ret.get(0);
|
|
|
}
|
|
|
}
|
|
@@ -390,7 +399,7 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
private String getImageUrl(StudentScoreEntity score, QuestionEntity q, Integer pageIndex) {
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@Override
|
|
|
public StudentScoreImageDto pollStudentScoreImage() {
|
|
|
return queue.poll();
|
|
@@ -399,13 +408,18 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
@Transactional
|
|
|
@Override
|
|
|
public void ocr(StudentScoreImageDto dto) {
|
|
|
- OrgInfo org=solarService.getOrgList().get(0);
|
|
|
- String ret=ocrDispose(dto, org);
|
|
|
- if(ret!=null) {
|
|
|
- updateAnswer(dto.getStudentScoreId(), ret);
|
|
|
+ try {
|
|
|
+ OrgInfo org = solarService.getOrgList().get(0);
|
|
|
+ String ret = ocrDispose(dto, org);
|
|
|
+ if (ret != null) {
|
|
|
+ updateAnswer(dto.getStudentScoreId(), ret);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ ocrErr(dto, e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
- public void updateAnswer(Long id, String answer) {
|
|
|
+
|
|
|
+ private void updateAnswer(Long id, String answer) {
|
|
|
UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
|
|
@@ -413,39 +427,141 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
|
|
|
lw.eq(StudentScoreEntity::getId, id);
|
|
|
this.update(wrapper);
|
|
|
}
|
|
|
- private String ocrDispose(StudentScoreImageDto dto,OrgInfo org) {
|
|
|
+
|
|
|
+ private String ocrDispose(StudentScoreImageDto dto, OrgInfo org) {
|
|
|
SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
|
|
|
try {
|
|
|
return ocrApiClient.forImage(signature, OcrType.HANDWRITING, UploadFile.build("image", "", dto.getImage()));
|
|
|
} catch (Exception e) {
|
|
|
- log.error("ocr异常",e);
|
|
|
- if(e instanceof RetrofitResponseError) {
|
|
|
- RetrofitResponseError tem=(RetrofitResponseError)e;
|
|
|
- if(tem.getCode()==503) {
|
|
|
- if(dto.getRetry()<=3) {
|
|
|
+ log.error("ocr异常", e);
|
|
|
+ if (e instanceof RetrofitResponseError) {
|
|
|
+ RetrofitResponseError tem = (RetrofitResponseError) e;
|
|
|
+ if (tem.getCode() == 503) {
|
|
|
+ if (dto.getRetry() <= 3) {
|
|
|
try {
|
|
|
Thread.sleep(3000);
|
|
|
} catch (InterruptedException e1) {
|
|
|
}
|
|
|
- dto.setRetry(dto.getRetry()+1);
|
|
|
+ dto.setRetry(dto.getRetry() + 1);
|
|
|
return ocrDispose(dto, org);
|
|
|
- }else {
|
|
|
- ocrErr(dto);
|
|
|
- return null;
|
|
|
+ } else {
|
|
|
+ throw new StatusException("重试次数过多");
|
|
|
}
|
|
|
- }else {
|
|
|
- ocrErr(dto);
|
|
|
- return null;
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ocrErr(StudentScoreImageDto dto,String err) {
|
|
|
+ updateAnswerErr(dto.getStudentScoreId(),err);
|
|
|
+ studentService.updateStatus(dto.getStudentId(), DataStatus.PROCESSING, DataStatus.FAILED);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StudentScoreEntity findOneToAiMarking() {
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
+ lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
|
|
|
+ lw.eq(StudentScoreEntity::getScoreStatus, DataStatus.WAITING);
|
|
|
+ wrapper.last("LIMIT 1");
|
|
|
+ return this.getOne(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public void aiMarking(StudentScoreEntity score) {
|
|
|
+ AiMarkingDto dto = new AiMarkingDto();
|
|
|
+ dto.setScoreInfo(score);
|
|
|
+ try {
|
|
|
+ OrgInfo org = solarService.getOrgList().get(0);
|
|
|
+ QuestionEntity q = questionService.getById(score.getQuestionId());
|
|
|
+ if (q == null) {
|
|
|
+ throw new StatusException("未找到试题信息");
|
|
|
+ }
|
|
|
+ AutoScoreRequest req = new AutoScoreRequest();
|
|
|
+ req.setQuestionBody(q.getContent());
|
|
|
+ req.setStandardAnswer(q.getAnswer());
|
|
|
+ req.setStudentAnswer(score.getAnswer());
|
|
|
+ req.setSubjectName(q.getSubjectName());
|
|
|
+ Double ret = aiMarkingDispose(dto, org, req);
|
|
|
+ if (ret != null) {
|
|
|
+ updateScore(score.getId(), Calculator.multiply(q.getFullScore(), ret));
|
|
|
+ if (allSuccess(score.getStudentId())) {
|
|
|
+ studentService.updateStatus(score.getStudentId(), DataStatus.PROCESSING, DataStatus.SUCCESS);
|
|
|
}
|
|
|
}else {
|
|
|
- ocrErr(dto);
|
|
|
- return null;
|
|
|
+ aiScoreErr(dto, "aiScore异常");
|
|
|
}
|
|
|
+ } catch (Exception e) {
|
|
|
+ aiScoreErr(dto, e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private boolean allSuccess(Long studentId) {
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
+ lw.eq(StudentScoreEntity::getStudentId, studentId);
|
|
|
+ List<StudentScoreEntity> list = this.list(wrapper);
|
|
|
+ for (StudentScoreEntity s : list) {
|
|
|
+ if (!DataStatus.SUCCESS.equals(s.getScoreStatus())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Double aiMarkingDispose(AiMarkingDto dto, OrgInfo org, AutoScoreRequest req) {
|
|
|
+ SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
|
|
|
+ try {
|
|
|
+ return aiService.autoScore(req, signature);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("aiScore异常", e);
|
|
|
+ if (e instanceof RetrofitResponseError) {
|
|
|
+ RetrofitResponseError tem = (RetrofitResponseError) e;
|
|
|
+ if (tem.getCode() == 503) {
|
|
|
+ if (dto.getRetry() <= 3) {
|
|
|
+ try {
|
|
|
+ Thread.sleep(3000);
|
|
|
+ } catch (InterruptedException e1) {
|
|
|
+ }
|
|
|
+ dto.setRetry(dto.getRetry() + 1);
|
|
|
+ return aiMarkingDispose(dto, org, req);
|
|
|
+ } else {
|
|
|
+ throw new StatusException("重试次数过多");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateScore(Long id, Double aiScore) {
|
|
|
+ UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
+ LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
+ lw.set(StudentScoreEntity::getScoreStatus, DataStatus.SUCCESS);
|
|
|
+ lw.set(StudentScoreEntity::getAiScore, aiScore);
|
|
|
+ lw.eq(StudentScoreEntity::getId, id);
|
|
|
+ this.update(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void aiScoreErr(AiMarkingDto dto, String err) {
|
|
|
+ updateScoreErr(dto.getScoreInfo().getId(), err);
|
|
|
+ studentService.updateStatus(dto.getScoreInfo().getStudentId(), DataStatus.PROCESSING, DataStatus.FAILED);
|
|
|
+ }
|
|
|
|
|
|
- private void ocrErr(StudentScoreImageDto dto) {
|
|
|
- updateAnswerErr(dto.getStudentScoreId(), "ocr异常");
|
|
|
- studentService.updateStatus(dto.getStudentId(), DataStatus.PROCESSING, DataStatus.FAILED);
|
|
|
+ private void updateScoreErr(Long id, String err) {
|
|
|
+ UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
+ LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
+ lw.set(StudentScoreEntity::getScoreStatus, DataStatus.FAILED);
|
|
|
+ lw.set(StudentScoreEntity::getErrMsg, err);
|
|
|
+ lw.eq(StudentScoreEntity::getId, id);
|
|
|
+ this.update(wrapper);
|
|
|
}
|
|
|
}
|