diff --git a/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java b/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java index b8ead246..2aff0450 100644 --- a/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java +++ b/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java @@ -196,4 +196,87 @@ public class BeanUtil extends cn.hutool.core.bean.BeanUtil { }).collect(Collectors.toList()); } + /** + * 复制集合中的Bean属性
+ * 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 + * + * @param collection 原Bean集合 + * @param targetType 目标Bean类型 + * @param copyOptions 拷贝选项 + * @param Bean类型 + * @return 复制后的List + * @since 5.6.4 + */ + public static List copyToListSupportExtMap(Collection collection, Class targetType, CopyOptions copyOptions) { + if (null == collection) { + return null; + } + if (collection.isEmpty()) { + return new ArrayList<>(0); + } + return collection.stream().map((source) -> { + final T target = ReflectUtil.newInstanceIfPossible(targetType); + copyProperties(source, target, copyOptions); + source.getTails().forEach((k, v) -> { + target.set(k, v); + }); + return target; + }).collect(Collectors.toList()); + } + + /** + * 复制集合中的Bean属性
+ * 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 + * + * @param collection 原Bean集合 + * @param targetType 目标Bean类型 + * @param Bean类型 + * @return 复制后的List + * @since 5.6.4 + */ + public static List copyToListSupportExtMap(Collection collection, Class targetType) { + if (null == collection) { + return null; + } + if (collection.isEmpty()) { + return new ArrayList<>(0); + } + return collection.stream().map((source) -> { + final T target = ReflectUtil.newInstanceIfPossible(targetType); + copyProperties(source, target); + source.getTails().forEach((k, v) -> { + target.set(k, v); + }); + return target; + }).collect(Collectors.toList()); + } + + /** + * 复制集合中的Bean属性
+ * 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。 + * + * @param source 原Bean + * @param target 目标Bean + * @return 复制后的目标Bean + * @since 5.6.4 + */ + public static R copyAllPropertiesSupportExtMap(T source, R target) { +// copyProperties(source, target); + + for (Field declaredField : source.getClass().getDeclaredFields()) { + String name = declaredField.getName(); + declaredField.setAccessible(true); + try { + target.set(name, declaredField.get(source)); + } catch (IllegalAccessException e) { + }finally { + declaredField.setAccessible(false); + } + } +// for (Map.Entry so : source.getTails().entrySet()) { +// target.set(so.getKey(), so.getValue()); +// } + return target; + } + } diff --git a/web/pom.xml b/web/pom.xml index d6fcce84..7f9858db 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -246,6 +246,33 @@ org.quartz-scheduler quartz + + org.apache.xmlgraphics + batik-all + 1.14 + pom + + + commons-io + commons-io + + + org.slf4j + slf4j-api + + + + + net.arnx + wmf2svg + 0.9.11 + + + org.slf4j + slf4j-api + + + diff --git a/web/src/main/java/cn/jlw/util/wmf2png/WmfToPng.java b/web/src/main/java/cn/jlw/util/wmf2png/WmfToPng.java new file mode 100644 index 00000000..d6acd348 --- /dev/null +++ b/web/src/main/java/cn/jlw/util/wmf2png/WmfToPng.java @@ -0,0 +1,207 @@ +package cn.jlw.util.wmf2png; + +import net.arnx.wmf2svg.gdi.svg.SvgGdi; +import net.arnx.wmf2svg.gdi.wmf.WmfParser; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.ImageTranscoder; +import org.apache.batik.transcoder.image.JPEGTranscoder; +import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.*; +import java.util.zip.GZIPOutputStream; + +public class WmfToPng { + + enum FileType { + SVG, + PNG; + } + + public static void main(String[] args) { + + String result = convert("d:\\80814378377.wmf"); + System.out.println(result); + + } + + @Deprecated + public static String convert2(String path) throws TranscoderException, + IOException { + String wmfPath = path; + File wmf = new File(wmfPath); + FileInputStream wmfStream = new FileInputStream(wmf); + ByteArrayOutputStream imageOut = new ByteArrayOutputStream(); + int noOfByteRead = 0; + while ((noOfByteRead = wmfStream.read()) != -1) { + imageOut.write(noOfByteRead); + } + imageOut.flush(); + wmfStream.close(); + // wmf 转换为svg + WMFTranscoder transcoder = new WMFTranscoder(); + // TranscodingHints hints = new TranscodingHints(); + // hints.put(WMFTranscoder.KEY_HEIGHT, 1000f); + // hints.put(WMFTranscoder.KEY_WIDTH, 1500f); + // transcoder.setTranscodingHints(hints); + TranscoderInput input = new TranscoderInput(new ByteArrayInputStream( + imageOut.toByteArray())); + ByteArrayOutputStream svg = new ByteArrayOutputStream(); + TranscoderOutput output = new TranscoderOutput(svg); + transcoder.transcode(input, output); + String svgFile = StringUtils.replace(wmfPath, "wmf", "svg"); + FileOutputStream fileOut = new FileOutputStream(svgFile); + fileOut.write(svg.toByteArray()); + fileOut.flush(); + fileOut.close(); + svg.close(); + // svg -> jpg + ImageTranscoder it = new JPEGTranscoder(); + it.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(0.5f)); + ByteArrayOutputStream jpg = new ByteArrayOutputStream(); + it.transcode(new TranscoderInput(new ByteArrayInputStream(svg + .toByteArray())), new TranscoderOutput(jpg)); + String jpgFile = StringUtils.replace(wmfPath, "wmf", "jpg"); + FileOutputStream jpgOut = new FileOutputStream(jpgFile); + jpgOut.write(jpg.toByteArray()); + jpgOut.flush(); + jpgOut.close(); + jpg.close(); + // Filor.deleteFile(svgFile);// 删除掉中间文件 + return jpgFile; + } + + public static String convert(String path, FileType fileType) { + try { + String svgFile = StringUtils.replace(path, "wmf", "svg"); + String filePath = StringUtils.replace(path, "wmf", "png"); + switch (fileType) { + case PNG: + wmfToSvg(path, svgFile); + svgToJpg(svgFile, filePath); + break; + case SVG: + wmfToSvg(path, svgFile); + filePath = svgFile; + break; + } + + return filePath; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + } + public static String convert(String path) { + return convert(path, FileType.PNG); + + } + + /** + * 将svg转化为JPG + * + * @param src + * @param dest + */ + public static String svgToJpg(String src, String dest) { + FileOutputStream jpgOut = null; + FileInputStream svgStream = null; + ByteArrayOutputStream svgOut = null; + ByteArrayInputStream svgInputStream = null; + ByteArrayOutputStream jpg = null; + try { + // 获取到svg文件 + File svg = new File(src); + svgStream = new FileInputStream(svg); + svgOut = new ByteArrayOutputStream(); + // 获取到svg的stream + int noOfByteRead = 0; + while ((noOfByteRead = svgStream.read()) != -1) { + svgOut.write(noOfByteRead); + } + JPEGTranscoder it = new JPEGTranscoder(); + it.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(0.9f)); + it.addTranscodingHint(ImageTranscoder.KEY_WIDTH, new Float(100)); + jpg = new ByteArrayOutputStream(); + svgInputStream = new ByteArrayInputStream(svgOut.toByteArray()); + it.transcode(new TranscoderInput(svgInputStream), + new TranscoderOutput(jpg)); + jpgOut = new FileOutputStream(dest); + jpgOut.write(jpg.toByteArray()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (svgInputStream != null) { + svgInputStream.close(); + } + if (jpg != null) { + jpg.close(); + } + if (svgStream != null) { + svgStream.close(); + } + if (svgOut != null) { + svgOut.close(); + } + if (jpgOut != null) { + jpgOut.flush(); + jpgOut.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return dest; + } + + /** + * 将wmf转换为svg + * + * @param src + * @param dest + */ + public static void wmfToSvg(String src, String dest) { + boolean compatible = false; + try { + InputStream in = new FileInputStream(src); + WmfParser parser = new WmfParser(); + final SvgGdi gdi = new SvgGdi(compatible); + parser.parse(in, gdi); + + Document doc = gdi.getDocument(); + OutputStream out = new FileOutputStream(dest); + if (dest.endsWith(".svgz")) { + out = new GZIPOutputStream(out); + } + + output(doc, out); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void output(Document doc, OutputStream out) throws Exception { + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + "-//W3C//DTD SVG 1.0//EN"); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + out.flush(); + out.close(); + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninLogDao.java b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninLogDao.java index fb50be98..59c0738b 100644 --- a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninLogDao.java +++ b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninLogDao.java @@ -1,6 +1,7 @@ package com.ibeetl.jlw.dao; import com.ibeetl.jlw.entity.TeacherOpenCourseStudentSigninLog; +import com.ibeetl.jlw.entity.vo.SummaryBySigninSettingIdsVO; import com.ibeetl.jlw.entity.vo.TeacherOpenCourseStudentSigninLogSummaryVO; import com.ibeetl.jlw.web.query.TeacherOpenCourseStudentSigninLogQuery; import org.beetl.sql.core.engine.PageQuery; @@ -36,4 +37,6 @@ public interface TeacherOpenCourseStudentSigninLogDao extends BaseMapper summaryBySigninSettingIds(String signinSettingIds); } diff --git a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninSettingDao.java b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninSettingDao.java index a9bc3b73..cdea4a2d 100644 --- a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninSettingDao.java +++ b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseStudentSigninSettingDao.java @@ -5,9 +5,8 @@ import com.ibeetl.jlw.web.query.TeacherOpenCourseStudentSigninSettingQuery; import org.beetl.sql.core.engine.PageQuery; import org.beetl.sql.mapper.BaseMapper; import org.beetl.sql.mapper.annotation.SqlResource; -import org.springframework.stereotype.Repository; import org.beetl.sql.mapper.annotation.Update; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Repository; import java.util.List; @@ -29,4 +28,5 @@ public interface TeacherOpenCourseStudentSigninSettingDao extends BaseMapper getByIds(String ids); List getValuesByQuery(TeacherOpenCourseStudentSigninSettingQuery teacherOpenCourseStudentSigninSettingQuery); List getValuesByQueryNotWithPermission(TeacherOpenCourseStudentSigninSettingQuery teacherOpenCourseStudentSigninSettingQuery); + } diff --git a/web/src/main/java/com/ibeetl/jlw/entity/MyXWPFParagraph.java b/web/src/main/java/com/ibeetl/jlw/entity/MyXWPFParagraph.java new file mode 100644 index 00000000..ad6c61cb --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/MyXWPFParagraph.java @@ -0,0 +1,276 @@ +package com.ibeetl.jlw.entity; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReUtil; +import cn.jlw.util.wmf2png.WmfToPng; +import com.alibaba.excel.util.StringUtils; +import com.google.common.collect.Maps; +import com.microsoft.schemas.vml.impl.CTShapeImpl; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.util.Strings; +import org.apache.poi.xwpf.usermodel.*; +import org.apache.xmlbeans.XmlObject; + +import javax.xml.namespace.QName; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.util.*; + +/** + *

+ * 扩展的Poi XWPFParagraph 类 + *

+ * + * @author mlx + * @date 2022/5/29 + * @modified + */ +@Data +@Slf4j +public class MyXWPFParagraph { + + private XWPFParagraph xwpfParagraph; + + public MyXWPFParagraph() { + + } + + /** + * 重新改写 XWPFParagraph 中的getText方法,适用于现有项目 + * @param absoluteDirPath 文件夹的绝对路径 + * @param wordFileName word文档临时文件的名称 + * @return + */ + public String getText(String absoluteDirPath, String wordFileName) { + StringBuffer out = new StringBuffer(); + Iterator i$ = this.xwpfParagraph.getIRuns().iterator(); + + while(i$.hasNext()) { + IRunElement run = (IRunElement)i$.next(); + if (run instanceof XWPFRun) { + XWPFRun xRun = (XWPFRun)run; + // 获取该段落所有图片 + List pics = xRun.getEmbeddedPictures(); + + // 只处理该段落的图片,比全文检索更优 + if (ObjectUtil.isNotEmpty(pics)) { + for (int i = 0; i < pics.size(); i++) { + XWPFPictureData pictureData = pics.get(i).getPictureData(); + // 获取到图片的网络地址 + String picHttpUrl = createFileReturnHttpUrl(absoluteDirPath, + getNewFileName(wordFileName, i, pictureData), pictureData.getData(), true); + out.append(String.format("", picHttpUrl)); + } + } + if (!xRun.getCTR().isSetRsidDel()) { + List> xwpfPictureDataList = getXWPFPictureDataList(xRun,true); + Map> styleMap = getStyle(xRun); + for (int i = 0; i < xwpfPictureDataList.size(); i++) { + for (int j = 0; j < xwpfPictureDataList.get(i).values().size(); j++) { + XWPFPictureData pictureData = new ArrayList<>(xwpfPictureDataList.get(i).values()).get(j); + // 资源id + String rid = (new ArrayList<>(xwpfPictureDataList.get(i).keySet())).get(j); + // 获取到图片的网络地址 + String picHttpUrl = createFileReturnHttpUrl(absoluteDirPath, + getNewFileName(wordFileName, 0, pictureData), pictureData.getData(), true); + out.append(String.format("" + , picHttpUrl + , styleMap.getOrDefault(rid, Maps.newHashMap()).getOrDefault("width", "") + , styleMap.getOrDefault(rid, Maps.newHashMap()).getOrDefault("height", "") + )); + } + } + + if (ObjectUtil.isEmpty(xwpfPictureDataList)) { + out.append(xRun); + } + } + } else if (run instanceof XWPFSDT) { + out.append(((XWPFSDT)run).getContent().getText()); + } else { + out.append(run); + } + } + + out.append(this.xwpfParagraph.getFootnoteText()); + + return out.toString(); + } + + /** + * 通过id获取图片 + * @param blipID 资源id + * @return + */ + private XWPFPictureData getParagraphPictures(String blipID) { + if (this.xwpfParagraph.getPart() instanceof XWPFDocument) { + XWPFDocument part = (XWPFDocument) this.xwpfParagraph.getPart(); + return part.getPictureDataByID(blipID); + } + return null; + } + + /** + * 获取imagedata标签 + * @param xRun + * @return + */ + private XmlObject[] getImageXmlObjectList(XWPFRun xRun) { + return xRun.getCTR().selectPath("declare namespace v=\"urn:schemas-microsoft-com:vml\" .//v:imagedata"); + } + + /** + * 获取id + * @param xmlObject + * @return + */ + private String getId(XmlObject xmlObject) { + XmlObject temp = xmlObject.selectAttribute(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id", "r")); + return null != temp && temp.validate() ? temp.getDomNode().getNodeValue() : ""; + } + + /** + * 获取id + * @param xRun + * @return + */ + private String getId(XWPFRun xRun) { + return getId(xRun.getCTR()); + } + + /** + * 获取样式 + * @param xRun + * @return 图片id对应的样式信息 + */ + private Map> getStyle(XWPFRun xRun) { + Map> res = new HashMap<>(10); + try { + XmlObject[] xmlObjects = xRun.getCTR().selectPath("declare namespace v='urn:schemas-microsoft-com:vml' .//v:shape"); + + for (XmlObject xmlObject : xmlObjects) { + String id = ReUtil.get("rId\\w+", xmlObject.toString(), 0); + String style = ((CTShapeImpl) xmlObject).xgetStyle().getStringValue(); + if (StringUtils.isNotBlank(id) && StringUtils.isNotBlank(style)) { + res.put(id, listConvertToMap(style.replaceAll("p.", "px").split("[:=;]"))); + } + } + }catch (Exception e) { + log.error(e.getMessage()); + } + return res; + } + + /** + * list转Map + * @param arr + * @return + */ + private static Map listConvertToMap(String[] arr) { + Map result = new HashMap<>(); + if (ObjectUtil.isNotEmpty(arr) && arr.length >= 2) { + for (int i = 0; i < arr.length - 1; i+=2) { + result.put(arr[i], arr[i + 1]); + } + } + return result; + } + + /** + * WPS编码格式下的图片获取 + * @param xRun + * @return + */ + private List getXWPFPictureDataList(XWPFRun xRun) { + List result = new ArrayList<>(); + List> xwpfPictureDataList = getXWPFPictureDataList(xRun, true); + for (Map stringXWPFPictureDataMap : xwpfPictureDataList) { + result.add((XWPFPictureData) stringXWPFPictureDataMap.values().toArray()[0]); + } + return result; + } + + /** + * WPS编码格式下的图片获取 + * @param xRun + * @return + */ + private List> getXWPFPictureDataList(XWPFRun xRun, boolean is) { + List> pics = new ArrayList(); + XmlObject[] picts = getImageXmlObjectList(xRun); + XmlObject[] var4 = picts; + int var5 = picts.length; + + for(int var6 = 0; var6 < var5; ++var6) { + XmlObject pict = var4[var6]; + String id = getId(pict); + if (Strings.isNotBlank(id)) { + pics.add(MapUtil.of(id, getParagraphPictures(id))); + } + } + return pics; + } + + + /** + * 创建文件,并获取该文件的网络地址 + * @param absolutePath 绝对路径 + * @param fileName 文件名称 + * @param data 文件字节集 + * @return + */ + public String createFileReturnHttpUrl(String absolutePath, String fileName, byte[] data) { + return createFileReturnHttpUrl(absolutePath, fileName, data, false); + } + + /** + * + * @param absolutePath 文件夹相对路径 + * @param fileName 图片名称 + * @param data 图片数据 + * @param tryConvertWmf2Png 尝试 wmf是否转换成png + * @return + */ + @SneakyThrows + public String createFileReturnHttpUrl(String absolutePath, String fileName, byte[] data, boolean tryConvertWmf2Png) { + File file = new File(absolutePath); + final String filePath = absolutePath + fileName; + String resultFilePath = filePath; + if (!file.exists()) {file.mkdirs();} + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)); + bos.write(data); + bos.close(); + if (tryConvertWmf2Png && fileName.toLowerCase().contains("wmf")) { + /** 原文件修改为png后缀 */ +// resultFilePath = absolutePath + fileName.replaceAll("\\.[^\\.]+$", ".png"); + resultFilePath = WmfToPng.convert(filePath); + /** 转换完成以后,删除WMF文件 */ + FileUtils.forceDelete(new File(filePath)); + } +// return FileEntity.absoluteUrl2HttpUrl(resultFilePath); + + return resultFilePath; + } + + /** + * 获取文件的新命名 + * @param tempFileName 临时文件的名称 + * @param index 循环中的索引 + * @param pictureData poi 图片对象 + * @return + */ + public String getNewFileName(String tempFileName, int index, XWPFPictureData pictureData) { + // 文件全名称 +// String rawName = pictureData.getFileName(); + // 文件类型后缀 + String fileExt = pictureData.suggestFileExtension(); + // 获取word里图片的关系id + String rid = pictureData.getParent().getRelationId(pictureData); + return String.format("%s_%s_%s.%s", tempFileName , rid , index , fileExt); + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseStudentSigninSetting.java b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseStudentSigninSetting.java index d3a41245..d8999012 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseStudentSigninSetting.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseStudentSigninSetting.java @@ -129,7 +129,11 @@ public class TeacherOpenCourseStudentSigninSetting extends BaseEntity{ // 学生总人数 - @FetchSql("select count(1) from student t where find_in_set(t.class_id, #schoolClassIdsRender#) ") + @FetchSql("select count(1) from teacher_open_course_merge_student t " + + "inner join student ta on ta.student_id = t.student_id and ta.student_status = 1 " + + "where 1 " + + "and t.teacher_open_course_id = #teacherOpenCourseId# " + + "and find_in_set(ta.class_id, #schoolClassIdsRender#) ") @UpdateIgnore @InsertIgnore private Integer signinStudentTotalCount; diff --git a/web/src/main/java/com/ibeetl/jlw/entity/vo/SummaryBySigninSettingIdsVO.java b/web/src/main/java/com/ibeetl/jlw/entity/vo/SummaryBySigninSettingIdsVO.java new file mode 100644 index 00000000..e35b0987 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/vo/SummaryBySigninSettingIdsVO.java @@ -0,0 +1,39 @@ +package com.ibeetl.jlw.entity.vo; + +import com.ibeetl.admin.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 功能描述:
+ * + * @author: mlx + * @description: + * @date: 2022/12/26 19:53 + * @version: 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SummaryBySigninSettingIdsVO extends BaseEntity { + + /** + * 签到配置ID + */ + private Long teacherOpenCourseStudentSigninSettingId; + /** + * 签到总人数 + */ + private Float signTotalCount; + /** + * 签到人数 + */ + private Float signCount; + /** + * 缺席人数 + */ + private Float signLackCount; + /** + * 到课率 100最大 + */ + private Float attendRate; +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java b/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java index 4b5ea55c..a45f65f2 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java @@ -19,8 +19,10 @@ import com.ibeetl.jlw.entity.UniversitiesCollegesJurisdictionExperimentalSystem; import com.ibeetl.jlw.entity.dto.QuestionSettingDTO; import com.ibeetl.jlw.entity.vo.QuestionTypeCountVO; import com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum; +import com.ibeetl.jlw.service.strategy.StrategyContext; import com.ibeetl.jlw.web.query.CourseInfoQuery; import com.ibeetl.jlw.web.query.ResourcesQuestionQuery; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; @@ -58,6 +60,7 @@ import static java.util.stream.Collectors.joining; */ @Service +@Slf4j @Transactional public class ResourcesQuestionService extends CoreBaseService{ @@ -70,6 +73,8 @@ public class ResourcesQuestionService extends CoreBaseService @Autowired private CorePlatformService platformService; @Autowired private ResourcesQuestionService resourcesQuestionService; + @Autowired private StrategyContext strategyContext; + public PageQuery queryByCondition(PageQuery query){ PageQuery ret = resourcesQuestionDao.queryByCondition(query); queryListAfter(ret.getList()); @@ -768,4 +773,255 @@ public class ResourcesQuestionService extends CoreBaseService dictParser(g); return g; } + +// @SneakyThrows +// public JsonResult importQBankWordTemplate(final Integer isPublic, @NotEmpty List fileEntityList, CoreUser coreUser, String referer) { +// // 公共库题目导入,用于下面的部分验证 +// final String publicReferer = "/jlw/question/importExcel.do"; +// /** 找出段落, 逐段落处理 */ +// AtomicReference inputStream = new AtomicReference<>(); +// AtomicReference xDocument = new AtomicReference<>(); +// +// //装结果 key:卷子访问地址 value:{标题,题目List} +// Map resultMap = new HashMap<>(); +// /** 仅用于存放标题 */ +// final String[] title = { Strings.EMPTY }; +// /** 标题暂存,可能是多个 */ +// final StringBuilder titleStb = new StringBuilder(); +// +// /** 循环文件集合 */ +// Long finalCorSchoolId = corSchoolId; +// fileEntityList.forEach(fileEntity -> { +// /** 上传文件的临时路径 */ +// String importPath = fileEntity.getAbsoluteUrl(); +// /** 新建文件夹绝对路径 */ +// String absoluteDirPath = String.format("%s%s%s%s%s%s", GetFile.p , SE , "filesystem" , SE , "temp" , SE ); +// /** 新建文件的绝对路径 */ +// String absolutePath = absoluteDirPath + fileEntity.getTempName(); +// //各种流 +// try { +// inputStream.set(new FileInputStream(importPath)); +// xDocument.set(new XWPFDocument(inputStream.get())); +// /** 集合拷贝 */ +// List myParagraphs = BeanCopyUtil.copyListProperties(xDocument.get().getParagraphs() , MyXWPFParagraph::new, (t, d) -> { +// d.setXwpfParagraph(t); +// }); +// +// /** 需要存库的题目集合 */ +// List questionList = new CopyOnWriteArrayList<>(); +// /** 题目类型 */ +// AtomicReference typeConcatEnum = new AtomicReference<>(); +// /** 段落类型 */ +// AtomicReference paragraphTypeEnum = new AtomicReference<>(); +// /** 每题分数 */ +// AtomicReference questionScore = new AtomicReference<>(BigDecimal.ZERO); +// /** 题干 */ +// AtomicReference questionStem = new AtomicReference<>(Strings.EMPTY); +// /** 答案 */ +// AtomicReference questionAnswer = new AtomicReference<>(Strings.EMPTY); +// /** 题目实体 */ +// AtomicReference question = new AtomicReference<>(); +// /** 题干计数 */ +// AtomicInteger recordsCount = new AtomicInteger(); +// /** 遍历所有段落 */ +// myParagraphs.forEach(e -> { +// /** 策略中的参数设置 */ +// strategyContext.setText(e.getText(absoluteDirPath, fileEntity.getTempName())).start(c -> { +// /** 第一行 */ +// if (recordsCount.get() == 1 && StringUtils.isBlank(title[0])) { +// /** 暴力取值 */ +// titleStb.append(title[0] = myParagraphs.get(0).getText(absoluteDirPath, fileEntity.getTempName())); +// } +// /** k 策略枚举:v 处理后的段落文字 */ +// c.forEach((k, v) -> { +// /** 记录段落类型, 未查询类型之外的类型 */ +// if (!k.equals(OTHER)) {paragraphTypeEnum.set(k);} +// switch(k) { +// /** 未匹配到的段落 */ +// case OTHER: { +// /** 如果非标题字符,可能是前一个段落的副行。需要追加到相应的段落中 */ +// insertCorrespondingParagraph(typeConcatEnum, paragraphTypeEnum.get() ,v, question, questionStem, questionAnswer); +// } break; +// /** 题目类型,分值,该类型的总分 */ +// case TYPE: { +// /** 题目类型作用域 */ +// String[] sp = v.split(","); +// typeConcatEnum.set(StrategyContext.QuestionTypeConcatEnum.matchText(sp[2])); +// questionScore.set(NumberUtil.toBigDecimal(sp[0]).setScale(1, RoundingMode.HALF_UP)); +// } break; +// /** 题干 */ +// case STEM: { +// final String numStartRegex = "^\\d+[\\.\\、\\.]"; +// if(ReUtil.contains(numStartRegex, v)) { +// /** 题干的编号需要移除掉 */ +// questionStem.set(v.replaceFirst(numStartRegex, Strings.EMPTY).trim()); +// /** 题数++ */ +// recordsCount.incrementAndGet(); +// /** 清空变量 */ +// questionAnswer.set(Strings.EMPTY); +// +// /** 带有编号的段落,是主题干。需要创建新的题目对象 */ +// Question qs = new Question(); +// /** 题目类型,查询插入操作 */ +// QuestionType questionType = selectOrInsert(typeConcatEnum.get(), coreUser); +// qs.setQuestionTypeId(questionType.getQuestionTypeId()); +// qs.putQuestionTypeConcat(questionType.getQuestionTypeConcat()); +// qs.putQuestionTypeName(questionType.getQuestionTypeName()); +// qs.setSourceCorSchoolId(finalCorSchoolId); +// qs.setQuestionScore(questionScore.get()); +// qs.setQuestionAddTime(new Date()); +// +// if (StringUtils.isNotBlank(questionStem.get())){ +// qs.setQuestionStem(questionStem.get().replace("<","<").replace(">",">")); +// }else { +// qs.setQuestionStem(questionStem.get()); +// } +// +// qs.setQuestionStatus(1); +// qs.set("sign", 0); +// qs.setUserId(coreUser.getId()); +// qs.setOrgId(coreUser.getOrgId()); +// +// question.set(qs); +// questionList.add(question.get()); +// } +// } break; +// /** 选项 */ +// case OPTION: { +// putQuestionOption(v, question.get()); +// } break; +// /** 答案 questionAnswer不包含答案: 字样 */ +// case ANSWER: { +// questionAnswer.set(questionAnswer.get().concat(v)); +// // questionAnswer.get() 默认不包含 【答案】 字样 +// questionAnswer.set(secondFormat(questionAnswer.get(), typeConcatEnum.get())); +// question.get().setQuestionAnswer(questionAnswer.get()); +// } break; +// } +// }); +// }); +// }); +// +// if (referer.contains(publicReferer)) { +// if(questionList.size() == 0){ +// titleStb.append("
没有解析到题目"); +// }else if(ObjectUtil.isEmpty(typeConcatEnum.get())) { +// titleStb.append("
没有解析到题型"); +// }else { +// titleStb.append("
解析结果:"); +// titleStb.append(recordsCount.get()); +// titleStb.append(" 道题目"); +// } +// } +// +// Object ids[] = getCourseIdsNew(title[0], groupByCourseId.get(), groupByCourseName.get()); +// final Long courseId = (Long)ids[0]; +// final Long chapterId = (Long)ids[1]; +// String finalTitle = !referer.contains(publicReferer)? titleStb.toString(): titleStb.append(ids[2] + Strings.EMPTY).toString(); +// +// /** 最后检测属性, 并行处理,没有向外部非线程安全的变量写入东西,无需担心丢数据 */ +// questionList.parallelStream().forEach(item -> { +// item.set("chapterId", ObjectUtil.defaultIfNull(chapterId, courseId)); +// questionValidator.checkQuestion(item); +// if (referer.contains(publicReferer)) { +// checkUnique(isPublic, item); +// } +// }); +// +// /** 本次上传的题目判断唯一性 */ +// checkLocalQuestionsUnique(questionList); +// +// /** 拼成前端需要的数据 */ +// resultMap.put(url2HttpUrl(fileEntity.getUrl()),new Object[]{finalTitle,questionList}); +// } catch (Exception ex) { ex.printStackTrace(); } +// finally { +// try {inputStream.get().close();xDocument.get().close();} +// catch (Exception ioe) {ioe.printStackTrace();} +// } +// }); +// +// return JsonResult.success(resultMap); +// } +// +// /** +// * 验证本地待上传的题库 是否存在重复, +// * 本次上传的题目判断唯一性 +// * @param questionList +// */ +// private void checkLocalQuestionsUnique(List questionList) { +// if (ObjectUtil.isEmpty(questionList)) { return; } +// // 默认给50道题的容量 +// final Set hashMap = new LinkedHashSet<>(50); +// final List strings = new ArrayList<>(50); +// for (Question question : questionList) { +// String questionToString = question.toString(); +// // 如果添加失败,则代表题目已经存在 +// if (!hashMap.add(questionToString)) { +// // 告知前端 错误异常 +// question.set("errMsg", String.format("可能与上面的第 【%s】 题重复",strings.indexOf(questionToString) + 1)); +// } +// else { strings.add(questionToString); } +// } +// +// hashMap.clear(); +// strings.clear(); +// } +// +// /** +// * 检测题目唯一 +// * @param question +// */ +// public void checkUnique(Integer isPublic, @NotNull Question question) { +// /** 正则去尽可能的精准匹配, 模糊查询img标签和()() 符号 */ +// question.set("questionStemRegexp", null); +// try { +// question.set("questionStemRegexp", "^" + question.getQuestionStemRegexp() + "$"); +// if (publicSqlManager.getMapper(QuestionDao.class).checkUnique(question) > 0) { +// question.set("errMsg", ObjectUtil.defaultIfNull(question.get("errMsg"), "") + "该题目可能存在重复!"); +// } +// }catch (Exception e){ +// log.error("报错了:"+question.getQuestionStemRegexp()); +// e.printStackTrace(); +// } +// } +// +// /** +// * 格式化答案 +// *
    +// *

    情况一:

    +// *
  1. ABC

    只取 ABC
  2. +// *
  3. 转大写
  4. +// *
  5. ABC => 转换成A,B,C
  6. +// *
+// *
    +// *

    情况二:

    +// *
  1. 只取 对
  2. +// *
  3. 转大写
  4. +// * +// * @param QuestionAnswer 答案 +// * @param questionTypeConcatEnum +// * @return +// */ +// public String secondFormat(String questionAnswer, StrategyContext.QuestionTypeConcatEnum typeConcatEnum) { +// if (StringUtils.isBlank(questionAnswer)) { return questionAnswer; } +// // 去除各种空格 +// questionAnswer = org.springframework.util.StringUtils.trimAllWhitespace(questionAnswer); +// //

    ABC

    只获取ABC 删除所有标签元素。 +// String replaced = questionAnswer.replaceAll(" |", ""); +// // 取中文或者字母, 也有移除空格等符号的操作 +// String res = ObjectUtil.defaultIfNull(ReUtil.getGroup0("[A-Za-z\\u4e00-\\u9fa5]+", replaced), replaced).toUpperCase(); +// // 如果只有字母,则拆分规则: ABC => A,B,C +// if (typeConcatEnum.equals(MULTIPLE) && ReUtil.isMatch("^[A-Za-z]+$",res)) { +// // ABC => A,B,C +// String[] split = res.split(""); +// Arrays.sort(split); +// res = ArrayUtil.join(split, ","); +// } +// // 判断题逻辑 +// if (typeConcatEnum.equals(DECIDE) && ReUtil.isMatch("(正确)|(错误)|A|B",res)) { +// // ABC => A,B,C +// res = res.replaceAll("(正确)|A", "对").replaceAll("(错误)|B", "错"); +// } +// return res; +// } } \ No newline at end of file diff --git a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseStudentSigninSettingService.java b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseStudentSigninSettingService.java index 571b425b..a4f81176 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseStudentSigninSettingService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseStudentSigninSettingService.java @@ -1,6 +1,7 @@ package com.ibeetl.jlw.service; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; import cn.jlw.util.ToolUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -8,8 +9,10 @@ import com.ibeetl.admin.core.service.CoreBaseService; import com.ibeetl.admin.core.util.PlatformException; import com.ibeetl.admin.core.web.JsonResult; import com.ibeetl.admin.core.web.JsonReturnCode; +import com.ibeetl.jlw.dao.TeacherOpenCourseStudentSigninLogDao; import com.ibeetl.jlw.dao.TeacherOpenCourseStudentSigninSettingDao; import com.ibeetl.jlw.entity.TeacherOpenCourseStudentSigninSetting; +import com.ibeetl.jlw.entity.vo.SummaryBySigninSettingIdsVO; import com.ibeetl.jlw.enums.StartStatusEnum; import com.ibeetl.jlw.validator.TeacherOpenCourseStudentSigninSettingQueryValidator; import com.ibeetl.jlw.web.query.TeacherOpenCourseStudentSigninSettingQuery; @@ -26,6 +29,10 @@ import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; + +import static com.ibeetl.admin.core.util.BeanUtil.copyAllPropertiesSupportExtMap; /** * 学生签到设置 Service @@ -39,14 +46,41 @@ import java.util.List; public class TeacherOpenCourseStudentSigninSettingService extends CoreBaseService implements DeleteResourcesBy{ @Autowired private TeacherOpenCourseStudentSigninSettingDao teacherOpenCourseStudentSigninSettingDao; + @Autowired private TeacherOpenCourseStudentSigninLogDao teacherOpenCourseStudentSigninLogDao; @Autowired private TeacherOpenCourseStudentSigninSettingQueryValidator settingQueryValidator; public PageQueryqueryByCondition(PageQuery query){ PageQuery ret = teacherOpenCourseStudentSigninSettingDao.queryByCondition(query); queryListAfter(ret.getList()); +// setSummaryInfo(ret.getList()); return ret; } + /** + * 根据签到ID,设置汇总数据 + * @param list + */ + private void setSummaryInfo(List list) { + if (ObjectUtil.isNotEmpty(list)) { + String signinSettingIds = list.stream().map(item -> item.getTeacherOpenCourseStudentSigninSettingId().toString()).collect(Collectors.joining(",")); + List vos = teacherOpenCourseStudentSigninLogDao.summaryBySigninSettingIds(signinSettingIds); + + CopyOnWriteArrayList vosSync = new CopyOnWriteArrayList<>(vos); + + for (SummaryBySigninSettingIdsVO vo : vosSync) { + Long settingId = vo.getTeacherOpenCourseStudentSigninSettingId(); + for (TeacherOpenCourseStudentSigninSetting signinSetting : list) { + // 比对id,进行值拷贝操作了 + if (signinSetting.getTeacherOpenCourseStudentSigninSettingId().equals(settingId)) { + copyAllPropertiesSupportExtMap(vo, signinSetting); + vosSync.removeIf(item -> item.getTeacherOpenCourseStudentSigninSettingId().equals(settingId)); + continue; + } + } + } + } + } + public PageQueryqueryByConditionQuery(PageQuery query){ PageQuery ret = teacherOpenCourseStudentSigninSettingDao.queryByConditionQuery(query); queryListAfter(ret.getList()); diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/StrategyContext.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/StrategyContext.java new file mode 100644 index 00000000..10b53cdb --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/StrategyContext.java @@ -0,0 +1,154 @@ +package com.ibeetl.jlw.service.strategy; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReUtil; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.assertj.core.util.Lists; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + *

    + * 策略Context + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +@Component +@Lazy +@Data +@Accessors(chain=true) +public class StrategyContext { + + /** + * 待处理的文本 + */ + private String text; + + /** + * 是否保存处理后的字符串到 {@link #result} 属性中 + */ + private boolean isSaveResult; + /** + * 策略集合 + */ + @Setter(AccessLevel.PRIVATE) + @Getter(AccessLevel.PRIVATE) + private List strategyList = Lists.newArrayList(); + /** + * 处理之后的结果集 + */ + @Setter(AccessLevel.PRIVATE) + private List> result = Inner.getInstance(); + + static class Inner { + private static List> result = new ArrayList<>(); + + static List> getInstance() { + return result; + } + } + + public StrategyContext() { + strategyList.add(new WordQuestionOption()); + strategyList.add(new WordQuestionAnswer()); + strategyList.add(new WordQuestionStem()); + strategyList.add(new WordQuestionType()); + // 必须放在最后一个,用于上述判断完以后,都不成立的话,就认为它是标题。 + strategyList.add(new WordQuestionOther()); + } + + /** + * 开始处理 + * @param consumer 每一个段落的消费 + */ + public void start(Consumer> consumer) { + + for (WordStrategy wordStrategy : strategyList) { + if (StringUtils.isNotBlank(text) && wordStrategy.support(text)) { +// System.out.print("策略:" + wordStrategy.getClass().getSimpleName() + ", 值:\t"); + /** 得到处理后的值 */ + String processedResult = wordStrategy.process(text, consumer); + if (isSaveResult) { + result.add(MapUtil.of(wordStrategy.getTypeEnum(), processedResult)); + } + return; + } + } + } + + /** + * 题目类型 + */ + @Getter + public enum QuestionTypeConcatEnum { + + SINGLE("单选题", 1, "单.+题"), + MULTIPLE("多选题",2, "多.+题"), + DECIDE("判断题",3, "[判断]{1,2}.+题"), + SHORT("简答题",4, "简.+题"); + + private String typeName; + private Integer typeConcat; + /** + * 取值正则 + */ + private String regex; + + QuestionTypeConcatEnum(String typeName, Integer typeConcat, String regex) { + this.typeName = typeName; + this.typeConcat = typeConcat; + this.regex = regex; + } + + /** + * 文本匹配枚举 + * @param text + * @return + */ + public static QuestionTypeConcatEnum matchText(@NotNull String text) { + for (QuestionTypeConcatEnum typeEnum: QuestionTypeConcatEnum.values()) { + if (ReUtil.contains(typeEnum.regex, text)) { + return typeEnum; + } + } + return null; + } + } + + /** + * 段落类型 + * Integer typeConcat = null;//展示方式(1单选 2多选 3判断 4简答) + * int type = 0; // 用于记录当前行内容类型 0标题 1题型 2题干 3选项 4答案 + */ + @Getter + public enum QuestionParagraphTypeEnum { + + OTHER(WordQuestionOther.class, 0), + TYPE(WordQuestionType.class, 1), + STEM(WordQuestionStem.class, 2), + OPTION(WordQuestionOption.class, 3), + ANSWER(WordQuestionAnswer.class, 4); + + private Class clz; + private Integer type; + + QuestionParagraphTypeEnum(Class clz, Integer type) { + this.clz = clz; + this.type = type; + } + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionAnswer.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionAnswer.java new file mode 100644 index 00000000..652ba8fe --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionAnswer.java @@ -0,0 +1,46 @@ +package com.ibeetl.jlw.service.strategy; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    + * 取题目答案 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public class WordQuestionAnswer implements WordStrategy { + + private final String REGEX = "^答案[\\:\\:  ]"; + + @Getter + private StrategyContext.QuestionParagraphTypeEnum typeEnum = StrategyContext.QuestionParagraphTypeEnum.ANSWER; + /** + * 例子: A. A、a、a. + * @param paragraph 段落字符串 + * @return + */ + @Override + public boolean support(String paragraph) { + Matcher matcher = Pattern.compile(REGEX).matcher(paragraph.trim()); + return matcher.find() && matcher.start() == 0; + } + + + @Override + public String process(String paragraph, Consumer> consumer) { + paragraph = paragraph.replaceFirst(REGEX, "").trim(); + Map strategyEnumStringMap = new HashMap<>(1); + strategyEnumStringMap.put(typeEnum, paragraph.trim()); + consumer.accept(strategyEnumStringMap); + return paragraph; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOption.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOption.java new file mode 100644 index 00000000..184b1007 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOption.java @@ -0,0 +1,53 @@ +package com.ibeetl.jlw.service.strategy; + + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    + * 取题目选项 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public class WordQuestionOption implements WordStrategy { + + /** + * 举例: A. A、 A. A  A + */ + private final String REGEX = "^[A-Za-z][\\.\\、\\.  ]"; + + @Getter + private StrategyContext.QuestionParagraphTypeEnum typeEnum = StrategyContext.QuestionParagraphTypeEnum.OPTION; + + /** + * 例子: A. A、a、a. + * @param paragraph 段落字符串 + * @return + */ + @Override + public boolean support(String paragraph) { + Matcher matcher = Pattern.compile(REGEX).matcher(paragraph.trim()); + return matcher.find() && matcher.start() == 0; + } + + + @Override + public String process(String paragraph, Consumer> consumer) { + /** 删除这里,可以使用到之前使用的逻辑 */ +// paragraph = paragraph.replaceFirst(REGEX, "").trim(); + paragraph = paragraph.trim(); + Map map = new HashMap<>(1); + map.put(typeEnum, paragraph.trim()); + consumer.accept(map); + return paragraph; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOther.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOther.java new file mode 100644 index 00000000..1c6d4208 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionOther.java @@ -0,0 +1,48 @@ +package com.ibeetl.jlw.service.strategy; + + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    + * 取word的未匹配到的部分 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public class WordQuestionOther implements WordStrategy { + + private final String REGEX = ".*"; + + @Getter + private StrategyContext.QuestionParagraphTypeEnum typeEnum = StrategyContext.QuestionParagraphTypeEnum.OTHER; + + + /** + * @param paragraph 段落字符串 + * @return + */ + @Override + public boolean support(String paragraph) { + Matcher matcher = Pattern.compile(REGEX).matcher(paragraph.trim()); + return matcher.find() && matcher.start() >= 0; + } + + + @Override + public String process(String paragraph, Consumer> consumer) { + Map map = new HashMap<>(1); + paragraph = paragraph.trim(); + map.put(typeEnum, paragraph); + consumer.accept(map); + return paragraph; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionStem.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionStem.java new file mode 100644 index 00000000..91f3d6ea --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionStem.java @@ -0,0 +1,57 @@ +package com.ibeetl.jlw.service.strategy; + + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    + * 取题干 + * 因为不同的表 所以必须用不同的策略去区分,题目和选项。 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public class WordQuestionStem implements WordStrategy { + + /** + * 匹配img标签 或者 1. 1、 开头的这类的题干 + */ +// private final String REGEX = "(\\d+[\\.\\、\\.])|()"; + private final String REGEX = "^\\d+[\\.\\、\\.]"; + + @Getter + private StrategyContext.QuestionParagraphTypeEnum typeEnum = StrategyContext.QuestionParagraphTypeEnum.STEM; + + /** + * 例子: 12. 12、 + * @param paragraph 段落字符串 + * @return + */ + @Override + public boolean support(String paragraph) { + Matcher matcher = Pattern.compile(REGEX).matcher(paragraph.trim()); + return matcher.find() && matcher.start() == 0; + } + + @Override + public String process(String paragraph, Consumer> consumer) { + // 保留图片部分,删除编号部分 + final String regex = ""; + if (!(Pattern.compile(regex).matcher(paragraph).find())) { + /*.replaceFirst("\\d+[\\.\\、]", "")*/ + paragraph = paragraph.trim(); + } + Map map = new HashMap<>(1); + map.put(typeEnum, paragraph); + consumer.accept(map); + return paragraph; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionType.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionType.java new file mode 100644 index 00000000..6aac6834 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordQuestionType.java @@ -0,0 +1,63 @@ +package com.ibeetl.jlw.service.strategy; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReUtil; +import lombok.Getter; +import org.apache.logging.log4j.util.Strings; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    + * 取题类型 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public class WordQuestionType implements WordStrategy { + + private final String REGEX = "([每题]{1,2}.+共?.+分[\\))]?$"; + + @Getter + private StrategyContext.QuestionParagraphTypeEnum typeEnum = StrategyContext.QuestionParagraphTypeEnum.TYPE; + + /** + * @param paragraph 段落字符串 + * @return + */ + @Override + public boolean support(String paragraph) { + Matcher matcher = Pattern.compile(REGEX).matcher(paragraph.trim()); + return matcher.find() && matcher.start() >= 0; + } + + + @Override + public String process(String paragraph, Consumer> consumer) { + /** 定义返回集合的长度 */ + final int len = 3; + List allGroup0 = ReUtil.findAllGroup0("\\d*\\.*\\d+", paragraph); + Map map = new HashMap<>(1); + /** 控制返回的长度必须是 {@link len} */ + if (ObjectUtil.isNotEmpty(allGroup0) && allGroup0.size() < len) { + for (int i = 0; i < len - allGroup0.size() - 1; i++) { + /** 追加一个null分的记录占位 */ + allGroup0.add("500"); + } + } + paragraph = paragraph.trim(); + /** 原数组放在最后一项 */ + allGroup0.add(paragraph); + String joinStr = Strings.join(allGroup0, ','); + map.put(typeEnum, joinStr); + consumer.accept(map); + return joinStr; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/strategy/WordStrategy.java b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordStrategy.java new file mode 100644 index 00000000..4b765b9f --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/service/strategy/WordStrategy.java @@ -0,0 +1,38 @@ +package com.ibeetl.jlw.service.strategy; + +import javax.validation.constraints.NotNull; +import java.util.Map; +import java.util.function.Consumer; + +/** + *

    + * word 策略 + *

    + * + * @author mlx + * @date 2022/5/30 + * @modified + */ +public interface WordStrategy { + + /** + * 设置段落的类型 + * @return + */ + StrategyContext.QuestionParagraphTypeEnum getTypeEnum(); + + /** + * 通过验证,才能执行 process + * @param paragraph 段落字符串 + * @return + */ + boolean support(@NotNull T paragraph); + + /** + * 执行方法体 + * @param paragraph 段落字符串 + * @param consumer 消费 + * @return + */ + String process(@NotNull T paragraph, Consumer> consumer); +} diff --git a/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java b/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java index 12c51511..d18fdcf5 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java @@ -625,4 +625,13 @@ public class ResourcesQuestionController{ public JsonResult> questionTypeGroupInfo(@RequestParam(required = false) Long courseInfoId, @SCoreUser CoreUser coreUser) { return JsonResult.success(resourcesQuestionService.getGroupQuestionTypeCount(courseInfoId)); } + + //导入模板信息(word) +// @PostMapping(MODEL + "/importQBankWordTemplate.json") +// @Function("question.query") +// @ResponseBody +// public JsonResult importQBankWordTemplate(Integer isPublic,@RFile List fileEntityList,@SCoreUser CoreUser coreUser, @RequestReferer String referer) { +// JsonResult jsonResult = resourcesQuestionService.importQBankWordTemplate(isPublic,fileEntityList,coreUser,referer); +// return jsonResult; +// } } diff --git a/web/src/main/resources/sql/jlw/teacherOpenCourseStudentSigninLog.md b/web/src/main/resources/sql/jlw/teacherOpenCourseStudentSigninLog.md index 54365f2f..5fc5acab 100644 --- a/web/src/main/resources/sql/jlw/teacherOpenCourseStudentSigninLog.md +++ b/web/src/main/resources/sql/jlw/teacherOpenCourseStudentSigninLog.md @@ -600,4 +600,25 @@ summary where 1=1 @if(!isEmpty(teacherOpenCourseId)){ and t.teacher_open_course_id =#teacherOpenCourseId# - @} \ No newline at end of file + @} + +summaryBySigninSettingIds +=== +* 签到根据签到ID汇总数据 + + SELECT + t.teacher_open_course_student_signin_setting_id, + count( + DISTINCT ( t.student_id )) AS sign_total_count, + sum( teacher_open_course_student_signin_log_tag = 10 ) AS sign_count, + sum( teacher_open_course_student_signin_log_tag = 20 ) AS sign_lack_count, + round( sum( teacher_open_course_student_signin_log_tag = 10 ) / count( DISTINCT ( t.student_id )), 4 ) * 100 AS attend_rate + FROM + teacher_open_course_student_signin_log t + where 1 + @if(!isEmpty(teacherOpenCourseStudentSigninSettingId)) { + and find_in_set(t.teacher_open_course_student_signin_setting_id, #signinSettingIds#) + @} + GROUP BY + t.teacher_open_course_student_signin_setting_id + \ No newline at end of file