123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- package cn.com.qmth.am.service.impl;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.nio.charset.StandardCharsets;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Comparator;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.apache.commons.collections4.CollectionUtils;
- import org.apache.commons.io.FileUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.qmth.boot.core.ai.model.llm.StandardAnswer;
- import com.qmth.boot.core.exception.StatusException;
- import com.qmth.boot.tools.excel.ExcelReader;
- import com.qmth.boot.tools.excel.enums.ExcelType;
- import com.qmth.boot.tools.excel.model.DataMap;
- import cn.com.qmth.am.bean.ImageSlice;
- import cn.com.qmth.am.bean.ImportResult;
- import cn.com.qmth.am.config.SysProperty;
- import cn.com.qmth.am.dao.QuestionDao;
- import cn.com.qmth.am.entity.QuestionEntity;
- import cn.com.qmth.am.enums.ImportFileName;
- import cn.com.qmth.am.service.QuestionService;
- @Service
- public class QuestionServiceImpl extends ServiceImpl<QuestionDao, QuestionEntity> implements QuestionService {
- private Pattern scoreRex = Pattern.compile("\\[\\[([0-9][0-9]*(.[0-9]+){0,1})分\\]\\]");
- private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码", "科目名称", "大题号", "小题号", "满分", "试题内容",
- "试题答案", "作答坐标" };
- @Autowired
- private SysProperty sysProperty;
- @Autowired
- private QuestionService questionService;
- @Override
- public void importQuestion() {
- File dir = new File(sysProperty.getDataDir());
- File[] fs = dir.listFiles();
- if (fs == null || fs.length == 0) {
- return;
- }
- for (File file : fs) {
- if (!file.isFile() || !file.getName().equals(ImportFileName.QUESTION_IMPORT.getName())) {
- continue;
- }
- InputStream inputStream = null;
- ImportResult ret = null;
- try {
- inputStream = new FileInputStream(file);
- ret = questionService.disposeFile(inputStream);
- } catch (Exception e) {
- String errMsg;
- if (e instanceof FileNotFoundException) {
- errMsg = "未找到文件:" + file.getAbsolutePath();
- } else {
- errMsg = "系统错误:" + e.getMessage();
- }
- ret = new ImportResult(errMsg);
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException e) {
- }
- }
- }
- moveFile(dir, file, ret);
- }
- }
- private void moveFile(File dir, File file, ImportResult ret) {
- try {
- boolean succss = CollectionUtils.isEmpty(ret.getErrMsg());
- if (succss) {
- File sucDir = new File(dir.getAbsoluteFile() + "/success/");
- if (!sucDir.exists()) {
- sucDir.mkdir();
- }
- File targetFile = new File(sucDir.getAbsoluteFile() + "/" + file.getName());
- if (targetFile.exists()) {
- targetFile.delete();
- }
- FileUtils.copyFile(file, targetFile);
- file.delete();
- String fname = file.getName().substring(0, file.getName().lastIndexOf("."));
- File msgFile = new File(sucDir.getAbsoluteFile() + "/" + fname + "_info.txt");
- if (msgFile.exists()) {
- msgFile.delete();
- }
- FileUtils.write(msgFile, ret.getCountInfo(), "utf-8");
- } else {
- File sucDir = new File(dir.getAbsoluteFile() + "/failed/");
- if (!sucDir.exists()) {
- sucDir.mkdir();
- }
- File targetFile = new File(sucDir.getAbsoluteFile() + "/" + file.getName());
- if (targetFile.exists()) {
- targetFile.delete();
- }
- FileUtils.copyFile(file, targetFile);
- file.delete();
- String fname = file.getName().substring(0, file.getName().lastIndexOf("."));
- File msgFile = new File(sucDir.getAbsoluteFile() + "/" + fname + "_info.txt");
- if (msgFile.exists()) {
- msgFile.delete();
- }
- FileUtils.writeLines(msgFile, StandardCharsets.UTF_8.name(), ret.getErrMsg());
- }
- } catch (IOException e) {
- throw new StatusException("文件处理出错", e);
- }
- }
- private String errorMsg(int lineNum, String msg) {
- return "第" + lineNum + "行 " + msg;
- }
- private String trimAndNullIfBlank(String s) {
- if (StringUtils.isBlank(s)) {
- return null;
- }
- return s.trim();
- }
- @Transactional
- @Override
- public ImportResult disposeFile(InputStream inputStream) {
- List<DataMap> lineList = null;
- ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0);
- try {
- lineList = reader.getDataMapList();
- } catch (Exception e) {
- throw new StatusException("Excel 解析失败");
- }
- if (!Arrays.equals(EXCEL_HEADER, reader.getColumnNames())) {
- throw new StatusException("Excel表头错误");
- }
- if (CollectionUtils.isEmpty(lineList)) {
- throw new StatusException("Excel无内容");
- }
- if (100001 < lineList.size()) {
- throw new StatusException("数据行数不能超过100000");
- }
- List<QuestionEntity> ss = new ArrayList<>();
- ImportResult ret = new ImportResult();
- List<String> failRecords = new ArrayList<>();
- ret.setErrMsg(failRecords);
- for (int i = 0; i < lineList.size(); i++) {
- DataMap line = lineList.get(i);
- StringBuilder msg = new StringBuilder();
- QuestionEntity imp = new QuestionEntity();
- String examId = trimAndNullIfBlank(line.get(EXCEL_HEADER[0]));
- if (StringUtils.isBlank(examId)) {
- msg.append(" 考试ID不能为空");
- } else if (examId.length() > 20) {
- msg.append(" 考试ID不能超过20个字符");
- } else {
- try {
- Long examIdVal = Long.parseLong(examId);
- imp.setExamId(examIdVal);
- } catch (NumberFormatException e) {
- msg.append(" 考试ID只能是数字");
- }
- }
- String subjectCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[1]));
- if (StringUtils.isBlank(subjectCode)) {
- msg.append(" 科目代码不能为空");
- } else if (subjectCode.length() > 100) {
- msg.append(" 科目代码不能超过100个字符");
- }
- imp.setSubjectCode(subjectCode);
- String subjectName = trimAndNullIfBlank(line.get(EXCEL_HEADER[2]));
- if (StringUtils.isBlank(subjectName)) {
- msg.append(" 科目名称不能为空");
- } else if (subjectName.length() > 100) {
- msg.append(" 科目名称不能超过100个字符");
- }
- imp.setSubjectName(subjectName);
- String mainNum = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
- if (StringUtils.isBlank(mainNum)) {
- msg.append(" 大题号不能为空");
- } else if (mainNum.length() > 10) {
- msg.append(" 大题号不能超过10个字符");
- } else {
- try {
- Integer mainNumVal = Integer.parseInt(mainNum);
- if (mainNumVal <= 0) {
- msg.append(" 大题号必须大于0");
- }
- imp.setMainNumber(mainNumVal);
- } catch (NumberFormatException e) {
- msg.append(" 大题号格式错误");
- }
- }
- String subNum = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
- if (StringUtils.isBlank(subNum)) {
- msg.append(" 小题号不能为空");
- } else if (subNum.length() > 10) {
- msg.append(" 小题号不能超过10个字符");
- }
- imp.setSubNumber(subNum);
- String fullScore = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
- if (StringUtils.isBlank(fullScore)) {
- msg.append(" 满分不能为空");
- } else if (fullScore.length() > 10) {
- msg.append(" 满分不能超过10个字符");
- } else {
- try {
- Double fullScoreVal = Double.parseDouble(fullScore);
- if (fullScoreVal <= 0) {
- msg.append(" 满分必须大于0");
- }
- imp.setFullScore(fullScoreVal);
- } catch (NumberFormatException e) {
- msg.append(" 满分格式错误");
- }
- }
- String content = trimAndNullIfBlank(line.get(EXCEL_HEADER[6]));
- if (StringUtils.isBlank(content)) {
- msg.append(" 试题内容不能为空");
- }
- imp.setContent(content);
- String answer = trimAndNullIfBlank(line.get(EXCEL_HEADER[7]));
- if (StringUtils.isBlank(answer)) {
- msg.append(" 试题答案不能为空");
- }
- imp.setAnswer(getStandardAnswer(answer));
- String imageSlice = trimAndNullIfBlank(line.get(EXCEL_HEADER[8]));
- if (StringUtils.isBlank(imageSlice)) {
- msg.append(" 作答坐标不能为空");
- } else if (imageSlice.length() > 1000) {
- msg.append(" 作答坐标不能超过1000个字符");
- } else {
- List<ImageSlice> val = getImageSlice(imageSlice);
- if (val == null) {
- msg.append(" 作答坐标格式有误");
- } else {
- imp.setImageSlice(val);
- }
- }
- if (msg.length() > 0) {
- failRecords.add(errorMsg(i + 1, msg.toString()));
- } else {
- ss.add(imp);
- }
- }
- if (CollectionUtils.isNotEmpty(failRecords)) {
- return ret;
- }
- try {
- saveQuestionBatch(ret, ss);
- } catch (Exception e) {
- failRecords.add("系统错误:" + e.getMessage());
- }
- return ret;
- }
- private List<ImageSlice> getImageSlice(String s) {
- if (StringUtils.isBlank(s)) {
- return null;
- }
- s = s.trim();
- if (StringUtils.isBlank(s)) {
- return null;
- }
- try {
- List<ImageSlice> list = new ArrayList<>();
- String[] items = s.split(",");
- for (int i = 0; i < items.length; i++) {
- String item = items[i];
- item = item.trim();
- String[] config = item.split(":");
- if (config.length != 5) {
- return null;
- }
- int iVal = Integer.valueOf(config[0]);
- int x = Integer.valueOf(config[1]);
- int y = Integer.valueOf(config[2]);
- int w = Integer.valueOf(config[3]);
- int h = Integer.valueOf(config[4]);
- ImageSlice ret = new ImageSlice();
- ret.setH(h);
- ret.setI(iVal);
- ret.setW(w);
- ret.setX(x);
- ret.setY(y);
- if (ret.getH() == null || ret.getI() == null || ret.getW() == null || ret.getX() == null
- || ret.getY() == null || ret.getI() < 0) {
- return null;
- }
- list.add(ret);
- }
- if (list.size() == 0) {
- return null;
- }
- list.sort(new Comparator<ImageSlice>() {
- @Override
- public int compare(ImageSlice o1, ImageSlice o2) {
- long c1 = o1.getI();
- long c2 = o2.getI();
- if (c1 < c2) {
- return -1;
- } else if (c1 > c2) {
- return 1;
- } else {
- return 0;
- }
- }
- });
- return list;
- } catch (Exception e) {
- return null;
- }
- }
- private List<StandardAnswer> getStandardAnswer(String s) {
- if (StringUtils.isBlank(s)) {
- return null;
- }
- s = s.trim();
- if (StringUtils.isBlank(s)) {
- return null;
- }
- List<StandardAnswer> list = new ArrayList<>();
- Matcher matcher = scoreRex.matcher(s);
- int start=0;
- Double score = null;
- while (matcher.find()) {
- if(start!=0) {
- StandardAnswer a=new StandardAnswer();
- list.add(a);
- a.setScore(score);
- a.setContent(s.substring(start,matcher.start()));
- }
- try {
- score=Double.valueOf(matcher.group(1));
- } catch (NumberFormatException e) {
- throw new StatusException("分数格式有误");
- }
- checkScore(score);
- start=matcher.end();
- }
- if(start<s.length()) {
- StandardAnswer a=new StandardAnswer();
- list.add(a);
- a.setScore(score);
- checkScore(score);
- a.setContent(s.substring(start,s.length()));
- }
- return list;
- }
-
- private void checkScore(Double score) {
- if(score==null) {
- throw new StatusException("分数不能为空");
- }
- }
- private void saveQuestionBatch(ImportResult ret, List<QuestionEntity> ss) {
- if (CollectionUtils.isEmpty(ss)) {
- ret.setCountInfo("新增数量:0,更新数量:0");
- return;
- }
- List<QuestionEntity> all = this.list();
- Map<String, QuestionEntity> old = new HashMap<>();
- Map<String, QuestionEntity> addMap = new HashMap<>();
- if (CollectionUtils.isNotEmpty(all)) {
- for (QuestionEntity s : all) {
- String key = s.getExamId() + "-" + s.getSubjectCode() + "-" + s.getMainNumber() + "-"
- + s.getSubNumber();
- old.put(key, s);
- }
- }
- List<QuestionEntity> adds = new ArrayList<>();
- List<QuestionEntity> updates = new ArrayList<>();
- for (QuestionEntity s : ss) {
- String key = s.getExamId() + "-" + s.getSubjectCode() + "-" + s.getMainNumber() + "-" + s.getSubNumber();
- if (old.get(key) == null) {
- QuestionEntity add = addMap.get(key);
- if (add != null) {
- add.setSubjectName(s.getSubjectName());
- add.setFullScore(s.getFullScore());
- add.setImageSlice(s.getImageSlice());
- add.setContent(s.getContent());
- add.setAnswer(s.getAnswer());
- } else {
- addMap.put(key, s);
- adds.add(s);
- }
- } else {
- QuestionEntity up = old.get(key);
- up.setSubjectName(s.getSubjectName());
- up.setFullScore(s.getFullScore());
- up.setImageSlice(s.getImageSlice());
- up.setContent(s.getContent());
- up.setAnswer(s.getAnswer());
- updates.add(up);
- }
- }
- if (CollectionUtils.isNotEmpty(adds)) {
- saveBatch(adds);
- }
- if (CollectionUtils.isNotEmpty(updates)) {
- updateBatchById(updates);
- }
- ret.setCountInfo("新增数量:" + adds.size() + ",更新数量:" + updates.size());
- }
- @Override
- public List<QuestionEntity> findByExamId(Long examId) {
- QueryWrapper<QuestionEntity> wrapper = new QueryWrapper<>();
- LambdaQueryWrapper<QuestionEntity> lw = wrapper.lambda();
- lw.eq(QuestionEntity::getExamId, examId);
- lw.orderByAsc(QuestionEntity::getSubjectCode).orderByAsc(QuestionEntity::getMainNumber)
- .orderByAsc(QuestionEntity::getSubNumber);
- return this.list(wrapper);
- }
- @Transactional
- @Override
- public void removeBy(Long examId, String subjectCode) {
- QueryWrapper<QuestionEntity> wrapper = new QueryWrapper<>();
- LambdaQueryWrapper<QuestionEntity> lw = wrapper.lambda();
- if (subjectCode != null) {
- lw.eq(QuestionEntity::getSubjectCode, subjectCode);
- }
- lw.eq(QuestionEntity::getExamId, examId);
- this.remove(wrapper);
- }
- }
|